Merge "Fix corrupted counter outputs by fixing flag caching" into androidx-main
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1830b7d..e596623 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -13,7 +13,6 @@
   - [AppCompat](https://developer.android.com/jetpack/androidx/releases/appcompat)
   - [Biometric](https://developer.android.com/training/sign-in/biometric-auth)
   - [Collection](https://developer.android.com/jetpack/androidx/releases/collection)
-  - [Compose Compiler](https://developer.android.com/jetpack/androidx/releases/compose-compiler)
   - [Compose Runtime](https://developer.android.com/jetpack/androidx/releases/compose-runtime)
   - [Core](https://developer.android.com/jetpack/androidx/releases/core)
   - [DataStore](https://developer.android.com/topic/libraries/architecture/datastore)
diff --git a/README.md b/README.md
index be89bdb..c6f0aa3 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,6 @@
 * [AppCompat](appcompat)
 * [Biometric](biometric)
 * [Collection](collection)
-* [Compose Compiler](compose/compiler)
 * [Compose Runtime](compose/runtime)
 * [Core](core)
 * [DataStore](datastore)
diff --git a/activity/activity-compose-lint/build.gradle b/activity/activity-compose-lint/build.gradle
index 88a73b5..42e9863 100644
--- a/activity/activity-compose-lint/build.gradle
+++ b/activity/activity-compose-lint/build.gradle
@@ -35,12 +35,16 @@
     compileOnly(libs.androidLintMinApi)
     compileOnly(libs.kotlinStdlib)
     bundleInside(projectOrArtifact(":compose:lint:common"))
+    compileOnly(libs.intellijCore)
+    compileOnly(libs.uast)
+    compileOnly(libs.intellijKotlinCompiler)
+
 
     testImplementation(projectOrArtifact(":compose:lint:common-test"))
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.kotlinReflect)
+    testRuntimeOnly(libs.kotlinReflect)
     testImplementation(libs.kotlinStdlibJdk8)
-    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintApi)
     testImplementation(libs.androidLintTests)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/activity/activity-compose/api/1.10.0-beta01.txt b/activity/activity-compose/api/1.10.0-beta01.txt
new file mode 100644
index 0000000..df9fcaf
--- /dev/null
+++ b/activity/activity-compose/api/1.10.0-beta01.txt
@@ -0,0 +1,55 @@
+// Signature format: 4.0
+package androidx.activity.compose {
+
+  public final class ActivityResultRegistryKt {
+    method @androidx.compose.runtime.Composable public static <I, O> androidx.activity.compose.ManagedActivityResultLauncher<I,O> rememberLauncherForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, kotlin.jvm.functions.Function1<? super O,kotlin.Unit> onResult);
+  }
+
+  public final class BackHandlerKt {
+    method @androidx.compose.runtime.Composable public static void BackHandler(optional boolean enabled, kotlin.jvm.functions.Function0<kotlin.Unit> onBack);
+  }
+
+  public final class ComponentActivityKt {
+    method public static void setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionContext? parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class LocalActivityResultRegistryOwner {
+    method @androidx.compose.runtime.Composable public androidx.activity.result.ActivityResultRegistryOwner? getCurrent();
+    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.result.ActivityResultRegistryOwner?> provides(androidx.activity.result.ActivityResultRegistryOwner registryOwner);
+    property @androidx.compose.runtime.Composable public final androidx.activity.result.ActivityResultRegistryOwner? current;
+    field public static final androidx.activity.compose.LocalActivityResultRegistryOwner INSTANCE;
+  }
+
+  public final class LocalFullyDrawnReporterOwner {
+    method @androidx.compose.runtime.Composable public androidx.activity.FullyDrawnReporterOwner? getCurrent();
+    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.FullyDrawnReporterOwner?> provides(androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
+    property @androidx.compose.runtime.Composable public final androidx.activity.FullyDrawnReporterOwner? current;
+    field public static final androidx.activity.compose.LocalFullyDrawnReporterOwner INSTANCE;
+  }
+
+  public final class LocalOnBackPressedDispatcherOwner {
+    method @androidx.compose.runtime.Composable public androidx.activity.OnBackPressedDispatcherOwner? getCurrent();
+    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.OnBackPressedDispatcherOwner?> provides(androidx.activity.OnBackPressedDispatcherOwner dispatcherOwner);
+    property @androidx.compose.runtime.Composable public final androidx.activity.OnBackPressedDispatcherOwner? current;
+    field public static final androidx.activity.compose.LocalOnBackPressedDispatcherOwner INSTANCE;
+  }
+
+  public final class ManagedActivityResultLauncher<I, O> extends androidx.activity.result.ActivityResultLauncher<I> {
+    method public androidx.activity.result.contract.ActivityResultContract<I,O> getContract();
+    method public void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
+    method @Deprecated public void unregister();
+    property public androidx.activity.result.contract.ActivityResultContract<I,O> contract;
+  }
+
+  public final class PredictiveBackHandlerKt {
+    method @androidx.compose.runtime.Composable public static void PredictiveBackHandler(optional boolean enabled, kotlin.jvm.functions.Function2<kotlinx.coroutines.flow.Flow<androidx.activity.BackEventCompat>,? super kotlin.coroutines.Continuation<kotlin.Unit>,? extends java.lang.Object?> onBack);
+  }
+
+  public final class ReportDrawnKt {
+    method @androidx.compose.runtime.Composable public static void ReportDrawn();
+    method @androidx.compose.runtime.Composable public static void ReportDrawnAfter(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block);
+    method @androidx.compose.runtime.Composable public static void ReportDrawnWhen(kotlin.jvm.functions.Function0<java.lang.Boolean> predicate);
+  }
+
+}
+
diff --git a/constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt b/activity/activity-compose/api/res-1.10.0-beta01.txt
similarity index 100%
copy from constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt
copy to activity/activity-compose/api/res-1.10.0-beta01.txt
diff --git a/activity/activity-compose/api/restricted_1.10.0-beta01.txt b/activity/activity-compose/api/restricted_1.10.0-beta01.txt
new file mode 100644
index 0000000..df9fcaf
--- /dev/null
+++ b/activity/activity-compose/api/restricted_1.10.0-beta01.txt
@@ -0,0 +1,55 @@
+// Signature format: 4.0
+package androidx.activity.compose {
+
+  public final class ActivityResultRegistryKt {
+    method @androidx.compose.runtime.Composable public static <I, O> androidx.activity.compose.ManagedActivityResultLauncher<I,O> rememberLauncherForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, kotlin.jvm.functions.Function1<? super O,kotlin.Unit> onResult);
+  }
+
+  public final class BackHandlerKt {
+    method @androidx.compose.runtime.Composable public static void BackHandler(optional boolean enabled, kotlin.jvm.functions.Function0<kotlin.Unit> onBack);
+  }
+
+  public final class ComponentActivityKt {
+    method public static void setContent(androidx.activity.ComponentActivity, optional androidx.compose.runtime.CompositionContext? parent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class LocalActivityResultRegistryOwner {
+    method @androidx.compose.runtime.Composable public androidx.activity.result.ActivityResultRegistryOwner? getCurrent();
+    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.result.ActivityResultRegistryOwner?> provides(androidx.activity.result.ActivityResultRegistryOwner registryOwner);
+    property @androidx.compose.runtime.Composable public final androidx.activity.result.ActivityResultRegistryOwner? current;
+    field public static final androidx.activity.compose.LocalActivityResultRegistryOwner INSTANCE;
+  }
+
+  public final class LocalFullyDrawnReporterOwner {
+    method @androidx.compose.runtime.Composable public androidx.activity.FullyDrawnReporterOwner? getCurrent();
+    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.FullyDrawnReporterOwner?> provides(androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
+    property @androidx.compose.runtime.Composable public final androidx.activity.FullyDrawnReporterOwner? current;
+    field public static final androidx.activity.compose.LocalFullyDrawnReporterOwner INSTANCE;
+  }
+
+  public final class LocalOnBackPressedDispatcherOwner {
+    method @androidx.compose.runtime.Composable public androidx.activity.OnBackPressedDispatcherOwner? getCurrent();
+    method public infix androidx.compose.runtime.ProvidedValue<androidx.activity.OnBackPressedDispatcherOwner?> provides(androidx.activity.OnBackPressedDispatcherOwner dispatcherOwner);
+    property @androidx.compose.runtime.Composable public final androidx.activity.OnBackPressedDispatcherOwner? current;
+    field public static final androidx.activity.compose.LocalOnBackPressedDispatcherOwner INSTANCE;
+  }
+
+  public final class ManagedActivityResultLauncher<I, O> extends androidx.activity.result.ActivityResultLauncher<I> {
+    method public androidx.activity.result.contract.ActivityResultContract<I,O> getContract();
+    method public void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
+    method @Deprecated public void unregister();
+    property public androidx.activity.result.contract.ActivityResultContract<I,O> contract;
+  }
+
+  public final class PredictiveBackHandlerKt {
+    method @androidx.compose.runtime.Composable public static void PredictiveBackHandler(optional boolean enabled, kotlin.jvm.functions.Function2<kotlinx.coroutines.flow.Flow<androidx.activity.BackEventCompat>,? super kotlin.coroutines.Continuation<kotlin.Unit>,? extends java.lang.Object?> onBack);
+  }
+
+  public final class ReportDrawnKt {
+    method @androidx.compose.runtime.Composable public static void ReportDrawn();
+    method @androidx.compose.runtime.Composable public static void ReportDrawnAfter(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block);
+    method @androidx.compose.runtime.Composable public static void ReportDrawnWhen(kotlin.jvm.functions.Function0<java.lang.Boolean> predicate);
+  }
+
+}
+
diff --git a/activity/activity-compose/build.gradle b/activity/activity-compose/build.gradle
index 0d8a233..1ec1fad 100644
--- a/activity/activity-compose/build.gradle
+++ b/activity/activity-compose/build.gradle
@@ -33,18 +33,35 @@
 dependencies {
 
     implementation(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesCore)
     api("androidx.compose.runtime:runtime:1.0.1")
     api("androidx.compose.runtime:runtime-saveable:1.0.1")
     api(projectOrArtifact(":activity:activity-ktx"))
-    api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
     api("androidx.compose.ui:ui:1.0.1")
+    api("androidx.core:core-ktx:1.13.0")
+    api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
+    implementation("androidx.lifecycle:lifecycle-common:2.6.1")
+    implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
+    implementation("androidx.savedstate:savedstate:1.2.1")
 
+    androidTestImplementation("androidx.annotation:annotation:1.8.1")
+    androidTestImplementation("androidx.compose.foundation:foundation-layout:1.6.0")
     androidTestImplementation projectOrArtifact(":compose:ui:ui-test-junit4")
     androidTestImplementation projectOrArtifact(":compose:material:material")
-    androidTestImplementation project(":compose:test-utils")
+    androidTestRuntimeOnly project(":compose:test-utils")
+    androidTestImplementation(project(":compose:foundation:foundation"))
+    androidTestImplementation(project(":compose:runtime:runtime"))
+    androidTestImplementation(project(":compose:ui:ui"))
+    androidTestImplementation(project(":compose:ui:ui-graphics"))
+    androidTestImplementation(project(":compose:ui:ui-test"))
+    androidTestImplementation(project(":compose:ui:ui-text"))
+    androidTestImplementation(project(":lifecycle:lifecycle-common"))
+    androidTestImplementation(project(":lifecycle:lifecycle-runtime"))
     androidTestImplementation projectOrArtifact(":lifecycle:lifecycle-runtime-testing")
+    androidTestImplementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.2")
+    androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testExtJunitKtx)
+    androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.truth)
 
@@ -57,11 +74,11 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2020"
     description = "Compose integration with Activity"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(projectOrArtifact(":activity:activity-compose:activity-compose-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.activity.compose"
 }
diff --git a/activity/activity-compose/integration-tests/activity-demos/build.gradle b/activity/activity-compose/integration-tests/activity-demos/build.gradle
index bfcfdbe..e69de29 100644
--- a/activity/activity-compose/integration-tests/activity-demos/build.gradle
+++ b/activity/activity-compose/integration-tests/activity-demos/build.gradle
@@ -1,48 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This file was created using the `create_project.py` script located in the
- * `<AndroidX root>/development/project-creator` directory.
- *
- * Please use that script when creating a new project, rather than copying an existing project and
- * modifying its settings.
- */
-import androidx.build.Publish
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-    id("AndroidXComposePlugin")
-    id("org.jetbrains.kotlin.android")
-}
-
-dependencies {
-    implementation(libs.kotlinStdlib)
-    implementation projectOrArtifact(":activity:activity-compose")
-    implementation projectOrArtifact(":activity:activity-compose:activity-compose-samples")
-}
-
-androidx {
-    name = "Compose Activity Demos"
-    publish = Publish.NONE
-    inceptionYear = "2020"
-    description = "This is a project for Activity demos."
-}
-
-android {
-    namespace "androidx.activity.compose.demos"
-}
diff --git a/activity/activity-compose/samples/build.gradle b/activity/activity-compose/samples/build.gradle
index 9f5fa5b..27063bd 100644
--- a/activity/activity-compose/samples/build.gradle
+++ b/activity/activity-compose/samples/build.gradle
@@ -32,11 +32,18 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesCore)
 
     compileOnly project(":annotation:annotation-sampled")
+    api("androidx.compose.foundation:foundation-layout:1.0.1")
+    api("androidx.compose.runtime:runtime:1.0.1")
     implementation "androidx.compose.foundation:foundation:1.0.1"
     implementation projectOrArtifact(":activity:activity-compose")
-    implementation projectOrArtifact(":activity:activity-ktx")
+    implementation projectOrArtifact(":activity:activity")
+    implementation("androidx.compose.ui:ui-graphics:1.0.1")
+    implementation("androidx.compose.ui:ui-text:1.0.1")
+    implementation("androidx.compose.ui:ui:1.0.1")
+    implementation("androidx.core:core-ktx:1.13.0")
     implementation "androidx.compose.material:material:1.0.1"
     // old version of common-java8 conflicts with newer version, because both have
     // DefaultLifecycleEventObserver.
@@ -54,5 +61,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.activity.compose.samples"
 }
diff --git a/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt b/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt
index 8cc48ff..1ba51b4 100644
--- a/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt
+++ b/activity/activity-compose/src/androidTest/java/androidx/activity/compose/PredictiveBackHandlerTest.kt
@@ -104,6 +104,36 @@
     }
 
     @Test
+    fun testHandleOnCompleteWithDispatcher() {
+        var counter = 0
+        lateinit var dispatcher: OnBackPressedDispatcher
+
+        rule.setContent {
+            PredictiveBackHandler { progress ->
+                progress.collect()
+                counter++
+            }
+            dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
+            Button(
+                onClick = {
+                    dispatcher.startGestureBack()
+                    dispatcher.api34Complete()
+                }
+            ) {
+                Text(text = "backPress")
+            }
+        }
+
+        rule.onNodeWithText("backPress").performClick()
+
+        rule.runOnIdle { assertThat(counter).isEqualTo(1) }
+
+        dispatcher.onBackPressed()
+
+        rule.runOnIdle { assertThat(counter).isEqualTo(2) }
+    }
+
+    @Test
     fun testDisabledBackHandler() {
         val result = mutableListOf<String>()
         var enabled by mutableStateOf(true)
diff --git a/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt b/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt
index 462ebe47..1000765 100644
--- a/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt
+++ b/activity/activity-compose/src/main/java/androidx/activity/compose/PredictiveBackHandler.kt
@@ -113,12 +113,14 @@
                 // finally, we close the channel to ensure no more events can be sent
                 // but let the job complete normally
                 onBackInstance?.close()
+                onBackInstance?.isPredictiveBack = false
             }
 
             override fun handleOnBackCancelled() {
                 super.handleOnBackCancelled()
                 // cancel will purge the channel of any sent events that are yet to be received
                 onBackInstance?.cancel()
+                onBackInstance?.isPredictiveBack = false
             }
         }
     }
@@ -143,7 +145,7 @@
 
 private class OnBackInstance(
     scope: CoroutineScope,
-    val isPredictiveBack: Boolean,
+    var isPredictiveBack: Boolean,
     onBack: suspend (progress: Flow<BackEventCompat>) -> Unit,
 ) {
     val channel = Channel<BackEventCompat>(capacity = BUFFERED, onBufferOverflow = SUSPEND)
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/activity/activity-ktx/api/1.10.0-beta01.txt
similarity index 100%
rename from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
rename to activity/activity-ktx/api/1.10.0-beta01.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/activity/activity-ktx/api/res-1.10.0-beta01.txt
similarity index 100%
rename from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
rename to activity/activity-ktx/api/res-1.10.0-beta01.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/activity/activity-ktx/api/restricted_1.10.0-beta01.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to activity/activity-ktx/api/restricted_1.10.0-beta01.txt
diff --git a/activity/activity-ktx/build.gradle b/activity/activity-ktx/build.gradle
index 6cc0206..2c7e7ac 100644
--- a/activity/activity-ktx/build.gradle
+++ b/activity/activity-ktx/build.gradle
@@ -31,11 +31,7 @@
 }
 
 dependencies {
-
     api(project(":activity:activity"))
-    api("androidx.core:core-ktx:1.13.0") {
-        because "Mirror activity dependency graph for -ktx artifacts"
-    }
     api("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
@@ -43,18 +39,6 @@
     api("androidx.savedstate:savedstate-ktx:1.2.1") {
         because 'Mirror activity dependency graph for -ktx artifacts'
     }
-    api(libs.kotlinStdlib)
-
-    androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
-    androidTestImplementation(libs.junit)
-    androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(project(":internal-testutils-runtime"), {
-        exclude group: "androidx.activity", module: "activity"
-    })
 }
 
 androidx {
@@ -62,9 +46,9 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'activity' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.activity.ktx"
 }
diff --git a/activity/activity-lint/build.gradle b/activity/activity-lint/build.gradle
index 7dabcfd..955e21f 100644
--- a/activity/activity-lint/build.gradle
+++ b/activity/activity-lint/build.gradle
@@ -32,11 +32,13 @@
     compileOnly("com.android.tools.build:builder-model:8.1.0")
     compileOnly(libs.androidLintMinApi)
     compileOnly(libs.kotlinStdlib)
+    compileOnly(libs.intellijCore)
+    compileOnly(libs.uast)
 
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.kotlinReflect)
+    testRuntimeOnly(libs.kotlinReflect)
     testImplementation(libs.kotlinStdlibJdk8)
-    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintApi)
     testImplementation(libs.androidLintTests)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/activity/activity/api/1.10.0-beta01.txt b/activity/activity/api/1.10.0-beta01.txt
new file mode 100644
index 0000000..be7377b
--- /dev/null
+++ b/activity/activity/api/1.10.0-beta01.txt
@@ -0,0 +1,548 @@
+// Signature format: 4.0
+package androidx.activity {
+
+  public final class ActivityViewModelLazyKt {
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+  public final class BackEventCompat {
+    ctor @RequiresApi(34) public BackEventCompat(android.window.BackEvent backEvent);
+    ctor @VisibleForTesting public BackEventCompat(float touchX, float touchY, @FloatRange(from=0.0, to=1.0) float progress, int swipeEdge);
+    method public float getProgress();
+    method public int getSwipeEdge();
+    method public float getTouchX();
+    method public float getTouchY();
+    method @RequiresApi(34) public android.window.BackEvent toBackEvent();
+    property public final float progress;
+    property public final int swipeEdge;
+    property public final float touchX;
+    property public final float touchY;
+    field public static final androidx.activity.BackEventCompat.Companion Companion;
+    field public static final int EDGE_LEFT = 0; // 0x0
+    field public static final int EDGE_RIGHT = 1; // 0x1
+  }
+
+  public static final class BackEventCompat.Companion {
+  }
+
+  public class ComponentActivity extends android.app.Activity implements androidx.activity.result.ActivityResultCaller androidx.activity.result.ActivityResultRegistryOwner androidx.activity.contextaware.ContextAware androidx.activity.FullyDrawnReporterOwner androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.core.view.MenuHost androidx.activity.OnBackPressedDispatcherOwner androidx.core.content.OnConfigurationChangedProvider androidx.core.app.OnMultiWindowModeChangedProvider androidx.core.app.OnNewIntentProvider androidx.core.app.OnPictureInPictureModeChangedProvider androidx.core.content.OnTrimMemoryProvider androidx.core.app.OnUserLeaveHintProvider androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+    ctor public ComponentActivity();
+    ctor @ContentView public ComponentActivity(@LayoutRes int contentLayoutId);
+    method public void addMenuProvider(androidx.core.view.MenuProvider provider);
+    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner);
+    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner, androidx.lifecycle.Lifecycle.State state);
+    method public final void addOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
+    method public final void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public final void addOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
+    method public final void addOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
+    method public final void addOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
+    method public final void addOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
+    method public final void addOnUserLeaveHintListener(Runnable listener);
+    method public final androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
+    method @Deprecated public Object? getLastCustomNonConfigurationInstance();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+    method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    method @CallSuper public void initializeViewTreeOwners();
+    method public void invalidateMenu();
+    method @Deprecated @CallSuper protected void onActivityResult(int requestCode, int resultCode, android.content.Intent? data);
+    method @Deprecated @CallSuper public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
+    method @Deprecated public Object? onRetainCustomNonConfigurationInstance();
+    method public final Object? onRetainNonConfigurationInstance();
+    method public android.content.Context? peekAvailableContext();
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public void removeMenuProvider(androidx.core.view.MenuProvider provider);
+    method public final void removeOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
+    method public final void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public final void removeOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
+    method public final void removeOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
+    method public final void removeOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
+    method public final void removeOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
+    method public final void removeOnUserLeaveHintListener(Runnable listener);
+    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode);
+    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode, android.os.Bundle? options);
+    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws android.content.IntentSender.SendIntentException;
+    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags, android.os.Bundle? options) throws android.content.IntentSender.SendIntentException;
+    property public final androidx.activity.result.ActivityResultRegistry activityResultRegistry;
+    property @CallSuper public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+    property public androidx.activity.FullyDrawnReporter fullyDrawnReporter;
+    property @Deprecated public Object? lastCustomNonConfigurationInstance;
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
+    property public final androidx.savedstate.SavedStateRegistry savedStateRegistry;
+    property public androidx.lifecycle.ViewModelStore viewModelStore;
+  }
+
+  public class ComponentDialog extends android.app.Dialog implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner {
+    ctor public ComponentDialog(android.content.Context context);
+    ctor public ComponentDialog(android.content.Context context, optional @StyleRes int themeResId);
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method @CallSuper public void initializeViewTreeOwners();
+    method @CallSuper public void onBackPressed();
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
+    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+  }
+
+  public final class EdgeToEdge {
+    method public static void enable(androidx.activity.ComponentActivity);
+    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle);
+    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle, optional androidx.activity.SystemBarStyle navigationBarStyle);
+  }
+
+  public final class FullyDrawnReporter {
+    ctor public FullyDrawnReporter(java.util.concurrent.Executor executor, kotlin.jvm.functions.Function0<kotlin.Unit> reportFullyDrawn);
+    method public void addOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
+    method public void addReporter();
+    method public boolean isFullyDrawnReported();
+    method public void removeOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
+    method public void removeReporter();
+    property public final boolean isFullyDrawnReported;
+  }
+
+  public final class FullyDrawnReporterKt {
+    method public static suspend inline Object? reportWhenComplete(androidx.activity.FullyDrawnReporter, kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> reporter, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public interface FullyDrawnReporterOwner {
+    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
+    property public abstract androidx.activity.FullyDrawnReporter fullyDrawnReporter;
+  }
+
+  public abstract class OnBackPressedCallback {
+    ctor public OnBackPressedCallback(boolean enabled);
+    method @MainThread public void handleOnBackCancelled();
+    method @MainThread public abstract void handleOnBackPressed();
+    method @MainThread public void handleOnBackProgressed(androidx.activity.BackEventCompat backEvent);
+    method @MainThread public void handleOnBackStarted(androidx.activity.BackEventCompat backEvent);
+    method @MainThread public final boolean isEnabled();
+    method @MainThread public final void remove();
+    method @MainThread public final void setEnabled(boolean);
+    property @MainThread public final boolean isEnabled;
+  }
+
+  public final class OnBackPressedDispatcher {
+    ctor public OnBackPressedDispatcher();
+    ctor public OnBackPressedDispatcher(optional Runnable? fallbackOnBackPressed);
+    ctor public OnBackPressedDispatcher(Runnable? fallbackOnBackPressed, androidx.core.util.Consumer<java.lang.Boolean>? onHasEnabledCallbacksChanged);
+    method @MainThread public void addCallback(androidx.activity.OnBackPressedCallback onBackPressedCallback);
+    method @MainThread public void addCallback(androidx.lifecycle.LifecycleOwner owner, androidx.activity.OnBackPressedCallback onBackPressedCallback);
+    method @MainThread @VisibleForTesting public void dispatchOnBackCancelled();
+    method @MainThread @VisibleForTesting public void dispatchOnBackProgressed(androidx.activity.BackEventCompat backEvent);
+    method @MainThread @VisibleForTesting public void dispatchOnBackStarted(androidx.activity.BackEventCompat backEvent);
+    method @MainThread public boolean hasEnabledCallbacks();
+    method @MainThread public void onBackPressed();
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher invoker);
+  }
+
+  public final class OnBackPressedDispatcherKt {
+    method public static androidx.activity.OnBackPressedCallback addCallback(androidx.activity.OnBackPressedDispatcher, optional androidx.lifecycle.LifecycleOwner? owner, optional boolean enabled, kotlin.jvm.functions.Function1<? super androidx.activity.OnBackPressedCallback,kotlin.Unit> onBackPressed);
+  }
+
+  public interface OnBackPressedDispatcherOwner extends androidx.lifecycle.LifecycleOwner {
+    method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+    property public abstract androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
+  }
+
+  public final class PipHintTrackerKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static suspend Object? trackPipAnimationHintView(android.app.Activity, android.view.View view, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public final class SystemBarStyle {
+    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
+    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
+    method public static androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
+    method public static androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
+    field public static final androidx.activity.SystemBarStyle.Companion Companion;
+  }
+
+  public static final class SystemBarStyle.Companion {
+    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
+    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
+    method public androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
+    method public androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
+  }
+
+  public final class ViewTreeFullyDrawnReporterOwner {
+    method public static androidx.activity.FullyDrawnReporterOwner? get(android.view.View);
+    method public static void set(android.view.View, androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
+  }
+
+  public final class ViewTreeOnBackPressedDispatcherOwner {
+    method public static androidx.activity.OnBackPressedDispatcherOwner? get(android.view.View);
+    method public static void set(android.view.View, androidx.activity.OnBackPressedDispatcherOwner onBackPressedDispatcherOwner);
+  }
+
+}
+
+package androidx.activity.contextaware {
+
+  public interface ContextAware {
+    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public android.content.Context? peekAvailableContext();
+    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+  }
+
+  public final class ContextAwareHelper {
+    ctor public ContextAwareHelper();
+    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public void clearAvailableContext();
+    method public void dispatchOnContextAvailable(android.content.Context context);
+    method public android.content.Context? peekAvailableContext();
+    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+  }
+
+  public final class ContextAwareKt {
+    method public static suspend inline <R> Object? withContextAvailable(androidx.activity.contextaware.ContextAware, kotlin.jvm.functions.Function1<android.content.Context,R> onContextAvailable, kotlin.coroutines.Continuation<R>);
+  }
+
+  public fun interface OnContextAvailableListener {
+    method public void onContextAvailable(android.content.Context context);
+  }
+
+}
+
+package androidx.activity.result {
+
+  public final class ActivityResult implements android.os.Parcelable {
+    ctor public ActivityResult(int resultCode, android.content.Intent? data);
+    method public int describeContents();
+    method public android.content.Intent? getData();
+    method public int getResultCode();
+    method public static String resultCodeToString(int resultCode);
+    method public void writeToParcel(android.os.Parcel dest, int flags);
+    property public final android.content.Intent? data;
+    property public final int resultCode;
+    field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult> CREATOR;
+    field public static final androidx.activity.result.ActivityResult.Companion Companion;
+  }
+
+  public static final class ActivityResult.Companion {
+    method public String resultCodeToString(int resultCode);
+  }
+
+  public fun interface ActivityResultCallback<O> {
+    method public void onActivityResult(O result);
+  }
+
+  public interface ActivityResultCaller {
+    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
+  }
+
+  public final class ActivityResultCallerKt {
+    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.activity.result.ActivityResultRegistry registry, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
+    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
+  }
+
+  public final class ActivityResultKt {
+    method public static operator int component1(androidx.activity.result.ActivityResult);
+    method public static operator android.content.Intent? component2(androidx.activity.result.ActivityResult);
+  }
+
+  public abstract class ActivityResultLauncher<I> {
+    ctor public ActivityResultLauncher();
+    method public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> getContract();
+    method public void launch(I input);
+    method public abstract void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
+    method @MainThread public abstract void unregister();
+    property public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> contract;
+  }
+
+  public final class ActivityResultLauncherKt {
+    method public static void launch(androidx.activity.result.ActivityResultLauncher<java.lang.Void?>, optional androidx.core.app.ActivityOptionsCompat? options);
+    method public static void launchUnit(androidx.activity.result.ActivityResultLauncher<kotlin.Unit>, optional androidx.core.app.ActivityOptionsCompat? options);
+  }
+
+  public abstract class ActivityResultRegistry {
+    ctor public ActivityResultRegistry();
+    method @MainThread public final boolean dispatchResult(int requestCode, int resultCode, android.content.Intent? data);
+    method @MainThread public final <O> boolean dispatchResult(int requestCode, O result);
+    method @MainThread public abstract <I, O> void onLaunch(int requestCode, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.core.app.ActivityOptionsCompat? options);
+    method public final void onRestoreInstanceState(android.os.Bundle? savedInstanceState);
+    method public final void onSaveInstanceState(android.os.Bundle outState);
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.lifecycle.LifecycleOwner lifecycleOwner, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+  }
+
+  public interface ActivityResultRegistryOwner {
+    method public androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
+    property public abstract androidx.activity.result.ActivityResultRegistry activityResultRegistry;
+  }
+
+  public final class IntentSenderRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.Intent? getFillInIntent();
+    method public int getFlagsMask();
+    method public int getFlagsValues();
+    method public android.content.IntentSender getIntentSender();
+    method public void writeToParcel(android.os.Parcel dest, int flags);
+    property public final android.content.Intent? fillInIntent;
+    property public final int flagsMask;
+    property public final int flagsValues;
+    property public final android.content.IntentSender intentSender;
+    field public static final android.os.Parcelable.Creator<androidx.activity.result.IntentSenderRequest> CREATOR;
+    field public static final androidx.activity.result.IntentSenderRequest.Companion Companion;
+  }
+
+  public static final class IntentSenderRequest.Builder {
+    ctor public IntentSenderRequest.Builder(android.app.PendingIntent pendingIntent);
+    ctor public IntentSenderRequest.Builder(android.content.IntentSender intentSender);
+    method public androidx.activity.result.IntentSenderRequest build();
+    method public androidx.activity.result.IntentSenderRequest.Builder setFillInIntent(android.content.Intent? fillInIntent);
+    method public androidx.activity.result.IntentSenderRequest.Builder setFlags(int values, int mask);
+  }
+
+  public static final class IntentSenderRequest.Companion {
+  }
+
+  public final class PickVisualMediaRequest {
+    method public long getAccentColor();
+    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab getDefaultTab();
+    method public int getMaxItems();
+    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType getMediaType();
+    method public boolean isCustomAccentColorApplied();
+    method public boolean isOrderedSelection();
+    property public final long accentColor;
+    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab;
+    property public final boolean isCustomAccentColorApplied;
+    property public final boolean isOrderedSelection;
+    property public final int maxItems;
+    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType;
+  }
+
+  public static final class PickVisualMediaRequest.Builder {
+    ctor public PickVisualMediaRequest.Builder();
+    method public androidx.activity.result.PickVisualMediaRequest build();
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setAccentColor(long accentColor);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setDefaultTab(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setMaxItems(@IntRange(from=2L) int maxItems);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setMediaType(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setOrderedSelection(boolean isOrderedSelection);
+  }
+
+  public final class PickVisualMediaRequestKt {
+    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
+    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(long accentColor, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+  }
+
+}
+
+package androidx.activity.result.contract {
+
+  public abstract class ActivityResultContract<I, O> {
+    ctor public ActivityResultContract();
+    method public abstract android.content.Intent createIntent(android.content.Context context, I input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<O>? getSynchronousResult(android.content.Context context, I input);
+    method public abstract O parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static final class ActivityResultContract.SynchronousResult<T> {
+    ctor public ActivityResultContract.SynchronousResult(T value);
+    method public T getValue();
+    property public final T value;
+  }
+
+  public final class ActivityResultContracts {
+  }
+
+  public static class ActivityResultContracts.CaptureVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
+    ctor public ActivityResultContracts.CaptureVideo();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
+    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.CreateDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
+    ctor @Deprecated public ActivityResultContracts.CreateDocument();
+    ctor public ActivityResultContracts.CreateDocument(String mimeType);
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.GetContent extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
+    ctor public ActivityResultContracts.GetContent();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.GetMultipleContents extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.GetMultipleContents();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String input);
+    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.OpenDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],android.net.Uri?> {
+    ctor public ActivityResultContracts.OpenDocument();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String[] input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  @RequiresApi(21) public static class ActivityResultContracts.OpenDocumentTree extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri?,android.net.Uri?> {
+    ctor public ActivityResultContracts.OpenDocumentTree();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri? input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, android.net.Uri? input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.OpenMultipleDocuments extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.OpenMultipleDocuments();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String[] input);
+    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static final class ActivityResultContracts.PickContact extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.net.Uri?> {
+    ctor public ActivityResultContracts.PickContact();
+    method public android.content.Intent createIntent(android.content.Context context, Void? input);
+    method public android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.PickMultipleVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.PickMultipleVisualMedia();
+    ctor public ActivityResultContracts.PickMultipleVisualMedia(optional int maxItems);
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.PickVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,android.net.Uri?> {
+    ctor public ActivityResultContracts.PickVisualMedia();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method @Deprecated public static final boolean isPhotoPickerAvailable();
+    method public static final boolean isPhotoPickerAvailable(android.content.Context context);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+    field public static final String ACTION_SYSTEM_FALLBACK_PICK_IMAGES = "androidx.activity.result.contract.action.PICK_IMAGES";
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion Companion;
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR = "androidx.activity.result.contract.extra.PICK_IMAGES_ACCENT_COLOR";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER = "androidx.activity.result.contract.extra.PICK_IMAGES_IN_ORDER";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB = "androidx.activity.result.contract.extra.PICK_IMAGES_LAUNCH_TAB";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX = "androidx.activity.result.contract.extra.PICK_IMAGES_MAX";
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.Companion {
+    method @Deprecated public boolean isPhotoPickerAvailable();
+    method public boolean isPhotoPickerAvailable(android.content.Context context);
+  }
+
+  public abstract static class ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public abstract int getValue();
+    property public abstract int value;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.ImageAndVideo implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.ImageOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageOnly INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.SingleMimeType implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    ctor public ActivityResultContracts.PickVisualMedia.SingleMimeType(String mimeType);
+    method public String getMimeType();
+    property public final String mimeType;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.VideoOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VideoOnly INSTANCE;
+  }
+
+  public static sealed interface ActivityResultContracts.PickVisualMedia.VisualMediaType {
+  }
+
+  public static final class ActivityResultContracts.RequestMultiplePermissions extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.Map<java.lang.String,java.lang.Boolean>> {
+    ctor public ActivityResultContracts.RequestMultiplePermissions();
+    method public android.content.Intent createIntent(android.content.Context context, String[] input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.Map<java.lang.String,java.lang.Boolean>>? getSynchronousResult(android.content.Context context, String[] input);
+    method public java.util.Map<java.lang.String,java.lang.Boolean> parseResult(int resultCode, android.content.Intent? intent);
+    field public static final String ACTION_REQUEST_PERMISSIONS = "androidx.activity.result.contract.action.REQUEST_PERMISSIONS";
+    field public static final androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion Companion;
+    field public static final String EXTRA_PERMISSIONS = "androidx.activity.result.contract.extra.PERMISSIONS";
+    field public static final String EXTRA_PERMISSION_GRANT_RESULTS = "androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS";
+  }
+
+  public static final class ActivityResultContracts.RequestMultiplePermissions.Companion {
+  }
+
+  public static final class ActivityResultContracts.RequestPermission extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.lang.Boolean> {
+    ctor public ActivityResultContracts.RequestPermission();
+    method public android.content.Intent createIntent(android.content.Context context, String input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, String input);
+    method public Boolean parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static final class ActivityResultContracts.StartActivityForResult extends androidx.activity.result.contract.ActivityResultContract<android.content.Intent,androidx.activity.result.ActivityResult> {
+    ctor public ActivityResultContracts.StartActivityForResult();
+    method public android.content.Intent createIntent(android.content.Context context, android.content.Intent input);
+    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
+    field public static final androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult.Companion Companion;
+    field public static final String EXTRA_ACTIVITY_OPTIONS_BUNDLE = "androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE";
+  }
+
+  public static final class ActivityResultContracts.StartActivityForResult.Companion {
+  }
+
+  public static final class ActivityResultContracts.StartIntentSenderForResult extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.IntentSenderRequest,androidx.activity.result.ActivityResult> {
+    ctor public ActivityResultContracts.StartIntentSenderForResult();
+    method public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.IntentSenderRequest input);
+    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
+    field public static final String ACTION_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.action.INTENT_SENDER_REQUEST";
+    field public static final androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.Companion Companion;
+    field public static final String EXTRA_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST";
+    field public static final String EXTRA_SEND_INTENT_EXCEPTION = "androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION";
+  }
+
+  public static final class ActivityResultContracts.StartIntentSenderForResult.Companion {
+  }
+
+  public static class ActivityResultContracts.TakePicture extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
+    ctor public ActivityResultContracts.TakePicture();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
+    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.TakePicturePreview extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.graphics.Bitmap?> {
+    ctor public ActivityResultContracts.TakePicturePreview();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, Void? input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, Void? input);
+    method public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  @Deprecated public static class ActivityResultContracts.TakeVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,android.graphics.Bitmap?> {
+    ctor @Deprecated public ActivityResultContracts.TakeVideo();
+    method @Deprecated @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
+    method @Deprecated public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, android.net.Uri input);
+    method @Deprecated public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+}
+
diff --git a/activity/activity/api/current.txt b/activity/activity/api/current.txt
index e5aaa29..be7377b 100644
--- a/activity/activity/api/current.txt
+++ b/activity/activity/api/current.txt
@@ -305,8 +305,16 @@
   }
 
   public final class PickVisualMediaRequest {
+    method public long getAccentColor();
+    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab getDefaultTab();
     method public int getMaxItems();
     method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType getMediaType();
+    method public boolean isCustomAccentColorApplied();
+    method public boolean isOrderedSelection();
+    property public final long accentColor;
+    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab;
+    property public final boolean isCustomAccentColorApplied;
+    property public final boolean isOrderedSelection;
     property public final int maxItems;
     property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType;
   }
@@ -314,13 +322,18 @@
   public static final class PickVisualMediaRequest.Builder {
     ctor public PickVisualMediaRequest.Builder();
     method public androidx.activity.result.PickVisualMediaRequest build();
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setAccentColor(long accentColor);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setDefaultTab(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
     method public androidx.activity.result.PickVisualMediaRequest.Builder setMaxItems(@IntRange(from=2L) int maxItems);
     method public androidx.activity.result.PickVisualMediaRequest.Builder setMediaType(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setOrderedSelection(boolean isOrderedSelection);
   }
 
   public final class PickVisualMediaRequestKt {
     method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
-    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
+    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(long accentColor, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
   }
 
 }
@@ -400,6 +413,7 @@
   }
 
   public static class ActivityResultContracts.PickMultipleVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.PickMultipleVisualMedia();
     ctor public ActivityResultContracts.PickMultipleVisualMedia(optional int maxItems);
     method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
     method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
@@ -415,6 +429,9 @@
     method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
     field public static final String ACTION_SYSTEM_FALLBACK_PICK_IMAGES = "androidx.activity.result.contract.action.PICK_IMAGES";
     field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion Companion;
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR = "androidx.activity.result.contract.extra.PICK_IMAGES_ACCENT_COLOR";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER = "androidx.activity.result.contract.extra.PICK_IMAGES_IN_ORDER";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB = "androidx.activity.result.contract.extra.PICK_IMAGES_LAUNCH_TAB";
     field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX = "androidx.activity.result.contract.extra.PICK_IMAGES_MAX";
   }
 
@@ -423,6 +440,23 @@
     method public boolean isPhotoPickerAvailable(android.content.Context context);
   }
 
+  public abstract static class ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public abstract int getValue();
+    property public abstract int value;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab INSTANCE;
+  }
+
   public static final class ActivityResultContracts.PickVisualMedia.ImageAndVideo implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
     field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo INSTANCE;
   }
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/activity/activity/api/res-1.10.0-beta01.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to activity/activity/api/res-1.10.0-beta01.txt
diff --git a/activity/activity/api/restricted_1.10.0-beta01.txt b/activity/activity/api/restricted_1.10.0-beta01.txt
new file mode 100644
index 0000000..3fc0729
--- /dev/null
+++ b/activity/activity/api/restricted_1.10.0-beta01.txt
@@ -0,0 +1,547 @@
+// Signature format: 4.0
+package androidx.activity {
+
+  public final class ActivityViewModelLazyKt {
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.activity.ComponentActivity, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+  public final class BackEventCompat {
+    ctor @RequiresApi(34) public BackEventCompat(android.window.BackEvent backEvent);
+    ctor @VisibleForTesting public BackEventCompat(float touchX, float touchY, @FloatRange(from=0.0, to=1.0) float progress, int swipeEdge);
+    method public float getProgress();
+    method public int getSwipeEdge();
+    method public float getTouchX();
+    method public float getTouchY();
+    method @RequiresApi(34) public android.window.BackEvent toBackEvent();
+    property public final float progress;
+    property public final int swipeEdge;
+    property public final float touchX;
+    property public final float touchY;
+    field public static final androidx.activity.BackEventCompat.Companion Companion;
+    field public static final int EDGE_LEFT = 0; // 0x0
+    field public static final int EDGE_RIGHT = 1; // 0x1
+  }
+
+  public static final class BackEventCompat.Companion {
+  }
+
+  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.activity.result.ActivityResultCaller androidx.activity.result.ActivityResultRegistryOwner androidx.activity.contextaware.ContextAware androidx.activity.FullyDrawnReporterOwner androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.core.view.MenuHost androidx.activity.OnBackPressedDispatcherOwner androidx.core.content.OnConfigurationChangedProvider androidx.core.app.OnMultiWindowModeChangedProvider androidx.core.app.OnNewIntentProvider androidx.core.app.OnPictureInPictureModeChangedProvider androidx.core.content.OnTrimMemoryProvider androidx.core.app.OnUserLeaveHintProvider androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+    ctor public ComponentActivity();
+    ctor @ContentView public ComponentActivity(@LayoutRes int contentLayoutId);
+    method public void addMenuProvider(androidx.core.view.MenuProvider provider);
+    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner);
+    method public void addMenuProvider(androidx.core.view.MenuProvider provider, androidx.lifecycle.LifecycleOwner owner, androidx.lifecycle.Lifecycle.State state);
+    method public final void addOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
+    method public final void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public final void addOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
+    method public final void addOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
+    method public final void addOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
+    method public final void addOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
+    method public final void addOnUserLeaveHintListener(Runnable listener);
+    method public final androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
+    method @Deprecated public Object? getLastCustomNonConfigurationInstance();
+    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+    method public final androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    method @CallSuper public void initializeViewTreeOwners();
+    method public void invalidateMenu();
+    method @Deprecated @CallSuper protected void onActivityResult(int requestCode, int resultCode, android.content.Intent? data);
+    method @Deprecated @CallSuper public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
+    method @Deprecated public Object? onRetainCustomNonConfigurationInstance();
+    method public final Object? onRetainNonConfigurationInstance();
+    method public android.content.Context? peekAvailableContext();
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public void removeMenuProvider(androidx.core.view.MenuProvider provider);
+    method public final void removeOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration> listener);
+    method public final void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public final void removeOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo> listener);
+    method public final void removeOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent> listener);
+    method public final void removeOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo> listener);
+    method public final void removeOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer> listener);
+    method public final void removeOnUserLeaveHintListener(Runnable listener);
+    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode);
+    method @Deprecated public void startActivityForResult(android.content.Intent intent, int requestCode, android.os.Bundle? options);
+    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws android.content.IntentSender.SendIntentException;
+    method @Deprecated @kotlin.jvm.Throws(exceptionClasses=SendIntentException::class) public void startIntentSenderForResult(android.content.IntentSender intent, int requestCode, android.content.Intent? fillInIntent, int flagsMask, int flagsValues, int extraFlags, android.os.Bundle? options) throws android.content.IntentSender.SendIntentException;
+    property public final androidx.activity.result.ActivityResultRegistry activityResultRegistry;
+    property @CallSuper public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+    property public androidx.activity.FullyDrawnReporter fullyDrawnReporter;
+    property @Deprecated public Object? lastCustomNonConfigurationInstance;
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
+    property public final androidx.savedstate.SavedStateRegistry savedStateRegistry;
+    property public androidx.lifecycle.ViewModelStore viewModelStore;
+  }
+
+  public class ComponentDialog extends android.app.Dialog implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner {
+    ctor public ComponentDialog(android.content.Context context);
+    ctor public ComponentDialog(android.content.Context context, optional @StyleRes int themeResId);
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method @CallSuper public void initializeViewTreeOwners();
+    method @CallSuper public void onBackPressed();
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property public final androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
+    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+  }
+
+  public final class EdgeToEdge {
+    method public static void enable(androidx.activity.ComponentActivity);
+    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle);
+    method public static void enable(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle, optional androidx.activity.SystemBarStyle navigationBarStyle);
+  }
+
+  public final class FullyDrawnReporter {
+    ctor public FullyDrawnReporter(java.util.concurrent.Executor executor, kotlin.jvm.functions.Function0<kotlin.Unit> reportFullyDrawn);
+    method public void addOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
+    method public void addReporter();
+    method public boolean isFullyDrawnReported();
+    method public void removeOnReportDrawnListener(kotlin.jvm.functions.Function0<kotlin.Unit> callback);
+    method public void removeReporter();
+    property public final boolean isFullyDrawnReported;
+  }
+
+  public final class FullyDrawnReporterKt {
+    method public static suspend inline Object? reportWhenComplete(androidx.activity.FullyDrawnReporter, kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> reporter, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public interface FullyDrawnReporterOwner {
+    method public androidx.activity.FullyDrawnReporter getFullyDrawnReporter();
+    property public abstract androidx.activity.FullyDrawnReporter fullyDrawnReporter;
+  }
+
+  public abstract class OnBackPressedCallback {
+    ctor public OnBackPressedCallback(boolean enabled);
+    method @MainThread public void handleOnBackCancelled();
+    method @MainThread public abstract void handleOnBackPressed();
+    method @MainThread public void handleOnBackProgressed(androidx.activity.BackEventCompat backEvent);
+    method @MainThread public void handleOnBackStarted(androidx.activity.BackEventCompat backEvent);
+    method @MainThread public final boolean isEnabled();
+    method @MainThread public final void remove();
+    method @MainThread public final void setEnabled(boolean);
+    property @MainThread public final boolean isEnabled;
+  }
+
+  public final class OnBackPressedDispatcher {
+    ctor public OnBackPressedDispatcher();
+    ctor public OnBackPressedDispatcher(optional Runnable? fallbackOnBackPressed);
+    ctor public OnBackPressedDispatcher(Runnable? fallbackOnBackPressed, androidx.core.util.Consumer<java.lang.Boolean>? onHasEnabledCallbacksChanged);
+    method @MainThread public void addCallback(androidx.activity.OnBackPressedCallback onBackPressedCallback);
+    method @MainThread public void addCallback(androidx.lifecycle.LifecycleOwner owner, androidx.activity.OnBackPressedCallback onBackPressedCallback);
+    method @MainThread @VisibleForTesting public void dispatchOnBackCancelled();
+    method @MainThread @VisibleForTesting public void dispatchOnBackProgressed(androidx.activity.BackEventCompat backEvent);
+    method @MainThread @VisibleForTesting public void dispatchOnBackStarted(androidx.activity.BackEventCompat backEvent);
+    method @MainThread public boolean hasEnabledCallbacks();
+    method @MainThread public void onBackPressed();
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public void setOnBackInvokedDispatcher(android.window.OnBackInvokedDispatcher invoker);
+  }
+
+  public final class OnBackPressedDispatcherKt {
+    method public static androidx.activity.OnBackPressedCallback addCallback(androidx.activity.OnBackPressedDispatcher, optional androidx.lifecycle.LifecycleOwner? owner, optional boolean enabled, kotlin.jvm.functions.Function1<? super androidx.activity.OnBackPressedCallback,kotlin.Unit> onBackPressed);
+  }
+
+  public interface OnBackPressedDispatcherOwner extends androidx.lifecycle.LifecycleOwner {
+    method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+    property public abstract androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
+  }
+
+  public final class PipHintTrackerKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.O) public static suspend Object? trackPipAnimationHintView(android.app.Activity, android.view.View view, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public final class SystemBarStyle {
+    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
+    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
+    method public static androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
+    method public static androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
+    field public static final androidx.activity.SystemBarStyle.Companion Companion;
+  }
+
+  public static final class SystemBarStyle.Companion {
+    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
+    method public androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim, optional kotlin.jvm.functions.Function1<? super android.content.res.Resources,java.lang.Boolean> detectDarkMode);
+    method public androidx.activity.SystemBarStyle dark(@ColorInt int scrim);
+    method public androidx.activity.SystemBarStyle light(@ColorInt int scrim, @ColorInt int darkScrim);
+  }
+
+  public final class ViewTreeFullyDrawnReporterOwner {
+    method public static androidx.activity.FullyDrawnReporterOwner? get(android.view.View);
+    method public static void set(android.view.View, androidx.activity.FullyDrawnReporterOwner fullyDrawnReporterOwner);
+  }
+
+  public final class ViewTreeOnBackPressedDispatcherOwner {
+    method public static androidx.activity.OnBackPressedDispatcherOwner? get(android.view.View);
+    method public static void set(android.view.View, androidx.activity.OnBackPressedDispatcherOwner onBackPressedDispatcherOwner);
+  }
+
+}
+
+package androidx.activity.contextaware {
+
+  public interface ContextAware {
+    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public android.content.Context? peekAvailableContext();
+    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+  }
+
+  public final class ContextAwareHelper {
+    ctor public ContextAwareHelper();
+    method public void addOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+    method public void clearAvailableContext();
+    method public void dispatchOnContextAvailable(android.content.Context context);
+    method public android.content.Context? peekAvailableContext();
+    method public void removeOnContextAvailableListener(androidx.activity.contextaware.OnContextAvailableListener listener);
+  }
+
+  public final class ContextAwareKt {
+    method public static suspend inline <R> Object? withContextAvailable(androidx.activity.contextaware.ContextAware, kotlin.jvm.functions.Function1<android.content.Context,R> onContextAvailable, kotlin.coroutines.Continuation<R>);
+  }
+
+  public fun interface OnContextAvailableListener {
+    method public void onContextAvailable(android.content.Context context);
+  }
+
+}
+
+package androidx.activity.result {
+
+  public final class ActivityResult implements android.os.Parcelable {
+    ctor public ActivityResult(int resultCode, android.content.Intent? data);
+    method public int describeContents();
+    method public android.content.Intent? getData();
+    method public int getResultCode();
+    method public static String resultCodeToString(int resultCode);
+    method public void writeToParcel(android.os.Parcel dest, int flags);
+    property public final android.content.Intent? data;
+    property public final int resultCode;
+    field public static final android.os.Parcelable.Creator<androidx.activity.result.ActivityResult> CREATOR;
+    field public static final androidx.activity.result.ActivityResult.Companion Companion;
+  }
+
+  public static final class ActivityResult.Companion {
+    method public String resultCodeToString(int resultCode);
+  }
+
+  public fun interface ActivityResultCallback<O> {
+    method public void onActivityResult(O result);
+  }
+
+  public interface ActivityResultCaller {
+    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public <I, O> androidx.activity.result.ActivityResultLauncher<I> registerForActivityResult(androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultRegistry registry, androidx.activity.result.ActivityResultCallback<O> callback);
+  }
+
+  public final class ActivityResultCallerKt {
+    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.activity.result.ActivityResultRegistry registry, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
+    method public static <I, O> androidx.activity.result.ActivityResultLauncher<kotlin.Unit> registerForActivityResult(androidx.activity.result.ActivityResultCaller, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, kotlin.jvm.functions.Function1<O,kotlin.Unit> callback);
+  }
+
+  public final class ActivityResultKt {
+    method public static operator int component1(androidx.activity.result.ActivityResult);
+    method public static operator android.content.Intent? component2(androidx.activity.result.ActivityResult);
+  }
+
+  public abstract class ActivityResultLauncher<I> {
+    ctor public ActivityResultLauncher();
+    method public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> getContract();
+    method public void launch(I input);
+    method public abstract void launch(I input, androidx.core.app.ActivityOptionsCompat? options);
+    method @MainThread public abstract void unregister();
+    property public abstract androidx.activity.result.contract.ActivityResultContract<I,? extends java.lang.Object?> contract;
+  }
+
+  public final class ActivityResultLauncherKt {
+    method public static void launch(androidx.activity.result.ActivityResultLauncher<java.lang.Void?>, optional androidx.core.app.ActivityOptionsCompat? options);
+    method public static void launchUnit(androidx.activity.result.ActivityResultLauncher<kotlin.Unit>, optional androidx.core.app.ActivityOptionsCompat? options);
+  }
+
+  public abstract class ActivityResultRegistry {
+    ctor public ActivityResultRegistry();
+    method @MainThread public final boolean dispatchResult(int requestCode, int resultCode, android.content.Intent? data);
+    method @MainThread public final <O> boolean dispatchResult(int requestCode, O result);
+    method @MainThread public abstract <I, O> void onLaunch(int requestCode, androidx.activity.result.contract.ActivityResultContract<I,O> contract, I input, androidx.core.app.ActivityOptionsCompat? options);
+    method public final void onRestoreInstanceState(android.os.Bundle? savedInstanceState);
+    method public final void onSaveInstanceState(android.os.Bundle outState);
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+    method public final <I, O> androidx.activity.result.ActivityResultLauncher<I> register(String key, androidx.lifecycle.LifecycleOwner lifecycleOwner, androidx.activity.result.contract.ActivityResultContract<I,O> contract, androidx.activity.result.ActivityResultCallback<O> callback);
+  }
+
+  public interface ActivityResultRegistryOwner {
+    method public androidx.activity.result.ActivityResultRegistry getActivityResultRegistry();
+    property public abstract androidx.activity.result.ActivityResultRegistry activityResultRegistry;
+  }
+
+  public final class IntentSenderRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.Intent? getFillInIntent();
+    method public int getFlagsMask();
+    method public int getFlagsValues();
+    method public android.content.IntentSender getIntentSender();
+    method public void writeToParcel(android.os.Parcel dest, int flags);
+    property public final android.content.Intent? fillInIntent;
+    property public final int flagsMask;
+    property public final int flagsValues;
+    property public final android.content.IntentSender intentSender;
+    field public static final android.os.Parcelable.Creator<androidx.activity.result.IntentSenderRequest> CREATOR;
+    field public static final androidx.activity.result.IntentSenderRequest.Companion Companion;
+  }
+
+  public static final class IntentSenderRequest.Builder {
+    ctor public IntentSenderRequest.Builder(android.app.PendingIntent pendingIntent);
+    ctor public IntentSenderRequest.Builder(android.content.IntentSender intentSender);
+    method public androidx.activity.result.IntentSenderRequest build();
+    method public androidx.activity.result.IntentSenderRequest.Builder setFillInIntent(android.content.Intent? fillInIntent);
+    method public androidx.activity.result.IntentSenderRequest.Builder setFlags(int values, int mask);
+  }
+
+  public static final class IntentSenderRequest.Companion {
+  }
+
+  public final class PickVisualMediaRequest {
+    method public long getAccentColor();
+    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab getDefaultTab();
+    method public int getMaxItems();
+    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType getMediaType();
+    method public boolean isCustomAccentColorApplied();
+    method public boolean isOrderedSelection();
+    property public final long accentColor;
+    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab;
+    property public final boolean isCustomAccentColorApplied;
+    property public final boolean isOrderedSelection;
+    property public final int maxItems;
+    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType;
+  }
+
+  public static final class PickVisualMediaRequest.Builder {
+    ctor public PickVisualMediaRequest.Builder();
+    method public androidx.activity.result.PickVisualMediaRequest build();
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setAccentColor(long accentColor);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setDefaultTab(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setMaxItems(@IntRange(from=2L) int maxItems);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setMediaType(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setOrderedSelection(boolean isOrderedSelection);
+  }
+
+  public final class PickVisualMediaRequestKt {
+    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
+    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(long accentColor, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+  }
+
+}
+
+package androidx.activity.result.contract {
+
+  public abstract class ActivityResultContract<I, O> {
+    ctor public ActivityResultContract();
+    method public abstract android.content.Intent createIntent(android.content.Context context, I input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<O>? getSynchronousResult(android.content.Context context, I input);
+    method public abstract O parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static final class ActivityResultContract.SynchronousResult<T> {
+    ctor public ActivityResultContract.SynchronousResult(T value);
+    method public T getValue();
+    property public final T value;
+  }
+
+  public final class ActivityResultContracts {
+  }
+
+  public static class ActivityResultContracts.CaptureVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
+    ctor public ActivityResultContracts.CaptureVideo();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
+    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.CreateDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
+    ctor @Deprecated public ActivityResultContracts.CreateDocument();
+    ctor public ActivityResultContracts.CreateDocument(String mimeType);
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.GetContent extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,android.net.Uri?> {
+    ctor public ActivityResultContracts.GetContent();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.GetMultipleContents extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.GetMultipleContents();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String input);
+    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.OpenDocument extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],android.net.Uri?> {
+    ctor public ActivityResultContracts.OpenDocument();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, String[] input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  @RequiresApi(21) public static class ActivityResultContracts.OpenDocumentTree extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri?,android.net.Uri?> {
+    ctor public ActivityResultContracts.OpenDocumentTree();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri? input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, android.net.Uri? input);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.OpenMultipleDocuments extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.OpenMultipleDocuments();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, String[] input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, String[] input);
+    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static final class ActivityResultContracts.PickContact extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.net.Uri?> {
+    ctor public ActivityResultContracts.PickContact();
+    method public android.content.Intent createIntent(android.content.Context context, Void? input);
+    method public android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.PickMultipleVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.PickMultipleVisualMedia();
+    ctor public ActivityResultContracts.PickMultipleVisualMedia(optional int maxItems);
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method public final java.util.List<android.net.Uri> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.PickVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,android.net.Uri?> {
+    ctor public ActivityResultContracts.PickVisualMedia();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.net.Uri?>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
+    method @Deprecated public static final boolean isPhotoPickerAvailable();
+    method public static final boolean isPhotoPickerAvailable(android.content.Context context);
+    method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
+    field public static final String ACTION_SYSTEM_FALLBACK_PICK_IMAGES = "androidx.activity.result.contract.action.PICK_IMAGES";
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion Companion;
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR = "androidx.activity.result.contract.extra.PICK_IMAGES_ACCENT_COLOR";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER = "androidx.activity.result.contract.extra.PICK_IMAGES_IN_ORDER";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB = "androidx.activity.result.contract.extra.PICK_IMAGES_LAUNCH_TAB";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX = "androidx.activity.result.contract.extra.PICK_IMAGES_MAX";
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.Companion {
+    method @Deprecated public boolean isPhotoPickerAvailable();
+    method public boolean isPhotoPickerAvailable(android.content.Context context);
+  }
+
+  public abstract static class ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public abstract int getValue();
+    property public abstract int value;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.ImageAndVideo implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.ImageOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageOnly INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.SingleMimeType implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    ctor public ActivityResultContracts.PickVisualMedia.SingleMimeType(String mimeType);
+    method public String getMimeType();
+    property public final String mimeType;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.VideoOnly implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VideoOnly INSTANCE;
+  }
+
+  public static sealed interface ActivityResultContracts.PickVisualMedia.VisualMediaType {
+  }
+
+  public static final class ActivityResultContracts.RequestMultiplePermissions extends androidx.activity.result.contract.ActivityResultContract<java.lang.String[],java.util.Map<java.lang.String,java.lang.Boolean>> {
+    ctor public ActivityResultContracts.RequestMultiplePermissions();
+    method public android.content.Intent createIntent(android.content.Context context, String[] input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.Map<java.lang.String,java.lang.Boolean>>? getSynchronousResult(android.content.Context context, String[] input);
+    method public java.util.Map<java.lang.String,java.lang.Boolean> parseResult(int resultCode, android.content.Intent? intent);
+    field public static final String ACTION_REQUEST_PERMISSIONS = "androidx.activity.result.contract.action.REQUEST_PERMISSIONS";
+    field public static final androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion Companion;
+    field public static final String EXTRA_PERMISSIONS = "androidx.activity.result.contract.extra.PERMISSIONS";
+    field public static final String EXTRA_PERMISSION_GRANT_RESULTS = "androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS";
+  }
+
+  public static final class ActivityResultContracts.RequestMultiplePermissions.Companion {
+  }
+
+  public static final class ActivityResultContracts.RequestPermission extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,java.lang.Boolean> {
+    ctor public ActivityResultContracts.RequestPermission();
+    method public android.content.Intent createIntent(android.content.Context context, String input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, String input);
+    method public Boolean parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static final class ActivityResultContracts.StartActivityForResult extends androidx.activity.result.contract.ActivityResultContract<android.content.Intent,androidx.activity.result.ActivityResult> {
+    ctor public ActivityResultContracts.StartActivityForResult();
+    method public android.content.Intent createIntent(android.content.Context context, android.content.Intent input);
+    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
+    field public static final androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult.Companion Companion;
+    field public static final String EXTRA_ACTIVITY_OPTIONS_BUNDLE = "androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE";
+  }
+
+  public static final class ActivityResultContracts.StartActivityForResult.Companion {
+  }
+
+  public static final class ActivityResultContracts.StartIntentSenderForResult extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.IntentSenderRequest,androidx.activity.result.ActivityResult> {
+    ctor public ActivityResultContracts.StartIntentSenderForResult();
+    method public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.IntentSenderRequest input);
+    method public androidx.activity.result.ActivityResult parseResult(int resultCode, android.content.Intent? intent);
+    field public static final String ACTION_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.action.INTENT_SENDER_REQUEST";
+    field public static final androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.Companion Companion;
+    field public static final String EXTRA_INTENT_SENDER_REQUEST = "androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST";
+    field public static final String EXTRA_SEND_INTENT_EXCEPTION = "androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION";
+  }
+
+  public static final class ActivityResultContracts.StartIntentSenderForResult.Companion {
+  }
+
+  public static class ActivityResultContracts.TakePicture extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,java.lang.Boolean> {
+    ctor public ActivityResultContracts.TakePicture();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.lang.Boolean>? getSynchronousResult(android.content.Context context, android.net.Uri input);
+    method public final Boolean parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public static class ActivityResultContracts.TakePicturePreview extends androidx.activity.result.contract.ActivityResultContract<java.lang.Void?,android.graphics.Bitmap?> {
+    ctor public ActivityResultContracts.TakePicturePreview();
+    method @CallSuper public android.content.Intent createIntent(android.content.Context context, Void? input);
+    method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, Void? input);
+    method public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  @Deprecated public static class ActivityResultContracts.TakeVideo extends androidx.activity.result.contract.ActivityResultContract<android.net.Uri,android.graphics.Bitmap?> {
+    ctor @Deprecated public ActivityResultContracts.TakeVideo();
+    method @Deprecated @CallSuper public android.content.Intent createIntent(android.content.Context context, android.net.Uri input);
+    method @Deprecated public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<android.graphics.Bitmap?>? getSynchronousResult(android.content.Context context, android.net.Uri input);
+    method @Deprecated public final android.graphics.Bitmap? parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+}
+
diff --git a/activity/activity/api/restricted_current.txt b/activity/activity/api/restricted_current.txt
index fdca347..3fc0729 100644
--- a/activity/activity/api/restricted_current.txt
+++ b/activity/activity/api/restricted_current.txt
@@ -304,8 +304,16 @@
   }
 
   public final class PickVisualMediaRequest {
+    method public long getAccentColor();
+    method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab getDefaultTab();
     method public int getMaxItems();
     method public androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType getMediaType();
+    method public boolean isCustomAccentColorApplied();
+    method public boolean isOrderedSelection();
+    property public final long accentColor;
+    property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab;
+    property public final boolean isCustomAccentColorApplied;
+    property public final boolean isOrderedSelection;
     property public final int maxItems;
     property public final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType;
   }
@@ -313,13 +321,18 @@
   public static final class PickVisualMediaRequest.Builder {
     ctor public PickVisualMediaRequest.Builder();
     method public androidx.activity.result.PickVisualMediaRequest build();
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setAccentColor(long accentColor);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setDefaultTab(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
     method public androidx.activity.result.PickVisualMediaRequest.Builder setMaxItems(@IntRange(from=2L) int maxItems);
     method public androidx.activity.result.PickVisualMediaRequest.Builder setMediaType(androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
+    method public androidx.activity.result.PickVisualMediaRequest.Builder setOrderedSelection(boolean isOrderedSelection);
   }
 
   public final class PickVisualMediaRequestKt {
     method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType);
-    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
+    method @Deprecated public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
+    method public static androidx.activity.result.PickVisualMediaRequest PickVisualMediaRequest(long accentColor, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType, optional @IntRange(from=2L) int maxItems, optional boolean isOrderedSelection, optional androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab defaultTab);
   }
 
 }
@@ -399,6 +412,7 @@
   }
 
   public static class ActivityResultContracts.PickMultipleVisualMedia extends androidx.activity.result.contract.ActivityResultContract<androidx.activity.result.PickVisualMediaRequest,java.util.List<android.net.Uri>> {
+    ctor public ActivityResultContracts.PickMultipleVisualMedia();
     ctor public ActivityResultContracts.PickMultipleVisualMedia(optional int maxItems);
     method @CallSuper public android.content.Intent createIntent(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
     method public final androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.List<android.net.Uri>>? getSynchronousResult(android.content.Context context, androidx.activity.result.PickVisualMediaRequest input);
@@ -414,6 +428,9 @@
     method public final android.net.Uri? parseResult(int resultCode, android.content.Intent? intent);
     field public static final String ACTION_SYSTEM_FALLBACK_PICK_IMAGES = "androidx.activity.result.contract.action.PICK_IMAGES";
     field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion Companion;
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR = "androidx.activity.result.contract.extra.PICK_IMAGES_ACCENT_COLOR";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER = "androidx.activity.result.contract.extra.PICK_IMAGES_IN_ORDER";
+    field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB = "androidx.activity.result.contract.extra.PICK_IMAGES_LAUNCH_TAB";
     field public static final String EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX = "androidx.activity.result.contract.extra.PICK_IMAGES_MAX";
   }
 
@@ -422,6 +439,23 @@
     method public boolean isPhotoPickerAvailable(android.content.Context context);
   }
 
+  public abstract static class ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public abstract int getValue();
+    property public abstract int value;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab INSTANCE;
+  }
+
+  public static final class ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab extends androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab {
+    method public int getValue();
+    property public int value;
+    field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab INSTANCE;
+  }
+
   public static final class ActivityResultContracts.PickVisualMedia.ImageAndVideo implements androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType {
     field public static final androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo INSTANCE;
   }
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 1cd39fb..e62b3c9 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -15,30 +15,33 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.activity"
 }
 
 dependencies {
 
-    api("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.collection:collection:1.0.0")
-    api("androidx.core:core:1.13.0")
+    api("androidx.annotation:annotation:1.8.1")
+    api("androidx.core:core-ktx:1.13.0")
+    api("androidx.lifecycle:lifecycle-common:2.6.1")
     api("androidx.lifecycle:lifecycle-runtime:2.6.1")
     api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
     api("androidx.savedstate:savedstate:1.2.1")
     api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1")
     implementation("androidx.profileinstaller:profileinstaller:1.3.1")
     implementation("androidx.tracing:tracing:1.0.0")
+    implementation(libs.kotlinCoroutinesCore)
     api(libs.kotlinStdlib)
 
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
-    androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.espressoCore, excludes.espresso)
-    androidTestImplementation(libs.leakcanary)
+    androidTestImplementation(libs.hamcrestCore)
+    androidTestImplementation(libs.junit)
     androidTestImplementation(libs.leakcanaryInstrumentation)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testMonitor)
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.testRules)
     androidTestImplementation(libs.truth)
@@ -56,7 +59,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Provides the base Activity subclass and the relevant hooks to build a composable structure on top."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/activity/activity/lint-baseline.xml b/activity/activity/lint-baseline.xml
index 242db20..59d592a0 100644
--- a/activity/activity/lint-baseline.xml
+++ b/activity/activity/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ObsoleteSdkInt"
@@ -22,8 +22,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is never &lt; 21"
-        errorLine1="            } else if (Build.VERSION.SDK_INT == 19 &amp;&amp; ContextCompat.checkSelfPermission("
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                Build.VERSION.SDK_INT == 19 &amp;&amp;"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/activity/ComponentActivity.kt"/>
     </issue>
@@ -31,8 +31,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    } else if (Build.VERSION.SDK_INT >= 21) {"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                if (Build.VERSION.SDK_INT >= 21) {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/activity/EdgeToEdge.kt"/>
     </issue>
@@ -40,8 +40,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    } else if (Build.VERSION.SDK_INT >= 21) {"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                if (Build.VERSION.SDK_INT >= 21) {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/activity/EdgeToEdge.kt"/>
     </issue>
@@ -49,8 +49,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    } else if (Build.VERSION.SDK_INT >= 21) {"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                if (Build.VERSION.SDK_INT >= 21) {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/activity/EdgeToEdge.kt"/>
     </issue>
diff --git a/activity/activity/src/androidTest/java/androidx/activity/EdgeToEdgeTest.kt b/activity/activity/src/androidTest/java/androidx/activity/EdgeToEdgeTest.kt
index 004ca84..d24f8b4 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/EdgeToEdgeTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/EdgeToEdgeTest.kt
@@ -33,6 +33,7 @@
 @RunWith(AndroidJUnit4::class)
 class EdgeToEdgeTest {
 
+    @Suppress("DEPRECATION")
     @Test
     fun enableAuto() {
         withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
@@ -73,6 +74,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun enableCustom() {
         withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
@@ -117,6 +119,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun enableDark() {
         withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
@@ -153,6 +156,7 @@
         }
     }
 
+    @Suppress("DEPRECATION")
     @Test
     fun enableLight() {
         withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
diff --git a/activity/activity/src/androidTest/java/androidx/activity/result/PickVisualMediaRequestTest.kt b/activity/activity/src/androidTest/java/androidx/activity/result/PickVisualMediaRequestTest.kt
index b51e602..abe6540 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/result/PickVisualMediaRequestTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/result/PickVisualMediaRequestTest.kt
@@ -109,4 +109,43 @@
         assertThat(intent.getIntExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, /* defaultValue= */ 0))
             .isEqualTo(/* expected= */ 7)
     }
+
+    @Test
+    fun testPickVisualMediaRequest_accentColor() {
+        // test default
+        var request = PickVisualMediaRequest()
+        assertThat(request.isCustomAccentColorApplied).isEqualTo(false)
+
+        // test given accent color in PickVisualMediaRequest
+        request = PickVisualMediaRequest(accentColor = 0xffff0000)
+        assertThat(request.isCustomAccentColorApplied).isEqualTo(true)
+        assertThat(request.accentColor).isEqualTo(0xffff0000)
+    }
+
+    @Test
+    fun testPickVisualMediaRequest_defaultTab() {
+        // test default
+        var request = PickVisualMediaRequest()
+        assertThat(request.defaultTab)
+            .isEqualTo(ActivityResultContracts.PickVisualMedia.DefaultTab.PhotosTab)
+
+        // test given default tab in PickVisualMediaRequest
+        request =
+            PickVisualMediaRequest(
+                defaultTab = ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab
+            )
+        assertThat(request.defaultTab)
+            .isEqualTo(ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab)
+    }
+
+    @Test
+    fun testPickVisualMediaRequest_isOrderedSelection() {
+        // test default
+        var request = PickVisualMediaRequest()
+        assertThat(request.isOrderedSelection).isEqualTo(false)
+
+        // test given isOrderedSelection in PickVisualMediaRequest
+        request = PickVisualMediaRequest(isOrderedSelection = true)
+        assertThat(request.isOrderedSelection).isEqualTo(true)
+    }
 }
diff --git a/activity/activity/src/main/java/androidx/activity/BackEventCompat.kt b/activity/activity/src/main/java/androidx/activity/BackEventCompat.kt
index 3760f12..5b73368 100644
--- a/activity/activity/src/main/java/androidx/activity/BackEventCompat.kt
+++ b/activity/activity/src/main/java/androidx/activity/BackEventCompat.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.window.BackEvent
-import androidx.annotation.DoNotInline
 import androidx.annotation.FloatRange
 import androidx.annotation.IntDef
 import androidx.annotation.RequiresApi
@@ -93,15 +92,14 @@
 
 @RequiresApi(34)
 internal object Api34Impl {
-    @DoNotInline
     fun createOnBackEvent(touchX: Float, touchY: Float, progress: Float, swipeEdge: Int) =
         BackEvent(touchX, touchY, progress, swipeEdge)
 
-    @DoNotInline fun progress(backEvent: BackEvent) = backEvent.progress
+    fun progress(backEvent: BackEvent) = backEvent.progress
 
-    @DoNotInline fun touchX(backEvent: BackEvent) = backEvent.touchX
+    fun touchX(backEvent: BackEvent) = backEvent.touchX
 
-    @DoNotInline fun touchY(backEvent: BackEvent) = backEvent.touchY
+    fun touchY(backEvent: BackEvent) = backEvent.touchY
 
-    @DoNotInline fun swipeEdge(backEvent: BackEvent) = backEvent.swipeEdge
+    fun swipeEdge(backEvent: BackEvent) = backEvent.swipeEdge
 }
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt b/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt
index b9d20e3..11a444a 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.kt
@@ -55,7 +55,6 @@
 import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.Companion.EXTRA_SEND_INTENT_EXCEPTION
 import androidx.annotation.CallSuper
 import androidx.annotation.ContentView
-import androidx.annotation.DoNotInline
 import androidx.annotation.LayoutRes
 import androidx.annotation.MainThread
 import androidx.annotation.RequiresApi
@@ -1037,7 +1036,6 @@
 
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     private object Api33Impl {
-        @DoNotInline
         fun getOnBackInvokedDispatcher(activity: Activity): OnBackInvokedDispatcher {
             return activity.getOnBackInvokedDispatcher()
         }
diff --git a/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt b/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt
index 4f78044..73953fb 100644
--- a/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt
+++ b/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt
@@ -254,6 +254,7 @@
 @RequiresApi(23)
 private class EdgeToEdgeApi23 : EdgeToEdgeBase() {
 
+    @Suppress("DEPRECATION")
     @DoNotInline
     override fun setUp(
         statusBarStyle: SystemBarStyle,
@@ -273,6 +274,7 @@
 @RequiresApi(26)
 private open class EdgeToEdgeApi26 : EdgeToEdgeBase() {
 
+    @Suppress("DEPRECATION")
     @DoNotInline
     override fun setUp(
         statusBarStyle: SystemBarStyle,
@@ -305,6 +307,7 @@
 @RequiresApi(29)
 private open class EdgeToEdgeApi29 : EdgeToEdgeApi28() {
 
+    @Suppress("DEPRECATION")
     @DoNotInline
     override fun setUp(
         statusBarStyle: SystemBarStyle,
diff --git a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
index cec1a37..2824d7c 100644
--- a/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
+++ b/activity/activity/src/main/java/androidx/activity/OnBackPressedDispatcher.kt
@@ -20,7 +20,6 @@
 import android.window.OnBackAnimationCallback
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
-import androidx.annotation.DoNotInline
 import androidx.annotation.MainThread
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
@@ -326,21 +325,18 @@
 
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     internal object Api33Impl {
-        @DoNotInline
         fun registerOnBackInvokedCallback(dispatcher: Any, priority: Int, callback: Any) {
             val onBackInvokedDispatcher = dispatcher as OnBackInvokedDispatcher
             val onBackInvokedCallback = callback as OnBackInvokedCallback
             onBackInvokedDispatcher.registerOnBackInvokedCallback(priority, onBackInvokedCallback)
         }
 
-        @DoNotInline
         fun unregisterOnBackInvokedCallback(dispatcher: Any, callback: Any) {
             val onBackInvokedDispatcher = dispatcher as OnBackInvokedDispatcher
             val onBackInvokedCallback = callback as OnBackInvokedCallback
             onBackInvokedDispatcher.unregisterOnBackInvokedCallback(onBackInvokedCallback)
         }
 
-        @DoNotInline
         fun createOnBackInvokedCallback(onBackInvoked: () -> Unit): OnBackInvokedCallback {
             return OnBackInvokedCallback { onBackInvoked() }
         }
@@ -348,7 +344,6 @@
 
     @RequiresApi(34)
     internal object Api34Impl {
-        @DoNotInline
         fun createOnBackAnimationCallback(
             onBackStarted: (backEvent: BackEventCompat) -> Unit,
             onBackProgressed: (backEvent: BackEventCompat) -> Unit,
diff --git a/activity/activity/src/main/java/androidx/activity/result/PickVisualMediaRequest.kt b/activity/activity/src/main/java/androidx/activity/result/PickVisualMediaRequest.kt
index fa023fe..16569cd 100644
--- a/activity/activity/src/main/java/androidx/activity/result/PickVisualMediaRequest.kt
+++ b/activity/activity/src/main/java/androidx/activity/result/PickVisualMediaRequest.kt
@@ -17,6 +17,7 @@
 package androidx.activity.result
 
 import androidx.activity.result.contract.ActivityResultContracts.PickMultipleVisualMedia
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.DefaultTab
 import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageAndVideo
 import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType
 import androidx.annotation.IntRange
@@ -45,6 +46,10 @@
  * @param maxItems limit the number of selectable items when using [PickMultipleVisualMedia]
  * @return a PickVisualMediaRequest that contains the given input
  */
+@Deprecated(
+    "Superseded by PickVisualMediaRequest that take optional isOrderedSelection and defaultTab",
+    level = DeprecationLevel.HIDDEN
+) // Binary API compatibility.
 @Suppress("MissingJvmstatic")
 fun PickVisualMediaRequest(
     mediaType: VisualMediaType = ImageAndVideo,
@@ -52,6 +57,61 @@
 ) = PickVisualMediaRequest.Builder().setMediaType(mediaType).setMaxItems(maxItems).build()
 
 /**
+ * Creates a request for a
+ * [androidx.activity.result.contract.ActivityResultContracts.PickMultipleVisualMedia] or
+ * [androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia] Activity Contract.
+ *
+ * @param mediaType type to go into the PickVisualMediaRequest
+ * @param maxItems limit the number of selectable items when using [PickMultipleVisualMedia]
+ * @param isOrderedSelection whether the user can control the order of selected media when using
+ *   [PickMultipleVisualMedia] (defaults to false)
+ * @param defaultTab the tab to initially open in the picker (defaults to [DefaultTab.PhotosTab])
+ * @return a PickVisualMediaRequest that contains the given input
+ */
+@Suppress("MissingJvmstatic")
+fun PickVisualMediaRequest(
+    mediaType: VisualMediaType = ImageAndVideo,
+    @IntRange(from = 2) maxItems: Int = PickMultipleVisualMedia.getMaxItems(),
+    isOrderedSelection: Boolean = false,
+    defaultTab: DefaultTab = DefaultTab.PhotosTab
+) =
+    PickVisualMediaRequest.Builder()
+        .setMediaType(mediaType)
+        .setMaxItems(maxItems)
+        .setOrderedSelection(isOrderedSelection)
+        .setDefaultTab(defaultTab)
+        .build()
+
+/**
+ * Creates a request for a
+ * [androidx.activity.result.contract.ActivityResultContracts.PickMultipleVisualMedia] or
+ * [androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia] Activity Contract.
+ *
+ * @param accentColor color long to customize picker accent color
+ * @param mediaType type to go into the PickVisualMediaRequest
+ * @param maxItems limit the number of selectable items when using [PickMultipleVisualMedia]
+ * @param isOrderedSelection whether the user can control the order of selected media when using
+ *   [PickMultipleVisualMedia] (defaults to false)
+ * @param defaultTab the tab to initially open in the picker (defaults to [DefaultTab.PhotosTab])
+ * @return a PickVisualMediaRequest that contains the given input
+ */
+@Suppress("MissingJvmstatic")
+fun PickVisualMediaRequest(
+    accentColor: Long,
+    mediaType: VisualMediaType = ImageAndVideo,
+    @IntRange(from = 2) maxItems: Int = PickMultipleVisualMedia.getMaxItems(),
+    isOrderedSelection: Boolean = false,
+    defaultTab: DefaultTab = DefaultTab.PhotosTab
+) =
+    PickVisualMediaRequest.Builder()
+        .setMediaType(mediaType)
+        .setMaxItems(maxItems)
+        .setOrderedSelection(isOrderedSelection)
+        .setDefaultTab(defaultTab)
+        .setAccentColor(accentColor)
+        .build()
+
+/**
  * A request for a
  * [androidx.activity.result.contract.ActivityResultContracts.PickMultipleVisualMedia] or
  * [androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia] Activity Contract.
@@ -64,11 +124,27 @@
     var maxItems: Int = PickMultipleVisualMedia.getMaxItems()
         internal set
 
+    var isOrderedSelection: Boolean = false
+        internal set
+
+    var defaultTab: DefaultTab = DefaultTab.PhotosTab
+        internal set
+
+    var isCustomAccentColorApplied: Boolean = false
+        internal set
+
+    var accentColor: Long = 0
+        internal set
+
     /** A builder for constructing [PickVisualMediaRequest] instances. */
     class Builder {
 
         private var mediaType: VisualMediaType = ImageAndVideo
         private var maxItems: Int = PickMultipleVisualMedia.getMaxItems()
+        private var isOrderedSelection: Boolean = false
+        private var defaultTab: DefaultTab = DefaultTab.PhotosTab
+        private var isCustomAccentColorApplied: Boolean = false
+        private var accentColor: Long = 0
 
         /**
          * Set the media type for the [PickVisualMediaRequest].
@@ -97,6 +173,50 @@
         }
 
         /**
+         * Set the ordered selection for the [PickVisualMediaRequest].
+         *
+         * Allow the user to control the order in which images are returned to the calling app. This
+         * parameter might be not supported by the underlying photo picker implementation.
+         *
+         * @param isOrderedSelection boolean to enable customisable selection order in the picker
+         * @return This builder.
+         */
+        fun setOrderedSelection(isOrderedSelection: Boolean): Builder {
+            this.isOrderedSelection = isOrderedSelection
+            return this
+        }
+
+        /**
+         * Set the default tab for the [PickVisualMediaRequest].
+         *
+         * The default tab is used to open the preferred view inside the photo picker at first such
+         * as, e.g. [DefaultTab.PhotosTab], [DefaultTab.AlbumsTab]. This parameter might be not
+         * supported by the underlying photo picker implementation.
+         *
+         * @param defaultTab the tab to launch the picker in
+         * @return This builder.
+         */
+        fun setDefaultTab(defaultTab: DefaultTab): Builder {
+            this.defaultTab = defaultTab
+            return this
+        }
+
+        /**
+         * Set the accent color for the [PickVisualMediaRequest].
+         *
+         * The accent color is used to change the main color in the photo picker. This parameter
+         * might be not supported by the underlying photo picker implementation.
+         *
+         * @param accentColor color long to apply as accent to the main color in the picker
+         * @return This builder.
+         */
+        fun setAccentColor(accentColor: Long): Builder {
+            this.accentColor = accentColor
+            this.isCustomAccentColorApplied = true
+            return this
+        }
+
+        /**
          * Build the PickVisualMediaRequest specified by this builder.
          *
          * @return the newly constructed PickVisualMediaRequest.
@@ -105,6 +225,10 @@
             PickVisualMediaRequest().apply {
                 this.mediaType = [email protected]
                 this.maxItems = [email protected]
+                this.isOrderedSelection = [email protected]
+                this.defaultTab = [email protected]
+                this.isCustomAccentColorApplied = [email protected]
+                this.accentColor = [email protected]
             }
     }
 }
diff --git a/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt b/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt
index ddc4981..b26fdae 100644
--- a/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt
+++ b/activity/activity/src/main/java/androidx/activity/result/contract/ActivityResultContracts.kt
@@ -33,10 +33,10 @@
 import androidx.activity.result.PickVisualMediaRequest
 import androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents.Companion.getClipDataUris
 import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.ACTION_SYSTEM_FALLBACK_PICK_IMAGES
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB
 import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX
-import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.GMS_ACTION_PICK_IMAGES
-import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.GMS_EXTRA_PICK_IMAGES_MAX
-import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.getGmsPicker
 import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.Companion.getSystemFallbackPicker
 import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.Companion.ACTION_INTENT_SENDER_REQUEST
 import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult.Companion.EXTRA_SEND_INTENT_EXCEPTION
@@ -629,6 +629,11 @@
             const val ACTION_SYSTEM_FALLBACK_PICK_IMAGES =
                 "androidx.activity.result.contract.action.PICK_IMAGES"
 
+            internal const val GMS_ACTION_PICK_IMAGES =
+                "com.google.android.gms.provider.action.PICK_IMAGES"
+            internal const val GMS_EXTRA_PICK_IMAGES_MAX =
+                "com.google.android.gms.provider.extra.PICK_IMAGES_MAX"
+
             /**
              * Extra that will be sent by [PickMultipleVisualMedia] to an Activity that handles
              * [ACTION_SYSTEM_FALLBACK_PICK_IMAGES] that indicates that maximum number of photos the
@@ -643,10 +648,39 @@
             const val EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX =
                 "androidx.activity.result.contract.extra.PICK_IMAGES_MAX"
 
-            internal const val GMS_ACTION_PICK_IMAGES =
-                "com.google.android.gms.provider.action.PICK_IMAGES"
-            internal const val GMS_EXTRA_PICK_IMAGES_MAX =
-                "com.google.android.gms.provider.extra.PICK_IMAGES_MAX"
+            /**
+             * Extra that will be sent by [PickVisualMedia] and [PickMultipleVisualMedia] to an
+             * Activity that handles [ACTION_SYSTEM_FALLBACK_PICK_IMAGES] that indicates the
+             * preferred default tab of the picker.
+             *
+             * If this extra is not present, the default tab of the picker will be used.
+             */
+            @Suppress("ActionValue")
+            /* Don't include SYSTEM_FALLBACK in the extra */
+            const val EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB =
+                "androidx.activity.result.contract.extra.PICK_IMAGES_LAUNCH_TAB"
+
+            /**
+             * Extra that will be sent by [PickMultipleVisualMedia] to an Activity that handles
+             * [ACTION_SYSTEM_FALLBACK_PICK_IMAGES] that indicates allowing the user to control the
+             * order in which images are returned to the calling app.
+             */
+            @Suppress("ActionValue")
+            /* Don't include SYSTEM_FALLBACK in the extra */
+            const val EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER =
+                "androidx.activity.result.contract.extra.PICK_IMAGES_IN_ORDER"
+
+            /**
+             * Extra that will be sent by [PickVisualMedia] and [PickMultipleVisualMedia] to an
+             * Activity that handles [ACTION_SYSTEM_FALLBACK_PICK_IMAGES] that indicates the
+             * preferred accent color of the picker.
+             *
+             * If this extra is not present, the default accent color of the picker will be used.
+             */
+            @Suppress("ActionValue")
+            /* Don't include SYSTEM_FALLBACK in the extra */
+            const val EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR =
+                "androidx.activity.result.contract.extra.PICK_IMAGES_ACCENT_COLOR"
 
             /**
              * Check if the current device has support for the photo picker by checking the running
@@ -656,9 +690,7 @@
             @SuppressLint("ClassVerificationFailure", "NewApi")
             @JvmStatic
             fun isPhotoPickerAvailable(context: Context): Boolean {
-                return isSystemPickerAvailable() ||
-                    isSystemFallbackPickerAvailable(context) ||
-                    isGmsPickerAvailable(context)
+                return isSystemPickerAvailable() || isSystemFallbackPickerAvailable(context)
             }
 
             /**
@@ -696,20 +728,6 @@
                 )
             }
 
-            @JvmStatic
-            internal fun isGmsPickerAvailable(context: Context): Boolean {
-                return getGmsPicker(context) != null
-            }
-
-            @Suppress("DEPRECATION")
-            @JvmStatic
-            internal fun getGmsPicker(context: Context): ResolveInfo? {
-                return context.packageManager.resolveActivity(
-                    Intent(GMS_ACTION_PICK_IMAGES),
-                    PackageManager.MATCH_DEFAULT_ONLY or PackageManager.MATCH_SYSTEM_ONLY
-                )
-            }
-
             internal fun getVisualMimeType(input: VisualMediaType): String? {
                 return when (input) {
                     is ImageOnly -> "image/*"
@@ -738,24 +756,46 @@
          */
         class SingleMimeType(val mimeType: String) : VisualMediaType
 
+        /** Represents filter input type accepted by the photo picker. */
+        abstract class DefaultTab private constructor() {
+            abstract val value: Int
+
+            /**
+             * [DefaultTab] object used to open the picker in Photos tab (also the default if no
+             * value is provided).
+             */
+            object PhotosTab : DefaultTab() {
+                override val value = MediaStore.PICK_IMAGES_TAB_IMAGES
+            }
+
+            /** [DefaultTab] object used to open the picker in Albums tab. */
+            object AlbumsTab : DefaultTab() {
+                override val value = MediaStore.PICK_IMAGES_TAB_ALBUMS
+            }
+        }
+
         @CallSuper
         override fun createIntent(context: Context, input: PickVisualMediaRequest): Intent {
             // Check if Photo Picker is available on the device
             return if (isSystemPickerAvailable()) {
                 Intent(MediaStore.ACTION_PICK_IMAGES).apply {
                     type = getVisualMimeType(input.mediaType)
+                    putExtra(MediaStore.EXTRA_PICK_IMAGES_LAUNCH_TAB, input.defaultTab.value)
+
+                    if (input.isCustomAccentColorApplied) {
+                        putExtra(MediaStore.EXTRA_PICK_IMAGES_ACCENT_COLOR, input.accentColor)
+                    }
                 }
             } else if (isSystemFallbackPickerAvailable(context)) {
                 val fallbackPicker = checkNotNull(getSystemFallbackPicker(context)).activityInfo
                 Intent(ACTION_SYSTEM_FALLBACK_PICK_IMAGES).apply {
                     setClassName(fallbackPicker.applicationInfo.packageName, fallbackPicker.name)
                     type = getVisualMimeType(input.mediaType)
-                }
-            } else if (isGmsPickerAvailable(context)) {
-                val gmsPicker = checkNotNull(getGmsPicker(context)).activityInfo
-                Intent(GMS_ACTION_PICK_IMAGES).apply {
-                    setClassName(gmsPicker.applicationInfo.packageName, gmsPicker.name)
-                    type = getVisualMimeType(input.mediaType)
+                    putExtra(EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB, input.defaultTab.value)
+
+                    if (input.isCustomAccentColorApplied) {
+                        putExtra(EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR, input.accentColor)
+                    }
                 }
             } else {
                 // For older devices running KitKat and higher and devices running Android 12
@@ -784,8 +824,8 @@
             return intent
                 .takeIf { resultCode == Activity.RESULT_OK }
                 ?.run {
-                    // Check both the data URI and ClipData since the GMS picker
-                    // only returns results through getClipDataUris()
+                    // Check both the data URI and ClipData since the fallback picker
+                    // may only return results through getClipDataUris()
                     data ?: getClipDataUris().firstOrNull()
                 }
         }
@@ -847,6 +887,12 @@
                     }
 
                     putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, currentMaxItems)
+                    putExtra(MediaStore.EXTRA_PICK_IMAGES_LAUNCH_TAB, input.defaultTab.value)
+                    putExtra(MediaStore.EXTRA_PICK_IMAGES_IN_ORDER, input.isOrderedSelection)
+
+                    if (input.isCustomAccentColorApplied) {
+                        putExtra(MediaStore.EXTRA_PICK_IMAGES_ACCENT_COLOR, input.accentColor)
+                    }
                 }
             } else if (PickVisualMedia.isSystemFallbackPickerAvailable(context)) {
                 val fallbackPicker = checkNotNull(getSystemFallbackPicker(context)).activityInfo
@@ -858,12 +904,12 @@
                     require(currentMaxItems > 1) { "Max items must be greater than 1" }
 
                     putExtra(EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_MAX, currentMaxItems)
-                }
-            } else if (PickVisualMedia.isGmsPickerAvailable(context)) {
-                val gmsPicker = checkNotNull(getGmsPicker(context)).activityInfo
-                Intent(GMS_ACTION_PICK_IMAGES).apply {
-                    setClassName(gmsPicker.applicationInfo.packageName, gmsPicker.name)
-                    putExtra(GMS_EXTRA_PICK_IMAGES_MAX, maxItems)
+                    putExtra(EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_LAUNCH_TAB, input.defaultTab.value)
+                    putExtra(EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_IN_ORDER, input.isOrderedSelection)
+
+                    if (input.isCustomAccentColorApplied) {
+                        putExtra(EXTRA_SYSTEM_FALLBACK_PICK_IMAGES_ACCENT_COLOR, input.accentColor)
+                    }
                 }
             } else {
                 // For older devices running KitKat and higher and devices running Android 12
diff --git a/activity/integration-tests/macrobenchmark-target/build.gradle b/activity/integration-tests/macrobenchmark-target/build.gradle
index 68c0b8a..e984599 100644
--- a/activity/integration-tests/macrobenchmark-target/build.gradle
+++ b/activity/integration-tests/macrobenchmark-target/build.gradle
@@ -12,7 +12,8 @@
 }
 
 android {
-    namespace "androidx.activity.integration.macrobenchmark.target"
+   compileSdk 35
+   namespace "androidx.activity.integration.macrobenchmark.target"
 }
 
 dependencies {
diff --git a/activity/integration-tests/testapp/build.gradle b/activity/integration-tests/testapp/build.gradle
index 09b6006..36913af 100644
--- a/activity/integration-tests/testapp/build.gradle
+++ b/activity/integration-tests/testapp/build.gradle
@@ -22,6 +22,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         applicationId "androidx.activity.integration.testapp"
     }
@@ -29,21 +30,20 @@
 }
 
 dependencies {
-    implementation(project(":activity:activity-ktx"))
+    implementation(libs.kotlinCoroutinesCore)
+    implementation(project(":activity:activity"))
     implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
     implementation("androidx.appcompat:appcompat:1.6.0")
     implementation("androidx.core:core-splashscreen:1.0.0")
+    implementation("androidx.core:core-ktx:1.13.0")
+    implementation("androidx.fragment:fragment:1.3.6")
+    implementation("androidx.lifecycle:lifecycle-common:2.6.1")
+    implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
 
     // Manually align dependencies across debugRuntime and debugAndroidTestRuntime.
-    androidTestImplementation("androidx.annotation:annotation:1.6.0")
-    androidTestImplementation("androidx.annotation:annotation-experimental:1.4.0")
+    androidTestImplementation("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.kotlinStdlib)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.espressoCore)
-    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it's own MockMaker
-    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it's own MockMaker
+    androidTestRuntimeOnly(libs.testCore)
+    androidTestRuntimeOnly(libs.testRunner)
 }
diff --git a/activity/integration-tests/testapp/src/main/java/androidx/activity/integration/testapp/MainActivity.kt b/activity/integration-tests/testapp/src/main/java/androidx/activity/integration/testapp/MainActivity.kt
index 9ab8601..5b0c417 100644
--- a/activity/integration-tests/testapp/src/main/java/androidx/activity/integration/testapp/MainActivity.kt
+++ b/activity/integration-tests/testapp/src/main/java/androidx/activity/integration/testapp/MainActivity.kt
@@ -131,19 +131,40 @@
                         PickVisualMediaRequest(PickVisualMedia.SingleMimeType("image/gif"))
                     )
                 }
+                button("Pick an image & show albums tab (w/ photo picker)") {
+                    pickVisualMedia.launch(
+                        PickVisualMediaRequest(
+                            mediaType = PickVisualMedia.ImageOnly,
+                            defaultTab = PickVisualMedia.DefaultTab.AlbumsTab
+                        )
+                    )
+                }
+                button("Pick an image & green accent color (w/ photo picker)") {
+                    pickVisualMedia.launch(
+                        PickVisualMediaRequest(
+                            mediaType = PickVisualMedia.ImageOnly,
+                            accentColor = 0xFF123456
+                        )
+                    )
+                }
                 button("Pick 5 visual media max (w/ photo picker)") {
                     pickMultipleVisualMedia.launch(
                         PickVisualMediaRequest(PickVisualMedia.ImageAndVideo)
                     )
                 }
-                button("Pick 9 visual media max (w/ photo picker)") {
+                button("Pick 3 visual media max (w/ photo picker)") {
                     pickMultipleVisualMedia.launch(
                         PickVisualMediaRequest(
                             mediaType = PickVisualMedia.ImageAndVideo,
-                            maxItems = 9
+                            maxItems = 3
                         )
                     )
                 }
+                button("Pick 5 visual media max (w/ photo picker) & selection order") {
+                    pickMultipleVisualMedia.launch(
+                        PickVisualMediaRequest(isOrderedSelection = true)
+                    )
+                }
                 button("Create document") { createDocument.launch("Temp") }
                 button("Open documents") { openDocuments.launch(arrayOf("*/*")) }
                 button("Start IntentSender") {
diff --git a/annotation/annotation-experimental/build.gradle b/annotation/annotation-experimental/build.gradle
index d86283c..2111e5f 100644
--- a/annotation/annotation-experimental/build.gradle
+++ b/annotation/annotation-experimental/build.gradle
@@ -44,7 +44,6 @@
     description = "Java annotation for use on unstable Android API surfaces. When used in " +
             "conjunction with the Experimental annotation lint checks, this annotation provides " +
             "functional parity with Kotlin's Experimental annotation."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/annotation/annotation/bcv/native/current.txt b/annotation/annotation/bcv/native/current.txt
new file mode 100644
index 0000000..8b2f305
--- /dev/null
+++ b/annotation/annotation/bcv/native/current.txt
@@ -0,0 +1,148 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.annotation:annotation>
+open annotation class androidx.annotation/AnyThread : kotlin/Annotation { // androidx.annotation/AnyThread|null[0]
+    constructor <init>() // androidx.annotation/AnyThread.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/CallSuper : kotlin/Annotation { // androidx.annotation/CallSuper|null[0]
+    constructor <init>() // androidx.annotation/CallSuper.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/CheckResult : kotlin/Annotation { // androidx.annotation/CheckResult|null[0]
+    constructor <init>(kotlin/String =...) // androidx.annotation/CheckResult.<init>|<init>(kotlin.String){}[0]
+    final val suggest // androidx.annotation/CheckResult.suggest|{}suggest[0]
+        final fun <get-suggest>(): kotlin/String // androidx.annotation/CheckResult.suggest.<get-suggest>|<get-suggest>(){}[0]
+}
+open annotation class androidx.annotation/ColorInt : kotlin/Annotation { // androidx.annotation/ColorInt|null[0]
+    constructor <init>() // androidx.annotation/ColorInt.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/ColorLong : kotlin/Annotation { // androidx.annotation/ColorLong|null[0]
+    constructor <init>() // androidx.annotation/ColorLong.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/Discouraged : kotlin/Annotation { // androidx.annotation/Discouraged|null[0]
+    constructor <init>(kotlin/String) // androidx.annotation/Discouraged.<init>|<init>(kotlin.String){}[0]
+    final val message // androidx.annotation/Discouraged.message|{}message[0]
+        final fun <get-message>(): kotlin/String // androidx.annotation/Discouraged.message.<get-message>|<get-message>(){}[0]
+}
+open annotation class androidx.annotation/EmptySuper : kotlin/Annotation { // androidx.annotation/EmptySuper|null[0]
+    constructor <init>() // androidx.annotation/EmptySuper.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/FloatRange : kotlin/Annotation { // androidx.annotation/FloatRange|null[0]
+    constructor <init>(kotlin/Double =..., kotlin/Double =..., kotlin/Boolean =..., kotlin/Boolean =...) // androidx.annotation/FloatRange.<init>|<init>(kotlin.Double;kotlin.Double;kotlin.Boolean;kotlin.Boolean){}[0]
+    final val from // androidx.annotation/FloatRange.from|{}from[0]
+        final fun <get-from>(): kotlin/Double // androidx.annotation/FloatRange.from.<get-from>|<get-from>(){}[0]
+    final val fromInclusive // androidx.annotation/FloatRange.fromInclusive|{}fromInclusive[0]
+        final fun <get-fromInclusive>(): kotlin/Boolean // androidx.annotation/FloatRange.fromInclusive.<get-fromInclusive>|<get-fromInclusive>(){}[0]
+    final val to // androidx.annotation/FloatRange.to|{}to[0]
+        final fun <get-to>(): kotlin/Double // androidx.annotation/FloatRange.to.<get-to>|<get-to>(){}[0]
+    final val toInclusive // androidx.annotation/FloatRange.toInclusive|{}toInclusive[0]
+        final fun <get-toInclusive>(): kotlin/Boolean // androidx.annotation/FloatRange.toInclusive.<get-toInclusive>|<get-toInclusive>(){}[0]
+}
+open annotation class androidx.annotation/GuardedBy : kotlin/Annotation { // androidx.annotation/GuardedBy|null[0]
+    constructor <init>(kotlin/String) // androidx.annotation/GuardedBy.<init>|<init>(kotlin.String){}[0]
+    final val value // androidx.annotation/GuardedBy.value|{}value[0]
+        final fun <get-value>(): kotlin/String // androidx.annotation/GuardedBy.value.<get-value>|<get-value>(){}[0]
+}
+open annotation class androidx.annotation/IntDef : kotlin/Annotation { // androidx.annotation/IntDef|null[0]
+    constructor <init>(kotlin/IntArray... =..., kotlin/Boolean =..., kotlin/Boolean =...) // androidx.annotation/IntDef.<init>|<init>(kotlin.IntArray...;kotlin.Boolean;kotlin.Boolean){}[0]
+    final val flag // androidx.annotation/IntDef.flag|{}flag[0]
+        final fun <get-flag>(): kotlin/Boolean // androidx.annotation/IntDef.flag.<get-flag>|<get-flag>(){}[0]
+    final val open // androidx.annotation/IntDef.open|{}open[0]
+        final fun <get-open>(): kotlin/Boolean // androidx.annotation/IntDef.open.<get-open>|<get-open>(){}[0]
+    final val value // androidx.annotation/IntDef.value|{}value[0]
+        final fun <get-value>(): kotlin/IntArray // androidx.annotation/IntDef.value.<get-value>|<get-value>(){}[0]
+}
+open annotation class androidx.annotation/IntRange : kotlin/Annotation { // androidx.annotation/IntRange|null[0]
+    constructor <init>(kotlin/Long =..., kotlin/Long =...) // androidx.annotation/IntRange.<init>|<init>(kotlin.Long;kotlin.Long){}[0]
+    final val from // androidx.annotation/IntRange.from|{}from[0]
+        final fun <get-from>(): kotlin/Long // androidx.annotation/IntRange.from.<get-from>|<get-from>(){}[0]
+    final val to // androidx.annotation/IntRange.to|{}to[0]
+        final fun <get-to>(): kotlin/Long // androidx.annotation/IntRange.to.<get-to>|<get-to>(){}[0]
+}
+open annotation class androidx.annotation/LongDef : kotlin/Annotation { // androidx.annotation/LongDef|null[0]
+    constructor <init>(kotlin/LongArray... =..., kotlin/Boolean =..., kotlin/Boolean =...) // androidx.annotation/LongDef.<init>|<init>(kotlin.LongArray...;kotlin.Boolean;kotlin.Boolean){}[0]
+    final val flag // androidx.annotation/LongDef.flag|{}flag[0]
+        final fun <get-flag>(): kotlin/Boolean // androidx.annotation/LongDef.flag.<get-flag>|<get-flag>(){}[0]
+    final val open // androidx.annotation/LongDef.open|{}open[0]
+        final fun <get-open>(): kotlin/Boolean // androidx.annotation/LongDef.open.<get-open>|<get-open>(){}[0]
+    final val value // androidx.annotation/LongDef.value|{}value[0]
+        final fun <get-value>(): kotlin/LongArray // androidx.annotation/LongDef.value.<get-value>|<get-value>(){}[0]
+}
+open annotation class androidx.annotation/MainThread : kotlin/Annotation { // androidx.annotation/MainThread|null[0]
+    constructor <init>() // androidx.annotation/MainThread.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/OpenForTesting : kotlin/Annotation { // androidx.annotation/OpenForTesting|null[0]
+    constructor <init>() // androidx.annotation/OpenForTesting.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/RequiresApi : kotlin/Annotation { // androidx.annotation/RequiresApi|null[1]
+    constructor <init>(kotlin/Int =..., kotlin/Int =...) // androidx.annotation/RequiresApi.<init>|<init>(kotlin.Int;kotlin.Int){}[1]
+    final val api // androidx.annotation/RequiresApi.api|{}api[1]
+        final fun <get-api>(): kotlin/Int // androidx.annotation/RequiresApi.api.<get-api>|<get-api>(){}[1]
+    final val value // androidx.annotation/RequiresApi.value|{}value[1]
+        final fun <get-value>(): kotlin/Int // androidx.annotation/RequiresApi.value.<get-value>|<get-value>(){}[1]
+}
+open annotation class androidx.annotation/RequiresFeature : kotlin/Annotation { // androidx.annotation/RequiresFeature|null[0]
+    constructor <init>(kotlin/String, kotlin/String) // androidx.annotation/RequiresFeature.<init>|<init>(kotlin.String;kotlin.String){}[0]
+    final val enforcement // androidx.annotation/RequiresFeature.enforcement|{}enforcement[0]
+        final fun <get-enforcement>(): kotlin/String // androidx.annotation/RequiresFeature.enforcement.<get-enforcement>|<get-enforcement>(){}[0]
+    final val name // androidx.annotation/RequiresFeature.name|{}name[0]
+        final fun <get-name>(): kotlin/String // androidx.annotation/RequiresFeature.name.<get-name>|<get-name>(){}[0]
+}
+open annotation class androidx.annotation/RestrictTo : kotlin/Annotation { // androidx.annotation/RestrictTo|null[0]
+    constructor <init>(kotlin/Array<out androidx.annotation/RestrictTo.Scope>...) // androidx.annotation/RestrictTo.<init>|<init>(kotlin.Array<out|androidx.annotation.RestrictTo.Scope>...){}[0]
+    final enum class Scope : kotlin/Enum<androidx.annotation/RestrictTo.Scope> { // androidx.annotation/RestrictTo.Scope|null[0]
+        enum entry GROUP_ID // androidx.annotation/RestrictTo.Scope.GROUP_ID|null[0]
+        enum entry LIBRARY // androidx.annotation/RestrictTo.Scope.LIBRARY|null[0]
+        enum entry LIBRARY_GROUP // androidx.annotation/RestrictTo.Scope.LIBRARY_GROUP|null[0]
+        enum entry LIBRARY_GROUP_PREFIX // androidx.annotation/RestrictTo.Scope.LIBRARY_GROUP_PREFIX|null[0]
+        enum entry SUBCLASSES // androidx.annotation/RestrictTo.Scope.SUBCLASSES|null[0]
+        enum entry TESTS // androidx.annotation/RestrictTo.Scope.TESTS|null[0]
+        final fun valueOf(kotlin/String): androidx.annotation/RestrictTo.Scope // androidx.annotation/RestrictTo.Scope.valueOf|valueOf#static(kotlin.String){}[0]
+        final fun values(): kotlin/Array<androidx.annotation/RestrictTo.Scope> // androidx.annotation/RestrictTo.Scope.values|values#static(){}[0]
+    }
+    final val value // androidx.annotation/RestrictTo.value|{}value[0]
+        final fun <get-value>(): kotlin/Array<out androidx.annotation/RestrictTo.Scope> // androidx.annotation/RestrictTo.value.<get-value>|<get-value>(){}[0]
+}
+open annotation class androidx.annotation/ReturnThis : kotlin/Annotation { // androidx.annotation/ReturnThis|null[0]
+    constructor <init>() // androidx.annotation/ReturnThis.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/Size : kotlin/Annotation { // androidx.annotation/Size|null[0]
+    constructor <init>(kotlin/Long =..., kotlin/Long =..., kotlin/Long =..., kotlin/Long =...) // androidx.annotation/Size.<init>|<init>(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+    final val max // androidx.annotation/Size.max|{}max[0]
+        final fun <get-max>(): kotlin/Long // androidx.annotation/Size.max.<get-max>|<get-max>(){}[0]
+    final val min // androidx.annotation/Size.min|{}min[0]
+        final fun <get-min>(): kotlin/Long // androidx.annotation/Size.min.<get-min>|<get-min>(){}[0]
+    final val multiple // androidx.annotation/Size.multiple|{}multiple[0]
+        final fun <get-multiple>(): kotlin/Long // androidx.annotation/Size.multiple.<get-multiple>|<get-multiple>(){}[0]
+    final val value // androidx.annotation/Size.value|{}value[0]
+        final fun <get-value>(): kotlin/Long // androidx.annotation/Size.value.<get-value>|<get-value>(){}[0]
+}
+open annotation class androidx.annotation/StringDef : kotlin/Annotation { // androidx.annotation/StringDef|null[0]
+    constructor <init>(kotlin/Array<out kotlin/String>... =..., kotlin/Boolean =...) // androidx.annotation/StringDef.<init>|<init>(kotlin.Array<out|kotlin.String>...;kotlin.Boolean){}[0]
+    final val open // androidx.annotation/StringDef.open|{}open[0]
+        final fun <get-open>(): kotlin/Boolean // androidx.annotation/StringDef.open.<get-open>|<get-open>(){}[0]
+    final val value // androidx.annotation/StringDef.value|{}value[0]
+        final fun <get-value>(): kotlin/Array<out kotlin/String> // androidx.annotation/StringDef.value.<get-value>|<get-value>(){}[0]
+}
+open annotation class androidx.annotation/UiThread : kotlin/Annotation { // androidx.annotation/UiThread|null[0]
+    constructor <init>() // androidx.annotation/UiThread.<init>|<init>(){}[0]
+}
+open annotation class androidx.annotation/VisibleForTesting : kotlin/Annotation { // androidx.annotation/VisibleForTesting|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.annotation/VisibleForTesting.<init>|<init>(kotlin.Int){}[0]
+    final object Companion { // androidx.annotation/VisibleForTesting.Companion|null[0]
+        final const val NONE // androidx.annotation/VisibleForTesting.Companion.NONE|{}NONE[0]
+            final fun <get-NONE>(): kotlin/Int // androidx.annotation/VisibleForTesting.Companion.NONE.<get-NONE>|<get-NONE>(){}[0]
+        final const val PACKAGE_PRIVATE // androidx.annotation/VisibleForTesting.Companion.PACKAGE_PRIVATE|{}PACKAGE_PRIVATE[0]
+            final fun <get-PACKAGE_PRIVATE>(): kotlin/Int // androidx.annotation/VisibleForTesting.Companion.PACKAGE_PRIVATE.<get-PACKAGE_PRIVATE>|<get-PACKAGE_PRIVATE>(){}[0]
+        final const val PRIVATE // androidx.annotation/VisibleForTesting.Companion.PRIVATE|{}PRIVATE[0]
+            final fun <get-PRIVATE>(): kotlin/Int // androidx.annotation/VisibleForTesting.Companion.PRIVATE.<get-PRIVATE>|<get-PRIVATE>(){}[0]
+        final const val PROTECTED // androidx.annotation/VisibleForTesting.Companion.PROTECTED|{}PROTECTED[0]
+            final fun <get-PROTECTED>(): kotlin/Int // androidx.annotation/VisibleForTesting.Companion.PROTECTED.<get-PROTECTED>|<get-PROTECTED>(){}[0]
+    }
+    final val otherwise // androidx.annotation/VisibleForTesting.otherwise|{}otherwise[0]
+        final fun <get-otherwise>(): kotlin/Int // androidx.annotation/VisibleForTesting.otherwise.<get-otherwise>|<get-otherwise>(){}[0]
+}
diff --git a/annotation/annotation/build.gradle b/annotation/annotation/build.gradle
index 0abb57e..0b88e7c 100644
--- a/annotation/annotation/build.gradle
+++ b/annotation/annotation/build.gradle
@@ -19,11 +19,12 @@
     androidNative()
     jvm()
     mac()
+    mingwX64()
     linux()
-    linuxArm64()
     ios()
     watchos()
     tvos()
+    wasmJs()
 
     defaultPlatform(PlatformIdentifier.JVM)
 
@@ -35,7 +36,14 @@
         }
 
         nonJvmMain {
-            dependsOn commonMain
+            dependsOn(commonMain)
+        }
+
+        wasmJsMain {
+            dependsOn(nonJvmMain)
+            dependencies {
+                api(libs.kotlinStdlibJs)
+            }
         }
 
         targets.configureEach { target ->
diff --git a/appcompat/appcompat-benchmark/build.gradle b/appcompat/appcompat-benchmark/build.gradle
index 039d310..8eb2d87 100644
--- a/appcompat/appcompat-benchmark/build.gradle
+++ b/appcompat/appcompat-benchmark/build.gradle
@@ -34,4 +34,3 @@
 android {
     namespace "androidx.appcompat.benchmark"
 }
-
diff --git a/appcompat/appcompat-lint/integration-tests/build.gradle b/appcompat/appcompat-lint/integration-tests/build.gradle
index cd7691f..4c2adc6 100644
--- a/appcompat/appcompat-lint/integration-tests/build.gradle
+++ b/appcompat/appcompat-lint/integration-tests/build.gradle
@@ -18,6 +18,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         vectorDrawables.useSupportLibrary = true
     }
diff --git a/appcompat/appcompat-lint/integration-tests/lint-baseline.xml b/appcompat/appcompat-lint/integration-tests/lint-baseline.xml
index f568c6a..abf06096 100644
--- a/appcompat/appcompat-lint/integration-tests/lint-baseline.xml
+++ b/appcompat/appcompat-lint/integration-tests/lint-baseline.xml
@@ -1,23 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.appcompat.AppCompatLintDemo is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            noop.setCompoundDrawableTintList(csl);"
-        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/appcompat/AppCompatLintDemo.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.appcompat.AppCompatLintDemo is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            noop.setCompoundDrawableTintMode(PorterDuff.Mode.DST);"
-        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/appcompat/AppCompatLintDemo.java"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="UseAndroidAlpha"
diff --git a/appcompat/appcompat-resources/build.gradle b/appcompat/appcompat-resources/build.gradle
index 50d8950..dcc74f0 100644
--- a/appcompat/appcompat-resources/build.gradle
+++ b/appcompat/appcompat-resources/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.6.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.vectordrawable:vectordrawable:1.1.0")
@@ -69,5 +69,4 @@
     inceptionYear = "2019"
     description = "Provides backward-compatible implementations of resource-related Android SDK" +
             "functionality, including color state list theming."
-    metalavaK2UastEnabled = true
 }
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java
index 31658ce..40ae0b8 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/resources/Compatibility.java
@@ -21,7 +21,6 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -48,20 +47,17 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         public static void inflate(@NonNull Drawable drawable, @NonNull Resources r,
                 @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
                 @Nullable Resources.Theme theme) throws IOException, XmlPullParserException {
             drawable.inflate(r, parser, attrs, theme);
         }
 
-        @DoNotInline
         public static int getChangingConfigurations(@NonNull TypedArray typedArray) {
             return typedArray.getChangingConfigurations();
         }
 
         @NonNull
-        @DoNotInline
         public static Drawable createFromXmlInner(@NonNull Resources r,
                 @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
                 @Nullable Resources.Theme theme) throws IOException, XmlPullParserException {
diff --git a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/DrawableUtils.java b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/DrawableUtils.java
index 04e9541..2329750 100644
--- a/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/DrawableUtils.java
+++ b/appcompat/appcompat-resources/src/main/java/androidx/appcompat/widget/DrawableUtils.java
@@ -26,7 +26,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -220,7 +219,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Insets getOpticalInsets(Drawable drawable) {
             return drawable.getOpticalInsets();
         }
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index aa6cbc1..12535c0 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -15,7 +15,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.3.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.13.0")
 
     // Required to make activity 1.5.0-rc01 dependencies resolve.
@@ -63,6 +63,7 @@
         exclude group: "androidx.appcompat", module: "appcompat"
         exclude group: "androidx.core", module: "core"
     })
+    androidTestImplementation(project(":internal-testutils-fonts"))
 
     testImplementation(libs.kotlinStdlib)
     testImplementation(libs.testCore)
@@ -113,5 +114,4 @@
     description = "Provides backwards-compatible implementations of UI-related Android SDK " +
             "functionality, including dark mode and Material theming."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/appcompat/appcompat/lint-baseline.xml b/appcompat/appcompat/lint-baseline.xml
index e5dbab9..9d24e27 100644
--- a/appcompat/appcompat/lint-baseline.xml
+++ b/appcompat/appcompat/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="NewApi"
@@ -193,7 +193,7 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(/* millis = */ 2000)"
+        errorLine1="        Thread.sleep(/* millis= */ 2000)"
         errorLine2="               ~~~~~">
         <location
             file="src/androidTest/java/androidx/appcompat/app/LocalesConfigChangedUsingInvalidTopLocale.kt"/>
@@ -202,7 +202,7 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(/* millis = */ 2000)"
+        errorLine1="        Thread.sleep(/* millis= */ 2000)"
         errorLine2="               ~~~~~">
         <location
             file="src/androidTest/java/androidx/appcompat/app/LocalesConfigChangedUsingInvalidTopLocale.kt"/>
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java
index c329690..21768cd 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java
@@ -18,11 +18,14 @@
 import static androidx.appcompat.testutils.TestUtilsActions.setEnabled;
 import static androidx.appcompat.testutils.TestUtilsActions.setTextAppearance;
 import static androidx.appcompat.testutils.TestUtilsMatchers.isBackground;
+import static androidx.core.view.ViewKt.drawToBitmap;
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.scrollTo;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
+import static junit.framework.Assert.assertFalse;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -30,8 +33,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.app.Instrumentation;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
@@ -67,6 +72,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
 
 import org.junit.Ignore;
@@ -476,7 +482,15 @@
     public void testFontVariation_setInXml() {
         final AppCompatTextView textView = mActivity.findViewById(
                 R.id.textview_fontVariation_textView);
-        assertEquals("'wdth' 30", textView.getFontVariationSettings());
+        assertEquals("'wght' 200", textView.getFontVariationSettings());
+
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        runCommandAndAssertBitmapChangedOrSame(textView, /* expectSame*/ true, () ->
+                instrumentation.runOnMainSync(() -> {
+                    textView.setFontVariationSettings("'wght' 100"); /* reset */
+                    textView.setFontVariationSettings("'wght' 200");
+                })
+        );
     }
 
     @SdkSuppress(minSdkVersion = 26)
@@ -485,6 +499,28 @@
         final AppCompatTextView textView = mActivity.findViewById(
                 R.id.textview_fontVariation_textAppearance);
         assertEquals("'wght' 300", textView.getFontVariationSettings());
+
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        runCommandAndAssertBitmapChangedOrSame(textView, /* expectSame*/ true, () ->
+                instrumentation.runOnMainSync(() -> {
+                    textView.setFontVariationSettings("'wght' 100"); /* reset */
+                    textView.setFontVariationSettings("'wght' 300");
+                })
+        );
+    }
+
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    public void testFontVariation_setByBothXmlAndTextAppearance() throws InterruptedException {
+        final AppCompatTextView textView = mActivity.findViewById(
+                R.id.textview_fontVariation_textAppearance);
+
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        runCommandAndAssertBitmapChangedOrSame(textView, /* expectSame*/ true, () ->
+                instrumentation.runOnMainSync(() ->
+                        textView.setTextAppearance(R.style.TextView_FontVariation)
+                )
+        );
     }
 
     @SdkSuppress(minSdkVersion = 26)
@@ -494,7 +530,7 @@
                 R.id.textview_fontVariation_textView_and_textAppearance);
         //FontVariation is set in both AppCompatTextView and textAppearance,
         //we should use the one in AppCompatTextView.
-        assertEquals("'wdth' 30", textView.getFontVariationSettings());
+        assertEquals("'wght' 700", textView.getFontVariationSettings());
     }
 
     @SdkSuppress(minSdkVersion = 26)
@@ -502,12 +538,59 @@
     @UiThreadTest
     public void testFontVariation_setTextAppearance() throws Throwable {
         final AppCompatTextView textView = mActivity.findViewById(
-                R.id.textview_simple
+                R.id.textview_fontVariation_textView
         );
         textView.setTextAppearance(textView.getContext(), R.style.TextView_FontVariation);
         assertEquals("'wght' 300", textView.getFontVariationSettings());
     }
 
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    public void testFontVariation_setTextAppearance_changesPixels() throws Throwable {
+        final AppCompatTextView textView = mActivity.findViewById(
+                R.id.textview_fontVariation_textView
+        );
+
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.runOnMainSync(() ->
+                textView.setFontVariationSettings("'wght' 900")
+        );
+        runCommandAndAssertBitmapChangedOrSame(textView, /* expectSame*/ false, () ->
+                instrumentation.runOnMainSync(() ->
+                        textView.setTextAppearance(textView.getContext(),
+                                R.style.TextView_FontVariation)
+                )
+        );
+    }
+
+    /**
+     * Run a command and confirm that the result of the command was a visible change to at least
+     * one pixel or exact similarity
+     *
+     * @param subject view to change
+     * @param expectSame when true, expect pixels to be identical, otherwise expect differences
+     * @param command comamnd to be called. it is not dispatched
+     */
+    public void runCommandAndAssertBitmapChangedOrSame(TextView subject, Boolean expectSame,
+            Runnable command) {
+        Bitmap before = drawToBitmap(subject, Bitmap.Config.ARGB_8888);
+        try {
+            command.run();
+            Bitmap after = drawToBitmap(subject, Bitmap.Config.ARGB_8888);
+            try {
+                if (expectSame) {
+                    assertTrue(before.sameAs(after));
+                } else {
+                    assertFalse(before.sameAs(after));
+                }
+            } finally {
+                after.recycle();
+            }
+        } finally {
+            before.recycle();
+        }
+    }
+
     @Test
     @UiThreadTest
     public void testBaselineAttributes() {
diff --git a/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_activity.xml b/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_activity.xml
index 151113a..6b88361 100644
--- a/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_activity.xml
+++ b/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_activity.xml
@@ -380,21 +380,25 @@
 
         <androidx.appcompat.widget.AppCompatTextView
             android:id="@+id/textview_fontVariation_textView"
+            android:text="A"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            app:fontVariationSettings="'wdth' 30"/>
+            android:fontFamily="@font/variable_font"
+            app:fontVariationSettings="'wght' 200"/>
 
         <androidx.appcompat.widget.AppCompatTextView
             android:id="@+id/textview_fontVariation_textAppearance"
+            android:text="A"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:fontFamily="@font/variable_font"
             android:textAppearance="@style/TextView_FontVariation"/>
 
         <androidx.appcompat.widget.AppCompatTextView
             android:id="@+id/textview_fontVariation_textView_and_textAppearance"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            app:fontVariationSettings="'wdth' 30"
+            app:fontVariationSettings="'wght' 700"
             android:textAppearance="@style/TextView_FontVariation"/>
 
         <androidx.appcompat.widget.AppCompatTextView
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AlertController.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AlertController.java
index 9a24db5..e1ca2bc 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AlertController.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AlertController.java
@@ -248,6 +248,7 @@
         if (mTitleView != null) {
             mTitleView.setText(title);
         }
+        mWindow.setTitle(title);
     }
 
     /**
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java
index 38de8f1..a1d6165b 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java
@@ -44,7 +44,6 @@
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.CallSuper;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IdRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.LayoutRes;
@@ -1155,7 +1154,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static LocaleList localeListForLanguageTags(String list) {
             return LocaleList.forLanguageTags(list);
         }
@@ -1167,14 +1165,12 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void localeManagerSetApplicationLocales(Object localeManager,
                 LocaleList locales) {
             LocaleManager mLocaleManager = (LocaleManager) localeManager;
             mLocaleManager.setApplicationLocales(locales);
         }
 
-        @DoNotInline
         static LocaleList localeManagerGetApplicationLocales(Object localeManager) {
             LocaleManager mLocaleManager = (LocaleManager) localeManager;
             return mLocaleManager.getApplicationLocales();
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index 75e4fbb..6f787f3 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -78,7 +78,6 @@
 import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.CallSuper;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IdRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -3892,12 +3891,10 @@
     static class Api21Impl {
         private Api21Impl() { }
 
-        @DoNotInline
         static boolean isPowerSaveMode(PowerManager powerManager) {
             return powerManager.isPowerSaveMode();
         }
 
-        @DoNotInline
         static String toLanguageTag(Locale locale) {
             return locale.toLanguageTag();
         }
@@ -3909,7 +3906,6 @@
 
         // Most methods of LocaleListCompat requires a minimum API of 24 to be used and these are
         // the helper implementations of those methods, used to indirectly invoke them in our code.
-        @DoNotInline
         static void generateConfigDelta_locale(@NonNull Configuration base,
                 @NonNull Configuration change, @NonNull Configuration delta) {
             final LocaleList baseLocales = base.getLocales();
@@ -3920,17 +3916,14 @@
             }
         }
 
-        @DoNotInline
         static LocaleListCompat getLocales(Configuration configuration) {
             return LocaleListCompat.forLanguageTags(configuration.getLocales().toLanguageTags());
         }
 
-        @DoNotInline
         static void setLocales(Configuration configuration, LocaleListCompat locales) {
             configuration.setLocales(LocaleList.forLanguageTags(locales.toLanguageTags()));
         }
 
-        @DoNotInline
         public static void setDefaultLocales(LocaleListCompat locales) {
             LocaleList.setDefault(LocaleList.forLanguageTags(locales.toLanguageTags()));
         }
@@ -3961,7 +3954,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static OnBackInvokedCallback registerOnBackPressedCallback(
                 Object dispatcher, AppCompatDelegateImpl delegate) {
             OnBackInvokedCallback onBackInvokedCallback = delegate::onBackPressed;
@@ -3971,14 +3963,12 @@
             return onBackInvokedCallback;
         }
 
-        @DoNotInline
         static void unregisterOnBackInvokedCallback(Object dispatcher, Object callback) {
             OnBackInvokedCallback onBackInvokedCallback = (OnBackInvokedCallback) callback;
             OnBackInvokedDispatcher typedDispatcher = (OnBackInvokedDispatcher) dispatcher;
             typedDispatcher.unregisterOnBackInvokedCallback(onBackInvokedCallback);
         }
 
-        @DoNotInline
         static OnBackInvokedDispatcher getOnBackInvokedDispatcher(Activity activity) {
             return activity.getOnBackInvokedDispatcher();
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesMetadataHolderService.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesMetadataHolderService.java
index 5f2668f..de453cf 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesMetadataHolderService.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesMetadataHolderService.java
@@ -25,7 +25,6 @@
 import android.os.Build;
 import android.os.IBinder;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.core.os.LocaleListCompat;
@@ -72,7 +71,6 @@
 
     @RequiresApi(24)
     private static class Api24Impl {
-        @DoNotInline
         static int getDisabledComponentFlag() {
             return PackageManager.MATCH_DISABLED_COMPONENTS;
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/view/WindowCallbackWrapper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/view/WindowCallbackWrapper.java
index a430dbe..c12c6ea 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/view/WindowCallbackWrapper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/view/WindowCallbackWrapper.java
@@ -30,7 +30,6 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
@@ -194,12 +193,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean onSearchRequested(Window.Callback callback, SearchEvent searchEvent) {
             return callback.onSearchRequested(searchEvent);
         }
 
-        @DoNotInline
         static ActionMode onWindowStartingActionMode(Window.Callback windowCallback,
                 ActionMode.Callback actionModeCallback, int i) {
             return windowCallback.onWindowStartingActionMode(actionModeCallback, i);
@@ -212,7 +209,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void onProvideKeyboardShortcuts(Window.Callback callback,
                 List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
             callback.onProvideKeyboardShortcuts(data, menu, deviceId);
@@ -226,7 +222,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void onPointerCaptureChanged(Window.Callback callback, boolean hasCapture) {
             callback.onPointerCaptureChanged(hasCapture);
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatReceiveContentHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatReceiveContentHelper.java
index 62d2a49..91bc26a 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatReceiveContentHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatReceiveContentHelper.java
@@ -33,7 +33,6 @@
 import android.view.View;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -117,7 +116,6 @@
     private static final class OnDropApi24Impl {
         private OnDropApi24Impl() {}
 
-        @DoNotInline
         static boolean onDropForTextView(@NonNull DragEvent event, @NonNull TextView view,
                 @NonNull Activity activity) {
             activity.requestDragAndDropPermissions(event);
@@ -134,7 +132,6 @@
             return true;
         }
 
-        @DoNotInline
         static boolean onDropForView(@NonNull DragEvent event, @NonNull View view,
                 @NonNull Activity activity) {
             activity.requestDragAndDropPermissions(event);
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java
index 638e833..37e4725 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java
@@ -46,7 +46,6 @@
 import android.widget.Spinner;
 import android.widget.SpinnerAdapter;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -1103,7 +1102,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setDropDownViewTheme(
                 @NonNull android.widget.ThemedSpinnerAdapter themedSpinnerAdapter,
                 @Nullable Resources.Theme theme
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java
index 81607aa..f675ec9 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java
@@ -20,7 +20,6 @@
 import android.view.textclassifier.TextClassifier;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -72,7 +71,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         @NonNull
         static TextClassifier getTextClassifier(@NonNull TextView textView) {
             final TextClassificationManager tcm =
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
index 158f467..93530a91 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.LocaleList;
+import android.text.TextUtils;
 import android.text.method.PasswordTransformationMethod;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -35,7 +36,6 @@
 import android.view.inputmethod.InputConnection;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -49,6 +49,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.Locale;
+import java.util.Objects;
 
 class AppCompatTextHelper {
 
@@ -76,6 +77,8 @@
     private int mStyle = Typeface.NORMAL;
     private int mFontWeight = TEXT_FONT_WEIGHT_UNSPECIFIED;
     private Typeface mFontTypeface;
+    @Nullable
+    private String mFontVariationSettings = null;
     private boolean mAsyncFontPending;
 
     AppCompatTextHelper(@NonNull TextView view) {
@@ -134,7 +137,6 @@
         ColorStateList textColor = null;
         ColorStateList textColorHint = null;
         ColorStateList textColorLink = null;
-        String fontVariation = null;
         String localeListString = null;
 
         // First check TextAppearance's textAllCaps value
@@ -164,10 +166,6 @@
             if (a.hasValue(R.styleable.TextAppearance_textLocale)) {
                 localeListString = a.getString(R.styleable.TextAppearance_textLocale);
             }
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-                    && a.hasValue(R.styleable.TextAppearance_fontVariationSettings)) {
-                fontVariation = a.getString(R.styleable.TextAppearance_fontVariationSettings);
-            }
             a.recycle();
         }
 
@@ -197,10 +195,6 @@
             localeListString = a.getString(R.styleable.TextAppearance_textLocale);
         }
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-                && a.hasValue(R.styleable.TextAppearance_fontVariationSettings)) {
-            fontVariation = a.getString(R.styleable.TextAppearance_fontVariationSettings);
-        }
         // In P, when the text size attribute is 0, this would not be set. Fix this here.
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
                 && a.hasValue(R.styleable.TextAppearance_android_textSize)) {
@@ -224,16 +218,9 @@
         if (!hasPwdTm && allCapsSet) {
             setAllCaps(allCaps);
         }
-        if (mFontTypeface != null) {
-            if (mFontWeight == TEXT_FONT_WEIGHT_UNSPECIFIED) {
-                mView.setTypeface(mFontTypeface, mStyle);
-            } else {
-                mView.setTypeface(mFontTypeface);
-            }
-        }
-        if (fontVariation != null) {
-            Api26Impl.setFontVariationSettings(mView, fontVariation);
-        }
+
+        applyFontAndVariationSettings(/* forceNullSet */ false);
+
         if (localeListString != null) {
             if (Build.VERSION.SDK_INT >= 24) {
                 Api24Impl.setTextLocales(mView, Api24Impl.forLanguageTags(localeListString));
@@ -355,7 +342,39 @@
         }
     }
 
-    private void updateTypefaceAndStyle(Context context, TintTypedArray a) {
+    /**
+     * Apply mFontTypeface previously loaded from XML, and apply mFontVariationSettings to it.
+     *
+     * This should only be called from xml initialization, or setTextAppearance.
+     *
+     * @param forceNullSet Explicit null values should clobber existing Typefaces
+     */
+    private void applyFontAndVariationSettings(boolean forceNullSet) {
+        if (mFontTypeface != null) {
+            if (mFontWeight == TEXT_FONT_WEIGHT_UNSPECIFIED) {
+                mView.setTypeface(mFontTypeface, mStyle);
+            } else {
+                mView.setTypeface(mFontTypeface);
+            }
+        } else if (forceNullSet) {
+            mView.setTypeface(null);
+        }
+
+        if (mFontVariationSettings != null && Build.VERSION.SDK_INT >= 26) {
+            Api26Impl.setFontVariationSettings(mView, mFontVariationSettings);
+        }
+    }
+
+    /**
+     * Load mFontTypeface from an xml, may be called multiple times with e.g. style, textAppearance,
+     * and attribute items.
+     *
+     * Note: Setting multiple fonts at different levels currently triggers multiple font-loads.
+     *
+     * @param context to check if restrictions avoid loading downloadable fonts
+     * @param a attributes to read from, e.g. a textappearance
+     */
+    private boolean updateTypefaceAndStyle(Context context, TintTypedArray a) {
         mStyle = a.getInt(R.styleable.TextAppearance_android_textStyle, mStyle);
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -366,6 +385,11 @@
             }
         }
 
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+                && a.hasValue(R.styleable.TextAppearance_fontVariationSettings)) {
+            mFontVariationSettings = a.getString(R.styleable.TextAppearance_fontVariationSettings);
+        }
+
         if (a.hasValue(R.styleable.TextAppearance_android_fontFamily)
                 || a.hasValue(R.styleable.TextAppearance_fontFamily)) {
             mFontTypeface = null;
@@ -375,27 +399,12 @@
             final int fontWeight = mFontWeight;
             final int style = mStyle;
             if (!context.isRestricted()) {
-                final WeakReference<TextView> textViewWeak = new WeakReference<>(mView);
-                ResourcesCompat.FontCallback replyCallback = new ResourcesCompat.FontCallback() {
-                    @Override
-                    public void onFontRetrieved(@NonNull Typeface typeface) {
-                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-                            if (fontWeight != TEXT_FONT_WEIGHT_UNSPECIFIED) {
-                                typeface = Api28Impl.create(typeface, fontWeight,
-                                        (style & Typeface.ITALIC) != 0);
-                            }
-                        }
-                        onAsyncTypefaceReceived(textViewWeak, typeface);
-                    }
-
-                    @Override
-                    public void onFontRetrievalFailed(int reason) {
-                        // Do nothing.
-                    }
-                };
+                ResourcesCompat.FontCallback replyCallback = makeFontCallback(fontWeight,
+                        style);
                 try {
                     // Note the callback will be triggered on the UI thread.
                     final Typeface typeface = a.getFont(fontFamilyId, mStyle, replyCallback);
+                    // assume Typeface does have fontVariationSettings in this path
                     if (typeface != null) {
                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
                                 && mFontWeight != TEXT_FONT_WEIGHT_UNSPECIFIED) {
@@ -426,7 +435,7 @@
                     }
                 }
             }
-            return;
+            return true;
         }
 
         if (a.hasValue(R.styleable.TextAppearance_android_typeface)) {
@@ -446,13 +455,39 @@
                     mFontTypeface = Typeface.MONOSPACE;
                     break;
             }
+            return true;
         }
+        return false;
+    }
+
+    @NonNull
+    private ResourcesCompat.FontCallback makeFontCallback(int fontWeight, int style) {
+        final WeakReference<TextView> textViewWeak = new WeakReference<>(mView);
+        return new ResourcesCompat.FontCallback() {
+            @Override
+            public void onFontRetrieved(@NonNull Typeface typeface) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                    if (fontWeight != TEXT_FONT_WEIGHT_UNSPECIFIED) {
+                        typeface = Api28Impl.create(typeface, fontWeight,
+                                (style & Typeface.ITALIC) != 0);
+                    }
+                }
+                onAsyncTypefaceReceived(textViewWeak, typeface);
+            }
+
+            @Override
+            public void onFontRetrievalFailed(int reason) {
+                // Do nothing.
+            }
+        };
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     void onAsyncTypefaceReceived(WeakReference<TextView> textViewWeak, final Typeface typeface) {
         if (mAsyncFontPending) {
+            // we assume that typeface has the correct variation settings from androidx.core
             mFontTypeface = typeface;
+            // TODO(b/266112457) unset mFontVariationSettings here so we don't double apply it
             final TextView textView = textViewWeak.get();
             if (textView != null) {
                 if (textView.isAttachedToWindow()) {
@@ -460,16 +495,32 @@
                     textView.post(new Runnable() {
                         @Override
                         public void run() {
-                            textView.setTypeface(typeface, style);
+                            applyNewTypefacePreservingVariationSettings(textView, typeface, style);
                         }
                     });
                 } else {
-                    textView.setTypeface(typeface, mStyle);
+                    applyNewTypefacePreservingVariationSettings(textView, typeface, mStyle);
                 }
             }
         }
     }
 
+    private static void applyNewTypefacePreservingVariationSettings(TextView textView,
+            Typeface typeface, int style) {
+        String fontVariationSettings = null;
+        if (Build.VERSION.SDK_INT >= 26) {
+            fontVariationSettings = Api26Impl.getFontVariationSettings(textView);
+            if (!TextUtils.isEmpty(fontVariationSettings)) {
+                Api26Impl.setFontVariationSettings(textView, null);
+            }
+        }
+
+        textView.setTypeface(typeface, style);
+        if (Build.VERSION.SDK_INT >= 26 && !TextUtils.isEmpty(fontVariationSettings)) {
+            Api26Impl.setFontVariationSettings(textView, fontVariationSettings);
+        }
+    }
+
     void onSetTextAppearance(Context context, int resId) {
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
                 resId, R.styleable.TextAppearance);
@@ -512,20 +563,9 @@
             }
         }
 
-        updateTypefaceAndStyle(context, a);
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-                && a.hasValue(R.styleable.TextAppearance_fontVariationSettings)) {
-            final String fontVariation = a.getString(
-                    R.styleable.TextAppearance_fontVariationSettings);
-            if (fontVariation != null) {
-                Api26Impl.setFontVariationSettings(mView, fontVariation);
-            }
-        }
+        boolean containsTypeface = updateTypefaceAndStyle(context, a);
         a.recycle();
-        if (mFontTypeface != null) {
-            mView.setTypeface(mFontTypeface, mStyle);
-        }
+        applyFontAndVariationSettings(containsTypeface);
     }
 
     void setAllCaps(boolean allCaps) {
@@ -736,17 +776,22 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
+        static String getFontVariationSettings(TextView textView) {
+            return textView.getFontVariationSettings();
+        }
+
         static boolean setFontVariationSettings(TextView textView, String fontVariationSettings) {
+            if (Objects.equals(textView.getFontVariationSettings(), fontVariationSettings)) {
+                // textView will early-exit if we don't clear fontVariationSettings
+                textView.setFontVariationSettings("");
+            }
             return textView.setFontVariationSettings(fontVariationSettings);
         }
 
-        @DoNotInline
         static int getAutoSizeStepGranularity(TextView textView) {
             return textView.getAutoSizeStepGranularity();
         }
 
-        @DoNotInline
         static void setAutoSizeTextTypeUniformWithConfiguration(TextView textView,
                 int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity,
                 int unit) {
@@ -754,7 +799,6 @@
                     autoSizeMaxTextSize, autoSizeStepGranularity, unit);
         }
 
-        @DoNotInline
         static void setAutoSizeTextTypeUniformWithPresetSizes(TextView textView, int[] presetSizes,
                 int unit) {
             textView.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
@@ -767,12 +811,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setTextLocales(TextView textView, LocaleList locales) {
             textView.setTextLocales(locales);
         }
 
-        @DoNotInline
         static LocaleList forLanguageTags(String list) {
             return LocaleList.forLanguageTags(list);
         }
@@ -784,7 +826,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Locale forLanguageTag(String languageTag) {
             return Locale.forLanguageTag(languageTag);
         }
@@ -797,7 +838,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Typeface create(Typeface family, int weight, boolean italic) {
             return Typeface.create(family, weight, italic);
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
index 684696a..cd09c92 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
@@ -36,7 +36,6 @@
 import android.util.TypedValue;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -839,7 +838,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         @NonNull
         static StaticLayout createStaticLayoutForMeasuring(
                 @NonNull CharSequence text,
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java
index 1553ee8..89472db 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java
@@ -32,7 +32,6 @@
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.appcompat.R;
@@ -799,7 +798,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void drawableHotspotChanged(View view, float x, float y) {
             view.drawableHotspotChanged(x, y);
         }
@@ -856,12 +854,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isSelectedChildViewEnabled(AbsListView view) {
             return view.isSelectedChildViewEnabled();
         }
 
-        @DoNotInline
         static void setSelectedChildViewEnabled(AbsListView view, boolean enabled) {
             view.setSelectedChildViewEnabled(enabled);
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ListPopupWindow.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ListPopupWindow.java
index 0045e43..ca8c609 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ListPopupWindow.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ListPopupWindow.java
@@ -45,7 +45,6 @@
 import android.widget.PopupWindow;
 
 import androidx.annotation.AttrRes;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -1463,12 +1462,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setEpicenterBounds(PopupWindow popupWindow, Rect bounds) {
             popupWindow.setEpicenterBounds(bounds);
         }
 
-        @DoNotInline
         static void setIsClippedToScreen(PopupWindow popupWindow, boolean enabled) {
             popupWindow.setIsClippedToScreen(enabled);
         }
@@ -1480,7 +1477,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getMaxAvailableHeight(PopupWindow popupWindow, View anchor, int yOffset,
                 boolean ignoreBottomDecorations) {
             return popupWindow.getMaxAvailableHeight(anchor, yOffset, ignoreBottomDecorations);
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java
index bc3cf31..44c6c66 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java
@@ -33,7 +33,6 @@
 import android.widget.ListAdapter;
 import android.widget.PopupWindow;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -248,12 +247,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setEnterTransition(PopupWindow popupWindow, Transition enterTransition) {
             popupWindow.setEnterTransition(enterTransition);
         }
 
-        @DoNotInline
         static void setExitTransition(PopupWindow popupWindow, Transition exitTransition) {
             popupWindow.setExitTransition(exitTransition);
         }
@@ -265,7 +262,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setTouchModal(PopupWindow popupWindow, boolean touchModal) {
             popupWindow.setTouchModal(touchModal);
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java
index dd27fed..0cce612 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java
@@ -72,7 +72,6 @@
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -2125,12 +2124,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setInputMethodMode(SearchAutoComplete searchAutoComplete, int mode) {
             searchAutoComplete.setInputMethodMode(mode);
         }
 
-        @DoNotInline
         static void refreshAutoCompleteResults(AutoCompleteTextView autoCompleteTextView) {
             autoCompleteTextView.refreshAutoCompleteResults();
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java
index 3b2f06e..a9db85c 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java
@@ -28,7 +28,6 @@
 import android.util.AttributeSet;
 import android.util.TypedValue;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -263,12 +262,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getType(TypedArray typedArray, int index) {
             return typedArray.getType(index);
         }
 
-        @DoNotInline
         static int getChangingConfigurations(TypedArray typedArray) {
             return typedArray.getChangingConfigurations();
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
index 7ab74f8..349f778 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
@@ -45,7 +45,6 @@
 import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.MainThread;
 import androidx.annotation.MenuRes;
@@ -2798,7 +2797,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void tryRegisterOnBackInvokedCallback(@NonNull Object dispatcherObj,
                 @NonNull Object callback) {
             OnBackInvokedDispatcher dispatcher = (OnBackInvokedDispatcher) dispatcherObj;
@@ -2806,7 +2804,6 @@
                     (OnBackInvokedCallback) callback);
         }
 
-        @DoNotInline
         static void tryUnregisterOnBackInvokedCallback(@NonNull Object dispatcherObj,
                 @NonNull Object callbackObj) {
             OnBackInvokedDispatcher dispatcher = (OnBackInvokedDispatcher) dispatcherObj;
@@ -2814,13 +2811,11 @@
         }
 
         @Nullable
-        @DoNotInline
         static OnBackInvokedDispatcher findOnBackInvokedDispatcher(@NonNull View view) {
             return view.findOnBackInvokedDispatcher();
         }
 
         @NonNull
-        @DoNotInline
         static OnBackInvokedCallback newOnBackInvokedCallback(@NonNull Runnable action) {
             return action::run;
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TooltipCompat.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TooltipCompat.java
index 84e8b01..3a84584 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TooltipCompat.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/TooltipCompat.java
@@ -19,7 +19,6 @@
 import android.os.Build;
 import android.view.View;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -59,7 +58,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setTooltipText(View view, CharSequence tooltipText) {
             view.setTooltipText(tooltipText);
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java
index 6d5238c..2369324 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/ViewUtils.java
@@ -28,7 +28,6 @@
 import android.view.WindowInsets;
 
 import androidx.annotation.ChecksSdkIntAtLeast;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -118,7 +117,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void computeFitSystemWindows(@NonNull View view, @NonNull Rect inoutInsets,
                 @NonNull Rect outLocalInsets) {
             WindowInsets in =
diff --git a/appcompat/appcompat/src/main/res/values-fr-rCA/strings.xml b/appcompat/appcompat/src/main/res/values-fr-rCA/strings.xml
index f065c79..a24b905 100644
--- a/appcompat/appcompat/src/main/res/values-fr-rCA/strings.xml
+++ b/appcompat/appcompat/src/main/res/values-fr-rCA/strings.xml
@@ -27,7 +27,7 @@
     <string name="abc_searchview_description_clear" msgid="3741173234950517107">"Effacer la requête"</string>
     <string name="abc_searchview_description_submit" msgid="1486535517437947103">"Envoyer la requête"</string>
     <string name="abc_searchview_description_voice" msgid="2293578557972875415">"Recherche vocale"</string>
-    <string name="abc_activitychooserview_choose_application" msgid="2165779757652331008">"Sélectionner une application"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2165779757652331008">"Sélectionner une appli"</string>
     <string name="abc_activity_chooser_view_see_all" msgid="1189761859438369441">"Tout afficher"</string>
     <string name="abc_shareactionprovider_share_with_application" msgid="9055268688411532828">"Partager avec <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="abc_shareactionprovider_share_with" msgid="8875138169939072951">"Partager avec"</string>
diff --git a/appcompat/integration-tests/receive-content-testapp/build.gradle b/appcompat/integration-tests/receive-content-testapp/build.gradle
index e414862..fc832e6 100644
--- a/appcompat/integration-tests/receive-content-testapp/build.gradle
+++ b/appcompat/integration-tests/receive-content-testapp/build.gradle
@@ -20,11 +20,12 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.appcompat.demo.receivecontent"
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(project(":appcompat:appcompat"))
     implementation(libs.constraintLayout)
     implementation(libs.guavaAndroid)
diff --git a/appsearch/appsearch-builtin-types/build.gradle b/appsearch/appsearch-builtin-types/build.gradle
index d709547..dbce0e2 100644
--- a/appsearch/appsearch-builtin-types/build.gradle
+++ b/appsearch/appsearch-builtin-types/build.gradle
@@ -53,9 +53,9 @@
             'objects based on http://schema.org. Data interchange with the system, and other ' +
             'apps, as well as structured parameters for semantic intents should use these ' +
             'built-in types as appropriate.'
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.appsearch.builtintypes"
 }
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/app/ShortcutAdapter.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/app/ShortcutAdapter.java
index 9d17a58..d98a917 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/app/ShortcutAdapter.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/app/ShortcutAdapter.java
@@ -23,11 +23,13 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.safeparcel.GenericDocumentParcel;
 import androidx.core.content.pm.ShortcutInfoCompat;
 import androidx.core.util.Preconditions;
 
@@ -61,6 +63,9 @@
             + "Please use androidx.appsearch.app.ShortcutAdapter.DEFAULT_NAMESPACE as the "
             + "namespace of the document if it will be used to create a shortcut.";
 
+    private static final String APPSEARCH_GENERIC_DOC_PARCEL_NAME_IN_BUNDLE =
+            "appsearch_generic_doc_parcel";
+
     /**
      * Converts given document to a {@link ShortcutInfoCompat.Builder}, which can be used to
      * construct a shortcut for donation through
@@ -117,16 +122,19 @@
             throw new IllegalArgumentException(NAMESPACE_CHECK_ERROR_MESSAGE);
         }
         final String name = doc.getPropertyString(FIELD_NAME);
+        final Bundle extras = new Bundle();
+        extras.putParcelable(APPSEARCH_GENERIC_DOC_PARCEL_NAME_IN_BUNDLE, doc.getDocumentParcel());
         return new ShortcutInfoCompat.Builder(context, doc.getId())
                 .setShortLabel(!TextUtils.isEmpty(name) ? name : doc.getId())
                 .setIntent(new Intent(Intent.ACTION_VIEW, getDocumentUri(doc)))
                 .setExcludedFromSurfaces(ShortcutInfoCompat.SURFACE_LAUNCHER)
-                .setTransientExtras(doc.getBundle());
+                .setTransientExtras(extras);
     }
 
     /**
      * Extracts {@link GenericDocument} from given {@link ShortcutInfoCompat} if applicable.
      * Returns null if document cannot be found in the given shortcut.
+     *
      * @exportToFramework:hide
      */
     @Nullable
@@ -137,7 +145,21 @@
         if (extras == null) {
             return null;
         }
-        return new GenericDocument(extras);
+
+        GenericDocumentParcel genericDocParcel;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            genericDocParcel = Api33Impl.getParcelableFromBundle(extras,
+                    APPSEARCH_GENERIC_DOC_PARCEL_NAME_IN_BUNDLE, GenericDocumentParcel.class);
+        } else {
+            @SuppressWarnings("deprecation")
+            GenericDocumentParcel tmp = (GenericDocumentParcel) extras.getParcelable(
+                    APPSEARCH_GENERIC_DOC_PARCEL_NAME_IN_BUNDLE);
+            genericDocParcel = tmp;
+        }
+        if (genericDocParcel == null) {
+            return null;
+        }
+        return new GenericDocument(genericDocParcel);
     }
 
     /**
@@ -177,4 +199,21 @@
                 .path(DEFAULT_NAMESPACE + "/" + id)
                 .build();
     }
+    @RequiresApi(33)
+    static class Api33Impl {
+        private Api33Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static <T> T getParcelableFromBundle(
+                @NonNull Bundle bundle,
+                @NonNull String key,
+                @NonNull Class<T> clazz) {
+            Preconditions.checkNotNull(bundle);
+            Preconditions.checkNotNull(key);
+            Preconditions.checkNotNull(clazz);
+            return bundle.getParcelable(key, clazz);
+        }
+    }
 }
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/properties/Keyword.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/properties/Keyword.java
index 2c40844..f84153b 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/properties/Keyword.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/properties/Keyword.java
@@ -73,6 +73,6 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mAsText);
+        return Objects.hashCode(mAsText);
     }
 }
diff --git a/appsearch/appsearch-debug-view/build.gradle b/appsearch/appsearch-debug-view/build.gradle
index 5d774b7..6dfac55 100644
--- a/appsearch/appsearch-debug-view/build.gradle
+++ b/appsearch/appsearch-debug-view/build.gradle
@@ -29,6 +29,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.appsearch.debugview"
 }
 
@@ -53,5 +54,4 @@
     description = "A support library for AndroidX AppSearch that contains activities and views " +
             "for debugging an application's integration with AppSearch."
     doNotDocumentReason = "No public API"
-    metalavaK2UastEnabled = true
 }
diff --git a/appsearch/appsearch-debug-view/samples/build.gradle b/appsearch/appsearch-debug-view/samples/build.gradle
index 25b9618..3321369 100644
--- a/appsearch/appsearch-debug-view/samples/build.gradle
+++ b/appsearch/appsearch-debug-view/samples/build.gradle
@@ -29,6 +29,7 @@
 }
 
 android {
+    compileSdkVersion 35
     namespace "androidx.appsearch.debugview.samples"
 }
 
diff --git a/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java b/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java
index 5d84149..17f6a11 100644
--- a/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java
+++ b/appsearch/appsearch-debug-view/samples/src/main/java/androidx/appsearch/debugview/samples/NotesActivity.java
@@ -31,6 +31,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.appsearch.app.AppSearchEnvironmentFactory;
 import androidx.appsearch.debugview.samples.model.Note;
 import androidx.appsearch.debugview.view.AppSearchDebugActivity;
 import androidx.core.content.ContextCompat;
@@ -49,7 +50,6 @@
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Executors;
 
 /**
  * Default Activity for AppSearch Debug View Sample App
@@ -80,7 +80,8 @@
         mListView = findViewById(R.id.list_view);
         mLoadingView = findViewById(R.id.text_view);
 
-        mBackgroundExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+        mBackgroundExecutor = MoreExecutors.listeningDecorator(AppSearchEnvironmentFactory
+                .getEnvironmentInstance().createCachedThreadPoolExecutor());
 
         mNotesAppSearchManagerFuture.setFuture(NotesAppSearchManager.createNotesAppSearchManager(
                 getApplicationContext(), mBackgroundExecutor));
diff --git a/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/DebugAppSearchManager.java b/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/DebugAppSearchManager.java
index a6a79da..79a4c6b 100644
--- a/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/DebugAppSearchManager.java
+++ b/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/DebugAppSearchManager.java
@@ -165,7 +165,7 @@
         SearchSpec.Builder searchSpecBuilder = new SearchSpec.Builder()
                 .setResultCountPerPage(PAGE_SIZE)
                 .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                .addProjection(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, Collections.emptyList());
+                .addProjection(SearchSpec.SCHEMA_TYPE_WILDCARD, Collections.emptyList());
         String retrieveAllQueryString = "";
 
         if (mSearchType == AppSearchDebugActivity.SEARCH_TYPE_GLOBAL) {
diff --git a/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/view/AppSearchDebugActivity.java b/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/view/AppSearchDebugActivity.java
index 8c979f8..08320075 100644
--- a/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/view/AppSearchDebugActivity.java
+++ b/appsearch/appsearch-debug-view/src/main/java/androidx/appsearch/debugview/view/AppSearchDebugActivity.java
@@ -24,6 +24,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchEnvironmentFactory;
 import androidx.appsearch.debugview.DebugAppSearchManager;
 import androidx.appsearch.debugview.R;
 import androidx.appsearch.exceptions.AppSearchException;
@@ -36,7 +37,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.concurrent.Executors;
 
 /**
  * Debug Activity for AppSearch.
@@ -105,7 +105,8 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_appsearchdebug);
 
-        mBackgroundExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
+        mBackgroundExecutor = MoreExecutors.listeningDecorator(AppSearchEnvironmentFactory
+                .getEnvironmentInstance().createCachedThreadPoolExecutor());
         mDbName = getIntent().getExtras().getString(DB_INTENT_KEY);
         String targetPackageName =
                 getIntent().getExtras().getString(TARGET_PACKAGE_NAME_INTENT_KEY);
diff --git a/appsearch/appsearch-ktx/build.gradle b/appsearch/appsearch-ktx/build.gradle
index 4196a33..5be9af0 100644
--- a/appsearch/appsearch-ktx/build.gradle
+++ b/appsearch/appsearch-ktx/build.gradle
@@ -47,9 +47,9 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = '2021'
     description = 'AndroidX AppSearch - Kotlin Extensions'
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.appsearch.ktx"
 }
diff --git a/appsearch/appsearch-local-storage/build.gradle b/appsearch/appsearch-local-storage/build.gradle
index 7c2ab0f..364c8af 100644
--- a/appsearch/appsearch-local-storage/build.gradle
+++ b/appsearch/appsearch-local-storage/build.gradle
@@ -30,6 +30,7 @@
 }
 
 android {
+    compileSdk 35
     compileOptions {
         sourceCompatibility = JavaVersion.VERSION_1_8
         targetCompatibility = JavaVersion.VERSION_1_8
@@ -73,7 +74,7 @@
 )
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     // icing project brings in repackaged protos only (not the runtime lib)
     bundleInside(project(":icing"))
@@ -102,5 +103,4 @@
     description =
         "An implementation of AppSearchSession which uses local app storage and a local copy of " +
                 "the search library"
-    metalavaK2UastEnabled = true
 }
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index 3833958..ae89b19 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -20,6 +20,10 @@
 import static androidx.appsearch.localstorage.util.PrefixUtil.addPrefixToDocument;
 import static androidx.appsearch.localstorage.util.PrefixUtil.createPrefix;
 import static androidx.appsearch.localstorage.util.PrefixUtil.removePrefixesFromDocument;
+import static androidx.appsearch.localstorage.visibilitystore.VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME;
+import static androidx.appsearch.localstorage.visibilitystore.VisibilityStore.VISIBILITY_DATABASE_NAME;
+import static androidx.appsearch.localstorage.visibilitystore.VisibilityStore.VISIBILITY_PACKAGE_NAME;
+import static androidx.appsearch.testutil.AppSearchTestUtils.createMockVisibilityChecker;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -27,11 +31,13 @@
 
 import android.content.Context;
 
+import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchResult;
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
 import androidx.appsearch.app.JoinSpec;
 import androidx.appsearch.app.PackageIdentifier;
 import androidx.appsearch.app.SearchResult;
@@ -41,7 +47,6 @@
 import androidx.appsearch.app.SearchSuggestionSpec;
 import androidx.appsearch.app.SetSchemaResponse;
 import androidx.appsearch.app.StorageInfo;
-import androidx.appsearch.app.VisibilityDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.stats.InitializeStats;
 import androidx.appsearch.localstorage.stats.OptimizeStats;
@@ -49,6 +54,7 @@
 import androidx.appsearch.localstorage.visibilitystore.CallerAccess;
 import androidx.appsearch.localstorage.visibilitystore.VisibilityChecker;
 import androidx.appsearch.localstorage.visibilitystore.VisibilityStore;
+import androidx.appsearch.localstorage.visibilitystore.VisibilityToDocumentConverter;
 import androidx.appsearch.observer.DocumentChangeInfo;
 import androidx.appsearch.observer.ObserverSpec;
 import androidx.appsearch.observer.SchemaChangeInfo;
@@ -58,6 +64,9 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.FlakyTest;
 
+import com.google.android.appsearch.proto.AndroidVOverlayProto;
+import com.google.android.appsearch.proto.PackageIdentifierProto;
+import com.google.android.appsearch.proto.VisibilityConfigProto;
 import com.google.android.icing.proto.DebugInfoProto;
 import com.google.android.icing.proto.DebugInfoVerbosity;
 import com.google.android.icing.proto.DocumentProto;
@@ -72,7 +81,9 @@
 import com.google.android.icing.proto.StorageInfoProto;
 import com.google.android.icing.proto.StringIndexingConfig;
 import com.google.android.icing.proto.TermMatchType;
+import com.google.android.icing.protobuf.ByteString;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.MoreExecutors;
 
@@ -117,8 +128,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
     }
 
     @After
@@ -400,7 +411,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -454,7 +465,7 @@
                 mContext.getPackageName(),
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -502,7 +513,7 @@
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()),
-                initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
+                initStatsBuilder, /*visibilityChecker=*/ null, ALWAYS_OPTIMIZE);
 
         // Check recovery state
         InitializeStats initStats = initStatsBuilder.build();
@@ -539,7 +550,7 @@
                 mContext.getPackageName(),
                 "database1",
                 Collections.singletonList(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -585,7 +596,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -598,7 +609,7 @@
                 "package2",
                 "database2",
                 schema2,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -650,7 +661,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -663,7 +674,7 @@
                 "package2",
                 "database2",
                 schema2,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -727,8 +738,7 @@
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
         // We need to share across packages
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> true;
+        VisibilityChecker mockVisibilityChecker = createMockVisibilityChecker(true);
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -736,8 +746,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Insert package1 schema
         List<AppSearchSchema> personSchema =
@@ -746,7 +756,7 @@
                 "package1",
                 "database1",
                 personSchema,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -764,7 +774,7 @@
                 "package2",
                 "database2",
                 callSchema,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -777,7 +787,7 @@
                 "package3",
                 "database3",
                 textSchema,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -903,8 +913,7 @@
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
         // We need to share across packages
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> true;
+        VisibilityChecker mockVisibilityChecker = createMockVisibilityChecker(true);
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -912,8 +921,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         AppSearchSchema.StringPropertyConfig personField =
                 new AppSearchSchema.StringPropertyConfig.Builder("personId")
@@ -931,7 +940,7 @@
                 "package1",
                 "database1",
                 personAndCallSchema,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -945,7 +954,7 @@
                 "package2",
                 "database2",
                 callSchema,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1107,7 +1116,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1166,7 +1175,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1215,7 +1224,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1273,7 +1282,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1334,7 +1343,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1392,7 +1401,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1423,7 +1432,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1476,7 +1485,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1539,7 +1548,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1595,7 +1604,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1661,7 +1670,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1721,7 +1730,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1765,7 +1774,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1828,7 +1837,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1891,7 +1900,7 @@
                 "package1",
                 "database1",
                 schema1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1979,7 +1988,7 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -1989,7 +1998,9 @@
         SchemaProto expectedProto = SchemaProto.newBuilder()
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("package$database1/Email").setVersion(0))
+                                .setSchemaType("package$database1/Email")
+                                .setDescription("")
+                                .setVersion(0))
                 .build();
 
         List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
@@ -2016,7 +2027,7 @@
                 "package",
                 "database1",
                 oldSchemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2031,7 +2042,7 @@
                 "package",
                 "database1",
                 newSchemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2055,7 +2066,7 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2065,9 +2076,14 @@
         SchemaProto expectedProto = SchemaProto.newBuilder()
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("package$database1/Email").setVersion(0))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
-                        "package$database1/Document").setVersion(0))
+                                .setSchemaType("package$database1/Email")
+                                .setDescription("")
+                                .setVersion(0))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder()
+                                .setSchemaType("package$database1/Document")
+                                .setDescription("")
+                                .setVersion(0))
                 .build();
 
         // Check both schema Email and Document saved correctly.
@@ -2083,7 +2099,7 @@
                         "package",
                         "database1",
                         finalSchemas,
-                        /*visibilityDocuments=*/ Collections.emptyList(),
+                        /*visibilityConfigs=*/ Collections.emptyList(),
                         /*forceOverride=*/ false,
                         /*version=*/ 0,
                         /* setSchemaStatsBuilder= */ null);
@@ -2098,7 +2114,7 @@
                 "package",
                 "database1",
                 finalSchemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2108,7 +2124,9 @@
         expectedProto = SchemaProto.newBuilder()
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("package$database1/Email").setVersion(0))
+                                .setSchemaType("package$database1/Email")
+                                .setDescription("")
+                                .setVersion(0))
                 .build();
 
         expectedTypes = new ArrayList<>();
@@ -2133,7 +2151,7 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2142,7 +2160,7 @@
                 "package",
                 "database2",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2152,14 +2170,24 @@
         SchemaProto expectedProto = SchemaProto.newBuilder()
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("package$database1/Email").setVersion(0))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
-                        "package$database1/Document").setVersion(0))
+                                .setSchemaType("package$database1/Email")
+                                .setDescription("")
+                                .setVersion(0))
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("package$database2/Email").setVersion(0))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
-                        "package$database2/Document").setVersion(0))
+                                .setSchemaType("package$database1/Document")
+                                .setDescription("")
+                                .setVersion(0))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder()
+                                .setSchemaType("package$database2/Email")
+                                .setDescription("")
+                                .setVersion(0))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder()
+                                .setSchemaType("package$database2/Document")
+                                .setDescription("")
+                                .setVersion(0))
                 .build();
 
         // Check Email and Document is saved in database 1 and 2 correctly.
@@ -2175,7 +2203,7 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2186,12 +2214,19 @@
         expectedProto = SchemaProto.newBuilder()
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("package$database1/Email").setVersion(0))
+                                .setSchemaType("package$database1/Email")
+                                .setDescription("")
+                                .setVersion(0))
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("package$database2/Email").setVersion(0))
-                .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
-                        "package$database2/Document").setVersion(0))
+                                .setSchemaType("package$database2/Email")
+                                .setDescription("")
+                                .setVersion(0))
+                .addTypes(
+                        SchemaTypeConfigProto.newBuilder()
+                                .setSchemaType("package$database2/Document")
+                                .setDescription("")
+                                .setVersion(0))
                 .build();
 
         // Check nothing changed in database2.
@@ -2215,7 +2250,7 @@
                 "package",
                 "database",
                 schema,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2265,8 +2300,8 @@
             existingPackages.add(PrefixUtil.getPackageName(existingSchemas.get(i).getSchemaType()));
         }
 
-        // Create VisibilityDocument
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("schema")
+        // Create VisibilityConfig
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("schema")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
@@ -2278,7 +2313,7 @@
                 "packageA",
                 "database",
                 schema,
-                /*visibilityDocuments=*/ ImmutableList.of(visibilityDocument),
+                /*visibilityConfigs=*/ ImmutableList.of(visibilityConfig),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2287,7 +2322,7 @@
                 "packageB",
                 "database",
                 schema,
-                /*visibilityDocuments=*/ ImmutableList.of(visibilityDocument),
+                /*visibilityConfigs=*/ ImmutableList.of(visibilityConfig),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2297,10 +2332,14 @@
         SchemaProto expectedProto = SchemaProto.newBuilder()
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("packageA$database/schema").setVersion(0))
+                                .setSchemaType("packageA$database/schema")
+                                .setDescription("")
+                                .setVersion(0))
                 .addTypes(
                         SchemaTypeConfigProto.newBuilder()
-                                .setSchemaType("packageB$database/schema").setVersion(0))
+                                .setSchemaType("packageB$database/schema")
+                                .setDescription("")
+                                .setVersion(0))
                 .build();
         List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
         expectedTypes.addAll(existingSchemas);
@@ -2309,22 +2348,22 @@
                 .containsExactlyElementsIn(expectedTypes);
 
         // Verify these two visibility documents are stored in AppSearch.
-        VisibilityDocument expectedVisibilityDocumentA =
-                new VisibilityDocument.Builder("packageA$database/schema")
+        InternalVisibilityConfig expectedVisibilityConfigA =
+                new InternalVisibilityConfig.Builder("packageA$database/schema")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                         .build();
-        VisibilityDocument expectedVisibilityDocumentB =
-                new VisibilityDocument.Builder("packageB$database/schema")
+        InternalVisibilityConfig expectedVisibilityConfigB =
+                new InternalVisibilityConfig.Builder("packageB$database/schema")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                         .build();
         assertThat(mAppSearchImpl.mVisibilityStoreLocked
                 .getVisibility("packageA$database/schema"))
-                .isEqualTo(expectedVisibilityDocumentA);
+                .isEqualTo(expectedVisibilityConfigA);
         assertThat(mAppSearchImpl.mVisibilityStoreLocked
                 .getVisibility("packageB$database/schema"))
-                .isEqualTo(expectedVisibilityDocumentB);
+                .isEqualTo(expectedVisibilityConfigB);
 
         // Prune packages
         mAppSearchImpl.prunePackageData(existingPackages);
@@ -2353,7 +2392,7 @@
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package1", "database1",
                 Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2366,7 +2405,7 @@
         internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package1", "database2",
                 Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2379,7 +2418,7 @@
         internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package2", "database1",
                 Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2401,7 +2440,7 @@
                 "package1",
                 "database1",
                 schemas1,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2410,7 +2449,7 @@
                 "package1",
                 "database2",
                 schemas2,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2419,7 +2458,7 @@
                 "package2",
                 "database1",
                 schemas3,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2429,7 +2468,8 @@
                 "package1$database2/type2",
                 "package2$database1/type3",
                 "VS#Pkg$VS#Db/VisibilityType",  // plus the stored Visibility schema
-                "VS#Pkg$VS#Db/VisibilityPermissionType");
+                "VS#Pkg$VS#Db/VisibilityPermissionType",
+                "VS#Pkg$VS#AndroidVDb/AndroidVOverlayType");
     }
 
     @FlakyTest(bugId = 204186664)
@@ -2442,7 +2482,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2541,7 +2581,7 @@
                 "package1",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2564,7 +2604,7 @@
                 "package1",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2585,7 +2625,7 @@
                 "package2",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2643,7 +2683,7 @@
                 "package1",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2666,7 +2706,7 @@
                 "package1",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2688,7 +2728,7 @@
                 "package1",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2697,7 +2737,7 @@
                 "package1",
                 "database2",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2755,7 +2795,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2768,7 +2808,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null));
@@ -2840,7 +2880,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2870,8 +2910,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         getResult = appSearchImpl2.getDocument("package", "database", "namespace1",
                 "id1",
                 Collections.emptyMap());
@@ -2887,7 +2927,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -2942,8 +2982,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         assertThrows(AppSearchException.class, () -> appSearchImpl2.getDocument("package",
                 "database",
                 "namespace1",
@@ -2964,7 +3004,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3021,8 +3061,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         assertThrows(AppSearchException.class, () -> appSearchImpl2.getDocument("package",
                 "database",
                 "namespace1",
@@ -3043,7 +3083,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3077,7 +3117,7 @@
                 .isEqualTo(2);
         assertThat(
                 storageInfo.getSchemaStoreStorageInfo().getNumSchemaTypes())
-                .isEqualTo(3); // +2 for VisibilitySchema
+                .isEqualTo(4); // +2 for VisibilitySchema, +1 for VisibilityOverlay
     }
 
     @Test
@@ -3088,7 +3128,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3122,7 +3162,7 @@
                 debugInfo.getDocumentInfo().getDocumentStorageInfo().getNumAliveDocuments())
                 .isEqualTo(2);
         assertThat(debugInfo.getSchemaInfo().getSchema().getTypesList())
-                .hasSize(3); // +2 for VisibilitySchema
+                .hasSize(4); // +2 for VisibilitySchema, +1 for VisibilityOverlay
     }
 
     @Test
@@ -3146,8 +3186,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Insert schema
         List<AppSearchSchema> schemas =
@@ -3156,7 +3196,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3225,8 +3265,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Insert schema
         List<AppSearchSchema> schemas =
@@ -3235,7 +3275,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3282,8 +3322,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Make sure the limit is maintained
         e = assertThrows(AppSearchException.class, () ->
@@ -3319,8 +3359,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Insert schema
         List<AppSearchSchema> schemas =
@@ -3329,7 +3369,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3432,8 +3472,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Insert schema
         List<AppSearchSchema> schemas =
@@ -3442,7 +3482,7 @@
                 "package1",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3451,7 +3491,7 @@
                 "package1",
                 "database2",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3460,7 +3500,7 @@
                 "package2",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3469,7 +3509,7 @@
                 "package2",
                 "database2",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3528,8 +3568,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // package1 should still be out of space
         e = assertThrows(AppSearchException.class, () ->
@@ -3585,8 +3625,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Insert schema
         List<AppSearchSchema> schemas = Collections.singletonList(
@@ -3602,7 +3642,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3738,8 +3778,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Insert schema
         List<AppSearchSchema> schemas = Collections.singletonList(
@@ -3751,7 +3791,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3821,8 +3861,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Insert schema
         List<AppSearchSchema> schemas = Collections.singletonList(
@@ -3834,7 +3874,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -3878,8 +3918,8 @@
                         return Integer.MAX_VALUE;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         // Index id2. This should pass but only because we check for replacements.
         mAppSearchImpl.putDocument(
@@ -3924,8 +3964,8 @@
                         return 2;
                     }
                 }, new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         AppSearchException e = assertThrows(AppSearchException.class, () ->
                 mAppSearchImpl.searchSuggestion(
@@ -3950,7 +3990,7 @@
                 mContext.getPackageName(),
                 "database1",
                 /*schemas=*/ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/false,
                 /*version=*/0,
                 /*setSchemaStatsBuilder=*/null);
@@ -4020,8 +4060,7 @@
         // Create a new mAppSearchImpl with a mock Visibility Checker
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> false;
+        VisibilityChecker mockVisibilityChecker = createMockVisibilityChecker(false);
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -4029,14 +4068,14 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4073,8 +4112,7 @@
         // Create a new mAppSearchImpl with a mock Visibility Checker
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> true;
+        VisibilityChecker mockVisibilityChecker = createMockVisibilityChecker(true);
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -4082,14 +4120,14 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4124,8 +4162,7 @@
         // Create a new mAppSearchImpl with a mock Visibility Checker
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> true;
+        VisibilityChecker mockVisibilityChecker = createMockVisibilityChecker(true);
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -4133,14 +4170,14 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4176,9 +4213,20 @@
         // Create a new mAppSearchImpl with a mock Visibility Checker
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) ->
-                        callerAccess.getCallingPackageName().equals("visiblePackage");
+        VisibilityChecker mockVisibilityChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                return callerAccess.getCallingPackageName().equals("visiblePackage");
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
+
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -4186,14 +4234,14 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4237,7 +4285,7 @@
 
     @Test
     public void testSetVisibility() throws Exception {
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
@@ -4249,7 +4297,7 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ ImmutableList.of(visibilityDocument),
+                /*visibilityConfigs=*/ ImmutableList.of(visibilityConfig),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4257,27 +4305,31 @@
         String prefix = PrefixUtil.createPrefix("package", "database1");
 
         // assert the visibility document is saved.
-        VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
-                .setNotDisplayedBySystem(true)
-                .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .build();
-        assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
+        InternalVisibilityConfig expectedDocument =
+                new InternalVisibilityConfig.Builder(prefix + "Email")
+                        .setNotDisplayedBySystem(true)
+                        .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                        .build();
+        assertThat(mAppSearchImpl.mVisibilityStoreLocked
+                .getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =
-                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        // Verify the InternalVisibilityConfig is saved to AppSearchImpl.
+        InternalVisibilityConfig actualDocument =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        mAppSearchImpl.getDocument(
+                                VISIBILITY_PACKAGE_NAME,
+                                VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix + "Email",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                        /*androidVOverlayDocument=*/null);
         assertThat(actualDocument).isEqualTo(expectedDocument);
     }
 
     @Test
     public void testSetVisibility_existingVisibilitySettingRetains() throws Exception {
         // Create Visibility Document for Email1
-        VisibilityDocument visibilityDocument1 = new VisibilityDocument.Builder("Email1")
+        InternalVisibilityConfig visibilityConfig1 = new InternalVisibilityConfig.Builder("Email1")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
@@ -4289,7 +4341,7 @@
                 "package1",
                 "database",
                 schemas1,
-                /*visibilityDocuments=*/ ImmutableList.of(visibilityDocument1),
+                /*visibilityConfigs=*/ ImmutableList.of(visibilityConfig1),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4297,24 +4349,29 @@
         String prefix1 = PrefixUtil.createPrefix("package1", "database");
 
         // assert the visibility document is saved.
-        VisibilityDocument expectedDocument1 = new VisibilityDocument.Builder(prefix1 + "Email1")
-                .setNotDisplayedBySystem(true)
-                .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .build();
-        assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix1 + "Email1"))
+        InternalVisibilityConfig expectedDocument1 =
+                new InternalVisibilityConfig.Builder(prefix1 + "Email1")
+                        .setNotDisplayedBySystem(true)
+                        .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                        .build();
+        assertThat(mAppSearchImpl.mVisibilityStoreLocked
+                .getVisibility(prefix1 + "Email1"))
                 .isEqualTo(expectedDocument1);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument1 =
-                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ prefix1 + "Email1",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        // Verify the InternalVisibilityConfig is saved to AppSearchImpl.
+        InternalVisibilityConfig actualDocument1 =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        mAppSearchImpl.getDocument(
+                                VISIBILITY_PACKAGE_NAME,
+                                VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix1 + "Email1",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                        /*androidVOverlayDocument=*/null);
+
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
 
         // Create Visibility Document for Email2
-        VisibilityDocument visibilityDocument2 = new VisibilityDocument.Builder("Email2")
+        InternalVisibilityConfig visibilityConfig2 = new InternalVisibilityConfig.Builder("Email2")
                 .setNotDisplayedBySystem(false)
                 .addVisibleToPackage(new PackageIdentifier("pkgFoo", new byte[32]))
                 .build();
@@ -4326,7 +4383,7 @@
                 "package2",
                 "database",
                 schemas2,
-                /*visibilityDocuments=*/ ImmutableList.of(visibilityDocument2),
+                /*visibilityConfigs=*/ ImmutableList.of(visibilityConfig2),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4334,39 +4391,46 @@
         String prefix2 = PrefixUtil.createPrefix("package2", "database");
 
         // assert the visibility document is saved.
-        VisibilityDocument expectedDocument2 = new VisibilityDocument.Builder(prefix2 + "Email2")
-                .setNotDisplayedBySystem(false)
-                .addVisibleToPackage(new PackageIdentifier("pkgFoo", new byte[32]))
-                .build();
-        assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix2 + "Email2"))
+        InternalVisibilityConfig expectedDocument2 =
+                new InternalVisibilityConfig.Builder(prefix2 + "Email2")
+                        .setNotDisplayedBySystem(false)
+                        .addVisibleToPackage(new PackageIdentifier("pkgFoo", new byte[32]))
+                        .build();
+        assertThat(mAppSearchImpl.mVisibilityStoreLocked
+                .getVisibility(prefix2 + "Email2"))
                 .isEqualTo(expectedDocument2);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument2 =  new VisibilityDocument.Builder(
-                mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ prefix2 + "Email2",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        // Verify the InternalVisibilityConfig is saved to AppSearchImpl.
+        InternalVisibilityConfig actualDocument2 =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        mAppSearchImpl.getDocument(
+                                VISIBILITY_PACKAGE_NAME,
+                                VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix2 + "Email2",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                        /*androidVOverlayDocument=*/null);
         assertThat(actualDocument2).isEqualTo(expectedDocument2);
 
         // Check the existing visibility document retains.
-        assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix1 + "Email1"))
+        assertThat(mAppSearchImpl.mVisibilityStoreLocked
+                .getVisibility(prefix1 + "Email1"))
                 .isEqualTo(expectedDocument1);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        actualDocument1 =  new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ prefix1 + "Email1",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        actualDocument1 = VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                mAppSearchImpl.getDocument(
+                        VISIBILITY_PACKAGE_NAME,
+                        VISIBILITY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                        /*id=*/ prefix1 + "Email1",
+                        /*typePropertyPaths=*/ Collections.emptyMap()),
+                /*androidVOverlayDocument=*/null);
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
     }
 
     @Test
     public void testSetVisibility_removeVisibilitySettings() throws Exception {
         // Create a non-all-default visibility document
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
@@ -4379,26 +4443,29 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ ImmutableList.of(visibilityDocument),
+                /*visibilityConfigs=*/ ImmutableList.of(visibilityConfig),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
         assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
         String prefix = PrefixUtil.createPrefix("package", "database1");
-        VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
-                .setNotDisplayedBySystem(true)
-                .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .build();
-        assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
+        InternalVisibilityConfig expectedDocument =
+                new InternalVisibilityConfig.Builder(prefix + "Email")
+                        .setNotDisplayedBySystem(true)
+                        .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                        .build();
+        assertThat(mAppSearchImpl.mVisibilityStoreLocked
+                .getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =
-                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        InternalVisibilityConfig actualDocument =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        mAppSearchImpl.getDocument(
+                                VISIBILITY_PACKAGE_NAME,
+                                VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix + "Email",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                        /*androidVOverlayDocument=*/null);
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // Set schema Email and its all-default visibility document to AppSearch database1
@@ -4406,7 +4473,7 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ ImmutableList.of(),
+                /*visibilityConfigs=*/ ImmutableList.of(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4414,12 +4481,12 @@
         // All-default visibility document won't be saved in AppSearch.
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
                 .isNull();
-        // Verify the VisibilityDocument is removed from AppSearchImpl.
+        // Verify the InternalVisibilityConfig is removed from AppSearchImpl.
         AppSearchException e = assertThrows(AppSearchException.class,
                 () -> mAppSearchImpl.getDocument(
-                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                        VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
+                        VISIBILITY_PACKAGE_NAME,
+                        VISIBILITY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                         /*id=*/ prefix + "Email",
                         /*typePropertyPaths=*/ Collections.emptyMap()));
         assertThat(e).hasMessageThat().contains(
@@ -4429,7 +4496,7 @@
     @Test
     public void testRemoveVisibility_noRemainingSettings() throws Exception {
         // Create a non-all-default visibility document
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
@@ -4442,25 +4509,29 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ ImmutableList.of(visibilityDocument),
+                /*visibilityConfigs=*/ ImmutableList.of(visibilityConfig),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
         String prefix = PrefixUtil.createPrefix("package", "database1");
-        VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
-                .setNotDisplayedBySystem(true)
-                .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .build();
-        assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
+        InternalVisibilityConfig expectedDocument =
+                new InternalVisibilityConfig.Builder(prefix + "Email")
+                        .setNotDisplayedBySystem(true)
+                        .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                        .build();
+        assertThat(mAppSearchImpl.mVisibilityStoreLocked
+                .getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =
-                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        // Verify the InternalVisibilityConfig is saved to AppSearchImpl.
+        InternalVisibilityConfig actualDocument =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        mAppSearchImpl.getDocument(
+                                VISIBILITY_PACKAGE_NAME,
+                                VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix + "Email",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                        /*androidVOverlayDocument=*/null);
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // remove the schema and visibility setting from AppSearch
@@ -4468,7 +4539,7 @@
                 "package",
                 "database1",
                 /*schemas=*/ new ArrayList<>(),
-                /*visibilityDocuments=*/ ImmutableList.of(),
+                /*visibilityConfigs=*/ ImmutableList.of(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4478,7 +4549,7 @@
                 "package",
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ ImmutableList.of(),
+                /*visibilityConfigs=*/ ImmutableList.of(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /* setSchemaStatsBuilder= */ null);
@@ -4488,9 +4559,9 @@
         // Verify there is no visibility setting for the schema.
         AppSearchException e = assertThrows(AppSearchException.class,
                 () -> mAppSearchImpl.getDocument(
-                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                        VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
+                        VISIBILITY_PACKAGE_NAME,
+                        VISIBILITY_DATABASE_NAME,
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                         /*id=*/ prefix + "Email",
                         /*typePropertyPaths=*/ Collections.emptyMap()));
         assertThat(e).hasMessageThat().contains(
@@ -4500,7 +4571,7 @@
     @Test
     public void testCloseAndReopen_visibilityInfoRetains() throws Exception {
         // set Schema and visibility to AppSearch
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
@@ -4510,7 +4581,7 @@
                 "packageName",
                 "databaseName",
                 schemas,
-                ImmutableList.of(visibilityDocument),
+                ImmutableList.of(visibilityConfig),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -4525,25 +4596,29 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         String prefix = PrefixUtil.createPrefix("packageName", "databaseName");
-        VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
-                .setNotDisplayedBySystem(true)
-                .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .build();
+        InternalVisibilityConfig expectedDocument =
+                new InternalVisibilityConfig.Builder(prefix + "Email")
+                        .setNotDisplayedBySystem(true)
+                        .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                        .build();
 
-        assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
+        assertThat(mAppSearchImpl.mVisibilityStoreLocked
+                .getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =
-                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        // Verify the InternalVisibilityConfig is saved to AppSearchImpl.
+        InternalVisibilityConfig actualDocument =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        mAppSearchImpl.getDocument(
+                                VISIBILITY_PACKAGE_NAME,
+                                VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix + "Email",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                        /*androidVOverlayDocument=*/null);
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // remove schema and visibility document
@@ -4566,16 +4641,16 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email")).isNull();
-        // Verify the VisibilityDocument is removed from AppSearchImpl.
+        // Verify the InternalVisibilityConfig is removed from AppSearchImpl.
         AppSearchException e = assertThrows(AppSearchException.class,
                 () -> mAppSearchImpl.getDocument(
-                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                        VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
+                        VISIBILITY_PACKAGE_NAME,
+                        VISIBILITY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                         /*id=*/ prefix + "Email",
                         /*typePropertyPaths=*/ Collections.emptyMap()));
         assertThat(e).hasMessageThat().contains(
@@ -4590,8 +4665,7 @@
         // Create a new mAppSearchImpl with a mock Visibility Checker
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> true;
+        VisibilityChecker mockVisibilityChecker = createMockVisibilityChecker(true);
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -4599,16 +4673,16 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add a schema type that is not displayed by the system
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ImmutableList.of(
-                        new VisibilityDocument.Builder("Type")
+                /*visibilityConfigs=*/ImmutableList.of(
+                        new InternalVisibilityConfig.Builder("Type")
                                 .setNotDisplayedBySystem(true).build()),
                 /*forceOverride=*/false,
                 /*version=*/0,
@@ -4631,7 +4705,7 @@
                 "package",
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("Type").build()),
-                /*visibilityDocuments=*/ImmutableList.of(),
+                /*visibilityConfigs=*/ImmutableList.of(),
                 /*forceOverride=*/false,
                 /*version=*/0,
                 /*setSchemaStatsBuilder=*/null);
@@ -4655,7 +4729,7 @@
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ImmutableList.of(),
+                /*visibilityConfigs=*/ImmutableList.of(),
                 /*forceOverride=*/false,
                 /*version=*/1,
                 /*setSchemaStatsBuilder=*/null);
@@ -4687,9 +4761,20 @@
         // Create a new mAppSearchImpl with a mock Visibility Checker
         mAppSearchImpl.close();
         File tempFolder = mTemporaryFolder.newFolder();
-        VisibilityChecker mockVisibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore)
-                        -> prefixedSchema.endsWith("VisibleType");
+        VisibilityChecker mockVisibilityChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                return prefixedSchema.endsWith("VisibleType");
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
+
         mAppSearchImpl = AppSearchImpl.create(
                 tempFolder,
                 new AppSearchConfigImpl(
@@ -4697,19 +4782,19 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                mockVisibilityChecker);
+                mockVisibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add two schema types that are not displayed by the system.
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package",
                 "database",
                 schemas,
-                /*visibilityDocuments=*/ImmutableList.of(
-                        new VisibilityDocument.Builder("VisibleType")
+                /*visibilityConfigs=*/ImmutableList.of(
+                        new InternalVisibilityConfig.Builder("VisibleType")
                                 .setNotDisplayedBySystem(true)
                                 .build(),
-                        new VisibilityDocument.Builder("PrivateType")
+                        new InternalVisibilityConfig.Builder("PrivateType")
                                 .setNotDisplayedBySystem(true)
                                 .build()),
                 /*forceOverride=*/false,
@@ -4728,13 +4813,258 @@
     }
 
     @Test
+    public void testGetSchema_global_publicAcl() throws Exception {
+        List<AppSearchSchema> schemas = ImmutableList.of(
+                new AppSearchSchema.Builder("PublicTypeA").build(),
+                new AppSearchSchema.Builder("PublicTypeB").build(),
+                new AppSearchSchema.Builder("PublicTypeC").build());
+
+        PackageIdentifier pkgA = new PackageIdentifier("A", new byte[32]);
+        PackageIdentifier pkgB = new PackageIdentifier("B", new byte[32]);
+        PackageIdentifier pkgC = new PackageIdentifier("C", new byte[32]);
+
+        // Create a new mAppSearchImpl with a mock Visibility Checker
+        mAppSearchImpl.close();
+        File tempFolder = mTemporaryFolder.newFolder();
+
+        // Package A is visible to package B & C, package B is visible to package C (based on
+        // canPackageQuery, which we are mocking).
+        Map<String, Set<String>> packageCanSee = ImmutableMap.of(
+                "A", ImmutableSet.of("A"),
+                "B", ImmutableSet.of("A", "B"),
+                "C", ImmutableSet.of("A", "B", "C"));
+        final VisibilityChecker publicAclMockChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                InternalVisibilityConfig param = visibilityStore.getVisibility(prefixedSchema);
+                return packageCanSee.get(callerAccess.getCallingPackageName())
+                        .contains(param.getVisibilityConfig().getPubliclyVisibleTargetPackage()
+                                .getPackageName());
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
+
+        mAppSearchImpl = AppSearchImpl.create(
+                tempFolder,
+                new AppSearchConfigImpl(
+                        new UnlimitedLimitConfig(),
+                        new LocalStorageIcingOptionsConfig()
+                ),
+                /*initStatsBuilder=*/ null,
+                publicAclMockChecker, ALWAYS_OPTIMIZE
+        );
+
+        List<InternalVisibilityConfig> visibilityConfigs = ImmutableList.of(
+                new InternalVisibilityConfig.Builder("PublicTypeA")
+                        .setPubliclyVisibleTargetPackage(pkgA).build(),
+                new InternalVisibilityConfig.Builder("PublicTypeB")
+                        .setPubliclyVisibleTargetPackage(pkgB).build(),
+                new InternalVisibilityConfig.Builder("PublicTypeC")
+                        .setPubliclyVisibleTargetPackage(pkgC).build());
+
+        // Add the three schema types, each with their own publicly visible target package.
+        InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                schemas,
+                visibilityConfigs,
+                /*forceOverride=*/true,
+                /*version=*/1,
+                /*setSchemaStatsBuilder=*/null);
+        assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+        // Verify access to schemas based on calling package
+        GetSchemaResponse getResponse = mAppSearchImpl.getSchema(
+                "package",
+                "database",
+                new CallerAccess(pkgA.getPackageName()));
+        assertThat(getResponse.getSchemas()).containsExactly(schemas.get(0));
+        assertThat(getResponse.getPubliclyVisibleSchemas()).containsKey("PublicTypeA");
+
+        getResponse = mAppSearchImpl.getSchema(
+                "package",
+                "database",
+                new CallerAccess(pkgB.getPackageName()));
+        assertThat(getResponse.getSchemas()).containsExactly(schemas.get(0), schemas.get(1));
+        assertThat(getResponse.getPubliclyVisibleSchemas()).containsKey("PublicTypeA");
+        assertThat(getResponse.getPubliclyVisibleSchemas()).containsKey("PublicTypeB");
+
+        getResponse = mAppSearchImpl.getSchema(
+                "package",
+                "database",
+                new CallerAccess(pkgC.getPackageName()));
+        assertThat(getResponse.getSchemas()).containsExactlyElementsIn(schemas);
+        assertThat(getResponse.getPubliclyVisibleSchemas()).containsKey("PublicTypeA");
+        assertThat(getResponse.getPubliclyVisibleSchemas()).containsKey("PublicTypeB");
+        assertThat(getResponse.getPubliclyVisibleSchemas()).containsKey("PublicTypeC");
+    }
+
+    @Test
+    public void testGetSchema_global_publicAcl_removal() throws Exception {
+        // This test to ensure the proper documents are created through setSchema, then removed
+        // when setSchema is called again
+        List<AppSearchSchema> schemas = ImmutableList.of(
+                new AppSearchSchema.Builder("PublicTypeA").build(),
+                new AppSearchSchema.Builder("PublicTypeB").build(),
+                new AppSearchSchema.Builder("PublicTypeC").build());
+
+        PackageIdentifier pkgA = new PackageIdentifier("A", new byte[32]);
+        PackageIdentifier pkgB = new PackageIdentifier("B", new byte[32]);
+        PackageIdentifier pkgC = new PackageIdentifier("C", new byte[32]);
+
+        // Create a new mAppSearchImpl with a mock Visibility Checker
+        mAppSearchImpl.close();
+        File tempFolder = mTemporaryFolder.newFolder();
+
+        // Package A is visible to package B & C, package B is visible to package C (based on
+        // canPackageQuery, which we are mocking).
+        Map<String, Set<String>> packageCanSee = ImmutableMap.of(
+                "A", ImmutableSet.of("A"),
+                "B", ImmutableSet.of("A", "B"),
+                "C", ImmutableSet.of("A", "B", "C"));
+        final VisibilityChecker publicAclMockChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                InternalVisibilityConfig param = visibilityStore.getVisibility(prefixedSchema);
+                return packageCanSee.get(callerAccess.getCallingPackageName())
+                        .contains(param.getVisibilityConfig()
+                                .getPubliclyVisibleTargetPackage().getPackageName());
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
+
+        mAppSearchImpl = AppSearchImpl.create(
+                tempFolder,
+                new AppSearchConfigImpl(
+                        new UnlimitedLimitConfig(),
+                        new LocalStorageIcingOptionsConfig()
+                ),
+                /*initStatsBuilder=*/ null,
+                publicAclMockChecker, ALWAYS_OPTIMIZE
+        );
+
+        List<InternalVisibilityConfig> visibilityConfigs = ImmutableList.of(
+                new InternalVisibilityConfig.Builder("PublicTypeA")
+                        .setPubliclyVisibleTargetPackage(pkgA).build(),
+                new InternalVisibilityConfig.Builder("PublicTypeB")
+                        .setPubliclyVisibleTargetPackage(pkgB).build(),
+                new InternalVisibilityConfig.Builder("PublicTypeC")
+                        .setPubliclyVisibleTargetPackage(pkgC).build());
+
+        // Add two schema types that are not displayed by the system.
+        InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                schemas,
+                visibilityConfigs,
+                /*forceOverride=*/true,
+                /*version=*/1,
+                /*setSchemaStatsBuilder=*/null);
+        assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+        // Now check for documents
+        GenericDocument visibilityOverlayA = mAppSearchImpl.getDocument(
+                VISIBILITY_PACKAGE_NAME,
+                ANDROID_V_OVERLAY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                "package$database/PublicTypeA",
+                Collections.emptyMap());
+        GenericDocument visibilityOverlayB = mAppSearchImpl.getDocument(
+                VISIBILITY_PACKAGE_NAME,
+                ANDROID_V_OVERLAY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                "package$database/PublicTypeB",
+                Collections.emptyMap());
+        GenericDocument visibilityOverlayC = mAppSearchImpl.getDocument(
+                VISIBILITY_PACKAGE_NAME,
+                ANDROID_V_OVERLAY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                "package$database/PublicTypeC",
+                Collections.emptyMap());
+
+        AndroidVOverlayProto overlayProtoA = AndroidVOverlayProto.newBuilder()
+                .setVisibilityConfig(VisibilityConfigProto.newBuilder()
+                        .setPubliclyVisibleTargetPackage(PackageIdentifierProto.newBuilder()
+                                .setPackageName("A")
+                                .setPackageSha256Cert(ByteString.copyFrom(new byte[32])).build())
+                        .build())
+                .build();
+        AndroidVOverlayProto overlayProtoB = AndroidVOverlayProto.newBuilder()
+                .setVisibilityConfig(VisibilityConfigProto.newBuilder()
+                        .setPubliclyVisibleTargetPackage(PackageIdentifierProto.newBuilder()
+                                .setPackageName("B")
+                                .setPackageSha256Cert(ByteString.copyFrom(new byte[32])).build())
+                        .build())
+                .build();
+        AndroidVOverlayProto overlayProtoC = AndroidVOverlayProto.newBuilder()
+                .setVisibilityConfig(VisibilityConfigProto.newBuilder()
+                        .setPubliclyVisibleTargetPackage(PackageIdentifierProto.newBuilder()
+                                .setPackageName("C")
+                                .setPackageSha256Cert(ByteString.copyFrom(new byte[32])).build())
+                        .build())
+                .build();
+
+        assertThat(visibilityOverlayA.getPropertyBytes("visibilityProtoSerializeProperty"))
+                .isEqualTo(overlayProtoA.toByteArray());
+        assertThat(visibilityOverlayB.getPropertyBytes("visibilityProtoSerializeProperty"))
+                .isEqualTo(overlayProtoB.toByteArray());
+        assertThat(visibilityOverlayC.getPropertyBytes("visibilityProtoSerializeProperty"))
+                .isEqualTo(overlayProtoC.toByteArray());
+
+        // now undo the "public" setting
+        visibilityConfigs = ImmutableList.of(
+                new InternalVisibilityConfig.Builder("PublicTypeA").build(),
+                new InternalVisibilityConfig.Builder("PublicTypeB").build(),
+                new InternalVisibilityConfig.Builder("PublicTypeC").build());
+
+        InternalSetSchemaResponse internalSetSchemaResponseRemoved = mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                schemas,
+                visibilityConfigs,
+                /*forceOverride=*/true,
+                /*version=*/1,
+                /*setSchemaStatsBuilder=*/null);
+        assertThat(internalSetSchemaResponseRemoved.isSuccess()).isTrue();
+
+        // Now check for documents again
+        Exception e = assertThrows(AppSearchException.class, () -> mAppSearchImpl.getDocument(
+                VISIBILITY_PACKAGE_NAME, VISIBILITY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                "package$database/PublicTypeA", Collections.emptyMap()));
+        assertThat(e.getMessage()).endsWith("not found.");
+        e = assertThrows(AppSearchException.class, () -> mAppSearchImpl.getDocument(
+                VISIBILITY_PACKAGE_NAME, VISIBILITY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                "package$database/PublicTypeB", Collections.emptyMap()));
+        assertThat(e.getMessage()).endsWith("not found.");
+        e = assertThrows(AppSearchException.class, () -> mAppSearchImpl.getDocument(
+                VISIBILITY_PACKAGE_NAME, VISIBILITY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                "package$database/PublicTypeC", Collections.emptyMap()));
+        assertThat(e.getMessage()).endsWith("not found.");
+    }
+
+    @Test
     public void testDispatchObserver_samePackage_noVisStore_accept() throws Exception {
         // Add a schema type
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -4776,8 +5106,7 @@
     @Test
     public void testDispatchObserver_samePackage_withVisStore_accept() throws Exception {
         // Make a visibility checker that rejects everything
-        final VisibilityChecker rejectChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> false;
+        final VisibilityChecker rejectChecker = createMockVisibilityChecker(false);
         mAppSearchImpl.close();
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir,
@@ -4786,15 +5115,15 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/null,
-                ALWAYS_OPTIMIZE,
-                rejectChecker);
+                rejectChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add a schema type
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -4840,7 +5169,7 @@
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -4850,7 +5179,7 @@
         TestObserverCallback observer = new TestObserverCallback();
         mAppSearchImpl.registerObserverCallback(
                 new CallerAccess(/*callingPackageName=*/
-                    "com.fake.Listening.package"),
+                        "com.fake.Listening.package"),
                 /*targetPackageName=*/mContext.getPackageName(),
                 new ObserverSpec.Builder().build(),
                 MoreExecutors.directExecutor(),
@@ -4879,9 +5208,19 @@
         final String fakeListeningPackage = "com.fake.listening.package";
 
         // Make a visibility checker that allows only fakeListeningPackage.
-        final VisibilityChecker visibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore)
-                        -> callerAccess.getCallingPackageName().equals(fakeListeningPackage);
+        final VisibilityChecker visibilityChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                return callerAccess.getCallingPackageName().equals(fakeListeningPackage);
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
         mAppSearchImpl.close();
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir,
@@ -4890,15 +5229,15 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/null,
-                ALWAYS_OPTIMIZE,
-                visibilityChecker);
+                visibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add a schema type
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -4942,8 +5281,7 @@
         final String fakeListeningPackage = "com.fake.Listening.package";
 
         // Make a visibility checker that rejects everything.
-        final VisibilityChecker rejectChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore) -> false;
+        final VisibilityChecker rejectChecker = createMockVisibilityChecker(false);
         mAppSearchImpl.close();
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir,
@@ -4952,15 +5290,15 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/null,
-                ALWAYS_OPTIMIZE,
-                rejectChecker);
+                rejectChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add a schema type
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5011,7 +5349,7 @@
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5037,7 +5375,7 @@
                         new AppSearchSchema.Builder("Type1").build(),
                         new AppSearchSchema.Builder("Type2").build(),
                         new AppSearchSchema.Builder("Type3").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5062,7 +5400,7 @@
                 ImmutableList.of(
                         new AppSearchSchema.Builder("Type1").build(),
                         new AppSearchSchema.Builder("Type2").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5082,7 +5420,7 @@
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type1").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5115,7 +5453,7 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5143,7 +5481,7 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 1,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5170,7 +5508,7 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 2,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5207,7 +5545,7 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5241,7 +5579,7 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5262,16 +5600,30 @@
         final String fakeListeningPackage = "com.fake.listening.package";
 
         // Make a fake visibility checker that actually looks at visibility store
-        final VisibilityChecker visibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore)
-                        -> {
-                    if (!callerAccess.getCallingPackageName().equals(fakeListeningPackage)) {
-                        return false;
+        final VisibilityChecker visibilityChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                if (!callerAccess.getCallingPackageName().equals(fakeListeningPackage)) {
+                    return false;
+                }
+
+                for (PackageIdentifier packageIdentifier :
+                        visibilityStore.getVisibility(prefixedSchema)
+                                .getVisibilityConfig().getAllowedPackages()) {
+                    if (packageIdentifier.getPackageName().equals(fakeListeningPackage)) {
+                        return true;
                     }
-                    Set<String> allowedPackages = new ArraySet<>(
-                            visibilityStore.getVisibility(prefixedSchema).getPackageNames());
-                    return allowedPackages.contains(fakeListeningPackage);
-                };
+                }
+                return false;
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
         mAppSearchImpl.close();
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir,
@@ -5280,8 +5632,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/null,
-                ALWAYS_OPTIMIZE,
-                visibilityChecker);
+                visibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Register an observer
         TestObserverCallback observer = new TestObserverCallback();
@@ -5300,16 +5652,15 @@
                 mContext.getPackageName(),
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ ImmutableList.of(
-                        new VisibilityDocument.Builder("Type1")
+                /*visibilityConfigs=*/ ImmutableList.of(
+                        new InternalVisibilityConfig.Builder("Type1")
                                 .addVisibleToPackage(
                                         new PackageIdentifier(fakeListeningPackage, new byte[0]))
                                 .build(),
-                        new VisibilityDocument.Builder("Type2")
+                        new InternalVisibilityConfig.Builder("Type2")
                                 .addVisibleToPackage(
                                         new PackageIdentifier(fakeListeningPackage, new byte[0]))
-                                .build()
-                ),
+                                .build()),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5330,12 +5681,12 @@
                 mContext.getPackageName(),
                 "database1",
                 schemas,
-                /*visibilityDocuments=*/ ImmutableList.of(
-                        new VisibilityDocument.Builder("Type1")
+                /*visibilityConfigs=*/ ImmutableList.of(
+                        new InternalVisibilityConfig.Builder("Type1")
                                 .addVisibleToPackage(
                                         new PackageIdentifier(fakeListeningPackage, new byte[0]))
                                 .build(),
-                        new VisibilityDocument.Builder("Type2").build()
+                        new InternalVisibilityConfig.Builder("Type2").build()
                 ),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
@@ -5365,13 +5716,12 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ ImmutableList.of(
-                        new VisibilityDocument.Builder("Type1")
+                /*visibilityConfigs=*/ ImmutableList.of(
+                        new InternalVisibilityConfig.Builder("Type1")
                                 .addVisibleToPackage(
                                         new PackageIdentifier(fakeListeningPackage, new byte[0]))
                                 .build(),
-                        new VisibilityDocument.Builder("Type2").build()
-                ),
+                        new InternalVisibilityConfig.Builder("Type2").build()),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5396,16 +5746,15 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ImmutableList.of(
-                        new VisibilityDocument.Builder("Type1")
+                /*visibilityConfigs=*/ImmutableList.of(
+                        new InternalVisibilityConfig.Builder("Type1")
                                 .addVisibleToPackage(
                                         new PackageIdentifier(fakeListeningPackage, new byte[0]))
                                 .build(),
-                        new VisibilityDocument.Builder("Type2")
+                        new InternalVisibilityConfig.Builder("Type2")
                                 .addVisibleToPackage(
                                         new PackageIdentifier(fakeListeningPackage, new byte[0]))
-                                .build()
-                ),
+                                .build()),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5426,10 +5775,20 @@
         final String fakeListeningPackage = "com.fake.listening.package";
 
         // Make a visibility checker that allows fakeListeningPackage access only to Type2.
-        final VisibilityChecker visibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore)
-                        -> callerAccess.getCallingPackageName().equals(fakeListeningPackage)
+        final VisibilityChecker visibilityChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                return callerAccess.getCallingPackageName().equals(fakeListeningPackage)
                         && prefixedSchema.endsWith("Type2");
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
         mAppSearchImpl.close();
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir,
@@ -5438,8 +5797,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/null,
-                ALWAYS_OPTIMIZE,
-                visibilityChecker);
+                visibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add a schema.
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
@@ -5460,7 +5819,7 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5494,7 +5853,7 @@
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .build())
                                 .build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5515,10 +5874,20 @@
         final String fakeListeningPackage = "com.fake.listening.package";
 
         // Make a visibility checker that allows fakeListeningPackage access only to Type2.
-        final VisibilityChecker visibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore)
-                        -> callerAccess.getCallingPackageName().equals(fakeListeningPackage)
+        final VisibilityChecker visibilityChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                return callerAccess.getCallingPackageName().equals(fakeListeningPackage)
                         && prefixedSchema.endsWith("Type2");
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
         mAppSearchImpl.close();
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir,
@@ -5527,8 +5896,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/null,
-                ALWAYS_OPTIMIZE,
-                visibilityChecker);
+                visibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add a schema.
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
@@ -5537,7 +5906,7 @@
                 ImmutableList.of(
                         new AppSearchSchema.Builder("Type1").build(),
                         new AppSearchSchema.Builder("Type2").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5557,7 +5926,7 @@
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(new AppSearchSchema.Builder("Type2").build()),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5575,7 +5944,7 @@
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5597,21 +5966,29 @@
 
         final String fakePackage2 = "com.fake.listening.package2";
 
-        final VisibilityChecker visibilityChecker =
-                (callerAccess, packageName, prefixedSchema, visibilityStore)
-                        -> {
-                    if (prefixedSchema.endsWith("Type1")) {
-                        return callerAccess.getCallingPackageName().equals(fakePackage1);
-                    } else if (prefixedSchema.endsWith("Type2")) {
-                        return callerAccess.getCallingPackageName().equals(fakePackage2);
-                    } else if (prefixedSchema.endsWith("Type3")) {
-                        return false;
-                    } else if (prefixedSchema.endsWith("Type4")) {
-                        return true;
-                    } else {
-                        throw new IllegalArgumentException(prefixedSchema);
-                    }
-                };
+        final VisibilityChecker visibilityChecker = new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(@NonNull CallerAccess callerAccess,
+                    @NonNull String packageName, @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                if (prefixedSchema.endsWith("Type1")) {
+                    return callerAccess.getCallingPackageName().equals(fakePackage1);
+                } else if (prefixedSchema.endsWith("Type2")) {
+                    return callerAccess.getCallingPackageName().equals(fakePackage2);
+                } else if (prefixedSchema.endsWith("Type3")) {
+                    return false;
+                } else if (prefixedSchema.endsWith("Type4")) {
+                    return true;
+                } else {
+                    throw new IllegalArgumentException(prefixedSchema);
+                }
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+                return false;
+            }
+        };
         mAppSearchImpl.close();
         mAppSearchImpl = AppSearchImpl.create(
                 mAppSearchDir,
@@ -5620,8 +5997,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/null,
-                ALWAYS_OPTIMIZE,
-                visibilityChecker);
+                visibilityChecker, ALWAYS_OPTIMIZE
+        );
 
         // Add a schema.
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
@@ -5633,7 +6010,7 @@
                         new AppSearchSchema.Builder("Type3").build(),
                         new AppSearchSchema.Builder("Type4").build()
                 ),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5669,7 +6046,7 @@
                 mContext.getPackageName(),
                 "database1",
                 ImmutableList.of(),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5724,7 +6101,7 @@
                                                 .build()
                                 ).build()
                 ),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 1,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5761,7 +6138,7 @@
                 mContext.getPackageName(),
                 "database1",
                 updatedSchemaTypes,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ false,
                 /*version=*/ 2,
                 /*setSchemaStatsBuilder=*/ null);
@@ -5783,7 +6160,7 @@
                 mContext.getPackageName(),
                 "database1",
                 updatedSchemaTypes,
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
                 /*version=*/ 3,
                 /*setSchemaStatsBuilder=*/ null);
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
index e9b8e47..aa4c96a 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchLoggerTest.java
@@ -80,8 +80,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         mLogger = new SimpleTestLogger();
     }
 
@@ -373,8 +373,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 initStatsBuilder,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         InitializeStats iStats = initStatsBuilder.build();
         appSearchImpl.close();
 
@@ -406,8 +406,8 @@
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         List<AppSearchSchema> schemas = ImmutableList.of(
                 new AppSearchSchema.Builder("Type1").build(),
                 new AppSearchSchema.Builder("Type2").build());
@@ -443,7 +443,7 @@
         appSearchImpl = AppSearchImpl.create(
                 folder, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()),
-                initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
+                initStatsBuilder, /*visibilityChecker=*/ null, ALWAYS_OPTIMIZE);
         InitializeStats iStats = initStatsBuilder.build();
 
         assertThat(iStats).isNotNull();
@@ -456,7 +456,8 @@
         assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(
                 InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
         assertThat(iStats.getDocumentCount()).isEqualTo(2);
-        assertThat(iStats.getSchemaTypeCount()).isEqualTo(4); // +2 for VisibilitySchema
+        // Type1 + Type2 +2 for VisibilitySchema, +1 for VisibilityOverlay
+        assertThat(iStats.getSchemaTypeCount()).isEqualTo(5);
         assertThat(iStats.hasReset()).isEqualTo(false);
         assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
         appSearchImpl.close();
@@ -471,7 +472,7 @@
         AppSearchImpl appSearchImpl = AppSearchImpl.create(
                 folder, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null, ALWAYS_OPTIMIZE);
 
         List<AppSearchSchema> schemas = ImmutableList.of(
                 new AppSearchSchema.Builder("Type1").build(),
@@ -511,7 +512,7 @@
         appSearchImpl = AppSearchImpl.create(
                 folder, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()),
-                initStatsBuilder, ALWAYS_OPTIMIZE, /*visibilityChecker=*/null);
+                initStatsBuilder, /*visibilityChecker=*/ null, ALWAYS_OPTIMIZE);
         InitializeStats iStats = initStatsBuilder.build();
 
         // Some of other fields are already covered by AppSearchImplTest#testReset()
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SchemaCacheTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SchemaCacheTest.java
new file mode 100644
index 0000000..fadf4bf
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SchemaCacheTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage;
+
+import static androidx.appsearch.localstorage.util.PrefixUtil.createPrefix;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.icing.proto.SchemaTypeConfigProto;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+
+import java.util.Map;
+
+public class SchemaCacheTest {
+
+    @Test
+    public void testGetSchemaTypesWithDescendants() throws Exception {
+        String prefix = createPrefix("package", "database");
+        SchemaTypeConfigProto personSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto artistSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Artist")
+                        .addParentTypes("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto otherSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Other")
+                        .build();
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                prefix, ImmutableMap.of(
+                        "package$database/Person", personSchema,
+                        "package$database/Artist", artistSchema,
+                        "package$database/Other", otherSchema));
+        SchemaCache schemaCache = new SchemaCache(schemaMap);
+
+        assertThat(schemaCache.getSchemaTypesWithDescendants(prefix,
+                ImmutableSet.of("package$database/Person"))).containsExactly(
+                "package$database/Person", "package$database/Artist");
+        assertThat(schemaCache.getSchemaTypesWithDescendants(prefix,
+                ImmutableSet.of("package$database/Artist"))).containsExactly(
+                "package$database/Artist");
+        assertThat(schemaCache.getSchemaTypesWithDescendants(prefix,
+                ImmutableSet.of("package$database/Other"))).containsExactly(
+                "package$database/Other");
+    }
+
+    @Test
+    public void testGetSchemaTypesWithDescendants_multipleLevel() throws Exception {
+        String prefix = createPrefix("package", "database");
+        SchemaTypeConfigProto schemaA =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/A")
+                        .build();
+        SchemaTypeConfigProto schemaB =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/B")
+                        .build();
+        SchemaTypeConfigProto schemaC =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/C")
+                        .addParentTypes("package$database/A")
+                        .build();
+        SchemaTypeConfigProto schemaD =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/D")
+                        .addParentTypes("package$database/C")
+                        .build();
+        SchemaTypeConfigProto schemaE =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/E")
+                        .addParentTypes("package$database/B")
+                        .addParentTypes("package$database/C")
+                        .build();
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                prefix, ImmutableMap.of(
+                        "package$database/A", schemaA,
+                        "package$database/B", schemaB,
+                        "package$database/C", schemaC,
+                        "package$database/D", schemaD,
+                        "package$database/E", schemaE));
+        SchemaCache schemaCache = new SchemaCache(schemaMap);
+
+        assertThat(schemaCache.getSchemaTypesWithDescendants(prefix,
+                ImmutableSet.of("package$database/A"))).containsExactly(
+                "package$database/A", "package$database/C", "package$database/D",
+                "package$database/E");
+        assertThat(schemaCache.getSchemaTypesWithDescendants(prefix,
+                ImmutableSet.of("package$database/B"))).containsExactly(
+                "package$database/B", "package$database/E");
+        assertThat(schemaCache.getSchemaTypesWithDescendants(prefix,
+                ImmutableSet.of("package$database/A", "package$database/B"))).containsExactly(
+                "package$database/A", "package$database/B", "package$database/C",
+                "package$database/D", "package$database/E");
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
index 0690cd5..f189baf 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/SearchResultsImplTest.java
@@ -55,8 +55,8 @@
                         new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()
                 ),
-                /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*initStatsBuilder=*/ null, /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
     }
 
     @After
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
index a8bb683..ad33c41 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.localstorage.AppSearchConfigImpl;
 import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
@@ -33,6 +34,7 @@
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -44,6 +46,10 @@
     private static final byte[] BYTE_ARRAY_2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
     private static final String SCHEMA_TYPE_1 = "sDocumentPropertiesSchemaType1";
     private static final String SCHEMA_TYPE_2 = "sDocumentPropertiesSchemaType2";
+    private static final EmbeddingVector sEmbedding1 = new EmbeddingVector(
+            new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+    private static final EmbeddingVector sEmbedding2 = new EmbeddingVector(
+            new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
     private static final GenericDocument DOCUMENT_PROPERTIES_1 =
             new GenericDocument.Builder<GenericDocument.Builder<?>>(
                     "namespace", "sDocumentProperties1", SCHEMA_TYPE_1)
@@ -429,4 +435,59 @@
                 expectedDocWithParentAsMetaField);
     }
     // @exportToFramework:endStrip()
+
+    @Test
+    public void testDocumentProtoConvert_EmbeddingProperty() throws Exception {
+        GenericDocument document =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("namespace", "id1",
+                        SCHEMA_TYPE_1)
+                        .setCreationTimestampMillis(5L)
+                        .setScore(1)
+                        .setTtlMillis(1L)
+                        .setPropertyLong("longKey1", 1L)
+                        .setPropertyDocument("documentKey1", DOCUMENT_PROPERTIES_1)
+                        .setPropertyEmbedding("embeddingKey1", sEmbedding1, sEmbedding2)
+                        .build();
+
+        // Create the Document proto. Need to sort the property order by key.
+        DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
+                .setUri("id1")
+                .setSchema(SCHEMA_TYPE_1)
+                .setCreationTimestampMs(5L)
+                .setScore(1)
+                .setTtlMs(1L)
+                .setNamespace("namespace");
+        HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>();
+        propertyProtoMap.put("longKey1",
+                PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
+        propertyProtoMap.put("documentKey1",
+                PropertyProto.newBuilder().setName("documentKey1").addDocumentValues(
+                        GenericDocumentToProtoConverter.toDocumentProto(DOCUMENT_PROPERTIES_1)));
+        propertyProtoMap.put("embeddingKey1",
+                PropertyProto.newBuilder().setName("embeddingKey1")
+                        .addVectorValues(PropertyProto.VectorProto.newBuilder()
+                                .addAllValues(Arrays.asList(1.1f, 2.2f, 3.3f))
+                                .setModelSignature("my_model_v1")
+                        )
+                        .addVectorValues(PropertyProto.VectorProto.newBuilder()
+                                .addAllValues(Arrays.asList(4.4f, 5.5f, 6.6f, 7.7f))
+                                .setModelSignature("my_model_v2")
+                        ));
+        List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
+        Collections.sort(sortedKey);
+        for (String key : sortedKey) {
+            documentProtoBuilder.addProperties(propertyProtoMap.get(key));
+        }
+        DocumentProto documentProto = documentProtoBuilder.build();
+
+        GenericDocument convertedGenericDocument =
+                GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX,
+                        SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
+                                new LocalStorageIcingOptionsConfig()));
+        DocumentProto convertedDocumentProto =
+                GenericDocumentToProtoConverter.toDocumentProto(document);
+
+        assertThat(convertedDocumentProto).isEqualTo(documentProto);
+        assertThat(convertedGenericDocument).isEqualTo(document);
+    }
 }
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
index 4513357..bcef397 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
@@ -21,6 +21,7 @@
 import androidx.appsearch.app.AppSearchSchema;
 
 import com.google.android.icing.proto.DocumentIndexingConfig;
+import com.google.android.icing.proto.EmbeddingIndexingConfig;
 import com.google.android.icing.proto.JoinableConfig;
 import com.google.android.icing.proto.PropertyConfigProto;
 import com.google.android.icing.proto.SchemaTypeConfigProto;
@@ -33,6 +34,128 @@
 
 public class SchemaToProtoConverterTest {
     @Test
+    public void testGetProto_DescriptionSet() {
+        AppSearchSchema emailSchema =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("A type of electronic message.")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                                        .setDescription("The most important part.")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("timestamp")
+                                        .setDescription("The time at which the email was sent.")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DoublePropertyConfig.Builder("importanceScore")
+                                        .setDescription(
+                                                "A value representing this document's importance.")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BooleanPropertyConfig.Builder("read")
+                                        .setDescription(
+                                                "Whether the email has been read by the recipient")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BytesPropertyConfig.Builder("attachment")
+                                        .setDescription("Documents that are attached to the email.")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .build())
+                        // We don't need to actually define the Person type for this test because
+                        // the converter will process each schema individually.
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                        "sender", "Person")
+                                        .setDescription("The person who wrote this email.")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .build();
+
+        SchemaTypeConfigProto expectedEmailProto =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("Email")
+                        .setDescription("A type of electronic message.")
+                        .setVersion(12345)
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("subject")
+                                        .setDescription("The most important part.")
+                                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                        .setStringIndexingConfig(
+                                                StringIndexingConfig.newBuilder()
+                                                        .setTokenizerType(
+                                                                StringIndexingConfig.TokenizerType
+                                                                        .Code.PLAIN)
+                                                        .setTermMatchType(
+                                                                TermMatchType.Code.PREFIX)))
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("timestamp")
+                                        .setDescription("The time at which the email was sent.")
+                                        .setDataType(PropertyConfigProto.DataType.Code.INT64)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL))
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("importanceScore")
+                                        .setDescription(
+                                                "A value representing this document's importance.")
+                                        .setDataType(PropertyConfigProto.DataType.Code.DOUBLE)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL))
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("read")
+                                        .setDescription(
+                                                "Whether the email has been read by the recipient")
+                                        .setDataType(PropertyConfigProto.DataType.Code.BOOLEAN)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL))
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("attachment")
+                                        .setDescription("Documents that are attached to the email.")
+                                        .setDataType(PropertyConfigProto.DataType.Code.BYTES)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.REPEATED))
+                        .addProperties(
+                                PropertyConfigProto.newBuilder()
+                                        .setPropertyName("sender")
+                                        .setSchemaType("Person")
+                                        .setDescription("The person who wrote this email.")
+                                        .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
+                                        .setCardinality(
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                                        .setDocumentIndexingConfig(
+                                                DocumentIndexingConfig.newBuilder()
+                                                        .setIndexNestedProperties(false)))
+                        .build();
+
+        assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(emailSchema, /*version=*/ 12345))
+                .isEqualTo(expectedEmailProto);
+        assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedEmailProto))
+                .isEqualTo(emailSchema);
+    }
+
+    @Test
     public void testGetProto_Email() {
         AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
                 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
@@ -53,9 +176,11 @@
 
         SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
                 .setSchemaType("Email")
+                .setDescription("")
                 .setVersion(12345)
                 .addProperties(PropertyConfigProto.newBuilder()
                         .setPropertyName("subject")
+                        .setDescription("")
                         .setDataType(PropertyConfigProto.DataType.Code.STRING)
                         .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
                         .setStringIndexingConfig(
@@ -66,6 +191,7 @@
                         )
                 ).addProperties(PropertyConfigProto.newBuilder()
                         .setPropertyName("body")
+                        .setDescription("")
                         .setDataType(PropertyConfigProto.DataType.Code.STRING)
                         .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
                         .setStringIndexingConfig(
@@ -99,9 +225,11 @@
 
         SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder()
                 .setSchemaType("MusicRecording")
+                .setDescription("")
                 .setVersion(0)
                 .addProperties(PropertyConfigProto.newBuilder()
                         .setPropertyName("artist")
+                        .setDescription("")
                         .setDataType(PropertyConfigProto.DataType.Code.STRING)
                         .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
                         .setStringIndexingConfig(
@@ -112,6 +240,7 @@
                         )
                 ).addProperties(PropertyConfigProto.newBuilder()
                         .setPropertyName("pubDate")
+                        .setDescription("")
                         .setDataType(PropertyConfigProto.DataType.Code.INT64)
                         .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
                 ).build();
@@ -130,28 +259,21 @@
                         .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                         .setJoinableValueType(AppSearchSchema.StringPropertyConfig
                                 .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        // TODO(b/274157614): Export this to framework when we can access hidden
-                        //  APIs.
-                        // @exportToFramework:startStrip()
-                        // TODO(b/274157614) start exporting this when it is unhidden in framework
-                        .setDeletionPropagation(true)
-                        // @exportToFramework:endStrip()
                         .build()
                 ).build();
 
         JoinableConfig joinableConfig = JoinableConfig.newBuilder()
                 .setValueType(JoinableConfig.ValueType.Code.QUALIFIED_ID)
-                // @exportToFramework:startStrip()
-                .setPropagateDelete(true)
-                // @exportToFramework:endStrip()
                 .build();
 
         SchemaTypeConfigProto expectedAlbumProto = SchemaTypeConfigProto.newBuilder()
                 .setSchemaType("Album")
+                .setDescription("")
                 .setVersion(0)
                 .addProperties(
                         PropertyConfigProto.newBuilder()
                                 .setPropertyName("artist")
+                                .setDescription("")
                                 .setDataType(PropertyConfigProto.DataType.Code.STRING)
                                 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
                                 .setStringIndexingConfig(StringIndexingConfig.newBuilder()
@@ -176,6 +298,7 @@
 
         SchemaTypeConfigProto expectedSchemaProto = SchemaTypeConfigProto.newBuilder()
                 .setSchemaType("EmailMessage")
+                .setDescription("")
                 .setVersion(12345)
                 .addParentTypes("Email")
                 .addParentTypes("Message")
@@ -212,10 +335,12 @@
 
         SchemaTypeConfigProto expectedPersonProto = SchemaTypeConfigProto.newBuilder()
                 .setSchemaType("Person")
+                .setDescription("")
                 .setVersion(0)
                 .addProperties(
                         PropertyConfigProto.newBuilder()
                                 .setPropertyName("name")
+                                .setDescription("")
                                 .setDataType(PropertyConfigProto.DataType.Code.STRING)
                                 .setCardinality(PropertyConfigProto.Cardinality.Code.REQUIRED)
                                 .setStringIndexingConfig(StringIndexingConfig.newBuilder()
@@ -225,6 +350,7 @@
                 .addProperties(
                         PropertyConfigProto.newBuilder()
                                 .setPropertyName("worksFor")
+                                .setDescription("")
                                 .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
                                 .setSchemaType("Organization")
                                 .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
@@ -236,4 +362,89 @@
         assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedPersonProto))
                 .isEqualTo(personSchema);
     }
+
+    @Test
+    public void testGetProto_EmbeddingProperty() {
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+                .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(
+                                AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(
+                                AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder("body")
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(
+                                AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(
+                                AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(
+                        new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding")
+                                .setCardinality(
+                                        AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingType(
+                                        AppSearchSchema.EmbeddingPropertyConfig
+                                                .INDEXING_TYPE_NONE)
+                                .build())
+                .addProperty(
+                        new AppSearchSchema.EmbeddingPropertyConfig.Builder("indexableEmbedding")
+                                .setCardinality(
+                                        AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingType(
+                                        AppSearchSchema.EmbeddingPropertyConfig
+                                                .INDEXING_TYPE_SIMILARITY)
+                                .build())
+                .build();
+
+        SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
+                .setSchemaType("Email")
+                .setDescription("")
+                .setVersion(12345)
+                .addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("subject")
+                        .setDescription("")
+                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                        .setStringIndexingConfig(
+                                StringIndexingConfig.newBuilder()
+                                        .setTokenizerType(
+                                                StringIndexingConfig.TokenizerType.Code.PLAIN)
+                                        .setTermMatchType(TermMatchType.Code.PREFIX)
+                        )
+                ).addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("body")
+                        .setDescription("")
+                        .setDataType(PropertyConfigProto.DataType.Code.STRING)
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                        .setStringIndexingConfig(
+                                StringIndexingConfig.newBuilder()
+                                        .setTokenizerType(
+                                                StringIndexingConfig.TokenizerType.Code.PLAIN)
+                                        .setTermMatchType(TermMatchType.Code.PREFIX)
+                        )
+                ).addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("embedding")
+                        .setDescription("")
+                        .setDataType(PropertyConfigProto.DataType.Code.VECTOR)
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                ).addProperties(PropertyConfigProto.newBuilder()
+                        .setPropertyName("indexableEmbedding")
+                        .setDescription("")
+                        .setDataType(PropertyConfigProto.DataType.Code.VECTOR)
+                        .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+                        .setEmbeddingIndexingConfig(
+                                EmbeddingIndexingConfig.newBuilder()
+                                        .setEmbeddingIndexingType(
+                                                EmbeddingIndexingConfig.EmbeddingIndexingType.Code
+                                                        .LINEAR_SEARCH)
+                        )
+                ).build();
+
+        assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(emailSchema, /*version=*/12345))
+                .isEqualTo(expectedEmailProto);
+        assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedEmailProto))
+                .isEqualTo(emailSchema);
+    }
 }
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
index ccf3b51..9ce2a32 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
@@ -27,6 +27,7 @@
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.AppSearchConfigImpl;
 import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
+import androidx.appsearch.localstorage.SchemaCache;
 import androidx.appsearch.localstorage.UnlimitedLimitConfig;
 import androidx.appsearch.localstorage.util.PrefixUtil;
 
@@ -84,7 +85,7 @@
         removePrefixesFromDocument(documentProtoBuilder);
         removePrefixesFromDocument(joinedDocProtoBuilder);
         SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
-                searchResultProto, schemaMap, config);
+                searchResultProto, new SchemaCache(schemaMap), config);
         assertThat(searchResultPage.getResults()).hasSize(1);
         SearchResult result = searchResultPage.getResults().get(0);
         assertThat(result.getPackageName()).isEqualTo("com.package.foo");
@@ -148,7 +149,8 @@
 
         removePrefixesFromDocument(documentProtoBuilder);
         Exception e = assertThrows(AppSearchException.class,
-                () -> SearchResultToProtoConverter.toSearchResultPage(searchResultProto, schemaMap,
+                () -> SearchResultToProtoConverter.toSearchResultPage(searchResultProto,
+                        new SchemaCache(schemaMap),
                         new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                                 new LocalStorageIcingOptionsConfig())));
         assertThat(e.getMessage())
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
index 778ab7a..dbb8f58 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
@@ -30,6 +30,7 @@
 import androidx.appsearch.localstorage.IcingOptionsConfig;
 import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
 import androidx.appsearch.localstorage.OptimizeStrategy;
+import androidx.appsearch.localstorage.SchemaCache;
 import androidx.appsearch.localstorage.UnlimitedLimitConfig;
 import androidx.appsearch.localstorage.util.PrefixUtil;
 import androidx.appsearch.localstorage.visibilitystore.CallerAccess;
@@ -78,8 +79,8 @@
                         mLocalStorageIcingOptionsConfig
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
     }
 
     @After
@@ -105,13 +106,13 @@
                 prefix2, ImmutableSet.of(
                         prefix2 + "namespace1",
                         prefix2 + "namespace2")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        prefix1 + "typeA", configProto,
-                        prefix1 + "typeB", configProto),
-                prefix2, ImmutableMap.of(
-                        prefix2 + "typeA", configProto,
-                        prefix2 + "typeB", configProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                prefix1 + "typeA", configProto,
+                                prefix1 + "typeB", configProto),
+                        prefix2, ImmutableMap.of(
+                                prefix2 + "typeA", configProto,
+                                prefix2 + "typeB", configProto))),
                 mLocalStorageIcingOptionsConfig);
         // Convert SearchSpec to proto.
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -155,13 +156,13 @@
                 prefix2, ImmutableSet.of(
                         prefix2 + "namespace1",
                         prefix2 + "namespace2")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        prefix1 + "typeA", configProto,
-                        prefix1 + "typeB", configProto),
-                prefix2, ImmutableMap.of(
-                        prefix2 + "typeA", configProto,
-                        prefix2 + "typeB", configProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                prefix1 + "typeA", configProto,
+                                prefix1 + "typeB", configProto),
+                        prefix2, ImmutableMap.of(
+                                prefix2 + "typeA", configProto,
+                                prefix2 + "typeB", configProto))),
                 mLocalStorageIcingOptionsConfig);
 
         // Convert SearchSpec to proto.
@@ -227,15 +228,15 @@
                         prefix2,
                         ImmutableSet.of(
                                 prefix2 + "namespace1", prefix2 + "namespace2")),
-                        /*schemaMap=*/ ImmutableMap.of(
-                        prefix1,
-                        ImmutableMap.of(
-                                prefix1 + "typeA", configProto,
-                                prefix1 + "typeB", configProto),
-                        prefix2,
-                        ImmutableMap.of(
-                                prefix2 + "typeA", configProto,
-                                prefix2 + "typeB", configProto)),
+                        new SchemaCache(/*schemaMap=*/ ImmutableMap.of(
+                                prefix1,
+                                ImmutableMap.of(
+                                        prefix1 + "typeA", configProto,
+                                        prefix1 + "typeB", configProto),
+                                prefix2,
+                                ImmutableMap.of(
+                                        prefix2 + "typeA", configProto,
+                                        prefix2 + "typeB", configProto))),
                         mLocalStorageIcingOptionsConfig);
 
         VisibilityStore visibilityStore = new VisibilityStore(mAppSearchImpl);
@@ -287,8 +288,9 @@
                 /*queryExpression=*/"",
                 searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
                 /*namespaceMap=*/ImmutableMap.of(prefix, ImmutableSet.of(prefix + namespace)),
-                /*schemaMap=*/ImmutableMap.of(prefix, ImmutableMap.of(prefix + schemaType,
-                SchemaTypeConfigProto.getDefaultInstance())),
+                new SchemaCache(/*schemaMap=*/
+                        ImmutableMap.of(prefix, ImmutableMap.of(prefix + schemaType,
+                                SchemaTypeConfigProto.getDefaultInstance()))),
                 mLocalStorageIcingOptionsConfig).toScoringSpecProto();
         TypePropertyWeights typePropertyWeights = TypePropertyWeights.newBuilder()
                 .setSchemaType(prefix + schemaType)
@@ -317,7 +319,7 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig).toScoringSpecProto();
 
         assertThat(scoringSpecProto.getOrderBy().getNumber())
@@ -342,11 +344,11 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
         ResultSpecProto resultSpecProto = convert.toResultSpecProto(
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of());
+                new SchemaCache());
 
         assertThat(resultSpecProto.getNumPerPage()).isEqualTo(123);
         assertThat(resultSpecProto.getSnippetSpec().getNumToSnippet()).isEqualTo(234);
@@ -380,12 +382,12 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
 
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of());
+                new SchemaCache());
 
         assertThat(resultSpecProto.getNumPerPage()).isEqualTo(123);
         assertThat(resultSpecProto.getSnippetSpec().getNumToSnippet()).isEqualTo(234);
@@ -428,12 +430,12 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
                 namespaceMap,
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
 
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 namespaceMap,
-                schemaMap);
+                new SchemaCache(schemaMap));
 
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(1);
         assertThat(resultSpecProto.getResultGroupings(0).getEntryGroupings(0).getNamespace())
@@ -479,12 +481,12 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
                 namespaceMap,
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
 
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 namespaceMap,
-                schemaMap);
+                new SchemaCache(schemaMap));
 
         assertThat(resultSpecProto.getTypePropertyMasksCount()).isEqualTo(1);
         assertThat(resultSpecProto.getTypePropertyMasks(0).getSchemaType()).isEqualTo(
@@ -507,7 +509,7 @@
         String actionPrefix = PrefixUtil.createPrefix("aiai", "database");
 
         SearchSpec searchSpec = new SearchSpec.Builder()
-                .addProjection(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, ImmutableList.of("name"))
+                .addProjection(SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("name"))
                 .build();
 
         SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
@@ -515,12 +517,12 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
 
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of());
+                new SchemaCache());
 
         assertThat(resultSpecProto.getTypePropertyMasksCount()).isEqualTo(1);
         assertThat(resultSpecProto.getTypePropertyMasks(0).getSchemaType()).isEqualTo(
@@ -534,7 +536,7 @@
     @Test
     public void testToResultSpecProto_projectionNoPrefixes_withWildcard() {
         SearchSpec searchSpec = new SearchSpec.Builder()
-                .addProjection(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, ImmutableList.of("name"))
+                .addProjection(SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("name"))
                 .build();
 
         SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
@@ -542,12 +544,12 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
 
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of());
+                new SchemaCache());
 
         assertThat(resultSpecProto.getTypePropertyMasksCount()).isEqualTo(1);
         assertThat(resultSpecProto.getTypePropertyMasks(0).getSchemaType()).isEqualTo(
@@ -555,8 +557,59 @@
         assertThat(resultSpecProto.getTypePropertyMasks(0).getPaths(0)).isEqualTo("name");
     }
 
-    // @exportToFramework:startStrip()
-    // TODO(b/274157614): Export this to framework when property filters are made public
+    @Test
+    public void testToResultSpecProto_projection_removeSchemaWithoutParentInFilter() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .addFilterSchemas("Person")
+                .addProjection("Artist", ImmutableList.of("name"))
+                .addProjection("Other", ImmutableList.of("email"))
+                .build();
+        String prefix = createPrefix("package", "database");
+        SchemaTypeConfigProto personSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto artistSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Artist")
+                        .addParentTypes("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto otherSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Other")
+                        .build();
+
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                prefix, ImmutableMap.of(
+                        "package$database/Person", personSchema,
+                        "package$database/Artist", artistSchema,
+                        "package$database/Other", otherSchema));
+        Map<String, Set<String>> namespaceMap = ImmutableMap.of(
+                prefix, ImmutableSet.of("package$database/namespace"));
+
+        SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+                /*queryExpression=*/"",
+                searchSpec,
+                /*prefixes=*/ImmutableSet.of(prefix),
+                /*namespaceMap=*/namespaceMap,
+                new SchemaCache(schemaMap),
+                mLocalStorageIcingOptionsConfig);
+
+        ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+                namespaceMap,
+                new SchemaCache(schemaMap));
+
+        // The "name" property specified in Artist's projection should remain in the result,
+        // since even though Artist doesn't exist in the original schema filters directly, we have
+        // specified its parent, Person, in the schema filters.
+        // The "email" property specified in Other's projection should be dropped as usual.
+        assertThat(resultSpecProto.getTypePropertyMasksCount()).isEqualTo(1);
+        assertThat(resultSpecProto.getTypePropertyMasks(0).getSchemaType()).isEqualTo(
+                "package$database/Artist");
+        assertThat(resultSpecProto.getTypePropertyMasks(0).getPathsCount()).isEqualTo(1);
+        assertThat(resultSpecProto.getTypePropertyMasks(0).getPaths(0)).isEqualTo("name");
+    }
+
     @Test
     public void testToSearchSpecProto_propertyFilter_withJoinSpec_packageFilter() {
         String personPrefix = PrefixUtil.createPrefix("contacts", "database");
@@ -591,7 +644,7 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
                 namespaceMap,
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
 
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -609,7 +662,6 @@
         assertThat(nestedSearchSpecProto.getTypePropertyFilters(0).getPaths(0)).isEqualTo("type");
     }
 
-    // TODO(b/274157614): Export this to framework when property filters are made public
     @Test
     public void testToSearchSpecProto_propertyFilter_withWildcard() {
         String personPrefix = PrefixUtil.createPrefix("contacts", "database");
@@ -624,7 +676,7 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
 
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -635,7 +687,57 @@
         assertThat(searchSpecProto.getTypePropertyFilters(0).getPaths(0)).isEqualTo("name");
     }
 
-    // @exportToFramework:endStrip()
+    @Test
+    public void testToSearchSpecProto_propertyFilter_removeSchemaWithoutParentInFilter() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .addFilterSchemas("Person")
+                .addFilterProperties("Artist", ImmutableList.of("name"))
+                .addFilterProperties("Other", ImmutableList.of("email"))
+                .build();
+        String prefix = createPrefix("package", "database");
+        SchemaTypeConfigProto personSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto artistSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Artist")
+                        .addParentTypes("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto otherSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Other")
+                        .build();
+
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                prefix, ImmutableMap.of(
+                        "package$database/Person", personSchema,
+                        "package$database/Artist", artistSchema,
+                        "package$database/Other", otherSchema));
+        Map<String, Set<String>> namespaceMap = ImmutableMap.of(
+                prefix, ImmutableSet.of("package$database/namespace"));
+
+        SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+                /*queryExpression=*/"",
+                searchSpec,
+                /*prefixes=*/ImmutableSet.of(prefix),
+                /*namespaceMap=*/namespaceMap,
+                new SchemaCache(schemaMap),
+                mLocalStorageIcingOptionsConfig);
+
+        SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
+
+        // The "name" property specified in Artist's property filters should remain in the result,
+        // since even though Artist doesn't exist in the original schema filters directly, we have
+        // specified its parent, Person, in the schema filters.
+        // The "email" property specified in Other's property filters should be dropped as usual.
+        assertThat(searchSpecProto.getTypePropertyFiltersCount()).isEqualTo(1);
+        assertThat(searchSpecProto.getTypePropertyFilters(0).getSchemaType()).isEqualTo(
+                "package$database/Artist");
+        assertThat(searchSpecProto.getTypePropertyFilters(0).getPathsCount()).isEqualTo(1);
+        assertThat(searchSpecProto.getTypePropertyFilters(0).getPaths(0)).isEqualTo("name");
+    }
+
     @Test
     public void testToResultSpecProto_weight_withJoinSpec_packageFilter() throws Exception {
         String personPrefix = PrefixUtil.createPrefix("contacts", "database");
@@ -671,7 +773,7 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
                 namespaceMap,
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
 
         ScoringSpecProto scoringSpecProto = converter.toScoringSpecProto();
@@ -709,7 +811,7 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 /*namespaceMap=*/ImmutableMap.of(
@@ -719,7 +821,7 @@
                         prefix2, ImmutableSet.of(
                                 prefix2 + "namespaceA",
                                 prefix2 + "namespaceB")),
-                /*schemaMap=*/ImmutableMap.of());
+                new SchemaCache());
 
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(2);
         // First grouping should have same package name.
@@ -762,11 +864,11 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                 namespaceMap,
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 namespaceMap,
-                /*schemaMap=*/ImmutableMap.of());
+                new SchemaCache());
 
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(2);
         // First grouping should have same namespace.
@@ -786,8 +888,6 @@
                         PrefixUtil.removePrefix(grouping2.getEntryGroupings(1).getNamespace()));
     }
 
-    // @exportToFramework:startStrip()
-    // TODO(b/258715421) start exporting this when it is unhidden in framework
     @Test
     public void testToResultSpecProto_groupBySchema() throws Exception {
         SearchSpec searchSpec = new SearchSpec.Builder()
@@ -811,11 +911,11 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                 /*namespaceMap=*/ImmutableMap.of(),
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 /*namespaceMap=*/ImmutableMap.of(),
-                schemaMap);
+                new SchemaCache(schemaMap));
 
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(2);
         // First grouping should have the same schema type.
@@ -834,7 +934,6 @@
                 .isEqualTo(
                     PrefixUtil.removePrefix(grouping2.getEntryGroupings(1).getSchema()));
     }
-    // @exportToFramework:endStrip()
 
     @Test
     public void testToResultSpecProto_groupByNamespaceAndPackage() throws Exception {
@@ -857,11 +956,12 @@
                 /*queryExpression=*/"query",
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
-                namespaceMap, /*schemaMap=*/ImmutableMap.of(),
+                namespaceMap,
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 namespaceMap,
-                /*schemaMap=*/ImmutableMap.of());
+                new SchemaCache());
 
         // All namespace should be separated.
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(4);
@@ -871,8 +971,6 @@
         assertThat(resultSpecProto.getResultGroupings(3).getEntryGroupingsList()).hasSize(1);
     }
 
-    // @exportToFramework:startStrip()
-    // TODO(b/258715421) start exporting this when it is unhidden in framework
     @Test
     public void testToResultSpecProto_groupBySchemaAndPackage() throws Exception {
         SearchSpec searchSpec = new SearchSpec.Builder()
@@ -896,11 +994,11 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                 /*namespaceMap=*/ImmutableMap.of(),
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
         ResultSpecProto resultSpecProto = converter.toResultSpecProto(
                 /*namespaceMap=*/ImmutableMap.of(),
-                schemaMap);
+                new SchemaCache(schemaMap));
 
         // All schema should be separated.
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(4);
@@ -940,9 +1038,10 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                 namespaceMap,
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
-        ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap, schemaMap);
+        ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap,
+                new SchemaCache(schemaMap));
 
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(4);
         ResultSpecProto.ResultGrouping grouping1 = resultSpecProto.getResultGroupings(0);
@@ -1032,9 +1131,10 @@
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                 namespaceMap,
-                schemaMap,
+                new SchemaCache(schemaMap),
                 mLocalStorageIcingOptionsConfig);
-        ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap, schemaMap);
+        ResultSpecProto resultSpecProto = converter.toResultSpecProto(namespaceMap,
+                new SchemaCache(schemaMap));
 
         assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(8);
         ResultSpecProto.ResultGrouping grouping1 = resultSpecProto.getResultGroupings(0);
@@ -1110,7 +1210,6 @@
         assertThat(grouping8.getEntryGroupings(0).getSchema())
                 .isEqualTo("package1$database/typeB");
     }
-    // @exportToFramework:endStrip()
 
     @Test
     public void testGetTargetNamespaceFilters_emptySearchingFilter() {
@@ -1127,7 +1226,8 @@
                 /*queryExpression=*/"",
                 searchSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
-                namespaceMap, /*schemaMap=*/ImmutableMap.of(),
+                namespaceMap,
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
 
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -1153,7 +1253,7 @@
                         "package$database1/namespace2"),
                 prefix2, ImmutableSet.of("package$database2/namespace3",
                         "package$database2/namespace4")),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
 
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
@@ -1176,7 +1276,7 @@
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix1, ImmutableSet.of("package$database1/namespace1",
                         "package$database1/namespace2")),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
         // If the searching namespace filter is not empty, the target namespace filter will be the
@@ -1200,7 +1300,7 @@
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix1, ImmutableSet.of("package$database1/namespace1",
                         "package$database1/namespace2")),
-                /*schemaMap=*/ImmutableMap.of(),
+                new SchemaCache(),
                 mLocalStorageIcingOptionsConfig);
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
         // If the searching namespace filter is not empty, the target namespace filter will be the
@@ -1222,13 +1322,13 @@
                 /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix1, ImmutableSet.of("package$database1/namespace1")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        "package$database1/typeA", schemaTypeConfigProto,
-                        "package$database1/typeB", schemaTypeConfigProto),
-                prefix2, ImmutableMap.of(
-                        "package$database2/typeC", schemaTypeConfigProto,
-                        "package$database2/typeD", schemaTypeConfigProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                "package$database1/typeA", schemaTypeConfigProto,
+                                "package$database1/typeB", schemaTypeConfigProto),
+                        prefix2, ImmutableMap.of(
+                                "package$database2/typeC", schemaTypeConfigProto,
+                                "package$database2/typeD", schemaTypeConfigProto))),
                 mLocalStorageIcingOptionsConfig);
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
         // Empty searching filter will get all types for target filter
@@ -1251,13 +1351,13 @@
                 /*prefixes=*/ImmutableSet.of(prefix1),
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix1, ImmutableSet.of("package$database1/namespace1")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        "package$database1/typeA", schemaTypeConfigProto,
-                        "package$database1/typeB", schemaTypeConfigProto),
-                prefix2, ImmutableMap.of(
-                        "package$database2/typeC", schemaTypeConfigProto,
-                        "package$database2/typeD", schemaTypeConfigProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                "package$database1/typeA", schemaTypeConfigProto,
+                                "package$database1/typeB", schemaTypeConfigProto),
+                        prefix2, ImmutableMap.of(
+                                "package$database2/typeC", schemaTypeConfigProto,
+                                "package$database2/typeD", schemaTypeConfigProto))),
                 mLocalStorageIcingOptionsConfig);
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
         // Only search prefix1 will return typeA and B.
@@ -1279,10 +1379,10 @@
                 /*prefixes=*/ImmutableSet.of(prefix1),
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix1, ImmutableSet.of("package$database1/namespace1")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        "package$database1/typeA", schemaTypeConfigProto,
-                        "package$database1/typeB", schemaTypeConfigProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                "package$database1/typeA", schemaTypeConfigProto,
+                                "package$database1/typeB", schemaTypeConfigProto))),
                 mLocalStorageIcingOptionsConfig);
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
         // If the searching schema filter is not empty, the target schema filter will be the
@@ -1293,6 +1393,96 @@
     }
 
     @Test
+    public void testGetTargetSchemaFilters_polymorphismExpansion() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .addFilterSchemas("Person", "nonExist").build();
+        String prefix = createPrefix("package", "database");
+        SchemaTypeConfigProto personSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto artistSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Artist")
+                        .addParentTypes("package$database/Person")
+                        .build();
+        SchemaTypeConfigProto otherSchema =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/Other")
+                        .build();
+
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                prefix, ImmutableMap.of(
+                        "package$database/Person", personSchema,
+                        "package$database/Artist", artistSchema,
+                        "package$database/Other", otherSchema));
+        SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+                /*queryExpression=*/"",
+                searchSpec,
+                /*prefixes=*/ImmutableSet.of(prefix),
+                /*namespaceMap=*/ImmutableMap.of(
+                prefix, ImmutableSet.of("package$database/namespace")),
+                new SchemaCache(schemaMap),
+                mLocalStorageIcingOptionsConfig);
+        SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
+        // The schema filter of "Person" specified in searchSpec will be expanded to "Artist" via
+        // polymorphism.
+        assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
+                "package$database/Person", "package$database/Artist");
+    }
+
+    @Test
+    public void testGetTargetSchemaFilters_polymorphismExpansion_multipleLevel() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .addFilterSchemas("A", "B").build();
+        String prefix = createPrefix("package", "database");
+        SchemaTypeConfigProto schemaA =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/A")
+                        .build();
+        SchemaTypeConfigProto schemaB =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/B")
+                        .build();
+        SchemaTypeConfigProto schemaC =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/C")
+                        .addParentTypes("package$database/A")
+                        .build();
+        SchemaTypeConfigProto schemaD =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/D")
+                        .addParentTypes("package$database/C")
+                        .build();
+        SchemaTypeConfigProto schemaE =
+                SchemaTypeConfigProto.newBuilder()
+                        .setSchemaType("package$database/E")
+                        .addParentTypes("package$database/B")
+                        .addParentTypes("package$database/C")
+                        .build();
+
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                prefix, ImmutableMap.of(
+                        "package$database/A", schemaA,
+                        "package$database/B", schemaB,
+                        "package$database/C", schemaC,
+                        "package$database/D", schemaD,
+                        "package$database/E", schemaE));
+        SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+                /*queryExpression=*/"",
+                searchSpec,
+                /*prefixes=*/ImmutableSet.of(prefix),
+                /*namespaceMap=*/ImmutableMap.of(
+                prefix, ImmutableSet.of("package$database/namespace")),
+                new SchemaCache(schemaMap),
+                mLocalStorageIcingOptionsConfig);
+        SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
+        assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
+                "package$database/A", "package$database/B", "package$database/C",
+                "package$database/D", "package$database/E");
+    }
+
+    @Test
     public void testGetTargetSchemaFilters_intersectionWithNonExistFilter() {
         // Put non-exist searching schema.
         SearchSpec searchSpec = new SearchSpec.Builder()
@@ -1306,10 +1496,10 @@
                 /*prefixes=*/ImmutableSet.of(prefix1),
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix1, ImmutableSet.of("package$database1/namespace1")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        "package$database1/typeA", schemaTypeConfigProto,
-                        "package$database1/typeB", schemaTypeConfigProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                "package$database1/typeA", schemaTypeConfigProto,
+                                "package$database1/typeB", schemaTypeConfigProto))),
                 mLocalStorageIcingOptionsConfig);
         SearchSpecProto searchSpecProto = converter.toSearchSpecProto();
         // If there is no intersection of the schema filters that user want to search over and
@@ -1335,11 +1525,11 @@
                 /*prefixes=*/ImmutableSet.of(prefix),
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix, ImmutableSet.of("package$database/namespace1")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix, ImmutableMap.of(
-                        "package$database/schema1", schemaTypeConfigProto,
-                        "package$database/schema2", schemaTypeConfigProto,
-                        "package$database/schema3", schemaTypeConfigProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix, ImmutableMap.of(
+                                "package$database/schema1", schemaTypeConfigProto,
+                                "package$database/schema2", schemaTypeConfigProto,
+                                "package$database/schema3", schemaTypeConfigProto))),
                 mLocalStorageIcingOptionsConfig);
 
         converter.removeInaccessibleSchemaFilter(
@@ -1386,7 +1576,7 @@
                         /*queryExpression=*/"",
                         searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
                         /*namespaceMap=*/namespaceMap,
-                        /*schemaMap=*/ImmutableMap.of(),
+                        new SchemaCache(),
                         mLocalStorageIcingOptionsConfig);
         assertThat(emptySchemaConverter.hasNothingToSearch()).isTrue();
 
@@ -1395,7 +1585,7 @@
                         /*queryExpression=*/"",
                         searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
                         /*namespaceMap=*/ImmutableMap.of(),
-                        schemaMap,
+                        new SchemaCache(schemaMap),
                         mLocalStorageIcingOptionsConfig);
         assertThat(emptyNamespaceConverter.hasNothingToSearch()).isTrue();
 
@@ -1403,7 +1593,8 @@
                 new SearchSpecToProtoConverter(
                         /*queryExpression=*/"",
                         searchSpec, /*prefixes=*/ImmutableSet.of(prefix),
-                        namespaceMap, schemaMap,
+                        namespaceMap,
+                        new SchemaCache(schemaMap),
                         mLocalStorageIcingOptionsConfig);
         assertThat(nonEmptyConverter.hasNothingToSearch()).isFalse();
 
@@ -1437,11 +1628,11 @@
                 /*prefixes=*/ImmutableSet.of(prefix),
                 /*namespaceMap=*/ImmutableMap.of(
                 prefix, ImmutableSet.of("package$database/namespace1")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix, ImmutableMap.of(
-                        "package$database/schema1", schemaTypeConfigProto,
-                        "package$database/schema2", schemaTypeConfigProto,
-                        "package$database/schema3", schemaTypeConfigProto)),
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix, ImmutableMap.of(
+                                "package$database/schema1", schemaTypeConfigProto,
+                                "package$database/schema2", schemaTypeConfigProto,
+                                "package$database/schema3", schemaTypeConfigProto))),
                 mLocalStorageIcingOptionsConfig);
 
         converter.removeInaccessibleSchemaFilter(
@@ -1489,7 +1680,7 @@
                         /*queryExpression=*/"",
                         searchSpec, /*prefixes=*/ImmutableSet.of(prefix1, prefix2),
                         namespaceMap,
-                        schemaTypeMap,
+                        new SchemaCache(schemaTypeMap),
                         mLocalStorageIcingOptionsConfig);
 
         TypePropertyWeights expectedTypePropertyWeight1 =
@@ -1538,9 +1729,9 @@
                         /*namespaceMap=*/ImmutableMap.of(
                         prefix1,
                         ImmutableSet.of(prefix1 + "namespace1")),
-                        /*schemaMap=*/ImmutableMap.of(
-                        prefix1,
-                        ImmutableMap.of(prefix1 + "typeA", schemaTypeConfigProto)),
+                        new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                                prefix1,
+                                ImmutableMap.of(prefix1 + "typeA", schemaTypeConfigProto))),
                         mLocalStorageIcingOptionsConfig);
 
         ScoringSpecProto convertedScoringSpecProto = converter.toScoringSpecProto();
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverterTest.java
index f918784..365d403 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverterTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import androidx.appsearch.app.SearchSuggestionSpec;
+import androidx.appsearch.localstorage.SchemaCache;
 import androidx.appsearch.localstorage.util.PrefixUtil;
 
 import com.google.android.icing.proto.NamespaceDocumentUriGroup;
@@ -54,10 +55,10 @@
                 prefix1, ImmutableSet.of(
                         prefix1 + "namespace1",
                         prefix1 + "namespace2")),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        prefix1 + "typeA", configProto,
-                        prefix1 + "typeB", configProto)));
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                prefix1 + "typeA", configProto,
+                                prefix1 + "typeB", configProto))));
 
         SuggestionSpecProto proto = converter.toSearchSuggestionSpecProto();
 
@@ -74,9 +75,6 @@
                         .build());
     }
 
-    // @exportToFramework:startStrip()
-    // TODO(b/230553264) remove this when it is deprecated and replaced by
-    //  advanced query property filters or it is exportable.
     @Test
     public void testToProto_propertyFilters() throws Exception {
         SearchSuggestionSpec searchSuggestionSpec =
@@ -91,10 +89,10 @@
                 searchSuggestionSpec,
                 /*prefixes=*/ImmutableSet.of(prefix1),
                 /*namespaceMap=*/ImmutableMap.of(),
-                /*schemaMap=*/ImmutableMap.of(
-                prefix1, ImmutableMap.of(
-                        prefix1 + "typeA", configProto,
-                        prefix1 + "typeB", configProto)));
+                new SchemaCache(/*schemaMap=*/ImmutableMap.of(
+                        prefix1, ImmutableMap.of(
+                                prefix1 + "typeA", configProto,
+                                prefix1 + "typeB", configProto))));
 
         SuggestionSpecProto proto = converter.toSearchSuggestionSpecProto();
         assertThat(proto.getTypePropertyFiltersList()).containsExactly(
@@ -103,5 +101,4 @@
                         .addPaths("property1").addPaths("property2")
                         .build());
     }
-    // @exportToFramework:endStrip()
 }
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java
index fbc9a93..ad86e02 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SnippetTest.java
@@ -23,6 +23,7 @@
 import androidx.appsearch.app.SearchResultPage;
 import androidx.appsearch.localstorage.AppSearchConfigImpl;
 import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
+import androidx.appsearch.localstorage.SchemaCache;
 import androidx.appsearch.localstorage.UnlimitedLimitConfig;
 import androidx.appsearch.localstorage.util.PrefixUtil;
 
@@ -95,7 +96,7 @@
         // Making ResultReader and getting Snippet values.
         SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
                 searchResultProto,
-                SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
+                new SchemaCache(SCHEMA_MAP), new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()));
         assertThat(searchResultPage.getResults()).hasSize(1);
         SearchResult.MatchInfo match = searchResultPage.getResults().get(0).getMatchInfos().get(0);
@@ -136,7 +137,7 @@
 
         SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
                 searchResultProto,
-                SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
+                new SchemaCache(SCHEMA_MAP), new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()));
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getMatchInfos()).isEmpty();
@@ -193,7 +194,7 @@
         // Making ResultReader and getting Snippet values.
         SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
                 searchResultProto,
-                SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
+                new SchemaCache(SCHEMA_MAP), new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()));
         assertThat(searchResultPage.getResults()).hasSize(1);
         SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0);
@@ -280,7 +281,7 @@
         // Making ResultReader and getting Snippet values.
         SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage(
                 searchResultProto,
-                SCHEMA_MAP, new AppSearchConfigImpl(new UnlimitedLimitConfig(),
+                new SchemaCache(SCHEMA_MAP), new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()));
         assertThat(searchResultPage.getResults()).hasSize(1);
         SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0);
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/AppSearchStatsTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/AppSearchStatsTest.java
index 667dd86..c86d4cc 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/AppSearchStatsTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/AppSearchStatsTest.java
@@ -227,6 +227,7 @@
         int nativeLockAcquisitionLatencyMillis = 20;
         int javaToNativeJniLatencyMillis = 21;
         int nativeToJavaJniLatencyMillis = 22;
+        String searchSourceLogTag = "tag";
         final SearchStats.Builder sStatsBuilder = new SearchStats.Builder(visibilityScope,
                 TEST_PACKAGE_NAME)
                 .setDatabase(TEST_DATA_BASE)
@@ -253,7 +254,8 @@
                 .setDocumentRetrievingLatencyMillis(nativeDocumentRetrievingLatencyMillis)
                 .setNativeLockAcquisitionLatencyMillis(nativeLockAcquisitionLatencyMillis)
                 .setJavaToNativeJniLatencyMillis(javaToNativeJniLatencyMillis)
-                .setNativeToJavaJniLatencyMillis(nativeToJavaJniLatencyMillis);
+                .setNativeToJavaJniLatencyMillis(nativeToJavaJniLatencyMillis)
+                .setSearchSourceLogTag(searchSourceLogTag);
         final SearchStats sStats = sStatsBuilder.build();
 
         assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
@@ -295,7 +297,7 @@
                 javaToNativeJniLatencyMillis);
         assertThat(sStats.getNativeToJavaJniLatencyMillis()).isEqualTo(
                 nativeToJavaJniLatencyMillis);
-
+        assertThat(sStats.getSearchSourceLogTag()).isEqualTo(searchSourceLogTag);
     }
 
     @Test
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/ClickStatsTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/ClickStatsTest.java
new file mode 100644
index 0000000..f8a941d
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/ClickStatsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class ClickStatsTest {
+    @Test
+    public void testBuilder() {
+        long timestampMillis = 1L;
+        long timeStayOnResultMillis = 2L;
+        int resultRankInBlock = 3;
+        int resultRankGlobal = 4;
+        boolean isGoodClick = false;
+
+        final ClickStats clickStats =
+                new ClickStats.Builder()
+                        .setTimestampMillis(timestampMillis)
+                        .setTimeStayOnResultMillis(timeStayOnResultMillis)
+                        .setResultRankInBlock(resultRankInBlock)
+                        .setResultRankGlobal(resultRankGlobal)
+                        .setIsGoodClick(isGoodClick)
+                        .build();
+
+        assertThat(clickStats.getTimestampMillis()).isEqualTo(timestampMillis);
+        assertThat(clickStats.getTimeStayOnResultMillis()).isEqualTo(timeStayOnResultMillis);
+        assertThat(clickStats.getResultRankInBlock()).isEqualTo(resultRankInBlock);
+        assertThat(clickStats.getResultRankGlobal()).isEqualTo(resultRankGlobal);
+        assertThat(clickStats.isGoodClick()).isEqualTo(isGoodClick);
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/SearchIntentStatsTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/SearchIntentStatsTest.java
new file mode 100644
index 0000000..66e2ed4
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/SearchIntentStatsTest.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class SearchIntentStatsTest {
+    static final String TEST_PACKAGE_NAME = "package.test";
+    static final String TEST_DATA_BASE = "testDataBase";
+
+    @Test
+    public void testBuilder() {
+        String prevQuery = "prev";
+        String currQuery = "curr";
+        long searchIntentTimestampMillis = 1L;
+        int numResultsFetched = 2;
+        int queryCorrectionType = SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT;
+
+        // Clicks associated with the search intent.
+        final ClickStats clickStats0 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(10L)
+                        .setTimeStayOnResultMillis(20L)
+                        .setResultRankInBlock(30)
+                        .setResultRankGlobal(40)
+                        .setIsGoodClick(false)
+                        .build();
+        final ClickStats clickStats1 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(11L)
+                        .setTimeStayOnResultMillis(21L)
+                        .setResultRankInBlock(31)
+                        .setResultRankGlobal(41)
+                        .setIsGoodClick(true)
+                        .build();
+
+        final SearchIntentStats searchIntentStats =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery(prevQuery)
+                        .setCurrQuery(currQuery)
+                        .setTimestampMillis(searchIntentTimestampMillis)
+                        .setNumResultsFetched(numResultsFetched)
+                        .setQueryCorrectionType(queryCorrectionType)
+                        .addClicksStats(clickStats0, clickStats1)
+                        .build();
+
+        assertThat(searchIntentStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchIntentStats.getPrevQuery()).isEqualTo(prevQuery);
+        assertThat(searchIntentStats.getCurrQuery()).isEqualTo(currQuery);
+        assertThat(searchIntentStats.getTimestampMillis()).isEqualTo(searchIntentTimestampMillis);
+        assertThat(searchIntentStats.getNumResultsFetched()).isEqualTo(numResultsFetched);
+        assertThat(searchIntentStats.getQueryCorrectionType()).isEqualTo(queryCorrectionType);
+        assertThat(searchIntentStats.getClicksStats()).containsExactly(clickStats0, clickStats1);
+    }
+
+    @Test
+    public void testBuilderCopy_allFieldsAreCopied() {
+        String prevQuery = "prev";
+        String currQuery = "curr";
+        long searchIntentTimestampMillis = 1L;
+        int numResultsFetched = 2;
+        int queryCorrectionType = SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT;
+
+        // Clicks associated with the search intent.
+        final ClickStats clickStats0 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(10L)
+                        .setTimeStayOnResultMillis(20L)
+                        .setResultRankInBlock(30)
+                        .setResultRankGlobal(40)
+                        .setIsGoodClick(false)
+                        .build();
+        final ClickStats clickStats1 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(11L)
+                        .setTimeStayOnResultMillis(21L)
+                        .setResultRankInBlock(31)
+                        .setResultRankGlobal(41)
+                        .setIsGoodClick(true)
+                        .build();
+
+        final SearchIntentStats searchIntentStats0 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery(prevQuery)
+                        .setCurrQuery(currQuery)
+                        .setTimestampMillis(searchIntentTimestampMillis)
+                        .setNumResultsFetched(numResultsFetched)
+                        .setQueryCorrectionType(queryCorrectionType)
+                        .addClicksStats(clickStats0, clickStats1)
+                        .build();
+        final SearchIntentStats searchIntentStats1 =
+                new SearchIntentStats.Builder(searchIntentStats0).build();
+
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo(prevQuery);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo(currQuery);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(searchIntentTimestampMillis);
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(numResultsFetched);
+        assertThat(searchIntentStats1.getQueryCorrectionType()).isEqualTo(queryCorrectionType);
+        assertThat(searchIntentStats1.getClicksStats()).containsExactly(clickStats0, clickStats1);
+    }
+
+    @Test
+    public void testBuilderCopy_copiedFieldsCanBeUpdated() {
+        // Clicks associated with the search intent.
+        final ClickStats clickStats0 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(10L)
+                        .setTimeStayOnResultMillis(20L)
+                        .setResultRankInBlock(30)
+                        .setResultRankGlobal(40)
+                        .setIsGoodClick(false)
+                        .build();
+        final ClickStats clickStats1 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(11L)
+                        .setTimeStayOnResultMillis(21L)
+                        .setResultRankInBlock(31)
+                        .setResultRankGlobal(41)
+                        .setIsGoodClick(true)
+                        .build();
+
+        final SearchIntentStats searchIntentStats0 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query1")
+                        .setCurrQuery("query2")
+                        .setTimestampMillis(1L)
+                        .setNumResultsFetched(2)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT)
+                        .addClicksStats(clickStats0, clickStats1)
+                        .build();
+
+        // Build another SearchIntentStats based on the previous one, with fields changed.
+        final ClickStats clickStats2 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(12L)
+                        .setTimeStayOnResultMillis(22L)
+                        .setResultRankInBlock(32)
+                        .setResultRankGlobal(42)
+                        .setIsGoodClick(true)
+                        .build();
+        final SearchIntentStats searchIntentStats1 =
+                new SearchIntentStats.Builder(searchIntentStats0)
+                        .setDatabase("database2")
+                        .setPrevQuery("query3")
+                        .setCurrQuery("query4")
+                        .setTimestampMillis(2L)
+                        .setNumResultsFetched(4)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT)
+                        .addClicksStats(clickStats2)
+                        .build();
+
+        // Check that searchIntentStats0 wasn't altered.
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchIntentStats0.getPrevQuery()).isEqualTo("query1");
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("query2");
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1L);
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(2);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT);
+        assertThat(searchIntentStats0.getClicksStats()).containsExactly(clickStats0, clickStats1);
+
+        // Check that searchIntentStats1 has the new values.
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo("database2");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("query3");
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("query4");
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(2L);
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(4);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats())
+                .containsExactly(clickStats0, clickStats1, clickStats2);
+    }
+
+    @Test
+    public void testBuilder_addClicksStats_byCollection() {
+        final ClickStats clickStats0 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(10L)
+                        .setTimeStayOnResultMillis(20L)
+                        .setResultRankInBlock(30)
+                        .setResultRankGlobal(40)
+                        .setIsGoodClick(false)
+                        .build();
+        final ClickStats clickStats1 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(11L)
+                        .setTimeStayOnResultMillis(21L)
+                        .setResultRankInBlock(31)
+                        .setResultRankGlobal(41)
+                        .setIsGoodClick(true)
+                        .build();
+        Set<ClickStats> clicksStats = ImmutableSet.of(clickStats0, clickStats1);
+
+        final SearchIntentStats searchIntentStats =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .addClicksStats(clicksStats)
+                        .build();
+
+        assertThat(searchIntentStats.getClicksStats()).containsExactlyElementsIn(clicksStats);
+    }
+
+    @Test
+    public void testBuilder_builderReuse() {
+        String prevQuery = "prev";
+        String currQuery = "curr";
+        long searchIntentTimestampMillis = 1;
+        int numResultsFetched = 2;
+        int queryCorrectionType = SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT;
+
+        final ClickStats clickStats0 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(10L)
+                        .setTimeStayOnResultMillis(20L)
+                        .setResultRankInBlock(30)
+                        .setResultRankGlobal(40)
+                        .setIsGoodClick(false)
+                        .build();
+        final ClickStats clickStats1 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(11L)
+                        .setTimeStayOnResultMillis(21L)
+                        .setResultRankInBlock(31)
+                        .setResultRankGlobal(41)
+                        .setIsGoodClick(true)
+                        .build();
+
+        SearchIntentStats.Builder builder =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery(prevQuery)
+                        .setCurrQuery(currQuery)
+                        .setTimestampMillis(searchIntentTimestampMillis)
+                        .setNumResultsFetched(numResultsFetched)
+                        .setQueryCorrectionType(queryCorrectionType)
+                        .addClicksStats(clickStats0, clickStats1);
+
+        final SearchIntentStats searchIntentStats0 = builder.build();
+
+        final ClickStats clickStats2 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(12L)
+                        .setTimeStayOnResultMillis(22L)
+                        .setResultRankInBlock(32)
+                        .setResultRankGlobal(42)
+                        .setIsGoodClick(true)
+                        .build();
+        builder.addClicksStats(clickStats2);
+
+        final SearchIntentStats searchIntentStats1 = builder.build();
+
+        // Check that searchIntentStats0 wasn't altered.
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchIntentStats0.getPrevQuery()).isEqualTo(prevQuery);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo(currQuery);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(searchIntentTimestampMillis);
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(numResultsFetched);
+        assertThat(searchIntentStats0.getQueryCorrectionType()).isEqualTo(queryCorrectionType);
+        assertThat(searchIntentStats0.getClicksStats()).containsExactly(clickStats0, clickStats1);
+
+        // Check that searchIntentStats1 has the new values.
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo(prevQuery);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo(currQuery);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(searchIntentTimestampMillis);
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(numResultsFetched);
+        assertThat(searchIntentStats1.getQueryCorrectionType()).isEqualTo(queryCorrectionType);
+        assertThat(searchIntentStats1.getClicksStats())
+                .containsExactly(clickStats0, clickStats1, clickStats2);
+    }
+
+    @Test
+    public void testBuilder_builderReuse_byCollection() {
+        String prevQuery = "prev";
+        String currQuery = "curr";
+        long searchIntentTimestampMillis = 1;
+        int numResultsFetched = 2;
+        int queryCorrectionType = SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT;
+
+        final ClickStats clickStats0 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(10L)
+                        .setTimeStayOnResultMillis(20L)
+                        .setResultRankInBlock(30)
+                        .setResultRankGlobal(40)
+                        .setIsGoodClick(false)
+                        .build();
+        final ClickStats clickStats1 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(11L)
+                        .setTimeStayOnResultMillis(21L)
+                        .setResultRankInBlock(31)
+                        .setResultRankGlobal(41)
+                        .setIsGoodClick(true)
+                        .build();
+
+        SearchIntentStats.Builder builder =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery(prevQuery)
+                        .setCurrQuery(currQuery)
+                        .setTimestampMillis(searchIntentTimestampMillis)
+                        .setNumResultsFetched(numResultsFetched)
+                        .setQueryCorrectionType(queryCorrectionType)
+                        .addClicksStats(ImmutableSet.of(clickStats0, clickStats1));
+
+        final SearchIntentStats searchIntentStats0 = builder.build();
+
+        final ClickStats clickStats2 =
+                new ClickStats.Builder()
+                        .setTimestampMillis(12L)
+                        .setTimeStayOnResultMillis(22L)
+                        .setResultRankInBlock(32)
+                        .setResultRankGlobal(42)
+                        .setIsGoodClick(true)
+                        .build();
+        builder.addClicksStats(ImmutableSet.of(clickStats2));
+
+        final SearchIntentStats searchIntentStats1 = builder.build();
+
+        // Check that searchIntentStats0 wasn't altered.
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchIntentStats0.getPrevQuery()).isEqualTo(prevQuery);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo(currQuery);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(searchIntentTimestampMillis);
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(numResultsFetched);
+        assertThat(searchIntentStats0.getQueryCorrectionType()).isEqualTo(queryCorrectionType);
+        assertThat(searchIntentStats0.getClicksStats()).containsExactly(clickStats0, clickStats1);
+
+        // Check that searchIntentStats1 has the new values.
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo(prevQuery);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo(currQuery);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(searchIntentTimestampMillis);
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(numResultsFetched);
+        assertThat(searchIntentStats1.getQueryCorrectionType()).isEqualTo(queryCorrectionType);
+        assertThat(searchIntentStats1.getClicksStats())
+                .containsExactly(clickStats0, clickStats1, clickStats2);
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/SearchSessionStatsTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/SearchSessionStatsTest.java
new file mode 100644
index 0000000..b287769
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/stats/SearchSessionStatsTest.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class SearchSessionStatsTest {
+    static final String TEST_PACKAGE_NAME = "package.test";
+    static final String TEST_DATA_BASE = "testDataBase";
+
+    @Test
+    public void testBuilder() {
+        final SearchIntentStats searchIntentStats0 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("")
+                        .setCurrQuery("query1")
+                        .setTimestampMillis(1L)
+                        .setNumResultsFetched(2)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(10L)
+                                        .setTimeStayOnResultMillis(20L)
+                                        .setResultRankInBlock(30)
+                                        .setResultRankGlobal(40)
+                                        .setIsGoodClick(false)
+                                        .build(),
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(11L)
+                                        .setTimeStayOnResultMillis(21L)
+                                        .setResultRankInBlock(31)
+                                        .setResultRankGlobal(41)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        final SearchIntentStats searchIntentStats1 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query1")
+                        .setCurrQuery("query2")
+                        .setTimestampMillis(2L)
+                        .setNumResultsFetched(4)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(12L)
+                                        .setTimeStayOnResultMillis(22L)
+                                        .setResultRankInBlock(32)
+                                        .setResultRankGlobal(42)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+
+        final SearchSessionStats searchSessionStats =
+                new SearchSessionStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .addSearchIntentsStats(searchIntentStats0, searchIntentStats1)
+                        .build();
+
+        assertThat(searchSessionStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchSessionStats.getSearchIntentsStats())
+                .containsExactly(searchIntentStats0, searchIntentStats1);
+    }
+
+    @Test
+    public void testBuilder_addSearchIntentsStats_byCollection() {
+        final SearchIntentStats searchIntentStats0 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("")
+                        .setCurrQuery("query1")
+                        .setTimestampMillis(1L)
+                        .setNumResultsFetched(2)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(10L)
+                                        .setTimeStayOnResultMillis(20L)
+                                        .setResultRankInBlock(30)
+                                        .setResultRankGlobal(40)
+                                        .setIsGoodClick(false)
+                                        .build(),
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(11L)
+                                        .setTimeStayOnResultMillis(21L)
+                                        .setResultRankInBlock(31)
+                                        .setResultRankGlobal(41)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        final SearchIntentStats searchIntentStats1 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query1")
+                        .setCurrQuery("query2")
+                        .setTimestampMillis(2L)
+                        .setNumResultsFetched(4)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(12L)
+                                        .setTimeStayOnResultMillis(22L)
+                                        .setResultRankInBlock(32)
+                                        .setResultRankGlobal(42)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        Set<SearchIntentStats> searchIntentsStats =
+                ImmutableSet.of(searchIntentStats0, searchIntentStats1);
+
+        final SearchSessionStats searchSessionStats =
+                new SearchSessionStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .addSearchIntentsStats(searchIntentsStats)
+                        .build();
+
+        assertThat(searchSessionStats.getSearchIntentsStats())
+                .containsExactlyElementsIn(searchIntentsStats);
+    }
+
+    @Test
+    public void testBuilder_builderReuse() {
+        final SearchIntentStats searchIntentStats0 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("")
+                        .setCurrQuery("query1")
+                        .setTimestampMillis(1L)
+                        .setNumResultsFetched(2)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(10L)
+                                        .setTimeStayOnResultMillis(20L)
+                                        .setResultRankInBlock(30)
+                                        .setResultRankGlobal(40)
+                                        .setIsGoodClick(false)
+                                        .build(),
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(11L)
+                                        .setTimeStayOnResultMillis(21L)
+                                        .setResultRankInBlock(31)
+                                        .setResultRankGlobal(41)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        final SearchIntentStats searchIntentStats1 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query1")
+                        .setCurrQuery("query2")
+                        .setTimestampMillis(2L)
+                        .setNumResultsFetched(4)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(12L)
+                                        .setTimeStayOnResultMillis(22L)
+                                        .setResultRankInBlock(32)
+                                        .setResultRankGlobal(42)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+
+        SearchSessionStats.Builder builder =
+                new SearchSessionStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .addSearchIntentsStats(searchIntentStats0, searchIntentStats1);
+
+        final SearchSessionStats searchSessionStats0 = builder.build();
+
+        final SearchIntentStats searchIntentStats2 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query2")
+                        .setCurrQuery("query3")
+                        .setTimestampMillis(3L)
+                        .setNumResultsFetched(6)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(13L)
+                                        .setTimeStayOnResultMillis(23L)
+                                        .setResultRankInBlock(33)
+                                        .setResultRankGlobal(43)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        builder.addSearchIntentsStats(searchIntentStats2);
+
+        final SearchSessionStats searchSessionStats1 = builder.build();
+
+        // Check that searchSessionStats0 wasn't altered.
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats())
+                .containsExactly(searchIntentStats0, searchIntentStats1);
+
+        // Check that searchSessionStats1 has the new values.
+        assertThat(searchSessionStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats1.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchSessionStats1.getSearchIntentsStats())
+                .containsExactly(searchIntentStats0, searchIntentStats1, searchIntentStats2);
+    }
+
+    @Test
+    public void testBuilder_builderReuse_byCollection() {
+        final SearchIntentStats searchIntentStats0 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("")
+                        .setCurrQuery("query1")
+                        .setTimestampMillis(1L)
+                        .setNumResultsFetched(2)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(10L)
+                                        .setTimeStayOnResultMillis(20L)
+                                        .setResultRankInBlock(30)
+                                        .setResultRankGlobal(40)
+                                        .setIsGoodClick(false)
+                                        .build(),
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(11L)
+                                        .setTimeStayOnResultMillis(21L)
+                                        .setResultRankInBlock(31)
+                                        .setResultRankGlobal(41)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        final SearchIntentStats searchIntentStats1 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query1")
+                        .setCurrQuery("query2")
+                        .setTimestampMillis(2L)
+                        .setNumResultsFetched(4)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(12L)
+                                        .setTimeStayOnResultMillis(22L)
+                                        .setResultRankInBlock(32)
+                                        .setResultRankGlobal(42)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+
+        SearchSessionStats.Builder builder =
+                new SearchSessionStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .addSearchIntentsStats(
+                                ImmutableSet.of(searchIntentStats0, searchIntentStats1));
+
+        final SearchSessionStats searchSessionStats0 = builder.build();
+
+        final SearchIntentStats searchIntentStats2 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query2")
+                        .setCurrQuery("query3")
+                        .setTimestampMillis(3L)
+                        .setNumResultsFetched(6)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(13L)
+                                        .setTimeStayOnResultMillis(23L)
+                                        .setResultRankInBlock(33)
+                                        .setResultRankGlobal(43)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        builder.addSearchIntentsStats(ImmutableSet.of(searchIntentStats2));
+
+        final SearchSessionStats searchSessionStats1 = builder.build();
+
+        // Check that searchSessionStats0 wasn't altered.
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats())
+                .containsExactly(searchIntentStats0, searchIntentStats1);
+
+        // Check that searchSessionStats1 has the new values.
+        assertThat(searchSessionStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats1.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(searchSessionStats1.getSearchIntentsStats())
+                .containsExactly(searchIntentStats0, searchIntentStats1, searchIntentStats2);
+    }
+
+    @Test
+    public void testGetEndSessionSearchIntentStats() {
+        final SearchIntentStats searchIntentStats0 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("")
+                        .setCurrQuery("query1")
+                        .setTimestampMillis(1L)
+                        .setNumResultsFetched(2)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(10L)
+                                        .setTimeStayOnResultMillis(20L)
+                                        .setResultRankInBlock(30)
+                                        .setResultRankGlobal(40)
+                                        .setIsGoodClick(false)
+                                        .build(),
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(11L)
+                                        .setTimeStayOnResultMillis(21L)
+                                        .setResultRankInBlock(31)
+                                        .setResultRankGlobal(41)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+        final SearchIntentStats searchIntentStats1 =
+                new SearchIntentStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setPrevQuery("query1")
+                        .setCurrQuery("query2")
+                        .setTimestampMillis(2L)
+                        .setNumResultsFetched(4)
+                        .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT)
+                        .addClicksStats(
+                                new ClickStats.Builder()
+                                        .setTimestampMillis(12L)
+                                        .setTimeStayOnResultMillis(22L)
+                                        .setResultRankInBlock(32)
+                                        .setResultRankGlobal(42)
+                                        .setIsGoodClick(true)
+                                        .build())
+                        .build();
+
+        final SearchSessionStats searchSessionStats =
+                new SearchSessionStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .addSearchIntentsStats(searchIntentStats0, searchIntentStats1)
+                        .build();
+
+        SearchIntentStats endSessionSearchIntentStats =
+                searchSessionStats.getEndSessionSearchIntentStats();
+        // End session SearchIntentStats should be identical to the last added SearchIntentStats,
+        // except the previous query is null and query correction type is
+        // QUERY_CORRECTION_TYPE_END_SESSION.
+        assertThat(endSessionSearchIntentStats).isNotNull();
+        assertThat(endSessionSearchIntentStats.getPackageName())
+                .isEqualTo(searchIntentStats1.getPackageName());
+        assertThat(endSessionSearchIntentStats.getDatabase())
+                .isEqualTo(searchIntentStats1.getDatabase());
+        assertThat(endSessionSearchIntentStats.getCurrQuery())
+                .isEqualTo(searchIntentStats1.getCurrQuery());
+        assertThat(endSessionSearchIntentStats.getTimestampMillis())
+                .isEqualTo(searchIntentStats1.getTimestampMillis());
+        assertThat(endSessionSearchIntentStats.getNumResultsFetched())
+                .isEqualTo(searchIntentStats1.getNumResultsFetched());
+        assertThat(endSessionSearchIntentStats.getClicksStats())
+                .containsExactlyElementsIn(searchIntentStats1.getClicksStats());
+
+        assertThat(endSessionSearchIntentStats.getPrevQuery()).isNull();
+        assertThat(endSessionSearchIntentStats.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_END_SESSION);
+    }
+
+    @Test
+    public void testGetEndSessionSearchIntentStats_emptySearchIntentsShouldReturnNull() {
+        // Create a SearchSessionStats without search intents.
+        final SearchSessionStats searchSessionStats =
+                new SearchSessionStats.Builder(TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .build();
+
+        assertThat(searchSessionStats.getEndSessionSearchIntentStats()).isNull();
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/ClickActionGenericDocumentTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/ClickActionGenericDocumentTest.java
new file mode 100644
index 0000000..36e0ca0
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/ClickActionGenericDocumentTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.usagereporting;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.usagereporting.ActionConstants;
+import androidx.appsearch.usagereporting.ClickAction;
+
+import org.junit.Test;
+
+public class ClickActionGenericDocumentTest {
+    @Test
+    public void testBuild() {
+        ClickActionGenericDocument clickActionGenericDocument =
+                new ClickActionGenericDocument.Builder("namespace", "click", "builtin:ClickAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("body")
+                        .setResultRankInBlock(12)
+                        .setResultRankGlobal(34)
+                        .setTimeStayOnResultMillis(2000)
+                        .build();
+
+        assertThat(clickActionGenericDocument.getNamespace()).isEqualTo("namespace");
+        assertThat(clickActionGenericDocument.getId()).isEqualTo("click");
+        assertThat(clickActionGenericDocument.getSchemaType()).isEqualTo("builtin:ClickAction");
+        assertThat(clickActionGenericDocument.getCreationTimestampMillis()).isEqualTo(1000);
+        assertThat(clickActionGenericDocument.getActionType())
+                .isEqualTo(ActionConstants.ACTION_TYPE_CLICK);
+        assertThat(clickActionGenericDocument.getQuery()).isEqualTo("body");
+        assertThat(clickActionGenericDocument.getResultRankInBlock()).isEqualTo(12);
+        assertThat(clickActionGenericDocument.getResultRankGlobal()).isEqualTo(34);
+        assertThat(clickActionGenericDocument.getTimeStayOnResultMillis()).isEqualTo(2000);
+    }
+
+    @Test
+    public void testBuild_fromGenericDocument() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "click", "builtin:ClickAction")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyLong("actionType", ActionConstants.ACTION_TYPE_CLICK)
+                        .setPropertyString("query", "body")
+                        .setPropertyLong("resultRankInBlock", 12)
+                        .setPropertyLong("resultRankGlobal", 34)
+                        .setPropertyLong("timeStayOnResultMillis", 2000)
+                        .build();
+        ClickActionGenericDocument clickActionGenericDocument =
+                new ClickActionGenericDocument.Builder(document).build();
+
+        assertThat(clickActionGenericDocument.getNamespace()).isEqualTo("namespace");
+        assertThat(clickActionGenericDocument.getId()).isEqualTo("click");
+        assertThat(clickActionGenericDocument.getSchemaType()).isEqualTo("builtin:ClickAction");
+        assertThat(clickActionGenericDocument.getCreationTimestampMillis()).isEqualTo(1000);
+        assertThat(clickActionGenericDocument.getActionType())
+                .isEqualTo(ActionConstants.ACTION_TYPE_CLICK);
+        assertThat(clickActionGenericDocument.getQuery()).isEqualTo("body");
+        assertThat(clickActionGenericDocument.getResultRankInBlock()).isEqualTo(12);
+        assertThat(clickActionGenericDocument.getResultRankGlobal()).isEqualTo(34);
+        assertThat(clickActionGenericDocument.getTimeStayOnResultMillis()).isEqualTo(2000);
+    }
+
+// @exportToFramework:startStrip()
+    @Test
+    public void testBuild_fromDocumentClass() throws Exception {
+        ClickAction clickAction =
+                new ClickAction.Builder("namespace", "click", /* actionTimestampMillis= */1000)
+                        .setQuery("body")
+                        .setReferencedQualifiedId("pkg$db/ns#doc")
+                        .setResultRankInBlock(12)
+                        .setResultRankGlobal(34)
+                        .setTimeStayOnResultMillis(2000)
+                        .build();
+        ClickActionGenericDocument clickActionGenericDocument =
+                new ClickActionGenericDocument.Builder(
+                        GenericDocument.fromDocumentClass(clickAction)).build();
+
+        assertThat(clickActionGenericDocument.getNamespace()).isEqualTo("namespace");
+        assertThat(clickActionGenericDocument.getId()).isEqualTo("click");
+        assertThat(clickActionGenericDocument.getSchemaType()).isEqualTo("builtin:ClickAction");
+        assertThat(clickActionGenericDocument.getCreationTimestampMillis()).isEqualTo(1000);
+        assertThat(clickActionGenericDocument.getActionType())
+                .isEqualTo(ActionConstants.ACTION_TYPE_CLICK);
+        assertThat(clickActionGenericDocument.getQuery()).isEqualTo("body");
+        assertThat(clickActionGenericDocument.getResultRankInBlock()).isEqualTo(12);
+        assertThat(clickActionGenericDocument.getResultRankGlobal()).isEqualTo(34);
+        assertThat(clickActionGenericDocument.getTimeStayOnResultMillis()).isEqualTo(2000);
+    }
+// @exportToFramework:endStrip()
+
+    @Test
+    public void testBuild_invalidActionTypeThrowsException() {
+        GenericDocument documentWithoutActionType =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:ClickAction")
+                        .build();
+        IllegalArgumentException e1 = assertThrows(IllegalArgumentException.class,
+                () -> new ClickActionGenericDocument.Builder(documentWithoutActionType));
+        assertThat(e1.getMessage())
+                .isEqualTo("Invalid action type for ClickActionGenericDocument");
+
+        GenericDocument documentWithUnknownActionType =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:ClickAction")
+                        .setPropertyLong("actionType", ActionConstants.ACTION_TYPE_UNKNOWN)
+                        .build();
+        IllegalArgumentException e2 = assertThrows(IllegalArgumentException.class,
+                () -> new ClickActionGenericDocument.Builder(documentWithUnknownActionType));
+        assertThat(e2.getMessage())
+                .isEqualTo("Invalid action type for ClickActionGenericDocument");
+
+        GenericDocument documentWithIncorrectActionType =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:SearchAction")
+                        .setPropertyLong("actionType", ActionConstants.ACTION_TYPE_SEARCH)
+                        .build();
+        IllegalArgumentException e3 = assertThrows(IllegalArgumentException.class,
+                () -> new ClickActionGenericDocument.Builder(documentWithIncorrectActionType));
+        assertThat(e3.getMessage())
+                .isEqualTo("Invalid action type for ClickActionGenericDocument");
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/SearchActionGenericDocumentTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/SearchActionGenericDocumentTest.java
new file mode 100644
index 0000000..ea705b7
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/SearchActionGenericDocumentTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.usagereporting;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.usagereporting.ActionConstants;
+import androidx.appsearch.usagereporting.SearchAction;
+
+import org.junit.Test;
+
+public class SearchActionGenericDocumentTest {
+    @Test
+    public void testBuild() {
+        SearchActionGenericDocument searchActionGenericDocument =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("body")
+                        .setFetchedResultCount(123)
+                        .build();
+
+        assertThat(searchActionGenericDocument.getNamespace()).isEqualTo("namespace");
+        assertThat(searchActionGenericDocument.getId()).isEqualTo("search");
+        assertThat(searchActionGenericDocument.getSchemaType()).isEqualTo("builtin:SearchAction");
+        assertThat(searchActionGenericDocument.getCreationTimestampMillis()).isEqualTo(1000);
+        assertThat(searchActionGenericDocument.getActionType())
+                .isEqualTo(ActionConstants.ACTION_TYPE_SEARCH);
+        assertThat(searchActionGenericDocument.getQuery()).isEqualTo("body");
+        assertThat(searchActionGenericDocument.getFetchedResultCount()).isEqualTo(123);
+    }
+
+    @Test
+    public void testBuild_fromGenericDocument() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyLong("actionType", ActionConstants.ACTION_TYPE_SEARCH)
+                        .setPropertyString("query", "body")
+                        .setPropertyLong("fetchedResultCount", 123)
+                        .build();
+        SearchActionGenericDocument searchActionGenericDocument =
+                new SearchActionGenericDocument(document);
+
+        assertThat(searchActionGenericDocument.getNamespace()).isEqualTo("namespace");
+        assertThat(searchActionGenericDocument.getId()).isEqualTo("search");
+        assertThat(searchActionGenericDocument.getSchemaType()).isEqualTo("builtin:SearchAction");
+        assertThat(searchActionGenericDocument.getCreationTimestampMillis()).isEqualTo(1000);
+        assertThat(searchActionGenericDocument.getActionType())
+                .isEqualTo(ActionConstants.ACTION_TYPE_SEARCH);
+        assertThat(searchActionGenericDocument.getQuery()).isEqualTo("body");
+        assertThat(searchActionGenericDocument.getFetchedResultCount()).isEqualTo(123);
+    }
+
+// @exportToFramework:startStrip()
+    @Test
+    public void testBuild_fromDocumentClass() throws Exception {
+        SearchAction searchAction =
+                new SearchAction.Builder("namespace", "search", /* actionTimestampMillis= */1000)
+                        .setQuery("body")
+                        .setFetchedResultCount(123)
+                        .build();
+        SearchActionGenericDocument searchActionGenericDocument =
+                new SearchActionGenericDocument(GenericDocument.fromDocumentClass(searchAction));
+
+        assertThat(searchActionGenericDocument.getNamespace()).isEqualTo("namespace");
+        assertThat(searchActionGenericDocument.getId()).isEqualTo("search");
+        assertThat(searchActionGenericDocument.getSchemaType()).isEqualTo("builtin:SearchAction");
+        assertThat(searchActionGenericDocument.getCreationTimestampMillis()).isEqualTo(1000);
+        assertThat(searchActionGenericDocument.getActionType())
+                .isEqualTo(ActionConstants.ACTION_TYPE_SEARCH);
+        assertThat(searchActionGenericDocument.getQuery()).isEqualTo("body");
+        assertThat(searchActionGenericDocument.getFetchedResultCount()).isEqualTo(123);
+    }
+// @exportToFramework:endStrip()
+
+    @Test
+    public void testBuild_invalidActionTypeThrowsException() {
+        GenericDocument documentWithoutActionType =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:SearchAction")
+                        .build();
+        IllegalArgumentException e1 = assertThrows(IllegalArgumentException.class,
+                () -> new SearchActionGenericDocument.Builder(documentWithoutActionType));
+        assertThat(e1.getMessage())
+                .isEqualTo("Invalid action type for SearchActionGenericDocument");
+
+        GenericDocument documentWithUnknownActionType =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:SearchAction")
+                        .setPropertyLong("actionType", ActionConstants.ACTION_TYPE_UNKNOWN)
+                        .build();
+        IllegalArgumentException e2 = assertThrows(IllegalArgumentException.class,
+                () -> new SearchActionGenericDocument.Builder(documentWithUnknownActionType));
+        assertThat(e2.getMessage())
+                .isEqualTo("Invalid action type for SearchActionGenericDocument");
+
+        GenericDocument documentWithIncorrectActionType =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:SearchAction")
+                        .setPropertyLong("actionType", ActionConstants.ACTION_TYPE_CLICK)
+                        .build();
+        IllegalArgumentException e3 = assertThrows(IllegalArgumentException.class,
+                () -> new SearchActionGenericDocument.Builder(documentWithIncorrectActionType));
+        assertThat(e3.getMessage())
+                .isEqualTo("Invalid action type for SearchActionGenericDocument");
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/SearchSessionStatsExtractorTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/SearchSessionStatsExtractorTest.java
new file mode 100644
index 0000000..24b59a6
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/usagereporting/SearchSessionStatsExtractorTest.java
@@ -0,0 +1,1349 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.usagereporting;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.PutDocumentsRequest;
+import androidx.appsearch.localstorage.stats.SearchIntentStats;
+import androidx.appsearch.localstorage.stats.SearchSessionStats;
+import androidx.appsearch.usagereporting.ClickAction;
+import androidx.appsearch.usagereporting.SearchAction;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class SearchSessionStatsExtractorTest {
+    private static final String TEST_PACKAGE_NAME = "test.package.name";
+    private static final String TEST_DATABASE = "database";
+
+    @Test
+    public void testExtract() {
+        // Create search action and click action generic documents.
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("tes")
+                        .setFetchedResultCount(20)
+                        .build();
+        GenericDocument clickAction1 =
+                new ClickActionGenericDocument.Builder("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("tes")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(2)
+                        .setTimeStayOnResultMillis(512)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc1")
+                        .build();
+        GenericDocument clickAction2 =
+                new ClickActionGenericDocument.Builder("namespace", "click2", "builtin:ClickAction")
+                        .setCreationTimestampMillis(3000)
+                        .setQuery("tes")
+                        .setResultRankInBlock(3)
+                        .setResultRankGlobal(6)
+                        .setTimeStayOnResultMillis(1024)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc2")
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(5000)
+                        .setQuery("test")
+                        .setFetchedResultCount(10)
+                        .build();
+        GenericDocument clickAction3 =
+                new ClickActionGenericDocument.Builder("namespace", "click3", "builtin:ClickAction")
+                        .setCreationTimestampMillis(6000)
+                        .setQuery("test")
+                        .setResultRankInBlock(2)
+                        .setResultRankGlobal(4)
+                        .setTimeStayOnResultMillis(512)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc3")
+                        .build();
+        GenericDocument clickAction4 =
+                new ClickActionGenericDocument.Builder("namespace", "click4", "builtin:ClickAction")
+                        .setCreationTimestampMillis(7000)
+                        .setQuery("test")
+                        .setResultRankInBlock(4)
+                        .setResultRankGlobal(8)
+                        .setTimeStayOnResultMillis(256)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc4")
+                        .build();
+        GenericDocument clickAction5 =
+                new ClickActionGenericDocument.Builder("namespace", "click5", "builtin:ClickAction")
+                        .setCreationTimestampMillis(8000)
+                        .setQuery("test")
+                        .setResultRankInBlock(6)
+                        .setResultRankGlobal(12)
+                        .setTimeStayOnResultMillis(2048)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc5")
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(
+                        searchAction1,
+                        clickAction1,
+                        clickAction2,
+                        searchAction2,
+                        clickAction3,
+                        clickAction4,
+                        clickAction5);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(2);
+
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("tes");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(20);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).hasSize(2);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getTimestampMillis()).isEqualTo(2000);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getResultRankInBlock()).isEqualTo(1);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getResultRankGlobal()).isEqualTo(2);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(512);
+        assertThat(searchIntentStats0.getClicksStats().get(0).isGoodClick()).isFalse();
+        assertThat(searchIntentStats0.getClicksStats().get(1).getTimestampMillis()).isEqualTo(3000);
+        assertThat(searchIntentStats0.getClicksStats().get(1).getResultRankInBlock()).isEqualTo(3);
+        assertThat(searchIntentStats0.getClicksStats().get(1).getResultRankGlobal()).isEqualTo(6);
+        assertThat(searchIntentStats0.getClicksStats().get(1).getTimeStayOnResultMillis())
+                .isEqualTo(1024);
+        assertThat(searchIntentStats0.getClicksStats().get(1).isGoodClick()).isFalse();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(5000);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("test");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("tes");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(10);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).hasSize(3);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getTimestampMillis()).isEqualTo(6000);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getResultRankInBlock()).isEqualTo(2);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getResultRankGlobal()).isEqualTo(4);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(512);
+        assertThat(searchIntentStats1.getClicksStats().get(0).isGoodClick()).isFalse();
+        assertThat(searchIntentStats1.getClicksStats().get(1).getTimestampMillis()).isEqualTo(7000);
+        assertThat(searchIntentStats1.getClicksStats().get(1).getResultRankInBlock()).isEqualTo(4);
+        assertThat(searchIntentStats1.getClicksStats().get(1).getResultRankGlobal()).isEqualTo(8);
+        assertThat(searchIntentStats1.getClicksStats().get(1).getTimeStayOnResultMillis())
+                .isEqualTo(256);
+        assertThat(searchIntentStats1.getClicksStats().get(1).isGoodClick()).isFalse();
+        assertThat(searchIntentStats1.getClicksStats().get(2).getTimestampMillis()).isEqualTo(8000);
+        assertThat(searchIntentStats1.getClicksStats().get(2).getResultRankInBlock()).isEqualTo(6);
+        assertThat(searchIntentStats1.getClicksStats().get(2).getResultRankGlobal()).isEqualTo(12);
+        assertThat(searchIntentStats1.getClicksStats().get(2).getTimeStayOnResultMillis())
+                .isEqualTo(2048);
+        assertThat(searchIntentStats1.getClicksStats().get(2).isGoodClick()).isTrue();
+    }
+
+    @Test
+    public void testExtract_noSearchActionShouldReturnEmptyList() {
+        // Create search action and click action generic documents.
+        GenericDocument clickAction1 =
+                new ClickActionGenericDocument.Builder("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("tes")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(2)
+                        .setTimeStayOnResultMillis(512)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc1")
+                        .build();
+        GenericDocument clickAction2 =
+                new ClickActionGenericDocument.Builder("namespace", "click2", "builtin:ClickAction")
+                        .setCreationTimestampMillis(3000)
+                        .setQuery("tes")
+                        .setResultRankInBlock(3)
+                        .setResultRankGlobal(6)
+                        .setTimeStayOnResultMillis(1024)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc2")
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(clickAction1, clickAction2);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void testExtract_shouldSkipUnknownActionTypeDocuments() {
+        // Create search action and click action generic documents.
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("tes")
+                        .setFetchedResultCount(20)
+                        .build();
+        GenericDocument clickAction1 =
+                new GenericDocument.Builder<>("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .setPropertyString("query", "tes")
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc1")
+                        .setPropertyLong("resultRankInBlock", 1)
+                        .setPropertyLong("resultRankGlobal", 2)
+                        .setPropertyLong("timeStayOnResultMillis", 512)
+                        .build();
+        GenericDocument clickAction2 =
+                new ClickActionGenericDocument.Builder("namespace", "click2", "builtin:ClickAction")
+                        .setCreationTimestampMillis(3000)
+                        .setQuery("tes")
+                        .setResultRankInBlock(3)
+                        .setResultRankGlobal(6)
+                        .setTimeStayOnResultMillis(1024)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc2")
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, clickAction1, clickAction2);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(1);
+
+        // Since clickAction1 doesn't have property "actionType", it should be skipped without
+        // throwing any exception.
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("tes");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(20);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).hasSize(1);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getTimestampMillis()).isEqualTo(3000);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getResultRankInBlock()).isEqualTo(3);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getResultRankGlobal()).isEqualTo(6);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(1024);
+        assertThat(searchIntentStats0.getClicksStats().get(0).isGoodClick()).isFalse();
+    }
+
+// @exportToFramework:startStrip()
+    @Test
+    public void testExtract_builtFromDocumentClass() throws Exception {
+        SearchAction searchAction1 =
+                new SearchAction.Builder("namespace", "search1", /* actionTimestampMillis= */1000)
+                        .setQuery("tes")
+                        .setFetchedResultCount(20)
+                        .build();
+        ClickAction clickAction1 =
+                new ClickAction.Builder("namespace", "click1", /* actionTimestampMillis= */2000)
+                        .setQuery("tes")
+                        .setReferencedQualifiedId("pkg$db/ns#doc1")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(2)
+                        .setTimeStayOnResultMillis(512)
+                        .build();
+        ClickAction clickAction2 =
+                new ClickAction.Builder("namespace", "click2", /* actionTimestampMillis= */3000)
+                        .setQuery("tes")
+                        .setReferencedQualifiedId("pkg$db/ns#doc2")
+                        .setResultRankInBlock(3)
+                        .setResultRankGlobal(6)
+                        .setTimeStayOnResultMillis(1024)
+                        .build();
+        SearchAction searchAction2 =
+                new SearchAction.Builder("namespace", "search2", /* actionTimestampMillis= */5000)
+                        .setQuery("test")
+                        .setFetchedResultCount(10)
+                        .build();
+        ClickAction clickAction3 =
+                new ClickAction.Builder("namespace", "click3", /* actionTimestampMillis= */6000)
+                        .setQuery("test")
+                        .setReferencedQualifiedId("pkg$db/ns#doc3")
+                        .setResultRankInBlock(2)
+                        .setResultRankGlobal(4)
+                        .setTimeStayOnResultMillis(512)
+                        .build();
+        ClickAction clickAction4 =
+                new ClickAction.Builder("namespace", "click4", /* actionTimestampMillis= */7000)
+                        .setQuery("test")
+                        .setReferencedQualifiedId("pkg$db/ns#doc4")
+                        .setResultRankInBlock(4)
+                        .setResultRankGlobal(8)
+                        .setTimeStayOnResultMillis(256)
+                        .build();
+        ClickAction clickAction5 =
+                new ClickAction.Builder("namespace", "click5", /* actionTimestampMillis= */8000)
+                        .setQuery("test")
+                        .setReferencedQualifiedId("pkg$db/ns#doc5")
+                        .setResultRankInBlock(6)
+                        .setResultRankGlobal(12)
+                        .setTimeStayOnResultMillis(2048)
+                        .build();
+
+        // Use PutDocumentsRequest taken action API to convert document class to GenericDocument.
+        PutDocumentsRequest putDocumentsRequest = new PutDocumentsRequest.Builder()
+                .addTakenActions(searchAction1, clickAction1, clickAction2,
+                        searchAction2, clickAction3, clickAction4, clickAction5)
+                .build();
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE,
+                                putDocumentsRequest.getTakenActionGenericDocuments());
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(2);
+
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("tes");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(20);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).hasSize(2);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getTimestampMillis()).isEqualTo(2000);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getResultRankInBlock()).isEqualTo(1);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getResultRankGlobal()).isEqualTo(2);
+        assertThat(searchIntentStats0.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(512);
+        assertThat(searchIntentStats0.getClicksStats().get(0).isGoodClick()).isFalse();
+        assertThat(searchIntentStats0.getClicksStats().get(1).getTimestampMillis()).isEqualTo(3000);
+        assertThat(searchIntentStats0.getClicksStats().get(1).getResultRankInBlock()).isEqualTo(3);
+        assertThat(searchIntentStats0.getClicksStats().get(1).getResultRankGlobal()).isEqualTo(6);
+        assertThat(searchIntentStats0.getClicksStats().get(1).getTimeStayOnResultMillis())
+                .isEqualTo(1024);
+        assertThat(searchIntentStats0.getClicksStats().get(1).isGoodClick()).isFalse();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(5000);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("test");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("tes");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(10);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).hasSize(3);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getTimestampMillis()).isEqualTo(6000);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getResultRankInBlock()).isEqualTo(2);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getResultRankGlobal()).isEqualTo(4);
+        assertThat(searchIntentStats1.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(512);
+        assertThat(searchIntentStats1.getClicksStats().get(0).isGoodClick()).isFalse();
+        assertThat(searchIntentStats1.getClicksStats().get(1).getTimestampMillis()).isEqualTo(7000);
+        assertThat(searchIntentStats1.getClicksStats().get(1).getResultRankInBlock()).isEqualTo(4);
+        assertThat(searchIntentStats1.getClicksStats().get(1).getResultRankGlobal()).isEqualTo(8);
+        assertThat(searchIntentStats1.getClicksStats().get(1).getTimeStayOnResultMillis())
+                .isEqualTo(256);
+        assertThat(searchIntentStats1.getClicksStats().get(1).isGoodClick()).isFalse();
+        assertThat(searchIntentStats1.getClicksStats().get(2).getTimestampMillis()).isEqualTo(8000);
+        assertThat(searchIntentStats1.getClicksStats().get(2).getResultRankInBlock()).isEqualTo(6);
+        assertThat(searchIntentStats1.getClicksStats().get(2).getResultRankGlobal()).isEqualTo(12);
+        assertThat(searchIntentStats1.getClicksStats().get(2).getTimeStayOnResultMillis())
+                .isEqualTo(2048);
+        assertThat(searchIntentStats1.getClicksStats().get(2).isGoodClick()).isTrue();
+    }
+// @exportToFramework:endStrip()
+
+    @Test
+    public void testExtract_detectAndSkipSearchNoise_appendNewCharacters() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("te")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setCreationTimestampMillis(3000)
+                        .setQuery("tes")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction4 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search4", "builtin:SearchAction")
+                        .setCreationTimestampMillis(3001)
+                        .setQuery("test")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction5 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search5", "builtin:SearchAction")
+                        .setCreationTimestampMillis(10000)
+                        .setQuery("testing")
+                        .setFetchedResultCount(0)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(
+                        searchAction1, searchAction2, searchAction3, searchAction4, searchAction5);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(3);
+
+        // searchAction2, searchAction3 should be considered as noise since they're intermediate
+        // search actions with no clicks. The extractor should create search intents only for the
+        // others.
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("t");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(3001);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("test");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("t");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 2
+        SearchIntentStats searchIntentStats2 = searchSessionStats0.getSearchIntentsStats().get(2);
+        assertThat(searchIntentStats2.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats2.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats2.getTimestampMillis()).isEqualTo(10000);
+        assertThat(searchIntentStats2.getCurrQuery()).isEqualTo("testing");
+        assertThat(searchIntentStats2.getPrevQuery()).isEqualTo("test");
+        assertThat(searchIntentStats2.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats2.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats2.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_detectAndSkipSearchNoise_deleteCharacters() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("testing")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("test")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setCreationTimestampMillis(3000)
+                        .setQuery("tes")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction4 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search4", "builtin:SearchAction")
+                        .setCreationTimestampMillis(3001)
+                        .setQuery("te")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction5 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search5", "builtin:SearchAction")
+                        .setCreationTimestampMillis(10000)
+                        .setQuery("t")
+                        .setFetchedResultCount(0)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(
+                        searchAction1, searchAction2, searchAction3, searchAction4, searchAction5);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(3);
+
+        // searchAction2, searchAction3 should be considered as noise since they're intermediate
+        // search actions with no clicks. The extractor should create search intents only for the
+        // others.
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("testing");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(3001);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("te");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("testing");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT);
+        assertThat(searchIntentStats1.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 2
+        SearchIntentStats searchIntentStats2 = searchSessionStats0.getSearchIntentsStats().get(2);
+        assertThat(searchIntentStats2.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats2.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats2.getTimestampMillis()).isEqualTo(10000);
+        assertThat(searchIntentStats2.getCurrQuery()).isEqualTo("t");
+        assertThat(searchIntentStats2.getPrevQuery()).isEqualTo("te");
+        assertThat(searchIntentStats2.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats2.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats2.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_occursAfterThresholdShouldNotBeSearchNoise() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(3001)
+                        .setQuery("te")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setCreationTimestampMillis(10000)
+                        .setQuery("test")
+                        .setFetchedResultCount(0)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, searchAction2, searchAction3);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(3);
+
+        // searchAction2 should not be considered as noise since it occurs after the threshold from
+        // searchAction1 (and therefore not intermediate search actions).
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("t");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(3001);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("te");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("t");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 2
+        SearchIntentStats searchIntentStats2 = searchSessionStats0.getSearchIntentsStats().get(2);
+        assertThat(searchIntentStats2.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats2.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats2.getTimestampMillis()).isEqualTo(10000);
+        assertThat(searchIntentStats2.getCurrQuery()).isEqualTo("test");
+        assertThat(searchIntentStats2.getPrevQuery()).isEqualTo("te");
+        assertThat(searchIntentStats2.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats2.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats2.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_nonPrefixQueryStringShouldNotBeSearchNoise() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("apple")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1500)
+                        .setQuery("application")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("email")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction4 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search4", "builtin:SearchAction")
+                        .setCreationTimestampMillis(10000)
+                        .setQuery("google")
+                        .setFetchedResultCount(0)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, searchAction2, searchAction3, searchAction4);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(4);
+
+        // searchAction2 and searchAction3 should not be considered as noise since neither query
+        // string is a prefix of the previous one (and therefore not intermediate search actions).
+
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("apple");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(1500);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("application");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("apple");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 2
+        SearchIntentStats searchIntentStats2 = searchSessionStats0.getSearchIntentsStats().get(2);
+        assertThat(searchIntentStats2.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats2.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats2.getTimestampMillis()).isEqualTo(2000);
+        assertThat(searchIntentStats2.getCurrQuery()).isEqualTo("email");
+        assertThat(searchIntentStats2.getPrevQuery()).isEqualTo("application");
+        assertThat(searchIntentStats2.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats2.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT);
+        assertThat(searchIntentStats2.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 3
+        SearchIntentStats searchIntentStats3 = searchSessionStats0.getSearchIntentsStats().get(3);
+        assertThat(searchIntentStats3.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats3.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats3.getTimestampMillis()).isEqualTo(10000);
+        assertThat(searchIntentStats3.getCurrQuery()).isEqualTo("google");
+        assertThat(searchIntentStats3.getPrevQuery()).isEqualTo("email");
+        assertThat(searchIntentStats3.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats3.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT);
+        assertThat(searchIntentStats3.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_lastSearchActionShouldNotBeSearchNoise() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("te")
+                        .setFetchedResultCount(0)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, searchAction2);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(2);
+
+        // searchAction2 should not be considered as noise since it is the last search action (and
+        // therefore not an intermediate search action).
+
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("t");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(2000);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("te");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("t");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_lastSearchActionOfRelatedSearchSequenceShouldNotBeSearchNoise() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("te")
+                        .setFetchedResultCount(0)
+                        .build();
+        GenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setCreationTimestampMillis(602001)
+                        .setQuery("test")
+                        .setFetchedResultCount(0)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, searchAction2, searchAction3);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        // searchAction2 should not be considered as noise:
+        // - searchAction3 is independent from searchAction2 and therefore forms an independent
+        //   search session.
+        // - So searchAction2 is the last search action of its search session (and therefore not an
+        // intermediate search action).
+        assertThat(result).hasSize(2);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(2);
+
+        SearchSessionStats searchSessionStats1 = result.get(1);
+        assertThat(searchSessionStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats1.getSearchIntentsStats()).hasSize(1);
+
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("t");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(2000);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("te");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("t");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).isEmpty();
+
+        // Search session 1, search intent 0
+        SearchIntentStats searchIntentStats2 = searchSessionStats1.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats2.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats2.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats2.getTimestampMillis()).isEqualTo(602001);
+        assertThat(searchIntentStats2.getCurrQuery()).isEqualTo("test");
+        assertThat(searchIntentStats2.getPrevQuery()).isNull();
+        assertThat(searchIntentStats2.getNumResultsFetched()).isEqualTo(0);
+        assertThat(searchIntentStats2.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats2.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_withClickActionShouldNotBeSearchNoise() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(20)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(2000)
+                        .setQuery("te")
+                        .setFetchedResultCount(10)
+                        .build();
+        GenericDocument clickAction1 =
+                new ClickActionGenericDocument.Builder("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2050)
+                        .setQuery("te")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(2)
+                        .setTimeStayOnResultMillis(512)
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#doc1")
+                        .build();
+        GenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setCreationTimestampMillis(10000)
+                        .setQuery("test")
+                        .setFetchedResultCount(5)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, searchAction2, clickAction1, searchAction3);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(3);
+
+        // Even though searchAction2 is an intermediate search action, it should not be considered
+        // as noise since there is at least 1 valid click action associated with it.
+
+        // Search session 0, search intent 0
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("t");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(20);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 0, search intent 1
+        SearchIntentStats searchIntentStats1 = searchSessionStats0.getSearchIntentsStats().get(1);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(2000);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("te");
+        assertThat(searchIntentStats1.getPrevQuery()).isEqualTo("t");
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(10);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats1.getClicksStats()).hasSize(1);
+
+        // Search session 0, search intent 2
+        SearchIntentStats searchIntentStats2 = searchSessionStats0.getSearchIntentsStats().get(2);
+        assertThat(searchIntentStats2.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats2.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats2.getTimestampMillis()).isEqualTo(10000);
+        assertThat(searchIntentStats2.getCurrQuery()).isEqualTo("test");
+        assertThat(searchIntentStats2.getPrevQuery()).isEqualTo("te");
+        assertThat(searchIntentStats2.getNumResultsFetched()).isEqualTo(5);
+        assertThat(searchIntentStats2.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+        assertThat(searchIntentStats2.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_independentSearchIntentShouldStartNewSearchSession() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(20)
+                        .build();
+        GenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setCreationTimestampMillis(601001)
+                        .setQuery("te")
+                        .setFetchedResultCount(10)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, searchAction2);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        // Since time difference between searchAction1 and searchAction2 exceeds the threshold,
+        // searchAction2 should be considered as an independent search intent and therefore a new
+        // search session stats is created.
+        assertThat(result).hasSize(2);
+
+        // Search session 0
+        SearchSessionStats searchSessionStats0 = result.get(0);
+        assertThat(searchSessionStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats0.getSearchIntentsStats()).hasSize(1);
+        SearchIntentStats searchIntentStats0 = searchSessionStats0.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats0.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats0.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats0.getTimestampMillis()).isEqualTo(1000);
+        assertThat(searchIntentStats0.getCurrQuery()).isEqualTo("t");
+        assertThat(searchIntentStats0.getPrevQuery()).isNull();
+        assertThat(searchIntentStats0.getNumResultsFetched()).isEqualTo(20);
+        assertThat(searchIntentStats0.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats0.getClicksStats()).isEmpty();
+
+        // Search session 1
+        SearchSessionStats searchSessionStats1 = result.get(1);
+        assertThat(searchSessionStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats1.getSearchIntentsStats()).hasSize(1);
+        SearchIntentStats searchIntentStats1 = searchSessionStats1.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats1.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchIntentStats1.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchIntentStats1.getTimestampMillis()).isEqualTo(601001);
+        assertThat(searchIntentStats1.getCurrQuery()).isEqualTo("te");
+        assertThat(searchIntentStats1.getPrevQuery()).isNull();
+        assertThat(searchIntentStats1.getNumResultsFetched()).isEqualTo(10);
+        assertThat(searchIntentStats1.getQueryCorrectionType())
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+        assertThat(searchIntentStats1.getClicksStats()).isEmpty();
+    }
+
+    @Test
+    public void testExtract_shouldSetIsGoodClick() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(20)
+                        .build();
+        GenericDocument clickAction1 =
+                new ClickActionGenericDocument.Builder("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .setTimeStayOnResultMillis(2001)
+                        .build();
+        GenericDocument clickAction2 =
+                new ClickActionGenericDocument.Builder("namespace", "click2", "builtin:ClickAction")
+                        .setCreationTimestampMillis(4500)
+                        .setTimeStayOnResultMillis(1999)
+                        .build();
+        GenericDocument clickAction3 =
+                new ClickActionGenericDocument.Builder("namespace", "click3", "builtin:ClickAction")
+                        .setCreationTimestampMillis(7000)
+                        .setTimeStayOnResultMillis(1)
+                        .build();
+        GenericDocument clickAction4 =
+                new ClickActionGenericDocument.Builder("namespace", "click4", "builtin:ClickAction")
+                        .setCreationTimestampMillis(7500)
+                        .setTimeStayOnResultMillis(2000)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(
+                        searchAction1, clickAction1, clickAction2, clickAction3, clickAction4);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats = result.get(0);
+        assertThat(searchSessionStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats.getSearchIntentsStats()).hasSize(1);
+
+        SearchIntentStats searchIntentStats = searchSessionStats.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats.getClicksStats()).hasSize(4);
+
+        assertThat(searchIntentStats.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(2001);
+        assertThat(searchIntentStats.getClicksStats().get(0).isGoodClick()).isTrue();
+
+        assertThat(searchIntentStats.getClicksStats().get(1).getTimeStayOnResultMillis())
+                .isEqualTo(1999);
+        assertThat(searchIntentStats.getClicksStats().get(1).isGoodClick()).isFalse();
+
+        assertThat(searchIntentStats.getClicksStats().get(2).getTimeStayOnResultMillis())
+                .isEqualTo(1);
+        assertThat(searchIntentStats.getClicksStats().get(2).isGoodClick()).isFalse();
+
+        assertThat(searchIntentStats.getClicksStats().get(3).getTimeStayOnResultMillis())
+                .isEqualTo(2000);
+        assertThat(searchIntentStats.getClicksStats().get(3).isGoodClick()).isTrue();
+    }
+
+    @Test
+    public void testExtract_unsetTimeStayOnResultShouldBeGoodClick() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(20)
+                        .build();
+        GenericDocument clickAction1 =
+                new ClickActionGenericDocument.Builder("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, clickAction1);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats = result.get(0);
+        assertThat(searchSessionStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats.getSearchIntentsStats()).hasSize(1);
+
+        SearchIntentStats searchIntentStats = searchSessionStats.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats.getClicksStats()).hasSize(1);
+
+        assertThat(result).hasSize(1);
+        assertThat(searchIntentStats.getClicksStats()).hasSize(1);
+
+        assertThat(searchIntentStats.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(0);
+        assertThat(searchIntentStats.getClicksStats().get(0).isGoodClick()).isTrue();
+    }
+
+    @Test
+    public void testExtract_nonPositiveTimeStayOnResultShouldBeGoodClick() {
+        GenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setQuery("t")
+                        .setFetchedResultCount(20)
+                        .build();
+        GenericDocument clickAction1 =
+                new ClickActionGenericDocument.Builder("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .setTimeStayOnResultMillis(-1)
+                        .build();
+        GenericDocument clickAction2 =
+                new ClickActionGenericDocument.Builder("namespace", "click2", "builtin:ClickAction")
+                        .setCreationTimestampMillis(3000)
+                        .setTimeStayOnResultMillis(0)
+                        .build();
+
+        List<GenericDocument> takenActionGenericDocuments =
+                Arrays.asList(searchAction1, clickAction1, clickAction2);
+
+        List<SearchSessionStats> result =
+                new SearchSessionStatsExtractor()
+                        .extract(TEST_PACKAGE_NAME, TEST_DATABASE, takenActionGenericDocuments);
+
+        assertThat(result).hasSize(1);
+
+        SearchSessionStats searchSessionStats = result.get(0);
+        assertThat(searchSessionStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(searchSessionStats.getDatabase()).isEqualTo(TEST_DATABASE);
+        assertThat(searchSessionStats.getSearchIntentsStats()).hasSize(1);
+
+        SearchIntentStats searchIntentStats = searchSessionStats.getSearchIntentsStats().get(0);
+        assertThat(searchIntentStats.getClicksStats()).hasSize(2);
+
+        assertThat(searchIntentStats.getClicksStats().get(0).getTimeStayOnResultMillis())
+                .isEqualTo(-1);
+        assertThat(searchIntentStats.getClicksStats().get(0).isGoodClick()).isTrue();
+
+        assertThat(searchIntentStats.getClicksStats().get(1).getTimeStayOnResultMillis())
+                .isEqualTo(0);
+        assertThat(searchIntentStats.getClicksStats().get(1).isGoodClick()).isTrue();
+    }
+
+    @Test
+    public void testGetQueryCorrectionType_unknown() {
+        SearchActionGenericDocument searchAction =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setQuery("test")
+                        .build();
+        SearchActionGenericDocument searchActionWithNullQueryStr =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .build();
+
+        // Query correction type should be unknown if the current search action's query string is
+        // null.
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchActionWithNullQueryStr,
+                        /* prevSearchAction= */ null))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_UNKNOWN);
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchActionWithNullQueryStr,
+                        /* prevSearchAction= */ searchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_UNKNOWN);
+
+        // Query correction type should be unknown if the previous search action contains null query
+        // string.
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction,
+                        /* prevSearchAction= */ searchActionWithNullQueryStr))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_UNKNOWN);
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchActionWithNullQueryStr,
+                        /* prevSearchAction= */ searchActionWithNullQueryStr))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void testGetQueryCorrectionType_firstQuery() {
+        SearchActionGenericDocument currSearchAction =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setQuery("test")
+                        .build();
+
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        currSearchAction, /* prevSearchAction= */ null))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY);
+    }
+
+    @Test
+    public void testGetQueryCorrectionType_refinement() {
+        SearchActionGenericDocument prevSearchAction =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "baseSearch", "builtin:SearchAction")
+                        .setQuery("test")
+                        .build();
+
+        // Append 1 new character should be query refinement.
+        SearchActionGenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setQuery("teste")
+                        .build();
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction1, prevSearchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+
+        // Append 2 new characters should be query refinement.
+        SearchActionGenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setQuery("tester")
+                        .build();
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction2, prevSearchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+
+        // Backspace 1 character should be query refinement.
+        SearchActionGenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setQuery("tes")
+                        .build();
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction3, prevSearchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+
+        // Backspace 1 character and append new character(s) should be query refinement.
+        SearchActionGenericDocument searchAction4 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search4", "builtin:SearchAction")
+                        .setQuery("tesla")
+                        .build();
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction4, prevSearchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT);
+    }
+
+    @Test
+    public void testGetQueryCorrectionType_abandonment() {
+        SearchActionGenericDocument prevSearchAction =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "baseSearch", "builtin:SearchAction")
+                        .setQuery("test")
+                        .build();
+
+        // Completely different query should be query abandonment.
+        SearchActionGenericDocument searchAction1 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search1", "builtin:SearchAction")
+                        .setQuery("unit")
+                        .build();
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction1, prevSearchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT);
+
+        // Backspace 2 characters should be query abandonment.
+        SearchActionGenericDocument searchAction2 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search2", "builtin:SearchAction")
+                        .setQuery("te")
+                        .build();
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction2, prevSearchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT);
+
+        // Backspace 2 characters and append new character(s) should be query abandonment.
+        SearchActionGenericDocument searchAction3 =
+                new SearchActionGenericDocument.Builder(
+                        "namespace", "search3", "builtin:SearchAction")
+                        .setQuery("texas")
+                        .build();
+        assertThat(
+                SearchSessionStatsExtractor.getQueryCorrectionType(
+                        /* currSearchAction= */ searchAction3, prevSearchAction))
+                .isEqualTo(SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT);
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationFromV2Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationFromV2Test.java
new file mode 100644
index 0000000..e50274b
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationFromV2Test.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.localstorage.visibilitystore;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.GetSchemaResponse;
+import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
+import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SetSchemaRequest;
+import androidx.appsearch.app.VisibilityPermissionConfig;
+import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.localstorage.AppSearchConfigImpl;
+import androidx.appsearch.localstorage.AppSearchImpl;
+import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
+import androidx.appsearch.localstorage.OptimizeStrategy;
+import androidx.appsearch.localstorage.UnlimitedLimitConfig;
+import androidx.appsearch.localstorage.util.PrefixUtil;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.util.Collections;
+
+// "V2 schema" refers to V2 of the VisibilityDocument schema, but no Visibility overlay schema
+// present. Simulates backwards compatibility situations.
+public class VisibilityStoreMigrationFromV2Test {
+
+    /**
+     * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
+     */
+    private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
+
+    @Rule
+    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    private File mFile;
+
+    @Before
+    public void setUp() throws Exception {
+        // Give ourselves global query permissions
+        mFile = mTemporaryFolder.newFolder();
+    }
+
+    @Test
+    public void testVisibilityMigration_from2() throws Exception {
+        // As such, we can treat V2 documents as V3 documents when upgrading, but we need to test
+        // this.
+
+        // Values for a "foo" client
+        String packageNameFoo = "packageFoo";
+        byte[] sha256CertFoo = new byte[32];
+        PackageIdentifier packageIdentifierFoo =
+                new PackageIdentifier(packageNameFoo, sha256CertFoo);
+
+        // Values for a "bar" client
+        String packageNameBar = "packageBar";
+        byte[] sha256CertBar = new byte[32];
+        PackageIdentifier packageIdentifierBar =
+                new PackageIdentifier(packageNameBar, sha256CertBar);
+
+        // Create AppSearchImpl with visibility document version 2;
+        AppSearchImpl appSearchImplInV2 = AppSearchImpl.create(mFile,
+                new AppSearchConfigImpl(new UnlimitedLimitConfig(),
+                        new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
+
+        // Erase overlay schemas since it doesn't exist in released V2 schema.
+        InternalSetSchemaResponse internalSetAndroidVSchemaResponse = appSearchImplInV2.setSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                // no overlay schema
+                ImmutableList.of(),
+                /*prefixedVisibilityBundles=*/ Collections.emptyList(),
+                /*forceOverride=*/ true, // force push the old version into disk
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST,
+                /*setSchemaStatsBuilder=*/ null);
+        assertThat(internalSetAndroidVSchemaResponse.isSuccess()).isTrue();
+
+        GetSchemaResponse getSchemaResponse = appSearchImplInV2.getSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                new CallerAccess(/*callingPackageName=*/VisibilityStore.VISIBILITY_PACKAGE_NAME));
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                VisibilityPermissionConfig.SCHEMA);
+        GetSchemaResponse getAndroidVOverlaySchemaResponse = appSearchImplInV2.getSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                new CallerAccess(/*callingPackageName=*/VisibilityStore.VISIBILITY_PACKAGE_NAME));
+        assertThat(getAndroidVOverlaySchemaResponse.getSchemas()).isEmpty();
+
+        // Build deprecated visibility documents in version 2
+        String prefix = PrefixUtil.createPrefix("package", "database");
+        InternalVisibilityConfig visibilityConfigV2 = new InternalVisibilityConfig.Builder(
+                prefix + "Schema")
+                .setNotDisplayedBySystem(true)
+                .addVisibleToPackage(
+                        new PackageIdentifier(packageNameFoo, sha256CertFoo))
+                .addVisibleToPackage(
+                        new PackageIdentifier(packageNameBar, sha256CertBar))
+                .addVisibleToPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_SMS,
+                                SetSchemaRequest.READ_CALENDAR))
+                .addVisibleToPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA))
+                .addVisibleToPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+                .build();
+        GenericDocument visibilityDocumentV2 =
+                VisibilityToDocumentConverter.createVisibilityDocument(visibilityConfigV2);
+
+        // Set client schema into AppSearchImpl with empty VisibilityDocument since we need to
+        // directly put old version of VisibilityDocument.
+        InternalSetSchemaResponse internalSetSchemaResponse = appSearchImplInV2.setSchema(
+                "package",
+                "database",
+                ImmutableList.of(
+                        new AppSearchSchema.Builder("Schema").build()),
+                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*forceOverride=*/ false,
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
+        assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+        // Put deprecated visibility documents in version 2 to AppSearchImpl
+        appSearchImplInV2.putDocument(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                visibilityDocumentV2,
+                /*sendChangeNotifications=*/ false,
+                /*logger=*/null);
+
+        // Persist to disk and re-open the AppSearchImpl
+        appSearchImplInV2.close();
+        AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile,
+                new AppSearchConfigImpl(new UnlimitedLimitConfig(),
+                        new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
+
+        InternalVisibilityConfig actualConfig =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        appSearchImpl.getDocument(
+                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix + "Schema",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                /*androidVOverlayDocument=*/null);
+
+        assertThat(actualConfig.isNotDisplayedBySystem()).isTrue();
+        assertThat(actualConfig.getVisibilityConfig().getAllowedPackages())
+                .containsExactly(packageIdentifierFoo, packageIdentifierBar);
+        assertThat(actualConfig.getVisibilityConfig().getRequiredPermissions())
+                .containsExactlyElementsIn(ImmutableSet.of(
+                        ImmutableSet.of(SetSchemaRequest.READ_SMS, SetSchemaRequest.READ_CALENDAR),
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA),
+                        ImmutableSet.of(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA)));
+
+        // Check that the visibility overlay schema was added.
+        getSchemaResponse = appSearchImpl.getSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                new CallerAccess(/*callingPackageName=*/VisibilityStore.VISIBILITY_PACKAGE_NAME));
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                VisibilityPermissionConfig.SCHEMA);
+        getAndroidVOverlaySchemaResponse = appSearchImpl.getSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                new CallerAccess(/*callingPackageName=*/VisibilityStore.VISIBILITY_PACKAGE_NAME));
+        assertThat(getAndroidVOverlaySchemaResponse.getSchemas()).containsExactly(
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA);
+
+        // But no overlay document was created.
+        AppSearchException e = assertThrows(AppSearchException.class,
+                 () -> appSearchImpl.getDocument(
+                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                        VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                         VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                        /*id=*/ prefix + "Schema",
+                        /*typePropertyPaths=*/ Collections.emptyMap()));
+        assertThat(e).hasMessageThat().contains("not found");
+
+        appSearchImpl.close();
+    }
+}
+
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
index 7b03a10..c91f51c 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
@@ -29,8 +29,8 @@
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
 import androidx.appsearch.app.PackageIdentifier;
-import androidx.appsearch.app.VisibilityDocument;
 import androidx.appsearch.localstorage.AppSearchConfigImpl;
 import androidx.appsearch.localstorage.AppSearchImpl;
 import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
@@ -82,19 +82,21 @@
         // "schema1" is accessible to packageFoo and "schema2" is accessible to packageBar.
         String prefix = PrefixUtil.createPrefix("package", "database");
         GenericDocument deprecatedVisibilityToPackageFoo = new GenericDocument.Builder<>(
-                VisibilityDocument.NAMESPACE, "", DEPRECATED_PACKAGE_SCHEMA_TYPE)
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE, "",
+                DEPRECATED_PACKAGE_SCHEMA_TYPE)
                 .setPropertyString(DEPRECATED_ACCESSIBLE_SCHEMA_PROPERTY, prefix + "Schema1")
                 .setPropertyString(DEPRECATED_PACKAGE_NAME_PROPERTY, packageNameFoo)
                 .setPropertyBytes(DEPRECATED_SHA_256_CERT_PROPERTY, sha256CertFoo)
                 .build();
         GenericDocument deprecatedVisibilityToPackageBar = new GenericDocument.Builder<>(
-                VisibilityDocument.NAMESPACE, "", DEPRECATED_PACKAGE_SCHEMA_TYPE)
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE, "",
+                DEPRECATED_PACKAGE_SCHEMA_TYPE)
                 .setPropertyString(DEPRECATED_ACCESSIBLE_SCHEMA_PROPERTY, prefix + "Schema2")
                 .setPropertyString(DEPRECATED_PACKAGE_NAME_PROPERTY, packageNameBar)
                 .setPropertyBytes(DEPRECATED_SHA_256_CERT_PROPERTY, sha256CertBar)
                 .build();
         GenericDocument deprecatedVisibilityDocument = new GenericDocument.Builder<>(
-                VisibilityDocument.NAMESPACE,
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                 VisibilityStoreMigrationHelperFromV0.getDeprecatedVisibilityDocumentId(
                         "package", "database"),
                 DEPRECATED_VISIBILITY_SCHEMA_TYPE)
@@ -131,34 +133,41 @@
         AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile,
                 new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
-        VisibilityDocument actualDocument1 = new VisibilityDocument.Builder(
+        GenericDocument actualDocument1 =
                 appSearchImpl.getDocument(
                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
                         VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                         /*id=*/ prefix + "Schema1",
-                        /*typePropertyPaths=*/ Collections.emptyMap())).build();
-        VisibilityDocument actualDocument2 = new VisibilityDocument.Builder(
+                        /*typePropertyPaths=*/ Collections.emptyMap());
+        GenericDocument actualDocument2 =
                 appSearchImpl.getDocument(
                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
                         VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                         /*id=*/ prefix + "Schema2",
-                        /*typePropertyPaths=*/ Collections.emptyMap())).build();
+                        /*typePropertyPaths=*/ Collections.emptyMap());
 
-        VisibilityDocument expectedDocument1 =
-                new VisibilityDocument.Builder(/*id=*/ prefix + "Schema1")
+        GenericDocument expectedDocument1 = VisibilityToDocumentConverter.createVisibilityDocument(
+                new InternalVisibilityConfig.Builder(prefix + "Schema1")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier(packageNameFoo, sha256CertFoo))
-                        .build();
-        VisibilityDocument expectedDocument2 =
-                new VisibilityDocument.Builder(/*id=*/ prefix + "Schema2")
+                        .build());
+        GenericDocument expectedDocument2 = VisibilityToDocumentConverter.createVisibilityDocument(
+                new InternalVisibilityConfig.Builder(prefix + "Schema2")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier(packageNameBar, sha256CertBar))
-                        .build();
+                        .build());
+
+        // Ignore the creation timestamp
+        actualDocument1 = new GenericDocument.Builder<>(actualDocument1)
+                .setCreationTimestampMillis(0).build();
+        actualDocument2 = new GenericDocument.Builder<>(actualDocument2)
+                .setCreationTimestampMillis(0).build();
+
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
         assertThat(actualDocument2).isEqualTo(expectedDocument2);
         appSearchImpl.close();
@@ -197,8 +206,8 @@
         AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile,
                 new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         InternalSetSchemaResponse internalSetSchemaResponse = appSearchImpl.setSchema(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
index a70da64..4b837ae 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
@@ -23,9 +23,9 @@
 
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
 import androidx.appsearch.app.PackageIdentifier;
 import androidx.appsearch.app.SetSchemaRequest;
-import androidx.appsearch.app.VisibilityDocument;
 import androidx.appsearch.localstorage.AppSearchConfigImpl;
 import androidx.appsearch.localstorage.AppSearchImpl;
 import androidx.appsearch.localstorage.LocalStorageIcingOptionsConfig;
@@ -66,17 +66,21 @@
         // Values for a "foo" client
         String packageNameFoo = "packageFoo";
         byte[] sha256CertFoo = new byte[32];
+        PackageIdentifier packageIdentifierFoo =
+                new PackageIdentifier(packageNameFoo, sha256CertFoo);
 
         // Values for a "bar" client
         String packageNameBar = "packageBar";
         byte[] sha256CertBar = new byte[32];
+        PackageIdentifier packageIdentifierBar =
+                new PackageIdentifier(packageNameBar, sha256CertBar);
 
         // Create AppSearchImpl with visibility document version 1;
         AppSearchImpl appSearchImplInV1 = AppSearchImpl.create(mFile,
                 new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         InternalSetSchemaResponse internalSetSchemaResponse = appSearchImplInV1.setSchema(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
@@ -125,24 +129,24 @@
         AppSearchImpl appSearchImpl = AppSearchImpl.create(mFile,
                 new AppSearchConfigImpl(new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()), /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
 
-        VisibilityDocument actualDocument = new VisibilityDocument.Builder(
-                appSearchImpl.getDocument(
-                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                        VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
-                        /*id=*/ prefix + "Schema",
-                        /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        InternalVisibilityConfig actualConfig =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        appSearchImpl.getDocument(
+                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ prefix + "Schema",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                /*androidVOverlayDocument=*/null);
 
-        assertThat(actualDocument.isNotDisplayedBySystem()).isTrue();
-        assertThat(actualDocument.getPackageNames()).asList().containsExactly(packageNameFoo,
-                packageNameBar);
-        assertThat(actualDocument.getSha256Certs()).isEqualTo(
-                new byte[][] {sha256CertFoo, sha256CertBar});
-        assertThat(actualDocument.getVisibleToPermissions()).containsExactlyElementsIn(
-                ImmutableSet.of(
+        assertThat(actualConfig.isNotDisplayedBySystem()).isTrue();
+        assertThat(actualConfig.getVisibilityConfig().getAllowedPackages())
+                .containsExactly(packageIdentifierFoo, packageIdentifierBar);
+        assertThat(actualConfig.getVisibilityConfig().getRequiredPermissions())
+                .containsExactlyElementsIn(ImmutableSet.of(
                         ImmutableSet.of(SetSchemaRequest.READ_SMS, SetSchemaRequest.READ_CALENDAR),
                         ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA),
                         ImmutableSet.of(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA)));
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
index 056acea..5ac5bda 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
@@ -21,9 +21,13 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
 import androidx.appsearch.app.PackageIdentifier;
-import androidx.appsearch.app.VisibilityDocument;
+import androidx.appsearch.app.SchemaVisibilityConfig;
+import androidx.appsearch.app.VisibilityPermissionConfig;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.AppSearchConfigImpl;
 import androidx.appsearch.localstorage.AppSearchImpl;
@@ -52,22 +56,21 @@
     private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
     @Rule
     public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-    private File mAppSearchDir;
     private AppSearchImpl mAppSearchImpl;
     private VisibilityStore mVisibilityStore;
 
     @Before
     public void setUp() throws Exception {
-        mAppSearchDir = mTemporaryFolder.newFolder();
+        File appSearchDir = mTemporaryFolder.newFolder();
         mAppSearchImpl = AppSearchImpl.create(
-                mAppSearchDir,
+                appSearchDir,
                 new AppSearchConfigImpl(
                         new UnlimitedLimitConfig(),
                         new LocalStorageIcingOptionsConfig()
                 ),
                 /*initStatsBuilder=*/ null,
-                ALWAYS_OPTIMIZE,
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                ALWAYS_OPTIMIZE);
         mVisibilityStore = new VisibilityStore(mAppSearchImpl);
     }
 
@@ -99,55 +102,83 @@
     }
 
     @Test
-    public void testSetAndGetVisibility() throws Exception {
-        String prefix = PrefixUtil.createPrefix("packageName", "databaseName");
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder(prefix + "Email")
-                .setNotDisplayedBySystem(true)
-                .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .build();
-        mVisibilityStore.setVisibility(ImmutableList.of(visibilityDocument));
-
-        assertThat(mVisibilityStore.getVisibility(prefix + "Email"))
-                .isEqualTo(visibilityDocument);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =
-                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
+    public void testSetVisibilitySchema() throws Exception {
+        GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
+                new CallerAccess(VisibilityStore.VISIBILITY_PACKAGE_NAME));
+
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                VisibilityPermissionConfig.SCHEMA);
+
+
+        GetSchemaResponse getAndroidVOverlaySchemaResponse = mAppSearchImpl.getSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                new CallerAccess(VisibilityStore.VISIBILITY_PACKAGE_NAME));
+
+        assertThat(getAndroidVOverlaySchemaResponse.getSchemas()).containsExactly(
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA);
+    }
+
+    @Test
+    public void testSetAndGetVisibility() throws Exception {
+        String prefix = PrefixUtil.createPrefix("packageName", "databaseName");
+        InternalVisibilityConfig visibilityConfig =
+                new InternalVisibilityConfig.Builder(prefix + "Email")
+                        .setNotDisplayedBySystem(true)
+                        .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                        .build();
+        mVisibilityStore.setVisibility(ImmutableList.of(visibilityConfig));
+
+        assertThat(mVisibilityStore.getVisibility(prefix + "Email"))
+                .isEqualTo(visibilityConfig);
+        // Verify the VisibilityConfig is saved to AppSearchImpl.
+        GenericDocument actualDocument = mAppSearchImpl.getDocument(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                 /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
-        assertThat(actualDocument).isEqualTo(visibilityDocument);
+                /*typePropertyPaths=*/ Collections.emptyMap());
+        // Ignore the creation timestamp
+        actualDocument =
+                new GenericDocument.Builder<>(actualDocument).setCreationTimestampMillis(0).build();
+
+        assertThat(actualDocument).isEqualTo(
+                VisibilityToDocumentConverter.createVisibilityDocument(visibilityConfig));
     }
 
     @Test
     public void testRemoveVisibility() throws Exception {
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
-        mVisibilityStore.setVisibility(ImmutableList.of(visibilityDocument));
+        mVisibilityStore.setVisibility(ImmutableList.of(visibilityConfig));
 
         assertThat(mVisibilityStore.getVisibility("Email"))
-                .isEqualTo(visibilityDocument);
-        // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument = new VisibilityDocument.Builder(
-                mAppSearchImpl.getDocument(
-                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                VisibilityDocument.NAMESPACE,
-                /*id=*/ "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap())).build();
-        assertThat(actualDocument).isEqualTo(visibilityDocument);
+                .isEqualTo(visibilityConfig);
+        // Verify the VisibilityConfig is saved to AppSearchImpl.
+        InternalVisibilityConfig actualConfig =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        mAppSearchImpl.getDocument(
+                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                                /*id=*/ "Email",
+                                /*typePropertyPaths=*/ Collections.emptyMap()),
+                        /*androidVOverlayDocument=*/null);
+        assertThat(actualConfig).isEqualTo(visibilityConfig);
 
-        mVisibilityStore.removeVisibility(ImmutableSet.of(visibilityDocument.getId()));
+        mVisibilityStore.removeVisibility(ImmutableSet.of(visibilityConfig.getSchemaType()));
         assertThat(mVisibilityStore.getVisibility("Email")).isNull();
-        // Verify the VisibilityDocument is removed from AppSearchImpl.
+        // Verify the VisibilityConfig is removed from AppSearchImpl.
         AppSearchException e = assertThrows(AppSearchException.class,
                 () -> mAppSearchImpl.getDocument(
                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
                         VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                         /*id=*/ "Email",
                         /*typePropertyPaths=*/ Collections.emptyMap()));
         assertThat(e).hasMessageThat().contains(
@@ -158,17 +189,17 @@
     public void testRecoverBrokenVisibilitySchema() throws Exception {
         // Create a broken schema which could be recovered to the latest schema in a compatible
         // change. Since we won't set force override to true to recover the broken case.
-        AppSearchSchema brokenSchema = new AppSearchSchema.Builder(VisibilityDocument.SCHEMA_TYPE)
-                .build();
+        AppSearchSchema brokenSchema = new AppSearchSchema.Builder(
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA_TYPE).build();
 
         // Index a broken schema into AppSearch, use the latest version to make it broken.
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 Collections.singletonList(brokenSchema),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
-                /*version=*/ VisibilityDocument.SCHEMA_VERSION_LATEST,
+                /*version=*/ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
                 /*setSchemaStatsBuilder=*/ null);
         assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
         // Create VisibilityStore should recover the broken schema
@@ -176,13 +207,270 @@
 
         // We should be able to set and get Visibility settings.
         String prefix = PrefixUtil.createPrefix("packageName", "databaseName");
-        VisibilityDocument visibilityDocument = new VisibilityDocument.Builder(prefix + "Email")
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder(
+                prefix + "Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
                 .build();
-        mVisibilityStore.setVisibility(ImmutableList.of(visibilityDocument));
+        mVisibilityStore.setVisibility(ImmutableList.of(visibilityConfig));
 
         assertThat(mVisibilityStore.getVisibility(prefix + "Email"))
-                .isEqualTo(visibilityDocument);
+                .isEqualTo(visibilityConfig);
+    }
+
+    @Test
+    public void testSetGetAndRemoveOverlayVisibility() throws Exception {
+        String prefix = PrefixUtil.createPrefix("packageName", "databaseName");
+        SchemaVisibilityConfig nestedvisibilityConfig = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+
+        InternalVisibilityConfig visibilityConfig =
+                new InternalVisibilityConfig.Builder(prefix + "Email")
+                        .addVisibleToConfig(nestedvisibilityConfig)
+                        .build();
+
+        mVisibilityStore.setVisibility(ImmutableList.of(visibilityConfig));
+
+        assertThat(mVisibilityStore.getVisibility(prefix + "Email"))
+                .isEqualTo(visibilityConfig);
+        // Verify the VisibilityConfig is saved to AppSearchImpl.
+        GenericDocument visibleToConfigOverlay = mAppSearchImpl.getDocument(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                /*id=*/ prefix + "Email",
+                /*typePropertyPaths=*/ Collections.emptyMap());
+        // Ignore the creation timestamp
+        visibleToConfigOverlay = new GenericDocument.Builder<>(visibleToConfigOverlay)
+                .setCreationTimestampMillis(0).build();
+        assertThat(visibleToConfigOverlay).isEqualTo(VisibilityToDocumentConverter
+                .createAndroidVOverlay(visibilityConfig));
+
+        mVisibilityStore.removeVisibility(ImmutableSet.of(prefix + "Email"));
+        // Verify the VisibilityConfig is removed from AppSearchImpl.
+        AppSearchException e = assertThrows(AppSearchException.class,
+                () -> mAppSearchImpl.getDocument(
+                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                        VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                        /*id=*/ prefix + "Email",
+                        /*typePropertyPaths=*/ Collections.emptyMap()));
+        assertThat(e).hasMessageThat().contains("not found.");
+    }
+
+    @Test
+    public void testSetVisibility_avoidRemoveOverlay() throws Exception {
+        // Set a visibility config w/o overlay
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
+                .setNotDisplayedBySystem(true)
+                .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
+                .build();
+        mVisibilityStore.setVisibility(ImmutableList.of(visibilityConfig));
+
+        // Put a fake AndroidVOverlay into AppSearchImpl, this is not added by VisibilityStore,
+        // just add a fake AndroidVOverlay to verify we won't remove it when we update the config
+        // which doesn't contain any overlay settings.
+        GenericDocument fakeAndroidVOverlay =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("androidVOverlay",
+                        "Email", "AndroidVOverlayType")
+                        .setCreationTimestampMillis(0)
+                        .build();
+        mAppSearchImpl.putDocument(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                fakeAndroidVOverlay,
+                /*sendChangeNotifications=*/ false,
+                /*logger=*/null);
+
+        // update the visibility config w/o overlay
+        InternalVisibilityConfig updateConfig = new InternalVisibilityConfig.Builder("Email")
+                .setNotDisplayedBySystem(true)
+                .addVisibleToPackage(new PackageIdentifier("pkgFoo", new byte[32]))
+                .build();
+        mVisibilityStore.setVisibility(ImmutableList.of(updateConfig));
+
+        // Verify we won't trigger a remove() call to AppSearchImpl by get the fakeAndroidVOverlay.
+        GenericDocument actualAndroidVOverlay = mAppSearchImpl.getDocument(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                /*id=*/ "Email",
+                /*typePropertyPaths=*/ Collections.emptyMap());
+
+        // Ignore the creation timestamp
+        actualAndroidVOverlay = new GenericDocument.Builder<>(actualAndroidVOverlay)
+                .setCreationTimestampMillis(0).build();
+        assertThat(actualAndroidVOverlay).isEqualTo(fakeAndroidVOverlay);
+    }
+
+    @Test
+    public void testSetVisibility_removeOverlay_publicAcl() throws Exception {
+        // Set a visibility config with public overlay
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
+                .setNotDisplayedBySystem(true)
+                .setPubliclyVisibleTargetPackage(
+                        new PackageIdentifier("pkgBar", new byte[32]))
+                .build();
+        mVisibilityStore.setVisibility(ImmutableList.of(visibilityConfig));
+
+        // verify the overlay document is created.
+        mAppSearchImpl.getDocument(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                /*id=*/ "Email",
+                /*typePropertyPaths=*/ Collections.emptyMap());
+
+        // update the visibility config w/o overlay
+        InternalVisibilityConfig updateConfig = new InternalVisibilityConfig.Builder("Email")
+                .setNotDisplayedBySystem(true)
+                .build();
+        mVisibilityStore.setVisibility(ImmutableList.of(updateConfig));
+
+        // Verify the overlay document is removed.
+        AppSearchException e = assertThrows(AppSearchException.class,
+                () -> mAppSearchImpl.getDocument(
+                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                        VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                        /*id=*/ "Email",
+                        /*typePropertyPaths=*/ Collections.emptyMap()));
+        assertThat(e).hasMessageThat().contains("not found.");
+    }
+
+    @Test
+    public void testSetVisibility_removeOverlay_visibleToConfig() throws Exception {
+        // Set a visibility config with visible to config.
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("Email")
+                .setNotDisplayedBySystem(true)
+                .addVisibleToConfig(new SchemaVisibilityConfig.Builder()
+                        .addRequiredPermissions(ImmutableSet.of(1)).build())
+                .build();
+        mVisibilityStore.setVisibility(ImmutableList.of(visibilityConfig));
+
+        // verify the overlay document is created.
+        mAppSearchImpl.getDocument(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                /*id=*/ "Email",
+                /*typePropertyPaths=*/ Collections.emptyMap());
+
+        // update the visibility config w/o overlay
+        InternalVisibilityConfig updateConfig = new InternalVisibilityConfig.Builder("Email")
+                .setNotDisplayedBySystem(true)
+                .build();
+        mVisibilityStore.setVisibility(ImmutableList.of(updateConfig));
+
+        // Verify the overlay document is removed.
+        AppSearchException e = assertThrows(AppSearchException.class,
+                () -> mAppSearchImpl.getDocument(
+                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                        VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                        /*id=*/ "Email",
+                        /*typePropertyPaths=*/ Collections.emptyMap()));
+        assertThat(e).hasMessageThat().contains("not found.");
+    }
+
+    @Test
+    public void testMigrateFromDeprecatedSchema() throws Exception {
+        // Set deprecated public acl schema to main visibility database.
+        mAppSearchImpl.setSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                ImmutableList.of(VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                VisibilityPermissionConfig.SCHEMA,
+                VisibilityToDocumentConverter.DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA),
+                /*visibilityConfigs=*/ Collections.emptyList(),
+                /*forceOverride=*/ true,
+                /*version=*/ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
+                /*setSchemaStatsBuilder=*/ null);
+
+        // Create VisibilityStore with success and force remove deprecated public acl schema from
+        // the main visibility database.
+        mVisibilityStore = new VisibilityStore(mAppSearchImpl);
+
+        GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.VISIBILITY_DATABASE_NAME,
+                new CallerAccess(VisibilityStore.VISIBILITY_PACKAGE_NAME));
+
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                VisibilityPermissionConfig.SCHEMA);
+    }
+
+    @Test
+    public void testMigrateFromDeprecatedOverlaySchema() throws Exception {
+        // Set deprecated overlay schema to overlay database.
+        AppSearchSchema deprecatedOverlaySchema =
+                new AppSearchSchema.Builder("AndroidVOverlayType")
+                        .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+                                "publiclyVisibleTargetPackage")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .build())
+                        .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(
+                                "publiclyVisibleTargetPackageSha256Cert")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .build())
+                        .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                "visibleToConfigProperty",
+                                "VisibleToConfigType")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                .build())
+                        .build();
+        AppSearchSchema deprecatedVisibleToConfigSchema =
+                new AppSearchSchema.Builder("VisibleToConfigType")
+                        .addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder(
+                                "notPlatformSurfaceable")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .build())
+                        .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+                                "packageName")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                .build())
+                        .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(
+                                "sha256Cert")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                .build())
+                        .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                "permission", VisibilityPermissionConfig.SCHEMA_TYPE)
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                .build())
+                        .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+                                "publiclyVisibleTargetPackage")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .build())
+                        .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(
+                                "publiclyVisibleTargetPackageSha256Cert")
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .build())
+                        .build();
+        mAppSearchImpl.setSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                ImmutableList.of(deprecatedOverlaySchema, deprecatedVisibleToConfigSchema,
+                        VisibilityPermissionConfig.SCHEMA),
+                /*visibilityConfigs=*/ Collections.emptyList(),
+                /*forceOverride=*/ true,
+                /*version=*/ VisibilityToDocumentConverter
+                        .OVERLAY_SCHEMA_VERSION_PUBLIC_ACL_VISIBLE_TO_CONFIG,
+                /*setSchemaStatsBuilder=*/ null);
+
+        // Create VisibilityStore with success and force remove override overlay schema.
+        mVisibilityStore = new VisibilityStore(mAppSearchImpl);
+
+        GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(
+                VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                VisibilityStore.ANDROID_V_OVERLAY_DATABASE_NAME,
+                new CallerAccess(VisibilityStore.VISIBILITY_PACKAGE_NAME));
+
+        assertThat(getSchemaResponse.getVersion()).isEqualTo(
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST);
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA);
     }
 }
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityToDocumentConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityToDocumentConverterTest.java
new file mode 100644
index 0000000..7c50c0d
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityToDocumentConverterTest.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.visibilitystore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.InternalVisibilityConfig;
+import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
+import androidx.appsearch.app.SetSchemaRequest;
+
+import com.google.android.appsearch.proto.AndroidVOverlayProto;
+import com.google.android.appsearch.proto.PackageIdentifierProto;
+import com.google.android.appsearch.proto.VisibilityConfigProto;
+import com.google.android.appsearch.proto.VisibleToPermissionProto;
+import com.google.android.icing.protobuf.ByteString;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class VisibilityToDocumentConverterTest {
+
+    @Test
+    public void testToGenericDocuments() throws Exception {
+        // Create a SetSchemaRequest for testing
+        byte[] cert1 = new byte[32];
+        byte[] cert2 = new byte[32];
+        byte[] cert3 = new byte[32];
+        byte[] cert4 = new byte[32];
+        Arrays.fill(cert1, (byte) 1);
+        Arrays.fill(cert2, (byte) 2);
+        Arrays.fill(cert3, (byte) 3);
+        Arrays.fill(cert4, (byte) 4);
+
+        SchemaVisibilityConfig visibleToConfig = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("com.example.test1", cert1))
+                .setPubliclyVisibleTargetPackage(
+                        new PackageIdentifier("com.example.test2", cert2))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+        SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder()
+                .addSchemas(new AppSearchSchema.Builder("someSchema").build())
+                .setSchemaTypeDisplayedBySystem("someSchema", false)
+                .setSchemaTypeVisibilityForPackage("someSchema", true,
+                        new PackageIdentifier("com.example.test3", cert3))
+                .addRequiredPermissionsForSchemaTypeVisibility(
+                        "someSchema", ImmutableSet.of(3, 4))
+                .setPubliclyVisibleSchema("someSchema",
+                        new PackageIdentifier("com.example.test4", cert4))
+                .addSchemaTypeVisibleToConfig("someSchema", visibleToConfig)
+                .build();
+
+        // Create android V overlay proto
+        VisibilityConfigProto visibleToConfigProto = VisibilityConfigProto.newBuilder()
+                .addVisibleToPackages(PackageIdentifierProto.newBuilder()
+                        .setPackageName("com.example.test1")
+                        .setPackageSha256Cert(ByteString.copyFrom(cert1)).build())
+                .setPubliclyVisibleTargetPackage(PackageIdentifierProto.newBuilder()
+                                .setPackageName("com.example.test2")
+                                .setPackageSha256Cert(ByteString.copyFrom(cert2)).build())
+                .addVisibleToPermissions(VisibleToPermissionProto.newBuilder()
+                        .addAllPermissions(ImmutableSet.of(1, 2)).build())
+                .build();
+        VisibilityConfigProto visibilityConfigProto = VisibilityConfigProto.newBuilder()
+                .setPubliclyVisibleTargetPackage(PackageIdentifierProto.newBuilder()
+                        .setPackageName("com.example.test4")
+                        .setPackageSha256Cert(ByteString.copyFrom(cert4)).build())
+                .build();
+        AndroidVOverlayProto overlayProto = AndroidVOverlayProto.newBuilder()
+                .setVisibilityConfig(visibilityConfigProto)
+                .addVisibleToConfigs(visibleToConfigProto)
+                .build();
+
+        // Create the expected AndroidVOverlay document
+        GenericDocument expectedAndroidVOverlay =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("androidVOverlay",
+                        "someSchema", "AndroidVOverlayType")
+                        .setCreationTimestampMillis(0)
+                        .setPropertyBytes("visibilityProtoSerializeProperty",
+                                overlayProto.toByteArray())
+                        .build();
+
+        // Create the expected visibility document
+        GenericDocument permissionDoc34 =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("", "",
+                        "VisibilityPermissionType")
+                        .setCreationTimestampMillis(0)
+                        .setPropertyLong("allRequiredPermissions", 3, 4).build();
+        GenericDocument expectedVisibilityDocument =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("", "someSchema",
+                        "VisibilityType")
+                        .setCreationTimestampMillis(0)
+                        .setPropertyBoolean("notPlatformSurfaceable", true)
+                        .setPropertyString("packageName", "com.example.test3")
+                        .setPropertyBytes("sha256Cert", cert3)
+                        .setPropertyDocument("permission", permissionDoc34)
+                        .build();
+
+        // Convert the SetSchemaRequest to a list of VisibilityConfig
+        List<InternalVisibilityConfig> visibilityConfigs =
+                InternalVisibilityConfig.toInternalVisibilityConfigs(setSchemaRequest);
+
+        // Check if the conversion is correct
+        assertThat(visibilityConfigs).hasSize(1);
+        InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(0);
+
+        assertThat(expectedVisibilityDocument).isEqualTo(
+                VisibilityToDocumentConverter.createVisibilityDocument(visibilityConfig));
+        assertThat(expectedAndroidVOverlay).isEqualTo(
+                VisibilityToDocumentConverter.createAndroidVOverlay(visibilityConfig));
+    }
+
+    @Test
+    public void testToVisibilityConfig() throws Exception {
+        byte[] cert1 = new byte[32];
+        byte[] cert2 = new byte[32];
+        byte[] cert3 = new byte[32];
+        byte[] cert4 = new byte[32];
+        Arrays.fill(cert1, (byte) 1);
+        Arrays.fill(cert2, (byte) 2);
+        Arrays.fill(cert3, (byte) 3);
+        Arrays.fill(cert4, (byte) 4);
+
+        // Create visibility proto property
+        VisibilityConfigProto visibleToConfigProto = VisibilityConfigProto.newBuilder()
+                .addVisibleToPackages(PackageIdentifierProto.newBuilder()
+                        .setPackageName("com.example.test1")
+                        .setPackageSha256Cert(ByteString.copyFrom(cert1)).build())
+                .setPubliclyVisibleTargetPackage(PackageIdentifierProto.newBuilder()
+                        .setPackageName("com.example.test2")
+                        .setPackageSha256Cert(ByteString.copyFrom(cert2)).build())
+                .addVisibleToPermissions(VisibleToPermissionProto.newBuilder()
+                        .addAllPermissions(ImmutableSet.of(1, 2)).build())
+                .build();
+        VisibilityConfigProto visibilityConfigProto = VisibilityConfigProto.newBuilder()
+                .setPubliclyVisibleTargetPackage(PackageIdentifierProto.newBuilder()
+                        .setPackageName("com.example.test4")
+                        .setPackageSha256Cert(ByteString.copyFrom(cert4)).build())
+                .build();
+        AndroidVOverlayProto overlayProto = AndroidVOverlayProto.newBuilder()
+                .setVisibilityConfig(visibilityConfigProto)
+                .addVisibleToConfigs(visibleToConfigProto)
+                .build();
+
+        // Create a visible config overlay for testing
+        GenericDocument androidVOverlay =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("androidVOverlay",
+                        "someSchema", "AndroidVOverlayType")
+                        .setCreationTimestampMillis(0)
+                        .setPropertyBytes("visibilityProtoSerializeProperty",
+                                overlayProto.toByteArray())
+                        .build();
+
+        // Create a VisibilityDocument for testing
+        GenericDocument permissionDoc34 =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("", "",
+                        "VisibilityPermissionType")
+                        .setCreationTimestampMillis(0)
+                        .setPropertyLong("allRequiredPermissions", 3, 4).build();
+        GenericDocument visibilityDoc =
+                new GenericDocument.Builder<GenericDocument.Builder<?>>("", "someSchema",
+                        "VisibilityType")
+                        .setCreationTimestampMillis(0)
+                        .setPropertyBoolean("notPlatformSurfaceable", true)
+                        .setPropertyString("packageName", "com.example.test3")
+                        .setPropertyBytes("sha256Cert", cert3)
+                        .setPropertyDocument("permission", permissionDoc34)
+                        .build();
+
+        // Create a VisibilityConfig using the Builder
+        InternalVisibilityConfig visibilityConfig =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        visibilityDoc, androidVOverlay);
+
+        // Check if the properties are set correctly
+        assertThat(visibilityDoc).isEqualTo(
+                VisibilityToDocumentConverter.createVisibilityDocument(visibilityConfig));
+        GenericDocument actualOverlayDoc =
+                VisibilityToDocumentConverter.createAndroidVOverlay(visibilityConfig);
+        AndroidVOverlayProto actualOverlayProto = AndroidVOverlayProto.parseFrom(
+                actualOverlayDoc.getPropertyBytes("visibilityProtoSerializeProperty"));
+        assertThat(actualOverlayProto).isEqualTo(overlayProto);
+        assertThat(androidVOverlay).isEqualTo(actualOverlayDoc);
+
+        // Verify rebuild from InternalVisibilityConfig remains the same.
+        InternalVisibilityConfig.Builder builder =
+                new InternalVisibilityConfig.Builder(visibilityConfig);
+        InternalVisibilityConfig rebuild = builder.build();
+        assertThat(visibilityConfig).isEqualTo(rebuild);
+
+        InternalVisibilityConfig modifiedConfig = builder
+                .setSchemaType("prefixedSchema")
+                .setNotDisplayedBySystem(false)
+                .addVisibleToPermissions(ImmutableSet.of(SetSchemaRequest.READ_SMS,
+                        SetSchemaRequest.READ_CALENDAR))
+                .clearVisibleToPackages()
+                .addVisibleToPackage(
+                        new PackageIdentifier("com.example.other", new byte[32]))
+                .setPubliclyVisibleTargetPackage(
+                        new PackageIdentifier("com.example.other", new byte[32]))
+                .clearVisibleToConfig()
+                .build();
+        assertThat(modifiedConfig.getSchemaType()).isEqualTo("prefixedSchema");
+
+        // Check that the rebuild stayed the same
+        assertThat(rebuild.getSchemaType()).isEqualTo("someSchema");
+        assertThat(rebuild.isNotDisplayedBySystem()).isTrue();
+        assertThat(rebuild.getVisibilityConfig().getRequiredPermissions())
+                .containsExactly(ImmutableSet.of(3, 4));
+        assertThat(rebuild.getVisibilityConfig().getAllowedPackages())
+                .containsExactly(new PackageIdentifier("com.example.test3", cert3));
+        assertThat(
+                rebuild.getVisibilityConfig().getPubliclyVisibleTargetPackage()).isEqualTo(
+                new PackageIdentifier("com.example.test4", cert4));
+
+        SchemaVisibilityConfig expectedVisibleToConfig = new SchemaVisibilityConfig.Builder()
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .addAllowedPackage(new PackageIdentifier("com.example.test1", cert1))
+                .setPubliclyVisibleTargetPackage(new PackageIdentifier("com.example.test2", cert2))
+                .build();
+        assertThat(rebuild.getVisibleToConfigs()).containsExactly(expectedVisibleToConfig);
+    }
+
+    @Test
+    public void testToGenericDocumentAndBack() {
+        // Create a SetSchemaRequest for testing
+        byte[] cert1 = new byte[32];
+        byte[] cert2 = new byte[32];
+        byte[] cert3 = new byte[32];
+        byte[] cert4 = new byte[32];
+        byte[] cert5 = new byte[32];
+        byte[] cert6 = new byte[32];
+        byte[] cert7 = new byte[32];
+        Arrays.fill(cert1, (byte) 1);
+        Arrays.fill(cert2, (byte) 2);
+        Arrays.fill(cert3, (byte) 3);
+        Arrays.fill(cert4, (byte) 4);
+        Arrays.fill(cert5, (byte) 5);
+        Arrays.fill(cert6, (byte) 6);
+        Arrays.fill(cert7, (byte) 7);
+
+        SchemaVisibilityConfig config1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("com.example.test1", cert1))
+                .setPubliclyVisibleTargetPackage(
+                        new PackageIdentifier("com.example.test2", cert2))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+        SchemaVisibilityConfig config2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("com.example.test3", cert3))
+                .addRequiredPermissions(ImmutableSet.of(3, 4))
+                .build();
+        SchemaVisibilityConfig config3 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("com.example.test4", cert4))
+                .setPubliclyVisibleTargetPackage(
+                        new PackageIdentifier("com.example.test5", cert5))
+                .build();
+        SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder()
+                .addSchemas(new AppSearchSchema.Builder("someSchema").build())
+                .setSchemaTypeDisplayedBySystem("someSchema", /*displayed=*/ true)
+                .setSchemaTypeVisibilityForPackage("someSchema", /*visible=*/ true,
+                        new PackageIdentifier("com.example.test6", cert6))
+                .addRequiredPermissionsForSchemaTypeVisibility("someSchema",
+                        ImmutableSet.of(1, 2))
+                .setPubliclyVisibleSchema("someSchema",
+                        new PackageIdentifier("com.example.test7", cert7))
+                .addSchemaTypeVisibleToConfig("someSchema", config1)
+                .addSchemaTypeVisibleToConfig("someSchema", config2)
+                .addSchemaTypeVisibleToConfig("someSchema", config3)
+                .build();
+
+        // Convert the SetSchemaRequest to a list of VisibilityConfig
+        List<InternalVisibilityConfig> visibilityConfigs =
+                InternalVisibilityConfig.toInternalVisibilityConfigs(setSchemaRequest);
+        InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(0);
+
+        GenericDocument visibilityDoc =
+                VisibilityToDocumentConverter.createVisibilityDocument(visibilityConfig);
+        GenericDocument androidVOverlay =
+                VisibilityToDocumentConverter.createAndroidVOverlay(visibilityConfig);
+
+        InternalVisibilityConfig rebuild =
+                VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                        visibilityDoc, androidVOverlay);
+
+        assertThat(rebuild).isEqualTo(visibilityConfig);
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityUtilTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityUtilTest.java
new file mode 100644
index 0000000..9a98739
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityUtilTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.visibilitystore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class VisibilityUtilTest {
+    @Test
+    public void testIsSchemaSearchableByCaller_selfAccessDefaultAllowed() {
+        CallerAccess callerAccess = new CallerAccess("package1");
+        assertThat(VisibilityUtil.isSchemaSearchableByCaller(callerAccess,
+                /*targetPackageName=*/ "package1",
+                /*prefixedSchema=*/ "schema",
+                /*visibilityStore=*/ null,
+                /*visibilityChecker=*/ null)).isTrue();
+        assertThat(VisibilityUtil.isSchemaSearchableByCaller(callerAccess,
+                /*targetPackageName=*/ "package2",
+                /*prefixedSchema=*/ "schema",
+                /*visibilityStore=*/ null,
+                /*visibilityChecker=*/ null)).isFalse();
+    }
+
+    @Test
+    public void testIsSchemaSearchableByCaller_selfAccessNotAllowed() {
+        CallerAccess callerAccess = new CallerAccess("package1") {
+            @Override
+            public boolean doesCallerHaveSelfAccess() {
+                return false;
+            }
+        };
+        assertThat(VisibilityUtil.isSchemaSearchableByCaller(callerAccess,
+                /*targetPackageName=*/ "package1",
+                /*prefixedSchema=*/ "schema",
+                /*visibilityStore=*/ null,
+                /*visibilityChecker=*/ null)).isFalse();
+        assertThat(VisibilityUtil.isSchemaSearchableByCaller(callerAccess,
+                /*targetPackageName=*/ "package2",
+                /*prefixedSchema=*/ "schema",
+                /*visibilityStore=*/ null,
+                /*visibilityChecker=*/ null)).isFalse();
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
index c7cccb8..5cecaa7 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
@@ -48,6 +48,12 @@
                 // fall through
             case Features.LIST_FILTER_QUERY_LANGUAGE:
                 // fall through
+            case Features.LIST_FILTER_HAS_PROPERTY_FUNCTION:
+                // fall through
+            case Features.LIST_FILTER_TOKENIZE_FUNCTION:
+                // fall through
+            case Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG:
+                // fall through
             case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
                 // fall through
             case Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH:
@@ -60,15 +66,23 @@
                 // fall through
             case Features.SEARCH_SUGGESTION:
                 // fall through
-            case Features.SCHEMA_SET_DELETION_PROPAGATION:
-                // fall through
             case Features.SET_SCHEMA_CIRCULAR_REFERENCES:
                 // fall through
             case Features.SCHEMA_ADD_PARENT_TYPE:
                 // fall through
+            case Features.SCHEMA_SET_DESCRIPTION:
+                // fall through
             case Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES:
                 // fall through
             case Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES:
+                // fall through
+            case Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG:
+                // fall through
+            case Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE:
+                // fall through
+            case Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG:
+                // fall through
+            case Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS:
                 return true;
             default:
                 return false;
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
index 4c9fbfe..d175aa6 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
@@ -16,7 +16,6 @@
 
 package androidx.appsearch.localstorage;
 
-import static androidx.appsearch.app.AppSearchResult.RESULT_INTERNAL_ERROR;
 import static androidx.appsearch.app.AppSearchResult.RESULT_SECURITY_ERROR;
 import static androidx.appsearch.app.InternalSetSchemaResponse.newFailedSetSchemaResponse;
 import static androidx.appsearch.app.InternalSetSchemaResponse.newSuccessfulSetSchemaResponse;
@@ -27,7 +26,6 @@
 import static androidx.appsearch.localstorage.util.PrefixUtil.getPrefix;
 import static androidx.appsearch.localstorage.util.PrefixUtil.removePrefixesFromDocument;
 
-import android.os.Bundle;
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -43,15 +41,16 @@
 import androidx.appsearch.app.GetByDocumentIdRequest;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
 import androidx.appsearch.app.JoinSpec;
 import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
 import androidx.appsearch.app.SearchResultPage;
 import androidx.appsearch.app.SearchSpec;
 import androidx.appsearch.app.SearchSuggestionResult;
 import androidx.appsearch.app.SearchSuggestionSpec;
 import androidx.appsearch.app.SetSchemaResponse;
 import androidx.appsearch.app.StorageInfo;
-import androidx.appsearch.app.VisibilityDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.converter.GenericDocumentToProtoConverter;
 import androidx.appsearch.localstorage.converter.ResultCodeToProtoConverter;
@@ -187,12 +186,8 @@
     @VisibleForTesting
     final IcingSearchEngine mIcingSearchEngineLocked;
 
-    // This map contains schema types and SchemaTypeConfigProtos for all package-database
-    // prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each
-    // prefixed schema type to its respective SchemaTypeConfigProto.
     @GuardedBy("mReadWriteLock")
-    private final Map<String, Map<String, SchemaTypeConfigProto>> mSchemaMapLocked =
-            new ArrayMap<>();
+    private final SchemaCache mSchemaCacheLocked = new SchemaCache();
 
     // This map contains namespaces for all package-database prefixes. All values in the map are
     // prefixed with the package-database prefix.
@@ -259,7 +254,7 @@
      * <p>Instead, logger instance needs to be passed to each individual method, like create, query
      * and putDocument.
      *
-     * @param initStatsBuilder collects stats for initialization if provided.
+     * @param initStatsBuilder  collects stats for initialization if provided.
      * @param visibilityChecker The {@link VisibilityChecker} that check whether the caller has
      *                          access to aa specific schema. Pass null will lost that ability and
      *                          global querier could only get their own data.
@@ -269,8 +264,8 @@
             @NonNull File icingDir,
             @NonNull AppSearchConfig config,
             @Nullable InitializeStats.Builder initStatsBuilder,
-            @NonNull OptimizeStrategy optimizeStrategy,
-            @Nullable VisibilityChecker visibilityChecker)
+            @Nullable VisibilityChecker visibilityChecker,
+            @NonNull OptimizeStrategy optimizeStrategy)
             throws AppSearchException {
         return new AppSearchImpl(icingDir, config, initStatsBuilder, optimizeStrategy,
                 visibilityChecker);
@@ -378,9 +373,12 @@
                 for (int i = 0; i < schemaProtoTypesList.size(); i++) {
                     SchemaTypeConfigProto schema = schemaProtoTypesList.get(i);
                     String prefixedSchemaType = schema.getSchemaType();
-                    addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema);
+                    mSchemaCacheLocked.addToSchemaMap(getPrefix(prefixedSchemaType), schema);
                 }
 
+                // Populate schema parent-to-children map
+                mSchemaCacheLocked.rebuildSchemaParentToChildrenMap();
+
                 // Populate namespace map
                 List<String> prefixedNamespaceList =
                         getAllNamespacesResultProto.getNamespacesList();
@@ -463,10 +461,10 @@
      * @param packageName                 The package name that owns the schemas.
      * @param databaseName                The name of the database where this schema lives.
      * @param schemas                     Schemas to set for this app.
-     * @param visibilityDocuments         {@link VisibilityDocument}s that contain all
+     * @param visibilityConfigs           {@link InternalVisibilityConfig}s that contain all
      *                                    visibility setting information for those schemas
      *                                    has user custom settings. Other schemas in the list
-     *                                    that don't has a {@link VisibilityDocument}
+     *                                    that don't has a {@link InternalVisibilityConfig}
      *                                    will be treated as having the default visibility,
      *                                    which is accessible by the system and no other packages.
      * @param forceOverride               Whether to force-apply the schema even if it is
@@ -490,7 +488,7 @@
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull List<AppSearchSchema> schemas,
-            @NonNull List<VisibilityDocument> visibilityDocuments,
+            @NonNull List<InternalVisibilityConfig> visibilityConfigs,
             boolean forceOverride,
             int version,
             @Nullable SetSchemaStats.Builder setSchemaStatsBuilder) throws AppSearchException {
@@ -508,7 +506,7 @@
                         packageName,
                         databaseName,
                         schemas,
-                        visibilityDocuments,
+                        visibilityConfigs,
                         forceOverride,
                         version,
                         setSchemaStatsBuilder);
@@ -517,7 +515,7 @@
                         packageName,
                         databaseName,
                         schemas,
-                        visibilityDocuments,
+                        visibilityConfigs,
                         forceOverride,
                         version,
                         setSchemaStatsBuilder);
@@ -539,7 +537,7 @@
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull List<AppSearchSchema> schemas,
-            @NonNull List<VisibilityDocument> visibilityDocuments,
+            @NonNull List<InternalVisibilityConfig> visibilityConfigs,
             boolean forceOverride,
             int version,
             @Nullable SetSchemaStats.Builder setSchemaStatsBuilder) throws AppSearchException {
@@ -588,7 +586,7 @@
                 packageName,
                 databaseName,
                 schemas,
-                visibilityDocuments,
+                visibilityConfigs,
                 forceOverride,
                 version,
                 setSchemaStatsBuilder);
@@ -717,7 +715,7 @@
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull List<AppSearchSchema> schemas,
-            @NonNull List<VisibilityDocument> visibilityDocuments,
+            @NonNull List<InternalVisibilityConfig> visibilityConfigs,
             boolean forceOverride,
             int version,
             @Nullable SetSchemaStats.Builder setSchemaStatsBuilder) throws AppSearchException {
@@ -791,42 +789,47 @@
         // Update derived data structures.
         for (SchemaTypeConfigProto schemaTypeConfigProto :
                 rewrittenSchemaResults.mRewrittenPrefixedTypes.values()) {
-            addToMap(mSchemaMapLocked, prefix, schemaTypeConfigProto);
+            mSchemaCacheLocked.addToSchemaMap(prefix, schemaTypeConfigProto);
         }
 
         for (String schemaType : rewrittenSchemaResults.mDeletedPrefixedTypes) {
-            removeFromMap(mSchemaMapLocked, prefix, schemaType);
+            mSchemaCacheLocked.removeFromSchemaMap(prefix, schemaType);
         }
+
+        mSchemaCacheLocked.rebuildSchemaParentToChildrenMapForPrefix(prefix);
+
         // Since the constructor of VisibilityStore will set schema. Avoid call visibility
         // store before we have already created it.
         if (mVisibilityStoreLocked != null) {
             // Add prefix to all visibility documents.
-            List<VisibilityDocument> prefixedVisibilityDocuments =
-                    new ArrayList<>(visibilityDocuments.size());
             // Find out which Visibility document is deleted or changed to all-default settings.
             // We need to remove them from Visibility Store.
             Set<String> deprecatedVisibilityDocuments =
                     new ArraySet<>(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet());
-            for (int i = 0; i < visibilityDocuments.size(); i++) {
-                VisibilityDocument unPrefixedDocument = visibilityDocuments.get(i);
-                // The VisibilityDocument is controlled by the client and it's untrusted but we
+            List<InternalVisibilityConfig> prefixedVisibilityConfigs =
+                    new ArrayList<>(visibilityConfigs.size());
+            for (int i = 0; i < visibilityConfigs.size(); i++) {
+                InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
+                // The VisibilityConfig is controlled by the client and it's untrusted but we
                 // make it safe by appending a prefix.
                 // We must control the package-database prefix. Therefore even if the client
                 // fake the id, they can only mess their own app. That's totally allowed and
                 // they can do this via the public API too.
-                String prefixedSchemaType = prefix + unPrefixedDocument.getId();
-                prefixedVisibilityDocuments.add(
-                        new VisibilityDocument.Builder(
-                                unPrefixedDocument).setId(prefixedSchemaType).build());
+                // TODO(b/275592563): Move prefixing into VisibilityConfig.createVisibilityDocument
+                //  and createVisibilityOverlay
+                String schemaType = visibilityConfig.getSchemaType();
+                String prefixedSchemaType = prefix + schemaType;
+                prefixedVisibilityConfigs.add(new InternalVisibilityConfig.Builder(visibilityConfig)
+                        .setSchemaType(prefixedSchemaType).build());
                 // This schema has visibility settings. We should keep it from the removal list.
-                deprecatedVisibilityDocuments.remove(prefixedSchemaType);
+                deprecatedVisibilityDocuments.remove(visibilityConfig.getSchemaType());
             }
             // Now deprecatedVisibilityDocuments contains those existing schemas that has
             // all-default visibility settings, add deleted schemas. That's all we need to
             // remove.
             deprecatedVisibilityDocuments.addAll(rewrittenSchemaResults.mDeletedPrefixedTypes);
             mVisibilityStoreLocked.removeVisibility(deprecatedVisibilityDocuments);
-            mVisibilityStoreLocked.setVisibility(prefixedVisibilityDocuments);
+            mVisibilityStoreLocked.setVisibility(prefixedVisibilityConfigs);
         }
         long saveVisibilitySettingEndTimeMillis = SystemClock.elapsedRealtime();
         if (setSchemaStatsBuilder != null) {
@@ -904,33 +907,44 @@
                 // schema. Avoid call visibility store before we have already created it.
                 if (mVisibilityStoreLocked != null) {
                     String typeName = typeConfig.getSchemaType().substring(typePrefix.length());
-                    VisibilityDocument visibilityDocument =
+                    InternalVisibilityConfig visibilityConfig =
                             mVisibilityStoreLocked.getVisibility(prefixedSchemaType);
-                    if (visibilityDocument != null) {
-                        if (visibilityDocument.isNotDisplayedBySystem()) {
-                            responseBuilder
-                                    .addSchemaTypeNotDisplayedBySystem(typeName);
+                    if (visibilityConfig != null) {
+                        if (visibilityConfig.isNotDisplayedBySystem()) {
+                            responseBuilder.addSchemaTypeNotDisplayedBySystem(typeName);
                         }
-                        String[] packageNames = visibilityDocument.getPackageNames();
-                        byte[][] sha256Certs = visibilityDocument.getSha256Certs();
-                        if (packageNames.length != sha256Certs.length) {
-                            throw new AppSearchException(RESULT_INTERNAL_ERROR,
-                                    "The length of package names and sha256Crets are different!");
-                        }
-                        if (packageNames.length != 0) {
-                            Set<PackageIdentifier> packageIdentifier = new ArraySet<>();
-                            for (int j = 0; j < packageNames.length; j++) {
-                                packageIdentifier.add(new PackageIdentifier(
-                                        packageNames[j], sha256Certs[j]));
-                            }
+                        List<PackageIdentifier> packageIdentifiers =
+                                visibilityConfig.getVisibilityConfig().getAllowedPackages();
+                        if (!packageIdentifiers.isEmpty()) {
                             responseBuilder.setSchemaTypeVisibleToPackages(typeName,
-                                    packageIdentifier);
+                                    new ArraySet<>(packageIdentifiers));
                         }
                         Set<Set<Integer>> visibleToPermissions =
-                                visibilityDocument.getVisibleToPermissions();
-                        if (visibleToPermissions != null) {
-                            responseBuilder.setRequiredPermissionsForSchemaTypeVisibility(
-                                    typeName,  visibleToPermissions);
+                                visibilityConfig.getVisibilityConfig().getRequiredPermissions();
+                        if (!visibleToPermissions.isEmpty()) {
+                            Set<Set<Integer>> visibleToPermissionsSet =
+                                    new ArraySet<>(visibleToPermissions.size());
+                            for (Set<Integer> permissionList : visibleToPermissions) {
+                                visibleToPermissionsSet.add(new ArraySet<>(permissionList));
+                            }
+
+                            responseBuilder.setRequiredPermissionsForSchemaTypeVisibility(typeName,
+                                    visibleToPermissionsSet);
+                        }
+
+                        // Check for Visibility properties from the overlay
+                        PackageIdentifier publiclyVisibleFromPackage =
+                                visibilityConfig.getVisibilityConfig()
+                                        .getPubliclyVisibleTargetPackage();
+                        if (publiclyVisibleFromPackage != null) {
+                            responseBuilder.setPubliclyVisibleSchema(
+                                    typeName, publiclyVisibleFromPackage);
+                        }
+                        Set<SchemaVisibilityConfig> visibleToConfigs =
+                                visibilityConfig.getVisibleToConfigs();
+                        if (!visibleToConfigs.isEmpty()) {
+                            responseBuilder.setSchemaTypeVisibleToConfigs(
+                                    typeName, visibleToConfigs);
                         }
                     }
                 }
@@ -1190,7 +1204,7 @@
             removePrefixesFromDocument(documentBuilder);
             String prefix = createPrefix(packageName, databaseName);
             Map<String, SchemaTypeConfigProto> schemaTypeMap =
-                    Preconditions.checkNotNull(mSchemaMapLocked.get(prefix));
+                    mSchemaCacheLocked.getSchemaMapForPrefix(prefix);
             return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build(),
                     prefix, schemaTypeMap, mConfig);
         } finally {
@@ -1232,7 +1246,7 @@
             // schema had ever been set for that prefix. Given we have retrieved a document from
             // the index, we know a schema had to have been set.
             Map<String, SchemaTypeConfigProto> schemaTypeMap =
-                    Preconditions.checkNotNull(mSchemaMapLocked.get(prefix));
+                    mSchemaCacheLocked.getSchemaMapForPrefix(prefix);
             return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build(),
                     prefix, schemaTypeMap, mConfig);
         } finally {
@@ -1254,6 +1268,8 @@
      */
     @NonNull
     @GuardedBy("mReadWriteLock")
+    // We only log getResultProto.toString() in fullPii trace for debugging.
+    @SuppressWarnings("LiteProtoToString")
     private DocumentProto getDocumentProtoByIdLocked(
             @NonNull String packageName,
             @NonNull String databaseName,
@@ -1317,8 +1333,9 @@
         SearchStats.Builder sStatsBuilder = null;
         if (logger != null) {
             sStatsBuilder =
-                    new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL,
-                            packageName).setDatabase(databaseName);
+                    new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, packageName)
+                            .setDatabase(databaseName)
+                            .setSearchSourceLogTag(searchSpec.getSearchSourceLogTag());
         }
 
         long javaLockAcquisitionLatencyStartMillis = SystemClock.elapsedRealtime();
@@ -1338,18 +1355,18 @@
                 if (sStatsBuilder != null && logger != null) {
                     sStatsBuilder.setStatusCode(AppSearchResult.RESULT_SECURITY_ERROR);
                 }
-                return new SearchResultPage(Bundle.EMPTY);
+                return new SearchResultPage();
             }
 
             String prefix = createPrefix(packageName, databaseName);
             SearchSpecToProtoConverter searchSpecToProtoConverter =
                     new SearchSpecToProtoConverter(queryExpression, searchSpec,
-                            Collections.singleton(prefix), mNamespaceMapLocked, mSchemaMapLocked,
+                            Collections.singleton(prefix), mNamespaceMapLocked, mSchemaCacheLocked,
                             mConfig);
             if (searchSpecToProtoConverter.hasNothingToSearch()) {
                 // there is nothing to search over given their search filters, so we can return an
                 // empty SearchResult and skip sending request to Icing.
-                return new SearchResultPage(Bundle.EMPTY);
+                return new SearchResultPage();
             }
 
             SearchResultPage searchResultPage =
@@ -1394,7 +1411,8 @@
             sStatsBuilder =
                     new SearchStats.Builder(
                             SearchStats.VISIBILITY_SCOPE_GLOBAL,
-                            callerAccess.getCallingPackageName());
+                            callerAccess.getCallingPackageName())
+                            .setSearchSourceLogTag(searchSpec.getSearchSourceLogTag());
         }
 
         long javaLockAcquisitionLatencyStartMillis = SystemClock.elapsedRealtime();
@@ -1417,13 +1435,13 @@
             // SearchSpec that wants to query every visible package.
             Set<String> packageFilters = new ArraySet<>();
             if (!searchSpec.getFilterPackageNames().isEmpty()) {
-                if (searchSpec.getJoinSpec() == null) {
+                JoinSpec joinSpec = searchSpec.getJoinSpec();
+                if (joinSpec == null) {
                     packageFilters.addAll(searchSpec.getFilterPackageNames());
-                } else if (!searchSpec.getJoinSpec().getNestedSearchSpec()
+                } else if (!joinSpec.getNestedSearchSpec()
                         .getFilterPackageNames().isEmpty()) {
                     packageFilters.addAll(searchSpec.getFilterPackageNames());
-                    packageFilters.addAll(
-                            searchSpec.getJoinSpec().getNestedSearchSpec().getFilterPackageNames());
+                    packageFilters.addAll(joinSpec.getNestedSearchSpec().getFilterPackageNames());
                 }
             }
 
@@ -1445,14 +1463,14 @@
             }
             SearchSpecToProtoConverter searchSpecToProtoConverter =
                     new SearchSpecToProtoConverter(queryExpression, searchSpec, prefixFilters,
-                            mNamespaceMapLocked, mSchemaMapLocked, mConfig);
+                            mNamespaceMapLocked, mSchemaCacheLocked, mConfig);
             // Remove those inaccessible schemas.
             searchSpecToProtoConverter.removeInaccessibleSchemaFilter(
                     callerAccess, mVisibilityStoreLocked, mVisibilityCheckerLocked);
             if (searchSpecToProtoConverter.hasNothingToSearch()) {
                 // there is nothing to search over given their search filters, so we can return an
                 // empty SearchResult and skip sending request to Icing.
-                return new SearchResultPage(Bundle.EMPTY);
+                return new SearchResultPage();
             }
             if (sStatsBuilder != null) {
                 sStatsBuilder.setAclCheckLatencyMillis(
@@ -1486,7 +1504,7 @@
         long rewriteSearchSpecLatencyStartMillis = SystemClock.elapsedRealtime();
         SearchSpecProto finalSearchSpec = searchSpecToProtoConverter.toSearchSpecProto();
         ResultSpecProto finalResultSpec = searchSpecToProtoConverter.toResultSpecProto(
-                mNamespaceMapLocked, mSchemaMapLocked);
+                mNamespaceMapLocked, mSchemaCacheLocked);
         ScoringSpecProto scoringSpec = searchSpecToProtoConverter.toScoringSpecProto();
         if (sStatsBuilder != null) {
             sStatsBuilder.setRewriteSearchSpecLatencyMillis((int)
@@ -1500,7 +1518,7 @@
         long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime();
         // Rewrite search result before we return.
         SearchResultPage searchResultPage = SearchResultToProtoConverter
-                .toSearchResultPage(searchResultProto, mSchemaMapLocked, mConfig);
+                .toSearchResultPage(searchResultProto, mSchemaCacheLocked, mConfig);
         if (sStatsBuilder != null) {
             sStatsBuilder.setRewriteSearchResultLatencyMillis(
                     (int) (SystemClock.elapsedRealtime()
@@ -1510,6 +1528,8 @@
     }
 
     @GuardedBy("mReadWriteLock")
+    // We only log searchSpec, scoringSpec and resultSpec in fullPii trace for debugging.
+    @SuppressWarnings("LiteProtoToString")
     private SearchResultProto searchInIcingLocked(
             @NonNull SearchSpecProto searchSpec,
             @NonNull ResultSpecProto resultSpec,
@@ -1581,7 +1601,7 @@
                             searchSuggestionSpec,
                             Collections.singleton(prefix),
                             mNamespaceMapLocked,
-                            mSchemaMapLocked);
+                            mSchemaCacheLocked);
 
             if (searchSuggestionSpecToProtoConverter.hasNothingToSearch()) {
                 // there is nothing to search over given their search filters, so we can return an
@@ -1616,7 +1636,7 @@
         mReadWriteLock.readLock().lock();
         try {
             Map<String, Set<String>> packageToDatabases = new ArrayMap<>();
-            for (String prefix : mSchemaMapLocked.keySet()) {
+            for (String prefix : mSchemaCacheLocked.getAllPrefixes()) {
                 String packageName = getPackageName(prefix);
 
                 Set<String> databases = packageToDatabases.get(packageName);
@@ -1696,7 +1716,7 @@
             long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime();
             // Rewrite search result before we return.
             SearchResultPage searchResultPage = SearchResultToProtoConverter
-                    .toSearchResultPage(searchResultProto, mSchemaMapLocked, mConfig);
+                    .toSearchResultPage(searchResultProto, mSchemaCacheLocked, mConfig);
             if (sStatsBuilder != null) {
                 sStatsBuilder.setRewriteSearchResultLatencyMillis(
                         (int) (SystemClock.elapsedRealtime()
@@ -1910,7 +1930,7 @@
 
             SearchSpecToProtoConverter searchSpecToProtoConverter =
                     new SearchSpecToProtoConverter(queryExpression, searchSpec,
-                            Collections.singleton(prefix), mNamespaceMapLocked, mSchemaMapLocked,
+                            Collections.singleton(prefix), mNamespaceMapLocked, mSchemaCacheLocked,
                             mConfig);
             if (searchSpecToProtoConverter.hasNothingToSearch()) {
                 // there is nothing to search over given their search filters, so we can return
@@ -2337,10 +2357,9 @@
                     }
                     for (String databaseName : databaseNames) {
                         String removedPrefix = createPrefix(packageName, databaseName);
-                        Map<String, SchemaTypeConfigProto> removedSchemas =
-                                Preconditions.checkNotNull(mSchemaMapLocked.remove(removedPrefix));
+                        Set<String> removedSchemas = mSchemaCacheLocked.removePrefix(removedPrefix);
                         if (mVisibilityStoreLocked != null) {
-                            mVisibilityStoreLocked.removeVisibility(removedSchemas.keySet());
+                            mVisibilityStoreLocked.removeVisibility(removedSchemas);
                         }
 
                         mNamespaceMapLocked.remove(removedPrefix);
@@ -2370,7 +2389,7 @@
                 resetResultProto.getStatus(),
                 resetResultProto);
         mOptimizeIntervalCountLocked = 0;
-        mSchemaMapLocked.clear();
+        mSchemaCacheLocked.clear();
         mNamespaceMapLocked.clear();
         mDocumentCountMapLocked.clear();
         synchronized (mNextPageTokensLocked) {
@@ -2616,24 +2635,6 @@
         values.add(prefixedValue);
     }
 
-    private static void addToMap(Map<String, Map<String, SchemaTypeConfigProto>> map, String prefix,
-            SchemaTypeConfigProto schemaTypeConfigProto) {
-        Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix);
-        if (schemaTypeMap == null) {
-            schemaTypeMap = new ArrayMap<>();
-            map.put(prefix, schemaTypeMap);
-        }
-        schemaTypeMap.put(schemaTypeConfigProto.getSchemaType(), schemaTypeConfigProto);
-    }
-
-    private static void removeFromMap(Map<String, Map<String, SchemaTypeConfigProto>> map,
-            String prefix, String schemaType) {
-        Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix);
-        if (schemaTypeMap != null) {
-            schemaTypeMap.remove(schemaType);
-        }
-    }
-
     /**
      * Checks the given status code and throws an {@link AppSearchException} if code is an error.
      *
@@ -2758,22 +2759,26 @@
         }
         if (LogUtil.DEBUG) {
             if (Log.isLoggable(icingTag, Log.VERBOSE)) {
-                IcingSearchEngine.setLoggingLevel(LogSeverity.Code.VERBOSE, /*verbosity=*/
-                        (short) 1);
+                boolean unused = IcingSearchEngine.setLoggingLevel(LogSeverity.Code.VERBOSE,
+                        /*verbosity=*/ (short) 1);
                 return;
             } else if (Log.isLoggable(icingTag, Log.DEBUG)) {
-                IcingSearchEngine.setLoggingLevel(LogSeverity.Code.DBG);
+                boolean unused = IcingSearchEngine.setLoggingLevel(LogSeverity.Code.DBG);
                 return;
             }
         }
-        if (Log.isLoggable(icingTag, Log.INFO)) {
-            IcingSearchEngine.setLoggingLevel(LogSeverity.Code.INFO);
-        } else if (Log.isLoggable(icingTag, Log.WARN)) {
-            IcingSearchEngine.setLoggingLevel(LogSeverity.Code.WARNING);
+        if (LogUtil.INFO) {
+            if (Log.isLoggable(icingTag, Log.INFO)) {
+                boolean unused = IcingSearchEngine.setLoggingLevel(LogSeverity.Code.INFO);
+                return;
+            }
+        }
+        if (Log.isLoggable(icingTag, Log.WARN)) {
+            boolean unused = IcingSearchEngine.setLoggingLevel(LogSeverity.Code.WARNING);
         } else if (Log.isLoggable(icingTag, Log.ERROR)) {
-            IcingSearchEngine.setLoggingLevel(LogSeverity.Code.ERROR);
+            boolean unused = IcingSearchEngine.setLoggingLevel(LogSeverity.Code.ERROR);
         } else {
-            IcingSearchEngine.setLoggingLevel(LogSeverity.Code.FATAL);
+            boolean unused = IcingSearchEngine.setLoggingLevel(LogSeverity.Code.FATAL);
         }
     }
 
@@ -2795,11 +2800,7 @@
     public List<String> getAllPrefixedSchemaTypes() {
         mReadWriteLock.readLock().lock();
         try {
-            List<String> cachedPrefixedSchemaTypes = new ArrayList<>();
-            for (Map<String, SchemaTypeConfigProto> value : mSchemaMapLocked.values()) {
-                cachedPrefixedSchemaTypes.addAll(value.keySet());
-            }
-            return cachedPrefixedSchemaTypes;
+            return mSchemaCacheLocked.getAllPrefixedSchemaTypes();
         } finally {
             mReadWriteLock.readLock().unlock();
         }
@@ -2814,7 +2815,7 @@
      *                    {@link AppSearchResult} code.
      * @return {@link AppSearchResult} error code
      */
-    private static @AppSearchResult.ResultCode int statusProtoToResultCode(
+    @AppSearchResult.ResultCode private static int statusProtoToResultCode(
             @NonNull StatusProto statusProto) {
         return ResultCodeToProtoConverter.toResultCode(statusProto.getCode());
     }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLogger.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLogger.java
index 3e9e739..4e54764 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLogger.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLogger.java
@@ -23,15 +23,18 @@
 import androidx.appsearch.localstorage.stats.OptimizeStats;
 import androidx.appsearch.localstorage.stats.PutDocumentStats;
 import androidx.appsearch.localstorage.stats.RemoveStats;
+import androidx.appsearch.localstorage.stats.SearchSessionStats;
 import androidx.appsearch.localstorage.stats.SearchStats;
 import androidx.appsearch.localstorage.stats.SetSchemaStats;
 import androidx.appsearch.stats.SchemaMigrationStats;
 
+import java.util.List;
+
 /**
  * An interface for implementing client-defined logging AppSearch operations stats.
  *
  * <p>Any implementation needs to provide general information on how to log all the stats types.
- * (e.g. {@link CallStats})
+ * (for example {@link CallStats})
  *
  * <p>All implementations of this interface must be thread safe.
  *
@@ -42,42 +45,79 @@
     /**
      * Logs {@link CallStats}
      */
-    void logStats(@NonNull CallStats stats);
+    default void logStats(@NonNull CallStats stats) {
+        // no-op
+    }
 
     /**
      * Logs {@link PutDocumentStats}
      */
-    void logStats(@NonNull PutDocumentStats stats);
+    default void logStats(@NonNull PutDocumentStats stats) {
+        // no-op
+    }
 
     /**
      * Logs {@link InitializeStats}
      */
-    void logStats(@NonNull InitializeStats stats);
+    default void logStats(@NonNull InitializeStats stats) {
+        // no-op
+    }
 
     /**
      * Logs {@link SearchStats}
      */
-    void logStats(@NonNull SearchStats stats);
+    default void logStats(@NonNull SearchStats stats) {
+        // no-op
+    }
 
-    /**
-     * Logs {@link RemoveStats}
-     */
-    void logStats(@NonNull RemoveStats stats);
+    /** Logs {@link RemoveStats} */
+    default void logStats(@NonNull RemoveStats stats) {
+        // no-op
+    }
 
     /**
      * Logs {@link OptimizeStats}
      */
-    void logStats(@NonNull OptimizeStats stats);
+    default void logStats(@NonNull OptimizeStats stats) {
+        // no-op
+    }
 
     /**
      * Logs {@link SetSchemaStats}
      */
-    void logStats(@NonNull SetSchemaStats stats);
+    default void logStats(@NonNull SetSchemaStats stats) {
+        // no-op
+    }
 
     /**
      * Logs {@link SchemaMigrationStats}
      */
-    void logStats(@NonNull SchemaMigrationStats stats);
+    default void logStats(@NonNull SchemaMigrationStats stats) {
+        // no-op
+    }
+
+    /**
+     * Logs a list of {@link SearchSessionStats}.
+     *
+     * <p>Since the client app may report search intents belonging to different search sessions in a
+     * single taken action reporting request, the stats extractor will separate them into multiple
+     * search sessions. Therefore, we need a list of {@link SearchSessionStats} here.
+     *
+     * <p>For example, the client app reports the following search intent sequence:
+     *
+     * <ul>
+     *   <li>t = 1, the user searches "a" with some clicks.
+     *   <li>t = 5, the user searches "app" with some clicks.
+     *   <li>t = 10000, the user searches "email" with some clicks.
+     * </ul>
+     *
+     * The extractor will detect "email" belongs to a completely independent search session, and
+     * creates 2 {@link SearchSessionStats} with search intents ["a", "app"] and ["email"]
+     * respectively.
+     */
+    default void logStats(@NonNull List<SearchSessionStats> searchSessionsStats) {
+        // no-op
+    }
 
     // TODO(b/173532925) Add remaining logStats once we add all the stats.
 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java
index 652fc94..74ed1c2 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchLoggerHelper.java
@@ -169,11 +169,9 @@
         Preconditions.checkNotNull(fromNativeStats);
         Preconditions.checkNotNull(toStatsBuilder);
 
-        @SuppressWarnings("deprecation")
-        int deleteType = DeleteStatsProto.DeleteType.Code.DEPRECATED_QUERY.getNumber();
         toStatsBuilder
                 .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
-                .setDeleteType(deleteType)
+                .setDeleteType(RemoveStats.QUERY)
                 .setDeletedDocumentCount(fromNativeStats.getNumDocumentsDeleted());
     }
 
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchMigrationHelper.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchMigrationHelper.java
index 659433a..e040e50 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchMigrationHelper.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchMigrationHelper.java
@@ -19,7 +19,6 @@
 import static androidx.appsearch.app.AppSearchResult.RESULT_INVALID_SCHEMA;
 import static androidx.appsearch.app.AppSearchResult.throwableToFailedResult;
 
-import android.os.Bundle;
 import android.os.Parcel;
 
 import androidx.annotation.NonNull;
@@ -32,6 +31,7 @@
 import androidx.appsearch.app.SearchSpec;
 import androidx.appsearch.app.SetSchemaResponse;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.safeparcel.GenericDocumentParcel;
 import androidx.appsearch.stats.SchemaMigrationStats;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
@@ -130,11 +130,11 @@
                                         + newDocument.getSchemaType()
                                         + ". But the schema types doesn't exist in the request");
                     }
-                    Bundle bundle = newDocument.getBundle();
+                    GenericDocumentParcel documentParcel = newDocument.getDocumentParcel();
                     byte[] serializedMessage;
                     Parcel parcel = Parcel.obtain();
                     try {
-                        parcel.writeBundle(bundle);
+                        documentParcel.writeToParcel(parcel, /* flags= */ 0);
                         serializedMessage = parcel.marshall();
                     } finally {
                         parcel.recycle();
@@ -224,17 +224,17 @@
             @NonNull CodedInputStream codedInputStream) throws IOException {
         byte[] serializedMessage = codedInputStream.readByteArray();
 
-        Bundle bundle;
+        GenericDocumentParcel documentParcel;
         Parcel parcel = Parcel.obtain();
         try {
             parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
             parcel.setDataPosition(0);
-            bundle = parcel.readBundle();
+            documentParcel = GenericDocumentParcel.CREATOR.createFromParcel(parcel);
         } finally {
             parcel.recycle();
         }
 
-        return new GenericDocument(bundle);
+        return new GenericDocument(documentParcel);
     }
 
     @Override
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
index ef8ec3b..7597399 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
@@ -93,7 +93,7 @@
             AppSearchBatchResult.Builder<String, GenericDocument> resultBuilder =
                     new AppSearchBatchResult.Builder<>();
 
-            Map<String, List<String>> typePropertyPaths = request.getProjectionsInternal();
+            Map<String, List<String>> typePropertyPaths = request.getProjections();
             CallerAccess access = new CallerAccess(mContext.getPackageName());
             for (String id : request.getIds()) {
                 try {
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/IcingOptionsConfig.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/IcingOptionsConfig.java
index 870df5a..d3246bf 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/IcingOptionsConfig.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/IcingOptionsConfig.java
@@ -36,7 +36,7 @@
 
     boolean DEFAULT_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT = false;
 
-    float DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD = 0.0f;
+    float DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD = 0.9f;
 
     /**
      * The default compression level in IcingSearchEngineOptions proto matches the
@@ -62,7 +62,7 @@
      */
     int DEFAULT_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD = 65536;
 
-    boolean DEFAULT_LITE_INDEX_SORT_AT_INDEXING = false;
+    boolean DEFAULT_LITE_INDEX_SORT_AT_INDEXING = true;
 
     /**
      * The default sort threshold for the lite index when sort at indexing is enabled.
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
index db55481..bc9715b 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
@@ -26,6 +26,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
 import androidx.appsearch.annotation.Document;
+import androidx.appsearch.app.AppSearchEnvironmentFactory;
 import androidx.appsearch.app.AppSearchSession;
 import androidx.appsearch.app.GlobalSearchSession;
 import androidx.appsearch.exceptions.AppSearchException;
@@ -39,7 +40,6 @@
 import java.io.File;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 /**
  * An AppSearch storage system which stores data locally in the app's storage space using a bundled
@@ -56,8 +56,6 @@
 public class LocalStorage {
     private static final String TAG = "AppSearchLocalStorage";
 
-    private static final String ICING_LIB_ROOT_DIR = "appsearch";
-
     /** Contains information about how to create the search session. */
     public static final class SearchContext {
         final Context mContext;
@@ -249,7 +247,8 @@
 
     // AppSearch multi-thread execution is guarded by Read & Write Lock in AppSearchImpl, all
     // mutate requests will need to gain write lock and query requests need to gain read lock.
-    static final Executor EXECUTOR = Executors.newCachedThreadPool();
+    static final Executor EXECUTOR = AppSearchEnvironmentFactory.getEnvironmentInstance()
+            .createCachedThreadPoolExecutor();
 
     private static volatile LocalStorage sInstance;
 
@@ -326,7 +325,8 @@
             @Nullable AppSearchLogger logger)
             throws AppSearchException {
         Preconditions.checkNotNull(context);
-        File icingDir = new File(context.getFilesDir(), ICING_LIB_ROOT_DIR);
+        File icingDir = AppSearchEnvironmentFactory.getEnvironmentInstance()
+                .getAppSearchDir(context, /* userHandle= */ null);
 
         long totalLatencyStartMillis = SystemClock.elapsedRealtime();
         InitializeStats.Builder initStatsBuilder = null;
@@ -346,8 +346,8 @@
                         /* shouldRetrieveParentInfo= */ true
                 ),
                 initStatsBuilder,
-                new JetpackOptimizeStrategy(),
-                /*visibilityChecker=*/null);
+                /*visibilityChecker=*/ null,
+                new JetpackOptimizeStrategy());
 
         if (logger != null) {
             initStatsBuilder.setTotalLatencyMillis(
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorageIcingOptionsConfig.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorageIcingOptionsConfig.java
index 74ea1c7..11b530e 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorageIcingOptionsConfig.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorageIcingOptionsConfig.java
@@ -96,6 +96,6 @@
 
     @Override
     public boolean getBuildPropertyExistenceMetadataHits() {
-        return DEFAULT_BUILD_PROPERTY_EXISTENCE_METADATA_HITS;
+        return true;
     }
 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/ObserverManager.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/ObserverManager.java
index d7c46ef..907a078 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/ObserverManager.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/ObserverManager.java
@@ -31,6 +31,7 @@
 import androidx.appsearch.observer.ObserverCallback;
 import androidx.appsearch.observer.ObserverSpec;
 import androidx.appsearch.observer.SchemaChangeInfo;
+import androidx.appsearch.util.ExceptionUtil;
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.core.util.ObjectsCompat;
@@ -75,8 +76,12 @@
 
         @Override
         public boolean equals(@Nullable Object o) {
-            if (this == o) return true;
-            if (!(o instanceof DocumentChangeGroupKey)) return false;
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof DocumentChangeGroupKey)) {
+                return false;
+            }
             DocumentChangeGroupKey that = (DocumentChangeGroupKey) o;
             return mPackageName.equals(that.mPackageName)
                     && mDatabaseName.equals(that.mDatabaseName)
@@ -410,8 +415,9 @@
 
                     try {
                         observerInfo.mObserverCallback.onSchemaChanged(schemaChangeInfo);
-                    } catch (Throwable t) {
-                        Log.w(TAG, "ObserverCallback threw exception during dispatch", t);
+                    } catch (RuntimeException e) {
+                        Log.w(TAG, "ObserverCallback threw exception during dispatch", e);
+                        ExceptionUtil.handleException(e);
                     }
                 }
             }
@@ -429,8 +435,9 @@
 
                     try {
                         observerInfo.mObserverCallback.onDocumentChanged(documentChangeInfo);
-                    } catch (Throwable t) {
-                        Log.w(TAG, "ObserverCallback threw exception during dispatch", t);
+                    } catch (RuntimeException e) {
+                        Log.w(TAG, "ObserverCallback threw exception during dispatch", e);
+                        ExceptionUtil.handleException(e);
                     }
                 }
             }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SchemaCache.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SchemaCache.java
new file mode 100644
index 0000000..7138182
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SchemaCache.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.collection.ArrayMap;
+import androidx.collection.ArraySet;
+import androidx.core.util.Preconditions;
+
+import com.google.android.icing.proto.SchemaTypeConfigProto;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ * Caches and manages schema information for AppSearch.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SchemaCache {
+    /**
+     * A map that contains schema types and SchemaTypeConfigProtos for all package-database
+     * prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each
+     * prefixed schema type to its respective SchemaTypeConfigProto.
+     */
+    private final Map<String, Map<String, SchemaTypeConfigProto>> mSchemaMap = new ArrayMap<>();
+
+    /**
+     * A map that contains schema types and all children schema types for all package-database
+     * prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each
+     * prefixed schema type to its respective list of children prefixed schema types.
+     */
+    private final Map<String, Map<String, List<String>>> mSchemaParentToChildrenMap =
+            new ArrayMap<>();
+
+    public SchemaCache() {
+    }
+
+    public SchemaCache(@NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+        mSchemaMap.putAll(Preconditions.checkNotNull(schemaMap));
+        rebuildSchemaParentToChildrenMap();
+    }
+
+    /**
+     * Returns the schema map for the given prefix.
+     */
+    @NonNull
+    public Map<String, SchemaTypeConfigProto> getSchemaMapForPrefix(@NonNull String prefix) {
+        Preconditions.checkNotNull(prefix);
+
+        Map<String, SchemaTypeConfigProto> schemaMap = mSchemaMap.get(prefix);
+        if (schemaMap == null) {
+            return Collections.emptyMap();
+        }
+        return schemaMap;
+    }
+
+    /**
+     * Returns a set of all prefixes stored in the cache.
+     */
+    @NonNull
+    public Set<String> getAllPrefixes() {
+        return Collections.unmodifiableSet(mSchemaMap.keySet());
+    }
+
+    /**
+     * Returns all prefixed schema types stored in the cache.
+     *
+     * <p>This method is inefficient to call repeatedly.
+     */
+    @NonNull
+    public List<String> getAllPrefixedSchemaTypes() {
+        List<String> cachedPrefixedSchemaTypes = new ArrayList<>();
+        for (Map<String, SchemaTypeConfigProto> value : mSchemaMap.values()) {
+            cachedPrefixedSchemaTypes.addAll(value.keySet());
+        }
+        return cachedPrefixedSchemaTypes;
+    }
+
+    /**
+     * Returns the schema types for the given set of prefixed schema types with their
+     * descendants, based on the schema parent-to-children map held in the cache.
+     */
+    @NonNull
+    public Set<String> getSchemaTypesWithDescendants(@NonNull String prefix,
+            @NonNull Set<String> prefixedSchemaTypes) {
+        Preconditions.checkNotNull(prefix);
+        Preconditions.checkNotNull(prefixedSchemaTypes);
+        Map<String, List<String>> parentToChildrenMap = mSchemaParentToChildrenMap.get(prefix);
+        if (parentToChildrenMap == null) {
+            parentToChildrenMap = Collections.emptyMap();
+        }
+
+        // Perform a BFS search on the inheritance graph started by the set of prefixedSchemaTypes.
+        Set<String> visited = new ArraySet<>();
+        Queue<String> prefixedSchemaQueue = new ArrayDeque<>(prefixedSchemaTypes);
+        while (!prefixedSchemaQueue.isEmpty()) {
+            String currentPrefixedSchema = prefixedSchemaQueue.poll();
+            if (visited.contains(currentPrefixedSchema)) {
+                continue;
+            }
+            visited.add(currentPrefixedSchema);
+            List<String> children = parentToChildrenMap.get(currentPrefixedSchema);
+            if (children == null) {
+                continue;
+            }
+            prefixedSchemaQueue.addAll(children);
+        }
+
+        return visited;
+    }
+
+    /**
+     * Rebuilds the schema parent-to-children map for the given prefix, based on the current
+     * schema map.
+     *
+     * <p>The schema parent-to-children map is required to be updated when
+     * {@link #addToSchemaMap} or {@link #removeFromSchemaMap} has been called. Otherwise, the
+     * results from {@link #getSchemaTypesWithDescendants} would be stale.
+     */
+    public void rebuildSchemaParentToChildrenMapForPrefix(@NonNull String prefix) {
+        Preconditions.checkNotNull(prefix);
+
+        mSchemaParentToChildrenMap.remove(prefix);
+        Map<String, SchemaTypeConfigProto> prefixedSchemaMap = mSchemaMap.get(prefix);
+        if (prefixedSchemaMap == null) {
+            return;
+        }
+
+        // Build the parent-to-children map for the current prefix.
+        Map<String, List<String>> parentToChildrenMap = new ArrayMap<>();
+        for (SchemaTypeConfigProto childSchemaConfig : prefixedSchemaMap.values()) {
+            for (int i = 0; i < childSchemaConfig.getParentTypesCount(); i++) {
+                String parent = childSchemaConfig.getParentTypes(i);
+                List<String> children = parentToChildrenMap.get(parent);
+                if (children == null) {
+                    children = new ArrayList<>();
+                    parentToChildrenMap.put(parent, children);
+                }
+                children.add(childSchemaConfig.getSchemaType());
+            }
+        }
+
+        // Record the map for the current prefix.
+        if (!parentToChildrenMap.isEmpty()) {
+            mSchemaParentToChildrenMap.put(prefix, parentToChildrenMap);
+        }
+    }
+
+    /**
+     * Rebuilds the schema parent-to-children map based on the current schema map.
+     *
+     * <p>The schema parent-to-children map is required to be updated when
+     * {@link #addToSchemaMap} or {@link #removeFromSchemaMap} has been called. Otherwise, the
+     * results from {@link #getSchemaTypesWithDescendants} would be stale.
+     */
+    public void rebuildSchemaParentToChildrenMap() {
+        mSchemaParentToChildrenMap.clear();
+        for (String prefix : mSchemaMap.keySet()) {
+            rebuildSchemaParentToChildrenMapForPrefix(prefix);
+        }
+    }
+
+    /**
+     * Adds a schema to the schema map.
+     *
+     * <p>Note that this method will invalidate the schema parent-to-children map in the cache,
+     * and either {@link #rebuildSchemaParentToChildrenMap} or
+     * {@link #rebuildSchemaParentToChildrenMapForPrefix} is required to be called to update the
+     * cache.
+     */
+    public void addToSchemaMap(@NonNull String prefix,
+            @NonNull SchemaTypeConfigProto schemaTypeConfigProto) {
+        Preconditions.checkNotNull(prefix);
+        Preconditions.checkNotNull(schemaTypeConfigProto);
+
+        Map<String, SchemaTypeConfigProto> schemaTypeMap = mSchemaMap.get(prefix);
+        if (schemaTypeMap == null) {
+            schemaTypeMap = new ArrayMap<>();
+            mSchemaMap.put(prefix, schemaTypeMap);
+        }
+        schemaTypeMap.put(schemaTypeConfigProto.getSchemaType(), schemaTypeConfigProto);
+    }
+
+    /**
+     * Removes a schema from the schema map.
+     *
+     * <p>Note that this method will invalidate the schema parent-to-children map in the cache,
+     * and either {@link #rebuildSchemaParentToChildrenMap} or
+     * {@link #rebuildSchemaParentToChildrenMapForPrefix} is required to be called to update the
+     * cache.
+     */
+    public void removeFromSchemaMap(@NonNull String prefix, @NonNull String schemaType) {
+        Preconditions.checkNotNull(prefix);
+        Preconditions.checkNotNull(schemaType);
+
+        Map<String, SchemaTypeConfigProto> schemaTypeMap = mSchemaMap.get(prefix);
+        if (schemaTypeMap != null) {
+            schemaTypeMap.remove(schemaType);
+        }
+    }
+
+    /**
+     * Removes the entry of the given prefix from both the schema map and the schema
+     * parent-to-children map, and returns the set of removed prefixed schema type.
+     */
+    @NonNull
+    public Set<String> removePrefix(@NonNull String prefix) {
+        Preconditions.checkNotNull(prefix);
+
+        Map<String, SchemaTypeConfigProto> removedSchemas =
+                Preconditions.checkNotNull(mSchemaMap.remove(prefix));
+        mSchemaParentToChildrenMap.remove(prefix);
+        return removedSchemas.keySet();
+    }
+
+    /**
+     * Clears all data in the cache.
+     */
+    public void clear() {
+        mSchemaMap.clear();
+        mSchemaParentToChildrenMap.clear();
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
index 36644b4..8094be0 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
@@ -34,6 +34,7 @@
 import androidx.appsearch.app.GetByDocumentIdRequest;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
 import androidx.appsearch.app.Migrator;
 import androidx.appsearch.app.PutDocumentsRequest;
 import androidx.appsearch.app.RemoveByDocumentIdRequest;
@@ -45,7 +46,6 @@
 import androidx.appsearch.app.SetSchemaRequest;
 import androidx.appsearch.app.SetSchemaResponse;
 import androidx.appsearch.app.StorageInfo;
-import androidx.appsearch.app.VisibilityDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.stats.OptimizeStats;
 import androidx.appsearch.localstorage.stats.RemoveStats;
@@ -80,7 +80,6 @@
     private final AppSearchImpl mAppSearchImpl;
     private final Executor mExecutor;
     private final Features mFeatures;
-    private final Context mContext;
     private final String mDatabaseName;
     @Nullable private final AppSearchLogger mLogger;
 
@@ -100,11 +99,11 @@
         mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
         mExecutor = Preconditions.checkNotNull(executor);
         mFeatures = Preconditions.checkNotNull(features);
-        mContext = Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(context);
         mDatabaseName = Preconditions.checkNotNull(databaseName);
         mLogger = logger;
 
-        mPackageName = mContext.getPackageName();
+        mPackageName = context.getPackageName();
         mSelfCallerAccess = new CallerAccess(/*callingPackageName=*/mPackageName);
     }
 
@@ -126,15 +125,15 @@
                         mPackageName, mDatabaseName);
             }
 
-            // Extract a Map<schema, VisibilityDocument> from the request.
-            List<VisibilityDocument> visibilityDocuments = VisibilityDocument
-                    .toVisibilityDocuments(request);
+            List<InternalVisibilityConfig> visibilityConfigs =
+                    InternalVisibilityConfig.toInternalVisibilityConfigs(request);
 
             Map<String, Migrator> migrators = request.getMigrators();
             // No need to trigger migration if user never set migrator.
-            if (migrators.size() == 0) {
+            if (migrators.isEmpty()) {
                 SetSchemaResponse setSchemaResponse = setSchemaNoMigrations(request,
-                        visibilityDocuments, firstSetSchemaStatsBuilder);
+                        visibilityConfigs,
+                        firstSetSchemaStatsBuilder);
 
                 long dispatchNotificationStartTimeMillis = SystemClock.elapsedRealtime();
                 // Schedule a task to dispatch change notifications. See requirements for where the
@@ -172,9 +171,9 @@
             Map<String, Migrator> activeMigrators = SchemaMigrationUtil.getActiveMigrators(
                     getSchemaResponse.getSchemas(), migrators, currentVersion, finalVersion);
             // No need to trigger migration if no migrator is active.
-            if (activeMigrators.size() == 0) {
+            if (activeMigrators.isEmpty()) {
                 SetSchemaResponse setSchemaResponse = setSchemaNoMigrations(request,
-                        visibilityDocuments, firstSetSchemaStatsBuilder);
+                        visibilityConfigs, firstSetSchemaStatsBuilder);
                 if (firstSetSchemaStatsBuilder != null) {
                     firstSetSchemaStatsBuilder.setTotalLatencyMillis(
                             (int) (SystemClock.elapsedRealtime() - startMillis));
@@ -194,7 +193,7 @@
                     mPackageName,
                     mDatabaseName,
                     new ArrayList<>(request.getSchemas()),
-                    visibilityDocuments,
+                    visibilityConfigs,
                     /*forceOverride=*/false,
                     request.getVersion(),
                     firstSetSchemaStatsBuilder);
@@ -233,7 +232,7 @@
                             mPackageName,
                             mDatabaseName,
                             new ArrayList<>(request.getSchemas()),
-                            visibilityDocuments,
+                            visibilityConfigs,
                             /*forceOverride=*/ true,
                             request.getVersion(),
                             secondSetSchemaStatsBuilder);
@@ -246,9 +245,8 @@
                     }
                 }
                 long secondSetSchemaLatencyEndTimeMillis = SystemClock.elapsedRealtime();
-                SetSchemaResponse.Builder responseBuilder = internalSetSchemaResponse
-                        .getSetSchemaResponse()
-                        .toBuilder()
+                SetSchemaResponse.Builder responseBuilder = new SetSchemaResponse.Builder(
+                        internalSetSchemaResponse.getSetSchemaResponse())
                         .addMigratedTypes(activeMigrators.keySet());
                 mIsMutated = true;
 
@@ -352,23 +350,26 @@
             @NonNull PutDocumentsRequest request) {
         Preconditions.checkNotNull(request);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
+
+        List<GenericDocument> documents = request.getGenericDocuments();
+        List<GenericDocument> takenActions = request.getTakenActionGenericDocuments();
+
         ListenableFuture<AppSearchBatchResult<String, Void>> future = execute(() -> {
             AppSearchBatchResult.Builder<String, Void> resultBuilder =
                     new AppSearchBatchResult.Builder<>();
-            for (int i = 0; i < request.getGenericDocuments().size(); i++) {
-                GenericDocument document = request.getGenericDocuments().get(i);
-                try {
-                    mAppSearchImpl.putDocument(
-                            mPackageName,
-                            mDatabaseName,
-                            document,
-                            /*sendChangeNotifications=*/ true,
-                            mLogger);
-                    resultBuilder.setSuccess(document.getId(), /*value=*/ null);
-                } catch (Throwable t) {
-                    resultBuilder.setResult(document.getId(), throwableToFailedResult(t));
-                }
+
+            // Normal documents.
+            for (int i = 0; i < documents.size(); i++) {
+                GenericDocument document = documents.get(i);
+                putGenericDocument(document, resultBuilder);
             }
+
+            // TakenAction documents.
+            for (int i = 0; i < takenActions.size(); i++) {
+                GenericDocument takenActionGenericDocument = takenActions.get(i);
+                putGenericDocument(takenActionGenericDocument, resultBuilder);
+            }
+
             // Now that the batch has been written. Persist the newly written data.
             mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
             mIsMutated = true;
@@ -382,7 +383,7 @@
 
         // The existing documents with same ID will be deleted, so there may be some resources that
         // could be released after optimize().
-        checkForOptimize(/*mutateBatchSize=*/ request.getGenericDocuments().size());
+        checkForOptimize(/*mutateBatchSize=*/ documents.size() + takenActions.size());
         return future;
     }
 
@@ -396,7 +397,7 @@
             AppSearchBatchResult.Builder<String, GenericDocument> resultBuilder =
                     new AppSearchBatchResult.Builder<>();
 
-            Map<String, List<String>> typePropertyPaths = request.getProjectionsInternal();
+            Map<String, List<String>> typePropertyPaths = request.getProjections();
             for (String id : request.getIds()) {
                 try {
                     GenericDocument document =
@@ -583,7 +584,7 @@
      * forceoverride in the request.
      */
     private SetSchemaResponse setSchemaNoMigrations(@NonNull SetSchemaRequest request,
-            @NonNull List<VisibilityDocument> visibilityDocuments,
+            @NonNull List<InternalVisibilityConfig> visibilityConfigs,
             @Nullable SetSchemaStats.Builder setSchemaStatsBuilder)
             throws AppSearchException {
         if (setSchemaStatsBuilder != null) {
@@ -593,7 +594,7 @@
                 mPackageName,
                 mDatabaseName,
                 new ArrayList<>(request.getSchemas()),
-                visibilityDocuments,
+                visibilityConfigs,
                 request.isForceOverride(),
                 request.getVersion(),
                 setSchemaStatsBuilder);
@@ -621,6 +622,28 @@
         mAppSearchImpl.dispatchAndClearChangeNotifications();
     }
 
+    /**
+     * Calls {@link AppSearchImpl} to put a generic document and sets the result.
+     *
+     * @param document the {@link GenericDocument} to put.
+     * @param resultBuilder an {@link AppSearchBatchResult.Builder} object for collecting the
+     *                      result.
+     */
+    private void putGenericDocument(
+            GenericDocument document, AppSearchBatchResult.Builder<String, Void> resultBuilder) {
+        try {
+            mAppSearchImpl.putDocument(
+                    mPackageName,
+                    mDatabaseName,
+                    document,
+                    /*sendChangeNotifications=*/ true,
+                    mLogger);
+            resultBuilder.setSuccess(document.getId(), /*value=*/ null);
+        } catch (Throwable t) {
+            resultBuilder.setResult(document.getId(), throwableToFailedResult(t));
+        }
+    }
+
     private void checkForOptimize(int mutateBatchSize) {
         mExecutor.execute(() -> {
             long totalLatencyStartMillis = SystemClock.elapsedRealtime();
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverter.java
index aedd191..2320d8b 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -19,6 +19,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.AppSearchConfig;
@@ -54,6 +55,8 @@
     private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
     private static final byte[][] EMPTY_BYTES_ARRAY = new byte[0][0];
     private static final GenericDocument[] EMPTY_DOCUMENT_ARRAY = new GenericDocument[0];
+    private static final EmbeddingVector[] EMPTY_EMBEDDING_ARRAY =
+            new EmbeddingVector[0];
 
     private GenericDocumentToProtoConverter() {
     }
@@ -109,6 +112,12 @@
                     DocumentProto proto = toDocumentProto(documentValues[j]);
                     propertyProto.addDocumentValues(proto);
                 }
+            } else if (property instanceof EmbeddingVector[]) {
+                EmbeddingVector[] embeddingValues = (EmbeddingVector[]) property;
+                for (int j = 0; j < embeddingValues.length; j++) {
+                    propertyProto.addVectorValues(
+                            embeddingVectorToVectorProto(embeddingValues[j]));
+                }
             } else if (property == null) {
                 throw new IllegalStateException(
                         String.format("Property \"%s\" doesn't have any value!", name));
@@ -205,6 +214,13 @@
                             schemaTypeMap, config);
                 }
                 documentBuilder.setPropertyDocument(name, values);
+            } else if (property.getVectorValuesCount() > 0) {
+                EmbeddingVector[] values =
+                        new EmbeddingVector[property.getVectorValuesCount()];
+                for (int j = 0; j < values.length; j++) {
+                    values[j] = vectorProtoToEmbeddingVector(property.getVectorValues(j));
+                }
+                documentBuilder.setPropertyEmbedding(name, values);
             } else {
                 // TODO(b/184966497): Optimize by caching PropertyConfigProto
                 SchemaTypeConfigProto schema =
@@ -216,6 +232,37 @@
     }
 
     /**
+     * Converts a {@link PropertyProto.VectorProto} into an {@link EmbeddingVector}.
+     */
+    @NonNull
+    public static EmbeddingVector vectorProtoToEmbeddingVector(
+            @NonNull PropertyProto.VectorProto vectorProto) {
+        Preconditions.checkNotNull(vectorProto);
+
+        float[] values = new float[vectorProto.getValuesCount()];
+        for (int i = 0; i < vectorProto.getValuesCount(); i++) {
+            values[i] = vectorProto.getValues(i);
+        }
+        return new EmbeddingVector(values, vectorProto.getModelSignature());
+    }
+
+    /**
+     * Converts an {@link EmbeddingVector} into a {@link PropertyProto.VectorProto}.
+     */
+    @NonNull
+    public static PropertyProto.VectorProto embeddingVectorToVectorProto(
+            @NonNull EmbeddingVector embedding) {
+        Preconditions.checkNotNull(embedding);
+
+        PropertyProto.VectorProto.Builder builder = PropertyProto.VectorProto.newBuilder();
+        for (int i = 0; i < embedding.getValues().length; i++) {
+            builder.addValues(embedding.getValues()[i]);
+        }
+        builder.setModelSignature(embedding.getModelSignature());
+        return builder.build();
+    }
+
+    /**
      * Get the list of unprefixed parent type names of {@code prefixedSchemaType}.
      *
      * <p>It's guaranteed that child types always appear before parent types in the list.
@@ -305,6 +352,9 @@
             case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT:
                 documentBuilder.setPropertyDocument(propertyName, EMPTY_DOCUMENT_ARRAY);
                 break;
+            case AppSearchSchema.PropertyConfig.DATA_TYPE_EMBEDDING:
+                documentBuilder.setPropertyEmbedding(propertyName, EMPTY_EMBEDDING_ARRAY);
+                break;
             default:
                 throw new IllegalStateException("Unknown type of value: " + propertyName);
         }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/ResultCodeToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/ResultCodeToProtoConverter.java
index 2106917..eedbb0d 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/ResultCodeToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/ResultCodeToProtoConverter.java
@@ -35,7 +35,7 @@
     private ResultCodeToProtoConverter() {}
 
     /** Converts an {@link StatusProto.Code} into a {@link AppSearchResult.ResultCode}. */
-    public static @AppSearchResult.ResultCode int toResultCode(
+    @AppSearchResult.ResultCode public static int toResultCode(
             @NonNull StatusProto.Code statusCode) {
         switch (statusCode) {
             case OK:
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
index e21213f..5014f04 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverter.java
@@ -24,6 +24,7 @@
 import androidx.core.util.Preconditions;
 
 import com.google.android.icing.proto.DocumentIndexingConfig;
+import com.google.android.icing.proto.EmbeddingIndexingConfig;
 import com.google.android.icing.proto.IntegerIndexingConfig;
 import com.google.android.icing.proto.JoinableConfig;
 import com.google.android.icing.proto.PropertyConfigProto;
@@ -55,6 +56,7 @@
         Preconditions.checkNotNull(schema);
         SchemaTypeConfigProto.Builder protoBuilder = SchemaTypeConfigProto.newBuilder()
                 .setSchemaType(schema.getSchemaType())
+                .setDescription(schema.getDescription())
                 .setVersion(version);
         List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
         for (int i = 0; i < properties.size(); i++) {
@@ -70,7 +72,8 @@
             @NonNull AppSearchSchema.PropertyConfig property) {
         Preconditions.checkNotNull(property);
         PropertyConfigProto.Builder builder = PropertyConfigProto.newBuilder()
-                .setPropertyName(property.getName());
+                .setPropertyName(property.getName())
+                .setDescription(property.getDescription());
 
         // Set dataType
         @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType();
@@ -101,11 +104,6 @@
                         .setValueType(
                                 convertJoinableValueTypeToProto(
                                         stringProperty.getJoinableValueType()))
-                        // @exportToFramework:startStrip()
-                        // Do not call this in framework as it will populate the proto field and
-                        // fail comparison tests.
-                        .setPropagateDelete(stringProperty.getDeletionPropagation())
-                        // @exportToFramework:endStrip()
                         .build();
                 builder.setJoinableConfig(joinableConfig);
             }
@@ -140,6 +138,20 @@
                         .build();
                 builder.setIntegerIndexingConfig(integerIndexingConfig);
             }
+        } else if (property instanceof AppSearchSchema.EmbeddingPropertyConfig) {
+            AppSearchSchema.EmbeddingPropertyConfig embeddingProperty =
+                    (AppSearchSchema.EmbeddingPropertyConfig) property;
+            // Set embedding indexing config only if it is indexable (i.e. not INDEXING_TYPE_NONE).
+            // Non-indexable embedding property only requires to builder.setDataType, without the
+            // need to set an EmbeddingIndexingConfig.
+            if (embeddingProperty.getIndexingType()
+                    != AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE) {
+                EmbeddingIndexingConfig embeddingIndexingConfig =
+                        EmbeddingIndexingConfig.newBuilder().setEmbeddingIndexingType(
+                                convertEmbeddingIndexingTypeToProto(
+                                        embeddingProperty.getIndexingType())).build();
+                builder.setEmbeddingIndexingConfig(embeddingIndexingConfig);
+            }
         }
         return builder.build();
     }
@@ -154,6 +166,7 @@
         Preconditions.checkNotNull(proto);
         AppSearchSchema.Builder builder =
                 new AppSearchSchema.Builder(proto.getSchemaType());
+        builder.setDescription(proto.getDescription());
         List<PropertyConfigProto> properties = proto.getPropertiesList();
         for (int i = 0; i < properties.size(); i++) {
             AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i));
@@ -177,20 +190,26 @@
                 return toLongPropertyConfig(proto);
             case DOUBLE:
                 return new AppSearchSchema.DoublePropertyConfig.Builder(proto.getPropertyName())
+                        .setDescription(proto.getDescription())
                         .setCardinality(proto.getCardinality().getNumber())
                         .build();
             case BOOLEAN:
                 return new AppSearchSchema.BooleanPropertyConfig.Builder(proto.getPropertyName())
+                        .setDescription(proto.getDescription())
                         .setCardinality(proto.getCardinality().getNumber())
                         .build();
             case BYTES:
                 return new AppSearchSchema.BytesPropertyConfig.Builder(proto.getPropertyName())
+                        .setDescription(proto.getDescription())
                         .setCardinality(proto.getCardinality().getNumber())
                         .build();
             case DOCUMENT:
                 return toDocumentPropertyConfig(proto);
+            case VECTOR:
+                return toEmbeddingPropertyConfig(proto);
             default:
-                throw new IllegalArgumentException("Invalid dataType: " + proto.getDataType());
+                throw new IllegalArgumentException(
+                        "Invalid dataType code: " + proto.getDataType().getNumber());
         }
     }
 
@@ -199,11 +218,11 @@
             @NonNull PropertyConfigProto proto) {
         AppSearchSchema.StringPropertyConfig.Builder builder =
                 new AppSearchSchema.StringPropertyConfig.Builder(proto.getPropertyName())
+                        .setDescription(proto.getDescription())
                         .setCardinality(proto.getCardinality().getNumber())
                         .setJoinableValueType(
                                 convertJoinableValueTypeFromProto(
                                         proto.getJoinableConfig().getValueType()))
-                        .setDeletionPropagation(proto.getJoinableConfig().getPropagateDelete())
                         .setTokenizerType(
                                 proto.getStringIndexingConfig().getTokenizerType().getNumber());
 
@@ -220,6 +239,7 @@
         AppSearchSchema.DocumentPropertyConfig.Builder builder =
                 new AppSearchSchema.DocumentPropertyConfig.Builder(
                                 proto.getPropertyName(), proto.getSchemaType())
+                        .setDescription(proto.getDescription())
                         .setCardinality(proto.getCardinality().getNumber())
                         .setShouldIndexNestedProperties(
                                 proto.getDocumentIndexingConfig().getIndexNestedProperties());
@@ -233,6 +253,7 @@
             @NonNull PropertyConfigProto proto) {
         AppSearchSchema.LongPropertyConfig.Builder builder =
                 new AppSearchSchema.LongPropertyConfig.Builder(proto.getPropertyName())
+                        .setDescription(proto.getDescription())
                         .setCardinality(proto.getCardinality().getNumber());
 
         // Set indexingType
@@ -244,6 +265,21 @@
     }
 
     @NonNull
+    private static AppSearchSchema.EmbeddingPropertyConfig toEmbeddingPropertyConfig(
+            @NonNull PropertyConfigProto proto) {
+        AppSearchSchema.EmbeddingPropertyConfig.Builder builder =
+                new AppSearchSchema.EmbeddingPropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber());
+
+        // Set indexingType
+        EmbeddingIndexingConfig.EmbeddingIndexingType.Code embeddingIndexingType =
+                proto.getEmbeddingIndexingConfig().getEmbeddingIndexingType();
+        builder.setIndexingType(convertEmbeddingIndexingTypeFromProto(embeddingIndexingType));
+
+        return builder.build();
+    }
+
+    @NonNull
     private static JoinableConfig.ValueType.Code convertJoinableValueTypeToProto(
             @AppSearchSchema.StringPropertyConfig.JoinableValueType int joinableValueType) {
         switch (joinableValueType) {
@@ -265,12 +301,11 @@
                 return AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
             case QUALIFIED_ID:
                 return AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID;
-            default:
-                // Avoid crashing in the 'read' path; we should try to interpret the document to the
-                // extent possible.
-                Log.w(TAG, "Invalid joinableValueType: " + joinableValueType.getNumber());
-                return AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
         }
+        // Avoid crashing in the 'read' path; we should try to interpret the document to the
+        // extent possible.
+        Log.w(TAG, "Invalid joinableValueType: " + joinableValueType.getNumber());
+        return AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
     }
 
     @NonNull
@@ -344,4 +379,34 @@
                 return AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
         }
     }
+
+    @NonNull
+    private static EmbeddingIndexingConfig.EmbeddingIndexingType.Code
+            convertEmbeddingIndexingTypeToProto(
+            @AppSearchSchema.EmbeddingPropertyConfig.IndexingType int indexingType) {
+        switch (indexingType) {
+            case AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE:
+                return EmbeddingIndexingConfig.EmbeddingIndexingType.Code.UNKNOWN;
+            case AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY:
+                return EmbeddingIndexingConfig.EmbeddingIndexingType.Code.LINEAR_SEARCH;
+            default:
+                throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
+        }
+    }
+
+    @AppSearchSchema.EmbeddingPropertyConfig.IndexingType
+    private static int convertEmbeddingIndexingTypeFromProto(
+            @NonNull EmbeddingIndexingConfig.EmbeddingIndexingType.Code indexingType) {
+        switch (indexingType) {
+            case UNKNOWN:
+                return AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE;
+            case LINEAR_SEARCH:
+                return AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY;
+            default:
+                // Avoid crashing in the 'read' path; we should try to interpret the document to the
+                // extent possible.
+                Log.w(TAG, "Invalid indexingType: " + indexingType.getNumber());
+                return AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE;
+        }
+    }
 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverter.java
index 8652cca..b88eccd 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverter.java
@@ -20,8 +20,6 @@
 import static androidx.appsearch.localstorage.util.PrefixUtil.getPackageName;
 import static androidx.appsearch.localstorage.util.PrefixUtil.removePrefixesFromDocument;
 
-import android.os.Bundle;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.app.AppSearchResult;
@@ -30,7 +28,7 @@
 import androidx.appsearch.app.SearchResultPage;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.AppSearchConfig;
-import androidx.core.util.Preconditions;
+import androidx.appsearch.localstorage.SchemaCache;
 
 import com.google.android.icing.proto.DocumentProto;
 import com.google.android.icing.proto.SchemaTypeConfigProto;
@@ -39,6 +37,7 @@
 import com.google.android.icing.proto.SnippetProto;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -55,24 +54,20 @@
      * Translate a {@link SearchResultProto} into {@link SearchResultPage}.
      *
      * @param proto         The {@link SearchResultProto} containing results.
-     * @param schemaMap     The cached Map of <Prefix, Map<PrefixedSchemaType, schemaProto>>
-     *                      stores all existing prefixed schema type.
+     * @param schemaCache   The SchemaCache instance held in AppSearch.
      * @return {@link SearchResultPage} of results.
      */
     @NonNull
     public static SearchResultPage toSearchResultPage(@NonNull SearchResultProto proto,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
-            @NonNull AppSearchConfig config)
+            @NonNull SchemaCache schemaCache, @NonNull AppSearchConfig config)
             throws AppSearchException {
-        Bundle bundle = new Bundle();
-        bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken());
-        ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount());
+        List<SearchResult> results = new ArrayList<>(proto.getResultsCount());
         for (int i = 0; i < proto.getResultsCount(); i++) {
-            SearchResult result = toUnprefixedSearchResult(proto.getResults(i), schemaMap, config);
-            resultBundles.add(result.getBundle());
+            SearchResult result = toUnprefixedSearchResult(proto.getResults(i), schemaCache,
+                    config);
+            results.add(result);
         }
-        bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
-        return new SearchResultPage(bundle);
+        return new SearchResultPage(proto.getNextPageToken(), results);
     }
 
     /**
@@ -80,26 +75,28 @@
      * database prefix will be removed from {@link GenericDocument}.
      *
      * @param proto          The proto to be converted.
-     * @param schemaMap      The cached Map of <Prefix, Map<PrefixedSchemaType, schemaProto>>
-     *                       stores all existing prefixed schema type.
+     * @param schemaCache   The SchemaCache instance held in AppSearch.
      * @return A {@link SearchResult}.
      */
     @NonNull
     private static SearchResult toUnprefixedSearchResult(
             @NonNull SearchResultProto.ResultProto proto,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+            @NonNull SchemaCache schemaCache,
             @NonNull AppSearchConfig config) throws AppSearchException {
 
         DocumentProto.Builder documentBuilder = proto.getDocument().toBuilder();
         String prefix = removePrefixesFromDocument(documentBuilder);
         Map<String, SchemaTypeConfigProto> schemaTypeMap =
-                Preconditions.checkNotNull(schemaMap.get(prefix));
+                schemaCache.getSchemaMapForPrefix(prefix);
         GenericDocument document =
                 GenericDocumentToProtoConverter.toGenericDocument(documentBuilder, prefix,
                         schemaTypeMap, config);
         SearchResult.Builder builder =
                 new SearchResult.Builder(getPackageName(prefix), getDatabaseName(prefix))
                         .setGenericDocument(document).setRankingSignal(proto.getScore());
+        for (int i = 0; i < proto.getAdditionalScoresCount(); i++) {
+            builder.addInformationalRankingSignal(proto.getAdditionalScores(i));
+        }
         if (proto.hasSnippet()) {
             for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) {
                 SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i);
@@ -118,7 +115,8 @@
                         "Nesting joined results within joined results not allowed.");
             }
 
-            builder.addJoinedResult(toUnprefixedSearchResult(joinedResultProto, schemaMap, config));
+            builder.addJoinedResult(
+                    toUnprefixedSearchResult(joinedResultProto, schemaCache, config));
         }
         return builder.build();
     }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
index 42cfd44..ad5be47 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
@@ -26,11 +26,14 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.EmbeddingVector;
+import androidx.appsearch.app.FeatureConstants;
 import androidx.appsearch.app.JoinSpec;
 import androidx.appsearch.app.SearchResult;
 import androidx.appsearch.app.SearchSpec;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.IcingOptionsConfig;
+import androidx.appsearch.localstorage.SchemaCache;
 import androidx.appsearch.localstorage.visibilitystore.CallerAccess;
 import androidx.appsearch.localstorage.visibilitystore.VisibilityChecker;
 import androidx.appsearch.localstorage.visibilitystore.VisibilityStore;
@@ -92,11 +95,9 @@
     private final Map<String, Set<String>> mNamespaceMap;
 
     /**
-     *The cached Map of {@code <Prefix, Map<PrefixedSchemaType, schemaProto>>} stores all
-     * prefixed schema filters which are stored inAppSearch. This is a field so that we can
-     * generated nested protos.
+     * The SchemaCache instance held in AppSearch.
      */
-    private final Map<String, Map<String, SchemaTypeConfigProto>> mSchemaMap;
+    private final SchemaCache mSchemaCache;
 
     /**
      * Optional config flags in {@link SearchSpecProto}.
@@ -120,21 +121,20 @@
      *                           database prefixes are allowed, so nothing will be searched.
      * @param namespaceMap  The cached Map of {@code <Prefix, Set<PrefixedNamespace>>} stores
      *                      all prefixed namespace filters which are stored in AppSearch.
-     * @param schemaMap     The cached Map of {@code <Prefix, Map<PrefixedSchemaType, schemaProto>>}
-     *                      stores all prefixed schema filters which are stored inAppSearch.
+     * @param schemaCache   The SchemaCache instance held in AppSearch.
      */
     public SearchSpecToProtoConverter(
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec,
             @NonNull Set<String> allAllowedPrefixes,
             @NonNull Map<String, Set<String>> namespaceMap,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+            @NonNull SchemaCache schemaCache,
             @NonNull IcingOptionsConfig icingOptionsConfig) {
         mQueryExpression = Preconditions.checkNotNull(queryExpression);
         mSearchSpec = Preconditions.checkNotNull(searchSpec);
         mAllAllowedPrefixes = Preconditions.checkNotNull(allAllowedPrefixes);
         mNamespaceMap = Preconditions.checkNotNull(namespaceMap);
-        mSchemaMap = Preconditions.checkNotNull(schemaMap);
+        mSchemaCache = Preconditions.checkNotNull(schemaCache);
         mIcingOptionsConfig = Preconditions.checkNotNull(icingOptionsConfig);
 
         // This field holds the prefix filters for the SearchSpec currently being handled, which
@@ -169,7 +169,7 @@
         if (!mTargetPrefixedNamespaceFilters.isEmpty()) {
             mTargetPrefixedSchemaFilters =
                     SearchSpecToProtoConverterUtil.generateTargetSchemaFilters(
-                            mCurrentSearchSpecPrefixFilters, schemaMap,
+                            mCurrentSearchSpecPrefixFilters, schemaCache,
                             searchSpec.getFilterSchemas());
         } else {
             mTargetPrefixedSchemaFilters = new ArraySet<>();
@@ -185,17 +185,17 @@
                 joinSpec.getNestedSearchSpec(),
                 mAllAllowedPrefixes,
                 namespaceMap,
-                schemaMap,
+                schemaCache,
                 mIcingOptionsConfig);
     }
 
     /**
-     * @return whether this search's target filters are empty. If any target filter is empty, we
+     * Returns whether this search's target filters are empty. If any target filter is empty, we
      * should skip send request to Icing.
      *
-     * <p> The nestedConverter is not checked as {@link SearchResult}s from the nested query have
-     * to be joined to a {@link SearchResult} from the parent query. If the parent query has
-     * nothing to search, then so does the child query.
+     * <p>The nestedConverter is not checked as {@link SearchResult}s from the nested query have to
+     * be joined to a {@link SearchResult} from the parent query. If the parent query has nothing to
+     * search, then so does the child query.
      */
     public boolean hasNothingToSearch() {
         return mTargetPrefixedNamespaceFilters.isEmpty() || mTargetPrefixedSchemaFilters.isEmpty();
@@ -291,6 +291,13 @@
                 .addAllSchemaTypeFilters(mTargetPrefixedSchemaFilters)
                 .setUseReadOnlySearch(mIcingOptionsConfig.getUseReadOnlySearch());
 
+        List<EmbeddingVector> searchEmbeddings = mSearchSpec.getSearchEmbeddings();
+        for (int i = 0; i < searchEmbeddings.size(); i++) {
+            protoBuilder.addEmbeddingQueryVectors(
+                    GenericDocumentToProtoConverter.embeddingVectorToVectorProto(
+                            searchEmbeddings.get(i)));
+        }
+
         // Convert type property filter map into type property mask proto.
         for (Map.Entry<String, List<String>> entry :
                 mSearchSpec.getFilterProperties().entrySet()) {
@@ -319,11 +326,22 @@
         }
         protoBuilder.setTermMatchType(termMatchCodeProto);
 
+        @SearchSpec.EmbeddingSearchMetricType int embeddingSearchMetricType =
+                mSearchSpec.getDefaultEmbeddingSearchMetricType();
+        SearchSpecProto.EmbeddingQueryMetricType.Code embeddingSearchMetricTypeProto =
+                SearchSpecProto.EmbeddingQueryMetricType.Code.forNumber(embeddingSearchMetricType);
+        if (embeddingSearchMetricTypeProto == null || embeddingSearchMetricTypeProto.equals(
+                SearchSpecProto.EmbeddingQueryMetricType.Code.UNKNOWN)) {
+            throw new IllegalArgumentException(
+                    "Invalid embedding search metric type: " + embeddingSearchMetricType);
+        }
+        protoBuilder.setEmbeddingQueryMetricType(embeddingSearchMetricTypeProto);
+
         if (mNestedConverter != null && !mNestedConverter.hasNothingToSearch()) {
             JoinSpecProto.NestedSpecProto nestedSpec =
                     JoinSpecProto.NestedSpecProto.newBuilder()
                             .setResultSpec(mNestedConverter.toResultSpecProto(
-                                    mNamespaceMap, mSchemaMap))
+                                    mNamespaceMap, mSchemaCache))
                             .setScoringSpec(mNestedConverter.toScoringSpecProto())
                             .setSearchSpec(mNestedConverter.toSearchSpecProto())
                             .build();
@@ -342,18 +360,18 @@
             protoBuilder.setJoinSpec(joinSpecProtoBuilder);
         }
 
-        // TODO(b/208654892) Remove this field once EXPERIMENTAL_ICING_ADVANCED_QUERY is fully
-        //  supported.
-        boolean turnOnIcingAdvancedQuery =
-                mSearchSpec.isNumericSearchEnabled() || mSearchSpec.isVerbatimSearchEnabled()
-                        || mSearchSpec.isListFilterQueryLanguageEnabled();
-        if (turnOnIcingAdvancedQuery) {
-            protoBuilder.setSearchType(
-                    SearchSpecProto.SearchType.Code.EXPERIMENTAL_ICING_ADVANCED_QUERY);
+        if (mSearchSpec.isListFilterHasPropertyFunctionEnabled()
+                && !mIcingOptionsConfig.getBuildPropertyExistenceMetadataHits()) {
+            // This condition should never be reached as long as Features.isFeatureSupported() is
+            // consistent with IcingOptionsConfig.
+            throw new UnsupportedOperationException(
+                    FeatureConstants.LIST_FILTER_HAS_PROPERTY_FUNCTION
+                            + " is currently not operational because the building process for the "
+                            + "associated metadata has not yet been turned on.");
         }
 
         // Set enabled features
-        protoBuilder.addAllEnabledFeatures(mSearchSpec.getEnabledFeatures());
+        protoBuilder.addAllEnabledFeatures(toIcingSearchFeatures(mSearchSpec.getEnabledFeatures()));
 
         return protoBuilder.build();
     }
@@ -390,14 +408,12 @@
      *
      * @param namespaceMap    The cached Map of {@code <Prefix, Set<PrefixedNamespace>>} stores
      *                        all existing prefixed namespace.
-     * @param schemaMap       The cached Map of {@code <Prefix, Map<PrefixedSchemaType,
-     *                        schemaProto>>} stores all prefixed schema filters which are stored
-     *                        inAppSearch.
+     * @param schemaCache     The SchemaCache instance held in AppSearch.
      */
     @NonNull
     public ResultSpecProto toResultSpecProto(
             @NonNull Map<String, Set<String>> namespaceMap,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+            @NonNull SchemaCache schemaCache) {
         ResultSpecProto.Builder resultSpecBuilder = ResultSpecProto.newBuilder()
                 .setNumPerPage(mSearchSpec.getResultCountPerPage())
                 .setSnippetSpec(
@@ -429,7 +445,7 @@
                 break;
             case SearchSpec.GROUPING_TYPE_PER_SCHEMA:
                 addPerSchemaResultGrouping(mCurrentSearchSpecPrefixFilters,
-                        mSearchSpec.getResultGroupingLimit(), schemaMap, resultSpecBuilder);
+                        mSearchSpec.getResultGroupingLimit(), schemaCache, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.SCHEMA_TYPE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_NAMESPACE:
@@ -441,20 +457,20 @@
             case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
                 addPerPackagePerSchemaResultGroupings(mCurrentSearchSpecPrefixFilters,
                         mSearchSpec.getResultGroupingLimit(),
-                        schemaMap, resultSpecBuilder);
+                        schemaCache, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.SCHEMA_TYPE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_NAMESPACE | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
                 addPerNamespaceAndSchemaResultGrouping(mCurrentSearchSpecPrefixFilters,
                         mSearchSpec.getResultGroupingLimit(),
-                        namespaceMap, schemaMap, resultSpecBuilder);
+                        namespaceMap, schemaCache, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE_AND_SCHEMA_TYPE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_NAMESPACE
                 | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
                 addPerPackagePerNamespacePerSchemaResultGrouping(mCurrentSearchSpecPrefixFilters,
                         mSearchSpec.getResultGroupingLimit(),
-                        namespaceMap, schemaMap, resultSpecBuilder);
+                        namespaceMap, schemaCache, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE_AND_SCHEMA_TYPE;
                 break;
             default:
@@ -468,7 +484,7 @@
         // Rewrite filters to include a database prefix.
         for (int i = 0; i < typePropertyMaskBuilders.size(); i++) {
             String unprefixedType = typePropertyMaskBuilders.get(i).getSchemaType();
-            if (unprefixedType.equals(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD)) {
+            if (unprefixedType.equals(SearchSpec.SCHEMA_TYPE_WILDCARD)) {
                 resultSpecBuilder.addTypePropertyMasks(typePropertyMaskBuilders.get(i).build());
             } else {
                 // Qualify the given schema types
@@ -502,6 +518,8 @@
         addTypePropertyWeights(mSearchSpec.getPropertyWeights(), protoBuilder);
 
         protoBuilder.setAdvancedScoringExpression(mSearchSpec.getAdvancedRankingExpression());
+        protoBuilder.addAllAdditionalAdvancedScoringExpressions(
+                mSearchSpec.getInformationalRankingExpressions());
 
         return protoBuilder.build();
     }
@@ -536,6 +554,26 @@
     }
 
     /**
+     * Maps a list of AppSearch search feature strings to the list of the corresponding Icing
+     * feature strings.
+     *
+     * @param appSearchFeatures The list of AppSearch search feature strings.
+     */
+    @NonNull
+    private static List<String> toIcingSearchFeatures(@NonNull List<String> appSearchFeatures) {
+        List<String> result = new ArrayList<>();
+        for (int i = 0; i < appSearchFeatures.size(); i++) {
+            String appSearchFeature = appSearchFeatures.get(i);
+            if (appSearchFeature.equals(FeatureConstants.LIST_FILTER_HAS_PROPERTY_FUNCTION)) {
+                result.add("HAS_PROPERTY_FUNCTION");
+            } else {
+                result.add(appSearchFeature);
+            }
+        }
+        return result;
+    }
+
+    /**
      * Returns a Map of namespace to prefixedNamespaces. This is NOT necessarily the
      * same as the list of namespaces. If a namespace exists under different packages and/or
      * different databases, they should still be grouped together.
@@ -593,7 +631,7 @@
             String packageName = getPackageName(prefix);
             // Create a new prefix without the database name. This will allow us to group namespaces
             // that have the same name and package but a different database name together.
-            String emptyDatabasePrefix = createPrefix(packageName, /*databaseName*/"");
+            String emptyDatabasePrefix = createPrefix(packageName, /* databaseName= */"");
             for (String prefixedNamespace : prefixedNamespaces) {
                 String namespace;
                 try {
@@ -623,17 +661,15 @@
      * different databases, they should still be grouped together.
      *
      * @param prefixes      Prefixes that we should prepend to all our filters.
-     * @param schemaMap     The schema map contains all prefixed existing schema types.
+     * @param schemaCache   The SchemaCache instance held in AppSearch.
      */
     private static Map<String, List<String>> getSchemaToPrefixedSchemas(
             @NonNull Set<String> prefixes,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+            @NonNull SchemaCache schemaCache) {
         Map<String, List<String>> schemaToPrefixedSchemas = new ArrayMap<>();
         for (String prefix : prefixes) {
-            Map<String, SchemaTypeConfigProto> prefixedSchemas = schemaMap.get(prefix);
-            if (prefixedSchemas == null) {
-                continue;
-            }
+            Map<String, SchemaTypeConfigProto> prefixedSchemas =
+                    schemaCache.getSchemaMapForPrefix(prefix);
             for (String prefixedSchema : prefixedSchemas.keySet()) {
                 String schema;
                 try {
@@ -661,17 +697,15 @@
      * schema, then those should be grouped together.
      *
      * @param prefixes      Prefixes that we should prepend to all our filters.
-     * @param schemaMap     The schema map contains all prefixed existing schema types.
+     * @param schemaCache   The SchemaCache instance held in AppSearch.
      */
     private static Map<String, List<String>> getPackageAndSchemaToPrefixedSchemas(
             @NonNull Set<String> prefixes,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+            @NonNull SchemaCache schemaCache) {
         Map<String, List<String>> packageAndSchemaToSchemas = new ArrayMap<>();
         for (String prefix : prefixes) {
-            Map<String, SchemaTypeConfigProto> prefixedSchemas = schemaMap.get(prefix);
-            if (prefixedSchemas == null) {
-                continue;
-            }
+            Map<String, SchemaTypeConfigProto> prefixedSchemas =
+                    schemaCache.getSchemaMapForPrefix(prefix);
             String packageName = getPackageName(prefix);
             // Create a new prefix without the database name. This will allow us to group schemas
             // that have the same name and package but a different database name together.
@@ -733,16 +767,16 @@
      *
      * @param prefixes          Prefixes that we should prepend to all our filters.
      * @param maxNumResults     The maximum number of results for each grouping to support.
-     * @param schemaMap         The schema map contains all prefixed existing schema types.
+     * @param schemaCache       The SchemaCache instance held in AppSearch.
      * @param resultSpecBuilder ResultSpecs as a specified by client.
      */
     private static void addPerPackagePerSchemaResultGroupings(
             @NonNull Set<String> prefixes,
             int maxNumResults,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+            @NonNull SchemaCache schemaCache,
             @NonNull ResultSpecProto.Builder resultSpecBuilder) {
         Map<String, List<String>> packageAndSchemaToSchemas =
-                getPackageAndSchemaToPrefixedSchemas(prefixes, schemaMap);
+                getPackageAndSchemaToPrefixedSchemas(prefixes, schemaCache);
 
         for (List<String> prefixedSchemas : packageAndSchemaToSchemas.values()) {
             List<ResultSpecProto.ResultGrouping.Entry> entries =
@@ -764,19 +798,19 @@
      * @param prefixes          Prefixes that we should prepend to all our filters.
      * @param maxNumResults     The maximum number of results for each grouping to support.
      * @param namespaceMap      The namespace map contains all prefixed existing namespaces.
-     * @param schemaMap         The schema map contains all prefixed existing schema types.
+     * @param schemaCache   The SchemaCache instance held in AppSearch.
      * @param resultSpecBuilder ResultSpec as specified by client.
      */
     private static void addPerPackagePerNamespacePerSchemaResultGrouping(
             @NonNull Set<String> prefixes,
             int maxNumResults,
             @NonNull Map<String, Set<String>> namespaceMap,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+            @NonNull SchemaCache schemaCache,
             @NonNull ResultSpecProto.Builder resultSpecBuilder) {
         Map<String, List<String>> packageAndNamespaceToNamespaces =
                 getPackageAndNamespaceToPrefixedNamespaces(prefixes, namespaceMap);
         Map<String, List<String>> packageAndSchemaToSchemas =
-                getPackageAndSchemaToPrefixedSchemas(prefixes, schemaMap);
+                getPackageAndSchemaToPrefixedSchemas(prefixes, schemaCache);
 
         for (List<String> prefixedNamespaces : packageAndNamespaceToNamespaces.values()) {
             for (List<String> prefixedSchemas : packageAndSchemaToSchemas.values()) {
@@ -884,16 +918,16 @@
      *
      * @param prefixes          Prefixes that we should prepend to all our filters.
      * @param maxNumResults     The maximum number of results for each grouping to support.
-     * @param schemaMap         The schema map contains all prefixed existing schema types.
+     * @param schemaCache       The SchemaCache instance held in AppSearch.
      * @param resultSpecBuilder ResultSpec as specified by client.
      */
     private static void addPerSchemaResultGrouping(
             @NonNull Set<String> prefixes,
             int maxNumResults,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+            @NonNull SchemaCache schemaCache,
             @NonNull ResultSpecProto.Builder resultSpecBuilder) {
         Map<String, List<String>> schemaToPrefixedSchemas =
-                getSchemaToPrefixedSchemas(prefixes, schemaMap);
+                getSchemaToPrefixedSchemas(prefixes, schemaCache);
 
         for (List<String> prefixedSchemas : schemaToPrefixedSchemas.values()) {
             List<ResultSpecProto.ResultGrouping.Entry> entries =
@@ -915,19 +949,19 @@
      * @param prefixes          Prefixes that we should prepend to all our filters.
      * @param maxNumResults     The maximum number of results for each grouping to support.
      * @param namespaceMap      The namespace map contains all prefixed existing namespaces.
-     * @param schemaMap         The schema map contains all prefixed existing schema types.
+     * @param schemaCache       The SchemaCache instance held in AppSearch.
      * @param resultSpecBuilder ResultSpec as specified by client.
      */
     private static void addPerNamespaceAndSchemaResultGrouping(
             @NonNull Set<String> prefixes,
             int maxNumResults,
             @NonNull Map<String, Set<String>> namespaceMap,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+            @NonNull SchemaCache schemaCache,
             @NonNull ResultSpecProto.Builder resultSpecBuilder) {
         Map<String, List<String>> namespaceToPrefixedNamespaces =
                 getNamespaceToPrefixedNamespaces(prefixes, namespaceMap);
         Map<String, List<String>> schemaToPrefixedSchemas =
-                getSchemaToPrefixedSchemas(prefixes, schemaMap);
+                getSchemaToPrefixedSchemas(prefixes, schemaCache);
 
         for (List<String> prefixedNamespaces : namespaceToPrefixedNamespaces.values()) {
             for (List<String> prefixedSchemas : schemaToPrefixedSchemas.values()) {
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterUtil.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterUtil.java
index 9015102..d3b7687 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterUtil.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterUtil.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.localstorage.SchemaCache;
 import androidx.collection.ArraySet;
 
 import com.google.android.icing.proto.SchemaTypeConfigProto;
@@ -74,33 +75,26 @@
      * intersection set with those prefixed schema candidates that are stored in AppSearch.
      *
      * @param prefixes              Set of database prefix which the caller want to access.
-     * @param schemaMap             The cached Map of
-     *                              {@code <Prefix, Map<PrefixedSchemaType, schemaProto>>}
-     *                              stores all prefixed schema filters which are stored in
-     *                              AppSearch.
+     * @param schemaCache           The SchemaCache instance held in AppSearch.
      * @param inputSchemaFilters    The set contains all desired but un-prefixed namespace filters
      *                              of user. If the inputSchemaFilters is empty, all existing
      *                              prefixedCandidates will be added to the prefixedTargetFilters.
      */
     static Set<String> generateTargetSchemaFilters(
             @NonNull Set<String> prefixes,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
+            @NonNull SchemaCache schemaCache,
             @NonNull List<String> inputSchemaFilters) {
         Set<String> targetPrefixedSchemaFilters = new ArraySet<>();
         // Append prefix to input schema filters and get the intersection of existing schema filter.
         for (String prefix : prefixes) {
             // Step1: find all prefixed schema candidates that are stored in AppSearch.
-            Map<String, SchemaTypeConfigProto> prefixedSchemaMap = schemaMap.get(prefix);
-            if (prefixedSchemaMap == null) {
-                // This is should never happen. All prefixes should be verified before reach
-                // here.
-                continue;
-            }
+            Map<String, SchemaTypeConfigProto> prefixedSchemaMap =
+                    schemaCache.getSchemaMapForPrefix(prefix);
             Set<String> prefixedSchemaCandidates = prefixedSchemaMap.keySet();
-            // Step2: get the intersection of user searching filters and those candidates which are
-            // stored in AppSearch.
-            addIntersectedFilters(prefix, prefixedSchemaCandidates, inputSchemaFilters,
-                    targetPrefixedSchemaFilters);
+            // Step2: get the intersection of user searching filters (after polymorphism
+            // expansion) and those candidates which are stored in AppSearch.
+            addIntersectedPolymorphicSchemaFilters(prefix, prefixedSchemaCandidates,
+                    schemaCache, inputSchemaFilters, targetPrefixedSchemaFilters);
         }
         return targetPrefixedSchemaFilters;
     }
@@ -136,4 +130,45 @@
             }
         }
     }
+
+    /**
+     * Find the schema intersection set of candidates existing in AppSearch and user specified
+     * schema filters after polymorphism expansion.
+     *
+     * @param prefix                The package and database's identifier.
+     * @param prefixedCandidates    The set contains all prefixed candidates which are existing
+     *                              in a database.
+     * @param schemaCache           The SchemaCache instance held in AppSearch.
+     * @param inputFilters          The set contains all desired but un-prefixed filters of user.
+     *                              If the inputFilters is empty, all prefixedCandidates will be
+     *                              added to the prefixedTargetFilters.
+     * @param prefixedTargetFilters The output set contains all desired prefixed filters which
+     *                              are existing in the database.
+     */
+    private static void addIntersectedPolymorphicSchemaFilters(
+            @NonNull String prefix,
+            @NonNull Set<String> prefixedCandidates,
+            @NonNull SchemaCache schemaCache,
+            @NonNull List<String> inputFilters,
+            @NonNull Set<String> prefixedTargetFilters) {
+        if (inputFilters.isEmpty()) {
+            // Client didn't specify certain schemas to search over, add all candidates.
+            // Polymorphism expansion is not necessary here, since expanding the set of all
+            // schema types will result in the same set of schema types.
+            prefixedTargetFilters.addAll(prefixedCandidates);
+            return;
+        }
+
+        Set<String> currentPrefixedTargetFilters = new ArraySet<>();
+        for (int i = 0; i < inputFilters.size(); i++) {
+            String prefixedTargetSchemaFilter = prefix + inputFilters.get(i);
+            if (prefixedCandidates.contains(prefixedTargetSchemaFilter)) {
+                currentPrefixedTargetFilters.add(prefixedTargetSchemaFilter);
+            }
+        }
+        // Expand schema filters by polymorphism.
+        currentPrefixedTargetFilters = schemaCache.getSchemaTypesWithDescendants(prefix,
+                currentPrefixedTargetFilters);
+        prefixedTargetFilters.addAll(currentPrefixedTargetFilters);
+    }
 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverter.java
index 52f57bd..7c99bb2 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSuggestionSpecToProtoConverter.java
@@ -19,10 +19,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.app.SearchSuggestionSpec;
+import androidx.appsearch.localstorage.SchemaCache;
 import androidx.core.util.Preconditions;
 
 import com.google.android.icing.proto.NamespaceDocumentUriGroup;
-import com.google.android.icing.proto.SchemaTypeConfigProto;
 import com.google.android.icing.proto.SuggestionScoringSpecProto;
 import com.google.android.icing.proto.SuggestionSpecProto;
 import com.google.android.icing.proto.TermMatchType;
@@ -73,7 +73,7 @@
             @NonNull SearchSuggestionSpec searchSuggestionSpec,
             @NonNull Set<String> prefixes,
             @NonNull Map<String, Set<String>> namespaceMap,
-            @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
+            @NonNull SchemaCache schemaCache) {
         mSuggestionQueryExpression = Preconditions.checkNotNull(suggestionQueryExpression);
         mSearchSuggestionSpec = Preconditions.checkNotNull(searchSuggestionSpec);
         mPrefixes = Preconditions.checkNotNull(prefixes);
@@ -83,11 +83,11 @@
                         prefixes, namespaceMap, searchSuggestionSpec.getFilterNamespaces());
         mTargetPrefixedSchemaFilters =
                 SearchSpecToProtoConverterUtil.generateTargetSchemaFilters(
-                        prefixes, schemaMap, searchSuggestionSpec.getFilterSchemas());
+                        prefixes, schemaCache, searchSuggestionSpec.getFilterSchemas());
     }
 
     /**
-     * @return whether this search's target filters are empty. If any target filter is empty, we
+     * Returns whether this search's target filters are empty. If any target filter is empty, we
      * should skip send request to Icing.
      */
     public boolean hasNothingToSearch() {
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java
index 02d2971..5f28ea0 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/CallStats.java
@@ -45,6 +45,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class CallStats {
+    /** Call types. */
     @IntDef(value = {
             CALL_TYPE_UNKNOWN,
             CALL_TYPE_INITIALIZE,
@@ -77,6 +78,7 @@
             CALL_TYPE_REGISTER_OBSERVER_CALLBACK,
             CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK,
             CALL_TYPE_GLOBAL_GET_NEXT_PAGE,
+            CALL_TYPE_EXECUTE_APP_FUNCTION
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CallType {
@@ -113,6 +115,7 @@
     public static final int CALL_TYPE_REGISTER_OBSERVER_CALLBACK = 28;
     public static final int CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK = 29;
     public static final int CALL_TYPE_GLOBAL_GET_NEXT_PAGE = 30;
+    public static final int CALL_TYPE_EXECUTE_APP_FUNCTION = 31;
 
     // These strings are for the subset of call types that correspond to an AppSearchManager API
     private static final String CALL_TYPE_STRING_INITIALIZE = "initialize";
@@ -144,6 +147,7 @@
     private static final String CALL_TYPE_STRING_UNREGISTER_OBSERVER_CALLBACK =
             "globalUnregisterObserverCallback";
     private static final String CALL_TYPE_STRING_GLOBAL_GET_NEXT_PAGE = "globalGetNextPage";
+    private static final String CALL_TYPE_STRING_EXECUTE_APP_FUNCTION = "executeAppFunction";
 
     @Nullable
     private final String mPackageName;
@@ -410,6 +414,8 @@
                 return CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK;
             case CALL_TYPE_STRING_GLOBAL_GET_NEXT_PAGE:
                 return CALL_TYPE_GLOBAL_GET_NEXT_PAGE;
+            case CALL_TYPE_STRING_EXECUTE_APP_FUNCTION:
+                return CALL_TYPE_EXECUTE_APP_FUNCTION;
             default:
                 return CALL_TYPE_UNKNOWN;
         }
@@ -445,6 +451,7 @@
                 CALL_TYPE_GET_STORAGE_INFO,
                 CALL_TYPE_REGISTER_OBSERVER_CALLBACK,
                 CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK,
-                CALL_TYPE_GLOBAL_GET_NEXT_PAGE));
+                CALL_TYPE_GLOBAL_GET_NEXT_PAGE,
+                CALL_TYPE_EXECUTE_APP_FUNCTION));
     }
 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/ClickStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/ClickStats.java
new file mode 100644
index 0000000..4477ce3
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/ClickStats.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.stats;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.core.util.Preconditions;
+
+// TODO(b/319285816): link converter here.
+/**
+ * Class holds detailed stats of a click action, converted from
+ * {@link androidx.appsearch.app.PutDocumentsRequest#getTakenActionGenericDocuments}.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class ClickStats {
+    private final long mTimestampMillis;
+
+    private final long mTimeStayOnResultMillis;
+
+    private final int mResultRankInBlock;
+
+    private final int mResultRankGlobal;
+
+    private final boolean mIsGoodClick;
+
+    ClickStats(@NonNull Builder builder) {
+        Preconditions.checkNotNull(builder);
+        mTimestampMillis = builder.mTimestampMillis;
+        mTimeStayOnResultMillis = builder.mTimeStayOnResultMillis;
+        mResultRankInBlock = builder.mResultRankInBlock;
+        mResultRankGlobal = builder.mResultRankGlobal;
+        mIsGoodClick = builder.mIsGoodClick;
+    }
+
+    /** Returns the click action timestamp in milliseconds since Unix epoch. */
+    public long getTimestampMillis() {
+        return mTimestampMillis;
+    }
+
+    /** Returns the time (duration) of the user staying on the clicked result. */
+    public long getTimeStayOnResultMillis() {
+        return mTimeStayOnResultMillis;
+    }
+
+    /** Returns the in-block rank of the clicked result. */
+    public int getResultRankInBlock() {
+        return mResultRankInBlock;
+    }
+
+    /** Returns the global rank of the clicked result. */
+    public int getResultRankGlobal() {
+        return mResultRankGlobal;
+    }
+
+    /**
+     * Returns whether this click is a good click or not.
+     *
+     * @see Builder#setIsGoodClick
+     */
+    public boolean isGoodClick() {
+        return mIsGoodClick;
+    }
+
+    /** Builder for {@link ClickStats} */
+    public static final class Builder {
+        private long mTimestampMillis;
+
+        private long mTimeStayOnResultMillis;
+
+        private int mResultRankInBlock;
+
+        private int mResultRankGlobal;
+
+        private boolean mIsGoodClick = true;
+
+        /** Sets the click action timestamp in milliseconds since Unix epoch. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setTimestampMillis(long timestampMillis) {
+            mTimestampMillis = timestampMillis;
+            return this;
+        }
+
+        /** Sets the time (duration) of the user staying on the clicked result. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setTimeStayOnResultMillis(long timeStayOnResultMillis) {
+            mTimeStayOnResultMillis = timeStayOnResultMillis;
+            return this;
+        }
+
+        /** Sets the in-block rank of the clicked result. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setResultRankInBlock(int resultRankInBlock) {
+            mResultRankInBlock = resultRankInBlock;
+            return this;
+        }
+
+        /** Sets the global rank of the clicked result. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setResultRankGlobal(int resultRankGlobal) {
+            mResultRankGlobal = resultRankGlobal;
+            return this;
+        }
+
+        /**
+         * Sets the flag indicating whether the click is good or not.
+         *
+         * <p>A good click means the user is satisfied by the clicked document. The caller should
+         * define its own criteria and set this field accordingly.
+         *
+         * <p>The default value is true if unset. We should treat it as a good click by default if
+         * the caller didn't specify or could not determine for several reasons:
+         *
+         * <ul>
+         *   <li>It may be difficult for the caller to determine if the user is satisfied by the
+         *       clicked document or not.
+         *   <li>AppSearch collects search quality metrics that are related to number of good
+         *       clicks. We don't want to demote the quality score aggressively by the undetermined
+         *       ones.
+         * </ul>
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setIsGoodClick(boolean isGoodClick) {
+            mIsGoodClick = isGoodClick;
+            return this;
+        }
+
+        /** Builds a new {@link ClickStats} from the {@link ClickStats.Builder}. */
+        @NonNull
+        public ClickStats build() {
+            return new ClickStats(/* builder= */ this);
+        }
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java
index 73a8c96..d650e7d 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/RemoveStats.java
@@ -37,6 +37,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public final class RemoveStats {
+    /** Types of stats available for remove API. */
     @IntDef(value = {
             // It needs to be sync with DeleteType.Code in
             // external/icing/proto/icing/proto/logging.proto#DeleteStatsProto
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchIntentStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchIntentStats.java
new file mode 100644
index 0000000..0886d2f
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchIntentStats.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.stats;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.core.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+// TODO(b/319285816): link converter here.
+/**
+ * Class holds detailed stats of a search intent, converted from
+ * {@link androidx.appsearch.app.PutDocumentsRequest#getTakenActionGenericDocuments}.
+ *
+ * A search intent includes a valid AppSearch search request, potentially followed by several user
+ * click actions (see {@link ClickStats}) on fetched result documents. Related information of a
+ * search intent will be extracted from
+ * {@link androidx.appsearch.app.PutDocumentsRequest#getTakenActionGenericDocuments}.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class SearchIntentStats {
+    /** AppSearch query correction type compared with the previous query. */
+    @IntDef(value = {
+            QUERY_CORRECTION_TYPE_UNKNOWN,
+            QUERY_CORRECTION_TYPE_FIRST_QUERY,
+            QUERY_CORRECTION_TYPE_REFINEMENT,
+            QUERY_CORRECTION_TYPE_ABANDONMENT,
+            QUERY_CORRECTION_TYPE_END_SESSION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface QueryCorrectionType {
+    }
+
+    public static final int QUERY_CORRECTION_TYPE_UNKNOWN = 0;
+
+    public static final int QUERY_CORRECTION_TYPE_FIRST_QUERY = 1;
+
+    public static final int QUERY_CORRECTION_TYPE_REFINEMENT = 2;
+
+    public static final int QUERY_CORRECTION_TYPE_ABANDONMENT = 3;
+
+    public static final int QUERY_CORRECTION_TYPE_END_SESSION = 4;
+
+    @NonNull
+    private final String mPackageName;
+
+    @Nullable
+    private final String mDatabase;
+
+    @Nullable
+    private final String mPrevQuery;
+
+    @Nullable
+    private final String mCurrQuery;
+
+    private final long mTimestampMillis;
+
+    private final int mNumResultsFetched;
+
+    @QueryCorrectionType
+    private final int mQueryCorrectionType;
+
+    @NonNull
+    private final List<ClickStats> mClicksStats;
+
+    SearchIntentStats(@NonNull Builder builder) {
+        Preconditions.checkNotNull(builder);
+        mPackageName = builder.mPackageName;
+        mDatabase = builder.mDatabase;
+        mPrevQuery = builder.mPrevQuery;
+        mCurrQuery = builder.mCurrQuery;
+        mTimestampMillis = builder.mTimestampMillis;
+        mNumResultsFetched = builder.mNumResultsFetched;
+        mQueryCorrectionType = builder.mQueryCorrectionType;
+        mClicksStats = builder.mClicksStats;
+    }
+
+    /** Returns calling package name. */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns calling database name.
+     *
+     * <p>For global search, database name will be null.
+     */
+    @Nullable
+    public String getDatabase() {
+        return mDatabase;
+    }
+
+    /** Returns the raw query string of the previous search intent. */
+    @Nullable
+    public String getPrevQuery() {
+        return mPrevQuery;
+    }
+
+    /** Returns the raw query string of this (current) search intent. */
+    @Nullable
+    public String getCurrQuery() {
+        return mCurrQuery;
+    }
+
+    /** Returns the search intent timestamp in milliseconds since Unix epoch. */
+    public long getTimestampMillis() {
+        return mTimestampMillis;
+    }
+
+    /**
+     * Returns total number of results fetched from AppSearch by the client in this search intent.
+     */
+    public int getNumResultsFetched() {
+        return mNumResultsFetched;
+    }
+
+    /**
+     * Returns the correction type of the query in this search intent compared with the previous
+     * search intent. Default value: {@link SearchIntentStats#QUERY_CORRECTION_TYPE_UNKNOWN}.
+     */
+    @QueryCorrectionType
+    public int getQueryCorrectionType() {
+        return mQueryCorrectionType;
+    }
+
+    /** Returns the list of {@link ClickStats} in this search intent. */
+    @NonNull
+    public List<ClickStats> getClicksStats() {
+        return mClicksStats;
+    }
+
+    /** Builder for {@link SearchIntentStats} */
+    public static final class Builder {
+        @NonNull
+        private final String mPackageName;
+
+        @Nullable
+        private String mDatabase;
+
+        @Nullable
+        private String mPrevQuery;
+
+        @Nullable
+        private String mCurrQuery;
+
+        private long mTimestampMillis;
+
+        private int mNumResultsFetched;
+
+        @QueryCorrectionType
+        private int mQueryCorrectionType = QUERY_CORRECTION_TYPE_UNKNOWN;
+
+        @NonNull
+        private List<ClickStats> mClicksStats = new ArrayList<>();
+
+        private boolean mBuilt = false;
+
+        /** Constructor for the {@link Builder}. */
+        public Builder(@NonNull String packageName) {
+            mPackageName = Preconditions.checkNotNull(packageName);
+        }
+
+        /** Constructor the {@link Builder} from an existing {@link SearchIntentStats}. */
+        public Builder(@NonNull SearchIntentStats searchIntentStats) {
+            Preconditions.checkNotNull(searchIntentStats);
+
+            mPackageName = searchIntentStats.getPackageName();
+            mDatabase = searchIntentStats.getDatabase();
+            mPrevQuery = searchIntentStats.getPrevQuery();
+            mCurrQuery = searchIntentStats.getCurrQuery();
+            mTimestampMillis = searchIntentStats.getTimestampMillis();
+            mNumResultsFetched = searchIntentStats.getNumResultsFetched();
+            mQueryCorrectionType = searchIntentStats.getQueryCorrectionType();
+            mClicksStats.addAll(searchIntentStats.getClicksStats());
+        }
+
+        /**
+         * Sets calling database name.
+         *
+         * <p>For global search, database name will be null.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setDatabase(@Nullable String database) {
+            resetIfBuilt();
+            mDatabase = database;
+            return this;
+        }
+
+        /** Sets the raw query string of the previous search intent. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setPrevQuery(@Nullable String prevQuery) {
+            resetIfBuilt();
+            mPrevQuery = prevQuery;
+            return this;
+        }
+
+        /** Sets the raw query string of this (current) search intent. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setCurrQuery(@Nullable String currQuery) {
+            resetIfBuilt();
+            mCurrQuery = currQuery;
+            return this;
+        }
+
+        /** Sets the search intent timestamp in milliseconds since Unix epoch. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setTimestampMillis(long timestampMillis) {
+            resetIfBuilt();
+            mTimestampMillis = timestampMillis;
+            return this;
+        }
+
+        /**
+         * Sets total number of results fetched from AppSearch by the client in this search intent.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setNumResultsFetched(int numResultsFetched) {
+            resetIfBuilt();
+            mNumResultsFetched = numResultsFetched;
+            return this;
+        }
+
+        /**
+         * Sets the correction type of the query in this search intent compared with the previous
+         * search intent.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setQueryCorrectionType(@QueryCorrectionType int queryCorrectionType) {
+            resetIfBuilt();
+            mQueryCorrectionType = queryCorrectionType;
+            return this;
+        }
+
+        /** Adds one or more {@link ClickStats} objects to this search intent. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addClicksStats(@NonNull ClickStats... clicksStats) {
+            Preconditions.checkNotNull(clicksStats);
+            resetIfBuilt();
+            return addClicksStats(Arrays.asList(clicksStats));
+        }
+
+        /** Adds a collection of {@link ClickStats} objects to this search intent. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addClicksStats(@NonNull Collection<? extends ClickStats> clicksStats) {
+            Preconditions.checkNotNull(clicksStats);
+            resetIfBuilt();
+            mClicksStats.addAll(clicksStats);
+            return this;
+        }
+
+        /**
+         * If built, make a copy of previous data for every field so that the builder can be reused.
+         */
+        private void resetIfBuilt() {
+            if (mBuilt) {
+                mClicksStats = new ArrayList<>(mClicksStats);
+                mBuilt = false;
+            }
+        }
+
+        /** Builds a new {@link SearchIntentStats} from the {@link Builder}. */
+        @NonNull
+        public SearchIntentStats build() {
+            mBuilt = true;
+            return new SearchIntentStats(/* builder= */ this);
+        }
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchSessionStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchSessionStats.java
new file mode 100644
index 0000000..11d8ea3
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchSessionStats.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.stats;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.core.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+// TODO(b/319285816): link converter here.
+/**
+ * Class holds detailed stats of a search session, converted from {@link
+ * androidx.appsearch.app.PutDocumentsRequest#getTakenActionGenericDocuments}. It contains a list of
+ * {@link SearchIntentStats} and aggregated metrics of them.
+ *
+ * <p>A search session is consist of a sequence of related search intents. See {@link
+ * SearchIntentStats} for more details.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class SearchSessionStats {
+    @NonNull private final String mPackageName;
+
+    @Nullable private final String mDatabase;
+
+    @NonNull private final List<SearchIntentStats> mSearchIntentsStats;
+
+    SearchSessionStats(@NonNull Builder builder) {
+        Preconditions.checkNotNull(builder);
+        mPackageName = builder.mPackageName;
+        mDatabase = builder.mDatabase;
+        mSearchIntentsStats = builder.mSearchIntentsStats;
+    }
+
+    /**
+     * Returns a nullable {@link SearchIntentStats} instance containing information of the last
+     * search intent which ended the search session.
+     *
+     * <p>If {@link #getSearchIntentsStats} is empty (i.e. the caller didn't add any {@link
+     * SearchIntentStats} via {@link Builder#addSearchIntentsStats}), then return null.
+     *
+     * <p>It is similar to the last element in {@link #getSearchIntentsStats}, except there is no
+     * previous query and the query correction type is tagged as {@link
+     * SearchIntentStats#QUERY_CORRECTION_TYPE_END_SESSION}.
+     *
+     * <p>This stats is useful to determine whether the user ended the search session with
+     * satisfaction (i.e. had found desired result documents) or not.
+     */
+    @Nullable
+    public SearchIntentStats getEndSessionSearchIntentStats() {
+        if (mSearchIntentsStats.isEmpty()) {
+            return null;
+        }
+
+        SearchIntentStats lastSearchIntentStats =
+                mSearchIntentsStats.get(mSearchIntentsStats.size() - 1);
+        return new SearchIntentStats.Builder(lastSearchIntentStats)
+                .setPrevQuery(null)
+                .setQueryCorrectionType(SearchIntentStats.QUERY_CORRECTION_TYPE_END_SESSION)
+                .build();
+    }
+
+    /** Returns calling package name. */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns calling database name.
+     *
+     * <p>For global search, database name will be null.
+     */
+    @Nullable
+    public String getDatabase() {
+        return mDatabase;
+    }
+
+    /** Returns the list of {@link SearchIntentStats} in this search session. */
+    @NonNull
+    public List<SearchIntentStats> getSearchIntentsStats() {
+        return mSearchIntentsStats;
+    }
+
+    /** Builder for {@link SearchSessionStats}. */
+    public static final class Builder {
+        @NonNull private final String mPackageName;
+
+        @Nullable private String mDatabase;
+
+        @NonNull private List<SearchIntentStats> mSearchIntentsStats = new ArrayList<>();
+
+        private boolean mBuilt = false;
+
+        /** Constructor for the {@link Builder}. */
+        public Builder(@NonNull String packageName) {
+            mPackageName = Preconditions.checkNotNull(packageName);
+        }
+
+        /**
+         * Sets calling database name.
+         *
+         * <p>For global search, database name will be null.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setDatabase(@Nullable String database) {
+            resetIfBuilt();
+            mDatabase = database;
+            return this;
+        }
+
+        /** Adds one or more {@link SearchIntentStats} objects to this search intent. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addSearchIntentsStats(@NonNull SearchIntentStats... searchIntentsStats) {
+            Preconditions.checkNotNull(searchIntentsStats);
+            resetIfBuilt();
+            return addSearchIntentsStats(Arrays.asList(searchIntentsStats));
+        }
+
+        /** Adds a collection of {@link SearchIntentStats} objects to this search intent. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addSearchIntentsStats(
+                @NonNull Collection<? extends SearchIntentStats> searchIntentsStats) {
+            Preconditions.checkNotNull(searchIntentsStats);
+            resetIfBuilt();
+            mSearchIntentsStats.addAll(searchIntentsStats);
+            return this;
+        }
+
+        /**
+         * If built, make a copy of previous data for every field so that the builder can be reused.
+         */
+        private void resetIfBuilt() {
+            if (mBuilt) {
+                mSearchIntentsStats = new ArrayList<>(mSearchIntentsStats);
+                mBuilt = false;
+            }
+        }
+
+        /** Builds a new {@link SearchSessionStats} from the {@link Builder}. */
+        @NonNull
+        public SearchSessionStats build() {
+            mBuilt = true;
+            return new SearchSessionStats(/* builder= */ this);
+        }
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java
index a87ae04..28d5117 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/stats/SearchStats.java
@@ -36,6 +36,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public final class SearchStats {
+    /** Types of Visibility scopes available for search. */
     @IntDef(value = {
             // Searches apps' own documents.
             VISIBILITY_SCOPE_LOCAL,
@@ -137,6 +138,7 @@
     private final int mNativeNumJoinedResultsCurrentPage;
     /** Time taken to join documents together. */
     private final int mNativeJoinLatencyMillis;
+    @Nullable private final String mSearchSourceLogTag;
 
     SearchStats(@NonNull Builder builder) {
         Preconditions.checkNotNull(builder);
@@ -170,6 +172,7 @@
         mJoinType = builder.mJoinType;
         mNativeNumJoinedResultsCurrentPage = builder.mNativeNumJoinedResultsCurrentPage;
         mNativeJoinLatencyMillis = builder.mNativeJoinLatencyMillis;
+        mSearchSourceLogTag = builder.mSearchSourceLogTag;
     }
 
     /** Returns the package name of the session. */
@@ -342,6 +345,12 @@
         return mNativeJoinLatencyMillis;
     }
 
+    /**  Returns a tag to indicate the source of this search, or {code null} if never set. */
+    @Nullable
+    public String getSearchSourceLogTag() {
+        return mSearchSourceLogTag;
+    }
+
     /** Builder for {@link SearchStats} */
     public static class Builder {
         @NonNull
@@ -377,6 +386,7 @@
         @JoinableValueType int mJoinType;
         int mNativeNumJoinedResultsCurrentPage;
         int mNativeJoinLatencyMillis;
+        @Nullable String mSearchSourceLogTag;
 
         /**
          * Constructor
@@ -604,6 +614,7 @@
         }
 
         /** Sets whether or not this is a join query */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setJoinType(@JoinableValueType int joinType) {
             mJoinType = joinType;
@@ -611,6 +622,7 @@
         }
 
         /** Set the total number of joined documents in a page. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setNativeNumJoinedResultsCurrentPage(int nativeNumJoinedResultsCurrentPage) {
             mNativeNumJoinedResultsCurrentPage = nativeNumJoinedResultsCurrentPage;
@@ -618,12 +630,21 @@
         }
 
         /** Sets time it takes to join documents together in icing. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setNativeJoinLatencyMillis(int nativeJoinLatencyMillis) {
             mNativeJoinLatencyMillis = nativeJoinLatencyMillis;
             return this;
         }
 
+        /** Sets a tag to indicate the source of this search. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setSearchSourceLogTag(@Nullable String searchSourceLogTag) {
+            mSearchSourceLogTag = searchSourceLogTag;
+            return this;
+        }
+
         /**
          * Constructs a new {@link SearchStats} from the contents of this
          * {@link SearchStats.Builder}.
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/ClickActionGenericDocument.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/ClickActionGenericDocument.java
new file mode 100644
index 0000000..baa3e4e
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/ClickActionGenericDocument.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.usagereporting;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.usagereporting.ActionConstants;
+import androidx.core.util.Preconditions;
+
+/**
+ * Wrapper class for
+ *  <!--@exportToFramework:ifJetpack()-->
+ *  {@link androidx.appsearch.usagereporting.ClickAction}
+ *  <!--@exportToFramework:else()
+ *  click action
+ *  -->
+ * {@link GenericDocument}, which contains getters for click action properties.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class ClickActionGenericDocument extends TakenActionGenericDocument {
+    private static final String PROPERTY_PATH_QUERY = "query";
+    private static final String PROPERTY_PATH_RESULT_RANK_IN_BLOCK = "resultRankInBlock";
+    private static final String PROPERTY_PATH_RESULT_RANK_GLOBAL = "resultRankGlobal";
+    private static final String PROPERTY_PATH_TIME_STAY_ON_RESULT_MILLIS = "timeStayOnResultMillis";
+
+    ClickActionGenericDocument(@NonNull GenericDocument document) {
+        super(Preconditions.checkNotNull(document));
+    }
+
+    /** Returns the string value of property {@code query}. */
+    @Nullable
+    public String getQuery() {
+        return getPropertyString(PROPERTY_PATH_QUERY);
+    }
+
+    /** Returns the integer value of property {@code resultRankInBlock}. */
+    public int getResultRankInBlock() {
+        return (int) getPropertyLong(PROPERTY_PATH_RESULT_RANK_IN_BLOCK);
+    }
+
+    /** Returns the integer value of property {@code resultRankGlobal}. */
+    public int getResultRankGlobal() {
+        return (int) getPropertyLong(PROPERTY_PATH_RESULT_RANK_GLOBAL);
+    }
+
+    /** Returns the long value of property {@code timeStayOnResultMillis}. */
+    public long getTimeStayOnResultMillis() {
+        return getPropertyLong(PROPERTY_PATH_TIME_STAY_ON_RESULT_MILLIS);
+    }
+
+    /** Builder for {@link ClickActionGenericDocument}. */
+    public static final class Builder extends TakenActionGenericDocument.Builder<Builder> {
+        /**
+         * Creates a new {@link ClickActionGenericDocument.Builder}.
+         *
+         * <p>Document IDs are unique within a namespace.
+         *
+         * <p>The number of namespaces per app should be kept small for efficiency reasons.
+         *
+         * @param namespace  the namespace to set for the {@link GenericDocument}.
+         * @param id         the unique identifier for the {@link GenericDocument} in its namespace.
+         * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
+         *                   provided {@code schemaType} must be defined using
+         *                   {@link AppSearchSession#setSchemaAsync} prior
+         *                   to inserting a document of this {@code schemaType} into the
+         *                   AppSearch index using
+         *                   {@link AppSearchSession#putAsync}.
+         *                   Otherwise, the document will be rejected by
+         *                   {@link AppSearchSession#putAsync} with result code
+         *                   {@link AppSearchResult#RESULT_NOT_FOUND}.
+         */
+        public Builder(@NonNull String namespace, @NonNull String id, @NonNull String schemaType) {
+            super(Preconditions.checkNotNull(namespace), Preconditions.checkNotNull(id),
+                    Preconditions.checkNotNull(schemaType), ActionConstants.ACTION_TYPE_CLICK);
+        }
+
+        /**
+         * Creates a new {@link ClickActionGenericDocument.Builder} from an existing
+         * {@link GenericDocument}.
+         *
+         * @param document a generic document object.
+         *
+         * @throws IllegalArgumentException if the integer value of property {@code actionType} is
+         *                                  not {@link ActionConstants#ACTION_TYPE_CLICK}.
+         */
+        public Builder(@NonNull GenericDocument document) {
+            super(Preconditions.checkNotNull(document));
+
+            if (document.getPropertyLong(PROPERTY_PATH_ACTION_TYPE)
+                    != ActionConstants.ACTION_TYPE_CLICK) {
+                throw new IllegalArgumentException(
+                        "Invalid action type for ClickActionGenericDocument");
+            }
+        }
+
+        /**
+         * Sets the string value of property {@code query} by the user-entered search input
+         * (without any operators or rewriting) that yielded the
+         * {@link androidx.appsearch.app.SearchResult} on which the user clicked.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setQuery(@NonNull String query) {
+            Preconditions.checkNotNull(query);
+            setPropertyString(PROPERTY_PATH_QUERY, query);
+            return this;
+        }
+
+        /**
+         * Sets the integer value of property {@code resultRankInBlock} by the rank of the clicked
+         * {@link androidx.appsearch.app.SearchResult} document among the user-defined block.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setResultRankInBlock(int resultRankInBlock) {
+            Preconditions.checkArgumentNonnegative(resultRankInBlock);
+            setPropertyLong(PROPERTY_PATH_RESULT_RANK_IN_BLOCK, resultRankInBlock);
+            return this;
+        }
+
+        /**
+         * Sets the integer value of property {@code resultRankGlobal} by the global rank of the
+         * clicked {@link androidx.appsearch.app.SearchResult} document.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setResultRankGlobal(int resultRankGlobal) {
+            Preconditions.checkArgumentNonnegative(resultRankGlobal);
+            setPropertyLong(PROPERTY_PATH_RESULT_RANK_GLOBAL, resultRankGlobal);
+            return this;
+        }
+
+        /**
+         * Sets the integer value of property {@code timeStayOnResultMillis} by the time in
+         * milliseconds that user stays on the {@link androidx.appsearch.app.SearchResult} document
+         * after clicking it.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setTimeStayOnResultMillis(long timeStayOnResultMillis) {
+            setPropertyLong(PROPERTY_PATH_TIME_STAY_ON_RESULT_MILLIS, timeStayOnResultMillis);
+            return this;
+        }
+
+        /** Builds a {@link ClickActionGenericDocument}. */
+        @Override
+        @NonNull
+        public ClickActionGenericDocument build() {
+            return new ClickActionGenericDocument(super.build());
+        }
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/SearchActionGenericDocument.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/SearchActionGenericDocument.java
new file mode 100644
index 0000000..bedbf21
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/SearchActionGenericDocument.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.usagereporting;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.usagereporting.ActionConstants;
+import androidx.core.util.Preconditions;
+
+/**
+ * Wrapper class for
+ *  <!--@exportToFramework:ifJetpack()-->
+ *  {@link androidx.appsearch.usagereporting.SearchAction}
+ *  <!--@exportToFramework:else()
+ *  search action
+ *  -->
+ * {@link GenericDocument}, which contains getters for search action properties.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SearchActionGenericDocument extends TakenActionGenericDocument {
+    private static final String PROPERTY_PATH_QUERY = "query";
+    private static final String PROPERTY_PATH_FETCHED_RESULT_COUNT = "fetchedResultCount";
+
+    SearchActionGenericDocument(@NonNull GenericDocument document) {
+        super(Preconditions.checkNotNull(document));
+    }
+
+    /** Returns the string value of property {@code query}. */
+    @Nullable
+    public String getQuery() {
+        return getPropertyString(PROPERTY_PATH_QUERY);
+    }
+
+    /** Returns the integer value of property {@code fetchedResultCount}. */
+    public int getFetchedResultCount() {
+        return (int) getPropertyLong(PROPERTY_PATH_FETCHED_RESULT_COUNT);
+    }
+
+    /** Builder for {@link SearchActionGenericDocument}. */
+    public static final class Builder extends TakenActionGenericDocument.Builder<Builder> {
+        /**
+         * Creates a new {@link SearchActionGenericDocument.Builder}.
+         *
+         * <p>Document IDs are unique within a namespace.
+         *
+         * <p>The number of namespaces per app should be kept small for efficiency reasons.
+         *
+         * @param namespace  the namespace to set for the {@link GenericDocument}.
+         * @param id         the unique identifier for the {@link GenericDocument} in its namespace.
+         * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
+         *                   provided {@code schemaType} must be defined using
+         *                   {@link AppSearchSession#setSchemaAsync} prior
+         *                   to inserting a document of this {@code schemaType} into the
+         *                   AppSearch index using
+         *                   {@link AppSearchSession#putAsync}.
+         *                   Otherwise, the document will be rejected by
+         *                   {@link AppSearchSession#putAsync} with result code
+         *                   {@link AppSearchResult#RESULT_NOT_FOUND}.
+         */
+        public Builder(@NonNull String namespace, @NonNull String id, @NonNull String schemaType) {
+            super(Preconditions.checkNotNull(namespace), Preconditions.checkNotNull(id),
+                    Preconditions.checkNotNull(schemaType), ActionConstants.ACTION_TYPE_SEARCH);
+        }
+
+        /**
+         * Creates a new {@link SearchActionGenericDocument.Builder} from an existing
+         * {@link GenericDocument}.
+         *
+         * @param document a generic document object.
+         *
+         * @throws IllegalArgumentException if the integer value of property {@code actionType} is
+         *                                  not {@link ActionConstants#ACTION_TYPE_SEARCH}.
+         */
+        public Builder(@NonNull GenericDocument document) {
+            super(Preconditions.checkNotNull(document));
+
+            if (document.getPropertyLong(PROPERTY_PATH_ACTION_TYPE)
+                    != ActionConstants.ACTION_TYPE_SEARCH) {
+                throw new IllegalArgumentException(
+                        "Invalid action type for SearchActionGenericDocument");
+            }
+        }
+
+        /**
+         * Sets the string value of property {@code query} by the user-entered search input
+         * (without any operators or rewriting).
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setQuery(@NonNull String query) {
+            Preconditions.checkNotNull(query);
+            setPropertyString(PROPERTY_PATH_QUERY, query);
+            return this;
+        }
+
+        /**
+         * Sets the integer value of property {@code fetchedResultCount} by total number of results
+         * fetched from AppSearch by the client in this search action.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setFetchedResultCount(int fetchedResultCount) {
+            Preconditions.checkArgumentNonnegative(fetchedResultCount);
+            setPropertyLong(PROPERTY_PATH_FETCHED_RESULT_COUNT, fetchedResultCount);
+            return this;
+        }
+
+        /** Builds a {@link SearchActionGenericDocument}. */
+        @Override
+        @NonNull
+        public SearchActionGenericDocument build() {
+            return new SearchActionGenericDocument(super.build());
+        }
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/SearchSessionStatsExtractor.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/SearchSessionStatsExtractor.java
new file mode 100644
index 0000000..72457c2
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/SearchSessionStatsExtractor.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.usagereporting;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.localstorage.stats.ClickStats;
+import androidx.appsearch.localstorage.stats.SearchIntentStats;
+import androidx.appsearch.localstorage.stats.SearchSessionStats;
+import androidx.appsearch.usagereporting.ActionConstants;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Extractor class for analyzing a list of taken action {@link GenericDocument} and creating a list
+ * of {@link SearchSessionStats}.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class SearchSessionStatsExtractor {
+    // TODO(b/319285816): make thresholds configurable.
+    /**
+     * Threshold for noise search intent detection, in millisecond. A search action will be
+     * considered as a noise (and skipped) if all of the following conditions are satisfied:
+     * <ul>
+     *     <li>The action timestamp (action document creation timestamp) difference between it and
+     *     its previous search action is below this threshold.
+     *     <li>There is no click action associated with it.
+     *     <li>Its raw query string is a prefix of the previous search action's raw query string (or
+     *     the other way around).
+     * </ul>
+     */
+    private static final long NOISE_SEARCH_INTENT_TIMESTAMP_DIFF_THRESHOLD_MILLIS = 2000L;
+
+    /**
+     * Threshold for independent search intent detection, in millisecond. If the action timestamp
+     * (action document creation timestamp) difference between the previous and the current search
+     * action exceeds this threshold, then the current search action will be considered as a
+     * completely independent search intent (i.e. belonging to a new search session), and there will
+     * be no correlation analysis between the previous and the current search action.
+     */
+    private static final long INDEPENDENT_SEARCH_INTENT_TIMESTAMP_DIFF_THRESHOLD_MILLIS =
+            10L * 60 * 1000;
+
+    /**
+     * Threshold for marking good click (compared with {@code timeStayOnResultMillis}), in
+     * millisecond. A good click means the user spent decent amount of time on the clicked result
+     * document.
+     */
+    private static final long GOOD_CLICK_TIME_STAY_ON_RESULT_THRESHOLD_MILLIS = 2000L;
+
+    /**
+     * Threshold for backspace count to become query abandonment. If the user hits backspace for at
+     * least QUERY_ABANDONMENT_BACKSPACE_COUNT times, then the query correction type will be
+     * determined as abandonment.
+     */
+    private static final int QUERY_ABANDONMENT_BACKSPACE_COUNT = 2;
+
+    /**
+     * Returns the query correction type between the previous and current search actions.
+     *
+     * @param currSearchAction the current search action {@link SearchActionGenericDocument}.
+     * @param prevSearchAction the previous search action {@link SearchActionGenericDocument}.
+     */
+    public static @SearchIntentStats.QueryCorrectionType int getQueryCorrectionType(
+            @NonNull SearchActionGenericDocument currSearchAction,
+            @Nullable SearchActionGenericDocument prevSearchAction) {
+        Objects.requireNonNull(currSearchAction);
+
+        if (currSearchAction.getQuery() == null) {
+            // Query correction type cannot be determined if the client didn't provide the raw query
+            // string.
+            return SearchIntentStats.QUERY_CORRECTION_TYPE_UNKNOWN;
+        }
+        if (prevSearchAction == null) {
+            // If the previous search action is missing, then it is the first query.
+            return SearchIntentStats.QUERY_CORRECTION_TYPE_FIRST_QUERY;
+        } else if (prevSearchAction.getQuery() == null) {
+            // Query correction type cannot be determined if the client didn't provide the raw query
+            // string.
+            return SearchIntentStats.QUERY_CORRECTION_TYPE_UNKNOWN;
+        }
+
+        // Determine the query correction type by comparing the current and previous raw query
+        // strings.
+        String prevQuery = prevSearchAction.getQuery();
+        String currQuery = currSearchAction.getQuery();
+        int commonPrefixLength = getCommonPrefixLength(prevQuery, currQuery);
+        // If the user hits backspace >= QUERY_ABANDONMENT_BACKSPACE_COUNT times, then it is query
+        // abandonment. Otherwise, it is query refinement.
+        if (commonPrefixLength <= prevQuery.length() - QUERY_ABANDONMENT_BACKSPACE_COUNT) {
+            return SearchIntentStats.QUERY_CORRECTION_TYPE_ABANDONMENT;
+        } else {
+            return SearchIntentStats.QUERY_CORRECTION_TYPE_REFINEMENT;
+        }
+    }
+
+    /**
+     * Returns a list of {@link SearchSessionStats} extracted from the given list of taken action
+     * {@link GenericDocument}.
+     *
+     * <p>A search session consists of several related search intents.
+     *
+     * <p>A search intent consists of a valid search action with 0 or more click actions. To extract
+     * search intent metrics, this function will try to group the given taken actions into several
+     * search intents, and yield a {@link SearchIntentStats} for each search intent. Finally related
+     * {@link SearchIntentStats} will be wrapped into {@link SearchSessionStats}.
+     *
+     * @param packageName The package name of the caller.
+     * @param database The database name of the caller.
+     * @param genericDocuments a list of taken actions in generic document form.
+     */
+    @NonNull
+    public List<SearchSessionStats> extract(
+            @NonNull String packageName,
+            @Nullable String database,
+            @NonNull List<GenericDocument> genericDocuments) {
+        Objects.requireNonNull(genericDocuments);
+
+        // Convert GenericDocument list to TakenActionGenericDocument list and sort them by document
+        // creation timestamp.
+        List<TakenActionGenericDocument> takenActionGenericDocuments =
+                new ArrayList<>(genericDocuments.size());
+        for (int i = 0; i < genericDocuments.size(); ++i) {
+            try {
+                takenActionGenericDocuments.add(
+                        TakenActionGenericDocument.create(genericDocuments.get(i)));
+            } catch (IllegalArgumentException e) {
+                // Skip generic documents with unknown action type.
+            }
+        }
+        Collections.sort(takenActionGenericDocuments,
+                (TakenActionGenericDocument doc1, TakenActionGenericDocument doc2) ->
+                        Long.compare(doc1.getCreationTimestampMillis(),
+                                doc2.getCreationTimestampMillis()));
+
+        List<SearchSessionStats> result = new ArrayList<>();
+        SearchSessionStats.Builder searchSessionStatsBuilder = null;
+        SearchActionGenericDocument prevSearchAction = null;
+        // Clients are expected to report search action followed by its associated click actions.
+        // For example, [searchAction1, clickAction1, searchAction2, searchAction3, clickAction2,
+        // clickAction3]:
+        // - There are 3 search actions and 3 click actions.
+        // - clickAction1 is associated with searchAction1.
+        // - There is no click action associated with searchAction2.
+        // - clickAction2 and clickAction3 are associated with searchAction3.
+        // Here we're going to break down the list into segments. Each segment starts with a search
+        // action followed by 0 or more associated click actions, and they form a single search
+        // intent. We will analyze and extract metrics from the taken actions for the search intent.
+        //
+        // If a search intent is considered independent from the previous one, then we will start a
+        // new search session analysis.
+        for (int i = 0; i < takenActionGenericDocuments.size(); ++i) {
+            if (takenActionGenericDocuments.get(i).getActionType()
+                    != ActionConstants.ACTION_TYPE_SEARCH) {
+                continue;
+            }
+
+            SearchActionGenericDocument currSearchAction =
+                    (SearchActionGenericDocument) takenActionGenericDocuments.get(i);
+            List<ClickActionGenericDocument> clickActions = new ArrayList<>();
+            // Get all click actions associated with the current search action by advancing until
+            // the next search action.
+            while (i + 1 < takenActionGenericDocuments.size()
+                    && takenActionGenericDocuments.get(i + 1).getActionType()
+                        != ActionConstants.ACTION_TYPE_SEARCH) {
+                if (takenActionGenericDocuments.get(i + 1).getActionType()
+                        == ActionConstants.ACTION_TYPE_CLICK) {
+                    clickActions.add(
+                            (ClickActionGenericDocument) takenActionGenericDocuments.get(i + 1));
+                }
+                ++i;
+            }
+
+            // Get the reference of the next search action if it exists.
+            SearchActionGenericDocument nextSearchAction = null;
+            if (i + 1 < takenActionGenericDocuments.size()
+                    && takenActionGenericDocuments.get(i + 1).getActionType()
+                        == ActionConstants.ACTION_TYPE_SEARCH) {
+                nextSearchAction =
+                        (SearchActionGenericDocument) takenActionGenericDocuments.get(i + 1);
+            }
+
+            if (prevSearchAction != null
+                    && isIndependentSearchAction(currSearchAction, prevSearchAction)) {
+                // If the current search action is independent from the previous one, then:
+                // - Build and append the previous search session stats.
+                // - Start a new search session analysis.
+                // - Ignore the previous search action when extracting stats.
+                if (searchSessionStatsBuilder != null) {
+                    result.add(searchSessionStatsBuilder.build());
+                    searchSessionStatsBuilder = null;
+                }
+                prevSearchAction = null;
+            } else if (clickActions.isEmpty()
+                    && isIntermediateSearchAction(
+                    currSearchAction, prevSearchAction, nextSearchAction)) {
+                // If the current search action is an intermediate search action with no click
+                // actions, then we consider it as a noise and skip it.
+                continue;
+            }
+
+            // Now we get a valid search intent (the current search action + a list of click actions
+            // associated with it). Extract metrics and add SearchIntentStats into this search
+            // session.
+            if (searchSessionStatsBuilder == null) {
+                searchSessionStatsBuilder =
+                        new SearchSessionStats.Builder(packageName).setDatabase(database);
+            }
+            searchSessionStatsBuilder.addSearchIntentsStats(
+                    createSearchIntentStats(
+                            packageName,
+                            database,
+                            currSearchAction,
+                            clickActions,
+                            prevSearchAction));
+            prevSearchAction = currSearchAction;
+        }
+        if (searchSessionStatsBuilder != null) {
+            result.add(searchSessionStatsBuilder.build());
+        }
+        return result;
+    }
+
+    /**
+     * Creates a {@link SearchIntentStats} object from the current search action + its associated
+     * click actions, and the previous search action (in generic document form).
+     */
+    private SearchIntentStats createSearchIntentStats(
+            @NonNull String packageName,
+            @Nullable String database,
+            @NonNull SearchActionGenericDocument currSearchAction,
+            @NonNull List<ClickActionGenericDocument> clickActions,
+            @Nullable SearchActionGenericDocument prevSearchAction) {
+        SearchIntentStats.Builder builder = new SearchIntentStats.Builder(packageName)
+                .setDatabase(database)
+                .setTimestampMillis(currSearchAction.getCreationTimestampMillis())
+                .setCurrQuery(currSearchAction.getQuery())
+                .setNumResultsFetched(currSearchAction.getFetchedResultCount())
+                .setQueryCorrectionType(getQueryCorrectionType(currSearchAction, prevSearchAction));
+        if (prevSearchAction != null) {
+            builder.setPrevQuery(prevSearchAction.getQuery());
+        }
+        for (int i = 0; i < clickActions.size(); ++i) {
+            builder.addClicksStats(createClickStats(clickActions.get(i)));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Creates a {@link ClickStats} object from the given click action (in generic document form).
+     */
+    private ClickStats createClickStats(ClickActionGenericDocument clickAction) {
+        // A click is considered good if:
+        // - The user spent decent amount of time on the clicked document.
+        // - OR the client didn't provide timeStayOnResultMillis. In this case, the value will be 0.
+        boolean isGoodClick =
+                clickAction.getTimeStayOnResultMillis() <= 0
+                        || clickAction.getTimeStayOnResultMillis()
+                        >= GOOD_CLICK_TIME_STAY_ON_RESULT_THRESHOLD_MILLIS;
+        return new ClickStats.Builder()
+                .setTimestampMillis(clickAction.getCreationTimestampMillis())
+                .setResultRankInBlock(clickAction.getResultRankInBlock())
+                .setResultRankGlobal(clickAction.getResultRankGlobal())
+                .setTimeStayOnResultMillis(clickAction.getTimeStayOnResultMillis())
+                .setIsGoodClick(isGoodClick)
+                .build();
+    }
+
+    /**
+     * Returns if the current search action is an intermediate search action.
+     *
+     * <p>An intermediate search action is used for detecting the situation when the user adds or
+     * deletes characters from the query (e.g. "a" -> "app" -> "apple" or "apple" -> "app" -> "a")
+     * within a short period of time. More precisely, it has to satisfy all of the following
+     * conditions:
+     * <ul>
+     *     <li>There are related (non-independent) search actions before and after it.
+     *     <li>It occurs within the threshold after its previous search action.
+     *     <li>Its raw query string is a prefix of its previous search action's raw query string, or
+     *     the opposite direction.
+     * </ul>
+     */
+    private static boolean isIntermediateSearchAction(
+            @NonNull SearchActionGenericDocument currSearchAction,
+            @Nullable SearchActionGenericDocument prevSearchAction,
+            @Nullable SearchActionGenericDocument nextSearchAction) {
+        Objects.requireNonNull(currSearchAction);
+
+        if (prevSearchAction == null || nextSearchAction == null) {
+            return false;
+        }
+
+        // Whether the next search action is independent from the current search action. If true,
+        // then the current search action will not be considered as an intermediate search action
+        // since it is the last search action of the search session.
+        boolean isNextSearchActionIndependent =
+                isIndependentSearchAction(nextSearchAction, currSearchAction);
+
+        // Whether the current search action occurs within the threshold after the previous search
+        // action.
+        boolean occursWithinTimeThreshold =
+                currSearchAction.getCreationTimestampMillis()
+                        - prevSearchAction.getCreationTimestampMillis()
+                        <= NOISE_SEARCH_INTENT_TIMESTAMP_DIFF_THRESHOLD_MILLIS;
+
+        // Whether the previous search action's raw query string is a prefix of the current search
+        // action's, or the opposite direction (e.g. "app" -> "apple" and "apple" -> "app").
+        String prevQuery = prevSearchAction.getQuery();
+        String currQuery = currSearchAction.getQuery();
+        boolean isPrefix = prevQuery != null && currQuery != null
+                && (currQuery.startsWith(prevQuery) || prevQuery.startsWith(currQuery));
+
+        return !isNextSearchActionIndependent && occursWithinTimeThreshold && isPrefix;
+    }
+
+    /**
+     * Returns if the current search action is independent from the previous search action.
+     *
+     * <p>If the current search action occurs later than the threshold after the previous search
+     * action, then they are considered independent.
+     */
+    private static boolean isIndependentSearchAction(
+            @NonNull SearchActionGenericDocument currSearchAction,
+            @NonNull SearchActionGenericDocument prevSearchAction) {
+        Objects.requireNonNull(currSearchAction);
+        Objects.requireNonNull(prevSearchAction);
+
+        long searchTimeDiffMillis = currSearchAction.getCreationTimestampMillis()
+                - prevSearchAction.getCreationTimestampMillis();
+        return searchTimeDiffMillis > INDEPENDENT_SEARCH_INTENT_TIMESTAMP_DIFF_THRESHOLD_MILLIS;
+    }
+
+    /** Returns the common prefix length of the given 2 strings. */
+    private static int getCommonPrefixLength(@NonNull String s1, @NonNull String s2) {
+        Objects.requireNonNull(s1);
+        Objects.requireNonNull(s2);
+
+        int minLength = Math.min(s1.length(), s2.length());
+        for (int i = 0; i < minLength; ++i) {
+            if (s1.charAt(i) != s2.charAt(i)) {
+                return i;
+            }
+        }
+        return minLength;
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/TakenActionGenericDocument.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/TakenActionGenericDocument.java
new file mode 100644
index 0000000..1af156d
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/usagereporting/TakenActionGenericDocument.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.usagereporting;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.usagereporting.ActionConstants;
+import androidx.core.util.Preconditions;
+
+/**
+ * Abstract wrapper class for {@link GenericDocument} of all types of taken actions, which contains
+ * common getters and constants.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public abstract class TakenActionGenericDocument extends GenericDocument {
+    protected static final String PROPERTY_PATH_ACTION_TYPE = "actionType";
+
+    /**
+     * Static factory method to create a concrete object of {@link TakenActionGenericDocument} child
+     * type, according to the given {@link GenericDocument}'s action type.
+     *
+     * @param document a generic document object.
+     *
+     * @throws IllegalArgumentException if the integer value of property {@code actionType} is
+     *                                  invalid.
+     */
+    @NonNull
+    public static TakenActionGenericDocument create(@NonNull GenericDocument document)
+            throws IllegalArgumentException {
+        Preconditions.checkNotNull(document);
+        int actionType = (int) document.getPropertyLong(PROPERTY_PATH_ACTION_TYPE);
+        switch (actionType) {
+            case ActionConstants.ACTION_TYPE_SEARCH:
+                return new SearchActionGenericDocument.Builder(document).build();
+            case ActionConstants.ACTION_TYPE_CLICK:
+                return new ClickActionGenericDocument.Builder(document).build();
+            default:
+                throw new IllegalArgumentException(
+                        "Cannot create taken action generic document with unknown action type");
+        }
+    }
+
+    protected TakenActionGenericDocument(@NonNull GenericDocument document) {
+        super(Preconditions.checkNotNull(document));
+    }
+
+    /** Returns the (enum) integer value of property {@code actionType}. */
+    public int getActionType() {
+        return (int) getPropertyLong(PROPERTY_PATH_ACTION_TYPE);
+    }
+
+    /** Abstract builder for {@link TakenActionGenericDocument}. */
+    abstract static class Builder<T extends Builder<T>> extends GenericDocument.Builder<T> {
+        /**
+         * Creates a new {@link TakenActionGenericDocument.Builder}.
+         *
+         * <p>Document IDs are unique within a namespace.
+         *
+         * <p>The number of namespaces per app should be kept small for efficiency reasons.
+         *
+         * @param namespace  the namespace to set for the {@link GenericDocument}.
+         * @param id         the unique identifier for the {@link GenericDocument} in its namespace.
+         * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
+         *                   provided {@code schemaType} must be defined using
+         *                   {@link AppSearchSession#setSchemaAsync} prior
+         *                   to inserting a document of this {@code schemaType} into the
+         *                   AppSearch index using
+         *                   {@link AppSearchSession#putAsync}.
+         *                   Otherwise, the document will be rejected by
+         *                   {@link AppSearchSession#putAsync} with result code
+         *                   {@link AppSearchResult#RESULT_NOT_FOUND}.
+         * @param actionType the action type of the taken action. See definitions in
+         *                   {@link ActionConstants}.
+         */
+        Builder(@NonNull String namespace, @NonNull String id, @NonNull String schemaType,
+                int actionType) {
+            super(Preconditions.checkNotNull(namespace), Preconditions.checkNotNull(id),
+                    Preconditions.checkNotNull(schemaType));
+
+            setPropertyLong(PROPERTY_PATH_ACTION_TYPE, actionType);
+        }
+
+        /**
+         * Creates a new {@link TakenActionGenericDocument.Builder} from an existing
+         * {@link GenericDocument}.
+         */
+        Builder(@NonNull GenericDocument document) {
+            super(Preconditions.checkNotNull(document));
+        }
+    }
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/CallerAccess.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/CallerAccess.java
index a22863a..4deb5c3 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/CallerAccess.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/CallerAccess.java
@@ -44,10 +44,19 @@
         return mCallingPackageName;
     }
 
+    /** Returns whether the caller should have default access to data in its own package. */
+    public boolean doesCallerHaveSelfAccess() {
+        return true;
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
-        if (this == o) return true;
-        if (!(o instanceof CallerAccess)) return false;
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof CallerAccess)) {
+            return false;
+        }
         CallerAccess that = (CallerAccess) o;
         return mCallingPackageName.equals(that.mCallingPackageName);
     }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityChecker.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityChecker.java
index 9db52a5..13f908d 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityChecker.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityChecker.java
@@ -38,4 +38,11 @@
             @NonNull String packageName,
             @NonNull String prefixedSchema,
             @NonNull VisibilityStore visibilityStore);
+
+    /**
+     * Checks whether the given package has access to system-surfaceable schemas.
+     *
+     * @param callerPackageName Package name of the caller.
+     */
+    boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName);
 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityDocumentV1.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityDocumentV1.java
index a45cf75..7bcea68 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityDocumentV1.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityDocumentV1.java
@@ -18,6 +18,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.PackageIdentifier;
@@ -143,6 +144,7 @@
         }
 
         /** Sets whether this schema has opted out of platform surfacing. */
+        @CanIgnoreReturnValue
         @NonNull
         Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
             return setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY,
@@ -150,6 +152,7 @@
         }
 
         /** Add {@link PackageIdentifier} of packages which has access to this schema. */
+        @CanIgnoreReturnValue
         @NonNull
         Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) {
             Preconditions.checkNotNull(packageIdentifiers);
@@ -158,6 +161,7 @@
         }
 
         /** Add {@link PackageIdentifier} of packages which has access to this schema. */
+        @CanIgnoreReturnValue
         @NonNull
         Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
             Preconditions.checkNotNull(packageIdentifier);
@@ -167,6 +171,7 @@
 
         /** Add a set of Android role that has access to the schema this
          * {@link VisibilityDocumentV1} represents. */
+        @CanIgnoreReturnValue
         @NonNull
         Builder setVisibleToRoles(@NonNull Set<Integer> visibleToRoles) {
             Preconditions.checkNotNull(visibleToRoles);
@@ -176,6 +181,7 @@
 
         /** Add a set of Android role that has access to the schema this
          * {@link VisibilityDocumentV1} represents. */
+        @CanIgnoreReturnValue
         @NonNull
         Builder setVisibleToPermissions(@NonNull Set<Integer> visibleToPermissions) {
             Preconditions.checkNotNull(visibleToPermissions);
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
index e3d5916..c221673 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
@@ -27,11 +27,15 @@
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.InternalSetSchemaResponse;
-import androidx.appsearch.app.VisibilityDocument;
-import androidx.appsearch.app.VisibilityPermissionDocument;
+import androidx.appsearch.app.InternalVisibilityConfig;
+import androidx.appsearch.app.VisibilityPermissionConfig;
+import androidx.appsearch.checker.initialization.qual.UnderInitialization;
+import androidx.appsearch.checker.initialization.qual.UnknownInitialization;
+import androidx.appsearch.checker.nullness.qual.RequiresNonNull;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.AppSearchImpl;
 import androidx.appsearch.localstorage.util.PrefixUtil;
+import androidx.appsearch.util.LogUtil;
 import androidx.collection.ArrayMap;
 import androidx.core.util.Preconditions;
 
@@ -47,10 +51,11 @@
  * Stores all visibility settings for all databases that AppSearchImpl knows about.
  * Persists the visibility settings and reloads them on initialization.
  *
- * <p>The VisibilityStore creates a {@link VisibilityDocument} for each schema. This document holds
- * the visibility settings that apply to that schema. The VisibilityStore also creates a
- * schema for these documents and has its own package and database so that its data doesn't
- * interfere with any clients' data. It persists the document and schema through AppSearchImpl.
+ * <p>The VisibilityStore creates a {@link InternalVisibilityConfig} for each schema. This config
+ * holds the visibility settings that apply to that schema. The VisibilityStore also creates a
+ * schema and documents for these {@link InternalVisibilityConfig} and has its own
+ * package and database so that its data doesn't interfere with any clients' data. It persists
+ * the document and schema through AppSearchImpl.
  *
  * <p>These visibility settings won't be used in AppSearch Jetpack, we only store them for clients
  * to look up.
@@ -67,12 +72,13 @@
     public static final String VISIBILITY_PACKAGE_NAME = "VS#Pkg";
 
     public static final String VISIBILITY_DATABASE_NAME = "VS#Db";
+    public static final String ANDROID_V_OVERLAY_DATABASE_NAME = "VS#AndroidVDb";
 
     /**
-     * Map of PrefixedSchemaType and VisibilityDocument stores visibility information for each
+     * Map of PrefixedSchemaType to InternalVisibilityConfig stores visibility information for each
      * schema type.
      */
-    private final Map<String, VisibilityDocument> mVisibilityDocumentMap = new ArrayMap<>();
+    private final Map<String, InternalVisibilityConfig> mVisibilityConfigMap = new ArrayMap<>();
 
     private final AppSearchImpl mAppSearchImpl;
 
@@ -86,7 +92,7 @@
                 new CallerAccess(/*callingPackageName=*/VISIBILITY_PACKAGE_NAME));
         List<VisibilityDocumentV1> visibilityDocumentsV1s = null;
         switch (getSchemaResponse.getVersion()) {
-            case VisibilityDocument.SCHEMA_VERSION_DOC_PER_PACKAGE:
+            case VisibilityToDocumentConverter.SCHEMA_VERSION_DOC_PER_PACKAGE:
                 // TODO (b/202194495) add VisibilityDocument in version 0 back instead of using
                 //  GenericDocument.
                 List<GenericDocument> visibilityDocumentsV0s =
@@ -95,7 +101,7 @@
                 visibilityDocumentsV1s = VisibilityStoreMigrationHelperFromV0
                         .toVisibilityDocumentV1(visibilityDocumentsV0s);
                 // fall through
-            case VisibilityDocument.SCHEMA_VERSION_DOC_PER_SCHEMA:
+            case VisibilityToDocumentConverter.SCHEMA_VERSION_DOC_PER_SCHEMA:
                 if (visibilityDocumentsV1s == null) {
                     // We need to read VisibilityDocument in Version 1 from AppSearch instead of
                     // taking from the above step.
@@ -106,37 +112,12 @@
                 setLatestSchemaAndDocuments(VisibilityStoreMigrationHelperFromV1
                         .toVisibilityDocumentsV2(visibilityDocumentsV1s));
                 break;
-            case VisibilityDocument.SCHEMA_VERSION_LATEST:
-                Set<AppSearchSchema> existingVisibilitySchema = getSchemaResponse.getSchemas();
-                if (existingVisibilitySchema.contains(VisibilityDocument.SCHEMA)
-                        && existingVisibilitySchema.contains(VisibilityPermissionDocument.SCHEMA)) {
-                    // The latest Visibility schema is in AppSearch, we must find our schema type.
-                    // Extract all stored Visibility Document into mVisibilityDocumentMap.
-                    loadVisibilityDocumentMap();
-                } else {
-                    // We must have a broken schema. Reset it to the latest version.
-                    // Do NOT set forceOverride to be true here, see comment below.
-                    InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
-                            VISIBILITY_PACKAGE_NAME,
-                            VISIBILITY_DATABASE_NAME,
-                            Arrays.asList(VisibilityDocument.SCHEMA,
-                                    VisibilityPermissionDocument.SCHEMA),
-                            /*visibilityDocuments=*/ Collections.emptyList(),
-                            /*forceOverride=*/ false,
-                            /*version=*/ VisibilityDocument.SCHEMA_VERSION_LATEST,
-                            /*setSchemaStatsBuilder=*/ null);
-                    if (!internalSetSchemaResponse.isSuccess()) {
-                        // If you hit problem here it means you made a incompatible change in
-                        // Visibility Schema without update the version number. You should bump
-                        // the version number and create a VisibilityStoreMigrationHelper which
-                        // can analyse the different between the old version and the new version
-                        // to migration user's visibility settings.
-                        throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
-                                "Fail to set the latest visibility schema to AppSearch. "
-                                        + "You may need to update the visibility schema version "
-                                        + "number.");
-                    }
-                }
+            case VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST:
+                verifyOrSetLatestVisibilitySchema(getSchemaResponse);
+                // Check the version for visibility overlay database.
+                migrateVisibilityOverlayDatabase();
+                // Now we have the latest schema, load visibility config map.
+                loadVisibilityConfigMap();
                 break;
             default:
                 // We must did something wrong.
@@ -146,30 +127,64 @@
     }
 
     /**
-     * Sets visibility settings for the given {@link VisibilityDocument}s. Any previous
-     * {@link VisibilityDocument}s with same prefixed schema type will be overwritten.
+     * Sets visibility settings for the given {@link InternalVisibilityConfig}s. Any previous
+     * {@link InternalVisibilityConfig}s with same prefixed schema type will be overwritten.
      *
-     * @param prefixedVisibilityDocuments List of prefixed {@link VisibilityDocument} which
-     *                                    contains schema type's visibility information.
+     * @param prefixedVisibilityConfigs List of prefixed {@link InternalVisibilityConfig}s which
+     *                                  contains schema type's visibility information.
      * @throws AppSearchException on AppSearchImpl error.
      */
-    public void setVisibility(@NonNull List<VisibilityDocument> prefixedVisibilityDocuments)
+    public void setVisibility(@NonNull List<InternalVisibilityConfig> prefixedVisibilityConfigs)
             throws AppSearchException {
-        Preconditions.checkNotNull(prefixedVisibilityDocuments);
+        Preconditions.checkNotNull(prefixedVisibilityConfigs);
         // Save new setting.
-        for (int i = 0; i < prefixedVisibilityDocuments.size(); i++) {
-            // put VisibilityDocument to AppSearchImpl and mVisibilityDocumentMap. If there is a
-            // VisibilityDocument with same prefixed schema exists, it will be replaced by new
-            // VisibilityDocument in both AppSearch and memory look up map.
-            VisibilityDocument prefixedVisibilityDocument = prefixedVisibilityDocuments.get(i);
+        for (int i = 0; i < prefixedVisibilityConfigs.size(); i++) {
+            // put VisibilityConfig to AppSearchImpl and mVisibilityConfigMap. If there is a
+            // VisibilityConfig with same prefixed schema exists, it will be replaced by new
+            // VisibilityConfig in both AppSearch and memory look up map.
+            InternalVisibilityConfig prefixedVisibilityConfig = prefixedVisibilityConfigs.get(i);
+            InternalVisibilityConfig oldVisibilityConfig =
+                    mVisibilityConfigMap.get(prefixedVisibilityConfig.getSchemaType());
             mAppSearchImpl.putDocument(
                     VISIBILITY_PACKAGE_NAME,
                     VISIBILITY_DATABASE_NAME,
-                    prefixedVisibilityDocument.toGenericDocument(),
+                    VisibilityToDocumentConverter.createVisibilityDocument(
+                            prefixedVisibilityConfig),
                     /*sendChangeNotifications=*/ false,
                     /*logger=*/ null);
-            mVisibilityDocumentMap.put(prefixedVisibilityDocument.getId(),
-                    prefixedVisibilityDocument);
+
+            // Put the android V visibility overlay document to AppSearchImpl.
+            GenericDocument androidVOverlay =
+                    VisibilityToDocumentConverter.createAndroidVOverlay(prefixedVisibilityConfig);
+            if (androidVOverlay != null) {
+                mAppSearchImpl.putDocument(
+                        VISIBILITY_PACKAGE_NAME,
+                        ANDROID_V_OVERLAY_DATABASE_NAME,
+                        androidVOverlay,
+                        /*sendChangeNotifications=*/ false,
+                        /*logger=*/ null);
+            } else if (isConfigContainsAndroidVOverlay(oldVisibilityConfig)) {
+                // We need to make sure to remove the VisibilityOverlay on disk as the current
+                // VisibilityConfig does not have a VisibilityOverlay.
+                // For performance improvement, we should only make the remove call if the old
+                // VisibilityConfig contains the overlay settings.
+                try {
+                    mAppSearchImpl.remove(VISIBILITY_PACKAGE_NAME,
+                            ANDROID_V_OVERLAY_DATABASE_NAME,
+                            VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                            prefixedVisibilityConfig.getSchemaType(),
+                            /*removeStatsBuilder=*/null);
+                } catch (AppSearchException e) {
+                    // If it already doesn't exist, that is fine
+                    if (e.getResultCode() != RESULT_NOT_FOUND) {
+                        throw e;
+                    }
+                }
+            }
+
+            // Put the VisibilityConfig to memory look up map.
+            mVisibilityConfigMap.put(prefixedVisibilityConfig.getSchemaType(),
+                    prefixedVisibilityConfig);
         }
         // Now that the visibility document has been written. Persist the newly written data.
         mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
@@ -182,12 +197,13 @@
     public void removeVisibility(@NonNull Set<String> prefixedSchemaTypes)
             throws AppSearchException {
         for (String prefixedSchemaType : prefixedSchemaTypes) {
-            if (mVisibilityDocumentMap.remove(prefixedSchemaType) != null) {
+            if (mVisibilityConfigMap.remove(prefixedSchemaType) != null) {
                 // The deleted schema is not all-default setting, we need to remove its
                 // VisibilityDocument from Icing.
                 try {
                     mAppSearchImpl.remove(VISIBILITY_PACKAGE_NAME, VISIBILITY_DATABASE_NAME,
-                            VisibilityDocument.NAMESPACE, prefixedSchemaType,
+                            VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                            prefixedSchemaType,
                             /*removeStatsBuilder=*/null);
                 } catch (AppSearchException e) {
                     if (e.getResultCode() == RESULT_NOT_FOUND) {
@@ -195,25 +211,45 @@
                         // to be fine if we cannot find it.
                         Log.e(TAG, "Cannot find visibility document for " + prefixedSchemaType
                                 + " to remove.");
-                        return;
+                    } else {
+                        throw e;
                     }
-                    throw e;
+                }
+
+                try {
+                    mAppSearchImpl.remove(VISIBILITY_PACKAGE_NAME,
+                            ANDROID_V_OVERLAY_DATABASE_NAME,
+                            VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                            prefixedSchemaType,
+                            /*removeStatsBuilder=*/null);
+                } catch (AppSearchException e) {
+                    if (e.getResultCode() == RESULT_NOT_FOUND) {
+                        // It's possible no overlay was set, so this this is fine.
+                        if (LogUtil.DEBUG) {
+                            Log.d(TAG, "Cannot find Android V overlay document for "
+                                    + prefixedSchemaType + " to remove.");
+                        }
+                    } else {
+                        throw e;
+                    }
                 }
             }
         }
     }
 
-    /** Gets the {@link VisibilityDocument} for the given prefixed schema type.     */
+    /** Gets the {@link InternalVisibilityConfig} for the given prefixed schema type.     */
     @Nullable
-    public VisibilityDocument getVisibility(@NonNull String prefixedSchemaType) {
-        return mVisibilityDocumentMap.get(prefixedSchemaType);
+    public InternalVisibilityConfig getVisibility(@NonNull String prefixedSchemaType) {
+        return mVisibilityConfigMap.get(prefixedSchemaType);
     }
 
     /**
-     * Loads all stored latest {@link VisibilityDocument} from Icing, and put them into
-     * {@link #mVisibilityDocumentMap}.
+     * Loads all stored latest {@link InternalVisibilityConfig} from Icing, and put them into
+     * {@link #mVisibilityConfigMap}.
      */
-    private void loadVisibilityDocumentMap() throws AppSearchException {
+    @RequiresNonNull("mAppSearchImpl")
+    private void loadVisibilityConfigMap(@UnderInitialization VisibilityStore this)
+            throws AppSearchException {
         // Populate visibility settings set
         List<String> cachedSchemaTypes = mAppSearchImpl.getAllPrefixedSchemaTypes();
         for (int i = 0; i < cachedSchemaTypes.size(); i++) {
@@ -223,16 +259,16 @@
                 continue; // Our own package. Skip.
             }
 
-            VisibilityDocument visibilityDocument;
+            GenericDocument visibilityDocument;
+            GenericDocument visibilityAndroidVOverlay = null;
             try {
                 // Note: We use the other clients' prefixed schema type as ids
-                visibilityDocument = new VisibilityDocument.Builder(
-                        mAppSearchImpl.getDocument(
-                                VISIBILITY_PACKAGE_NAME,
-                                VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefixedSchemaType,
-                                /*typePropertyPaths=*/ Collections.emptyMap())).build();
+                visibilityDocument = mAppSearchImpl.getDocument(
+                        VISIBILITY_PACKAGE_NAME,
+                        VISIBILITY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
+                        /*id=*/ prefixedSchemaType,
+                        /*typePropertyPaths=*/ Collections.emptyMap());
             } catch (AppSearchException e) {
                 if (e.getResultCode() == RESULT_NOT_FOUND) {
                     // The schema has all default setting and we won't have a VisibilityDocument for
@@ -242,25 +278,48 @@
                 // Otherwise, this is some other error we should pass up.
                 throw e;
             }
-            mVisibilityDocumentMap.put(prefixedSchemaType, visibilityDocument);
+
+            try {
+                visibilityAndroidVOverlay = mAppSearchImpl.getDocument(
+                        VISIBILITY_PACKAGE_NAME,
+                        ANDROID_V_OVERLAY_DATABASE_NAME,
+                        VisibilityToDocumentConverter.ANDROID_V_OVERLAY_NAMESPACE,
+                        /*id=*/ prefixedSchemaType,
+                        /*typePropertyPaths=*/ Collections.emptyMap());
+            } catch (AppSearchException e) {
+                if (e.getResultCode() != RESULT_NOT_FOUND) {
+                    // This is some other error we should pass up.
+                    throw e;
+                }
+                // Otherwise we continue inserting into visibility document map as the overlay
+                // map can be null
+            }
+
+            mVisibilityConfigMap.put(
+                    prefixedSchemaType,
+                    VisibilityToDocumentConverter.createInternalVisibilityConfig(
+                            visibilityDocument, visibilityAndroidVOverlay));
         }
     }
 
     /**
-     * Set the latest version of {@link VisibilityDocument} and its schema to AppSearch.
+     * Set the latest version of {@link InternalVisibilityConfig} and its schema to AppSearch.
      */
-    private void setLatestSchemaAndDocuments(@NonNull List<VisibilityDocument> migratedDocuments)
+    @RequiresNonNull("mAppSearchImpl")
+    private void setLatestSchemaAndDocuments(
+            @UnderInitialization VisibilityStore this,
+            @NonNull List<InternalVisibilityConfig> migratedDocuments)
             throws AppSearchException {
         // The latest schema type doesn't exist yet. Add it. Set forceOverride true to
         // delete old schema.
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 VISIBILITY_PACKAGE_NAME,
                 VISIBILITY_DATABASE_NAME,
-                Arrays.asList(VisibilityDocument.SCHEMA,
-                        VisibilityPermissionDocument.SCHEMA),
-                /*visibilityDocuments=*/ Collections.emptyList(),
+                Arrays.asList(VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                        VisibilityPermissionConfig.SCHEMA),
+                /*visibilityConfigs=*/ Collections.emptyList(),
                 /*forceOverride=*/ true,
-                /*version=*/ VisibilityDocument.SCHEMA_VERSION_LATEST,
+                /*version=*/ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
                 /*setSchemaStatsBuilder=*/ null);
         if (!internalSetSchemaResponse.isSuccess()) {
             // Impossible case, we just set forceOverride to be true, we should never
@@ -268,15 +327,185 @@
             throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
                     internalSetSchemaResponse.getErrorMessage());
         }
+        InternalSetSchemaResponse internalSetAndroidVOverlaySchemaResponse =
+                mAppSearchImpl.setSchema(
+                        VISIBILITY_PACKAGE_NAME,
+                        ANDROID_V_OVERLAY_DATABASE_NAME,
+                        Collections.singletonList(
+                                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA),
+                        /*visibilityConfigs=*/ Collections.emptyList(),
+                        /*forceOverride=*/ true,
+                        /*version=*/ VisibilityToDocumentConverter
+                                .ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST,
+                        /*setSchemaStatsBuilder=*/ null);
+        if (!internalSetAndroidVOverlaySchemaResponse.isSuccess()) {
+            // Impossible case, we just set forceOverride to be true, we should never
+            // fail in incompatible changes.
+            throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
+                    internalSetAndroidVOverlaySchemaResponse.getErrorMessage());
+        }
         for (int i = 0; i < migratedDocuments.size(); i++) {
-            VisibilityDocument migratedDocument = migratedDocuments.get(i);
-            mVisibilityDocumentMap.put(migratedDocument.getId(), migratedDocument);
+            InternalVisibilityConfig migratedConfig = migratedDocuments.get(i);
+            mVisibilityConfigMap.put(migratedConfig.getSchemaType(), migratedConfig);
             mAppSearchImpl.putDocument(
                     VISIBILITY_PACKAGE_NAME,
                     VISIBILITY_DATABASE_NAME,
-                    migratedDocument.toGenericDocument(),
+                    VisibilityToDocumentConverter.createVisibilityDocument(migratedConfig),
                     /*sendChangeNotifications=*/ false,
                     /*logger=*/ null);
         }
     }
+
+    /**
+     * Check and migrate visibility schemas in {@link #ANDROID_V_OVERLAY_DATABASE_NAME} to
+     * {@link VisibilityToDocumentConverter#ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST}.
+     */
+    @RequiresNonNull("mAppSearchImpl")
+    private void migrateVisibilityOverlayDatabase(@UnderInitialization VisibilityStore this)
+            throws AppSearchException {
+        GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(
+                VISIBILITY_PACKAGE_NAME,
+                ANDROID_V_OVERLAY_DATABASE_NAME,
+                new CallerAccess(/*callingPackageName=*/VISIBILITY_PACKAGE_NAME));
+        switch (getSchemaResponse.getVersion()) {
+            case VisibilityToDocumentConverter.OVERLAY_SCHEMA_VERSION_PUBLIC_ACL_VISIBLE_TO_CONFIG:
+                // Force override to next version. This version hasn't released to any public
+                // version. There shouldn't have any public device in this state, so we don't
+                // actually need to migrate any document.
+                InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                        VISIBILITY_PACKAGE_NAME,
+                        ANDROID_V_OVERLAY_DATABASE_NAME,
+                        Collections.singletonList(
+                                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA),
+                        /*visibilityConfigs=*/ Collections.emptyList(),
+                        /*forceOverride=*/ true,  // force update to nest version.
+                        VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST,
+                        /*setSchemaStatsBuilder=*/ null);
+                if (!internalSetSchemaResponse.isSuccess()) {
+                    // Impossible case, we just set forceOverride to be true, we should never
+                    // fail in incompatible changes.
+                    throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
+                            internalSetSchemaResponse.getErrorMessage());
+                }
+                break;
+            case VisibilityToDocumentConverter.OVERLAY_SCHEMA_VERSION_ALL_IN_PROTO:
+                verifyOrSetLatestVisibilityOverlaySchema(getSchemaResponse);
+                break;
+            default:
+                // We must did something wrong.
+                throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
+                        "Found unsupported visibility version: " + getSchemaResponse.getVersion());
+        }
+    }
+
+    /**
+     * Verify the existing visibility schema, set the latest visibilility schema if it's missing.
+     */
+    @RequiresNonNull("mAppSearchImpl")
+    private void verifyOrSetLatestVisibilitySchema(
+            @UnderInitialization VisibilityStore this, @NonNull GetSchemaResponse getSchemaResponse)
+            throws AppSearchException {
+        // We cannot change the schema version past 2 as detecting version "3" would hit the
+        // default block and throw an AppSearchException. This is why we added
+        // VisibilityOverlay.
+
+        // Check Visibility schema first.
+        Set<AppSearchSchema> existingVisibilitySchema = getSchemaResponse.getSchemas();
+        // Force to override visibility schema if it contains DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA.
+        // The DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA was added to VISIBILITY_DATABASE_NAME and
+        // removed to ANDROID_V_OVERLAY_DATABASE_NAME. We need to force update the schema to
+        // migrate devices that have already store public acl schema.
+        // TODO(b/321326441) remove this method when we no longer to migrate devices in this state.
+        if (existingVisibilitySchema.contains(
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA)
+                && existingVisibilitySchema.contains(VisibilityPermissionConfig.SCHEMA)
+                && existingVisibilitySchema.contains(
+                VisibilityToDocumentConverter.DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA)) {
+            InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                    VISIBILITY_PACKAGE_NAME,
+                    VISIBILITY_DATABASE_NAME,
+                    Arrays.asList(VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                            VisibilityPermissionConfig.SCHEMA),
+                    /*visibilityConfigs=*/ Collections.emptyList(),
+                    /*forceOverride=*/ true,
+                    /*version=*/ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
+                    /*setSchemaStatsBuilder=*/ null);
+            if (!internalSetSchemaResponse.isSuccess()) {
+                throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
+                        "Fail to force override deprecated visibility schema with public acl.");
+            }
+        } else if (!(existingVisibilitySchema.contains(
+                VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA)
+                && existingVisibilitySchema.contains(VisibilityPermissionConfig.SCHEMA))) {
+            // We must have a broken schema. Reset it to the latest version.
+            // Do NOT set forceOverride to be true here, see comment below.
+            InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                    VISIBILITY_PACKAGE_NAME,
+                    VISIBILITY_DATABASE_NAME,
+                    Arrays.asList(VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_SCHEMA,
+                            VisibilityPermissionConfig.SCHEMA),
+                    /*visibilityConfigs=*/ Collections.emptyList(),
+                    /*forceOverride=*/ false,
+                    /*version=*/ VisibilityToDocumentConverter.SCHEMA_VERSION_LATEST,
+                    /*setSchemaStatsBuilder=*/ null);
+            if (!internalSetSchemaResponse.isSuccess()) {
+                // If you hit problem here it means you made a incompatible change in
+                // Visibility Schema without update the version number. You should bump
+                // the version number and create a VisibilityStoreMigrationHelper which
+                // can analyse the different between the old version and the new version
+                // to migration user's visibility settings.
+                throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
+                        "Fail to set the latest visibility schema to AppSearch. "
+                                + "You may need to update the visibility schema version "
+                                + "number.");
+            }
+        }
+    }
+
+    /**
+     * Verify the existing visibility overlay schema, set the latest overlay schema if it's missing.
+     */
+    @RequiresNonNull("mAppSearchImpl")
+    private void verifyOrSetLatestVisibilityOverlaySchema(
+            @UnknownInitialization VisibilityStore this,
+            @NonNull GetSchemaResponse getAndroidVOverlaySchemaResponse)
+            throws AppSearchException {
+        // Check Android V overlay schema.
+        Set<AppSearchSchema> existingAndroidVOverlaySchema =
+                getAndroidVOverlaySchemaResponse.getSchemas();
+        if (!existingAndroidVOverlaySchema.contains(
+                VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA)) {
+            // We must have a broken schema. Reset it to the latest version.
+            // Do NOT set forceOverride to be true here, see comment below.
+            InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                    VISIBILITY_PACKAGE_NAME,
+                    ANDROID_V_OVERLAY_DATABASE_NAME,
+                    Collections.singletonList(
+                            VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA),
+                    /*visibilityConfigs=*/ Collections.emptyList(),
+                    /*forceOverride=*/ false,
+                    VisibilityToDocumentConverter.ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST,
+                    /*setSchemaStatsBuilder=*/ null);
+            if (!internalSetSchemaResponse.isSuccess()) {
+                // If you hit problem here it means you made a incompatible change in
+                // Visibility Schema. You should create new overlay schema
+                throw new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
+                        "Fail to set the overlay visibility schema to AppSearch. "
+                                + "You may need to create new overlay schema.");
+            }
+        }
+    }
+
+    /**
+     * Whether the given {@link InternalVisibilityConfig} contains Android V overlay settings.
+     *
+     * <p> Android V overlay {@link VisibilityToDocumentConverter#ANDROID_V_OVERLAY_SCHEMA}
+     * contains public acl and visible to config.
+     */
+    private static boolean isConfigContainsAndroidVOverlay(
+            @Nullable InternalVisibilityConfig config) {
+        return config != null
+                && (config.getVisibilityConfig().getPubliclyVisibleTargetPackage() != null
+                || !config.getVisibleToConfigs().isEmpty());
+    }
 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0.java
index 4fb4de9..ca255bf 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0.java
@@ -24,7 +24,6 @@
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.PackageIdentifier;
-import androidx.appsearch.app.VisibilityDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.AppSearchImpl;
 import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -153,7 +152,7 @@
                     deprecatedDocuments.add(appSearchImpl.getDocument(
                             VisibilityStore.VISIBILITY_PACKAGE_NAME,
                             VisibilityStore.VISIBILITY_DATABASE_NAME,
-                            VisibilityDocument.NAMESPACE,
+                            VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                             getDeprecatedVisibilityDocumentId(packageName, databaseName),
                             /*typePropertyPaths=*/ Collections.emptyMap()));
                 } catch (AppSearchException e) {
@@ -206,15 +205,15 @@
                 for (GenericDocument deprecatedPackageDocument : deprecatedPackageDocuments) {
                     String prefixedSchemaType = Preconditions.checkNotNull(
                             deprecatedPackageDocument.getPropertyString(
-                            DEPRECATED_ACCESSIBLE_SCHEMA_PROPERTY));
+                                    DEPRECATED_ACCESSIBLE_SCHEMA_PROPERTY));
                     VisibilityDocumentV1.Builder visibilityBuilder =
                             getOrCreateBuilder(documentBuilderMap, prefixedSchemaType);
                     String packageName = Preconditions.checkNotNull(
                             deprecatedPackageDocument.getPropertyString(
-                                DEPRECATED_PACKAGE_NAME_PROPERTY));
+                                    DEPRECATED_PACKAGE_NAME_PROPERTY));
                     byte[] sha256Cert = Preconditions.checkNotNull(
                             deprecatedPackageDocument.getPropertyBytes(
-                                DEPRECATED_SHA_256_CERT_PROPERTY));
+                                    DEPRECATED_SHA_256_CERT_PROPERTY));
                     visibilityBuilder.addVisibleToPackage(
                             new PackageIdentifier(packageName, sha256Cert));
                 }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1.java
index 5a082fc..185c19c 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1.java
@@ -20,9 +20,9 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.appsearch.app.AppSearchResult;
+import androidx.appsearch.app.InternalVisibilityConfig;
 import androidx.appsearch.app.PackageIdentifier;
 import androidx.appsearch.app.SetSchemaRequest;
-import androidx.appsearch.app.VisibilityDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.localstorage.AppSearchImpl;
 import androidx.appsearch.localstorage.util.PrefixUtil;
@@ -67,7 +67,7 @@
                 visibilityDocumentV1s.add(new VisibilityDocumentV1(appSearchImpl.getDocument(
                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
                         VisibilityStore.VISIBILITY_DATABASE_NAME,
-                        VisibilityDocument.NAMESPACE,
+                        VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
                         allPrefixedSchemaTypes.get(i),
                         /*typePropertyPaths=*/ Collections.emptyMap())));
             } catch (AppSearchException e) {
@@ -91,13 +91,13 @@
      * @param visibilityDocumentV1s          The deprecated Visibility Document we found.
      */
     @NonNull
-    static List<VisibilityDocument> toVisibilityDocumentsV2(
+    static List<InternalVisibilityConfig> toVisibilityDocumentsV2(
             @NonNull List<VisibilityDocumentV1> visibilityDocumentV1s) {
-        List<VisibilityDocument> latestVisibilityDocuments =
+        List<InternalVisibilityConfig> latestVisibilityDocuments =
                 new ArrayList<>(visibilityDocumentV1s.size());
         for (int i = 0; i < visibilityDocumentV1s.size(); i++) {
             VisibilityDocumentV1 visibilityDocumentV1 = visibilityDocumentV1s.get(i);
-            Set<Set<Integer>> visibleToPermissions = new ArraySet<>();
+            Set<Set<Integer>> visibleToPermissionSets = new ArraySet<>();
             Set<Integer> deprecatedVisibleToRoles = visibilityDocumentV1.getVisibleToRoles();
             if (deprecatedVisibleToRoles != null) {
                 for (int deprecatedVisibleToRole : deprecatedVisibleToRoles) {
@@ -111,29 +111,28 @@
                                     .READ_ASSISTANT_APP_SEARCH_DATA);
                             break;
                     }
-                    visibleToPermissions.add(visibleToPermission);
+                    visibleToPermissionSets.add(visibleToPermission);
                 }
             }
             Set<Integer> deprecatedVisibleToPermissions =
                     visibilityDocumentV1.getVisibleToPermissions();
             if (deprecatedVisibleToPermissions != null) {
-                visibleToPermissions.add(deprecatedVisibleToPermissions);
+                visibleToPermissionSets.add(deprecatedVisibleToPermissions);
             }
 
-            Set<PackageIdentifier> packageIdentifiers = new ArraySet<>();
+            InternalVisibilityConfig.Builder latestVisibilityDocumentBuilder =
+                    new InternalVisibilityConfig.Builder(visibilityDocumentV1.getId())
+                            .setNotDisplayedBySystem(visibilityDocumentV1.isNotDisplayedBySystem());
             String[] packageNames = visibilityDocumentV1.getPackageNames();
             byte[][] sha256Certs = visibilityDocumentV1.getSha256Certs();
             if (packageNames.length == sha256Certs.length) {
                 for (int j = 0; j < packageNames.length; j++) {
-                    packageIdentifiers.add(new PackageIdentifier(packageNames[j], sha256Certs[j]));
+                    latestVisibilityDocumentBuilder.addVisibleToPackage(
+                            new PackageIdentifier(packageNames[j], sha256Certs[j]));
                 }
             }
-            VisibilityDocument.Builder latestVisibilityDocumentBuilder =
-                    new VisibilityDocument.Builder(visibilityDocumentV1.getId())
-                    .setNotDisplayedBySystem(visibilityDocumentV1.isNotDisplayedBySystem())
-                    .addVisibleToPackages(packageIdentifiers);
-            if (!visibleToPermissions.isEmpty()) {
-                latestVisibilityDocumentBuilder.setVisibleToPermissions(visibleToPermissions);
+            for (Set<Integer> visibleToPermissions : visibleToPermissionSets) {
+                latestVisibilityDocumentBuilder.addVisibleToPermissions(visibleToPermissions);
             }
             latestVisibilityDocuments.add(latestVisibilityDocumentBuilder.build());
         }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityToDocumentConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityToDocumentConverter.java
new file mode 100644
index 0000000..c7c5606
--- /dev/null
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityToDocumentConverter.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.localstorage.visibilitystore;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.InternalVisibilityConfig;
+import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
+import androidx.appsearch.app.VisibilityPermissionConfig;
+import androidx.collection.ArraySet;
+
+import com.google.android.appsearch.proto.AndroidVOverlayProto;
+import com.google.android.appsearch.proto.PackageIdentifierProto;
+import com.google.android.appsearch.proto.VisibilityConfigProto;
+import com.google.android.appsearch.proto.VisibleToPermissionProto;
+import com.google.android.icing.protobuf.ByteString;
+import com.google.android.icing.protobuf.InvalidProtocolBufferException;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utilities for working with {@link VisibilityChecker} and {@link VisibilityStore}.
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class VisibilityToDocumentConverter {
+    private static final String TAG = "AppSearchVisibilityToDo";
+
+    /**
+     * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
+     */
+    public static final String VISIBILITY_DOCUMENT_SCHEMA_TYPE = "VisibilityType";
+    /** Namespace of documents that contain visibility settings */
+    public static final String VISIBILITY_DOCUMENT_NAMESPACE = "";
+
+    /**
+     * The Schema type for the Android V visibility setting overlay documents, that allow for
+     * additional visibility settings.
+     */
+    public static final String ANDROID_V_OVERLAY_SCHEMA_TYPE = "AndroidVOverlayType";
+    /** Namespace of documents that contain Android V visibility setting overlay documents */
+    public static final String ANDROID_V_OVERLAY_NAMESPACE = "androidVOverlay";
+    /** Property that holds the serialized {@link AndroidVOverlayProto}. */
+    public static final String VISIBILITY_PROTO_SERIALIZE_PROPERTY =
+            "visibilityProtoSerializeProperty";
+
+    /**
+     * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
+     */
+    private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable";
+
+    /** Property that holds the package name that can access a schema. */
+    private static final String VISIBLE_TO_PACKAGE_IDENTIFIER_PROPERTY = "packageName";
+
+    /** Property that holds the SHA 256 certificate of the app that can access a schema. */
+    private static final String VISIBLE_TO_PACKAGE_SHA_256_CERT_PROPERTY = "sha256Cert";
+
+    /** Property that holds the required permissions to access the schema. */
+    private static final String PERMISSION_PROPERTY = "permission";
+
+    // The initial schema version, one VisibilityConfig contains all visibility information for
+    // whole package.
+    public static final int SCHEMA_VERSION_DOC_PER_PACKAGE = 0;
+
+    // One VisibilityConfig contains visibility information for a single schema.
+    public static final int SCHEMA_VERSION_DOC_PER_SCHEMA = 1;
+
+    // One VisibilityConfig contains visibility information for a single schema. The permission
+    // visibility information is stored in a document property VisibilityPermissionConfig of the
+    // outer doc.
+    public static final int SCHEMA_VERSION_NESTED_PERMISSION_SCHEMA = 2;
+
+    public static final int SCHEMA_VERSION_LATEST = SCHEMA_VERSION_NESTED_PERMISSION_SCHEMA;
+
+    // The initial schema version, the overlay schema contains public acl and visible to config
+    // properties.
+    public static final int OVERLAY_SCHEMA_VERSION_PUBLIC_ACL_VISIBLE_TO_CONFIG = 0;
+
+    // The overlay schema only contains a proto property contains all visibility setting.
+    public static final int OVERLAY_SCHEMA_VERSION_ALL_IN_PROTO = 1;
+
+    // The version number of schema saved in Android V overlay database.
+    public static final int ANDROID_V_OVERLAY_SCHEMA_VERSION_LATEST =
+            OVERLAY_SCHEMA_VERSION_ALL_IN_PROTO;
+
+    /**
+     * Schema for the VisibilityStore's documents.
+     *
+     * <p>NOTE: If you update this, also update {@link #SCHEMA_VERSION_LATEST}.
+     */
+    public static final AppSearchSchema VISIBILITY_DOCUMENT_SCHEMA =
+            new AppSearchSchema.Builder(VISIBILITY_DOCUMENT_SCHEMA_TYPE)
+                    .addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder(
+                            NOT_DISPLAYED_BY_SYSTEM_PROPERTY)
+                            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                            .build())
+                    .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+                            VISIBLE_TO_PACKAGE_IDENTIFIER_PROPERTY)
+                            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                            .build())
+                    .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(
+                            VISIBLE_TO_PACKAGE_SHA_256_CERT_PROPERTY)
+                            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                            .build())
+                    .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
+                            PERMISSION_PROPERTY, VisibilityPermissionConfig.SCHEMA_TYPE)
+                            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                            .build())
+                    .build();
+
+    /**  Schema for the VisibilityStore's Android V visibility setting overlay. */
+    public static final AppSearchSchema ANDROID_V_OVERLAY_SCHEMA =
+            new AppSearchSchema.Builder(ANDROID_V_OVERLAY_SCHEMA_TYPE)
+                    .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(
+                            VISIBILITY_PROTO_SERIALIZE_PROPERTY)
+                            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                            .build())
+                    .build();
+    /**
+     * The Deprecated schemas and properties that we need to remove from visibility database.
+     * TODO(b/321326441) remove this method when we no longer to migrate devices in this state.
+     */
+    static final AppSearchSchema DEPRECATED_PUBLIC_ACL_OVERLAY_SCHEMA =
+            new AppSearchSchema.Builder("PublicAclOverlayType")
+                    .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+                            "publiclyVisibleTargetPackage")
+                            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                            .build())
+                    .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(
+                            "publiclyVisibleTargetPackageSha256Cert")
+                            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                            .build())
+                    .build();
+
+    /**
+     * Constructs a {@link InternalVisibilityConfig} from two {@link GenericDocument}s.
+     *
+     * <p>This constructor is still needed until we don't treat Visibility related documents as
+     * {@link GenericDocument}s internally.
+     *
+     * @param visibilityDocument       a {@link GenericDocument} holding visibility properties
+     *                                 in {@link #VISIBILITY_DOCUMENT_SCHEMA}
+     * @param androidVOverlayDocument  a {@link GenericDocument} holding visibility properties
+     *                                 in {@link #ANDROID_V_OVERLAY_SCHEMA}
+     */
+    @NonNull
+    public static InternalVisibilityConfig createInternalVisibilityConfig(
+            @NonNull GenericDocument visibilityDocument,
+            @Nullable GenericDocument androidVOverlayDocument) {
+        Objects.requireNonNull(visibilityDocument);
+
+        // Parse visibility proto if required
+        AndroidVOverlayProto androidVOverlayProto = null;
+        if (androidVOverlayDocument != null) {
+            try {
+                byte[] androidVOverlayProtoBytes = androidVOverlayDocument.getPropertyBytes(
+                        VISIBILITY_PROTO_SERIALIZE_PROPERTY);
+                if (androidVOverlayProtoBytes != null) {
+                    androidVOverlayProto = AndroidVOverlayProto.parseFrom(
+                            androidVOverlayProtoBytes);
+                }
+            } catch (InvalidProtocolBufferException e) {
+                Log.e(TAG, "Get an invalid android V visibility overlay proto.", e);
+            }
+        }
+
+        // Handle all visibility settings other than visibleToConfigs
+        SchemaVisibilityConfig schemaVisibilityConfig = createVisibilityConfig(
+                visibilityDocument, androidVOverlayProto);
+
+        // Handle visibleToConfigs
+        String schemaType = visibilityDocument.getId();
+        InternalVisibilityConfig.Builder builder = new InternalVisibilityConfig.Builder(schemaType)
+                .setNotDisplayedBySystem(visibilityDocument
+                        .getPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY))
+                .setVisibilityConfig(schemaVisibilityConfig);
+        if (androidVOverlayProto != null) {
+            List<VisibilityConfigProto> visibleToConfigProtoList =
+                    androidVOverlayProto.getVisibleToConfigsList();
+            for (int i = 0; i < visibleToConfigProtoList.size(); i++) {
+                SchemaVisibilityConfig visibleToConfig =
+                        convertVisibilityConfigFromProto(visibleToConfigProtoList.get(i));
+                builder.addVisibleToConfig(visibleToConfig);
+            }
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Constructs a {@link SchemaVisibilityConfig} from a {@link GenericDocument} containing legacy
+     * visibility settings, and an {@link AndroidVOverlayProto} containing extended visibility
+     * settings.
+     *
+     * <p>This constructor is still needed until we don't treat Visibility related documents as
+     * {@link GenericDocument}s internally.
+     *
+     * @param visibilityDocument   a {@link GenericDocument} holding all visibility properties
+     *                             other than publiclyVisibleTargetPackage.
+     * @param androidVOverlayProto the proto containing post-V visibility settings
+     */
+    @NonNull
+    private static SchemaVisibilityConfig createVisibilityConfig(
+            @NonNull GenericDocument visibilityDocument,
+            @Nullable AndroidVOverlayProto androidVOverlayProto) {
+        Objects.requireNonNull(visibilityDocument);
+
+        // Pre-V visibility settings come from visibilityDocument
+        SchemaVisibilityConfig.Builder builder = new SchemaVisibilityConfig.Builder();
+
+        String[] visibleToPackageNames =
+                visibilityDocument.getPropertyStringArray(VISIBLE_TO_PACKAGE_IDENTIFIER_PROPERTY);
+        byte[][] visibleToPackageShaCerts =
+                visibilityDocument.getPropertyBytesArray(VISIBLE_TO_PACKAGE_SHA_256_CERT_PROPERTY);
+        if (visibleToPackageNames != null && visibleToPackageShaCerts != null) {
+            for (int i = 0; i < visibleToPackageNames.length; i++) {
+                builder.addAllowedPackage(
+                        new PackageIdentifier(
+                                visibleToPackageNames[i], visibleToPackageShaCerts[i]));
+            }
+        }
+
+        GenericDocument[] visibleToPermissionDocs =
+                visibilityDocument.getPropertyDocumentArray(PERMISSION_PROPERTY);
+        if (visibleToPermissionDocs != null) {
+            for (int i = 0; i < visibleToPermissionDocs.length; ++i) {
+                long[] visibleToPermissionLongs = visibleToPermissionDocs[i].getPropertyLongArray(
+                        VisibilityPermissionConfig.ALL_REQUIRED_PERMISSIONS_PROPERTY);
+                if (visibleToPermissionLongs != null) {
+                    Set<Integer> allRequiredPermissions =
+                            new ArraySet<>(visibleToPermissionLongs.length);
+                    for (int j = 0; j < visibleToPermissionLongs.length; j++) {
+                        allRequiredPermissions.add((int) visibleToPermissionLongs[j]);
+                    }
+                    builder.addRequiredPermissions(allRequiredPermissions);
+                }
+            }
+        }
+
+        // Post-V visibility settings come from androidVOverlayProto
+        if (androidVOverlayProto != null) {
+            SchemaVisibilityConfig androidVOverlayConfig =
+                    convertVisibilityConfigFromProto(
+                            androidVOverlayProto.getVisibilityConfig());
+            builder.setPubliclyVisibleTargetPackage(
+                    androidVOverlayConfig.getPubliclyVisibleTargetPackage());
+        }
+
+        return builder.build();
+    }
+
+    @NonNull
+    private static SchemaVisibilityConfig convertVisibilityConfigFromProto(
+            @NonNull VisibilityConfigProto proto) {
+        SchemaVisibilityConfig.Builder builder = new SchemaVisibilityConfig.Builder();
+
+        List<PackageIdentifierProto> visibleToPackageProtoList = proto.getVisibleToPackagesList();
+        for (int i = 0; i < visibleToPackageProtoList.size(); i++) {
+            PackageIdentifierProto visibleToPackage = proto.getVisibleToPackages(i);
+            builder.addAllowedPackage(convertPackageIdentifierFromProto(visibleToPackage));
+        }
+
+        List<VisibleToPermissionProto> visibleToPermissionProtoList =
+                proto.getVisibleToPermissionsList();
+        for (int i = 0; i < visibleToPermissionProtoList.size(); i++) {
+            VisibleToPermissionProto visibleToPermissionProto = visibleToPermissionProtoList.get(i);
+            Set<Integer> visibleToPermissions =
+                    new ArraySet<>(visibleToPermissionProto.getPermissionsList());
+            builder.addRequiredPermissions(visibleToPermissions);
+        }
+
+        if (proto.hasPubliclyVisibleTargetPackage()) {
+            PackageIdentifierProto publiclyVisibleTargetPackage =
+                    proto.getPubliclyVisibleTargetPackage();
+            builder.setPubliclyVisibleTargetPackage(
+                    convertPackageIdentifierFromProto(publiclyVisibleTargetPackage));
+        }
+
+        return builder.build();
+    }
+
+    private static VisibilityConfigProto convertSchemaVisibilityConfigToProto(
+            @NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
+        VisibilityConfigProto.Builder builder = VisibilityConfigProto.newBuilder();
+
+        List<PackageIdentifier> visibleToPackages = schemaVisibilityConfig.getAllowedPackages();
+        for (int i = 0; i < visibleToPackages.size(); i++) {
+            PackageIdentifier visibleToPackage = visibleToPackages.get(i);
+            builder.addVisibleToPackages(convertPackageIdentifierToProto(visibleToPackage));
+        }
+
+        Set<Set<Integer>> visibleToPermissions = schemaVisibilityConfig.getRequiredPermissions();
+        if (!visibleToPermissions.isEmpty()) {
+            for (Set<Integer> allRequiredPermissions : visibleToPermissions) {
+                builder.addVisibleToPermissions(
+                        VisibleToPermissionProto.newBuilder()
+                                .addAllPermissions(allRequiredPermissions));
+            }
+        }
+
+        PackageIdentifier publicAclPackage =
+                schemaVisibilityConfig.getPubliclyVisibleTargetPackage();
+        if (publicAclPackage != null) {
+            builder.setPubliclyVisibleTargetPackage(
+                    convertPackageIdentifierToProto(publicAclPackage));
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Returns the {@link GenericDocument} for the visibility schema.
+     *
+     * @param config the configuration to populate into the document
+     */
+    @NonNull
+    public static GenericDocument createVisibilityDocument(
+            @NonNull InternalVisibilityConfig config) {
+        GenericDocument.Builder<?> builder = new GenericDocument.Builder<>(
+                VISIBILITY_DOCUMENT_NAMESPACE,
+                config.getSchemaType(), // We are using the prefixedSchemaType to be the id
+                VISIBILITY_DOCUMENT_SCHEMA_TYPE);
+        builder.setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY,
+                config.isNotDisplayedBySystem());
+        SchemaVisibilityConfig schemaVisibilityConfig = config.getVisibilityConfig();
+        List<PackageIdentifier> visibleToPackages = schemaVisibilityConfig.getAllowedPackages();
+        String[] visibleToPackageNames = new String[visibleToPackages.size()];
+        byte[][] visibleToPackageSha256Certs = new byte[visibleToPackages.size()][32];
+        for (int i = 0; i < visibleToPackages.size(); i++) {
+            visibleToPackageNames[i] = visibleToPackages.get(i).getPackageName();
+            visibleToPackageSha256Certs[i] = visibleToPackages.get(i).getSha256Certificate();
+        }
+        builder.setPropertyString(VISIBLE_TO_PACKAGE_IDENTIFIER_PROPERTY, visibleToPackageNames);
+        builder.setPropertyBytes(VISIBLE_TO_PACKAGE_SHA_256_CERT_PROPERTY,
+                visibleToPackageSha256Certs);
+
+        // Generate an array of GenericDocument for VisibilityPermissionConfig.
+        Set<Set<Integer>> visibleToPermissions = schemaVisibilityConfig.getRequiredPermissions();
+        if (!visibleToPermissions.isEmpty()) {
+            GenericDocument[] permissionGenericDocs =
+                    new GenericDocument[visibleToPermissions.size()];
+            int i = 0;
+            for (Set<Integer> allRequiredPermissions : visibleToPermissions) {
+                VisibilityPermissionConfig permissionDocument =
+                        new VisibilityPermissionConfig(allRequiredPermissions);
+                permissionGenericDocs[i++] = permissionDocument.toGenericDocument();
+            }
+            builder.setPropertyDocument(PERMISSION_PROPERTY, permissionGenericDocs);
+        }
+
+        // The creationTimestamp doesn't matter for Visibility documents.
+        // But to make tests pass, we set it 0 so two GenericDocuments generated from
+        // the same VisibilityConfig can be same.
+        builder.setCreationTimestampMillis(0L);
+
+        return builder.build();
+    }
+
+    /**
+     * Returns the {@link GenericDocument} for the Android V overlay schema if it is provided,
+     * null otherwise.
+     */
+    @Nullable
+    public static GenericDocument createAndroidVOverlay(
+            @NonNull InternalVisibilityConfig internalVisibilityConfig) {
+        PackageIdentifier publiclyVisibleTargetPackage =
+                internalVisibilityConfig.getVisibilityConfig().getPubliclyVisibleTargetPackage();
+        Set<SchemaVisibilityConfig> visibleToConfigs =
+                internalVisibilityConfig.getVisibleToConfigs();
+        if (publiclyVisibleTargetPackage == null && visibleToConfigs.isEmpty()) {
+            // This config doesn't contains any Android V overlay settings
+            return null;
+        }
+
+        VisibilityConfigProto.Builder visibilityConfigProtoBuilder =
+                VisibilityConfigProto.newBuilder();
+        // Set publicAcl
+        if (publiclyVisibleTargetPackage != null) {
+            visibilityConfigProtoBuilder.setPubliclyVisibleTargetPackage(
+                    convertPackageIdentifierToProto(publiclyVisibleTargetPackage));
+        }
+
+        // Set visibleToConfigs
+        AndroidVOverlayProto.Builder androidVOverlayProtoBuilder =
+                AndroidVOverlayProto.newBuilder().setVisibilityConfig(visibilityConfigProtoBuilder);
+        if (!visibleToConfigs.isEmpty()) {
+            for (SchemaVisibilityConfig visibleToConfig : visibleToConfigs) {
+                VisibilityConfigProto visibleToConfigProto =
+                        convertSchemaVisibilityConfigToProto(visibleToConfig);
+                androidVOverlayProtoBuilder.addVisibleToConfigs(visibleToConfigProto);
+            }
+        }
+
+        GenericDocument.Builder<?> androidVOverlayBuilder = new GenericDocument.Builder<>(
+                ANDROID_V_OVERLAY_NAMESPACE,
+                internalVisibilityConfig.getSchemaType(),
+                ANDROID_V_OVERLAY_SCHEMA_TYPE)
+                .setPropertyBytes(
+                        VISIBILITY_PROTO_SERIALIZE_PROPERTY,
+                        androidVOverlayProtoBuilder.build().toByteArray());
+
+        // The creationTimestamp doesn't matter for Visibility documents.
+        // But to make tests pass, we set it 0 so two GenericDocuments generated from
+        // the same VisibilityConfig can be same.
+        androidVOverlayBuilder.setCreationTimestampMillis(0L);
+
+        return androidVOverlayBuilder.build();
+    }
+
+    @NonNull
+    private static PackageIdentifierProto convertPackageIdentifierToProto(
+            @NonNull PackageIdentifier packageIdentifier) {
+        return PackageIdentifierProto.newBuilder()
+                .setPackageName(packageIdentifier.getPackageName())
+                .setPackageSha256Cert(ByteString.copyFrom(packageIdentifier.getSha256Certificate()))
+                .build();
+    }
+
+    @NonNull
+    private static PackageIdentifier convertPackageIdentifierFromProto(
+            @NonNull PackageIdentifierProto packageIdentifierProto) {
+        return new PackageIdentifier(
+                packageIdentifierProto.getPackageName(),
+                packageIdentifierProto.getPackageSha256Cert().toByteArray());
+    }
+
+    private VisibilityToDocumentConverter() {}
+}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityUtil.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityUtil.java
index 69d6a9a..075f44e 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityUtil.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityUtil.java
@@ -54,8 +54,11 @@
         Preconditions.checkNotNull(targetPackageName);
         Preconditions.checkNotNull(prefixedSchema);
 
-        if (callerAccess.getCallingPackageName().equals(targetPackageName)) {
-            return true;  // Everyone is always allowed to retrieve their own data.
+        // If the caller is allowed default access to its own data, check if the calling package
+        // and the target package are the same.
+        if (callerAccess.doesCallerHaveSelfAccess()
+                && callerAccess.getCallingPackageName().equals(targetPackageName)) {
+            return true;   // Caller is allowed to retrieve its own data.
         }
         if (visibilityStore == null || visibilityChecker == null) {
             return false;  // No visibility is configured at this time; no other access possible.
diff --git a/appsearch/appsearch-platform-storage/api/current.txt b/appsearch/appsearch-platform-storage/api/current.txt
index 29bfa55..ae1a294 100644
--- a/appsearch/appsearch-platform-storage/api/current.txt
+++ b/appsearch/appsearch-platform-storage/api/current.txt
@@ -2,6 +2,7 @@
 package androidx.appsearch.platformstorage {
 
   @RequiresApi(android.os.Build.VERSION_CODES.S) public final class PlatformStorage {
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.EnterpriseGlobalSearchSession!> createEnterpriseGlobalSearchSessionAsync(androidx.appsearch.platformstorage.PlatformStorage.GlobalSearchContext);
     method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.GlobalSearchSession!> createGlobalSearchSessionAsync(androidx.appsearch.platformstorage.PlatformStorage.GlobalSearchContext);
     method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.AppSearchSession!> createSearchSessionAsync(androidx.appsearch.platformstorage.PlatformStorage.SearchContext);
   }
diff --git a/appsearch/appsearch-platform-storage/api/restricted_current.txt b/appsearch/appsearch-platform-storage/api/restricted_current.txt
index 29bfa55..ae1a294 100644
--- a/appsearch/appsearch-platform-storage/api/restricted_current.txt
+++ b/appsearch/appsearch-platform-storage/api/restricted_current.txt
@@ -2,6 +2,7 @@
 package androidx.appsearch.platformstorage {
 
   @RequiresApi(android.os.Build.VERSION_CODES.S) public final class PlatformStorage {
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.EnterpriseGlobalSearchSession!> createEnterpriseGlobalSearchSessionAsync(androidx.appsearch.platformstorage.PlatformStorage.GlobalSearchContext);
     method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.GlobalSearchSession!> createGlobalSearchSessionAsync(androidx.appsearch.platformstorage.PlatformStorage.GlobalSearchContext);
     method public static com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.AppSearchSession!> createSearchSessionAsync(androidx.appsearch.platformstorage.PlatformStorage.SearchContext);
   }
diff --git a/appsearch/appsearch-platform-storage/build.gradle b/appsearch/appsearch-platform-storage/build.gradle
index 2009937..a0110fc 100644
--- a/appsearch/appsearch-platform-storage/build.gradle
+++ b/appsearch/appsearch-platform-storage/build.gradle
@@ -29,12 +29,12 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
-    implementation project(":appsearch:appsearch")
+    implementation(project(":appsearch:appsearch"))
     implementation('androidx.collection:collection:1.2.0')
-    implementation("androidx.concurrent:concurrent-futures:1.0.0")
-    implementation("androidx.core:core:1.1.0")
+    implementation('androidx.concurrent:concurrent-futures:1.0.0')
+    implementation('androidx.core:core:1.0.0')
 
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRules)
@@ -48,9 +48,9 @@
     inceptionYear = "2021"
     description =
         "An implementation of AppSearchSession which uses the AppSearch service on Android S+"
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk = 35
     namespace "androidx.appsearch.platformstorage"
 }
diff --git a/appsearch/appsearch-platform-storage/src/androidTest/java/androidx/appsearch/platformstorage/util/SchemaValidationUtilTest.java b/appsearch/appsearch-platform-storage/src/androidTest/java/androidx/appsearch/platformstorage/util/SchemaValidationUtilTest.java
new file mode 100644
index 0000000..0e551e4
--- /dev/null
+++ b/appsearch/appsearch-platform-storage/src/androidTest/java/androidx/appsearch/platformstorage/util/SchemaValidationUtilTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.platformstorage.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.AppSearchSchema.BooleanPropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.BytesPropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.LongPropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+import androidx.appsearch.exceptions.IllegalSchemaException;
+import androidx.collection.ArraySet;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class SchemaValidationUtilTest {
+    static final int MAX_SECTIONS_ALLOWED = 64;
+
+    @Test
+    public void testValidate_simpleSchemas() {
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("subject")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new BooleanPropertyConfig.Builder("boolProperty")
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("body")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
+                .addProperty(new StringPropertyConfig.Builder("name")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new LongPropertyConfig.Builder("age")
+                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
+                        .build()
+                ).addProperty(new BytesPropertyConfig.Builder("byteProperty")
+                        .build()
+                ).build();
+
+        AppSearchSchema[] schemas = {emailSchema, personSchema};
+        // Test that schemas are valid and no exceptions are thrown
+        SchemaValidationUtil.checkSchemasAreValidOrThrow(new ArraySet<>(schemas),
+                MAX_SECTIONS_ALLOWED);
+    }
+
+    @Test
+    public void testValidate_nestedSchemas() {
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("subject")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("org", "Organization")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("body")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("sender", "Person")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("recipient", "Person")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).build();
+
+        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
+                .addProperty(new StringPropertyConfig.Builder("name")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("nickname")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new LongPropertyConfig.Builder("age")
+                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("address", "Address")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).build();
+
+        AppSearchSchema addressSchema = new AppSearchSchema.Builder("Address")
+                .addProperty(new StringPropertyConfig.Builder("streetName")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new LongPropertyConfig.Builder("zipcode")
+                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
+                        .build()
+                ).build();
+
+        AppSearchSchema orgSchema = new AppSearchSchema.Builder("Organization")
+                .addProperty(new StringPropertyConfig.Builder("name")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("address", "Address")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).build();
+
+        AppSearchSchema[] schemas = {emailSchema, personSchema, addressSchema, orgSchema};
+        // Test that schemas are valid and no exceptions are thrown
+        SchemaValidationUtil.checkSchemasAreValidOrThrow(new ArraySet<>(schemas),
+                MAX_SECTIONS_ALLOWED);
+    }
+
+    @Test
+    public void testValidate_schemasWithValidCycle() {
+        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
+                .addProperty(new StringPropertyConfig.Builder("name")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("nickname")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("address", "Address")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new LongPropertyConfig.Builder("age")
+                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
+                        .setShouldIndexNestedProperties(false)
+                        .build()
+                ).build();
+
+        AppSearchSchema orgSchema = new AppSearchSchema.Builder("Organization")
+                .addProperty(new DocumentPropertyConfig.Builder("funder", "Person")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("address", "Address")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("employees", "Person")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("name")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        AppSearchSchema addressSchema = new AppSearchSchema.Builder("Address")
+                .addProperty(new StringPropertyConfig.Builder("streetName")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new LongPropertyConfig.Builder("zipcode")
+                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
+                        .build()
+                ).build();
+
+        AppSearchSchema[] schemas = {personSchema, orgSchema, addressSchema};
+        // Test that schemas are valid and no exceptions are thrown
+        SchemaValidationUtil.checkSchemasAreValidOrThrow(new ArraySet<>(schemas),
+                MAX_SECTIONS_ALLOWED);
+    }
+
+    @Test
+    public void testValidate_maxSections() {
+        AppSearchSchema.Builder personSchemaBuilder = new AppSearchSchema.Builder("Person");
+        for (int i = 0; i < MAX_SECTIONS_ALLOWED; i++) {
+            personSchemaBuilder.addProperty(new StringPropertyConfig.Builder("string" + i)
+                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .build());
+        }
+        Set<AppSearchSchema> schemas = new ArraySet<>();
+        schemas.add(personSchemaBuilder.build());
+        // Test that schemas are valid and no exceptions are thrown
+        SchemaValidationUtil.checkSchemasAreValidOrThrow(schemas, MAX_SECTIONS_ALLOWED);
+
+        // Add one more property to bring the number of sections over the max limit
+        personSchemaBuilder.addProperty(new StringPropertyConfig.Builder(
+                "string" + MAX_SECTIONS_ALLOWED + 1)
+                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                .build());
+        schemas.clear();
+        schemas.add(personSchemaBuilder.build());
+        IllegalSchemaException exception = assertThrows(IllegalSchemaException.class,
+                () -> SchemaValidationUtil.checkSchemasAreValidOrThrow(schemas,
+                        MAX_SECTIONS_ALLOWED));
+        assertThat(exception.getMessage()).contains("Too many properties to be indexed");
+    }
+
+    @Test
+    public void testValidate_schemasWithInvalidCycleThrowsError() {
+        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
+                .addProperty(new StringPropertyConfig.Builder("name")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("nickname")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                        .build()
+                ).addProperty(new LongPropertyConfig.Builder("age")
+                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).build();
+
+        AppSearchSchema orgSchema = new AppSearchSchema.Builder("Organization")
+                .addProperty(new DocumentPropertyConfig.Builder("funder", "Person")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("employees", "Person")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).build();
+
+        AppSearchSchema[] schemas = {personSchema, orgSchema};
+        IllegalSchemaException exception = assertThrows(IllegalSchemaException.class,
+                () -> SchemaValidationUtil.checkSchemasAreValidOrThrow(new ArraySet<>(schemas),
+                        MAX_SECTIONS_ALLOWED));
+        assertThat(exception.getMessage()).contains("Invalid cycle");
+    }
+
+    @Test
+    public void testValidate_unknownDocumentConfigThrowsError() {
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("subject")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("body")
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("unknown", "Unknown")
+                        .setShouldIndexNestedProperties(true)
+                        .build()
+                ).addProperty(new DocumentPropertyConfig.Builder("unknown2", "Unknown")
+                        .setShouldIndexNestedProperties(false)
+                        .build()
+                ).build();
+
+        AppSearchSchema[] schemas = {emailSchema};
+        IllegalSchemaException exception = assertThrows(IllegalSchemaException.class,
+                () -> SchemaValidationUtil.checkSchemasAreValidOrThrow(new ArraySet<>(schemas),
+                        MAX_SECTIONS_ALLOWED));
+        assertThat(exception.getMessage()).contains("Undefined schema type");
+    }
+}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/EnterpriseGlobalSearchSessionImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/EnterpriseGlobalSearchSessionImpl.java
new file mode 100644
index 0000000..ac05d09
--- /dev/null
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/EnterpriseGlobalSearchSessionImpl.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.platformstorage;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchBatchResult;
+import androidx.appsearch.app.EnterpriseGlobalSearchSession;
+import androidx.appsearch.app.Features;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.GetByDocumentIdRequest;
+import androidx.appsearch.app.GetSchemaResponse;
+import androidx.appsearch.app.SearchResults;
+import androidx.appsearch.app.SearchSpec;
+import androidx.appsearch.platformstorage.converter.AppSearchResultToPlatformConverter;
+import androidx.appsearch.platformstorage.converter.GenericDocumentToPlatformConverter;
+import androidx.appsearch.platformstorage.converter.GetSchemaResponseToPlatformConverter;
+import androidx.appsearch.platformstorage.converter.RequestToPlatformConverter;
+import androidx.appsearch.platformstorage.converter.SearchSpecToPlatformConverter;
+import androidx.appsearch.platformstorage.util.BatchResultCallbackAdapter;
+import androidx.concurrent.futures.ResolvableFuture;
+import androidx.core.util.Preconditions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.Executor;
+
+/**
+ * An implementation of {@link EnterpriseGlobalSearchSession} which proxies to a
+ * platform {@link android.app.appsearch.EnterpriseGlobalSearchSession}.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+class EnterpriseGlobalSearchSessionImpl implements EnterpriseGlobalSearchSession {
+    private final android.app.appsearch.EnterpriseGlobalSearchSession mPlatformSession;
+    private final Executor mExecutor;
+    private final Features mFeatures;
+
+    EnterpriseGlobalSearchSessionImpl(
+            @NonNull android.app.appsearch.EnterpriseGlobalSearchSession platformSession,
+            @NonNull Executor executor,
+            @NonNull Features features) {
+        mPlatformSession = Preconditions.checkNotNull(platformSession);
+        mExecutor = Preconditions.checkNotNull(executor);
+        mFeatures = Preconditions.checkNotNull(features);
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
+            @NonNull String packageName, @NonNull String databaseName,
+            @NonNull GetByDocumentIdRequest request) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(request);
+        ResolvableFuture<AppSearchBatchResult<String, GenericDocument>> future =
+                ResolvableFuture.create();
+        mPlatformSession.getByDocumentId(packageName, databaseName,
+                RequestToPlatformConverter.toPlatformGetByDocumentIdRequest(request), mExecutor,
+                new BatchResultCallbackAdapter<>(future,
+                        GenericDocumentToPlatformConverter::toJetpackGenericDocument));
+        return future;
+    }
+
+    @Override
+    @NonNull
+    public SearchResults search(
+            @NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec) {
+        Preconditions.checkNotNull(queryExpression);
+        Preconditions.checkNotNull(searchSpec);
+        android.app.appsearch.SearchResults platformSearchResults =
+                mPlatformSession.search(
+                        queryExpression,
+                        SearchSpecToPlatformConverter.toPlatformSearchSpec(searchSpec));
+        return new SearchResultsImpl(platformSearchResults, searchSpec, mExecutor);
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<GetSchemaResponse> getSchemaAsync(@NonNull String packageName,
+            @NonNull String databaseName) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(databaseName);
+        ResolvableFuture<GetSchemaResponse> future = ResolvableFuture.create();
+        mPlatformSession.getSchema(packageName, databaseName, mExecutor,
+                result -> AppSearchResultToPlatformConverter.platformAppSearchResultToFuture(result,
+                        future, GetSchemaResponseToPlatformConverter::toJetpackGetSchemaResponse));
+        return future;
+    }
+
+    @NonNull
+    @Override
+    public Features getFeatures() {
+        return mFeatures;
+    }
+}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
index 4d7b3f3..aa0b554 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
@@ -16,15 +16,11 @@
 package androidx.appsearch.platformstorage;
 
 import android.content.Context;
-import android.content.pm.ModuleInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
 import androidx.appsearch.app.Features;
+import androidx.appsearch.platformstorage.util.AppSearchVersionUtil;
 import androidx.core.util.Preconditions;
 
 /**
@@ -32,12 +28,6 @@
  * level.
  */
 final class FeaturesImpl implements Features {
-    private static final String APPSEARCH_MODULE_NAME = "com.android.appsearch";
-
-    // This will be set to -1 to indicate the AppSearch version code hasn't bee checked, then to
-    // 0 if it is not found, or the version code if it is found.
-    private static volatile long sAppSearchVersionCode = -1;
-
     // Context is used to check mainline module version, as support varies by module version.
     private final Context mContext;
 
@@ -80,23 +70,39 @@
             case Features.SET_SCHEMA_CIRCULAR_REFERENCES:
                 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
-            // Beyond Android U features
+            // Android V Features
             case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
-                // TODO(b/258715421) : Update to reflect support in Android U+ once this feature has
-                // an extservices sdk that includes it.
-                // fall through
-            case Features.SCHEMA_SET_DELETION_PROPAGATION:
-                // TODO(b/268521214) : Update when feature is ready in service-appsearch.
                 // fall through
             case Features.SCHEMA_ADD_PARENT_TYPE:
-                // TODO(b/269295094) : Update when feature is ready in service-appsearch.
                 // fall through
             case Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES:
-                // TODO(b/289150947) : Update when feature is ready in service-appsearch.
                 // fall through
             case Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES:
-                // TODO(b/296088047) : Update when feature is ready in service-appsearch.
-                return false;
+                // fall through
+            case Features.LIST_FILTER_HAS_PROPERTY_FUNCTION:
+                // fall through
+            case Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG:
+                // fall through
+            case Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE:
+                // fall through
+            case Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG:
+                // fall through
+            case Features.ENTERPRISE_GLOBAL_SEARCH_SESSION:
+                return Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
+
+            // Beyond Android V Features
+            case Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG:
+                // TODO(b/326656531) : Update when feature is ready in service-appsearch.
+                // fall through
+            case Features.SCHEMA_SET_DESCRIPTION:
+                // TODO(b/326987971) : Update when feature is ready in service-appsearch.
+                // fall through
+            case Features.LIST_FILTER_TOKENIZE_FUNCTION:
+                // TODO(b/332620561) : Update when feature is ready in service-appsearch.
+                // fall through
+            case Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS:
+                // TODO(b/332642571) : Update when feature is ready in service-appsearch.
+                // fall through
             default:
                 return false;
         }
@@ -107,53 +113,11 @@
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
             return 64;
         } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.TIRAMISU) {
-            // Sixty-four properties were enabled in mainline module 'aml_ase_331311020'
-            return getAppSearchVersionCode(mContext) >= 331311020 ? 64 : 16;
+            // Sixty-four properties were enabled in mainline module of the U base version
+            return AppSearchVersionUtil.getAppSearchVersionCode(mContext)
+                    >= AppSearchVersionUtil.APPSEARCH_U_BASE_VERSION_CODE ? 64 : 16;
         } else {
             return 16;
         }
     }
-
-    @RequiresApi(Build.VERSION_CODES.Q)
-    private static long getAppSearchVersionCode(Context context) {
-        if (sAppSearchVersionCode != -1) {
-            return sAppSearchVersionCode;
-        }
-        synchronized (FeaturesImpl.class) {
-            // Check again in case it was assigned while waiting
-            if (sAppSearchVersionCode == -1) {
-                long appsearchVersionCode = 0;
-                try {
-                    PackageManager packageManager = context.getPackageManager();
-                    String appSearchPackageName =
-                            ApiHelperForQ.getAppSearchPackageName(packageManager);
-                    if (appSearchPackageName != null) {
-                        PackageInfo pInfo = packageManager
-                                .getPackageInfo(appSearchPackageName, PackageManager.MATCH_APEX);
-                        appsearchVersionCode = ApiHelperForQ.getPackageInfoLongVersionCode(pInfo);
-                    }
-                } catch (PackageManager.NameNotFoundException e) {
-                    // Module not installed
-                }
-                sAppSearchVersionCode = appsearchVersionCode;
-            }
-        }
-        return sAppSearchVersionCode;
-    }
-
-    @RequiresApi(Build.VERSION_CODES.Q)
-    private static class ApiHelperForQ {
-        @DoNotInline
-        static long getPackageInfoLongVersionCode(PackageInfo pInfo) {
-            return pInfo.getLongVersionCode();
-        }
-
-        @DoNotInline
-        static String getAppSearchPackageName(PackageManager packageManager)
-                throws PackageManager.NameNotFoundException {
-            ModuleInfo appSearchModule =
-                    packageManager.getModuleInfo(APPSEARCH_MODULE_NAME, 1);
-            return appSearchModule.getPackageName();
-        }
-    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java
index c1ca5d4..ba65543 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java
@@ -20,9 +20,13 @@
 import android.content.Context;
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.appsearch.app.AppSearchEnvironmentFactory;
 import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.EnterpriseGlobalSearchSession;
+import androidx.appsearch.app.Features;
 import androidx.appsearch.app.GlobalSearchSession;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.platformstorage.converter.SearchContextToPlatformConverter;
@@ -33,7 +37,7 @@
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.function.Consumer;
 
 /**
  * An AppSearch storage system which stores data in the central AppSearch service, available on
@@ -201,7 +205,8 @@
     // execute() won't return anything, we will hang forever waiting for the execution.
     // AppSearch multi-thread execution is guarded by Read & Write Lock in AppSearchImpl, all
     // mutate requests will need to gain write lock and query requests need to gain read lock.
-    static final Executor EXECUTOR = Executors.newCachedThreadPool();
+    static final Executor EXECUTOR = AppSearchEnvironmentFactory.getEnvironmentInstance()
+            .createCachedThreadPoolExecutor();
 
     /**
      * Opens a new {@link AppSearchSession} on this storage.
@@ -224,7 +229,7 @@
                     if (result.isSuccess()) {
                         future.set(
                                 new SearchSessionImpl(result.getResultValue(), context.mExecutor,
-                                        new FeaturesImpl(context.mContext)));
+                                        context.mContext));
                     } else {
                         // Without the SuppressLint annotation on the method, this line causes a
                         // lint error because getResultCode isn't defined as returning a value from
@@ -266,4 +271,56 @@
                 });
         return future;
     }
+
+    /**
+     * Opens a new {@link EnterpriseGlobalSearchSession} on this storage.
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @SuppressLint("WrongConstant")
+    @NonNull
+    public static ListenableFuture<EnterpriseGlobalSearchSession>
+            createEnterpriseGlobalSearchSessionAsync(@NonNull GlobalSearchContext context) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            throw new UnsupportedOperationException(
+                    Features.ENTERPRISE_GLOBAL_SEARCH_SESSION
+                            + " is not supported on this AppSearch implementation");
+        }
+        Preconditions.checkNotNull(context);
+        AppSearchManager appSearchManager =
+                context.mContext.getSystemService(AppSearchManager.class);
+        ResolvableFuture<EnterpriseGlobalSearchSession> future = ResolvableFuture.create();
+        ApiHelperForV.createEnterpriseGlobalSearchSession(
+                appSearchManager,
+                context.mExecutor,
+                result -> {
+                    if (result.isSuccess()) {
+                        future.set(new EnterpriseGlobalSearchSessionImpl(
+                                result.getResultValue(), context.mExecutor,
+                                new FeaturesImpl(context.mContext)));
+                    } else {
+                        // Without the SuppressLint annotation on the method, this line causes a
+                        // lint error because getResultCode isn't defined as returning a value from
+                        // AppSearchResult.ResultCode
+                        future.setException(
+                                new AppSearchException(
+                                        result.getResultCode(), result.getErrorMessage()));
+                    }
+                });
+        return future;
+    }
+
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class ApiHelperForV {
+        private ApiHelperForV() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static void createEnterpriseGlobalSearchSession(@NonNull AppSearchManager appSearchManager,
+                @NonNull Executor executor,
+                @NonNull Consumer<android.app.appsearch.AppSearchResult<
+                        android.app.appsearch.EnterpriseGlobalSearchSession>> callback) {
+            appSearchManager.createEnterpriseGlobalSearchSession(executor, callback);
+        }
+    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java
index d0514b0..92cbd50 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/SearchSessionImpl.java
@@ -15,8 +15,11 @@
  */
 package androidx.appsearch.platformstorage;
 
+import static androidx.appsearch.platformstorage.util.SchemaValidationUtil.checkSchemasAreValidOrThrow;
+
 import android.annotation.SuppressLint;
 import android.app.appsearch.AppSearchResult;
+import android.content.Context;
 import android.os.Build;
 
 import androidx.annotation.DoNotInline;
@@ -40,6 +43,7 @@
 import androidx.appsearch.app.SetSchemaResponse;
 import androidx.appsearch.app.StorageInfo;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.exceptions.IllegalSchemaException;
 import androidx.appsearch.platformstorage.converter.AppSearchResultToPlatformConverter;
 import androidx.appsearch.platformstorage.converter.GenericDocumentToPlatformConverter;
 import androidx.appsearch.platformstorage.converter.GetSchemaResponseToPlatformConverter;
@@ -49,6 +53,7 @@
 import androidx.appsearch.platformstorage.converter.SearchSuggestionResultToPlatformConverter;
 import androidx.appsearch.platformstorage.converter.SearchSuggestionSpecToPlatformConverter;
 import androidx.appsearch.platformstorage.converter.SetSchemaRequestToPlatformConverter;
+import androidx.appsearch.platformstorage.util.AppSearchVersionUtil;
 import androidx.appsearch.platformstorage.util.BatchResultCallbackAdapter;
 import androidx.concurrent.futures.ResolvableFuture;
 import androidx.core.util.Preconditions;
@@ -70,15 +75,17 @@
 class SearchSessionImpl implements AppSearchSession {
     private final android.app.appsearch.AppSearchSession mPlatformSession;
     private final Executor mExecutor;
+    private final Context mContext;
     private final Features mFeatures;
 
     SearchSessionImpl(
             @NonNull android.app.appsearch.AppSearchSession platformSession,
             @NonNull Executor executor,
-            @NonNull Features features) {
+            @NonNull Context context) {
         mPlatformSession = Preconditions.checkNotNull(platformSession);
         mExecutor = Preconditions.checkNotNull(executor);
-        mFeatures = Preconditions.checkNotNull(features);
+        mContext = Preconditions.checkNotNull(context);
+        mFeatures = new FeaturesImpl(mContext);
     }
 
     @Override
@@ -86,6 +93,18 @@
     public ListenableFuture<SetSchemaResponse> setSchemaAsync(@NonNull SetSchemaRequest request) {
         Preconditions.checkNotNull(request);
         ResolvableFuture<SetSchemaResponse> future = ResolvableFuture.create();
+        if (needsSchemaValidation()) {
+            try {
+                checkSchemasAreValidOrThrow(request.getSchemas(),
+                        getFeatures().getMaxIndexedProperties());
+            } catch (IllegalSchemaException e) {
+                future.setException(
+                        new AppSearchException(
+                                androidx.appsearch.app.AppSearchResult.RESULT_INVALID_ARGUMENT,
+                                e.getMessage()));
+                return future;
+            }
+        }
         mPlatformSession.setSchema(
                 SetSchemaRequestToPlatformConverter.toPlatformSetSchemaRequest(request),
                 mExecutor,
@@ -318,6 +337,17 @@
         mPlatformSession.close();
     }
 
+    private boolean needsSchemaValidation() {
+        long appsearchVersionCode = AppSearchVersionUtil.getAppSearchVersionCode(mContext);
+        // Due to b/300135897, we'd like to validate the schema before sending the setSchema
+        // request to IcingLib on some versions of AppSearch.
+        // For these versions, IcingLib and AppSearch would crash if we try to set an
+        // invalid schema where the number of sections in a schema type exceeds the maximum
+        // limit.
+        return appsearchVersionCode >= AppSearchVersionUtil.APPSEARCH_U_BASE_VERSION_CODE
+                && appsearchVersionCode < AppSearchVersionUtil.APPSEARCH_M2023_11_VERSION_CODE;
+    }
+
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     static class ApiHelperForU {
         private ApiHelperForU() {
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GenericDocumentToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GenericDocumentToPlatformConverter.java
index 3775ab7..01882d6 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GenericDocumentToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GenericDocumentToPlatformConverter.java
@@ -21,6 +21,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.EmbeddingVector;
+import androidx.appsearch.app.Features;
 import androidx.appsearch.app.GenericDocument;
 import androidx.core.util.Preconditions;
 
@@ -87,6 +89,10 @@
                     platformSubDocuments[j] = toPlatformGenericDocument(documentValues[j]);
                 }
                 platformBuilder.setPropertyDocument(propertyName, platformSubDocuments);
+            } else if (property instanceof EmbeddingVector[]) {
+                // TODO(b/326656531): Remove this once embedding search APIs are available.
+                throw new UnsupportedOperationException(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG
+                        + " is not available on this AppSearch implementation.");
             } else {
                 throw new IllegalStateException(
                         String.format("Property \"%s\" has unsupported value type %s", propertyName,
@@ -143,6 +149,8 @@
                 }
                 jetpackBuilder.setPropertyDocument(propertyName, jetpackSubDocuments);
             } else {
+                // TODO(b/326656531) : Add an entry for EmbeddingVector once it becomes
+                //  available in platform.
                 throw new IllegalStateException(
                         String.format("Property \"%s\" has unsupported value type %s", propertyName,
                                 property.getClass().toString()));
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java
index 2fb4a40..568e3db5 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/GetSchemaResponseToPlatformConverter.java
@@ -24,9 +24,13 @@
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
+import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -49,7 +53,7 @@
         Preconditions.checkNotNull(platformResponse);
         GetSchemaResponse.Builder jetpackBuilder = new GetSchemaResponse.Builder();
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
-            // Android API level in S-v2 and lower won't have any supported feature.
+            // Android API level in S-v2 and lower won't have visibility information.
             jetpackBuilder.setVisibilitySettingSupported(false);
         }
         for (android.app.appsearch.AppSearchSchema platformSchema : platformResponse.getSchemas()) {
@@ -72,6 +76,29 @@
                         entry.getValue());
             }
         }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Convert publicly visible schemas
+            Map<String, PackageIdentifier> publiclyVisibleSchemas =
+                    ApiHelperForV.getPubliclyVisibleSchemas(platformResponse);
+            if (!publiclyVisibleSchemas.isEmpty()) {
+                for (Map.Entry<String, PackageIdentifier> entry :
+                        publiclyVisibleSchemas.entrySet()) {
+                    jetpackBuilder.setPubliclyVisibleSchema(entry.getKey(), entry.getValue());
+                }
+            }
+
+            // Convert schemas visible to configs
+            Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs =
+                    ApiHelperForV.getSchemasVisibleToConfigs(platformResponse);
+            if (!schemasVisibleToConfigs.isEmpty()) {
+                for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry :
+                        schemasVisibleToConfigs.entrySet()) {
+                    jetpackBuilder.setSchemaTypeVisibleToConfigs(entry.getKey(), entry.getValue());
+                }
+            }
+        }
+
         return jetpackBuilder.build();
     }
 
@@ -130,4 +157,95 @@
             return platformResponse.getRequiredPermissionsForSchemaTypeVisibility();
         }
     }
+
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class ApiHelperForV {
+        private ApiHelperForV() {}
+
+        @DoNotInline
+        @NonNull
+        static Map<String, PackageIdentifier> getPubliclyVisibleSchemas(
+                android.app.appsearch.GetSchemaResponse platformResponse) {
+            Map<String, android.app.appsearch.PackageIdentifier> platformPubliclyVisibleSchemas =
+                    platformResponse.getPubliclyVisibleSchemas();
+            if (platformPubliclyVisibleSchemas.isEmpty()) {
+                return Collections.emptyMap();
+            }
+            Map<String, PackageIdentifier> jetpackPubliclyVisibleSchemas =
+                    new ArrayMap<>(platformPubliclyVisibleSchemas.size());
+            for (Map.Entry<String, android.app.appsearch.PackageIdentifier> entry :
+                    platformPubliclyVisibleSchemas.entrySet()) {
+                jetpackPubliclyVisibleSchemas.put(
+                        entry.getKey(),
+                        new PackageIdentifier(
+                                entry.getValue().getPackageName(),
+                                entry.getValue().getSha256Certificate()));
+            }
+            return jetpackPubliclyVisibleSchemas;
+        }
+
+        @DoNotInline
+        @NonNull
+        static Map<String, Set<SchemaVisibilityConfig>> getSchemasVisibleToConfigs(
+                android.app.appsearch.GetSchemaResponse platformResponse) {
+            Map<String, Set<android.app.appsearch.SchemaVisibilityConfig>>
+                    platformSchemasVisibleToConfigs =
+                    platformResponse.getSchemaTypesVisibleToConfigs();
+            if (platformSchemasVisibleToConfigs.isEmpty()) {
+                return Collections.emptyMap();
+            }
+            Map<String, Set<SchemaVisibilityConfig>> jetpackSchemasVisibleToConfigs =
+                    new ArrayMap<>(platformSchemasVisibleToConfigs.size());
+            for (Map.Entry<String, Set<android.app.appsearch.SchemaVisibilityConfig>> entry :
+                    platformSchemasVisibleToConfigs.entrySet()) {
+                Set<SchemaVisibilityConfig> jetpackConfigPerType =
+                        new ArraySet<>(entry.getValue().size());
+                for (android.app.appsearch.SchemaVisibilityConfig platformConfigPerType :
+                        entry.getValue()) {
+                    SchemaVisibilityConfig jetpackConfig =
+                            toJetpackSchemaVisibilityConfig(platformConfigPerType);
+                    jetpackConfigPerType.add(jetpackConfig);
+                }
+                jetpackSchemasVisibleToConfigs.put(entry.getKey(), jetpackConfigPerType);
+            }
+            return jetpackSchemasVisibleToConfigs;
+        }
+
+        /**
+         * Translates a platform {@link android.app.appsearch.SchemaVisibilityConfig} into a jetpack
+         * {@link SchemaVisibilityConfig}.
+         */
+        @NonNull
+        private static SchemaVisibilityConfig toJetpackSchemaVisibilityConfig(
+                @NonNull android.app.appsearch.SchemaVisibilityConfig platformConfig) {
+            Preconditions.checkNotNull(platformConfig);
+            SchemaVisibilityConfig.Builder jetpackBuilder = new SchemaVisibilityConfig.Builder();
+
+            // Translate allowedPackages
+            List<android.app.appsearch.PackageIdentifier> allowedPackages =
+                    platformConfig.getAllowedPackages();
+            for (int i = 0; i < allowedPackages.size(); i++) {
+                jetpackBuilder.addAllowedPackage(new PackageIdentifier(
+                        allowedPackages.get(i).getPackageName(),
+                        allowedPackages.get(i).getSha256Certificate()));
+            }
+
+            // Translate requiredPermissions
+            for (Set<Integer> requiredPermissions : platformConfig.getRequiredPermissions()) {
+                jetpackBuilder.addRequiredPermissions(requiredPermissions);
+            }
+
+            // Translate publiclyVisibleTargetPackage
+            android.app.appsearch.PackageIdentifier publiclyVisibleTargetPackage =
+                    platformConfig.getPubliclyVisibleTargetPackage();
+            if (publiclyVisibleTargetPackage != null) {
+                jetpackBuilder.setPubliclyVisibleTargetPackage(
+                        new PackageIdentifier(
+                                publiclyVisibleTargetPackage.getPackageName(),
+                                publiclyVisibleTargetPackage.getSha256Certificate()));
+            }
+
+            return jetpackBuilder.build();
+        }
+    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/RequestToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/RequestToPlatformConverter.java
index f4b2d43..814d8f2 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/RequestToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/RequestToPlatformConverter.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -51,10 +52,28 @@
         Preconditions.checkNotNull(jetpackRequest);
         android.app.appsearch.PutDocumentsRequest.Builder platformBuilder =
                 new android.app.appsearch.PutDocumentsRequest.Builder();
+        // Convert normal generic documents.
         for (GenericDocument jetpackDocument : jetpackRequest.getGenericDocuments()) {
             platformBuilder.addGenericDocuments(
                     GenericDocumentToPlatformConverter.toPlatformGenericDocument(jetpackDocument));
         }
+        // Convert taken action generic documents.
+        for (GenericDocument jetpackTakenActionGenericDocument :
+                jetpackRequest.getTakenActionGenericDocuments()) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                ApiHelperForV.addTakenActionGenericDocuments(
+                        platformBuilder,
+                        GenericDocumentToPlatformConverter.toPlatformGenericDocument(
+                                jetpackTakenActionGenericDocument));
+            } else {
+                // This version of platform-storage doesn't support the dedicated
+                // addTakenActionGenericDocuments API, but we can still add them to the index via
+                // the put API (just without logging).
+                platformBuilder.addGenericDocuments(
+                        GenericDocumentToPlatformConverter.toPlatformGenericDocument(
+                                jetpackTakenActionGenericDocument));
+            }
+        }
         return platformBuilder.build();
     }
 
@@ -71,7 +90,7 @@
                         jetpackRequest.getNamespace())
                         .addIds(jetpackRequest.getIds());
         for (Map.Entry<String, List<String>> projection :
-                jetpackRequest.getProjectionsInternal().entrySet()) {
+                jetpackRequest.getProjections().entrySet()) {
             platformBuilder.addProjection(projection.getKey(), projection.getValue());
         }
         return platformBuilder.build();
@@ -122,4 +141,24 @@
                 .setUsageTimestampMillis(jetpackRequest.getUsageTimestampMillis())
                 .build();
     }
+
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class ApiHelperForV {
+        private ApiHelperForV() {}
+
+        @DoNotInline
+        static void addTakenActionGenericDocuments(
+                android.app.appsearch.PutDocumentsRequest.Builder platformBuilder,
+                android.app.appsearch.GenericDocument platformTakenActionGenericDocument) {
+            try {
+                platformBuilder.addTakenActionGenericDocuments(platformTakenActionGenericDocument);
+            } catch (android.app.appsearch.exceptions.AppSearchException e) {
+                // This method incorrectly declares that it throws AppSearchException, whereas in
+                // fact there's nothing in its implementation that would do so. Suppress it here
+                // instead of piping all the way through the stack.
+                throw new RuntimeException(
+                        "Unexpected AppSearchException which should not be possible", e);
+            }
+        }
+    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java
index 5cf9946..4b00cab 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SchemaToPlatformConverter.java
@@ -30,6 +30,7 @@
 import androidx.appsearch.app.Features;
 import androidx.core.util.Preconditions;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -52,17 +53,27 @@
         Preconditions.checkNotNull(jetpackSchema);
         android.app.appsearch.AppSearchSchema.Builder platformBuilder =
                 new android.app.appsearch.AppSearchSchema.Builder(jetpackSchema.getSchemaType());
+        if (!jetpackSchema.getDescription().isEmpty()) {
+            // TODO(b/326987971): Remove this once description becomes available.
+            throw new UnsupportedOperationException(Features.SCHEMA_SET_DESCRIPTION
+                    + " is not available on this AppSearch implementation.");
+        }
+        if (!jetpackSchema.getParentTypes().isEmpty()) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                throw new UnsupportedOperationException(Features.SCHEMA_ADD_PARENT_TYPE
+                        + " is not available on this AppSearch implementation.");
+            }
+            List<String> parentTypes = jetpackSchema.getParentTypes();
+            for (int i = 0; i < parentTypes.size(); i++) {
+                ApiHelperForV.addParentType(platformBuilder, parentTypes.get(i));
+            }
+        }
         List<AppSearchSchema.PropertyConfig> properties = jetpackSchema.getProperties();
         for (int i = 0; i < properties.size(); i++) {
             android.app.appsearch.AppSearchSchema.PropertyConfig platformProperty =
                     toPlatformProperty(properties.get(i));
             platformBuilder.addProperty(platformProperty);
         }
-        if (!jetpackSchema.getParentTypes().isEmpty()) {
-            // TODO(b/269295094): Remove this once polymorphism becomes available.
-            throw new UnsupportedOperationException(Features.SCHEMA_ADD_PARENT_TYPE
-                    + " is not available on this AppSearch implementation.");
-        }
         return platformBuilder.build();
     }
 
@@ -78,12 +89,18 @@
                 new AppSearchSchema.Builder(platformSchema.getSchemaType());
         List<android.app.appsearch.AppSearchSchema.PropertyConfig> properties =
                 platformSchema.getProperties();
+        // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+        // available in platform.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            List<String> parentTypes = ApiHelperForV.getParentTypes(platformSchema);
+            for (int i = 0; i < parentTypes.size(); i++) {
+                jetpackBuilder.addParentType(parentTypes.get(i));
+            }
+        }
         for (int i = 0; i < properties.size(); i++) {
             AppSearchSchema.PropertyConfig jetpackProperty = toJetpackProperty(properties.get(i));
             jetpackBuilder.addProperty(jetpackProperty);
         }
-        // TODO(b/269295094): Call jetpackBuilder.addParentType() to add parent types once
-        //  polymorphism becomes available in platform.
         return jetpackBuilder.build();
     }
 
@@ -94,6 +111,11 @@
     private static android.app.appsearch.AppSearchSchema.PropertyConfig toPlatformProperty(
             @NonNull AppSearchSchema.PropertyConfig jetpackProperty) {
         Preconditions.checkNotNull(jetpackProperty);
+        if (!jetpackProperty.getDescription().isEmpty()) {
+            // TODO(b/326987971): Remove this once description becomes available.
+            throw new UnsupportedOperationException(Features.SCHEMA_SET_DESCRIPTION
+                    + " is not available on this AppSearch implementation.");
+        }
         if (jetpackProperty instanceof AppSearchSchema.StringPropertyConfig) {
             AppSearchSchema.StringPropertyConfig stringProperty =
                     (AppSearchSchema.StringPropertyConfig) jetpackProperty;
@@ -110,12 +132,6 @@
                         TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType");
             }
 
-            if (stringProperty.getDeletionPropagation()) {
-                // TODO(b/268521214): Update once deletion propagation is available.
-                throw new UnsupportedOperationException("Setting deletion propagation is not "
-                        + "supported on this AppSearch implementation.");
-            }
-
             if (stringProperty.getJoinableValueType()
                     == AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID) {
                 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -163,18 +179,26 @@
         } else if (jetpackProperty instanceof AppSearchSchema.DocumentPropertyConfig) {
             AppSearchSchema.DocumentPropertyConfig documentProperty =
                     (AppSearchSchema.DocumentPropertyConfig) jetpackProperty;
+            android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder platformBuilder =
+                    new android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder(
+                            documentProperty.getName(), documentProperty.getSchemaType())
+                            .setCardinality(documentProperty.getCardinality())
+                            .setShouldIndexNestedProperties(
+                                    documentProperty.shouldIndexNestedProperties());
             if (!documentProperty.getIndexableNestedProperties().isEmpty()) {
-                // TODO(b/289150947): Update and set list once indexable-nested-properties-list is
-                //  available.
-                throw new UnsupportedOperationException(
-                        "DocumentPropertyConfig.addIndexableNestedProperties is not supported on "
-                                + "this AppSearch implementation.");
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                    throw new UnsupportedOperationException(
+                            "DocumentPropertyConfig.addIndexableNestedProperties is not supported "
+                                    + "on this AppSearch implementation.");
+                }
+                ApiHelperForV.addIndexableNestedProperties(
+                        platformBuilder, documentProperty.getIndexableNestedProperties());
             }
-            return new android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder(
-                    documentProperty.getName(), documentProperty.getSchemaType())
-                    .setCardinality(documentProperty.getCardinality())
-                    .setShouldIndexNestedProperties(documentProperty.shouldIndexNestedProperties())
-                    .build();
+            return platformBuilder.build();
+        } else if (jetpackProperty instanceof AppSearchSchema.EmbeddingPropertyConfig) {
+            // TODO(b/326656531): Remove this once embedding search APIs are available.
+            throw new UnsupportedOperationException(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG
+                    + " is not available on this AppSearch implementation.");
         } else {
             throw new IllegalArgumentException(
                     "Invalid dataType: " + jetpackProperty.getDataType());
@@ -201,6 +225,8 @@
                 jetpackBuilder.setJoinableValueType(
                         ApiHelperForU.getJoinableValueType(stringProperty));
             }
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in platform.
             return jetpackBuilder.build();
         } else if (platformProperty
                 instanceof android.app.appsearch.AppSearchSchema.LongPropertyConfig) {
@@ -213,19 +239,27 @@
                 jetpackBuilder.setIndexingType(
                         ApiHelperForU.getIndexingType(longProperty));
             }
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in platform.
             return jetpackBuilder.build();
         } else if (platformProperty
                 instanceof android.app.appsearch.AppSearchSchema.DoublePropertyConfig) {
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in platform.
             return new AppSearchSchema.DoublePropertyConfig.Builder(platformProperty.getName())
                     .setCardinality(platformProperty.getCardinality())
                     .build();
         } else if (platformProperty
                 instanceof android.app.appsearch.AppSearchSchema.BooleanPropertyConfig) {
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in platform.
             return new AppSearchSchema.BooleanPropertyConfig.Builder(platformProperty.getName())
                     .setCardinality(platformProperty.getCardinality())
                     .build();
         } else if (platformProperty
                 instanceof android.app.appsearch.AppSearchSchema.BytesPropertyConfig) {
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in platform.
             return new AppSearchSchema.BytesPropertyConfig.Builder(platformProperty.getName())
                     .setCardinality(platformProperty.getCardinality())
                     .build();
@@ -233,15 +267,24 @@
                 instanceof android.app.appsearch.AppSearchSchema.DocumentPropertyConfig) {
             android.app.appsearch.AppSearchSchema.DocumentPropertyConfig documentProperty =
                     (android.app.appsearch.AppSearchSchema.DocumentPropertyConfig) platformProperty;
-            return new AppSearchSchema.DocumentPropertyConfig.Builder(
-                    documentProperty.getName(),
-                    documentProperty.getSchemaType())
-                    .setCardinality(documentProperty.getCardinality())
-                    .setShouldIndexNestedProperties(documentProperty.shouldIndexNestedProperties())
-                    .build();
-            // TODO(b/289150947): Add the indexable_nested_properties_list once it becomes
-            //  available in platform.
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in platform.
+            AppSearchSchema.DocumentPropertyConfig.Builder jetpackBuilder =
+                    new AppSearchSchema.DocumentPropertyConfig.Builder(
+                            documentProperty.getName(),
+                            documentProperty.getSchemaType())
+                            .setCardinality(documentProperty.getCardinality())
+                            .setShouldIndexNestedProperties(
+                                    documentProperty.shouldIndexNestedProperties());
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                List<String> indexableNestedProperties =
+                        ApiHelperForV.getIndexableNestedProperties(documentProperty);
+                jetpackBuilder.addIndexableNestedProperties(indexableNestedProperties);
+            }
+            return jetpackBuilder.build();
         } else {
+            // TODO(b/326656531) : Add an entry for EmbeddingPropertyConfig once it becomes
+            //  available in platform.
             throw new IllegalArgumentException(
                     "Invalid property type " + platformProperty.getClass()
                             + ": " + platformProperty);
@@ -288,4 +331,38 @@
             return longPropertyConfig.getIndexingType();
         }
     }
+
+
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class ApiHelperForV {
+        private ApiHelperForV() {}
+
+        @DoNotInline
+        @SuppressLint("NewApi")
+        static void addParentType(
+                android.app.appsearch.AppSearchSchema.Builder platformBuilder,
+                @NonNull String parentSchemaType) {
+            platformBuilder.addParentType(parentSchemaType);
+        }
+
+        @DoNotInline
+        static void addIndexableNestedProperties(
+                android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder
+                        platformBuilder,
+                @NonNull Collection<String> indexableNestedProperties) {
+            platformBuilder.addIndexableNestedProperties(indexableNestedProperties);
+        }
+
+        @DoNotInline
+        static List<String> getParentTypes(android.app.appsearch.AppSearchSchema platformSchema) {
+            return platformSchema.getParentTypes();
+        }
+
+        @DoNotInline
+        static List<String> getIndexableNestedProperties(
+                android.app.appsearch.AppSearchSchema.DocumentPropertyConfig
+                        platformDocumentProperty) {
+            return platformDocumentProperty.getIndexableNestedProperties();
+        }
+    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
index 47bff5a..125da54 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
@@ -77,15 +77,12 @@
                 .setSnippetCountPerProperty(jetpackSearchSpec.getSnippetCountPerProperty())
                 .setMaxSnippetSize(jetpackSearchSpec.getMaxSnippetSize());
         if (jetpackSearchSpec.getResultGroupingTypeFlags() != 0) {
-            // TODO(b/258715421): Add Build.VERSION.SDK_INT condition once there is an extservices
-            // sdk that includes SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA.
-            if (true) {
-                if ((jetpackSearchSpec.getResultGroupingTypeFlags()
-                        & SearchSpec.GROUPING_TYPE_PER_SCHEMA) != 0) {
-                    throw new UnsupportedOperationException(
+            if ((jetpackSearchSpec.getResultGroupingTypeFlags()
+                    & SearchSpec.GROUPING_TYPE_PER_SCHEMA) != 0
+                    && Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                throw new UnsupportedOperationException(
                         Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
-                            + " is not available on this AppSearch implementation.");
-                }
+                                + " is not available on this AppSearch implementation.");
             }
             platformBuilder.setResultGrouping(
                     jetpackSearchSpec.getResultGroupingTypeFlags(),
@@ -107,6 +104,7 @@
         }
 
         if (!jetpackSearchSpec.getEnabledFeatures().isEmpty()) {
+            // Copy U features
             if (jetpackSearchSpec.isNumericSearchEnabled()
                     || jetpackSearchSpec.isVerbatimSearchEnabled()
                     || jetpackSearchSpec.isListFilterQueryLanguageEnabled()) {
@@ -118,6 +116,27 @@
                 }
                 ApiHelperForU.copyEnabledFeatures(platformBuilder, jetpackSearchSpec);
             }
+            // Copy V features
+            if (jetpackSearchSpec.isListFilterHasPropertyFunctionEnabled()) {
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                    throw new UnsupportedOperationException(
+                            Features.LIST_FILTER_HAS_PROPERTY_FUNCTION
+                                    + " is not available on this AppSearch implementation.");
+                }
+                ApiHelperForV.copyEnabledFeatures(platformBuilder, jetpackSearchSpec);
+            }
+            // Copy beyond-V features
+            if (jetpackSearchSpec.isEmbeddingSearchEnabled()
+                    || !jetpackSearchSpec.getSearchEmbeddings().isEmpty()) {
+                // TODO(b/326656531): Remove this once embedding search APIs are available.
+                throw new UnsupportedOperationException(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG
+                        + " is not available on this AppSearch implementation.");
+            }
+            if (jetpackSearchSpec.isListFilterTokenizeFunctionEnabled()) {
+                // TODO(b/332620561): Remove this once 'tokenize' is supported.
+                throw new UnsupportedOperationException(Features.LIST_FILTER_TOKENIZE_FUNCTION
+                        + " is not available on this AppSearch implementation.");
+            }
         }
 
         if (jetpackSearchSpec.getJoinSpec() != null) {
@@ -129,9 +148,29 @@
         }
 
         if (!jetpackSearchSpec.getFilterProperties().isEmpty()) {
-            // TODO(b/296088047): Remove this once property filters become available.
-            throw new UnsupportedOperationException(Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES
-                    + " is not available on this AppSearch implementation.");
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                throw new UnsupportedOperationException(Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES
+                        + " is not available on this AppSearch implementation.");
+            }
+            ApiHelperForV.addFilterProperties(
+                    platformBuilder, jetpackSearchSpec.getFilterProperties());
+        }
+
+        if (jetpackSearchSpec.getSearchSourceLogTag() != null) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                throw new UnsupportedOperationException(
+                        Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG
+                                + " is not available on this AppSearch implementation.");
+            }
+            ApiHelperForV.setSearchSourceLogTag(
+                    platformBuilder, jetpackSearchSpec.getSearchSourceLogTag());
+        }
+
+        if (!jetpackSearchSpec.getInformationalRankingExpressions().isEmpty()) {
+            // TODO(b/332642571): Remove this once informational ranking expressions are available.
+            throw new UnsupportedOperationException(
+                    Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS
+                            + " are not available on this AppSearch implementation.");
         }
         return platformBuilder.build();
     }
@@ -176,4 +215,34 @@
             }
         }
     }
+
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class ApiHelperForV {
+        private ApiHelperForV() {}
+
+        @DoNotInline
+        static void addFilterProperties(
+                @NonNull android.app.appsearch.SearchSpec.Builder platformBuilder,
+                Map<String, List<String>> properties) {
+            for (Map.Entry<String, List<String>> entry : properties.entrySet()) {
+                platformBuilder.addFilterProperties(entry.getKey(), entry.getValue());
+            }
+        }
+
+        @DoNotInline
+        static void copyEnabledFeatures(
+                @NonNull android.app.appsearch.SearchSpec.Builder platformBuilder,
+                @NonNull SearchSpec jetpackSpec) {
+            if (jetpackSpec.isListFilterHasPropertyFunctionEnabled()) {
+                platformBuilder.setListFilterHasPropertyFunctionEnabled(true);
+            }
+        }
+
+        @DoNotInline
+        static void setSearchSourceLogTag(
+                android.app.appsearch.SearchSpec.Builder platformBuilder,
+                String searchSourceLogTag) {
+            platformBuilder.setSearchSourceLogTag(searchSourceLogTag);
+        }
+    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java
index 5479749..d354fe5 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSuggestionSpecToPlatformConverter.java
@@ -19,12 +19,15 @@
 import android.annotation.SuppressLint;
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.Features;
 import androidx.appsearch.app.SearchSuggestionSpec;
 import androidx.core.util.Preconditions;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -62,6 +65,32 @@
             platformBuilder.addFilterDocumentIds(documentIdFilters.getKey(),
                     documentIdFilters.getValue());
         }
+
+        Map<String, List<String>> jetpackFilterProperties =
+                jetpackSearchSuggestionSpec.getFilterProperties();
+        if (!jetpackFilterProperties.isEmpty()) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                throw new UnsupportedOperationException(Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES
+                        + " is not available on this AppSearch implementation.");
+            }
+            for (Map.Entry<String, List<String>> entry : jetpackFilterProperties.entrySet()) {
+                ApiHelperForV.addFilterProperties(
+                        platformBuilder, entry.getKey(), entry.getValue());
+            }
+        }
         return platformBuilder.build();
     }
+
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class ApiHelperForV {
+        private ApiHelperForV() {}
+
+        @DoNotInline
+        static void addFilterProperties(
+                android.app.appsearch.SearchSuggestionSpec.Builder platformBuilder,
+                String schema,
+                Collection<String> propertyPaths) {
+            platformBuilder.addFilterProperties(schema, propertyPaths);
+        }
+    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java
index d03d22c..4085b8f 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SetSchemaRequestToPlatformConverter.java
@@ -26,10 +26,12 @@
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.Migrator;
 import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
 import androidx.appsearch.app.SetSchemaRequest;
 import androidx.appsearch.app.SetSchemaResponse;
 import androidx.core.util.Preconditions;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
@@ -86,6 +88,38 @@
                 }
             }
         }
+
+        if (!jetpackRequest.getPubliclyVisibleSchemas().isEmpty()) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                throw new UnsupportedOperationException(
+                        "Publicly visible schema are not supported on this AppSearch "
+                                + "implementation.");
+            }
+            for (Map.Entry<String, PackageIdentifier> entry :
+                    jetpackRequest.getPubliclyVisibleSchemas().entrySet()) {
+                PackageIdentifier publiclyVisibleTargetPackage = entry.getValue();
+                ApiHelperForV.setPubliclyVisibleSchema(
+                        platformBuilder,
+                        entry.getKey(),
+                        new android.app.appsearch.PackageIdentifier(
+                                publiclyVisibleTargetPackage.getPackageName(),
+                                publiclyVisibleTargetPackage.getSha256Certificate()));
+            }
+        }
+
+        if (!jetpackRequest.getSchemasVisibleToConfigs().isEmpty()) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+                throw new UnsupportedOperationException(
+                        "Schema visible to config are not supported on this AppSearch "
+                                + "implementation.");
+            }
+            for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry :
+                    jetpackRequest.getSchemasVisibleToConfigs().entrySet()) {
+                ApiHelperForV.addSchemaTypeVisibleToConfig(
+                        platformBuilder, entry.getKey(), entry.getValue());
+            }
+        }
+
         for (Map.Entry<String, Migrator> entry : jetpackRequest.getMigrators().entrySet()) {
             Migrator jetpackMigrator = entry.getValue();
             android.app.appsearch.Migrator platformMigrator = new android.app.appsearch.Migrator() {
@@ -176,4 +210,66 @@
             platformBuilder.addRequiredPermissionsForSchemaTypeVisibility(schemaType, permissions);
         }
     }
+
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class ApiHelperForV {
+        private ApiHelperForV() {}
+
+        @DoNotInline
+        static void setPubliclyVisibleSchema(
+                android.app.appsearch.SetSchemaRequest.Builder platformBuilder,
+                String schemaType,
+                android.app.appsearch.PackageIdentifier publiclyVisibleTargetPackage) {
+            platformBuilder.setPubliclyVisibleSchema(schemaType, publiclyVisibleTargetPackage);
+        }
+
+        @DoNotInline
+        public static void addSchemaTypeVisibleToConfig(
+                android.app.appsearch.SetSchemaRequest.Builder platformBuilder,
+                String schemaType,
+                Set<SchemaVisibilityConfig> jetpackConfigs) {
+            for (SchemaVisibilityConfig jetpackConfig : jetpackConfigs) {
+                android.app.appsearch.SchemaVisibilityConfig platformConfig =
+                        toPlatformSchemaVisibilityConfig(jetpackConfig);
+                platformBuilder.addSchemaTypeVisibleToConfig(schemaType, platformConfig);
+            }
+        }
+
+        /**
+         * Translates a jetpack {@link SchemaVisibilityConfig} into a platform
+         * {@link android.app.appsearch.SchemaVisibilityConfig}.
+         */
+        @NonNull
+        private static android.app.appsearch.SchemaVisibilityConfig
+                toPlatformSchemaVisibilityConfig(@NonNull SchemaVisibilityConfig jetpackConfig) {
+            Preconditions.checkNotNull(jetpackConfig);
+            android.app.appsearch.SchemaVisibilityConfig.Builder platformBuilder =
+                    new android.app.appsearch.SchemaVisibilityConfig.Builder();
+
+            // Translate allowedPackages
+            List<PackageIdentifier> allowedPackages = jetpackConfig.getAllowedPackages();
+            for (int i = 0; i < allowedPackages.size(); i++) {
+                platformBuilder.addAllowedPackage(new android.app.appsearch.PackageIdentifier(
+                        allowedPackages.get(i).getPackageName(),
+                        allowedPackages.get(i).getSha256Certificate()));
+            }
+
+            // Translate requiredPermissions
+            for (Set<Integer> requiredPermissions : jetpackConfig.getRequiredPermissions()) {
+                platformBuilder.addRequiredPermissions(requiredPermissions);
+            }
+
+            // Translate publiclyVisibleTargetPackage
+            PackageIdentifier publiclyVisibleTargetPackage =
+                    jetpackConfig.getPubliclyVisibleTargetPackage();
+            if (publiclyVisibleTargetPackage != null) {
+                platformBuilder.setPubliclyVisibleTargetPackage(
+                        new android.app.appsearch.PackageIdentifier(
+                                publiclyVisibleTargetPackage.getPackageName(),
+                                publiclyVisibleTargetPackage.getSha256Certificate()));
+            }
+
+            return platformBuilder.build();
+        }
+    }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/util/AppSearchVersionUtil.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/util/AppSearchVersionUtil.java
new file mode 100644
index 0000000..3d778ec
--- /dev/null
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/util/AppSearchVersionUtil.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.platformstorage.util;
+
+import android.content.Context;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.util.Preconditions;
+
+/**
+ * Utilities for retrieving platform AppSearch's module version code.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class AppSearchVersionUtil {
+    public static final long APPSEARCH_U_BASE_VERSION_CODE = 331311020;
+    public static final long APPSEARCH_M2023_11_VERSION_CODE = 341113000;
+
+    private static final String APPSEARCH_MODULE_NAME = "com.android.appsearch";
+
+    // This will be set to -1 to indicate the AppSearch version code hasn't bee checked, then to
+    // 0 if it is not found, or the version code if it is found.
+    private static volatile long sAppSearchVersionCode = -1;
+
+    private AppSearchVersionUtil() {
+    }
+
+    /**
+     * Returns AppSearch's version code from the context.
+     */
+    @RequiresApi(Build.VERSION_CODES.Q)
+    public static long getAppSearchVersionCode(@NonNull Context context) {
+        Preconditions.checkNotNull(context);
+        if (sAppSearchVersionCode != -1) {
+            return sAppSearchVersionCode;
+        }
+        synchronized (AppSearchVersionUtil.class) {
+            // Check again in case it was assigned while waiting
+            if (sAppSearchVersionCode == -1) {
+                long appsearchVersionCode = 0;
+                try {
+                    PackageManager packageManager = context.getPackageManager();
+                    String appSearchPackageName =
+                            ApiHelperForQ.getAppSearchPackageName(packageManager);
+                    if (appSearchPackageName != null) {
+                        PackageInfo pInfo = packageManager
+                                .getPackageInfo(appSearchPackageName, PackageManager.MATCH_APEX);
+                        appsearchVersionCode = ApiHelperForQ.getPackageInfoLongVersionCode(pInfo);
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    // Module not installed
+                }
+                sAppSearchVersionCode = appsearchVersionCode;
+            }
+        }
+        return sAppSearchVersionCode;
+    }
+
+    @RequiresApi(Build.VERSION_CODES.Q)
+    private static class ApiHelperForQ {
+        @DoNotInline
+        static long getPackageInfoLongVersionCode(PackageInfo pInfo) {
+            return pInfo.getLongVersionCode();
+        }
+
+        @DoNotInline
+        static String getAppSearchPackageName(PackageManager packageManager)
+                throws PackageManager.NameNotFoundException {
+            ModuleInfo appSearchModule =
+                    packageManager.getModuleInfo(APPSEARCH_MODULE_NAME, 1);
+            return appSearchModule.getPackageName();
+        }
+    }
+}
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/util/SchemaValidationUtil.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/util/SchemaValidationUtil.java
new file mode 100644
index 0000000..7a98515b
--- /dev/null
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/util/SchemaValidationUtil.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.platformstorage.util;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.LongPropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
+import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+import androidx.appsearch.exceptions.IllegalSchemaException;
+import androidx.collection.ArrayMap;
+import androidx.collection.ArraySet;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utilities for schema validation.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SchemaValidationUtil {
+    private SchemaValidationUtil() {
+    }
+
+    /**
+     * Checks that the set of AppSearch schemas form valid schema-type definitions, and do not
+     * exceed the maximum number of indexed properties allowed.
+     *
+     * @param appSearchSchemas   Set of AppSearch Schemas for a
+     *                           {@link androidx.appsearch.app.SetSchemaRequest}.
+     * @param maxSectionsAllowed The maximum number of sections allowed per AppSearch schema.
+     * @throws IllegalSchemaException if any schema in the set is invalid. A schema is invalid if
+     *                                it contains an invalid cycle, has an undefined document
+     *                                property config, or exceeds the
+     *                                maximum number of sections allowed.
+     */
+    public static void checkSchemasAreValidOrThrow(@NonNull Set<AppSearchSchema> appSearchSchemas,
+            int maxSectionsAllowed) throws IllegalSchemaException {
+        Map<String, AppSearchSchema> knownSchemas = new ArrayMap<>();
+        for (AppSearchSchema schema : appSearchSchemas) {
+            knownSchemas.put(schema.getSchemaType(), schema);
+        }
+        Map<String, Integer> cachedNumSectionsMap = new ArrayMap<>();
+        for (AppSearchSchema schema : appSearchSchemas) {
+            // Check that the number of sections is below the max allowed
+            int numSections = getNumSectionsInSchemaOrThrow(schema, knownSchemas,
+                    cachedNumSectionsMap, new ArraySet<>());
+            if (numSections > maxSectionsAllowed) {
+                throw new IllegalSchemaException(
+                        "Too many properties to be indexed, max " + "number of properties allowed: "
+                                + maxSectionsAllowed);
+            }
+        }
+    }
+
+    /**
+     * Returns the number of indexes sections in a given AppSearch schema.
+     *
+     * @param schema                    The AppSearch schema to get the number of sections for.
+     * @param knownSchemas              Map of known schema-type strings to their corresponding
+     *                                  schemas.
+     * @param cachedNumSectionsInSchema Map of the cached number of sections in schemas which have
+     *                                  already been expanded.
+     * @param visitedSchemaTypes        Set of schemas that have already been expanded as parents
+     *                                  of the current schema.
+     * @throws IllegalSchemaException if the schema contains an invalid cycle, or contains a
+     *                                DocumentPropertyConfig where the config's schema type is
+     *                                unknown.
+     */
+    private static int getNumSectionsInSchemaOrThrow(@NonNull AppSearchSchema schema,
+            @NonNull Map<String, AppSearchSchema> knownSchemas,
+            @NonNull Map<String, Integer> cachedNumSectionsInSchema,
+            @NonNull Set<String> visitedSchemaTypes)
+            throws IllegalSchemaException {
+        String schemaType = schema.getSchemaType();
+        if (visitedSchemaTypes.contains(schemaType)) {
+            // We've hit an illegal cycle where all DocumentPropertyConfigs set
+            // shouldIndexNestedProperties = true.
+            throw new IllegalSchemaException(
+                    "Invalid cycle detected in schema type configs. '" + schemaType
+                            + "' references itself.");
+        }
+        if (cachedNumSectionsInSchema.containsKey(schemaType)) {
+            // We've already calculated and cached the number of sections in this AppSearch schema,
+            // just return this value.
+            return cachedNumSectionsInSchema.get(schemaType);
+        }
+
+        visitedSchemaTypes.add(schemaType);
+        int numSections = 0;
+        for (PropertyConfig property : schema.getProperties()) {
+            if (property.getDataType() == PropertyConfig.DATA_TYPE_DOCUMENT) {
+                DocumentPropertyConfig documentProperty = (DocumentPropertyConfig) property;
+                String docPropertySchemaType = documentProperty.getSchemaType();
+                if (!knownSchemas.containsKey(docPropertySchemaType)) {
+                    // The schema type that this document property config is referring to
+                    // does not exist in the provided schemas
+                    throw new IllegalSchemaException(
+                            "Undefined schema type: " + docPropertySchemaType);
+                }
+                if (!documentProperty.shouldIndexNestedProperties()) {
+                    numSections += documentProperty.getIndexableNestedProperties().size();
+                } else {
+                    numSections += getNumSectionsInSchemaOrThrow(
+                            knownSchemas.get(docPropertySchemaType), knownSchemas,
+                            cachedNumSectionsInSchema, visitedSchemaTypes);
+                }
+            } else {
+                numSections += isPropertyIndexable(property) ? 1 : 0;
+            }
+        }
+        visitedSchemaTypes.remove(schemaType);
+        cachedNumSectionsInSchema.put(schemaType, numSections);
+        return numSections;
+    }
+
+    private static boolean isPropertyIndexable(PropertyConfig propertyConfig) {
+        switch (propertyConfig.getDataType()) {
+            case PropertyConfig.DATA_TYPE_STRING:
+                return ((StringPropertyConfig) propertyConfig).getIndexingType()
+                        != StringPropertyConfig.INDEXING_TYPE_NONE;
+            case PropertyConfig.DATA_TYPE_LONG:
+                return ((LongPropertyConfig) propertyConfig).getIndexingType()
+                        != LongPropertyConfig.INDEXING_TYPE_NONE;
+            case PropertyConfig.DATA_TYPE_DOCUMENT:
+                DocumentPropertyConfig documentProperty = (DocumentPropertyConfig) propertyConfig;
+                return documentProperty.shouldIndexNestedProperties()
+                        || !documentProperty.getIndexableNestedProperties().isEmpty();
+            case PropertyConfig.DATA_TYPE_DOUBLE:
+                // fallthrough
+            case PropertyConfig.DATA_TYPE_BOOLEAN:
+                // fallthrough
+            case PropertyConfig.DATA_TYPE_BYTES:
+                // fallthrough
+            default:
+                return false;
+        }
+    }
+}
diff --git a/appsearch/appsearch-play-services-storage/build.gradle b/appsearch/appsearch-play-services-storage/build.gradle
index 4de462e..bcbbc55 100644
--- a/appsearch/appsearch-play-services-storage/build.gradle
+++ b/appsearch/appsearch-play-services-storage/build.gradle
@@ -30,8 +30,7 @@
 
 dependencies {
     implementation project(":appsearch:appsearch")
-    // TODO(b/278583111) Swap with the core library version in which Function gets added.
-    implementation project(":core:core")
+    implementation("androidx.core:core:1.12.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation('androidx.collection:collection:1.2.0')
     implementation("com.google.android.gms:play-services-appsearch:16.0.0", {
@@ -52,10 +51,10 @@
     description =
             "An implementation of AppSearchSession and GlobalSearchSession on pre-S devices using " +
                     "play-services-appsearch SDK with Gogle Play Services as storage backend."
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.appsearch.playservicesstorage"
      defaultConfig {
     }
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
index 2f580b9..6393a15 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
@@ -26,88 +26,68 @@
 final class FeaturesImpl implements Features {
     @Override
     public boolean isFeatureSupported(@NonNull String feature) {
-        // TODO(b/274986359): Update based on features available in {@link Features} and those
-        //  supported by play-services-appsearch.
         switch (feature) {
-            // Android T Features
             case Features.ADD_PERMISSIONS_AND_GET_VISIBILITY:
                 // fall through
             case Features.GLOBAL_SEARCH_SESSION_GET_SCHEMA:
                 // fall through
             case Features.GLOBAL_SEARCH_SESSION_GET_BY_ID:
                 // fall through
+            case Features.JOIN_SPEC_AND_QUALIFIED_ID:
+                // fall through
+            case Features.NUMERIC_SEARCH:
+                // fall through
+            case Features.VERBATIM_SEARCH:
+                // fall through
+            case Features.LIST_FILTER_QUERY_LANGUAGE:
+                // fall through
+            case Features.LIST_FILTER_HAS_PROPERTY_FUNCTION:
+                // fall through
+            case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
+                // fall through
             case Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH:
                 // fall through
-                return true; // AppSearch features in T, present in GMSCore AppSearch.
+            case Features.SEARCH_SPEC_PROPERTY_WEIGHTS:
+                // fall through
+            case Features.TOKENIZER_TYPE_RFC822:
+                // fall through
+            case Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION:
+                // fall through
+            case Features.SEARCH_SUGGESTION:
+                // fall through
+            case Features.SET_SCHEMA_CIRCULAR_REFERENCES:
+                // fall through
+            case Features.SCHEMA_ADD_PARENT_TYPE:
+                // fall through
+            case Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES:
+                // fall through
+            case Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES:
+                // fall through
+            case Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG:
+                // fall through
+            case Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE:
+                // fall through
+            case Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG:
+                return true; // AppSearch features present in GMSCore AppSearch.
 
             // RegisterObserver and UnregisterObserver are not yet supported by GMSCore AppSearch.
             // TODO(b/208654892) : Update to reflect support once this feature is supported.
             case Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK:
-            // Android U Features
-            case Features.SEARCH_SPEC_PROPERTY_WEIGHTS:
-                // TODO(b/203700301) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
                 // fall through
-            case Features.TOKENIZER_TYPE_RFC822:
-                // TODO(b/259294369) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
+            case Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG:
                 // fall through
-            case Features.NUMERIC_SEARCH:
-                // TODO(b/259744228) : Update to reflect support in Android U+ once this feature is
-                // synced over into service-appsearch.
+            case Features.SCHEMA_SET_DESCRIPTION:
                 // fall through
-            case SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION:
-                // TODO(b/261474063) : Update to reflect support in Android U+ once advanced
-                //  ranking becomes available.
+            case Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS:
                 // fall through
-            case Features.JOIN_SPEC_AND_QUALIFIED_ID:
-                // TODO(b/256022027) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
+            case Features.LIST_FILTER_TOKENIZE_FUNCTION:
                 // fall through
-            case Features.VERBATIM_SEARCH:
-                // TODO(b/204333391) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.LIST_FILTER_QUERY_LANGUAGE:
-                // TODO(b/208654892) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
-                // TODO(b/258715421) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.SEARCH_SUGGESTION:
-                // TODO(b/227356108) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.SCHEMA_SET_DELETION_PROPAGATION:
-                // TODO(b/268521214) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.SET_SCHEMA_CIRCULAR_REFERENCES:
-                // TODO(b/280698121) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.SCHEMA_ADD_PARENT_TYPE:
-                // TODO(b/269295094) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES:
-                // TODO(b/289150947) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                // fall through
-            case Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES:
-                // TODO(b/296088047) : Update to reflect support in Android U+ once this feature is
-                //  synced over into service-appsearch.
-                return false;
             default:
-                return false; // AppSearch features in U+, absent in GMSCore AppSearch.
+                return false; // AppSearch features absent in GMSCore AppSearch.
         }
     }
     @Override
     public int getMaxIndexedProperties() {
-        // TODO(b/241310816): Update to reflect support in Android U+ once 64 indexable properties
-        //  are possible in service-appsearch.
-        return 16;
+        return 64;
     }
 }
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/GlobalSearchSessionImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/GlobalSearchSessionImpl.java
index 61c90ac..5bc02ef 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/GlobalSearchSessionImpl.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/GlobalSearchSessionImpl.java
@@ -56,6 +56,8 @@
     private final Features mFeatures;
     private final Executor mExecutor;
 
+    private boolean mIsClosed = false;
+
     GlobalSearchSessionImpl(
             @NonNull GlobalSearchClient gmsClient,
             @NonNull Features features,
@@ -69,6 +71,10 @@
     public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
             @NonNull String packageName, @NonNull String databaseName,
             @NonNull GetByDocumentIdRequest request) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkNotNull(request);
+        Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed");
         return AppSearchTaskFutures.toListenableFuture(
                 mGmsClient.getByDocumentId(packageName, databaseName,
                         RequestToGmsConverter.toGmsGetByDocumentIdRequest(request)),
@@ -80,6 +86,9 @@
     @NonNull
     @Override
     public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
+        Preconditions.checkNotNull(queryExpression);
+        Preconditions.checkNotNull(searchSpec);
+        Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed");
         com.google.android.gms.appsearch.SearchResults searchResults =
                 mGmsClient.search(queryExpression,
                         SearchSpecToGmsConverter.toGmsSearchSpec(searchSpec));
@@ -90,6 +99,8 @@
     @Override
     public ListenableFuture<Void> reportSystemUsageAsync(
             @NonNull ReportSystemUsageRequest request) {
+        Preconditions.checkNotNull(request);
+        Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed");
         Task<Void> flushTask = Tasks.forResult(null);
         return AppSearchTaskFutures.toListenableFuture(flushTask, /* valueMapper= */ i-> i,
                 mExecutor);
@@ -99,6 +110,9 @@
     @Override
     public ListenableFuture<GetSchemaResponse> getSchemaAsync(@NonNull String packageName,
             @NonNull String databaseName) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(databaseName);
+        Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed");
         return AppSearchTaskFutures.toListenableFuture(
                 mGmsClient.getSchema(packageName, databaseName),
                 GetSchemaResponseToGmsConverter::toJetpackGetSchemaResponse, mExecutor);
@@ -130,6 +144,6 @@
 
     @Override
     public void close() {
-        mGmsClient.close();
+        mIsClosed = true;
     }
 }
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/PlayServicesStorage.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/PlayServicesStorage.java
index ca2bee2..8a3556d 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/PlayServicesStorage.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/PlayServicesStorage.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import androidx.annotation.NonNull;
+import androidx.appsearch.app.AppSearchEnvironmentFactory;
 import androidx.appsearch.app.AppSearchSession;
 import androidx.appsearch.app.GlobalSearchSession;
 import androidx.appsearch.playservicesstorage.util.AppSearchTaskFutures;
@@ -33,7 +34,6 @@
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 /**
  * An AppSearch storage system which stores data in the central AppSearch service in Google
@@ -200,7 +200,8 @@
     // execute() won't return anything, we will hang forever waiting for the execution.
     // AppSearch multi-thread execution is guarded by Read & Write Lock in AppSearchImpl, all
     // mutate requests will need to gain write lock and query requests need to gain read lock.
-    static final Executor EXECUTOR = Executors.newCachedThreadPool();
+    static final Executor EXECUTOR = AppSearchEnvironmentFactory.getEnvironmentInstance()
+            .createCachedThreadPoolExecutor();
 
     /**
      * Opens a new {@link AppSearchSession} on this storage.
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchSessionImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchSessionImpl.java
index df99af7..998ded9 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchSessionImpl.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/SearchSessionImpl.java
@@ -40,6 +40,8 @@
 import androidx.appsearch.playservicesstorage.converter.RequestToGmsConverter;
 import androidx.appsearch.playservicesstorage.converter.ResponseToGmsConverter;
 import androidx.appsearch.playservicesstorage.converter.SearchSpecToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.SearchSuggestionResultToGmsConverter;
+import androidx.appsearch.playservicesstorage.converter.SearchSuggestionSpecToGmsConverter;
 import androidx.appsearch.playservicesstorage.converter.SetSchemaRequestToGmsConverter;
 import androidx.appsearch.playservicesstorage.util.AppSearchTaskFutures;
 import androidx.core.util.Preconditions;
@@ -149,9 +151,16 @@
     public ListenableFuture<List<SearchSuggestionResult>> searchSuggestionAsync(
             @NonNull String suggestionQueryExpression,
             @NonNull SearchSuggestionSpec searchSuggestionSpec) {
-        // TODO(b/274986359): Implement searchSuggestionAsync for PlayServicesStorage.
-        throw new UnsupportedOperationException(
-                "Search Suggestion is not yet supported on this AppSearch implementation.");
+        Preconditions.checkNotNull(suggestionQueryExpression);
+        Preconditions.checkNotNull(searchSuggestionSpec);
+        return AppSearchTaskFutures.toListenableFuture(
+                mGmsClient.searchSuggestion(
+                        suggestionQueryExpression,
+                        SearchSuggestionSpecToGmsConverter.toGmsSearchSuggestionSpec(
+                                searchSuggestionSpec),
+                        mDatabaseName),
+                SearchSuggestionResultToGmsConverter :: toGmsSearchSuggestionResults,
+                mExecutor);
     }
 
     @NonNull
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GenericDocumentToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GenericDocumentToGmsConverter.java
index 2b78fbf..0aec84e 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GenericDocumentToGmsConverter.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GenericDocumentToGmsConverter.java
@@ -18,6 +18,8 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.EmbeddingVector;
+import androidx.appsearch.app.Features;
 import androidx.appsearch.app.GenericDocument;
 import androidx.core.util.Preconditions;
 
@@ -71,6 +73,10 @@
                 }
                 gmsBuilder.setPropertyDocument(propertyName,
                         gmsSubDocuments);
+            } else if (property instanceof EmbeddingVector[]) {
+                // TODO(b/326656531): Remove this once embedding search APIs are available.
+                throw new UnsupportedOperationException(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG
+                        + " is not available on this AppSearch implementation.");
             } else {
                 throw new IllegalStateException(
                         String.format("Property \"%s\" has unsupported value type %s",
@@ -121,6 +127,8 @@
                 }
                 jetpackBuilder.setPropertyDocument(propertyName, jetpackSubDocuments);
             } else {
+                // TODO(b/326656531) : Add an entry for EmbeddingVector once it becomes
+                //  available in gms-appsearch.
                 throw new IllegalStateException(
                         String.format("Property \"%s\" has unsupported value type %s", propertyName,
                                 property.getClass().toString()));
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GetSchemaResponseToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GetSchemaResponseToGmsConverter.java
index bacb0b8..bb37609 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GetSchemaResponseToGmsConverter.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/GetSchemaResponseToGmsConverter.java
@@ -20,9 +20,13 @@
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
+import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -67,9 +71,73 @@
             jetpackBuilder.setRequiredPermissionsForSchemaTypeVisibility(entry.getKey(),
                     entry.getValue());
         }
+        // Convert publicly visible schemas
+        Map<String, PackageIdentifier> publiclyVisibleSchemas =
+                getPubliclyVisibleSchemas(gmsResponse);
+        if (!publiclyVisibleSchemas.isEmpty()) {
+            for (Map.Entry<String, PackageIdentifier> entry :
+                    publiclyVisibleSchemas.entrySet()) {
+                jetpackBuilder.setPubliclyVisibleSchema(entry.getKey(), entry.getValue());
+            }
+        }
+
+        // Convert schemas visible to configs
+        Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs =
+                getSchemasVisibleToConfigs(gmsResponse);
+        if (!schemasVisibleToConfigs.isEmpty()) {
+            for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry :
+                    schemasVisibleToConfigs.entrySet()) {
+                jetpackBuilder.setSchemaTypeVisibleToConfigs(entry.getKey(), entry.getValue());
+            }
+        }
         return jetpackBuilder.build();
     }
 
+    private static Map<String, PackageIdentifier> getPubliclyVisibleSchemas(
+            com.google.android.gms.appsearch.GetSchemaResponse gmsResponse) {
+        Map<String, com.google.android.gms.appsearch.PackageIdentifier>
+                gmsPubliclyVisibleSchemas = gmsResponse.getPubliclyVisibleSchemas();
+        if (gmsPubliclyVisibleSchemas.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<String, PackageIdentifier> jetpackPubliclyVisibleSchemas =
+                new ArrayMap<>(gmsPubliclyVisibleSchemas.size());
+        for (Map.Entry<String, com.google.android.gms.appsearch.PackageIdentifier> entry :
+                gmsPubliclyVisibleSchemas.entrySet()) {
+            jetpackPubliclyVisibleSchemas.put(
+                    entry.getKey(),
+                    new PackageIdentifier(
+                            entry.getValue().getPackageName(),
+                            entry.getValue().getSha256Certificate()));
+        }
+        return jetpackPubliclyVisibleSchemas;
+    }
+
+    private static Map<String, Set<SchemaVisibilityConfig>> getSchemasVisibleToConfigs(
+            com.google.android.gms.appsearch.GetSchemaResponse gmsResponse) {
+        Map<String, Set<com.google.android.gms.appsearch.SchemaVisibilityConfig>>
+                gmsSchemasVisibleToConfigs =
+                gmsResponse.getSchemaTypesVisibleToConfigs();
+        if (gmsSchemasVisibleToConfigs.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<String, Set<SchemaVisibilityConfig>> jetpackSchemasVisibleToConfigs =
+                new ArrayMap<>(gmsSchemasVisibleToConfigs.size());
+        for (Map.Entry<String, Set<com.google.android.gms.appsearch.SchemaVisibilityConfig>> entry :
+                gmsSchemasVisibleToConfigs.entrySet()) {
+            Set<SchemaVisibilityConfig> jetpackConfigPerType =
+                    new ArraySet<>(entry.getValue().size());
+            for (com.google.android.gms.appsearch.SchemaVisibilityConfig gmsConfigPerType :
+                    entry.getValue()) {
+                SchemaVisibilityConfig jetpackConfig =
+                        toJetpackSchemaVisibilityConfig(gmsConfigPerType);
+                jetpackConfigPerType.add(jetpackConfig);
+            }
+            jetpackSchemasVisibleToConfigs.put(entry.getKey(), jetpackConfigPerType);
+        }
+        return jetpackSchemasVisibleToConfigs;
+    }
+
     /**
      * Adds package visibilities in a Gms
      * {@link com.google.android.gms.appsearch.GetSchemaResponse} into
@@ -97,4 +165,42 @@
             }
         }
     }
+
+    /**
+     * Translates a platform {@link com.google.android.gms.appsearch.SchemaVisibilityConfig} into
+     * a jetpack
+     * {@link SchemaVisibilityConfig}.
+     */
+    @NonNull
+    private static SchemaVisibilityConfig toJetpackSchemaVisibilityConfig(
+            @NonNull com.google.android.gms.appsearch.SchemaVisibilityConfig platformConfig) {
+        Preconditions.checkNotNull(platformConfig);
+        SchemaVisibilityConfig.Builder jetpackBuilder = new SchemaVisibilityConfig.Builder();
+
+        // Translate allowedPackages
+        List<com.google.android.gms.appsearch.PackageIdentifier> allowedPackages =
+                platformConfig.getAllowedPackages();
+        for (int i = 0; i < allowedPackages.size(); i++) {
+            jetpackBuilder.addAllowedPackage(new PackageIdentifier(
+                    allowedPackages.get(i).getPackageName(),
+                    allowedPackages.get(i).getSha256Certificate()));
+        }
+
+        // Translate requiredPermissions
+        for (Set<Integer> requiredPermissions : platformConfig.getRequiredPermissions()) {
+            jetpackBuilder.addRequiredPermissions(requiredPermissions);
+        }
+
+        // Translate publiclyVisibleTargetPackage
+        com.google.android.gms.appsearch.PackageIdentifier publiclyVisibleTargetPackage =
+                platformConfig.getPubliclyVisibleTargetPackage();
+        if (publiclyVisibleTargetPackage != null) {
+            jetpackBuilder.setPubliclyVisibleTargetPackage(
+                    new PackageIdentifier(
+                            publiclyVisibleTargetPackage.getPackageName(),
+                            publiclyVisibleTargetPackage.getSha256Certificate()));
+        }
+
+        return jetpackBuilder.build();
+    }
 }
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/JoinSpecToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/JoinSpecToGmsConverter.java
new file mode 100644
index 0000000..08d661f
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/JoinSpecToGmsConverter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.playservicesstorage.converter;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.JoinSpec;
+import androidx.core.util.Preconditions;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link JoinSpec}.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class JoinSpecToGmsConverter {
+    private JoinSpecToGmsConverter() {
+    }
+
+    /**
+     * Translates a Jetpack {@link JoinSpec} into a gms
+     * {@link com.google.android.gms.appsearch.JoinSpec}.
+     */
+    @SuppressLint("WrongConstant")
+    @NonNull
+    public static com.google.android.gms.appsearch.JoinSpec toGmsJoinSpec(
+            @NonNull JoinSpec jetpackSpec) {
+        Preconditions.checkNotNull(jetpackSpec);
+        return new com.google.android.gms.appsearch.JoinSpec
+                .Builder(jetpackSpec.getChildPropertyExpression())
+                .setNestedSearch(
+                        jetpackSpec.getNestedQuery(),
+                        SearchSpecToGmsConverter.toGmsSearchSpec(
+                                jetpackSpec.getNestedSearchSpec()))
+                .setMaxJoinedResultCount(jetpackSpec.getMaxJoinedResultCount())
+                .setAggregationScoringStrategy(jetpackSpec.getAggregationScoringStrategy())
+                .build();
+    }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/RequestToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/RequestToGmsConverter.java
index 34cebb8..8c6f88a 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/RequestToGmsConverter.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/RequestToGmsConverter.java
@@ -47,10 +47,18 @@
         Preconditions.checkNotNull(jetpackRequest);
         com.google.android.gms.appsearch.PutDocumentsRequest.Builder gmsBuilder =
                 new com.google.android.gms.appsearch.PutDocumentsRequest.Builder();
+        // Convert normal generic documents.
         for (GenericDocument jetpackDocument : jetpackRequest.getGenericDocuments()) {
             gmsBuilder.addGenericDocuments(
+                    GenericDocumentToGmsConverter.toGmsGenericDocument(jetpackDocument));
+        }
+        // Convert taken action generic documents.
+        for (GenericDocument jetpackTakenActionGenericDocument :
+                jetpackRequest.getTakenActionGenericDocuments()) {
+            gmsBuilder.addTakenActionGenericDocuments(
                     GenericDocumentToGmsConverter.toGmsGenericDocument(
-                            jetpackDocument));
+                            jetpackTakenActionGenericDocument)
+            );
         }
         return gmsBuilder.build();
     }
@@ -68,7 +76,7 @@
                         jetpackRequest.getNamespace())
                         .addIds(jetpackRequest.getIds());
         for (Map.Entry<String, List<String>> projection :
-                jetpackRequest.getProjectionsInternal().entrySet()) {
+                jetpackRequest.getProjections().entrySet()) {
             gmsBuilder.addProjection(projection.getKey(), projection.getValue());
         }
         return gmsBuilder.build();
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SchemaToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SchemaToGmsConverter.java
index 3c507a5..a777688 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SchemaToGmsConverter.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SchemaToGmsConverter.java
@@ -19,6 +19,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.Features;
 import androidx.core.util.Preconditions;
 
 import java.util.List;
@@ -44,6 +45,17 @@
         com.google.android.gms.appsearch.AppSearchSchema.Builder gmsBuilder =
                 new com.google.android.gms.appsearch.AppSearchSchema
                         .Builder(jetpackSchema.getSchemaType());
+        if (!jetpackSchema.getDescription().isEmpty()) {
+            // TODO(b/326987971): Remove this once description becomes available.
+            throw new UnsupportedOperationException(Features.SCHEMA_SET_DESCRIPTION
+                    + " is not available on this AppSearch implementation.");
+        }
+        if (!jetpackSchema.getParentTypes().isEmpty()) {
+            List<String> parentTypes = jetpackSchema.getParentTypes();
+            for (int i = 0; i < parentTypes.size(); i++) {
+                gmsBuilder.addParentType(parentTypes.get(i));
+            }
+        }
         List<AppSearchSchema.PropertyConfig> properties = jetpackSchema.getProperties();
         for (int i = 0; i < properties.size(); i++) {
             com.google.android.gms.appsearch.AppSearchSchema.PropertyConfig gmsProperty =
@@ -64,8 +76,14 @@
         Preconditions.checkNotNull(gmsSchema);
         AppSearchSchema.Builder jetpackBuilder =
                 new AppSearchSchema.Builder(gmsSchema.getSchemaType());
+        // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+        //  available in gms.
         List<com.google.android.gms.appsearch.AppSearchSchema.PropertyConfig> properties =
                 gmsSchema.getProperties();
+        List<String> parentTypes = gmsSchema.getParentTypes();
+        for (int i = 0; i < parentTypes.size(); i++) {
+            jetpackBuilder.addParentType(parentTypes.get(i));
+        }
         for (int i = 0; i < properties.size(); i++) {
             AppSearchSchema.PropertyConfig jetpackProperty = toJetpackProperty(properties.get(i));
             jetpackBuilder.addProperty(jetpackProperty);
@@ -77,6 +95,11 @@
     private static com.google.android.gms.appsearch.AppSearchSchema.PropertyConfig toGmsProperty(
             @NonNull AppSearchSchema.PropertyConfig jetpackProperty) {
         Preconditions.checkNotNull(jetpackProperty);
+        if (!jetpackProperty.getDescription().isEmpty()) {
+            // TODO(b/326987971): Remove this once description becomes available.
+            throw new UnsupportedOperationException(Features.SCHEMA_SET_DESCRIPTION
+                    + " is not available on this AppSearch implementation.");
+        }
         if (jetpackProperty instanceof AppSearchSchema.StringPropertyConfig) {
             AppSearchSchema.StringPropertyConfig stringProperty =
                     (AppSearchSchema.StringPropertyConfig) jetpackProperty;
@@ -87,18 +110,9 @@
                             .setCardinality(stringProperty.getCardinality())
                             .setIndexingType(stringProperty.getIndexingType())
                             .setTokenizerType(stringProperty.getTokenizerType());
-            if (stringProperty.getDeletionPropagation()) {
-                // TODO(b/268521214): Update once deletion propagation is available.
-                throw new UnsupportedOperationException("Setting deletion propagation is not "
-                        + "supported on this AppSearch implementation.");
-            }
-
             if (stringProperty.getJoinableValueType()
                     == AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID) {
-                //TODO(b/274986359) Add GMSCore feature check for Joins once available.
-                throw new UnsupportedOperationException(
-                        "StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID is not supported"
-                                + " on this AppSearch implementation.");
+                gmsBuilder.setJoinableValueType(stringProperty.getJoinableValueType());
             }
             return gmsBuilder.build();
         } else if (jetpackProperty instanceof AppSearchSchema.LongPropertyConfig) {
@@ -111,10 +125,7 @@
                             .setCardinality(jetpackProperty.getCardinality());
             if (longProperty.getIndexingType()
                     == AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE) {
-                //TODO(b/274986359) Add GMSCore feature check for Indexing Range once available.
-                throw new UnsupportedOperationException(
-                        "LongProperty.INDEXING_TYPE_RANGE is not supported on this AppSearch "
-                                + "implementation.");
+                longPropertyBuilder.setIndexingType(longProperty.getIndexingType());
             }
             return longPropertyBuilder.build();
         } else if (jetpackProperty instanceof AppSearchSchema.DoublePropertyConfig) {
@@ -139,11 +150,16 @@
             AppSearchSchema.DocumentPropertyConfig documentProperty =
                     (AppSearchSchema.DocumentPropertyConfig) jetpackProperty;
             return new com.google.android.gms.appsearch.AppSearchSchema.DocumentPropertyConfig
-                    .Builder(
-                    documentProperty.getName(), documentProperty.getSchemaType())
+                    .Builder(documentProperty.getName(), documentProperty.getSchemaType())
                     .setCardinality(documentProperty.getCardinality())
-                    .setShouldIndexNestedProperties(documentProperty.shouldIndexNestedProperties())
-                    .build();
+                    .setShouldIndexNestedProperties(
+                            documentProperty.shouldIndexNestedProperties())
+                    .addIndexableNestedProperties(
+                            documentProperty.getIndexableNestedProperties()).build();
+        } else if (jetpackProperty instanceof AppSearchSchema.EmbeddingPropertyConfig) {
+            // TODO(b/326656531): Remove this once embedding search APIs are available.
+            throw new UnsupportedOperationException(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG
+                    + " is not available on this AppSearch implementation.");
         } else {
             throw new IllegalArgumentException(
                     "Invalid dataType: " + jetpackProperty.getDataType());
@@ -160,31 +176,45 @@
             com.google.android.gms.appsearch.AppSearchSchema.StringPropertyConfig stringProperty =
                     (com.google.android.gms.appsearch.AppSearchSchema.StringPropertyConfig)
                             gmsProperty;
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            //  available in gms.
             return new AppSearchSchema.StringPropertyConfig.Builder(stringProperty.getName())
                     .setCardinality(stringProperty.getCardinality())
                     .setIndexingType(stringProperty.getIndexingType())
                     .setTokenizerType(stringProperty.getTokenizerType())
+                    .setJoinableValueType(stringProperty.getJoinableValueType())
                     .build();
         } else if (gmsProperty
                 instanceof com.google.android.gms.appsearch.AppSearchSchema.LongPropertyConfig) {
+            com.google.android.gms.appsearch.AppSearchSchema.LongPropertyConfig longProperty =
+                    (com.google.android.gms.appsearch.AppSearchSchema.LongPropertyConfig)
+                            gmsProperty;
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            //  available in gms.
             return new AppSearchSchema.LongPropertyConfig.Builder(
                     gmsProperty.getName())
                     .setCardinality(gmsProperty.getCardinality())
+                    .setIndexingType(longProperty.getIndexingType())
                     .build();
         } else if (gmsProperty
                 instanceof com.google.android.gms.appsearch.AppSearchSchema.DoublePropertyConfig) {
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            //  available in gms.
             return new AppSearchSchema.DoublePropertyConfig.Builder(
                     gmsProperty.getName())
-                    .setCardinality(gmsProperty.getCardinality())
-                    .build();
+                    .setCardinality(gmsProperty.getCardinality()).build();
         } else if (gmsProperty
                 instanceof com.google.android.gms.appsearch.AppSearchSchema.BooleanPropertyConfig) {
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in gms.
             return new AppSearchSchema.BooleanPropertyConfig.Builder(
                     gmsProperty.getName())
                     .setCardinality(gmsProperty.getCardinality())
                     .build();
         } else if (gmsProperty
                 instanceof com.google.android.gms.appsearch.AppSearchSchema.BytesPropertyConfig) {
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            // available in gms.
             return new AppSearchSchema.BytesPropertyConfig.Builder(
                     gmsProperty.getName())
                     .setCardinality(gmsProperty.getCardinality())
@@ -196,13 +226,20 @@
                     documentProperty =
                     (com.google.android.gms.appsearch.AppSearchSchema.DocumentPropertyConfig)
                             gmsProperty;
+            // TODO(b/326987971): Call jetpackBuilder.setDescription() once descriptions become
+            //  available in gms.
             return new AppSearchSchema.DocumentPropertyConfig.Builder(
                     documentProperty.getName(),
                     documentProperty.getSchemaType())
                     .setCardinality(documentProperty.getCardinality())
-                    .setShouldIndexNestedProperties(documentProperty.shouldIndexNestedProperties())
+                    .setShouldIndexNestedProperties(
+                            documentProperty.shouldIndexNestedProperties())
+                    .addIndexableNestedProperties(
+                            documentProperty.getIndexableNestedProperties())
                     .build();
         } else {
+            // TODO(b/326656531) : Add an entry for EmbeddingPropertyConfig once it becomes
+            //  available in gms-appsearch.
             throw new IllegalArgumentException(
                     "Invalid property type " + gmsProperty.getClass()
                             + ": " + gmsProperty);
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchResultToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchResultToGmsConverter.java
index 81a0206..91afc41 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchResultToGmsConverter.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchResultToGmsConverter.java
@@ -68,6 +68,10 @@
                     gmsMatches.get(i));
             builder.addMatchInfo(jetpackMatchInfo);
         }
+        for (com.google.android.gms.appsearch.SearchResult joinedResult :
+                gmsResult.getJoinedResults()) {
+            builder.addJoinedResult(toJetpackSearchResult(joinedResult));
+        }
         return builder.build();
     }
 
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSpecToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSpecToGmsConverter.java
index 1b7701b..0d687c0 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSpecToGmsConverter.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSpecToGmsConverter.java
@@ -27,7 +27,6 @@
 
 /**
  * Translates between Gms and Jetpack versions of {@link SearchSpec}.
-
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public final class SearchSpecToGmsConverter {
@@ -43,10 +42,7 @@
                 new com.google.android.gms.appsearch.SearchSpec.Builder();
 
         if (!jetpackSearchSpec.getAdvancedRankingExpression().isEmpty()) {
-            //TODO(b/274986359) Add GMSCore feature check for Advanced Ranking once available.
-            throw new UnsupportedOperationException(
-                    Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION
-                            + " is not available on this AppSearch implementation.");
+            gmsBuilder.setRankingStrategy(jetpackSearchSpec.getAdvancedRankingExpression());
         } else {
             gmsBuilder.setRankingStrategy(jetpackSearchSpec.getRankingStrategy());
         }
@@ -57,7 +53,6 @@
                 .addFilterNamespaces(jetpackSearchSpec.getFilterNamespaces())
                 .addFilterPackageNames(jetpackSearchSpec.getFilterPackageNames())
                 .setResultCountPerPage(jetpackSearchSpec.getResultCountPerPage())
-                .setRankingStrategy(jetpackSearchSpec.getRankingStrategy())
                 .setOrder(jetpackSearchSpec.getOrder())
                 .setSnippetCount(jetpackSearchSpec.getSnippetCount())
                 .setSnippetCountPerProperty(jetpackSearchSpec.getSnippetCountPerProperty())
@@ -72,36 +67,61 @@
             gmsBuilder.addProjection(projection.getKey(), projection.getValue());
         }
 
-        if (!jetpackSearchSpec.getEnabledFeatures().isEmpty()) {
-            if (jetpackSearchSpec.isNumericSearchEnabled()
-                    || jetpackSearchSpec.isVerbatimSearchEnabled()
-                    || jetpackSearchSpec.isListFilterQueryLanguageEnabled()) {
-                //TODO(b/274986359) Add GMSCore feature check for NUMERIC_SEARCH,
-                // VERBATIM_SEARCH and LIST_FILTER_QUERY_LANGUAGE once available.
-                throw new UnsupportedOperationException(
-                        "Advanced query features (NUMERIC_SEARCH, VERBATIM_SEARCH and "
-                                + "LIST_FILTER_QUERY_LANGUAGE) are not supported with this "
-                                + "backend/Android API level combination.");
+        if (!jetpackSearchSpec.getPropertyWeights().isEmpty()) {
+            for (Map.Entry<String, Map<String, Double>> entry :
+                    jetpackSearchSpec.getPropertyWeights().entrySet()) {
+                gmsBuilder.setPropertyWeights(entry.getKey(), entry.getValue());
             }
         }
 
-        if (!jetpackSearchSpec.getPropertyWeights().isEmpty()) {
-            //TODO(b/274986359) Add GMSCore feature check for Property Weights once available.
-            throw new UnsupportedOperationException(
-                    "Property weights are not supported with this backend/Android API level "
-                            + "combination.");
+        if (!jetpackSearchSpec.getEnabledFeatures().isEmpty()) {
+            if (jetpackSearchSpec.isNumericSearchEnabled()) {
+                gmsBuilder.setNumericSearchEnabled(true);
+            }
+            if (jetpackSearchSpec.isVerbatimSearchEnabled()) {
+                gmsBuilder.setVerbatimSearchEnabled(true);
+            }
+            if (jetpackSearchSpec.isListFilterQueryLanguageEnabled()) {
+                gmsBuilder.setListFilterQueryLanguageEnabled(true);
+            }
+            if (jetpackSearchSpec.isListFilterHasPropertyFunctionEnabled()) {
+                gmsBuilder.setListFilterHasPropertyFunctionEnabled(true);
+            }
+            // Copy beyond-V features
+            if (jetpackSearchSpec.isEmbeddingSearchEnabled()
+                    || !jetpackSearchSpec.getSearchEmbeddings().isEmpty()) {
+                // TODO(b/326656531): Remove this once embedding search APIs are available.
+                throw new UnsupportedOperationException(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG
+                        + " is not available on this AppSearch implementation.");
+            }
+            if (jetpackSearchSpec.isListFilterTokenizeFunctionEnabled()) {
+                // TODO(b/332620561): Remove this once 'tokenize' is supported.
+                throw new UnsupportedOperationException(Features.LIST_FILTER_TOKENIZE_FUNCTION
+                        + " is not available on this AppSearch implementation.");
+            }
         }
 
         if (jetpackSearchSpec.getJoinSpec() != null) {
-            //TODO(b/274986359) Add GMSCore feature check for Joins once available.
-            throw new UnsupportedOperationException("JoinSpec is not available on this "
-                    + "AppSearch implementation.");
+            gmsBuilder.setJoinSpec(JoinSpecToGmsConverter.toGmsJoinSpec(
+                    jetpackSearchSpec.getJoinSpec()));
         }
 
         if (!jetpackSearchSpec.getFilterProperties().isEmpty()) {
-            // TODO(b/296088047): Remove this once property filters become available.
-            throw new UnsupportedOperationException(Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES
-                    + " is not available on this AppSearch implementation.");
+            for (Map.Entry<String, List<String>> entry :
+                    jetpackSearchSpec.getFilterProperties().entrySet()) {
+                gmsBuilder.addFilterProperties(entry.getKey(), entry.getValue());
+            }
+        }
+
+        if (jetpackSearchSpec.getSearchSourceLogTag() != null) {
+            gmsBuilder.setSearchSourceLogTag(jetpackSearchSpec.getSearchSourceLogTag());
+        }
+
+        if (!jetpackSearchSpec.getInformationalRankingExpressions().isEmpty()) {
+            // TODO(b/332642571): Remove this once informational ranking expressions are available.
+            throw new UnsupportedOperationException(
+                    Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS
+                            + " are not available on this AppSearch implementation.");
         }
 
         return gmsBuilder.build();
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSuggestionResultToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSuggestionResultToGmsConverter.java
new file mode 100644
index 0000000..f5b8568
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSuggestionResultToGmsConverter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.playservicesstorage.converter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.SearchSuggestionResult;
+import androidx.core.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link SearchSuggestionResult}.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SearchSuggestionResultToGmsConverter {
+    private SearchSuggestionResultToGmsConverter() {}
+
+    /** Translates from Platform to Jetpack versions of {@linkSearchSuggestionResult}   */
+    @NonNull
+    public static List<SearchSuggestionResult> toGmsSearchSuggestionResults(
+            @NonNull List<com.google.android.gms.appsearch.SearchSuggestionResult>
+                    gmsSearchSuggestionResults) {
+        Preconditions.checkNotNull(gmsSearchSuggestionResults);
+        List<SearchSuggestionResult> jetpackSearchSuggestionResults =
+                new ArrayList<>(gmsSearchSuggestionResults.size());
+        for (int i = 0; i < gmsSearchSuggestionResults.size(); i++) {
+            jetpackSearchSuggestionResults.add(new SearchSuggestionResult.Builder()
+                    .setSuggestedResult(gmsSearchSuggestionResults.get(i).getSuggestedResult())
+                    .build());
+        }
+        return jetpackSearchSuggestionResults;
+    }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSuggestionSpecToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSuggestionSpecToGmsConverter.java
new file mode 100644
index 0000000..18556a7
--- /dev/null
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SearchSuggestionSpecToGmsConverter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.playservicesstorage.converter;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.SearchSuggestionSpec;
+import androidx.core.util.Preconditions;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates between Gms and Jetpack versions of {@link SearchSuggestionSpec}.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SearchSuggestionSpecToGmsConverter {
+    private SearchSuggestionSpecToGmsConverter() {
+    }
+
+    /** Translates from Jetpack to Gms version of {@link SearchSuggestionSpec}. */
+    // Most jetpackSearchSuggestionSpec.get calls cause WrongConstant lint errors because the
+    // methods are not defined as returning the same constants as the corresponding setter
+    // expects, but they do
+    @SuppressLint("WrongConstant")
+    @NonNull
+    public static com.google.android.gms.appsearch.SearchSuggestionSpec toGmsSearchSuggestionSpec(
+            @NonNull SearchSuggestionSpec jetpackSearchSuggestionSpec) {
+        Preconditions.checkNotNull(jetpackSearchSuggestionSpec);
+
+        com.google.android.gms.appsearch.SearchSuggestionSpec.Builder gmsBuilder =
+                new com.google.android.gms.appsearch.SearchSuggestionSpec.Builder(
+                        jetpackSearchSuggestionSpec.getMaximumResultCount());
+
+        gmsBuilder
+                .addFilterNamespaces(jetpackSearchSuggestionSpec.getFilterNamespaces())
+                .addFilterSchemas(jetpackSearchSuggestionSpec.getFilterSchemas())
+                .setRankingStrategy(jetpackSearchSuggestionSpec.getRankingStrategy());
+        for (Map.Entry<String, List<String>> documentIdFilters :
+                jetpackSearchSuggestionSpec.getFilterDocumentIds().entrySet()) {
+            gmsBuilder.addFilterDocumentIds(documentIdFilters.getKey(),
+                    documentIdFilters.getValue());
+        }
+
+        Map<String, List<String>> jetpackFilterProperties =
+                jetpackSearchSuggestionSpec.getFilterProperties();
+        if (!jetpackFilterProperties.isEmpty()) {
+            for (Map.Entry<String, List<String>> entry : jetpackFilterProperties.entrySet()) {
+                gmsBuilder.addFilterProperties(entry.getKey(), entry.getValue());
+            }
+        }
+        return gmsBuilder.build();
+    }
+}
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SetSchemaRequestToGmsConverter.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SetSchemaRequestToGmsConverter.java
index 3929b54..348849c 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SetSchemaRequestToGmsConverter.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/converter/SetSchemaRequestToGmsConverter.java
@@ -22,16 +22,17 @@
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.Migrator;
 import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
 import androidx.appsearch.app.SetSchemaRequest;
 import androidx.appsearch.app.SetSchemaResponse;
 import androidx.core.util.Preconditions;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 /**
  * Translates between Gms and Jetpack versions of {@link SetSchemaRequest}.
-
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public final class SetSchemaRequestToGmsConverter {
@@ -76,6 +77,28 @@
                 }
             }
         }
+        if (!jetpackRequest.getPubliclyVisibleSchemas().isEmpty()) {
+            for (Map.Entry<String, PackageIdentifier> entry :
+                    jetpackRequest.getPubliclyVisibleSchemas().entrySet()) {
+                PackageIdentifier publiclyVisibleTargetPackage = entry.getValue();
+                gmsBuilder.setPubliclyVisibleSchema(
+                        entry.getKey(),
+                        new com.google.android.gms.appsearch.PackageIdentifier(
+                                publiclyVisibleTargetPackage.getPackageName(),
+                                publiclyVisibleTargetPackage.getSha256Certificate()));
+            }
+        }
+
+        if (!jetpackRequest.getSchemasVisibleToConfigs().isEmpty()) {
+            for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry :
+                    jetpackRequest.getSchemasVisibleToConfigs().entrySet()) {
+                for (SchemaVisibilityConfig jetpackConfig : entry.getValue()) {
+                    com.google.android.gms.appsearch.SchemaVisibilityConfig gmsConfig =
+                            toGmsSchemaVisibilityConfig(jetpackConfig);
+                    gmsBuilder.addSchemaTypeVisibleToConfig(entry.getKey(), gmsConfig);
+                }
+            }
+        }
         for (Map.Entry<String, Migrator> entry : jetpackRequest.getMigrators().entrySet()) {
             Migrator jetpackMigrator = entry.getValue();
             com.google.android.gms.appsearch.Migrator gmsMigrator =
@@ -161,4 +184,41 @@
         }
         return jetpackBuilder.build();
     }
+
+    /**
+     * Translates a jetpack {@link SchemaVisibilityConfig} into a gms
+     * {@link com.google.android.gms.appsearch.SchemaVisibilityConfig}.
+     */
+    @NonNull
+    private static com.google.android.gms.appsearch.SchemaVisibilityConfig
+            toGmsSchemaVisibilityConfig(@NonNull SchemaVisibilityConfig jetpackConfig) {
+        Preconditions.checkNotNull(jetpackConfig);
+        com.google.android.gms.appsearch.SchemaVisibilityConfig.Builder gmsBuilder =
+                new com.google.android.gms.appsearch.SchemaVisibilityConfig.Builder();
+
+        // Translate allowedPackages
+        List<PackageIdentifier> allowedPackages = jetpackConfig.getAllowedPackages();
+        for (int i = 0; i < allowedPackages.size(); i++) {
+            gmsBuilder.addAllowedPackage(new com.google.android.gms.appsearch.PackageIdentifier(
+                    allowedPackages.get(i).getPackageName(),
+                    allowedPackages.get(i).getSha256Certificate()));
+        }
+
+        // Translate requiredPermissions
+        for (Set<Integer> requiredPermissions : jetpackConfig.getRequiredPermissions()) {
+            gmsBuilder.addRequiredPermissions(requiredPermissions);
+        }
+
+        // Translate publiclyVisibleTargetPackage
+        PackageIdentifier publiclyVisibleTargetPackage =
+                jetpackConfig.getPubliclyVisibleTargetPackage();
+        if (publiclyVisibleTargetPackage != null) {
+            gmsBuilder.setPubliclyVisibleTargetPackage(
+                    new com.google.android.gms.appsearch.PackageIdentifier(
+                            publiclyVisibleTargetPackage.getPackageName(),
+                            publiclyVisibleTargetPackage.getSha256Certificate()));
+        }
+
+        return gmsBuilder.build();
+    }
 }
diff --git a/appsearch/appsearch-test-util/build.gradle b/appsearch/appsearch-test-util/build.gradle
index 98e2ce4..c6daefc 100644
--- a/appsearch/appsearch-test-util/build.gradle
+++ b/appsearch/appsearch-test-util/build.gradle
@@ -42,5 +42,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.appsearch.testutil"
 }
diff --git a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java
index 6d996e9..7391cbb 100644
--- a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java
+++ b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java
@@ -27,7 +27,9 @@
 import androidx.appsearch.app.GetByDocumentIdRequest;
 import androidx.appsearch.app.SearchResult;
 import androidx.appsearch.app.SearchResults;
+import androidx.appsearch.localstorage.visibilitystore.CallerAccess;
 import androidx.appsearch.localstorage.visibilitystore.VisibilityChecker;
+import androidx.appsearch.localstorage.visibilitystore.VisibilityStore;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -115,14 +117,56 @@
     }
 
     /**
-     * Creates a mock {@link VisibilityChecker}.
+     * Creates a mock {@link VisibilityChecker} where schema is searchable if prefixedSchema is
+     * one of the provided set of visiblePrefixedSchemas and caller does not have system access.
+     *
      * @param visiblePrefixedSchemas Schema types that are accessible to any caller.
-     * @return
+     * @return Mocked {@link VisibilityChecker} instance.
      */
     @NonNull
     public static VisibilityChecker createMockVisibilityChecker(
             @NonNull Set<String> visiblePrefixedSchemas) {
-        return (callerAccess, packageName, prefixedSchema, visibilityStore) ->
-                visiblePrefixedSchemas.contains(prefixedSchema);
+        return new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(
+                    @NonNull CallerAccess callerAccess,
+                    @NonNull String packageName,
+                    @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                return visiblePrefixedSchemas.contains(prefixedSchema);
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String s) {
+                return false;
+            }
+        };
+    }
+
+    /**
+     * Creates a mock {@link VisibilityChecker}, where it can be configured if schema is searchable
+     * by caller and caller does not have system access.
+     *
+     * @param isSchemaSearchableByCaller Schema visibility for caller.
+     * @return Mocked {@link VisibilityChecker} instance.
+     */
+    @NonNull
+    public static VisibilityChecker createMockVisibilityChecker(
+            boolean isSchemaSearchableByCaller) {
+        return new VisibilityChecker() {
+            @Override
+            public boolean isSchemaSearchableByCaller(
+                    @NonNull CallerAccess callerAccess,
+                    @NonNull String packageName,
+                    @NonNull String prefixedSchema,
+                    @NonNull VisibilityStore visibilityStore) {
+                return isSchemaSearchableByCaller;
+            }
+
+            @Override
+            public boolean doesCallerHaveSystemAccess(@NonNull String s) {
+                return false;
+            }
+        };
     }
 }
diff --git a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/SimpleTestLogger.java b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/SimpleTestLogger.java
index 0c9464b..cc3f74c 100644
--- a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/SimpleTestLogger.java
+++ b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/SimpleTestLogger.java
@@ -25,6 +25,7 @@
 import androidx.appsearch.localstorage.stats.OptimizeStats;
 import androidx.appsearch.localstorage.stats.PutDocumentStats;
 import androidx.appsearch.localstorage.stats.RemoveStats;
+import androidx.appsearch.localstorage.stats.SearchSessionStats;
 import androidx.appsearch.localstorage.stats.SearchStats;
 import androidx.appsearch.localstorage.stats.SetSchemaStats;
 import androidx.appsearch.stats.SchemaMigrationStats;
@@ -63,6 +64,8 @@
     /** Holds {@link androidx.appsearch.stats.SchemaMigrationStats} after logging. */
     @Nullable
     public SchemaMigrationStats mSchemaMigrationStats;
+    /** Holds {@link SearchSessionStats} after logging. */
+    @NonNull public List<SearchSessionStats> mSearchSessionsStats = new ArrayList<>();
 
     @Override
     public void logStats(@NonNull CallStats stats) {
@@ -103,4 +106,9 @@
     public void logStats(@NonNull SchemaMigrationStats stats) {
         mSchemaMigrationStats = stats;
     }
+
+    @Override
+    public void logStats(@NonNull List<SearchSessionStats> searchSessionsStats) {
+        mSearchSessionsStats.addAll(searchSessionsStats);
+    }
 }
diff --git a/appsearch/appsearch/api/current.txt b/appsearch/appsearch/api/current.txt
index aff9289..9eff333 100644
--- a/appsearch/appsearch/api/current.txt
+++ b/appsearch/appsearch/api/current.txt
@@ -35,6 +35,12 @@
     method public abstract boolean required() default false;
   }
 
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.EmbeddingProperty {
+    method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE;
+    method public abstract String name() default "";
+    method public abstract boolean required() default false;
+  }
+
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Id {
   }
 
@@ -115,11 +121,14 @@
     field public static final int RESULT_NOT_FOUND = 6; // 0x6
     field public static final int RESULT_OK = 0; // 0x0
     field public static final int RESULT_OUT_OF_SPACE = 5; // 0x5
+    field public static final int RESULT_RATE_LIMITED = 10; // 0xa
     field public static final int RESULT_SECURITY_ERROR = 8; // 0x8
+    field public static final int RESULT_TIMED_OUT = 11; // 0xb
     field public static final int RESULT_UNKNOWN_ERROR = 1; // 0x1
   }
 
   public final class AppSearchSchema {
+    method public String getDescription();
     method public java.util.List<java.lang.String!> getParentTypes();
     method public java.util.List<androidx.appsearch.app.AppSearchSchema.PropertyConfig!> getProperties();
     method public String getSchemaType();
@@ -132,6 +141,7 @@
     ctor public AppSearchSchema.BooleanPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.BooleanPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.BooleanPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.BooleanPropertyConfig.Builder setDescription(String);
   }
 
   public static final class AppSearchSchema.Builder {
@@ -139,6 +149,7 @@
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_ADD_PARENT_TYPE) public androidx.appsearch.app.AppSearchSchema.Builder addParentType(String);
     method public androidx.appsearch.app.AppSearchSchema.Builder addProperty(androidx.appsearch.app.AppSearchSchema.PropertyConfig);
     method public androidx.appsearch.app.AppSearchSchema build();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.Builder setDescription(String);
   }
 
   public static final class AppSearchSchema.BytesPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
@@ -148,6 +159,7 @@
     ctor public AppSearchSchema.BytesPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.BytesPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.BytesPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.BytesPropertyConfig.Builder setDescription(String);
   }
 
   public static final class AppSearchSchema.DocumentPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
@@ -164,6 +176,7 @@
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES) public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder addIndexableNestedPropertyPaths(java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder setDescription(String);
     method public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder setShouldIndexNestedProperties(boolean);
   }
 
@@ -174,6 +187,21 @@
     ctor public AppSearchSchema.DoublePropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.DoublePropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.DoublePropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.DoublePropertyConfig.Builder setDescription(String);
+  }
+
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public static final class AppSearchSchema.EmbeddingPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
+    method public int getIndexingType();
+    field public static final int INDEXING_TYPE_NONE = 0; // 0x0
+    field public static final int INDEXING_TYPE_SIMILARITY = 1; // 0x1
+  }
+
+  public static final class AppSearchSchema.EmbeddingPropertyConfig.Builder {
+    ctor public AppSearchSchema.EmbeddingPropertyConfig.Builder(String);
+    method public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig build();
+    method public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.Builder setDescription(String);
+    method public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.Builder setIndexingType(int);
   }
 
   public static final class AppSearchSchema.LongPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
@@ -186,11 +214,13 @@
     ctor public AppSearchSchema.LongPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.Builder setDescription(String);
     method public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.Builder setIndexingType(int);
   }
 
   public abstract static class AppSearchSchema.PropertyConfig {
     method public int getCardinality();
+    method public String getDescription();
     method public String getName();
     field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
     field public static final int CARDINALITY_REPEATED = 1; // 0x1
@@ -198,7 +228,6 @@
   }
 
   public static final class AppSearchSchema.StringPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
-    method public boolean getDeletionPropagation();
     method public int getIndexingType();
     method public int getJoinableValueType();
     method public int getTokenizerType();
@@ -217,7 +246,7 @@
     ctor public AppSearchSchema.StringPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int);
-    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DELETION_PROPAGATION) public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setDeletionPropagation(boolean);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setDescription(String);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setJoinableValueType(int);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int);
@@ -248,25 +277,47 @@
     method public androidx.appsearch.app.GenericDocument toGenericDocument(T) throws androidx.appsearch.exceptions.AppSearchException;
   }
 
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public final class EmbeddingVector {
+    ctor public EmbeddingVector(float[], String);
+    method public String getModelSignature();
+    method public float[] getValues();
+  }
+
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ENTERPRISE_GLOBAL_SEARCH_SESSION) public interface EnterpriseGlobalSearchSession {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.AppSearchBatchResult<java.lang.String!,androidx.appsearch.app.GenericDocument!>!> getByDocumentIdAsync(String, String, androidx.appsearch.app.GetByDocumentIdRequest);
+    method public androidx.appsearch.app.Features getFeatures();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.GetSchemaResponse!> getSchemaAsync(String, String);
+    method public androidx.appsearch.app.SearchResults search(String, androidx.appsearch.app.SearchSpec);
+  }
+
   public interface Features {
     method public int getMaxIndexedProperties();
     method public boolean isFeatureSupported(String);
     field public static final String ADD_PERMISSIONS_AND_GET_VISIBILITY = "ADD_PERMISSIONS_AND_GET_VISIBILITY";
+    field public static final String ENTERPRISE_GLOBAL_SEARCH_SESSION = "ENTERPRISE_GLOBAL_SEARCH_SESSION";
     field public static final String GLOBAL_SEARCH_SESSION_GET_BY_ID = "GLOBAL_SEARCH_SESSION_GET_BY_ID";
     field public static final String GLOBAL_SEARCH_SESSION_GET_SCHEMA = "GLOBAL_SEARCH_SESSION_GET_SCHEMA";
     field public static final String GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK = "GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK";
     field public static final String JOIN_SPEC_AND_QUALIFIED_ID = "JOIN_SPEC_AND_QUALIFIED_ID";
+    field public static final String LIST_FILTER_HAS_PROPERTY_FUNCTION = "LIST_FILTER_HAS_PROPERTY_FUNCTION";
     field public static final String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
+    field public static final String LIST_FILTER_TOKENIZE_FUNCTION = "LIST_FILTER_TOKENIZE_FUNCTION";
     field public static final String NUMERIC_SEARCH = "NUMERIC_SEARCH";
     field public static final String SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES = "SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES";
     field public static final String SCHEMA_ADD_PARENT_TYPE = "SCHEMA_ADD_PARENT_TYPE";
-    field public static final String SCHEMA_SET_DELETION_PROPAGATION = "SCHEMA_SET_DELETION_PROPAGATION";
+    field public static final String SCHEMA_EMBEDDING_PROPERTY_CONFIG = "SCHEMA_EMBEDDING_PROPERTY_CONFIG";
+    field public static final String SCHEMA_SET_DESCRIPTION = "SCHEMA_SET_DESCRIPTION";
     field public static final String SEARCH_RESULT_MATCH_INFO_SUBMATCH = "SEARCH_RESULT_MATCH_INFO_SUBMATCH";
+    field public static final String SEARCH_SPEC_ADD_FILTER_PROPERTIES = "SEARCH_SPEC_ADD_FILTER_PROPERTIES";
+    field public static final String SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS = "SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS";
     field public static final String SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION = "SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION";
     field public static final String SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA = "SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA";
     field public static final String SEARCH_SPEC_PROPERTY_WEIGHTS = "SEARCH_SPEC_PROPERTY_WEIGHTS";
+    field public static final String SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG = "SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG";
     field public static final String SEARCH_SUGGESTION = "SEARCH_SUGGESTION";
     field public static final String SET_SCHEMA_CIRCULAR_REFERENCES = "SET_SCHEMA_CIRCULAR_REFERENCES";
+    field public static final String SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG = "SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG";
+    field public static final String SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE = "SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE";
     field public static final String TOKENIZER_TYPE_RFC822 = "TOKENIZER_TYPE_RFC822";
     field public static final String VERBATIM_SEARCH = "VERBATIM_SEARCH";
   }
@@ -287,6 +338,8 @@
     method public androidx.appsearch.app.GenericDocument![]? getPropertyDocumentArray(String);
     method public double getPropertyDouble(String);
     method public double[]? getPropertyDoubleArray(String);
+    method public androidx.appsearch.app.EmbeddingVector? getPropertyEmbedding(String);
+    method public androidx.appsearch.app.EmbeddingVector![]? getPropertyEmbeddingArray(String);
     method public long getPropertyLong(String);
     method public long[]? getPropertyLongArray(String);
     method public java.util.Set<java.lang.String!> getPropertyNames();
@@ -301,6 +354,7 @@
   }
 
   public static class GenericDocument.Builder<BuilderType extends androidx.appsearch.app.GenericDocument.Builder> {
+    ctor public GenericDocument.Builder(androidx.appsearch.app.GenericDocument);
     ctor public GenericDocument.Builder(String, String, String);
     method public androidx.appsearch.app.GenericDocument build();
     method public BuilderType clearProperty(String);
@@ -311,6 +365,7 @@
     method public BuilderType setPropertyBytes(String, byte[]!...);
     method public BuilderType setPropertyDocument(String, androidx.appsearch.app.GenericDocument!...);
     method public BuilderType setPropertyDouble(String, double...);
+    method public BuilderType setPropertyEmbedding(String, androidx.appsearch.app.EmbeddingVector!...);
     method public BuilderType setPropertyLong(String, long...);
     method public BuilderType setPropertyString(String, java.lang.String!...);
     method public BuilderType setSchemaType(String);
@@ -336,8 +391,10 @@
   }
 
   public final class GetSchemaResponse {
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,androidx.appsearch.app.PackageIdentifier!> getPubliclyVisibleSchemas();
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,java.util.Set<java.util.Set<java.lang.Integer!>!>!> getRequiredPermissionsForSchemaTypeVisibility();
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Set<java.lang.String!> getSchemaTypesNotDisplayedBySystem();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.SchemaVisibilityConfig!>!> getSchemaTypesVisibleToConfigs();
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.PackageIdentifier!>!> getSchemaTypesVisibleToPackages();
     method public java.util.Set<androidx.appsearch.app.AppSearchSchema!> getSchemas();
     method @IntRange(from=0) public int getVersion();
@@ -348,7 +405,9 @@
     method public androidx.appsearch.app.GetSchemaResponse.Builder addSchema(androidx.appsearch.app.AppSearchSchema);
     method public androidx.appsearch.app.GetSchemaResponse.Builder addSchemaTypeNotDisplayedBySystem(String);
     method public androidx.appsearch.app.GetSchemaResponse build();
+    method public androidx.appsearch.app.GetSchemaResponse.Builder setPubliclyVisibleSchema(String, androidx.appsearch.app.PackageIdentifier);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setRequiredPermissionsForSchemaTypeVisibility(String, java.util.Set<java.util.Set<java.lang.Integer!>!>);
+    method public androidx.appsearch.app.GetSchemaResponse.Builder setSchemaTypeVisibleToConfigs(String, java.util.Set<androidx.appsearch.app.SchemaVisibilityConfig!>);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setSchemaTypeVisibleToPackages(String, java.util.Set<androidx.appsearch.app.PackageIdentifier!>);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setVersion(@IntRange(from=0) int);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setVisibilitySettingSupported(boolean);
@@ -423,6 +482,7 @@
 
   public final class PutDocumentsRequest {
     method public java.util.List<androidx.appsearch.app.GenericDocument!> getGenericDocuments();
+    method public java.util.List<androidx.appsearch.app.GenericDocument!> getTakenActionGenericDocuments();
   }
 
   public static final class PutDocumentsRequest.Builder {
@@ -431,6 +491,8 @@
     method public androidx.appsearch.app.PutDocumentsRequest.Builder addDocuments(java.util.Collection<? extends java.lang.Object!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.PutDocumentsRequest.Builder addGenericDocuments(androidx.appsearch.app.GenericDocument!...);
     method public androidx.appsearch.app.PutDocumentsRequest.Builder addGenericDocuments(java.util.Collection<? extends androidx.appsearch.app.GenericDocument!>);
+    method public androidx.appsearch.app.PutDocumentsRequest.Builder addTakenActions(androidx.appsearch.usagereporting.TakenAction!...) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.PutDocumentsRequest.Builder addTakenActions(java.util.Collection<? extends androidx.appsearch.usagereporting.TakenAction!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.PutDocumentsRequest build();
   }
 
@@ -472,11 +534,28 @@
     method public androidx.appsearch.app.ReportUsageRequest.Builder setUsageTimestampMillis(long);
   }
 
+  public final class SchemaVisibilityConfig {
+    method public java.util.List<androidx.appsearch.app.PackageIdentifier!> getAllowedPackages();
+    method public androidx.appsearch.app.PackageIdentifier? getPubliclyVisibleTargetPackage();
+    method public java.util.Set<java.util.Set<java.lang.Integer!>!> getRequiredPermissions();
+  }
+
+  public static final class SchemaVisibilityConfig.Builder {
+    ctor public SchemaVisibilityConfig.Builder();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder addAllowedPackage(androidx.appsearch.app.PackageIdentifier);
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder addRequiredPermissions(java.util.Set<java.lang.Integer!>);
+    method public androidx.appsearch.app.SchemaVisibilityConfig build();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder clearAllowedPackages();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder clearRequiredPermissions();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder setPubliclyVisibleTargetPackage(androidx.appsearch.app.PackageIdentifier?);
+  }
+
   public final class SearchResult {
     method public String getDatabaseName();
     method public <T> T getDocument(Class<T!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public <T> T getDocument(Class<T!>, java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!>?) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.GenericDocument getGenericDocument();
+    method public java.util.List<java.lang.Double!> getInformationalRankingSignals();
     method public java.util.List<androidx.appsearch.app.SearchResult!> getJoinedResults();
     method public java.util.List<androidx.appsearch.app.SearchResult.MatchInfo!> getMatchInfos();
     method public String getPackageName();
@@ -485,6 +564,7 @@
 
   public static final class SearchResult.Builder {
     ctor public SearchResult.Builder(String, String);
+    method public androidx.appsearch.app.SearchResult.Builder addInformationalRankingSignal(double);
     method public androidx.appsearch.app.SearchResult.Builder addJoinedResult(androidx.appsearch.app.SearchResult);
     method public androidx.appsearch.app.SearchResult.Builder addMatchInfo(androidx.appsearch.app.SearchResult.MatchInfo);
     method public androidx.appsearch.app.SearchResult build();
@@ -526,9 +606,12 @@
 
   public final class SearchSpec {
     method public String getAdvancedRankingExpression();
+    method public int getDefaultEmbeddingSearchMetricType();
     method public java.util.List<java.lang.String!> getFilterNamespaces();
     method public java.util.List<java.lang.String!> getFilterPackageNames();
+    method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterProperties();
     method public java.util.List<java.lang.String!> getFilterSchemas();
+    method public java.util.List<java.lang.String!> getInformationalRankingExpressions();
     method public androidx.appsearch.app.JoinSpec? getJoinSpec();
     method public int getMaxSnippetSize();
     method public int getOrder();
@@ -540,18 +623,26 @@
     method public int getResultCountPerPage();
     method public int getResultGroupingLimit();
     method public int getResultGroupingTypeFlags();
+    method public java.util.List<androidx.appsearch.app.EmbeddingVector!> getSearchEmbeddings();
+    method public String? getSearchSourceLogTag();
     method public int getSnippetCount();
     method public int getSnippetCountPerProperty();
     method public int getTermMatch();
+    method public boolean isEmbeddingSearchEnabled();
+    method public boolean isListFilterHasPropertyFunctionEnabled();
     method public boolean isListFilterQueryLanguageEnabled();
+    method public boolean isListFilterTokenizeFunctionEnabled();
     method public boolean isNumericSearchEnabled();
     method public boolean isVerbatimSearchEnabled();
+    field public static final int EMBEDDING_SEARCH_METRIC_TYPE_COSINE = 1; // 0x1
+    field public static final int EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT = 2; // 0x2
+    field public static final int EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN = 3; // 0x3
     field public static final int GROUPING_TYPE_PER_NAMESPACE = 2; // 0x2
     field public static final int GROUPING_TYPE_PER_PACKAGE = 1; // 0x1
     field @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA) public static final int GROUPING_TYPE_PER_SCHEMA = 4; // 0x4
     field public static final int ORDER_ASCENDING = 1; // 0x1
     field public static final int ORDER_DESCENDING = 0; // 0x0
-    field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
+    field @Deprecated public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
     field public static final int RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION = 9; // 0x9
     field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2
     field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
@@ -562,6 +653,7 @@
     field public static final int RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP = 7; // 0x7
     field public static final int RANKING_STRATEGY_USAGE_COUNT = 4; // 0x4
     field public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; // 0x5
+    field public static final String SCHEMA_TYPE_WILDCARD = "*";
     field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
     field public static final int TERM_MATCH_PREFIX = 2; // 0x2
   }
@@ -574,15 +666,27 @@
     method public androidx.appsearch.app.SearchSpec.Builder addFilterNamespaces(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterPackageNames(java.lang.String!...);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterPackageNames(java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterProperties(Class<? extends java.lang.Object!>, java.util.Collection<java.lang.String!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterProperties(String, java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterPropertyPaths(Class<? extends java.lang.Object!>, java.util.Collection<androidx.appsearch.app.PropertyPath!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterPropertyPaths(String, java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterSchemas(java.lang.String!...);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterSchemas(java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS) public androidx.appsearch.app.SearchSpec.Builder addInformationalRankingExpressions(java.lang.String!...);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS) public androidx.appsearch.app.SearchSpec.Builder addInformationalRankingExpressions(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addProjection(String, java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addProjectionPaths(String, java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.SearchSpec.Builder addProjectionPathsForDocumentClass(Class<? extends java.lang.Object!>, java.util.Collection<androidx.appsearch.app.PropertyPath!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SearchSpec.Builder addProjectionsForDocumentClass(Class<? extends java.lang.Object!>, java.util.Collection<java.lang.String!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder addSearchEmbeddings(androidx.appsearch.app.EmbeddingVector!...);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder addSearchEmbeddings(java.util.Collection<androidx.appsearch.app.EmbeddingVector!>);
     method public androidx.appsearch.app.SearchSpec build();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder setDefaultEmbeddingSearchMetricType(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder setEmbeddingSearchEnabled(boolean);
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.JOIN_SPEC_AND_QUALIFIED_ID) public androidx.appsearch.app.SearchSpec.Builder setJoinSpec(androidx.appsearch.app.JoinSpec);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.LIST_FILTER_HAS_PROPERTY_FUNCTION) public androidx.appsearch.app.SearchSpec.Builder setListFilterHasPropertyFunctionEnabled(boolean);
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.LIST_FILTER_QUERY_LANGUAGE) public androidx.appsearch.app.SearchSpec.Builder setListFilterQueryLanguageEnabled(boolean);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.LIST_FILTER_TOKENIZE_FUNCTION) public androidx.appsearch.app.SearchSpec.Builder setListFilterTokenizeFunctionEnabled(boolean);
     method public androidx.appsearch.app.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=0x2710) int);
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.NUMERIC_SEARCH) public androidx.appsearch.app.SearchSpec.Builder setNumericSearchEnabled(boolean);
     method public androidx.appsearch.app.SearchSpec.Builder setOrder(int);
@@ -594,6 +698,7 @@
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION) public androidx.appsearch.app.SearchSpec.Builder setRankingStrategy(String);
     method public androidx.appsearch.app.SearchSpec.Builder setResultCountPerPage(@IntRange(from=0, to=0x2710) int);
     method public androidx.appsearch.app.SearchSpec.Builder setResultGrouping(int, int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG) public androidx.appsearch.app.SearchSpec.Builder setSearchSourceLogTag(String);
     method public androidx.appsearch.app.SearchSpec.Builder setSnippetCount(@IntRange(from=0, to=0x2710) int);
     method public androidx.appsearch.app.SearchSpec.Builder setSnippetCountPerProperty(@IntRange(from=0, to=0x2710) int);
     method public androidx.appsearch.app.SearchSpec.Builder setTermMatch(int);
@@ -613,6 +718,7 @@
   public final class SearchSuggestionSpec {
     method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterDocumentIds();
     method public java.util.List<java.lang.String!> getFilterNamespaces();
+    method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterProperties();
     method public java.util.List<java.lang.String!> getFilterSchemas();
     method public int getMaximumResultCount();
     method public int getRankingStrategy();
@@ -629,6 +735,10 @@
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterDocumentIds(String, java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterNamespaces(java.lang.String!...);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterNamespaces(java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterProperties(Class<? extends java.lang.Object!>, java.util.Collection<java.lang.String!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterProperties(String, java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterPropertyPaths(Class<? extends java.lang.Object!>, java.util.Collection<androidx.appsearch.app.PropertyPath!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterPropertyPaths(String, java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.lang.String!...);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSuggestionSpec build();
@@ -637,9 +747,11 @@
 
   public final class SetSchemaRequest {
     method public java.util.Map<java.lang.String!,androidx.appsearch.app.Migrator!> getMigrators();
+    method public java.util.Map<java.lang.String!,androidx.appsearch.app.PackageIdentifier!> getPubliclyVisibleSchemas();
     method public java.util.Map<java.lang.String!,java.util.Set<java.util.Set<java.lang.Integer!>!>!> getRequiredPermissionsForSchemaTypeVisibility();
     method public java.util.Set<androidx.appsearch.app.AppSearchSchema!> getSchemas();
     method public java.util.Set<java.lang.String!> getSchemasNotDisplayedBySystem();
+    method public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.SchemaVisibilityConfig!>!> getSchemasVisibleToConfigs();
     method public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.PackageIdentifier!>!> getSchemasVisibleToPackages();
     method @IntRange(from=1) public int getVersion();
     method public boolean isForceOverride();
@@ -653,26 +765,32 @@
 
   public static final class SetSchemaRequest.Builder {
     ctor public SetSchemaRequest.Builder();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder addDocumentClassVisibleToConfig(Class<? extends java.lang.Object!>, androidx.appsearch.app.SchemaVisibilityConfig) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder addDocumentClasses(Class<? extends java.lang.Object!>!...) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder addDocumentClasses(java.util.Collection<? extends java.lang.Class<? extends java.lang.Object!>!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder addRequiredPermissionsForDocumentClassVisibility(Class<? extends java.lang.Object!>, java.util.Set<java.lang.Integer!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder addRequiredPermissionsForSchemaTypeVisibility(String, java.util.Set<java.lang.Integer!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder addSchemaTypeVisibleToConfig(String, androidx.appsearch.app.SchemaVisibilityConfig);
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchemas(androidx.appsearch.app.AppSearchSchema!...);
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchemas(java.util.Collection<androidx.appsearch.app.AppSearchSchema!>);
     method public androidx.appsearch.app.SetSchemaRequest build();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder clearDocumentClassVisibleToConfigs(Class<? extends java.lang.Object!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder clearRequiredPermissionsForDocumentClassVisibility(Class<? extends java.lang.Object!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder clearRequiredPermissionsForSchemaTypeVisibility(String);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder clearSchemaTypeVisibleToConfigs(String);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setDocumentClassDisplayedBySystem(Class<? extends java.lang.Object!>, boolean) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder setDocumentClassVisibilityForPackage(Class<? extends java.lang.Object!>, boolean, androidx.appsearch.app.PackageIdentifier) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder setForceOverride(boolean);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setMigrator(String, androidx.appsearch.app.Migrator);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setMigrators(java.util.Map<java.lang.String!,androidx.appsearch.app.Migrator!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE) public androidx.appsearch.app.SetSchemaRequest.Builder setPubliclyVisibleDocumentClass(Class<? extends java.lang.Object!>, androidx.appsearch.app.PackageIdentifier?) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE) public androidx.appsearch.app.SetSchemaRequest.Builder setPubliclyVisibleSchema(String, androidx.appsearch.app.PackageIdentifier?);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(String, boolean);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(String, boolean, androidx.appsearch.app.PackageIdentifier);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setVersion(@IntRange(from=1) int);
   }
 
-  public class SetSchemaResponse {
+  public final class SetSchemaResponse {
     method public java.util.Set<java.lang.String!> getDeletedTypes();
     method public java.util.Set<java.lang.String!> getIncompatibleTypes();
     method public java.util.Set<java.lang.String!> getMigratedTypes();
@@ -700,7 +818,7 @@
     method public String getSchemaType();
   }
 
-  public class StorageInfo {
+  public final class StorageInfo {
     method public int getAliveDocumentsCount();
     method public int getAliveNamespacesCount();
     method public long getSizeBytes();
@@ -771,6 +889,51 @@
 
 }
 
+package androidx.appsearch.usagereporting {
+
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.JOIN_SPEC_AND_QUALIFIED_ID) @androidx.appsearch.annotation.Document(name="builtin:ClickAction") public class ClickAction extends androidx.appsearch.usagereporting.TakenAction {
+    method public String? getQuery();
+    method public String? getReferencedQualifiedId();
+    method public int getResultRankGlobal();
+    method public int getResultRankInBlock();
+    method public long getTimeStayOnResultMillis();
+  }
+
+  @androidx.appsearch.annotation.Document.BuilderProducer public static final class ClickAction.Builder {
+    ctor public ClickAction.Builder(androidx.appsearch.usagereporting.ClickAction);
+    ctor public ClickAction.Builder(String, String, long);
+    method public androidx.appsearch.usagereporting.ClickAction build();
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setDocumentTtlMillis(long);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setQuery(String?);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setReferencedQualifiedId(String?);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setResultRankGlobal(int);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setResultRankInBlock(int);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setTimeStayOnResultMillis(long);
+  }
+
+  @androidx.appsearch.annotation.Document(name="builtin:SearchAction") public class SearchAction extends androidx.appsearch.usagereporting.TakenAction {
+    method public int getFetchedResultCount();
+    method public String? getQuery();
+  }
+
+  @androidx.appsearch.annotation.Document.BuilderProducer public static final class SearchAction.Builder {
+    ctor public SearchAction.Builder(androidx.appsearch.usagereporting.SearchAction);
+    ctor public SearchAction.Builder(String, String, long);
+    method public androidx.appsearch.usagereporting.SearchAction build();
+    method public androidx.appsearch.usagereporting.SearchAction.Builder setDocumentTtlMillis(long);
+    method public androidx.appsearch.usagereporting.SearchAction.Builder setFetchedResultCount(int);
+    method public androidx.appsearch.usagereporting.SearchAction.Builder setQuery(String?);
+  }
+
+  @androidx.appsearch.annotation.Document(name="builtin:TakenAction") public abstract class TakenAction {
+    method public long getActionTimestampMillis();
+    method public long getDocumentTtlMillis();
+    method public String getId();
+    method public String getNamespace();
+  }
+
+}
+
 package androidx.appsearch.util {
 
   public class DocumentIdUtil {
diff --git a/appsearch/appsearch/api/restricted_current.txt b/appsearch/appsearch/api/restricted_current.txt
index aff9289..9eff333 100644
--- a/appsearch/appsearch/api/restricted_current.txt
+++ b/appsearch/appsearch/api/restricted_current.txt
@@ -35,6 +35,12 @@
     method public abstract boolean required() default false;
   }
 
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.EmbeddingProperty {
+    method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE;
+    method public abstract String name() default "";
+    method public abstract boolean required() default false;
+  }
+
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Id {
   }
 
@@ -115,11 +121,14 @@
     field public static final int RESULT_NOT_FOUND = 6; // 0x6
     field public static final int RESULT_OK = 0; // 0x0
     field public static final int RESULT_OUT_OF_SPACE = 5; // 0x5
+    field public static final int RESULT_RATE_LIMITED = 10; // 0xa
     field public static final int RESULT_SECURITY_ERROR = 8; // 0x8
+    field public static final int RESULT_TIMED_OUT = 11; // 0xb
     field public static final int RESULT_UNKNOWN_ERROR = 1; // 0x1
   }
 
   public final class AppSearchSchema {
+    method public String getDescription();
     method public java.util.List<java.lang.String!> getParentTypes();
     method public java.util.List<androidx.appsearch.app.AppSearchSchema.PropertyConfig!> getProperties();
     method public String getSchemaType();
@@ -132,6 +141,7 @@
     ctor public AppSearchSchema.BooleanPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.BooleanPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.BooleanPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.BooleanPropertyConfig.Builder setDescription(String);
   }
 
   public static final class AppSearchSchema.Builder {
@@ -139,6 +149,7 @@
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_ADD_PARENT_TYPE) public androidx.appsearch.app.AppSearchSchema.Builder addParentType(String);
     method public androidx.appsearch.app.AppSearchSchema.Builder addProperty(androidx.appsearch.app.AppSearchSchema.PropertyConfig);
     method public androidx.appsearch.app.AppSearchSchema build();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.Builder setDescription(String);
   }
 
   public static final class AppSearchSchema.BytesPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
@@ -148,6 +159,7 @@
     ctor public AppSearchSchema.BytesPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.BytesPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.BytesPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.BytesPropertyConfig.Builder setDescription(String);
   }
 
   public static final class AppSearchSchema.DocumentPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
@@ -164,6 +176,7 @@
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES) public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder addIndexableNestedPropertyPaths(java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder setDescription(String);
     method public androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig.Builder setShouldIndexNestedProperties(boolean);
   }
 
@@ -174,6 +187,21 @@
     ctor public AppSearchSchema.DoublePropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.DoublePropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.DoublePropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.DoublePropertyConfig.Builder setDescription(String);
+  }
+
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public static final class AppSearchSchema.EmbeddingPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
+    method public int getIndexingType();
+    field public static final int INDEXING_TYPE_NONE = 0; // 0x0
+    field public static final int INDEXING_TYPE_SIMILARITY = 1; // 0x1
+  }
+
+  public static final class AppSearchSchema.EmbeddingPropertyConfig.Builder {
+    ctor public AppSearchSchema.EmbeddingPropertyConfig.Builder(String);
+    method public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig build();
+    method public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.Builder setDescription(String);
+    method public androidx.appsearch.app.AppSearchSchema.EmbeddingPropertyConfig.Builder setIndexingType(int);
   }
 
   public static final class AppSearchSchema.LongPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
@@ -186,11 +214,13 @@
     ctor public AppSearchSchema.LongPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.Builder setCardinality(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.Builder setDescription(String);
     method public androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.Builder setIndexingType(int);
   }
 
   public abstract static class AppSearchSchema.PropertyConfig {
     method public int getCardinality();
+    method public String getDescription();
     method public String getName();
     field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
     field public static final int CARDINALITY_REPEATED = 1; // 0x1
@@ -198,7 +228,6 @@
   }
 
   public static final class AppSearchSchema.StringPropertyConfig extends androidx.appsearch.app.AppSearchSchema.PropertyConfig {
-    method public boolean getDeletionPropagation();
     method public int getIndexingType();
     method public int getJoinableValueType();
     method public int getTokenizerType();
@@ -217,7 +246,7 @@
     ctor public AppSearchSchema.StringPropertyConfig.Builder(String);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig build();
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int);
-    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DELETION_PROPAGATION) public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setDeletionPropagation(boolean);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_SET_DESCRIPTION) public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setDescription(String);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setJoinableValueType(int);
     method public androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int);
@@ -248,25 +277,47 @@
     method public androidx.appsearch.app.GenericDocument toGenericDocument(T) throws androidx.appsearch.exceptions.AppSearchException;
   }
 
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public final class EmbeddingVector {
+    ctor public EmbeddingVector(float[], String);
+    method public String getModelSignature();
+    method public float[] getValues();
+  }
+
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ENTERPRISE_GLOBAL_SEARCH_SESSION) public interface EnterpriseGlobalSearchSession {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.AppSearchBatchResult<java.lang.String!,androidx.appsearch.app.GenericDocument!>!> getByDocumentIdAsync(String, String, androidx.appsearch.app.GetByDocumentIdRequest);
+    method public androidx.appsearch.app.Features getFeatures();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.appsearch.app.GetSchemaResponse!> getSchemaAsync(String, String);
+    method public androidx.appsearch.app.SearchResults search(String, androidx.appsearch.app.SearchSpec);
+  }
+
   public interface Features {
     method public int getMaxIndexedProperties();
     method public boolean isFeatureSupported(String);
     field public static final String ADD_PERMISSIONS_AND_GET_VISIBILITY = "ADD_PERMISSIONS_AND_GET_VISIBILITY";
+    field public static final String ENTERPRISE_GLOBAL_SEARCH_SESSION = "ENTERPRISE_GLOBAL_SEARCH_SESSION";
     field public static final String GLOBAL_SEARCH_SESSION_GET_BY_ID = "GLOBAL_SEARCH_SESSION_GET_BY_ID";
     field public static final String GLOBAL_SEARCH_SESSION_GET_SCHEMA = "GLOBAL_SEARCH_SESSION_GET_SCHEMA";
     field public static final String GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK = "GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK";
     field public static final String JOIN_SPEC_AND_QUALIFIED_ID = "JOIN_SPEC_AND_QUALIFIED_ID";
+    field public static final String LIST_FILTER_HAS_PROPERTY_FUNCTION = "LIST_FILTER_HAS_PROPERTY_FUNCTION";
     field public static final String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
+    field public static final String LIST_FILTER_TOKENIZE_FUNCTION = "LIST_FILTER_TOKENIZE_FUNCTION";
     field public static final String NUMERIC_SEARCH = "NUMERIC_SEARCH";
     field public static final String SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES = "SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES";
     field public static final String SCHEMA_ADD_PARENT_TYPE = "SCHEMA_ADD_PARENT_TYPE";
-    field public static final String SCHEMA_SET_DELETION_PROPAGATION = "SCHEMA_SET_DELETION_PROPAGATION";
+    field public static final String SCHEMA_EMBEDDING_PROPERTY_CONFIG = "SCHEMA_EMBEDDING_PROPERTY_CONFIG";
+    field public static final String SCHEMA_SET_DESCRIPTION = "SCHEMA_SET_DESCRIPTION";
     field public static final String SEARCH_RESULT_MATCH_INFO_SUBMATCH = "SEARCH_RESULT_MATCH_INFO_SUBMATCH";
+    field public static final String SEARCH_SPEC_ADD_FILTER_PROPERTIES = "SEARCH_SPEC_ADD_FILTER_PROPERTIES";
+    field public static final String SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS = "SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS";
     field public static final String SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION = "SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION";
     field public static final String SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA = "SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA";
     field public static final String SEARCH_SPEC_PROPERTY_WEIGHTS = "SEARCH_SPEC_PROPERTY_WEIGHTS";
+    field public static final String SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG = "SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG";
     field public static final String SEARCH_SUGGESTION = "SEARCH_SUGGESTION";
     field public static final String SET_SCHEMA_CIRCULAR_REFERENCES = "SET_SCHEMA_CIRCULAR_REFERENCES";
+    field public static final String SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG = "SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG";
+    field public static final String SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE = "SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE";
     field public static final String TOKENIZER_TYPE_RFC822 = "TOKENIZER_TYPE_RFC822";
     field public static final String VERBATIM_SEARCH = "VERBATIM_SEARCH";
   }
@@ -287,6 +338,8 @@
     method public androidx.appsearch.app.GenericDocument![]? getPropertyDocumentArray(String);
     method public double getPropertyDouble(String);
     method public double[]? getPropertyDoubleArray(String);
+    method public androidx.appsearch.app.EmbeddingVector? getPropertyEmbedding(String);
+    method public androidx.appsearch.app.EmbeddingVector![]? getPropertyEmbeddingArray(String);
     method public long getPropertyLong(String);
     method public long[]? getPropertyLongArray(String);
     method public java.util.Set<java.lang.String!> getPropertyNames();
@@ -301,6 +354,7 @@
   }
 
   public static class GenericDocument.Builder<BuilderType extends androidx.appsearch.app.GenericDocument.Builder> {
+    ctor public GenericDocument.Builder(androidx.appsearch.app.GenericDocument);
     ctor public GenericDocument.Builder(String, String, String);
     method public androidx.appsearch.app.GenericDocument build();
     method public BuilderType clearProperty(String);
@@ -311,6 +365,7 @@
     method public BuilderType setPropertyBytes(String, byte[]!...);
     method public BuilderType setPropertyDocument(String, androidx.appsearch.app.GenericDocument!...);
     method public BuilderType setPropertyDouble(String, double...);
+    method public BuilderType setPropertyEmbedding(String, androidx.appsearch.app.EmbeddingVector!...);
     method public BuilderType setPropertyLong(String, long...);
     method public BuilderType setPropertyString(String, java.lang.String!...);
     method public BuilderType setSchemaType(String);
@@ -336,8 +391,10 @@
   }
 
   public final class GetSchemaResponse {
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,androidx.appsearch.app.PackageIdentifier!> getPubliclyVisibleSchemas();
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,java.util.Set<java.util.Set<java.lang.Integer!>!>!> getRequiredPermissionsForSchemaTypeVisibility();
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Set<java.lang.String!> getSchemaTypesNotDisplayedBySystem();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.SchemaVisibilityConfig!>!> getSchemaTypesVisibleToConfigs();
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.PackageIdentifier!>!> getSchemaTypesVisibleToPackages();
     method public java.util.Set<androidx.appsearch.app.AppSearchSchema!> getSchemas();
     method @IntRange(from=0) public int getVersion();
@@ -348,7 +405,9 @@
     method public androidx.appsearch.app.GetSchemaResponse.Builder addSchema(androidx.appsearch.app.AppSearchSchema);
     method public androidx.appsearch.app.GetSchemaResponse.Builder addSchemaTypeNotDisplayedBySystem(String);
     method public androidx.appsearch.app.GetSchemaResponse build();
+    method public androidx.appsearch.app.GetSchemaResponse.Builder setPubliclyVisibleSchema(String, androidx.appsearch.app.PackageIdentifier);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setRequiredPermissionsForSchemaTypeVisibility(String, java.util.Set<java.util.Set<java.lang.Integer!>!>);
+    method public androidx.appsearch.app.GetSchemaResponse.Builder setSchemaTypeVisibleToConfigs(String, java.util.Set<androidx.appsearch.app.SchemaVisibilityConfig!>);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setSchemaTypeVisibleToPackages(String, java.util.Set<androidx.appsearch.app.PackageIdentifier!>);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setVersion(@IntRange(from=0) int);
     method public androidx.appsearch.app.GetSchemaResponse.Builder setVisibilitySettingSupported(boolean);
@@ -423,6 +482,7 @@
 
   public final class PutDocumentsRequest {
     method public java.util.List<androidx.appsearch.app.GenericDocument!> getGenericDocuments();
+    method public java.util.List<androidx.appsearch.app.GenericDocument!> getTakenActionGenericDocuments();
   }
 
   public static final class PutDocumentsRequest.Builder {
@@ -431,6 +491,8 @@
     method public androidx.appsearch.app.PutDocumentsRequest.Builder addDocuments(java.util.Collection<? extends java.lang.Object!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.PutDocumentsRequest.Builder addGenericDocuments(androidx.appsearch.app.GenericDocument!...);
     method public androidx.appsearch.app.PutDocumentsRequest.Builder addGenericDocuments(java.util.Collection<? extends androidx.appsearch.app.GenericDocument!>);
+    method public androidx.appsearch.app.PutDocumentsRequest.Builder addTakenActions(androidx.appsearch.usagereporting.TakenAction!...) throws androidx.appsearch.exceptions.AppSearchException;
+    method public androidx.appsearch.app.PutDocumentsRequest.Builder addTakenActions(java.util.Collection<? extends androidx.appsearch.usagereporting.TakenAction!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.PutDocumentsRequest build();
   }
 
@@ -472,11 +534,28 @@
     method public androidx.appsearch.app.ReportUsageRequest.Builder setUsageTimestampMillis(long);
   }
 
+  public final class SchemaVisibilityConfig {
+    method public java.util.List<androidx.appsearch.app.PackageIdentifier!> getAllowedPackages();
+    method public androidx.appsearch.app.PackageIdentifier? getPubliclyVisibleTargetPackage();
+    method public java.util.Set<java.util.Set<java.lang.Integer!>!> getRequiredPermissions();
+  }
+
+  public static final class SchemaVisibilityConfig.Builder {
+    ctor public SchemaVisibilityConfig.Builder();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder addAllowedPackage(androidx.appsearch.app.PackageIdentifier);
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder addRequiredPermissions(java.util.Set<java.lang.Integer!>);
+    method public androidx.appsearch.app.SchemaVisibilityConfig build();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder clearAllowedPackages();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder clearRequiredPermissions();
+    method public androidx.appsearch.app.SchemaVisibilityConfig.Builder setPubliclyVisibleTargetPackage(androidx.appsearch.app.PackageIdentifier?);
+  }
+
   public final class SearchResult {
     method public String getDatabaseName();
     method public <T> T getDocument(Class<T!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public <T> T getDocument(Class<T!>, java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!>?) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.GenericDocument getGenericDocument();
+    method public java.util.List<java.lang.Double!> getInformationalRankingSignals();
     method public java.util.List<androidx.appsearch.app.SearchResult!> getJoinedResults();
     method public java.util.List<androidx.appsearch.app.SearchResult.MatchInfo!> getMatchInfos();
     method public String getPackageName();
@@ -485,6 +564,7 @@
 
   public static final class SearchResult.Builder {
     ctor public SearchResult.Builder(String, String);
+    method public androidx.appsearch.app.SearchResult.Builder addInformationalRankingSignal(double);
     method public androidx.appsearch.app.SearchResult.Builder addJoinedResult(androidx.appsearch.app.SearchResult);
     method public androidx.appsearch.app.SearchResult.Builder addMatchInfo(androidx.appsearch.app.SearchResult.MatchInfo);
     method public androidx.appsearch.app.SearchResult build();
@@ -526,9 +606,12 @@
 
   public final class SearchSpec {
     method public String getAdvancedRankingExpression();
+    method public int getDefaultEmbeddingSearchMetricType();
     method public java.util.List<java.lang.String!> getFilterNamespaces();
     method public java.util.List<java.lang.String!> getFilterPackageNames();
+    method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterProperties();
     method public java.util.List<java.lang.String!> getFilterSchemas();
+    method public java.util.List<java.lang.String!> getInformationalRankingExpressions();
     method public androidx.appsearch.app.JoinSpec? getJoinSpec();
     method public int getMaxSnippetSize();
     method public int getOrder();
@@ -540,18 +623,26 @@
     method public int getResultCountPerPage();
     method public int getResultGroupingLimit();
     method public int getResultGroupingTypeFlags();
+    method public java.util.List<androidx.appsearch.app.EmbeddingVector!> getSearchEmbeddings();
+    method public String? getSearchSourceLogTag();
     method public int getSnippetCount();
     method public int getSnippetCountPerProperty();
     method public int getTermMatch();
+    method public boolean isEmbeddingSearchEnabled();
+    method public boolean isListFilterHasPropertyFunctionEnabled();
     method public boolean isListFilterQueryLanguageEnabled();
+    method public boolean isListFilterTokenizeFunctionEnabled();
     method public boolean isNumericSearchEnabled();
     method public boolean isVerbatimSearchEnabled();
+    field public static final int EMBEDDING_SEARCH_METRIC_TYPE_COSINE = 1; // 0x1
+    field public static final int EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT = 2; // 0x2
+    field public static final int EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN = 3; // 0x3
     field public static final int GROUPING_TYPE_PER_NAMESPACE = 2; // 0x2
     field public static final int GROUPING_TYPE_PER_PACKAGE = 1; // 0x1
     field @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA) public static final int GROUPING_TYPE_PER_SCHEMA = 4; // 0x4
     field public static final int ORDER_ASCENDING = 1; // 0x1
     field public static final int ORDER_DESCENDING = 0; // 0x0
-    field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
+    field @Deprecated public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
     field public static final int RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION = 9; // 0x9
     field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2
     field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
@@ -562,6 +653,7 @@
     field public static final int RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP = 7; // 0x7
     field public static final int RANKING_STRATEGY_USAGE_COUNT = 4; // 0x4
     field public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; // 0x5
+    field public static final String SCHEMA_TYPE_WILDCARD = "*";
     field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
     field public static final int TERM_MATCH_PREFIX = 2; // 0x2
   }
@@ -574,15 +666,27 @@
     method public androidx.appsearch.app.SearchSpec.Builder addFilterNamespaces(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterPackageNames(java.lang.String!...);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterPackageNames(java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterProperties(Class<? extends java.lang.Object!>, java.util.Collection<java.lang.String!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterProperties(String, java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterPropertyPaths(Class<? extends java.lang.Object!>, java.util.Collection<androidx.appsearch.app.PropertyPath!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSpec.Builder addFilterPropertyPaths(String, java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterSchemas(java.lang.String!...);
     method public androidx.appsearch.app.SearchSpec.Builder addFilterSchemas(java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS) public androidx.appsearch.app.SearchSpec.Builder addInformationalRankingExpressions(java.lang.String!...);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS) public androidx.appsearch.app.SearchSpec.Builder addInformationalRankingExpressions(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addProjection(String, java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSpec.Builder addProjectionPaths(String, java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.SearchSpec.Builder addProjectionPathsForDocumentClass(Class<? extends java.lang.Object!>, java.util.Collection<androidx.appsearch.app.PropertyPath!>) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SearchSpec.Builder addProjectionsForDocumentClass(Class<? extends java.lang.Object!>, java.util.Collection<java.lang.String!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder addSearchEmbeddings(androidx.appsearch.app.EmbeddingVector!...);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder addSearchEmbeddings(java.util.Collection<androidx.appsearch.app.EmbeddingVector!>);
     method public androidx.appsearch.app.SearchSpec build();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder setDefaultEmbeddingSearchMetricType(int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) public androidx.appsearch.app.SearchSpec.Builder setEmbeddingSearchEnabled(boolean);
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.JOIN_SPEC_AND_QUALIFIED_ID) public androidx.appsearch.app.SearchSpec.Builder setJoinSpec(androidx.appsearch.app.JoinSpec);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.LIST_FILTER_HAS_PROPERTY_FUNCTION) public androidx.appsearch.app.SearchSpec.Builder setListFilterHasPropertyFunctionEnabled(boolean);
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.LIST_FILTER_QUERY_LANGUAGE) public androidx.appsearch.app.SearchSpec.Builder setListFilterQueryLanguageEnabled(boolean);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.LIST_FILTER_TOKENIZE_FUNCTION) public androidx.appsearch.app.SearchSpec.Builder setListFilterTokenizeFunctionEnabled(boolean);
     method public androidx.appsearch.app.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=0x2710) int);
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.NUMERIC_SEARCH) public androidx.appsearch.app.SearchSpec.Builder setNumericSearchEnabled(boolean);
     method public androidx.appsearch.app.SearchSpec.Builder setOrder(int);
@@ -594,6 +698,7 @@
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION) public androidx.appsearch.app.SearchSpec.Builder setRankingStrategy(String);
     method public androidx.appsearch.app.SearchSpec.Builder setResultCountPerPage(@IntRange(from=0, to=0x2710) int);
     method public androidx.appsearch.app.SearchSpec.Builder setResultGrouping(int, int);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG) public androidx.appsearch.app.SearchSpec.Builder setSearchSourceLogTag(String);
     method public androidx.appsearch.app.SearchSpec.Builder setSnippetCount(@IntRange(from=0, to=0x2710) int);
     method public androidx.appsearch.app.SearchSpec.Builder setSnippetCountPerProperty(@IntRange(from=0, to=0x2710) int);
     method public androidx.appsearch.app.SearchSpec.Builder setTermMatch(int);
@@ -613,6 +718,7 @@
   public final class SearchSuggestionSpec {
     method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterDocumentIds();
     method public java.util.List<java.lang.String!> getFilterNamespaces();
+    method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getFilterProperties();
     method public java.util.List<java.lang.String!> getFilterSchemas();
     method public int getMaximumResultCount();
     method public int getRankingStrategy();
@@ -629,6 +735,10 @@
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterDocumentIds(String, java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterNamespaces(java.lang.String!...);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterNamespaces(java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterProperties(Class<? extends java.lang.Object!>, java.util.Collection<java.lang.String!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterProperties(String, java.util.Collection<java.lang.String!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterPropertyPaths(Class<? extends java.lang.Object!>, java.util.Collection<androidx.appsearch.app.PropertyPath!>) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES) public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterPropertyPaths(String, java.util.Collection<androidx.appsearch.app.PropertyPath!>);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.lang.String!...);
     method public androidx.appsearch.app.SearchSuggestionSpec.Builder addFilterSchemas(java.util.Collection<java.lang.String!>);
     method public androidx.appsearch.app.SearchSuggestionSpec build();
@@ -637,9 +747,11 @@
 
   public final class SetSchemaRequest {
     method public java.util.Map<java.lang.String!,androidx.appsearch.app.Migrator!> getMigrators();
+    method public java.util.Map<java.lang.String!,androidx.appsearch.app.PackageIdentifier!> getPubliclyVisibleSchemas();
     method public java.util.Map<java.lang.String!,java.util.Set<java.util.Set<java.lang.Integer!>!>!> getRequiredPermissionsForSchemaTypeVisibility();
     method public java.util.Set<androidx.appsearch.app.AppSearchSchema!> getSchemas();
     method public java.util.Set<java.lang.String!> getSchemasNotDisplayedBySystem();
+    method public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.SchemaVisibilityConfig!>!> getSchemasVisibleToConfigs();
     method public java.util.Map<java.lang.String!,java.util.Set<androidx.appsearch.app.PackageIdentifier!>!> getSchemasVisibleToPackages();
     method @IntRange(from=1) public int getVersion();
     method public boolean isForceOverride();
@@ -653,26 +765,32 @@
 
   public static final class SetSchemaRequest.Builder {
     ctor public SetSchemaRequest.Builder();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder addDocumentClassVisibleToConfig(Class<? extends java.lang.Object!>, androidx.appsearch.app.SchemaVisibilityConfig) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder addDocumentClasses(Class<? extends java.lang.Object!>!...) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder addDocumentClasses(java.util.Collection<? extends java.lang.Class<? extends java.lang.Object!>!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder addRequiredPermissionsForDocumentClassVisibility(Class<? extends java.lang.Object!>, java.util.Set<java.lang.Integer!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder addRequiredPermissionsForSchemaTypeVisibility(String, java.util.Set<java.lang.Integer!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder addSchemaTypeVisibleToConfig(String, androidx.appsearch.app.SchemaVisibilityConfig);
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchemas(androidx.appsearch.app.AppSearchSchema!...);
     method public androidx.appsearch.app.SetSchemaRequest.Builder addSchemas(java.util.Collection<androidx.appsearch.app.AppSearchSchema!>);
     method public androidx.appsearch.app.SetSchemaRequest build();
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder clearDocumentClassVisibleToConfigs(Class<? extends java.lang.Object!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder clearRequiredPermissionsForDocumentClassVisibility(Class<? extends java.lang.Object!>) throws androidx.appsearch.exceptions.AppSearchException;
     method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.ADD_PERMISSIONS_AND_GET_VISIBILITY) public androidx.appsearch.app.SetSchemaRequest.Builder clearRequiredPermissionsForSchemaTypeVisibility(String);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG) public androidx.appsearch.app.SetSchemaRequest.Builder clearSchemaTypeVisibleToConfigs(String);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setDocumentClassDisplayedBySystem(Class<? extends java.lang.Object!>, boolean) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder setDocumentClassVisibilityForPackage(Class<? extends java.lang.Object!>, boolean, androidx.appsearch.app.PackageIdentifier) throws androidx.appsearch.exceptions.AppSearchException;
     method public androidx.appsearch.app.SetSchemaRequest.Builder setForceOverride(boolean);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setMigrator(String, androidx.appsearch.app.Migrator);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setMigrators(java.util.Map<java.lang.String!,androidx.appsearch.app.Migrator!>);
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE) public androidx.appsearch.app.SetSchemaRequest.Builder setPubliclyVisibleDocumentClass(Class<? extends java.lang.Object!>, androidx.appsearch.app.PackageIdentifier?) throws androidx.appsearch.exceptions.AppSearchException;
+    method @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE) public androidx.appsearch.app.SetSchemaRequest.Builder setPubliclyVisibleSchema(String, androidx.appsearch.app.PackageIdentifier?);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(String, boolean);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(String, boolean, androidx.appsearch.app.PackageIdentifier);
     method public androidx.appsearch.app.SetSchemaRequest.Builder setVersion(@IntRange(from=1) int);
   }
 
-  public class SetSchemaResponse {
+  public final class SetSchemaResponse {
     method public java.util.Set<java.lang.String!> getDeletedTypes();
     method public java.util.Set<java.lang.String!> getIncompatibleTypes();
     method public java.util.Set<java.lang.String!> getMigratedTypes();
@@ -700,7 +818,7 @@
     method public String getSchemaType();
   }
 
-  public class StorageInfo {
+  public final class StorageInfo {
     method public int getAliveDocumentsCount();
     method public int getAliveNamespacesCount();
     method public long getSizeBytes();
@@ -771,6 +889,51 @@
 
 }
 
+package androidx.appsearch.usagereporting {
+
+  @RequiresFeature(enforcement="androidx.appsearch.app.Features#isFeatureSupported", name=androidx.appsearch.app.Features.JOIN_SPEC_AND_QUALIFIED_ID) @androidx.appsearch.annotation.Document(name="builtin:ClickAction") public class ClickAction extends androidx.appsearch.usagereporting.TakenAction {
+    method public String? getQuery();
+    method public String? getReferencedQualifiedId();
+    method public int getResultRankGlobal();
+    method public int getResultRankInBlock();
+    method public long getTimeStayOnResultMillis();
+  }
+
+  @androidx.appsearch.annotation.Document.BuilderProducer public static final class ClickAction.Builder {
+    ctor public ClickAction.Builder(androidx.appsearch.usagereporting.ClickAction);
+    ctor public ClickAction.Builder(String, String, long);
+    method public androidx.appsearch.usagereporting.ClickAction build();
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setDocumentTtlMillis(long);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setQuery(String?);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setReferencedQualifiedId(String?);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setResultRankGlobal(int);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setResultRankInBlock(int);
+    method public androidx.appsearch.usagereporting.ClickAction.Builder setTimeStayOnResultMillis(long);
+  }
+
+  @androidx.appsearch.annotation.Document(name="builtin:SearchAction") public class SearchAction extends androidx.appsearch.usagereporting.TakenAction {
+    method public int getFetchedResultCount();
+    method public String? getQuery();
+  }
+
+  @androidx.appsearch.annotation.Document.BuilderProducer public static final class SearchAction.Builder {
+    ctor public SearchAction.Builder(androidx.appsearch.usagereporting.SearchAction);
+    ctor public SearchAction.Builder(String, String, long);
+    method public androidx.appsearch.usagereporting.SearchAction build();
+    method public androidx.appsearch.usagereporting.SearchAction.Builder setDocumentTtlMillis(long);
+    method public androidx.appsearch.usagereporting.SearchAction.Builder setFetchedResultCount(int);
+    method public androidx.appsearch.usagereporting.SearchAction.Builder setQuery(String?);
+  }
+
+  @androidx.appsearch.annotation.Document(name="builtin:TakenAction") public abstract class TakenAction {
+    method public long getActionTimestampMillis();
+    method public long getDocumentTtlMillis();
+    method public String getId();
+    method public String getNamespace();
+  }
+
+}
+
 package androidx.appsearch.util {
 
   public class DocumentIdUtil {
diff --git a/appsearch/appsearch/build.gradle b/appsearch/appsearch/build.gradle
index 8d67baa..3af6377 100644
--- a/appsearch/appsearch/build.gradle
+++ b/appsearch/appsearch/build.gradle
@@ -29,6 +29,7 @@
 }
 
 android {
+    compileSdk 35
     buildTypes.configureEach {
         consumerProguardFiles "proguard-rules.pro"
     }
@@ -44,6 +45,8 @@
     implementation('androidx.concurrent:concurrent-futures:1.0.0')
     implementation("androidx.core:core:1.6.0")
 
+    annotationProcessor project(':appsearch:appsearch-compiler')
+
     androidTestAnnotationProcessor project(':appsearch:appsearch-compiler')
     androidTestImplementation project(':appsearch:appsearch-builtin-types')
     androidTestImplementation project(':appsearch:appsearch-local-storage')
@@ -69,5 +72,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = '2019'
     description = 'AndroidX AppSearch - App Indexing'
-    metalavaK2UastEnabled = true
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java
index 5d52268..7d18f74 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTestBase.java
@@ -24,6 +24,7 @@
 import static androidx.appsearch.testutil.AppSearchTestUtils.checkIsBatchResultSuccess;
 import static androidx.appsearch.testutil.AppSearchTestUtils.convertSearchResultsToDocuments;
 import static androidx.appsearch.testutil.AppSearchTestUtils.doGet;
+import static androidx.appsearch.testutil.AppSearchTestUtils.retrieveAllSearchResults;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -2764,4 +2765,160 @@
         assertThat(person.getFirstName()).isEqualTo("first");
         assertThat(person.getLastName()).isEqualTo("last");
     }
+
+    @Document
+    static class EmailWithEmbedding {
+        @Document.Namespace
+        String mNamespace;
+
+        @Document.Id
+        String mId;
+
+        @Document.CreationTimestampMillis
+        long mCreationTimestampMillis;
+
+        @Document.StringProperty
+        String mSender;
+
+        // Default non-indexable embedding
+        @Document.EmbeddingProperty
+        EmbeddingVector mSenderEmbedding;
+
+        @Document.EmbeddingProperty(indexingType = 1)
+        EmbeddingVector mTitleEmbedding;
+
+        @Document.EmbeddingProperty(indexingType = 1)
+        Collection<EmbeddingVector> mReceiverEmbeddings;
+
+        @Document.EmbeddingProperty(indexingType = 1)
+        EmbeddingVector[] mBodyEmbeddings;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            EmailWithEmbedding email = (EmailWithEmbedding) o;
+            return Objects.equals(mNamespace, email.mNamespace) && Objects.equals(mId,
+                    email.mId) && Objects.equals(mSender, email.mSender)
+                    && Objects.equals(mSenderEmbedding, email.mSenderEmbedding)
+                    && Objects.equals(mTitleEmbedding, email.mTitleEmbedding)
+                    && Objects.equals(mReceiverEmbeddings, email.mReceiverEmbeddings)
+                    && Arrays.equals(mBodyEmbeddings, email.mBodyEmbeddings);
+        }
+
+        public static EmailWithEmbedding createSampleDoc() {
+            EmbeddingVector embedding1 =
+                    new EmbeddingVector(new float[]{1, 2, 3}, "model1");
+            EmbeddingVector embedding2 =
+                    new EmbeddingVector(new float[]{-1, -2, -3}, "model2");
+            EmbeddingVector embedding3 =
+                    new EmbeddingVector(new float[]{0.1f, 0.2f, 0.3f, 0.4f}, "model3");
+            EmbeddingVector embedding4 =
+                    new EmbeddingVector(new float[]{-0.1f, -0.2f, -0.3f, -0.4f}, "model3");
+            EmailWithEmbedding email = new EmailWithEmbedding();
+            email.mNamespace = "namespace";
+            email.mId = "id";
+            email.mCreationTimestampMillis = 1000;
+            email.mSender = "sender";
+            email.mSenderEmbedding = embedding1;
+            email.mTitleEmbedding = embedding2;
+            email.mReceiverEmbeddings = Collections.singletonList(embedding3);
+            email.mBodyEmbeddings = new EmbeddingVector[]{embedding3, embedding4};
+            return email;
+        }
+    }
+
+    @Test
+    public void testEmbeddingGenericDocumentConversion() throws Exception {
+        EmailWithEmbedding inEmail = EmailWithEmbedding.createSampleDoc();
+        GenericDocument genericDocument1 = GenericDocument.fromDocumentClass(inEmail);
+        GenericDocument genericDocument2 = GenericDocument.fromDocumentClass(inEmail);
+        EmailWithEmbedding outEmail = genericDocument2.toDocumentClass(EmailWithEmbedding.class);
+
+        assertThat(inEmail).isNotSameInstanceAs(outEmail);
+        assertThat(inEmail).isEqualTo(outEmail);
+        assertThat(genericDocument1).isNotSameInstanceAs(genericDocument2);
+        assertThat(genericDocument1).isEqualTo(genericDocument2);
+    }
+
+    @Test
+    public void testEmbeddingSearch() throws Exception {
+        assumeTrue(mSession.getFeatures().isFeatureSupported(
+                Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+
+        mSession.setSchemaAsync(new SetSchemaRequest.Builder()
+                .addDocumentClasses(EmailWithEmbedding.class)
+                .build()).get();
+
+        // Create and add a document
+        EmailWithEmbedding email = EmailWithEmbedding.createSampleDoc();
+        checkIsBatchResultSuccess(mSession.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addDocuments(email)
+                        .build()));
+
+        // An empty query should retrieve this document.
+        SearchResults searchResults = mSession.search("",
+                new SearchSpec.Builder().build());
+        List<SearchResult> results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(1);
+        // Convert GenericDocument to EmailWithEmbedding and check values.
+        EmailWithEmbedding outputDocument = results.get(0).getDocument(EmailWithEmbedding.class);
+        assertThat(outputDocument).isEqualTo(email);
+
+        // senderEmbedding is non-indexable, so querying for it will return nothing.
+        searchResults = mSession.search("semanticSearch(getSearchSpecEmbedding(0), 0.9, 1)",
+                new SearchSpec.Builder()
+                        .setDefaultEmbeddingSearchMetricType(
+                                SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
+                        .addSearchEmbeddings(email.mSenderEmbedding)
+                        .setRankingStrategy(
+                                "sum(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                        .setListFilterQueryLanguageEnabled(true)
+                        .setEmbeddingSearchEnabled(true)
+                        .build());
+        results = retrieveAllSearchResults(searchResults);
+        assertThat(results).isEmpty();
+
+        // titleEmbedding is indexable, and querying for it using itself will return a cosine
+        // similarity score of 1.
+        searchResults = mSession.search("semanticSearch(getSearchSpecEmbedding(0), 0.9, 1)",
+                new SearchSpec.Builder()
+                        .setDefaultEmbeddingSearchMetricType(
+                                SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
+                        .addSearchEmbeddings(email.mTitleEmbedding)
+                        .setRankingStrategy(
+                                "sum(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                        .setListFilterQueryLanguageEnabled(true)
+                        .setEmbeddingSearchEnabled(true)
+                        .build());
+        results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(1);
+        assertThat(results.get(0).getRankingSignal()).isWithin(0.00001).of(1);
+        // Convert GenericDocument to EmailWithEmbedding and check values.
+        outputDocument = results.get(0).getDocument(EmailWithEmbedding.class);
+        assertThat(outputDocument).isEqualTo(email);
+
+        // Both receiverEmbeddings and bodyEmbeddings are indexable, and in this specific
+        // document, they together hold three embedding vectors with the same signature.
+        searchResults = mSession.search("semanticSearch(getSearchSpecEmbedding(0), -1, 1)",
+                new SearchSpec.Builder()
+                        .setDefaultEmbeddingSearchMetricType(
+                                SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
+                        // Using one of the three vectors to query
+                        .addSearchEmbeddings(email.mBodyEmbeddings[0])
+                        .setRankingStrategy(
+                                // We should get a score of 3 for "len", since there are three
+                                // embedding vectors matched.
+                                "len(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                        .setListFilterQueryLanguageEnabled(true)
+                        .setEmbeddingSearchEnabled(true)
+                        .build());
+        results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(1);
+        assertThat(results.get(0).getRankingSignal()).isEqualTo(3);
+        // Convert GenericDocument to EmailWithEmbedding and check values.
+        outputDocument = results.get(0).getDocument(EmailWithEmbedding.class);
+        assertThat(outputDocument).isEqualTo(email);
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSchemaInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSchemaInternalTest.java
deleted file mode 100644
index 686c44f..0000000
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSchemaInternalTest.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appsearch.app;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.appsearch.testutil.AppSearchEmail;
-
-import org.junit.Test;
-
-import java.util.List;
-
-/** Tests for private APIs of {@link AppSearchSchema}. */
-public class AppSearchSchemaInternalTest {
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testParentTypes() {
-        AppSearchSchema schema =
-                new AppSearchSchema.Builder("EmailMessage")
-                        .addParentType("Email")
-                        .addParentType("Message")
-                        .build();
-        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testDuplicateParentTypes() {
-        AppSearchSchema schema =
-                new AppSearchSchema.Builder("EmailMessage")
-                        .addParentType("Email")
-                        .addParentType("Message")
-                        .addParentType("Email")
-                        .build();
-        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testDocumentPropertyConfig_indexableNestedPropertyStrings() {
-        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
-                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedProperties("prop1", "prop2", "prop1.prop2")
-                        .build();
-        assertThat(documentPropertyConfig.getIndexableNestedProperties())
-                .containsExactly("prop1", "prop2", "prop1.prop2");
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testDocumentPropertyConfig_indexableNestedPropertyPropertyPaths() {
-        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
-                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedPropertyPaths(
-                                new PropertyPath("prop1"), new PropertyPath("prop1.prop2"))
-                        .build();
-        assertThat(documentPropertyConfig.getIndexableNestedProperties())
-                .containsExactly("prop1", "prop1.prop2");
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testDocumentPropertyConfig_indexableNestedPropertyProperty_duplicatePaths() {
-        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
-                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedPropertyPaths(
-                                new PropertyPath("prop1"), new PropertyPath("prop1.prop2"))
-                        .addIndexableNestedProperties("prop1")
-                        .build();
-        assertThat(documentPropertyConfig.getIndexableNestedProperties())
-                .containsExactly("prop1", "prop1.prop2");
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testDocumentPropertyConfig_reusingBuilderDoesNotAffectPreviouslyBuiltConfigs() {
-        AppSearchSchema.DocumentPropertyConfig.Builder builder =
-                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedProperties("prop1");
-        AppSearchSchema.DocumentPropertyConfig config1 = builder.build();
-        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
-
-        builder.addIndexableNestedProperties("prop2");
-        AppSearchSchema.DocumentPropertyConfig config2 = builder.build();
-        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
-        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
-
-        builder.addIndexableNestedPropertyPaths(new PropertyPath("prop3"));
-        AppSearchSchema.DocumentPropertyConfig config3 = builder.build();
-        assertThat(config3.getIndexableNestedProperties())
-                .containsExactly("prop1", "prop2", "prop3");
-        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
-        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testPropertyConfig() {
-        AppSearchSchema schema =
-                new AppSearchSchema.Builder("Test")
-                        .addProperty(
-                                new AppSearchSchema.StringPropertyConfig.Builder("string")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                AppSearchSchema.StringPropertyConfig
-                                                        .INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                AppSearchSchema.StringPropertyConfig
-                                                        .TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.LongPropertyConfig.Builder("long")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setIndexingType(
-                                                AppSearchSchema.LongPropertyConfig
-                                                        .INDEXING_TYPE_NONE)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.LongPropertyConfig.Builder("indexableLong")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setIndexingType(
-                                                AppSearchSchema.LongPropertyConfig
-                                                        .INDEXING_TYPE_RANGE)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.DoublePropertyConfig.Builder("double")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.DocumentPropertyConfig.Builder(
-                                                "document1", AppSearchEmail.SCHEMA_TYPE)
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                                        .setShouldIndexNestedProperties(true)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.DocumentPropertyConfig.Builder(
-                                                "document2", AppSearchEmail.SCHEMA_TYPE)
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                                        .setShouldIndexNestedProperties(false)
-                                        .addIndexableNestedProperties("path1", "path2", "path3")
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId1")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setJoinableValueType(
-                                                AppSearchSchema.StringPropertyConfig
-                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                                        .build())
-                        .addProperty(
-                                new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId2")
-                                        .setCardinality(
-                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setJoinableValueType(
-                                                AppSearchSchema.StringPropertyConfig
-                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                                        .build())
-                        .build();
-
-        assertThat(schema.getSchemaType()).isEqualTo("Test");
-        List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
-        assertThat(properties).hasSize(10);
-
-        assertThat(properties.get(0).getName()).isEqualTo("string");
-        assertThat(properties.get(0).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
-        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getIndexingType())
-                .isEqualTo(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS);
-        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getTokenizerType())
-                .isEqualTo(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN);
-
-        assertThat(properties.get(1).getName()).isEqualTo("long");
-        assertThat(properties.get(1).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(((AppSearchSchema.LongPropertyConfig) properties.get(1)).getIndexingType())
-                .isEqualTo(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE);
-
-        assertThat(properties.get(2).getName()).isEqualTo("indexableLong");
-        assertThat(properties.get(2).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(((AppSearchSchema.LongPropertyConfig) properties.get(2)).getIndexingType())
-                .isEqualTo(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE);
-
-        assertThat(properties.get(3).getName()).isEqualTo("double");
-        assertThat(properties.get(3).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
-        assertThat(properties.get(3)).isInstanceOf(AppSearchSchema.DoublePropertyConfig.class);
-
-        assertThat(properties.get(4).getName()).isEqualTo("boolean");
-        assertThat(properties.get(4).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
-        assertThat(properties.get(4)).isInstanceOf(AppSearchSchema.BooleanPropertyConfig.class);
-
-        assertThat(properties.get(5).getName()).isEqualTo("bytes");
-        assertThat(properties.get(5).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(properties.get(5)).isInstanceOf(AppSearchSchema.BytesPropertyConfig.class);
-
-        assertThat(properties.get(6).getName()).isEqualTo("document1");
-        assertThat(properties.get(6).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
-        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(6)).getSchemaType())
-                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
-        assertThat(
-                        ((AppSearchSchema.DocumentPropertyConfig) properties.get(6))
-                                .shouldIndexNestedProperties())
-                .isEqualTo(true);
-
-        assertThat(properties.get(7).getName()).isEqualTo("document2");
-        assertThat(properties.get(7).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
-        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(7)).getSchemaType())
-                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
-        assertThat(
-                        ((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
-                                .shouldIndexNestedProperties())
-                .isEqualTo(false);
-        assertThat(
-                        ((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
-                                .getIndexableNestedProperties())
-                .containsExactly("path1", "path2", "path3");
-
-        assertThat(properties.get(8).getName()).isEqualTo("qualifiedId1");
-        assertThat(properties.get(8).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(
-                        ((AppSearchSchema.StringPropertyConfig) properties.get(8))
-                                .getJoinableValueType())
-                .isEqualTo(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
-
-        assertThat(properties.get(9).getName()).isEqualTo("qualifiedId2");
-        assertThat(properties.get(9).getCardinality())
-                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
-        assertThat(
-                        ((AppSearchSchema.StringPropertyConfig) properties.get(9))
-                                .getJoinableValueType())
-                .isEqualTo(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
-    }
-}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
index c984f92..b72cf54 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
@@ -21,16 +21,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
 import androidx.appsearch.testutil.AppSearchEmail;
-import androidx.appsearch.util.DocumentIdUtil;
-import androidx.test.core.app.ApplicationProvider;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -46,6 +42,8 @@
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
+
+/** This class holds all tests that won't be exported to the framework.  */
 public abstract class AppSearchSessionInternalTestBase {
 
     static final String DB_NAME_1 = "";
@@ -77,730 +75,10 @@
         mDb1.setSchemaAsync(new SetSchemaRequest.Builder().setForceOverride(true).build()).get();
     }
 
-    // TODO(b/228240987) delete this test when we support property restrict for multiple terms
-    @Test
-    public void testSearchSuggestion_propertyFilter() throws Exception {
-        // Schema registration
-        AppSearchSchema schemaType1 =
-                new AppSearchSchema.Builder("Type1")
-                        .addProperty(
-                                new StringPropertyConfig.Builder("propertyone")
-                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                        .build())
-                        .addProperty(
-                                new StringPropertyConfig.Builder("propertytwo")
-                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                        .build())
-                        .build();
-        AppSearchSchema schemaType2 =
-                new AppSearchSchema.Builder("Type2")
-                        .addProperty(
-                                new StringPropertyConfig.Builder("propertythree")
-                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                        .build())
-                        .addProperty(
-                                new StringPropertyConfig.Builder("propertyfour")
-                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                        .build())
-                        .build();
-        mDb1.setSchemaAsync(
-                        new SetSchemaRequest.Builder().addSchemas(schemaType1, schemaType2).build())
-                .get();
-
-        // Index documents
-        GenericDocument doc1 =
-                new GenericDocument.Builder<>("namespace", "id1", "Type1")
-                        .setPropertyString("propertyone", "termone")
-                        .setPropertyString("propertytwo", "termtwo")
-                        .build();
-        GenericDocument doc2 =
-                new GenericDocument.Builder<>("namespace", "id2", "Type2")
-                        .setPropertyString("propertythree", "termthree")
-                        .setPropertyString("propertyfour", "termfour")
-                        .build();
-
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(doc1, doc2).build()));
-
-        SearchSuggestionResult resultOne =
-                new SearchSuggestionResult.Builder().setSuggestedResult("termone").build();
-        SearchSuggestionResult resultTwo =
-                new SearchSuggestionResult.Builder().setSuggestedResult("termtwo").build();
-        SearchSuggestionResult resultThree =
-                new SearchSuggestionResult.Builder().setSuggestedResult("termthree").build();
-        SearchSuggestionResult resultFour =
-                new SearchSuggestionResult.Builder().setSuggestedResult("termfour").build();
-
-        // Only search for type1/propertyone
-        List<SearchSuggestionResult> suggestions =
-                mDb1.searchSuggestionAsync(
-                                /* suggestionQueryExpression= */ "t",
-                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
-                                        .addFilterSchemas("Type1")
-                                        .addFilterProperties(
-                                                "Type1", ImmutableList.of("propertyone"))
-                                        .build())
-                        .get();
-        assertThat(suggestions).containsExactly(resultOne);
-
-        // Only search for type1/propertyone and type1/propertytwo
-        suggestions =
-                mDb1.searchSuggestionAsync(
-                                /* suggestionQueryExpression= */ "t",
-                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
-                                        .addFilterSchemas("Type1")
-                                        .addFilterProperties(
-                                                "Type1",
-                                                ImmutableList.of("propertyone", "propertytwo"))
-                                        .build())
-                        .get();
-        assertThat(suggestions).containsExactly(resultOne, resultTwo);
-
-        // Only search for type1/propertyone and type2/propertythree
-        suggestions =
-                mDb1.searchSuggestionAsync(
-                                /* suggestionQueryExpression= */ "t",
-                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
-                                        .addFilterSchemas("Type1", "Type2")
-                                        .addFilterProperties(
-                                                "Type1", ImmutableList.of("propertyone"))
-                                        .addFilterProperties(
-                                                "Type2", ImmutableList.of("propertythree"))
-                                        .build())
-                        .get();
-        assertThat(suggestions).containsExactly(resultOne, resultThree);
-
-        // Only search for type1/propertyone and type2/propertyfour, in addFilterPropertyPaths
-        suggestions =
-                mDb1.searchSuggestionAsync(
-                                /* suggestionQueryExpression= */ "t",
-                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
-                                        .addFilterSchemas("Type1", "Type2")
-                                        .addFilterProperties(
-                                                "Type1", ImmutableList.of("propertyone"))
-                                        .addFilterPropertyPaths(
-                                                "Type2",
-                                                ImmutableList.of(new PropertyPath("propertyfour")))
-                                        .build())
-                        .get();
-        assertThat(suggestions).containsExactly(resultOne, resultFour);
-
-        // Only search for type1/propertyone and everything in type2
-        suggestions =
-                mDb1.searchSuggestionAsync(
-                                /* suggestionQueryExpression= */ "t",
-                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
-                                        .addFilterProperties(
-                                                "Type1", ImmutableList.of("propertyone"))
-                                        .build())
-                        .get();
-        assertThat(suggestions).containsExactly(resultOne, resultThree, resultFour);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_typePropertyFilters() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .build()).get();
-
-        // Index two documents
-        AppSearchEmail email1 =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setCreationTimestampMillis(1000)
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        AppSearchEmail email2 =
-                new AppSearchEmail.Builder("namespace", "id2")
-                        .setCreationTimestampMillis(1000)
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example subject with some body")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder()
-                        .addGenericDocuments(email1, email2).build()));
-
-        // Query with type property filters {"Email", ["subject", "to"]}
-        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("subject", "to"))
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        // Only email2 should be returned because email1 doesn't have the term "body" in subject
-        // or to fields
-        assertThat(documents).containsExactly(email2);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_typePropertyFiltersWithDifferentSchemaTypes() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .addSchemas(new AppSearchSchema.Builder("Note")
-                                .addProperty(new StringPropertyConfig.Builder("title")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .addProperty(new StringPropertyConfig.Builder("body")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .build())
-                        .build()).get();
-
-        // Index two documents
-        AppSearchEmail email =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setCreationTimestampMillis(1000)
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        GenericDocument note =
-                new GenericDocument.Builder<>("namespace", "id2", "Note")
-                        .setCreationTimestampMillis(1000)
-                        .setPropertyString("title", "Note title")
-                        .setPropertyString("body", "Note body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder()
-                        .addGenericDocuments(email, note).build()));
-
-        // Query with type property paths {"Email": ["subject", "to"], "Note": ["body"]}. Note
-        // schema has body in its property filter but Email schema doesn't.
-        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("subject", "to"))
-                .addFilterProperties("Note", ImmutableList.of("body"))
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        // Only the note document should be returned because the email property filter doesn't
-        // allow searching in the body.
-        assertThat(documents).containsExactly(note);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_typePropertyFiltersWithWildcard() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .addSchemas(new AppSearchSchema.Builder("Note")
-                                .addProperty(new StringPropertyConfig.Builder("title")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .addProperty(new StringPropertyConfig.Builder("body")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .build())
-                        .build()).get();
-
-        // Index two documents
-        AppSearchEmail email =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setCreationTimestampMillis(1000)
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example subject with some body")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        GenericDocument note =
-                new GenericDocument.Builder<>("namespace", "id2", "Note")
-                        .setCreationTimestampMillis(1000)
-                        .setPropertyString("title", "Note title")
-                        .setPropertyString("body", "Note body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder()
-                        .addGenericDocuments(email, note).build()));
-
-        // Query with type property paths {"*": ["subject", "title"]}
-        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties(SearchSpec.SCHEMA_TYPE_WILDCARD,
-                        ImmutableList.of("subject", "title"))
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        // The wildcard property filter will apply to both the Email and Note schema. The email
-        // document should be returned since it has the term "body" in its subject property. The
-        // note document should not be returned since it doesn't have the term "body" in the title
-        // property (subject property is not applicable for Note schema)
-        assertThat(documents).containsExactly(email);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_typePropertyFiltersWithWildcardAndExplicitSchema() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .addSchemas(new AppSearchSchema.Builder("Note")
-                                .addProperty(new StringPropertyConfig.Builder("title")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .addProperty(new StringPropertyConfig.Builder("body")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .build())
-                        .build()).get();
-
-        // Index two documents
-        AppSearchEmail email =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setCreationTimestampMillis(1000)
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example subject with some body")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        GenericDocument note =
-                new GenericDocument.Builder<>("namespace", "id2", "Note")
-                        .setCreationTimestampMillis(1000)
-                        .setPropertyString("title", "Note title")
-                        .setPropertyString("body", "Note body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder()
-                        .addGenericDocuments(email, note).build()));
-
-        // Query with type property paths {"*": ["subject", "title"], "Note": ["body"]}
-        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties(SearchSpec.SCHEMA_TYPE_WILDCARD,
-                        ImmutableList.of("subject", "title"))
-                .addFilterProperties("Note", ImmutableList.of("body"))
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        // The wildcard property filter will only apply to the Email schema since Note schema has
-        // its own explicit property filter specified. The email document should be returned since
-        // it has the term "body" in its subject property. The note document should also be returned
-        // since it has the term "body" in the body property.
-        assertThat(documents).containsExactly(email, note);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_typePropertyFiltersNonExistentType() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .addSchemas(new AppSearchSchema.Builder("Note")
-                                .addProperty(new StringPropertyConfig.Builder("title")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .addProperty(new StringPropertyConfig.Builder("body")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .build())
-                        .build()).get();
-
-        // Index two documents
-        AppSearchEmail email =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setCreationTimestampMillis(1000)
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example subject with some body")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        GenericDocument note =
-                new GenericDocument.Builder<>("namespace", "id2", "Note")
-                        .setCreationTimestampMillis(1000)
-                        .setPropertyString("title", "Note title")
-                        .setPropertyString("body", "Note body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder()
-                        .addGenericDocuments(email, note).build()));
-
-        // Query with type property paths {"NonExistentType": ["to", "title"]}
-        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties("NonExistentType", ImmutableList.of("to", "title"))
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        // The supplied property filters don't apply to either schema types. Both the documents
-        // should be returned since the term "body" is present in at least one of their properties.
-        assertThat(documents).containsExactly(email, note);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_typePropertyFiltersEmpty() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .addSchemas(new AppSearchSchema.Builder("Note")
-                                .addProperty(new StringPropertyConfig.Builder("title")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .addProperty(new StringPropertyConfig.Builder("body")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                        .setTokenizerType(
-                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .build())
-                                .build())
-                        .build()).get();
-
-        // Index two documents
-        AppSearchEmail email =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setCreationTimestampMillis(1000)
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        GenericDocument note =
-                new GenericDocument.Builder<>("namespace", "id2", "Note")
-                        .setCreationTimestampMillis(1000)
-                        .setPropertyString("title", "Note title")
-                        .setPropertyString("body", "Note body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder()
-                        .addGenericDocuments(email, note).build()));
-
-        // Query with type property paths {"email": []}
-        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, Collections.emptyList())
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        // The email document should not be returned since the property filter doesn't allow
-        // searching any property.
-        assertThat(documents).containsExactly(note);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQueryWithJoin_typePropertyFiltersOnNestedSpec() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        assumeTrue(mDb1.getFeatures()
-                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
-
-        // A full example of how join might be used with property filters in join spec
-        AppSearchSchema actionSchema = new AppSearchSchema.Builder("ViewAction")
-                .addProperty(new StringPropertyConfig.Builder("entityId")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setJoinableValueType(StringPropertyConfig
-                                .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new StringPropertyConfig.Builder("note")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new StringPropertyConfig.Builder("viewType")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).build();
-
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA, actionSchema)
-                        .build()).get();
-
-        // Index 2 email documents
-        AppSearchEmail inEmail =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-
-        AppSearchEmail inEmail2 =
-                new AppSearchEmail.Builder("namespace", "id2")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-
-        // Index 2 viewAction documents, one for email1 and the other for email2
-        String qualifiedId1 =
-                DocumentIdUtil.createQualifiedId(
-                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
-                        "namespace", "id1");
-        String qualifiedId2 =
-                DocumentIdUtil.createQualifiedId(
-                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
-                        "namespace", "id2");
-        GenericDocument viewAction1 = new GenericDocument.Builder<>("NS", "id3", "ViewAction")
-                .setPropertyString("entityId", qualifiedId1)
-                .setPropertyString("note", "Viewed email on Monday")
-                .setPropertyString("viewType", "Stared").build();
-        GenericDocument viewAction2 = new GenericDocument.Builder<>("NS", "id4", "ViewAction")
-                .setPropertyString("entityId", qualifiedId2)
-                .setPropertyString("note", "Viewed email on Tuesday")
-                .setPropertyString("viewType", "Viewed").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail, inEmail2,
-                                viewAction1, viewAction2)
-                        .build()));
-
-        // The nested search spec only allows searching the viewType property for viewAction
-        // schema type. It also specifies a property filter for Email schema.
-        SearchSpec nestedSearchSpec =
-                new SearchSpec.Builder()
-                        .addFilterProperties("ViewAction", ImmutableList.of("viewType"))
-                        .addFilterProperties(AppSearchEmail.SCHEMA_TYPE,
-                                ImmutableList.of("subject"))
-                        .build();
-
-        // Search for the term "Viewed" in join spec
-        JoinSpec js = new JoinSpec.Builder("entityId")
-                .setNestedSearch("Viewed", nestedSearchSpec)
-                .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
-                .build();
-
-        SearchResults searchResults = mDb1.search("body email", new SearchSpec.Builder()
-                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
-                .setJoinSpec(js)
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-
-        List<SearchResult> sr = searchResults.getNextPageAsync().get();
-
-        // Both email docs are returned, email2 comes first because it has higher number of
-        // joined documents. The property filters for Email schema specified in the nested search
-        // specs don't apply to the outer query (otherwise none of the email documents would have
-        // been returned).
-        assertThat(sr).hasSize(2);
-
-        // Email2 has a viewAction document viewAction2 that satisfies the property filters in
-        // the join spec, so it should be present in the joined results.
-        assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("id2");
-        assertThat(sr.get(0).getRankingSignal()).isEqualTo(1.0);
-        assertThat(sr.get(0).getJoinedResults()).hasSize(1);
-        assertThat(sr.get(0).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction2);
-
-        // Email1 has a viewAction document viewAction1 but it doesn't satisfy the property filters
-        // in the join spec, so it should not be present in the joined results.
-        assertThat(sr.get(1).getGenericDocument().getId()).isEqualTo("id1");
-        assertThat(sr.get(1).getRankingSignal()).isEqualTo(0.0);
-        assertThat(sr.get(1).getJoinedResults()).isEmpty();
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQueryWithJoin_typePropertyFiltersOnOuterSpec() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        assumeTrue(mDb1.getFeatures()
-                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
-
-        // A full example of how join might be used with property filters in join spec
-        AppSearchSchema actionSchema = new AppSearchSchema.Builder("ViewAction")
-                .addProperty(new StringPropertyConfig.Builder("entityId")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setJoinableValueType(StringPropertyConfig
-                                .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new StringPropertyConfig.Builder("note")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).addProperty(new StringPropertyConfig.Builder("viewType")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build()
-                ).build();
-
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA, actionSchema)
-                        .build()).get();
-
-        // Index 2 email documents
-        AppSearchEmail inEmail =
-                new AppSearchEmail.Builder("namespace", "id1")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-
-        AppSearchEmail inEmail2 =
-                new AppSearchEmail.Builder("namespace", "id2")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-
-        // Index 2 viewAction documents, one for email1 and the other for email2
-        String qualifiedId1 =
-                DocumentIdUtil.createQualifiedId(
-                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
-                        "namespace", "id1");
-        String qualifiedId2 =
-                DocumentIdUtil.createQualifiedId(
-                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
-                        "namespace", "id2");
-        GenericDocument viewAction1 = new GenericDocument.Builder<>("NS", "id3", "ViewAction")
-                .setPropertyString("entityId", qualifiedId1)
-                .setPropertyString("note", "Viewed email on Monday")
-                .setPropertyString("viewType", "Stared").build();
-        GenericDocument viewAction2 = new GenericDocument.Builder<>("NS", "id4", "ViewAction")
-                .setPropertyString("entityId", qualifiedId2)
-                .setPropertyString("note", "Viewed email on Tuesday")
-                .setPropertyString("viewType", "Viewed").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail, inEmail2,
-                                viewAction1, viewAction2)
-                        .build()));
-
-        // The nested search spec doesn't specify any property filters.
-        SearchSpec nestedSearchSpec = new SearchSpec.Builder().build();
-
-        // Search for the term "Viewed" in join spec
-        JoinSpec js = new JoinSpec.Builder("entityId")
-                .setNestedSearch("Viewed", nestedSearchSpec)
-                .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
-                .build();
-
-        // Outer search spec adds property filters for both Email and ViewAction schema
-        SearchResults searchResults = mDb1.search("body email", new SearchSpec.Builder()
-                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
-                .setJoinSpec(js)
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("body"))
-                .addFilterProperties("ViewAction", ImmutableList.of("viewType"))
-                .build());
-
-        List<SearchResult> sr = searchResults.getNextPageAsync().get();
-
-        // Both email docs are returned as they both satisfy the property filters for Email, email2
-        // comes first because it has higher id lexicographically.
-        assertThat(sr).hasSize(2);
-
-        // Email2 has a viewAction document viewAction2 that satisfies the property filters in
-        // the outer spec (although those property filters are irrelevant for joined documents),
-        // it should be present in the joined results.
-        assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("id2");
-        assertThat(sr.get(0).getRankingSignal()).isEqualTo(1.0);
-        assertThat(sr.get(0).getJoinedResults()).hasSize(1);
-        assertThat(sr.get(0).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction2);
-
-        // Email1 has a viewAction document viewAction1 that doesn't satisfy the property filters
-        // in the outer spec, but property filters in the outer spec should not apply on joined
-        // documents, so viewAction1 should be present in the joined results.
-        assertThat(sr.get(1).getGenericDocument().getId()).isEqualTo("id1");
-        assertThat(sr.get(1).getRankingSignal()).isEqualTo(1.0);
-        assertThat(sr.get(0).getJoinedResults()).hasSize(1);
-        assertThat(sr.get(1).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction1);
-    }
-
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_typePropertyFiltersNotSupported() throws Exception {
-        assumeFalse(mDb1.getFeatures().isFeatureSupported(
-                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .build()).get();
-
-        // Query with type property filters {"Email", ["subject", "to"]} and verify that unsupported
-        // exception is thrown
-        SearchSpec searchSpec = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("subject", "to"))
-                .build();
-        UnsupportedOperationException exception =
-                assertThrows(UnsupportedOperationException.class,
-                        () -> mDb1.search("body", searchSpec));
-        assertThat(exception).hasMessageThat().contains(Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES
-                + " is not available on this AppSearch implementation.");
-    }
-
     // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
     @Test
     public void testGetSchema_joinableValueType() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_SET_DELETION_PROPAGATION));
         AppSearchSchema inSchema =
                 new AppSearchSchema.Builder("Test")
                         .addProperty(
@@ -820,11 +98,6 @@
                                         .setJoinableValueType(
                                                 StringPropertyConfig
                                                         .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                                        // TODO(b/274157614): Export this to framework when we
-                                        //  can access hidden APIs.
-                                        // @exportToFramework:startStrip()
-                                        .setDeletionPropagation(true)
-                                        // @exportToFramework:endStrip()
                                         .build())
                         .build();
 
@@ -837,465 +110,6 @@
         assertThat(actual).containsExactlyElementsIn(request.getSchemas());
     }
 
-    // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
-    @Test
-    public void testGetSchema_deletionPropagation_unsupported() {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
-        assumeFalse(
-                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_SET_DELETION_PROPAGATION));
-        AppSearchSchema schema =
-                new AppSearchSchema.Builder("Test")
-                        .addProperty(
-                                new StringPropertyConfig.Builder("qualifiedIdDeletionPropagation")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setJoinableValueType(
-                                                StringPropertyConfig
-                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                                        .setDeletionPropagation(true)
-                                        .build())
-                        .build();
-        SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
-        Exception e =
-                assertThrows(
-                        UnsupportedOperationException.class,
-                        () -> mDb1.setSchemaAsync(request).get());
-        assertThat(e.getMessage())
-                .isEqualTo(
-                        "Setting deletion propagation is not supported "
-                                + "on this AppSearch implementation.");
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_ResultGroupingLimits_SchemaGroupingSupported() throws Exception {
-        assumeTrue(
-                mDb1.getFeatures()
-                        .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
-        // Schema registration
-        AppSearchSchema genericSchema =
-                new AppSearchSchema.Builder("Generic")
-                        .addProperty(
-                                new StringPropertyConfig.Builder("foo")
-                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                        .setIndexingType(
-                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                        .build())
-                        .build();
-        mDb1.setSchemaAsync(
-                        new SetSchemaRequest.Builder()
-                                .addSchemas(AppSearchEmail.SCHEMA)
-                                .addSchemas(genericSchema)
-                                .build())
-                .get();
-
-        // Index four documents.
-        AppSearchEmail inEmail1 =
-                new AppSearchEmail.Builder("namespace1", "id1")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
-        AppSearchEmail inEmail2 =
-                new AppSearchEmail.Builder("namespace1", "id2")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
-        AppSearchEmail inEmail3 =
-                new AppSearchEmail.Builder("namespace2", "id3")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
-        AppSearchEmail inEmail4 =
-                new AppSearchEmail.Builder("namespace2", "id4")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
-        AppSearchEmail inEmail5 =
-                new AppSearchEmail.Builder("namespace2", "id5")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail5).build()));
-        GenericDocument inDoc1 =
-                new GenericDocument.Builder<>("namespace3", "id6", "Generic")
-                        .setPropertyString("foo", "body")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inDoc1).build()));
-        GenericDocument inDoc2 =
-                new GenericDocument.Builder<>("namespace3", "id7", "Generic")
-                        .setPropertyString("foo", "body")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inDoc2).build()));
-        GenericDocument inDoc3 =
-                new GenericDocument.Builder<>("namespace4", "id8", "Generic")
-                        .setPropertyString("foo", "body")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inDoc3).build()));
-
-        // Query with per package result grouping. Only the last document 'doc3' should be
-        // returned.
-        SearchResults searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_PACKAGE, /* resultLimit= */ 1)
-                                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3);
-
-        // Query with per namespace result grouping. Only the last document in each namespace should
-        // be returned ('doc3', 'doc2', 'email5' and 'email2').
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE,
-                                        /* resultLimit= */ 1)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per namespace result grouping. Two of the last documents in each namespace
-        // should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4', 'email2', 'email1')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE,
-                                        /* resultLimit= */ 2)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents)
-                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
-
-        // Query with per schema result grouping. Only the last document of each schema type should
-        // be returned ('doc3', 'email5')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* resultLimit= */ 1)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inEmail5);
-
-        // Query with per schema result grouping. Only the last two documents of each schema type
-        // should be returned ('doc3', 'doc2', 'email5', 'email4')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* resultLimit= */ 2)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
-
-        // Query with per package and per namespace result grouping. Only the last document in each
-        // namespace should be returned ('doc3', 'doc2', 'email5' and 'email2').
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
-                                        /* resultLimit= */ 1)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per package and per namespace result grouping. Only the last two documents
-        // in each namespace should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4',
-        // 'email2', 'email1')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
-                                        /* resultLimit= */ 2)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents)
-                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
-
-        // Query with per package and per schema type result grouping. Only the last document in
-        // each schema type should be returned. ('doc3', 'email5')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
-                                        /* resultLimit= */ 1)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inEmail5);
-
-        // Query with per package and per schema type result grouping. Only the last two document in
-        // each schema type should be returned. ('doc3', 'doc2', 'email5', 'email4')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
-                                        /* resultLimit= */ 2)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
-
-        // Query with per namespace and per schema type result grouping. Only the last document in
-        // each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2').
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
-                                        /* resultLimit= */ 1)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per namespace and per schema type result grouping. Only the last two documents
-        // in each namespace should be returned. ('doc3', 'doc2', 'doc1', 'email5', 'email4',
-        // 'email2', 'email1')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
-                                        /* resultLimit= */ 2)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents)
-                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
-
-        // Query with per namespace, per package and per schema type result grouping. Only the last
-        // document in each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
-                                        /* resultLimit= */ 1)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per namespace, per package and per schema type result grouping. Only the last
-        // two documents in each namespace should be returned.('doc3', 'doc2', 'doc1', 'email5',
-        // 'email4', 'email2', 'email1')
-        searchResults =
-                mDb1.search(
-                        "body",
-                        new SearchSpec.Builder()
-                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                                .setResultGrouping(
-                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
-                                        /* resultLimit= */ 2)
-                                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents)
-                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
-    }
-
-    // TODO(b/291122592): move to CTS once the APIs it uses are public
-    @Test
-    public void testQuery_ResultGroupingLimits_SchemaGroupingNotSupported() throws Exception {
-        assumeFalse(
-                mDb1.getFeatures()
-                        .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
-        // Schema registration
-        mDb1.setSchemaAsync(
-                        new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
-                .get();
-
-        // Index four documents.
-        AppSearchEmail inEmail1 =
-                new AppSearchEmail.Builder("namespace1", "id1")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
-        AppSearchEmail inEmail2 =
-                new AppSearchEmail.Builder("namespace1", "id2")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
-        AppSearchEmail inEmail3 =
-                new AppSearchEmail.Builder("namespace2", "id3")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
-        AppSearchEmail inEmail4 =
-                new AppSearchEmail.Builder("namespace2", "id4")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("This is the body of the testPut email")
-                        .build();
-        checkIsBatchResultSuccess(
-                mDb1.putAsync(
-                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec1 =
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .setResultGrouping(
-                                SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* resultLimit= */ 1)
-                        .build();
-        UnsupportedOperationException exception =
-                assertThrows(
-                        UnsupportedOperationException.class,
-                        () -> mDb1.search("body", searchSpec1));
-        assertThat(exception)
-                .hasMessageThat()
-                .contains(
-                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
-                                + " is not available on this"
-                                + " AppSearch implementation.");
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec2 =
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .setResultGrouping(
-                                SearchSpec.GROUPING_TYPE_PER_PACKAGE
-                                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
-                                /* resultLimit= */ 1)
-                        .build();
-        exception =
-                assertThrows(
-                        UnsupportedOperationException.class,
-                        () -> mDb1.search("body", searchSpec2));
-        assertThat(exception)
-                .hasMessageThat()
-                .contains(
-                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
-                                + " is not available on this"
-                                + " AppSearch implementation.");
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec3 =
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .setResultGrouping(
-                                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
-                                /* resultLimit= */ 1)
-                        .build();
-        exception =
-                assertThrows(
-                        UnsupportedOperationException.class,
-                        () -> mDb1.search("body", searchSpec3));
-        assertThat(exception)
-                .hasMessageThat()
-                .contains(
-                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
-                                + " is not available on this"
-                                + " AppSearch implementation.");
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec4 =
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .setResultGrouping(
-                                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                                        | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
-                                /* resultLimit= */ 1)
-                        .build();
-        exception =
-                assertThrows(
-                        UnsupportedOperationException.class,
-                        () -> mDb1.search("body", searchSpec4));
-        assertThat(exception)
-                .hasMessageThat()
-                .contains(
-                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
-                                + " is not available on this"
-                                + " AppSearch implementation.");
-    }
-
     @Test
     public void testQuery_typeFilterWithPolymorphism() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
@@ -1496,6 +310,113 @@
     }
 
     @Test
+    public void testQuery_projectionWithPolymorphismAndSchemaFilter() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+
+        // Schema registration
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("emailAddress")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        AppSearchSchema artistSchema =
+                new AppSearchSchema.Builder("Artist")
+                        .addParentType("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("emailAddress")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("company")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchema)
+                                .addSchemas(artistSchema)
+                                .build())
+                .get();
+
+        // Index two documents
+        GenericDocument personDoc =
+                new GenericDocument.Builder<>("namespace", "id1", "Person")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Person")
+                        .setPropertyString("emailAddress", "[email protected]")
+                        .build();
+        GenericDocument artistDoc =
+                new GenericDocument.Builder<>("namespace", "id2", "Artist")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Artist")
+                        .setPropertyString("emailAddress", "[email protected]")
+                        .setPropertyString("company", "Company")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder()
+                                .addGenericDocuments(personDoc, artistDoc)
+                                .build()));
+
+        // Query with type property paths {"Person", ["name"]} and {"Artist", ["emailAddress"]}, and
+        // a schema filter for the "Person".
+        // This will be expanded to paths {"Person", ["name"]} and
+        // {"Artist", ["name", "emailAddress"]}, and filters for both "Person" and "Artist" via
+        // polymorphism.
+        SearchResults searchResults =
+                mDb1.search(
+                        "Foo",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .addFilterSchemas("Person")
+                                .addProjection("Person", ImmutableList.of("name"))
+                                .addProjection("Artist", ImmutableList.of("emailAddress"))
+                                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // The person document should have been returned with only the "name" property. The artist
+        // document should have been returned with all of its properties.
+        GenericDocument expectedPerson =
+                new GenericDocument.Builder<>("namespace", "id1", "Person")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Person")
+                        .build();
+        GenericDocument expectedArtist =
+                new GenericDocument.Builder<>("namespace", "id2", "Artist")
+                        .setParentTypes(Collections.singletonList("Person"))
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Artist")
+                        .setPropertyString("emailAddress", "[email protected]")
+                        .build();
+        assertThat(documents).containsExactly(expectedPerson, expectedArtist);
+    }
+
+    @Test
     public void testQuery_indexBasedOnParentTypePolymorphism() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
 
@@ -1664,4 +585,167 @@
         assertThat(documents).hasSize(4);
         assertThat(documents).containsExactly(expectedDocA, expectedDocB, expectedDocC, docD);
     }
+
+    // TODO(b/336277840): Move this if setParentTypes becomes public
+    @Test
+    public void testQuery_wildcardProjection_polymorphism() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+        AppSearchSchema messageSchema = new AppSearchSchema.Builder("Message")
+                .addProperty(new StringPropertyConfig.Builder("sender")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("content")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .build();
+        AppSearchSchema textSchema = new AppSearchSchema.Builder("Text")
+                .addParentType("Message")
+                .addProperty(new StringPropertyConfig.Builder("sender")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("content")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .build();
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+                .addParentType("Message")
+                .addProperty(new StringPropertyConfig.Builder("sender")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("content")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .build();
+
+        // Schema registration
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+                .addSchemas(messageSchema, textSchema, emailSchema).build()).get();
+
+        // Index two child documents
+        GenericDocument text = new GenericDocument.Builder<>("namespace", "id1", "Text")
+                .setCreationTimestampMillis(1000)
+                .setPropertyString("sender", "Some sender")
+                .setPropertyString("content", "Some note")
+                .build();
+        GenericDocument email = new GenericDocument.Builder<>("namespace", "id2", "Email")
+                .setCreationTimestampMillis(1000)
+                .setPropertyString("sender", "Some sender")
+                .setPropertyString("content", "Some note")
+                .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(new PutDocumentsRequest.Builder()
+                .addGenericDocuments(email, text).build()));
+
+        SearchResults searchResults = mDb1.search("Some", new SearchSpec.Builder()
+                .addFilterSchemas("Message")
+                .addProjection(SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("sender"))
+                .addFilterProperties(SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("content"))
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // We specified the parent document in the filter schemas, but only indexed child documents.
+        // As we also specified a wildcard schema type projection, it should apply to the child docs
+        // The content property must not appear. Also emailNoContent should not appear as we are
+        // filter on the content property
+        GenericDocument expectedText = new GenericDocument.Builder<>("namespace", "id1", "Text")
+                .setParentTypes(Collections.singletonList("Message"))
+                .setCreationTimestampMillis(1000)
+                .setPropertyString("sender", "Some sender")
+                .build();
+        GenericDocument expectedEmail = new GenericDocument.Builder<>("namespace", "id2", "Email")
+                .setParentTypes(Collections.singletonList("Message"))
+                .setCreationTimestampMillis(1000)
+                .setPropertyString("sender", "Some sender")
+                .build();
+        assertThat(documents).containsExactly(expectedText, expectedEmail);
+    }
+
+    // TODO(b/336277840): Move this if setParentTypes becomes public
+    @Test
+    public void testQuery_wildcardFilterSchema_polymorphism() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+        AppSearchSchema messageSchema = new AppSearchSchema.Builder("Message")
+                .addProperty(new StringPropertyConfig.Builder("content")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .build();
+        AppSearchSchema textSchema = new AppSearchSchema.Builder("Text")
+                .addParentType("Message")
+                .addProperty(new StringPropertyConfig.Builder("content")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("carrier")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .build();
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
+                .addParentType("Message")
+                .addProperty(new StringPropertyConfig.Builder("content")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("attachment")
+                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .build();
+
+        // Schema registration
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+                .addSchemas(messageSchema, textSchema, emailSchema).build()).get();
+
+        // Index two child documents
+        GenericDocument text = new GenericDocument.Builder<>("namespace", "id1", "Text")
+                .setCreationTimestampMillis(1000)
+                .setPropertyString("content", "Some note")
+                .setPropertyString("carrier", "Network Inc")
+                .build();
+        GenericDocument email = new GenericDocument.Builder<>("namespace", "id2", "Email")
+                .setCreationTimestampMillis(1000)
+                .setPropertyString("content", "Some note")
+                .setPropertyString("attachment", "Network report")
+                .build();
+
+        checkIsBatchResultSuccess(mDb1.putAsync(new PutDocumentsRequest.Builder()
+                .addGenericDocuments(email, text).build()));
+
+        // Both email and text would match for "Network", but only text should match as it is in the
+        // right property
+        SearchResults searchResults = mDb1.search("Network", new SearchSpec.Builder()
+                .addFilterSchemas("Message")
+                .addFilterProperties(SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("carrier"))
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // We specified the parent document in the filter schemas, but only indexed child documents.
+        // As we also specified a wildcard schema type projection, it should apply to the child docs
+        // The content property must not appear. Also emailNoContent should not appear as we are
+        // filter on the content property
+        GenericDocument expectedText = new GenericDocument.Builder<>("namespace", "id1", "Text")
+                .setParentTypes(Collections.singletonList("Message"))
+                .setCreationTimestampMillis(1000)
+                .setPropertyString("content", "Some note")
+                .setPropertyString("carrier", "Network Inc")
+                .build();
+        assertThat(documents).containsExactly(expectedText);
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionLocalInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionLocalInternalTest.java
index 6bcc933..718aaa2 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionLocalInternalTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionLocalInternalTest.java
@@ -26,7 +26,6 @@
 
 import java.util.concurrent.ExecutorService;
 
-// TODO(b/227356108): move this test to cts test once we un-hide search suggestion API.
 public class AppSearchSessionLocalInternalTest extends AppSearchSessionInternalTestBase {
     @Override
     protected ListenableFuture<AppSearchSession> createSearchSessionAsync(@NonNull String dbName) {
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionPlatformInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionPlatformInternalTest.java
index 4165efe..76f32b4 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionPlatformInternalTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionPlatformInternalTest.java
@@ -26,11 +26,8 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 
-import org.junit.Test;
-
 import java.util.concurrent.ExecutorService;
 
-// TODO(b/227356108): move this test to cts test once we un-hide search suggestion API.
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
 public class AppSearchSessionPlatformInternalTest extends AppSearchSessionInternalTestBase {
     @Override
@@ -48,10 +45,4 @@
                 new PlatformStorage.SearchContext.Builder(context, dbName)
                         .setWorkerExecutor(executor).build());
     }
-
-    @Override
-    @Test
-    public void testSearchSuggestion_propertyFilter() throws Exception {
-        // TODO(b/227356108) enable the test when suggestion is ready in platform.
-    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GenericDocumentInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GenericDocumentInternalTest.java
index 7859463..fc5be8f 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GenericDocumentInternalTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/GenericDocumentInternalTest.java
@@ -18,9 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.os.Bundle;
 import android.os.Parcel;
 
+import androidx.appsearch.safeparcel.GenericDocumentParcel;
+
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -44,7 +45,7 @@
 
         // Serialize the document
         Parcel inParcel = Parcel.obtain();
-        inParcel.writeBundle(inDoc.getBundle());
+        inParcel.writeParcelable(inDoc.getDocumentParcel(), /*parcelableFlags=*/ 0);
         byte[] data = inParcel.marshall();
         inParcel.recycle();
 
@@ -52,11 +53,13 @@
         Parcel outParcel = Parcel.obtain();
         outParcel.unmarshall(data, 0, data.length);
         outParcel.setDataPosition(0);
-        Bundle outBundle = outParcel.readBundle();
+        @SuppressWarnings("deprecation")
+        GenericDocumentParcel documentParcel =
+                outParcel.readParcelable(GenericDocumentParcel.class.getClassLoader());
         outParcel.recycle();
 
         // Compare results
-        GenericDocument outDoc = new GenericDocument(outBundle);
+        GenericDocument outDoc = new GenericDocument(documentParcel);
         assertThat(inDoc).isEqualTo(outDoc);
         assertThat(outDoc.getPropertyString("propString")).isEqualTo("Hello");
         assertThat(outDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][]{{1, 2}});
@@ -83,7 +86,7 @@
 
         // Serialize the document
         Parcel inParcel = Parcel.obtain();
-        inParcel.writeBundle(inDoc.getBundle());
+        inParcel.writeParcelable(inDoc.getDocumentParcel(), /*parcelableFlags=*/ 0);
         byte[] data = inParcel.marshall();
         inParcel.recycle();
 
@@ -91,11 +94,13 @@
         Parcel outParcel = Parcel.obtain();
         outParcel.unmarshall(data, 0, data.length);
         outParcel.setDataPosition(0);
-        Bundle outBundle = outParcel.readBundle();
+        @SuppressWarnings("deprecation")
+        GenericDocumentParcel documentParcel =
+                outParcel.readParcelable(GenericDocumentParcel.class.getClassLoader());
         outParcel.recycle();
 
         // Compare results
-        GenericDocument outDoc = new GenericDocument(outBundle);
+        GenericDocument outDoc = new GenericDocument(documentParcel);
         assertThat(inDoc).isEqualTo(outDoc);
         assertThat(outDoc.getParentTypes()).isEqualTo(Arrays.asList("Class1", "Class2"));
         assertThat(outDoc.getPropertyString("propString")).isEqualTo("Hello");
@@ -112,47 +117,23 @@
                 .setParentTypes(new ArrayList<>(Arrays.asList("Class1", "Class2")))
                 .setScore(42)
                 .setPropertyString("propString", "Hello")
-                .setPropertyBytes("propBytes", new byte[][]{{1, 2}})
-                .setPropertyDocument(
-                        "propDocument",
-                        new GenericDocument.Builder<>("namespace", "id2", "schema2")
-                                .setPropertyString("propString", "Goodbye")
-                                .setPropertyBytes("propBytes", new byte[][]{{3, 4}})
-                                .build())
                 .build();
 
         GenericDocument newDoc = new GenericDocument.Builder<>(oldDoc)
                 .setParentTypes(new ArrayList<>(Arrays.asList("Class3", "Class4")))
-                .setPropertyBytes("propBytes", new byte[][]{{1, 2}})
-                .setPropertyDocument(
-                        "propDocument",
-                        new GenericDocument.Builder<>("namespace", "id3", "schema3")
-                                .setPropertyString("propString", "Bye")
-                                .setPropertyBytes("propBytes", new byte[][]{{5, 6}})
-                                .build())
+                .setPropertyString("propString", "Bye")
                 .build();
 
         // Check that the original GenericDocument is unmodified.
         assertThat(oldDoc.getParentTypes()).isEqualTo(Arrays.asList("Class1", "Class2"));
         assertThat(oldDoc.getScore()).isEqualTo(42);
         assertThat(oldDoc.getPropertyString("propString")).isEqualTo("Hello");
-        assertThat(oldDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][]{{1, 2}});
-        assertThat(oldDoc.getPropertyDocument("propDocument").getPropertyString("propString"))
-                .isEqualTo("Goodbye");
-        assertThat(oldDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
-                .isEqualTo(new byte[][]{{3, 4}});
 
         // Check that the new GenericDocument has modified the original fields correctly.
         assertThat(newDoc.getParentTypes()).isEqualTo(Arrays.asList("Class3", "Class4"));
-        assertThat(newDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][]{{1, 2}});
-        assertThat(newDoc.getPropertyDocument("propDocument").getPropertyString("propString"))
-                .isEqualTo("Bye");
-        assertThat(newDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
-                .isEqualTo(new byte[][]{{5, 6}});
+        assertThat(newDoc.getPropertyString("propString")).isEqualTo("Bye");
 
         // Check that the new GenericDocument copies fields that aren't set.
         assertThat(oldDoc.getScore()).isEqualTo(newDoc.getScore());
-        assertThat(oldDoc.getPropertyString("propString")).isEqualTo(newDoc.getPropertyString(
-                "propString"));
     }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/InternalVisibilityConfigTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/InternalVisibilityConfigTest.java
new file mode 100644
index 0000000..c63e53e
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/InternalVisibilityConfigTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class InternalVisibilityConfigTest {
+
+    @Test
+    public void testVisibilityConfig_setNotDisplayBySystem() {
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("schema")
+                .setNotDisplayedBySystem(true).build();
+
+        assertThat(visibilityConfig.isNotDisplayedBySystem()).isTrue();
+    }
+
+    @Test
+    public void testVisibilityConfig_setVisibilityConfig() {
+        String visibleToPackage1 = "com.example.package";
+        byte[] visibleToPackageCert1 = new byte[32];
+        String visibleToPackage2 = "com.example.package2";
+        byte[] visibleToPackageCert2 = new byte[32];
+
+        SchemaVisibilityConfig innerConfig1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(
+                        new PackageIdentifier(visibleToPackage1, visibleToPackageCert1))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+        SchemaVisibilityConfig innerConfig2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(
+                        new PackageIdentifier(visibleToPackage2, visibleToPackageCert2))
+                .addRequiredPermissions(ImmutableSet.of(3, 4))
+                .build();
+
+        InternalVisibilityConfig visibilityConfig = new InternalVisibilityConfig.Builder("schema")
+                .addVisibleToConfig(innerConfig1)
+                .addVisibleToConfig(innerConfig2)
+                .build();
+
+        assertThat(visibilityConfig.getVisibleToConfigs())
+                .containsExactly(innerConfig1, innerConfig2);
+    }
+
+    @Test
+    public void testToInternalVisibilityConfig() {
+        byte[] packageSha256Cert = new byte[32];
+        packageSha256Cert[0] = 24;
+        packageSha256Cert[8] = 23;
+        packageSha256Cert[16] = 22;
+        packageSha256Cert[24] = 21;
+
+        // Create a SetSchemaRequest for testing
+        SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder()
+                .addSchemas(new AppSearchSchema.Builder("testSchema").build())
+                .setSchemaTypeDisplayedBySystem("testSchema", false)
+                .setSchemaTypeVisibilityForPackage("testSchema", /*visible=*/true,
+                        new PackageIdentifier("com.example.test", packageSha256Cert))
+                .setPubliclyVisibleSchema("testSchema",
+                        new PackageIdentifier("com.example.test1", packageSha256Cert))
+                .build();
+
+        // Convert the SetSchemaRequest to GenericDocument map
+        List<InternalVisibilityConfig> visibilityConfigs =
+                InternalVisibilityConfig.toInternalVisibilityConfigs(setSchemaRequest);
+
+        // Check if the conversion is correct
+        assertThat(visibilityConfigs).hasSize(1);
+        InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(0);
+        assertThat(visibilityConfig.isNotDisplayedBySystem()).isTrue();
+        assertThat(visibilityConfig.getVisibilityConfig().getAllowedPackages())
+                .containsExactly(new PackageIdentifier("com.example.test", packageSha256Cert));
+        assertThat(visibilityConfig.getVisibilityConfig().getPubliclyVisibleTargetPackage())
+                .isNotNull();
+        assertThat(
+                visibilityConfig.getVisibilityConfig().getPubliclyVisibleTargetPackage()
+                        .getPackageName())
+                .isEqualTo("com.example.test1");
+        assertThat(
+                visibilityConfig.getVisibilityConfig().getPubliclyVisibleTargetPackage()
+                        .getSha256Certificate())
+                .isEqualTo(packageSha256Cert);
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/JoinSpecInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/JoinSpecInternalTest.java
new file mode 100644
index 0000000..9f79469
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/JoinSpecInternalTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class JoinSpecInternalTest {
+    @Test
+    public void testJoinSpecBuilderCopyConstructor() {
+        JoinSpec joinSpec = new JoinSpec.Builder("childPropertyExpression")
+                .setMaxJoinedResultCount(10)
+                .setNestedSearch("nestedQuery", new SearchSpec.Builder().build())
+                .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
+                .build();
+        JoinSpec joinSpecCopy = new JoinSpec.Builder(joinSpec).build();
+        assertThat(joinSpecCopy.getMaxJoinedResultCount()).isEqualTo(
+                joinSpec.getMaxJoinedResultCount());
+        assertThat(joinSpecCopy.getChildPropertyExpression()).isEqualTo(
+                joinSpec.getChildPropertyExpression());
+        assertThat(joinSpecCopy.getNestedQuery()).isEqualTo(joinSpec.getNestedQuery());
+        assertThat(joinSpecCopy.getNestedSearchSpec()).isNotNull();
+        assertThat(joinSpecCopy.getAggregationScoringStrategy()).isEqualTo(
+                joinSpec.getAggregationScoringStrategy());
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultInternalTest.java
new file mode 100644
index 0000000..bec84cb
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultInternalTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class SearchResultInternalTest {
+    @Test
+    public void testSearchResultBuilderCopyConstructor() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "id", "schemaType").build();
+        SearchResult searchResult = new SearchResult.Builder("package", "database")
+                .setGenericDocument(document)
+                .setRankingSignal(1.23)
+                .addJoinedResult(new SearchResult.Builder("pkg1", "db1").setGenericDocument(
+                        document).build())
+                .addJoinedResult(new SearchResult.Builder("pkg2", "db2").setGenericDocument(
+                        document).build())
+                .addMatchInfo(new SearchResult.MatchInfo.Builder("propertyPath1").build())
+                .addMatchInfo(new SearchResult.MatchInfo.Builder("propertyPath2").build())
+                .addMatchInfo(new SearchResult.MatchInfo.Builder("propertyPath3").build())
+                .build();
+        SearchResult searchResultCopy = new SearchResult.Builder(searchResult).build();
+        assertThat(searchResultCopy.getGenericDocument()).isEqualTo(
+                searchResult.getGenericDocument());
+        assertThat(searchResultCopy.getRankingSignal()).isEqualTo(searchResult.getRankingSignal());
+        // Specifically test JoinedResults and MatchInfos with different sizes since briefly had
+        // a bug where we looped through joinedResults using matchInfos.size()
+        assertThat(searchResultCopy.getJoinedResults().size()).isEqualTo(
+                searchResult.getJoinedResults().size());
+        assertThat(searchResultCopy.getJoinedResults().get(0).getPackageName()).isEqualTo("pkg1");
+        assertThat(searchResultCopy.getJoinedResults().get(0).getDatabaseName()).isEqualTo("db1");
+        assertThat(searchResultCopy.getJoinedResults().get(1).getPackageName()).isEqualTo("pkg2");
+        assertThat(searchResultCopy.getJoinedResults().get(1).getDatabaseName()).isEqualTo("db2");
+        assertThat(searchResultCopy.getMatchInfos().size()).isEqualTo(
+                searchResult.getMatchInfos().size());
+        assertThat(searchResultCopy.getMatchInfos().get(0).getPropertyPath()).isEqualTo(
+                "propertyPath1");
+        assertThat(searchResultCopy.getMatchInfos().get(1).getPropertyPath()).isEqualTo(
+                "propertyPath2");
+        assertThat(searchResultCopy.getMatchInfos().get(2).getPropertyPath()).isEqualTo(
+                "propertyPath3");
+    }
+
+    @Test
+    public void testSearchResultBuilderCopyConstructor_informationalRankingSignal() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "id", "schemaType").build();
+        SearchResult searchResult = new SearchResult.Builder("package", "database")
+                .setGenericDocument(document)
+                .setRankingSignal(1.23)
+                .addInformationalRankingSignal(2)
+                .addInformationalRankingSignal(3)
+                .build();
+        SearchResult searchResultCopy = new SearchResult.Builder(searchResult).build();
+        assertThat(searchResultCopy.getRankingSignal()).isEqualTo(searchResult.getRankingSignal());
+        assertThat(searchResultCopy.getInformationalRankingSignals()).isEqualTo(
+                searchResult.getInformationalRankingSignals());
+    }
+
+    @Test
+    public void testSearchResultBuilder_clearJoinedResults() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "id", "schemaType").build();
+        SearchResult searchResult = new SearchResult.Builder("package", "database")
+                .setGenericDocument(document)
+                .addJoinedResult(new SearchResult.Builder("pkg", "db").setGenericDocument(
+                        document).build())
+                .clearJoinedResults()
+                .build();
+        assertThat(searchResult.getJoinedResults()).isEmpty();
+    }
+
+    @Test
+    public void testMatchInfoBuilderCopyConstructor() {
+        SearchResult.MatchRange exactMatchRange = new SearchResult.MatchRange(3, 8);
+        SearchResult.MatchRange submatchRange = new SearchResult.MatchRange(3, 5);
+        SearchResult.MatchRange snippetMatchRange = new SearchResult.MatchRange(1, 10);
+        SearchResult.MatchInfo matchInfo =
+                new SearchResult.MatchInfo.Builder("propertyPath1")
+                        .setExactMatchRange(exactMatchRange)
+                        .setSubmatchRange(submatchRange)
+                        .setSnippetRange(snippetMatchRange).build();
+        SearchResult.MatchInfo matchInfoCopy =
+                new SearchResult.MatchInfo.Builder(matchInfo).build();
+        assertThat(matchInfoCopy.getPropertyPath()).isEqualTo("propertyPath1");
+        assertThat(matchInfoCopy.getExactMatchRange()).isEqualTo(exactMatchRange);
+        assertThat(matchInfoCopy.getSubmatchRange()).isEqualTo(submatchRange);
+        assertThat(matchInfoCopy.getSnippetRange()).isEqualTo(snippetMatchRange);
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultPageInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultPageInternalTest.java
new file mode 100644
index 0000000..e37d1f854
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchResultPageInternalTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class SearchResultPageInternalTest {
+    @Test
+    public void testSearchResultPage() {
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "id", "schemaType").build();
+        List<SearchResult> results = Arrays.asList(
+                new SearchResult.Builder("package1", "database1").setGenericDocument(
+                        document).build(),
+                new SearchResult.Builder("package2", "database2").setGenericDocument(
+                        document).build()
+        );
+        SearchResultPage searchResultPage = new SearchResultPage(/*nextPageToken=*/ 123, results);
+        assertThat(searchResultPage.getNextPageToken()).isEqualTo(123);
+        List<SearchResult> searchResults = searchResultPage.getResults();
+        assertThat(searchResults).hasSize(2);
+        assertThat(searchResults.get(0).getPackageName()).isEqualTo("package1");
+        assertThat(searchResults.get(0).getDatabaseName()).isEqualTo("database1");
+        assertThat(searchResults.get(0).getGenericDocument()).isEqualTo(document);
+        assertThat(searchResults.get(1).getPackageName()).isEqualTo("package2");
+        assertThat(searchResults.get(1).getDatabaseName()).isEqualTo("database2");
+        assertThat(searchResults.get(1).getGenericDocument()).isEqualTo(document);
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecInternalTest.java
index ed00e5a..e733fc3 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecInternalTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSpecInternalTest.java
@@ -18,22 +18,17 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertThrows;
-
-import android.os.Bundle;
-
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 
 import org.junit.Test;
 
-import java.util.List;
-import java.util.Map;
+import java.util.Arrays;
 
 /** Tests for private APIs of {@link SearchSpec}. */
 public class SearchSpecInternalTest {
 
     @Test
-    public void testGetBundle() {
+    public void testSearchSpecBuilder() {
         SearchSpec searchSpec = new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
                 .addFilterNamespaces("namespace1", "namespace2")
@@ -50,28 +45,135 @@
                 .setListFilterQueryLanguageEnabled(true)
                 .build();
 
-        Bundle bundle = searchSpec.getBundle();
-        assertThat(bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD))
-                .isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
-        assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD)).containsExactly(
+        assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
+        assertThat(searchSpec.getFilterNamespaces()).containsExactly(
                 "namespace1", "namespace2");
-        assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_FIELD)).containsExactly(
+        assertThat(searchSpec.getFilterSchemas()).containsExactly(
                 "schemaTypes1", "schemaTypes2");
-        assertThat(bundle.getStringArrayList(SearchSpec.PACKAGE_NAME_FIELD)).containsExactly(
+        assertThat(searchSpec.getFilterPackageNames()).containsExactly(
                 "package1", "package2");
-        assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5);
-        assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10);
-        assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15);
-        assertThat(bundle.getInt(SearchSpec.NUM_PER_PAGE_FIELD)).isEqualTo(42);
-        assertThat(bundle.getInt(SearchSpec.ORDER_FIELD)).isEqualTo(SearchSpec.ORDER_ASCENDING);
-        assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD))
+        assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
+        assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
+        assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
+        assertThat(searchSpec.getResultCountPerPage()).isEqualTo(42);
+        assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
+        assertThat(searchSpec.getRankingStrategy())
                 .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
-        assertThat(bundle.getStringArrayList(SearchSpec.ENABLED_FEATURES_FIELD)).containsExactly(
+        assertThat(searchSpec.getEnabledFeatures()).containsExactly(
                 Features.NUMERIC_SEARCH, Features.VERBATIM_SEARCH,
                 Features.LIST_FILTER_QUERY_LANGUAGE);
     }
 
     @Test
+    public void testSearchSpecBuilderCopyConstructor() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                .addFilterNamespaces("namespace1", "namespace2")
+                .addFilterSchemas("schemaTypes1", "schemaTypes2")
+                .addFilterPackageNames("package1", "package2")
+                .addFilterProperties("schemaTypes1", Arrays.asList("path1", "path2"))
+                .addProjection("schemaTypes1", Arrays.asList("path1", "path2"))
+                .setPropertyWeights("schemaTypes1", ImmutableMap.of("path1", 1.0, "path2", 2.0))
+                .setSnippetCount(5)
+                .setSnippetCountPerProperty(10)
+                .setMaxSnippetSize(15)
+                .setResultCountPerPage(42)
+                .setOrder(SearchSpec.ORDER_ASCENDING)
+                .setRankingStrategy("advancedExpression")
+                .setNumericSearchEnabled(true)
+                .setVerbatimSearchEnabled(true)
+                .setListFilterQueryLanguageEnabled(true)
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, 10)
+                .setSearchSourceLogTag("searchSourceLogTag")
+                .build();
+
+        SearchSpec searchSpecCopy = new SearchSpec.Builder(searchSpec).build();
+        assertThat(searchSpecCopy.getTermMatch()).isEqualTo(searchSpec.getTermMatch());
+        assertThat(searchSpecCopy.getFilterNamespaces()).isEqualTo(
+                searchSpec.getFilterNamespaces());
+        assertThat(searchSpecCopy.getFilterSchemas()).isEqualTo(searchSpecCopy.getFilterSchemas());
+        assertThat(searchSpecCopy.getFilterPackageNames()).isEqualTo(
+                searchSpec.getFilterPackageNames());
+        assertThat(searchSpecCopy.getFilterProperties()).isEqualTo(
+                searchSpec.getFilterProperties());
+        assertThat(searchSpecCopy.getProjections()).isEqualTo(searchSpec.getProjections());
+        assertThat(searchSpecCopy.getPropertyWeights()).isEqualTo(searchSpec.getPropertyWeights());
+        assertThat(searchSpecCopy.getSnippetCount()).isEqualTo(searchSpec.getSnippetCount());
+        assertThat(searchSpecCopy.getSnippetCountPerProperty()).isEqualTo(
+                searchSpec.getSnippetCountPerProperty());
+        assertThat(searchSpecCopy.getMaxSnippetSize()).isEqualTo(searchSpec.getMaxSnippetSize());
+        assertThat(searchSpecCopy.getResultCountPerPage()).isEqualTo(
+                searchSpec.getResultCountPerPage());
+        assertThat(searchSpecCopy.getOrder()).isEqualTo(searchSpec.getOrder());
+        assertThat(searchSpecCopy.getRankingStrategy()).isEqualTo(searchSpec.getRankingStrategy());
+        assertThat(searchSpecCopy.getEnabledFeatures()).containsExactlyElementsIn(
+                searchSpec.getEnabledFeatures());
+        assertThat(searchSpecCopy.getResultGroupingTypeFlags()).isEqualTo(
+                searchSpec.getResultGroupingTypeFlags());
+        assertThat(searchSpecCopy.getResultGroupingLimit()).isEqualTo(
+                searchSpec.getResultGroupingLimit());
+        assertThat(searchSpecCopy.getJoinSpec()).isEqualTo(searchSpec.getJoinSpec());
+        assertThat(searchSpecCopy.getAdvancedRankingExpression()).isEqualTo(
+                searchSpec.getAdvancedRankingExpression());
+        assertThat(searchSpecCopy.getSearchSourceLogTag()).isEqualTo(
+                searchSpec.getSearchSourceLogTag());
+    }
+
+    @Test
+    public void testSearchSpecBuilderCopyConstructor_embeddingSearch() {
+        EmbeddingVector embedding1 = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+        EmbeddingVector embedding2 = new EmbeddingVector(
+                new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(embedding1, embedding2)
+                .build();
+
+        // Check that copy constructor works.
+        SearchSpec searchSpecCopy = new SearchSpec.Builder(searchSpec).build();
+        assertThat(searchSpecCopy.getEnabledFeatures()).containsExactlyElementsIn(
+                searchSpec.getEnabledFeatures());
+        assertThat(searchSpecCopy.getDefaultEmbeddingSearchMetricType()).isEqualTo(
+                searchSpec.getDefaultEmbeddingSearchMetricType());
+        assertThat(searchSpecCopy.getSearchEmbeddings()).containsExactlyElementsIn(
+                searchSpec.getSearchEmbeddings());
+    }
+
+    @Test
+    public void testSearchSpecBuilderCopyConstructor_informationalRankingExpressions() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setRankingStrategy("advancedExpression")
+                .addInformationalRankingExpressions("this.relevanceScore()")
+                .build();
+
+        SearchSpec searchSpecCopy = new SearchSpec.Builder(searchSpec).build();
+        assertThat(searchSpecCopy.getRankingStrategy()).isEqualTo(searchSpec.getRankingStrategy());
+        assertThat(searchSpecCopy.getAdvancedRankingExpression()).isEqualTo(
+                searchSpec.getAdvancedRankingExpression());
+        assertThat(searchSpecCopy.getInformationalRankingExpressions()).isEqualTo(
+                searchSpec.getInformationalRankingExpressions());
+    }
+
+    // TODO(b/309826655): Flag guard this test.
+    @Test
+    public void testGetBundle_hasProperty() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setNumericSearchEnabled(true)
+                .setVerbatimSearchEnabled(true)
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterHasPropertyFunctionEnabled(true)
+                .build();
+
+        assertThat(searchSpec.getEnabledFeatures()).containsExactly(
+                Features.NUMERIC_SEARCH, Features.VERBATIM_SEARCH,
+                Features.LIST_FILTER_QUERY_LANGUAGE, Features.LIST_FILTER_HAS_PROPERTY_FUNCTION);
+    }
+
+    @Test
     public void testBuildMultipleSearchSpecs() {
         SearchSpec.Builder builder = new SearchSpec.Builder();
         SearchSpec searchSpec1 = builder.build();
@@ -90,37 +192,46 @@
                 Features.VERBATIM_SEARCH, Features.LIST_FILTER_QUERY_LANGUAGE);
     }
 
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
     @Test
-    public void testGetPropertyFiltersTypePropertyMasks() {
+    public void testGetEnabledFeatures_embeddingSearch() {
         SearchSpec searchSpec = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                .addFilterProperties("TypeA", ImmutableList.of("field1", "field2.subfield2"))
-                .addFilterProperties("TypeB", ImmutableList.of("field7"))
-                .addFilterProperties("TypeC", ImmutableList.of())
+                .setNumericSearchEnabled(true)
+                .setVerbatimSearchEnabled(true)
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterHasPropertyFunctionEnabled(true)
+                .setEmbeddingSearchEnabled(true)
                 .build();
+        assertThat(searchSpec.getEnabledFeatures()).containsExactly(
+                Features.NUMERIC_SEARCH, Features.VERBATIM_SEARCH,
+                Features.LIST_FILTER_QUERY_LANGUAGE, Features.LIST_FILTER_HAS_PROPERTY_FUNCTION,
+                FeatureConstants.EMBEDDING_SEARCH);
 
-        Map<String, List<String>> typePropertyPathMap = searchSpec.getFilterProperties();
-        assertThat(typePropertyPathMap.keySet())
-                .containsExactly("TypeA", "TypeB", "TypeC");
-        assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
-        assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
-        assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
+        // Check that copy constructor works.
+        SearchSpec searchSpecCopy = new SearchSpec.Builder(searchSpec).build();
+        assertThat(searchSpecCopy.getEnabledFeatures()).containsExactly(
+                Features.NUMERIC_SEARCH, Features.VERBATIM_SEARCH,
+                Features.LIST_FILTER_QUERY_LANGUAGE, Features.LIST_FILTER_HAS_PROPERTY_FUNCTION,
+                FeatureConstants.EMBEDDING_SEARCH);
     }
 
-    // TODO(b/296088047): move to CTS once the APIs it uses are public
     @Test
-    public void testBuilder_throwsException_whenTypePropertyFilterNotInSchemaFilter() {
-        SearchSpec.Builder searchSpecBuilder = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                .addFilterSchemas("Schema1", "Schema2")
-                .addFilterPropertyPaths("Schema3", ImmutableList.of(
-                        new PropertyPath("field1"), new PropertyPath("field2.subfield2")));
+    public void testGetEnabledFeatures_tokenize() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setNumericSearchEnabled(true)
+                .setVerbatimSearchEnabled(true)
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterTokenizeFunctionEnabled(true)
+                .build();
+        assertThat(searchSpec.getEnabledFeatures()).containsExactly(
+                Features.NUMERIC_SEARCH, Features.VERBATIM_SEARCH,
+                Features.LIST_FILTER_QUERY_LANGUAGE,
+                FeatureConstants.LIST_FILTER_TOKENIZE_FUNCTION);
 
-        IllegalStateException exception =
-                assertThrows(IllegalStateException.class, searchSpecBuilder::build);
-        assertThat(exception.getMessage())
-                .isEqualTo("The schema: Schema3 exists in the property filter but doesn't"
-                        + " exist in the schema filter.");
+        // Check that copy constructor works.
+        SearchSpec searchSpecCopy = new SearchSpec.Builder(searchSpec).build();
+        assertThat(searchSpecCopy.getEnabledFeatures()).containsExactly(
+                Features.NUMERIC_SEARCH, Features.VERBATIM_SEARCH,
+                Features.LIST_FILTER_QUERY_LANGUAGE,
+                FeatureConstants.LIST_FILTER_TOKENIZE_FUNCTION);
     }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSuggestionSpecInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSuggestionSpecInternalTest.java
deleted file mode 100644
index bc68f37..0000000
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SearchSuggestionSpecInternalTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appsearch.app;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Test;
-
-// TODO(b/228240987) delete this test when we support property restrict for multiple terms
-public class SearchSuggestionSpecInternalTest {
-
-    @Test
-    public void testBuildSearchSuggestionSpec_withPropertyFilter() throws Exception {
-        SearchSuggestionSpec searchSuggestionSpec =
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
-                        .setRankingStrategy(SearchSuggestionSpec
-                                .SUGGESTION_RANKING_STRATEGY_TERM_FREQUENCY)
-                        .addFilterSchemas("Person", "Email")
-                        .addFilterSchemas(ImmutableList.of("Foo"))
-                        .addFilterProperties("Email", ImmutableList.of("Subject", "body"))
-                        .addFilterPropertyPaths("Foo",
-                                ImmutableList.of(new PropertyPath("Bar")))
-                        .build();
-
-        assertThat(searchSuggestionSpec.getMaximumResultCount()).isEqualTo(123);
-        assertThat(searchSuggestionSpec.getFilterSchemas())
-                .containsExactly("Person", "Email", "Foo");
-        assertThat(searchSuggestionSpec.getFilterProperties())
-                .containsExactly("Email",  ImmutableList.of("Subject", "body"),
-                        "Foo",  ImmutableList.of("Bar"));
-    }
-
-    @Test
-    public void testPropertyFilterMustMatchSchemaFilter() throws Exception {
-        IllegalStateException e = assertThrows(IllegalStateException.class,
-                () -> new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
-                        .addFilterSchemas("Person")
-                        .addFilterProperties("Email", ImmutableList.of("Subject", "body"))
-                        .build());
-        assertThat(e).hasMessageThat().contains("The schema: Email exists in the "
-                + "property filter but doesn't exist in the schema filter.");
-    }
-
-    @Test
-    public void testRebuild_withPropertyFilter() throws Exception {
-        SearchSuggestionSpec.Builder builder =
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
-                        .addFilterSchemas("Person", "Email")
-                        .addFilterProperties("Email", ImmutableList.of("Subject", "body"));
-
-        SearchSuggestionSpec original = builder.build();
-
-        builder.addFilterSchemas("Message", "Foo")
-                .addFilterProperties("Foo", ImmutableList.of("Bar"));
-        SearchSuggestionSpec rebuild = builder.build();
-
-        assertThat(original.getMaximumResultCount()).isEqualTo(123);
-        assertThat(original.getFilterSchemas())
-                .containsExactly("Person", "Email");
-        assertThat(original.getFilterProperties())
-                .containsExactly("Email",  ImmutableList.of("Subject", "body"));
-
-        assertThat(rebuild.getMaximumResultCount()).isEqualTo(123);
-        assertThat(rebuild.getFilterSchemas())
-                .containsExactly("Person", "Email", "Message", "Foo");
-        assertThat(rebuild.getFilterProperties())
-                .containsExactly("Email",  ImmutableList.of("Subject", "body"),
-                        "Foo",  ImmutableList.of("Bar"));
-    }
-}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java
index 37e1255..145679a 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/SetSchemaResponseInternalTest.java
@@ -18,8 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertThrows;
-
 import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
 
@@ -55,7 +53,7 @@
         assertThat(original.getMigratedTypes()).containsExactly("migrated1");
         assertThat(original.getMigrationFailures()).containsExactly(failure1);
 
-        SetSchemaResponse rebuild = original.toBuilder()
+        SetSchemaResponse rebuild = new SetSchemaResponse.Builder(original)
                         .addDeletedType("delete2")
                         .addIncompatibleType("incompatible2")
                         .addMigratedType("migrated2")
@@ -82,7 +80,6 @@
                 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId1")
                         .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
                         .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .setDeletionPropagation(true)
                         .build())
                 .build();
 
@@ -95,20 +92,5 @@
                 .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
         assertThat(((StringPropertyConfig) properties.get(0)).getJoinableValueType())
                 .isEqualTo(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
-        assertThat(((StringPropertyConfig) properties.get(0)).getDeletionPropagation())
-                .isEqualTo(true);
-    }
-
-    // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
-    @Test
-    public void testStringPropertyConfig_setJoinableProperty_deletePropagationError() {
-        final StringPropertyConfig.Builder builder =
-                new StringPropertyConfig.Builder("qualifiedId")
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setDeletionPropagation(true);
-        IllegalStateException e =
-                assertThrows(IllegalStateException.class, () -> builder.build());
-        assertThat(e).hasMessageThat().contains(
-                "Cannot set deletion propagation without setting a joinable value type");
     }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java
index 4118c45..0e4952c 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java
@@ -25,13 +25,23 @@
 import androidx.appsearch.app.AppSearchSchema.LongPropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+import androidx.appsearch.app.PropertyPath;
+import androidx.appsearch.flags.CheckFlagsRule;
+import androidx.appsearch.flags.DeviceFlagsValueProvider;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.flags.RequiresFlagsEnabled;
 import androidx.appsearch.testutil.AppSearchEmail;
 
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Collections;
+import java.util.List;
 
 public class AppSearchSchemaCtsTest {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     public void testInvalidEnums() {
         StringPropertyConfig.Builder builder = new StringPropertyConfig.Builder("test");
@@ -165,6 +175,243 @@
     }
 
     @Test
+    public void testParentTypes() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("EmailMessage")
+                        .addParentType("Email")
+                        .addParentType("Message")
+                        .build();
+        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
+    }
+
+    @Test
+    public void testDuplicateParentTypes() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("EmailMessage")
+                        .addParentType("Email")
+                        .addParentType("Message")
+                        .addParentType("Email")
+                        .build();
+        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
+    }
+
+    @Test
+    public void testDocumentPropertyConfig_indexableNestedPropertyStrings() {
+        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedProperties("prop1", "prop2", "prop1.prop2")
+                        .build();
+        assertThat(documentPropertyConfig.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop2", "prop1.prop2");
+    }
+
+    @Test
+    public void testDocumentPropertyConfig_indexableNestedPropertyPropertyPaths() {
+        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedPropertyPaths(
+                                new PropertyPath("prop1"), new PropertyPath("prop1.prop2"))
+                        .build();
+        assertThat(documentPropertyConfig.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop1.prop2");
+    }
+
+    @Test
+    public void testDocumentPropertyConfig_indexableNestedPropertyProperty_duplicatePaths() {
+        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedPropertyPaths(
+                                new PropertyPath("prop1"), new PropertyPath("prop1.prop2"))
+                        .addIndexableNestedProperties("prop1")
+                        .build();
+        assertThat(documentPropertyConfig.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop1.prop2");
+    }
+
+    @Test
+    public void testDocumentPropertyConfig_reusingBuilderDoesNotAffectPreviouslyBuiltConfigs() {
+        AppSearchSchema.DocumentPropertyConfig.Builder builder =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedProperties("prop1");
+        AppSearchSchema.DocumentPropertyConfig config1 = builder.build();
+        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
+
+        builder.addIndexableNestedProperties("prop2");
+        AppSearchSchema.DocumentPropertyConfig config2 = builder.build();
+        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
+        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
+
+        builder.addIndexableNestedPropertyPaths(new PropertyPath("prop3"));
+        AppSearchSchema.DocumentPropertyConfig config3 = builder.build();
+        assertThat(config3.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop2", "prop3");
+        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
+        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
+    }
+
+    @Test
+    public void testPropertyConfig() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("Test")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("string")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("long")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.LongPropertyConfig
+                                                        .INDEXING_TYPE_NONE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("indexableLong")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.LongPropertyConfig
+                                                        .INDEXING_TYPE_RANGE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DoublePropertyConfig.Builder("double")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                        "document1", AppSearchEmail.SCHEMA_TYPE)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(true)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                        "document2", AppSearchEmail.SCHEMA_TYPE)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties("path1", "path2", "path3")
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId1")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setJoinableValueType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId2")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setJoinableValueType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .build())
+                        .build();
+
+        assertThat(schema.getSchemaType()).isEqualTo("Test");
+        List<PropertyConfig> properties = schema.getProperties();
+        assertThat(properties).hasSize(10);
+
+        assertThat(properties.get(0).getName()).isEqualTo("string");
+        assertThat(properties.get(0).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
+        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getIndexingType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS);
+        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(
+                0)).getTokenizerType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN);
+
+        assertThat(properties.get(1).getName()).isEqualTo("long");
+        assertThat(properties.get(1).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(((AppSearchSchema.LongPropertyConfig) properties.get(1)).getIndexingType())
+                .isEqualTo(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE);
+
+        assertThat(properties.get(2).getName()).isEqualTo("indexableLong");
+        assertThat(properties.get(2).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(((AppSearchSchema.LongPropertyConfig) properties.get(2)).getIndexingType())
+                .isEqualTo(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE);
+
+        assertThat(properties.get(3).getName()).isEqualTo("double");
+        assertThat(properties.get(3).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
+        assertThat(properties.get(3)).isInstanceOf(AppSearchSchema.DoublePropertyConfig.class);
+
+        assertThat(properties.get(4).getName()).isEqualTo("boolean");
+        assertThat(properties.get(4).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
+        assertThat(properties.get(4)).isInstanceOf(AppSearchSchema.BooleanPropertyConfig.class);
+
+        assertThat(properties.get(5).getName()).isEqualTo("bytes");
+        assertThat(properties.get(5).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(properties.get(5)).isInstanceOf(AppSearchSchema.BytesPropertyConfig.class);
+
+        assertThat(properties.get(6).getName()).isEqualTo("document1");
+        assertThat(properties.get(6).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
+        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(6)).getSchemaType())
+                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
+        assertThat(
+                ((AppSearchSchema.DocumentPropertyConfig) properties.get(6))
+                        .shouldIndexNestedProperties())
+                .isEqualTo(true);
+
+        assertThat(properties.get(7).getName()).isEqualTo("document2");
+        assertThat(properties.get(7).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
+        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(7)).getSchemaType())
+                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
+        assertThat(
+                ((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
+                        .shouldIndexNestedProperties())
+                .isEqualTo(false);
+        assertThat(
+                ((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
+                        .getIndexableNestedProperties())
+                .containsExactly("path1", "path2", "path3");
+
+        assertThat(properties.get(8).getName()).isEqualTo("qualifiedId1");
+        assertThat(properties.get(8).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(
+                ((AppSearchSchema.StringPropertyConfig) properties.get(8))
+                        .getJoinableValueType())
+                .isEqualTo(
+                        AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+
+        assertThat(properties.get(9).getName()).isEqualTo("qualifiedId2");
+        assertThat(properties.get(9).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
+        assertThat(
+                ((AppSearchSchema.StringPropertyConfig) properties.get(9))
+                        .getJoinableValueType())
+                .isEqualTo(
+                        AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+    }
+
+    @Test
     public void testInvalidStringPropertyConfigsTokenizerNone() {
         // Everything should work fine with the defaults.
         final StringPropertyConfig.Builder builder =
@@ -195,6 +442,57 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTIONS)  // setDescription
+    public void testEquals_failure_differentDescription() {
+        AppSearchSchema.Builder schemaBuilder =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("A type of electronic message")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build());
+        AppSearchSchema schema1 = schemaBuilder.build();
+        AppSearchSchema schema2 =
+                schemaBuilder.setDescription("Mail, but like with an 'e'").build();
+        assertThat(schema1).isNotEqualTo(schema2);
+        assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTIONS)  // setDescription
+    public void testEquals_failure_differentPropertyDescription() {
+        AppSearchSchema schema1 =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("A type of electronic message")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setDescription("A summary of the contents of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                .build();
+        AppSearchSchema schema2 =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("A type of electronic message")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setDescription("The beginning of a message.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                .build();
+        assertThat(schema1).isNotEqualTo(schema2);
+        assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode());
+    }
+
+    @Test
     public void testInvalidStringPropertyConfigsTokenizerNonNone() {
         // Setting indexing type to be NONE with tokenizer type PLAIN or VERBATIM or RFC822 should
         // fail. Regardless of whether NONE is set explicitly or just kept as default.
@@ -250,162 +548,253 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTIONS)  // setDescription
     public void testAppSearchSchema_toString() {
-        AppSearchSchema schema = new AppSearchSchema.Builder("testSchema")
-                .addProperty(new StringPropertyConfig.Builder("string1")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("string2")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("string3")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("string4")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_VERBATIM)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("string5")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_RFC822)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("qualifiedId1")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("qualifiedId2")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .build())
-                .addProperty(new AppSearchSchema.LongPropertyConfig.Builder("long")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_NONE)
-                        .build())
-                .addProperty(new AppSearchSchema.LongPropertyConfig.Builder("indexableLong")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
-                        .build())
-                .addProperty(new AppSearchSchema.DoublePropertyConfig.Builder("double")
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .build())
-                .addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .build())
-                .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .build())
-                .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
-                        "document", AppSearchEmail.SCHEMA_TYPE)
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(true)
-                        .build())
-                .build();
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("testSchema")
+                        .setDescription("a test schema")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("string1")
+                                        .setDescription("first string")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("string2")
+                                        .setDescription("second string")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("string3")
+                                        .setDescription("third string")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("string4")
+                                        .setDescription("fourth string")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_VERBATIM)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("string5")
+                                        .setDescription("fifth string")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_RFC822)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("qualifiedId1")
+                                        .setDescription("first qualifiedId")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setJoinableValueType(
+                                                StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("qualifiedId2")
+                                        .setDescription("second qualifiedId")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setJoinableValueType(
+                                                StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("long")
+                                        .setDescription("a long")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_NONE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("indexableLong")
+                                        .setDescription("an indexed long")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DoublePropertyConfig.Builder("double")
+                                        .setDescription("a double")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
+                                        .setDescription("a boolean")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
+                                        .setDescription("some bytes")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "document", AppSearchEmail.SCHEMA_TYPE)
+                                        .setDescription("a document")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(true)
+                                        .build())
+                        .build();
 
         String schemaString = schema.toString();
 
-        String expectedString = "{\n"
-                + "  schemaType: \"testSchema\",\n"
-                + "  properties: [\n"
-                + "    {\n"
-                + "      name: \"boolean\",\n"
-                + "      cardinality: CARDINALITY_REQUIRED,\n"
-                + "      dataType: DATA_TYPE_BOOLEAN,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"bytes\",\n"
-                + "      cardinality: CARDINALITY_OPTIONAL,\n"
-                + "      dataType: DATA_TYPE_BYTES,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"document\",\n"
-                + "      shouldIndexNestedProperties: true,\n"
-                + "      schemaType: \"builtin:Email\",\n"
-                + "      cardinality: CARDINALITY_REPEATED,\n"
-                + "      dataType: DATA_TYPE_DOCUMENT,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"double\",\n"
-                + "      cardinality: CARDINALITY_REPEATED,\n"
-                + "      dataType: DATA_TYPE_DOUBLE,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"indexableLong\",\n"
-                + "      indexingType: INDEXING_TYPE_RANGE,\n"
-                + "      cardinality: CARDINALITY_OPTIONAL,\n"
-                + "      dataType: DATA_TYPE_LONG,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"long\",\n"
-                + "      indexingType: INDEXING_TYPE_NONE,\n"
-                + "      cardinality: CARDINALITY_OPTIONAL,\n"
-                + "      dataType: DATA_TYPE_LONG,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"qualifiedId1\",\n"
-                + "      indexingType: INDEXING_TYPE_NONE,\n"
-                + "      tokenizerType: TOKENIZER_TYPE_NONE,\n"
-                + "      joinableValueType: JOINABLE_VALUE_TYPE_QUALIFIED_ID,\n"
-                + "      cardinality: CARDINALITY_REQUIRED,\n"
-                + "      dataType: DATA_TYPE_STRING,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"qualifiedId2\",\n"
-                + "      indexingType: INDEXING_TYPE_NONE,\n"
-                + "      tokenizerType: TOKENIZER_TYPE_NONE,\n"
-                + "      joinableValueType: JOINABLE_VALUE_TYPE_QUALIFIED_ID,\n"
-                + "      cardinality: CARDINALITY_OPTIONAL,\n"
-                + "      dataType: DATA_TYPE_STRING,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"string1\",\n"
-                + "      indexingType: INDEXING_TYPE_NONE,\n"
-                + "      tokenizerType: TOKENIZER_TYPE_NONE,\n"
-                + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
-                + "      cardinality: CARDINALITY_REQUIRED,\n"
-                + "      dataType: DATA_TYPE_STRING,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"string2\",\n"
-                + "      indexingType: INDEXING_TYPE_EXACT_TERMS,\n"
-                + "      tokenizerType: TOKENIZER_TYPE_PLAIN,\n"
-                + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
-                + "      cardinality: CARDINALITY_REQUIRED,\n"
-                + "      dataType: DATA_TYPE_STRING,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"string3\",\n"
-                + "      indexingType: INDEXING_TYPE_PREFIXES,\n"
-                + "      tokenizerType: TOKENIZER_TYPE_PLAIN,\n"
-                + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
-                + "      cardinality: CARDINALITY_REQUIRED,\n"
-                + "      dataType: DATA_TYPE_STRING,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"string4\",\n"
-                + "      indexingType: INDEXING_TYPE_PREFIXES,\n"
-                + "      tokenizerType: TOKENIZER_TYPE_VERBATIM,\n"
-                + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
-                + "      cardinality: CARDINALITY_REQUIRED,\n"
-                + "      dataType: DATA_TYPE_STRING,\n"
-                + "    },\n"
-                + "    {\n"
-                + "      name: \"string5\",\n"
-                + "      indexingType: INDEXING_TYPE_PREFIXES,\n"
-                + "      tokenizerType: TOKENIZER_TYPE_RFC822,\n"
-                + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
-                + "      cardinality: CARDINALITY_REQUIRED,\n"
-                + "      dataType: DATA_TYPE_STRING,\n"
-                + "    }\n"
-                + "  ]\n"
-                + "}";
+        String expectedString =
+                "{\n"
+                        + "  schemaType: \"testSchema\",\n"
+                        + "  description: \"a test schema\",\n"
+                        + "  properties: [\n"
+                        + "    {\n"
+                        + "      name: \"boolean\",\n"
+                        + "      description: \"a boolean\",\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_BOOLEAN,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"bytes\",\n"
+                        + "      description: \"some bytes\",\n"
+                        + "      cardinality: CARDINALITY_OPTIONAL,\n"
+                        + "      dataType: DATA_TYPE_BYTES,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"document\",\n"
+                        + "      description: \"a document\",\n"
+                        + "      shouldIndexNestedProperties: true,\n"
+                        + "      schemaType: \"builtin:Email\",\n"
+                        + "      cardinality: CARDINALITY_REPEATED,\n"
+                        + "      dataType: DATA_TYPE_DOCUMENT,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"double\",\n"
+                        + "      description: \"a double\",\n"
+                        + "      cardinality: CARDINALITY_REPEATED,\n"
+                        + "      dataType: DATA_TYPE_DOUBLE,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"indexableLong\",\n"
+                        + "      description: \"an indexed long\",\n"
+                        + "      indexingType: INDEXING_TYPE_RANGE,\n"
+                        + "      cardinality: CARDINALITY_OPTIONAL,\n"
+                        + "      dataType: DATA_TYPE_LONG,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"long\",\n"
+                        + "      description: \"a long\",\n"
+                        + "      indexingType: INDEXING_TYPE_NONE,\n"
+                        + "      cardinality: CARDINALITY_OPTIONAL,\n"
+                        + "      dataType: DATA_TYPE_LONG,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"qualifiedId1\",\n"
+                        + "      description: \"first qualifiedId\",\n"
+                        + "      indexingType: INDEXING_TYPE_NONE,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_NONE,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_QUALIFIED_ID,\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"qualifiedId2\",\n"
+                        + "      description: \"second qualifiedId\",\n"
+                        + "      indexingType: INDEXING_TYPE_NONE,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_NONE,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_QUALIFIED_ID,\n"
+                        + "      cardinality: CARDINALITY_OPTIONAL,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"string1\",\n"
+                        + "      description: \"first string\",\n"
+                        + "      indexingType: INDEXING_TYPE_NONE,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_NONE,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"string2\",\n"
+                        + "      description: \"second string\",\n"
+                        + "      indexingType: INDEXING_TYPE_EXACT_TERMS,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_PLAIN,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"string3\",\n"
+                        + "      description: \"third string\",\n"
+                        + "      indexingType: INDEXING_TYPE_PREFIXES,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_PLAIN,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"string4\",\n"
+                        + "      description: \"fourth string\",\n"
+                        + "      indexingType: INDEXING_TYPE_PREFIXES,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_VERBATIM,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    },\n"
+                        + "    {\n"
+                        + "      name: \"string5\",\n"
+                        + "      description: \"fifth string\",\n"
+                        + "      indexingType: INDEXING_TYPE_PREFIXES,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_RFC822,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    }\n"
+                        + "  ]\n"
+                        + "}";
+
+        String[] lines = expectedString.split("\n");
+        for (String line : lines) {
+            assertThat(schemaString).contains(line);
+        }
+    }
+
+    @Test
+    public void testAppSearchSchema_toStringNoDescriptionSet() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("testSchema")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("string1")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                                        .build())
+                        .build();
+
+        String schemaString = schema.toString();
+
+        String expectedString =
+                          "{\n"
+                        + "  schemaType: \"testSchema\",\n"
+                        + "  description: \"\",\n"
+                        + "  properties: [\n"
+                        + "    {\n"
+                        + "      name: \"string1\",\n"
+                        + "      description: \"\",\n"
+                        + "      indexingType: INDEXING_TYPE_NONE,\n"
+                        + "      tokenizerType: TOKENIZER_TYPE_NONE,\n"
+                        + "      joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"
+                        + "      cardinality: CARDINALITY_REQUIRED,\n"
+                        + "      dataType: DATA_TYPE_STRING,\n"
+                        + "    }\n"
+                        + "  ]\n"
+                        + "}";
 
         String[] lines = expectedString.split("\n");
         for (String line : lines) {
@@ -466,4 +855,120 @@
                         "DocumentIndexingConfig#shouldIndexNestedProperties is required to be false"
                                 + " when one or more indexableNestedProperties are provided.");
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingPropertyConfig() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("Test")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("string")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("indexableLong")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.LongPropertyConfig
+                                                        .INDEXING_TYPE_RANGE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                        "document1", AppSearchEmail.SCHEMA_TYPE)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(true)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.EmbeddingPropertyConfig
+                                                        .INDEXING_TYPE_NONE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.EmbeddingPropertyConfig.Builder(
+                                        "indexableEmbedding")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.EmbeddingPropertyConfig
+                                                        .INDEXING_TYPE_SIMILARITY)
+                                        .build())
+                        .build();
+
+        assertThat(schema.getSchemaType()).isEqualTo("Test");
+        List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
+        assertThat(properties).hasSize(5);
+
+        assertThat(properties.get(0).getName()).isEqualTo("string");
+        assertThat(properties.get(0).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
+        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getIndexingType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS);
+        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getTokenizerType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN);
+
+        assertThat(properties.get(1).getName()).isEqualTo("indexableLong");
+        assertThat(properties.get(1).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(((AppSearchSchema.LongPropertyConfig) properties.get(1)).getIndexingType())
+                .isEqualTo(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE);
+
+        assertThat(properties.get(2).getName()).isEqualTo("document1");
+        assertThat(properties.get(2).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
+        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(2)).getSchemaType())
+                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
+        assertThat(
+                ((AppSearchSchema.DocumentPropertyConfig) properties.get(2))
+                        .shouldIndexNestedProperties())
+                .isEqualTo(true);
+
+        assertThat(properties.get(3).getName()).isEqualTo("embedding");
+        assertThat(properties.get(3).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(((AppSearchSchema.EmbeddingPropertyConfig) properties.get(3)).getIndexingType())
+                .isEqualTo(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE);
+
+        assertThat(properties.get(4).getName()).isEqualTo("indexableEmbedding");
+        assertThat(properties.get(4).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(((AppSearchSchema.EmbeddingPropertyConfig) properties.get(4)).getIndexingType())
+                .isEqualTo(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingPropertyConfig_defaultValues() {
+        AppSearchSchema.EmbeddingPropertyConfig builder =
+                new AppSearchSchema.EmbeddingPropertyConfig.Builder("test").build();
+        assertThat(builder.getIndexingType()).isEqualTo(
+                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE);
+        assertThat(builder.getCardinality()).isEqualTo(
+                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingPropertyConfig_setIndexingType() {
+        assertThrows(IllegalArgumentException.class, () ->
+                new AppSearchSchema.EmbeddingPropertyConfig.Builder("titleEmbedding")
+                        .setIndexingType(5).build());
+        assertThrows(IllegalArgumentException.class, () ->
+                new AppSearchSchema.EmbeddingPropertyConfig.Builder("titleEmbedding")
+                        .setIndexingType(2).build());
+        assertThrows(IllegalArgumentException.class, () ->
+                new AppSearchSchema.EmbeddingPropertyConfig.Builder("titleEmbedding")
+                        .setIndexingType(-1).build());
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationCtsTestBase.java
index 59c17f5..895a58a 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaMigrationCtsTestBase.java
@@ -154,9 +154,11 @@
     }
 
     @Test
-    public void testSchemaMigration_A_B_C_D() throws Exception {
+    public void test_ForceOverride_BackwardsCompatible_Trigger_MigrateIncompatibleType()
+            throws Exception {
         // create a backwards compatible schema and update the version
-        AppSearchSchema B_C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsCompatibleTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
                         .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                         .setIndexingType(
@@ -166,7 +168,7 @@
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(B_C_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsCompatibleTriggerSchema)
                         .setMigrator("testSchema", ACTIVE_NOOP_MIGRATOR)
                         .setForceOverride(true)
                         .setVersion(2)     // upgrade version
@@ -174,9 +176,11 @@
     }
 
     @Test
-    public void testSchemaMigration_A_B_NC_D() throws Exception {
+    public void testForceOverride_BackwardsCompatible_NoTrigger_MigrateIncompatibleType()
+            throws Exception {
         // create a backwards compatible schema but don't update the version
-        AppSearchSchema B_NC_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsCompatibleNoTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
                         .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                         .setIndexingType(
@@ -186,20 +190,22 @@
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(B_NC_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsCompatibleNoTriggerSchema)
                         .setMigrator("testSchema", ACTIVE_NOOP_MIGRATOR)
                         .setForceOverride(true)
                         .build()).get();
     }
 
     @Test
-    public void testSchemaMigration_A_NB_C_D() throws Exception {
+    public void testForceOverride_BackwardsIncompatible_Trigger_MigrateIncompatibleType()
+            throws Exception {
         // create a backwards incompatible schema and update the version
-        AppSearchSchema NB_C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsIncompatibleTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(NB_C_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsIncompatibleTriggerSchema)
                         .setMigrator("testSchema", ACTIVE_NOOP_MIGRATOR)
                         .setForceOverride(true)
                         .setVersion(2)     // upgrade version
@@ -207,13 +213,15 @@
     }
 
     @Test
-    public void testSchemaMigration_A_NB_C_ND() throws Exception {
+    public void testForceOverride_BackwardsIncompatible_Trigger_NoMigrateIncompatibleType()
+            throws Exception {
         // create a backwards incompatible schema and update the version
-        AppSearchSchema NB_C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsIncompatibleTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(NB_C_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsIncompatibleTriggerSchema)
                         .setMigrator("testSchema", INACTIVE_MIGRATOR)  //ND
                         .setForceOverride(true)
                         .setVersion(2)     // upgrade version
@@ -221,35 +229,42 @@
     }
 
     @Test
-    public void testSchemaMigration_A_NB_NC_D() throws Exception {
+    public void testForceOverride_BackwardsIncompatible_NoTrigger_MigrateIncompatibleType()
+            throws Exception {
         // create a backwards incompatible schema but don't update the version
-        AppSearchSchema NB_NC_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsIncompatibleNoTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(NB_NC_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsIncompatibleNoTriggerSchema)
                         .setMigrator("testSchema", ACTIVE_NOOP_MIGRATOR)
                         .setForceOverride(true)
                         .build()).get();
     }
 
     @Test
-    public void testSchemaMigration_A_NB_NC_ND() throws Exception {
+    public void testForceOverride_BackwardsIncompatible_NoTrigger_NoMigrateIncompatibleType()
+            throws Exception {
         // create a backwards incompatible schema but don't update the version
-        AppSearchSchema $B_$C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsIncompatibleNoMigrateIncompatibleTypeSchema =
+                new AppSearchSchema.Builder("testSchema")
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas($B_$C_Schema)
+                new SetSchemaRequest.Builder().addSchemas(
+                                backwardsIncompatibleNoMigrateIncompatibleTypeSchema)
                         .setMigrator("testSchema", INACTIVE_MIGRATOR)  //ND
                         .setForceOverride(true)
                         .build()).get();
     }
 
     @Test
-    public void testSchemaMigration_NA_B_C_D() throws Exception {
+    public void testNoForceOverride_BackwardsCompatible_Trigger_MigrateIncompatibleType()
+            throws Exception {
         // create a backwards compatible schema and update the version
-        AppSearchSchema B_C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsCompatibleTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
                         .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                         .setIndexingType(
@@ -259,16 +274,18 @@
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(B_C_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsCompatibleTriggerSchema)
                         .setMigrator("testSchema", ACTIVE_NOOP_MIGRATOR)
                         .setVersion(2)     // upgrade version
                         .build()).get();
     }
 
     @Test
-    public void testSchemaMigration_NA_B_NC_D() throws Exception {
+    public void testNoForceOverride_BackwardsCompatible_NoTrigger_MigrateIncompatibleType()
+            throws Exception {
         // create a backwards compatible schema but don't update the version
-        AppSearchSchema B_NC_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsCompatibleNoTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
                         .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                         .setIndexingType(
@@ -278,34 +295,38 @@
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(B_NC_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsCompatibleNoTriggerSchema)
                         .setMigrator("testSchema", ACTIVE_NOOP_MIGRATOR)
                         .setForceOverride(true)
                         .build()).get();
     }
 
     @Test
-    public void testSchemaMigration_NA_NB_C_D() throws Exception {
+    public void testNoForceOverride_BackwardsIncompatible_Trigger_MigrateIncompatibleType()
+            throws Exception {
         // create a backwards incompatible schema and update the version
-        AppSearchSchema NB_C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsIncompatibleTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .build();
 
         mDb.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(NB_C_Schema)
+                new SetSchemaRequest.Builder().addSchemas(backwardsIncompatibleTriggerSchema)
                         .setMigrator("testSchema", ACTIVE_NOOP_MIGRATOR)
                         .setVersion(2)     // upgrade version
                         .build()).get();
     }
 
     @Test
-    public void testSchemaMigration_NA_NB_C_ND() throws Exception {
+    public void testNoForceOverride_BackwardsIncompatible_Trigger_NoMigrateIncompatibleType()
+            throws Exception {
         // create a backwards incompatible schema and update the version
-        AppSearchSchema $B_C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsCompatibleTriggerSchema = new AppSearchSchema
+                .Builder("testSchema")
                 .build();
 
         ExecutionException exception = assertThrows(ExecutionException.class,
                 () -> mDb.setSchemaAsync(
-                        new SetSchemaRequest.Builder().addSchemas($B_C_Schema)
+                        new SetSchemaRequest.Builder().addSchemas(backwardsCompatibleTriggerSchema)
                                 .setMigrator("testSchema", INACTIVE_MIGRATOR)  //ND
                                 .setVersion(2)     // upgrade version
                                 .build()).get());
@@ -313,14 +334,17 @@
     }
 
     @Test
-    public void testSchemaMigration_NA_NB_NC_ND() throws Exception {
+    public void testNoForceOverride_BackwardsIncompatible_NoTrigger_NoMigrateIncompatibleType()
+            throws Exception {
         // create a backwards incompatible schema but don't update the version
-        AppSearchSchema $B_$C_Schema = new AppSearchSchema.Builder("testSchema")
+        AppSearchSchema backwardsIncompatibleNoTriggerNoMigrateIncompatibleTypeSchema =
+                new AppSearchSchema.Builder("testSchema")
                 .build();
 
         ExecutionException exception = assertThrows(ExecutionException.class,
                 () -> mDb.setSchemaAsync(
-                        new SetSchemaRequest.Builder().addSchemas($B_$C_Schema)
+                        new SetSchemaRequest.Builder().addSchemas(
+                                backwardsIncompatibleNoTriggerNoMigrateIncompatibleTypeSchema)
                                 .setMigrator("testSchema", INACTIVE_MIGRATOR)  //ND
                                 .build()).get());
         assertThat(exception).hasMessageThat().contains("Schema is incompatible.");
@@ -704,7 +728,7 @@
         assertThat(result.getSuccesses()).containsExactly("id1", null);
         assertThat(result.getFailures()).isEmpty();
 
-        Migrator migrator_sourceToNowhere = new Migrator() {
+        Migrator migratorSourceToNowhere = new Migrator() {
             @Override
             public boolean shouldMigrate(int currentVersion, int finalVersion) {
                 return true;
@@ -732,7 +756,7 @@
         ExecutionException exception = assertThrows(ExecutionException.class,
                 () -> mDb.setSchemaAsync(new SetSchemaRequest.Builder()
                         .addSchemas(new AppSearchSchema.Builder("emptySchema").build())
-                        .setMigrator("sourceSchema", migrator_sourceToNowhere)
+                        .setMigrator("sourceSchema", migratorSourceToNowhere)
                         .setVersion(2).build())   // upgrade version
                         .get());
         assertThat(exception).hasMessageThat().contains(
@@ -744,7 +768,7 @@
         exception = assertThrows(ExecutionException.class,
                 () -> mDb.setSchemaAsync(new SetSchemaRequest.Builder()
                         .addSchemas(new AppSearchSchema.Builder("emptySchema").build())
-                        .setMigrator("sourceSchema", migrator_sourceToNowhere)
+                        .setMigrator("sourceSchema", migratorSourceToNowhere)
                         .setForceOverride(true)
                         .setVersion(2).build())   // upgrade version
                         .get());
@@ -761,7 +785,7 @@
         mDb.setSchemaAsync(new SetSchemaRequest.Builder()
                 .addSchemas(destinationSchema).setForceOverride(true).build()).get();
 
-        Migrator migrator_nowhereToDestination = new Migrator() {
+        Migrator migratorNowhereToDestination = new Migrator() {
             @Override
             public boolean shouldMigrate(int currentVersion, int finalVersion) {
                 return true;
@@ -789,7 +813,7 @@
         SetSchemaResponse setSchemaResponse =
                 mDb.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(destinationSchema)
                         .addSchemas(new AppSearchSchema.Builder("emptySchema").build())
-                        .setMigrator("nonExistSchema", migrator_nowhereToDestination)
+                        .setMigrator("nonExistSchema", migratorNowhereToDestination)
                         .setVersion(2) //  upgrade version
                         .build()).get();
         assertThat(setSchemaResponse.getMigratedTypes()).isEmpty();
@@ -798,7 +822,7 @@
         setSchemaResponse =
                 mDb.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(destinationSchema)
                         .addSchemas(new AppSearchSchema.Builder("emptySchema").build())
-                        .setMigrator("nonExistSchema", migrator_nowhereToDestination)
+                        .setMigrator("nonExistSchema", migratorNowhereToDestination)
                         .setVersion(2) //  upgrade version
                         .setForceOverride(true).build()).get();
         assertThat(setSchemaResponse.getMigratedTypes()).isEmpty();
@@ -809,7 +833,7 @@
         // set empty schema
         mDb.setSchemaAsync(new SetSchemaRequest.Builder()
                 .setForceOverride(true).build()).get();
-        Migrator migrator_nowhereToNowhere = new Migrator() {
+        Migrator migratorNowhereToNowhere = new Migrator() {
             @Override
             public boolean shouldMigrate(int currentVersion, int finalVersion) {
                 return true;
@@ -837,7 +861,7 @@
         SetSchemaResponse setSchemaResponse =
                 mDb.setSchemaAsync(new SetSchemaRequest.Builder()
                         .addSchemas(new AppSearchSchema.Builder("emptySchema").build())
-                        .setMigrator("nonExistSchema", migrator_nowhereToNowhere)
+                        .setMigrator("nonExistSchema", migratorNowhereToNowhere)
                         .setVersion(2)  //  upgrade version
                         .build()).get();
         assertThat(setSchemaResponse.getMigratedTypes()).isEmpty();
@@ -846,7 +870,7 @@
         setSchemaResponse =
                 mDb.setSchemaAsync(new SetSchemaRequest.Builder()
                         .addSchemas(new AppSearchSchema.Builder("emptySchema").build())
-                        .setMigrator("nonExistSchema", migrator_nowhereToNowhere)
+                        .setMigrator("nonExistSchema", migratorNowhereToNowhere)
                         .setVersion(2) //  upgrade version
                         .setForceOverride(true).build()).get();
         assertThat(setSchemaResponse.getMigratedTypes()).isEmpty();
@@ -1208,7 +1232,7 @@
         @Override
         public GenericDocument onUpgrade(int currentVersion, int finalVersion,
                 @NonNull GenericDocument document) {
-            GenericDocument.Builder docBuilder =
+            GenericDocument.Builder<?> docBuilder =
                     new GenericDocument.Builder<>("namespace", "id", "TypeB")
                             .setCreationTimestampMillis(DOCUMENT_CREATION_TIME);
             if (currentVersion == 2) {
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
index 672c0f3..97698a7 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
@@ -41,6 +41,7 @@
 import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
 import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.Features;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.GetByDocumentIdRequest;
@@ -51,6 +52,7 @@
 import androidx.appsearch.app.PutDocumentsRequest;
 import androidx.appsearch.app.RemoveByDocumentIdRequest;
 import androidx.appsearch.app.ReportUsageRequest;
+import androidx.appsearch.app.SchemaVisibilityConfig;
 import androidx.appsearch.app.SearchResult;
 import androidx.appsearch.app.SearchResults;
 import androidx.appsearch.app.SearchSpec;
@@ -60,7 +62,13 @@
 import androidx.appsearch.app.StorageInfo;
 import androidx.appsearch.cts.app.customer.EmailDocument;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.CheckFlagsRule;
+import androidx.appsearch.flags.DeviceFlagsValueProvider;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.flags.RequiresFlagsEnabled;
 import androidx.appsearch.testutil.AppSearchEmail;
+import androidx.appsearch.usagereporting.ClickAction;
+import androidx.appsearch.usagereporting.SearchAction;
 import androidx.appsearch.util.DocumentIdUtil;
 import androidx.collection.ArrayMap;
 import androidx.test.core.app.ApplicationProvider;
@@ -73,6 +81,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -89,6 +98,14 @@
     static final String DB_NAME_1 = "";
     static final String DB_NAME_2 = "testDb2";
 
+    // Since we cannot call non-public API in the cts test, make a copy of these 2 action types, so
+    // we can create taken actions in GenericDocument form.
+    private static final int ACTION_TYPE_SEARCH = 1;
+    private static final int ACTION_TYPE_CLICK = 2;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
     private AppSearchSession mDb1;
@@ -170,6 +187,178 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTIONS)  // setDescription
+    public void testSetSchema_schemaDescription_notSupported() throws Exception {
+        assumeFalse(mDb1.getFeatures().isFeatureSupported(
+                Features.SCHEMA_SET_DESCRIPTION));
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email1")
+                .setDescription("Unsupported description")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        SetSchemaRequest request = new SetSchemaRequest.Builder()
+                .addSchemas(schema)
+                .build();
+
+        UnsupportedOperationException exception = assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.setSchemaAsync(request).get());
+        assertThat(exception).hasMessageThat().contains(Features.SCHEMA_SET_DESCRIPTION
+                + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTIONS)  // setDescription
+    public void testSetSchema_propertyDescription_notSupported() throws Exception {
+        assumeFalse(mDb1.getFeatures().isFeatureSupported(
+                Features.SCHEMA_SET_DESCRIPTION));
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email1")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setDescription("Unsupported description")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        SetSchemaRequest request = new SetSchemaRequest.Builder()
+                .addSchemas(schema)
+                .build();
+
+        UnsupportedOperationException exception = assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.setSchemaAsync(request).get());
+        assertThat(exception).hasMessageThat().contains(Features.SCHEMA_SET_DESCRIPTION
+                + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTIONS)  // setDescription
+    public void testSetSchema_updateSchemaDescription() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_SET_DESCRIPTION));
+
+        AppSearchSchema schema1 =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("A type of electronic message.")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setDescription("A summary of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("body")
+                                        .setDescription("All of the content of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema1).build())
+                .get();
+
+        Set<AppSearchSchema> actualSchemaTypes = mDb1.getSchemaAsync().get().getSchemas();
+        assertThat(actualSchemaTypes).containsExactly(schema1);
+
+        // Change the type description.
+        AppSearchSchema schema2 =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("Like mail but with an 'a'.")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setDescription("A summary of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("body")
+                                        .setDescription("All of the content of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema2).build())
+                .get();
+
+        GetSchemaResponse getSchemaResponse = mDb1.getSchemaAsync().get();
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(schema2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTIONS)  // setDescription
+    public void testSetSchema_updatePropertyDescription() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_SET_DESCRIPTION));
+
+        AppSearchSchema schema1 =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("A type of electronic message.")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setDescription("A summary of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("body")
+                                        .setDescription("All of the content of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema1).build())
+                .get();
+
+        Set<AppSearchSchema> actualSchemaTypes = mDb1.getSchemaAsync().get().getSchemas();
+        assertThat(actualSchemaTypes).containsExactly(schema1);
+
+        // Change the type description.
+        AppSearchSchema schema2 =
+                new AppSearchSchema.Builder("Email")
+                        .setDescription("A type of electronic message.")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setDescription("The most important part of the email.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("body")
+                                        .setDescription("All the other stuff.")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema2).build())
+                .get();
+
+        GetSchemaResponse getSchemaResponse = mDb1.getSchemaAsync().get();
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(schema2);
+    }
+
+    @Test
     public void testSetSchema_updateVersion() throws Exception {
         AppSearchSchema schema = new AppSearchSchema.Builder("Email")
                 .addProperty(new StringPropertyConfig.Builder("subject")
@@ -420,6 +609,46 @@
     }
 
     @Test
+    public void testSetSchemaWithInvalidCycle_circularReferencesSupported() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SET_SCHEMA_CIRCULAR_REFERENCES));
+
+        // Create schema with invalid cycle: Person -> Organization -> Person... where all
+        // DocumentPropertyConfigs have setShouldIndexNestedProperties(true).
+        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
+                .addProperty(new StringPropertyConfig.Builder("name")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setShouldIndexNestedProperties(true)
+                        .build())
+                .build();
+        AppSearchSchema organizationSchema = new AppSearchSchema.Builder("Organization")
+                .addProperty(new StringPropertyConfig.Builder("name")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build())
+                .addProperty(new DocumentPropertyConfig.Builder("funder", "Person")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setShouldIndexNestedProperties(true)
+                        .build())
+                .build();
+
+        SetSchemaRequest setSchemaRequest =
+                new SetSchemaRequest.Builder().addSchemas(personSchema, organizationSchema).build();
+        ExecutionException executionException =
+                assertThrows(ExecutionException.class,
+                        () -> mDb1.setSchemaAsync(setSchemaRequest).get());
+        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+        AppSearchException exception = (AppSearchException) executionException.getCause();
+        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+        assertThat(exception).hasMessageThat().containsMatch("Invalid cycle|Infinite loop");
+    }
+
+    @Test
     public void testSetSchemaWithValidCycle_circularReferencesNotSupported() {
         assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.SET_SCHEMA_CIRCULAR_REFERENCES));
 
@@ -467,8 +696,6 @@
     }
 // @exportToFramework:endStrip()
 
-// @exportToFramework:startStrip()
-
     /** Test indexing maximum properties into a schema. */
     @Test
     public void testSetSchema_maxProperties() throws Exception {
@@ -485,11 +712,89 @@
         Set<AppSearchSchema> actual1 = mDb1.getSchemaAsync().get().getSchemas();
         assertThat(actual1).containsExactly(maxSchema);
 
-        // TODO(b/300135897): Expand test to assert adding more than allowed properties is
-        //  fixed once fixed.
+        schemaBuilder.addProperty(new StringPropertyConfig.Builder("toomuch")
+                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                .build());
+        ExecutionException exception = assertThrows(ExecutionException.class, () ->
+                mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+                        .addSchemas(schemaBuilder.build()).setForceOverride(true).build()).get());
+        Throwable cause = exception.getCause();
+        assertThat(cause).isInstanceOf(AppSearchException.class);
+        assertThat(cause.getMessage()).isEqualTo("Too many properties to be indexed, max "
+                + "number of properties allowed: " + maxProperties);
     }
 
     @Test
+    public void testSetSchema_maxProperties_nestedSchemas() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SET_SCHEMA_CIRCULAR_REFERENCES));
+
+        int maxProperties = mDb1.getFeatures().getMaxIndexedProperties();
+        AppSearchSchema.Builder personSchemaBuilder = new AppSearchSchema.Builder("Person");
+        for (int i = 0; i < maxProperties / 3 + 1; i++) {
+            personSchemaBuilder.addProperty(new StringPropertyConfig.Builder("string" + i)
+                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .build());
+        }
+        personSchemaBuilder.addProperty(new DocumentPropertyConfig.Builder("worksFor",
+                "Organization")
+                .setShouldIndexNestedProperties(false)
+                .build());
+        personSchemaBuilder.addProperty(new DocumentPropertyConfig.Builder("address", "Address")
+                .setShouldIndexNestedProperties(true)
+                .build());
+
+        AppSearchSchema.Builder orgSchemaBuilder = new AppSearchSchema.Builder("Organization");
+        for (int i = 0; i < maxProperties / 3; i++) {
+            orgSchemaBuilder.addProperty(new StringPropertyConfig.Builder("string" + i)
+                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .build());
+        }
+        orgSchemaBuilder.addProperty(new DocumentPropertyConfig.Builder("funder", "Person")
+                .setShouldIndexNestedProperties(true)
+                .build());
+
+        AppSearchSchema.Builder addressSchemaBuilder = new AppSearchSchema.Builder("Address");
+        for (int i = 0; i < maxProperties / 3; i++) {
+            addressSchemaBuilder.addProperty(new StringPropertyConfig.Builder("string" + i)
+                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .build());
+        }
+
+        AppSearchSchema personSchema = personSchemaBuilder.build();
+        AppSearchSchema orgSchema = orgSchemaBuilder.build();
+        AppSearchSchema addressSchema = addressSchemaBuilder.build();
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(personSchema, orgSchema, addressSchema)
+                        .build()).get();
+        Set<AppSearchSchema> schemas = mDb1.getSchemaAsync().get().getSchemas();
+        assertThat(schemas).containsExactly(personSchema, orgSchema, addressSchema);
+
+        // Add one more property to bring the number of sections over the max limit
+        personSchemaBuilder.addProperty(new StringPropertyConfig.Builder("toomuch")
+                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                .build());
+        ExecutionException exception = assertThrows(ExecutionException.class,
+                () -> mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchemaBuilder.build(), orgSchema, addressSchema)
+                                .setForceOverride(true)
+                                .build()
+                ).get());
+        Throwable cause = exception.getCause();
+        assertThat(cause).isInstanceOf(AppSearchException.class);
+        assertThat(cause.getMessage()).contains("Too many properties to be indexed");
+    }
+
+// @exportToFramework:startStrip()
+
+    @Test
     public void testGetSchema() throws Exception {
         AppSearchSchema emailSchema1 = new AppSearchSchema.Builder("Email1")
                 .addProperty(new StringPropertyConfig.Builder("subject")
@@ -672,6 +977,58 @@
     }
 
     @Test
+    public void testGetSchema_visibilitySetting_oneSharedSchema() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.ADD_PERMISSIONS_AND_GET_VISIBILITY));
+
+        AppSearchSchema noteSchema = new AppSearchSchema.Builder("Note")
+                .addProperty(new StringPropertyConfig.Builder("subject").build()).build();
+        SetSchemaRequest.Builder requestBuilder = new SetSchemaRequest.Builder()
+                .addSchemas(AppSearchEmail.SCHEMA, noteSchema)
+                .setSchemaTypeDisplayedBySystem(noteSchema.getSchemaType(), false)
+                .setSchemaTypeVisibilityForPackage(
+                        noteSchema.getSchemaType(),
+                        true,
+                        new PackageIdentifier("com.some.package1", new byte[32]))
+                .addRequiredPermissionsForSchemaTypeVisibility(
+                        noteSchema.getSchemaType(),
+                        Collections.singleton(SetSchemaRequest.READ_SMS));
+        if (mDb1.getFeatures().isFeatureSupported(
+                Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE)) {
+            requestBuilder.setPubliclyVisibleSchema(
+                    noteSchema.getSchemaType(),
+                    new PackageIdentifier("com.some.package2", new byte[32]));
+        }
+        SetSchemaRequest request = requestBuilder.build();
+        mDb1.setSchemaAsync(request).get();
+
+        GetSchemaResponse getSchemaResponse = mDb1.getSchemaAsync().get();
+        Set<AppSearchSchema> actual = getSchemaResponse.getSchemas();
+        assertThat(actual).hasSize(2);
+        assertThat(actual).isEqualTo(request.getSchemas());
+
+        // Check visibility settings. Schemas without settings shouldn't appear in the result at
+        // all, even with empty maps as values.
+        assertThat(getSchemaResponse.getSchemaTypesNotDisplayedBySystem())
+                .containsExactly(noteSchema.getSchemaType());
+        assertThat(getSchemaResponse.getSchemaTypesVisibleToPackages())
+                .containsExactly(
+                        noteSchema.getSchemaType(),
+                        ImmutableSet.of(new PackageIdentifier("com.some.package1", new byte[32])));
+        assertThat(getSchemaResponse.getRequiredPermissionsForSchemaTypeVisibility())
+                .containsExactly(
+                        noteSchema.getSchemaType(),
+                        ImmutableSet.of(ImmutableSet.of(SetSchemaRequest.READ_SMS)));
+        if (mDb1.getFeatures().isFeatureSupported(
+                Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE)) {
+            assertThat(getSchemaResponse.getPubliclyVisibleSchemas())
+                    .containsExactly(
+                            noteSchema.getSchemaType(),
+                            new PackageIdentifier("com.some.package2", new byte[32]));
+        }
+    }
+
+    @Test
     public void testGetSchema_visibilitySetting_notSupported() throws Exception {
         assumeFalse(mDb1.getFeatures().isFeatureSupported(
                 Features.ADD_PERMISSIONS_AND_GET_VISIBILITY));
@@ -740,6 +1097,101 @@
     }
 
     @Test
+    public void testSetSchema_publiclyVisible() throws Exception {
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE));
+
+        PackageIdentifier pkg = new PackageIdentifier(mContext.getPackageName(), new byte[32]);
+        SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA)
+                .setPubliclyVisibleSchema("builtin:Email", pkg).build();
+
+        mDb1.setSchemaAsync(request).get();
+        GetSchemaResponse getSchemaResponse = mDb1.getSchemaAsync().get();
+
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(AppSearchEmail.SCHEMA);
+        assertThat(getSchemaResponse.getPubliclyVisibleSchemas())
+                .isEqualTo(ImmutableMap.of("builtin:Email", pkg));
+
+        AppSearchEmail email = new AppSearchEmail.Builder("namespace", "id1")
+                .setSubject("testPut example").build();
+
+        // mDb1 and mDb2 are in the same package, so we can't REALLY test out public acl. But we
+        // can make sure they their own documents under the Public ACL.
+        AppSearchBatchResult<String, Void> putResult =
+                checkIsBatchResultSuccess(mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(email).build()));
+        assertThat(putResult.getSuccesses()).containsExactly("id1", null);
+        assertThat(putResult.getFailures()).isEmpty();
+
+        GetByDocumentIdRequest getByDocumentIdRequest =
+                new GetByDocumentIdRequest.Builder("namespace")
+                        .addIds("id1")
+                        .build();
+        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
+        assertThat(outDocuments).hasSize(1);
+        assertThat(outDocuments).containsExactly(email);
+    }
+
+    @Test
+    public void testSetSchema_publiclyVisible_unsupported() {
+        assumeFalse(mDb1.getFeatures()
+                .isFeatureSupported(Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE));
+
+        SetSchemaRequest request = new SetSchemaRequest.Builder()
+                .addSchemas(new AppSearchSchema.Builder("Email").build())
+                .setPubliclyVisibleSchema("Email",
+                        new PackageIdentifier(mContext.getPackageName(), new byte[32])).build();
+        Exception e = assertThrows(UnsupportedOperationException.class,
+                () -> mDb1.setSchemaAsync(request).get());
+        assertThat(e.getMessage()).isEqualTo("Publicly visible schema are not supported on this "
+                + "AppSearch implementation.");
+    }
+
+    @Test
+    public void testSetSchema_visibleToConfig() throws Exception {
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG));
+        byte[] cert1 = new byte[32];
+        byte[] cert2 = new byte[32];
+        Arrays.fill(cert1, (byte) 1);
+        Arrays.fill(cert2, (byte) 2);
+        PackageIdentifier pkg1 = new PackageIdentifier("package1", cert1);
+        PackageIdentifier pkg2 = new PackageIdentifier("package2", cert2);
+        SchemaVisibilityConfig config1 = new SchemaVisibilityConfig.Builder()
+                .setPubliclyVisibleTargetPackage(pkg1)
+                .addRequiredPermissions(ImmutableSet.of(1, 2)).build();
+        SchemaVisibilityConfig config2 = new SchemaVisibilityConfig.Builder()
+                .setPubliclyVisibleTargetPackage(pkg2)
+                .addRequiredPermissions(ImmutableSet.of(3, 4)).build();
+        SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA)
+                .addSchemaTypeVisibleToConfig("builtin:Email", config1)
+                .addSchemaTypeVisibleToConfig("builtin:Email", config2)
+                .build();
+        mDb1.setSchemaAsync(request).get();
+
+        GetSchemaResponse getSchemaResponse = mDb1.getSchemaAsync().get();
+        assertThat(getSchemaResponse.getSchemas()).containsExactly(AppSearchEmail.SCHEMA);
+        assertThat(getSchemaResponse.getSchemaTypesVisibleToConfigs())
+                .isEqualTo(ImmutableMap.of("builtin:Email", ImmutableSet.of(config1, config2)));
+    }
+
+    @Test
+    public void testSetSchema_visibleToConfig_unsupported() {
+        assumeFalse(mDb1.getFeatures()
+                .isFeatureSupported(Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG));
+
+        SchemaVisibilityConfig config = new SchemaVisibilityConfig.Builder()
+                .addRequiredPermissions(ImmutableSet.of(1, 2)).build();
+        SetSchemaRequest request = new SetSchemaRequest.Builder()
+                .addSchemas(new AppSearchSchema.Builder("Email").build())
+                .addSchemaTypeVisibleToConfig("Email", config).build();
+        Exception e = assertThrows(UnsupportedOperationException.class,
+                () -> mDb1.setSchemaAsync(request).get());
+        assertThat(e.getMessage()).isEqualTo("Schema visible to config are not supported on"
+                + " this AppSearch implementation.");
+    }
+
+    @Test
     public void testGetSchema_longPropertyIndexingType() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.NUMERIC_SEARCH));
         AppSearchSchema inSchema = new AppSearchSchema.Builder("Test")
@@ -1112,9 +1564,106 @@
         assertThat(result.getSuccesses()).containsExactly("id1", null);
         assertThat(result.getFailures()).isEmpty();
     }
+
+    @Test
+    public void testPutDocuments_takenActions() throws Exception {
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+        // Schema registration
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addDocumentClasses(SearchAction.class, ClickAction.class)
+                                .build())
+                .get();
+
+        // Put a SearchAction and ClickAction document
+        SearchAction searchAction =
+                new SearchAction.Builder("namespace", "search", /* actionTimestampMillis= */1000)
+                        .setDocumentTtlMillis(0)
+                        .setQuery("query")
+                        .setFetchedResultCount(10)
+                        .build();
+        ClickAction clickAction =
+                new ClickAction.Builder("namespace", "click", /* actionTimestampMillis= */2000)
+                        .setDocumentTtlMillis(0)
+                        .setQuery("query")
+                        .setReferencedQualifiedId("pkg$db/ns#refId")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(3)
+                        .setTimeStayOnResultMillis(1024)
+                        .build();
+
+        AppSearchBatchResult<String, Void> result = checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addTakenActions(searchAction, clickAction)
+                        .build()));
+        assertThat(result.getSuccesses()).containsEntry("search", null);
+        assertThat(result.getSuccesses()).containsEntry("click", null);
+        assertThat(result.getFailures()).isEmpty();
+    }
 // @exportToFramework:endStrip()
 
     @Test
+    public void testPutDocuments_takenActionGenericDocuments() throws Exception {
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+        // Schema registration
+        AppSearchSchema searchActionSchema = new AppSearchSchema.Builder("builtin:SearchAction")
+                .addProperty(new LongPropertyConfig.Builder("actionType")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("query")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+        AppSearchSchema clickActionSchema = new AppSearchSchema.Builder("builtin:ClickAction")
+                .addProperty(new LongPropertyConfig.Builder("actionType")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("query")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("referencedQualifiedId")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                        .build()
+                ).build();
+
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder().addSchemas(searchActionSchema, clickActionSchema)
+                        .build()).get();
+
+        // Put search action and click action generic documents.
+        GenericDocument searchAction =
+                new GenericDocument.Builder<>("namespace", "search", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyLong("actionType", ACTION_TYPE_SEARCH)
+                        .setPropertyString("query", "body")
+                        .build();
+        GenericDocument clickAction =
+                new GenericDocument.Builder<>("namespace", "click", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .setPropertyLong("actionType", ACTION_TYPE_CLICK)
+                        .setPropertyString("query", "body")
+                        .setPropertyString("referencedQualifiedId", "pkg$db/ns#refId")
+                        .build();
+
+        AppSearchBatchResult<String, Void> result = checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addTakenActionGenericDocuments(searchAction, clickAction)
+                        .build()));
+        assertThat(result.getSuccesses()).containsEntry("search", null);
+        assertThat(result.getSuccesses()).containsEntry("click", null);
+        assertThat(result.getFailures()).isEmpty();
+    }
+
+    @Test
     public void testUpdateSchema() throws Exception {
         // Schema registration
         AppSearchSchema oldEmailSchema = new AppSearchSchema.Builder(AppSearchEmail.SCHEMA_TYPE)
@@ -2112,6 +2661,247 @@
         assertThat(sr.get(0).getRankingSignal()).isEqualTo(6.0);
     }
 
+// @exportToFramework:startStrip()
+    @Test
+    public void testQueryRankByClickActions_useTakenAction() throws Exception {
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+        // Schema registration
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .addDocumentClasses(SearchAction.class, ClickAction.class)
+                                .build())
+                .get();
+
+        // Index several email documents
+        AppSearchEmail inEmail1 =
+                new AppSearchEmail.Builder("namespace", "email1")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .setScore(1)
+                        .build();
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace", "email2")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .setScore(1)
+                        .build();
+
+        String qualifiedId1 = DocumentIdUtil.createQualifiedId(
+                mContext.getPackageName(), DB_NAME_1, inEmail1);
+        String qualifiedId2 = DocumentIdUtil.createQualifiedId(
+                mContext.getPackageName(), DB_NAME_1, inEmail2);
+
+        SearchAction searchAction =
+                new SearchAction.Builder("namespace", "search1", /* actionTimestampMillis= */1000)
+                        .setDocumentTtlMillis(0)
+                        .setQuery("body")
+                        .setFetchedResultCount(20)
+                        .build();
+        ClickAction clickAction1 =
+                new ClickAction.Builder("namespace", "click1", /* actionTimestampMillis= */2000)
+                        .setDocumentTtlMillis(0)
+                        .setQuery("body")
+                        .setReferencedQualifiedId(qualifiedId1)
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(1)
+                        .setTimeStayOnResultMillis(512)
+                        .build();
+        ClickAction clickAction2 =
+                new ClickAction.Builder("namespace", "click2", /* actionTimestampMillis= */3000)
+                        .setDocumentTtlMillis(0)
+                        .setQuery("body")
+                        .setReferencedQualifiedId(qualifiedId2)
+                        .setResultRankInBlock(2)
+                        .setResultRankGlobal(2)
+                        .setTimeStayOnResultMillis(128)
+                        .build();
+        ClickAction clickAction3 =
+                new ClickAction.Builder("namespace", "click3", /* actionTimestampMillis= */4000)
+                        .setDocumentTtlMillis(0)
+                        .setQuery("body")
+                        .setReferencedQualifiedId(qualifiedId1)
+                        .setResultRankInBlock(2)
+                        .setResultRankGlobal(2)
+                        .setTimeStayOnResultMillis(256)
+                        .build();
+
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(inEmail1, inEmail2)
+                        .addTakenActions(searchAction, clickAction1, clickAction2, clickAction3)
+                        .build()));
+
+        SearchSpec nestedSearchSpec =
+                new SearchSpec.Builder()
+                        .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
+                        .setOrder(SearchSpec.ORDER_DESCENDING)
+                        .addFilterDocumentClasses(ClickAction.class)
+                        .build();
+
+        // Note: SearchSpec.Builder#setMaxJoinedResultCount only limits the number of child
+        // documents returned. It does not affect the number of child documents that are scored.
+        JoinSpec js = new JoinSpec.Builder("referencedQualifiedId")
+                .setNestedSearch("query:body", nestedSearchSpec)
+                .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
+                .setMaxJoinedResultCount(0)
+                .build();
+
+        // Search "body" for AppSearchEmail documents, ranking by ClickAction signals with
+        // query = "body".
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
+                .setOrder(SearchSpec.ORDER_DESCENDING)
+                .setJoinSpec(js)
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterSchemas(AppSearchEmail.SCHEMA_TYPE)
+                .build());
+
+        List<SearchResult> sr = searchResults.getNextPageAsync().get();
+
+        assertThat(sr).hasSize(2);
+        assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("email1");
+        assertThat(sr.get(0).getRankingSignal()).isEqualTo(2.0);
+        assertThat(sr.get(1).getGenericDocument().getId()).isEqualTo("email2");
+        assertThat(sr.get(1).getRankingSignal()).isEqualTo(1.0);
+    }
+// @exportToFramework:endStrip()
+
+    @Test
+    public void testQueryRankByTakenActions_useTakenActionGenericDocument() throws Exception {
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+        AppSearchSchema searchActionSchema = new AppSearchSchema.Builder("builtin:SearchAction")
+                .addProperty(new LongPropertyConfig.Builder("actionType")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("query")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+        AppSearchSchema clickActionSchema = new AppSearchSchema.Builder("builtin:ClickAction")
+                .addProperty(new LongPropertyConfig.Builder("actionType")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .build())
+                .addProperty(new StringPropertyConfig.Builder("query")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("referencedQualifiedId")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                        .build()
+                ).build();
+
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA, searchActionSchema, clickActionSchema)
+                        .build())
+                .get();
+
+        // Index several email documents
+        AppSearchEmail inEmail1 =
+                new AppSearchEmail.Builder("namespace", "email1")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .setScore(1)
+                        .build();
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace", "email2")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .setScore(1)
+                        .build();
+
+        String qualifiedId1 = DocumentIdUtil.createQualifiedId(
+                mContext.getPackageName(), DB_NAME_1, inEmail1);
+        String qualifiedId2 = DocumentIdUtil.createQualifiedId(
+                mContext.getPackageName(), DB_NAME_1, inEmail2);
+
+        GenericDocument searchAction =
+                new GenericDocument.Builder<>("namespace", "search1", "builtin:SearchAction")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyLong("actionType", ACTION_TYPE_SEARCH)
+                        .setPropertyString("query", "body")
+                        .build();
+        GenericDocument clickAction1 =
+                new GenericDocument.Builder<>("namespace", "click1", "builtin:ClickAction")
+                        .setCreationTimestampMillis(2000)
+                        .setPropertyLong("actionType", ACTION_TYPE_CLICK)
+                        .setPropertyString("query", "body")
+                        .setPropertyString("referencedQualifiedId", qualifiedId1)
+                        .build();
+        GenericDocument clickAction2 =
+                new GenericDocument.Builder<>("namespace", "click2", "builtin:ClickAction")
+                        .setCreationTimestampMillis(3000)
+                        .setPropertyLong("actionType", ACTION_TYPE_CLICK)
+                        .setPropertyString("query", "body")
+                        .setPropertyString("referencedQualifiedId", qualifiedId2)
+                        .build();
+        GenericDocument clickAction3 =
+                new GenericDocument.Builder<>("namespace", "click3", "builtin:ClickAction")
+                        .setCreationTimestampMillis(4000)
+                        .setPropertyLong("actionType", ACTION_TYPE_CLICK)
+                        .setPropertyString("query", "body")
+                        .setPropertyString("referencedQualifiedId", qualifiedId1)
+                        .build();
+
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(inEmail1, inEmail2)
+                        .addTakenActionGenericDocuments(
+                                searchAction, clickAction1, clickAction2, clickAction3)
+                        .build()));
+
+        SearchSpec nestedSearchSpec =
+                new SearchSpec.Builder()
+                        .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
+                        .setOrder(SearchSpec.ORDER_DESCENDING)
+                        .addFilterSchemas("builtin:ClickAction")
+                        .build();
+
+        // Note: SearchSpec.Builder#setMaxJoinedResultCount only limits the number of child
+        // documents returned. It does not affect the number of child documents that are scored.
+        JoinSpec js = new JoinSpec.Builder("referencedQualifiedId")
+                .setNestedSearch("query:body", nestedSearchSpec)
+                .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
+                .setMaxJoinedResultCount(0)
+                .build();
+
+        // Search "body" for AppSearchEmail documents, ranking by ClickAction signals with
+        // query = "body".
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
+                .setOrder(SearchSpec.ORDER_DESCENDING)
+                .setJoinSpec(js)
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterSchemas(AppSearchEmail.SCHEMA_TYPE)
+                .build());
+
+        List<SearchResult> sr = searchResults.getNextPageAsync().get();
+
+        assertThat(sr).hasSize(2);
+        assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("email1");
+        assertThat(sr.get(0).getRankingSignal()).isEqualTo(2.0);
+        assertThat(sr.get(1).getGenericDocument().getId()).isEqualTo("email2");
+        assertThat(sr.get(1).getRankingSignal()).isEqualTo(1.0);
+    }
+
     @Test
     public void testQuery_invalidAdvancedRanking() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(
@@ -2267,7 +3057,7 @@
         assertThat(documents).hasSize(1);
         assertThat(documents).containsExactly(inDoc);
 
-        // Query only for non-exist type
+        // Query only for non-existent type
         searchResults = mDb1.search("body", new SearchSpec.Builder()
                 .addFilterSchemas("nonExistType")
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
@@ -2353,7 +3143,7 @@
         assertThat(documents).hasSize(1);
         assertThat(documents).containsExactly(expectedEmail);
 
-        // Query only for non-exist namespace
+        // Query only for non-existent namespace
         searchResults = mDb1.search("body",
                 new SearchSpec.Builder()
                         .addFilterNamespaces("nonExistNamespace")
@@ -2699,7 +3489,7 @@
         SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                 .addProjection(
-                        SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, ImmutableList.of("body", "to"))
+                        SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("body", "to"))
                 .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
 
@@ -2760,7 +3550,7 @@
         // Query with type property paths {"*", []}
         SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addProjection(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, Collections.emptyList())
+                .addProjection(SearchSpec.SCHEMA_TYPE_WILDCARD, Collections.emptyList())
                 .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
 
@@ -2822,7 +3612,7 @@
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                 .addProjection("NonExistentType", Collections.emptyList())
                 .addProjection(
-                        SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD, ImmutableList.of("body", "to"))
+                        SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("body", "to"))
                 .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
 
@@ -2842,6 +3632,23 @@
     }
 
     @Test
+    public void testSearchSpec_setSourceTag_notSupported() {
+        assumeFalse(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG));
+        // UnsupportedOperationException will be thrown with these queries so no need to
+        // define a schema and index document.
+        SearchSpec.Builder builder = new SearchSpec.Builder();
+        SearchSpec searchSpec = builder.setSearchSourceLogTag("tag").build();
+
+        UnsupportedOperationException exception = assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("\"Hello, world!\"", searchSpec));
+        assertThat(exception).hasMessageThat().contains(
+                Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG
+                        + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
     public void testQuery_twoInstances() throws Exception {
         // Schema registration
         mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
@@ -2889,6 +3696,332 @@
     }
 
     @Test
+    public void testQuery_typePropertyFilters() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA)
+                        .build()).get();
+
+        // Index two documents
+        AppSearchEmail email1 =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        AppSearchEmail email2 =
+                new AppSearchEmail.Builder("namespace", "id2")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example subject with some body")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(email1, email2).build()));
+
+        // Query with type property filters {"Email", ["subject", "to"]}
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("subject", "to"))
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        // Only email2 should be returned because email1 doesn't have the term "body" in subject
+        // or to fields
+        assertThat(documents).containsExactly(email2);
+    }
+
+    @Test
+    public void testQuery_typePropertyFiltersWithDifferentSchemaTypes() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA)
+                        .addSchemas(new AppSearchSchema.Builder("Note")
+                                .addProperty(new StringPropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .addProperty(new StringPropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .build())
+                        .build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("namespace", "id2", "Note")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(email, note).build()));
+
+        // Query with type property paths {"Email": ["subject", "to"], "Note": ["body"]}. Note
+        // schema has body in its property filter but Email schema doesn't.
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("subject", "to"))
+                .addFilterProperties("Note", ImmutableList.of("body"))
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        // Only the note document should be returned because the email property filter doesn't
+        // allow searching in the body.
+        assertThat(documents).containsExactly(note);
+    }
+
+    @Test
+    public void testQuery_typePropertyFiltersWithWildcard() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA)
+                        .addSchemas(new AppSearchSchema.Builder("Note")
+                                .addProperty(new StringPropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .addProperty(new StringPropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .build())
+                        .build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example subject with some body")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("namespace", "id2", "Note")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(email, note).build()));
+
+        // Query with type property paths {"*": ["subject", "title"]}
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties(SearchSpec.SCHEMA_TYPE_WILDCARD,
+                        ImmutableList.of("subject", "title"))
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        // The wildcard property filter will apply to both the Email and Note schema. The email
+        // document should be returned since it has the term "body" in its subject property. The
+        // note document should not be returned since it doesn't have the term "body" in the title
+        // property (subject property is not applicable for Note schema)
+        assertThat(documents).containsExactly(email);
+    }
+
+    @Test
+    public void testQuery_typePropertyFiltersWithWildcardAndExplicitSchema() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA)
+                        .addSchemas(new AppSearchSchema.Builder("Note")
+                                .addProperty(new StringPropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .addProperty(new StringPropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .build())
+                        .build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example subject with some body")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("namespace", "id2", "Note")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(email, note).build()));
+
+        // Query with type property paths {"*": ["subject", "title"], "Note": ["body"]}
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties(SearchSpec.SCHEMA_TYPE_WILDCARD,
+                        ImmutableList.of("subject", "title"))
+                .addFilterProperties("Note", ImmutableList.of("body"))
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        // The wildcard property filter will only apply to the Email schema since Note schema has
+        // its own explicit property filter specified. The email document should be returned since
+        // it has the term "body" in its subject property. The note document should also be returned
+        // since it has the term "body" in the body property.
+        assertThat(documents).containsExactly(email, note);
+    }
+
+    @Test
+    public void testQuery_typePropertyFiltersNonExistentType() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA)
+                        .addSchemas(new AppSearchSchema.Builder("Note")
+                                .addProperty(new StringPropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .addProperty(new StringPropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .build())
+                        .build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example subject with some body")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("namespace", "id2", "Note")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(email, note).build()));
+
+        // Query with type property paths {"NonExistentType": ["to", "title"]}
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties("NonExistentType", ImmutableList.of("to", "title"))
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        // The supplied property filters don't apply to either schema types. Both the documents
+        // should be returned since the term "body" is present in at least one of their properties.
+        assertThat(documents).containsExactly(email, note);
+    }
+
+    @Test
+    public void testQuery_typePropertyFiltersEmpty() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA)
+                        .addSchemas(new AppSearchSchema.Builder("Note")
+                                .addProperty(new StringPropertyConfig.Builder("title")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .addProperty(new StringPropertyConfig.Builder("body")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                                .build())
+                        .build()).get();
+
+        // Index two documents
+        AppSearchEmail email =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setCreationTimestampMillis(1000)
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        GenericDocument note =
+                new GenericDocument.Builder<>("namespace", "id2", "Note")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("title", "Note title")
+                        .setPropertyString("body", "Note body").build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder()
+                        .addGenericDocuments(email, note).build()));
+
+        // Query with type property paths {"email": []}
+        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, Collections.emptyList())
+                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        // The email document should not be returned since the property filter doesn't allow
+        // searching any property.
+        assertThat(documents).containsExactly(note);
+    }
+
+    @Test
     public void testSnippet() throws Exception {
         // Schema registration
         AppSearchSchema genericSchema = new AppSearchSchema.Builder("Generic")
@@ -2929,10 +4062,10 @@
         assertThat(matchInfo.getFullText()).isEqualTo("A commonly used fake word is foo. "
                 + "Another nonsense word that’s used a lot is bar");
         assertThat(matchInfo.getExactMatchRange()).isEqualTo(
-                new SearchResult.MatchRange(/*lower=*/29,  /*upper=*/32));
+                new SearchResult.MatchRange(/*start=*/29,  /*end=*/32));
         assertThat(matchInfo.getExactMatch()).isEqualTo("foo");
         assertThat(matchInfo.getSnippetRange()).isEqualTo(
-                new SearchResult.MatchRange(/*lower=*/26,  /*upper=*/33));
+                new SearchResult.MatchRange(/*start=*/26,  /*end=*/33));
         assertThat(matchInfo.getSnippet()).isEqualTo("is foo.");
 
         if (!mDb1.getFeatures().isFeatureSupported(
@@ -2941,7 +4074,7 @@
             assertThrows(UnsupportedOperationException.class, matchInfo::getSubmatch);
         } else {
             assertThat(matchInfo.getSubmatchRange()).isEqualTo(
-                    new SearchResult.MatchRange(/*lower=*/29,  /*upper=*/31));
+                    new SearchResult.MatchRange(/*start=*/29,  /*end=*/31));
             assertThat(matchInfo.getSubmatch()).isEqualTo("fo");
         }
     }
@@ -3068,7 +4201,7 @@
         SearchResult.MatchInfo matchInfo = matchInfos.get(0);
         assertThat(matchInfo.getFullText()).isEqualTo(japanese);
         assertThat(matchInfo.getExactMatchRange()).isEqualTo(
-                new SearchResult.MatchRange(/*lower=*/44,  /*upper=*/45));
+                new SearchResult.MatchRange(/*start=*/44,  /*end=*/45));
         assertThat(matchInfo.getExactMatch()).isEqualTo("は");
 
         if (!mDb1.getFeatures().isFeatureSupported(
@@ -3077,7 +4210,7 @@
             assertThrows(UnsupportedOperationException.class, matchInfo::getSubmatch);
         } else {
             assertThat(matchInfo.getSubmatchRange()).isEqualTo(
-                    new SearchResult.MatchRange(/*lower=*/44,  /*upper=*/45));
+                    new SearchResult.MatchRange(/*start=*/44,  /*end=*/45));
             assertThat(matchInfo.getSubmatch()).isEqualTo("は");
         }
     }
@@ -3992,7 +5125,7 @@
         // returned.
         SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*limit=*/ 1)
                 .build());
         List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
         assertThat(documents).containsExactly(inEmail4);
@@ -4001,7 +5134,7 @@
         // be returned ('email4' and 'email2').
         searchResults = mDb1.search("body", new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 1)
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*limit=*/ 1)
                 .build());
         documents = convertSearchResultsToDocuments(searchResults);
         assertThat(documents).containsExactly(inEmail4, inEmail2);
@@ -4011,13 +5144,442 @@
         searchResults = mDb1.search("body", new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                 .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                        | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+                        | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*limit=*/ 1)
                 .build());
         documents = convertSearchResultsToDocuments(searchResults);
         assertThat(documents).containsExactly(inEmail4, inEmail2);
     }
 
     @Test
+    public void testQuery_ResultGroupingLimits_SchemaGroupingSupported() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures()
+                .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
+        // Schema registration
+        AppSearchSchema genericSchema =
+                new AppSearchSchema.Builder("Generic")
+                .addProperty(
+                    new StringPropertyConfig.Builder("foo")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(
+                            StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                    .addSchemas(AppSearchEmail.SCHEMA)
+                    .addSchemas(genericSchema)
+                    .build())
+                .get();
+
+        // Index four documents.
+        AppSearchEmail inEmail1 =
+                new AppSearchEmail.Builder("namespace1", "id1")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace1", "id2")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
+        AppSearchEmail inEmail3 =
+                new AppSearchEmail.Builder("namespace2", "id3")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
+        AppSearchEmail inEmail4 =
+                new AppSearchEmail.Builder("namespace2", "id4")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
+        AppSearchEmail inEmail5 =
+                new AppSearchEmail.Builder("namespace2", "id5")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail5).build()));
+        GenericDocument inDoc1 =
+                new GenericDocument.Builder<>("namespace3", "id6", "Generic")
+                .setPropertyString("foo", "body")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inDoc1).build()));
+        GenericDocument inDoc2 =
+                new GenericDocument.Builder<>("namespace3", "id7", "Generic")
+                .setPropertyString("foo", "body")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inDoc2).build()));
+        GenericDocument inDoc3 =
+                new GenericDocument.Builder<>("namespace4", "id8", "Generic")
+                .setPropertyString("foo", "body")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inDoc3).build()));
+
+        // Query with per package result grouping. Only the last document 'doc3' should be
+        // returned.
+        SearchResults searchResults =
+                mDb1.search(
+                "body",
+                    new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_PACKAGE, /* limit= */ 1)
+                    .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3);
+
+        // Query with per namespace result grouping. Only the last document in each namespace should
+        // be returned ('doc3', 'doc2', 'email5' and 'email2').
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE,
+                        /* limit= */ 1)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per namespace result grouping. Two of the last documents in each namespace
+        // should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4', 'email2', 'email1')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE,
+                        /* limit= */ 2)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+
+        // Query with per schema result grouping. Only the last document of each schema type should
+        // be returned ('doc3', 'email5')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* limit= */ 1)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inEmail5);
+
+        // Query with per schema result grouping. Only the last two documents of each schema type
+        // should be returned ('doc3', 'doc2', 'email5', 'email4')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* limit= */ 2)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
+
+        // Query with per package and per namespace result grouping. Only the last document in each
+        // namespace should be returned ('doc3', 'doc2', 'email5' and 'email2').
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                            | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                        /* limit= */ 1)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per package and per namespace result grouping. Only the last two documents
+        // in each namespace should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4',
+        // 'email2', 'email1')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                            | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                        /* limit= */ 2)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+
+        // Query with per package and per schema type result grouping. Only the last document in
+        // each schema type should be returned. ('doc3', 'email5')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                            | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                        /* limit= */ 1)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inEmail5);
+
+        // Query with per package and per schema type result grouping. Only the last two document in
+        // each schema type should be returned. ('doc3', 'doc2', 'email5', 'email4')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                            | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                        /* limit= */ 2)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
+
+        // Query with per namespace and per schema type result grouping. Only the last document in
+        // each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2').
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                            | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                        /* limit= */ 1)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per namespace and per schema type result grouping. Only the last two documents
+        // in each namespace should be returned. ('doc3', 'doc2', 'doc1', 'email5', 'email4',
+        // 'email2', 'email1')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                            | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                        /* limit= */ 2)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+
+        // Query with per namespace, per package and per schema type result grouping. Only the last
+        // document in each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                            | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                            | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                        /* limit= */ 1)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per namespace, per package and per schema type result grouping. Only the last
+        // two documents in each namespace should be returned.('doc3', 'doc2', 'doc1', 'email5',
+        // 'email4', 'email2', 'email1')
+        searchResults =
+            mDb1.search(
+                "body",
+                new SearchSpec.Builder()
+                    .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                    .setResultGrouping(
+                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                            | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                            | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                        /* limit= */ 2)
+                    .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+    }
+
+    @Test
+    public void testQuery_ResultGroupingLimits_SchemaGroupingNotSupported() throws Exception {
+        assumeFalse(
+                mDb1.getFeatures()
+                .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
+                .get();
+
+        // Index four documents.
+        AppSearchEmail inEmail1 =
+                new AppSearchEmail.Builder("namespace1", "id1")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace1", "id2")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
+        AppSearchEmail inEmail3 =
+                new AppSearchEmail.Builder("namespace2", "id3")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
+        AppSearchEmail inEmail4 =
+                new AppSearchEmail.Builder("namespace2", "id4")
+                .setFrom("[email protected]")
+                .setTo("[email protected]", "[email protected]")
+                .setSubject("testPut example")
+                .setBody("This is the body of the testPut email")
+                .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                    new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec1 =
+                new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .setResultGrouping(
+                    SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* limit= */ 1)
+                .build();
+        UnsupportedOperationException exception =
+                assertThrows(
+                UnsupportedOperationException.class,
+                    () -> mDb1.search("body", searchSpec1));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                    Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                    + " is not available on this"
+                    + " AppSearch implementation.");
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec2 =
+                new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .setResultGrouping(
+                    SearchSpec.GROUPING_TYPE_PER_PACKAGE
+                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                    /* limit= */ 1)
+                .build();
+        exception =
+            assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("body", searchSpec2));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                    Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                    + " is not available on this"
+                    + " AppSearch implementation.");
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec3 =
+                new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .setResultGrouping(
+                    SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                    /* limit= */ 1)
+                .build();
+        exception =
+            assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("body", searchSpec3));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                    Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                    + " is not available on this"
+                    + " AppSearch implementation.");
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec4 =
+                new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .setResultGrouping(
+                    SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                        | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                    /* limit= */ 1)
+                .build();
+        exception =
+            assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("body", searchSpec4));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                    Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                    + " is not available on this"
+                    + " AppSearch implementation.");
+    }
+
+    @Test
     public void testIndexNestedDocuments() throws Exception {
         // Schema registration
         mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
@@ -4308,8 +5870,6 @@
                 .build();
         mDb1.putAsync(new PutDocumentsRequest.Builder().addGenericDocuments(email).build()).get();
 
-        // ListFilterQueryLanguage is enabled so that EXPERIMENTAL_ICING_ADVANCED_QUERY gets enabled
-        // in IcingLib.
         // Disable VERBATIM_SEARCH in the SearchSpec.
         SearchResults searchResults = mDb1.search("\"Hello, world!\"",
                 new SearchSpec.Builder()
@@ -4507,6 +6067,138 @@
     }
 
     @Test
+    public void testQuery_listFilterQueryHasPropertyFunction_notSupported() throws Exception {
+        assumeFalse(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_HAS_PROPERTY_FUNCTION));
+
+        // UnsupportedOperationException will be thrown with these queries so no need to
+        // define a schema and index document.
+        SearchSpec.Builder builder = new SearchSpec.Builder();
+        SearchSpec searchSpec = builder.setListFilterHasPropertyFunctionEnabled(true).build();
+
+        UnsupportedOperationException exception = assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("\"Hello, world!\"", searchSpec));
+        assertThat(exception).hasMessageThat().contains(Features.LIST_FILTER_HAS_PROPERTY_FUNCTION
+                + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
+    public void testQuery_hasPropertyFunction() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_HAS_PROPERTY_FUNCTION));
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema")
+                .addProperty(new StringPropertyConfig.Builder("prop1")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("prop2")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+                .setForceOverride(true).addSchemas(schema).build()).get();
+
+        GenericDocument doc1 = new GenericDocument.Builder<>(
+                "namespace", "id1", "Schema")
+                .setPropertyString("prop1", "Hello, world!")
+                .build();
+        GenericDocument doc2 = new GenericDocument.Builder<>(
+                "namespace", "id2", "Schema")
+                .setPropertyString("prop2", "Hello, world!")
+                .build();
+        GenericDocument doc3 = new GenericDocument.Builder<>(
+                "namespace", "id3", "Schema")
+                .setPropertyString("prop1", "Hello, world!")
+                .setPropertyString("prop2", "Hello, world!")
+                .build();
+        mDb1.putAsync(new PutDocumentsRequest.Builder()
+                .addGenericDocuments(doc1, doc2, doc3).build()).get();
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterHasPropertyFunctionEnabled(true)
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build();
+        SearchResults searchResults = mDb1.search("hasProperty(\"prop1\")",
+                searchSpec);
+        List<SearchResult> page = searchResults.getNextPageAsync().get();
+        assertThat(page).hasSize(2);
+        assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id3");
+        assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id1");
+
+        searchResults = mDb1.search("hasProperty(\"prop2\")", searchSpec);
+        page = searchResults.getNextPageAsync().get();
+        assertThat(page).hasSize(2);
+        assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id3");
+        assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id2");
+
+        searchResults = mDb1.search(
+                "hasProperty(\"prop1\") AND hasProperty(\"prop2\")",
+                searchSpec);
+        page = searchResults.getNextPageAsync().get();
+        assertThat(page).hasSize(1);
+        assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id3");
+    }
+
+    @Test
+    public void testQuery_hasPropertyFunctionWithoutEnablingFeatureFails() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_HAS_PROPERTY_FUNCTION));
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema")
+                .addProperty(new StringPropertyConfig.Builder("prop")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+                .setForceOverride(true).addSchemas(schema).build()).get();
+
+        GenericDocument doc = new GenericDocument.Builder<>(
+                "namespace1", "id1", "Schema")
+                .setPropertyString("prop", "Hello, world!")
+                .build();
+        mDb1.putAsync(new PutDocumentsRequest.Builder().addGenericDocuments(doc).build()).get();
+
+        // Enable LIST_FILTER_HAS_PROPERTY_FUNCTION but disable LIST_FILTER_QUERY_LANGUAGE in the
+        // SearchSpec.
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(false)
+                .setListFilterHasPropertyFunctionEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search("hasProperty(\"prop\")",
+                searchSpec);
+        ExecutionException executionException = assertThrows(ExecutionException.class,
+                () -> searchResults.getNextPageAsync().get());
+        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+        AppSearchException exception = (AppSearchException) executionException.getCause();
+        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+        assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
+        assertThat(exception).hasMessageThat().contains(Features.LIST_FILTER_QUERY_LANGUAGE);
+
+        // Disable LIST_FILTER_HAS_PROPERTY_FUNCTION in the SearchSpec.
+        searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterHasPropertyFunctionEnabled(false)
+                .build();
+        SearchResults searchResults2 = mDb1.search("hasProperty(\"prop\")",
+                searchSpec);
+        executionException = assertThrows(ExecutionException.class,
+                () -> searchResults2.getNextPageAsync().get());
+        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+        exception = (AppSearchException) executionException.getCause();
+        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+        assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
+        assertThat(exception).hasMessageThat().contains("HAS_PROPERTY_FUNCTION");
+    }
+
+    @Test
     public void testQuery_propertyWeightsNotSupported() throws Exception {
         assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SPEC_PROPERTY_WEIGHTS));
 
@@ -4793,6 +6485,257 @@
     }
 
     @Test
+    public void testQueryWithJoin_typePropertyFiltersOnNestedSpec() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+        // A full example of how join might be used with property filters in join spec
+        AppSearchSchema actionSchema = new AppSearchSchema.Builder("ViewAction")
+                .addProperty(new StringPropertyConfig.Builder("entityId")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setJoinableValueType(StringPropertyConfig
+                                .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("note")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("viewType")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA, actionSchema)
+                        .build()).get();
+
+        // Index 2 email documents
+        AppSearchEmail inEmail =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace", "id2")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+
+        // Index 2 viewAction documents, one for email1 and the other for email2
+        String qualifiedId1 =
+                DocumentIdUtil.createQualifiedId(
+                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
+                        "namespace", "id1");
+        String qualifiedId2 =
+                DocumentIdUtil.createQualifiedId(
+                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
+                        "namespace", "id2");
+        GenericDocument viewAction1 = new GenericDocument.Builder<>("NS", "id3", "ViewAction")
+                .setPropertyString("entityId", qualifiedId1)
+                .setPropertyString("note", "Viewed email on Monday")
+                .setPropertyString("viewType", "Stared").build();
+        GenericDocument viewAction2 = new GenericDocument.Builder<>("NS", "id4", "ViewAction")
+                .setPropertyString("entityId", qualifiedId2)
+                .setPropertyString("note", "Viewed email on Tuesday")
+                .setPropertyString("viewType", "Viewed").build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail, inEmail2,
+                                viewAction1, viewAction2)
+                        .build()));
+
+        // The nested search spec only allows searching the viewType property for viewAction
+        // schema type. It also specifies a property filter for Email schema.
+        SearchSpec nestedSearchSpec =
+                new SearchSpec.Builder()
+                        .addFilterProperties("ViewAction", ImmutableList.of("viewType"))
+                        .addFilterProperties(AppSearchEmail.SCHEMA_TYPE,
+                                ImmutableList.of("subject"))
+                        .build();
+
+        // Search for the term "Viewed" in join spec
+        JoinSpec js = new JoinSpec.Builder("entityId")
+                .setNestedSearch("Viewed", nestedSearchSpec)
+                .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
+                .build();
+
+        SearchResults searchResults = mDb1.search("body email", new SearchSpec.Builder()
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
+                .setJoinSpec(js)
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build());
+
+        List<SearchResult> sr = searchResults.getNextPageAsync().get();
+
+        // Both email docs are returned, email2 comes first because it has higher number of
+        // joined documents. The property filters for Email schema specified in the nested search
+        // specs don't apply to the outer query (otherwise none of the email documents would have
+        // been returned).
+        assertThat(sr).hasSize(2);
+
+        // Email2 has a viewAction document viewAction2 that satisfies the property filters in
+        // the join spec, so it should be present in the joined results.
+        assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("id2");
+        assertThat(sr.get(0).getRankingSignal()).isEqualTo(1.0);
+        assertThat(sr.get(0).getJoinedResults()).hasSize(1);
+        assertThat(sr.get(0).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction2);
+
+        // Email1 has a viewAction document viewAction1 but it doesn't satisfy the property filters
+        // in the join spec, so it should not be present in the joined results.
+        assertThat(sr.get(1).getGenericDocument().getId()).isEqualTo("id1");
+        assertThat(sr.get(1).getRankingSignal()).isEqualTo(0.0);
+        assertThat(sr.get(1).getJoinedResults()).isEmpty();
+    }
+
+    @Test
+    public void testQueryWithJoin_typePropertyFiltersOnOuterSpec() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        assumeTrue(mDb1.getFeatures()
+                .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
+
+        // A full example of how join might be used with property filters in join spec
+        AppSearchSchema actionSchema = new AppSearchSchema.Builder("ViewAction")
+                .addProperty(new StringPropertyConfig.Builder("entityId")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setJoinableValueType(StringPropertyConfig
+                                .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("note")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).addProperty(new StringPropertyConfig.Builder("viewType")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA, actionSchema)
+                        .build()).get();
+
+        // Index 2 email documents
+        AppSearchEmail inEmail =
+                new AppSearchEmail.Builder("namespace", "id1")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace", "id2")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+
+        // Index 2 viewAction documents, one for email1 and the other for email2
+        String qualifiedId1 =
+                DocumentIdUtil.createQualifiedId(
+                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
+                        "namespace", "id1");
+        String qualifiedId2 =
+                DocumentIdUtil.createQualifiedId(
+                        ApplicationProvider.getApplicationContext().getPackageName(), DB_NAME_1,
+                        "namespace", "id2");
+        GenericDocument viewAction1 = new GenericDocument.Builder<>("NS", "id3", "ViewAction")
+                .setPropertyString("entityId", qualifiedId1)
+                .setPropertyString("note", "Viewed email on Monday")
+                .setPropertyString("viewType", "Stared").build();
+        GenericDocument viewAction2 = new GenericDocument.Builder<>("NS", "id4", "ViewAction")
+                .setPropertyString("entityId", qualifiedId2)
+                .setPropertyString("note", "Viewed email on Tuesday")
+                .setPropertyString("viewType", "Viewed").build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail, inEmail2,
+                                viewAction1, viewAction2)
+                        .build()));
+
+        // The nested search spec doesn't specify any property filters.
+        SearchSpec nestedSearchSpec = new SearchSpec.Builder().build();
+
+        // Search for the term "Viewed" in join spec
+        JoinSpec js = new JoinSpec.Builder("entityId")
+                .setNestedSearch("Viewed", nestedSearchSpec)
+                .setAggregationScoringStrategy(JoinSpec.AGGREGATION_SCORING_RESULT_COUNT)
+                .build();
+
+        // Outer search spec adds property filters for both Email and ViewAction schema
+        SearchResults searchResults = mDb1.search("body email", new SearchSpec.Builder()
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_JOIN_AGGREGATE_SCORE)
+                .setJoinSpec(js)
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("body"))
+                .addFilterProperties("ViewAction", ImmutableList.of("viewType"))
+                .build());
+
+        List<SearchResult> sr = searchResults.getNextPageAsync().get();
+
+        // Both email docs are returned as they both satisfy the property filters for Email, email2
+        // comes first because it has higher id lexicographically.
+        assertThat(sr).hasSize(2);
+
+        // Email2 has a viewAction document viewAction2 that satisfies the property filters in
+        // the outer spec (although those property filters are irrelevant for joined documents),
+        // it should be present in the joined results.
+        assertThat(sr.get(0).getGenericDocument().getId()).isEqualTo("id2");
+        assertThat(sr.get(0).getRankingSignal()).isEqualTo(1.0);
+        assertThat(sr.get(0).getJoinedResults()).hasSize(1);
+        assertThat(sr.get(0).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction2);
+
+        // Email1 has a viewAction document viewAction1 that doesn't satisfy the property filters
+        // in the outer spec, but property filters in the outer spec should not apply on joined
+        // documents, so viewAction1 should be present in the joined results.
+        assertThat(sr.get(1).getGenericDocument().getId()).isEqualTo("id1");
+        assertThat(sr.get(1).getRankingSignal()).isEqualTo(1.0);
+        assertThat(sr.get(0).getJoinedResults()).hasSize(1);
+        assertThat(sr.get(1).getJoinedResults().get(0).getGenericDocument()).isEqualTo(viewAction1);
+    }
+
+    @Test
+    public void testQuery_typePropertyFiltersNotSupported() throws Exception {
+        assumeFalse(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                new SetSchemaRequest.Builder()
+                        .addSchemas(AppSearchEmail.SCHEMA)
+                        .build()).get();
+
+        // Query with type property filters {"Email", ["subject", "to"]} and verify that unsupported
+        // exception is thrown
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .addFilterProperties(AppSearchEmail.SCHEMA_TYPE, ImmutableList.of("subject", "to"))
+                .build();
+        UnsupportedOperationException exception =
+                assertThrows(UnsupportedOperationException.class,
+                        () -> mDb1.search("body", searchSpec));
+        assertThat(exception).hasMessageThat().contains(Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES
+                + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
     public void testSimpleJoin() throws Exception {
         assumeTrue(mDb1.getFeatures()
                 .isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
@@ -4915,7 +6858,7 @@
         assertThrows(UnsupportedOperationException.class, () ->
                 mDb1.searchSuggestionAsync(
                         /*suggestionQueryExpression=*/"t",
-                        new SearchSuggestionSpec.Builder(/*totalResultCount=*/2).build()).get());
+                        new SearchSuggestionSpec.Builder(/*maximumResultCount=*/2).build()).get());
     }
 
     @Test
@@ -4960,14 +6903,14 @@
 
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultOne, resultTwo, resultThree, resultFour)
                 .inOrder();
 
         // Query first 2 suggestions, and they will be ranked.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/2).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/2).build()).get();
         assertThat(suggestions).containsExactly(resultOne, resultTwo).inOrder();
     }
 
@@ -5008,21 +6951,21 @@
         // namespace1 has 2 results.
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterNamespaces("namespace1").build()).get();
         assertThat(suggestions).containsExactly(resultFoo, resultFo).inOrder();
 
         // namespace2 has 1 result.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterNamespaces("namespace2").build()).get();
         assertThat(suggestions).containsExactly(resultFoo).inOrder();
 
         // namespace2 and 3 has 2 results.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterNamespaces("namespace2", "namespace3")
                         .build()).get();
         assertThat(suggestions).containsExactly(resultFoo, resultFool);
@@ -5030,7 +6973,7 @@
         // non exist namespace has empty result
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterNamespaces("nonExistNamespace").build()).get();
         assertThat(suggestions).isEmpty();
     }
@@ -5077,7 +7020,7 @@
         // Only search for namespace1/doc1
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterNamespaces("namespace1")
                         .addFilterDocumentIds("namespace1", "id1")
                         .build()).get();
@@ -5086,7 +7029,7 @@
         // Only search for namespace1/doc1 and namespace1/doc2
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterNamespaces("namespace1")
                         .addFilterDocumentIds("namespace1", ImmutableList.of("id1", "id2"))
                         .build()).get();
@@ -5095,7 +7038,7 @@
         // Only search for namespace1/doc1 and namespace2/doc3
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterNamespaces("namespace1", "namespace2")
                         .addFilterDocumentIds("namespace1", "id1")
                         .addFilterDocumentIds("namespace2", ImmutableList.of("id3"))
@@ -5105,7 +7048,7 @@
         // Only search for namespace1/doc1 and everything in namespace2
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterDocumentIds("namespace1", "id1")
                         .build()).get();
         assertThat(suggestions).containsExactly(resultOne, resultThree, resultFour);
@@ -5163,21 +7106,21 @@
         // Type1 has 2 results.
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterSchemas("Type1").build()).get();
         assertThat(suggestions).containsExactly(resultFoo, resultFo).inOrder();
 
         // Type2 has 1 result.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterSchemas("Type2").build()).get();
         assertThat(suggestions).containsExactly(resultFoo).inOrder();
 
         // Type2 and 3 has 2 results.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterSchemas("Type2", "Type3")
                         .build()).get();
         assertThat(suggestions).containsExactly(resultFoo, resultFool);
@@ -5185,7 +7128,7 @@
         // non exist type has empty result.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .addFilterSchemas("nonExistType").build()).get();
         assertThat(suggestions).isEmpty();
     }
@@ -5233,13 +7176,13 @@
         // prefix f has 2 results.
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultFoo, resultFool);
 
         // prefix b has 2 results.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"b",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultBar, resultBaz);
     }
 
@@ -5285,7 +7228,7 @@
         // rank by NONE, the order should be arbitrary but all terms appear.
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .setRankingStrategy(SearchSuggestionSpec
                                 .SUGGESTION_RANKING_STRATEGY_NONE)
                         .build()).get();
@@ -5294,7 +7237,7 @@
         // rank by document count, the order should be term1:3 > term2:2 > term3:1
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .setRankingStrategy(SearchSuggestionSpec
                                 .SUGGESTION_RANKING_STRATEGY_DOCUMENT_COUNT)
                         .build()).get();
@@ -5303,7 +7246,7 @@
         // rank by term frequency, the order should be term3:5 > term2:4 > term1:3
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10)
                         .setRankingStrategy(SearchSuggestionSpec
                                 .SUGGESTION_RANKING_STRATEGY_TERM_FREQUENCY)
                         .build()).get();
@@ -5348,7 +7291,7 @@
         // prefix t has 3 results.
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultTwo, resultThree, resultTart);
 
         // Delete the document
@@ -5359,7 +7302,7 @@
         // now prefix t has 2 results.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultThree, resultTart);
     }
 
@@ -5397,7 +7340,7 @@
         // prefix t has 3 results.
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultTwo, resultThree, resultTart);
 
         // replace the document
@@ -5411,7 +7354,7 @@
         // prefix t has 2 results for now.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultThree, resultTwist);
     }
 
@@ -5448,13 +7391,13 @@
         // database 1 could get suggestion results
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).containsExactly(resultOne, resultTwo).inOrder();
 
         // database 2 couldn't get suggestion results
         suggestions = mDb2.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         assertThat(suggestions).isEmpty();
     }
 
@@ -5488,7 +7431,7 @@
         // Search "bar AND f" only document 1 should match the search.
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"bar f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         SearchSuggestionResult barFo =
                 new SearchSuggestionResult.Builder().setSuggestedResult("bar fo").build();
         assertThat(suggestions).containsExactly(barFo);
@@ -5497,7 +7440,7 @@
         // match.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"bar OR cat f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         SearchSuggestionResult barCatFo =
                 new SearchSuggestionResult.Builder().setSuggestedResult("bar OR cat fo").build();
         SearchSuggestionResult barCatFoo =
@@ -5507,7 +7450,7 @@
         // Search for "(bar AND cat) OR f", all documents could match.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"(bar cat) OR f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         SearchSuggestionResult barCatOrFo =
                 new SearchSuggestionResult.Builder().setSuggestedResult("(bar cat) OR fo").build();
         SearchSuggestionResult barCatOrFoo =
@@ -5520,7 +7463,7 @@
         // Search for "-bar f", document2 "cat foo" could and document3 "fool" could match.
         suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"-bar f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         SearchSuggestionResult noBarFoo =
                 new SearchSuggestionResult.Builder().setSuggestedResult("-bar foo").build();
         SearchSuggestionResult noBarFool =
@@ -5529,6 +7472,162 @@
     }
 
     @Test
+    public void testSearchSuggestion_propertyFilter() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+        // Schema registration
+        AppSearchSchema schemaType1 =
+                new AppSearchSchema.Builder("Type1")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertyone")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertytwo")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        AppSearchSchema schemaType2 =
+                new AppSearchSchema.Builder("Type2")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertythree")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertyfour")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder().addSchemas(schemaType1, schemaType2).build())
+                .get();
+
+        // Index documents
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Type1")
+                        .setPropertyString("propertyone", "termone")
+                        .setPropertyString("propertytwo", "termtwo")
+                        .build();
+        GenericDocument doc2 =
+                new GenericDocument.Builder<>("namespace", "id2", "Type2")
+                        .setPropertyString("propertythree", "termthree")
+                        .setPropertyString("propertyfour", "termfour")
+                        .build();
+
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(doc1, doc2).build()));
+
+        SearchSuggestionResult resultOne =
+                new SearchSuggestionResult.Builder().setSuggestedResult("termone").build();
+        SearchSuggestionResult resultTwo =
+                new SearchSuggestionResult.Builder().setSuggestedResult("termtwo").build();
+        SearchSuggestionResult resultThree =
+                new SearchSuggestionResult.Builder().setSuggestedResult("termthree").build();
+        SearchSuggestionResult resultFour =
+                new SearchSuggestionResult.Builder().setSuggestedResult("termfour").build();
+
+        // Only search for type1/propertyone
+        List<SearchSuggestionResult> suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* maximumResultCount= */ 10)
+                                        .addFilterSchemas("Type1")
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .build())
+                        .get();
+        assertThat(suggestions).containsExactly(resultOne);
+
+        // Only search for type1/propertyone and type1/propertytwo
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* maximumResultCount= */ 10)
+                                        .addFilterSchemas("Type1")
+                                        .addFilterProperties(
+                                                "Type1",
+                                                ImmutableList.of("propertyone", "propertytwo"))
+                                        .build())
+                        .get();
+        assertThat(suggestions).containsExactly(resultOne, resultTwo);
+
+        // Only search for type1/propertyone and type2/propertythree
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* maximumResultCount= */ 10)
+                                        .addFilterSchemas("Type1", "Type2")
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .addFilterProperties(
+                                                "Type2", ImmutableList.of("propertythree"))
+                                        .build())
+                        .get();
+        assertThat(suggestions).containsExactly(resultOne, resultThree);
+
+        // Only search for type1/propertyone and type2/propertyfour, in addFilterPropertyPaths
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* maximumResultCount= */ 10)
+                                        .addFilterSchemas("Type1", "Type2")
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .addFilterPropertyPaths(
+                                                "Type2",
+                                                ImmutableList.of(new PropertyPath("propertyfour")))
+                                        .build())
+                        .get();
+        assertThat(suggestions).containsExactly(resultOne, resultFour);
+
+        // Only search for type1/propertyone and everything in type2
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* maximumResultCount= */ 10)
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .build())
+                        .get();
+        assertThat(suggestions).containsExactly(resultOne, resultThree, resultFour);
+    }
+
+    @Test
+    public void testSearchSuggestion_propertyFilter_notSupported() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
+        assumeFalse(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES));
+
+        SearchSuggestionSpec searchSuggestionSpec =
+                new SearchSuggestionSpec.Builder(/* maximumResultCount= */ 10)
+                    .addFilterSchemas("Type1")
+                    .addFilterProperties("Type1", ImmutableList.of("property"))
+                    .build();
+
+        // Search suggest with type property filters {"Email", ["property"]} and verify that
+        // unsupported exception is thrown
+        UnsupportedOperationException exception =
+                assertThrows(UnsupportedOperationException.class,
+                        () -> mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t", searchSuggestionSpec).get());
+        assertThat(exception).hasMessageThat().contains(Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES
+                + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
     public void testSearchSuggestion_PropertyRestriction() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SEARCH_SUGGESTION));
         // Schema registration
@@ -5566,7 +7665,7 @@
         // Search for "bar AND subject:f"
         List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
                 /*suggestionQueryExpression=*/"bar subject:f",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10).build()).get();
+                new SearchSuggestionSpec.Builder(/*maximumResultCount=*/10).build()).get();
         SearchSuggestionResult barSubjectFo =
                 new SearchSuggestionResult.Builder().setSuggestedResult("bar subject:fo").build();
         SearchSuggestionResult barSubjectFoo =
@@ -5609,6 +7708,13 @@
         Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas();
         assertThat(actual).hasSize(3);
         assertThat(actual).isEqualTo(request.getSchemas());
+
+        // Check that calling getParentType() for the EmailMessage schema returns Email and Message
+        for (AppSearchSchema schema : actual) {
+            if (schema.getSchemaType().equals("EmailMessage")) {
+                assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
+            }
+        }
     }
 
     @Test
@@ -5641,6 +7747,70 @@
     }
 
     @Test
+    public void testGetSchema_indexableNestedPropsList() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
+
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                        "worksFor", "Organization")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties(Collections.singleton("name"))
+                                        .build())
+                        .build();
+        AppSearchSchema organizationSchema =
+                new AppSearchSchema.Builder("Organization")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("notes")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        SetSchemaRequest setSchemaRequest =
+                new SetSchemaRequest.Builder()
+                        .addSchemas(personSchema, organizationSchema)
+                        .build();
+        mDb1.setSchemaAsync(setSchemaRequest).get();
+
+        Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas();
+        assertThat(actual).hasSize(2);
+        assertThat(actual).isEqualTo(setSchemaRequest.getSchemas());
+
+        for (AppSearchSchema schema : actual) {
+            if (schema.getSchemaType().equals("Person")) {
+                for (PropertyConfig property : schema.getProperties()) {
+                    if (property.getName().equals("worksFor")) {
+                        assertThat(
+                                ((DocumentPropertyConfig) property)
+                                        .getIndexableNestedProperties()).containsExactly("name");
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
     public void testSetSchema_dataTypeIncompatibleWithParentTypes() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
         AppSearchSchema messageSchema =
@@ -6473,4 +8643,643 @@
         assertThat(emailSchema.toString()).contains(expectedIndexableNestedPropertyMessage);
 
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingSearch_simple() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding1")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding2")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+        // Index documents
+        GenericDocument doc0 =
+                new GenericDocument.Builder<>("namespace", "id0", "Email")
+                        .setPropertyString("body", "foo")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                        new float[]{-0.1f, -0.2f, -0.3f, 0.4f, 0.5f},
+                                        "my_model_v1"),
+                                new EmbeddingVector(
+                                        new float[]{0.6f, 0.7f, 0.8f}, "my_model_v2"))
+                        .build();
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Email")
+                        .setPropertyString("body", "bar")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{-0.1f, 0.2f, -0.3f, -0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                new float[]{0.6f, 0.7f, -0.8f}, "my_model_v2"))
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(doc0, doc1).build()));
+
+        // Add an embedding search with dot product semantic scores:
+        // - document 0: -0.5 (embedding1), 0.3 (embedding2)
+        // - document 1: -0.9 (embedding1)
+        EmbeddingVector searchEmbedding = new EmbeddingVector(
+                new float[]{1, -1, -1, 1, -1}, "my_model_v1");
+
+        // Match documents that have embeddings with a similarity closer to 0 that is
+        // greater than -1.
+        //
+        // The matched embeddings for each doc are:
+        // - document 0: -0.5 (embedding1), 0.3 (embedding2)
+        // - document 1: -0.9 (embedding1)
+        // The scoring expression for each doc will be evaluated as:
+        // - document 0: sum({-0.5, 0.3}) + sum({}) = -0.2
+        // - document 1: sum({-0.9}) + sum({}) = -0.9
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(searchEmbedding)
+                .setRankingStrategy(
+                        "sum(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search(
+                "semanticSearch(getSearchSpecEmbedding(0), -1, 1)", searchSpec);
+        List<SearchResult> results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(2);
+        assertThat(results.get(0).getGenericDocument()).isEqualTo(doc0);
+        assertThat(results.get(0).getRankingSignal()).isWithin(0.00001).of(-0.2);
+        assertThat(results.get(1).getGenericDocument()).isEqualTo(doc1);
+        assertThat(results.get(1).getRankingSignal()).isWithin(0.00001).of(-0.9);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingSearch_propertyRestriction() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding1")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding2")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+        // Index documents
+        GenericDocument doc0 =
+                new GenericDocument.Builder<>("namespace", "id0", "Email")
+                        .setPropertyString("body", "foo")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                        new float[]{-0.1f, -0.2f, -0.3f, 0.4f, 0.5f},
+                                        "my_model_v1"),
+                                new EmbeddingVector(
+                                        new float[]{0.6f, 0.7f, 0.8f}, "my_model_v2"))
+                        .build();
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Email")
+                        .setPropertyString("body", "bar")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{-0.1f, 0.2f, -0.3f, -0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                new float[]{0.6f, 0.7f, -0.8f}, "my_model_v2"))
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(doc0, doc1).build()));
+
+        // Add an embedding search with dot product semantic scores:
+        // - document 0: -0.5 (embedding1), 0.3 (embedding2)
+        // - document 1: -0.9 (embedding1)
+        EmbeddingVector searchEmbedding = new EmbeddingVector(
+                new float[]{1, -1, -1, 1, -1}, "my_model_v1");
+
+        // Create a query similar as above but with a property restriction, which still matches
+        // document 0 and document 1 but the semantic score 0.3 should be removed from document 0.
+        //
+        // The matched embeddings for each doc are:
+        // - document 0: -0.5 (embedding1)
+        // - document 1: -0.9 (embedding1)
+        // The scoring expression for each doc will be evaluated as:
+        // - document 0: sum({-0.5}) = -0.5
+        // - document 1: sum({-0.9}) = -0.9
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(searchEmbedding)
+                .setRankingStrategy("sum(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search(
+                "embedding1:semanticSearch(getSearchSpecEmbedding(0), -1, 1)", searchSpec);
+        List<SearchResult> results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(2);
+        assertThat(results.get(0).getGenericDocument()).isEqualTo(doc0);
+        assertThat(results.get(0).getRankingSignal()).isWithin(0.00001).of(-0.5);
+        assertThat(results.get(1).getGenericDocument()).isEqualTo(doc1);
+        assertThat(results.get(1).getRankingSignal()).isWithin(0.00001).of(-0.9);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingSearch_multipleSearchEmbeddings() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding1")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding2")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+        // Index documents
+        GenericDocument doc0 =
+                new GenericDocument.Builder<>("namespace", "id0", "Email")
+                        .setPropertyString("body", "foo")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                        new float[]{-0.1f, -0.2f, -0.3f, 0.4f, 0.5f},
+                                        "my_model_v1"),
+                                new EmbeddingVector(
+                                        new float[]{0.6f, 0.7f, 0.8f}, "my_model_v2"))
+                        .build();
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Email")
+                        .setPropertyString("body", "bar")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{-0.1f, 0.2f, -0.3f, -0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                new float[]{0.6f, 0.7f, -0.8f}, "my_model_v2"))
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(doc0, doc1).build()));
+
+        // Add an embedding search with dot product semantic scores:
+        // - document 0: -0.5 (embedding1), 0.3 (embedding2)
+        // - document 1: -0.9 (embedding1)
+        EmbeddingVector searchEmbedding1 = new EmbeddingVector(
+                new float[]{1, -1, -1, 1, -1}, "my_model_v1");
+        // Add an embedding search with dot product semantic scores:
+        // - document 0: -0.5 (embedding2)
+        // - document 1: -2.1 (embedding2)
+        EmbeddingVector searchEmbedding2 = new EmbeddingVector(
+                new float[]{-1, -1, 1}, "my_model_v2");
+
+        // Create a complex query that matches all hits from all documents.
+        //
+        // The matched embeddings for each doc are:
+        // - document 0: -0.5 (embedding1), 0.3 (embedding2), -0.5 (embedding2)
+        // - document 1: -0.9 (embedding1), -2.1 (embedding2)
+        // The scoring expression for each doc will be evaluated as:
+        // - document 0: sum({-0.5, 0.3}) + sum({-0.5}) = -0.7
+        // - document 1: sum({-0.9}) + sum({-2.1}) = -3
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(searchEmbedding1, searchEmbedding2)
+                .setRankingStrategy("sum(this.matchedSemanticScores(getSearchSpecEmbedding(0))) + "
+                        + "sum(this.matchedSemanticScores(getSearchSpecEmbedding(1)))")
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search(
+                "semanticSearch(getSearchSpecEmbedding(0)) OR "
+                        + "semanticSearch(getSearchSpecEmbedding(1))", searchSpec);
+        List<SearchResult> results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(2);
+        assertThat(results.get(0).getGenericDocument()).isEqualTo(doc0);
+        assertThat(results.get(0).getRankingSignal()).isWithin(0.00001).of(-0.7);
+        assertThat(results.get(1).getGenericDocument()).isEqualTo(doc1);
+        assertThat(results.get(1).getRankingSignal()).isWithin(0.00001).of(-3);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingSearch_hybrid() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding1")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding2")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+        // Index documents
+        GenericDocument doc0 =
+                new GenericDocument.Builder<>("namespace", "id0", "Email")
+                        .setPropertyString("body", "foo")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                        new float[]{-0.1f, -0.2f, -0.3f, 0.4f, 0.5f},
+                                        "my_model_v1"),
+                                new EmbeddingVector(
+                                        new float[]{0.6f, 0.7f, 0.8f}, "my_model_v2"))
+                        .build();
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Email")
+                        .setPropertyString("body", "bar")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{-0.1f, 0.2f, -0.3f, -0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                new float[]{0.6f, 0.7f, -0.8f}, "my_model_v2"))
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(doc0, doc1).build()));
+
+        // Add an embedding search with dot product semantic scores:
+        // - document 0: -0.5 (embedding2)
+        // - document 1: -2.1 (embedding2)
+        EmbeddingVector searchEmbedding = new EmbeddingVector(
+                new float[]{-1, -1, 1}, "my_model_v2");
+
+        // Create a hybrid query that matches document 0 because of term-based search
+        // and document 1 because of embedding-based search.
+        //
+        // The matched embeddings for each doc are:
+        // - document 1: -2.1 (embedding2)
+        // The scoring expression for each doc will be evaluated as:
+        // - document 0: sum({}) = 0
+        // - document 1: sum({-2.1}) = -2.1
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(searchEmbedding)
+                .setRankingStrategy("sum(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search(
+                "foo OR semanticSearch(getSearchSpecEmbedding(0), -10, -1)", searchSpec);
+        List<SearchResult> results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(2);
+        assertThat(results.get(0).getGenericDocument()).isEqualTo(doc0);
+        assertThat(results.get(0).getRankingSignal()).isWithin(0.00001).of(0);
+        assertThat(results.get(1).getGenericDocument()).isEqualTo(doc1);
+        assertThat(results.get(1).getRankingSignal()).isWithin(0.00001).of(-2.1);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingSearchWithoutEnablingFeatureFails() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding1")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .build())
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding2")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+        // Index documents
+        GenericDocument doc =
+                new GenericDocument.Builder<>("namespace", "id0", "Email")
+                        .setPropertyString("body", "foo")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding1", new EmbeddingVector(
+                                new float[]{0.1f, 0.2f, 0.3f, 0.4f, 0.5f}, "my_model_v1"))
+                        .setPropertyEmbedding("embedding2", new EmbeddingVector(
+                                        new float[]{-0.1f, -0.2f, -0.3f, 0.4f, 0.5f},
+                                        "my_model_v1"),
+                                new EmbeddingVector(
+                                        new float[]{0.6f, 0.7f, 0.8f}, "my_model_v2"))
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(doc).build()));
+
+        EmbeddingVector searchEmbedding = new EmbeddingVector(
+                new float[]{1, -1, -1, 1, -1}, "my_model_v1");
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(searchEmbedding)
+                .setRankingStrategy(
+                        "sum(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                .setListFilterQueryLanguageEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search(
+                "semanticSearch(getSearchSpecEmbedding(0), -1, 1)", searchSpec);
+        ExecutionException executionException = assertThrows(ExecutionException.class,
+                () -> searchResults.getNextPageAsync().get());
+        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+        AppSearchException exception = (AppSearchException) executionException.getCause();
+        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+        assertThat(exception).hasMessageThat().contains("Attempted use of unenabled feature");
+        assertThat(exception).hasMessageThat().contains("EMBEDDING_SEARCH");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingSearch_notSupported() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+        assumeFalse(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+        UnsupportedOperationException exception = assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("semanticSearch(getSearchSpecEmbedding(0), -1, 1)", searchSpec));
+        assertThat(exception).hasMessageThat().contains(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG
+                + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION)
+    public void testTokenizeSearch_simple() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_TOKENIZE_FUNCTION));
+
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email")
+                .addProperty(new StringPropertyConfig.Builder("body")
+                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+        // Index documents
+        GenericDocument doc0 =
+                new GenericDocument.Builder<>("namespace", "id0", "Email")
+                        .setPropertyString("body", "foo bar")
+                        .setCreationTimestampMillis(1000)
+                        .build();
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Email")
+                        .setPropertyString("body", "bar")
+                        .setCreationTimestampMillis(1000)
+                        .build();
+        GenericDocument doc2 =
+                new GenericDocument.Builder<>("namespace", "id2", "Email")
+                        .setPropertyString("body", "foo")
+                        .setCreationTimestampMillis(1000)
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(doc0, doc1, doc2).build()));
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterTokenizeFunctionEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search("tokenize(\"foo.\")", searchSpec);
+        List<GenericDocument> results = convertSearchResultsToDocuments(searchResults);
+        assertThat(results).containsExactly(doc2, doc0);
+
+        searchResults = mDb1.search("tokenize(\"bar, foo\")", searchSpec);
+        results = convertSearchResultsToDocuments(searchResults);
+        assertThat(results).containsExactly(doc0);
+
+        searchResults = mDb1.search("tokenize(\"\\\"bar, \\\"foo\\\"\")", searchSpec);
+        results = convertSearchResultsToDocuments(searchResults);
+        assertThat(results).containsExactly(doc0);
+
+        searchResults = mDb1.search("tokenize(\"bar ) foo\")", searchSpec);
+        results = convertSearchResultsToDocuments(searchResults);
+        assertThat(results).containsExactly(doc0);
+
+        searchResults = mDb1.search("tokenize(\"bar foo(\")", searchSpec);
+        results = convertSearchResultsToDocuments(searchResults);
+        assertThat(results).containsExactly(doc0);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION)
+    public void testTokenizeSearch_notSupported() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_QUERY_LANGUAGE));
+        assumeFalse(
+                mDb1.getFeatures().isFeatureSupported(Features.LIST_FILTER_TOKENIZE_FUNCTION));
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterTokenizeFunctionEnabled(true)
+                .build();
+        UnsupportedOperationException exception = assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("tokenize(\"foo.\")", searchSpec));
+        assertThat(exception).hasMessageThat().contains(Features.LIST_FILTER_TOKENIZE_FUNCTION
+                + " is not available on this AppSearch implementation.");
+    }
+
+    @Test
+    @RequiresFlagsEnabled({
+            Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS,
+            Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG})
+    public void testInformationalRankingExpressions() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG));
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS));
+
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("Email")
+                .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding")
+                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                        .setIndexingType(
+                                AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
+        // Index documents
+        final int doc0DocScore = 2;
+        GenericDocument doc0 =
+                new GenericDocument.Builder<>("namespace", "id0", "Email")
+                        .setScore(doc0DocScore)
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding", new EmbeddingVector(
+                                new float[]{-0.1f, -0.2f, -0.3f, -0.4f, -0.5f}, "my_model"))
+                        .build();
+        final int doc1DocScore = 3;
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Email")
+                        .setScore(doc1DocScore)
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyEmbedding("embedding", new EmbeddingVector(
+                                        new float[]{-0.1f, 0.2f, -0.3f, -0.4f, 0.5f}, "my_model"),
+                                new EmbeddingVector(
+                                        new float[]{-0.1f, -0.2f, -0.3f, -0.4f, -0.5f}, "my_model"))
+                        .build();
+        checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(doc0, doc1).build()));
+
+        // Add an embedding search with dot product semantic scores:
+        // - document 0: 0.5
+        // - document 1: -0.9, 0.5
+        EmbeddingVector searchEmbedding = new EmbeddingVector(
+                new float[]{1, -1, -1, 1, -1}, "my_model");
+
+        // Make an embedding query that matches all documents.
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(searchEmbedding)
+                .setRankingStrategy(
+                        "sum(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                .addInformationalRankingExpressions(
+                        "len(this.matchedSemanticScores(getSearchSpecEmbedding(0)))")
+                .addInformationalRankingExpressions("this.documentScore()")
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+        SearchResults searchResults = mDb1.search(
+                "semanticSearch(getSearchSpecEmbedding(0))", searchSpec);
+        List<SearchResult> results = retrieveAllSearchResults(searchResults);
+        assertThat(results).hasSize(2);
+        // doc0:
+        assertThat(results.get(0).getGenericDocument()).isEqualTo(doc0);
+        assertThat(results.get(0).getRankingSignal()).isWithin(0.00001).of(0.5);
+        // doc0 has 1 embedding vector and a document score of 2.
+        assertThat(results.get(0).getInformationalRankingSignals())
+                .containsExactly(1.0, (double) doc0DocScore).inOrder();
+
+        // doc1:
+        assertThat(results.get(1).getGenericDocument()).isEqualTo(doc1);
+        assertThat(results.get(1).getRankingSignal()).isWithin(0.00001).of(-0.9 + 0.5);
+        // doc1 has 2 embedding vectors and a document score of 3.
+        assertThat(results.get(1).getInformationalRankingSignals())
+                .containsExactly(2.0, (double) doc1DocScore).inOrder();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+    public void testInformationalRankingExpressions_notSupported() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION));
+        assumeFalse(mDb1.getFeatures().isFeatureSupported(
+                Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS));
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setRankingStrategy("this.documentScore() + 1")
+                .addInformationalRankingExpressions("this.documentScore()")
+                .build();
+        UnsupportedOperationException exception = assertThrows(
+                UnsupportedOperationException.class,
+                () -> mDb1.search("foo", searchSpec));
+        assertThat(exception).hasMessageThat().contains(
+                Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS
+                + " are not available on this AppSearch implementation.");
+    }
+
+    @Test
+    public void testPutDocuments_emptyBytesAndDocuments() throws Exception {
+        // Schema registration
+        AppSearchSchema schema = new AppSearchSchema.Builder("testSchema")
+                .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                        .build())
+                .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
+                        "document", AppSearchEmail.SCHEMA_TYPE)
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                        .setShouldIndexNestedProperties(true)
+                        .build())
+                .build();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
+                .addSchemas(schema, AppSearchEmail.SCHEMA).build()).get();
+
+        // Index a document
+        GenericDocument document = new GenericDocument.Builder<>("namespace", "id1", "testSchema")
+                .setPropertyBytes("bytes")
+                .setPropertyDocument("document")
+                .build();
+
+        AppSearchBatchResult<String, Void> result = checkIsBatchResultSuccess(mDb1.putAsync(
+                new PutDocumentsRequest.Builder().addGenericDocuments(document).build()));
+        assertThat(result.getSuccesses()).containsExactly("id1", null);
+        assertThat(result.getFailures()).isEmpty();
+
+        GetByDocumentIdRequest request = new GetByDocumentIdRequest.Builder("namespace")
+                .addIds("id1")
+                .build();
+        List<GenericDocument> outDocuments = doGet(mDb1, request);
+        assertThat(outDocuments).hasSize(1);
+        GenericDocument outDocument = outDocuments.get(0);
+        assertThat(outDocument.getPropertyBytesArray("bytes")).isEmpty();
+        assertThat(outDocument.getPropertyDocumentArray("document")).isEmpty();
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionGmsCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionGmsCtsTest.java
index 453d9225..6f08abf 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionGmsCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionGmsCtsTest.java
@@ -18,24 +18,18 @@
 package androidx.appsearch.cts.app;
 
 import android.content.Context;
-import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchSession;
 import androidx.appsearch.playservicesstorage.PlayServicesStorage;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SdkSuppress;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
 import org.junit.Assume;
-import org.junit.Test;
 
 import java.util.concurrent.ExecutorService;
 
-// TODO(b/237116468): Remove SdkSuppress once AppSearchAttributionSource available for lower API
-//  levels.
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
 public class AppSearchSessionGmsCtsTest extends AppSearchSessionCtsTestBase {
 
     private boolean mIsGmsAvailable;
@@ -68,13 +62,4 @@
             super.tearDown();
         }
     }
-
-    @Override
-    @Test
-    public void testRfc822_unsupportedFeature_throwsException() {
-        // TODO(b/280463238): // TODO(b/280463238): KNOWN_ISSUE will be fixed in next
-        //  play-services-appsearch drop.
-        // expected: tokenizerType is out of range of [0, 1] (too high)
-        // but was : tokenizerType is out of range of [%d, %d] (too high) [0, 1]
-    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionLocalCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionLocalCtsTest.java
index aba266a..79e7ad3 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionLocalCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionLocalCtsTest.java
@@ -20,7 +20,6 @@
 
 import static androidx.appsearch.testutil.AppSearchTestUtils.checkIsBatchResultSuccess;
 import static androidx.appsearch.testutil.AppSearchTestUtils.convertSearchResultsToDocuments;
-import static androidx.appsearch.testutil.AppSearchTestUtils.doGet;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -34,7 +33,6 @@
 import androidx.appsearch.app.AppSearchSession;
 import androidx.appsearch.app.Features;
 import androidx.appsearch.app.GenericDocument;
-import androidx.appsearch.app.GetByDocumentIdRequest;
 import androidx.appsearch.app.Migrator;
 import androidx.appsearch.app.PutDocumentsRequest;
 import androidx.appsearch.app.SearchResult;
@@ -560,44 +558,4 @@
         assertThat(result.getFailures().get("id1").getErrorMessage())
                 .contains("was too large to write. Max is 16777215");
     }
-
-    @Test
-    public void testPutDocuments_emptyBytesAndDocuments() throws Exception {
-        Context context = ApplicationProvider.getApplicationContext();
-        AppSearchSession db = LocalStorage.createSearchSessionAsync(
-                new LocalStorage.SearchContext.Builder(context, DB_NAME_1).build()).get();
-        // Schema registration
-        AppSearchSchema schema = new AppSearchSchema.Builder("testSchema")
-                .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                        .build())
-                .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
-                        "document", AppSearchEmail.SCHEMA_TYPE)
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(true)
-                        .build())
-                .build();
-        db.setSchemaAsync(new SetSchemaRequest.Builder()
-                .addSchemas(schema, AppSearchEmail.SCHEMA).build()).get();
-
-        // Index a document
-        GenericDocument document = new GenericDocument.Builder<>("namespace", "id1", "testSchema")
-                .setPropertyBytes("bytes")
-                .setPropertyDocument("document")
-                .build();
-
-        AppSearchBatchResult<String, Void> result = checkIsBatchResultSuccess(db.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(document).build()));
-        assertThat(result.getSuccesses()).containsExactly("id1", null);
-        assertThat(result.getFailures()).isEmpty();
-
-        GetByDocumentIdRequest request = new GetByDocumentIdRequest.Builder("namespace")
-                .addIds("id1")
-                .build();
-        List<GenericDocument> outDocuments = doGet(db, request);
-        assertThat(outDocuments).hasSize(1);
-        GenericDocument outDocument = outDocuments.get(0);
-        assertThat(outDocument.getPropertyBytesArray("bytes")).isEmpty();
-        assertThat(outDocument.getPropertyDocumentArray("document")).isEmpty();
-    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java
index 0fdd512..f2fc0c2 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionPlatformCtsTest.java
@@ -16,9 +16,6 @@
 // @exportToFramework:skipFile()
 package androidx.appsearch.cts.app;
 
-import static androidx.appsearch.testutil.AppSearchTestUtils.checkIsBatchResultSuccess;
-import static androidx.appsearch.testutil.AppSearchTestUtils.doGet;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assume.assumeTrue;
@@ -30,16 +27,9 @@
 import android.os.Build;
 
 import androidx.annotation.NonNull;
-import androidx.appsearch.app.AppSearchBatchResult;
-import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.AppSearchSession;
 import androidx.appsearch.app.Features;
-import androidx.appsearch.app.GenericDocument;
-import androidx.appsearch.app.GetByDocumentIdRequest;
-import androidx.appsearch.app.PutDocumentsRequest;
-import androidx.appsearch.app.SetSchemaRequest;
 import androidx.appsearch.platformstorage.PlatformStorage;
-import androidx.appsearch.testutil.AppSearchEmail;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SdkSuppress;
 
@@ -93,51 +83,11 @@
     }
 
     @Test
+    @Override
     public void testPutDocuments_emptyBytesAndDocuments() throws Exception {
-        Context context = ApplicationProvider.getApplicationContext();
-        AppSearchSession db = PlatformStorage.createSearchSessionAsync(
-                new PlatformStorage.SearchContext.Builder(context, DB_NAME_1).build()).get();
-        // Schema registration
-        AppSearchSchema schema = new AppSearchSchema.Builder("testSchema")
-                .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                        .build())
-                .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
-                        "document", AppSearchEmail.SCHEMA_TYPE)
-                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(true)
-                        .build())
-                .build();
-        db.setSchemaAsync(new SetSchemaRequest.Builder()
-                .addSchemas(schema, AppSearchEmail.SCHEMA).build()).get();
-
-        // Index a document
-        GenericDocument document = new GenericDocument.Builder<>("namespace", "id1", "testSchema")
-                .setPropertyBytes("bytes")
-                .setPropertyDocument("document")
-                .build();
-
-        AppSearchBatchResult<String, Void> result = checkIsBatchResultSuccess(db.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(document).build()));
-        assertThat(result.getSuccesses()).containsExactly("id1", null);
-        assertThat(result.getFailures()).isEmpty();
-
-        GetByDocumentIdRequest request = new GetByDocumentIdRequest.Builder("namespace")
-                .addIds("id1")
-                .build();
-        List<GenericDocument> outDocuments = doGet(db, request);
-        assertThat(outDocuments).hasSize(1);
-        GenericDocument outDocument = outDocuments.get(0);
-        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.S
-                || Build.VERSION.SDK_INT == Build.VERSION_CODES.S_V2) {
-            // We fixed b/204677124 in Android T, so in S and S_V2, getByteArray and
-            // getDocumentArray will return null if we set empty properties.
-            assertThat(outDocument.getPropertyBytesArray("bytes")).isNull();
-            assertThat(outDocument.getPropertyDocumentArray("document")).isNull();
-        } else {
-            assertThat(outDocument.getPropertyBytesArray("bytes")).isEmpty();
-            assertThat(outDocument.getPropertyDocumentArray("document")).isEmpty();
-        }
+        // b/185441119 was fixed in Android T, this test will fail on S_V2 devices and below.
+        assumeTrue(Build.VERSION.SDK_INT >= 33);
+        super.testPutDocuments_emptyBytesAndDocuments();
     }
 
     @Override
@@ -202,5 +152,4 @@
     @Override
     @Test
     public void testQuery_advancedRankingWithJoin() throws Exception { }
-
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java
index 1d70497..b666e4d 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GenericDocumentCtsTest.java
@@ -20,10 +20,18 @@
 
 import static org.junit.Assert.assertThrows;
 
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.flags.CheckFlagsRule;
+import androidx.appsearch.flags.DeviceFlagsValueProvider;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.flags.RequiresFlagsEnabled;
 
+import org.junit.Rule;
 import org.junit.Test;
 
+import java.util.Objects;
+
 public class GenericDocumentCtsTest {
     private static final byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
     private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
@@ -36,6 +44,9 @@
             .setCreationTimestampMillis(6789L)
             .build();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     @SuppressWarnings("deprecation")
     public void testMaxIndexedProperties() {
@@ -364,9 +375,31 @@
                 () -> builder.setPropertyString("testKey", "string1", nullString));
     }
 
-// @exportToFramework:startStrip()
+    @Test
+    public void testDocumentInvalid_setNullByteValues() {
+        GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("namespace", "id1",
+                "schemaType1");
+        byte[] nullBytes = null;
 
-    // TODO(b/171882200): Expose this test in Android T
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> builder.setPropertyBytes("propBytes", new byte[][]{{1, 2}, nullBytes}));
+    }
+
+    @Test
+    public void testDocumentInvalid_setNullDocValues() {
+        GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("namespace", "id1",
+                "schemaType1");
+        GenericDocument doc = new GenericDocument.Builder<>("namespace",
+                "id2",
+                "schemaType2").build();
+        GenericDocument nullDoc = null;
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> builder.setPropertyDocument("propDocs", doc, nullDoc));
+    }
+
     @Test
     public void testDocument_toBuilder() {
         GenericDocument document1 = new GenericDocument.Builder<>(
@@ -381,12 +414,13 @@
                 .build();
         GenericDocument document2 =
                 new GenericDocument.Builder<>(document1)
-                .setId("id2")
-                .setNamespace("namespace2")
-                .setPropertyBytes("byteKey1", sByteArray2)
-                .setPropertyLong("longKey2", 10L)
-                .clearProperty("booleanKey1")
-                .build();
+                        .setId("id2")
+                        .setNamespace("namespace2")
+                        .setSchemaType("schemaType2")
+                        .setPropertyBytes("byteKey1", sByteArray2)
+                        .setPropertyLong("longKey2", 10L)
+                        .clearProperty("booleanKey1")
+                        .build();
 
         // Make sure old doc hasn't changed
         assertThat(document1.getId()).isEqualTo("id1");
@@ -399,7 +433,7 @@
 
         // Make sure the new doc contains the expected values
         GenericDocument expectedDoc = new GenericDocument.Builder<>(
-                "namespace2", "id2", "schemaType1")
+                "namespace2", "id2", "schemaType2")
                 .setCreationTimestampMillis(5L)
                 .setPropertyLong("longKey1", 1L, 2L, 3L)
                 .setPropertyLong("longKey2", 10L)
@@ -411,7 +445,51 @@
         assertThat(document2).isEqualTo(expectedDoc);
     }
 
-// @exportToFramework:endStrip()
+    @Test
+    public void testDocument_toBuilder_doesNotModifyOriginal() {
+        GenericDocument oldDoc = new GenericDocument.Builder<>("namespace", "id1", "schema1")
+                .setScore(42)
+                .setPropertyString("propString", "Hello")
+                .setPropertyBytes("propBytes", new byte[][]{{1, 2}})
+                .setPropertyDocument(
+                        "propDocument",
+                        new GenericDocument.Builder<>("namespace", "id2", "schema2")
+                                .setPropertyString("propString", "Goodbye")
+                                .setPropertyBytes("propBytes", new byte[][]{{3, 4}})
+                                .build())
+                .build();
+
+        GenericDocument newDoc = new GenericDocument.Builder<>(oldDoc)
+                .setPropertyBytes("propBytes", new byte[][]{{1, 2}})
+                .setPropertyDocument(
+                        "propDocument",
+                        new GenericDocument.Builder<>("namespace", "id3", "schema3")
+                                .setPropertyString("propString", "Bye")
+                                .setPropertyBytes("propBytes", new byte[][]{{5, 6}})
+                                .build())
+                .build();
+
+        // Check that the original GenericDocument is unmodified.
+        assertThat(oldDoc.getScore()).isEqualTo(42);
+        assertThat(oldDoc.getPropertyString("propString")).isEqualTo("Hello");
+        assertThat(oldDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][]{{1, 2}});
+        assertThat(oldDoc.getPropertyDocument("propDocument").getPropertyString("propString"))
+                .isEqualTo("Goodbye");
+        assertThat(oldDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
+                .isEqualTo(new byte[][]{{3, 4}});
+
+        // Check that the new GenericDocument has modified the original fields correctly.
+        assertThat(newDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][]{{1, 2}});
+        assertThat(newDoc.getPropertyDocument("propDocument").getPropertyString("propString"))
+                .isEqualTo("Bye");
+        assertThat(newDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
+                .isEqualTo(new byte[][]{{5, 6}});
+
+        // Check that the new GenericDocument copies fields that aren't set.
+        assertThat(oldDoc.getScore()).isEqualTo(newDoc.getScore());
+        assertThat(oldDoc.getPropertyString("propString")).isEqualTo(newDoc.getPropertyString(
+                "propString"));
+    }
 
     @Test
     public void testRetrieveTopLevelProperties() {
@@ -886,4 +964,259 @@
         assertThat(documents[1].getPropertyNames()).containsExactly("propString", "propInts",
                 "propIntsTwo");
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocumentEquals_identicalWithEmbeddingValues() {
+        EmbeddingVector embedding1 = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+        EmbeddingVector embedding2 = new EmbeddingVector(
+                new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
+
+        GenericDocument document1 = new GenericDocument.Builder<>("namespace", "id1",
+                "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setTtlMillis(1L)
+                .setPropertyLong("longKey1", 1L, 2L, 3L)
+                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                .setPropertyBoolean("booleanKey1", true, false, true)
+                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
+                .setPropertyEmbedding("embeddingKey1", embedding1, embedding2)
+                .build();
+        GenericDocument document2 = new GenericDocument.Builder<>("namespace", "id1",
+                "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setTtlMillis(1L)
+                .setPropertyLong("longKey1", 1L, 2L, 3L)
+                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                .setPropertyBoolean("booleanKey1", true, false, true)
+                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
+                .setPropertyEmbedding("embeddingKey1", embedding1, embedding2)
+                .build();
+        assertThat(document1).isEqualTo(document2);
+        assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocumentEquals_differentOrderWithEmbeddingValues() {
+        EmbeddingVector embedding1 = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+        EmbeddingVector embedding2 = new EmbeddingVector(
+                new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
+
+        GenericDocument document1 = new GenericDocument.Builder<>("namespace", "id1",
+                "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setPropertyLong("longKey1", 1L, 2L, 3L)
+                .setPropertyEmbedding("embeddingKey1", embedding1, embedding2)
+                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                .setPropertyBoolean("booleanKey1", true, false, true)
+                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
+                .build();
+
+        // Create second document with same parameter but different order.
+        GenericDocument document2 = new GenericDocument.Builder<>("namespace", "id1",
+                "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setPropertyBoolean("booleanKey1", true, false, true)
+                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
+                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                .setPropertyLong("longKey1", 1L, 2L, 3L)
+                .setPropertyEmbedding("embeddingKey1", embedding1, embedding2)
+                .build();
+        assertThat(document1).isEqualTo(document2);
+        assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocumentGetEmbeddingValue() {
+        EmbeddingVector embedding = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+
+        GenericDocument document = new GenericDocument.Builder<>("namespace", "id1", "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setScore(1)
+                .setTtlMillis(1L)
+                .setPropertyLong("longKey1", 1L)
+                .setPropertyDouble("doubleKey1", 1.0)
+                .setPropertyBoolean("booleanKey1", true)
+                .setPropertyString("stringKey1", "test-value1")
+                .setPropertyEmbedding("embeddingKey1", embedding)
+                .build();
+        assertThat(document.getId()).isEqualTo("id1");
+        assertThat(document.getTtlMillis()).isEqualTo(1L);
+        assertThat(document.getSchemaType()).isEqualTo("schemaType1");
+        assertThat(document.getCreationTimestampMillis()).isEqualTo(5);
+        assertThat(document.getScore()).isEqualTo(1);
+        assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L);
+        assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(1.0);
+        assertThat(document.getPropertyBoolean("booleanKey1")).isTrue();
+        assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
+        assertThat(Objects.requireNonNull(document.getPropertyEmbedding(
+                "embeddingKey1")).getValues()).usingExactEquality()
+                .containsExactly(1.1f, 2.2f, 3.3f).inOrder();
+        assertThat(Objects.requireNonNull(
+                document.getPropertyEmbedding("embeddingKey1")).getModelSignature()).isEqualTo(
+                "my_model_v1");
+
+        assertThat(document.getProperty("longKey1")).isInstanceOf(long[].class);
+        assertThat((long[]) document.getProperty("longKey1")).asList().containsExactly(1L);
+        assertThat(document.getProperty("doubleKey1")).isInstanceOf(double[].class);
+        assertThat((double[]) document.getProperty("doubleKey1")).usingTolerance(
+                0.05).containsExactly(1.0);
+        assertThat(document.getProperty("booleanKey1")).isInstanceOf(boolean[].class);
+        assertThat((boolean[]) document.getProperty("booleanKey1")).asList().containsExactly(true);
+        assertThat(document.getProperty("stringKey1")).isInstanceOf(String[].class);
+        assertThat((String[]) document.getProperty("stringKey1")).asList().containsExactly(
+                "test-value1");
+        assertThat((EmbeddingVector[]) document.getProperty(
+                "embeddingKey1")).asList().containsExactly(embedding).inOrder();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocumentGetArrayEmbeddingValues() {
+        EmbeddingVector embedding1 = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+        EmbeddingVector embedding2 = new EmbeddingVector(
+                new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
+
+        GenericDocument document = new GenericDocument.Builder<>("namespace", "id1", "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setPropertyLong("longKey1", 1L, 2L, 3L)
+                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                .setPropertyBoolean("booleanKey1", true, false, true)
+                .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
+                .setPropertyEmbedding("embeddingKey1", embedding1, embedding2)
+                .build();
+
+        assertThat(document.getId()).isEqualTo("id1");
+        assertThat(document.getSchemaType()).isEqualTo("schemaType1");
+        assertThat(document.getPropertyLongArray("longKey1")).asList()
+                .containsExactly(1L, 2L, 3L).inOrder();
+        assertThat(document.getPropertyDoubleArray("doubleKey1")).usingExactEquality()
+                .containsExactly(1.0, 2.0, 3.0).inOrder();
+        assertThat(document.getPropertyBooleanArray("booleanKey1")).asList()
+                .containsExactly(true, false, true).inOrder();
+        assertThat(document.getPropertyStringArray("stringKey1")).asList()
+                .containsExactly("test-value1", "test-value2", "test-value3").inOrder();
+        assertThat(document.getPropertyEmbeddingArray("embeddingKey1")).asList()
+                .containsExactly(embedding1, embedding2).inOrder();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocument_setEmptyEmbeddingValues() {
+        GenericDocument document = new GenericDocument.Builder<>("namespace", "id1", "schemaType1")
+                .setPropertyBoolean("booleanKey")
+                .setPropertyString("stringKey")
+                .setPropertyBytes("byteKey")
+                .setPropertyDouble("doubleKey")
+                .setPropertyDocument("documentKey")
+                .setPropertyLong("longKey")
+                .setPropertyEmbedding("embeddingKey")
+                .build();
+        assertThat(document.getPropertyBooleanArray("booleanKey")).isEmpty();
+        assertThat(document.getPropertyStringArray("stringKey")).isEmpty();
+        assertThat(document.getPropertyBytesArray("byteKey")).isEmpty();
+        assertThat(document.getPropertyDoubleArray("doubleKey")).isEmpty();
+        assertThat(document.getPropertyDocumentArray("documentKey")).isEmpty();
+        assertThat(document.getPropertyLongArray("longKey")).isEmpty();
+        assertThat(document.getPropertyEmbeddingArray("embeddingKey")).isEmpty();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocumentInvalid_setNullEmbeddingValues() {
+        EmbeddingVector embedding = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+
+        GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("namespace", "id1",
+                "schemaType1");
+        EmbeddingVector nullEmbedding = null;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> builder.setPropertyEmbedding("propEmbeddings",
+                        new EmbeddingVector[]{embedding, nullEmbedding}));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocument_toBuilderWithEmbeddingValues() {
+        EmbeddingVector embedding1 = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+        EmbeddingVector embedding2 = new EmbeddingVector(
+                new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
+
+        GenericDocument document1 = new GenericDocument.Builder<>(
+                /*namespace=*/"", "id1", "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setPropertyLong("longKey1", 1L, 2L, 3L)
+                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                .setPropertyBoolean("booleanKey1", true, false, true)
+                .setPropertyString("stringKey1", "String1", "String2", "String3")
+                .setPropertyEmbedding("embeddingKey1", embedding1, embedding2)
+                .build();
+        GenericDocument document2 =
+                new GenericDocument.Builder<>(document1)
+                        .setId("id2")
+                        .setNamespace("namespace2")
+                        .setPropertyEmbedding("embeddingKey1", embedding2)
+                        .setPropertyLong("longKey2", 10L)
+                        .clearProperty("booleanKey1")
+                        .build();
+
+        // Make sure old doc hasn't changed
+        assertThat(document1.getId()).isEqualTo("id1");
+        assertThat(document1.getNamespace()).isEqualTo("");
+        assertThat(document1.getPropertyLongArray("longKey1")).asList()
+                .containsExactly(1L, 2L, 3L).inOrder();
+        assertThat(document1.getPropertyBooleanArray("booleanKey1")).asList()
+                .containsExactly(true, false, true).inOrder();
+        assertThat(document1.getPropertyLongArray("longKey2")).isNull();
+        assertThat(document1.getPropertyEmbeddingArray("embeddingKey1")).asList()
+                .containsExactly(embedding1, embedding2).inOrder();
+
+        // Make sure the new doc contains the expected values
+        GenericDocument expectedDoc = new GenericDocument.Builder<>(
+                "namespace2", "id2", "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setPropertyLong("longKey1", 1L, 2L, 3L)
+                .setPropertyLong("longKey2", 10L)
+                .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
+                .setPropertyString("stringKey1", "String1", "String2", "String3")
+                .setPropertyEmbedding("embeddingKey1", embedding2)
+                .build();
+        assertThat(document2).isEqualTo(expectedDoc);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testDocumentGetPropertyNamesWithEmbeddingValue() {
+        EmbeddingVector embedding = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+
+        GenericDocument document = new GenericDocument.Builder<>("namespace", "id1", "schemaType1")
+                .setCreationTimestampMillis(5L)
+                .setScore(1)
+                .setTtlMillis(1L)
+                .setPropertyLong("longKey1", 1L)
+                .setPropertyDouble("doubleKey1", 1.0)
+                .setPropertyBoolean("booleanKey1", true)
+                .setPropertyString("stringKey1", "test-value1")
+                .setPropertyEmbedding("embeddingKey1", embedding)
+                .build();
+        assertThat(document.getPropertyNames()).containsExactly("longKey1", "doubleKey1",
+                "booleanKey1", "stringKey1", "embeddingKey1");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingValuesCannotBeEmpty() {
+        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+                () -> new EmbeddingVector(new float[]{}, "my_model"));
+        assertThat(exception).hasMessageThat().contains("Embedding values cannot be empty.");
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GetSchemaResponseCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GetSchemaResponseCtsTest.java
index 405db5a..4d5a4f9 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GetSchemaResponseCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GetSchemaResponseCtsTest.java
@@ -23,6 +23,7 @@
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.GetSchemaResponse;
 import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
 import androidx.appsearch.app.SetSchemaRequest;
 
 import com.google.common.collect.ImmutableSet;
@@ -30,6 +31,7 @@
 import org.junit.Test;
 
 import java.util.Arrays;
+import java.util.Map;
 
 public class GetSchemaResponseCtsTest {
     @Test
@@ -76,10 +78,10 @@
                         ImmutableSet.of(packageIdentifier2))
                 .setRequiredPermissionsForSchemaTypeVisibility("Email2",
                         ImmutableSet.of(
-                                        ImmutableSet.of(SetSchemaRequest.READ_CONTACTS,
-                                                SetSchemaRequest.READ_EXTERNAL_STORAGE),
-                                        ImmutableSet.of(SetSchemaRequest
-                                                .READ_ASSISTANT_APP_SEARCH_DATA))
+                                ImmutableSet.of(SetSchemaRequest.READ_CONTACTS,
+                                        SetSchemaRequest.READ_EXTERNAL_STORAGE),
+                                ImmutableSet.of(SetSchemaRequest
+                                        .READ_ASSISTANT_APP_SEARCH_DATA))
                 ).build();
 
         // rebuild won't effect the original object
@@ -156,6 +158,30 @@
                                 .READ_ASSISTANT_APP_SEARCH_DATA)));
     }
 
+
+    @Test
+    public void setVisibilityConfig() {
+        SchemaVisibilityConfig visibilityConfig1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("pkg1", new byte[32]))
+                .setPubliclyVisibleTargetPackage(new PackageIdentifier("pkg2", new byte[32]))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+        SchemaVisibilityConfig visibilityConfig2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("pkg3", new byte[32]))
+                .setPubliclyVisibleTargetPackage(new PackageIdentifier("pkg4", new byte[32]))
+                .addRequiredPermissions(ImmutableSet.of(3, 4))
+                .build();
+
+        GetSchemaResponse getSchemaResponse =
+                new GetSchemaResponse.Builder().setVersion(42)
+                        .setSchemaTypeVisibleToConfigs("Email",
+                                ImmutableSet.of(visibilityConfig1, visibilityConfig2))
+                        .build();
+
+        assertThat(getSchemaResponse.getSchemaTypesVisibleToConfigs()).containsExactly("Email",
+                ImmutableSet.of(visibilityConfig1, visibilityConfig2));
+    }
+
     @Test
     public void getEmptyVisibility() {
         GetSchemaResponse getSchemaResponse =
@@ -166,11 +192,19 @@
         assertThat(getSchemaResponse.getRequiredPermissionsForSchemaTypeVisibility()).isEmpty();
     }
 
+    @Test
+    public void getEmptyVisibility_visibilityConfig() {
+        GetSchemaResponse getSchemaResponse =
+                new GetSchemaResponse.Builder().setVersion(42)
+                        .build();
+        assertThat(getSchemaResponse.getSchemaTypesVisibleToConfigs()).isEmpty();
+    }
+
     // @exportToFramework:startStrip()
     // Not exported as setVisibilitySettingSupported is hidden in framework
-     /**
+    /**
      * Makes sure an exception is thrown when visibility getters are called after visibility is set
-      * to no supported.
+     * to no supported.
      */
     @Test
     public void setVisibility_setFalse() {
@@ -209,6 +243,14 @@
                 getSchemaResponse::getRequiredPermissionsForSchemaTypeVisibility);
         assertThat(e.getMessage()).isEqualTo("Get visibility setting is not supported with"
                 + " this backend/Android API level combination.");
+        e = assertThrows(UnsupportedOperationException.class,
+                getSchemaResponse::getPubliclyVisibleSchemas);
+        assertThat(e.getMessage()).isEqualTo("Get visibility setting is not supported with"
+                + " this backend/Android API level combination.");
+        e = assertThrows(UnsupportedOperationException.class,
+                getSchemaResponse::getSchemaTypesVisibleToConfigs);
+        assertThat(e.getMessage()).isEqualTo("Get visibility setting is not supported with"
+                + " this backend/Android API level combination.");
     }
 
     /**
@@ -248,4 +290,56 @@
                 original::getRequiredPermissionsForSchemaTypeVisibility);
     }
     // @exportToFramework:endStrip()
+
+    @Test
+    public void testVisibility_publicVisibility() {
+        byte[] sha256cert1 = new byte[32];
+        byte[] sha256cert2 = new byte[32];
+        Arrays.fill(sha256cert1, (byte) 1);
+        Arrays.fill(sha256cert2, (byte) 1);
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("Email", sha256cert1);
+        PackageIdentifier packageIdentifier2 = new PackageIdentifier("Email", sha256cert2);
+
+        GetSchemaResponse getSchemaResponse = new GetSchemaResponse.Builder()
+                .setPubliclyVisibleSchema("Email1", packageIdentifier2)
+                .setPubliclyVisibleSchema("Email1", packageIdentifier1)
+                .build();
+        assertThat(getSchemaResponse.getPubliclyVisibleSchemas().get("Email1"))
+                .isEqualTo(packageIdentifier1);
+    }
+
+    // @exportToFramework:startStrip()
+    // Not exported as setVisibilitySettingSupported is hidden in framework
+    @Test
+    public void testVisibility_publicVisibility_clearVisibility() {
+        byte[] sha256cert1 = new byte[32];
+        Arrays.fill(sha256cert1, (byte) 1);
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("Email", sha256cert1);
+        GetSchemaResponse getSchemaResponse = new GetSchemaResponse.Builder()
+                .setPubliclyVisibleSchema("Email1", packageIdentifier1)
+                // This should clear all visibility settings.
+                .setVisibilitySettingSupported(true)
+                .build();
+
+        Map<String, PackageIdentifier> publiclyVisibleSchemas =
+                getSchemaResponse.getPubliclyVisibleSchemas();
+        assertThat(publiclyVisibleSchemas).isEmpty();
+    }
+
+    @Test
+    public void testVisibility_publicVisibility_notSupported() {
+        byte[] sha256cert1 = new byte[32];
+        Arrays.fill(sha256cert1, (byte) 1);
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("Email", sha256cert1);
+        GetSchemaResponse getSchemaResponse = new GetSchemaResponse.Builder()
+                .setPubliclyVisibleSchema("Email1", packageIdentifier1)
+                .setVisibilitySettingSupported(false)
+                .build();
+
+        Exception e = assertThrows(UnsupportedOperationException.class,
+                getSchemaResponse::getPubliclyVisibleSchemas);
+        assertThat(e.getMessage()).isEqualTo("Get visibility setting is not supported with"
+                + " this backend/Android API level combination.");
+    }
+    // @exportToFramework:endStrip()
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
index 9f38b9c..7df269c 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
@@ -751,7 +751,7 @@
                 snapshotResults("body", new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                         .setResultGrouping(
-                                SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+                                SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*limit=*/ 1)
                         .build());
         assertThat(documents).containsExactly(inEmail4);
 
@@ -761,7 +761,7 @@
                 snapshotResults("body", new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                         .setResultGrouping(
-                                SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 1)
+                                SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*limit=*/ 1)
                         .build());
         assertThat(documents).containsExactly(inEmail4, inEmail3);
 
@@ -772,7 +772,7 @@
                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                         .setResultGrouping(
                                 SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                                        | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
+                                        | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*limit=*/ 1)
                         .build());
         assertThat(documents).containsExactly(inEmail4, inEmail3);
     }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionGmsCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionGmsCtsTest.java
index 40d97ef..6e77eae 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionGmsCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionGmsCtsTest.java
@@ -18,23 +18,18 @@
 package androidx.appsearch.cts.app;
 
 import android.content.Context;
-import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchSession;
 import androidx.appsearch.app.GlobalSearchSession;
 import androidx.appsearch.playservicesstorage.PlayServicesStorage;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SdkSuppress;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
 import org.junit.Assume;
 import org.junit.Ignore;
 
-// TODO(b/237116468): Remove SdkSuppress once AppSearchAttributionSource available for lower API
-//  levels.
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
 public class GlobalSearchSessionGmsCtsTest extends GlobalSearchSessionCtsTestBase {
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private boolean mIsGmsAvailable;
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/PutDocumentsRequestCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/PutDocumentsRequestCtsTest.java
index c2b06d4..a50f7fc 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/PutDocumentsRequestCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/PutDocumentsRequestCtsTest.java
@@ -20,14 +20,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
+
 import android.content.Context;
 
 import androidx.appsearch.annotation.Document;
 import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.PutDocumentsRequest;
 import androidx.appsearch.app.SetSchemaRequest;
 import androidx.appsearch.localstorage.LocalStorage;
 import androidx.appsearch.testutil.AppSearchEmail;
+import androidx.appsearch.usagereporting.ClickAction;
+import androidx.appsearch.usagereporting.SearchAction;
+import androidx.appsearch.usagereporting.TakenAction;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.google.common.collect.ImmutableSet;
@@ -50,7 +56,99 @@
         assertThat(request.getGenericDocuments().get(1).getId()).isEqualTo("test2");
     }
 
-// @exportToFramework:startStrip()
+    @Test
+    public void duplicateIdForNormalAndTakenActionGenericDocumentThrowsException()
+            throws Exception {
+        GenericDocument normalDocument = new GenericDocument.Builder<>(
+                "namespace", "id", "builtin:Thing").build();
+        GenericDocument takenActionGenericDocument = new GenericDocument.Builder<>(
+                "namespace", "id", "builtin:ClickAction").build();
+
+        PutDocumentsRequest.Builder builder = new PutDocumentsRequest.Builder()
+                .addGenericDocuments(normalDocument)
+                .addTakenActionGenericDocuments(takenActionGenericDocument);
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> builder.build());
+        assertThat(e.getMessage()).isEqualTo("Document id " + takenActionGenericDocument.getId()
+                + " cannot exist in both taken action and normal document");
+    }
+
+    @Test
+    public void addTakenActionGenericDocuments() throws Exception {
+        GenericDocument searchActionGenericDocument1 = new GenericDocument.Builder<>(
+                "namespace", "search1", "builtin:SearchAction").build();
+        GenericDocument clickActionGenericDocument1 = new GenericDocument.Builder<>(
+                "namespace", "click1", "builtin:ClickAction").build();
+        GenericDocument clickActionGenericDocument2 = new GenericDocument.Builder<>(
+                "namespace", "click2", "builtin:ClickAction").build();
+        GenericDocument searchActionGenericDocument2 = new GenericDocument.Builder<>(
+                "namespace", "search2", "builtin:SearchAction").build();
+        GenericDocument clickActionGenericDocument3 = new GenericDocument.Builder<>(
+                "namespace", "click3", "builtin:ClickAction").build();
+        GenericDocument clickActionGenericDocument4 = new GenericDocument.Builder<>(
+                "namespace", "click4", "builtin:ClickAction").build();
+        GenericDocument clickActionGenericDocument5 = new GenericDocument.Builder<>(
+                "namespace", "click5", "builtin:ClickAction").build();
+
+        PutDocumentsRequest request = new PutDocumentsRequest.Builder()
+                .addTakenActionGenericDocuments(
+                        searchActionGenericDocument1, clickActionGenericDocument1,
+                        clickActionGenericDocument2, searchActionGenericDocument2,
+                        clickActionGenericDocument3, clickActionGenericDocument4,
+                        clickActionGenericDocument5)
+                .build();
+
+        // Generic documents should contain nothing.
+        assertThat(request.getGenericDocuments()).isEmpty();
+
+        // Taken action generic documents should contain correct taken action generic documents.
+        assertThat(request.getTakenActionGenericDocuments()).hasSize(7);
+        assertThat(request.getTakenActionGenericDocuments().get(0).getId()).isEqualTo("search1");
+        assertThat(request.getTakenActionGenericDocuments().get(1).getId()).isEqualTo("click1");
+        assertThat(request.getTakenActionGenericDocuments().get(2).getId()).isEqualTo("click2");
+        assertThat(request.getTakenActionGenericDocuments().get(3).getId()).isEqualTo("search2");
+        assertThat(request.getTakenActionGenericDocuments().get(4).getId()).isEqualTo("click3");
+        assertThat(request.getTakenActionGenericDocuments().get(5).getId()).isEqualTo("click4");
+        assertThat(request.getTakenActionGenericDocuments().get(6).getId()).isEqualTo("click5");
+    }
+
+    @Test
+    public void addTakenActionGenericDocuments_byCollection() throws Exception {
+        Set<GenericDocument> takenActionGenericDocuments = ImmutableSet.of(
+                new GenericDocument.Builder<>(
+                        "namespace", "search1", "builtin:SearchAction").build(),
+                new GenericDocument.Builder<>(
+                        "namespace", "click1", "builtin:ClickAction").build(),
+                new GenericDocument.Builder<>(
+                        "namespace", "click2", "builtin:ClickAction").build(),
+                new GenericDocument.Builder<>(
+                        "namespace", "search2", "builtin:SearchAction").build(),
+                new GenericDocument.Builder<>(
+                        "namespace", "click3", "builtin:ClickAction").build(),
+                new GenericDocument.Builder<>(
+                        "namespace", "click4", "builtin:ClickAction").build(),
+                new GenericDocument.Builder<>(
+                        "namespace", "click5", "builtin:ClickAction").build());
+
+        PutDocumentsRequest request = new PutDocumentsRequest.Builder()
+                .addTakenActionGenericDocuments(takenActionGenericDocuments)
+                .build();
+
+        // Generic documents should contain nothing.
+        assertThat(request.getGenericDocuments()).isEmpty();
+
+        // Taken action generic documents should contain correct taken action generic documents.
+        assertThat(request.getTakenActionGenericDocuments()).hasSize(7);
+        assertThat(request.getTakenActionGenericDocuments().get(0).getId()).isEqualTo("search1");
+        assertThat(request.getTakenActionGenericDocuments().get(1).getId()).isEqualTo("click1");
+        assertThat(request.getTakenActionGenericDocuments().get(2).getId()).isEqualTo("click2");
+        assertThat(request.getTakenActionGenericDocuments().get(3).getId()).isEqualTo("search2");
+        assertThat(request.getTakenActionGenericDocuments().get(4).getId()).isEqualTo("click3");
+        assertThat(request.getTakenActionGenericDocuments().get(5).getId()).isEqualTo("click4");
+        assertThat(request.getTakenActionGenericDocuments().get(6).getId()).isEqualTo("click5");
+    }
+
+    // @exportToFramework:startStrip()
     @Document
     static class Card {
         @Document.Namespace
@@ -87,5 +185,84 @@
 
         assertThat(request.getGenericDocuments().get(0).getId()).isEqualTo("cardId");
     }
+
+    @Test
+    public void addTakenActions() throws Exception {
+        SearchAction searchAction1 =
+                new SearchAction.Builder("namespace", "search1", /* actionTimestampMillis= */1000)
+                        .build();
+        ClickAction clickAction1 =
+                new ClickAction.Builder("namespace", "click1", /* actionTimestampMillis= */2000)
+                        .build();
+        ClickAction clickAction2 =
+                new ClickAction.Builder("namespace", "click2", /* actionTimestampMillis= */3000)
+                        .build();
+        SearchAction searchAction2 =
+                new SearchAction.Builder("namespace", "search2", /* actionTimestampMillis= */4000)
+                        .build();
+        ClickAction clickAction3 =
+                new ClickAction.Builder("namespace", "click3", /* actionTimestampMillis= */5000)
+                        .build();
+        ClickAction clickAction4 =
+                new ClickAction.Builder("namespace", "click4", /* actionTimestampMillis= */6000)
+                        .build();
+        ClickAction clickAction5 =
+                new ClickAction.Builder("namespace", "click5", /* actionTimestampMillis= */7000)
+                        .build();
+
+        PutDocumentsRequest request = new PutDocumentsRequest.Builder()
+                .addTakenActions(searchAction1, clickAction1, clickAction2, searchAction2,
+                        clickAction3, clickAction4, clickAction5)
+                .build();
+
+        // Generic documents should contain nothing.
+        assertThat(request.getGenericDocuments()).isEmpty();
+
+        // Taken action generic documents should contain correct taken action generic documents.
+        assertThat(request.getTakenActionGenericDocuments()).hasSize(7);
+        assertThat(request.getTakenActionGenericDocuments().get(0).getId()).isEqualTo("search1");
+        assertThat(request.getTakenActionGenericDocuments().get(1).getId()).isEqualTo("click1");
+        assertThat(request.getTakenActionGenericDocuments().get(2).getId()).isEqualTo("click2");
+        assertThat(request.getTakenActionGenericDocuments().get(3).getId()).isEqualTo("search2");
+        assertThat(request.getTakenActionGenericDocuments().get(4).getId()).isEqualTo("click3");
+        assertThat(request.getTakenActionGenericDocuments().get(5).getId()).isEqualTo("click4");
+        assertThat(request.getTakenActionGenericDocuments().get(6).getId()).isEqualTo("click5");
+    }
+
+    @Test
+    public void addTakenActions_byCollection() throws Exception {
+        Set<TakenAction> takenActions = ImmutableSet.of(
+                new SearchAction.Builder("namespace", "search1", /* actionTimestampMillis= */1000)
+                        .build(),
+                new ClickAction.Builder("namespace", "click1", /* actionTimestampMillis= */2000)
+                        .build(),
+                new ClickAction.Builder("namespace", "click2", /* actionTimestampMillis= */3000)
+                        .build(),
+                new SearchAction.Builder("namespace", "search2", /* actionTimestampMillis= */4000)
+                        .build(),
+                new ClickAction.Builder("namespace", "click3", /* actionTimestampMillis= */5000)
+                        .build(),
+                new ClickAction.Builder("namespace", "click4", /* actionTimestampMillis= */6000)
+                        .build(),
+                new ClickAction.Builder("namespace", "click5", /* actionTimestampMillis= */7000)
+                        .build());
+
+        PutDocumentsRequest request = new PutDocumentsRequest.Builder()
+                .addTakenActions(takenActions)
+                .build();
+
+        // Generic documents should contain nothing.
+        assertThat(request.getGenericDocuments()).isEmpty();
+
+        // Taken action generic documents should contain correct taken action generic documents.
+        assertThat(request.getTakenActionGenericDocuments()).hasSize(7);
+        assertThat(request.getTakenActionGenericDocuments().get(0).getId()).isEqualTo("search1");
+        assertThat(request.getTakenActionGenericDocuments().get(1).getId()).isEqualTo("click1");
+        assertThat(request.getTakenActionGenericDocuments().get(2).getId()).isEqualTo("click2");
+        assertThat(request.getTakenActionGenericDocuments().get(3).getId()).isEqualTo("search2");
+        assertThat(request.getTakenActionGenericDocuments().get(4).getId()).isEqualTo("click3");
+        assertThat(request.getTakenActionGenericDocuments().get(5).getId()).isEqualTo("click4");
+        assertThat(request.getTakenActionGenericDocuments().get(6).getId()).isEqualTo("click5");
+    }
 // @exportToFramework:endStrip()
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SchemaVisibilityConfigCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SchemaVisibilityConfigCtsTest.java
new file mode 100644
index 0000000..6787a56f
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SchemaVisibilityConfigCtsTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class SchemaVisibilityConfigCtsTest {
+
+    @Test
+    public void testBuildVisibilityConfig() {
+        byte[] cert1 = new byte[32];
+        Arrays.fill(cert1, (byte) 1);
+        byte[] cert2 = new byte[32];
+        Arrays.fill(cert2, (byte) 2);
+        SchemaVisibilityConfig schemaVisibilityConfig = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("pkg1", cert1))
+                .setPubliclyVisibleTargetPackage(new PackageIdentifier("pkg2", cert2))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+
+        assertThat(schemaVisibilityConfig.getRequiredPermissions())
+                .containsExactly(ImmutableSet.of(1, 2));
+        assertThat(schemaVisibilityConfig.getAllowedPackages())
+                .containsExactly(new PackageIdentifier("pkg1", cert1));
+        assertThat(schemaVisibilityConfig.getPubliclyVisibleTargetPackage())
+                .isEqualTo(new PackageIdentifier("pkg2", cert2));
+    }
+
+    @Test
+    public void testVisibilityConfigEquals() {
+        // Create two VisibilityConfig instances with the same properties
+        SchemaVisibilityConfig visibilityConfig1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("pkg1", new byte[32]))
+                .setPubliclyVisibleTargetPackage(new PackageIdentifier("pkg2", new byte[32]))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+
+        SchemaVisibilityConfig visibilityConfig2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier("pkg1", new byte[32]))
+                .setPubliclyVisibleTargetPackage(new PackageIdentifier("pkg2", new byte[32]))
+                .addRequiredPermissions(ImmutableSet.of(1, 2))
+                .build();
+
+        // Test equals method
+        assertThat(visibilityConfig1).isEqualTo(visibilityConfig2);
+        assertThat(visibilityConfig2).isEqualTo(visibilityConfig1);
+    }
+
+    @Test
+    public void testVisibilityConfig_rebuild() {
+        String visibleToPackage = "com.example.package";
+        byte[] visibleToPackageCert = new byte[32];
+
+        String publiclyVisibleTarget = "com.example.test";
+        byte[] publiclyVisibleTargetCert = new byte[32];
+
+        SchemaVisibilityConfig.Builder builder = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(new PackageIdentifier(visibleToPackage, visibleToPackageCert))
+                .setPubliclyVisibleTargetPackage(new PackageIdentifier(
+                        publiclyVisibleTarget, publiclyVisibleTargetCert))
+                .addRequiredPermissions(ImmutableSet.of(1, 2));
+
+        // Create a VisibilityConfig using the Builder
+        SchemaVisibilityConfig original = builder.build();
+
+        SchemaVisibilityConfig rebuild = builder.clearAllowedPackages()
+                .setPubliclyVisibleTargetPackage(null)
+                .clearRequiredPermissions().build();
+
+        // Check if the properties are set correctly
+        assertThat(original.getAllowedPackages()).containsExactly(
+                new PackageIdentifier(visibleToPackage, visibleToPackageCert));
+        assertThat(original.getPubliclyVisibleTargetPackage()).isEqualTo(
+                new PackageIdentifier(publiclyVisibleTarget, publiclyVisibleTargetCert));
+        assertThat(original.getRequiredPermissions()).containsExactly(ImmutableSet.of(1, 2));
+
+        assertThat(rebuild.getAllowedPackages()).isEmpty();
+        assertThat(rebuild.getPubliclyVisibleTargetPackage()).isNull();
+        assertThat(rebuild.getRequiredPermissions()).isEmpty();
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchResultCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchResultCtsTest.java
index 426d01a..382d5272 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchResultCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchResultCtsTest.java
@@ -22,11 +22,18 @@
 
 import androidx.appsearch.app.PropertyPath;
 import androidx.appsearch.app.SearchResult;
+import androidx.appsearch.flags.CheckFlagsRule;
+import androidx.appsearch.flags.DeviceFlagsValueProvider;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.flags.RequiresFlagsEnabled;
 import androidx.appsearch.testutil.AppSearchEmail;
 
+import org.junit.Rule;
 import org.junit.Test;
 
 public class SearchResultCtsTest {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Test
     public void testBuildSearchResult() {
@@ -172,4 +179,48 @@
         SearchResult rebuildJoinedResult2 = rebuild.getJoinedResults().get(1);
         assertThat(rebuildJoinedResult2.getGenericDocument().getId()).isEqualTo("id3");
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+    public void testBuildSearchResult_informationalRankingSignals() {
+        AppSearchEmail email = new AppSearchEmail.Builder("namespace1", "id1")
+                .setBody("Hello World.")
+                .build();
+        SearchResult searchResult = new SearchResult.Builder("packageName", "databaseName")
+                .setGenericDocument(email)
+                .setRankingSignal(2.9)
+                .addInformationalRankingSignal(3.0)
+                .addInformationalRankingSignal(4.0)
+                .build();
+
+        assertThat(searchResult.getRankingSignal()).isEqualTo(2.9);
+        assertThat(searchResult.getInformationalRankingSignals())
+                .containsExactly(3.0, 4.0).inOrder();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+    public void testRebuild_informationalRankingSignals() {
+        AppSearchEmail email = new AppSearchEmail.Builder("namespace1", "id1")
+                .setBody("Hello World.")
+                .build();
+
+        SearchResult.Builder searchResultBuilder =
+                new SearchResult.Builder("packageName", "databaseName")
+                        .setGenericDocument(email)
+                        .setRankingSignal(2.9)
+                        .addInformationalRankingSignal(3.0)
+                        .addInformationalRankingSignal(4.0);
+
+        SearchResult original = searchResultBuilder.build();
+        SearchResult rebuild = searchResultBuilder.addInformationalRankingSignal(5).build();
+
+        // Rebuild won't effect the original object
+        assertThat(original.getRankingSignal()).isEqualTo(2.9);
+        assertThat(original.getInformationalRankingSignals()).containsExactly(3.0, 4.0).inOrder();
+
+        assertThat(rebuild.getRankingSignal()).isEqualTo(2.9);
+        assertThat(rebuild.getInformationalRankingSignals())
+                .containsExactly(3.0, 4.0, 5.0).inOrder();
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java
index f465eaf..a6820fb 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSpecCtsTest.java
@@ -24,14 +24,20 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.appsearch.annotation.Document;
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.JoinSpec;
 import androidx.appsearch.app.PropertyPath;
 import androidx.appsearch.app.SearchSpec;
+import androidx.appsearch.flags.CheckFlagsRule;
+import androidx.appsearch.flags.DeviceFlagsValueProvider;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.flags.RequiresFlagsEnabled;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Collections;
@@ -40,6 +46,9 @@
 import java.util.Set;
 
 public class SearchSpecCtsTest {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     public void testBuildSearchSpecWithoutTermMatch() {
         SearchSpec searchSpec = new SearchSpec.Builder().addFilterSchemas("testSchemaType").build();
@@ -118,6 +127,52 @@
     }
 
     @Test
+    public void testBuildSearchSpec_searchSourceLogTag() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                .setSearchSourceLogTag("logTag")
+                .build();
+
+        assertThat(searchSpec.getSearchSourceLogTag()).isEqualTo("logTag");
+    }
+
+    @Test
+    public void testBuildSearchSpec_searchSourceLogTag_exceedLengthLimitation() {
+        String longTag = new String(new char[110]);
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> new SearchSpec.Builder()
+                        .setSearchSourceLogTag(longTag));
+        assertThat(e).hasMessageThat().contains(
+                "The maximum supported tag length is 100. This tag is too long");
+    }
+
+    @Test
+    public void testBuildSearchSpec_searchSourceLogTag_defaultIsNull() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                .build();
+
+        assertThat(searchSpec.getSearchSourceLogTag()).isNull();
+    }
+
+    // TODO(b/309826655): Flag guard this test.
+    @Test
+    public void testBuildSearchSpec_hasProperty() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setNumericSearchEnabled(true)
+                .setVerbatimSearchEnabled(true)
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterHasPropertyFunctionEnabled(true)
+                .build();
+
+        assertThat(searchSpec.isNumericSearchEnabled()).isTrue();
+        assertThat(searchSpec.isVerbatimSearchEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterHasPropertyFunctionEnabled()).isTrue();
+    }
+
+    @Test
     public void testGetProjectionTypePropertyMasks() {
         SearchSpec searchSpec = new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
@@ -404,6 +459,24 @@
         assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isFalse();
     }
 
+    // TODO(b/309826655): Flag guard this test.
+    @Test
+    public void testSetFeatureEnabledToFalse_hasProperty() {
+        SearchSpec.Builder builder = new SearchSpec.Builder();
+        SearchSpec searchSpec = builder
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterHasPropertyFunctionEnabled(true)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterHasPropertyFunctionEnabled()).isTrue();
+
+        searchSpec = builder
+                .setListFilterQueryLanguageEnabled(false)
+                .setListFilterHasPropertyFunctionEnabled(false)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isFalse();
+        assertThat(searchSpec.isListFilterHasPropertyFunctionEnabled()).isFalse();
+    }
 
     @Test
     public void testInvalidAdvancedRanking() {
@@ -463,20 +536,6 @@
     }
 
     @Test
-    public void testProjections_withSchemaFilter() throws Exception {
-        SearchSpec.Builder searchSpecBuilder = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                .addFilterSchemas("Filter")
-                .addProjectionPathsForDocumentClass(King.class, ImmutableList.of(
-                        new PropertyPath("field1"), new PropertyPath("field2.subfield2")));
-
-        IllegalArgumentException exception =
-                assertThrows(IllegalArgumentException.class, searchSpecBuilder::build);
-        assertThat(exception.getMessage())
-                .isEqualTo("Projection requested for schema not in schemas filters: King");
-    }
-
-    @Test
     public void testTypePropertyWeightsForDocumentClass() throws Exception {
         SearchSpec searchSpec = new SearchSpec.Builder()
                 .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
@@ -553,6 +612,40 @@
     }
 
     @Test
+    public void testGetPropertyFiltersTypePropertyMasks() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                .addFilterProperties("TypeA", ImmutableList.of("field1", "field2.subfield2"))
+                .addFilterProperties("TypeB", ImmutableList.of("field7"))
+                .addFilterProperties("TypeC", ImmutableList.of())
+                .build();
+
+        Map<String, List<String>> typePropertyPathMap = searchSpec.getFilterProperties();
+        assertThat(typePropertyPathMap.keySet())
+                .containsExactly("TypeA", "TypeB", "TypeC");
+        assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
+        assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
+        assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
+    }
+
+    @Test
+    public void testFilterSchemas_wildcardProjection() {
+        // Should not crash
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .addFilterSchemas("ParentType")
+                .addProjection(SearchSpec.SCHEMA_TYPE_WILDCARD, Collections.singletonList("TypeA"))
+                .addFilterProperties(SearchSpec.SCHEMA_TYPE_WILDCARD,
+                        Collections.singletonList("TypeB"))
+                .build();
+
+        assertThat(searchSpec.getFilterSchemas()).containsExactly("ParentType");
+        assertThat(searchSpec.getProjections())
+                .containsExactly(SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("TypeA"));
+        assertThat(searchSpec.getFilterProperties())
+                .containsExactly(SearchSpec.SCHEMA_TYPE_WILDCARD, ImmutableList.of("TypeB"));
+    }
+
+    @Test
     public void testRebuild() {
         JoinSpec originalJoinSpec = new JoinSpec.Builder("entityId")
                 .setNestedSearch("joe", new SearchSpec.Builder().addFilterSchemas("Action").build())
@@ -583,4 +676,163 @@
         assertThat(rebuild.getJoinSpec().getNestedSearchSpec().getFilterSchemas())
                 .containsExactly("CallAction");
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testEmbeddingSearch() {
+        EmbeddingVector embedding1 = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+        EmbeddingVector embedding2 = new EmbeddingVector(
+                new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(embedding1, embedding2)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isEmbeddingSearchEnabled()).isTrue();
+        assertThat(searchSpec.getDefaultEmbeddingSearchMetricType()).isEqualTo(
+                SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT);
+        assertThat(searchSpec.getSearchEmbeddings()).containsExactly(embedding1, embedding2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testRebuild_embeddingSearch() {
+        EmbeddingVector embedding1 = new EmbeddingVector(
+                new float[]{1.1f, 2.2f, 3.3f}, "my_model_v1");
+        EmbeddingVector embedding2 = new EmbeddingVector(
+                new float[]{4.4f, 5.5f, 6.6f, 7.7f}, "my_model_v2");
+
+        // Create a builder
+        SearchSpec.Builder searchSpecBuilder = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .setDefaultEmbeddingSearchMetricType(
+                        SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT)
+                .addSearchEmbeddings(embedding1);
+        SearchSpec searchSpec1 = searchSpecBuilder.build();
+
+        // Add a new embedding to the builder and rebuild. We should see that the new embedding
+        // is only added to searchSpec2.
+        searchSpecBuilder.addSearchEmbeddings(embedding2);
+        SearchSpec searchSpec2 = searchSpecBuilder.build();
+
+        assertThat(searchSpec1.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec1.isEmbeddingSearchEnabled()).isTrue();
+        assertThat(searchSpec1.getDefaultEmbeddingSearchMetricType()).isEqualTo(
+                SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT);
+        assertThat(searchSpec1.getSearchEmbeddings()).containsExactly(embedding1);
+
+        assertThat(searchSpec2.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec2.isEmbeddingSearchEnabled()).isTrue();
+        assertThat(searchSpec2.getDefaultEmbeddingSearchMetricType()).isEqualTo(
+                SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT);
+        assertThat(searchSpec2.getSearchEmbeddings()).containsExactly(embedding1, embedding2);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testBuildSearchSpec_embeddingSearch() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setNumericSearchEnabled(true)
+                .setVerbatimSearchEnabled(true)
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterHasPropertyFunctionEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+
+        assertThat(searchSpec.isNumericSearchEnabled()).isTrue();
+        assertThat(searchSpec.isVerbatimSearchEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterHasPropertyFunctionEnabled()).isTrue();
+        assertThat(searchSpec.isEmbeddingSearchEnabled()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public void testSetFeatureEnabledToFalse_embeddingSearch() {
+        SearchSpec.Builder builder = new SearchSpec.Builder();
+        SearchSpec searchSpec = builder
+                .setListFilterQueryLanguageEnabled(true)
+                .setEmbeddingSearchEnabled(true)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isEmbeddingSearchEnabled()).isTrue();
+
+        searchSpec = builder
+                .setListFilterQueryLanguageEnabled(false)
+                .setEmbeddingSearchEnabled(false)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isFalse();
+        assertThat(searchSpec.isEmbeddingSearchEnabled()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION)
+    public void testListFilterTokenizeFunction() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterTokenizeFunctionEnabled(true)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterTokenizeFunctionEnabled()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION)
+    public void testSetFeatureEnabledToFalse_tokenizeFunction() {
+        SearchSpec.Builder builder = new SearchSpec.Builder();
+        SearchSpec searchSpec = builder
+                .setListFilterQueryLanguageEnabled(true)
+                .setListFilterTokenizeFunctionEnabled(true)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterTokenizeFunctionEnabled()).isTrue();
+
+        searchSpec = builder
+                .setListFilterTokenizeFunctionEnabled(false)
+                .build();
+        assertThat(searchSpec.isListFilterQueryLanguageEnabled()).isTrue();
+        assertThat(searchSpec.isListFilterTokenizeFunctionEnabled()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+    public void testInformationalRankingExpressions() {
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setOrder(SearchSpec.ORDER_ASCENDING)
+                .setRankingStrategy("this.documentScore()")
+                .addInformationalRankingExpressions("this.relevanceScore()")
+                .build();
+        assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
+        assertThat(searchSpec.getRankingStrategy())
+                .isEqualTo(SearchSpec.RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION);
+        assertThat(searchSpec.getAdvancedRankingExpression())
+                .isEqualTo("this.documentScore()");
+        assertThat(searchSpec.getInformationalRankingExpressions()).containsExactly(
+                "this.relevanceScore()");
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+    public void testRebuild_informationalRankingExpressions() {
+        SearchSpec.Builder searchSpecBuilder =
+                new SearchSpec.Builder().addInformationalRankingExpressions(
+                        "this.relevanceScore()");
+
+        SearchSpec original = searchSpecBuilder.build();
+        SearchSpec rebuild = searchSpecBuilder
+                .addInformationalRankingExpressions("this.documentScore()")
+                .build();
+
+        // Rebuild won't effect the original object
+        assertThat(original.getInformationalRankingExpressions())
+                .containsExactly("this.relevanceScore()");
+
+        assertThat(rebuild.getInformationalRankingExpressions())
+                .containsExactly("this.relevanceScore()", "this.documentScore()").inOrder();
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java
index 69979c3..6f1e79b 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SearchSuggestionSpecCtsTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertThrows;
 
+import androidx.appsearch.app.PropertyPath;
 import androidx.appsearch.app.SearchSuggestionSpec;
 
 import com.google.common.collect.ImmutableList;
@@ -75,6 +76,38 @@
     }
 
     @Test
+    public void testBuildSearchSuggestionSpec_withPropertyFilter() throws Exception {
+        SearchSuggestionSpec searchSuggestionSpec =
+                new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
+                        .setRankingStrategy(SearchSuggestionSpec
+                                .SUGGESTION_RANKING_STRATEGY_TERM_FREQUENCY)
+                        .addFilterSchemas("Person", "Email")
+                        .addFilterSchemas(ImmutableList.of("Foo"))
+                        .addFilterProperties("Email", ImmutableList.of("Subject", "body"))
+                        .addFilterPropertyPaths("Foo",
+                                ImmutableList.of(new PropertyPath("Bar")))
+                        .build();
+
+        assertThat(searchSuggestionSpec.getMaximumResultCount()).isEqualTo(123);
+        assertThat(searchSuggestionSpec.getFilterSchemas())
+                .containsExactly("Person", "Email", "Foo");
+        assertThat(searchSuggestionSpec.getFilterProperties())
+                .containsExactly("Email",  ImmutableList.of("Subject", "body"),
+                        "Foo",  ImmutableList.of("Bar"));
+    }
+
+    @Test
+    public void testPropertyFilterMustMatchSchemaFilter() throws Exception {
+        IllegalStateException e = assertThrows(IllegalStateException.class,
+                () -> new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
+                        .addFilterSchemas("Person")
+                        .addFilterProperties("Email", ImmutableList.of("Subject", "body"))
+                        .build());
+        assertThat(e).hasMessageThat().contains("The schema: Email exists in the "
+                + "property filter but doesn't exist in the schema filter.");
+    }
+
+    @Test
     public void testRebuild() throws Exception {
         SearchSuggestionSpec.Builder builder =
                 new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
@@ -106,4 +139,31 @@
         assertThat(rebuild.getFilterSchemas())
                 .containsExactly("Person", "Email", "Message", "Foo");
     }
+
+    @Test
+    public void testRebuild_withPropertyFilter() throws Exception {
+        SearchSuggestionSpec.Builder builder =
+                new SearchSuggestionSpec.Builder(/*totalResultCount=*/123)
+                        .addFilterSchemas("Person", "Email")
+                        .addFilterProperties("Email", ImmutableList.of("Subject", "body"));
+
+        SearchSuggestionSpec original = builder.build();
+
+        builder.addFilterSchemas("Message", "Foo")
+                .addFilterProperties("Foo", ImmutableList.of("Bar"));
+        SearchSuggestionSpec rebuild = builder.build();
+
+        assertThat(original.getMaximumResultCount()).isEqualTo(123);
+        assertThat(original.getFilterSchemas())
+                .containsExactly("Person", "Email");
+        assertThat(original.getFilterProperties())
+                .containsExactly("Email",  ImmutableList.of("Subject", "body"));
+
+        assertThat(rebuild.getMaximumResultCount()).isEqualTo(123);
+        assertThat(rebuild.getFilterSchemas())
+                .containsExactly("Person", "Email", "Message", "Foo");
+        assertThat(rebuild.getFilterProperties())
+                .containsExactly("Email",  ImmutableList.of("Subject", "body"),
+                        "Foo",  ImmutableList.of("Bar"));
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java
index d7de450..1876b87 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/SetSchemaRequestCtsTest.java
@@ -30,6 +30,7 @@
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.app.Migrator;
 import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.app.SchemaVisibilityConfig;
 import androidx.appsearch.app.SetSchemaRequest;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.testutil.AppSearchEmail;
@@ -201,6 +202,32 @@
     }
 
     @Test
+    public void testInvalidSchemaReferences_fromPubliclyVisible() {
+        IllegalArgumentException expected = assertThrows(IllegalArgumentException.class,
+                () -> new SetSchemaRequest.Builder().setPubliclyVisibleSchema("InvalidSchema",
+                        new PackageIdentifier("com.foo.package",
+                                /*sha256Certificate=*/ new byte[]{})).build());
+        assertThat(expected).hasMessageThat().contains("referenced, but were not added");
+    }
+
+    @Test
+    public void testInvalidSchemaReferences_fromVisibleToConfigs() {
+        byte[] sha256cert1 = new byte[32];
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("Email", sha256cert1);
+        SchemaVisibilityConfig config = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier1)
+                .addRequiredPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+                .build();
+
+        IllegalArgumentException expected = assertThrows(IllegalArgumentException.class,
+                () -> new SetSchemaRequest.Builder()
+                        .addSchemaTypeVisibleToConfig("InvalidSchema", config)
+                        .build());
+        assertThat(expected).hasMessageThat().contains("referenced, but were not added");
+    }
+
+    @Test
     public void testSetSchemaTypeDisplayedBySystem_displayed() {
         AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
 
@@ -276,7 +303,7 @@
                         "Schema2", ImmutableSet.of(
                                 ImmutableSet.of(SetSchemaRequest.READ_EXTERNAL_STORAGE)
                         )
-            );
+                );
 
         // Clear the permissions in the builder
         setSchemaRequestBuilder.clearRequiredPermissionsForSchemaTypeVisibility("Schema1");
@@ -287,7 +314,7 @@
                         "Schema2", ImmutableSet.of(
                                 ImmutableSet.of(SetSchemaRequest.READ_EXTERNAL_STORAGE)
                         )
-            );
+                );
 
         // Old object should remain unchanged
         assertThat(request.getRequiredPermissionsForSchemaTypeVisibility())
@@ -300,7 +327,7 @@
                         "Schema2", ImmutableSet.of(
                                 ImmutableSet.of(SetSchemaRequest.READ_EXTERNAL_STORAGE)
                         )
-            );
+                );
     }
 
     @Test
@@ -375,6 +402,132 @@
         assertThat(request.getSchemasVisibleToPackages()).isEmpty();
     }
 
+    @Test
+    public void testPubliclyVisibleSchemaType() {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        PackageIdentifier packageIdentifier =
+                new PackageIdentifier("com.package.foo", /*sha256Certificate=*/ new byte[]{});
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addSchemas(schema).setPubliclyVisibleSchema(
+                        "Schema", packageIdentifier).build();
+        assertThat(request.getPubliclyVisibleSchemas())
+                .containsExactly("Schema", packageIdentifier);
+    }
+
+    @Test
+    public void testPubliclyVisibleSchemaType_removal() {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        PackageIdentifier packageIdentifier =
+                new PackageIdentifier("com.package.foo", /*sha256Certificate=*/ new byte[]{});
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addSchemas(schema).setPubliclyVisibleSchema(
+                        "Schema", packageIdentifier).build();
+        assertThat(request.getPubliclyVisibleSchemas())
+                .containsExactly("Schema", packageIdentifier);
+
+        // Removed Schema
+        request = new SetSchemaRequest.Builder().addSchemas(schema)
+                .setPubliclyVisibleSchema("Schema", packageIdentifier)
+                .setPubliclyVisibleSchema("Schema", null)
+                .build();
+        assertThat(request.getPubliclyVisibleSchemas()).isEmpty();
+    }
+
+    @Test
+    public void testPubliclyVisibleSchemaType_deduped() {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        PackageIdentifier packageIdentifier =
+                new PackageIdentifier("com.package.foo", /*sha256Certificate=*/ new byte[]{});
+        PackageIdentifier packageIdentifier2 =
+                new PackageIdentifier("com.package.bar", /*sha256Certificate=*/ new byte[]{});
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder().addSchemas(schema).setPubliclyVisibleSchema(
+                        "Schema", packageIdentifier).build();
+        assertThat(request.getPubliclyVisibleSchemas())
+                .containsExactly("Schema", packageIdentifier);
+
+        // Deduped schema
+        request = new SetSchemaRequest.Builder().addSchemas(schema)
+                .setPubliclyVisibleSchema("Schema", packageIdentifier2)
+                .setPubliclyVisibleSchema("Schema", packageIdentifier)
+                .build();
+        assertThat(request.getPubliclyVisibleSchemas())
+                .containsExactly("Schema", packageIdentifier);
+    }
+
+    @Test
+    public void testSetSchemaTypeVisibleForConfigs() {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        PackageIdentifier packageIdentifier2 = new PackageIdentifier("com.package.bar",
+                new byte[]{100});
+
+        SchemaVisibilityConfig config1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier1)
+                .addRequiredPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+                .build();
+        SchemaVisibilityConfig config2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier2)
+                .addRequiredPermissions(ImmutableSet.of(
+                        SetSchemaRequest.READ_HOME_APP_SEARCH_DATA,
+                        SetSchemaRequest.READ_CALENDAR))
+                .build();
+
+        SetSchemaRequest request = new SetSchemaRequest.Builder()
+                .addSchemas(schema)
+                .addSchemaTypeVisibleToConfig("Schema", config1)
+                .addSchemaTypeVisibleToConfig("Schema", config2)
+                .build();
+
+        assertThat(request.getSchemasVisibleToConfigs()).containsExactly("Schema",
+                ImmutableSet.of(config1, config2));
+    }
+
+    @Test
+    public void testClearSchemaTypeVisibleForConfigs() {
+        AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        PackageIdentifier packageIdentifier2 = new PackageIdentifier("com.package.bar",
+                new byte[]{100});
+
+        SchemaVisibilityConfig config1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier1)
+                .addRequiredPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+                .build();
+        SchemaVisibilityConfig config2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier2)
+                .addRequiredPermissions(ImmutableSet.of(
+                        SetSchemaRequest.READ_HOME_APP_SEARCH_DATA,
+                        SetSchemaRequest.READ_CALENDAR))
+                .build();
+
+        SetSchemaRequest.Builder builder = new SetSchemaRequest.Builder()
+                .addSchemas(schema)
+                .addSchemaTypeVisibleToConfig("Schema", config1)
+                .addSchemaTypeVisibleToConfig("Schema", config2);
+
+        SetSchemaRequest original = builder.build();
+        assertThat(original.getSchemasVisibleToConfigs()).containsExactly("Schema",
+                ImmutableSet.of(config1, config2));
+
+        builder.clearSchemaTypeVisibleToConfigs("Schema");
+        SetSchemaRequest rebuild = builder.build();
+
+        // rebuild has empty visible to configs
+        assertThat(rebuild.getSchemasVisibleToConfigs()).isEmpty();
+        // original keep in the same state
+        assertThat(original.getSchemasVisibleToConfigs()).containsExactly("Schema",
+                ImmutableSet.of(config1, config2));
+    }
 
     // @exportToFramework:startStrip()
     @Document
@@ -611,7 +764,7 @@
                         "Queen", ImmutableSet.of(
                                 ImmutableSet.of(SetSchemaRequest.READ_EXTERNAL_STORAGE)
                         )
-            );
+                );
 
         // Clear the permissions in the builder
         setSchemaRequestBuilder.clearRequiredPermissionsForDocumentClassVisibility(King.class);
@@ -622,7 +775,7 @@
                         "Queen", ImmutableSet.of(
                                 ImmutableSet.of(SetSchemaRequest.READ_EXTERNAL_STORAGE)
                         )
-            );
+                );
 
         // Old object should remain unchanged
         assertThat(request.getRequiredPermissionsForSchemaTypeVisibility())
@@ -635,7 +788,76 @@
                         "Queen", ImmutableSet.of(
                                 ImmutableSet.of(SetSchemaRequest.READ_EXTERNAL_STORAGE)
                         )
-            );
+                );
+    }
+
+    @Test
+    public void testSetDocumentClassVisibleForConfigs() throws Exception {
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        PackageIdentifier packageIdentifier2 = new PackageIdentifier("com.package.bar",
+                new byte[]{100});
+
+        SchemaVisibilityConfig config1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier1)
+                .addRequiredPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+                .build();
+        SchemaVisibilityConfig config2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier2)
+                .addRequiredPermissions(ImmutableSet.of(
+                        SetSchemaRequest.READ_HOME_APP_SEARCH_DATA,
+                        SetSchemaRequest.READ_CALENDAR))
+                .build();
+
+        SetSchemaRequest request = new SetSchemaRequest.Builder()
+                .addDocumentClasses(King.class, Queen.class)
+                .addDocumentClassVisibleToConfig(King.class, config1)
+                .addDocumentClassVisibleToConfig(King.class, config2)
+                .build();
+
+        assertThat(request.getSchemasVisibleToConfigs()).containsExactly("King",
+                ImmutableSet.of(config1, config2));
+    }
+
+    @Test
+    public void testClearDocumentClassVisibleForConfigs() throws Exception {
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("com.package.foo",
+                new byte[]{100});
+        PackageIdentifier packageIdentifier2 = new PackageIdentifier("com.package.bar",
+                new byte[]{100});
+
+        SchemaVisibilityConfig config1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier1)
+                .addRequiredPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+                .build();
+        SchemaVisibilityConfig config2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier2)
+                .addRequiredPermissions(ImmutableSet.of(
+                        SetSchemaRequest.READ_HOME_APP_SEARCH_DATA,
+                        SetSchemaRequest.READ_CALENDAR))
+                .build();
+
+        SetSchemaRequest.Builder builder = new SetSchemaRequest.Builder()
+                .addDocumentClasses(King.class, Queen.class)
+                .addDocumentClassVisibleToConfig(King.class, config1)
+                .addDocumentClassVisibleToConfig(King.class, config2);
+
+        SetSchemaRequest original = builder.build();
+
+        assertThat(original.getSchemasVisibleToConfigs()).containsExactly("King",
+                ImmutableSet.of(config1, config2));
+
+        // Clear the visbleToConfigs
+        builder.clearDocumentClassVisibleToConfigs(King.class);
+        SetSchemaRequest rebuild = builder.build();
+
+        // rebuild object has empty visibleToConfigs
+        assertThat(rebuild.getSchemasVisibleToConfigs()).isEmpty();
+        // original keep in same state.
+        assertThat(original.getSchemasVisibleToConfigs()).containsExactly("King",
+                ImmutableSet.of(config1, config2));
     }
 // @exportToFramework:endStrip()
 
@@ -740,6 +962,91 @@
     }
 
     @Test
+    public void testRebuild_visibleConfigs() {
+        byte[] sha256cert1 = new byte[32];
+        byte[] sha256cert2 = new byte[32];
+        Arrays.fill(sha256cert1, (byte) 1);
+        Arrays.fill(sha256cert2, (byte) 2);
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("Email", sha256cert1);
+        PackageIdentifier packageIdentifier2 = new PackageIdentifier("Email", sha256cert2);
+
+        SchemaVisibilityConfig config1 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier1)
+                .addRequiredPermissions(
+                        ImmutableSet.of(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+                .build();
+        SchemaVisibilityConfig config2 = new SchemaVisibilityConfig.Builder()
+                .addAllowedPackage(packageIdentifier2)
+                .addRequiredPermissions(ImmutableSet.of(
+                        SetSchemaRequest.READ_HOME_APP_SEARCH_DATA,
+                        SetSchemaRequest.READ_CALENDAR))
+                .build();
+
+        AppSearchSchema schema1 = new AppSearchSchema.Builder("Email1")
+                .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(
+                                AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+        AppSearchSchema schema2 = new AppSearchSchema.Builder("Email2")
+                .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                        .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                        .setIndexingType(
+                                AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                        .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                        .build()
+                ).build();
+
+        SetSchemaRequest.Builder builder = new SetSchemaRequest.Builder()
+                .addSchemas(schema1)
+                .addSchemaTypeVisibleToConfig("Email1", config1);
+
+        SetSchemaRequest original = builder.build();
+        SetSchemaRequest rebuild = builder
+                .addSchemas(schema2)
+                .addSchemaTypeVisibleToConfig("Email2", config2)
+                .build();
+
+        assertThat(original.getSchemas()).containsExactly(schema1);
+        assertThat(original.getSchemasVisibleToConfigs()).containsExactly(
+                "Email1", ImmutableSet.of(config1));
+
+        assertThat(rebuild.getSchemas()).containsExactly(schema1, schema2);
+        assertThat(rebuild.getSchemasVisibleToConfigs()).containsExactly(
+                "Email1", ImmutableSet.of(config1),
+                "Email2", ImmutableSet.of(config2));
+    }
+
+    @Test
+    public void testSetVisibility_publicVisibility_rebuild() {
+        byte[] sha256cert1 = new byte[32];
+        byte[] sha256cert2 = new byte[32];
+        Arrays.fill(sha256cert1, (byte) 1);
+        Arrays.fill(sha256cert2, (byte) 2);
+        PackageIdentifier packageIdentifier1 = new PackageIdentifier("Email", sha256cert1);
+        PackageIdentifier packageIdentifier2 = new PackageIdentifier("Email", sha256cert2);
+        AppSearchSchema schema1 = new AppSearchSchema.Builder("Email1").build();
+        AppSearchSchema schema2 = new AppSearchSchema.Builder("Email2").build();
+
+        SetSchemaRequest.Builder builder = new SetSchemaRequest.Builder()
+                .addSchemas(schema1).setPubliclyVisibleSchema("Email1", packageIdentifier1);
+
+        SetSchemaRequest original = builder.build();
+        SetSchemaRequest rebuild = builder.addSchemas(schema2)
+                .setPubliclyVisibleSchema("Email2", packageIdentifier2).build();
+
+        assertThat(original.getSchemas()).containsExactly(schema1);
+        assertThat(original.getPubliclyVisibleSchemas())
+                .containsExactly("Email1", packageIdentifier1);
+
+        assertThat(rebuild.getSchemas()).containsExactly(schema1, schema2);
+        assertThat(original.getPubliclyVisibleSchemas())
+                .containsExactly("Email1", packageIdentifier1);
+    }
+
+    @Test
     public void getAndModify() {
         byte[] sha256cert1 = new byte[32];
         byte[] sha256cert2 = new byte[32];
@@ -769,7 +1076,8 @@
                 .build();
 
         // get the visibility setting and modify the output object.
-        // skip getSchemasNotDisplayedBySystem since it returns an unmodifiable object.
+        // skip getSchemasNotDisplayedBySystem and getPubliclyVisibleSchemas since they return
+        // unmodifiable objects.
         request.getSchemasVisibleToPackages().put("Email2", ImmutableSet.of(packageIdentifier2));
         request.getRequiredPermissionsForSchemaTypeVisibility().put("Email2",
                 ImmutableSet.of(ImmutableSet.of(SetSchemaRequest.READ_CALENDAR)));
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/observer/DocumentChangeInfoCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/observer/DocumentChangeInfoCtsTest.java
index 06f1bd4..55840c6 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/observer/DocumentChangeInfoCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/observer/DocumentChangeInfoCtsTest.java
@@ -27,17 +27,17 @@
 public class DocumentChangeInfoCtsTest {
     @Test
     public void testConstructor() {
-        DocumentChangeInfo DocumentChangeInfo = new DocumentChangeInfo(
+        DocumentChangeInfo documentChangeInfo = new DocumentChangeInfo(
                 "packageName",
                 "databaseName",
                 "namespace",
                 "SchemaName",
                 ImmutableSet.of("documentId1", "documentId2"));
-        assertThat(DocumentChangeInfo.getPackageName()).isEqualTo("packageName");
-        assertThat(DocumentChangeInfo.getDatabaseName()).isEqualTo("databaseName");
-        assertThat(DocumentChangeInfo.getNamespace()).isEqualTo("namespace");
-        assertThat(DocumentChangeInfo.getSchemaName()).isEqualTo("SchemaName");
-        assertThat(DocumentChangeInfo.getChangedDocumentIds())
+        assertThat(documentChangeInfo.getPackageName()).isEqualTo("packageName");
+        assertThat(documentChangeInfo.getDatabaseName()).isEqualTo("databaseName");
+        assertThat(documentChangeInfo.getNamespace()).isEqualTo("namespace");
+        assertThat(documentChangeInfo.getSchemaName()).isEqualTo("SchemaName");
+        assertThat(documentChangeInfo.getChangedDocumentIds())
                 .containsExactly("documentId1", "documentId2");
     }
 
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/usagereporting/ClickActionCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/usagereporting/ClickActionCtsTest.java
new file mode 100644
index 0000000..48f7846
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/usagereporting/ClickActionCtsTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+
+package androidx.appsearch.cts.usagereporting;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.usagereporting.ActionConstants;
+import androidx.appsearch.usagereporting.ClickAction;
+import androidx.appsearch.usagereporting.TakenAction;
+
+import org.junit.Test;
+
+public class ClickActionCtsTest {
+    @Test
+    public void testBuilder() {
+        ClickAction clickAction =
+                new ClickAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                        .setDocumentTtlMillis(456)
+                        .setQuery("query")
+                        .setReferencedQualifiedId("pkg$db/ns#refId")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(3)
+                        .setTimeStayOnResultMillis(65536)
+                        .build();
+
+        assertThat(clickAction.getNamespace()).isEqualTo("namespace");
+        assertThat(clickAction.getId()).isEqualTo("id");
+        assertThat(clickAction.getActionTimestampMillis()).isEqualTo(123);
+        assertThat(clickAction.getDocumentTtlMillis()).isEqualTo(456);
+        assertThat(clickAction.getActionType()).isEqualTo(ActionConstants.ACTION_TYPE_CLICK);
+        assertThat(clickAction.getQuery()).isEqualTo("query");
+        assertThat(clickAction.getReferencedQualifiedId()).isEqualTo("pkg$db/ns#refId");
+        assertThat(clickAction.getResultRankInBlock()).isEqualTo(1);
+        assertThat(clickAction.getResultRankGlobal()).isEqualTo(3);
+        assertThat(clickAction.getTimeStayOnResultMillis()).isEqualTo(65536);
+    }
+
+    @Test
+    public void testBuilder_defaultValues() {
+        ClickAction clickAction =
+                new ClickAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                        .build();
+
+        assertThat(clickAction.getNamespace()).isEqualTo("namespace");
+        assertThat(clickAction.getId()).isEqualTo("id");
+        assertThat(clickAction.getActionTimestampMillis()).isEqualTo(123);
+        assertThat(clickAction.getDocumentTtlMillis())
+                .isEqualTo(TakenAction.DEFAULT_DOCUMENT_TTL_MILLIS);
+        assertThat(clickAction.getActionType()).isEqualTo(ActionConstants.ACTION_TYPE_CLICK);
+        assertThat(clickAction.getQuery()).isNull();
+        assertThat(clickAction.getReferencedQualifiedId()).isNull();
+        assertThat(clickAction.getResultRankInBlock()).isEqualTo(-1);
+        assertThat(clickAction.getResultRankGlobal()).isEqualTo(-1);
+        assertThat(clickAction.getTimeStayOnResultMillis()).isEqualTo(-1);
+    }
+
+    @Test
+    public void testBuilderCopy_allFieldsAreCopied() {
+        ClickAction clickAction1 =
+                new ClickAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                        .setDocumentTtlMillis(456)
+                        .setQuery("query")
+                        .setReferencedQualifiedId("pkg$db/ns#refId")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(3)
+                        .setTimeStayOnResultMillis(65536)
+                        .build();
+        ClickAction clickAction2 = new ClickAction.Builder(clickAction1).build();
+
+        // All fields should be copied correctly from clickAction1 to the builder and propagates to
+        // clickAction2 after calling build().
+        assertThat(clickAction2.getNamespace()).isEqualTo("namespace");
+        assertThat(clickAction2.getId()).isEqualTo("id");
+        assertThat(clickAction2.getActionTimestampMillis()).isEqualTo(123);
+        assertThat(clickAction2.getDocumentTtlMillis()).isEqualTo(456);
+        assertThat(clickAction2.getActionType()).isEqualTo(ActionConstants.ACTION_TYPE_CLICK);
+        assertThat(clickAction2.getQuery()).isEqualTo("query");
+        assertThat(clickAction2.getReferencedQualifiedId()).isEqualTo("pkg$db/ns#refId");
+        assertThat(clickAction2.getResultRankInBlock()).isEqualTo(1);
+        assertThat(clickAction2.getResultRankGlobal()).isEqualTo(3);
+        assertThat(clickAction2.getTimeStayOnResultMillis()).isEqualTo(65536);
+    }
+
+    @Test
+    public void testToGenericDocument() throws Exception {
+        ClickAction clickAction =
+                new ClickAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                        .setDocumentTtlMillis(456)
+                        .setQuery("query")
+                        .setReferencedQualifiedId("pkg$db/ns#refId")
+                        .setResultRankInBlock(1)
+                        .setResultRankGlobal(3)
+                        .setTimeStayOnResultMillis(65536)
+                        .build();
+
+        GenericDocument document = GenericDocument.fromDocumentClass(clickAction);
+        assertThat(document.getNamespace()).isEqualTo("namespace");
+        assertThat(document.getId()).isEqualTo("id");
+        assertThat(document.getCreationTimestampMillis()).isEqualTo(123);
+        assertThat(document.getTtlMillis()).isEqualTo(456);
+        assertThat(document.getPropertyLong("actionType"))
+                .isEqualTo(ActionConstants.ACTION_TYPE_CLICK);
+        assertThat(document.getPropertyString("query")).isEqualTo("query");
+        assertThat(document.getPropertyString("referencedQualifiedId"))
+                .isEqualTo("pkg$db/ns#refId");
+        assertThat(document.getPropertyLong("resultRankInBlock")).isEqualTo(1);
+        assertThat(document.getPropertyLong("resultRankGlobal")).isEqualTo(3);
+        assertThat(document.getPropertyLong("timeStayOnResultMillis")).isEqualTo(65536);
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/usagereporting/SearchActionCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/usagereporting/SearchActionCtsTest.java
new file mode 100644
index 0000000..a4a63a6
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/usagereporting/SearchActionCtsTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+
+package androidx.appsearch.cts.usagereporting;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.usagereporting.ActionConstants;
+import androidx.appsearch.usagereporting.SearchAction;
+import androidx.appsearch.usagereporting.TakenAction;
+
+import org.junit.Test;
+
+public class SearchActionCtsTest {
+    @Test
+    public void testBuilder() {
+        SearchAction searchAction =
+                new SearchAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                    .setDocumentTtlMillis(456)
+                    .setQuery("query")
+                    .setFetchedResultCount(1)
+                    .build();
+
+        assertThat(searchAction.getNamespace()).isEqualTo("namespace");
+        assertThat(searchAction.getId()).isEqualTo("id");
+        assertThat(searchAction.getActionTimestampMillis()).isEqualTo(123);
+        assertThat(searchAction.getDocumentTtlMillis()).isEqualTo(456);
+        assertThat(searchAction.getActionType()).isEqualTo(ActionConstants.ACTION_TYPE_SEARCH);
+        assertThat(searchAction.getQuery()).isEqualTo("query");
+        assertThat(searchAction.getFetchedResultCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void testBuilder_defaultValues() {
+        SearchAction searchAction =
+                new SearchAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                        .build();
+
+        assertThat(searchAction.getNamespace()).isEqualTo("namespace");
+        assertThat(searchAction.getId()).isEqualTo("id");
+        assertThat(searchAction.getActionTimestampMillis()).isEqualTo(123);
+        assertThat(searchAction.getDocumentTtlMillis())
+                .isEqualTo(TakenAction.DEFAULT_DOCUMENT_TTL_MILLIS);
+        assertThat(searchAction.getActionType()).isEqualTo(ActionConstants.ACTION_TYPE_SEARCH);
+        assertThat(searchAction.getQuery()).isNull();
+        assertThat(searchAction.getFetchedResultCount()).isEqualTo(-1);
+    }
+
+    @Test
+    public void testBuilderCopy_allFieldsAreCopied() {
+        SearchAction searchAction1 =
+                new SearchAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                        .setDocumentTtlMillis(456)
+                        .setQuery("query")
+                        .setFetchedResultCount(1)
+                        .build();
+        SearchAction searchAction2 = new SearchAction.Builder(searchAction1).build();
+
+        assertThat(searchAction2.getNamespace()).isEqualTo("namespace");
+        assertThat(searchAction2.getId()).isEqualTo("id");
+        assertThat(searchAction2.getActionTimestampMillis()).isEqualTo(123);
+        assertThat(searchAction2.getDocumentTtlMillis()).isEqualTo(456);
+        assertThat(searchAction2.getActionType()).isEqualTo(ActionConstants.ACTION_TYPE_SEARCH);
+        assertThat(searchAction2.getQuery()).isEqualTo("query");
+        assertThat(searchAction2.getFetchedResultCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void testToGenericDocument() throws Exception {
+        SearchAction searchAction =
+                new SearchAction.Builder("namespace", "id", /* actionTimestampMillis= */123)
+                    .setDocumentTtlMillis(456)
+                    .setQuery("query")
+                    .setFetchedResultCount(1)
+                    .build();
+
+        GenericDocument document = GenericDocument.fromDocumentClass(searchAction);
+        assertThat(document.getNamespace()).isEqualTo("namespace");
+        assertThat(document.getId()).isEqualTo("id");
+        assertThat(document.getCreationTimestampMillis()).isEqualTo(123);
+        assertThat(document.getTtlMillis()).isEqualTo(456);
+        assertThat(document.getPropertyLong("actionType"))
+                .isEqualTo(ActionConstants.ACTION_TYPE_SEARCH);
+        assertThat(document.getPropertyString("query")).isEqualTo("query");
+        assertThat(document.getPropertyLong("fetchedResultCount")).isEqualTo(1);
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/CheckFlagsRule.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/CheckFlagsRule.java
new file mode 100644
index 0000000..3ec5386
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/CheckFlagsRule.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.flags;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Shim for real CheckFlagsRule defined in Framework.
+ *
+ * <p>In Jetpack, this shim does nothing and exists only for code sync purpose.
+ */
+public final class CheckFlagsRule implements TestRule {
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return base;
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/DeviceFlagsValueProvider.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/DeviceFlagsValueProvider.java
new file mode 100644
index 0000000..1a2858a
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/DeviceFlagsValueProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.flags;
+
+/**
+ * Shim for real DeviceFlagsValueProvider defined in Framework.
+ *
+ * <p>In Jetpack, this shim does nothing and exists only for code sync purpose.
+ */
+public final class DeviceFlagsValueProvider {
+    private DeviceFlagsValueProvider() {}
+
+    /** Provides a shim rule that can be used to check the status of flags on device */
+    public static CheckFlagsRule createCheckFlagsRule() {
+        return new CheckFlagsRule();
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
new file mode 100644
index 0000000..14169d7
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.flags;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class FlagsTest {
+    @Test
+    public void testFlagValue_enableSafeParcelable2() {
+        assertThat(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2).isEqualTo(
+                "com.android.appsearch.flags.enable_safe_parcelable_2");
+    }
+
+    @Test
+    public void testFlagValue_enableListFilterHasPropertyFunction() {
+        assertThat(Flags.FLAG_ENABLE_LIST_FILTER_HAS_PROPERTY_FUNCTION).isEqualTo(
+                "com.android.appsearch.flags.enable_list_filter_has_property_function");
+    }
+
+    @Test
+    public void testFlagValue_enableGroupingTypePerSchema() {
+        assertThat(Flags.FLAG_ENABLE_GROUPING_TYPE_PER_SCHEMA).isEqualTo(
+                "com.android.appsearch.flags.enable_grouping_type_per_schema");
+    }
+
+    @Test
+    public void testFlagValue_enableGenericDocumentCopyConstructor() {
+        assertThat(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_COPY_CONSTRUCTOR).isEqualTo("com.android"
+                + ".appsearch.flags.enable_generic_document_copy_constructor");
+    }
+
+    @Test
+    public void testFlagValue_enableSearchSpecFilterProperties() {
+        assertThat(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES).isEqualTo(
+                "com.android.appsearch.flags.enable_search_spec_filter_properties");
+    }
+
+    @Test
+    public void testFlagValue_enableSearchSpecSetSearchSourceLogTag() {
+        assertThat(Flags.FLAG_ENABLE_SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG).isEqualTo(
+                "com.android.appsearch.flags.enable_search_spec_set_search_source_log_tag");
+    }
+
+    @Test
+    public void testFlagValue_enableSetSchemaVisibleToConfigs() {
+        assertThat(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS).isEqualTo("com"
+                + ".android.appsearch.flags.enable_set_schema_visible_to_configs");
+    }
+
+    @Test
+    public void testFlagValue_enablePutDocumentsRequestAddTakenActions() {
+        assertThat(Flags.FLAG_ENABLE_PUT_DOCUMENTS_REQUEST_ADD_TAKEN_ACTIONS).isEqualTo(
+                "com.android.appsearch.flags.enable_put_documents_request_add_taken_actions");
+    }
+
+    @Test
+    public void testFlagValue_enableGenericDocumentBuilderHiddenMethods() {
+        assertThat(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_BUILDER_HIDDEN_METHODS).isEqualTo("com"
+                + ".android.appsearch.flags.enable_generic_document_builder_hidden_methods");
+    }
+
+    @Test
+    public void testFlagValue_enableSetPubliclyVisibleSchema() {
+        assertThat(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
+                .isEqualTo(
+                        "com.android.appsearch.flags.enable_set_publicly_visible_schema");
+    }
+
+    @Test
+    public void testFlagValue_enableEnterpriseGlobalSearchSession() {
+        assertThat(Flags.FLAG_ENABLE_ENTERPRISE_GLOBAL_SEARCH_SESSION)
+                .isEqualTo("com.android.appsearch.flags.enable_enterprise_global_search_session");
+    }
+
+    @Test
+    public void testFlagValue_enableResultDeniedAndResultRateLimited() {
+        assertThat(Flags.FLAG_ENABLE_RESULT_DENIED_AND_RESULT_RATE_LIMITED)
+                .isEqualTo(
+                        "com.android.appsearch.flags.enable_result_denied_and_result_rate_limited");
+    }
+
+    @Test
+    public void testFlagValue_enableGetParentTypesAndIndexableNestedProperties() {
+        assertThat(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES)
+                .isEqualTo(
+                        "com.android.appsearch.flags"
+                                + ".enable_get_parent_types_and_indexable_nested_properties");
+    }
+
+    @Test
+    public void testFlagValue_enableSchemaEmbeddingPropertyConfig() {
+        assertThat(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+                .isEqualTo("com.android.appsearch.flags.enable_schema_embedding_property_config");
+    }
+
+    @Test
+    public void testFlagValue_enableListFilterTokenizeFunction() {
+        assertThat(Flags.FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION)
+                .isEqualTo("com.android.appsearch.flags.enable_list_filter_tokenize_function");
+    }
+
+    @Test
+    public void testFlagValue_enableInformationalRankingExpressions() {
+        assertThat(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+                .isEqualTo("com.android.appsearch.flags.enable_informational_ranking_expressions");
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/RequiresFlagsEnabled.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/RequiresFlagsEnabled.java
new file mode 100644
index 0000000..d4f3bf0
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/RequiresFlagsEnabled.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.flags;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Shim for real RequiresFlagsEnabled defined in Framework.
+ *
+ * <p>In Jetpack, this shim does nothing and exists only for code sync purpose.
+ *
+ * <p>In Framework, indicates that a specific test or class should be run only if all of the given
+ * feature flags are enabled in the device's current state. Enforced by the {@code CheckFlagsRule}.
+ *
+ * <p>This annotation works together with RequiresFlagsDisabled to define the value that is
+ * required of the flag by the test for the test to run. It is an error for either a method or class
+ * to require that a particular flag be both enabled and disabled.
+ *
+ * <p>If the value of a particular flag is required (by either {@code RequiresFlagsEnabled} or
+ * {@code RequiresFlagsDisabled}) by both the class and test method, then the values must be
+ * consistent.
+ *
+ * <p>If the value of a one flag is required by an annotation on the class, and the value of a
+ * different flag is required by an annotation of the method, then both requirements apply.
+ *
+ * <p>With {@code CheckFlagsRule}, test(s) will be skipped with 'assumption failed' when any of the
+ * required flag on the target Android platform is disabled.
+ *
+ * <p>Both {@code SetFlagsRule} and {@code CheckFlagsRule} will fail the test if a particular flag
+ * is both set (with {@code EnableFlags} or {@code DisableFlags}) and required (with {@code
+ * RequiresFlagsEnabled} or {@code RequiresFlagsDisabled}).
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface RequiresFlagsEnabled {
+    /**
+     * The list of the feature flags that require to be enabled. Each item is the full flag name
+     * with the format {package_name}.{flag_name}.
+     */
+    String[] value();
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/safeparcel/GenericDocumentParcelTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/safeparcel/GenericDocumentParcelTest.java
index d8cc9bc..0900ec8 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/safeparcel/GenericDocumentParcelTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/safeparcel/GenericDocumentParcelTest.java
@@ -20,9 +20,15 @@
 
 import static org.junit.Assert.assertThrows;
 
+import android.os.Parcel;
+
+import androidx.appsearch.app.EmbeddingVector;
+
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 
 /** Tests for {@link androidx.appsearch.app.GenericDocument} related SafeParcels. */
@@ -34,6 +40,8 @@
         double[] doubleValues = {1.0, 2.0};
         boolean[] booleanValues = {true, false};
         byte[][] bytesValues = {new byte[1]};
+        EmbeddingVector[] embeddingValues = {new EmbeddingVector(new float[1],
+                "my_model")};
         GenericDocumentParcel[] docValues = {(new GenericDocumentParcel.Builder(
                 "namespace", "id", "schemaType")).build()};
 
@@ -52,6 +60,9 @@
         assertThat(new PropertyParcel.Builder("name").setBytesValues(
                 bytesValues).build().getBytesValues()).isEqualTo(
                 Arrays.copyOf(bytesValues, bytesValues.length));
+        assertThat(new PropertyParcel.Builder("name").setEmbeddingValues(
+                embeddingValues).build().getEmbeddingValues()).isEqualTo(
+                Arrays.copyOf(embeddingValues, embeddingValues.length));
         assertThat(new PropertyParcel.Builder("name").setDocumentValues(
                 docValues).build().getDocumentValues()).isEqualTo(
                 Arrays.copyOf(docValues, docValues.length));
@@ -85,14 +96,14 @@
         builder.putInPropertyMap(/*name=*/ "stringArray", /*values=*/ stringArray);
         GenericDocumentParcel genericDocumentParcel = builder.build();
 
-        PropertyParcel[] properties = genericDocumentParcel.getProperties();
+        List<PropertyParcel> properties = genericDocumentParcel.getProperties();
         Map<String, PropertyParcel> propertyMap = genericDocumentParcel.getPropertyMap();
         PropertyParcel longArrayProperty = new PropertyParcel.Builder(
                 /*name=*/ "longArray").setLongValues(longArray).build();
         PropertyParcel stringArrayProperty = new PropertyParcel.Builder(
                 /*name=*/ "stringArray").setStringValues(stringArray).build();
 
-        assertThat(properties).asList().containsExactly(longArrayProperty, stringArrayProperty);
+        assertThat(properties).containsExactly(longArrayProperty, stringArrayProperty);
         assertThat(propertyMap).containsExactly("longArray", longArrayProperty,
                 "stringArray", stringArrayProperty);
     }
@@ -106,8 +117,10 @@
                         /*schemaType=*/ "schemaType");
         long[] longArray = new long[]{1L, 2L, 3L};
         String[] stringArray = new String[]{"hello", "world", "!"};
+        List<String> parentTypes = new ArrayList<>(Arrays.asList("parentType1", "parentType2"));
         builder.putInPropertyMap(/*name=*/ "longArray", /*values=*/ longArray);
         builder.putInPropertyMap(/*name=*/ "stringArray", /*values=*/ stringArray);
+        builder.setParentTypes(parentTypes);
         GenericDocumentParcel genericDocumentParcel = builder.build();
 
         GenericDocumentParcel genericDocumentParcelCopy =
@@ -124,9 +137,130 @@
                 genericDocumentParcel.getTtlMillis());
         assertThat(genericDocumentParcelCopy.getScore()).isEqualTo(
                 genericDocumentParcel.getScore());
+        assertThat(genericDocumentParcelCopy.getParentTypes()).isEqualTo(
+                genericDocumentParcel.getParentTypes());
+
         // Check it is a copy.
         assertThat(genericDocumentParcelCopy).isNotSameInstanceAs(genericDocumentParcel);
         assertThat(genericDocumentParcelCopy.getProperties()).isEqualTo(
                 genericDocumentParcel.getProperties());
     }
+
+    @Test
+    public void testGenericDocumentParcelWithParentTypes() {
+        GenericDocumentParcel.Builder builder =
+                new GenericDocumentParcel.Builder(
+                        /*namespace=*/ "namespace",
+                        /*id=*/ "id",
+                        /*schemaType=*/ "schemaType");
+        List<String> parentTypes = new ArrayList<>(Arrays.asList("parentType1", "parentType2"));
+
+        builder.setParentTypes(parentTypes);
+        GenericDocumentParcel genericDocumentParcel = builder.build();
+
+        assertThat(genericDocumentParcel.getParentTypes()).isEqualTo(parentTypes);
+    }
+
+    @Test
+    public void testGenericDocumentParcel_builderCanBeReused() {
+        GenericDocumentParcel.Builder builder =
+                new GenericDocumentParcel.Builder(
+                        /*namespace=*/ "namespace",
+                        /*id=*/ "id",
+                        /*schemaType=*/ "schemaType");
+        long[] longArray = new long[]{1L, 2L, 3L};
+        String[] stringArray = new String[]{"hello", "world", "!"};
+        List<String> parentTypes = new ArrayList<>(Arrays.asList("parentType1", "parentType2"));
+        builder.putInPropertyMap(/*name=*/ "longArray", /*values=*/ longArray);
+        builder.putInPropertyMap(/*name=*/ "stringArray", /*values=*/ stringArray);
+        builder.setParentTypes(parentTypes);
+
+        GenericDocumentParcel genericDocumentParcel = builder.build();
+        builder.setParentTypes(new ArrayList<>(Arrays.asList("parentType3", "parentType4")));
+        builder.clearProperty("longArray");
+        builder.putInPropertyMap(/*name=*/ "stringArray", /*values=*/ new String[]{""});
+
+        PropertyParcel longArrayProperty = new PropertyParcel.Builder(
+                /*name=*/ "longArray").setLongValues(longArray).build();
+        PropertyParcel stringArrayProperty = new PropertyParcel.Builder(
+                /*name=*/ "stringArray").setStringValues(stringArray).build();
+        assertThat(genericDocumentParcel.getParentTypes()).isEqualTo(parentTypes);
+        assertThat(genericDocumentParcel.getPropertyMap()).containsExactly("longArray",
+                longArrayProperty, "stringArray", stringArrayProperty);
+    }
+
+    @Test
+    public void testRecreateFromParcelWithParentTypes() {
+        String[] stringArray = new String[]{"Hello", "world"};
+        long[] longArray = new long[]{1L, 2L};
+        double[] doubleArray = new double[]{1.1, 2.2};
+        boolean[] booleanArray = new boolean[]{true, false};
+        byte[][] bytesArray = new byte[][]{{1, 2}};
+        GenericDocumentParcel inDoc = new GenericDocumentParcel.Builder(
+                "namespace1", "id1", "schema1")
+                .setParentTypes(new ArrayList<>(Arrays.asList("Class1", "Class2")))
+                .setScore(42)
+                .setTtlMillis(43)
+                .setCreationTimestampMillis(44)
+                .putInPropertyMap("propStrings", stringArray)
+                .putInPropertyMap("propLongs", longArray)
+                .putInPropertyMap("propDoubles", doubleArray)
+                .putInPropertyMap("propBytes", bytesArray)
+                .putInPropertyMap("propBooleans", booleanArray)
+                .putInPropertyMap(
+                        "propDoc",
+                        new GenericDocumentParcel[]{
+                                new GenericDocumentParcel.Builder(
+                                        "namespace2", "id2", "schema2")
+                                        .putInPropertyMap("propStrings", new String[]{"Goodbye"})
+                                        .putInPropertyMap("propBytes", new byte[][]{{3, 4}})
+                                        .build()})
+                .build();
+
+        // Serialize the document
+        Parcel inParcel = Parcel.obtain();
+        inParcel.writeParcelable(inDoc, /*flags=*/ 0);
+        byte[] data = inParcel.marshall();
+        inParcel.recycle();
+
+        // Deserialize the document
+        Parcel outParcel = Parcel.obtain();
+        outParcel.unmarshall(data, 0, data.length);
+        outParcel.setDataPosition(0);
+        @SuppressWarnings("deprecation")
+        GenericDocumentParcel outDoc = outParcel.readParcelable(getClass().getClassLoader());
+        outParcel.recycle();
+
+        // Compare results
+        assertThat(outDoc.getId()).isEqualTo("id1");
+        assertThat(outDoc.getNamespace()).isEqualTo("namespace1");
+        assertThat(outDoc.getSchemaType()).isEqualTo("schema1");
+        assertThat(outDoc.getParentTypes()).isEqualTo(Arrays.asList("Class1", "Class2"));
+        assertThat(outDoc.getScore()).isEqualTo(42);
+        assertThat(outDoc.getTtlMillis()).isEqualTo(43);
+        assertThat(outDoc.getCreationTimestampMillis()).isEqualTo(44);
+
+        // Properties
+        Map<String, PropertyParcel> propertyMap = outDoc.getPropertyMap();
+        assertThat(propertyMap.get("propStrings").getStringValues()).isEqualTo(stringArray);
+        assertThat(propertyMap.get("propLongs").getLongValues()).isEqualTo(longArray);
+        assertThat(propertyMap.get("propDoubles").getDoubleValues()).isEqualTo(doubleArray);
+        assertThat(propertyMap.get("propBytes").getBytesValues()).isEqualTo(bytesArray);
+        assertThat(propertyMap.get("propBooleans").getBooleanValues()).isEqualTo(booleanArray);
+
+        // Check inner doc.
+        GenericDocumentParcel[] innerDocs = propertyMap.get("propDoc").getDocumentValues();
+        assertThat(innerDocs).hasLength(1);
+        assertThat(innerDocs[0].getNamespace()).isEqualTo("namespace2");
+        assertThat(innerDocs[0].getId()).isEqualTo("id2");
+        assertThat(innerDocs[0].getSchemaType()).isEqualTo("schema2");
+        assertThat(innerDocs[0].getPropertyMap().get("propStrings").getStringValues()).isEqualTo(
+                new String[]{"Goodbye"});
+        assertThat(innerDocs[0].getPropertyMap().get("propBytes").getBytesValues()).isEqualTo(
+                new byte[][]{{3, 4}});
+
+        // Finally check equals and hashcode
+        assertThat(inDoc).isEqualTo(outDoc);
+        assertThat(inDoc.hashCode()).isEqualTo(outDoc.hashCode());
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/safeparcel/PropertyParcelTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/safeparcel/PropertyParcelTest.java
new file mode 100644
index 0000000..a5ce50b
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/safeparcel/PropertyParcelTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.safeparcel;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import org.junit.Test;
+
+public class PropertyParcelTest {
+    @Test
+    public void testTwoDimensionByteArray_serializationSupported() {
+        int row = 20;
+        int col = 10;
+        byte[][] bytesArray = new byte[row][col];
+        for (int i = 0; i < row; ++i) {
+            for (int j = 0; j < col; ++j) {
+                bytesArray[i][j] = (byte) (i + j);
+            }
+        }
+
+        String propertyName = "propertyName";
+        PropertyParcel expectedPropertyParcel =
+                new PropertyParcel.Builder(propertyName).setBytesValues(bytesArray).build();
+        Parcel data = Parcel.obtain();
+        try {
+            data.writeParcelable(expectedPropertyParcel, /* flags= */ 0);
+            data.setDataPosition(0);
+            @SuppressWarnings("deprecation")
+            PropertyParcel actualPropertyParcel = data.readParcelable(
+                    PropertyParcelTest.class.getClassLoader());
+            assertThat(expectedPropertyParcel).isEqualTo(actualPropertyParcel);
+        } finally {
+            data.recycle();
+        }
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
index 59415fb..935c5bf 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.LongSerializer;
 import androidx.appsearch.app.StringSerializer;
 
@@ -228,7 +229,6 @@
          * <p>If not specified, defaults to {@link
          * AppSearchSchema.StringPropertyConfig#INDEXING_TYPE_NONE} (the field will not be indexed
          * and cannot be queried).
-         * TODO(b/171857731) renamed to TermMatchType when using String-specific indexing config.
          */
         @AppSearchSchema.StringPropertyConfig.IndexingType int indexingType()
                 default AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
@@ -540,6 +540,42 @@
     }
 
     /**
+     * Configures an {@link EmbeddingVector} field of a class as a property known to AppSearch.
+     */
+    @Documented
+    @Retention(RetentionPolicy.CLASS)
+    @Target({ElementType.FIELD, ElementType.METHOD})
+    @interface EmbeddingProperty {
+        /**
+         * The name of this property. This string is used to query against this property.
+         *
+         * <p>If not specified, the name of the field in the code will be used instead.
+         */
+        String name() default "";
+
+        /**
+         * Configures how a property should be indexed so that it can be retrieved by queries.
+         *
+         * <p>If not specified, defaults to
+         * {@link AppSearchSchema.EmbeddingPropertyConfig#INDEXING_TYPE_NONE} (the field will not be
+         * indexed and cannot be queried).
+         */
+        @AppSearchSchema.EmbeddingPropertyConfig.IndexingType int indexingType()
+                default AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE;
+
+        /**
+         * Configures whether this property must be specified for the document to be valid.
+         *
+         * <p>This attribute does not apply to properties of a repeated type (e.g. a list).
+         *
+         * <p>Please make sure you understand the consequences of required fields on
+         * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before
+         * setting this attribute to {@code true}.
+         */
+        boolean required() default false;
+    }
+
+    /**
      * Marks a static method or a builder class directly as a builder producer. A builder class
      * should contain a "build()" method to construct the AppSearch document object and setter
      * methods to set field values.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java
index 8263467..08a16d2 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBatchResult.java
@@ -44,12 +44,14 @@
  * @see AppSearchSession#removeAsync
  */
 public final class AppSearchBatchResult<KeyType, ValueType> {
-    @NonNull private final Map<KeyType, ValueType> mSuccesses;
+    @NonNull private final Map<KeyType,
+            @androidx.appsearch.checker.nullness.qual.Nullable ValueType> mSuccesses;
     @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
     @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mAll;
 
     AppSearchBatchResult(
-            @NonNull Map<KeyType, ValueType> successes,
+            @NonNull Map<KeyType, @androidx.appsearch.checker.nullness.qual.Nullable ValueType>
+                    successes,
             @NonNull Map<KeyType, AppSearchResult<ValueType>> failures,
             @NonNull Map<KeyType, AppSearchResult<ValueType>> all) {
         mSuccesses = Preconditions.checkNotNull(successes);
@@ -123,7 +125,8 @@
      * @param <ValueType> The type of the result objects for successful results.
      */
     public static final class Builder<KeyType, ValueType> {
-        private ArrayMap<KeyType, ValueType> mSuccesses = new ArrayMap<>();
+        private ArrayMap<KeyType, @androidx.appsearch.checker.nullness.qual.Nullable ValueType>
+                mSuccesses = new ArrayMap<>();
         private ArrayMap<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
         private ArrayMap<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
         private boolean mBuilt = false;
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchEnvironment.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchEnvironment.java
new file mode 100644
index 0000000..aa5326b
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchEnvironment.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.app;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+import java.io.File;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An interface which exposes environment specific methods for AppSearch.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public interface AppSearchEnvironment {
+
+    /** Returns the directory to initialize appsearch based on the environment. */
+    @NonNull
+    File getAppSearchDir(@NonNull Context context, @Nullable UserHandle userHandle);
+
+    /** Returns the correct context for the user based on the environment. */
+    @NonNull
+    Context createContextAsUser(@NonNull Context context, @NonNull UserHandle userHandle);
+
+    /** Returns an ExecutorService based on given parameters. */
+    @NonNull
+    ExecutorService createExecutorService(
+            int corePoolSize,
+            int maxConcurrency,
+            long keepAliveTime,
+            @NonNull TimeUnit unit,
+            @NonNull BlockingQueue<Runnable> workQueue,
+            int priority);
+
+    /** Returns an ExecutorService with a single thread. */
+    @NonNull
+    ExecutorService createSingleThreadExecutor();
+
+    /** Creates and returns an Executor with cached thread pools. */
+    @NonNull
+    ExecutorService createCachedThreadPoolExecutor();
+
+    /**
+     * Returns a cache directory for creating temporary files like in case of migrating documents.
+     */
+    @Nullable
+    File getCacheDir(@NonNull Context context);
+
+    /** Returns if we can log INFO level logs. */
+    boolean isInfoLoggingEnabled();
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchEnvironmentFactory.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchEnvironmentFactory.java
new file mode 100644
index 0000000..9ae8ac1
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchEnvironmentFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.app;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * This is a factory class for implementations needed based on the environment.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class AppSearchEnvironmentFactory {
+    private static volatile AppSearchEnvironment sAppSearchEnvironment;
+
+    /** Returns the singleton instance of {@link AppSearchEnvironment}. */
+    @NonNull
+    public static AppSearchEnvironment getEnvironmentInstance() {
+        AppSearchEnvironment localRef = sAppSearchEnvironment;
+        if (localRef == null) {
+            synchronized (AppSearchEnvironmentFactory.class) {
+                localRef = sAppSearchEnvironment;
+                if (localRef == null) {
+                    sAppSearchEnvironment = localRef =
+                            new JetpackAppSearchEnvironment();
+                }
+            }
+        }
+        return localRef;
+    }
+
+    /** Sets an instance of {@link AppSearchEnvironment}. for testing.*/
+    @VisibleForTesting
+    public static void setEnvironmentInstanceForTest(
+            @NonNull AppSearchEnvironment appSearchEnvironment) {
+        synchronized (AppSearchEnvironmentFactory.class) {
+            sAppSearchEnvironment = appSearchEnvironment;
+        }
+    }
+
+    private AppSearchEnvironmentFactory() {
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java
index d8c79a5..86c8102 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchResult.java
@@ -22,6 +22,8 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
 import androidx.appsearch.util.LogUtil;
 import androidx.core.util.ObjectsCompat;
 import androidx.core.util.Preconditions;
@@ -54,6 +56,7 @@
             RESULT_SECURITY_ERROR,
             RESULT_DENIED,
             RESULT_RATE_LIMITED,
+            RESULT_TIMED_OUT
     })
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     @Retention(RetentionPolicy.SOURCE)
@@ -101,20 +104,22 @@
     /**
      * The requested operation is denied for the caller. This error is logged and returned for
      * denylist rejections.
-     * <!--@exportToFramework:hide-->
      */
-    // TODO(b/279047435): unhide this the next time we can make API changes
+    @FlaggedApi(Flags.FLAG_ENABLE_RESULT_DENIED_AND_RESULT_RATE_LIMITED)
     public static final int RESULT_DENIED = 9;
 
     /**
-     * The caller has hit AppSearch's rate limit and the requested operation has been rejected.
-     * <!--@exportToFramework:hide-->
+     * The caller has hit AppSearch's rate limit and the requested operation has been rejected. The
+     * caller is recommended to reschedule tasks with exponential backoff.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    // TODO(b/279047435): unhide this the next time we can make API changes
+    @FlaggedApi(Flags.FLAG_ENABLE_RESULT_DENIED_AND_RESULT_RATE_LIMITED)
     public static final int RESULT_RATE_LIMITED = 10;
 
-    private final @ResultCode int mResultCode;
+    /** The operation was timed out. */
+    @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+    public static final int RESULT_TIMED_OUT = 11;
+
+    @ResultCode private final int mResultCode;
     @Nullable private final ValueType mResultValue;
     @Nullable private final String mErrorMessage;
 
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
index a4fd79ef..b300d3e 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
@@ -18,6 +18,8 @@
 
 import android.annotation.SuppressLint;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -27,7 +29,15 @@
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.exceptions.IllegalSchemaException;
-import androidx.appsearch.util.BundleUtil;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.PropertyConfigParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.DocumentIndexingConfigParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.JoinableConfigParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.StringIndexingConfigParcel;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.AppSearchSchemaCreator;
 import androidx.appsearch.util.IndentingStringBuilder;
 import androidx.collection.ArraySet;
 import androidx.core.util.ObjectsCompat;
@@ -41,6 +51,7 @@
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -52,28 +63,36 @@
  *
  * @see AppSearchSession#setSchemaAsync
  */
-public final class AppSearchSchema {
-    private static final String SCHEMA_TYPE_FIELD = "schemaType";
-    private static final String PROPERTIES_FIELD = "properties";
-    private static final String PARENT_TYPES_FIELD = "parentTypes";
-
-    private final Bundle mBundle;
-
-    /** @exportToFramework:hide */
[email protected](creator = "AppSearchSchemaCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class AppSearchSchema extends AbstractSafeParcelable {
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public AppSearchSchema(@NonNull Bundle bundle) {
-        Preconditions.checkNotNull(bundle);
-        mBundle = bundle;
-    }
-
-    /**
-     * Returns the {@link Bundle} populated by this builder.
-     * @exportToFramework:hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
     @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    public static final Parcelable.Creator<AppSearchSchema> CREATOR = new AppSearchSchemaCreator();
+
+    @Field(id = 1, getter = "getSchemaType")
+    private final String mSchemaType;
+
+    @Field(id = 2)
+    final List<PropertyConfigParcel> mPropertyConfigParcels;
+
+    @Field(id = 3, getter = "getParentTypes")
+    private final List<String> mParentTypes;
+
+    @Field(id = 4, getter = "getDescription")
+    private final String mDescription;
+
+    @Constructor
+    AppSearchSchema(
+            @Param(id = 1) @NonNull String schemaType,
+            @Param(id = 2) @NonNull List<PropertyConfigParcel> propertyConfigParcels,
+            @Param(id = 3) @NonNull List<String> parentTypes,
+            @Param(id = 4) @NonNull String description) {
+        mSchemaType = Objects.requireNonNull(schemaType);
+        mPropertyConfigParcels = Objects.requireNonNull(propertyConfigParcels);
+        mParentTypes = Objects.requireNonNull(parentTypes);
+        mDescription = Objects.requireNonNull(description);
     }
 
     @Override
@@ -88,7 +107,7 @@
      * Appends a debugging string for the {@link AppSearchSchema} instance to the given string
      * builder.
      *
-     * @param builder     the builder to append to.
+     * @param builder the builder to append to.
      */
     private void appendAppSearchSchemaString(@NonNull IndentingStringBuilder builder) {
         Preconditions.checkNotNull(builder);
@@ -96,6 +115,7 @@
         builder.append("{\n");
         builder.increaseIndentLevel();
         builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
+        builder.append("description: \"").append(getDescription()).append("\",\n");
         builder.append("properties: [\n");
 
         AppSearchSchema.PropertyConfig[] sortedProperties = getProperties()
@@ -121,7 +141,22 @@
     /** Returns the name of this schema type, such as Email. */
     @NonNull
     public String getSchemaType() {
-        return mBundle.getString(SCHEMA_TYPE_FIELD, "");
+        return mSchemaType;
+    }
+
+    /**
+     * Returns a natural language description of this schema type.
+     *
+     * <p>Ex. The description for an Email type could be "A type of electronic message".
+     *
+     * <p>This information is purely to help apps consuming this type to understand its semantic
+     * meaning. This field has no effect in AppSearch - it is just stored with the AppSearchSchema.
+     * If {@link Builder#setDescription} is uncalled, then this method will return an empty string.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+    @NonNull
+    public String getDescription() {
+        return mDescription;
     }
 
     /**
@@ -130,35 +165,25 @@
      * <p>This method creates a new list when called.
      */
     @NonNull
-    @SuppressWarnings({"MixedMutabilityReturnType", "deprecation"})
+    @SuppressWarnings({"MixedMutabilityReturnType"})
     public List<PropertyConfig> getProperties() {
-        ArrayList<Bundle> propertyBundles =
-                mBundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD);
-        if (propertyBundles == null || propertyBundles.isEmpty()) {
+        if (mPropertyConfigParcels.isEmpty()) {
             return Collections.emptyList();
         }
-        List<PropertyConfig> ret = new ArrayList<>(propertyBundles.size());
-        for (int i = 0; i < propertyBundles.size(); i++) {
-            ret.add(PropertyConfig.fromBundle(propertyBundles.get(i)));
+        List<PropertyConfig> ret = new ArrayList<>(mPropertyConfigParcels.size());
+        for (int i = 0; i < mPropertyConfigParcels.size(); i++) {
+            ret.add(PropertyConfig.fromParcel(mPropertyConfigParcels.get(i)));
         }
         return ret;
     }
 
     /**
      * Returns the list of parent types of this schema for polymorphism.
-     *
-     * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
-     * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via Mainline
-     *   are possible.
-     * -->
      */
+    @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES)
     @NonNull
     public List<String> getParentTypes() {
-        List<String> parentTypes = mBundle.getStringArrayList(AppSearchSchema.PARENT_TYPES_FIELD);
-        if (parentTypes == null) {
-            return Collections.emptyList();
-        }
-        return Collections.unmodifiableList(parentTypes);
+        return Collections.unmodifiableList(mParentTypes);
     }
 
     @Override
@@ -173,6 +198,9 @@
         if (!getSchemaType().equals(otherSchema.getSchemaType())) {
             return false;
         }
+        if (!getDescription().equals(otherSchema.getDescription())) {
+            return false;
+        }
         if (!getParentTypes().equals(otherSchema.getParentTypes())) {
             return false;
         }
@@ -181,21 +209,51 @@
 
     @Override
     public int hashCode() {
-        return ObjectsCompat.hash(getSchemaType(), getProperties(), getParentTypes());
+        return ObjectsCompat.hash(
+            getSchemaType(),
+            getProperties(),
+            getParentTypes(),
+            getDescription());
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        AppSearchSchemaCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link AppSearchSchema objects}. */
     public static final class Builder {
         private final String mSchemaType;
-        private ArrayList<Bundle> mPropertyBundles = new ArrayList<>();
+        private String mDescription = "";
+        private ArrayList<PropertyConfigParcel> mPropertyConfigParcels = new ArrayList<>();
         private LinkedHashSet<String> mParentTypes = new LinkedHashSet<>();
         private final Set<String> mPropertyNames = new ArraySet<>();
         private boolean mBuilt = false;
 
         /** Creates a new {@link AppSearchSchema.Builder}. */
         public Builder(@NonNull String schemaType) {
-            Preconditions.checkNotNull(schemaType);
-            mSchemaType = schemaType;
+            mSchemaType = Preconditions.checkNotNull(schemaType);
+        }
+
+        /**
+         * Sets a natural language description of this schema type.
+         *
+         * <p> For more details about the description field, see {@link
+         * AppSearchSchema#getDescription}.
+         */
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SCHEMA_SET_DESCRIPTION)
+        @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+        @CanIgnoreReturnValue
+        @NonNull
+        public AppSearchSchema.Builder setDescription(@NonNull String description) {
+            Objects.requireNonNull(description);
+            resetIfBuilt();
+            mDescription = description;
+            return this;
         }
 
         /** Adds a property to the given type. */
@@ -208,7 +266,7 @@
             if (!mPropertyNames.add(name)) {
                 throw new IllegalSchemaException("Property defined more than once: " + name);
             }
-            mPropertyBundles.add(propertyConfig.mBundle);
+            mPropertyConfigParcels.add(propertyConfig.mPropertyConfigParcel);
             return this;
         }
 
@@ -273,11 +331,9 @@
          */
         @CanIgnoreReturnValue
         @NonNull
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SCHEMA_ADD_PARENT_TYPE)
-        // @exportToFramework:endStrip()
         public AppSearchSchema.Builder addParentType(@NonNull String parentSchemaType) {
             Preconditions.checkNotNull(parentSchemaType);
             resetIfBuilt();
@@ -288,18 +344,16 @@
         /** Constructs a new {@link AppSearchSchema} from the contents of this builder. */
         @NonNull
         public AppSearchSchema build() {
-            Bundle bundle = new Bundle();
-            bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mSchemaType);
-            bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles);
-            bundle.putStringArrayList(AppSearchSchema.PARENT_TYPES_FIELD,
-                    new ArrayList<>(mParentTypes));
             mBuilt = true;
-            return new AppSearchSchema(bundle);
+            return new AppSearchSchema(mSchemaType,
+                    mPropertyConfigParcels,
+                    new ArrayList<>(mParentTypes),
+                    mDescription);
         }
 
         private void resetIfBuilt() {
             if (mBuilt) {
-                mPropertyBundles = new ArrayList<>(mPropertyBundles);
+                mPropertyConfigParcels = new ArrayList<>(mPropertyConfigParcels);
                 mParentTypes = new LinkedHashSet<>(mParentTypes);
                 mBuilt = false;
             }
@@ -313,10 +367,6 @@
      * a property.
      */
     public abstract static class PropertyConfig {
-        static final String NAME_FIELD = "name";
-        static final String DATA_TYPE_FIELD = "dataType";
-        static final String CARDINALITY_FIELD = "cardinality";
-
         /**
          * Physical data-types of the contents of the property.
          *
@@ -333,28 +383,47 @@
                 DATA_TYPE_BOOLEAN,
                 DATA_TYPE_BYTES,
                 DATA_TYPE_DOCUMENT,
+                DATA_TYPE_EMBEDDING,
         })
         @Retention(RetentionPolicy.SOURCE)
-        public @interface DataType {}
+        public @interface DataType {
+        }
 
-        /** @exportToFramework:hide */
+        /**
+         * Constant value for String data type.
+         *
+         * @exportToFramework:hide
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public static final int DATA_TYPE_STRING = 1;
 
-        /** @exportToFramework:hide */
+        /**
+         * Constant value for Long data type.
+         *
+         * @exportToFramework:hide
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public static final int DATA_TYPE_LONG = 2;
 
-        /** @exportToFramework:hide */
+        /**
+         * Constant value for Double data type.
+         *
+         * @exportToFramework:hide
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public static final int DATA_TYPE_DOUBLE = 3;
 
-        /** @exportToFramework:hide */
+        /**
+         * Constant value for Boolean data type.
+         *
+         * @exportToFramework:hide
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public static final int DATA_TYPE_BOOLEAN = 4;
 
         /**
          * Unstructured BLOB.
+         *
          * @exportToFramework:hide
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -364,12 +433,21 @@
          * Indicates that the property is itself a {@link GenericDocument}, making it part of a
          * hierarchical schema. Any property using this DataType MUST have a valid
          * {@link PropertyConfig#getSchemaType}.
+         *
          * @exportToFramework:hide
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public static final int DATA_TYPE_DOCUMENT = 6;
 
         /**
+         * Indicates that the property is an {@link EmbeddingVector}.
+         *
+         * @exportToFramework:hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public static final int DATA_TYPE_EMBEDDING = 7;
+
+        /**
          * The cardinality of the property (whether it is required, optional or repeated).
          *
          * <p>NOTE: The integer values of these constants must match the proto enum constants in
@@ -384,7 +462,8 @@
                 CARDINALITY_REQUIRED,
         })
         @Retention(RetentionPolicy.SOURCE)
-        public @interface Cardinality {}
+        public @interface Cardinality {
+        }
 
         /** Any number of items (including zero) [0...*]. */
         public static final int CARDINALITY_REPEATED = 1;
@@ -395,13 +474,10 @@
         /** Exactly one value [1]. */
         public static final int CARDINALITY_REQUIRED = 3;
 
-        final Bundle mBundle;
+        final PropertyConfigParcel mPropertyConfigParcel;
 
-        @Nullable
-        private Integer mHashCode;
-
-        PropertyConfig(@NonNull Bundle bundle) {
-            mBundle = Preconditions.checkNotNull(bundle);
+        PropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            mPropertyConfigParcel = Preconditions.checkNotNull(propertyConfigParcel);
         }
 
         @Override
@@ -416,7 +492,7 @@
          * Appends a debug string for the {@link AppSearchSchema.PropertyConfig} instance to the
          * given string builder.
          *
-         * @param builder        the builder to append to.
+         * @param builder the builder to append to.
          */
         void appendPropertyConfigString(@NonNull IndentingStringBuilder builder) {
             Preconditions.checkNotNull(builder);
@@ -424,6 +500,7 @@
             builder.append("{\n");
             builder.increaseIndentLevel();
             builder.append("name: \"").append(getName()).append("\",\n");
+            builder.append("description: \"").append(getDescription()).append("\",\n");
 
             if (this instanceof AppSearchSchema.StringPropertyConfig) {
                 ((StringPropertyConfig) this)
@@ -469,6 +546,9 @@
                 case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT:
                     builder.append("dataType: DATA_TYPE_DOCUMENT,\n");
                     break;
+                case PropertyConfig.DATA_TYPE_EMBEDDING:
+                    builder.append("dataType: DATA_TYPE_EMBEDDING,\n");
+                    break;
                 default:
                     builder.append("dataType: DATA_TYPE_UNKNOWN,\n");
             }
@@ -479,7 +559,24 @@
         /** Returns the name of this property. */
         @NonNull
         public String getName() {
-            return mBundle.getString(NAME_FIELD, "");
+            return mPropertyConfigParcel.getName();
+        }
+
+        /**
+         * Returns a natural language description of this property.
+         *
+         * <p>Ex. The description for the "homeAddress" property of a "Person" type could be "the
+         * address at which this person lives".
+         *
+         * <p>This information is purely to help apps consuming this type the semantic meaning of
+         * its properties. This field has no effect in AppSearch - it is just stored with the
+         * AppSearchSchema. If the description is not set, then this method will return an empty
+         * string.
+         */
+        @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+        @NonNull
+        public String getDescription() {
+            return mPropertyConfigParcel.getDescription();
         }
 
         /**
@@ -490,7 +587,7 @@
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @DataType
         public int getDataType() {
-            return mBundle.getInt(DATA_TYPE_FIELD, -1);
+            return mPropertyConfigParcel.getDataType();
         }
 
         /**
@@ -498,7 +595,7 @@
          */
         @Cardinality
         public int getCardinality() {
-            return mBundle.getInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL);
+            return mPropertyConfigParcel.getCardinality();
         }
 
         @Override
@@ -510,15 +607,12 @@
                 return false;
             }
             PropertyConfig otherProperty = (PropertyConfig) other;
-            return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle);
+            return ObjectsCompat.equals(mPropertyConfigParcel, otherProperty.mPropertyConfigParcel);
         }
 
         @Override
         public int hashCode() {
-            if (mHashCode == null) {
-                mHashCode = BundleUtil.deepHashCode(mBundle);
-            }
-            return mHashCode;
+            return mPropertyConfigParcel.hashCode();
         }
 
         /**
@@ -528,41 +622,40 @@
          * <p>The bundle is not cloned.
          *
          * @throws IllegalArgumentException if the bundle does no contain a recognized
-         * value in its {@code DATA_TYPE_FIELD}.
+         *                                  value in its {@code DATA_TYPE_FIELD}.
          * @exportToFramework:hide
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @NonNull
-        public static PropertyConfig fromBundle(@NonNull Bundle propertyBundle) {
-            switch (propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)) {
+        public static PropertyConfig fromParcel(
+                @NonNull PropertyConfigParcel propertyConfigParcel) {
+            Preconditions.checkNotNull(propertyConfigParcel);
+            switch (propertyConfigParcel.getDataType()) {
                 case PropertyConfig.DATA_TYPE_STRING:
-                    return new StringPropertyConfig(propertyBundle);
+                    return new StringPropertyConfig(propertyConfigParcel);
                 case PropertyConfig.DATA_TYPE_LONG:
-                    return new LongPropertyConfig(propertyBundle);
+                    return new LongPropertyConfig(propertyConfigParcel);
                 case PropertyConfig.DATA_TYPE_DOUBLE:
-                    return new DoublePropertyConfig(propertyBundle);
+                    return new DoublePropertyConfig(propertyConfigParcel);
                 case PropertyConfig.DATA_TYPE_BOOLEAN:
-                    return new BooleanPropertyConfig(propertyBundle);
+                    return new BooleanPropertyConfig(propertyConfigParcel);
                 case PropertyConfig.DATA_TYPE_BYTES:
-                    return new BytesPropertyConfig(propertyBundle);
+                    return new BytesPropertyConfig(propertyConfigParcel);
                 case PropertyConfig.DATA_TYPE_DOCUMENT:
-                    return new DocumentPropertyConfig(propertyBundle);
+                    return new DocumentPropertyConfig(propertyConfigParcel);
+                case PropertyConfig.DATA_TYPE_EMBEDDING:
+                    return new EmbeddingPropertyConfig(propertyConfigParcel);
                 default:
                     throw new IllegalArgumentException(
                             "Unsupported property bundle of type "
-                                    + propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)
-                                    + "; contents: " + propertyBundle);
+                                    + propertyConfigParcel.getDataType()
+                                    + "; contents: " + propertyConfigParcel);
             }
         }
     }
 
     /** Configuration for a property of type String in a Document. */
     public static final class StringPropertyConfig extends PropertyConfig {
-        private static final String INDEXING_TYPE_FIELD = "indexingType";
-        private static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
-        private static final String JOINABLE_VALUE_TYPE_FIELD = "joinableValueType";
-        private static final String DELETION_PROPAGATION_FIELD = "deletionPropagation";
-
         /**
          * Encapsulates the configurations on how AppSearch should query/index these terms.
          * @exportToFramework:hide
@@ -574,7 +667,8 @@
                 INDEXING_TYPE_PREFIXES,
         })
         @Retention(RetentionPolicy.SOURCE)
-        public @interface IndexingType {}
+        public @interface IndexingType {
+        }
 
         /** Content in this property will not be tokenized or indexed. */
         public static final int INDEXING_TYPE_NONE = 0;
@@ -611,7 +705,8 @@
                 TOKENIZER_TYPE_RFC822
         })
         @Retention(RetentionPolicy.SOURCE)
-        public @interface TokenizerType {}
+        public @interface TokenizerType {
+        }
 
         /**
          * This value indicates that no tokens should be extracted from this property.
@@ -646,11 +741,9 @@
          * <p>It is only valid for tokenizer_type to be 'VERBATIM' if {@link #getIndexingType} is
          * {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}.
          */
-// @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.VERBATIM_SEARCH)
-// @exportToFramework:endStrip()
         public static final int TOKENIZER_TYPE_VERBATIM = 2;
 
         /**
@@ -663,11 +756,9 @@
          * <p>It is only valid for tokenizer_type to be 'RFC822' if {@link #getIndexingType} is
          * {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}.
          */
-// @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.TOKENIZER_TYPE_RFC822)
-// @exportToFramework:endStrip()
         public static final int TOKENIZER_TYPE_RFC822 = 3;
 
         /**
@@ -684,7 +775,8 @@
         })
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @Retention(RetentionPolicy.SOURCE)
-        public @interface JoinableValueType {}
+        public @interface JoinableValueType {
+        }
 
         /** Content in this property is not joinable. */
         public static final int JOINABLE_VALUE_TYPE_NONE = 0;
@@ -700,27 +792,37 @@
          *     {@link PropertyConfig#CARDINALITY_REQUIRED}.
          * </ul>
          */
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.JOIN_SPEC_AND_QUALIFIED_ID)
-        // @exportToFramework:endStrip()
         public static final int JOINABLE_VALUE_TYPE_QUALIFIED_ID = 1;
 
-        StringPropertyConfig(@NonNull Bundle bundle) {
-            super(bundle);
+        StringPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            super(propertyConfigParcel);
         }
 
         /** Returns how the property is indexed. */
-        @IndexingType
+        @StringPropertyConfig.IndexingType
         public int getIndexingType() {
-            return mBundle.getInt(INDEXING_TYPE_FIELD);
+            StringIndexingConfigParcel indexingConfigParcel =
+                    mPropertyConfigParcel.getStringIndexingConfigParcel();
+            if (indexingConfigParcel == null) {
+                return INDEXING_TYPE_NONE;
+            }
+
+            return indexingConfigParcel.getIndexingType();
         }
 
         /** Returns how this property is tokenized (split into words). */
         @TokenizerType
         public int getTokenizerType() {
-            return mBundle.getInt(TOKENIZER_TYPE_FIELD);
+            StringIndexingConfigParcel indexingConfigParcel =
+                    mPropertyConfigParcel.getStringIndexingConfigParcel();
+            if (indexingConfigParcel == null) {
+                return TOKENIZER_TYPE_NONE;
+            }
+
+            return indexingConfigParcel.getTokenizerType();
         }
 
         /**
@@ -728,27 +830,27 @@
          */
         @JoinableValueType
         public int getJoinableValueType() {
-            return mBundle.getInt(JOINABLE_VALUE_TYPE_FIELD, JOINABLE_VALUE_TYPE_NONE);
-        }
+            JoinableConfigParcel joinableConfigParcel = mPropertyConfigParcel
+                    .getJoinableConfigParcel();
+            if (joinableConfigParcel == null) {
+                return JOINABLE_VALUE_TYPE_NONE;
+            }
 
-        /**
-         * Returns whether or not documents in this schema should be deleted when the document
-         * referenced by this field is deleted.
-         *
-         * @see JoinSpec
-         * @<!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()hide-->
-         */
-        public boolean getDeletionPropagation() {
-            return mBundle.getBoolean(DELETION_PROPAGATION_FIELD, false);
+            return joinableConfigParcel.getJoinableValueType();
         }
 
         /** Builder for {@link StringPropertyConfig}. */
         public static final class Builder {
             private final String mPropertyName;
-            @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
-            @IndexingType private int mIndexingType = INDEXING_TYPE_NONE;
-            @TokenizerType private int mTokenizerType = TOKENIZER_TYPE_NONE;
-            @JoinableValueType private int mJoinableValueType = JOINABLE_VALUE_TYPE_NONE;
+            private String mDescription = "";
+            @Cardinality
+            private int mCardinality = CARDINALITY_OPTIONAL;
+            @StringPropertyConfig.IndexingType
+            private int mIndexingType = INDEXING_TYPE_NONE;
+            @TokenizerType
+            private int mTokenizerType = TOKENIZER_TYPE_NONE;
+            @JoinableValueType
+            private int mJoinableValueType = JOINABLE_VALUE_TYPE_NONE;
             private boolean mDeletionPropagation = false;
 
             /** Creates a new {@link StringPropertyConfig.Builder}. */
@@ -757,6 +859,24 @@
             }
 
             /**
+             * Sets a natural language description of this property.
+             *
+             * <p> For more details about the description field, see {@link
+             * AppSearchSchema.PropertyConfig#getDescription}.
+             */
+            @CanIgnoreReturnValue
+            @RequiresFeature(
+                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                    name = Features.SCHEMA_SET_DESCRIPTION)
+            @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public StringPropertyConfig.Builder setDescription(@NonNull String description) {
+                mDescription = Objects.requireNonNull(description);
+                return this;
+            }
+
+            /**
              * Sets the cardinality of the property (whether it is optional, required or repeated).
              *
              * <p>If this method is not called, the default cardinality is
@@ -781,7 +901,8 @@
              */
             @CanIgnoreReturnValue
             @NonNull
-            public StringPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
+            public StringPropertyConfig.Builder setIndexingType(
+                    @StringPropertyConfig.IndexingType int indexingType) {
                 Preconditions.checkArgumentInRange(
                         indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType");
                 mIndexingType = indexingType;
@@ -830,25 +951,6 @@
             }
 
             /**
-             * Configures whether or not documents in this schema will be removed when the document
-             * referred to by this property is deleted.
-             *
-             * <p> Requires that a joinable value type is set.
-             * @<!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()hide-->
-             */
-            @SuppressWarnings("MissingGetterMatchingBuilder")  // getDeletionPropagation
-            @NonNull
-            // @exportToFramework:startStrip()
-            @RequiresFeature(
-                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
-                    name = Features.SCHEMA_SET_DELETION_PROPAGATION)
-            // @exportToFramework:endStrip()
-            public Builder setDeletionPropagation(boolean deletionPropagation) {
-                mDeletionPropagation = deletionPropagation;
-                return this;
-            }
-
-            /**
              * Constructs a new {@link StringPropertyConfig} from the contents of this builder.
              */
             @NonNull
@@ -868,15 +970,17 @@
                     Preconditions.checkState(!mDeletionPropagation, "Cannot set deletion "
                             + "propagation without setting a joinable value type");
                 }
-                Bundle bundle = new Bundle();
-                bundle.putString(NAME_FIELD, mPropertyName);
-                bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_STRING);
-                bundle.putInt(CARDINALITY_FIELD, mCardinality);
-                bundle.putInt(INDEXING_TYPE_FIELD, mIndexingType);
-                bundle.putInt(TOKENIZER_TYPE_FIELD, mTokenizerType);
-                bundle.putInt(JOINABLE_VALUE_TYPE_FIELD, mJoinableValueType);
-                bundle.putBoolean(DELETION_PROPAGATION_FIELD, mDeletionPropagation);
-                return new StringPropertyConfig(bundle);
+                PropertyConfigParcel.StringIndexingConfigParcel stringConfigParcel =
+                        new StringIndexingConfigParcel(mIndexingType, mTokenizerType);
+                JoinableConfigParcel joinableConfigParcel =
+                        new JoinableConfigParcel(mJoinableValueType, mDeletionPropagation);
+                return new StringPropertyConfig(
+                        PropertyConfigParcel.createForString(
+                                mPropertyName,
+                                mDescription,
+                                mCardinality,
+                                stringConfigParcel,
+                                joinableConfigParcel));
             }
         }
 
@@ -886,7 +990,7 @@
          *
          * <p>This appends fields specific to a {@link StringPropertyConfig} instance.
          *
-         * @param builder        the builder to append to.
+         * @param builder the builder to append to.
          */
         void appendStringPropertyConfigFields(@NonNull IndentingStringBuilder builder) {
             switch (getIndexingType()) {
@@ -935,11 +1039,10 @@
 
     /** Configuration for a property containing a 64-bit integer. */
     public static final class LongPropertyConfig extends PropertyConfig {
-        private static final String INDEXING_TYPE_FIELD = "indexingType";
-
         /**
          * Encapsulates the configurations on how AppSearch should query/index these 64-bit
          * integers.
+         *
          * @exportToFramework:hide
          */
         @IntDef(value = {
@@ -948,7 +1051,8 @@
         })
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @Retention(RetentionPolicy.SOURCE)
-        public @interface IndexingType {}
+        public @interface IndexingType {
+        }
 
         /** Content in this property will not be indexed. */
         public static final int INDEXING_TYPE_NONE = 0;
@@ -959,28 +1063,34 @@
          *
          * <p>For example, a property with 1024 should match numeric search range query [0, 2000].
          */
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.NUMERIC_SEARCH)
-        // @exportToFramework:endStrip()
         public static final int INDEXING_TYPE_RANGE = 1;
 
-        LongPropertyConfig(@NonNull Bundle bundle) {
-            super(bundle);
+        LongPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            super(propertyConfigParcel);
         }
 
         /** Returns how the property is indexed. */
-        @IndexingType
+        @LongPropertyConfig.IndexingType
         public int getIndexingType() {
-            return mBundle.getInt(INDEXING_TYPE_FIELD, INDEXING_TYPE_NONE);
+            PropertyConfigParcel.IntegerIndexingConfigParcel indexingConfigParcel =
+                    mPropertyConfigParcel.getIntegerIndexingConfigParcel();
+            if (indexingConfigParcel == null) {
+                return INDEXING_TYPE_NONE;
+            }
+            return indexingConfigParcel.getIndexingType();
         }
 
         /** Builder for {@link LongPropertyConfig}. */
         public static final class Builder {
             private final String mPropertyName;
-            @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
-            @IndexingType private int mIndexingType = INDEXING_TYPE_NONE;
+            private String mDescription = "";
+            @Cardinality
+            private int mCardinality = CARDINALITY_OPTIONAL;
+            @LongPropertyConfig.IndexingType
+            private int mIndexingType = INDEXING_TYPE_NONE;
 
             /** Creates a new {@link LongPropertyConfig.Builder}. */
             public Builder(@NonNull String propertyName) {
@@ -988,6 +1098,24 @@
             }
 
             /**
+             * Sets a natural language description of this property.
+             *
+             * <p> For more details about the description field, see {@link
+             * AppSearchSchema.PropertyConfig#getDescription}.
+             */
+            @CanIgnoreReturnValue
+            @RequiresFeature(
+                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                    name = Features.SCHEMA_SET_DESCRIPTION)
+            @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public LongPropertyConfig.Builder setDescription(@NonNull String description) {
+                mDescription = Objects.requireNonNull(description);
+                return this;
+            }
+
+            /**
              * Sets the cardinality of the property (whether it is optional, required or repeated).
              *
              * <p>If this method is not called, the default cardinality is
@@ -1012,7 +1140,8 @@
              */
             @CanIgnoreReturnValue
             @NonNull
-            public LongPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
+            public LongPropertyConfig.Builder setIndexingType(
+                    @LongPropertyConfig.IndexingType int indexingType) {
                 Preconditions.checkArgumentInRange(
                         indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_RANGE, "indexingType");
                 mIndexingType = indexingType;
@@ -1022,12 +1151,9 @@
             /** Constructs a new {@link LongPropertyConfig} from the contents of this builder. */
             @NonNull
             public LongPropertyConfig build() {
-                Bundle bundle = new Bundle();
-                bundle.putString(NAME_FIELD, mPropertyName);
-                bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_LONG);
-                bundle.putInt(CARDINALITY_FIELD, mCardinality);
-                bundle.putInt(INDEXING_TYPE_FIELD, mIndexingType);
-                return new LongPropertyConfig(bundle);
+                return new LongPropertyConfig(
+                        PropertyConfigParcel.createForLong(
+                                mPropertyName, mDescription, mCardinality, mIndexingType));
             }
         }
 
@@ -1037,7 +1163,7 @@
          *
          * <p>This appends fields specific to a {@link LongPropertyConfig} instance.
          *
-         * @param builder        the builder to append to.
+         * @param builder the builder to append to.
          */
         void appendLongPropertyConfigFields(@NonNull IndentingStringBuilder builder) {
             switch (getIndexingType()) {
@@ -1055,14 +1181,16 @@
 
     /** Configuration for a property containing a double-precision decimal number. */
     public static final class DoublePropertyConfig extends PropertyConfig {
-        DoublePropertyConfig(@NonNull Bundle bundle) {
-            super(bundle);
+        DoublePropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            super(propertyConfigParcel);
         }
 
         /** Builder for {@link DoublePropertyConfig}. */
         public static final class Builder {
             private final String mPropertyName;
-            @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
+            private String mDescription = "";
+            @Cardinality
+            private int mCardinality = CARDINALITY_OPTIONAL;
 
             /** Creates a new {@link DoublePropertyConfig.Builder}. */
             public Builder(@NonNull String propertyName) {
@@ -1070,6 +1198,24 @@
             }
 
             /**
+             * Sets a natural language description of this property.
+             *
+             * <p> For more details about the description field, see {@link
+             * AppSearchSchema.PropertyConfig#getDescription}.
+             */
+            @CanIgnoreReturnValue
+            @RequiresFeature(
+                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                    name = Features.SCHEMA_SET_DESCRIPTION)
+            @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public DoublePropertyConfig.Builder setDescription(@NonNull String description) {
+                mDescription = Objects.requireNonNull(description);
+                return this;
+            }
+
+            /**
              * Sets the cardinality of the property (whether it is optional, required or repeated).
              *
              * <p>If this method is not called, the default cardinality is
@@ -1088,25 +1234,25 @@
             /** Constructs a new {@link DoublePropertyConfig} from the contents of this builder. */
             @NonNull
             public DoublePropertyConfig build() {
-                Bundle bundle = new Bundle();
-                bundle.putString(NAME_FIELD, mPropertyName);
-                bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOUBLE);
-                bundle.putInt(CARDINALITY_FIELD, mCardinality);
-                return new DoublePropertyConfig(bundle);
+                return new DoublePropertyConfig(
+                        PropertyConfigParcel.createForDouble(
+                                mPropertyName, mDescription, mCardinality));
             }
         }
     }
 
     /** Configuration for a property containing a boolean. */
     public static final class BooleanPropertyConfig extends PropertyConfig {
-        BooleanPropertyConfig(@NonNull Bundle bundle) {
-            super(bundle);
+        BooleanPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            super(propertyConfigParcel);
         }
 
         /** Builder for {@link BooleanPropertyConfig}. */
         public static final class Builder {
             private final String mPropertyName;
-            @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
+            private String mDescription = "";
+            @Cardinality
+            private int mCardinality = CARDINALITY_OPTIONAL;
 
             /** Creates a new {@link BooleanPropertyConfig.Builder}. */
             public Builder(@NonNull String propertyName) {
@@ -1114,6 +1260,24 @@
             }
 
             /**
+              Sets a natural language description of this property.
+             *
+             * <p> For more details about the description field, see {@link
+             * AppSearchSchema.PropertyConfig#getDescription}.
+             */
+            @CanIgnoreReturnValue
+            @RequiresFeature(
+                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                    name = Features.SCHEMA_SET_DESCRIPTION)
+            @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public BooleanPropertyConfig.Builder setDescription(@NonNull String description) {
+                mDescription = Objects.requireNonNull(description);
+                return this;
+            }
+
+            /**
              * Sets the cardinality of the property (whether it is optional, required or repeated).
              *
              * <p>If this method is not called, the default cardinality is
@@ -1132,25 +1296,25 @@
             /** Constructs a new {@link BooleanPropertyConfig} from the contents of this builder. */
             @NonNull
             public BooleanPropertyConfig build() {
-                Bundle bundle = new Bundle();
-                bundle.putString(NAME_FIELD, mPropertyName);
-                bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BOOLEAN);
-                bundle.putInt(CARDINALITY_FIELD, mCardinality);
-                return new BooleanPropertyConfig(bundle);
+                return new BooleanPropertyConfig(
+                        PropertyConfigParcel.createForBoolean(
+                                mPropertyName, mDescription, mCardinality));
             }
         }
     }
 
     /** Configuration for a property containing a byte array. */
     public static final class BytesPropertyConfig extends PropertyConfig {
-        BytesPropertyConfig(@NonNull Bundle bundle) {
-            super(bundle);
+        BytesPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            super(propertyConfigParcel);
         }
 
         /** Builder for {@link BytesPropertyConfig}. */
         public static final class Builder {
             private final String mPropertyName;
-            @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
+            private String mDescription = "";
+            @Cardinality
+            private int mCardinality = CARDINALITY_OPTIONAL;
 
             /** Creates a new {@link BytesPropertyConfig.Builder}. */
             public Builder(@NonNull String propertyName) {
@@ -1158,6 +1322,24 @@
             }
 
             /**
+             * Sets a natural language description of this property.
+             *
+             * <p> For more details about the description field, see {@link
+             * AppSearchSchema.PropertyConfig#getDescription}.
+             */
+            @CanIgnoreReturnValue
+            @RequiresFeature(
+                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                    name = Features.SCHEMA_SET_DESCRIPTION)
+            @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public BytesPropertyConfig.Builder setDescription(@NonNull String description) {
+                mDescription = Objects.requireNonNull(description);
+                return this;
+            }
+
+            /**
              * Sets the cardinality of the property (whether it is optional, required or repeated).
              *
              * <p>If this method is not called, the default cardinality is
@@ -1178,30 +1360,23 @@
              */
             @NonNull
             public BytesPropertyConfig build() {
-                Bundle bundle = new Bundle();
-                bundle.putString(NAME_FIELD, mPropertyName);
-                bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BYTES);
-                bundle.putInt(CARDINALITY_FIELD, mCardinality);
-                return new BytesPropertyConfig(bundle);
+                return new BytesPropertyConfig(
+                        PropertyConfigParcel.createForBytes(
+                                mPropertyName, mDescription, mCardinality));
             }
         }
     }
 
     /** Configuration for a property containing another Document. */
     public static final class DocumentPropertyConfig extends PropertyConfig {
-        private static final String SCHEMA_TYPE_FIELD = "schemaType";
-        private static final String INDEX_NESTED_PROPERTIES_FIELD = "indexNestedProperties";
-        private static final String INDEXABLE_NESTED_PROPERTIES_LIST_FIELD =
-                "indexableNestedPropertiesList";
-
-        DocumentPropertyConfig(@NonNull Bundle bundle) {
-            super(bundle);
+        DocumentPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            super(propertyConfigParcel);
         }
 
         /** Returns the logical schema-type of the contents of this document property. */
         @NonNull
         public String getSchemaType() {
-            return Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
+            return Preconditions.checkNotNull(mPropertyConfigParcel.getSchemaType());
         }
 
         /**
@@ -1215,24 +1390,33 @@
          * indexing a subset of properties from the nested document.
          */
         public boolean shouldIndexNestedProperties() {
-            return mBundle.getBoolean(INDEX_NESTED_PROPERTIES_FIELD);
+            DocumentIndexingConfigParcel indexingConfigParcel =
+                    mPropertyConfigParcel.getDocumentIndexingConfigParcel();
+            if (indexingConfigParcel == null) {
+                return false;
+            }
+
+            return indexingConfigParcel.shouldIndexNestedProperties();
         }
 
         /**
          * Returns the list of indexable nested properties for the nested document.
-         *
-         * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
-         * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
-         *   Mainline are possible.
-         * -->
          */
+        @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES)
         @NonNull
         public List<String> getIndexableNestedProperties() {
+            DocumentIndexingConfigParcel indexingConfigParcel =
+                    mPropertyConfigParcel.getDocumentIndexingConfigParcel();
+            if (indexingConfigParcel == null) {
+                return Collections.emptyList();
+            }
+
             List<String> indexableNestedPropertiesList =
-                    mBundle.getStringArrayList(INDEXABLE_NESTED_PROPERTIES_LIST_FIELD);
+                    indexingConfigParcel.getIndexableNestedPropertiesList();
             if (indexableNestedPropertiesList == null) {
                 return Collections.emptyList();
             }
+
             return Collections.unmodifiableList(indexableNestedPropertiesList);
         }
 
@@ -1240,7 +1424,9 @@
         public static final class Builder {
             private final String mPropertyName;
             private final String mSchemaType;
-            @Cardinality private int mCardinality = CARDINALITY_OPTIONAL;
+            private String mDescription = "";
+            @Cardinality
+            private int mCardinality = CARDINALITY_OPTIONAL;
             private boolean mShouldIndexNestedProperties = false;
             private final Set<String> mIndexableNestedPropertiesList = new ArraySet<>();
 
@@ -1250,9 +1436,9 @@
              * @param propertyName The logical name of the property in the schema, which will be
              *                     used as the key for this property in
              *                     {@link GenericDocument.Builder#setPropertyDocument}.
-             * @param schemaType The type of documents which will be stored in this property.
-             *                   Documents of different types cannot be mixed into a single
-             *                   property.
+             * @param schemaType   The type of documents which will be stored in this property.
+             *                     Documents of different types cannot be mixed into a single
+             *                     property.
              */
             public Builder(@NonNull String propertyName, @NonNull String schemaType) {
                 mPropertyName = Preconditions.checkNotNull(propertyName);
@@ -1260,6 +1446,24 @@
             }
 
             /**
+             * Sets a natural language description of this property.
+             *
+             * <p> For more details about the description field, see {@link
+             * AppSearchSchema.PropertyConfig#getDescription}.
+             */
+            @CanIgnoreReturnValue
+            @RequiresFeature(
+                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                    name = Features.SCHEMA_SET_DESCRIPTION)
+            @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public DocumentPropertyConfig.Builder setDescription(@NonNull String description) {
+                mDescription = Objects.requireNonNull(description);
+                return this;
+            }
+
+            /**
              * Sets the cardinality of the property (whether it is optional, required or repeated).
              *
              * <p>If this method is not called, the default cardinality is
@@ -1297,19 +1501,13 @@
              * Adds one or more properties for indexing from the nested document property.
              *
              * @see #addIndexableNestedProperties(Collection)
-             *
-             * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
-             * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
-             *   Mainline are possible.
-             * -->
              */
+            @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES)
             @CanIgnoreReturnValue
             @NonNull
-            // @exportToFramework:startStrip()
             @RequiresFeature(
                     enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                     name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)
-            // @exportToFramework:endStrip()
             public DocumentPropertyConfig.Builder addIndexableNestedProperties(
                     @NonNull String... indexableNestedProperties) {
                 Preconditions.checkNotNull(indexableNestedProperties);
@@ -1320,20 +1518,14 @@
              * Adds one or more property paths for indexing from the nested document property.
              *
              * @see #addIndexableNestedProperties(Collection)
-             *
-             * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
-             * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
-             *   Mainline are possible.
-             * -->
              */
+            @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES)
             @CanIgnoreReturnValue
             @SuppressLint("MissingGetterMatchingBuilder")
             @NonNull
-            // @exportToFramework:startStrip()
             @RequiresFeature(
                     enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                     name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)
-            // @exportToFramework:endStrip()
             public DocumentPropertyConfig.Builder addIndexableNestedPropertyPaths(
                     @NonNull PropertyPath... indexableNestedPropertyPaths) {
                 Preconditions.checkNotNull(indexableNestedPropertyPaths);
@@ -1371,11 +1563,9 @@
              */
             @CanIgnoreReturnValue
             @NonNull
-            // @exportToFramework:startStrip()
             @RequiresFeature(
                     enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                     name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)
-            // @exportToFramework:endStrip()
             public DocumentPropertyConfig.Builder addIndexableNestedProperties(
                     @NonNull Collection<String> indexableNestedProperties) {
                 Preconditions.checkNotNull(indexableNestedProperties);
@@ -1387,20 +1577,14 @@
              * Adds one or more property paths for indexing from the nested document property.
              *
              * @see #addIndexableNestedProperties(Collection)
-             *
-             * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
-             * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
-             *   Mainline are possible.
-             * -->
              */
+            @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES)
             @CanIgnoreReturnValue
             @SuppressLint("MissingGetterMatchingBuilder")
             @NonNull
-            // @exportToFramework:startStrip()
             @RequiresFeature(
                     enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                     name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)
-            // @exportToFramework:endStrip()
             public DocumentPropertyConfig.Builder addIndexableNestedPropertyPaths(
                     @NonNull Collection<PropertyPath> indexableNestedPropertyPaths) {
                 Preconditions.checkNotNull(indexableNestedPropertyPaths);
@@ -1426,15 +1610,14 @@
                                     + "to be false when one or more indexableNestedProperties are "
                                     + "provided.");
                 }
-                Bundle bundle = new Bundle();
-                bundle.putString(NAME_FIELD, mPropertyName);
-                bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT);
-                bundle.putInt(CARDINALITY_FIELD, mCardinality);
-                bundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, mShouldIndexNestedProperties);
-                bundle.putStringArrayList(INDEXABLE_NESTED_PROPERTIES_LIST_FIELD,
-                        new ArrayList<>(mIndexableNestedPropertiesList));
-                bundle.putString(SCHEMA_TYPE_FIELD, mSchemaType);
-                return new DocumentPropertyConfig(bundle);
+                return new DocumentPropertyConfig(
+                        PropertyConfigParcel.createForDocument(
+                                mPropertyName,
+                                mDescription,
+                                mCardinality,
+                                mSchemaType,
+                                new DocumentIndexingConfigParcel(mShouldIndexNestedProperties,
+                                        new ArrayList<>(mIndexableNestedPropertiesList))));
             }
         }
 
@@ -1444,7 +1627,7 @@
          *
          * <p>This appends fields specific to a {@link DocumentPropertyConfig} instance.
          *
-         * @param builder        the builder to append to.
+         * @param builder the builder to append to.
          */
         void appendDocumentPropertyConfigFields(@NonNull IndentingStringBuilder builder) {
             builder
@@ -1459,4 +1642,133 @@
             builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
         }
     }
+
+    /**
+     * Configuration for a property of type {@link EmbeddingVector} in a Document.
+     */
+    @RequiresFeature(
+            enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+            name = Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public static final class EmbeddingPropertyConfig extends PropertyConfig {
+        /**
+         * Encapsulates the configurations on how AppSearch should query/index these embedding
+         * vectors.
+         *
+         * @exportToFramework:hide
+         */
+        @IntDef(value = {
+                INDEXING_TYPE_NONE,
+                INDEXING_TYPE_SIMILARITY
+        })
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface IndexingType {
+        }
+
+        /** Content in this property will not be indexed. */
+        public static final int INDEXING_TYPE_NONE = 0;
+
+        /**
+         * Embedding vectors in this property will be indexed.
+         *
+         * <p>The index offers 100% accuracy, but has linear time complexity based on the number
+         * of embedding vectors within the index.
+         */
+        public static final int INDEXING_TYPE_SIMILARITY = 1;
+
+        EmbeddingPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) {
+            super(propertyConfigParcel);
+        }
+
+        /** Returns how the property is indexed. */
+        @EmbeddingPropertyConfig.IndexingType
+        public int getIndexingType() {
+            PropertyConfigParcel.EmbeddingIndexingConfigParcel indexingConfigParcel =
+                    mPropertyConfigParcel.getEmbeddingIndexingConfigParcel();
+            if (indexingConfigParcel == null) {
+                return INDEXING_TYPE_NONE;
+            }
+            return indexingConfigParcel.getIndexingType();
+        }
+
+        /** Builder for {@link EmbeddingPropertyConfig}. */
+        @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        public static final class Builder {
+            private final String mPropertyName;
+            private String mDescription = "";
+            @Cardinality
+            private int mCardinality = CARDINALITY_OPTIONAL;
+            @EmbeddingPropertyConfig.IndexingType
+            private int mIndexingType = INDEXING_TYPE_NONE;
+
+            /** Creates a new {@link EmbeddingPropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mPropertyName = Preconditions.checkNotNull(propertyName);
+            }
+
+            /**
+             * Sets a natural language description of this property.
+             *
+             * <p> For more details about the description field, see {@link
+             * AppSearchSchema.PropertyConfig#getDescription}.
+             */
+            @CanIgnoreReturnValue
+            @RequiresFeature(
+                    enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                    name = Features.SCHEMA_SET_DESCRIPTION)
+            @FlaggedApi(Flags.FLAG_ENABLE_APP_FUNCTIONS)
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public EmbeddingPropertyConfig.Builder setDescription(@NonNull String description) {
+                mDescription = Objects.requireNonNull(description);
+                return this;
+            }
+
+            /**
+             * Sets the cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>If this method is not called, the default cardinality is
+             * {@link PropertyConfig#CARDINALITY_OPTIONAL}.
+             */
+            @CanIgnoreReturnValue
+            @SuppressWarnings("MissingGetterMatchingBuilder")  // getter defined in superclass
+            @NonNull
+            public EmbeddingPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mCardinality = cardinality;
+                return this;
+            }
+
+            /**
+             * Configures how a property should be indexed so that it can be retrieved by queries.
+             *
+             * <p>If this method is not called, the default indexing type is
+             * {@link EmbeddingPropertyConfig#INDEXING_TYPE_NONE}, so that it will not be indexed
+             * and cannot be matched by queries.
+             */
+            @CanIgnoreReturnValue
+            @NonNull
+            public EmbeddingPropertyConfig.Builder setIndexingType(
+                    @EmbeddingPropertyConfig.IndexingType int indexingType) {
+                Preconditions.checkArgumentInRange(
+                        indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_SIMILARITY,
+                        "indexingType");
+                mIndexingType = indexingType;
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link EmbeddingPropertyConfig} from the contents of this
+             * builder.
+             */
+            @NonNull
+            public EmbeddingPropertyConfig build() {
+                return new EmbeddingPropertyConfig(
+                        PropertyConfigParcel.createForEmbedding(
+                                mPropertyName, mDescription, mCardinality, mIndexingType));
+            }
+        }
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
index 05d1815..58821b7 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSession.java
@@ -176,7 +176,7 @@
      * <p>The newly added custom functions covered by this feature are:
      * <ul>
      *     <li>createList(String...)</li>
-     *     <li>search(String, List<String>)</li>
+     *     <li>search(String, {@code List<String>})</li>
      *     <li>propertyDefined(String)</li>
      * </ul>
      *
@@ -187,13 +187,14 @@
      * query language and an optional list of strings that specify the properties to be
      * restricted to. This exists as a convenience for multiple property restricts. So,
      * for example, the query `(subject:foo OR body:foo) (subject:bar OR body:bar)`
-     * could be rewritten as `search("foo bar", createList("subject", "bar"))`.
+     * could be rewritten as `search("foo bar", createList("subject", "body"))`.
      *
      * <p>propertyDefined takes a string specifying the property of interest and matches all
      * documents of any type that defines the specified property
      * (ex. `propertyDefined("sender.name")`). Note that propertyDefined will match so long as
-     * the document's type defines the specified property. It does NOT require that the document
-     * actually hold any values for this property.
+     * the document's type defines the specified property. Unlike the "hasProperty" function
+     * below, this function does NOT require that the document actually hold any values for this
+     * property.
      *
      * <p>{@link Features#NUMERIC_SEARCH}: This feature covers numeric search expressions. In the
      * query language, the values of properties that have
@@ -209,6 +210,68 @@
      *
      * <p>Ex. `"foo/bar" OR baz` will ensure that 'foo/bar' is treated as a single 'verbatim' token.
      *
+     * <p>{@link Features#LIST_FILTER_HAS_PROPERTY_FUNCTION}: This feature covers the
+     * "hasProperty" function in query expressions, which takes a string specifying the property
+     * of interest and matches all documents that hold values for this property. Not to be
+     * confused with the "propertyDefined" function, which checks whether a document's schema
+     * has defined the property, instead of whether a document itself has this property.
+     *
+     * <p>Ex. `foo hasProperty("sender.name")` will return all documents that have the term "foo"
+     * AND have values in the property "sender.name". Consider two documents, documentA and
+     * documentB, of the same schema with an optional property "sender.name". If documentA sets
+     * "foo" in this property but documentB does not, then `hasProperty("sender.name")` will only
+     * match documentA. However, `propertyDefined("sender.name")` will match both documentA and
+     * documentB, regardless of whether a value is actually set.
+     *
+     * <p>{@link Features#SCHEMA_EMBEDDING_PROPERTY_CONFIG}: This feature covers the
+     * "semanticSearch" and "getSearchSpecEmbedding" functions in query expressions, which are
+     * used for semantic search.
+     *
+     * <p>Usage: semanticSearch(getSearchSpecEmbedding({embedding_index}), {low}, {high}, {metric})
+     * <ul>
+     *     <li>semanticSearch matches all documents that have at least one embedding vector with
+     *     a matching model signature (see {@link EmbeddingVector#getModelSignature()}) and a
+     *     similarity score within the range specified based on the provided metric.</li>
+     *     <li>getSearchSpecEmbedding({embedding_index}) retrieves the embedding search passed in
+     *     {@link SearchSpec.Builder#addSearchEmbeddings} based on the index specified, which
+     *     starts from 0.</li>
+     *     <li>"low" and "high" are floating point numbers that specify the similarity score
+     *     range. If omitted, they default to negative and positive infinity, respectively.</li>
+     *     <li>"metric" is a string value that specifies how embedding similarities should be
+     *     calculated. If omitted, it defaults to the metric specified in
+     *     {@link SearchSpec.Builder#setDefaultEmbeddingSearchMetricType(int)}. Possible
+     *     values:</li>
+     *     <ul>
+     *         <li>"COSINE"</li>
+     *         <li>"DOT_PRODUCT"</li>
+     *         <li>"EUCLIDEAN"</li>
+     *     </ul>
+     * </ul>
+     *
+     * <p>Examples:
+     * <ul>
+     *     <li>Basic: semanticSearch(getSearchSpecEmbedding(0), 0.5, 1, "COSINE")</li>
+     *     <li>With a property restriction:
+     *     property1:semanticSearch(getSearchSpecEmbedding(0), 0.5, 1)</li>
+     *     <li>Hybrid: foo OR semanticSearch(getSearchSpecEmbedding(0), 0.5, 1)</li>
+     *     <li>Complex: (foo OR semanticSearch(getSearchSpecEmbedding(0), 0.5, 1)) AND bar</li>
+     * </ul>
+     *
+     * <p>{@link Features#LIST_FILTER_TOKENIZE_FUNCTION}: This feature covers the
+     * "tokenize" function in query expressions, which takes a string and treats the entire string
+     * as plain text. This string is then segmented, normalized and stripped of punctuation-only
+     * segments. The remaining tokens are then AND'd together. This function is useful for callers
+     * who wish to provide user input, but want to ensure that that user input does not invoke any
+     * query operators.
+     *
+     * <p>Ex. `foo OR tokenize("bar OR baz.")`. The string "bar OR baz." will be segmented into
+     * "bar", "OR", "baz", ".". Punctuation is removed and the segments are normalized to "bar",
+     * "or", "baz". This query will be equivalent to `foo OR (bar AND or AND baz)`.
+     *
+     * <p>Ex. `tokenize("\"bar\" OR \\baz")`. Quotation marks and escape characters must be escaped.
+     * This query will be segmented into "\"", "bar", "\"", "OR", "\", "baz". Once stripped of
+     * punctuation and normalized, this will be equivalent to the query `bar AND or AND baz`.
+     *
      * <p>The availability of each of these features can be checked by calling
      * {@link Features#isFeatureSupported} with the desired feature.
      *
@@ -223,6 +286,8 @@
      *                        match type, etc.
      * @return a {@link SearchResults} object for retrieved matched documents.
      */
+    // TODO(b/326656531): Refine the javadoc to provide guidance on the best practice of
+    //  embedding searches and how to select an appropriate metric.
     @NonNull
     SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
 
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/EmbeddingVector.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/EmbeddingVector.java
new file mode 100644
index 0000000..1addca7
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/EmbeddingVector.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresFeature;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.EmbeddingVectorCreator;
+import androidx.core.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Embeddings are vector representations of data, such as text, images, and audio, which can be
+ * generated by machine learning models and used for semantic search. This class represents an
+ * embedding vector, which wraps a float array for the values of the embedding vector and a model
+ * signature that can be any string to distinguish between embedding vectors generated by
+ * different models.
+ *
+ * <p>For more details on how embedding search works, check {@link AppSearchSession#search} and
+ * {@link SearchSpec.Builder#setRankingStrategy(String)}.
+ *
+ * @see SearchSpec.Builder#addSearchEmbeddings
+ * @see GenericDocument.Builder#setPropertyEmbedding
+ */
+@RequiresFeature(
+        enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+        name = Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+@FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
[email protected](creator = "EmbeddingVectorCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class EmbeddingVector extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final Parcelable.Creator<EmbeddingVector> CREATOR =
+            new EmbeddingVectorCreator();
+    @NonNull
+    @Field(id = 1, getter = "getValues")
+    private final float[] mValues;
+    @NonNull
+    @Field(id = 2, getter = "getModelSignature")
+    private final String mModelSignature;
+    @Nullable
+    private Integer mHashCode;
+
+    /**
+     * Creates a new {@link EmbeddingVector}.
+     *
+     * @throws IllegalArgumentException if {@code values} is empty.
+     */
+    @Constructor
+    public EmbeddingVector(
+            @Param(id = 1) @NonNull float[] values,
+            @Param(id = 2) @NonNull String modelSignature) {
+        mValues = Preconditions.checkNotNull(values);
+        if (mValues.length == 0) {
+            throw new IllegalArgumentException("Embedding values cannot be empty.");
+        }
+        mModelSignature = Preconditions.checkNotNull(modelSignature);
+    }
+
+    /**
+     * Returns the values of this embedding vector.
+     */
+    @NonNull
+    public float[] getValues() {
+        return mValues;
+    }
+
+    /**
+     * Returns the model signature of this embedding vector, which is an arbitrary string to
+     * distinguish between embedding vectors generated by different models.
+     */
+    @NonNull
+    public String getModelSignature() {
+        return mModelSignature;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null) return false;
+        if (!(o instanceof EmbeddingVector)) return false;
+        EmbeddingVector that = (EmbeddingVector) o;
+        return Arrays.equals(mValues, that.mValues)
+                && mModelSignature.equals(that.mModelSignature);
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = Objects.hash(Arrays.hashCode(mValues), mModelSignature);
+        }
+        return mHashCode;
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        EmbeddingVectorCreator.writeToParcel(this, dest, flags);
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/EnterpriseGlobalSearchSession.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/EnterpriseGlobalSearchSession.java
new file mode 100644
index 0000000..eaaec59
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/EnterpriseGlobalSearchSession.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.app;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresFeature;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides a connection to all enterprise (work profile) AppSearch databases the querying
+ * application has been granted access to.
+ *
+ * <p>This session can be created from any user profile but will only properly return results when
+ * created from the main profile. If the user is not the main profile or an associated work profile
+ * does not exist, queries will still successfully complete but with empty results.
+ *
+ * <p>Schemas must be explicitly tagged enterprise and may require additional permissions to be
+ * visible from an enterprise session. Retrieved documents may also have certain fields restricted
+ * or modified unlike if they were retrieved directly from {@link GlobalSearchSession} on the work
+ * profile.
+ *
+ * <p>All implementations of this interface must be thread safe.
+ *
+ * @see GlobalSearchSession
+ */
+@RequiresFeature(
+        enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+        name = Features.ENTERPRISE_GLOBAL_SEARCH_SESSION)
+public interface EnterpriseGlobalSearchSession {
+    /**
+     * Retrieves {@link GenericDocument} documents, belonging to the specified package name and
+     * database name and identified by the namespace and ids in the request, from the
+     * {@link EnterpriseGlobalSearchSession} database. When a call is successful, the result will be
+     * returned in the successes section of the {@link AppSearchBatchResult} object in the callback.
+     * If the package doesn't exist, database doesn't exist, or if the calling package doesn't have
+     * access, these failures will be reflected as {@link AppSearchResult} objects with a
+     * RESULT_NOT_FOUND status code in the failures section of the {@link AppSearchBatchResult}
+     * object.
+     *
+     * @param packageName the name of the package to get from
+     * @param databaseName the name of the database to get from
+     * @param request a request containing a namespace and IDs of the documents to retrieve.
+     */
+    @NonNull
+    ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
+            @NonNull String packageName,
+            @NonNull String databaseName,
+            @NonNull GetByDocumentIdRequest request);
+
+    /**
+     * Retrieves documents from all enterprise (work profile) AppSearch databases that the querying
+     * application has access to.
+     *
+     * <p>Applications can be granted access to documents by specifying
+     * {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}, or
+     * {@link SetSchemaRequest.Builder#setDocumentClassVisibilityForPackage} when building a schema.
+     *
+     * <p>Document access can also be granted to system UIs by specifying
+     * {@link SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}, or
+     * {@link SetSchemaRequest.Builder#setDocumentClassDisplayedBySystem}
+     * when building a schema.
+     *
+     * <p>See {@link AppSearchSession#search} for a detailed explanation on
+     * forming a query string.
+     *
+     * <p>This method is lightweight. The heavy work will be done in
+     * {@link SearchResults#getNextPageAsync}.
+     *
+     * @param queryExpression query string to search.
+     * @param searchSpec      spec for setting document filters, adding projection, setting term
+     *                        match type, etc.
+     * @return a {@link SearchResults} object for retrieved matched documents.
+     */
+    @NonNull
+    SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+
+    /**
+     * Retrieves the collection of schemas most recently successfully provided to
+     * {@link AppSearchSession#setSchemaAsync} for any types belonging to the requested package and
+     * database that the caller has been granted access to.
+     *
+     * <p> If the requested package/database combination does not exist or the caller has not been
+     * granted access to it, then an empty GetSchemaResponse will be returned.
+     *
+     *
+     * @param packageName the package that owns the requested {@link AppSearchSchema} instances.
+     * @param databaseName the database that owns the requested {@link AppSearchSchema} instances.
+     * @return The pending {@link GetSchemaResponse} containing the schemas that the caller has
+     * access to or an empty GetSchemaResponse if the request package and database does not
+     * exist, has not set a schema or contains no schemas that are accessible to the caller.
+     */
+    // This call hits disk; async API prevents us from treating these calls as properties.
+    @SuppressLint("KotlinPropertyAccess")
+    @NonNull
+    ListenableFuture<GetSchemaResponse> getSchemaAsync(@NonNull String packageName,
+            @NonNull String databaseName);
+
+    /**
+     * Returns the {@link Features} to check for the availability of certain features
+     * for this session.
+     */
+    @NonNull
+    Features getFeatures();
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/FeatureConstants.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/FeatureConstants.java
index 9fb1df0..c89fb1e 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/FeatureConstants.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/FeatureConstants.java
@@ -27,13 +27,25 @@
  * @exportToFramework:hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface FeatureConstants {
+public final class FeatureConstants {
     /** Feature constants for {@link Features#NUMERIC_SEARCH}. */
-    String NUMERIC_SEARCH = "NUMERIC_SEARCH";
+    public static final String NUMERIC_SEARCH = "NUMERIC_SEARCH";
 
     /**  Feature constants for {@link Features#VERBATIM_SEARCH}.   */
-    String VERBATIM_SEARCH = "VERBATIM_SEARCH";
+    public static final String VERBATIM_SEARCH = "VERBATIM_SEARCH";
 
     /**  Feature constants for {@link Features#LIST_FILTER_QUERY_LANGUAGE}.  */
-    String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
+    public static final String LIST_FILTER_QUERY_LANGUAGE = "LIST_FILTER_QUERY_LANGUAGE";
+
+    /**  Feature constants for {@link Features#LIST_FILTER_HAS_PROPERTY_FUNCTION}.  */
+    public static final String LIST_FILTER_HAS_PROPERTY_FUNCTION =
+            "LIST_FILTER_HAS_PROPERTY_FUNCTION";
+
+    /** A feature constant for the "semanticSearch" function in {@link AppSearchSession#search}. */
+    public static final String EMBEDDING_SEARCH = "EMBEDDING_SEARCH";
+
+    /** A feature constant for the "tokenize" function in {@link AppSearchSession#search}. */
+    public static final String LIST_FILTER_TOKENIZE_FUNCTION = "TOKENIZE";
+
+    private FeatureConstants() {}
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
index bfc4deb..e89dc99 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
@@ -16,7 +16,8 @@
 package androidx.appsearch.app;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
+
+import java.util.Set;
 
 /**
  * A class that encapsulates all features that are only supported in certain cases (e.g. only on
@@ -29,6 +30,8 @@
  */
 
 // @exportToFramework:copyToPath(../../../cts/tests/appsearch/testutils/src/android/app/appsearch/testutil/external/Features.java)
+// Note: When adding new fields, The @RequiresFeature is needed in setters but could be skipped in
+// getters if call the getter won't send unsupported requests to the AppSearch-framework-impl.
 public interface Features {
 
     /**
@@ -60,8 +63,8 @@
 
     /**
      * Feature for {@link #isFeatureSupported(String)}. This feature covers
-     * {@link SetSchemaRequest.Builder#addAllowedRoleForSchemaTypeVisibility},
-     * {@link SetSchemaRequest.Builder#clearAllowedRolesForSchemaTypeVisibility},
+     * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility(String, Set)},
+     * {@link SetSchemaRequest.Builder#clearRequiredPermissionsForSchemaTypeVisibility(String)},
      * {@link GetSchemaResponse#getSchemaTypesNotDisplayedBySystem()},
      * {@link GetSchemaResponse#getSchemaTypesVisibleToPackages()},
      * {@link GetSchemaResponse#getRequiredPermissionsForSchemaTypeVisibility()},
@@ -84,6 +87,7 @@
      * <p>For details on the numeric search expressions in the query language, see
      * {@link AppSearchSession#search}.
      */
+    // Note: The preferred name of this feature should have been LIST_FILTER_NUMERIC_SEARCH.
     String NUMERIC_SEARCH = FeatureConstants.NUMERIC_SEARCH;
 
     /**
@@ -94,6 +98,7 @@
      *
      * <p>For details on the verbatim string operator, see {@link AppSearchSession#search}.
      */
+    // Note: The preferred name of this feature should have been LIST_FILTER_VERBATIM_SEARCH.
     String VERBATIM_SEARCH = FeatureConstants.VERBATIM_SEARCH;
 
     /**
@@ -106,6 +111,42 @@
     String LIST_FILTER_QUERY_LANGUAGE = FeatureConstants.LIST_FILTER_QUERY_LANGUAGE;
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers the use of the
+     * "hasProperty" function in query expressions.
+     *
+     * <p>For details on the "hasProperty" function in the query language, see
+     * {@link AppSearchSession#search}.
+     */
+    String LIST_FILTER_HAS_PROPERTY_FUNCTION = FeatureConstants.LIST_FILTER_HAS_PROPERTY_FUNCTION;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers the use of the
+     * "tokenize" function in query expressions.
+     *
+     * <p>For details on the "tokenize" function in the query language, see
+     * {@link AppSearchSession#search}.
+     */
+    String LIST_FILTER_TOKENIZE_FUNCTION = "LIST_FILTER_TOKENIZE_FUNCTION";
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers whether or not the
+     * AppSearch backend can store the descriptions returned by
+     * {@link AppSearchSchema#getDescription} and
+     * {@link AppSearchSchema.PropertyConfig#getDescription}.
+     */
+    String SCHEMA_SET_DESCRIPTION = "SCHEMA_SET_DESCRIPTION";
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers
+     * {@link AppSearchSchema.EmbeddingPropertyConfig}.
+     *
+     * <p>For details on the embedding search expressions, see {@link AppSearchSession#search} for
+     * the query language and {@link SearchSpec.Builder#setRankingStrategy(String)} for the ranking
+     * language.
+     */
+    String SCHEMA_EMBEDDING_PROPERTY_CONFIG = "SCHEMA_EMBEDDING_PROPERTY_CONFIG";
+
+    /**
      * Feature for {@link #isFeatureSupported(String)}. This feature covers
      * {@link SearchSpec#GROUPING_TYPE_PER_SCHEMA}
      */
@@ -119,10 +160,9 @@
 
     /**
      * Feature for {@link #isFeatureSupported(String)}. This feature covers
-     * {@link SearchSpec.Builder#addFilterProperties}.
-     * @exportToFramework:hide
+     * {@link SearchSpec.Builder#addFilterProperties} and
+     * {@link SearchSuggestionSpec.Builder#addFilterProperties}.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     String SEARCH_SPEC_ADD_FILTER_PROPERTIES = "SEARCH_SPEC_ADD_FILTER_PROPERTIES";
 
     /**
@@ -145,12 +185,6 @@
     String SEARCH_SUGGESTION = "SEARCH_SUGGESTION";
 
     /**
-     * Feature for {@link #isFeatureSupported(String)}. This feature covers
-     * {@link AppSearchSchema.StringPropertyConfig.Builder#setDeletionPropagation}.
-     */
-    String SCHEMA_SET_DELETION_PROPAGATION = "SCHEMA_SET_DELETION_PROPAGATION";
-
-    /**
      * Feature for {@link #isFeatureSupported(String)}. This feature covers setting schemas with
      * circular references for {@link AppSearchSession#setSchemaAsync}.
      */
@@ -170,6 +204,38 @@
     String SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES = "SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES";
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers
+     * {@link SearchSpec.Builder#setSearchSourceLogTag(String)}.
+     */
+    String SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG = "SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG";
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers
+     * {@link SetSchemaRequest.Builder#setPubliclyVisibleSchema(String, PackageIdentifier)}.
+     */
+    String SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE = "SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE";
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers
+     * {@link SetSchemaRequest.Builder#addSchemaTypeVisibleToConfig}.
+     */
+    String SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG =
+            "SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG";
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers
+     * {@link EnterpriseGlobalSearchSession}
+     */
+    String ENTERPRISE_GLOBAL_SEARCH_SESSION = "ENTERPRISE_GLOBAL_SEARCH_SESSION";
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers
+     * {@link SearchSpec.Builder#addInformationalRankingExpressions}.
+     */
+    String SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS =
+            "SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS";
+
+    /**
      * Returns whether a feature is supported at run-time. Feature support depends on the
      * feature in question, the AppSearch backend being used and the Android version of the
      * device.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
index 57134edba..6de5448 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GenericDocument.java
@@ -17,8 +17,6 @@
 package androidx.appsearch.app;
 
 import android.annotation.SuppressLint;
-import android.os.Bundle;
-import android.os.Parcelable;
 import android.util.Log;
 
 import androidx.annotation.IntRange;
@@ -27,9 +25,11 @@
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.annotation.Document;
-import androidx.appsearch.app.PropertyPath.PathSegment;
 import androidx.appsearch.exceptions.AppSearchException;
-import androidx.appsearch.util.BundleUtil;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.GenericDocumentParcel;
+import androidx.appsearch.safeparcel.PropertyParcel;
 import androidx.appsearch.util.IndentingStringBuilder;
 import androidx.core.util.Preconditions;
 
@@ -39,6 +39,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -64,23 +65,9 @@
     /** The maximum number of indexed properties a document can have. */
     private static final int MAX_INDEXED_PROPERTIES = 16;
 
-    /** The default score of document. */
-    private static final int DEFAULT_SCORE = 0;
-
-    /** The default time-to-live in millisecond of a document, which is infinity. */
-    private static final long DEFAULT_TTL_MILLIS = 0L;
-
-    private static final String PROPERTIES_FIELD = "properties";
-    private static final String BYTE_ARRAY_FIELD = "byteArray";
-    private static final String SCHEMA_TYPE_FIELD = "schemaType";
-    private static final String ID_FIELD = "id";
-    private static final String SCORE_FIELD = "score";
-    private static final String TTL_MILLIS_FIELD = "ttlMillis";
-    private static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis";
-    private static final String NAMESPACE_FIELD = "namespace";
-    private static final String PARENT_TYPES_FIELD = "parentTypes";
-
     /**
+     * Fixed constant synthetic property for parent types.
+     *
      * <!--@exportToFramework:hide-->
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -104,6 +91,7 @@
      * {@link AppSearchSchema.LongPropertyConfig#INDEXING_TYPE_RANGE}.
      *
      * <!--@exportToFramework:ifJetpack()-->
+     *
      * @deprecated This is no longer a static value, but depends on SDK version and what AppSearch
      * implementation is being used. Use {@link Features#getMaxIndexedProperties} instead.
      * <!--@exportToFramework:else()-->
@@ -138,43 +126,20 @@
     }
 // @exportToFramework:endStrip()
 
-    /**
-     * Contains all {@link GenericDocument} information in a packaged format.
-     *
-     * <p>Keys are the {@code *_FIELD} constants in this class.
-     */
-    @NonNull
-    final Bundle mBundle;
-
-    /** Contains all properties in {@link GenericDocument} to support getting properties via name */
-    @NonNull
-    private final Bundle mProperties;
-
-    @NonNull
-    private final String mId;
-    @NonNull
-    private final String mSchemaType;
-    private final long mCreationTimestampMillis;
-    @Nullable
-    private Integer mHashCode;
+    /** The class to hold all meta data and properties for this {@link GenericDocument}. */
+    private final GenericDocumentParcel mDocumentParcel;
 
     /**
-     * Rebuilds a {@link GenericDocument} from a bundle.
+     * Rebuilds a {@link GenericDocument} from a {@link GenericDocumentParcel}.
      *
-     * @param bundle Packaged {@link GenericDocument} data, such as the result of
-     *               {@link #getBundle}.
+     * @param documentParcel Packaged {@link GenericDocument} data, such as the result of
+     *                       {@link #getDocumentParcel()}.
      * @exportToFramework:hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @SuppressWarnings("deprecation")
-    public GenericDocument(@NonNull Bundle bundle) {
-        Preconditions.checkNotNull(bundle);
-        mBundle = bundle;
-        mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD));
-        mId = Preconditions.checkNotNull(mBundle.getString(ID_FIELD));
-        mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
-        mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD,
-                System.currentTimeMillis());
+    public GenericDocument(@NonNull GenericDocumentParcel documentParcel) {
+        mDocumentParcel = Objects.requireNonNull(documentParcel);
     }
 
     /**
@@ -183,36 +148,37 @@
      * <p>This method should be only used by constructor of a subclass.
      */
     protected GenericDocument(@NonNull GenericDocument document) {
-        this(document.mBundle);
+        this(document.mDocumentParcel);
     }
 
     /**
-     * Returns the {@link Bundle} populated by this builder.
+     * Returns the {@link GenericDocumentParcel} holding the values for this
+     * {@link GenericDocument}.
      *
      * @exportToFramework:hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    public GenericDocumentParcel getDocumentParcel() {
+        return mDocumentParcel;
     }
 
     /** Returns the unique identifier of the {@link GenericDocument}. */
     @NonNull
     public String getId() {
-        return mId;
+        return mDocumentParcel.getId();
     }
 
     /** Returns the namespace of the {@link GenericDocument}. */
     @NonNull
     public String getNamespace() {
-        return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ "");
+        return mDocumentParcel.getNamespace();
     }
 
     /** Returns the {@link AppSearchSchema} type of the {@link GenericDocument}. */
     @NonNull
     public String getSchemaType() {
-        return mSchemaType;
+        return mDocumentParcel.getSchemaType();
     }
 
     /**
@@ -224,7 +190,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @Nullable
     public List<String> getParentTypes() {
-        List<String> result = mBundle.getStringArrayList(PARENT_TYPES_FIELD);
+        List<String> result = mDocumentParcel.getParentTypes();
         if (result == null) {
             return null;
         }
@@ -238,7 +204,7 @@
      */
     /*@exportToFramework:CurrentTimeMillisLong*/
     public long getCreationTimestampMillis() {
-        return mCreationTimestampMillis;
+        return mDocumentParcel.getCreationTimestampMillis();
     }
 
     /**
@@ -252,7 +218,7 @@
      * until the app is uninstalled or {@link AppSearchSession#removeAsync} is called.
      */
     public long getTtlMillis() {
-        return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
+        return mDocumentParcel.getTtlMillis();
     }
 
     /**
@@ -267,13 +233,13 @@
      * <p>Any non-negative integer can be used a score.
      */
     public int getScore() {
-        return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE);
+        return mDocumentParcel.getScore();
     }
 
     /** Returns the names of all properties defined in this document. */
     @NonNull
     public Set<String> getPropertyNames() {
-        return Collections.unmodifiableSet(mProperties.keySet());
+        return Collections.unmodifiableSet(mDocumentParcel.getPropertyNames());
     }
 
     /**
@@ -334,61 +300,36 @@
      *
      * @param path The path to look for.
      * @return The entry with the given path as an object or {@code null} if there is no such path.
-     *   The returned object will be one of the following types: {@code String[]}, {@code long[]},
-     *   {@code double[]}, {@code boolean[]}, {@code byte[][]}, {@code GenericDocument[]}.
+     * The returned object will be one of the following types: {@code String[]}, {@code long[]},
+     * {@code double[]}, {@code boolean[]}, {@code byte[][]}, {@code GenericDocument[]}.
      */
     @Nullable
     public Object getProperty(@NonNull String path) {
-        Preconditions.checkNotNull(path);
+        Objects.requireNonNull(path);
         Object rawValue =
-                getRawPropertyFromRawDocument(new PropertyPath(path), /*pathIndex=*/0, mBundle);
+                getRawPropertyFromRawDocument(new PropertyPath(path), /*pathIndex=*/ 0,
+                        mDocumentParcel.getPropertyMap());
 
         // Unpack the raw value into the types the user expects, if required.
-        if (rawValue instanceof Bundle) {
-            // getRawPropertyFromRawDocument may return a document as a bare Bundle as a performance
-            // optimization for lookups.
-            GenericDocument document = new GenericDocument((Bundle) rawValue);
+        if (rawValue instanceof GenericDocumentParcel) {
+            // getRawPropertyFromRawDocument may return a document as a bare documentParcel
+            // as a performance optimization for lookups.
+            GenericDocument document = new GenericDocument((GenericDocumentParcel) rawValue);
             return new GenericDocument[]{document};
         }
 
-        if (rawValue instanceof List) {
-            // byte[][] fields are packed into List<Bundle> where each Bundle contains just a single
-            // entry: BYTE_ARRAY_FIELD -> byte[].
-            @SuppressWarnings("unchecked")
-            List<Bundle> bundles = (List<Bundle>) rawValue;
-            byte[][] bytes = new byte[bundles.size()][];
-            for (int i = 0; i < bundles.size(); i++) {
-                Bundle bundle = bundles.get(i);
-                if (bundle == null) {
-                    Log.e(TAG, "The inner bundle is null at " + i + ", for path: " + path);
-                    continue;
-                }
-                byte[] innerBytes = bundle.getByteArray(BYTE_ARRAY_FIELD);
-                if (innerBytes == null) {
-                    Log.e(TAG, "The bundle at " + i + " contains a null byte[].");
-                    continue;
-                }
-                bytes[i] = innerBytes;
-            }
-            return bytes;
-        }
-
-        if (rawValue instanceof Parcelable[]) {
-            // The underlying Bundle of nested GenericDocuments is packed into a Parcelable array.
+        if (rawValue instanceof GenericDocumentParcel[]) {
+            // The underlying parcelable of nested GenericDocuments is packed into
+            // a Parcelable array.
             // We must unpack it into GenericDocument instances.
-            Parcelable[] bundles = (Parcelable[]) rawValue;
-            GenericDocument[] documents = new GenericDocument[bundles.length];
-            for (int i = 0; i < bundles.length; i++) {
-                if (bundles[i] == null) {
-                    Log.e(TAG, "The inner bundle is null at " + i + ", for path: " + path);
+            GenericDocumentParcel[] docParcels = (GenericDocumentParcel[]) rawValue;
+            GenericDocument[] documents = new GenericDocument[docParcels.length];
+            for (int i = 0; i < docParcels.length; i++) {
+                if (docParcels[i] == null) {
+                    Log.e(TAG, "The inner parcel is null at " + i + ", for path: " + path);
                     continue;
                 }
-                if (!(bundles[i] instanceof Bundle)) {
-                    Log.e(TAG, "The inner element at " + i + " is a " + bundles[i].getClass()
-                            + ", not a Bundle for path: " + path);
-                    continue;
-                }
-                documents[i] = new GenericDocument((Bundle) bundles[i]);
+                documents[i] = new GenericDocument(docParcels[i]);
             }
             return documents;
         }
@@ -407,25 +348,21 @@
      * But in the case where we collect documents across repeated nested documents, we need to
      * recurse back into this method, and so we also keep track of the index into the path.
      *
-     * @param path the PropertyPath object representing the path
-     * @param pathIndex the index into the path we start at
-     * @param documentBundle the bundle that contains the path we are looking up
+     * @param path        the PropertyPath object representing the path
+     * @param pathIndex   the index into the path we start at
+     * @param propertyMap the map containing the path we are looking up
      * @return the raw property
      */
     @Nullable
     @SuppressWarnings("deprecation")
     private static Object getRawPropertyFromRawDocument(
-            @NonNull PropertyPath path, int pathIndex, @NonNull Bundle documentBundle) {
-        Preconditions.checkNotNull(path);
-        Preconditions.checkNotNull(documentBundle);
-        Bundle properties = Preconditions.checkNotNull(documentBundle.getBundle(PROPERTIES_FIELD));
-
-
+            @NonNull PropertyPath path, int pathIndex,
+            @NonNull Map<String, PropertyParcel> propertyMap) {
+        Objects.requireNonNull(path);
+        Objects.requireNonNull(propertyMap);
         for (int i = pathIndex; i < path.size(); i++) {
-            PathSegment segment = path.get(i);
-
-            Object currentElementValue = properties.get(segment.getPropertyName());
-
+            PropertyPath.PathSegment segment = path.get(i);
+            Object currentElementValue = propertyMap.get(segment.getPropertyName());
             if (currentElementValue == null) {
                 return null;
             }
@@ -435,61 +372,77 @@
             // "recipients[0]", currentElementValue now contains the value of "recipients" while we
             // need the value of "recipients[0]".
             int index = segment.getPropertyIndex();
-            if (index != PathSegment.NON_REPEATED_CARDINALITY) {
+            if (index != PropertyPath.PathSegment.NON_REPEATED_CARDINALITY) {
+                // For properties bundle, now we will only get PropertyParcel as the value.
+                PropertyParcel propertyParcel = (PropertyParcel) currentElementValue;
+
                 // Extract the right array element
                 Object extractedValue = null;
-                if (currentElementValue instanceof String[]) {
-                    String[] stringValues = (String[]) currentElementValue;
-                    if (index < stringValues.length) {
+                if (propertyParcel.getStringValues() != null) {
+                    String[] stringValues = propertyParcel.getStringValues();
+                    if (stringValues != null && index < stringValues.length) {
                         extractedValue = Arrays.copyOfRange(stringValues, index, index + 1);
                     }
-                } else if (currentElementValue instanceof long[]) {
-                    long[] longValues = (long[]) currentElementValue;
-                    if (index < longValues.length) {
+                } else if (propertyParcel.getLongValues() != null) {
+                    long[] longValues = propertyParcel.getLongValues();
+                    if (longValues != null && index < longValues.length) {
                         extractedValue = Arrays.copyOfRange(longValues, index, index + 1);
                     }
-                } else if (currentElementValue instanceof double[]) {
-                    double[] doubleValues = (double[]) currentElementValue;
-                    if (index < doubleValues.length) {
+                } else if (propertyParcel.getDoubleValues() != null) {
+                    double[] doubleValues = propertyParcel.getDoubleValues();
+                    if (doubleValues != null && index < doubleValues.length) {
                         extractedValue = Arrays.copyOfRange(doubleValues, index, index + 1);
                     }
-                } else if (currentElementValue instanceof boolean[]) {
-                    boolean[] booleanValues = (boolean[]) currentElementValue;
-                    if (index < booleanValues.length) {
+                } else if (propertyParcel.getBooleanValues() != null) {
+                    boolean[] booleanValues = propertyParcel.getBooleanValues();
+                    if (booleanValues != null && index < booleanValues.length) {
                         extractedValue = Arrays.copyOfRange(booleanValues, index, index + 1);
                     }
-                } else if (currentElementValue instanceof List) {
-                    @SuppressWarnings("unchecked")
-                    List<Bundle> bundles = (List<Bundle>) currentElementValue;
-                    if (index < bundles.size()) {
-                        extractedValue = bundles.subList(index, index + 1);
+                } else if (propertyParcel.getBytesValues() != null) {
+                    byte[][] bytesValues = propertyParcel.getBytesValues();
+                    if (bytesValues != null && index < bytesValues.length) {
+                        extractedValue = Arrays.copyOfRange(bytesValues, index, index + 1);
                     }
-                } else if (currentElementValue instanceof Parcelable[]) {
+                } else if (propertyParcel.getDocumentValues() != null) {
                     // Special optimization: to avoid creating new singleton arrays for traversing
-                    // paths we return the bare document Bundle in this particular case.
-                    Parcelable[] bundles = (Parcelable[]) currentElementValue;
-                    if (index < bundles.length) {
-                        extractedValue = bundles[index];
+                    // paths we return the bare document parcel in this particular case.
+                    GenericDocumentParcel[] docValues = propertyParcel.getDocumentValues();
+                    if (docValues != null && index < docValues.length) {
+                        extractedValue = docValues[index];
+                    }
+                } else if (propertyParcel.getEmbeddingValues() != null) {
+                    EmbeddingVector[] embeddingValues = propertyParcel.getEmbeddingValues();
+                    if (embeddingValues != null && index < embeddingValues.length) {
+                        extractedValue = Arrays.copyOfRange(embeddingValues, index, index + 1);
                     }
                 } else {
-                    throw new IllegalStateException("Unsupported value type: "
-                            + currentElementValue);
+                    throw new IllegalStateException(
+                            "Unsupported value type: " + currentElementValue);
                 }
                 currentElementValue = extractedValue;
             }
 
             // at the end of the path, either something like "...foo" or "...foo[1]"
             if (currentElementValue == null || i == path.size() - 1) {
+                if (currentElementValue != null && currentElementValue instanceof PropertyParcel) {
+                    // Unlike previous bundle-based implementation, now each
+                    // value is wrapped in PropertyParcel.
+                    // Here we need to get and return the actual value for non-repeated fields.
+                    currentElementValue = ((PropertyParcel) currentElementValue).getValues();
+                }
                 return currentElementValue;
             }
 
-            // currentElementValue is now a Bundle or Parcelable[], we can continue down the path
-            if (currentElementValue instanceof Bundle) {
-                properties = ((Bundle) currentElementValue).getBundle(PROPERTIES_FIELD);
-            } else if (currentElementValue instanceof Parcelable[]) {
-                Parcelable[] parcelables = (Parcelable[]) currentElementValue;
-                if (parcelables.length == 1) {
-                    properties = ((Bundle) parcelables[0]).getBundle(PROPERTIES_FIELD);
+            // currentElementValue is now a GenericDocumentParcel or PropertyParcel,
+            // we can continue down the path.
+            if (currentElementValue instanceof GenericDocumentParcel) {
+                propertyMap = ((GenericDocumentParcel) currentElementValue).getPropertyMap();
+            } else if (currentElementValue instanceof PropertyParcel
+                    && ((PropertyParcel) currentElementValue).getDocumentValues() != null) {
+                GenericDocumentParcel[] docParcels =
+                        ((PropertyParcel) currentElementValue).getDocumentValues();
+                if (docParcels != null && docParcels.length == 1) {
+                    propertyMap = docParcels[0].getPropertyMap();
                     continue;
                 }
 
@@ -516,17 +469,21 @@
                 // repeated values. The implementation is optimized for these two cases, requiring
                 // no additional allocations. So we've decided that the above performance
                 // characteristics are OK for the less used path.
-                List<Object> accumulator = new ArrayList<>(parcelables.length);
-                for (Parcelable parcelable : parcelables) {
-                    // recurse as we need to branch
-                    Object value = getRawPropertyFromRawDocument(path, /*pathIndex=*/i + 1,
-                            (Bundle) parcelable);
-                    if (value != null) {
-                        accumulator.add(value);
+                if (docParcels != null) {
+                    List<Object> accumulator = new ArrayList<>(docParcels.length);
+                    for (GenericDocumentParcel docParcel : docParcels) {
+                        // recurse as we need to branch
+                        Object value =
+                                getRawPropertyFromRawDocument(
+                                        path, /*pathIndex=*/ i + 1,
+                                        ((GenericDocumentParcel) docParcel).getPropertyMap());
+                        if (value != null) {
+                            accumulator.add(value);
+                        }
                     }
+                    // Break the path traversing loop
+                    return flattenAccumulator(accumulator);
                 }
-                // Break the path traversing loop
-                return flattenAccumulator(accumulator);
             } else {
                 Log.e(TAG, "Failed to apply path to document; no nested value found: " + path);
                 return null;
@@ -540,10 +497,10 @@
      * Combines accumulated repeated properties from multiple documents into a single array.
      *
      * @param accumulator List containing objects of the following types: {@code String[]},
-     *                    {@code long[]}, {@code double[]}, {@code boolean[]}, {@code List<Bundle>},
-     *                    or {@code Parcelable[]}.
+     *                    {@code long[]}, {@code double[]}, {@code boolean[]}, {@code byte[][]},
+     *                    or {@code GenericDocumentParcelable[]}.
      * @return The result of concatenating each individual list element into a larger array/list of
-     *         the same type.
+     * the same type.
      */
     @Nullable
     private static Object flattenAccumulator(@NonNull List<Object> accumulator) {
@@ -607,28 +564,29 @@
             }
             return result;
         }
-        if (first instanceof List) {
+        if (first instanceof byte[][]) {
             int length = 0;
             for (int i = 0; i < accumulator.size(); i++) {
-                length += ((List<?>) accumulator.get(i)).size();
+                length += ((byte[][]) accumulator.get(i)).length;
             }
-            List<Bundle> result = new ArrayList<>(length);
+            byte[][] result = new byte[length][];
+            int total = 0;
             for (int i = 0; i < accumulator.size(); i++) {
-                @SuppressWarnings("unchecked")
-                List<Bundle> castValue = (List<Bundle>) accumulator.get(i);
-                result.addAll(castValue);
+                byte[][] castValue = (byte[][]) accumulator.get(i);
+                System.arraycopy(castValue, 0, result, total, castValue.length);
+                total += castValue.length;
             }
             return result;
         }
-        if (first instanceof Parcelable[]) {
+        if (first instanceof GenericDocumentParcel[]) {
             int length = 0;
             for (int i = 0; i < accumulator.size(); i++) {
-                length += ((Parcelable[]) accumulator.get(i)).length;
+                length += ((GenericDocumentParcel[]) accumulator.get(i)).length;
             }
-            Parcelable[] result = new Parcelable[length];
+            GenericDocumentParcel[] result = new GenericDocumentParcel[length];
             int total = 0;
             for (int i = 0; i < accumulator.size(); i++) {
-                Parcelable[] castValue = (Parcelable[]) accumulator.get(i);
+                GenericDocumentParcel[] castValue = (GenericDocumentParcel[]) accumulator.get(i);
                 System.arraycopy(castValue, 0, result, total, castValue.length);
                 total += castValue.length;
             }
@@ -754,6 +712,27 @@
         return propertyArray[0];
     }
 
+    /**
+     * Retrieves an {@code EmbeddingVector} property by path.
+     *
+     * <p>See {@link #getProperty} for a detailed description of the path syntax.
+     *
+     * @param path The path to look for.
+     * @return The first {@code EmbeddingVector[]} associated with the given path or
+     * {@code null} if there is no such value or the value is of a different type.
+     */
+    @Nullable
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public EmbeddingVector getPropertyEmbedding(@NonNull String path) {
+        Preconditions.checkNotNull(path);
+        EmbeddingVector[] propertyArray = getPropertyEmbeddingArray(path);
+        if (propertyArray == null || propertyArray.length == 0) {
+            return null;
+        }
+        warnIfSinglePropertyTooLong("Embedding", path, propertyArray.length);
+        return propertyArray[0];
+    }
+
     /** Prints a warning to logcat if the given propertyLength is greater than 1. */
     private static void warnIfSinglePropertyTooLong(
             @NonNull String propertyType, @NonNull String path, int propertyLength) {
@@ -862,13 +841,13 @@
      * returns {@code null}.
      *
      * <!--@exportToFramework:ifJetpack()-->
-     *   <p>If it has been set via {@link Builder#setPropertyBytes} to an empty {@code byte[][]},
-     *   this method returns an empty {@code byte[][]}.
+     * <p>If it has been set via {@link Builder#setPropertyBytes} to an empty {@code byte[][]},
+     * this method returns an empty {@code byte[][]}.
      * <!--@exportToFramework:else()
-     *   <p>If it has been set via {@link Builder#setPropertyBytes} to an empty {@code byte[][]},
-     *   this method returns an empty {@code byte[][]} starting in
-     *   {@link android.os.Build.VERSION_CODES#TIRAMISU Android T} and {@code null} in earlier
-     *   versions of Android.
+     * <p>If it has been set via {@link Builder#setPropertyBytes} to an empty {@code byte[][]},
+     * this method returns an empty {@code byte[][]} starting in
+     * {@link android.os.Build.VERSION_CODES#TIRAMISU Android T} and {@code null} in earlier
+     * versions of Android.
      * -->
      *
      * @param path The path to look for.
@@ -892,13 +871,13 @@
      * returns {@code null}.
      *
      * <!--@exportToFramework:ifJetpack()-->
-     *   <p>If it has been set via {@link Builder#setPropertyDocument} to an empty
-     *   {@code GenericDocument[]}, this method returns an empty {@code GenericDocument[]}.
+     * <p>If it has been set via {@link Builder#setPropertyDocument} to an empty
+     * {@code GenericDocument[]}, this method returns an empty {@code GenericDocument[]}.
      * <!--@exportToFramework:else()
-     *   <p>If it has been set via {@link Builder#setPropertyDocument} to an empty
-     *   {@code GenericDocument[]}, this method returns an empty {@code GenericDocument[]} starting
-     *   in {@link android.os.Build.VERSION_CODES#TIRAMISU Android T} and {@code null} in earlier
-     *   versions of Android.
+     * <p>If it has been set via {@link Builder#setPropertyDocument} to an empty
+     * {@code GenericDocument[]}, this method returns an empty {@code GenericDocument[]} starting
+     * in {@link android.os.Build.VERSION_CODES#TIRAMISU Android T} and {@code null} in earlier
+     * versions of Android.
      * -->
      *
      * @param path The path to look for.
@@ -914,11 +893,36 @@
     }
 
     /**
+     * Retrieves a repeated {@code EmbeddingVector[]} property by path.
+     *
+     * <p>See {@link #getProperty} for a detailed description of the path syntax.
+     *
+     * <p>If the property has not been set via {@link Builder#setPropertyEmbedding}, this method
+     * returns {@code null}.
+     *
+     * <p>If it has been set via {@link Builder#setPropertyEmbedding} to an empty
+     * {@code EmbeddingVector[]}, this method returns an empty
+     * {@code EmbeddingVector[]}.
+     *
+     * @param path The path to look for.
+     * @return The {@code EmbeddingVector[]} associated with the given path, or
+     * {@code null} if no value is set or the value is of a different type.
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @Nullable
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public EmbeddingVector[] getPropertyEmbeddingArray(@NonNull String path) {
+        Preconditions.checkNotNull(path);
+        Object value = getProperty(path);
+        return safeCastProperty(path, value, EmbeddingVector[].class);
+    }
+
+    /**
      * Casts a repeated property to the provided type, logging an error and returning {@code null}
      * if the cast fails.
      *
-     * @param path Path to the property within the document. Used for logging.
-     * @param value Value of the property
+     * @param path   Path to the property within the document. Used for logging.
+     * @param value  Value of the property
      * @param tClass Class to cast the value into
      */
     @Nullable
@@ -1056,6 +1060,7 @@
      * {@link GenericDocument.Builder}.
      *
      * <p>The returned builder is a deep copy whose data is separate from this document.
+     *
      * @deprecated This API is not compliant with API guidelines.
      * Use {@link Builder#Builder(GenericDocument)} instead.
      * <!--@exportToFramework:hide-->
@@ -1064,8 +1069,7 @@
     @NonNull
     @Deprecated
     public GenericDocument.Builder<GenericDocument.Builder<?>> toBuilder() {
-        Bundle clonedBundle = BundleUtil.deepCopy(mBundle);
-        return new GenericDocument.Builder<>(clonedBundle);
+        return new Builder<>(new GenericDocumentParcel.Builder(mDocumentParcel));
     }
 
     @Override
@@ -1077,15 +1081,12 @@
             return false;
         }
         GenericDocument otherDocument = (GenericDocument) other;
-        return BundleUtil.deepEquals(this.mBundle, otherDocument.mBundle);
+        return mDocumentParcel.equals(otherDocument.mDocumentParcel);
     }
 
     @Override
     public int hashCode() {
-        if (mHashCode == null) {
-            mHashCode = BundleUtil.deepHashCode(mBundle);
-        }
-        return mHashCode;
+        return mDocumentParcel.hashCode();
     }
 
     @Override
@@ -1099,7 +1100,7 @@
     /**
      * Appends a debug string for the {@link GenericDocument} instance to the given string builder.
      *
-     * @param builder     the builder to append to.
+     * @param builder the builder to append to.
      */
     void appendGenericDocumentString(@NonNull IndentingStringBuilder builder) {
         Preconditions.checkNotNull(builder);
@@ -1147,9 +1148,9 @@
     /**
      * Appends a debug string for the given document property to the given string builder.
      *
-     * @param propertyName  name of property to create string for.
-     * @param property      property object to create string for.
-     * @param builder       the builder to append to.
+     * @param propertyName name of property to create string for.
+     * @param property     property object to create string for.
+     * @param builder      the builder to append to.
      */
     private void appendPropertyString(@NonNull String propertyName, @NonNull Object property,
             @NonNull IndentingStringBuilder builder) {
@@ -1178,7 +1179,7 @@
                     builder.append("\"").append((String) propertyElement).append("\"");
                 } else if (propertyElement instanceof byte[]) {
                     builder.append(Arrays.toString((byte[]) propertyElement));
-                } else {
+                } else if (propertyElement != null) {
                     builder.append(propertyElement.toString());
                 }
                 if (i != propertyArrLength - 1) {
@@ -1197,11 +1198,10 @@
     // This builder is specifically designed to be extended by classes deriving from
     // GenericDocument.
     @SuppressLint("StaticFinalBuilder")
+    @SuppressWarnings("rawtypes")
     public static class Builder<BuilderType extends Builder> {
-        private Bundle mBundle;
-        private Bundle mProperties;
+        private final GenericDocumentParcel.Builder mDocumentParcelBuilder;
         private final BuilderType mBuilderTypeInstance;
-        private boolean mBuilt = false;
 
         /**
          * Creates a new {@link GenericDocument.Builder}.
@@ -1228,41 +1228,31 @@
             Preconditions.checkNotNull(id);
             Preconditions.checkNotNull(schemaType);
 
-            mBundle = new Bundle();
             mBuilderTypeInstance = (BuilderType) this;
-            mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
-            mBundle.putString(GenericDocument.ID_FIELD, id);
-            mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
-            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
-            mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
-
-            mProperties = new Bundle();
-            mBundle.putBundle(PROPERTIES_FIELD, mProperties);
+            mDocumentParcelBuilder = new GenericDocumentParcel.Builder(namespace, id, schemaType);
         }
 
         /**
-         * Creates a new {@link GenericDocument.Builder} from the given Bundle.
+         * Creates a new {@link GenericDocument.Builder} from the given
+         * {@link GenericDocumentParcel.Builder}.
          *
          * <p>The bundle is NOT copied.
          */
         @SuppressWarnings("unchecked")
-        Builder(@NonNull Bundle bundle) {
-            mBundle = Preconditions.checkNotNull(bundle);
-            // mProperties is NonNull and initialized to empty Bundle() in builder.
-            mProperties = Preconditions.checkNotNull(mBundle.getBundle(PROPERTIES_FIELD));
+        Builder(@NonNull GenericDocumentParcel.Builder documentParcelBuilder) {
+            mDocumentParcelBuilder = Objects.requireNonNull(documentParcelBuilder);
             mBuilderTypeInstance = (BuilderType) this;
         }
 
         /**
          * Creates a new {@link GenericDocument.Builder} from the given GenericDocument.
          *
-         * <p>The GenericDocument is deep copied, i.e. changes to the new GenericDocument
-         * returned by this function will NOT affect the original GenericDocument.
-         * <!--@exportToFramework:hide-->
+         * <p>The GenericDocument is deep copied, that is, it changes to a new GenericDocument
+         * returned by this function and will NOT affect the original GenericDocument.
          */
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @FlaggedApi(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_COPY_CONSTRUCTOR)
         public Builder(@NonNull GenericDocument document) {
-            this(BundleUtil.deepCopy(document.getBundle()));
+            this(new GenericDocumentParcel.Builder(document.mDocumentParcel));
         }
 
         /**
@@ -1272,14 +1262,13 @@
          * <p>Document IDs are unique within a namespace.
          *
          * <p>The number of namespaces per app should be kept small for efficiency reasons.
-         * <!--@exportToFramework:hide-->
          */
+        @FlaggedApi(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_BUILDER_HIDDEN_METHODS)
         @CanIgnoreReturnValue
         @NonNull
         public BuilderType setNamespace(@NonNull String namespace) {
             Preconditions.checkNotNull(namespace);
-            resetIfBuilt();
-            mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
+            mDocumentParcelBuilder.setNamespace(namespace);
             return mBuilderTypeInstance;
         }
 
@@ -1287,15 +1276,17 @@
          * Sets the ID of this document, changing the value provided in the constructor. No
          * special values are reserved or understood by the infrastructure.
          *
-         * <p>Document IDs are unique within a namespace.
-         * <!--@exportToFramework:hide-->
+         * <p>Document IDs are unique within the combination of package, database, and namespace.
+         *
+         * <p>Setting a document with a duplicate id will overwrite the original document with
+         * the new document, enforcing uniqueness within the above constraint.
          */
+        @FlaggedApi(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_BUILDER_HIDDEN_METHODS)
         @CanIgnoreReturnValue
         @NonNull
         public BuilderType setId(@NonNull String id) {
             Preconditions.checkNotNull(id);
-            resetIfBuilt();
-            mBundle.putString(GenericDocument.ID_FIELD, id);
+            mDocumentParcelBuilder.setId(id);
             return mBuilderTypeInstance;
         }
 
@@ -1303,15 +1294,15 @@
          * Sets the schema type of this document, changing the value provided in the constructor.
          *
          * <p>To successfully index a document, the schema type must match the name of an
-         * {@link AppSearchSchema} object previously provided to {@link AppSearchSession#setSchemaAsync}.
-         * <!--@exportToFramework:hide-->
+         * {@link AppSearchSchema} object previously provided to
+         * {@link AppSearchSession#setSchemaAsync}.
          */
+        @FlaggedApi(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_BUILDER_HIDDEN_METHODS)
         @CanIgnoreReturnValue
         @NonNull
         public BuilderType setSchemaType(@NonNull String schemaType) {
             Preconditions.checkNotNull(schemaType);
-            resetIfBuilt();
-            mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
+            mDocumentParcelBuilder.setSchemaType(schemaType);
             return mBuilderTypeInstance;
         }
 
@@ -1326,9 +1317,7 @@
         @NonNull
         public BuilderType setParentTypes(@NonNull List<String> parentTypes) {
             Preconditions.checkNotNull(parentTypes);
-            resetIfBuilt();
-            mBundle.putStringArrayList(GenericDocument.PARENT_TYPES_FIELD,
-                    new ArrayList<>(parentTypes));
+            mDocumentParcelBuilder.setParentTypes(parentTypes);
             return mBuilderTypeInstance;
         }
 
@@ -1352,8 +1341,7 @@
             if (score < 0) {
                 throw new IllegalArgumentException("Document score cannot be negative.");
             }
-            resetIfBuilt();
-            mBundle.putInt(GenericDocument.SCORE_FIELD, score);
+            mDocumentParcelBuilder.setScore(score);
             return mBuilderTypeInstance;
         }
 
@@ -1371,9 +1359,7 @@
         @NonNull
         public BuilderType setCreationTimestampMillis(
                 /*@exportToFramework:CurrentTimeMillisLong*/ long creationTimestampMillis) {
-            resetIfBuilt();
-            mBundle.putLong(
-                    GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, creationTimestampMillis);
+            mDocumentParcelBuilder.setCreationTimestampMillis(creationTimestampMillis);
             return mBuilderTypeInstance;
         }
 
@@ -1397,8 +1383,7 @@
             if (ttlMillis < 0) {
                 throw new IllegalArgumentException("Document ttlMillis cannot be negative.");
             }
-            resetIfBuilt();
-            mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, ttlMillis);
+            mDocumentParcelBuilder.setTtlMillis(ttlMillis);
             return mBuilderTypeInstance;
         }
 
@@ -1406,9 +1391,9 @@
          * Sets one or multiple {@code String} values for a property, replacing its previous
          * values.
          *
-         * @param name    the name associated with the {@code values}. Must match the name
-         *                for this property as given in
-         *                {@link AppSearchSchema.PropertyConfig#getName}.
+         * @param name   the name associated with the {@code values}. Must match the name
+         *               for this property as given in
+         *               {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code String} values of the property.
          * @throws IllegalArgumentException if no values are provided, or if a passed in
          *                                  {@code String} is {@code null} or "".
@@ -1418,8 +1403,13 @@
         public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) {
             Preconditions.checkNotNull(name);
             Preconditions.checkNotNull(values);
-            resetIfBuilt();
-            putInPropertyBundle(name, values);
+            validatePropertyName(name);
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The String at " + i + " is null.");
+                }
+            }
+            mDocumentParcelBuilder.putInPropertyMap(name, values);
             return mBuilderTypeInstance;
         }
 
@@ -1427,9 +1417,9 @@
          * Sets one or multiple {@code boolean} values for a property, replacing its previous
          * values.
          *
-         * @param name    the name associated with the {@code values}. Must match the name
-         *                for this property as given in
-         *                {@link AppSearchSchema.PropertyConfig#getName}.
+         * @param name   the name associated with the {@code values}. Must match the name
+         *               for this property as given in
+         *               {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code boolean} values of the property.
          * @throws IllegalArgumentException if the name is empty or {@code null}.
          */
@@ -1438,8 +1428,8 @@
         public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) {
             Preconditions.checkNotNull(name);
             Preconditions.checkNotNull(values);
-            resetIfBuilt();
-            putInPropertyBundle(name, values);
+            validatePropertyName(name);
+            mDocumentParcelBuilder.putInPropertyMap(name, values);
             return mBuilderTypeInstance;
         }
 
@@ -1447,9 +1437,9 @@
          * Sets one or multiple {@code long} values for a property, replacing its previous
          * values.
          *
-         * @param name    the name associated with the {@code values}. Must match the name
-         *                for this property as given in
-         *                {@link AppSearchSchema.PropertyConfig#getName}.
+         * @param name   the name associated with the {@code values}. Must match the name
+         *               for this property as given in
+         *               {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code long} values of the property.
          * @throws IllegalArgumentException if the name is empty or {@code null}.
          */
@@ -1458,8 +1448,8 @@
         public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) {
             Preconditions.checkNotNull(name);
             Preconditions.checkNotNull(values);
-            resetIfBuilt();
-            putInPropertyBundle(name, values);
+            validatePropertyName(name);
+            mDocumentParcelBuilder.putInPropertyMap(name, values);
             return mBuilderTypeInstance;
         }
 
@@ -1467,9 +1457,9 @@
          * Sets one or multiple {@code double} values for a property, replacing its previous
          * values.
          *
-         * @param name    the name associated with the {@code values}. Must match the name
-         *                for this property as given in
-         *                {@link AppSearchSchema.PropertyConfig#getName}.
+         * @param name   the name associated with the {@code values}. Must match the name
+         *               for this property as given in
+         *               {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code double} values of the property.
          * @throws IllegalArgumentException if the name is empty or {@code null}.
          */
@@ -1478,17 +1468,17 @@
         public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) {
             Preconditions.checkNotNull(name);
             Preconditions.checkNotNull(values);
-            resetIfBuilt();
-            putInPropertyBundle(name, values);
+            validatePropertyName(name);
+            mDocumentParcelBuilder.putInPropertyMap(name, values);
             return mBuilderTypeInstance;
         }
 
         /**
          * Sets one or multiple {@code byte[]} for a property, replacing its previous values.
          *
-         * @param name    the name associated with the {@code values}. Must match the name
-         *                for this property as given in
-         *                {@link AppSearchSchema.PropertyConfig#getName}.
+         * @param name   the name associated with the {@code values}. Must match the name
+         *               for this property as given in
+         *               {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code byte[]} of the property.
          * @throws IllegalArgumentException if no values are provided, or if a passed in
          *                                  {@code byte[]} is {@code null}, or if name is empty.
@@ -1498,8 +1488,13 @@
         public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) {
             Preconditions.checkNotNull(name);
             Preconditions.checkNotNull(values);
-            resetIfBuilt();
-            putInPropertyBundle(name, values);
+            validatePropertyName(name);
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The byte[] at " + i + " is null.");
+                }
+            }
+            mDocumentParcelBuilder.putInPropertyMap(name, values);
             return mBuilderTypeInstance;
         }
 
@@ -1507,9 +1502,9 @@
          * Sets one or multiple {@link GenericDocument} values for a property, replacing its
          * previous values.
          *
-         * @param name    the name associated with the {@code values}. Must match the name
-         *                for this property as given in
-         *                {@link AppSearchSchema.PropertyConfig#getName}.
+         * @param name   the name associated with the {@code values}. Must match the name
+         *               for this property as given in
+         *               {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@link GenericDocument} values of the property.
          * @throws IllegalArgumentException if no values are provided, or if a passed in
          *                                  {@link GenericDocument} is {@code null}, or if name
@@ -1521,8 +1516,43 @@
                 @NonNull String name, @NonNull GenericDocument... values) {
             Preconditions.checkNotNull(name);
             Preconditions.checkNotNull(values);
-            resetIfBuilt();
-            putInPropertyBundle(name, values);
+            validatePropertyName(name);
+            GenericDocumentParcel[] documentParcels = new GenericDocumentParcel[values.length];
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException("The document at " + i + " is null.");
+                }
+                documentParcels[i] = values[i].getDocumentParcel();
+            }
+            mDocumentParcelBuilder.putInPropertyMap(name, documentParcels);
+            return mBuilderTypeInstance;
+        }
+
+        /**
+         * Sets one or multiple {@code EmbeddingVector} values for a property, replacing
+         * its previous values.
+         *
+         * @param name   the name associated with the {@code values}. Must match the name
+         *               for this property as given in
+         *               {@link AppSearchSchema.PropertyConfig#getName}.
+         * @param values the {@code EmbeddingVector} values of the property.
+         * @throws IllegalArgumentException if the name is empty or {@code null}.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        public BuilderType setPropertyEmbedding(@NonNull String name,
+                @NonNull EmbeddingVector... values) {
+            Preconditions.checkNotNull(name);
+            Preconditions.checkNotNull(values);
+            validatePropertyName(name);
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] == null) {
+                    throw new IllegalArgumentException(
+                            "The EmbeddingVector at " + i + " is null.");
+                }
+            }
+            mDocumentParcelBuilder.putInPropertyMap(name, values);
             return mBuilderTypeInstance;
         }
 
@@ -1531,95 +1561,27 @@
          *
          * <p>Note that this method does not support property paths.
          *
+         * <p>You should check for the existence of the property in {@link #getPropertyNames} if
+         * you need to make sure the property being cleared actually exists.
+         *
+         * <p>If the string passed is an invalid or nonexistent property, no error message or
+         * behavior will be observed.
+         *
          * @param name The name of the property to clear.
-         * <!--@exportToFramework:hide-->
          */
+        @FlaggedApi(Flags.FLAG_ENABLE_GENERIC_DOCUMENT_BUILDER_HIDDEN_METHODS)
         @CanIgnoreReturnValue
         @NonNull
         public BuilderType clearProperty(@NonNull String name) {
             Preconditions.checkNotNull(name);
-            resetIfBuilt();
-            mProperties.remove(name);
+            mDocumentParcelBuilder.clearProperty(name);
             return mBuilderTypeInstance;
         }
 
-        private void putInPropertyBundle(@NonNull String name, @NonNull String[] values)
-                throws IllegalArgumentException {
-            validatePropertyName(name);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] == null) {
-                    throw new IllegalArgumentException("The String at " + i + " is null.");
-                }
-            }
-            mProperties.putStringArray(name, values);
-        }
-
-        private void putInPropertyBundle(@NonNull String name, @NonNull boolean[] values) {
-            validatePropertyName(name);
-            mProperties.putBooleanArray(name, values);
-        }
-
-        private void putInPropertyBundle(@NonNull String name, @NonNull double[] values) {
-            validatePropertyName(name);
-            mProperties.putDoubleArray(name, values);
-        }
-
-        private void putInPropertyBundle(@NonNull String name, @NonNull long[] values) {
-            validatePropertyName(name);
-            mProperties.putLongArray(name, values);
-        }
-
-        /**
-         * Converts and saves a byte[][] into {@link #mProperties}.
-         *
-         * <p>Bundle doesn't support for two dimension array byte[][], we are converting byte[][]
-         * into ArrayList<Bundle>, and each elements will contain a one dimension byte[].
-         */
-        private void putInPropertyBundle(@NonNull String name, @NonNull byte[][] values) {
-            validatePropertyName(name);
-            ArrayList<Bundle> bundles = new ArrayList<>(values.length);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] == null) {
-                    throw new IllegalArgumentException("The byte[] at " + i + " is null.");
-                }
-                Bundle bundle = new Bundle();
-                bundle.putByteArray(BYTE_ARRAY_FIELD, values[i]);
-                bundles.add(bundle);
-            }
-            mProperties.putParcelableArrayList(name, bundles);
-        }
-
-        private void putInPropertyBundle(@NonNull String name, @NonNull GenericDocument[] values) {
-            validatePropertyName(name);
-            Parcelable[] documentBundles = new Parcelable[values.length];
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] == null) {
-                    throw new IllegalArgumentException("The document at " + i + " is null.");
-                }
-                documentBundles[i] = values[i].mBundle;
-            }
-            mProperties.putParcelableArray(name, documentBundles);
-        }
-
         /** Builds the {@link GenericDocument} object. */
         @NonNull
         public GenericDocument build() {
-            mBuilt = true;
-            // Set current timestamp for creation timestamp by default.
-            if (mBundle.getLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, -1) == -1) {
-                mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
-                        System.currentTimeMillis());
-            }
-            return new GenericDocument(mBundle);
-        }
-
-        private void resetIfBuilt() {
-            if (mBuilt) {
-                mBundle = BundleUtil.deepCopy(mBundle);
-                // mProperties is NonNull and initialized to empty Bundle() in builder.
-                mProperties = Preconditions.checkNotNull(mBundle.getBundle(PROPERTIES_FIELD));
-                mBuilt = false;
-            }
+            return new GenericDocument(mDocumentParcelBuilder.build());
         }
 
         /** Method to ensure property names are not blank */
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java
index ee59e34..be17430 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetByDocumentIdRequest.java
@@ -16,9 +16,20 @@
 
 package androidx.appsearch.app;
 
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.GetByDocumentIdRequestCreator;
+import androidx.appsearch.util.BundleUtil;
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
@@ -29,6 +40,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -37,7 +49,13 @@
  *
  * @see AppSearchSession#getByDocumentIdAsync
  */
-public final class GetByDocumentIdRequest {
+@SuppressWarnings("HiddenSuperclass")
[email protected](creator = "GetByDocumentIdRequestCreator")
+public final class GetByDocumentIdRequest extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull public static final Parcelable.Creator<GetByDocumentIdRequest> CREATOR =
+            new GetByDocumentIdRequestCreator();
     /**
      * Schema type to be used in
      * {@link GetByDocumentIdRequest.Builder#addProjection}
@@ -45,15 +63,30 @@
      * property paths set.
      */
     public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
-    private final String mNamespace;
-    private final Set<String> mIds;
-    private final Map<String, List<String>> mTypePropertyPathsMap;
 
-    GetByDocumentIdRequest(@NonNull String namespace, @NonNull Set<String> ids, @NonNull Map<String,
-            List<String>> typePropertyPathsMap) {
-        mNamespace = Preconditions.checkNotNull(namespace);
-        mIds = Preconditions.checkNotNull(ids);
-        mTypePropertyPathsMap = Preconditions.checkNotNull(typePropertyPathsMap);
+    @NonNull
+    @Field(id = 1, getter = "getNamespace")
+    private final String mNamespace;
+    @NonNull
+    @Field(id = 2)
+    final List<String> mIds;
+    @NonNull
+    @Field(id = 3)
+    final Bundle mTypePropertyPaths;
+
+    /**
+     * Cache of the ids. Comes from inflating mIds at first use.
+     */
+    @Nullable private Set<String> mIdsCached;
+
+    @Constructor
+    GetByDocumentIdRequest(
+            @Param(id = 1) @NonNull String namespace,
+            @Param(id = 2) @NonNull List<String> ids,
+            @Param(id = 3) @NonNull Bundle typePropertyPaths) {
+        mNamespace = Objects.requireNonNull(namespace);
+        mIds = Objects.requireNonNull(ids);
+        mTypePropertyPaths = Objects.requireNonNull(typePropertyPaths);
     }
 
     /** Returns the namespace attached to the request. */
@@ -65,7 +98,10 @@
     /** Returns the set of document IDs attached to the request. */
     @NonNull
     public Set<String> getIds() {
-        return Collections.unmodifiableSet(mIds);
+        if (mIdsCached == null) {
+            mIdsCached = Collections.unmodifiableSet(new ArraySet<>(mIds));
+        }
+        return mIdsCached;
     }
 
     /**
@@ -78,11 +114,15 @@
      */
     @NonNull
     public Map<String, List<String>> getProjections() {
-        Map<String, List<String>> copy = new ArrayMap<>();
-        for (Map.Entry<String, List<String>> entry : mTypePropertyPathsMap.entrySet()) {
-            copy.put(entry.getKey(), new ArrayList<>(entry.getValue()));
+        Set<String> schemas = mTypePropertyPaths.keySet();
+        Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
+        for (String schema : schemas) {
+            List<String> propertyPaths = mTypePropertyPaths.getStringArrayList(schema);
+            if (propertyPaths != null) {
+                typePropertyPathsMap.put(schema, Collections.unmodifiableList(propertyPaths));
+            }
         }
-        return copy;
+        return typePropertyPathsMap;
     }
 
     /**
@@ -95,38 +135,34 @@
      */
     @NonNull
     public Map<String, List<PropertyPath>> getProjectionPaths() {
-        Map<String, List<PropertyPath>> copy = new ArrayMap<>(mTypePropertyPathsMap.size());
-        for (Map.Entry<String, List<String>> entry : mTypePropertyPathsMap.entrySet()) {
-            List<PropertyPath> propertyPathList = new ArrayList<>(entry.getValue().size());
-            for (String p: entry.getValue()) {
-                propertyPathList.add(new PropertyPath(p));
+        Set<String> schemas = mTypePropertyPaths.keySet();
+        Map<String, List<PropertyPath>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
+        for (String schema : schemas) {
+            List<String> paths = mTypePropertyPaths.getStringArrayList(schema);
+            if (paths != null) {
+                int pathsSize = paths.size();
+                List<PropertyPath> propertyPathList = new ArrayList<>(pathsSize);
+                for (int i = 0; i < pathsSize; i++) {
+                    propertyPathList.add(new PropertyPath(paths.get(i)));
+                }
+                typePropertyPathsMap.put(schema, Collections.unmodifiableList(propertyPathList));
             }
-            copy.put(entry.getKey(), propertyPathList);
         }
-        return copy;
+        return typePropertyPathsMap;
     }
 
-    /**
-     * Returns a map from schema type to property paths to be used for projection.
-     *
-     * <p>If the map is empty, then all properties will be retrieved for all results.
-     *
-     * <p>A more efficient version of {@link #getProjections}, but it returns a modifiable map.
-     * This is not meant to be unhidden and should only be used by internal classes.
-     *
-     * @exportToFramework:hide
-     */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Map<String, List<String>> getProjectionsInternal() {
-        return mTypePropertyPathsMap;
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        GetByDocumentIdRequestCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link GetByDocumentIdRequest} objects. */
     public static final class Builder {
         private final String mNamespace;
-        private ArraySet<String> mIds = new ArraySet<>();
-        private ArrayMap<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
+        private List<String> mIds = new ArrayList<>();
+        private Bundle mProjectionTypePropertyPaths = new Bundle();
         private boolean mBuilt = false;
 
         /** Creates a {@link GetByDocumentIdRequest.Builder} instance. */
@@ -176,12 +212,12 @@
             Preconditions.checkNotNull(schemaType);
             Preconditions.checkNotNull(propertyPaths);
             resetIfBuilt();
-            List<String> propertyPathsList = new ArrayList<>(propertyPaths.size());
+            ArrayList<String> propertyPathsList = new ArrayList<>(propertyPaths.size());
             for (String propertyPath : propertyPaths) {
                 Preconditions.checkNotNull(propertyPath);
                 propertyPathsList.add(propertyPath);
             }
-            mProjectionTypePropertyPaths.put(schemaType, propertyPathsList);
+            mProjectionTypePropertyPaths.putStringArrayList(schemaType, propertyPathsList);
             return this;
         }
 
@@ -223,11 +259,11 @@
 
         private void resetIfBuilt() {
             if (mBuilt) {
-                mIds = new ArraySet<>(mIds);
+                mIds = new ArrayList<>(mIds);
                 // No need to clone each propertyPathsList inside mProjectionTypePropertyPaths since
                 // the builder only replaces it, never adds to it. So even if the builder is used
                 // again, the previous one will remain with the object.
-                mProjectionTypePropertyPaths = new ArrayMap<>(mProjectionTypePropertyPaths);
+                mProjectionTypePropertyPaths = BundleUtil.deepCopy(mProjectionTypePropertyPaths);
                 mBuilt = false;
             }
         }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java
index 0933654..74f370e 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GetSchemaResponse.java
@@ -17,7 +17,8 @@
 package androidx.appsearch.app;
 
 import android.annotation.SuppressLint;
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
@@ -25,6 +26,11 @@
 import androidx.annotation.RequiresFeature;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.GetSchemaResponseCreator;
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
@@ -36,58 +42,88 @@
 import java.util.Set;
 
 /** The response class of {@link AppSearchSession#getSchemaAsync} */
-public final class GetSchemaResponse {
-    private static final String VERSION_FIELD = "version";
-    private static final String SCHEMAS_FIELD = "schemas";
-    private static final String SCHEMAS_NOT_DISPLAYED_BY_SYSTEM_FIELD =
-            "schemasNotDisplayedBySystem";
-    private static final String SCHEMAS_VISIBLE_TO_PACKAGES_FIELD = "schemasVisibleToPackages";
-    private static final String SCHEMAS_VISIBLE_TO_PERMISSION_FIELD =
-            "schemasVisibleToPermissions";
-    private static final String ALL_REQUIRED_PERMISSION_FIELD =
-            "allRequiredPermission";
[email protected](creator = "GetSchemaResponseCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class GetSchemaResponse extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull
+    public static final Parcelable.Creator<GetSchemaResponse> CREATOR =
+            new GetSchemaResponseCreator();
+
+    @Field(id = 1, getter = "getVersion")
+    private final int mVersion;
+
+    @Field(id = 2)
+    final List<AppSearchSchema> mSchemas;
+
+    /**
+     * List of VisibilityConfigs for the current schema. May be {@code null} if retrieving the
+     * visibility settings is not possible on the current backend.
+     */
+    @Field(id = 3)
+    @Nullable
+    final List<InternalVisibilityConfig> mVisibilityConfigs;
+
+    /**
+     * This set contains all schemas most recently successfully provided to
+     * {@link AppSearchSession#setSchemaAsync}. We do lazy fetch, the object will be created when
+     * you first time fetch it.
+     */
+    @Nullable
+    private Set<AppSearchSchema> mSchemasCached;
+
     /**
      * This Set contains all schemas that are not displayed by the system. All values in the set are
      * prefixed with the package-database prefix. We do lazy fetch, the object will be created
      * when you first time fetch it.
      */
     @Nullable
-    private Set<String> mSchemasNotDisplayedBySystem;
+    private Set<String> mSchemasNotDisplayedBySystemCached;
+
     /**
      * This map contains all schemas and {@link PackageIdentifier} that has access to the schema.
      * All keys in the map are prefixed with the package-database prefix. We do lazy fetch, the
      * object will be created when you first time fetch it.
      */
     @Nullable
-    private Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
+    private Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackagesCached;
 
     /**
      * This map contains all schemas and Android Permissions combinations that are required to
      * access the schema. All keys in the map are prefixed with the package-database prefix. We
      * do lazy fetch, the object will be created when you first time fetch it.
      * The Map is constructed in ANY-ALL cases. The querier could read the {@link GenericDocument}
-     * objects under the {@code schemaType} if they holds ALL required permissions of ANY
+     * objects under the {@code schemaType} if they hold ALL required permissions of ANY
      * combinations.
-     * The value set represents
-     * {@link androidx.appsearch.app.SetSchemaRequest.AppSearchSupportedPermission}.
+     * @see SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility(String, Set)
      */
     @Nullable
-    private Map<String, Set<Set<Integer>>> mSchemasVisibleToPermissions;
-
-    private final Bundle mBundle;
-
-    GetSchemaResponse(@NonNull Bundle bundle) {
-        mBundle = Preconditions.checkNotNull(bundle);
-    }
+    private Map<String, Set<Set<Integer>>> mSchemasVisibleToPermissionsCached;
 
     /**
-     * Returns the {@link Bundle} populated by this builder.
-     * @exportToFramework:hide
+     * This map contains all publicly visible schemas and the {@link PackageIdentifier} specifying
+     * the package that the schemas are from.
      */
-    @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public Bundle getBundle() {
-        return mBundle;
+    @Nullable
+    private Map<String, PackageIdentifier> mPubliclyVisibleSchemasCached;
+
+    /**
+     * This map contains all {@link SchemaVisibilityConfig}s that has access to the schema.
+     * All keys in the map are prefixed with the package-database prefix. We do lazy fetch, the
+     * object will be created when you first time fetch it.
+     */
+    @Nullable
+    private Map<String, Set<SchemaVisibilityConfig>> mSchemasVisibleToConfigsCached;
+
+    @Constructor
+    GetSchemaResponse(
+            @Param(id = 1) int version,
+            @Param(id = 2) @NonNull List<AppSearchSchema> schemas,
+            @Param(id = 3) @Nullable List<InternalVisibilityConfig> visibilityConfigs) {
+        mVersion = version;
+        mSchemas = Preconditions.checkNotNull(schemas);
+        mVisibilityConfigs = visibilityConfigs;
     }
 
     /**
@@ -97,25 +133,19 @@
      */
     @IntRange(from = 0)
     public int getVersion() {
-        return mBundle.getInt(VERSION_FIELD);
+        return mVersion;
     }
 
     /**
      * Return the schemas most recently successfully provided to
      * {@link AppSearchSession#setSchemaAsync}.
-     *
-     * <p>It is inefficient to call this method repeatedly.
      */
     @NonNull
-    @SuppressWarnings("deprecation")
     public Set<AppSearchSchema> getSchemas() {
-        ArrayList<Bundle> schemaBundles = Preconditions.checkNotNull(
-                    mBundle.getParcelableArrayList(SCHEMAS_FIELD));
-        Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size());
-        for (int i = 0; i < schemaBundles.size(); i++) {
-            schemas.add(new AppSearchSchema(schemaBundles.get(i)));
+        if (mSchemasCached == null) {
+            mSchemasCached = Collections.unmodifiableSet(new ArraySet<>(mSchemas));
         }
-        return schemas;
+        return mSchemasCached;
     }
 
     /**
@@ -126,21 +156,22 @@
      * called with false.
      * <!--@exportToFramework:else()-->
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     @NonNull
     public Set<String> getSchemaTypesNotDisplayedBySystem() {
-        checkGetVisibilitySettingSupported();
-        if (mSchemasNotDisplayedBySystem == null) {
-            List<String> schemasNotDisplayedBySystemList =
-                    mBundle.getStringArrayList(SCHEMAS_NOT_DISPLAYED_BY_SYSTEM_FIELD);
-            mSchemasNotDisplayedBySystem =
-                    Collections.unmodifiableSet(new ArraySet<>(schemasNotDisplayedBySystemList));
+        List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
+        if (mSchemasNotDisplayedBySystemCached == null) {
+            Set<String> copy = new ArraySet<>();
+            for (int i = 0; i < visibilityConfigs.size(); i++) {
+                if (visibilityConfigs.get(i).isNotDisplayedBySystem()) {
+                    copy.add(visibilityConfigs.get(i).getSchemaType());
+                }
+            }
+            mSchemasNotDisplayedBySystemCached = Collections.unmodifiableSet(copy);
         }
-        return mSchemasNotDisplayedBySystem;
+        return mSchemasNotDisplayedBySystemCached;
     }
 
     /**
@@ -151,37 +182,32 @@
      * called with false.
      * <!--@exportToFramework:else()-->
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     @NonNull
-    @SuppressWarnings("deprecation")
     public Map<String, Set<PackageIdentifier>> getSchemaTypesVisibleToPackages() {
-        checkGetVisibilitySettingSupported();
-        if (mSchemasVisibleToPackages == null) {
-            Bundle schemaVisibleToPackagesBundle = Preconditions.checkNotNull(
-                        mBundle.getBundle(SCHEMAS_VISIBLE_TO_PACKAGES_FIELD));
+        List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
+        if (mSchemasVisibleToPackagesCached == null) {
             Map<String, Set<PackageIdentifier>> copy = new ArrayMap<>();
-            for (String key : schemaVisibleToPackagesBundle.keySet()) {
-                List<Bundle> PackageIdentifierBundles = Preconditions.checkNotNull(
-                            schemaVisibleToPackagesBundle.getParcelableArrayList(key));
-                Set<PackageIdentifier> packageIdentifiers =
-                        new ArraySet<>(PackageIdentifierBundles.size());
-                for (int i = 0; i < PackageIdentifierBundles.size(); i++) {
-                    packageIdentifiers.add(new PackageIdentifier(PackageIdentifierBundles.get(i)));
+            for (int i = 0; i < visibilityConfigs.size(); i++) {
+                InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
+                List<PackageIdentifier> visibleToPackages =
+                        visibilityConfig.getVisibilityConfig().getAllowedPackages();
+                if (!visibleToPackages.isEmpty()) {
+                    copy.put(
+                            visibilityConfig.getSchemaType(),
+                            Collections.unmodifiableSet(new ArraySet<>(visibleToPackages)));
                 }
-                copy.put(key, packageIdentifiers);
             }
-            mSchemasVisibleToPackages = Collections.unmodifiableMap(copy);
+            mSchemasVisibleToPackagesCached = Collections.unmodifiableMap(copy);
         }
-        return mSchemasVisibleToPackages;
+        return mSchemasVisibleToPackagesCached;
     }
 
     /**
-     * Returns a mapping of schema types to the Map of {@link android.Manifest.permission}
-     * combinations that querier must hold to access that schema type.
+     * Returns a mapping of schema types to the set of {@link android.Manifest.permission}
+     * combination sets that querier must hold to access that schema type.
      *
      * <p> The querier could read the {@link GenericDocument} objects under the {@code schemaType}
      * if they holds ALL required permissions of ANY of the individual value sets.
@@ -189,12 +215,12 @@
      * <p>For example, if the Map contains {@code {% verbatim %}{{permissionA, PermissionB},
      * { PermissionC, PermissionD}, {PermissionE}}{% endverbatim %}}.
      * <ul>
-     *     <li>A querier holds both PermissionA and PermissionB has access.</li>
-     *     <li>A querier holds both PermissionC and PermissionD has access.</li>
-     *     <li>A querier holds only PermissionE has access.</li>
-     *     <li>A querier holds both PermissionA and PermissionE has access.</li>
-     *     <li>A querier holds only PermissionA doesn't have access.</li>
-     *     <li>A querier holds both PermissionA and PermissionC doesn't have access.</li>
+     *     <li>A querier holding both PermissionA and PermissionB has access.</li>
+     *     <li>A querier holding both PermissionC and PermissionD has access.</li>
+     *     <li>A querier holding only PermissionE has access.</li>
+     *     <li>A querier holding both PermissionA and PermissionE has access.</li>
+     *     <li>A querier holding only PermissionA doesn't have access.</li>
+     *     <li>A querier holding only PermissionA and PermissionC doesn't have access.</li>
      * </ul>
      *
      * @return The map contains schema type and all combinations of required permission for querier
@@ -208,56 +234,118 @@
      * called with false.
      * <!--@exportToFramework:else()-->
      */
-    // @exportToFramework:startStrip()
+    // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     @NonNull
-    @SuppressWarnings("deprecation")
     public Map<String, Set<Set<Integer>>> getRequiredPermissionsForSchemaTypeVisibility() {
-        checkGetVisibilitySettingSupported();
-        if (mSchemasVisibleToPermissions == null) {
+        List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
+        if (mSchemasVisibleToPermissionsCached == null) {
             Map<String, Set<Set<Integer>>> copy = new ArrayMap<>();
-            Bundle schemaVisibleToPermissionBundle = Preconditions.checkNotNull(
-                        mBundle.getBundle(SCHEMAS_VISIBLE_TO_PERMISSION_FIELD));
-            for (String key : schemaVisibleToPermissionBundle.keySet()) {
-                ArrayList<Bundle> allRequiredPermissionsBundle =
-                        schemaVisibleToPermissionBundle.getParcelableArrayList(key);
-                Set<Set<Integer>> visibleToPermissions = new ArraySet<>();
-                if (allRequiredPermissionsBundle != null) {
-                    // This should never be null
-                    for (int i = 0; i < allRequiredPermissionsBundle.size(); i++) {
-                        visibleToPermissions.add(new ArraySet<>(allRequiredPermissionsBundle.get(i)
-                                .getIntegerArrayList(ALL_REQUIRED_PERMISSION_FIELD)));
-                    }
+            for (int i = 0; i < visibilityConfigs.size(); i++) {
+                InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
+                Set<Set<Integer>> visibleToPermissions =
+                        visibilityConfig.getVisibilityConfig().getRequiredPermissions();
+                if (!visibleToPermissions.isEmpty()) {
+                    copy.put(
+                            visibilityConfig.getSchemaType(),
+                            Collections.unmodifiableSet(visibleToPermissions));
                 }
-                copy.put(key, visibleToPermissions);
             }
-            mSchemasVisibleToPermissions = Collections.unmodifiableMap(copy);
+            mSchemasVisibleToPermissionsCached = Collections.unmodifiableMap(copy);
         }
-        return mSchemasVisibleToPermissions;
+        return mSchemasVisibleToPermissionsCached;
     }
 
-    private void checkGetVisibilitySettingSupported() {
-        if (!mBundle.containsKey(SCHEMAS_VISIBLE_TO_PACKAGES_FIELD)) {
+    /**
+     * Returns a mapping of publicly visible schemas to the {@link PackageIdentifier} specifying
+     * the package the schemas are from.
+     *
+     * <p> If no schemas have been set as publicly visible, an empty set will be returned.
+     * <!--@exportToFramework:ifJetpack()-->
+     * @throws UnsupportedOperationException if {@link Builder#setVisibilitySettingSupported} was
+     * called with false.
+     * <!--@exportToFramework:else()-->
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
+    @RequiresFeature(
+            enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+            name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
+    @NonNull
+    public Map<String, PackageIdentifier> getPubliclyVisibleSchemas() {
+        List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
+        if (mPubliclyVisibleSchemasCached == null) {
+            Map<String, PackageIdentifier> copy = new ArrayMap<>();
+            for (int i = 0; i < visibilityConfigs.size(); i++) {
+                InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
+                PackageIdentifier publiclyVisibleTargetPackage =
+                        visibilityConfig.getVisibilityConfig().getPubliclyVisibleTargetPackage();
+                if (publiclyVisibleTargetPackage != null) {
+                    copy.put(visibilityConfig.getSchemaType(), publiclyVisibleTargetPackage);
+                }
+            }
+            mPubliclyVisibleSchemasCached = Collections.unmodifiableMap(copy);
+        }
+        return mPubliclyVisibleSchemasCached;
+    }
+
+    /**
+     * Returns a mapping of schema types to the set of {@link SchemaVisibilityConfig} that have
+     * access to that schema type.
+     *
+     * @see SetSchemaRequest.Builder#addSchemaTypeVisibleToConfig
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+    @RequiresFeature(
+            enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+            name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
+    @NonNull
+    public Map<String, Set<SchemaVisibilityConfig>> getSchemaTypesVisibleToConfigs() {
+        List<InternalVisibilityConfig> visibilityConfigs = getVisibilityConfigsOrThrow();
+        if (mSchemasVisibleToConfigsCached == null) {
+            Map<String, Set<SchemaVisibilityConfig>> copy = new ArrayMap<>();
+            for (int i = 0; i < visibilityConfigs.size(); i++) {
+                InternalVisibilityConfig visibilityConfig = visibilityConfigs.get(i);
+                Set<SchemaVisibilityConfig> nestedVisibilityConfigs =
+                        visibilityConfig.getVisibleToConfigs();
+                if (!nestedVisibilityConfigs.isEmpty()) {
+                    copy.put(visibilityConfig.getSchemaType(),
+                            Collections.unmodifiableSet(nestedVisibilityConfigs));
+                }
+            }
+            mSchemasVisibleToConfigsCached = Collections.unmodifiableMap(copy);
+        }
+        return mSchemasVisibleToConfigsCached;
+    }
+
+    @NonNull
+    private List<InternalVisibilityConfig> getVisibilityConfigsOrThrow() {
+        List<InternalVisibilityConfig> visibilityConfigs = mVisibilityConfigs;
+        if (visibilityConfigs == null) {
             throw new UnsupportedOperationException("Get visibility setting is not supported with "
                     + "this backend/Android API level combination.");
         }
+        return visibilityConfigs;
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        GetSchemaResponseCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link GetSchemaResponse} objects. */
     public static final class Builder {
         private int mVersion = 0;
-        private ArrayList<Bundle> mSchemaBundles = new ArrayList<>();
+        private ArrayList<AppSearchSchema> mSchemas = new ArrayList<>();
         /**
          * Creates the object when we actually set them. If we never set visibility settings, we
          * should throw {@link UnsupportedOperationException} in the visibility getters.
          */
         @Nullable
-        private ArrayList<String> mSchemasNotDisplayedBySystem;
-        private Bundle mSchemasVisibleToPackages;
-        private Bundle mSchemasVisibleToPermissions;
+        private Map<String, InternalVisibilityConfig.Builder> mVisibilityConfigBuilders;
         private boolean mBuilt = false;
 
         /** Create a {@link Builder} object} */
@@ -284,7 +372,7 @@
         public Builder addSchema(@NonNull AppSearchSchema schema) {
             Preconditions.checkNotNull(schema);
             resetIfBuilt();
-            mSchemaBundles.add(schema.getBundle());
+            mSchemas.add(schema);
             return this;
         }
 
@@ -302,10 +390,9 @@
         public Builder addSchemaTypeNotDisplayedBySystem(@NonNull String schemaType) {
             Preconditions.checkNotNull(schemaType);
             resetIfBuilt();
-            if (mSchemasNotDisplayedBySystem == null) {
-                mSchemasNotDisplayedBySystem = new ArrayList<>();
-            }
-            mSchemasNotDisplayedBySystem.add(schemaType);
+            InternalVisibilityConfig.Builder visibilityConfigBuilder =
+                    getOrCreateVisibilityConfigBuilder(schemaType);
+            visibilityConfigBuilder.setNotDisplayedBySystem(true);
             return this;
         }
 
@@ -337,11 +424,11 @@
             Preconditions.checkNotNull(schemaType);
             Preconditions.checkNotNull(packageIdentifiers);
             resetIfBuilt();
-            ArrayList<Bundle> bundles = new ArrayList<>(packageIdentifiers.size());
+            InternalVisibilityConfig.Builder visibilityConfigBuilder =
+                    getOrCreateVisibilityConfigBuilder(schemaType);
             for (PackageIdentifier packageIdentifier : packageIdentifiers) {
-                bundles.add(packageIdentifier.getBundle());
+                visibilityConfigBuilder.addVisibleToPackage(packageIdentifier);
             }
-            mSchemasVisibleToPackages.putParcelableArrayList(schemaType, bundles);
             return this;
         }
 
@@ -364,41 +451,103 @@
          *     <li>A querier holds both PermissionA and PermissionC doesn't have access.</li>
          * </ul>
          *
+         * @param schemaType              The schema type to set visibility on.
+         * @param visibleToPermissionSets The Sets of Android permissions that will be required to
+         *                                access the given schema.
          * @see android.Manifest.permission#READ_SMS
          * @see android.Manifest.permission#READ_CALENDAR
          * @see android.Manifest.permission#READ_CONTACTS
          * @see android.Manifest.permission#READ_EXTERNAL_STORAGE
          * @see android.Manifest.permission#READ_HOME_APP_SEARCH_DATA
          * @see android.Manifest.permission#READ_ASSISTANT_APP_SEARCH_DATA
-         *
-         * @param schemaType             The schema type to set visibility on.
-         * @param visibleToPermissions   The Android permissions that will be required to access
-         *                               the given schema.
          */
+        // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
         // Getter getRequiredPermissionsForSchemaTypeVisibility returns a map for all schemaTypes.
         @CanIgnoreReturnValue
         @SuppressLint("MissingGetterMatchingBuilder")
+        // @SetSchemaRequest is an IntDef annotation applied to Set<Set<Integer>>.
+        @SuppressWarnings("SupportAnnotationUsage")
         @NonNull
         public Builder setRequiredPermissionsForSchemaTypeVisibility(
                 @NonNull String schemaType,
                 @SetSchemaRequest.AppSearchSupportedPermission @NonNull
-                        Set<Set<Integer>> visibleToPermissions) {
+                        Set<Set<Integer>> visibleToPermissionSets) {
             Preconditions.checkNotNull(schemaType);
-            Preconditions.checkNotNull(visibleToPermissions);
+            Preconditions.checkNotNull(visibleToPermissionSets);
             resetIfBuilt();
-            ArrayList<Bundle> visibleToPermissionsBundle = new ArrayList<>();
-            for (Set<Integer> allRequiredPermissions : visibleToPermissions) {
-                for (int permission : allRequiredPermissions) {
-                    Preconditions.checkArgumentInRange(permission, SetSchemaRequest.READ_SMS,
-                            SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA, "permission");
-                }
-                Bundle allRequiredPermissionsBundle = new Bundle();
-                allRequiredPermissionsBundle.putIntegerArrayList(
-                        ALL_REQUIRED_PERMISSION_FIELD, new ArrayList<>(allRequiredPermissions));
-                visibleToPermissionsBundle.add(allRequiredPermissionsBundle);
+            InternalVisibilityConfig.Builder visibilityConfigBuilder =
+                    getOrCreateVisibilityConfigBuilder(schemaType);
+            for (Set<Integer> visibleToPermissions : visibleToPermissionSets) {
+                visibilityConfigBuilder.addVisibleToPermissions(visibleToPermissions);
             }
-            mSchemasVisibleToPermissions.putParcelableArrayList(schemaType,
-                    visibleToPermissionsBundle);
+            return this;
+        }
+
+        /**
+         * Specify that the schema should be publicly available, to packages which already have
+         * visibility to {@code packageIdentifier}.
+         *
+         * @param schemaType the schema to make publicly accessible.
+         * @param packageIdentifier the package from which the document schema is from.
+         * @see SetSchemaRequest.Builder#setPubliclyVisibleSchema
+         */
+        // Merged list available from getPubliclyVisibleSchemas
+        @CanIgnoreReturnValue
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
+        @NonNull
+        public Builder setPubliclyVisibleSchema(
+                @NonNull String schemaType, @NonNull PackageIdentifier packageIdentifier) {
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(packageIdentifier);
+            resetIfBuilt();
+            InternalVisibilityConfig.Builder visibilityConfigBuilder =
+                    getOrCreateVisibilityConfigBuilder(schemaType);
+            visibilityConfigBuilder.setPubliclyVisibleTargetPackage(packageIdentifier);
+            return this;
+        }
+
+        /**
+         * Sets the documents from the provided {@code schemaType} can be read by the caller if they
+         * match the ALL visibility requirements set in {@link SchemaVisibilityConfig}.
+         *
+         * <p> The requirements in a {@link SchemaVisibilityConfig} is "AND" relationship. A
+         * caller must match ALL requirements to access the schema. For example, a caller must hold
+         * required permissions AND it is a specified package.
+         *
+         * <p> The querier could have access if they match ALL requirements in ANY of the given
+         * {@link SchemaVisibilityConfig}s
+         *
+         * <p>For example, if the Set contains {@code {% verbatim %}{{PackageA and Permission1},
+         * {PackageB and Permission2}}{% endverbatim %}}.
+         * <ul>
+         *     <li>A querier from packageA could read if they holds Permission1.</li>
+         *     <li>A querier from packageA could NOT read if they only holds Permission2 instead of
+         *     Permission1.</li>
+         *     <li>A querier from packageB could read if they holds Permission2.</li>
+         *     <li>A querier from packageC could never read.</li>
+         *     <li>A querier holds both PermissionA and PermissionE has access.</li>
+         * </ul>
+         *
+         * @param schemaType         The schema type to set visibility on.
+         * @param visibleToConfigs   The {@link SchemaVisibilityConfig}s hold all requirements that
+         *                           a call must to match to access the schema.
+         */
+        // Merged map available from getSchemasVisibleToConfigs
+        @CanIgnoreReturnValue
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+        @NonNull
+        public Builder setSchemaTypeVisibleToConfigs(@NonNull String schemaType,
+                @NonNull Set<SchemaVisibilityConfig> visibleToConfigs) {
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(visibleToConfigs);
+            resetIfBuilt();
+            InternalVisibilityConfig.Builder visibilityConfigBuilder =
+                    getOrCreateVisibilityConfigBuilder(schemaType);
+            for (SchemaVisibilityConfig visibleToConfig : visibleToConfigs) {
+                visibilityConfigBuilder.addVisibleToConfig(visibleToConfig);
+            }
             return this;
         }
 
@@ -416,17 +565,14 @@
          * @exportToFramework:hide
          */
          // Visibility setting is determined by SDK version, so it won't be needed in framework
+        @CanIgnoreReturnValue
         @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
         public Builder setVisibilitySettingSupported(boolean visibilitySettingSupported) {
             if (visibilitySettingSupported) {
-                mSchemasNotDisplayedBySystem = new ArrayList<>();
-                mSchemasVisibleToPackages = new Bundle();
-                mSchemasVisibleToPermissions = new Bundle();
+                mVisibilityConfigBuilders = new ArrayMap<>();
             } else {
-                mSchemasNotDisplayedBySystem = null;
-                mSchemasVisibleToPackages = null;
-                mSchemasVisibleToPermissions = null;
+                mVisibilityConfigBuilders = null;
             }
             return this;
         }
@@ -434,33 +580,37 @@
         /** Builds a {@link GetSchemaResponse} object. */
         @NonNull
         public GetSchemaResponse build() {
-            Bundle bundle = new Bundle();
-            bundle.putInt(VERSION_FIELD, mVersion);
-            bundle.putParcelableArrayList(SCHEMAS_FIELD, mSchemaBundles);
-            if (mSchemasNotDisplayedBySystem != null) {
-                // Only save the visibility fields if it was actually set.
-                bundle.putStringArrayList(SCHEMAS_NOT_DISPLAYED_BY_SYSTEM_FIELD,
-                        mSchemasNotDisplayedBySystem);
-                bundle.putBundle(SCHEMAS_VISIBLE_TO_PACKAGES_FIELD, mSchemasVisibleToPackages);
-                bundle.putBundle(SCHEMAS_VISIBLE_TO_PERMISSION_FIELD, mSchemasVisibleToPermissions);
+            List<InternalVisibilityConfig> visibilityConfigs = null;
+            if (mVisibilityConfigBuilders != null) {
+                visibilityConfigs = new ArrayList<>();
+                for (InternalVisibilityConfig.Builder builder :
+                        mVisibilityConfigBuilders.values()) {
+                    visibilityConfigs.add(builder.build());
+                }
             }
             mBuilt = true;
-            return new GetSchemaResponse(bundle);
+            return new GetSchemaResponse(mVersion, mSchemas, visibilityConfigs);
+        }
+
+        @NonNull
+        private InternalVisibilityConfig.Builder getOrCreateVisibilityConfigBuilder(
+                @NonNull String schemaType) {
+            if (mVisibilityConfigBuilders == null) {
+                throw new IllegalStateException("GetSchemaResponse is not configured with"
+                        + "visibility setting support");
+            }
+            InternalVisibilityConfig.Builder builder = mVisibilityConfigBuilders.get(schemaType);
+            if (builder == null) {
+                builder = new InternalVisibilityConfig.Builder(schemaType);
+                mVisibilityConfigBuilders.put(schemaType, builder);
+            }
+            return builder;
         }
 
         private void resetIfBuilt() {
             if (mBuilt) {
-                mSchemaBundles = new ArrayList<>(mSchemaBundles);
-                if (mSchemasNotDisplayedBySystem != null) {
-                    // Only reset the visibility fields if it was actually set.
-                    mSchemasNotDisplayedBySystem = new ArrayList<>(mSchemasNotDisplayedBySystem);
-                    Bundle copyVisibleToPackages = new Bundle();
-                    copyVisibleToPackages.putAll(mSchemasVisibleToPackages);
-                    mSchemasVisibleToPackages = copyVisibleToPackages;
-                    Bundle copyVisibleToPermissions = new Bundle();
-                    copyVisibleToPermissions.putAll(mSchemasVisibleToPermissions);
-                    mSchemasVisibleToPermissions = copyVisibleToPermissions;
-                }
+                // No need to copy mVisibilityConfigBuilders -- it gets copied during build().
+                mSchemas = new ArrayList<>(mSchemas);
                 mBuilt = false;
             }
         }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java
index 7f9dfb0..17fbfbd 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java
@@ -53,11 +53,9 @@
      * @param request a request containing a namespace and IDs of the documents to retrieve.
      */
     @NonNull
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.GLOBAL_SEARCH_SESSION_GET_BY_ID)
-    // @exportToFramework:endStrip()
     ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
             @NonNull String packageName,
             @NonNull String databaseName,
@@ -127,11 +125,9 @@
     // This call hits disk; async API prevents us from treating these calls as properties.
     @SuppressLint("KotlinPropertyAccess")
     @NonNull
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.GLOBAL_SEARCH_SESSION_GET_SCHEMA)
-    // @exportToFramework:endStrip()
     ListenableFuture<GetSchemaResponse> getSchemaAsync(@NonNull String packageName,
             @NonNull String databaseName);
 
@@ -170,11 +166,9 @@
      * @throws UnsupportedOperationException if this feature is not available on this
      *                                       AppSearch implementation.
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK)
-    // @exportToFramework:endStrip()
     void registerObserverCallback(
             @NonNull String targetPackageName,
             @NonNull ObserverSpec spec,
@@ -204,11 +198,9 @@
      * @throws UnsupportedOperationException if this feature is not available on this
      *                                       AppSearch implementation.
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK)
-    // @exportToFramework:endStrip()
     void unregisterObserverCallback(
             @NonNull String targetPackageName, @NonNull ObserverCallback observer)
             throws AppSearchException;
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/InternalSetSchemaResponse.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/InternalSetSchemaResponse.java
index 3a1da37..2ece210 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/InternalSetSchemaResponse.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/InternalSetSchemaResponse.java
@@ -16,11 +16,17 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.InternalSetSchemaResponseCreator;
 import androidx.core.util.Preconditions;
 
 /**
@@ -34,36 +40,30 @@
  * @exportToFramework:hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class InternalSetSchemaResponse {
-
-    private static final String IS_SUCCESS_FIELD = "isSuccess";
-    private static final String SET_SCHEMA_RESPONSE_BUNDLE_FIELD = "setSchemaResponseBundle";
-    private static final String ERROR_MESSAGE_FIELD = "errorMessage";
-
-    private final Bundle mBundle;
-
-    public InternalSetSchemaResponse(@NonNull Bundle bundle) {
-        mBundle = Preconditions.checkNotNull(bundle);
-    }
-
-    private InternalSetSchemaResponse(boolean isSuccess,
-            @NonNull SetSchemaResponse setSchemaResponse,
-            @Nullable String errorMessage) {
-        Preconditions.checkNotNull(setSchemaResponse);
-        mBundle = new Bundle();
-        mBundle.putBoolean(IS_SUCCESS_FIELD, isSuccess);
-        mBundle.putBundle(SET_SCHEMA_RESPONSE_BUNDLE_FIELD, setSchemaResponse.getBundle());
-        mBundle.putString(ERROR_MESSAGE_FIELD, errorMessage);
-    }
-
-    /**
-     * Returns the {@link Bundle} populated by this builder.
-     * @exportToFramework:hide
-     */
-    @NonNull
[email protected](creator = "InternalSetSchemaResponseCreator")
+public class InternalSetSchemaResponse extends AbstractSafeParcelable {
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public Bundle getBundle() {
-        return mBundle;
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull public static final Parcelable.Creator<InternalSetSchemaResponse> CREATOR =
+            new InternalSetSchemaResponseCreator();
+
+    @Field(id = 1, getter = "isSuccess")
+    private final boolean mIsSuccess;
+
+    @Field(id = 2, getter = "getSetSchemaResponse")
+    private final SetSchemaResponse mSetSchemaResponse;
+    @Field(id = 3, getter = "getErrorMessage")
+    @Nullable private final String mErrorMessage;
+
+    @Constructor
+    public InternalSetSchemaResponse(
+            @Param(id = 1) boolean isSuccess,
+            @Param(id = 2) @NonNull SetSchemaResponse setSchemaResponse,
+            @Param(id = 3) @Nullable String errorMessage) {
+        Preconditions.checkNotNull(setSchemaResponse);
+        mIsSuccess = isSuccess;
+        mSetSchemaResponse = setSchemaResponse;
+        mErrorMessage = errorMessage;
     }
 
     /**
@@ -94,7 +94,7 @@
 
     /** Returns {@code true} if the schema request is proceeded successfully. */
     public boolean isSuccess() {
-        return mBundle.getBoolean(IS_SUCCESS_FIELD);
+        return mIsSuccess;
     }
 
     /**
@@ -104,7 +104,7 @@
      */
     @NonNull
     public SetSchemaResponse getSetSchemaResponse() {
-        return new SetSchemaResponse(mBundle.getBundle(SET_SCHEMA_RESPONSE_BUNDLE_FIELD));
+        return mSetSchemaResponse;
     }
 
 
@@ -115,6 +115,13 @@
      */
     @Nullable
     public String getErrorMessage() {
-        return mBundle.getString(ERROR_MESSAGE_FIELD);
+        return mErrorMessage;
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        InternalSetSchemaResponseCreator.writeToParcel(this, dest, flags);
     }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/InternalVisibilityConfig.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/InternalVisibilityConfig.java
new file mode 100644
index 0000000..843d1cf
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/InternalVisibilityConfig.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.InternalVisibilityConfigCreator;
+import androidx.collection.ArraySet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * An expanded version of {@link SchemaVisibilityConfig} which includes fields for internal use by
+ * AppSearch.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
[email protected](creator = "InternalVisibilityConfigCreator")
+public final class InternalVisibilityConfig extends AbstractSafeParcelable {
+    @NonNull
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final Parcelable.Creator<InternalVisibilityConfig> CREATOR =
+            new InternalVisibilityConfigCreator();
+
+    /**
+     * Build the List of {@link InternalVisibilityConfig}s from visibility settings.
+     */
+    @NonNull
+    public static List<InternalVisibilityConfig> toInternalVisibilityConfigs(
+            @NonNull SetSchemaRequest setSchemaRequest) {
+        Set<AppSearchSchema> searchSchemas = setSchemaRequest.getSchemas();
+        Set<String> schemasNotDisplayedBySystem = setSchemaRequest.getSchemasNotDisplayedBySystem();
+        Map<String, Set<PackageIdentifier>> schemasVisibleToPackages =
+                setSchemaRequest.getSchemasVisibleToPackages();
+        Map<String, Set<Set<Integer>>> schemasVisibleToPermissions =
+                setSchemaRequest.getRequiredPermissionsForSchemaTypeVisibility();
+        Map<String, PackageIdentifier> publiclyVisibleSchemas =
+                setSchemaRequest.getPubliclyVisibleSchemas();
+        Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs =
+                setSchemaRequest.getSchemasVisibleToConfigs();
+
+        List<InternalVisibilityConfig> result = new ArrayList<>(searchSchemas.size());
+        for (AppSearchSchema searchSchema : searchSchemas) {
+            String schemaType = searchSchema.getSchemaType();
+            InternalVisibilityConfig.Builder builder =
+                    new InternalVisibilityConfig.Builder(schemaType)
+                            .setNotDisplayedBySystem(
+                                    schemasNotDisplayedBySystem.contains(schemaType));
+
+            Set<PackageIdentifier> visibleToPackages = schemasVisibleToPackages.get(schemaType);
+            if (visibleToPackages != null) {
+                for (PackageIdentifier packageIdentifier : visibleToPackages) {
+                    builder.addVisibleToPackage(packageIdentifier);
+                }
+            }
+
+            Set<Set<Integer>> visibleToPermissionSets = schemasVisibleToPermissions.get(schemaType);
+            if (visibleToPermissionSets != null) {
+                for (Set<Integer> visibleToPermissions : visibleToPermissionSets) {
+                    builder.addVisibleToPermissions(visibleToPermissions);
+                }
+            }
+
+            PackageIdentifier publiclyVisibleTargetPackage = publiclyVisibleSchemas.get(schemaType);
+            if (publiclyVisibleTargetPackage != null) {
+                builder.setPubliclyVisibleTargetPackage(publiclyVisibleTargetPackage);
+            }
+
+            Set<SchemaVisibilityConfig> visibleToConfigs = schemasVisibleToConfigs.get(schemaType);
+            if (visibleToConfigs != null) {
+                for (SchemaVisibilityConfig schemaVisibilityConfig : visibleToConfigs) {
+                    builder.addVisibleToConfig(schemaVisibilityConfig);
+                }
+            }
+
+            result.add(builder.build());
+        }
+        return result;
+    }
+
+    @NonNull
+    @Field(id = 1, getter = "getSchemaType")
+    private final String mSchemaType;
+
+    @Field(id = 2, getter = "isNotDisplayedBySystem")
+    private final boolean mIsNotDisplayedBySystem;
+
+    /** The public visibility settings available in VisibilityConfig. */
+    @NonNull
+    @Field(id = 3, getter = "getVisibilityConfig")
+    private final SchemaVisibilityConfig mVisibilityConfig;
+
+    /** Extended visibility settings from {@link SetSchemaRequest#getSchemasVisibleToConfigs()} */
+    @NonNull
+    @Field(id = 4)
+    final List<SchemaVisibilityConfig> mVisibleToConfigs;
+
+    @Constructor
+    InternalVisibilityConfig(
+            @Param(id = 1) @NonNull String schemaType,
+            @Param(id = 2) boolean isNotDisplayedBySystem,
+            @Param(id = 3) @NonNull SchemaVisibilityConfig schemaVisibilityConfig,
+            @Param(id = 4) @NonNull List<SchemaVisibilityConfig> visibleToConfigs) {
+        mIsNotDisplayedBySystem = isNotDisplayedBySystem;
+        mSchemaType = Objects.requireNonNull(schemaType);
+        mVisibilityConfig = Objects.requireNonNull(schemaVisibilityConfig);
+        mVisibleToConfigs = Objects.requireNonNull(visibleToConfigs);
+    }
+
+    /**
+     * Gets the schemaType for this VisibilityConfig.
+     *
+     * <p>This is being used as the document id when we convert a {@link InternalVisibilityConfig}
+     * to a {@link GenericDocument}.
+     */
+    @NonNull
+    public String getSchemaType() {
+        return mSchemaType;
+    }
+
+    /** Returns whether this schema is visible to the system. */
+    public boolean isNotDisplayedBySystem() {
+        return mIsNotDisplayedBySystem;
+    }
+
+    /**
+     * Returns the visibility settings stored in the public {@link SchemaVisibilityConfig} object.
+     */
+    @NonNull
+    public SchemaVisibilityConfig getVisibilityConfig() {
+        return mVisibilityConfig;
+    }
+
+    /**
+     * Returns required {@link SchemaVisibilityConfig} sets for a caller need to match to access the
+     * schema this {@link InternalVisibilityConfig} represents.
+     */
+    @NonNull
+    public Set<SchemaVisibilityConfig> getVisibleToConfigs() {
+        return new ArraySet<>(mVisibleToConfigs);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        InternalVisibilityConfigCreator.writeToParcel(this, dest, flags);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null) {
+            return false;
+        }
+        if (!(o instanceof InternalVisibilityConfig)) {
+            return false;
+        }
+        InternalVisibilityConfig that = (InternalVisibilityConfig) o;
+        return mIsNotDisplayedBySystem == that.mIsNotDisplayedBySystem
+                && Objects.equals(mSchemaType, that.mSchemaType)
+                && Objects.equals(mVisibilityConfig, that.mVisibilityConfig)
+                && Objects.equals(mVisibleToConfigs, that.mVisibleToConfigs);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsNotDisplayedBySystem, mSchemaType, mVisibilityConfig,
+                mVisibleToConfigs);
+    }
+
+    /** The builder class of {@link InternalVisibilityConfig}. */
+    @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+    public static final class Builder {
+        private String mSchemaType;
+        private boolean mIsNotDisplayedBySystem;
+        private SchemaVisibilityConfig.Builder mVisibilityConfigBuilder;
+        private List<SchemaVisibilityConfig> mVisibleToConfigs = new ArrayList<>();
+        private boolean mBuilt;
+
+        /**
+         * Creates a {@link Builder} for a {@link InternalVisibilityConfig}.
+         *
+         * @param schemaType The SchemaType of the {@link AppSearchSchema} that this {@link
+         *                   InternalVisibilityConfig} represents. The package and database prefix
+         *                   will be added in server side. We are using prefixed schema type to be
+         *                   the final id of this {@link InternalVisibilityConfig}. This will be
+         *                   used as as an AppSearch id.
+         * @see GenericDocument#getId
+         */
+        public Builder(@NonNull String schemaType) {
+            mSchemaType = Objects.requireNonNull(schemaType);
+            mVisibilityConfigBuilder = new SchemaVisibilityConfig.Builder();
+        }
+
+        /** Creates a {@link Builder} from an existing {@link InternalVisibilityConfig} */
+        public Builder(@NonNull InternalVisibilityConfig internalVisibilityConfig) {
+            Objects.requireNonNull(internalVisibilityConfig);
+            mSchemaType = internalVisibilityConfig.mSchemaType;
+            mIsNotDisplayedBySystem = internalVisibilityConfig.mIsNotDisplayedBySystem;
+            mVisibilityConfigBuilder = new SchemaVisibilityConfig.Builder(
+                    internalVisibilityConfig.getVisibilityConfig());
+            mVisibleToConfigs = internalVisibilityConfig.mVisibleToConfigs;
+        }
+
+        /** Sets schemaType, which will be as the id when converting to {@link GenericDocument}. */
+        @NonNull
+        @CanIgnoreReturnValue
+        public Builder setSchemaType(@NonNull String schemaType) {
+            resetIfBuilt();
+            mSchemaType = Objects.requireNonNull(schemaType);
+            return this;
+        }
+
+        /**
+         * Resets all values contained in the VisibilityConfig with the values from the given
+         * VisibiltiyConfig.
+         */
+        @NonNull
+        @CanIgnoreReturnValue
+        public Builder setVisibilityConfig(@NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
+            resetIfBuilt();
+            mVisibilityConfigBuilder = new SchemaVisibilityConfig.Builder(schemaVisibilityConfig);
+            return this;
+        }
+
+        /**
+         * Sets whether this schema has opted out of platform surfacing.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
+            resetIfBuilt();
+            mIsNotDisplayedBySystem = notDisplayedBySystem;
+            return this;
+        }
+
+        /**
+         * Add {@link PackageIdentifier} of packages which has access to this schema.
+         *
+         * @see SchemaVisibilityConfig.Builder#addAllowedPackage
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
+            resetIfBuilt();
+            mVisibilityConfigBuilder.addAllowedPackage(packageIdentifier);
+            return this;
+        }
+
+        /**
+         * Clears the list of packages which have access to this schema.
+         *
+         * @see SchemaVisibilityConfig.Builder#clearAllowedPackages
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder clearVisibleToPackages() {
+            resetIfBuilt();
+            mVisibilityConfigBuilder.clearAllowedPackages();
+            return this;
+        }
+
+        /**
+         * Adds a set of required Android {@link android.Manifest.permission} combination a package
+         * needs to hold to access the schema.
+         *
+         * @see SchemaVisibilityConfig.Builder#addRequiredPermissions
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addVisibleToPermissions(@NonNull Set<Integer> visibleToPermissions) {
+            resetIfBuilt();
+            mVisibilityConfigBuilder.addRequiredPermissions(visibleToPermissions);
+            return this;
+        }
+
+        /**
+         * Clears all required permissions combinations set to this {@link SchemaVisibilityConfig}.
+         *
+         * @see SchemaVisibilityConfig.Builder#clearRequiredPermissions
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder clearVisibleToPermissions() {
+            resetIfBuilt();
+            mVisibilityConfigBuilder.clearRequiredPermissions();
+            return this;
+        }
+
+        /**
+         * Specify that this schema should be publicly available, to the same packages that have
+         * visibility to the package passed as a parameter. This visibility is determined by the
+         * result of {@link android.content.pm.PackageManager#canPackageQuery}.
+         *
+         * @see SchemaVisibilityConfig.Builder#setPubliclyVisibleTargetPackage
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setPubliclyVisibleTargetPackage(
+                @Nullable PackageIdentifier packageIdentifier) {
+            resetIfBuilt();
+            mVisibilityConfigBuilder.setPubliclyVisibleTargetPackage(packageIdentifier);
+            return this;
+        }
+
+        /**
+         * Add the {@link SchemaVisibilityConfig} for a caller need to match to access the schema
+         * this {@link InternalVisibilityConfig} represents.
+         *
+         * <p> You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig},
+         * and the querier will have access if they match ANY of the
+         * {@link SchemaVisibilityConfig}.
+         *
+         * @param schemaVisibilityConfig The {@link SchemaVisibilityConfig} hold all requirements
+         *                               that a call must match to access the schema.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addVisibleToConfig(@NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
+            Objects.requireNonNull(schemaVisibilityConfig);
+            resetIfBuilt();
+            mVisibleToConfigs.add(schemaVisibilityConfig);
+            return this;
+        }
+
+        /** Clears the set of {@link SchemaVisibilityConfig} which have access to this schema. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder clearVisibleToConfig() {
+            resetIfBuilt();
+            mVisibleToConfigs.clear();
+            return this;
+        }
+
+        private void resetIfBuilt() {
+            if (mBuilt) {
+                mVisibleToConfigs = new ArrayList<>(mVisibleToConfigs);
+                mBuilt = false;
+            }
+        }
+
+        /** Build a {@link InternalVisibilityConfig} */
+        @NonNull
+        public InternalVisibilityConfig build() {
+            mBuilt = true;
+            return new InternalVisibilityConfig(
+                    mSchemaType,
+                    mIsNotDisplayedBySystem,
+                    mVisibilityConfigBuilder.build(),
+                    mVisibleToConfigs);
+        }
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/JetpackAppSearchEnvironment.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/JetpackAppSearchEnvironment.java
new file mode 100644
index 0000000..bd13255
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/JetpackAppSearchEnvironment.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+package androidx.appsearch.app;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+import java.io.File;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Contains utility methods for Framework implementation of AppSearch.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class JetpackAppSearchEnvironment implements AppSearchEnvironment {
+
+    /**
+     * Returns AppSearch directory in the credential encrypted system directory for the given user.
+     *
+     * <p>This folder should only be accessed after unlock.
+     */
+    @Override
+    @NonNull
+    public File getAppSearchDir(@NonNull Context context, @Nullable UserHandle unused) {
+        return new File(context.getFilesDir(), "appsearch");
+    }
+
+    /** Creates context for the user based on the userHandle. */
+    @Override
+    @NonNull
+    public Context createContextAsUser(@NonNull Context context, @NonNull UserHandle userHandle) {
+        return context;
+    }
+
+    /** Creates and returns a ThreadPoolExecutor for given parameters. */
+    @Override
+    @NonNull
+    public ExecutorService createExecutorService(
+            int corePoolSize,
+            int maxConcurrency,
+            long keepAliveTime,
+            @NonNull TimeUnit unit,
+            @NonNull BlockingQueue<Runnable> workQueue,
+            int priority) {
+        return new ThreadPoolExecutor(
+                corePoolSize,
+                maxConcurrency,
+                keepAliveTime,
+                unit,
+                workQueue);
+    }
+
+    /** Creates and returns an ExecutorService with a single thread. */
+    @Override
+    @NonNull
+    public ExecutorService createSingleThreadExecutor() {
+        return Executors.newSingleThreadExecutor();
+    }
+
+    /** Creates and returns an Executor with cached thread pools. */
+    @Override
+    @NonNull
+    public ExecutorService createCachedThreadPoolExecutor() {
+        return Executors.newCachedThreadPool();
+    }
+
+    /**
+     * Returns a cache directory for creating temporary files like in case of migrating documents.
+     */
+    @Override
+    @Nullable
+    public File getCacheDir(@NonNull Context context) {
+        return context.getCacheDir();
+    }
+
+    @Override
+    public boolean isInfoLoggingEnabled() {
+        // INFO logging is enabled by default in Jetpack AppSearch.
+        return true;
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java
index 3639a79..16aff14 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/JoinSpec.java
@@ -16,16 +16,23 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.JoinSpecCreator;
 import androidx.core.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * This class represents the specifications for the joining operation in search.
@@ -117,12 +124,29 @@
  * return the signals calculated by scoring the joined documents using the scoring strategy in the
  * nested {@link SearchSpec}, as in {@link SearchResult#getRankingSignal}.
  */
-public final class JoinSpec {
-    static final String NESTED_QUERY = "nestedQuery";
-    static final String NESTED_SEARCH_SPEC = "nestedSearchSpec";
-    static final String CHILD_PROPERTY_EXPRESSION = "childPropertyExpression";
-    static final String MAX_JOINED_RESULT_COUNT = "maxJoinedResultCount";
-    static final String AGGREGATION_SCORING_STRATEGY = "aggregationScoringStrategy";
[email protected](creator = "JoinSpecCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class JoinSpec extends AbstractSafeParcelable {
+    /** Creator class for {@link JoinSpec}. */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull
+    public static final Parcelable.Creator<JoinSpec> CREATOR = new JoinSpecCreator();
+
+    @Field(id = 1, getter = "getNestedQuery")
+    private final String mNestedQuery;
+
+    @Field(id = 2, getter = "getNestedSearchSpec")
+    private final SearchSpec mNestedSearchSpec;
+
+    @Field(id = 3, getter = "getChildPropertyExpression")
+    private final String mChildPropertyExpression;
+
+    @Field(id = 4, getter = "getMaxJoinedResultCount")
+    private final int mMaxJoinedResultCount;
+
+    @Field(id = 5, getter = "getAggregationScoringStrategy")
+    private final int mAggregationScoringStrategy;
 
     private static final int DEFAULT_MAX_JOINED_RESULT_COUNT = 10;
 
@@ -158,8 +182,10 @@
     public @interface AggregationScoringStrategy {
     }
 
-    /** Do not score the aggregation of joined documents. This is for the case where we want to
-     * perform a join, but keep the parent ranking signal. */
+    /**
+     * Do not score the aggregation of joined documents. This is for the case where we want to
+     * perform a join, but keep the parent ranking signal.
+     */
     public static final int AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL = 0;
     /** Score the aggregation of joined documents by counting the number of results. */
     public static final int AGGREGATION_SCORING_RESULT_COUNT = 1;
@@ -172,33 +198,27 @@
     /** Score the aggregation of joined documents using the sum of ranking signal. */
     public static final int AGGREGATION_SCORING_SUM_RANKING_SIGNAL = 5;
 
-    private final Bundle mBundle;
-
-    /** @exportToFramework:hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public JoinSpec(@NonNull Bundle bundle) {
-        Preconditions.checkNotNull(bundle);
-        mBundle = bundle;
+    @Constructor
+    JoinSpec(
+            @Param(id = 1) @NonNull String nestedQuery,
+            @Param(id = 2) @NonNull SearchSpec nestedSearchSpec,
+            @Param(id = 3) @NonNull String childPropertyExpression,
+            @Param(id = 4) int maxJoinedResultCount,
+            @Param(id = 5) @AggregationScoringStrategy int aggregationScoringStrategy) {
+        mNestedQuery = Objects.requireNonNull(nestedQuery);
+        mNestedSearchSpec = Objects.requireNonNull(nestedSearchSpec);
+        mChildPropertyExpression = Objects.requireNonNull(childPropertyExpression);
+        mMaxJoinedResultCount = maxJoinedResultCount;
+        mAggregationScoringStrategy = aggregationScoringStrategy;
     }
 
-    /**
-     * Returns the {@link Bundle} populated by this builder.
-     *
-     * @exportToFramework:hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Bundle getBundle() {
-        return mBundle;
-    }
 
     /**
      * Returns the query to run on the joined documents.
-     *
      */
     @NonNull
     public String getNestedQuery() {
-        return mBundle.getString(NESTED_QUERY);
+        return mNestedQuery;
     }
 
     /**
@@ -211,7 +231,7 @@
      */
     @NonNull
     public String getChildPropertyExpression() {
-        return mBundle.getString(CHILD_PROPERTY_EXPRESSION);
+        return mChildPropertyExpression;
     }
 
     /**
@@ -219,7 +239,7 @@
      * with a default of 10 SearchResults.
      */
     public int getMaxJoinedResultCount() {
-        return mBundle.getInt(MAX_JOINED_RESULT_COUNT);
+        return mMaxJoinedResultCount;
     }
 
     /**
@@ -231,7 +251,7 @@
      */
     @NonNull
     public SearchSpec getNestedSearchSpec() {
-        return new SearchSpec(mBundle.getBundle(NESTED_SEARCH_SPEC));
+        return mNestedSearchSpec;
     }
 
     /**
@@ -244,7 +264,14 @@
      */
     @AggregationScoringStrategy
     public int getAggregationScoringStrategy() {
-        return mBundle.getInt(AGGREGATION_SCORING_STRATEGY);
+        return mAggregationScoringStrategy;
+    }
+
+    @Override
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        JoinSpecCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link JoinSpec objects}. */
@@ -257,7 +284,8 @@
         private SearchSpec mNestedSearchSpec = EMPTY_SEARCH_SPEC;
         private final String mChildPropertyExpression;
         private int mMaxJoinedResultCount = DEFAULT_MAX_JOINED_RESULT_COUNT;
-        @AggregationScoringStrategy private int mAggregationScoringStrategy =
+        @AggregationScoringStrategy
+        private int mAggregationScoringStrategy =
                 AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL;
 
         /**
@@ -289,6 +317,17 @@
             mChildPropertyExpression = childPropertyExpression;
         }
 
+        /** @exportToFramework:hide */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public Builder(@NonNull JoinSpec joinSpec) {
+            Preconditions.checkNotNull(joinSpec);
+            mNestedQuery = joinSpec.getNestedQuery();
+            mNestedSearchSpec = joinSpec.getNestedSearchSpec();
+            mChildPropertyExpression = joinSpec.getChildPropertyExpression();
+            mMaxJoinedResultCount = joinSpec.getMaxJoinedResultCount();
+            mAggregationScoringStrategy = joinSpec.getAggregationScoringStrategy();
+        }
+
         /**
          * Sets the query and the SearchSpec for the documents being joined. This will score and
          * rank the joined documents as well as filter the joined documents.
@@ -362,13 +401,13 @@
          */
         @NonNull
         public JoinSpec build() {
-            Bundle bundle = new Bundle();
-            bundle.putString(NESTED_QUERY, mNestedQuery);
-            bundle.putBundle(NESTED_SEARCH_SPEC, mNestedSearchSpec.getBundle());
-            bundle.putString(CHILD_PROPERTY_EXPRESSION, mChildPropertyExpression);
-            bundle.putInt(MAX_JOINED_RESULT_COUNT, mMaxJoinedResultCount);
-            bundle.putInt(AGGREGATION_SCORING_STRATEGY, mAggregationScoringStrategy);
-            return new JoinSpec(bundle);
+            return new JoinSpec(
+                    mNestedQuery,
+                    mNestedSearchSpec,
+                    mChildPropertyExpression,
+                    mMaxJoinedResultCount,
+                    mAggregationScoringStrategy
+            );
         }
     }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PackageIdentifier.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PackageIdentifier.java
index 6c4bc59..c05d567 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PackageIdentifier.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PackageIdentifier.java
@@ -16,20 +16,18 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
-import androidx.appsearch.util.BundleUtil;
+import androidx.appsearch.safeparcel.PackageIdentifierParcel;
 import androidx.core.util.Preconditions;
 
-/** This class represents a uniquely identifiable package. */
+/**
+ * This class represents a uniquely identifiable package.
+ */
 public class PackageIdentifier {
-    private static final String PACKAGE_NAME_FIELD = "packageName";
-    private static final String SHA256_CERTIFICATE_FIELD = "sha256Certificate";
-
-    private final Bundle mBundle;
+    @NonNull
+    private final PackageIdentifierParcel mPackageIdentifierParcel;
 
     /**
      * Creates a unique identifier for a package.
@@ -43,36 +41,41 @@
      * new android.content.pm.Signature(outputDigest).toByteArray();
      * </pre>
      *
-     * @param packageName Name of the package.
+     * @param packageName       Name of the package.
      * @param sha256Certificate SHA-256 certificate digest of the package.
      */
     public PackageIdentifier(@NonNull String packageName, @NonNull byte[] sha256Certificate) {
-        mBundle = new Bundle();
-        mBundle.putString(PACKAGE_NAME_FIELD, packageName);
-        mBundle.putByteArray(SHA256_CERTIFICATE_FIELD, sha256Certificate);
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(sha256Certificate);
+        mPackageIdentifierParcel = new PackageIdentifierParcel(packageName, sha256Certificate);
     }
 
     /** @exportToFramework:hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public PackageIdentifier(@NonNull Bundle bundle) {
-        mBundle = Preconditions.checkNotNull(bundle);
+    public PackageIdentifier(@NonNull PackageIdentifierParcel packageIdentifierParcel) {
+        mPackageIdentifierParcel = Preconditions.checkNotNull(packageIdentifierParcel);
     }
 
-    /** @exportToFramework:hide */
+    /**
+     * Returns the {@link PackageIdentifierParcel} holding the values for this
+     * {@link PackageIdentifier}.
+     *
+     * @exportToFramework:hide
+     */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    public PackageIdentifierParcel getPackageIdentifierParcel() {
+        return mPackageIdentifierParcel;
     }
 
     @NonNull
     public String getPackageName() {
-        return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD));
+        return mPackageIdentifierParcel.getPackageName();
     }
 
     @NonNull
     public byte[] getSha256Certificate() {
-        return Preconditions.checkNotNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD));
+        return mPackageIdentifierParcel.getSha256Certificate();
     }
 
     @Override
@@ -80,15 +83,15 @@
         if (this == obj) {
             return true;
         }
-        if (obj == null || !(obj instanceof PackageIdentifier)) {
+        if (!(obj instanceof PackageIdentifier)) {
             return false;
         }
         final PackageIdentifier other = (PackageIdentifier) obj;
-        return BundleUtil.deepEquals(mBundle, other.mBundle);
+        return mPackageIdentifierParcel.equals(other.getPackageIdentifierParcel());
     }
 
     @Override
     public int hashCode() {
-        return BundleUtil.deepHashCode(mBundle);
+        return mPackageIdentifierParcel.hashCode();
     }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PropertyPath.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PropertyPath.java
index e0557a6..0e69136 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PropertyPath.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PropertyPath.java
@@ -20,6 +20,8 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.appsearch.checker.initialization.qual.UnderInitialization;
+import androidx.appsearch.checker.nullness.qual.RequiresNonNull;
 import androidx.core.util.ObjectsCompat;
 import androidx.core.util.Preconditions;
 
@@ -73,7 +75,9 @@
         }
     }
 
-    private void recursivePathScan(String path) throws IllegalArgumentException {
+    @RequiresNonNull("mPathList")
+    private void recursivePathScan(@UnderInitialization PropertyPath this, String path)
+            throws IllegalArgumentException {
         // Determine whether the path is just a raw property name with no control characters
         int controlPos = -1;
         boolean controlIsIndex = false;
@@ -128,7 +132,9 @@
      * @return the rest of the path after the end brackets, or null if there is nothing after them
      */
     @Nullable
-    private String consumePropertyWithIndex(@NonNull String path, int controlPos) {
+    @RequiresNonNull("mPathList")
+    private String consumePropertyWithIndex(
+            @UnderInitialization PropertyPath this, @NonNull String path, int controlPos) {
         Preconditions.checkNotNull(path);
         String propertyName = path.substring(0, controlPos);
         int endBracketIdx = path.indexOf(']', controlPos);
@@ -210,17 +216,23 @@
     }
 
     @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null) return false;
-        if (!(o instanceof PropertyPath)) return false;
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null) {
+            return false;
+        }
+        if (!(o instanceof PropertyPath)) {
+            return false;
+        }
         PropertyPath that = (PropertyPath) o;
         return ObjectsCompat.equals(mPathList, that.mPathList);
     }
 
     @Override
     public int hashCode() {
-        return ObjectsCompat.hash(mPathList);
+        return ObjectsCompat.hashCode(mPathList);
     }
 
     /**
@@ -292,9 +304,7 @@
             mPropertyIndex = propertyIndex;
         }
 
-        /**
-         * @return the property name
-         */
+        /** Returns the name of the property. */
         @NonNull
         public String getPropertyName() {
             return mPropertyName;
@@ -319,10 +329,16 @@
         }
 
         @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null) return false;
-            if (!(o instanceof PathSegment)) return false;
+        public boolean equals(@Nullable Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null) {
+                return false;
+            }
+            if (!(o instanceof PathSegment)) {
+                return false;
+            }
             PathSegment that = (PathSegment) o;
             return mPropertyIndex == that.mPropertyIndex
                     && mPropertyName.equals(that.mPropertyName);
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java
index 6edf85e..e0332b4 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/PutDocumentsRequest.java
@@ -19,8 +19,13 @@
 import android.annotation.SuppressLint;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.usagereporting.TakenAction;
+import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
 import java.util.ArrayList;
@@ -28,6 +33,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Encapsulates a request to index documents into an {@link AppSearchSession} database.
@@ -43,8 +49,11 @@
 public final class PutDocumentsRequest {
     private final List<GenericDocument> mDocuments;
 
-    PutDocumentsRequest(List<GenericDocument> documents) {
+    private final List<GenericDocument> mTakenActions;
+
+    PutDocumentsRequest(List<GenericDocument> documents, List<GenericDocument> takenActions) {
         mDocuments = documents;
+        mTakenActions = takenActions;
     }
 
     /** Returns a list of {@link GenericDocument} objects that are part of this request. */
@@ -53,9 +62,27 @@
         return Collections.unmodifiableList(mDocuments);
     }
 
+    /**
+     * Returns a list of {@link GenericDocument} objects containing taken action metrics that are
+     * part of this request.
+     *
+     * <!--@exportToFramework:ifJetpack()-->
+     * <p>See {@link Builder#addTakenActions(TakenAction...)}.
+     * <!--@exportToFramework:else()
+     * <p>See {@link Builder#addTakenActionGenericDocuments(GenericDocument...)}.
+     * -->
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_ENABLE_PUT_DOCUMENTS_REQUEST_ADD_TAKEN_ACTIONS)
+    public List<GenericDocument> getTakenActionGenericDocuments() {
+        return Collections.unmodifiableList(mTakenActions);
+    }
+
+
     /** Builder for {@link PutDocumentsRequest} objects. */
     public static final class Builder {
         private ArrayList<GenericDocument> mDocuments = new ArrayList<>();
+        private ArrayList<GenericDocument> mTakenActions = new ArrayList<>();
         private boolean mBuilt = false;
 
         /** Adds one or more {@link GenericDocument} objects to the request. */
@@ -121,18 +148,219 @@
             }
             return addGenericDocuments(genericDocuments);
         }
+
+        /**
+         * Adds one or more {@link TakenAction} objects to the request.
+         *
+         * <p>Clients can construct {@link TakenAction} documents to report the user's actions on
+         * search results, and these actions can be used as signals to boost result ranking in
+         * future search requests. See {@link TakenAction} for more details.
+         *
+         * <p>Clients should report search and click actions together sorted by
+         * {@link TakenAction#getActionTimestampMillis} in chronological order.
+         * <p>For example, if there are 2 search actions, with 1 click action associated with the
+         * first and 2 click actions associated with the second, then clients should report
+         * [searchAction1, clickAction1, searchAction2, clickAction2, clickAction3].
+         *
+         * <p>Certain anonymized information about actions reported using this API may be uploaded
+         * using statsd and may be used to improve the quality of the search algorithms. Most of
+         * the information in this class is already non-identifiable, such as durations and its
+         * position in the result set. Identifiable information which you choose to provide, such
+         * as the query string, will be anonymized using techniques like Federated Analytics to
+         * ensure only the most frequently searched terms across the whole user population are
+         * retained and available for study.
+         *
+         * <p>You can alternatively use the {@link #addDocuments(Object...)} API with
+         * {@link TakenAction} document to retain the benefits of joining and using it on-device,
+         * without triggering any of the anonymized stats uploading described above.
+         *
+         * @param takenActions one or more {@link TakenAction} objects.
+         */
+        // Merged list available from getTakenActionGenericDocuments()
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addTakenActions(
+                @NonNull TakenAction... takenActions) throws AppSearchException {
+            Preconditions.checkNotNull(takenActions);
+            resetIfBuilt();
+            return addTakenActions(Arrays.asList(takenActions));
+        }
+
+        /**
+         * Adds a collection of {@link TakenAction} objects to the request.
+         *
+         * @see #addTakenActions(TakenAction...)
+         *
+         * @param takenActions a collection of {@link TakenAction} objects.
+         */
+        // Merged list available from getTakenActionGenericDocuments()
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addTakenActions(@NonNull Collection<? extends TakenAction> takenActions)
+                throws AppSearchException {
+            Preconditions.checkNotNull(takenActions);
+            resetIfBuilt();
+            List<GenericDocument> genericDocuments = new ArrayList<>(takenActions.size());
+            for (Object takenAction : takenActions) {
+                GenericDocument genericDocument = GenericDocument.fromDocumentClass(takenAction);
+                genericDocuments.add(genericDocument);
+            }
+            mTakenActions.addAll(genericDocuments);
+            return this;
+        }
 // @exportToFramework:endStrip()
 
-        /** Creates a new {@link PutDocumentsRequest} object. */
+        /**
+         * Adds one or more {@link GenericDocument} objects containing taken action metrics to the
+         * request.
+         *
+         * <p>It is recommended to use taken action document classes in Jetpack library to construct
+         * taken action documents.
+         *
+         * <p>The document creation timestamp of the {@link GenericDocument} should be set to the
+         * actual action timestamp via {@link GenericDocument.Builder#setCreationTimestampMillis}.
+         *
+         * <p>Clients should report search and click actions together sorted by
+         * {@link GenericDocument#getCreationTimestampMillis} in chronological order.
+         * <p>For example, if there are 2 search actions, with 1 click action associated with the
+         * first and 2 click actions associated with the second, then clients should report
+         * [searchAction1, clickAction1, searchAction2, clickAction2, clickAction3].
+         *
+         * <p>Different types of taken actions and metrics to be collected by AppSearch:
+         * <ul>
+         *  <li>
+         *      Search action
+         *      <ul>
+         *          <li>actionType: LONG, the enum value of the action type.
+         *          <p>Requires to be {@code 1} for search actions.
+         *
+         *          <li>query: STRING, the user-entered search input (without any operators or
+         *          rewriting).
+         *
+         *          <li>fetchedResultCount: LONG, the number of {@link SearchResult} documents
+         *          fetched from AppSearch in this search action.
+         *      </ul>
+         *  </li>
+         *
+         *  <li>
+         *      Click action
+         *      <ul>
+         *          <li>actionType: LONG, the enum value of the action type.
+         *          <p>Requires to be {@code 2} for click actions.
+         *
+         *          <li>query: STRING, the user-entered search input (without any operators or
+         *          rewriting) that yielded the {@link SearchResult} on which the user took action.
+         *
+         *          <li>referencedQualifiedId: STRING, the qualified id of the {@link SearchResult}
+         *          document that the user takes action on.
+         *          <p>A qualified id is a string generated by package, database, namespace, and
+         *          document id. See
+         *          {@link androidx.appsearch.util.DocumentIdUtil#createQualifiedId} for more
+         *          details.
+         *
+         *          <li>resultRankInBlock: LONG, the rank of the {@link SearchResult} document among
+         *          the user-defined block.
+         *          <p>The client can define its own custom definition for block, for example,
+         *          corpus name, group, etc.
+         *          <p>For example, a client defines the block as corpus, and AppSearch returns 5
+         *          documents with corpus = ["corpus1", "corpus1", "corpus2", "corpus3", "corpus2"].
+         *          Then the block ranks of them = [1, 2, 1, 1, 2].
+         *          <p>If the client is not presenting the results in multiple blocks, they should
+         *          set this value to match resultRankGlobal.
+         *
+         *          <li>resultRankGlobal: LONG, the global rank of the {@link SearchResult}
+         *          document.
+         *          <p>Global rank reflects the order of {@link SearchResult} documents returned by
+         *          AppSearch.
+         *          <p>For example, AppSearch returns 2 pages with 10 {@link SearchResult} documents
+         *          for each page. Then the global ranks of them will be 1 to 10 for the first page,
+         *          and 11 to 20 for the second page.
+         *
+         *          <li>timeStayOnResultMillis: LONG, the time in milliseconds that user stays on
+         *          the {@link SearchResult} document after clicking it.
+         *      </ul>
+         *  </li>
+         * </ul>
+         *
+         * <p>Certain anonymized information about actions reported using this API may be uploaded
+         * using statsd and may be used to improve the quality of the search algorithms. Most of
+         * the information in this class is already non-identifiable, such as durations and its
+         * position in the result set. Identifiable information which you choose to provide, such
+         * as the query string, will be anonymized using techniques like Federated Analytics to
+         * ensure only the most frequently searched terms across the whole user population are
+         * retained and available for study.
+         *
+         * <p>You can alternatively use the {@link #addGenericDocuments(GenericDocument...)} API to
+         * retain the benefits of joining and using it on-device, without triggering any of the
+         * anonymized stats uploading described above.
+         *
+         * @param takenActionGenericDocuments one or more {@link GenericDocument} objects containing
+         *                                    taken action metric fields.
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @CanIgnoreReturnValue
+        @NonNull
+        @FlaggedApi(Flags.FLAG_ENABLE_PUT_DOCUMENTS_REQUEST_ADD_TAKEN_ACTIONS)
+        public Builder addTakenActionGenericDocuments(
+                @NonNull GenericDocument... takenActionGenericDocuments) throws AppSearchException {
+            Preconditions.checkNotNull(takenActionGenericDocuments);
+            resetIfBuilt();
+            return addTakenActionGenericDocuments(Arrays.asList(takenActionGenericDocuments));
+        }
+
+        /**
+         * Adds a collection of {@link GenericDocument} objects containing taken action metrics to
+         * the request.
+         *
+         * @see #addTakenActionGenericDocuments(GenericDocument...)
+         *
+         * @param takenActionGenericDocuments a collection of {@link GenericDocument} objects
+         *                                    containing taken action metric fields.
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @CanIgnoreReturnValue
+        @NonNull
+        @FlaggedApi(Flags.FLAG_ENABLE_PUT_DOCUMENTS_REQUEST_ADD_TAKEN_ACTIONS)
+        public Builder addTakenActionGenericDocuments(@NonNull Collection<?
+                extends GenericDocument> takenActionGenericDocuments) throws AppSearchException {
+            Preconditions.checkNotNull(takenActionGenericDocuments);
+            resetIfBuilt();
+            mTakenActions.addAll(takenActionGenericDocuments);
+            return this;
+        }
+
+        /**
+         * Creates a new {@link PutDocumentsRequest} object.
+         *
+         * @throws IllegalArgumentException if there is any id collision between normal and action
+         *                                  documents.
+         */
         @NonNull
         public PutDocumentsRequest build() {
             mBuilt = true;
-            return new PutDocumentsRequest(mDocuments);
+
+            // Verify there is no id collision between normal documents and action documents.
+            Set<String> idSet = new ArraySet<>();
+            for (int i = 0; i < mDocuments.size(); i++) {
+                idSet.add(mDocuments.get(i).getId());
+            }
+            for (int i = 0; i < mTakenActions.size(); i++) {
+                GenericDocument takenAction = mTakenActions.get(i);
+                if (idSet.contains(takenAction.getId())) {
+                    throw new IllegalArgumentException("Document id " + takenAction.getId()
+                            + " cannot exist in both taken action and normal document");
+                }
+            }
+
+            return new PutDocumentsRequest(mDocuments, mTakenActions);
         }
 
         private void resetIfBuilt() {
             if (mBuilt) {
                 mDocuments = new ArrayList<>(mDocuments);
+                mTakenActions = new ArrayList<>(mTakenActions);
                 mBuilt = false;
             }
         }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java
index 40cd591..ce76455 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/RemoveByDocumentIdRequest.java
@@ -16,14 +16,27 @@
 
 package androidx.appsearch.app;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.RemoveByDocumentIdRequestCreator;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -32,13 +45,37 @@
  *
  * @see AppSearchSession#removeAsync
  */
-public final class RemoveByDocumentIdRequest {
-    private final String mNamespace;
-    private final Set<String> mIds;
[email protected](creator = "RemoveByDocumentIdRequestCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class RemoveByDocumentIdRequest extends AbstractSafeParcelable {
+    /** Creator class for {@link android.app.appsearch.RemoveByDocumentIdRequest}. */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull
+    public static final Parcelable.Creator<RemoveByDocumentIdRequest> CREATOR =
+            new RemoveByDocumentIdRequestCreator();
 
-    RemoveByDocumentIdRequest(String namespace, Set<String> ids) {
-        mNamespace = namespace;
-        mIds = ids;
+    @NonNull
+    @Field(id = 1, getter = "getNamespace")
+    private final String mNamespace;
+    @NonNull
+    @Field(id = 2)
+    final List<String> mIds;
+    @Nullable
+    private Set<String> mIdsCached;
+
+    /**
+     * Removes documents by ID.
+     *
+     * @param namespace    Namespace of the document to remove.
+     * @param ids The IDs of the documents to delete
+     */
+    @Constructor
+    RemoveByDocumentIdRequest(
+            @Param(id = 1) @NonNull String namespace,
+            @Param(id = 2) @NonNull List<String> ids) {
+        mNamespace = Objects.requireNonNull(namespace);
+        mIds = Objects.requireNonNull(ids);
     }
 
     /** Returns the namespace to remove documents from. */
@@ -50,7 +87,17 @@
     /** Returns the set of document IDs attached to the request. */
     @NonNull
     public Set<String> getIds() {
-        return Collections.unmodifiableSet(mIds);
+        if (mIdsCached == null) {
+            mIdsCached = Collections.unmodifiableSet(new ArraySet<>(mIds));
+        }
+        return mIdsCached;
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        RemoveByDocumentIdRequestCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link RemoveByDocumentIdRequest} objects. */
@@ -87,7 +134,7 @@
         @NonNull
         public RemoveByDocumentIdRequest build() {
             mBuilt = true;
-            return new RemoveByDocumentIdRequest(mNamespace, mIds);
+            return new RemoveByDocumentIdRequest(mNamespace, new ArrayList<>(mIds));
         }
 
         private void resetIfBuilt() {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
index 58e7d9b..aafcc61 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/ReportUsageRequest.java
@@ -16,10 +16,21 @@
 
 package androidx.appsearch.app;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.ReportUsageRequestCreator;
 import androidx.core.util.Preconditions;
 
+import java.util.Objects;
+
 /**
  * A request to report usage of a document.
  *
@@ -27,18 +38,34 @@
  *
  * @see AppSearchSession#reportUsageAsync
  */
-public final class ReportUsageRequest {
-    private final String mNamespace;
-    private final String mDocumentId;
-    private final long mUsageTimestampMillis;
+@SuppressWarnings("HiddenSuperclass")
[email protected](creator = "ReportUsageRequestCreator")
+public final class ReportUsageRequest extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull public static final Parcelable.Creator<ReportUsageRequest> CREATOR =
+            new ReportUsageRequestCreator();
 
+    @NonNull
+    @Field(id = 1, getter = "getNamespace")
+    private final String mNamespace;
+    @NonNull
+    @Field(id = 2, getter = "getDocumentId")
+    private final String mDocumentId;
+    @Field(id = 3, getter = "getUsageTimestampMillis")
+    private final  long mUsageTimestampMillis;
+
+    @Constructor
     ReportUsageRequest(
-            @NonNull String namespace, @NonNull String documentId, long usageTimestampMillis) {
-        mNamespace = Preconditions.checkNotNull(namespace);
-        mDocumentId = Preconditions.checkNotNull(documentId);
+            @Param(id = 1) @NonNull String namespace,
+            @Param(id = 2) @NonNull String documentId,
+            @Param(id = 3) long usageTimestampMillis) {
+        mNamespace = Objects.requireNonNull(namespace);
+        mDocumentId = Objects.requireNonNull(documentId);
         mUsageTimestampMillis = usageTimestampMillis;
     }
 
+
     /** Returns the namespace of the document that was used. */
     @NonNull
     public String getNamespace() {
@@ -62,6 +89,13 @@
         return mUsageTimestampMillis;
     }
 
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        ReportUsageRequestCreator.writeToParcel(this, dest, flags);
+    }
+
     /** Builder for {@link ReportUsageRequest} objects. */
     public static final class Builder {
         private final String mNamespace;
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SchemaVisibilityConfig.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SchemaVisibilityConfig.java
new file mode 100644
index 0000000..32b69b3
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SchemaVisibilityConfig.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.PackageIdentifierParcel;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.VisibilityConfigCreator;
+import androidx.collection.ArraySet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A class to hold a all necessary Visibility information corresponding to the same schema. This
+ * pattern allows for easier association of these documents.
+ *
+ * <p> This does not correspond to any schema, the properties held in this class are kept in two
+ * separate schemas, VisibilityConfig and PublicAclOverlay.
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
[email protected](creator = "VisibilityConfigCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class SchemaVisibilityConfig extends AbstractSafeParcelable {
+    @NonNull
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final Parcelable.Creator<SchemaVisibilityConfig> CREATOR =
+            new VisibilityConfigCreator();
+
+    @NonNull
+    @Field(id = 1)
+    final List<PackageIdentifierParcel> mAllowedPackages;
+
+    @NonNull
+    @Field(id = 2)
+    final List<VisibilityPermissionConfig> mRequiredPermissions;
+
+    @Nullable
+    @Field(id = 3)
+    final PackageIdentifierParcel mPubliclyVisibleTargetPackage;
+
+    @Nullable private Integer mHashCode;
+    @Nullable private List<PackageIdentifier> mAllowedPackagesCached;
+    @Nullable private Set<Set<Integer>> mRequiredPermissionsCached;
+
+    @Constructor
+    SchemaVisibilityConfig(
+            @Param(id = 1) @NonNull List<PackageIdentifierParcel> allowedPackages,
+            @Param(id = 2) @NonNull List<VisibilityPermissionConfig> requiredPermissions,
+            @Param(id = 3) @Nullable PackageIdentifierParcel publiclyVisibleTargetPackage) {
+        mAllowedPackages = Objects.requireNonNull(allowedPackages);
+        mRequiredPermissions = Objects.requireNonNull(requiredPermissions);
+        mPubliclyVisibleTargetPackage = publiclyVisibleTargetPackage;
+    }
+
+     /** Returns a list of {@link PackageIdentifier}s of packages that can access this schema. */
+    @NonNull
+    public List<PackageIdentifier> getAllowedPackages() {
+        if (mAllowedPackagesCached == null) {
+            mAllowedPackagesCached = new ArrayList<>(mAllowedPackages.size());
+            for (int i = 0; i < mAllowedPackages.size(); i++) {
+                mAllowedPackagesCached.add(new PackageIdentifier(mAllowedPackages.get(i)));
+            }
+        }
+        return mAllowedPackagesCached;
+    }
+
+    /**
+     * Returns an array of Integers representing Android Permissions that the caller must hold to
+     * access the schema this {@link SchemaVisibilityConfig} represents.
+     * @see SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility(String, Set)
+     */
+    @NonNull
+    public Set<Set<Integer>> getRequiredPermissions() {
+        if (mRequiredPermissionsCached == null) {
+            mRequiredPermissionsCached = new ArraySet<>(mRequiredPermissions.size());
+            for (int i = 0; i < mRequiredPermissions.size(); i++) {
+                VisibilityPermissionConfig permissionConfig = mRequiredPermissions.get(i);
+                Set<Integer> requiredPermissions = permissionConfig.getAllRequiredPermissions();
+                if (mRequiredPermissionsCached != null && requiredPermissions != null) {
+                    mRequiredPermissionsCached.add(requiredPermissions);
+                }
+            }
+        }
+        // Added for nullness checker as it is @Nullable, we initialize it above if it is null.
+        return Objects.requireNonNull(mRequiredPermissionsCached);
+    }
+
+    /**
+     * Returns the {@link PackageIdentifier} of the package that will be used as the target package
+     * in a call to {@link android.content.pm.PackageManager#canPackageQuery} to determine which
+     * packages can access this publicly visible schema. Returns null if the schema is not publicly
+     * visible.
+     */
+    @Nullable
+    public PackageIdentifier getPubliclyVisibleTargetPackage() {
+        if (mPubliclyVisibleTargetPackage == null) {
+            return null;
+        }
+        return new PackageIdentifier(mPubliclyVisibleTargetPackage);
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        VisibilityConfigCreator.writeToParcel(this, dest, flags);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null) {
+            return false;
+        }
+        if (!(o instanceof SchemaVisibilityConfig)) {
+            return false;
+        }
+        SchemaVisibilityConfig that = (SchemaVisibilityConfig) o;
+        return Objects.equals(mAllowedPackages, that.mAllowedPackages)
+                && Objects.equals(mRequiredPermissions, that.mRequiredPermissions)
+                && Objects.equals(
+                        mPubliclyVisibleTargetPackage, that.mPubliclyVisibleTargetPackage);
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = Objects.hash(
+                    mAllowedPackages,
+                    mRequiredPermissions,
+                    mPubliclyVisibleTargetPackage);
+        }
+        return mHashCode;
+    }
+
+    /** The builder class of {@link SchemaVisibilityConfig}. */
+    @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+    public static final class Builder {
+        private List<PackageIdentifierParcel> mAllowedPackages = new ArrayList<>();
+        private List<VisibilityPermissionConfig> mRequiredPermissions = new ArrayList<>();
+        @Nullable private PackageIdentifierParcel mPubliclyVisibleTargetPackage;
+        private boolean mBuilt;
+
+        /** Creates a {@link Builder} for a {@link SchemaVisibilityConfig}. */
+        public Builder() {}
+
+        /**
+         * Creates a {@link Builder} copying the values from an existing
+         * {@link SchemaVisibilityConfig}.
+         *
+         * @exportToFramework:hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public Builder(@NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
+            Objects.requireNonNull(schemaVisibilityConfig);
+            mAllowedPackages = new ArrayList<>(schemaVisibilityConfig.mAllowedPackages);
+            mRequiredPermissions = new ArrayList<>(schemaVisibilityConfig.mRequiredPermissions);
+            mPubliclyVisibleTargetPackage = schemaVisibilityConfig.mPubliclyVisibleTargetPackage;
+        }
+
+        /** Add {@link PackageIdentifier} of packages which has access to this schema. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addAllowedPackage(@NonNull PackageIdentifier packageIdentifier) {
+            Objects.requireNonNull(packageIdentifier);
+            resetIfBuilt();
+            mAllowedPackages.add(packageIdentifier.getPackageIdentifierParcel());
+            return this;
+        }
+
+        /** Clears the list of packages which have access to this schema. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder clearAllowedPackages() {
+            resetIfBuilt();
+            mAllowedPackages.clear();
+            return this;
+        }
+
+        /**
+         * Adds a set of required Android {@link android.Manifest.permission} combination a
+         * package needs to hold to access the schema this {@link SchemaVisibilityConfig}
+         * represents.
+         *
+         * <p> If the querier holds ALL of the required permissions in this combination, they will
+         * have access to read {@link GenericDocument} objects of the given schema type.
+         *
+         * <p> You can call this method repeatedly to add multiple permission combinations, and the
+         * querier will have access if they holds ANY of the combinations.
+         *
+         * <p>Merged Set available from {@link #getRequiredPermissions()}.
+         *
+         * @see SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility for
+         * supported Permissions.
+         */
+        @SuppressWarnings("RequiresPermission")  // No permission required to call this method
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addRequiredPermissions(@NonNull Set<Integer> visibleToPermissions) {
+            Objects.requireNonNull(visibleToPermissions);
+            resetIfBuilt();
+            mRequiredPermissions.add(new VisibilityPermissionConfig(visibleToPermissions));
+            return this;
+        }
+
+        /**
+         * Clears all required permissions combinations set to this {@link SchemaVisibilityConfig}.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder clearRequiredPermissions() {
+            resetIfBuilt();
+            mRequiredPermissions.clear();
+            return this;
+        }
+
+        /**
+         * Specify that this schema should be publicly available, to the same packages that have
+         * visibility to the package passed as a parameter. This visibility is determined by the
+         * result of {@link android.content.pm.PackageManager#canPackageQuery}.
+         *
+         * <p> It is possible for the packageIdentifier parameter to be different from the
+         * package performing the indexing. This might happen in the case of an on-device indexer
+         * processing information about various packages. The visibility will be the same
+         * regardless of which package indexes the document, as the visibility is based on the
+         * packageIdentifier parameter.
+         *
+         * <p> Calling this with packageIdentifier set to null is valid, and will remove public
+         * visibility for the schema.
+         *
+         * @param packageIdentifier the {@link PackageIdentifier} of the package that will be used
+         *                          as the target package in a call to {@link
+         *                          android.content.pm.PackageManager#canPackageQuery} to determine
+         *                          which packages can access this publicly visible schema.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setPubliclyVisibleTargetPackage(
+                @Nullable PackageIdentifier packageIdentifier) {
+            resetIfBuilt();
+            if (packageIdentifier == null) {
+                mPubliclyVisibleTargetPackage = null;
+            } else {
+                mPubliclyVisibleTargetPackage = packageIdentifier.getPackageIdentifierParcel();
+            }
+            return this;
+        }
+
+        private void resetIfBuilt() {
+            if (mBuilt) {
+                mAllowedPackages = new ArrayList<>(mAllowedPackages);
+                mRequiredPermissions = new ArrayList<>(mRequiredPermissions);
+                mBuilt = false;
+            }
+        }
+
+        /** Build a {@link SchemaVisibilityConfig} */
+        @NonNull
+        public SchemaVisibilityConfig build() {
+            mBuilt = true;
+            return new SchemaVisibilityConfig(
+                    mAllowedPackages,
+                    mRequiredPermissions,
+                    mPubliclyVisibleTargetPackage);
+        }
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
index 187304f..0fce482 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
@@ -16,7 +16,8 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -24,10 +25,18 @@
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.GenericDocumentParcel;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.MatchInfoCreator;
+import androidx.appsearch.safeparcel.stub.StubCreators.SearchResultCreator;
 import androidx.core.util.ObjectsCompat;
 import androidx.core.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -46,36 +55,61 @@
  *
  * @see SearchResults
  */
-public final class SearchResult {
-    static final String DOCUMENT_FIELD = "document";
-    static final String MATCH_INFOS_FIELD = "matchInfos";
-    static final String PACKAGE_NAME_FIELD = "packageName";
-    static final String DATABASE_NAME_FIELD = "databaseName";
-    static final String RANKING_SIGNAL_FIELD = "rankingSignal";
-    static final String JOINED_RESULTS = "joinedResults";
[email protected](creator = "SearchResultCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class SearchResult extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull public static final Parcelable.Creator<SearchResult> CREATOR =
+            new SearchResultCreator();
 
+    @Field(id = 1)
+    final GenericDocumentParcel mDocument;
+    @Field(id = 2)
+    final List<MatchInfo> mMatchInfos;
+    @Field(id = 3, getter = "getPackageName")
+    private final String mPackageName;
+    @Field(id = 4, getter = "getDatabaseName")
+    private final String mDatabaseName;
+    @Field(id = 5, getter = "getRankingSignal")
+    private final double mRankingSignal;
+    @Field(id = 6, getter = "getJoinedResults")
+    private final List<SearchResult> mJoinedResults;
     @NonNull
-    private final Bundle mBundle;
+    @Field(id = 7, getter = "getInformationalRankingSignals")
+    private final List<Double> mInformationalRankingSignals;
 
-    /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */
-    @Nullable
-    private GenericDocument mDocument;
 
-    /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */
+    /** Cache of the {@link GenericDocument}. Comes from mDocument at first use. */
     @Nullable
-    private List<MatchInfo> mMatchInfos;
+    private GenericDocument mDocumentCached;
+
+    /** Cache of the inflated {@link MatchInfo}. Comes from inflating mMatchInfos at first use. */
+    @Nullable
+    private List<MatchInfo> mMatchInfosCached;
 
     /** @exportToFramework:hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public SearchResult(@NonNull Bundle bundle) {
-        mBundle = Preconditions.checkNotNull(bundle);
-    }
-
-    /** @exportToFramework:hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    @Constructor
+    SearchResult(
+            @Param(id = 1) @NonNull GenericDocumentParcel document,
+            @Param(id = 2) @NonNull List<MatchInfo> matchInfos,
+            @Param(id = 3) @NonNull String packageName,
+            @Param(id = 4) @NonNull String databaseName,
+            @Param(id = 5) double rankingSignal,
+            @Param(id = 6) @NonNull List<SearchResult> joinedResults,
+            @Param(id = 7) @Nullable List<Double> informationalRankingSignals) {
+        mDocument = Preconditions.checkNotNull(document);
+        mMatchInfos = Preconditions.checkNotNull(matchInfos);
+        mPackageName = Preconditions.checkNotNull(packageName);
+        mDatabaseName = Preconditions.checkNotNull(databaseName);
+        mRankingSignal = rankingSignal;
+        mJoinedResults = Collections.unmodifiableList(Preconditions.checkNotNull(joinedResults));
+        if (informationalRankingSignals != null) {
+            mInformationalRankingSignals = Collections.unmodifiableList(
+                    informationalRankingSignals);
+        } else {
+            mInformationalRankingSignals = Collections.emptyList();
+        }
     }
 
 // @exportToFramework:startStrip()
@@ -92,7 +126,7 @@
      * @see GenericDocument#toDocumentClass(Class)
      */
     @NonNull
-    public <T> T getDocument(@NonNull Class<T> documentClass) throws AppSearchException {
+    public <T> T getDocument(@NonNull java.lang.Class<T> documentClass) throws AppSearchException {
         return getDocument(documentClass, /* documentClassMap= */null);
     }
 
@@ -112,7 +146,7 @@
      * @see GenericDocument#toDocumentClass(Class, Map)
      */
     @NonNull
-    public <T> T getDocument(@NonNull Class<T> documentClass,
+    public <T> T getDocument(@NonNull java.lang.Class<T> documentClass,
             @Nullable Map<String, List<String>> documentClassMap) throws AppSearchException {
         Preconditions.checkNotNull(documentClass);
         return getGenericDocument().toDocumentClass(documentClass, documentClassMap);
@@ -126,11 +160,10 @@
      */
     @NonNull
     public GenericDocument getGenericDocument() {
-        if (mDocument == null) {
-            mDocument = new GenericDocument(
-                    Preconditions.checkNotNull(mBundle.getBundle(DOCUMENT_FIELD)));
+        if (mDocumentCached == null) {
+            mDocumentCached = new GenericDocument(mDocument);
         }
-        return mDocument;
+        return mDocumentCached;
     }
 
     /**
@@ -143,22 +176,21 @@
      * value, this method returns an empty list.
      */
     @NonNull
-    @SuppressWarnings("deprecation")
     public List<MatchInfo> getMatchInfos() {
-        if (mMatchInfos == null) {
-            List<Bundle> matchBundles =
-                    Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCH_INFOS_FIELD));
-            mMatchInfos = new ArrayList<>(matchBundles.size());
-            for (int i = 0; i < matchBundles.size(); i++) {
-                MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument());
-                if (mMatchInfos != null) {
+        if (mMatchInfosCached == null) {
+            mMatchInfosCached = new ArrayList<>(mMatchInfos.size());
+            for (int i = 0; i < mMatchInfos.size(); i++) {
+                MatchInfo matchInfo = mMatchInfos.get(i);
+                matchInfo.setDocument(getGenericDocument());
+                if (mMatchInfosCached != null) {
                     // This additional check is added for NullnessChecker.
-                    mMatchInfos.add(matchInfo);
+                    mMatchInfosCached.add(matchInfo);
                 }
             }
+            mMatchInfosCached = Collections.unmodifiableList(mMatchInfosCached);
         }
         // This check is added for NullnessChecker, mMatchInfos will always be NonNull.
-        return Preconditions.checkNotNull(mMatchInfos);
+        return Preconditions.checkNotNull(mMatchInfosCached);
     }
 
     /**
@@ -168,7 +200,7 @@
      */
     @NonNull
     public String getPackageName() {
-        return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD));
+        return mPackageName;
     }
 
     /**
@@ -178,7 +210,7 @@
      */
     @NonNull
     public String getDatabaseName() {
-        return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD));
+        return mDatabaseName;
     }
 
     /**
@@ -207,7 +239,17 @@
      * @return Ranking signal of the document
      */
     public double getRankingSignal() {
-        return mBundle.getDouble(RANKING_SIGNAL_FIELD);
+        return mRankingSignal;
+    }
+
+    /**
+     * Returns the informational ranking signals of the {@link GenericDocument}, according to the
+     * expressions added in {@link SearchSpec.Builder#addInformationalRankingExpressions}.
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+    public List<Double> getInformationalRankingSignals() {
+        return mInformationalRankingSignals;
     }
 
     /**
@@ -225,28 +267,26 @@
      * @return a List of SearchResults containing joined documents.
      */
     @NonNull
-    @SuppressWarnings("deprecation") // Bundle#getParcelableArrayList(String) is deprecated.
     public List<SearchResult> getJoinedResults() {
-        ArrayList<Bundle> bundles = mBundle.getParcelableArrayList(JOINED_RESULTS);
-        if (bundles == null) {
-            return new ArrayList<>();
-        }
-        List<SearchResult> res = new ArrayList<>(bundles.size());
-        for (int i = 0; i < bundles.size(); i++) {
-            res.add(new SearchResult(bundles.get(i)));
-        }
+        return mJoinedResults;
+    }
 
-        return res;
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        SearchResultCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link SearchResult} objects. */
     public static final class Builder {
         private final String mPackageName;
         private final String mDatabaseName;
-        private ArrayList<Bundle> mMatchInfoBundles = new ArrayList<>();
+        private List<MatchInfo> mMatchInfos = new ArrayList<>();
         private GenericDocument mGenericDocument;
         private double mRankingSignal;
-        private ArrayList<Bundle> mJoinedResults = new ArrayList<>();
+        private List<Double> mInformationalRankingSignals = new ArrayList<>();
+        private List<SearchResult> mJoinedResults = new ArrayList<>();
         private boolean mBuilt = false;
 
         /**
@@ -260,6 +300,26 @@
             mDatabaseName = Preconditions.checkNotNull(databaseName);
         }
 
+        /** @exportToFramework:hide */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public Builder(@NonNull SearchResult searchResult) {
+            Preconditions.checkNotNull(searchResult);
+            mPackageName = searchResult.getPackageName();
+            mDatabaseName = searchResult.getDatabaseName();
+            mGenericDocument = searchResult.getGenericDocument();
+            mRankingSignal = searchResult.getRankingSignal();
+            mInformationalRankingSignals = new ArrayList<>(
+                    searchResult.getInformationalRankingSignals());
+            List<MatchInfo> matchInfos = searchResult.getMatchInfos();
+            for (int i = 0; i < matchInfos.size(); i++) {
+                addMatchInfo(new MatchInfo.Builder(matchInfos.get(i)).build());
+            }
+            List<SearchResult> joinedResults = searchResult.getJoinedResults();
+            for (int i = 0; i < joinedResults.size(); i++) {
+                addJoinedResult(joinedResults.get(i));
+            }
+        }
+
 // @exportToFramework:startStrip()
         /**
          * Sets the document which matched.
@@ -298,7 +358,7 @@
                     "This MatchInfo is already associated with a SearchResult and can't be "
                             + "reassigned");
             resetIfBuilt();
-            mMatchInfoBundles.add(matchInfo.mBundle);
+            mMatchInfos.add(matchInfo);
             return this;
         }
 
@@ -311,6 +371,17 @@
             return this;
         }
 
+        /** Adds the informational ranking signal of the matched document in this SearchResult. */
+        @CanIgnoreReturnValue
+        @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+        @NonNull
+        public Builder addInformationalRankingSignal(double rankingSignal) {
+            resetIfBuilt();
+            mInformationalRankingSignals.add(rankingSignal);
+            return this;
+        }
+
+
         /**
          * Adds a {@link SearchResult} that was joined by the {@link JoinSpec}.
          * @param joinedResult The joined SearchResult to add.
@@ -319,28 +390,43 @@
         @NonNull
         public Builder addJoinedResult(@NonNull SearchResult joinedResult) {
             resetIfBuilt();
-            mJoinedResults.add(joinedResult.getBundle());
+            mJoinedResults.add(joinedResult);
+            return this;
+        }
+
+        /**
+         * Clears the {@link SearchResult}s that were joined.
+         *
+         * @exportToFramework:hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder clearJoinedResults() {
+            resetIfBuilt();
+            mJoinedResults.clear();
             return this;
         }
 
         /** Constructs a new {@link SearchResult}. */
         @NonNull
         public SearchResult build() {
-            Bundle bundle = new Bundle();
-            bundle.putString(PACKAGE_NAME_FIELD, mPackageName);
-            bundle.putString(DATABASE_NAME_FIELD, mDatabaseName);
-            bundle.putBundle(DOCUMENT_FIELD, mGenericDocument.getBundle());
-            bundle.putDouble(RANKING_SIGNAL_FIELD, mRankingSignal);
-            bundle.putParcelableArrayList(MATCH_INFOS_FIELD, mMatchInfoBundles);
-            bundle.putParcelableArrayList(JOINED_RESULTS, mJoinedResults);
             mBuilt = true;
-            return new SearchResult(bundle);
+            return new SearchResult(
+                    mGenericDocument.getDocumentParcel(),
+                    mMatchInfos,
+                    mPackageName,
+                    mDatabaseName,
+                    mRankingSignal,
+                    mJoinedResults,
+                    mInformationalRankingSignals);
         }
 
         private void resetIfBuilt() {
             if (mBuilt) {
-                mMatchInfoBundles = new ArrayList<>(mMatchInfoBundles);
+                mMatchInfos = new ArrayList<>(mMatchInfos);
                 mJoinedResults = new ArrayList<>(mJoinedResults);
+                mInformationalRankingSignals = new ArrayList<>(mInformationalRankingSignals);
                 mBuilt = false;
             }
         }
@@ -410,20 +496,32 @@
      *      <li>{@link MatchInfo#getSnippet()} returns "Testing 1"</li>
      * </ul>
      */
-    public static final class MatchInfo {
-        /** The path of the matching snippet property. */
-        private static final String PROPERTY_PATH_FIELD = "propertyPath";
-        private static final String EXACT_MATCH_RANGE_LOWER_FIELD = "exactMatchRangeLower";
-        private static final String EXACT_MATCH_RANGE_UPPER_FIELD = "exactMatchRangeUpper";
-        private static final String SUBMATCH_RANGE_LOWER_FIELD = "submatchRangeLower";
-        private static final String SUBMATCH_RANGE_UPPER_FIELD = "submatchRangeUpper";
-        private static final String SNIPPET_RANGE_LOWER_FIELD = "snippetRangeLower";
-        private static final String SNIPPET_RANGE_UPPER_FIELD = "snippetRangeUpper";
+    @SafeParcelable.Class(creator = "MatchInfoCreator")
+    @SuppressWarnings("HiddenSuperclass")
+    public static final class MatchInfo extends AbstractSafeParcelable {
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+        @NonNull public static final Parcelable.Creator<MatchInfo> CREATOR =
+                new MatchInfoCreator();
 
+        /** The path of the matching snippet property. */
+        @Field(id = 1, getter = "getPropertyPath")
         private final String mPropertyPath;
+        @Field(id = 2)
+        final int mExactMatchRangeStart;
+        @Field(id = 3)
+        final int mExactMatchRangeEnd;
+        @Field(id = 4)
+        final int mSubmatchRangeStart;
+        @Field(id = 5)
+        final int mSubmatchRangeEnd;
+        @Field(id = 6)
+        final int mSnippetRangeStart;
+        @Field(id = 7)
+        final int mSnippetRangeEnd;
+
         @Nullable
         private PropertyPath mPropertyPathObject = null;
-        final Bundle mBundle;
 
         /**
          * Document which the match comes from.
@@ -432,7 +530,7 @@
          * {@link #getExactMatch}, will throw {@link NullPointerException}.
          */
         @Nullable
-        final GenericDocument mDocument;
+        private GenericDocument mDocument = null;
 
         /** Full text of the matched property. Populated on first use. */
         @Nullable
@@ -440,23 +538,35 @@
 
         /** Range of property that exactly matched the query. Populated on first use. */
         @Nullable
-        private MatchRange mExactMatchRange;
+        private MatchRange mExactMatchRangeCached;
 
         /**
          * Range of property that corresponds to the subsequence of the exact match that directly
          * matches a query term. Populated on first use.
          */
         @Nullable
-        private MatchRange mSubmatchRange;
+        private MatchRange mSubmatchRangeCached;
 
         /** Range of some reasonable amount of context around the query. Populated on first use. */
         @Nullable
-        private MatchRange mWindowRange;
+        private MatchRange mWindowRangeCached;
 
-        MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) {
-            mBundle = Preconditions.checkNotNull(bundle);
-            mDocument = document;
-            mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
+        @Constructor
+        MatchInfo(
+                @Param(id = 1) @NonNull String propertyPath,
+                @Param(id = 2) int exactMatchRangeStart,
+                @Param(id = 3) int exactMatchRangeEnd,
+                @Param(id = 4) int submatchRangeStart,
+                @Param(id = 5) int submatchRangeEnd,
+                @Param(id = 6) int snippetRangeStart,
+                @Param(id = 7) int snippetRangeEnd) {
+            mPropertyPath = Preconditions.checkNotNull(propertyPath);
+            mExactMatchRangeStart = exactMatchRangeStart;
+            mExactMatchRangeEnd = exactMatchRangeEnd;
+            mSubmatchRangeStart = submatchRangeStart;
+            mSubmatchRangeEnd = submatchRangeEnd;
+            mSnippetRangeStart = snippetRangeStart;
+            mSnippetRangeEnd = snippetRangeEnd;
         }
 
         /**
@@ -521,12 +631,12 @@
          */
         @NonNull
         public MatchRange getExactMatchRange() {
-            if (mExactMatchRange == null) {
-                mExactMatchRange = new MatchRange(
-                        mBundle.getInt(EXACT_MATCH_RANGE_LOWER_FIELD),
-                        mBundle.getInt(EXACT_MATCH_RANGE_UPPER_FIELD));
+            if (mExactMatchRangeCached == null) {
+                mExactMatchRangeCached = new MatchRange(
+                        mExactMatchRangeStart,
+                        mExactMatchRangeEnd);
             }
-            return mExactMatchRange;
+            return mExactMatchRangeCached;
         }
 
         /**
@@ -555,20 +665,18 @@
          * false.
          * <!--@exportToFramework:else()-->
          */
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH)
-        // @exportToFramework:endStrip()
         @NonNull
         public MatchRange getSubmatchRange() {
             checkSubmatchSupported();
-            if (mSubmatchRange == null) {
-                mSubmatchRange = new MatchRange(
-                        mBundle.getInt(SUBMATCH_RANGE_LOWER_FIELD),
-                        mBundle.getInt(SUBMATCH_RANGE_UPPER_FIELD));
+            if (mSubmatchRangeCached == null) {
+                mSubmatchRangeCached = new MatchRange(
+                        mSubmatchRangeStart,
+                        mSubmatchRangeEnd);
             }
-            return mSubmatchRange;
+            return mSubmatchRangeCached;
         }
 
         /**
@@ -585,11 +693,9 @@
          * false.
          * <!--@exportToFramework:else()-->
          */
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH)
-        // @exportToFramework:endStrip()
         @NonNull
         public CharSequence getSubmatch() {
             checkSubmatchSupported();
@@ -606,12 +712,12 @@
          */
         @NonNull
         public MatchRange getSnippetRange() {
-            if (mWindowRange == null) {
-                mWindowRange = new MatchRange(
-                        mBundle.getInt(SNIPPET_RANGE_LOWER_FIELD),
-                        mBundle.getInt(SNIPPET_RANGE_UPPER_FIELD));
+            if (mWindowRangeCached == null) {
+                mWindowRangeCached = new MatchRange(
+                        mSnippetRangeStart,
+                        mSnippetRangeEnd);
             }
-            return mWindowRange;
+            return mWindowRangeCached;
         }
 
         /**
@@ -634,7 +740,7 @@
         }
 
         private void checkSubmatchSupported() {
-            if (!mBundle.containsKey(SUBMATCH_RANGE_LOWER_FIELD)) {
+            if (mSubmatchRangeStart == -1) {
                 throw new UnsupportedOperationException(
                         "Submatch is not supported with this backend/Android API level "
                                 + "combination");
@@ -651,11 +757,29 @@
             return result;
         }
 
+        /**
+         * Sets the {@link GenericDocument} for {@link MatchInfo}.
+         *
+         * {@link MatchInfo} lacks a constructor that populates {@link MatchInfo#mDocument}
+         * This provides the ability to set {@link MatchInfo#mDocument}
+         */
+        void setDocument(@NonNull GenericDocument document) {
+            mDocument = document;
+        }
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            MatchInfoCreator.writeToParcel(this, dest, flags);
+        }
+
         /** Builder for {@link MatchInfo} objects. */
         public static final class Builder {
             private final String mPropertyPath;
             private MatchRange mExactMatchRange = new MatchRange(0, 0);
-            @Nullable private MatchRange mSubmatchRange;
+            int mSubmatchRangeStart = -1;
+            int mSubmatchRangeEnd = -1;
             private MatchRange mSnippetRange = new MatchRange(0, 0);
 
             /**
@@ -675,6 +799,17 @@
                 mPropertyPath = Preconditions.checkNotNull(propertyPath);
             }
 
+            /** @exportToFramework:hide */
+            @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+            public Builder(@NonNull MatchInfo matchInfo) {
+                Preconditions.checkNotNull(matchInfo);
+                mPropertyPath = matchInfo.mPropertyPath;
+                mExactMatchRange = matchInfo.getExactMatchRange();
+                mSubmatchRangeStart = matchInfo.mSubmatchRangeStart;
+                mSubmatchRangeEnd = matchInfo.mSubmatchRangeEnd;
+                mSnippetRange = matchInfo.getSnippetRange();
+            }
+
             /** Sets the exact {@link MatchRange} corresponding to the given entry. */
             @CanIgnoreReturnValue
             @NonNull
@@ -684,11 +819,15 @@
             }
 
 
-            /** Sets the submatch {@link MatchRange} corresponding to the given entry. */
+            /**
+             * Sets the start and end of a submatch {@link MatchRange} corresponding
+             * to the given entry.
+             */
             @CanIgnoreReturnValue
             @NonNull
             public Builder setSubmatchRange(@NonNull MatchRange matchRange) {
-                mSubmatchRange = Preconditions.checkNotNull(matchRange);
+                mSubmatchRangeStart = matchRange.getStart();
+                mSubmatchRangeEnd = matchRange.getEnd();
                 return this;
             }
 
@@ -703,24 +842,14 @@
             /** Constructs a new {@link MatchInfo}. */
             @NonNull
             public MatchInfo build() {
-                Bundle bundle = new Bundle();
-                bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, mPropertyPath);
-                bundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, mExactMatchRange.getStart());
-                bundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, mExactMatchRange.getEnd());
-                if (mSubmatchRange != null) {
-                    // Only populate the submatch fields if it was actually set.
-                    bundle.putInt(MatchInfo.SUBMATCH_RANGE_LOWER_FIELD, mSubmatchRange.getStart());
-                }
-
-                if (mSubmatchRange != null) {
-                    // Only populate the submatch fields if it was actually set.
-                    // Moved to separate block for Nullness Checker.
-                    bundle.putInt(MatchInfo.SUBMATCH_RANGE_UPPER_FIELD, mSubmatchRange.getEnd());
-                }
-
-                bundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, mSnippetRange.getStart());
-                bundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, mSnippetRange.getEnd());
-                return new MatchInfo(bundle, /*document=*/ null);
+                return new MatchInfo(
+                    mPropertyPath,
+                    mExactMatchRange.getStart(),
+                    mExactMatchRange.getEnd(),
+                    mSubmatchRangeStart,
+                    mSubmatchRangeEnd,
+                    mSnippetRange.getStart(),
+                    mSnippetRange.getEnd());
             }
         }
     }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResultPage.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResultPage.java
index 568c2b6..d5d17a3 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResultPage.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResultPage.java
@@ -16,14 +16,16 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
-import androidx.core.util.Preconditions;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.SearchResultPageCreator;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -32,26 +34,29 @@
  * @exportToFramework:hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class SearchResultPage {
-    public static final String RESULTS_FIELD = "results";
-    public static final String NEXT_PAGE_TOKEN_FIELD = "nextPageToken";
[email protected](creator = "SearchResultPageCreator")
+public class SearchResultPage extends AbstractSafeParcelable {
+    @NonNull public static final Parcelable.Creator<SearchResultPage> CREATOR =
+            new SearchResultPageCreator();
+
+    @Field(id = 1, getter = "getNextPageToken")
     private final long mNextPageToken;
-
     @Nullable
-    private List<SearchResult> mResults;
+    @Field(id = 2, getter = "getResults")
+    private final List<SearchResult> mResults;
 
-    @NonNull
-    private final Bundle mBundle;
-
-    public SearchResultPage(@NonNull Bundle bundle) {
-        mBundle = Preconditions.checkNotNull(bundle);
-        mNextPageToken = mBundle.getLong(NEXT_PAGE_TOKEN_FIELD);
+    @Constructor
+    public SearchResultPage(
+            @Param(id = 1) long nextPageToken,
+            @Param(id = 2) @Nullable List<SearchResult> results) {
+        mNextPageToken = nextPageToken;
+        mResults = results;
     }
 
-    /** Returns the {@link Bundle} of this class. */
-    @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    /** Default constructor for {@link SearchResultPage}. */
+    public SearchResultPage() {
+        mNextPageToken = 0;
+        mResults = Collections.emptyList();
     }
 
     /** Returns the Token to get next {@link SearchResultPage}. */
@@ -61,19 +66,15 @@
 
     /** Returns all {@link androidx.appsearch.app.SearchResult}s of this page */
     @NonNull
-    @SuppressWarnings("deprecation")
     public List<SearchResult> getResults() {
         if (mResults == null) {
-            ArrayList<Bundle> resultBundles = mBundle.getParcelableArrayList(RESULTS_FIELD);
-            if (resultBundles == null) {
-                mResults = Collections.emptyList();
-            } else {
-                mResults = new ArrayList<>(resultBundles.size());
-                for (int i = 0; i < resultBundles.size(); i++) {
-                    mResults.add(new SearchResult(resultBundles.get(i)));
-                }
-            }
+            return Collections.emptyList();
         }
         return mResults;
     }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        SearchResultPageCreator.writeToParcel(this, dest, flags);
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
index 9b86fc3..1b43e15 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
@@ -18,6 +18,8 @@
 
 import android.annotation.SuppressLint;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
@@ -28,6 +30,11 @@
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.annotation.Document;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.SearchSpecCreator;
 import androidx.appsearch.util.BundleUtil;
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
@@ -41,49 +48,119 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
  * This class represents the specification logic for AppSearch. It can be used to set the type of
  * search, like prefix or exact only or apply filters to search for a specific schema type only etc.
  */
-public final class SearchSpec {
[email protected](creator = "SearchSpecCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class SearchSpec extends AbstractSafeParcelable {
+
+    /**  Creator class for {@link SearchSpec}. */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull
+    public static final Parcelable.Creator<SearchSpec> CREATOR =
+            new SearchSpecCreator();
+
     /**
      * Schema type to be used in {@link SearchSpec.Builder#addProjection} to apply
      * property paths to all results, excepting any types that have had their own, specific
      * property paths set.
+     *
+     * @deprecated use {@link #SCHEMA_TYPE_WILDCARD} instead.
      */
+    @Deprecated
     public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
 
     /**
      * Schema type to be used in {@link SearchSpec.Builder#addFilterProperties(String, Collection)}
-     * to apply property paths to all results, excepting any types that have had their own, specific
-     * property paths set.
-     * @exportToFramework:hide
+     * and {@link SearchSpec.Builder#addProjection} to apply property paths to all results,
+     * excepting any types that have had their own, specific property paths set.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES)
     public static final String SCHEMA_TYPE_WILDCARD = "*";
 
-    static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
-    static final String SCHEMA_FIELD = "schema";
-    static final String NAMESPACE_FIELD = "namespace";
-    static final String PROPERTY_FIELD = "property";
-    static final String PACKAGE_NAME_FIELD = "packageName";
-    static final String NUM_PER_PAGE_FIELD = "numPerPage";
-    static final String RANKING_STRATEGY_FIELD = "rankingStrategy";
-    static final String ORDER_FIELD = "order";
-    static final String SNIPPET_COUNT_FIELD = "snippetCount";
-    static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty";
-    static final String MAX_SNIPPET_FIELD = "maxSnippet";
-    static final String PROJECTION_TYPE_PROPERTY_PATHS_FIELD = "projectionTypeFieldMasks";
-    static final String RESULT_GROUPING_TYPE_FLAGS = "resultGroupingTypeFlags";
-    static final String RESULT_GROUPING_LIMIT = "resultGroupingLimit";
-    static final String TYPE_PROPERTY_WEIGHTS_FIELD = "typePropertyWeightsField";
-    static final String JOIN_SPEC = "joinSpec";
-    static final String ADVANCED_RANKING_EXPRESSION = "advancedRankingExpression";
-    static final String ENABLED_FEATURES_FIELD = "enabledFeatures";
+    @Field(id = 1, getter = "getTermMatch")
+    private final int mTermMatchType;
 
-    /** @exportToFramework:hide */
+    @Field(id = 2, getter = "getFilterSchemas")
+    private final List<String> mSchemas;
+
+    @Field(id = 3, getter = "getFilterNamespaces")
+    private final List<String> mNamespaces;
+
+    @Field(id = 4)
+    final Bundle mTypePropertyFilters;
+
+    @Field(id = 5, getter = "getFilterPackageNames")
+    private final List<String> mPackageNames;
+
+    @Field(id = 6, getter = "getResultCountPerPage")
+    private final int mResultCountPerPage;
+
+    @Field(id = 7, getter = "getRankingStrategy")
+    @RankingStrategy
+    private final int mRankingStrategy;
+
+    @Field(id = 8, getter = "getOrder")
+    @Order
+    private final int mOrder;
+
+    @Field(id = 9, getter = "getSnippetCount")
+    private final int mSnippetCount;
+
+    @Field(id = 10, getter = "getSnippetCountPerProperty")
+    private final int mSnippetCountPerProperty;
+
+    @Field(id = 11, getter = "getMaxSnippetSize")
+    private final int mMaxSnippetSize;
+
+    @Field(id = 12)
+    final Bundle mProjectionTypePropertyMasks;
+
+    @Field(id = 13, getter = "getResultGroupingTypeFlags")
+    @GroupingType
+    private final int mResultGroupingTypeFlags;
+
+    @Field(id = 14, getter = "getResultGroupingLimit")
+    private final int mGroupingLimit;
+
+    @Field(id = 15)
+    final Bundle mTypePropertyWeightsField;
+
+    @Nullable
+    @Field(id = 16, getter = "getJoinSpec")
+    private final JoinSpec mJoinSpec;
+
+    @Field(id = 17, getter = "getAdvancedRankingExpression")
+    private final String mAdvancedRankingExpression;
+
+    @Field(id = 18, getter = "getEnabledFeatures")
+    private final List<String> mEnabledFeatures;
+
+    @Field(id = 19, getter = "getSearchSourceLogTag")
+    @Nullable private final String mSearchSourceLogTag;
+
+    @NonNull
+    @Field(id = 20, getter = "getSearchEmbeddings")
+    private final List<EmbeddingVector> mSearchEmbeddings;
+
+    @Field(id = 21, getter = "getDefaultEmbeddingSearchMetricType")
+    private final int mDefaultEmbeddingSearchMetricType;
+
+    @NonNull
+    @Field(id = 22, getter = "getInformationalRankingExpressions")
+    private final List<String> mInformationalRankingExpressions;
+
+    /**
+     * Default number of documents per page.
+     *
+     * @exportToFramework:hide
+     */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final int DEFAULT_NUM_PER_PAGE = 10;
 
@@ -224,43 +301,112 @@
     /**
      * Results should be grouped together by schema type for the purpose of enforcing a limit on the
      * number of results returned per schema type.
-     *
-     * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
-     * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
-     *   Mainline are possible.
-     * -->
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA)
-    // @exportToFramework:endStrip()
+    @FlaggedApi(Flags.FLAG_ENABLE_GROUPING_TYPE_PER_SCHEMA)
     public static final int GROUPING_TYPE_PER_SCHEMA = 1 << 2;
 
-    private final Bundle mBundle;
-
-    /** @exportToFramework:hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public SearchSpec(@NonNull Bundle bundle) {
-        Preconditions.checkNotNull(bundle);
-        mBundle = bundle;
-    }
-
     /**
-     * Returns the {@link Bundle} populated by this builder.
+     * Type of scoring used to calculate similarity for embedding vectors. For details of each, see
+     * comments above each value.
      *
      * @exportToFramework:hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    // NOTE: The integer values of these constants must match the proto enum constants in
+    // {@link SearchSpecProto.EmbeddingQueryMetricType.Code}
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @IntDef(value = {
+            EMBEDDING_SEARCH_METRIC_TYPE_COSINE,
+            EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT,
+            EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmbeddingSearchMetricType {
     }
 
+    /**
+     * Cosine similarity as metric for embedding search and ranking.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public static final int EMBEDDING_SEARCH_METRIC_TYPE_COSINE = 1;
+    /**
+     * Dot product similarity as metric for embedding search and ranking.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public static final int EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT = 2;
+    /**
+     * Euclidean distance as metric for embedding search and ranking.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public static final int EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN = 3;
+
+
+    @Constructor
+    SearchSpec(
+            @Param(id = 1) int termMatchType,
+            @Param(id = 2) @NonNull List<String> schemas,
+            @Param(id = 3) @NonNull List<String> namespaces,
+            @Param(id = 4) @NonNull Bundle properties,
+            @Param(id = 5) @NonNull List<String> packageNames,
+            @Param(id = 6) int resultCountPerPage,
+            @Param(id = 7) @RankingStrategy int rankingStrategy,
+            @Param(id = 8) @Order int order,
+            @Param(id = 9) int snippetCount,
+            @Param(id = 10) int snippetCountPerProperty,
+            @Param(id = 11) int maxSnippetSize,
+            @Param(id = 12) @NonNull Bundle projectionTypePropertyMasks,
+            @Param(id = 13) int resultGroupingTypeFlags,
+            @Param(id = 14) int groupingLimit,
+            @Param(id = 15) @NonNull Bundle typePropertyWeightsField,
+            @Param(id = 16) @Nullable JoinSpec joinSpec,
+            @Param(id = 17) @NonNull String advancedRankingExpression,
+            @Param(id = 18) @NonNull List<String> enabledFeatures,
+            @Param(id = 19) @Nullable String searchSourceLogTag,
+            @Param(id = 20) @Nullable List<EmbeddingVector> searchEmbeddings,
+            @Param(id = 21) int defaultEmbeddingSearchMetricType,
+            @Param(id = 22) @Nullable List<String> informationalRankingExpressions
+    ) {
+        mTermMatchType = termMatchType;
+        mSchemas = Collections.unmodifiableList(Preconditions.checkNotNull(schemas));
+        mNamespaces = Collections.unmodifiableList(Preconditions.checkNotNull(namespaces));
+        mTypePropertyFilters = Preconditions.checkNotNull(properties);
+        mPackageNames = Collections.unmodifiableList(Preconditions.checkNotNull(packageNames));
+        mResultCountPerPage = resultCountPerPage;
+        mRankingStrategy = rankingStrategy;
+        mOrder = order;
+        mSnippetCount = snippetCount;
+        mSnippetCountPerProperty = snippetCountPerProperty;
+        mMaxSnippetSize = maxSnippetSize;
+        mProjectionTypePropertyMasks = Preconditions.checkNotNull(projectionTypePropertyMasks);
+        mResultGroupingTypeFlags = resultGroupingTypeFlags;
+        mGroupingLimit = groupingLimit;
+        mTypePropertyWeightsField = Preconditions.checkNotNull(typePropertyWeightsField);
+        mJoinSpec = joinSpec;
+        mAdvancedRankingExpression = Preconditions.checkNotNull(advancedRankingExpression);
+        mEnabledFeatures = Collections.unmodifiableList(
+                Preconditions.checkNotNull(enabledFeatures));
+        mSearchSourceLogTag = searchSourceLogTag;
+        if (searchEmbeddings != null) {
+            mSearchEmbeddings = Collections.unmodifiableList(searchEmbeddings);
+        } else {
+            mSearchEmbeddings = Collections.emptyList();
+        }
+        mDefaultEmbeddingSearchMetricType = defaultEmbeddingSearchMetricType;
+        if (informationalRankingExpressions != null) {
+            mInformationalRankingExpressions = Collections.unmodifiableList(
+                    informationalRankingExpressions);
+        } else {
+            mInformationalRankingExpressions = Collections.emptyList();
+        }
+    }
+
+
     /** Returns how the query terms should match terms in the index. */
     @TermMatch
     public int getTermMatch() {
-        return mBundle.getInt(TERM_MATCH_TYPE_FIELD, -1);
+        return mTermMatchType;
     }
 
     /**
@@ -270,11 +416,10 @@
      */
     @NonNull
     public List<String> getFilterSchemas() {
-        List<String> schemas = mBundle.getStringArrayList(SCHEMA_FIELD);
-        if (schemas == null) {
+        if (mSchemas == null) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(schemas);
+        return mSchemas;
     }
 
     /**
@@ -284,19 +429,15 @@
      *
      * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned
      * by this function, rather than calling it multiple times.
-     *
-     * @exportToFramework:hide
      */
     @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES)
     public Map<String, List<String>> getFilterProperties() {
-        Bundle typePropertyPathsBundle = Preconditions.checkNotNull(
-                mBundle.getBundle(PROPERTY_FIELD));
-        Set<String> schemas = typePropertyPathsBundle.keySet();
+        Set<String> schemas = mTypePropertyFilters.keySet();
         Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
         for (String schema : schemas) {
             typePropertyPathsMap.put(schema, Preconditions.checkNotNull(
-                    typePropertyPathsBundle.getStringArrayList(schema)));
+                    mTypePropertyFilters.getStringArrayList(schema)));
         }
         return typePropertyPathsMap;
     }
@@ -308,11 +449,10 @@
      */
     @NonNull
     public List<String> getFilterNamespaces() {
-        List<String> namespaces = mBundle.getStringArrayList(NAMESPACE_FIELD);
-        if (namespaces == null) {
+        if (mNamespaces == null) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(namespaces);
+        return mNamespaces;
     }
 
     /**
@@ -324,45 +464,44 @@
      */
     @NonNull
     public List<String> getFilterPackageNames() {
-        List<String> packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD);
-        if (packageNames == null) {
+        if (mPackageNames == null) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(packageNames);
+        return mPackageNames;
     }
 
     /** Returns the number of results per page in the result set. */
     public int getResultCountPerPage() {
-        return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
+        return mResultCountPerPage;
     }
 
     /** Returns the ranking strategy. */
     @RankingStrategy
     public int getRankingStrategy() {
-        return mBundle.getInt(RANKING_STRATEGY_FIELD);
+        return mRankingStrategy;
     }
 
     /** Returns the order of returned search results (descending or ascending). */
     @Order
     public int getOrder() {
-        return mBundle.getInt(ORDER_FIELD);
+        return mOrder;
     }
 
     /** Returns how many documents to generate snippets for. */
     public int getSnippetCount() {
-        return mBundle.getInt(SNIPPET_COUNT_FIELD);
+        return mSnippetCount;
     }
 
     /**
      * Returns how many matches for each property of a matching document to generate snippets for.
      */
     public int getSnippetCountPerProperty() {
-        return mBundle.getInt(SNIPPET_COUNT_PER_PROPERTY_FIELD);
+        return mSnippetCountPerProperty;
     }
 
     /** Returns the maximum size of a snippet in characters. */
     public int getMaxSnippetSize() {
-        return mBundle.getInt(MAX_SNIPPET_FIELD);
+        return mMaxSnippetSize;
     }
 
     /**
@@ -377,13 +516,12 @@
      */
     @NonNull
     public Map<String, List<String>> getProjections() {
-        Bundle typePropertyPathsBundle = Preconditions.checkNotNull(
-                mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD));
-        Set<String> schemas = typePropertyPathsBundle.keySet();
+        Set<String> schemas = mProjectionTypePropertyMasks.keySet();
         Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
         for (String schema : schemas) {
-            typePropertyPathsMap.put(schema, Preconditions.checkNotNull(
-                    typePropertyPathsBundle.getStringArrayList(schema)));
+            typePropertyPathsMap.put(schema,
+                    Objects.requireNonNull(
+                            mProjectionTypePropertyMasks.getStringArrayList(schema)));
         }
         return typePropertyPathsMap;
     }
@@ -400,16 +538,19 @@
      */
     @NonNull
     public Map<String, List<PropertyPath>> getProjectionPaths() {
-        Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
-        Set<String> schemas = typePropertyPathsBundle.keySet();
+        Set<String> schemas = mProjectionTypePropertyMasks.keySet();
         Map<String, List<PropertyPath>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
         for (String schema : schemas) {
-            ArrayList<String> propertyPathList = typePropertyPathsBundle.getStringArrayList(schema);
-            List<PropertyPath> copy = new ArrayList<>(propertyPathList.size());
-            for (String p: propertyPathList) {
-                copy.add(new PropertyPath(p));
+            ArrayList<String> propertyPathList = mProjectionTypePropertyMasks.getStringArrayList(
+                    schema);
+            if (propertyPathList != null) {
+                List<PropertyPath> copy = new ArrayList<>(propertyPathList.size());
+                for (int i = 0; i < propertyPathList.size(); i++) {
+                    String p = propertyPathList.get(i);
+                    copy.add(new PropertyPath(p));
+                }
+                typePropertyPathsMap.put(schema, copy);
             }
-            typePropertyPathsMap.put(schema, copy);
         }
         return typePropertyPathsMap;
     }
@@ -425,18 +566,20 @@
      */
     @NonNull
     public Map<String, Map<String, Double>> getPropertyWeights() {
-        Bundle typePropertyWeightsBundle = mBundle.getBundle(TYPE_PROPERTY_WEIGHTS_FIELD);
-        Set<String> schemaTypes = typePropertyWeightsBundle.keySet();
+        Set<String> schemaTypes = mTypePropertyWeightsField.keySet();
         Map<String, Map<String, Double>> typePropertyWeightsMap = new ArrayMap<>(
                 schemaTypes.size());
         for (String schemaType : schemaTypes) {
-            Bundle propertyPathBundle = typePropertyWeightsBundle.getBundle(schemaType);
-            Set<String> propertyPaths = propertyPathBundle.keySet();
-            Map<String, Double> propertyPathWeights = new ArrayMap<>(propertyPaths.size());
-            for (String propertyPath : propertyPaths) {
-                propertyPathWeights.put(propertyPath, propertyPathBundle.getDouble(propertyPath));
+            Bundle propertyPathBundle = mTypePropertyWeightsField.getBundle(schemaType);
+            if (propertyPathBundle != null) {
+                Set<String> propertyPaths = propertyPathBundle.keySet();
+                Map<String, Double> propertyPathWeights = new ArrayMap<>(propertyPaths.size());
+                for (String propertyPath : propertyPaths) {
+                    propertyPathWeights.put(propertyPath,
+                            propertyPathBundle.getDouble(propertyPath));
+                }
+                typePropertyWeightsMap.put(schemaType, propertyPathWeights);
             }
-            typePropertyWeightsMap.put(schemaType, propertyPathWeights);
         }
         return typePropertyWeightsMap;
     }
@@ -452,19 +595,22 @@
      */
     @NonNull
     public Map<String, Map<PropertyPath, Double>> getPropertyWeightPaths() {
-        Bundle typePropertyWeightsBundle = mBundle.getBundle(TYPE_PROPERTY_WEIGHTS_FIELD);
-        Set<String> schemaTypes = typePropertyWeightsBundle.keySet();
+        Set<String> schemaTypes = mTypePropertyWeightsField.keySet();
         Map<String, Map<PropertyPath, Double>> typePropertyWeightsMap = new ArrayMap<>(
                 schemaTypes.size());
         for (String schemaType : schemaTypes) {
-            Bundle propertyPathBundle = typePropertyWeightsBundle.getBundle(schemaType);
-            Set<String> propertyPaths = propertyPathBundle.keySet();
-            Map<PropertyPath, Double> propertyPathWeights = new ArrayMap<>(propertyPaths.size());
-            for (String propertyPath : propertyPaths) {
-                propertyPathWeights.put(new PropertyPath(propertyPath),
-                        propertyPathBundle.getDouble(propertyPath));
+            Bundle propertyPathBundle = mTypePropertyWeightsField.getBundle(schemaType);
+            if (propertyPathBundle != null) {
+                Set<String> propertyPaths = propertyPathBundle.keySet();
+                Map<PropertyPath, Double> propertyPathWeights =
+                        new ArrayMap<>(propertyPaths.size());
+                for (String propertyPath : propertyPaths) {
+                    propertyPathWeights.put(
+                            new PropertyPath(propertyPath),
+                            propertyPathBundle.getDouble(propertyPath));
+                }
+                typePropertyWeightsMap.put(schemaType, propertyPathWeights);
             }
-            typePropertyWeightsMap.put(schemaType, propertyPathWeights);
         }
         return typePropertyWeightsMap;
     }
@@ -475,7 +621,7 @@
      */
     @GroupingType
     public int getResultGroupingTypeFlags() {
-        return mBundle.getInt(RESULT_GROUPING_TYPE_FLAGS);
+        return mResultGroupingTypeFlags;
     }
 
     /**
@@ -485,7 +631,7 @@
      * {@link Builder#setResultGrouping(int, int)} was not called.
      */
     public int getResultGroupingLimit() {
-        return mBundle.getInt(RESULT_GROUPING_LIMIT, Integer.MAX_VALUE);
+        return mGroupingLimit;
     }
 
     /**
@@ -493,11 +639,7 @@
      */
     @Nullable
     public JoinSpec getJoinSpec() {
-        Bundle joinSpec = mBundle.getBundle(JOIN_SPEC);
-        if (joinSpec == null) {
-            return null;
-        }
-        return new JoinSpec(joinSpec);
+        return mJoinSpec;
     }
 
     /**
@@ -506,28 +648,104 @@
      */
     @NonNull
     public String getAdvancedRankingExpression() {
-        return mBundle.getString(ADVANCED_RANKING_EXPRESSION, "");
+        return mAdvancedRankingExpression;
+    }
+
+
+    /**
+     * Gets a tag to indicate the source of this search, or {@code null} if
+     * {@link Builder#setSearchSourceLogTag(String)} was not called.
+     *
+     * <p> Some AppSearch implementations may log a hash of this tag using statsd. This tag may be
+     * used for tracing performance issues and crashes to a component of an app.
+     *
+     * <p>Call {@link Builder#setSearchSourceLogTag} and give a unique value if you want to
+     * distinguish this search scenario with other search scenarios during performance analysis.
+     *
+     * <p>Under no circumstances will AppSearch log the raw String value using statsd, but it
+     * will be provided as-is to custom {@code AppSearchLogger} implementations you have
+     * registered in your app.
+     */
+    @Nullable
+    @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG)
+    public String getSearchSourceLogTag() {
+        return mSearchSourceLogTag;
+    }
+
+    /**
+     * Returns the list of {@link EmbeddingVector} for embedding search.
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public List<EmbeddingVector> getSearchEmbeddings() {
+        return mSearchEmbeddings;
+    }
+
+    /**
+     * Returns the default embedding metric type used for embedding search
+     * (see {@link AppSearchSession#search}) and ranking
+     * (see {@link SearchSpec.Builder#setRankingStrategy(String)}).
+     */
+    @EmbeddingSearchMetricType
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public int getDefaultEmbeddingSearchMetricType() {
+        return mDefaultEmbeddingSearchMetricType;
+    }
+
+    /**
+     * Returns the informational ranking expressions.
+     *
+     * @see Builder#addInformationalRankingExpressions
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+    public List<String> getInformationalRankingExpressions() {
+        return mInformationalRankingExpressions;
     }
 
     /**
      * Returns whether the NUMERIC_SEARCH feature is enabled.
      */
     public boolean isNumericSearchEnabled() {
-        return getEnabledFeatures().contains(FeatureConstants.NUMERIC_SEARCH);
+        return mEnabledFeatures.contains(FeatureConstants.NUMERIC_SEARCH);
     }
 
     /**
      * Returns whether the VERBATIM_SEARCH feature is enabled.
      */
     public boolean isVerbatimSearchEnabled() {
-        return getEnabledFeatures().contains(FeatureConstants.VERBATIM_SEARCH);
+        return mEnabledFeatures.contains(FeatureConstants.VERBATIM_SEARCH);
     }
 
     /**
      * Returns whether the LIST_FILTER_QUERY_LANGUAGE feature is enabled.
      */
     public boolean isListFilterQueryLanguageEnabled() {
-        return getEnabledFeatures().contains(FeatureConstants.LIST_FILTER_QUERY_LANGUAGE);
+        return mEnabledFeatures.contains(FeatureConstants.LIST_FILTER_QUERY_LANGUAGE);
+    }
+
+    /**
+     * Returns whether the LIST_FILTER_HAS_PROPERTY_FUNCTION feature is enabled.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_HAS_PROPERTY_FUNCTION)
+    public boolean isListFilterHasPropertyFunctionEnabled() {
+        return mEnabledFeatures.contains(FeatureConstants.LIST_FILTER_HAS_PROPERTY_FUNCTION);
+    }
+
+    /**
+     * Returns whether the embedding search feature is enabled.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+    public boolean isEmbeddingSearchEnabled() {
+        return mEnabledFeatures.contains(FeatureConstants.EMBEDDING_SEARCH);
+    }
+
+    /**
+     * Returns whether the LIST_FILTER_TOKENIZE_FUNCTION feature is enabled.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION)
+    public boolean isListFilterTokenizeFunctionEnabled() {
+        return mEnabledFeatures.contains(FeatureConstants.LIST_FILTER_TOKENIZE_FUNCTION);
     }
 
     /**
@@ -539,21 +757,31 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @NonNull
     public List<String> getEnabledFeatures() {
-        return mBundle.getStringArrayList(ENABLED_FEATURES_FIELD);
+        return mEnabledFeatures;
+    }
+
+    @Override
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        SearchSpecCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link SearchSpec objects}. */
     public static final class Builder {
-        private ArrayList<String> mSchemas = new ArrayList<>();
-        private ArrayList<String> mNamespaces = new ArrayList<>();
+        private List<String> mSchemas = new ArrayList<>();
+        private List<String> mNamespaces = new ArrayList<>();
         private Bundle mTypePropertyFilters = new Bundle();
-        private ArrayList<String> mPackageNames = new ArrayList<>();
+        private List<String> mPackageNames = new ArrayList<>();
         private ArraySet<String> mEnabledFeatures = new ArraySet<>();
         private Bundle mProjectionTypePropertyMasks = new Bundle();
         private Bundle mTypePropertyWeights = new Bundle();
+        private List<EmbeddingVector> mSearchEmbeddings = new ArrayList<>();
 
         private int mResultCountPerPage = DEFAULT_NUM_PER_PAGE;
         @TermMatch private int mTermMatchType = TERM_MATCH_PREFIX;
+        @EmbeddingSearchMetricType
+        private int mDefaultEmbeddingSearchMetricType = EMBEDDING_SEARCH_METRIC_TYPE_COSINE;
         private int mSnippetCount = 0;
         private int mSnippetCountPerProperty = MAX_SNIPPET_PER_PROPERTY_COUNT;
         private int mMaxSnippetSize = 0;
@@ -561,10 +789,53 @@
         @Order private int mOrder = ORDER_DESCENDING;
         @GroupingType private int mGroupingTypeFlags = 0;
         private int mGroupingLimit = 0;
-        private JoinSpec mJoinSpec;
+        @Nullable private JoinSpec mJoinSpec;
         private String mAdvancedRankingExpression = "";
+        private List<String> mInformationalRankingExpressions = new ArrayList<>();
+        @Nullable private String mSearchSourceLogTag;
         private boolean mBuilt = false;
 
+        /** Constructs a new builder for {@link SearchSpec} objects. */
+        public Builder() {
+        }
+
+        /** @exportToFramework:hide */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public Builder(@NonNull SearchSpec searchSpec) {
+            Objects.requireNonNull(searchSpec);
+            mSchemas = new ArrayList<>(searchSpec.getFilterSchemas());
+            mNamespaces = new ArrayList<>(searchSpec.getFilterNamespaces());
+            for (Map.Entry<String, List<String>> entry :
+                    searchSpec.getFilterProperties().entrySet()) {
+                addFilterProperties(entry.getKey(), entry.getValue());
+            }
+            mPackageNames = new ArrayList<>(searchSpec.getFilterPackageNames());
+            mEnabledFeatures = new ArraySet<>(searchSpec.getEnabledFeatures());
+            for (Map.Entry<String, List<String>> entry : searchSpec.getProjections().entrySet()) {
+                addProjection(entry.getKey(), entry.getValue());
+            }
+            for (Map.Entry<String, Map<String, Double>> entry :
+                    searchSpec.getPropertyWeights().entrySet()) {
+                setPropertyWeights(entry.getKey(), entry.getValue());
+            }
+            mSearchEmbeddings = new ArrayList<>(searchSpec.getSearchEmbeddings());
+            mResultCountPerPage = searchSpec.getResultCountPerPage();
+            mTermMatchType = searchSpec.getTermMatch();
+            mDefaultEmbeddingSearchMetricType = searchSpec.getDefaultEmbeddingSearchMetricType();
+            mSnippetCount = searchSpec.getSnippetCount();
+            mSnippetCountPerProperty = searchSpec.getSnippetCountPerProperty();
+            mMaxSnippetSize = searchSpec.getMaxSnippetSize();
+            mRankingStrategy = searchSpec.getRankingStrategy();
+            mOrder = searchSpec.getOrder();
+            mGroupingTypeFlags = searchSpec.getResultGroupingTypeFlags();
+            mGroupingLimit = searchSpec.getResultGroupingLimit();
+            mJoinSpec = searchSpec.getJoinSpec();
+            mAdvancedRankingExpression = searchSpec.getAdvancedRankingExpression();
+            mInformationalRankingExpressions = new ArrayList<>(
+                    searchSpec.getInformationalRankingExpressions());
+            mSearchSourceLogTag = searchSpec.getSearchSourceLogTag();
+        }
+
         /**
          * Sets how the query terms should match {@code TermMatchCode} in the index.
          *
@@ -626,12 +897,13 @@
         @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
         public Builder addFilterDocumentClasses(
-                @NonNull Collection<? extends Class<?>> documentClasses) throws AppSearchException {
+                @NonNull Collection<? extends java.lang.Class<?>> documentClasses)
+                throws AppSearchException {
             Preconditions.checkNotNull(documentClasses);
             resetIfBuilt();
             List<String> schemas = new ArrayList<>(documentClasses.size());
             DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
-            for (Class<?> documentClass : documentClasses) {
+            for (java.lang.Class<?> documentClass : documentClasses) {
                 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
                 schemas.add(factory.getSchemaName());
             }
@@ -655,7 +927,7 @@
         @CanIgnoreReturnValue
         @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder addFilterDocumentClasses(@NonNull Class<?>... documentClasses)
+        public Builder addFilterDocumentClasses(@NonNull java.lang.Class<?>... documentClasses)
                 throws AppSearchException {
             Preconditions.checkNotNull(documentClasses);
             resetIfBuilt();
@@ -685,17 +957,13 @@
          * @param schema the {@link AppSearchSchema} that contains the target properties
          * @param propertyPaths The String version of {@link PropertyPath}. A dot-delimited
          *                      sequence of property names.
-         *
-         * @exportToFramework:hide
          */
-         // TODO(b/296088047) unhide from framework when type property filters are made public.
+        @CanIgnoreReturnValue
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
-        // @exportToFramework:endStrip()
+        @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES)
         public Builder addFilterProperties(@NonNull String schema,
                 @NonNull Collection<String> propertyPaths) {
             Preconditions.checkNotNull(schema);
@@ -720,17 +988,14 @@
          *
          * @param schema the {@link AppSearchSchema} that contains the target properties
          * @param propertyPaths The {@link PropertyPath} to search search over
-         *
-         * @exportToFramework:hide
          */
-         // TODO(b/296088047) unhide from framework when type property filters are made public.
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        // @exportToFramework:startStrip()
+        // Getter method is getFilterProperties
+        @SuppressLint("MissingGetterMatchingBuilder")
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
-        // @exportToFramework:endStrip()
+        @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES)
         public Builder addFilterPropertyPaths(@NonNull String schema,
                 @NonNull Collection<PropertyPath> propertyPaths) {
             Preconditions.checkNotNull(schema);
@@ -744,6 +1009,7 @@
 
 
 // @exportToFramework:startStrip()
+
         /**
          * Adds property paths for the specified type to the property filter of
          * {@link SearchSpec} Entry. Only returns documents that have matches under the specified
@@ -758,11 +1024,10 @@
          *
          */
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
-        public Builder addFilterProperties(@NonNull Class<?> documentClass,
+        public Builder addFilterProperties(@NonNull java.lang.Class<?> documentClass,
                 @NonNull Collection<String> propertyPaths) throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             Preconditions.checkNotNull(propertyPaths);
@@ -787,11 +1052,12 @@
          *
          */
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        // Getter method is getFilterProperties
+        @SuppressLint("MissingGetterMatchingBuilder")
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
-        public Builder addFilterPropertyPaths(@NonNull Class<?> documentClass,
+        public Builder addFilterPropertyPaths(@NonNull java.lang.Class<?> documentClass,
                 @NonNull Collection<PropertyPath> propertyPaths) throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             Preconditions.checkNotNull(propertyPaths);
@@ -966,6 +1232,19 @@
          *     current document being scored. Property weights come from what's specified in
          *     {@link SearchSpec}. After normalizing, each provided weight will be divided by the
          *     maximum weight, so that each of them will be <= 1.
+         *     <li>this.matchedSemanticScores(getSearchSpecEmbedding({embedding_index}), {metric})
+         *     <p>Returns a list of the matched similarity scores from "semanticSearch" in the query
+         *     expression (see also {@link AppSearchSession#search}) based on embedding_index and
+         *     metric. If metric is omitted, it defaults to the metric specified in
+         *     {@link SearchSpec.Builder#setDefaultEmbeddingSearchMetricType(int)}. If no
+         *     "semanticSearch" is called for embedding_index and metric in the query, this
+         *     function will return an empty list. If multiple "semanticSearch"s are called for
+         *     the same embedding_index and metric, this function will return a list of their
+         *     merged scores.
+         *     <p>Example: `this.matchedSemanticScores(getSearchSpecEmbedding(0), "COSINE")` will
+         *     return a list of matched scores within the range of [0.5, 1], if
+         *     `semanticSearch(getSearchSpecEmbedding(0), 0.5, 1, "COSINE")` is called in the
+         *     query expression.
          * </ul>
          *
          * <p>Some errors may occur when using advanced ranking.
@@ -1011,11 +1290,9 @@
          */
         @CanIgnoreReturnValue
         @NonNull
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_SPEC_ADVANCED_RANKING_EXPRESSION)
-        // @exportToFramework:endStrip()
         public Builder setRankingStrategy(@NonNull String advancedRankingExpression) {
             Preconditions.checkStringNotEmpty(advancedRankingExpression);
             resetIfBuilt();
@@ -1025,6 +1302,88 @@
         }
 
         /**
+         * Adds informational ranking expressions to be evaluated for each document in the search
+         * result. The values of these expressions will be returned to the caller via
+         * {@link SearchResult#getInformationalRankingSignals()}. These expressions are purely for
+         * the caller to retrieve additional information about the result and have no effect on
+         * ranking.
+         *
+         * <p>The syntax is exactly the same as specified in
+         * {@link SearchSpec.Builder#setRankingStrategy(String)}.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS)
+        @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+        public Builder addInformationalRankingExpressions(
+                @NonNull String... informationalRankingExpressions) {
+            Preconditions.checkNotNull(informationalRankingExpressions);
+            resetIfBuilt();
+            return addInformationalRankingExpressions(
+                    Arrays.asList(informationalRankingExpressions));
+        }
+
+        /**
+         * Adds informational ranking expressions to be evaluated for each document in the search
+         * result. The values of these expressions will be returned to the caller via
+         * {@link SearchResult#getInformationalRankingSignals()}. These expressions are purely for
+         * the caller to retrieve additional information about the result and have no effect on
+         * ranking.
+         *
+         * <p>The syntax is exactly the same as specified in
+         * {@link SearchSpec.Builder#setRankingStrategy(String)}.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS)
+        @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS)
+        public Builder addInformationalRankingExpressions(
+                @NonNull Collection<String> informationalRankingExpressions) {
+            Preconditions.checkNotNull(informationalRankingExpressions);
+            resetIfBuilt();
+            mInformationalRankingExpressions.addAll(informationalRankingExpressions);
+            return this;
+        }
+
+        /**
+         * Sets an optional log tag to indicate the source of this search.
+         *
+         * <p>Some AppSearch implementations may log a hash of this tag using statsd. This tag
+         * may be used for tracing performance issues and crashes to a component of an app.
+         *
+         * <p>Call this method and give a unique value if you want to distinguish this search
+         * scenario with other search scenarios during performance analysis.
+         *
+         * <p>Under no circumstances will AppSearch log the raw String value using statsd, but it
+         * will be provided as-is to custom {@code AppSearchLogger} implementations you have
+         * registered in your app.
+         *
+         * @param searchSourceLogTag A String to indicate the source caller of this search. It is
+         *                           used to label the search statsd for performance analysis. It
+         *                           is not the tag we are using in {@link android.util.Log}. The
+         *                           length of the teg should between 1 and 100.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG)
+        @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG)
+        public Builder setSearchSourceLogTag(@NonNull String searchSourceLogTag) {
+            Preconditions.checkStringNotEmpty(searchSourceLogTag);
+            Preconditions.checkArgument(searchSourceLogTag.length() <= 100,
+                    "The maximum supported tag length is 100. This tag is too long: "
+                            + searchSourceLogTag.length());
+            resetIfBuilt();
+            mSearchSourceLogTag = searchSourceLogTag;
+            return this;
+        }
+
+        /**
          * Sets the order of returned search results, the default is
          * {@link #ORDER_DESCENDING}, meaning that results with higher scores come first.
          *
@@ -1143,9 +1502,8 @@
          * results of that type will be retrieved.
          *
          * <p>If property path is added for the
-         * {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will
-         * apply to all results, excepting any types that have their own, specific property paths
-         * set.
+         * {@link SearchSpec#SCHEMA_TYPE_WILDCARD}, then those property paths will apply to all
+         * results, excepting any types that have their own, specific property paths set.
          *
          * <p>Suppose the following document is in the index.
          * <pre>{@code
@@ -1226,7 +1584,8 @@
         @SuppressLint("MissingGetterMatchingBuilder")  // Projections available from getProjections
         @NonNull
         public SearchSpec.Builder addProjectionsForDocumentClass(
-                @NonNull Class<?> documentClass, @NonNull Collection<String> propertyPaths)
+                @NonNull java.lang.Class<?> documentClass,
+                @NonNull Collection<String> propertyPaths)
                 throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             resetIfBuilt();
@@ -1247,7 +1606,8 @@
         @SuppressLint("MissingGetterMatchingBuilder")  // Projections available from getProjections
         @NonNull
         public SearchSpec.Builder addProjectionPathsForDocumentClass(
-                @NonNull Class<?> documentClass, @NonNull Collection<PropertyPath> propertyPaths)
+                @NonNull java.lang.Class<?> documentClass,
+                @NonNull Collection<PropertyPath> propertyPaths)
                 throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             resetIfBuilt();
@@ -1319,12 +1679,10 @@
          *                            weight to set for that property.
          * @throws IllegalArgumentException if a weight is equal to or less than 0.0.
          */
-        // @exportToFramework:startStrip()
         @CanIgnoreReturnValue
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_SPEC_PROPERTY_WEIGHTS)
-        // @exportToFramework:endStrip()
         @NonNull
         public SearchSpec.Builder setPropertyWeights(@NonNull String schemaType,
                 @NonNull Map<String, Double> propertyPathWeights) {
@@ -1354,12 +1712,10 @@
          *
          * @param joinSpec a specification on how to perform the Join operation.
          */
-        // @exportToFramework:startStrip()
         @CanIgnoreReturnValue
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.JOIN_SPEC_AND_QUALIFIED_ID)
-        // @exportToFramework:endStrip()
         @NonNull
         public Builder setJoinSpec(@NonNull JoinSpec joinSpec) {
             resetIfBuilt();
@@ -1397,12 +1753,10 @@
          *                            weight to set for that property.
          * @throws IllegalArgumentException if a weight is equal to or less than 0.0.
          */
-        // @exportToFramework:startStrip()
         @CanIgnoreReturnValue
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_SPEC_PROPERTY_WEIGHTS)
-        // @exportToFramework:endStrip()
         @NonNull
         public SearchSpec.Builder setPropertyWeightPaths(@NonNull String schemaType,
                 @NonNull Map<PropertyPath, Double> propertyPathWeights) {
@@ -1460,7 +1814,7 @@
                 name = Features.SEARCH_SPEC_PROPERTY_WEIGHTS)
         @NonNull
         public SearchSpec.Builder setPropertyWeightsForDocumentClass(
-                @NonNull Class<?> documentClass,
+                @NonNull java.lang.Class<?> documentClass,
                 @NonNull Map<String, Double> propertyPathWeights) throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
@@ -1508,7 +1862,7 @@
                 name = Features.SEARCH_SPEC_PROPERTY_WEIGHTS)
         @NonNull
         public SearchSpec.Builder setPropertyWeightPathsForDocumentClass(
-                @NonNull Class<?> documentClass,
+                @NonNull java.lang.Class<?> documentClass,
                 @NonNull Map<PropertyPath, Double> propertyPathWeights) throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
@@ -1518,6 +1872,72 @@
 // @exportToFramework:endStrip()
 
         /**
+         * Adds an embedding search to {@link SearchSpec} Entry, which will be referred in the
+         * query expression and the ranking expression for embedding search.
+         *
+         * @see AppSearchSession#search
+         * @see SearchSpec.Builder#setRankingStrategy(String)
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        public Builder addSearchEmbeddings(@NonNull EmbeddingVector... searchEmbeddings) {
+            Preconditions.checkNotNull(searchEmbeddings);
+            resetIfBuilt();
+            return addSearchEmbeddings(Arrays.asList(searchEmbeddings));
+        }
+
+        /**
+         * Adds an embedding search to {@link SearchSpec} Entry, which will be referred in the
+         * query expression and the ranking expression for embedding search.
+         *
+         * @see AppSearchSession#search
+         * @see SearchSpec.Builder#setRankingStrategy(String)
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        public Builder addSearchEmbeddings(
+                @NonNull Collection<EmbeddingVector> searchEmbeddings) {
+            Preconditions.checkNotNull(searchEmbeddings);
+            resetIfBuilt();
+            mSearchEmbeddings.addAll(searchEmbeddings);
+            return this;
+        }
+
+        /**
+         * Sets the default embedding metric type used for embedding search
+         * (see {@link AppSearchSession#search}) and ranking
+         * (see {@link SearchSpec.Builder#setRankingStrategy(String)}).
+         *
+         * <p>If this method is not called, the default embedding search metric type is
+         * {@link SearchSpec#EMBEDDING_SEARCH_METRIC_TYPE_COSINE}. Metrics specified within
+         * "semanticSearch" or "matchedSemanticScores" functions in search/ranking expressions
+         * will override this default.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        public Builder setDefaultEmbeddingSearchMetricType(
+                @EmbeddingSearchMetricType int defaultEmbeddingSearchMetricType) {
+            Preconditions.checkArgumentInRange(defaultEmbeddingSearchMetricType,
+                    EMBEDDING_SEARCH_METRIC_TYPE_COSINE,
+                    EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN, "Embedding search metric type");
+            resetIfBuilt();
+            mDefaultEmbeddingSearchMetricType = defaultEmbeddingSearchMetricType;
+            return this;
+        }
+
+        /**
          * Sets the NUMERIC_SEARCH feature as enabled/disabled according to the enabled parameter.
          *
          * @param enabled Enables the feature if true, otherwise disables it.
@@ -1526,11 +1946,10 @@
          * {@link AppSearchSchema.LongPropertyConfig#INDEXING_TYPE_RANGE} and all other numeric
          * querying features.
          */
-        // @exportToFramework:startStrip()
+        @CanIgnoreReturnValue
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.NUMERIC_SEARCH)
-        // @exportToFramework:endStrip()
         @NonNull
         public Builder setNumericSearchEnabled(boolean enabled) {
             modifyEnabledFeature(FeatureConstants.NUMERIC_SEARCH, enabled);
@@ -1550,11 +1969,10 @@
          * <p>For example, The verbatim string operator '"foo/bar" OR baz' will ensure that
          * 'foo/bar' is treated as a single 'verbatim' token.
          */
-        // @exportToFramework:startStrip()
+        @CanIgnoreReturnValue
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.VERBATIM_SEARCH)
-        // @exportToFramework:endStrip()
         @NonNull
         public Builder setVerbatimSearchEnabled(boolean enabled) {
             modifyEnabledFeature(FeatureConstants.VERBATIM_SEARCH, enabled);
@@ -1578,7 +1996,7 @@
          * <p>The newly added custom functions covered by this feature are:
          * <ul>
          * <li>createList(String...)</li>
-         * <li>termSearch(String, List<String>)</li>
+         * <li>termSearch(String, {@code List<String>})</li>
          * </ul>
          *
          * <p>createList takes a variable number of strings and returns a list of strings.
@@ -1590,11 +2008,10 @@
          * for example, the query "(subject:foo OR body:foo) (subject:bar OR body:bar)"
          * could be rewritten as "termSearch(\"foo bar\", createList(\"subject\", \"bar\"))"
          */
-        // @exportToFramework:startStrip()
+        @CanIgnoreReturnValue
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.LIST_FILTER_QUERY_LANGUAGE)
-        // @exportToFramework:endStrip()
         @NonNull
         public Builder setListFilterQueryLanguageEnabled(boolean enabled) {
             modifyEnabledFeature(FeatureConstants.LIST_FILTER_QUERY_LANGUAGE, enabled);
@@ -1602,6 +2019,65 @@
         }
 
         /**
+         * Sets the LIST_FILTER_HAS_PROPERTY_FUNCTION feature as enabled/disabled according to
+         * the enabled parameter.
+         *
+         * @param enabled Enables the feature if true, otherwise disables it
+         *
+         * <p>If disabled, disallows the use of the "hasProperty" function. See
+         * {@link AppSearchSession#search} for more details about the function.
+         */
+        @CanIgnoreReturnValue
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.LIST_FILTER_HAS_PROPERTY_FUNCTION)
+        @NonNull
+        @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_HAS_PROPERTY_FUNCTION)
+        public Builder setListFilterHasPropertyFunctionEnabled(boolean enabled) {
+            modifyEnabledFeature(FeatureConstants.LIST_FILTER_HAS_PROPERTY_FUNCTION, enabled);
+            return this;
+        }
+
+        /**
+         * Sets the embedding search feature as enabled/disabled according to the enabled parameter.
+         *
+         * <p>If disabled, disallows the use of the "semanticSearch" function. See
+         * {@link AppSearchSession#search} for more details about the function.
+         *
+         * @param enabled Enables the feature if true, otherwise disables it
+         */
+        @CanIgnoreReturnValue
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        @NonNull
+        @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG)
+        public Builder setEmbeddingSearchEnabled(boolean enabled) {
+            modifyEnabledFeature(FeatureConstants.EMBEDDING_SEARCH, enabled);
+            return this;
+        }
+
+        /**
+         * Sets the LIST_FILTER_TOKENIZE_FUNCTION feature as enabled/disabled according to
+         * the enabled parameter.
+         *
+         * @param enabled Enables the feature if true, otherwise disables it
+         *
+         * <p>If disabled, disallows the use of the "tokenize" function. See
+         * {@link AppSearchSession#search} for more details about the function.
+         */
+        @CanIgnoreReturnValue
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.LIST_FILTER_TOKENIZE_FUNCTION)
+        @NonNull
+        @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION)
+        public Builder setListFilterTokenizeFunctionEnabled(boolean enabled) {
+            modifyEnabledFeature(FeatureConstants.LIST_FILTER_TOKENIZE_FUNCTION, enabled);
+            return this;
+        }
+
+        /**
          * Constructs a new {@link SearchSpec} from the contents of this builder.
          *
          * @throws IllegalArgumentException if property weights are provided with a
@@ -1617,7 +2093,6 @@
          */
         @NonNull
         public SearchSpec build() {
-            Bundle bundle = new Bundle();
             if (mJoinSpec != null) {
                 if (mRankingStrategy != RANKING_STRATEGY_JOIN_AGGREGATE_SCORE
                         && mJoinSpec.getAggregationScoringStrategy()
@@ -1626,59 +2101,26 @@
                             + "the nested JoinSpec, but ranking strategy is not "
                             + "RANKING_STRATEGY_JOIN_AGGREGATE_SCORE");
                 }
-                bundle.putBundle(JOIN_SPEC, mJoinSpec.getBundle());
             } else if (mRankingStrategy == RANKING_STRATEGY_JOIN_AGGREGATE_SCORE) {
                 throw new IllegalStateException("Attempting to rank based on joined documents, but "
                         + "no JoinSpec provided");
             }
             if (!mTypePropertyWeights.isEmpty()
-                    && RANKING_STRATEGY_RELEVANCE_SCORE != mRankingStrategy
-                    && RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION != mRankingStrategy) {
+                    && mRankingStrategy != RANKING_STRATEGY_RELEVANCE_SCORE
+                    && mRankingStrategy != RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION) {
                 throw new IllegalArgumentException("Property weights are only compatible with the "
                         + "RANKING_STRATEGY_RELEVANCE_SCORE and "
                         + "RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION ranking strategies.");
             }
 
-            // If the schema filter isn't empty, and there is a schema with a projection but not
-            // in the filter, that is a SearchSpec user error.
-            if (!mSchemas.isEmpty()) {
-                for (String schema : mProjectionTypePropertyMasks.keySet()) {
-                    if (!mSchemas.contains(schema)) {
-                        throw new IllegalArgumentException("Projection requested for schema not "
-                                + "in schemas filters: " + schema);
-                    }
-                }
-            }
-
-            Set<String> schemaFilter = new ArraySet<>(mSchemas);
-            if (!mSchemas.isEmpty()) {
-                for (String schema : mTypePropertyFilters.keySet()) {
-                    if (!schemaFilter.contains(schema)) {
-                        throw new IllegalStateException(
-                                "The schema: " + schema + " exists in the property filter but "
-                                        + "doesn't exist in the schema filter.");
-                    }
-                }
-            }
-            bundle.putStringArrayList(SCHEMA_FIELD, mSchemas);
-            bundle.putBundle(PROPERTY_FIELD, mTypePropertyFilters);
-            bundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
-            bundle.putStringArrayList(PACKAGE_NAME_FIELD, mPackageNames);
-            bundle.putStringArrayList(ENABLED_FEATURES_FIELD, new ArrayList<>(mEnabledFeatures));
-            bundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks);
-            bundle.putInt(NUM_PER_PAGE_FIELD, mResultCountPerPage);
-            bundle.putInt(TERM_MATCH_TYPE_FIELD, mTermMatchType);
-            bundle.putInt(SNIPPET_COUNT_FIELD, mSnippetCount);
-            bundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, mSnippetCountPerProperty);
-            bundle.putInt(MAX_SNIPPET_FIELD, mMaxSnippetSize);
-            bundle.putInt(RANKING_STRATEGY_FIELD, mRankingStrategy);
-            bundle.putInt(ORDER_FIELD, mOrder);
-            bundle.putInt(RESULT_GROUPING_TYPE_FLAGS, mGroupingTypeFlags);
-            bundle.putInt(RESULT_GROUPING_LIMIT, mGroupingLimit);
-            bundle.putBundle(TYPE_PROPERTY_WEIGHTS_FIELD, mTypePropertyWeights);
-            bundle.putString(ADVANCED_RANKING_EXPRESSION, mAdvancedRankingExpression);
             mBuilt = true;
-            return new SearchSpec(bundle);
+            return new SearchSpec(mTermMatchType, mSchemas, mNamespaces,
+                    mTypePropertyFilters, mPackageNames, mResultCountPerPage,
+                    mRankingStrategy, mOrder, mSnippetCount, mSnippetCountPerProperty,
+                    mMaxSnippetSize, mProjectionTypePropertyMasks, mGroupingTypeFlags,
+                    mGroupingLimit, mTypePropertyWeights, mJoinSpec, mAdvancedRankingExpression,
+                    new ArrayList<>(mEnabledFeatures), mSearchSourceLogTag, mSearchEmbeddings,
+                    mDefaultEmbeddingSearchMetricType, mInformationalRankingExpressions);
         }
 
         private void resetIfBuilt() {
@@ -1689,6 +2131,9 @@
                 mPackageNames = new ArrayList<>(mPackageNames);
                 mProjectionTypePropertyMasks = BundleUtil.deepCopy(mProjectionTypePropertyMasks);
                 mTypePropertyWeights = BundleUtil.deepCopy(mTypePropertyWeights);
+                mSearchEmbeddings = new ArrayList<>(mSearchEmbeddings);
+                mInformationalRankingExpressions = new ArrayList<>(
+                        mInformationalRankingExpressions);
                 mBuilt = false;
             }
         }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java
index 63fd696..c5171fd1 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionResult.java
@@ -16,38 +16,40 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
-import androidx.appsearch.util.BundleUtil;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.SearchSuggestionResultCreator;
 import androidx.core.util.Preconditions;
 
 /**
  * The result class of the {@link AppSearchSession#searchSuggestionAsync}.
  */
-public final class SearchSuggestionResult {
[email protected](creator = "SearchSuggestionResultCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class SearchSuggestionResult extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull
+    public static final Parcelable.Creator<SearchSuggestionResult> CREATOR =
+            new SearchSuggestionResultCreator();
 
-    private static final String SUGGESTED_RESULT_FIELD = "suggestedResult";
-    private final Bundle mBundle;
+    @Field(id = 1, getter = "getSuggestedResult")
+    private final String mSuggestedResult;
     @Nullable
     private Integer mHashCode;
 
-    SearchSuggestionResult(@NonNull Bundle bundle) {
-        mBundle = Preconditions.checkNotNull(bundle);
-    }
-
-    /**
-     * Returns the {@link Bundle} populated by this builder.
-     *
-     * @exportToFramework:hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    @Constructor
+    SearchSuggestionResult(@Param(id = 1) String suggestedResult) {
+        mSuggestedResult = Preconditions.checkNotNull(suggestedResult);
     }
 
     /**
@@ -60,7 +62,7 @@
      */
     @NonNull
     public String getSuggestedResult() {
-        return Preconditions.checkNotNull(mBundle.getString(SUGGESTED_RESULT_FIELD));
+        return mSuggestedResult;
     }
 
     @Override
@@ -72,13 +74,13 @@
             return false;
         }
         SearchSuggestionResult otherResult = (SearchSuggestionResult) other;
-        return BundleUtil.deepEquals(this.mBundle, otherResult.mBundle);
+        return mSuggestedResult.equals(otherResult.mSuggestedResult);
     }
 
     @Override
     public int hashCode() {
         if (mHashCode == null) {
-            mHashCode = BundleUtil.deepHashCode(mBundle);
+            mHashCode = mSuggestedResult.hashCode();
         }
         return mHashCode;
     }
@@ -102,12 +104,17 @@
             return this;
         }
 
-        /** Build a {@link SearchSuggestionResult} object*/
+        /** Build a {@link SearchSuggestionResult} object */
         @NonNull
         public SearchSuggestionResult build() {
-            Bundle bundle = new Bundle();
-            bundle.putString(SUGGESTED_RESULT_FIELD, mSuggestedResult);
-            return new SearchSuggestionResult(bundle);
+            return new SearchSuggestionResult(mSuggestedResult);
         }
     }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        SearchSuggestionResultCreator.writeToParcel(this, dest, flags);
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java
index 4b87f9c..f2db304 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSuggestionSpec.java
@@ -18,14 +18,22 @@
 
 import android.annotation.SuppressLint;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresFeature;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.annotation.Document;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.SearchSuggestionSpecCreator;
 import androidx.appsearch.util.BundleUtil;
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
@@ -47,24 +55,48 @@
  *
  * @see AppSearchSession#searchSuggestionAsync
  */
-public final class SearchSuggestionSpec {
-    static final String NAMESPACE_FIELD = "namespace";
-    static final String SCHEMA_FIELD = "schema";
-    static final String PROPERTY_FIELD = "property";
-    static final String DOCUMENT_IDS_FIELD = "documentIds";
-    static final String MAXIMUM_RESULT_COUNT_FIELD = "maximumResultCount";
-    static final String RANKING_STRATEGY_FIELD = "rankingStrategy";
-    private final Bundle mBundle;
[email protected](creator = "SearchSuggestionSpecCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class SearchSuggestionSpec extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull public static final Parcelable.Creator<SearchSuggestionSpec> CREATOR =
+            new SearchSuggestionSpecCreator();
+    @Field(id = 1, getter = "getFilterNamespaces")
+    private final List<String> mFilterNamespaces;
+    @Field(id = 2, getter = "getFilterSchemas")
+    private final List<String> mFilterSchemas;
+    // Maps are not supported by SafeParcelable fields, using Bundle instead. Here the key is
+    // schema type and value is a list of target property paths in that schema to search over.
+    @Field(id = 3)
+    final Bundle mFilterProperties;
+    // Maps are not supported by SafeParcelable fields, using Bundle instead. Here the key is
+    // namespace and value is a list of target document ids in that namespace to search over.
+    @Field(id = 4)
+    final Bundle mFilterDocumentIds;
+    @Field(id = 5, getter = "getRankingStrategy")
+    private final int mRankingStrategy;
+    @Field(id = 6, getter = "getMaximumResultCount")
     private final int mMaximumResultCount;
 
     /** @exportToFramework:hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public SearchSuggestionSpec(@NonNull Bundle bundle) {
-        Preconditions.checkNotNull(bundle);
-        mBundle = bundle;
-        mMaximumResultCount = bundle.getInt(MAXIMUM_RESULT_COUNT_FIELD);
-        Preconditions.checkArgument(mMaximumResultCount >= 1,
+    @Constructor
+    public SearchSuggestionSpec(
+            @Param(id = 1) @NonNull List<String> filterNamespaces,
+            @Param(id = 2) @NonNull List<String> filterSchemas,
+            @Param(id = 3) @NonNull Bundle filterProperties,
+            @Param(id = 4) @NonNull Bundle filterDocumentIds,
+            @Param(id = 5) @SuggestionRankingStrategy int rankingStrategy,
+            @Param(id = 6) int maximumResultCount) {
+        Preconditions.checkArgument(maximumResultCount >= 1,
                 "MaximumResultCount must be positive.");
+        mFilterNamespaces = Preconditions.checkNotNull(filterNamespaces);
+        mFilterSchemas = Preconditions.checkNotNull(filterSchemas);
+        mFilterProperties = Preconditions.checkNotNull(filterProperties);
+        mFilterDocumentIds = Preconditions.checkNotNull(filterDocumentIds);
+        mRankingStrategy = rankingStrategy;
+        mMaximumResultCount = maximumResultCount;
     }
 
     /**
@@ -111,17 +143,6 @@
     public static final int SUGGESTION_RANKING_STRATEGY_NONE = 2;
 
     /**
-     * Returns the {@link Bundle} populated by this builder.
-     *
-     * @exportToFramework:hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Bundle getBundle() {
-        return mBundle;
-    }
-
-    /**
      * Returns the maximum number of wanted suggestion that will be returned in the result object.
      */
     public int getMaximumResultCount() {
@@ -135,17 +156,16 @@
      */
     @NonNull
     public List<String> getFilterNamespaces() {
-        List<String> namespaces = mBundle.getStringArrayList(NAMESPACE_FIELD);
-        if (namespaces == null) {
+        if (mFilterNamespaces == null) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(namespaces);
+        return Collections.unmodifiableList(mFilterNamespaces);
     }
 
     /** Returns the ranking strategy. */
     @SuggestionRankingStrategy
     public int getRankingStrategy() {
-        return mBundle.getInt(RANKING_STRATEGY_FIELD);
+        return mRankingStrategy;
     }
 
     /**
@@ -155,11 +175,10 @@
      */
     @NonNull
     public List<String> getFilterSchemas() {
-        List<String> schemaTypes = mBundle.getStringArrayList(SCHEMA_FIELD);
-        if (schemaTypes == null) {
+        if (mFilterSchemas == null) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(schemaTypes);
+        return Collections.unmodifiableList(mFilterSchemas);
     }
 
     /**
@@ -173,20 +192,15 @@
      *
      * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned
      * by this function, rather than calling it multiple times.
-     *
-     * @exportToFramework:hide
      */
-    // TODO(b/228240987) migrate this API when we support property restrict for multiple terms
     @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES)
     public Map<String, List<String>> getFilterProperties() {
-        Bundle typePropertyPathsBundle = Preconditions.checkNotNull(
-                mBundle.getBundle(PROPERTY_FIELD));
-        Set<String> schemas = typePropertyPathsBundle.keySet();
+        Set<String> schemas = mFilterProperties.keySet();
         Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size());
         for (String schema : schemas) {
             typePropertyPathsMap.put(schema, Preconditions.checkNotNull(
-                    typePropertyPathsBundle.getStringArrayList(schema)));
+                    mFilterProperties.getStringArrayList(schema)));
         }
         return typePropertyPathsMap;
     }
@@ -205,13 +219,11 @@
      */
     @NonNull
     public Map<String, List<String>> getFilterDocumentIds() {
-        Bundle documentIdsBundle = Preconditions.checkNotNull(
-                mBundle.getBundle(DOCUMENT_IDS_FIELD));
-        Set<String> namespaces = documentIdsBundle.keySet();
+        Set<String> namespaces = mFilterDocumentIds.keySet();
         Map<String, List<String>> documentIdsMap = new ArrayMap<>(namespaces.size());
         for (String namespace : namespaces) {
             documentIdsMap.put(namespace, Preconditions.checkNotNull(
-                    documentIdsBundle.getStringArrayList(namespace)));
+                    mFilterDocumentIds.getStringArrayList(namespace)));
         }
         return documentIdsMap;
     }
@@ -328,7 +340,7 @@
         @SuppressLint("MissingGetterMatchingBuilder")
         @CanIgnoreReturnValue
         @NonNull
-        public Builder addFilterDocumentClasses(@NonNull Class<?>... documentClasses)
+        public Builder addFilterDocumentClasses(@NonNull java.lang.Class<?>... documentClasses)
                 throws AppSearchException {
             Preconditions.checkNotNull(documentClasses);
             resetIfBuilt();
@@ -353,12 +365,13 @@
         @CanIgnoreReturnValue
         @NonNull
         public Builder addFilterDocumentClasses(
-                @NonNull Collection<? extends Class<?>> documentClasses) throws AppSearchException {
+                @NonNull Collection<? extends java.lang.Class<?>> documentClasses)
+                throws AppSearchException {
             Preconditions.checkNotNull(documentClasses);
             resetIfBuilt();
             List<String> schemas = new ArrayList<>(documentClasses.size());
             DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
-            for (Class<?> documentClass : documentClasses) {
+            for (java.lang.Class<?> documentClass : documentClasses) {
                 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
                 schemas.add(factory.getSchemaName());
             }
@@ -385,11 +398,13 @@
          * @param propertyPaths The String version of {@link PropertyPath}. A dot-delimited
          *                      sequence of property names indicating which property in the
          *                      document these snippets correspond to.
-         * @exportToFramework:hide
          */
-        // TODO(b/228240987) migrate this API when we support property restrict for multiple terms
+        @CanIgnoreReturnValue
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
+        @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES)
         public Builder addFilterProperties(@NonNull String schema,
                 @NonNull Collection<String> propertyPaths) {
             Preconditions.checkNotNull(schema);
@@ -418,12 +433,15 @@
          *
          * @param schema the {@link AppSearchSchema} that contains the target properties
          * @param propertyPaths The {@link PropertyPath} to search suggestion over
-         *
-         * @exportToFramework:hide
          */
-        // TODO(b/228240987) migrate this API when we support property restrict for multiple terms
+        @CanIgnoreReturnValue
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        // Getter method is getFilterProperties
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
+        @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES)
         public Builder addFilterPropertyPaths(@NonNull String schema,
                 @NonNull Collection<PropertyPath> propertyPaths) {
             Preconditions.checkNotNull(schema);
@@ -453,12 +471,12 @@
          * @param propertyPaths The String version of {@link PropertyPath}. A
          * {@code dot-delimited sequence of property names indicating which property in the
          * document these snippets correspond to.
-         * @exportToFramework:hide
          */
-        // TODO(b/228240987) migrate this API when we support property restrict for multiple terms
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        public Builder addFilterProperties(@NonNull Class<?> documentClass,
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
+        public Builder addFilterProperties(@NonNull java.lang.Class<?> documentClass,
                 @NonNull Collection<String> propertyPaths) throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             Preconditions.checkNotNull(propertyPaths);
@@ -484,12 +502,14 @@
          *
          * @param documentClass class annotated with {@link Document}.
          * @param propertyPaths The {@link PropertyPath} to search suggestion over
-         * @exportToFramework:hide
          */
-        // TODO(b/228240987) migrate this API when we support property restrict for multiple terms
         @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        public Builder addFilterPropertyPaths(@NonNull Class<?> documentClass,
+        // Getter method is getFilterProperties
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SEARCH_SPEC_ADD_FILTER_PROPERTIES)
+        public Builder addFilterPropertyPaths(@NonNull java.lang.Class<?> documentClass,
                 @NonNull Collection<PropertyPath> propertyPaths) throws AppSearchException {
             Preconditions.checkNotNull(documentClass);
             Preconditions.checkNotNull(propertyPaths);
@@ -540,7 +560,6 @@
         /** Constructs a new {@link SearchSpec} from the contents of this builder. */
         @NonNull
         public SearchSuggestionSpec build() {
-            Bundle bundle = new Bundle();
             if (!mSchemas.isEmpty()) {
                 Set<String> schemaFilter = new ArraySet<>(mSchemas);
                 for (String schema : mTypePropertyFilters.keySet()) {
@@ -561,14 +580,14 @@
                     }
                 }
             }
-            bundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
-            bundle.putStringArrayList(SCHEMA_FIELD, mSchemas);
-            bundle.putBundle(PROPERTY_FIELD, mTypePropertyFilters);
-            bundle.putBundle(DOCUMENT_IDS_FIELD, mDocumentIds);
-            bundle.putInt(MAXIMUM_RESULT_COUNT_FIELD, mTotalResultCount);
-            bundle.putInt(RANKING_STRATEGY_FIELD, mRankingStrategy);
             mBuilt = true;
-            return new SearchSuggestionSpec(bundle);
+            return new SearchSuggestionSpec(
+                    mNamespaces,
+                    mSchemas,
+                    mTypePropertyFilters,
+                    mDocumentIds,
+                    mRankingStrategy,
+                    mTotalResultCount);
         }
 
         private void resetIfBuilt() {
@@ -581,4 +600,11 @@
             }
         }
     }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        SearchSuggestionSpecCreator.writeToParcel(this, dest, flags);
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
index 6cc0e85..3cabaab 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaRequest.java
@@ -21,10 +21,13 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresFeature;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
@@ -100,13 +103,13 @@
             READ_EXTERNAL_STORAGE,
             READ_HOME_APP_SEARCH_DATA,
             READ_ASSISTANT_APP_SEARCH_DATA,
+            ENTERPRISE_ACCESS,
+            MANAGED_PROFILE_CONTACTS_ACCESS,
     })
     @Retention(RetentionPolicy.SOURCE)
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public @interface AppSearchSupportedPermission {}
 
@@ -114,72 +117,85 @@
      * The {@link android.Manifest.permission#READ_SMS} AppSearch supported in
      * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility}
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     public static final int READ_SMS = 1;
 
     /**
      * The {@link android.Manifest.permission#READ_CALENDAR} AppSearch supported in
      * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility}
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     public static final int READ_CALENDAR = 2;
 
     /**
      * The {@link android.Manifest.permission#READ_CONTACTS} AppSearch supported in
      * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility}
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     public static final int READ_CONTACTS = 3;
 
     /**
      * The {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} AppSearch supported in
      * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility}
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     public static final int READ_EXTERNAL_STORAGE = 4;
 
     /**
      * The {@link android.Manifest.permission#READ_HOME_APP_SEARCH_DATA} AppSearch supported in
      * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility}
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     public static final int READ_HOME_APP_SEARCH_DATA = 5;
 
     /**
      * The {@link android.Manifest.permission#READ_ASSISTANT_APP_SEARCH_DATA} AppSearch supported in
      * {@link SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility}
      */
-    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-    // @exportToFramework:endStrip()
     public static final int READ_ASSISTANT_APP_SEARCH_DATA = 6;
 
+    /**
+     * A schema must have this permission set through {@link
+     * SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} to be visible to an
+     * {@link EnterpriseGlobalSearchSession}. A call from a regular {@link GlobalSearchSession} will
+     * not count as having this permission.
+     *
+     * @exportToFramework:hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final int ENTERPRISE_ACCESS = 7;
+
+    /**
+     * A schema with this permission set through {@link
+     * SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility} requires the caller
+     * to have managed profile contacts access from {@link android.app.admin.DevicePolicyManager} to
+     * be visible. This permission indicates that the protected schema may expose managed profile
+     * data for contacts search.
+     *
+     * @exportToFramework:hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final int MANAGED_PROFILE_CONTACTS_ACCESS = 8;
+
     private final Set<AppSearchSchema> mSchemas;
     private final Set<String> mSchemasNotDisplayedBySystem;
     private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
     private final Map<String, Set<Set<Integer>>> mSchemasVisibleToPermissions;
+    private final Map<String, PackageIdentifier> mPubliclyVisibleSchemas;
+    private final Map<String, Set<SchemaVisibilityConfig>> mSchemasVisibleToConfigs;
     private final Map<String, Migrator> mMigrators;
     private final boolean mForceOverride;
     private final int mVersion;
@@ -188,6 +204,8 @@
             @NonNull Set<String> schemasNotDisplayedBySystem,
             @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
             @NonNull Map<String, Set<Set<Integer>>> schemasVisibleToPermissions,
+            @NonNull Map<String, PackageIdentifier> publiclyVisibleSchemas,
+            @NonNull Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs,
             @NonNull Map<String, Migrator> migrators,
             boolean forceOverride,
             int version) {
@@ -195,6 +213,8 @@
         mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem);
         mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages);
         mSchemasVisibleToPermissions = Preconditions.checkNotNull(schemasVisibleToPermissions);
+        mPubliclyVisibleSchemas = Preconditions.checkNotNull(publiclyVisibleSchemas);
+        mSchemasVisibleToConfigs = Preconditions.checkNotNull(schemasVisibleToConfigs);
         mMigrators = Preconditions.checkNotNull(migrators);
         mForceOverride = forceOverride;
         mVersion = version;
@@ -258,12 +278,41 @@
      *         {@link SetSchemaRequest#READ_HOME_APP_SEARCH_DATA} and
      *         {@link SetSchemaRequest#READ_ASSISTANT_APP_SEARCH_DATA}.
      */
+    // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
     @NonNull
     public Map<String, Set<Set<Integer>>> getRequiredPermissionsForSchemaTypeVisibility() {
         return deepCopy(mSchemasVisibleToPermissions);
     }
 
     /**
+     * Returns a mapping of publicly visible schemas to the {@link PackageIdentifier} specifying
+     * the package the schemas are from.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
+    @NonNull
+    public Map<String, PackageIdentifier> getPubliclyVisibleSchemas() {
+        return Collections.unmodifiableMap(mPubliclyVisibleSchemas);
+    }
+
+    /**
+     * Returns a mapping of schema types to the set of {@link SchemaVisibilityConfig} that have
+     * access to that schema type.
+     *
+     * <p>It’s inefficient to call this method repeatedly.
+     * @see SetSchemaRequest.Builder#addSchemaTypeVisibleToConfig
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+    @NonNull
+    public Map<String, Set<SchemaVisibilityConfig>> getSchemasVisibleToConfigs() {
+        Map<String, Set<SchemaVisibilityConfig>> copy = new ArrayMap<>();
+        for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry :
+                mSchemasVisibleToConfigs.entrySet()) {
+            copy.put(entry.getKey(), new ArraySet<>(entry.getValue()));
+        }
+        return copy;
+    }
+
+    /**
      * Returns the map of {@link Migrator}, the key will be the schema type of the
      * {@link Migrator} associated with.
      */
@@ -307,6 +356,9 @@
         private ArrayMap<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
                 new ArrayMap<>();
         private ArrayMap<String, Set<Set<Integer>>> mSchemasVisibleToPermissions = new ArrayMap<>();
+        private ArrayMap<String, PackageIdentifier> mPubliclyVisibleSchemas = new ArrayMap<>();
+        private ArrayMap<String, Set<SchemaVisibilityConfig>> mSchemaVisibleToConfigs =
+                new ArrayMap<>();
         private ArrayMap<String, Migrator> mMigrators = new ArrayMap<>();
         private boolean mForceOverride = false;
         private int mVersion = DEFAULT_VERSION;
@@ -451,10 +503,16 @@
          * <p> You can call this method to add multiple permission combinations, and the querier
          * will have access if they holds ANY of the combinations.
          *
-         * <p>The supported Permissions are {@link #READ_SMS}, {@link #READ_CALENDAR},
+         * <p> The supported Permissions are {@link #READ_SMS}, {@link #READ_CALENDAR},
          * {@link #READ_CONTACTS}, {@link #READ_EXTERNAL_STORAGE},
          * {@link #READ_HOME_APP_SEARCH_DATA} and {@link #READ_ASSISTANT_APP_SEARCH_DATA}.
          *
+         * <p> The relationship between permissions added in this method and package visibility
+         * setting {@link #setSchemaTypeVisibilityForPackage} is "OR". The caller could access
+         * the schema if they match ANY requirements. If you want to set "AND" requirements like
+         * a caller must hold required permissions AND it is a specified package, please use
+         * {@link #addSchemaTypeVisibleToConfig}.
+         *
          * @see android.Manifest.permission#READ_SMS
          * @see android.Manifest.permission#READ_CALENDAR
          * @see android.Manifest.permission#READ_CONTACTS
@@ -467,14 +525,13 @@
          *                         schema.
          * @throws IllegalArgumentException – if input unsupported permission.
          */
+        // TODO(b/237388235): add enterprise permissions to javadocs after they're unhidden
         // Merged list available from getRequiredPermissionsForSchemaTypeVisibility
         @CanIgnoreReturnValue
         @SuppressLint("MissingGetterMatchingBuilder")
-        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-        // @exportToFramework:endStrip()
         @NonNull
         public Builder addRequiredPermissionsForSchemaTypeVisibility(@NonNull String schemaType,
                 @AppSearchSupportedPermission @NonNull Set<Integer> permissions) {
@@ -482,7 +539,7 @@
             Preconditions.checkNotNull(permissions);
             for (int permission : permissions) {
                 Preconditions.checkArgumentInRange(permission, READ_SMS,
-                        READ_ASSISTANT_APP_SEARCH_DATA, "permission");
+                        MANAGED_PROFILE_CONTACTS_ACCESS, "permission");
             }
             resetIfBuilt();
             Set<Set<Integer>> visibleToPermissions = mSchemasVisibleToPermissions.get(schemaType);
@@ -495,12 +552,10 @@
         }
 
         /**  Clears all required permissions combinations for the given schema type.  */
-        // @exportToFramework:startStrip()
         @CanIgnoreReturnValue
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.ADD_PERMISSIONS_AND_GET_VISIBILITY)
-        // @exportToFramework:endStrip()
         @NonNull
         public Builder clearRequiredPermissionsForSchemaTypeVisibility(@NonNull String schemaType) {
             Preconditions.checkNotNull(schemaType);
@@ -525,6 +580,12 @@
          *
          * <p>By default, data sharing between applications is disabled.
          *
+         * <p> The relationship between permissions added in this method and package visibility
+         * setting {@link #setSchemaTypeVisibilityForPackage} is "OR". The caller could access
+         * the schema if they match ANY requirements. If you want to set "AND" requirements like
+         * a caller must hold required permissions AND it is a specified package, please use
+         * {@link #addSchemaTypeVisibleToConfig}.
+         *
          * @param schemaType        The schema type to set visibility on.
          * @param visible           Whether the {@code schemaType} will be visible or not.
          * @param packageIdentifier Represents the package that will be granted visibility.
@@ -564,6 +625,132 @@
         }
 
         /**
+         * Specify that the schema should be publicly available, to packages which already have
+         * visibility to {@code packageIdentifier}. This visibility is determined by the result of
+         * {@link android.content.pm.PackageManager#canPackageQuery}.
+         *
+         * <p> It is possible for the packageIdentifier parameter to be different from the
+         * package performing the indexing. This might happen in the case of an on-device indexer
+         * processing information about various packages. The visibility will be the same
+         * regardless of which package indexes the document, as the visibility is based on the
+         * packageIdentifier parameter.
+         *
+         * <p> If this is called repeatedly with the same schema, the {@link PackageIdentifier} in
+         * the last call will be used as the "from" package for that schema.
+         *
+         * <p> Calling this with packageIdentifier set to null is valid, and will remove public
+         * visibility for the schema.
+         *
+         * @param schema the schema to make publicly accessible.
+         * @param packageIdentifier if an app can see this package via
+         *                          PackageManager#canPackageQuery, it will be able to see the
+         *                          documents of type {@code schema}.
+         */
+        // Merged list available from getPubliclyVisibleSchemas
+        @CanIgnoreReturnValue
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE)
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
+        @NonNull
+        public Builder setPubliclyVisibleSchema(@NonNull String schema,
+                @Nullable PackageIdentifier packageIdentifier) {
+            Preconditions.checkNotNull(schema);
+            resetIfBuilt();
+
+            // If the package identifier is null or empty we clear public visibility
+            if (packageIdentifier == null || packageIdentifier.getPackageName().isEmpty()) {
+                mPubliclyVisibleSchemas.remove(schema);
+                return this;
+            }
+
+            mPubliclyVisibleSchemas.put(schema, packageIdentifier);
+            return this;
+        }
+
+// @exportToFramework:startStrip()
+        /**
+         * Specify that the schema should be publicly available, to packages which already have
+         * visibility to {@code packageIdentifier}.
+         *
+         * @param documentClass the document to make publicly accessible.
+         * @param packageIdentifier if an app can see this package via
+         *                          PackageManager#canPackageQuery, it will be able to see the
+         *                          documents of type {@code documentClass}.
+         * @see SetSchemaRequest.Builder#setPubliclyVisibleSchema
+         */
+        // Merged list available from getPubliclyVisibleSchemas
+        @CanIgnoreReturnValue
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SET_SCHEMA_REQUEST_SET_PUBLICLY_VISIBLE)
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA)
+        @NonNull
+        public Builder setPubliclyVisibleDocumentClass(@NonNull Class<?> documentClass,
+                @Nullable PackageIdentifier packageIdentifier) throws AppSearchException {
+            Preconditions.checkNotNull(documentClass);
+            resetIfBuilt();
+            DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
+            DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
+            return setPubliclyVisibleSchema(factory.getSchemaName(), packageIdentifier);
+        }
+// @exportToFramework:endStrip()
+
+        /**
+         * Sets the documents from the provided {@code schemaType} can be read by the caller if they
+         * match the ALL visibility requirements set in {@link SchemaVisibilityConfig}.
+         *
+         * <p> The requirements in a {@link SchemaVisibilityConfig} is "AND" relationship. A
+         * caller must match ALL requirements to access the schema. For example, a caller must hold
+         * required permissions AND it is a specified package.
+         *
+         * <p> You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig}s,
+         * and the querier will have access if they match ANY of the
+         * {@link SchemaVisibilityConfig}.
+         *
+         * @param schemaType              The schema type to set visibility on.
+         * @param schemaVisibilityConfig  The {@link SchemaVisibilityConfig} holds all requirements
+         *                                that a call must to match to access the schema.
+         */
+        // Merged list available from getSchemasVisibleToConfigs
+        @CanIgnoreReturnValue
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG)
+        @NonNull
+        public Builder addSchemaTypeVisibleToConfig(@NonNull String schemaType,
+                @NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(schemaVisibilityConfig);
+            resetIfBuilt();
+            Set<SchemaVisibilityConfig> visibleToConfigs = mSchemaVisibleToConfigs.get(schemaType);
+            if (visibleToConfigs == null) {
+                visibleToConfigs = new ArraySet<>();
+                mSchemaVisibleToConfigs.put(schemaType, visibleToConfigs);
+            }
+            visibleToConfigs.add(schemaVisibilityConfig);
+            return this;
+        }
+
+        /**  Clears all visible to {@link SchemaVisibilityConfig} for the given schema type. */
+        @CanIgnoreReturnValue
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG)
+        @NonNull
+        public Builder clearSchemaTypeVisibleToConfigs(@NonNull String schemaType) {
+            Preconditions.checkNotNull(schemaType);
+            resetIfBuilt();
+            mSchemaVisibleToConfigs.remove(schemaType);
+            return this;
+        }
+
+        /**
          * Sets the {@link Migrator} associated with the given SchemaType.
          *
          * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
@@ -684,6 +871,12 @@
          *
          * <p>By default, app data sharing between applications is disabled.
          *
+         * <p> The relationship between visible packages added in this method and permission
+         * visibility setting {@link #addRequiredPermissionsForSchemaTypeVisibility} is "OR". The
+         * caller could access the schema if they match ANY requirements. If you want to set
+         * "AND" requirements like a caller must hold required permissions AND it is a specified
+         * package, please use {@link #addSchemaTypeVisibleToConfig}.
+         *
          * <p>Merged list available from {@link #getSchemasVisibleToPackages()}.
          *
          * @param documentClass     The {@link androidx.appsearch.annotation.Document} class to set
@@ -721,6 +914,12 @@
          * {@link #READ_CONTACTS}, {@link #READ_EXTERNAL_STORAGE},
          * {@link #READ_HOME_APP_SEARCH_DATA} and {@link #READ_ASSISTANT_APP_SEARCH_DATA}.
          *
+         * <p> The relationship between visible packages added in this method and permission
+         * visibility setting {@link #addRequiredPermissionsForSchemaTypeVisibility} is "OR". The
+         * caller could access the schema if they match ANY requirements. If you want to set
+         * "AND" requirements like a caller must hold required permissions AND it is a specified
+         * package, please use {@link #addSchemaTypeVisibleToConfig}.
+         *
          * <p>Merged map available from {@link #getRequiredPermissionsForSchemaTypeVisibility()}.
          * @see android.Manifest.permission#READ_SMS
          * @see android.Manifest.permission#READ_CALENDAR
@@ -768,6 +967,58 @@
             DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
             return clearRequiredPermissionsForSchemaTypeVisibility(factory.getSchemaName());
         }
+
+        /**
+         * Sets the documents from the provided {@code schemaType} can be read by the caller if they
+         * match the ALL visibility requirements set in {@link SchemaVisibilityConfig}.
+         *
+         * <p> The requirements in a {@link SchemaVisibilityConfig} is "AND" relationship. A
+         * caller must match ALL requirements to access the schema. For example, a caller must hold
+         * required permissions AND it is a specified package.
+         *
+         * <p> You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig}s,
+         * and the querier will have access if they match ANY of the {@link SchemaVisibilityConfig}.
+         *
+         * @param documentClass            A class annotated with
+         *                                 {@link androidx.appsearch.annotation.Document}, the
+         *                                 visibility of which will be configured
+         * @param schemaVisibilityConfig   The {@link SchemaVisibilityConfig} holds all
+         *                                 requirements that a call must to match to access the
+         *                                 schema.
+         */
+        // Merged list available from getSchemasVisibleToConfigs
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG)
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+        @NonNull
+        public Builder addDocumentClassVisibleToConfig(
+                @NonNull Class<?> documentClass,
+                @NonNull SchemaVisibilityConfig schemaVisibilityConfig)
+                throws AppSearchException {
+            Preconditions.checkNotNull(documentClass);
+            resetIfBuilt();
+            DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
+            DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
+            return addSchemaTypeVisibleToConfig(factory.getSchemaName(),
+                    schemaVisibilityConfig);
+        }
+
+        /**  Clears all visible to {@link SchemaVisibilityConfig} for the given schema type. */
+        @RequiresFeature(
+                enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+                name = Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG)
+        @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
+        @NonNull
+        public Builder clearDocumentClassVisibleToConfigs(
+                @NonNull Class<?> documentClass) throws AppSearchException {
+            Preconditions.checkNotNull(documentClass);
+            resetIfBuilt();
+            DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
+            DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
+            return clearSchemaTypeVisibleToConfigs(factory.getSchemaName());
+        }
 // @exportToFramework:endStrip()
 
         /**
@@ -841,6 +1092,8 @@
             Set<String> referencedSchemas = new ArraySet<>(mSchemasNotDisplayedBySystem);
             referencedSchemas.addAll(mSchemasVisibleToPackages.keySet());
             referencedSchemas.addAll(mSchemasVisibleToPermissions.keySet());
+            referencedSchemas.addAll(mPubliclyVisibleSchemas.keySet());
+            referencedSchemas.addAll(mSchemaVisibleToConfigs.keySet());
 
             for (AppSearchSchema schema : mSchemas) {
                 referencedSchemas.remove(schema.getSchemaType());
@@ -861,6 +1114,8 @@
                     mSchemasNotDisplayedBySystem,
                     mSchemasVisibleToPackages,
                     mSchemasVisibleToPermissions,
+                    mPubliclyVisibleSchemas,
+                    mSchemaVisibleToConfigs,
                     mMigrators,
                     mForceOverride,
                     mVersion);
@@ -876,8 +1131,18 @@
                 }
                 mSchemasVisibleToPackages = schemasVisibleToPackages;
 
+                mPubliclyVisibleSchemas = new ArrayMap<>(mPubliclyVisibleSchemas);
+
                 mSchemasVisibleToPermissions = deepCopy(mSchemasVisibleToPermissions);
 
+                ArrayMap<String, Set<SchemaVisibilityConfig>> schemaVisibleToConfigs =
+                        new ArrayMap<>(mSchemaVisibleToConfigs.size());
+                for (Map.Entry<String, Set<SchemaVisibilityConfig>> entry :
+                        mSchemaVisibleToConfigs.entrySet()) {
+                    schemaVisibleToConfigs.put(entry.getKey(), new ArraySet<>(entry.getValue()));
+                }
+                mSchemaVisibleToConfigs = schemaVisibleToConfigs;
+
                 mSchemas = new ArraySet<>(mSchemas);
                 mSchemasNotDisplayedBySystem = new ArraySet<>(mSchemasNotDisplayedBySystem);
                 mMigrators = new ArrayMap<>(mMigrators);
@@ -886,8 +1151,8 @@
         }
     }
 
-    static ArrayMap<String, Set<Set<Integer>>> deepCopy(@NonNull Map<String,
-            Set<Set<Integer>>> original) {
+    private static ArrayMap<String, Set<Set<Integer>>> deepCopy(
+            @NonNull Map<String, Set<Set<Integer>>> original) {
         ArrayMap<String, Set<Set<Integer>>> copy = new ArrayMap<>(original.size());
         for (Map.Entry<String, Set<Set<Integer>>> entry : original.entrySet()) {
             Set<Set<Integer>> valueCopy = new ArraySet<>();
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java
index 07d087a..85866fd 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SetSchemaResponse.java
@@ -16,12 +16,19 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.MigrationFailureCreator;
+import androidx.appsearch.safeparcel.stub.StubCreators.SetSchemaResponseCreator;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
@@ -32,57 +39,70 @@
 import java.util.Set;
 
 /** The response class of {@link AppSearchSession#setSchemaAsync} */
-public class SetSchemaResponse {
[email protected](creator = "SetSchemaResponseCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class SetSchemaResponse extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull public static final Parcelable.Creator<SetSchemaResponse> CREATOR =
+            new SetSchemaResponseCreator();
 
-    private static final String DELETED_TYPES_FIELD = "deletedTypes";
-    private static final String INCOMPATIBLE_TYPES_FIELD = "incompatibleTypes";
-    private static final String MIGRATED_TYPES_FIELD = "migratedTypes";
+    @Field(id = 1)
+    final List<String> mDeletedTypes;
+    @Field(id = 2)
+    final List<String> mIncompatibleTypes;
+    @Field(id = 3)
+    final List<String> mMigratedTypes;
 
-    private final Bundle mBundle;
     /**
-     * The migrationFailures won't be saved in the bundle. Since:
+     * The migrationFailures won't be saved as a SafeParcelable field. Since:
      * <ul>
      *     <li>{@link MigrationFailure} is generated in {@link AppSearchSession} which will be
-     *         the SDK side in platform. We don't need to pass it from service side via binder.
-     *     <li>Translate multiple {@link MigrationFailure}s to bundles in {@link Builder} and then
-     *         back in constructor will be a huge waste.
+     *         the SDK side in platform. We don't need to pass it from service side via binder as
+     *         a part of {@link SetSchemaResponse}.
+     *     <li>Writing multiple {@link MigrationFailure}s to SafeParcelable in {@link Builder} and
+     *     then back in constructor will be a huge waste.
      * </ul>
      */
     private final List<MigrationFailure> mMigrationFailures;
 
-    /** Cache of the inflated deleted schema types. Comes from inflating mBundles at first use. */
-    @Nullable
-    private Set<String> mDeletedTypes;
+    /** Cache of the inflated deleted schema types. Comes from inflating mDeletedTypes at first use
+     */
+    @Nullable private Set<String> mDeletedTypesCached;
 
-    /** Cache of the inflated migrated schema types. Comes from inflating mBundles at first use. */
-    @Nullable
-    private Set<String> mMigratedTypes;
+    /** Cache of the inflated migrated schema types. Comes from inflating mMigratedTypes at first
+     *  use.
+     */
+    @Nullable private Set<String> mMigratedTypesCached;
 
     /**
-     * Cache of the inflated incompatible schema types. Comes from inflating mBundles at first use.
+     * Cache of the inflated incompatible schema types. Comes from inflating mIncompatibleTypes at
+     * first use.
      */
-    @Nullable
-    private Set<String> mIncompatibleTypes;
+    @Nullable private Set<String> mIncompatibleTypesCached;
 
-    SetSchemaResponse(@NonNull Bundle bundle, @NonNull List<MigrationFailure> migrationFailures) {
-        mBundle = Preconditions.checkNotNull(bundle);
+    @Constructor
+    SetSchemaResponse(
+            @Param(id = 1) @NonNull List<String> deletedTypes,
+            @Param(id = 2) @NonNull List<String> incompatibleTypes,
+            @Param(id = 3) @NonNull List<String> migratedTypes) {
+        mDeletedTypes = deletedTypes;
+        mIncompatibleTypes = incompatibleTypes;
+        mMigratedTypes = migratedTypes;
+        mMigrationFailures = Collections.emptyList();
+    }
+
+    SetSchemaResponse(
+            @NonNull List<String> deletedTypes,
+            @NonNull List<String> incompatibleTypes,
+            @NonNull List<String> migratedTypes,
+            @NonNull List<MigrationFailure> migrationFailures) {
+        mDeletedTypes = deletedTypes;
+        mIncompatibleTypes = incompatibleTypes;
+        mMigratedTypes = migratedTypes;
         mMigrationFailures = Preconditions.checkNotNull(migrationFailures);
     }
 
-    SetSchemaResponse(@NonNull Bundle bundle) {
-        this(bundle, /*migrationFailures=*/ Collections.emptyList());
-    }
-
-    /**
-     * Returns the {@link Bundle} populated by this builder.
-     * @exportToFramework:hide
-     */
-    @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public Bundle getBundle() {
-        return mBundle;
-    }
-
     /**
      * Returns a {@link List} of all failed {@link MigrationFailure}.
      *
@@ -109,11 +129,10 @@
      */
     @NonNull
     public Set<String> getDeletedTypes() {
-        if (mDeletedTypes == null) {
-            mDeletedTypes = new ArraySet<>(
-                    Preconditions.checkNotNull(mBundle.getStringArrayList(DELETED_TYPES_FIELD)));
+        if (mDeletedTypesCached == null) {
+            mDeletedTypesCached = new ArraySet<>(Preconditions.checkNotNull(mDeletedTypes));
         }
-        return Collections.unmodifiableSet(mDeletedTypes);
+        return Collections.unmodifiableSet(mDeletedTypesCached);
     }
 
     /**
@@ -131,11 +150,10 @@
      */
     @NonNull
     public Set<String> getMigratedTypes() {
-        if (mMigratedTypes == null) {
-            mMigratedTypes = new ArraySet<>(
-                    Preconditions.checkNotNull(mBundle.getStringArrayList(MIGRATED_TYPES_FIELD)));
+        if (mMigratedTypesCached == null) {
+            mMigratedTypesCached = new ArraySet<>(Preconditions.checkNotNull(mMigratedTypes));
         }
-        return Collections.unmodifiableSet(mMigratedTypes);
+        return Collections.unmodifiableSet(mMigratedTypesCached);
     }
 
     /**
@@ -151,27 +169,11 @@
      */
     @NonNull
     public Set<String> getIncompatibleTypes() {
-        if (mIncompatibleTypes == null) {
-            mIncompatibleTypes = new ArraySet<>(
-                    Preconditions.checkNotNull(
-                            mBundle.getStringArrayList(INCOMPATIBLE_TYPES_FIELD)));
+        if (mIncompatibleTypesCached == null) {
+            mIncompatibleTypesCached =
+                    new ArraySet<>(Preconditions.checkNotNull(mIncompatibleTypes));
         }
-        return Collections.unmodifiableSet(mIncompatibleTypes);
-    }
-
-    /**
-     * Translates the {@link SetSchemaResponse}'s bundle to {@link Builder}.
-     * @exportToFramework:hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    // TODO(b/179302942) change to Builder(mBundle) powered by mBundle.deepCopy
-    public Builder toBuilder() {
-        return new Builder()
-                .addDeletedTypes(getDeletedTypes())
-                .addIncompatibleTypes(getIncompatibleTypes())
-                .addMigratedTypes(getMigratedTypes())
-                .addMigrationFailures(mMigrationFailures);
+        return Collections.unmodifiableSet(mIncompatibleTypesCached);
     }
 
     /** Builder for {@link SetSchemaResponse} objects. */
@@ -182,6 +184,23 @@
         private ArrayList<String> mIncompatibleTypes = new ArrayList<>();
         private boolean mBuilt = false;
 
+        /**
+         * Creates a new {@link SetSchemaResponse.Builder} from the given SetSchemaResponse.
+         *
+         * @exportToFramework:hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public Builder(@NonNull SetSchemaResponse setSchemaResponse) {
+            Preconditions.checkNotNull(setSchemaResponse);
+            mDeletedTypes.addAll(setSchemaResponse.getDeletedTypes());
+            mIncompatibleTypes.addAll(setSchemaResponse.getIncompatibleTypes());
+            mMigratedTypes.addAll(setSchemaResponse.getMigratedTypes());
+            mMigrationFailures.addAll(setSchemaResponse.getMigrationFailures());
+        }
+
+        /** Create a {@link Builder} object} */
+        public Builder() {}
+
         /**  Adds {@link MigrationFailure}s to the list of migration failures. */
         @CanIgnoreReturnValue
         @NonNull
@@ -266,15 +285,15 @@
         /** Builds a {@link SetSchemaResponse} object. */
         @NonNull
         public SetSchemaResponse build() {
-            Bundle bundle = new Bundle();
-            bundle.putStringArrayList(INCOMPATIBLE_TYPES_FIELD, mIncompatibleTypes);
-            bundle.putStringArrayList(DELETED_TYPES_FIELD, mDeletedTypes);
-            bundle.putStringArrayList(MIGRATED_TYPES_FIELD, mMigratedTypes);
             mBuilt = true;
             // Avoid converting the potential thousands of MigrationFailures to Pracelable and
             // back just for put in bundle. In platform, we should set MigrationFailures in
             // AppSearchSession after we pass SetSchemaResponse via binder.
-            return new SetSchemaResponse(bundle, mMigrationFailures);
+            return new SetSchemaResponse(
+                    mDeletedTypes,
+                    mIncompatibleTypes,
+                    mMigratedTypes,
+                    mMigrationFailures);
         }
 
         private void resetIfBuilt() {
@@ -288,18 +307,50 @@
         }
     }
 
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        SetSchemaResponseCreator.writeToParcel(this, dest, flags);
+    }
+
     /**
      * The class represents a post-migrated {@link GenericDocument} that failed to be saved by
      * {@link AppSearchSession#setSchemaAsync}.
      */
-    public static class MigrationFailure {
-        private static final String SCHEMA_TYPE_FIELD = "schemaType";
-        private static final String NAMESPACE_FIELD = "namespace";
-        private static final String DOCUMENT_ID_FIELD = "id";
-        private static final String ERROR_MESSAGE_FIELD = "errorMessage";
-        private static final String RESULT_CODE_FIELD = "resultCode";
+    @SafeParcelable.Class(creator = "MigrationFailureCreator")
+    @SuppressWarnings("HiddenSuperclass")
+    public static class MigrationFailure extends AbstractSafeParcelable {
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+        @NonNull
+        public static final Parcelable.Creator<MigrationFailure> CREATOR =
+                new MigrationFailureCreator();
 
-        private final Bundle mBundle;
+        @Field(id = 1, getter = "getNamespace")
+        private final String mNamespace;
+        @Field(id = 2, getter = "getDocumentId")
+        private final String mDocumentId;
+        @Field(id = 3, getter = "getSchemaType")
+        private final String mSchemaType;
+        @Field(id = 4)
+        @Nullable final String mErrorMessage;
+        @Field(id = 5)
+        final int mResultCode;
+
+        @Constructor
+        MigrationFailure(
+                @Param(id = 1) @NonNull String namespace,
+                @Param(id = 2) @NonNull String documentId,
+                @Param(id = 3) @NonNull String schemaType,
+                @Param(id = 4) @Nullable String errorMessage,
+                @Param(id = 5) int resultCode) {
+            mNamespace = namespace;
+            mDocumentId = documentId;
+            mSchemaType = schemaType;
+            mErrorMessage = errorMessage;
+            mResultCode = resultCode;
+        }
 
         /**
          * Constructs a new {@link MigrationFailure}.
@@ -315,49 +366,33 @@
                 @NonNull String documentId,
                 @NonNull String schemaType,
                 @NonNull AppSearchResult<?> failedResult) {
-            mBundle = new Bundle();
-            mBundle.putString(NAMESPACE_FIELD, Preconditions.checkNotNull(namespace));
-            mBundle.putString(DOCUMENT_ID_FIELD, Preconditions.checkNotNull(documentId));
-            mBundle.putString(SCHEMA_TYPE_FIELD, Preconditions.checkNotNull(schemaType));
+            mNamespace = namespace;
+            mDocumentId = documentId;
+            mSchemaType = schemaType;
 
             Preconditions.checkNotNull(failedResult);
             Preconditions.checkArgument(
                     !failedResult.isSuccess(), "failedResult was actually successful");
-            mBundle.putString(ERROR_MESSAGE_FIELD, failedResult.getErrorMessage());
-            mBundle.putInt(RESULT_CODE_FIELD, failedResult.getResultCode());
-        }
-
-        MigrationFailure(@NonNull Bundle bundle) {
-            mBundle = Preconditions.checkNotNull(bundle);
-        }
-
-        /**
-         * Returns the Bundle of the {@link MigrationFailure}.
-         *
-         * @exportToFramework:hide
-         */
-        @NonNull
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        public Bundle getBundle() {
-            return mBundle;
+            mErrorMessage = failedResult.getErrorMessage();
+            mResultCode = failedResult.getResultCode();
         }
 
         /** Returns the namespace of the {@link GenericDocument} that failed to be migrated. */
         @NonNull
         public String getNamespace() {
-            return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/"");
+            return mNamespace;
         }
 
         /** Returns the id of the {@link GenericDocument} that failed to be migrated. */
         @NonNull
         public String getDocumentId() {
-            return mBundle.getString(DOCUMENT_ID_FIELD, /*defaultValue=*/"");
+            return mDocumentId;
         }
 
         /** Returns the schema type of the {@link GenericDocument} that failed to be migrated. */
         @NonNull
         public String getSchemaType() {
-            return mBundle.getString(SCHEMA_TYPE_FIELD, /*defaultValue=*/"");
+            return mSchemaType;
         }
 
         /**
@@ -366,8 +401,7 @@
          */
         @NonNull
         public AppSearchResult<Void> getAppSearchResult() {
-            return AppSearchResult.newFailedResult(mBundle.getInt(RESULT_CODE_FIELD),
-                    mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/""));
+            return AppSearchResult.newFailedResult(mResultCode, mErrorMessage);
         }
 
         @NonNull
@@ -377,5 +411,12 @@
                     + getNamespace() + ", documentId: " + getDocumentId() + ", appSearchResult: "
                     + getAppSearchResult().toString() + "}";
         }
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            MigrationFailureCreator.writeToParcel(this, dest, flags);
+        }
     }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java
index d95eff6..fd22846 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/StorageInfo.java
@@ -16,39 +16,49 @@
 
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
-import androidx.core.util.Preconditions;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.StorageInfoCreator;
 
 /** The response class of {@code AppSearchSession#getStorageInfo}. */
-public class StorageInfo {
-
-    private static final String SIZE_BYTES_FIELD = "sizeBytes";
-    private static final String ALIVE_DOCUMENTS_COUNT = "aliveDocumentsCount";
-    private static final String ALIVE_NAMESPACES_COUNT = "aliveNamespacesCount";
-
-    private final Bundle mBundle;
-
-    StorageInfo(@NonNull Bundle bundle) {
-        mBundle = Preconditions.checkNotNull(bundle);
-    }
-
-    /**
-     * Returns the {@link Bundle} populated by this builder.
-     * @exportToFramework:hide
-     */
-    @NonNull
[email protected](creator = "StorageInfoCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class StorageInfo extends AbstractSafeParcelable {
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public Bundle getBundle() {
-        return mBundle;
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull
+    public static final Parcelable.Creator<StorageInfo> CREATOR = new StorageInfoCreator();
+
+    @Field(id = 1, getter = "getSizeBytes")
+    private long mSizeBytes;
+
+    @Field(id = 2, getter = "getAliveDocumentsCount")
+    private int mAliveDocumentsCount;
+
+    @Field(id = 3, getter = "getAliveNamespacesCount")
+    private int mAliveNamespacesCount;
+
+    @Constructor
+    StorageInfo(
+            @Param(id = 1) long sizeBytes,
+            @Param(id = 2) int aliveDocumentsCount,
+            @Param(id = 3) int aliveNamespacesCount) {
+        mSizeBytes = sizeBytes;
+        mAliveDocumentsCount = aliveDocumentsCount;
+        mAliveNamespacesCount = aliveNamespacesCount;
     }
 
     /** Returns the estimated size of the session's database in bytes. */
     public long getSizeBytes() {
-        return mBundle.getLong(SIZE_BYTES_FIELD);
+        return mSizeBytes;
     }
 
     /**
@@ -58,7 +68,7 @@
      * set in {@link GenericDocument.Builder#setTtlMillis}.
      */
     public int getAliveDocumentsCount() {
-        return mBundle.getInt(ALIVE_DOCUMENTS_COUNT);
+        return mAliveDocumentsCount;
     }
 
     /**
@@ -69,7 +79,7 @@
      * set in {@link GenericDocument.Builder#setTtlMillis}.
      */
     public int getAliveNamespacesCount() {
-        return mBundle.getInt(ALIVE_NAMESPACES_COUNT);
+        return mAliveNamespacesCount;
     }
 
     /** Builder for {@link StorageInfo} objects. */
@@ -105,11 +115,14 @@
         /** Builds a {@link StorageInfo} object. */
         @NonNull
         public StorageInfo build() {
-            Bundle bundle = new Bundle();
-            bundle.putLong(SIZE_BYTES_FIELD, mSizeBytes);
-            bundle.putInt(ALIVE_DOCUMENTS_COUNT, mAliveDocumentsCount);
-            bundle.putInt(ALIVE_NAMESPACES_COUNT, mAliveNamespacesCount);
-            return new StorageInfo(bundle);
+            return new StorageInfo(mSizeBytes, mAliveDocumentsCount, mAliveNamespacesCount);
         }
     }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        StorageInfoCreator.writeToParcel(this, dest, flags);
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
deleted file mode 100644
index 2bea91f..0000000
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.appsearch.app;
-
-import android.os.Parcel;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.appsearch.annotation.CanIgnoreReturnValue;
-import androidx.appsearch.safeparcel.AbstractSafeParcelable;
-import androidx.appsearch.safeparcel.SafeParcelable;
-import androidx.appsearch.safeparcel.stub.StubCreators.VisibilityDocumentCreator;
-import androidx.collection.ArraySet;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Holds the visibility settings that apply to a schema type.
- * @exportToFramework:hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
[email protected](creator = "VisibilityDocumentCreator")
-public final class VisibilityDocument extends AbstractSafeParcelable {
-    @NonNull
-    public static final VisibilityDocumentCreator CREATOR = new VisibilityDocumentCreator();
-
-    /**
-     * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
-     */
-    public static final String SCHEMA_TYPE = "VisibilityType";
-    /** Namespace of documents that contain visibility settings */
-    public static final String NAMESPACE = "";
-
-    /**
-     * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
-     */
-    private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable";
-
-    /** Property that holds the package name that can access a schema. */
-    private static final String PACKAGE_NAME_PROPERTY = "packageName";
-
-    /** Property that holds the SHA 256 certificate of the app that can access a schema. */
-    private static final String SHA_256_CERT_PROPERTY = "sha256Cert";
-
-    /** Property that holds the required permissions to access the schema. */
-    private static final String PERMISSION_PROPERTY = "permission";
-
-    // The initial schema version, one VisibilityDocument contains all visibility information for
-    // whole package.
-    public static final int SCHEMA_VERSION_DOC_PER_PACKAGE = 0;
-
-    // One VisibilityDocument contains visibility information for a single schema.
-    public static final int SCHEMA_VERSION_DOC_PER_SCHEMA = 1;
-
-    // One VisibilityDocument contains visibility information for a single schema.
-    public static final int SCHEMA_VERSION_NESTED_PERMISSION_SCHEMA = 2;
-
-    public static final int SCHEMA_VERSION_LATEST = SCHEMA_VERSION_NESTED_PERMISSION_SCHEMA;
-
-    /**
-     * Schema for the VisibilityStore's documents.
-     *
-     * <p>NOTE: If you update this, also update {@link #SCHEMA_VERSION_LATEST}.
-     */
-    public static final AppSearchSchema
-            SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
-            .addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder(
-                    NOT_DISPLAYED_BY_SYSTEM_PROPERTY)
-                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                    .build())
-            .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
-                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .build())
-            .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY)
-                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .build())
-            .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(PERMISSION_PROPERTY,
-                    VisibilityPermissionDocument.SCHEMA_TYPE)
-                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .build())
-            .build();
-
-    @NonNull
-    @Field(id = 1, getter = "getId")
-    private String mId;
-
-    @Field(id = 2, getter = "isNotDisplayedBySystem")
-    private final boolean mIsNotDisplayedBySystem;
-
-    @NonNull
-    @Field(id = 3, getter = "getPackageNames")
-    private final String[] mPackageNames;
-
-    @NonNull
-    @Field(id = 4, getter = "getSha256Certs")
-    private final byte[][] mSha256Certs;
-
-    @Nullable
-    @Field(id = 5, getter = "getPermissionDocuments")
-    private final VisibilityPermissionDocument[] mPermissionDocuments;
-
-    @Nullable
-    // We still need to convert this class to a GenericDocument until we completely treat it
-    // differently in AppSearchImpl.
-    // TODO(b/298118943) Remove this once internally we don't use GenericDocument to store
-    //  visibility information.
-    private GenericDocument mGenericDocument;
-
-    @Nullable
-    private Integer mHashCode;
-
-    @Constructor
-    VisibilityDocument(
-            @Param(id = 1) @NonNull String id,
-            @Param(id = 2) boolean isNotDisplayedBySystem,
-            @Param(id = 3) @NonNull String[] packageNames,
-            @Param(id = 4) @NonNull byte[][] sha256Certs,
-            @Param(id = 5) @Nullable VisibilityPermissionDocument[] permissionDocuments) {
-        mId = Objects.requireNonNull(id);
-        mIsNotDisplayedBySystem = isNotDisplayedBySystem;
-        mPackageNames = Objects.requireNonNull(packageNames);
-        mSha256Certs = Objects.requireNonNull(sha256Certs);
-        mPermissionDocuments = permissionDocuments;
-    }
-
-    /**
-     * Gets the id for this VisibilityDocument.
-     *
-     * <p>This is being used as the document id when we convert a {@link VisibilityDocument}
-     * to a {@link GenericDocument}.
-     */
-    @NonNull
-    public String getId() {
-        return mId;
-    }
-
-    /** Returns whether this schema is visible to the system. */
-    public boolean isNotDisplayedBySystem() {
-        return mIsNotDisplayedBySystem;
-    }
-
-    /**
-     * Returns a package name array which could access this schema. Use {@link #getSha256Certs()} to
-     * get package's sha 256 certs. The same index of package names array and sha256Certs array
-     * represents same package.
-     */
-    @NonNull
-    public String[] getPackageNames() {
-        return mPackageNames;
-    }
-
-    /**
-     * Returns a package sha256Certs array which could access this schema. Use {@link
-     * #getPackageNames()} to get package's name. The same index of package names array and
-     * sha256Certs array represents same package.
-     */
-    @NonNull
-    public byte[][] getSha256Certs() {
-        return mSha256Certs;
-    }
-
-    /** Gets a list of {@link VisibilityDocument}.
-     *
-     * <p>A {@link VisibilityDocument} holds all required permissions for the caller need to have
-     * to access the schema this {@link VisibilityDocument} presents.
-     */
-    @Nullable
-    VisibilityPermissionDocument[] getPermissionDocuments() {
-        return mPermissionDocuments;
-    }
-
-    /**
-     * Returns an array of Android Permissions that caller mush hold to access the schema this
-     * {@link VisibilityDocument} represents.
-     */
-    @NonNull
-    public Set<Set<Integer>> getVisibleToPermissions() {
-        if (mPermissionDocuments == null) {
-            return Collections.emptySet();
-        }
-        Set<Set<Integer>> visibleToPermissions = new ArraySet<>(mPermissionDocuments.length);
-        for (VisibilityPermissionDocument permissionDocument : mPermissionDocuments) {
-            Set<Integer> requiredPermissions = permissionDocument.getAllRequiredPermissions();
-            if (requiredPermissions != null) {
-                visibleToPermissions.add(requiredPermissions);
-            }
-        }
-        return visibleToPermissions;
-    }
-
-    /** Build the List of {@link VisibilityDocument} from visibility settings. */
-    @NonNull
-    public static List<VisibilityDocument> toVisibilityDocuments(
-            @NonNull SetSchemaRequest setSchemaRequest) {
-        Set<AppSearchSchema> searchSchemas = setSchemaRequest.getSchemas();
-        Set<String> schemasNotDisplayedBySystem = setSchemaRequest.getSchemasNotDisplayedBySystem();
-        Map<String, Set<PackageIdentifier>> schemasVisibleToPackages =
-                setSchemaRequest.getSchemasVisibleToPackages();
-        Map<String, Set<Set<Integer>>> schemasVisibleToPermissions =
-                setSchemaRequest.getRequiredPermissionsForSchemaTypeVisibility();
-        List<VisibilityDocument> visibilityDocuments = new ArrayList<>(searchSchemas.size());
-        for (AppSearchSchema searchSchema : searchSchemas) {
-            String schemaType = searchSchema.getSchemaType();
-            VisibilityDocument.Builder documentBuilder =
-                    new VisibilityDocument.Builder(/*id=*/ searchSchema.getSchemaType());
-            documentBuilder.setNotDisplayedBySystem(
-                    schemasNotDisplayedBySystem.contains(schemaType));
-
-            if (schemasVisibleToPackages.containsKey(schemaType)) {
-                documentBuilder.addVisibleToPackages(schemasVisibleToPackages.get(schemaType));
-            }
-
-            if (schemasVisibleToPermissions.containsKey(schemaType)) {
-                documentBuilder.setVisibleToPermissions(
-                        schemasVisibleToPermissions.get(schemaType));
-            }
-            visibilityDocuments.add(documentBuilder.build());
-        }
-        return visibilityDocuments;
-    }
-
-    /**
-     * Generates a {@link GenericDocument} from the current class.
-     *
-     * <p>This conversion is needed until we don't treat Visibility related documents as
-     * {@link GenericDocument}s internally.
-     */
-    @NonNull
-    public GenericDocument toGenericDocument() {
-        if (mGenericDocument == null) {
-            GenericDocument.Builder<?> builder = new GenericDocument.Builder<>(
-                    NAMESPACE, mId, SCHEMA_TYPE);
-            builder.setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, mIsNotDisplayedBySystem);
-            builder.setPropertyString(PACKAGE_NAME_PROPERTY, mPackageNames);
-            builder.setPropertyBytes(SHA_256_CERT_PROPERTY, mSha256Certs);
-
-            // Generate an array of GenericDocument for VisibilityPermissionDocument.
-            if (mPermissionDocuments != null) {
-                GenericDocument[] permissionGenericDocs =
-                        new GenericDocument[mPermissionDocuments.length];
-                for (int i = 0; i < mPermissionDocuments.length; ++i) {
-                    permissionGenericDocs[i] = mPermissionDocuments[i].toGenericDocument();
-                }
-                builder.setPropertyDocument(PERMISSION_PROPERTY, permissionGenericDocs);
-            }
-
-            // The creationTimestamp doesn't matter for Visibility documents.
-            // But to make tests pass, we set it 0 so two GenericDocuments generated from
-            // the same VisibilityDocument can be same.
-            builder.setCreationTimestampMillis(0L);
-
-            mGenericDocument = builder.build();
-        }
-        return mGenericDocument;
-    }
-
-    @Override
-    public int hashCode() {
-        if (mHashCode == null) {
-            mHashCode = Objects.hash(
-                    mId,
-                    mIsNotDisplayedBySystem,
-                    Arrays.hashCode(mPackageNames),
-                    Arrays.deepHashCode(mSha256Certs),
-                    Arrays.hashCode(mPermissionDocuments));
-        }
-        return mHashCode;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object other) {
-        if (this == other) {
-            return true;
-        }
-        if (!(other instanceof VisibilityDocument)) {
-            return false;
-        }
-        VisibilityDocument otherVisibilityDocument = (VisibilityDocument) other;
-        return mId.equals(otherVisibilityDocument.mId)
-                && mIsNotDisplayedBySystem == otherVisibilityDocument.mIsNotDisplayedBySystem
-                && Arrays.equals(
-                mPackageNames, otherVisibilityDocument.mPackageNames)
-                && Arrays.deepEquals(
-                mSha256Certs, otherVisibilityDocument.mSha256Certs)
-                && Arrays.equals(
-                mPermissionDocuments, otherVisibilityDocument.mPermissionDocuments);
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        VisibilityDocumentCreator.writeToParcel(this, dest, flags);
-    }
-
-    /** Builder for {@link VisibilityDocument}. */
-    public static final class Builder {
-        private final Set<PackageIdentifier> mPackageIdentifiers = new ArraySet<>();
-        private String mId;
-        private boolean mIsNotDisplayedBySystem;
-        private VisibilityPermissionDocument[] mPermissionDocuments;
-
-        /**
-         * Creates a {@link Builder} for a {@link VisibilityDocument}.
-         *
-         * @param id The SchemaType of the {@link AppSearchSchema} that this {@link
-         *     VisibilityDocument} represents. The package and database prefix will be added in
-         *     server side. We are using prefixed schema type to be the final id of this {@link
-         *     VisibilityDocument}.
-         */
-        public Builder(@NonNull String id) {
-            mId = Objects.requireNonNull(id);
-        }
-
-        /**
-         * Constructs a {@link VisibilityDocument} from a {@link GenericDocument}.
-         *
-         * <p>This constructor is still needed until we don't treat Visibility related documents as
-         * {@link GenericDocument}s internally.
-         */
-        public Builder(@NonNull GenericDocument genericDocument) {
-            Objects.requireNonNull(genericDocument);
-
-            mId = genericDocument.getId();
-            mIsNotDisplayedBySystem = genericDocument.getPropertyBoolean(
-                    NOT_DISPLAYED_BY_SYSTEM_PROPERTY);
-
-            String[] packageNames = genericDocument.getPropertyStringArray(PACKAGE_NAME_PROPERTY);
-            byte[][] sha256Certs = genericDocument.getPropertyBytesArray(SHA_256_CERT_PROPERTY);
-            for (int i = 0; i < packageNames.length; ++i) {
-                mPackageIdentifiers.add(new PackageIdentifier(packageNames[i], sha256Certs[i]));
-            }
-
-            GenericDocument[] permissionDocs =
-                    genericDocument.getPropertyDocumentArray(PERMISSION_PROPERTY);
-            if (permissionDocs != null) {
-                mPermissionDocuments = new VisibilityPermissionDocument[permissionDocs.length];
-                for (int i = 0; i < permissionDocs.length; ++i) {
-                    mPermissionDocuments[i] = new VisibilityPermissionDocument.Builder(
-                            permissionDocs[i]).build();
-                }
-            }
-        }
-
-        public Builder(@NonNull VisibilityDocument visibilityDocument) {
-            Objects.requireNonNull(visibilityDocument);
-
-            mIsNotDisplayedBySystem = visibilityDocument.mIsNotDisplayedBySystem;
-            mPermissionDocuments = visibilityDocument.mPermissionDocuments;
-            for (int i = 0; i < visibilityDocument.mPackageNames.length; ++i) {
-                mPackageIdentifiers.add(new PackageIdentifier(visibilityDocument.mPackageNames[i],
-                            visibilityDocument.mSha256Certs[i]));
-            }
-        }
-
-        /** Sets id. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder setId(@NonNull String id) {
-            mId = Objects.requireNonNull(id);
-            return this;
-        }
-
-        /** Sets whether this schema has opted out of platform surfacing. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
-            mIsNotDisplayedBySystem = notDisplayedBySystem;
-            return this;
-        }
-
-        /** Add {@link PackageIdentifier} of packages which has access to this schema. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) {
-            mPackageIdentifiers.addAll(Objects.requireNonNull(packageIdentifiers));
-            return this;
-        }
-
-        /** Add {@link PackageIdentifier} of packages which has access to this schema. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
-            mPackageIdentifiers.add(Objects.requireNonNull(packageIdentifier));
-            return this;
-        }
-
-        /**
-         * Sets required permission sets for a package needs to hold to the schema this {@link
-         * VisibilityDocument} represents.
-         *
-         * <p>The querier could have access if they holds ALL required permissions of ANY of the
-         * individual value sets.
-         */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder setVisibleToPermissions(@NonNull Set<Set<Integer>> visibleToPermissions) {
-            Objects.requireNonNull(visibleToPermissions);
-            mPermissionDocuments =
-                    new VisibilityPermissionDocument[visibleToPermissions.size()];
-            int i = 0;
-            for (Set<Integer> allRequiredPermissions : visibleToPermissions) {
-                mPermissionDocuments[i++] =
-                        new VisibilityPermissionDocument.Builder(
-                                NAMESPACE, /*id=*/ String.valueOf(i))
-                                .setVisibleToAllRequiredPermissions(allRequiredPermissions)
-                                .build();
-            }
-            return this;
-        }
-
-        /** Build a {@link VisibilityDocument} */
-        @NonNull
-        public VisibilityDocument build() {
-            String[] packageNames = new String[mPackageIdentifiers.size()];
-            byte[][] sha256Certs = new byte[mPackageIdentifiers.size()][32];
-            int i = 0;
-            for (PackageIdentifier packageIdentifier : mPackageIdentifiers) {
-                packageNames[i] = packageIdentifier.getPackageName();
-                sha256Certs[i] = packageIdentifier.getSha256Certificate();
-                ++i;
-            }
-            return new VisibilityDocument(mId, mIsNotDisplayedBySystem,
-                    packageNames, sha256Certs, mPermissionDocuments);
-        }
-    }
-}
-
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionConfig.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionConfig.java
new file mode 100644
index 0000000..40e0f81
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionConfig.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.VisibilityPermissionConfigCreator;
+import androidx.collection.ArraySet;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * The config class that holds all required permissions for a caller need to hold to access the
+ * schema which the outer {@link SchemaVisibilityConfig} represents.
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
[email protected](creator = "VisibilityPermissionConfigCreator")
+public final class VisibilityPermissionConfig extends AbstractSafeParcelable {
+    @NonNull
+    public static final Parcelable.Creator<VisibilityPermissionConfig> CREATOR =
+            new VisibilityPermissionConfigCreator();
+
+    /**
+     * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
+     */
+    public static final String SCHEMA_TYPE = "VisibilityPermissionType";
+
+    /** Property that holds the required permissions to access the schema. */
+    public static final String ALL_REQUIRED_PERMISSIONS_PROPERTY = "allRequiredPermissions";
+
+    /**
+     * Schema for the VisibilityStore's documents.
+     *
+     * <p>NOTE: If you update this, also update schema version number in
+     * VisibilityToDocumentConverter
+     */
+    public static final AppSearchSchema
+            SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+            .addProperty(new AppSearchSchema.LongPropertyConfig
+                    .Builder(ALL_REQUIRED_PERMISSIONS_PROPERTY)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                    .build())
+            .build();
+
+    @Nullable
+    @Field(id = 1)
+    final int[] mAllRequiredPermissions;
+
+    @Nullable
+    // We still need to convert this class to a GenericDocument until we completely treat it
+    // differently in AppSearchImpl.
+    // TODO(b/298118943) Remove this once internally we don't use GenericDocument to store
+    //  visibility information.
+    private GenericDocument mGenericDocument;
+
+    @Nullable
+    private Integer mHashCode;
+
+    @Constructor
+    VisibilityPermissionConfig(@Param(id = 1) @Nullable int[] allRequiredPermissions) {
+        mAllRequiredPermissions = allRequiredPermissions;
+    }
+
+    /**
+     * Sets a set of Android Permissions that caller must hold to access the schema that the
+     * outer {@link SchemaVisibilityConfig} represents.
+     */
+    public VisibilityPermissionConfig(@NonNull Set<Integer> allRequiredPermissions) {
+        mAllRequiredPermissions = toInts(Objects.requireNonNull(allRequiredPermissions));
+    }
+
+    /**
+     * Returns an array of Android Permissions that caller mush hold to access the schema that the
+     * outer {@link SchemaVisibilityConfig} represents.
+     */
+    @Nullable
+    public Set<Integer> getAllRequiredPermissions() {
+        return toIntegerSet(mAllRequiredPermissions);
+    }
+
+    @NonNull
+    private static int[] toInts(@NonNull Set<Integer> properties) {
+        int[] outputs = new int[properties.size()];
+        int i = 0;
+        for (int property : properties) {
+            outputs[i++] = property;
+        }
+        return outputs;
+    }
+
+    @Nullable
+    private static Set<Integer> toIntegerSet(@Nullable int[] properties) {
+        if (properties == null) {
+            return null;
+        }
+        Set<Integer> outputs = new ArraySet<>(properties.length);
+        for (int property : properties) {
+            outputs.add(property);
+        }
+        return outputs;
+    }
+
+    /**
+     * Generates a {@link GenericDocument} from the current class.
+     *
+     * <p>This conversion is needed until we don't treat Visibility related documents as
+     * {@link GenericDocument}s internally.
+     */
+    @NonNull
+    public GenericDocument toGenericDocument() {
+        if (mGenericDocument == null) {
+            // This is used as a nested document, we do not need a namespace or id.
+            GenericDocument.Builder<?> builder = new GenericDocument.Builder<>(
+                    /*namespace=*/"", /*id=*/"", SCHEMA_TYPE);
+
+            if (mAllRequiredPermissions != null) {
+                // GenericDocument only supports long, so int[] needs to be converted to
+                // long[] here.
+                long[] longs = new long[mAllRequiredPermissions.length];
+                for (int i = 0; i < mAllRequiredPermissions.length; ++i) {
+                    longs[i] = mAllRequiredPermissions[i];
+                }
+                builder.setPropertyLong(ALL_REQUIRED_PERMISSIONS_PROPERTY, longs);
+            }
+
+            // The creationTimestamp doesn't matter for Visibility documents.
+            // But to make tests pass, we set it 0 so two GenericDocuments generated from
+            // the same VisibilityPermissionConfig can be same.
+            builder.setCreationTimestampMillis(0L);
+
+            mGenericDocument = builder.build();
+        }
+        return mGenericDocument;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = Arrays.hashCode(mAllRequiredPermissions);
+        }
+        return mHashCode;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof VisibilityPermissionConfig)) {
+            return false;
+        }
+        VisibilityPermissionConfig otherVisibilityPermissionConfig =
+                (VisibilityPermissionConfig) other;
+        return Arrays.equals(mAllRequiredPermissions,
+                otherVisibilityPermissionConfig.mAllRequiredPermissions);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        VisibilityPermissionConfigCreator.writeToParcel(this, dest, flags);
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
deleted file mode 100644
index 54269fd..0000000
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appsearch.app;
-
-import android.os.Parcel;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.appsearch.annotation.CanIgnoreReturnValue;
-import androidx.appsearch.safeparcel.AbstractSafeParcelable;
-import androidx.appsearch.safeparcel.SafeParcelable;
-import androidx.appsearch.safeparcel.stub.StubCreators.VisibilityPermissionDocumentCreator;
-import androidx.collection.ArraySet;
-
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * The nested document that holds all required permissions for a caller need to hold to access the
- * schema which the outer {@link VisibilityDocument} represents.
- * @exportToFramework:hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
[email protected](creator = "VisibilityPermissionDocumentCreator")
-public final class VisibilityPermissionDocument extends AbstractSafeParcelable {
-    @NonNull
-    public static final VisibilityPermissionDocumentCreator CREATOR =
-            new VisibilityPermissionDocumentCreator();
-
-    /**
-     * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
-     */
-    public static final String SCHEMA_TYPE = "VisibilityPermissionType";
-
-    /** Property that holds the required permissions to access the schema. */
-    private static final String ALL_REQUIRED_PERMISSIONS_PROPERTY = "allRequiredPermissions";
-
-    /**
-     * Schema for the VisibilityStore's documents.
-     *
-     * <p>NOTE: If you update this, also update {@link VisibilityDocument#SCHEMA_VERSION_LATEST}.
-     */
-    public static final AppSearchSchema
-            SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
-            .addProperty(new AppSearchSchema.LongPropertyConfig
-                    .Builder(ALL_REQUIRED_PERMISSIONS_PROPERTY)
-                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
-                    .build())
-            .build();
-
-    @NonNull
-    @Field(id = 1, getter = "getId")
-    private final String mId;
-
-    @NonNull
-    @Field(id = 2, getter = "getNamespace")
-    private final String mNamespace;
-
-    @Nullable
-    @Field(id = 3, getter = "getAllRequiredPermissionsInts")
-    // SafeParcelable doesn't support Set<Integer>, so we have to convert it to int[].
-    private final int[] mAllRequiredPermissions;
-
-    @Nullable
-    // We still need to convert this class to a GenericDocument until we completely treat it
-    // differently in AppSearchImpl.
-    // TODO(b/298118943) Remove this once internally we don't use GenericDocument to store
-    //  visibility information.
-    private GenericDocument mGenericDocument;
-
-    @Nullable
-    private Integer mHashCode;
-
-    @Constructor
-    VisibilityPermissionDocument(
-            @Param(id = 1) @NonNull String id,
-            @Param(id = 2) @NonNull String namespace,
-            @Param(id = 3) @Nullable int[] allRequiredPermissions) {
-        mId = Objects.requireNonNull(id);
-        mNamespace = Objects.requireNonNull(namespace);
-        mAllRequiredPermissions = allRequiredPermissions;
-    }
-
-    /**
-     * Gets the id for this {@link VisibilityPermissionDocument}.
-     *
-     * <p>This is being used as the document id when we convert a
-     * {@link VisibilityPermissionDocument} to a {@link GenericDocument}.
-     */
-    @NonNull
-    public String getId() {
-        return mId;
-    }
-
-    /**
-     * Gets the namespace for this {@link VisibilityPermissionDocument}.
-     *
-     * <p>This is being used as the namespace when we convert a
-     * {@link VisibilityPermissionDocument} to a {@link GenericDocument}.
-     */
-    @NonNull
-    public String getNamespace() {
-        return mNamespace;
-    }
-
-    /** Gets the required Android Permissions in an int array. */
-    @Nullable
-    int[] getAllRequiredPermissionsInts() {
-        return mAllRequiredPermissions;
-    }
-
-    /**
-     * Returns an array of Android Permissions that caller mush hold to access the schema that the
-     * outer {@link VisibilityDocument} represents.
-     */
-    @Nullable
-    public Set<Integer> getAllRequiredPermissions() {
-        return toIntegerSet(mAllRequiredPermissions);
-    }
-
-    @NonNull
-    private static int[] toInts(@NonNull Set<Integer> properties) {
-        int[] outputs = new int[properties.size()];
-        int i = 0;
-        for (int property : properties) {
-            outputs[i++] = property;
-        }
-        return outputs;
-    }
-
-    @Nullable
-    private static Set<Integer> toIntegerSet(@Nullable int[] properties) {
-        if (properties == null) {
-            return null;
-        }
-        Set<Integer> outputs = new ArraySet<>(properties.length);
-        for (int property : properties) {
-            outputs.add(property);
-        }
-        return outputs;
-    }
-
-    /**
-     * Generates a {@link GenericDocument} from the current class.
-     *
-     * <p>This conversion is needed until we don't treat Visibility related documents as
-     * {@link GenericDocument}s internally.
-     */
-    @NonNull
-    public GenericDocument toGenericDocument() {
-        if (mGenericDocument == null) {
-            GenericDocument.Builder<?> builder = new GenericDocument.Builder<>(
-                    mNamespace, mId, SCHEMA_TYPE);
-
-            if (mAllRequiredPermissions != null) {
-                // GenericDocument only supports long, so int[] needs to be converted to
-                // long[] here.
-                long[] longs = new long[mAllRequiredPermissions.length];
-                for (int i = 0; i < mAllRequiredPermissions.length; ++i) {
-                    longs[i] = mAllRequiredPermissions[i];
-                }
-                builder.setPropertyLong(ALL_REQUIRED_PERMISSIONS_PROPERTY, longs);
-            }
-
-            mGenericDocument = builder.build();
-        }
-        return mGenericDocument;
-    }
-
-    @Override
-    public int hashCode() {
-        if (mHashCode == null) {
-            mHashCode = Objects.hash(mId, mNamespace, Arrays.hashCode(mAllRequiredPermissions));
-        }
-        return mHashCode;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object other) {
-        if (this == other) {
-            return true;
-        }
-        if (!(other instanceof VisibilityPermissionDocument)) {
-            return false;
-        }
-        VisibilityPermissionDocument otherVisibilityPermissionDocument =
-                (VisibilityPermissionDocument) other;
-        return mId.equals(otherVisibilityPermissionDocument.mId)
-                && mNamespace.equals(otherVisibilityPermissionDocument.mNamespace)
-                && Arrays.equals(
-                mAllRequiredPermissions,
-                otherVisibilityPermissionDocument.mAllRequiredPermissions);
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        VisibilityPermissionDocumentCreator.writeToParcel(this, dest, flags);
-    }
-
-    /** Builder for {@link VisibilityPermissionDocument}. */
-    public static final class Builder {
-        private String mId;
-        private String mNamespace;
-        private int[] mAllRequiredPermissions;
-
-        /**
-         * Constructs a {@link VisibilityPermissionDocument} from a {@link GenericDocument}.
-         *
-         * <p>This constructor is still needed until we don't treat Visibility related documents as
-         * {@link GenericDocument}s internally.
-         */
-        public Builder(@NonNull GenericDocument genericDocument) {
-            Objects.requireNonNull(genericDocument);
-            mId = genericDocument.getId();
-            mNamespace = genericDocument.getNamespace();
-            // GenericDocument only supports long[], so we need to convert it back to int[].
-            long[] longs = genericDocument.getPropertyLongArray(
-                    ALL_REQUIRED_PERMISSIONS_PROPERTY);
-            if (longs != null) {
-                mAllRequiredPermissions = new int[longs.length];
-                for (int i = 0; i < longs.length; ++i) {
-                    mAllRequiredPermissions[i] = (int) longs[i];
-                }
-            }
-        }
-
-        /** Creates a {@link VisibilityDocument.Builder} for a {@link VisibilityDocument}. */
-        public Builder(@NonNull String namespace, @NonNull String id) {
-            mNamespace = Objects.requireNonNull(namespace);
-            mId = Objects.requireNonNull(id);
-        }
-
-        /**
-         * Sets a set of Android Permissions that caller mush hold to access the schema that the
-         * outer {@link VisibilityDocument} represents.
-         */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder setVisibleToAllRequiredPermissions(
-                @NonNull Set<Integer> allRequiredPermissions) {
-            mAllRequiredPermissions = toInts(Objects.requireNonNull(allRequiredPermissions));
-            return this;
-        }
-
-        /** Builds a {@link VisibilityPermissionDocument} */
-        @NonNull
-        public VisibilityPermissionDocument build() {
-            return new VisibilityPermissionDocument(mId,
-                    mNamespace,
-                    mAllRequiredPermissions);
-        }
-    }
-}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/checker/initialization/qual/UnderInitialization.java b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/initialization/qual/UnderInitialization.java
new file mode 100644
index 0000000..4fb00e1
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/initialization/qual/UnderInitialization.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.checker.initialization.qual;
+
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// This is an annotation stub to avoid dependencies on annotations that aren't
+// in the Android platform source tree.
+
+/**
+ * <!--@exportToFramework:hide-->
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Target({
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PACKAGE,
+        ElementType.PARAMETER,
+        ElementType.TYPE,
+        ElementType.TYPE_PARAMETER,
+        ElementType.TYPE_USE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface UnderInitialization {
+
+    // These fields maintain API compatibility with annotations that expect arguments.
+
+    String[] value() default {};
+
+    boolean result() default false;
+
+    String[] expression() default "";
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/checker/initialization/qual/UnknownInitialization.java b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/initialization/qual/UnknownInitialization.java
new file mode 100644
index 0000000..8faa0dd
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/initialization/qual/UnknownInitialization.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.checker.initialization.qual;
+
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// This is an annotation stub to avoid dependencies on annotations that aren't
+// in the Android platform source tree.
+
+/**
+ * <!--@exportToFramework:hide-->
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Target({
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PACKAGE,
+        ElementType.PARAMETER,
+        ElementType.TYPE,
+        ElementType.TYPE_PARAMETER,
+        ElementType.TYPE_USE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface UnknownInitialization {
+
+    // These fields maintain API compatibility with annotations that expect arguments.
+
+    String[] value() default {};
+
+    boolean result() default false;
+
+    String[] expression() default "";
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/checker/nullness/qual/Nullable.java b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/nullness/qual/Nullable.java
new file mode 100644
index 0000000..c9137c5
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/nullness/qual/Nullable.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.checker.nullness.qual;
+
+
+// This is an annotation stub to avoid dependencies on annotations that aren't
+// in the Android platform source tree.
+
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!--@exportToFramework:hide-->
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Target({
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PACKAGE,
+        ElementType.PARAMETER,
+        ElementType.TYPE,
+        ElementType.TYPE_PARAMETER,
+        ElementType.TYPE_USE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {
+
+    // These fields maintain API compatibility with annotations that expect arguments.
+
+    String[] value() default {};
+
+    boolean result() default false;
+
+    String[] expression() default "";
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/checker/nullness/qual/RequiresNonNull.java b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/nullness/qual/RequiresNonNull.java
new file mode 100644
index 0000000..4c39b9b
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/checker/nullness/qual/RequiresNonNull.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.checker.nullness.qual;
+
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// This is an annotation stub to avoid dependencies on annotations that aren't
+// in the Android platform source tree.
+
+/**
+ * <!--@exportToFramework:hide-->
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Target({
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.LOCAL_VARIABLE,
+        ElementType.METHOD,
+        ElementType.PACKAGE,
+        ElementType.PARAMETER,
+        ElementType.TYPE,
+        ElementType.TYPE_PARAMETER,
+        ElementType.TYPE_USE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface RequiresNonNull {
+
+    // These fields maintain API compatibility with annotations that expect arguments.
+
+    String[] value() default {};
+
+    boolean result() default false;
+
+    String[] expression() default "";
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java b/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java
index 2930d2d..b39435f 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/exceptions/AppSearchException.java
@@ -27,7 +27,7 @@
  * for propagating to the client.
  */
 public class AppSearchException extends Exception {
-    private final @AppSearchResult.ResultCode int mResultCode;
+    @AppSearchResult.ResultCode private final int mResultCode;
 
     /**
      * Initializes an {@link AppSearchException} with no message.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/flags/FlaggedApi.java b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/FlaggedApi.java
new file mode 100644
index 0000000..693be73
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/FlaggedApi.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.flags;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * Indicates an API is part of a feature that is guarded by an aconfig flag in the framework, and
+ * only available if the flag is enabled.
+ *
+ * <p>Our own Jetpack version is created here for code sync purpose.
+ */
+// @exportToFramework:skipFile()
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public @interface FlaggedApi {
+    String value();
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
new file mode 100644
index 0000000..5ad11f3
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// @exportToFramework:skipFile()
+package androidx.appsearch.flags;
+
+
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchSchema;
+
+import java.util.Collection;
+
+/**
+ * Flags to control different features.
+ *
+ * <p>In Jetpack, those values can't be changed during runtime.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class Flags {
+    private Flags() {
+    }
+
+    // The prefix of all the flags defined for AppSearch. The prefix has
+    // "com.android.appsearch.flags", aka the package name for generated AppSearch flag classes in
+    // the framework, plus an additional trailing '.'.
+    private static final String FLAG_PREFIX =
+            "com.android.appsearch.flags.";
+
+    // The full string values for flags defined in the framework.
+    //
+    // The values of the static variables are the names of the flag defined in the framework's
+    // aconfig files. E.g. "enable_safe_parcelable", with FLAG_PREFIX as the prefix.
+    //
+    // The name of the each static variable should be "FLAG_" + capitalized value of the flag.
+
+    /** Enable SafeParcelable related features. */
+    public static final String FLAG_ENABLE_SAFE_PARCELABLE_2 =
+            FLAG_PREFIX + "enable_safe_parcelable_2";
+
+    /** Enable the "hasProperty" function in list filter query expressions. */
+    public static final String FLAG_ENABLE_LIST_FILTER_HAS_PROPERTY_FUNCTION =
+            FLAG_PREFIX + "enable_list_filter_has_property_function";
+
+    /** Enable the "tokenize" function in list filter query expressions. */
+    public static final String FLAG_ENABLE_LIST_FILTER_TOKENIZE_FUNCTION =
+            FLAG_PREFIX + "enable_list_filter_tokenize_function";
+
+
+    /** Enable Schema Type Grouping related features. */
+    public static final String FLAG_ENABLE_GROUPING_TYPE_PER_SCHEMA =
+            FLAG_PREFIX + "enable_grouping_type_per_schema";
+
+    /** Enable GenericDocument to take another GenericDocument to copy construct. */
+    public static final String FLAG_ENABLE_GENERIC_DOCUMENT_COPY_CONSTRUCTOR =
+            FLAG_PREFIX + "enable_generic_document_copy_constructor";
+
+    /**
+     * Enable the {@link androidx.appsearch.app.SearchSpec.Builder#addFilterProperties} and
+     * {@link androidx.appsearch.app.SearchSuggestionSpec.Builder#addFilterProperties}.
+     */
+    public static final String FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES =
+            FLAG_PREFIX + "enable_search_spec_filter_properties";
+    /**
+     * Enable the {@link androidx.appsearch.app.SearchSpec.Builder#setSearchSourceLogTag} method.
+     */
+    public static final String FLAG_ENABLE_SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG =
+            FLAG_PREFIX + "enable_search_spec_set_search_source_log_tag";
+
+    /** Enable addTakenActions API in PutDocumentsRequest. */
+    public static final String FLAG_ENABLE_PUT_DOCUMENTS_REQUEST_ADD_TAKEN_ACTIONS =
+            FLAG_PREFIX + "enable_put_documents_request_add_taken_actions";
+
+    /** Enable setPubliclyVisibleSchema in SetSchemaRequest. */
+    public static final String FLAG_ENABLE_SET_PUBLICLY_VISIBLE_SCHEMA = FLAG_PREFIX
+            + "enable_set_publicly_visible_schema";
+
+    /**
+     * Enable {@link androidx.appsearch.app.GenericDocument.Builder} to use previously hidden
+     * methods.
+     */
+    public static final String FLAG_ENABLE_GENERIC_DOCUMENT_BUILDER_HIDDEN_METHODS = FLAG_PREFIX
+            + "enable_generic_document_builder_hidden_methods";
+
+    public static final String FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS = FLAG_PREFIX
+            + "enable_set_schema_visible_to_configs";
+
+    /** Enable {@link androidx.appsearch.app.EnterpriseGlobalSearchSession}. */
+    public static final String FLAG_ENABLE_ENTERPRISE_GLOBAL_SEARCH_SESSION =
+            FLAG_PREFIX + "enable_enterprise_global_search_session";
+
+    /**
+     * Enables {@link android.app.appsearch.functions.AppFunctionManager} and app functions related
+     * stuff.
+     */
+    public static final String FLAG_ENABLE_APP_FUNCTIONS = FLAG_PREFIX + "enable_app_functions";
+
+    /**
+     * Enable {@link androidx.appsearch.app.AppSearchResult#RESULT_DENIED} and
+     * {@link androidx.appsearch.app.AppSearchResult#RESULT_RATE_LIMITED} which were previously
+     * hidden.
+     */
+    public static final String FLAG_ENABLE_RESULT_DENIED_AND_RESULT_RATE_LIMITED =
+            FLAG_PREFIX + "enable_result_denied_and_result_rate_limited";
+
+    /**
+     * Enables {@link AppSearchSchema#getParentTypes()},
+     * {@link AppSearchSchema.DocumentPropertyConfig#getIndexableNestedProperties()} and variants of
+     * {@link AppSearchSchema.DocumentPropertyConfig.Builder#addIndexableNestedProperties(Collection)}}.
+     */
+    public static final String FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES =
+            FLAG_PREFIX + "enable_get_parent_types_and_indexable_nested_properties";
+
+    /** Enables embedding search related APIs. */
+    public static final String FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG =
+            FLAG_PREFIX + "enable_schema_embedding_property_config";
+
+    /** Enables informational ranking expressions. */
+    public static final String FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS =
+            FLAG_PREFIX + "enable_informational_ranking_expressions";
+
+    // Whether the features should be enabled.
+    //
+    // In Jetpack, those should always return true.
+
+    /** Whether SafeParcelable should be enabled. */
+    public static boolean enableSafeParcelable() {
+        return true;
+    }
+
+    /** Whether the "hasProperty" function in list filter query expressions should be enabled. */
+    public static boolean enableListFilterHasPropertyFunction() {
+        return true;
+    }
+
+    /** Whether Schema Type Grouping should be enabled. */
+    public static boolean enableGroupingTypePerSchema() {
+        return true;
+    }
+
+    /** Whether Generic Document Copy Constructing should be enabled. */
+    public static boolean enableGenericDocumentCopyConstructor() {
+        return true;
+    }
+
+    /**
+     * Whether the {@link androidx.appsearch.app.SearchSpec.Builder#addFilterProperties} and
+     * {@link androidx.appsearch.app.SearchSuggestionSpec.Builder#addFilterProperties} should be
+     * enabled.
+     */
+    public static boolean enableSearchSpecFilterProperties() {
+        return true;
+    }
+
+    /**
+     * Whether the {@link androidx.appsearch.app.SearchSpec.Builder#setSearchSourceLogTag} should
+     * be enabled.
+     */
+    public static boolean enableSearchSpecSetSearchSourceLogTag() {
+        return true;
+    }
+
+    /** Whether addTakenActions API in PutDocumentsRequest should be enabled. */
+    public static boolean enablePutDocumentsRequestAddTakenActions() {
+        return true;
+    }
+
+    /** Whether setPubliclyVisibleSchema in SetSchemaRequest.Builder should be enabled. */
+    public static boolean enableSetPubliclyVisibleSchema() {
+        return true;
+    }
+
+    /**
+     * Whether {@link androidx.appsearch.app.GenericDocument.Builder#setNamespace(String)},
+     * {@link androidx.appsearch.app.GenericDocument.Builder#setId(String)},
+     * {@link androidx.appsearch.app.GenericDocument.Builder#setSchemaType(String)}, and
+     * {@link androidx.appsearch.app.GenericDocument.Builder#clearProperty(String)}
+     * should be enabled.
+     */
+    public static boolean enableGenericDocumentBuilderHiddenMethods() {
+        return true;
+    }
+
+    /**
+     * Whether
+     * {@link androidx.appsearch.app.SetSchemaRequest.Builder #setSchemaTypeVisibilityForConfigs}
+     * should be enabled.
+     */
+    public static boolean enableSetSchemaVisibleToConfigs() {
+        return true;
+    }
+
+    /** Whether {@link androidx.appsearch.app.EnterpriseGlobalSearchSession} should be enabled. */
+    public static boolean enableEnterpriseGlobalSearchSession() {
+        return true;
+    }
+
+    /**
+     * Whether {@link androidx.appsearch.app.AppSearchResult#RESULT_DENIED} and
+     * {@link androidx.appsearch.app.AppSearchResult#RESULT_RATE_LIMITED} should be enabled.
+     */
+    public static boolean enableResultDeniedAndResultRateLimited() {
+        return true;
+    }
+
+    /**
+     * Whether {@link AppSearchSchema#getParentTypes()},
+     * {@link AppSearchSchema.DocumentPropertyConfig#getIndexableNestedProperties()} and variants of
+     * {@link AppSearchSchema.DocumentPropertyConfig.Builder#addIndexableNestedProperties(Collection)}}
+     * should be enabled.
+     */
+    public static boolean enableGetParentTypesAndIndexableNestedProperties() {
+        return true;
+    }
+
+    /** Whether embedding search related APIs should be enabled. */
+    public static boolean enableSchemaEmbeddingPropertyConfig() {
+        return true;
+    }
+
+    /** Whether the "tokenize" function in list filter query expressions should be enabled. */
+    public static boolean enableListFilterTokenizeFunction() {
+        return true;
+    }
+
+    /** Whether informational ranking expressions should be enabled. */
+    public static boolean enableInformationalRankingExpressions() {
+        return true;
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java
index 903929b..baae8d1 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/observer/ObserverSpec.java
@@ -17,7 +17,8 @@
 package androidx.appsearch.observer;
 
 import android.annotation.SuppressLint;
-import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -27,6 +28,11 @@
 import androidx.appsearch.app.DocumentClassFactory;
 import androidx.appsearch.app.DocumentClassFactoryRegistry;
 import androidx.appsearch.exceptions.AppSearchException;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.ObserverSpecCreator;
 import androidx.collection.ArraySet;
 import androidx.core.util.Preconditions;
 
@@ -41,31 +47,27 @@
  * Configures the types, namespaces and other properties that {@link ObserverCallback} instances
  * match against.
  */
-public final class ObserverSpec {
-    private static final String FILTER_SCHEMA_FIELD = "filterSchema";
[email protected](creator = "ObserverSpecCreator")
+@SuppressWarnings("HiddenSuperclass")
+public final class ObserverSpec extends AbstractSafeParcelable {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @NonNull
+    public static final Parcelable.Creator<ObserverSpec> CREATOR =
+            new ObserverSpecCreator();
 
-    private final Bundle mBundle;
+    @Field(id = 1)
+    final List<String> mFilterSchemas;
 
     /** Populated on first use */
-    @Nullable
-    private volatile Set<String> mFilterSchemas;
+    @Nullable private volatile Set<String> mFilterSchemasCached;
 
     /** @exportToFramework:hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public ObserverSpec(@NonNull Bundle bundle) {
-        Preconditions.checkNotNull(bundle);
-        mBundle = bundle;
-    }
-
-    /**
-     * Returns the {@link Bundle} backing this spec.
-     *
-     * @exportToFramework:hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Bundle getBundle() {
-        return mBundle;
+    @Constructor
+    public ObserverSpec(
+            @Param(id = 1) @NonNull List<String> filterSchemas) {
+        mFilterSchemas = Preconditions.checkNotNull(filterSchemas);
     }
 
     /**
@@ -75,15 +77,14 @@
      */
     @NonNull
     public Set<String> getFilterSchemas() {
-        if (mFilterSchemas == null) {
-            List<String> schemas = mBundle.getStringArrayList(FILTER_SCHEMA_FIELD);
-            if (schemas == null) {
-                mFilterSchemas = Collections.emptySet();
+        if (mFilterSchemasCached == null) {
+            if (mFilterSchemas == null) {
+                mFilterSchemasCached = Collections.emptySet();
             } else {
-                mFilterSchemas = Collections.unmodifiableSet(new ArraySet<>(schemas));
+                mFilterSchemasCached = Collections.unmodifiableSet(new ArraySet<>(mFilterSchemas));
             }
         }
-        return mFilterSchemas;
+        return mFilterSchemasCached;
     }
 
     /** Builder for {@link ObserverSpec} instances. */
@@ -134,7 +135,7 @@
         @SuppressLint("MissingGetterMatchingBuilder")
         @CanIgnoreReturnValue
         @NonNull
-        public Builder addFilterDocumentClasses(@NonNull Class<?>... documentClasses)
+        public Builder addFilterDocumentClasses(@NonNull java.lang.Class<?>... documentClasses)
                 throws AppSearchException {
             Preconditions.checkNotNull(documentClasses);
             resetIfBuilt();
@@ -155,12 +156,13 @@
         @CanIgnoreReturnValue
         @NonNull
         public Builder addFilterDocumentClasses(
-                @NonNull Collection<? extends Class<?>> documentClasses) throws AppSearchException {
+                @NonNull Collection<? extends java.lang.Class<?>> documentClasses)
+                throws AppSearchException {
             Preconditions.checkNotNull(documentClasses);
             resetIfBuilt();
             List<String> schemas = new ArrayList<>(documentClasses.size());
             DocumentClassFactoryRegistry registry = DocumentClassFactoryRegistry.getInstance();
-            for (Class<?> documentClass : documentClasses) {
+            for (java.lang.Class<?> documentClass : documentClasses) {
                 DocumentClassFactory<?> factory = registry.getOrCreateFactory(documentClass);
                 schemas.add(factory.getSchemaName());
             }
@@ -172,10 +174,8 @@
         /** Constructs a new {@link ObserverSpec} from the contents of this builder. */
         @NonNull
         public ObserverSpec build() {
-            Bundle bundle = new Bundle();
-            bundle.putStringArrayList(FILTER_SCHEMA_FIELD, mFilterSchemas);
             mBuilt = true;
-            return new ObserverSpec(bundle);
+            return new ObserverSpec(mFilterSchemas);
         }
 
         private void resetIfBuilt() {
@@ -185,4 +185,11 @@
             }
         }
     }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        ObserverSpecCreator.writeToParcel(this, dest, flags);
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/AbstractSafeParcelable.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/AbstractSafeParcelable.java
index a677ff8..bdc4bb5 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/AbstractSafeParcelable.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/AbstractSafeParcelable.java
@@ -211,4 +211,10 @@
      */
     public void writeToParcel(@NonNull Parcel dest, int flags) {
     }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Override
+    public final int describeContents() {
+        return 0;
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java
index d921290..1d407fa 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcel.java
@@ -16,20 +16,22 @@
 
 package androidx.appsearch.safeparcel;
 
+import android.annotation.SuppressLint;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.AppSearchSession;
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.GenericDocument;
-import androidx.appsearch.safeparcel.stub.StubCreators.GenericDocumentParcelCreator;
 import androidx.collection.ArrayMap;
 
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -41,9 +43,11 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @SafeParcelable.Class(creator = "GenericDocumentParcelCreator")
-public final class GenericDocumentParcel extends AbstractSafeParcelable {
+// This won't be used to send data over binder, and we have to use Parcelable for code sync purpose.
+@SuppressLint("BanParcelableUsage")
+public final class GenericDocumentParcel extends AbstractSafeParcelable implements Parcelable {
     @NonNull
-    public static final GenericDocumentParcelCreator CREATOR =
+    public static final Parcelable.Creator<GenericDocumentParcel> CREATOR =
             new GenericDocumentParcelCreator();
 
     /** The default score of document. */
@@ -83,7 +87,15 @@
      */
     @Field(id = 7, getter = "getProperties")
     @NonNull
-    private final PropertyParcel[] mProperties;
+    private final List<PropertyParcel> mProperties;
+
+    /**
+     * Contains all parent properties for this {@link GenericDocument} in a list.
+     *
+     */
+    @Field(id = 8, getter = "getParentTypes")
+    @Nullable
+    private final List<String> mParentTypes;
 
     /**
      * Contains all properties in {@link GenericDocument} to support getting properties via name
@@ -110,9 +122,10 @@
             @Param(id = 4) long creationTimestampMillis,
             @Param(id = 5) long ttlMillis,
             @Param(id = 6) int score,
-            @Param(id = 7) @NonNull PropertyParcel[] properties) {
+            @Param(id = 7) @NonNull List<PropertyParcel> properties,
+            @Param(id = 8) @Nullable List<String> parentTypes) {
         this(namespace, id, schemaType, creationTimestampMillis, ttlMillis, score,
-                properties, createPropertyMapFromPropertyArray(properties));
+                properties, createPropertyMapFromPropertyArray(properties), parentTypes);
     }
 
     /**
@@ -128,8 +141,9 @@
             long creationTimestampMillis,
             long ttlMillis,
             int score,
-            @NonNull PropertyParcel[] properties,
-            @NonNull Map<String, PropertyParcel> propertyMap) {
+            @NonNull List<PropertyParcel> properties,
+            @NonNull Map<String, PropertyParcel> propertyMap,
+            @Nullable List<String> parentTypes) {
         mNamespace = Objects.requireNonNull(namespace);
         mId = Objects.requireNonNull(id);
         mSchemaType = Objects.requireNonNull(schemaType);
@@ -138,14 +152,23 @@
         mScore = score;
         mProperties = Objects.requireNonNull(properties);
         mPropertyMap = Objects.requireNonNull(propertyMap);
+        mParentTypes = parentTypes;
+    }
+
+    /** Returns the {@link GenericDocumentParcel} object from the given {@link GenericDocument}. */
+    @NonNull
+    public static GenericDocumentParcel fromGenericDocument(
+            @NonNull GenericDocument genericDocument) {
+        Objects.requireNonNull(genericDocument);
+        return genericDocument.getDocumentParcel();
     }
 
     private static Map<String, PropertyParcel> createPropertyMapFromPropertyArray(
-            @NonNull PropertyParcel[] properties) {
+            @NonNull List<PropertyParcel> properties) {
         Objects.requireNonNull(properties);
-        Map<String, PropertyParcel> propertyMap = new ArrayMap<>(properties.length);
-        for (int i = 0; i < properties.length; ++i) {
-            PropertyParcel property = properties[i];
+        Map<String, PropertyParcel> propertyMap = new ArrayMap<>(properties.size());
+        for (int i = 0; i < properties.size(); ++i) {
+            PropertyParcel property = properties.get(i);
             propertyMap.put(property.getPropertyName(), property);
         }
         return propertyMap;
@@ -193,7 +216,7 @@
 
     /** Returns all the properties the document has. */
     @NonNull
-    public PropertyParcel[] getProperties() {
+    public List<PropertyParcel> getProperties() {
         return mProperties;
     }
 
@@ -203,6 +226,12 @@
         return mPropertyMap;
     }
 
+    /** Returns the list of parent types for the {@link GenericDocument}. */
+    @Nullable
+    public List<String> getParentTypes() {
+        return mParentTypes;
+    }
+
     @Override
     public boolean equals(@Nullable Object other) {
         if (this == other) {
@@ -218,8 +247,9 @@
                 && mTtlMillis == otherDocument.mTtlMillis
                 && mCreationTimestampMillis == otherDocument.mCreationTimestampMillis
                 && mScore == otherDocument.mScore
-                && Arrays.equals(mProperties, otherDocument.mProperties)
-                && Objects.equals(mPropertyMap, otherDocument.mPropertyMap);
+                && Objects.equals(mProperties, otherDocument.mProperties)
+                && Objects.equals(mPropertyMap, otherDocument.mPropertyMap)
+                && Objects.equals(mParentTypes, otherDocument.mParentTypes);
     }
 
     @Override
@@ -232,8 +262,9 @@
                     mTtlMillis,
                     mScore,
                     mCreationTimestampMillis,
-                    Arrays.hashCode(mProperties),
-                    mPropertyMap.hashCode());
+                    Objects.hashCode(mProperties),
+                    Objects.hashCode(mPropertyMap),
+                    Objects.hashCode(mParentTypes));
         }
         return mHashCode;
     }
@@ -252,10 +283,10 @@
         private long mTtlMillis;
         private int mScore;
         private Map<String, PropertyParcel> mPropertyMap;
-        private boolean mBuilt = false;
+        @Nullable private List<String> mParentTypes;
 
         /**
-         * Creates a new {@link GenericDocument.Builder}.
+         * Creates a new {@link GenericDocumentParcel.Builder}.
          *
          * <p>Document IDs are unique within a namespace.
          *
@@ -275,7 +306,6 @@
          * Creates a new {@link GenericDocumentParcel.Builder} from the given
          * {@link GenericDocumentParcel}.
          */
-        @VisibleForTesting
         public Builder(@NonNull GenericDocumentParcel documentSafeParcel) {
             Objects.requireNonNull(documentSafeParcel);
 
@@ -292,6 +322,10 @@
             for (PropertyParcel value : propertyMap.values()) {
                 mPropertyMap.put(value.getPropertyName(), value);
             }
+
+            // We don't need to create a shallow copy here, as in the setter for ParentTypes we
+            // will create a new list anyway.
+            mParentTypes = documentSafeParcel.mParentTypes;
         }
 
         /**
@@ -306,7 +340,6 @@
         @NonNull
         public Builder setNamespace(@NonNull String namespace) {
             Objects.requireNonNull(namespace);
-            resetIfBuilt();
             mNamespace = namespace;
             return this;
         }
@@ -321,7 +354,6 @@
         @NonNull
         public Builder setId(@NonNull String id) {
             Objects.requireNonNull(id);
-            resetIfBuilt();
             mId = id;
             return this;
         }
@@ -336,7 +368,6 @@
         @NonNull
         public Builder setSchemaType(@NonNull String schemaType) {
             Objects.requireNonNull(schemaType);
-            resetIfBuilt();
             mSchemaType = schemaType;
             return this;
         }
@@ -345,7 +376,6 @@
         @CanIgnoreReturnValue
         @NonNull
         public Builder setScore(int score) {
-            resetIfBuilt();
             mScore = score;
             return this;
         }
@@ -364,7 +394,6 @@
         @NonNull
         public Builder setCreationTimestampMillis(
                 /*@exportToFramework:CurrentTimeMillisLong*/ long creationTimestampMillis) {
-            resetIfBuilt();
             mCreationTimestampMillis = creationTimestampMillis;
             return this;
         }
@@ -388,12 +417,24 @@
             if (ttlMillis < 0) {
                 throw new IllegalArgumentException("Document ttlMillis cannot be negative.");
             }
-            resetIfBuilt();
             mTtlMillis = ttlMillis;
             return this;
         }
 
         /**
+         * Sets the list of parent types of the {@link GenericDocument}'s type.
+         *
+         * <p>Child types must appear before parent types in the list.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setParentTypes(@NonNull List<String> parentTypes) {
+            Objects.requireNonNull(parentTypes);
+            mParentTypes = new ArrayList<>(parentTypes);
+            return this;
+        }
+
+        /**
          * Clears the value for the property with the given name.
          *
          * <p>Note that this method does not support property paths.
@@ -404,40 +445,43 @@
         @NonNull
         public Builder clearProperty(@NonNull String name) {
             Objects.requireNonNull(name);
-            resetIfBuilt();
             mPropertyMap.remove(name);
             return this;
         }
 
         /** puts an array of {@link String} in property map. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder putInPropertyMap(@NonNull String name, @NonNull String[] values)
                 throws IllegalArgumentException {
-            mPropertyMap.put(name,
+            putInPropertyMap(name,
                     new PropertyParcel.Builder(name).setStringValues(values).build());
             return this;
         }
 
         /** puts an array of boolean in property map. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder putInPropertyMap(@NonNull String name, @NonNull boolean[] values) {
-            mPropertyMap.put(name,
+            putInPropertyMap(name,
                     new PropertyParcel.Builder(name).setBooleanValues(values).build());
             return this;
         }
 
         /** puts an array of double in property map. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder putInPropertyMap(@NonNull String name, @NonNull double[] values) {
-            mPropertyMap.put(name,
+            putInPropertyMap(name,
                     new PropertyParcel.Builder(name).setDoubleValues(values).build());
             return this;
         }
 
         /** puts an array of long in property map. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder putInPropertyMap(@NonNull String name, @NonNull long[] values) {
-            mPropertyMap.put(name,
+            putInPropertyMap(name,
                     new PropertyParcel.Builder(name).setLongValues(values).build());
             return this;
         }
@@ -445,26 +489,47 @@
         /**
          * Converts and saves a byte[][] into {@link #mProperties}.
          */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder putInPropertyMap(@NonNull String name, @NonNull byte[][] values) {
-            mPropertyMap.put(name,
+            putInPropertyMap(name,
                     new PropertyParcel.Builder(name).setBytesValues(values).build());
             return this;
         }
 
         /** puts an array of {@link GenericDocumentParcel} in property map. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder putInPropertyMap(@NonNull String name,
                 @NonNull GenericDocumentParcel[] values) {
-            mPropertyMap.put(name,
+            putInPropertyMap(name,
                     new PropertyParcel.Builder(name).setDocumentValues(values).build());
             return this;
         }
 
+        /** puts an array of {@link EmbeddingVector} in property map. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder putInPropertyMap(@NonNull String name,
+                @NonNull EmbeddingVector[] values) {
+            putInPropertyMap(name,
+                    new PropertyParcel.Builder(name).setEmbeddingValues(values).build());
+            return this;
+        }
+
+        /** Directly puts a {@link PropertyParcel} in property map. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder putInPropertyMap(@NonNull String name,
+                @NonNull PropertyParcel value) {
+            Objects.requireNonNull(value);
+            mPropertyMap.put(name, value);
+            return this;
+        }
+
         /** Builds the {@link GenericDocument} object. */
         @NonNull
         public GenericDocumentParcel build() {
-            mBuilt = true;
             // Set current timestamp for creation timestamp by default.
             if (mCreationTimestampMillis == INVALID_CREATION_TIMESTAMP_MILLIS) {
                 mCreationTimestampMillis = System.currentTimeMillis();
@@ -476,19 +541,8 @@
                     mCreationTimestampMillis,
                     mTtlMillis,
                     mScore,
-                    mPropertyMap.values().toArray(new PropertyParcel[0]));
-        }
-
-        void resetIfBuilt() {
-            if (mBuilt) {
-                Map<String, PropertyParcel> propertyMap = mPropertyMap;
-                mPropertyMap = new ArrayMap<>(propertyMap.size());
-                for (PropertyParcel value : propertyMap.values()) {
-                    // PropertyParcel is not deep copied since it is not mutable.
-                    mPropertyMap.put(value.getPropertyName(), value);
-                }
-                mBuilt = false;
-            }
+                    new ArrayList<>(mPropertyMap.values()),
+                    mParentTypes);
         }
     }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcelCreator.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcelCreator.java
new file mode 100644
index 0000000..98aee5b
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/GenericDocumentParcelCreator.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.safeparcel;
+
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An implemented creator for {@link GenericDocumentParcel}.
+ *
+ * <p>In Jetpack, in order to serialize
+ * {@link GenericDocumentParcel} for {@link androidx.appsearch.app.GenericDocument},
+ * {@link PropertyParcel} needs to be a real {@link Parcelable}.
+ */
+// @exportToFramework:skipFile()
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class GenericDocumentParcelCreator implements
+        Parcelable.Creator<GenericDocumentParcel> {
+    private static final String PROPERTIES_FIELD = "properties";
+    private static final String SCHEMA_TYPE_FIELD = "schemaType";
+    private static final String ID_FIELD = "id";
+    private static final String SCORE_FIELD = "score";
+    private static final String TTL_MILLIS_FIELD = "ttlMillis";
+    private static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis";
+    private static final String NAMESPACE_FIELD = "namespace";
+    private static final String PARENT_TYPES_FIELD = "parentTypes";
+
+    /** Creates a {@link GenericDocumentParcel} from a {@link Bundle}. */
+    @NonNull
+    private static GenericDocumentParcel createGenericDocumentParcelFromBundle(
+            @NonNull Bundle genericDocumentParcelBundle) {
+        // Get namespace, id, and schema type
+        String namespace = genericDocumentParcelBundle.getString(NAMESPACE_FIELD);
+        String id = genericDocumentParcelBundle.getString(ID_FIELD);
+        String schemaType = genericDocumentParcelBundle.getString(SCHEMA_TYPE_FIELD);
+
+        // Those three can NOT be null.
+        if (namespace == null || id == null || schemaType == null) {
+            throw new IllegalArgumentException("GenericDocumentParcel bundle doesn't have "
+                    + "namespace, id, or schemaType.");
+        }
+
+        GenericDocumentParcel.Builder builder = new GenericDocumentParcel.Builder(namespace,
+                id, schemaType);
+        List<String> parentTypes =
+                genericDocumentParcelBundle.getStringArrayList(PARENT_TYPES_FIELD);
+        if (parentTypes != null) {
+            builder.setParentTypes(parentTypes);
+        }
+        builder.setScore(genericDocumentParcelBundle.getInt(SCORE_FIELD));
+        builder.setCreationTimestampMillis(
+                genericDocumentParcelBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD));
+        builder.setTtlMillis(genericDocumentParcelBundle.getLong(TTL_MILLIS_FIELD));
+
+        // properties
+        Bundle propertyBundle = genericDocumentParcelBundle.getBundle(PROPERTIES_FIELD);
+        if (propertyBundle != null) {
+            for (String propertyName : propertyBundle.keySet()) {
+                // SuppressWarnings can be applied on a local variable, but not any
+                // single line of code.
+                @SuppressWarnings("deprecation")
+                PropertyParcel propertyParcel = propertyBundle.getParcelable(propertyName);
+                builder.putInPropertyMap(propertyName, propertyParcel);
+            }
+        }
+
+        return builder.build();
+    }
+
+    /** Creates a {@link Bundle} from a {@link GenericDocumentParcel}. */
+    @NonNull
+    private static Bundle createBundleFromGenericDocumentParcel(
+            @NonNull GenericDocumentParcel genericDocumentParcel) {
+        Bundle genericDocumentParcelBundle = new Bundle();
+
+        // Common fields
+        genericDocumentParcelBundle.putString(NAMESPACE_FIELD,
+                genericDocumentParcel.getNamespace());
+        genericDocumentParcelBundle.putString(ID_FIELD, genericDocumentParcel.getId());
+        genericDocumentParcelBundle.putString(SCHEMA_TYPE_FIELD,
+                genericDocumentParcel.getSchemaType());
+        genericDocumentParcelBundle.putStringArrayList(PARENT_TYPES_FIELD,
+                (ArrayList<String>) genericDocumentParcel.getParentTypes());
+        genericDocumentParcelBundle.putInt(SCORE_FIELD, genericDocumentParcel.getScore());
+        genericDocumentParcelBundle.putLong(CREATION_TIMESTAMP_MILLIS_FIELD,
+                genericDocumentParcel.getCreationTimestampMillis());
+        genericDocumentParcelBundle.putLong(TTL_MILLIS_FIELD,
+                genericDocumentParcel.getTtlMillis());
+
+        // Properties
+        Bundle properties = new Bundle();
+        List<PropertyParcel> propertyParcels = genericDocumentParcel.getProperties();
+        for (int i = 0; i < propertyParcels.size(); ++i) {
+            PropertyParcel propertyParcel = propertyParcels.get(i);
+            properties.putParcelable(propertyParcel.getPropertyName(), propertyParcel);
+        }
+        genericDocumentParcelBundle.putBundle(PROPERTIES_FIELD, properties);
+
+        return genericDocumentParcelBundle;
+    }
+
+    @Nullable
+    @Override
+    public GenericDocumentParcel createFromParcel(Parcel in) {
+        Bundle bundle = in.readBundle(getClass().getClassLoader());
+        return createGenericDocumentParcelFromBundle(bundle);
+    }
+
+    @Override
+    public GenericDocumentParcel[] newArray(int size) {
+        return new GenericDocumentParcel[size];
+    }
+
+    /** Writes a {@link GenericDocumentParcel} to a {@link Parcel}. */
+    public static void writeToParcel(@NonNull GenericDocumentParcel genericDocumentParcel,
+            @NonNull android.os.Parcel parcel, int flags) {
+        parcel.writeBundle(createBundleFromGenericDocumentParcel(genericDocumentParcel));
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PackageIdentifierParcel.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PackageIdentifierParcel.java
new file mode 100644
index 0000000..f405f48
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PackageIdentifierParcel.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.safeparcel;
+
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.PackageIdentifier;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.core.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Holds data for a {@link PackageIdentifier}.
+ *
+ * TODO(b/275592563): This class is currently used in GetSchemaResponse as a bundle, and
+ * therefore needs to implement Parcelable directly. Reassess if this is still needed once
+ * VisibilityConfig becomes available, and if not we should switch to a SafeParcelable
+ * implementation instead.
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
[email protected](creator = "PackageIdentifierParcelCreator")
+@SuppressLint("BanParcelableUsage")
+public final class PackageIdentifierParcel extends AbstractSafeParcelable implements Parcelable {
+    @NonNull
+    public static final Parcelable.Creator<PackageIdentifierParcel> CREATOR =
+            new PackageIdentifierParcelCreator();
+
+    @Field(id = 1, getter = "getPackageName")
+    private final String mPackageName;
+    @Field(id = 2, getter = "getSha256Certificate")
+    private final byte[] mSha256Certificate;
+
+    /**
+     * Creates a unique identifier for a package.
+     *
+     * @see PackageIdentifier
+     */
+    @Constructor
+    public PackageIdentifierParcel(@Param(id = 1) @NonNull String packageName,
+            @Param(id = 2) @NonNull byte[] sha256Certificate) {
+        mPackageName = Preconditions.checkNotNull(packageName);
+        mSha256Certificate = Preconditions.checkNotNull(sha256Certificate);
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @NonNull
+    public byte[] getSha256Certificate() {
+        return mSha256Certificate;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PackageIdentifierParcel)) {
+            return false;
+        }
+        final PackageIdentifierParcel other = (PackageIdentifierParcel) obj;
+        return mPackageName.equals(other.mPackageName)
+                && Arrays.equals(mSha256Certificate, other.mSha256Certificate);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageName, Arrays.hashCode(mSha256Certificate));
+    }
+
+    @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        PackageIdentifierParcelCreator.writeToParcel(this, dest, flags);
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PackageIdentifierParcelCreator.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PackageIdentifierParcelCreator.java
new file mode 100644
index 0000000..16fe177
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PackageIdentifierParcelCreator.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.safeparcel;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.core.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * An implemented creator for {@link PackageIdentifierParcel}.
+ *
+ * <p>In Jetpack, {@link androidx.appsearch.app.PackageIdentifier} is serialized in a bundle for
+ * {@link androidx.appsearch.app.GetSchemaResponse}, and therefore needs to implement a real
+ * {@link Parcelable}.
+ */
+// @exportToFramework:skipFile()
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PackageIdentifierParcelCreator implements Parcelable.Creator<PackageIdentifierParcel> {
+    private static final String PACKAGE_NAME_FIELD = "packageName";
+    private static final String SHA256_CERTIFICATE_FIELD = "sha256Certificate";
+
+    public PackageIdentifierParcelCreator() {
+    }
+
+    /**
+     * Creates a {@link PackageIdentifierParcel} from a {@link Bundle}
+     */
+    @NonNull
+    private static PackageIdentifierParcel createPackageIdentifierFromBundle(
+            @NonNull Bundle packageIdentifierBundle) {
+        Objects.requireNonNull(packageIdentifierBundle);
+        String packageName =
+                Preconditions.checkNotNull(packageIdentifierBundle.getString(PACKAGE_NAME_FIELD));
+        byte[] sha256Certificate =
+                Preconditions.checkNotNull(
+                        packageIdentifierBundle.getByteArray(SHA256_CERTIFICATE_FIELD));
+
+        return new PackageIdentifierParcel(packageName, sha256Certificate);
+    }
+
+    /** Creates a {@link Bundle} from a {@link PackageIdentifierParcel}. */
+    @NonNull
+    private static Bundle createBundleFromPackageIdentifier(
+            @NonNull PackageIdentifierParcel packageIdentifierParcel) {
+        Objects.requireNonNull(packageIdentifierParcel);
+        Bundle packageIdentifierBundle = new Bundle();
+        packageIdentifierBundle.putString(PACKAGE_NAME_FIELD,
+                packageIdentifierParcel.getPackageName());
+        packageIdentifierBundle.putByteArray(SHA256_CERTIFICATE_FIELD,
+                packageIdentifierParcel.getSha256Certificate());
+
+        return packageIdentifierBundle;
+    }
+
+    @NonNull
+    @Override
+    public PackageIdentifierParcel createFromParcel(Parcel parcel) {
+        Bundle bundle = Preconditions.checkNotNull(parcel.readBundle(getClass().getClassLoader()));
+        return createPackageIdentifierFromBundle(bundle);
+    }
+
+    @NonNull
+    @Override
+    public PackageIdentifierParcel[] newArray(int size) {
+        return new PackageIdentifierParcel[size];
+    }
+
+    /** Writes a {@link PackageIdentifierParcel} to a {@link Parcel}. */
+    public static void writeToParcel(@NonNull PackageIdentifierParcel packageIdentifierParcel,
+            @NonNull android.os.Parcel parcel, int flags) {
+        parcel.writeBundle(createBundleFromPackageIdentifier(packageIdentifierParcel));
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyConfigParcel.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyConfigParcel.java
index 61029bd..96c53ae 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyConfigParcel.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyConfigParcel.java
@@ -17,6 +17,7 @@
 package androidx.appsearch.safeparcel;
 
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -27,10 +28,12 @@
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JoinableValueType;
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TokenizerType;
 import androidx.appsearch.safeparcel.stub.StubCreators.DocumentIndexingConfigParcelCreator;
+import androidx.appsearch.safeparcel.stub.StubCreators.EmbeddingIndexingConfigParcelCreator;
 import androidx.appsearch.safeparcel.stub.StubCreators.IntegerIndexingConfigParcelCreator;
 import androidx.appsearch.safeparcel.stub.StubCreators.JoinableConfigParcelCreator;
 import androidx.appsearch.safeparcel.stub.StubCreators.PropertyConfigParcelCreator;
 import androidx.appsearch.safeparcel.stub.StubCreators.StringIndexingConfigParcelCreator;
+import androidx.core.util.ObjectsCompat;
 
 import java.util.List;
 import java.util.Objects;
@@ -49,7 +52,8 @@
 @SafeParcelable.Class(creator = "PropertyConfigParcelCreator")
 public final class PropertyConfigParcel extends AbstractSafeParcelable {
     @NonNull
-    public static final PropertyConfigParcelCreator CREATOR = new PropertyConfigParcelCreator();
+    public static final Parcelable.Creator<PropertyConfigParcel> CREATOR =
+            new PropertyConfigParcelCreator();
 
     @Field(id = 1, getter = "getName")
     private final String mName;
@@ -63,23 +67,32 @@
     private final int mCardinality;
 
     @Field(id = 4, getter = "getSchemaType")
-    private final String mSchemaType;
+    @Nullable private final String mSchemaType;
 
     @Field(id = 5, getter = "getStringIndexingConfigParcel")
-    private final StringIndexingConfigParcel mStringIndexingConfigParcel;
+    @Nullable private final StringIndexingConfigParcel mStringIndexingConfigParcel;
 
     @Field(id = 6, getter = "getDocumentIndexingConfigParcel")
-    private final DocumentIndexingConfigParcel mDocumentIndexingConfigParcel;
+    @Nullable private final DocumentIndexingConfigParcel mDocumentIndexingConfigParcel;
 
     @Field(id = 7, getter = "getIntegerIndexingConfigParcel")
-    private final IntegerIndexingConfigParcel mIntegerIndexingConfigParcel;
+    @Nullable private final IntegerIndexingConfigParcel mIntegerIndexingConfigParcel;
 
     @Field(id = 8, getter = "getJoinableConfigParcel")
-    private final JoinableConfigParcel mJoinableConfigParcel;
+    @Nullable private final JoinableConfigParcel mJoinableConfigParcel;
+
+    @Field(id = 9, getter = "getDescription")
+    private final String mDescription;
+
+    @Field(id = 10, getter = "getEmbeddingIndexingConfigParcel")
+    private final EmbeddingIndexingConfigParcel mEmbeddingIndexingConfigParcel;
+
+    @Nullable
+    private Integer mHashCode;
 
     /** Constructor for {@link PropertyConfigParcel}. */
     @Constructor
-    public PropertyConfigParcel(
+    PropertyConfigParcel(
             @Param(id = 1) @NonNull String name,
             @Param(id = 2) @DataType int dataType,
             @Param(id = 3) @Cardinality int cardinality,
@@ -87,7 +100,9 @@
             @Param(id = 5) @Nullable StringIndexingConfigParcel stringIndexingConfigParcel,
             @Param(id = 6) @Nullable DocumentIndexingConfigParcel documentIndexingConfigParcel,
             @Param(id = 7) @Nullable IntegerIndexingConfigParcel integerIndexingConfigParcel,
-            @Param(id = 8) @Nullable JoinableConfigParcel joinableConfigParcel) {
+            @Param(id = 8) @Nullable JoinableConfigParcel joinableConfigParcel,
+            @Param(id = 9) @NonNull String description,
+            @Param(id = 10) @Nullable EmbeddingIndexingConfigParcel embeddingIndexingConfigParcel) {
         mName = Objects.requireNonNull(name);
         mDataType = dataType;
         mCardinality = cardinality;
@@ -96,6 +111,147 @@
         mDocumentIndexingConfigParcel = documentIndexingConfigParcel;
         mIntegerIndexingConfigParcel = integerIndexingConfigParcel;
         mJoinableConfigParcel = joinableConfigParcel;
+        mDescription = Objects.requireNonNull(description);
+        mEmbeddingIndexingConfigParcel = embeddingIndexingConfigParcel;
+    }
+
+    /** Creates a {@link PropertyConfigParcel} for String. */
+    @NonNull
+    public static PropertyConfigParcel createForString(
+            @NonNull String propertyName,
+            @NonNull String description,
+            @Cardinality int cardinality,
+            @NonNull StringIndexingConfigParcel stringIndexingConfigParcel,
+            @NonNull JoinableConfigParcel joinableConfigParcel) {
+        return new PropertyConfigParcel(
+                Objects.requireNonNull(propertyName),
+                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING,
+                cardinality,
+                /*schemaType=*/ null,
+                Objects.requireNonNull(stringIndexingConfigParcel),
+                /*documentIndexingConfigParcel=*/ null,
+                /*integerIndexingConfigParcel=*/ null,
+                Objects.requireNonNull(joinableConfigParcel),
+                Objects.requireNonNull(description),
+                /*embeddingIndexingConfigParcel=*/ null);
+    }
+
+    /** Creates a {@link PropertyConfigParcel} for Long. */
+    @NonNull
+    public static PropertyConfigParcel createForLong(
+            @NonNull String propertyName,
+            @NonNull String description,
+            @Cardinality int cardinality,
+            @AppSearchSchema.LongPropertyConfig.IndexingType int indexingType) {
+        return new PropertyConfigParcel(
+                Objects.requireNonNull(propertyName),
+                AppSearchSchema.PropertyConfig.DATA_TYPE_LONG,
+                cardinality,
+                /*schemaType=*/ null,
+                /*stringIndexingConfigParcel=*/ null,
+                /*documentIndexingConfigParcel=*/ null,
+                new IntegerIndexingConfigParcel(indexingType),
+                /*joinableConfigParcel=*/ null,
+                Objects.requireNonNull(description),
+                /*embeddingIndexingConfigParcel=*/ null);
+    }
+
+    /** Creates a {@link PropertyConfigParcel} for Double. */
+    @NonNull
+    public static PropertyConfigParcel createForDouble(
+            @NonNull String propertyName,
+            @NonNull String description,
+            @Cardinality int cardinality) {
+        return new PropertyConfigParcel(
+                Objects.requireNonNull(propertyName),
+                AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE,
+                cardinality,
+                /*schemaType=*/ null,
+                /*stringIndexingConfigParcel=*/ null,
+                /*documentIndexingConfigParcel=*/ null,
+                /*integerIndexingConfigParcel=*/ null,
+                /*joinableConfigParcel=*/ null,
+                Objects.requireNonNull(description),
+                /*embeddingIndexingConfigParcel=*/ null);
+    }
+
+    /** Creates a {@link PropertyConfigParcel} for Boolean. */
+    @NonNull
+    public static PropertyConfigParcel createForBoolean(
+            @NonNull String propertyName,
+            @NonNull String description,
+            @Cardinality int cardinality) {
+        return new PropertyConfigParcel(
+                Objects.requireNonNull(propertyName),
+                AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN,
+                cardinality,
+                /*schemaType=*/ null,
+                /*stringIndexingConfigParcel=*/ null,
+                /*documentIndexingConfigParcel=*/ null,
+                /*integerIndexingConfigParcel=*/ null,
+                /*joinableConfigParcel=*/ null,
+                Objects.requireNonNull(description),
+                /*embeddingIndexingConfigParcel=*/ null);
+    }
+
+    /** Creates a {@link PropertyConfigParcel} for Bytes. */
+    @NonNull
+    public static PropertyConfigParcel createForBytes(
+            @NonNull String propertyName,
+            @NonNull String description,
+            @Cardinality int cardinality) {
+        return new PropertyConfigParcel(
+                Objects.requireNonNull(propertyName),
+                AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES,
+                cardinality,
+                /*schemaType=*/ null,
+                /*stringIndexingConfigParcel=*/ null,
+                /*documentIndexingConfigParcel=*/ null,
+                /*integerIndexingConfigParcel=*/ null,
+                /*joinableConfigParcel=*/ null,
+                Objects.requireNonNull(description),
+                /*embeddingIndexingConfigParcel=*/ null);
+    }
+
+    /** Creates a {@link PropertyConfigParcel} for Document. */
+    @NonNull
+    public static PropertyConfigParcel createForDocument(
+            @NonNull String propertyName,
+            @NonNull String description,
+            @Cardinality int cardinality,
+            @NonNull String schemaType,
+            @NonNull DocumentIndexingConfigParcel documentIndexingConfigParcel) {
+        return new PropertyConfigParcel(
+                Objects.requireNonNull(propertyName),
+                AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT,
+                cardinality,
+                Objects.requireNonNull(schemaType),
+                /*stringIndexingConfigParcel=*/ null,
+                Objects.requireNonNull(documentIndexingConfigParcel),
+                /*integerIndexingConfigParcel=*/ null,
+                /*joinableConfigParcel=*/ null,
+                Objects.requireNonNull(description),
+                /*embeddingIndexingConfigParcel=*/ null);
+    }
+
+    /** Creates a {@link PropertyConfigParcel} for Embedding. */
+    @NonNull
+    public static PropertyConfigParcel createForEmbedding(
+            @NonNull String propertyName,
+            @NonNull String description,
+            @Cardinality int cardinality,
+            @AppSearchSchema.EmbeddingPropertyConfig.IndexingType int indexingType) {
+        return new PropertyConfigParcel(
+                Objects.requireNonNull(propertyName),
+                AppSearchSchema.PropertyConfig.DATA_TYPE_EMBEDDING,
+                cardinality,
+                /*schemaType=*/ null,
+                /*stringIndexingConfigParcel=*/ null,
+                /*documentIndexingConfigParcel=*/ null,
+                /*integerIndexingConfigParcel=*/ null,
+                /*joinableConfigParcel=*/ null,
+                Objects.requireNonNull(description),
+                new EmbeddingIndexingConfigParcel(indexingType));
     }
 
     /** Gets name for the property. */
@@ -104,6 +260,12 @@
         return mName;
     }
 
+    /** Gets description for the property. */
+    @NonNull
+    public String getDescription() {
+        return mDescription;
+    }
+
     /** Gets data type for the property. */
     @DataType
     public int getDataType() {
@@ -146,11 +308,84 @@
         return mJoinableConfigParcel;
     }
 
+    /** Gets the {@link EmbeddingIndexingConfigParcel}. */
+    @Nullable
+    public EmbeddingIndexingConfigParcel getEmbeddingIndexingConfigParcel() {
+        return mEmbeddingIndexingConfigParcel;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        PropertyConfigParcelCreator.writeToParcel(this, dest, flags);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof PropertyConfigParcel)) {
+            return false;
+        }
+        PropertyConfigParcel otherProperty = (PropertyConfigParcel) other;
+        return ObjectsCompat.equals(mName, otherProperty.mName)
+                && Objects.equals(mDescription, otherProperty.mDescription)
+                && ObjectsCompat.equals(mDataType, otherProperty.mDataType)
+                && ObjectsCompat.equals(mCardinality, otherProperty.mCardinality)
+                && ObjectsCompat.equals(mSchemaType, otherProperty.mSchemaType)
+                && ObjectsCompat.equals(
+                mStringIndexingConfigParcel, otherProperty.mStringIndexingConfigParcel)
+                && ObjectsCompat.equals(
+                mDocumentIndexingConfigParcel, otherProperty.mDocumentIndexingConfigParcel)
+                && ObjectsCompat.equals(
+                mIntegerIndexingConfigParcel, otherProperty.mIntegerIndexingConfigParcel)
+                && ObjectsCompat.equals(
+                mJoinableConfigParcel, otherProperty.mJoinableConfigParcel)
+                && ObjectsCompat.equals(
+                mEmbeddingIndexingConfigParcel, otherProperty.mEmbeddingIndexingConfigParcel);
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode =
+                ObjectsCompat.hash(
+                        mName,
+                        mDescription,
+                        mDataType,
+                        mCardinality,
+                        mSchemaType,
+                        mStringIndexingConfigParcel,
+                        mDocumentIndexingConfigParcel,
+                        mIntegerIndexingConfigParcel,
+                        mJoinableConfigParcel,
+                        mEmbeddingIndexingConfigParcel);
+        }
+        return mHashCode;
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "{name: " + mName
+                + ", description: " + mDescription
+                + ", dataType: " + mDataType
+                + ", cardinality: " + mCardinality
+                + ", schemaType: " + mSchemaType
+                + ", stringIndexingConfigParcel: " + mStringIndexingConfigParcel
+                + ", documentIndexingConfigParcel: " + mDocumentIndexingConfigParcel
+                + ", integerIndexingConfigParcel: " + mIntegerIndexingConfigParcel
+                + ", joinableConfigParcel: " + mJoinableConfigParcel
+                + ", embeddingIndexingConfigParcel: " + mEmbeddingIndexingConfigParcel
+                + "}";
+    }
+
     /** Class to hold join configuration for a String type. */
     @SafeParcelable.Class(creator = "JoinableConfigParcelCreator")
     public static class JoinableConfigParcel extends AbstractSafeParcelable {
         @NonNull
-        public static final JoinableConfigParcelCreator CREATOR = new JoinableConfigParcelCreator();
+        public static final Parcelable.Creator<JoinableConfigParcel> CREATOR =
+                new JoinableConfigParcelCreator();
 
         @JoinableValueType
         @Field(id = 1, getter = "getJoinableValueType")
@@ -183,13 +418,38 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             JoinableConfigParcelCreator.writeToParcel(this, dest, flags);
         }
+
+        @Override
+        public int hashCode() {
+            return ObjectsCompat.hash(mJoinableValueType, mDeletionPropagation);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof JoinableConfigParcel)) {
+                return false;
+            }
+            JoinableConfigParcel otherObject = (JoinableConfigParcel) other;
+            return ObjectsCompat.equals(mJoinableValueType, otherObject.mJoinableValueType)
+                    && ObjectsCompat.equals(mDeletionPropagation, otherObject.mDeletionPropagation);
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "{joinableValueType: " + mJoinableValueType
+                    + ", deletePropagation " + mDeletionPropagation + "}";
+        }
     }
 
     /** Class to hold configuration a string type. */
     @SafeParcelable.Class(creator = "StringIndexingConfigParcelCreator")
     public static class StringIndexingConfigParcel extends AbstractSafeParcelable {
         @NonNull
-        public static final StringIndexingConfigParcelCreator CREATOR =
+        public static final Parcelable.Creator<StringIndexingConfigParcel> CREATOR =
                 new StringIndexingConfigParcelCreator();
 
         @AppSearchSchema.StringPropertyConfig.IndexingType
@@ -225,13 +485,38 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             StringIndexingConfigParcelCreator.writeToParcel(this, dest, flags);
         }
+
+        @Override
+        public int hashCode() {
+            return ObjectsCompat.hash(mIndexingType, mTokenizerType);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof StringIndexingConfigParcel)) {
+                return false;
+            }
+            StringIndexingConfigParcel otherObject = (StringIndexingConfigParcel) other;
+            return ObjectsCompat.equals(mIndexingType, otherObject.mIndexingType)
+                    && ObjectsCompat.equals(mTokenizerType, otherObject.mTokenizerType);
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "{indexingType: " + mIndexingType
+                    + ", tokenizerType " + mTokenizerType + "}";
+        }
     }
 
     /** Class to hold configuration for integer property type. */
     @SafeParcelable.Class(creator = "IntegerIndexingConfigParcelCreator")
     public static class IntegerIndexingConfigParcel extends AbstractSafeParcelable {
         @NonNull
-        public static final IntegerIndexingConfigParcelCreator CREATOR =
+        public static final Parcelable.Creator<IntegerIndexingConfigParcel> CREATOR =
                 new IntegerIndexingConfigParcelCreator();
 
         @AppSearchSchema.LongPropertyConfig.IndexingType
@@ -255,13 +540,36 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             IntegerIndexingConfigParcelCreator.writeToParcel(this, dest, flags);
         }
+
+        @Override
+        public int hashCode() {
+            return ObjectsCompat.hashCode(mIndexingType);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof IntegerIndexingConfigParcel)) {
+                return false;
+            }
+            IntegerIndexingConfigParcel otherObject = (IntegerIndexingConfigParcel) other;
+            return ObjectsCompat.equals(mIndexingType, otherObject.mIndexingType);
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "{indexingType: " + mIndexingType + "}";
+        }
     }
 
     /** Class to hold configuration for document property type. */
     @SafeParcelable.Class(creator = "DocumentIndexingConfigParcelCreator")
     public static class DocumentIndexingConfigParcel extends AbstractSafeParcelable {
         @NonNull
-        public static final DocumentIndexingConfigParcelCreator CREATOR =
+        public static final Parcelable.Creator<DocumentIndexingConfigParcel> CREATOR =
                 new DocumentIndexingConfigParcelCreator();
 
         @Field(id = 1, getter = "shouldIndexNestedProperties")
@@ -295,10 +603,86 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             DocumentIndexingConfigParcelCreator.writeToParcel(this, dest, flags);
         }
+
+        @Override
+        public int hashCode() {
+            return ObjectsCompat.hash(mIndexNestedProperties, mIndexableNestedPropertiesList);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof DocumentIndexingConfigParcel)) {
+                return false;
+            }
+            DocumentIndexingConfigParcel otherObject = (DocumentIndexingConfigParcel) other;
+            return ObjectsCompat.equals(mIndexNestedProperties, otherObject.mIndexNestedProperties)
+                    && ObjectsCompat.equals(mIndexableNestedPropertiesList,
+                    otherObject.mIndexableNestedPropertiesList);
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "{indexNestedProperties: " + mIndexNestedProperties
+                    + ", indexableNestedPropertiesList: " + mIndexableNestedPropertiesList
+                    + "}";
+        }
     }
 
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        PropertyConfigParcelCreator.writeToParcel(this, dest, flags);
+    /** Class to hold configuration for embedding property. */
+    @SafeParcelable.Class(creator = "EmbeddingIndexingConfigParcelCreator")
+    public static class EmbeddingIndexingConfigParcel extends AbstractSafeParcelable {
+        @NonNull
+        public static final Parcelable.Creator<EmbeddingIndexingConfigParcel> CREATOR =
+                new EmbeddingIndexingConfigParcelCreator();
+
+        @AppSearchSchema.EmbeddingPropertyConfig.IndexingType
+        @Field(id = 1, getter = "getIndexingType")
+        private final int mIndexingType;
+
+        /** Constructor for {@link EmbeddingIndexingConfigParcel}. */
+        @Constructor
+        public EmbeddingIndexingConfigParcel(
+                @Param(id = 1) @AppSearchSchema.EmbeddingPropertyConfig.IndexingType
+                int indexingType) {
+            mIndexingType = indexingType;
+        }
+
+        /** Gets the indexing type for this embedding property. */
+        @AppSearchSchema.EmbeddingPropertyConfig.IndexingType
+        public int getIndexingType() {
+            return mIndexingType;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            EmbeddingIndexingConfigParcelCreator.writeToParcel(this, dest, flags);
+        }
+
+        @Override
+        public int hashCode() {
+            return ObjectsCompat.hashCode(mIndexingType);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof EmbeddingIndexingConfigParcel)) {
+                return false;
+            }
+            EmbeddingIndexingConfigParcel otherObject = (EmbeddingIndexingConfigParcel) other;
+            return ObjectsCompat.equals(mIndexingType, otherObject.mIndexingType);
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "{indexingType: " + mIndexingType + "}";
+        }
     }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyParcel.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyParcel.java
index 3762adc..3e4fb3a 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyParcel.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyParcel.java
@@ -17,12 +17,15 @@
 package androidx.appsearch.safeparcel;
 
 
+import android.annotation.SuppressLint;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
-import androidx.appsearch.safeparcel.stub.StubCreators.PropertyParcelCreator;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.app.EmbeddingVector;
 
 import java.util.Arrays;
 import java.util.Objects;
@@ -36,8 +39,11 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @SafeParcelable.Class(creator = "PropertyParcelCreator")
-public final class PropertyParcel extends AbstractSafeParcelable {
-    @NonNull public static final PropertyParcelCreator CREATOR = new PropertyParcelCreator();
+// This won't be used to send data over binder, and we have to use Parcelable for code sync purpose.
+@SuppressLint("BanParcelableUsage")
+public final class PropertyParcel extends AbstractSafeParcelable implements Parcelable {
+    @NonNull public static final Parcelable.Creator<PropertyParcel> CREATOR =
+            new PropertyParcelCreator();
 
     @NonNull
     @Field(id = 1, getter = "getPropertyName")
@@ -67,6 +73,10 @@
     @Field(id = 7, getter = "getDocumentValues")
     private final GenericDocumentParcel[] mDocumentValues;
 
+    @Nullable
+    @Field(id = 8, getter = "getEmbeddingValues")
+    private final EmbeddingVector[] mEmbeddingValues;
+
     @Nullable private Integer mHashCode;
 
     @Constructor
@@ -77,7 +87,8 @@
             @Param(id = 4) @Nullable double[] doubleValues,
             @Param(id = 5) @Nullable boolean[] booleanValues,
             @Param(id = 6) @Nullable byte[][] bytesValues,
-            @Param(id = 7) @Nullable GenericDocumentParcel[] documentValues) {
+            @Param(id = 7) @Nullable GenericDocumentParcel[] documentValues,
+            @Param(id = 8) @Nullable EmbeddingVector[] embeddingValues) {
         mPropertyName = Objects.requireNonNull(propertyName);
         mStringValues = stringValues;
         mLongValues = longValues;
@@ -85,6 +96,7 @@
         mBooleanValues = booleanValues;
         mBytesValues = bytesValues;
         mDocumentValues = documentValues;
+        mEmbeddingValues = embeddingValues;
         checkOnlyOneArrayCanBeSet();
     }
 
@@ -130,6 +142,12 @@
         return mDocumentValues;
     }
 
+    /** Returns {@link EmbeddingVector}s in an array. */
+    @Nullable
+    public EmbeddingVector[] getEmbeddingValues() {
+        return mEmbeddingValues;
+    }
+
     /**
      * Returns the held values in an array for this property.
      *
@@ -155,6 +173,9 @@
         if (mDocumentValues != null) {
             return mDocumentValues;
         }
+        if (mEmbeddingValues != null) {
+            return mEmbeddingValues;
+        }
         return null;
     }
 
@@ -183,6 +204,9 @@
         if (mDocumentValues != null) {
             ++notNullCount;
         }
+        if (mEmbeddingValues != null) {
+            ++notNullCount;
+        }
         if (notNullCount == 0 || notNullCount > 1) {
             throw new IllegalArgumentException(
                     "One and only one type array can be set in PropertyParcel");
@@ -205,6 +229,8 @@
                 hashCode = Arrays.deepHashCode(mBytesValues);
             } else if (mDocumentValues != null) {
                 hashCode = Arrays.hashCode(mDocumentValues);
+            } else if (mEmbeddingValues != null) {
+                hashCode = Arrays.deepHashCode(mEmbeddingValues);
             }
             mHashCode = Objects.hash(mPropertyName, hashCode);
         }
@@ -228,7 +254,13 @@
                 && Arrays.equals(mDoubleValues, otherPropertyParcel.mDoubleValues)
                 && Arrays.equals(mBooleanValues, otherPropertyParcel.mBooleanValues)
                 && Arrays.deepEquals(mBytesValues, otherPropertyParcel.mBytesValues)
-                && Arrays.equals(mDocumentValues, otherPropertyParcel.mDocumentValues);
+                && Arrays.equals(mDocumentValues, otherPropertyParcel.mDocumentValues)
+                && Arrays.deepEquals(mEmbeddingValues, otherPropertyParcel.mEmbeddingValues);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        PropertyParcelCreator.writeToParcel(this, dest, flags);
     }
 
     /** Builder for {@link PropertyParcel}. */
@@ -240,12 +272,14 @@
         private boolean[] mBooleanValues;
         private byte[][] mBytesValues;
         private GenericDocumentParcel[] mDocumentValues;
+        private EmbeddingVector[] mEmbeddingValues;
 
         public Builder(@NonNull String propertyName) {
             mPropertyName = Objects.requireNonNull(propertyName);
         }
 
         /** Sets String values. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setStringValues(@NonNull String[] stringValues) {
             mStringValues = Objects.requireNonNull(stringValues);
@@ -253,6 +287,7 @@
         }
 
         /** Sets long values. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setLongValues(@NonNull long[] longValues) {
             mLongValues = Objects.requireNonNull(longValues);
@@ -260,6 +295,7 @@
         }
 
         /** Sets double values. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setDoubleValues(@NonNull double[] doubleValues) {
             mDoubleValues = Objects.requireNonNull(doubleValues);
@@ -267,6 +303,7 @@
         }
 
         /** Sets boolean values. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setBooleanValues(@NonNull boolean[] booleanValues) {
             mBooleanValues = Objects.requireNonNull(booleanValues);
@@ -274,6 +311,7 @@
         }
 
         /** Sets a two dimension byte array. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setBytesValues(@NonNull byte[][] bytesValues) {
             mBytesValues = Objects.requireNonNull(bytesValues);
@@ -281,12 +319,21 @@
         }
 
         /** Sets document values. */
+        @CanIgnoreReturnValue
         @NonNull
         public Builder setDocumentValues(@NonNull GenericDocumentParcel[] documentValues) {
             mDocumentValues = Objects.requireNonNull(documentValues);
             return this;
         }
 
+        /** Sets embedding values. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setEmbeddingValues(@NonNull EmbeddingVector[] embeddingValues) {
+            mEmbeddingValues = Objects.requireNonNull(embeddingValues);
+            return this;
+        }
+
         /** Builds a {@link PropertyParcel}. */
         @NonNull
         public PropertyParcel build() {
@@ -297,12 +344,8 @@
                     mDoubleValues,
                     mBooleanValues,
                     mBytesValues,
-                    mDocumentValues);
+                    mDocumentValues,
+                    mEmbeddingValues);
         }
     }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        PropertyParcelCreator.writeToParcel(this, dest, flags);
-    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyParcelCreator.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyParcelCreator.java
new file mode 100644
index 0000000..46be473
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/PropertyParcelCreator.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.safeparcel;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.EmbeddingVector;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An implemented creator for {@link PropertyParcel}.
+ *
+ * <p>In Jetpack, in order to serialize
+ * {@link GenericDocumentParcel} for {@link androidx.appsearch.app.GenericDocument},
+ * {@link PropertyParcel} needs to be a real {@link Parcelable}.
+ */
+// @exportToFramework:skipFile()
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PropertyParcelCreator implements Parcelable.Creator<PropertyParcel> {
+    private static final String PROPERTY_NAME_FIELD = "propertyName";
+    private static final String STRING_ARRAY_FIELD = "stringArray";
+    private static final String LONG_ARRAY_FIELD = "longArray";
+    private static final String DOUBLE_ARRAY_FIELD = "doubleArray";
+    private static final String BOOLEAN_ARRAY_FIELD = "booleanArray";
+    // 1d
+    private static final String BYTE_ARRAY_FIELD = "byteArray";
+    // 2d
+    private static final String BYTES_ARRAY_FIELD = "bytesArray";
+    private static final String DOC_ARRAY_FIELD = "docArray";
+    private static final String EMBEDDING_VALUE_FIELD = "embeddingValue";
+    private static final String EMBEDDING_MODEL_SIGNATURE_FIELD = "embeddingModelSignature";
+    private static final String EMBEDDING_ARRAY_FIELD = "embeddingArray";
+
+    public PropertyParcelCreator() {
+    }
+
+    /** Creates a {@link PropertyParcel} from a {@link Bundle}. */
+    @SuppressWarnings({"unchecked"})
+    @NonNull
+    private static PropertyParcel createPropertyParcelFromBundle(
+            @NonNull Bundle propertyParcelBundle) {
+        Objects.requireNonNull(propertyParcelBundle);
+        String propertyName = propertyParcelBundle.getString(PROPERTY_NAME_FIELD);
+
+        Objects.requireNonNull(propertyName);
+        PropertyParcel.Builder builder = new PropertyParcel.Builder(propertyName);
+
+        // Get the values out of the bundle.
+        String[] stringValues = propertyParcelBundle.getStringArray(STRING_ARRAY_FIELD);
+        long[] longValues = propertyParcelBundle.getLongArray(LONG_ARRAY_FIELD);
+        double[] doubleValues = propertyParcelBundle.getDoubleArray(DOUBLE_ARRAY_FIELD);
+        boolean[] booleanValues = propertyParcelBundle.getBooleanArray(BOOLEAN_ARRAY_FIELD);
+
+        List<Bundle> bytesArray;
+        // SuppressWarnings can be applied on a local variable, but not any single line of
+        // code.
+        @SuppressWarnings("deprecation")
+        List<Bundle> tmpList = propertyParcelBundle.getParcelableArrayList(BYTES_ARRAY_FIELD);
+        bytesArray = tmpList;
+
+        Parcelable[] docValues;
+        // SuppressWarnings can be applied on a local variable, but not any single line of
+        // code.
+        @SuppressWarnings("deprecation")
+        Parcelable[] tmpParcel = propertyParcelBundle.getParcelableArray(DOC_ARRAY_FIELD);
+        docValues = tmpParcel;
+
+        // SuppressWarnings can be applied on a local variable, but not any single line of
+        // code.
+        @SuppressWarnings("deprecation")
+        List<Bundle> embeddingArray = propertyParcelBundle.getParcelableArrayList(
+                EMBEDDING_ARRAY_FIELD);
+
+        // Only one of those values will be set.
+        boolean valueSet = false;
+        if (stringValues != null) {
+            builder.setStringValues(stringValues);
+            valueSet = true;
+        } else if (longValues != null) {
+            builder.setLongValues(longValues);
+            valueSet = true;
+        } else if (doubleValues != null) {
+            builder.setDoubleValues(doubleValues);
+            valueSet = true;
+        } else if (booleanValues != null) {
+            builder.setBooleanValues(booleanValues);
+            valueSet = true;
+        } else if (bytesArray != null) {
+            byte[][] bytes = new byte[bytesArray.size()][];
+            for (int i = 0; i < bytesArray.size(); i++) {
+                Bundle byteArray = bytesArray.get(i);
+                if (byteArray == null) {
+                    continue;
+                }
+                byte[] innerBytes = byteArray.getByteArray(BYTE_ARRAY_FIELD);
+                if (innerBytes == null) {
+                    continue;
+                }
+                bytes[i] = innerBytes;
+            }
+            builder.setBytesValues(bytes);
+            valueSet = true;
+        } else if (docValues != null && docValues.length > 0) {
+            GenericDocumentParcel[] documentParcels =
+                    new GenericDocumentParcel[docValues.length];
+            System.arraycopy(docValues, 0, documentParcels, 0, docValues.length);
+            builder.setDocumentValues(documentParcels);
+            valueSet = true;
+        } else if (embeddingArray != null) {
+            EmbeddingVector[] embeddings = new EmbeddingVector[embeddingArray.size()];
+            for (int i = 0; i < embeddingArray.size(); i++) {
+                Bundle embeddingBundle = embeddingArray.get(i);
+                if (embeddingBundle == null) {
+                    continue;
+                }
+                float[] values = embeddingBundle.getFloatArray(EMBEDDING_VALUE_FIELD);
+                String modelSignature = embeddingBundle.getString(EMBEDDING_MODEL_SIGNATURE_FIELD);
+                if (values == null || modelSignature == null) {
+                    continue;
+                }
+                embeddings[i] = new EmbeddingVector(values, modelSignature);
+            }
+            builder.setEmbeddingValues(embeddings);
+            valueSet = true;
+        }
+
+        if (!valueSet) {
+            throw new IllegalArgumentException("property bundle passed in doesn't have any "
+                    + "value set.");
+        }
+
+        return builder.build();
+    }
+
+    /** Creates a {@link Bundle} from a {@link PropertyParcel}. */
+    @NonNull
+    private static Bundle createBundleFromPropertyParcel(
+            @NonNull PropertyParcel propertyParcel) {
+        Objects.requireNonNull(propertyParcel);
+        Bundle propertyParcelBundle = new Bundle();
+        propertyParcelBundle.putString(PROPERTY_NAME_FIELD, propertyParcel.getPropertyName());
+
+        // Check and set the properties
+        String[] stringValues = propertyParcel.getStringValues();
+        long[] longValues = propertyParcel.getLongValues();
+        double[] doubleValues = propertyParcel.getDoubleValues();
+        boolean[] booleanValues = propertyParcel.getBooleanValues();
+        byte[][] bytesArray = propertyParcel.getBytesValues();
+        GenericDocumentParcel[] docArray = propertyParcel.getDocumentValues();
+        EmbeddingVector[] embeddingArray = propertyParcel.getEmbeddingValues();
+
+        if (stringValues != null) {
+            propertyParcelBundle.putStringArray(STRING_ARRAY_FIELD, stringValues);
+        } else if (longValues != null) {
+            propertyParcelBundle.putLongArray(LONG_ARRAY_FIELD, longValues);
+        } else if (doubleValues != null) {
+            propertyParcelBundle.putDoubleArray(DOUBLE_ARRAY_FIELD, doubleValues);
+        } else if (booleanValues != null) {
+            propertyParcelBundle.putBooleanArray(BOOLEAN_ARRAY_FIELD, booleanValues);
+        } else if (bytesArray != null) {
+            ArrayList<Bundle> bundles = new ArrayList<>(bytesArray.length);
+            for (int i = 0; i < bytesArray.length; i++) {
+                Bundle byteArray = new Bundle();
+                byteArray.putByteArray(BYTE_ARRAY_FIELD, bytesArray[i]);
+                bundles.add(byteArray);
+            }
+            propertyParcelBundle.putParcelableArrayList(BYTES_ARRAY_FIELD, bundles);
+        } else if (docArray != null) {
+            propertyParcelBundle.putParcelableArray(DOC_ARRAY_FIELD, docArray);
+        } else if (embeddingArray != null) {
+            ArrayList<Bundle> bundles = new ArrayList<>(embeddingArray.length);
+            for (int i = 0; i < embeddingArray.length; i++) {
+                Bundle embedding = new Bundle();
+                embedding.putFloatArray(EMBEDDING_VALUE_FIELD, embeddingArray[i].getValues());
+                embedding.putString(EMBEDDING_MODEL_SIGNATURE_FIELD,
+                        embeddingArray[i].getModelSignature());
+                bundles.add(embedding);
+            }
+            propertyParcelBundle.putParcelableArrayList(EMBEDDING_ARRAY_FIELD, bundles);
+        }
+
+        return propertyParcelBundle;
+    }
+
+    @NonNull
+    @Override
+    public PropertyParcel createFromParcel(Parcel in) {
+        Bundle bundle = in.readBundle(getClass().getClassLoader());
+        return createPropertyParcelFromBundle(bundle);
+    }
+
+    @Override
+    public PropertyParcel[] newArray(int size) {
+        return new PropertyParcel[size];
+    }
+
+    /** Writes a {@link PropertyParcel} to a {@link Parcel}. */
+    public static void writeToParcel(@NonNull PropertyParcel propertyParcel,
+            @NonNull android.os.Parcel parcel, int flags) {
+        parcel.writeBundle(createBundleFromPropertyParcel(propertyParcel));
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/SafeParcelable.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/SafeParcelable.java
index fa70911..8a25a36 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/SafeParcelable.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/SafeParcelable.java
@@ -74,4 +74,7 @@
          */
         boolean doNotParcelTypeDefaultValues() default false;
     }
+
+    /** Provide same interface as {@link android.os.Parcelable} for code sync purpose. */
+    int describeContents();
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/AbstractCreator.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/AbstractCreator.java
index 22755d0..53afaa7 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/AbstractCreator.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/AbstractCreator.java
@@ -17,6 +17,7 @@
 package androidx.appsearch.safeparcel.stub;
 
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
@@ -31,7 +32,21 @@
  */
 // @exportToFramework:skipFile()
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-abstract class AbstractCreator {
+abstract class AbstractCreator<T> implements Parcelable.Creator<T> {
+    @Override
+    public T createFromParcel(Parcel var1) {
+        // This is here only for code sync purpose.
+        throw new UnsupportedOperationException("createFromParcel is not implemented and should "
+                + "not be used.");
+    }
+
+    @Override
+    public T[] newArray(int var1) {
+        // This is here only for code sync purpose.
+        throw new UnsupportedOperationException("newArray is not implemented and should "
+                + "not be used.");
+    }
+
     public static void writeToParcel(
             @NonNull SafeParcelable safeParcelable,
             @NonNull Parcel parcel,
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
index 97887bf..48d197c 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
@@ -16,9 +16,34 @@
 package androidx.appsearch.safeparcel.stub;
 
 import androidx.annotation.RestrictTo;
-import androidx.appsearch.safeparcel.GenericDocumentParcel;
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.EmbeddingVector;
+import androidx.appsearch.app.GetByDocumentIdRequest;
+import androidx.appsearch.app.GetSchemaResponse;
+import androidx.appsearch.app.InternalSetSchemaResponse;
+import androidx.appsearch.app.InternalVisibilityConfig;
+import androidx.appsearch.app.JoinSpec;
+import androidx.appsearch.app.RemoveByDocumentIdRequest;
+import androidx.appsearch.app.ReportUsageRequest;
+import androidx.appsearch.app.SchemaVisibilityConfig;
+import androidx.appsearch.app.SearchResult;
+import androidx.appsearch.app.SearchResult.MatchInfo;
+import androidx.appsearch.app.SearchResultPage;
+import androidx.appsearch.app.SearchSpec;
+import androidx.appsearch.app.SearchSuggestionResult;
+import androidx.appsearch.app.SearchSuggestionSpec;
+import androidx.appsearch.app.SetSchemaResponse;
+import androidx.appsearch.app.SetSchemaResponse.MigrationFailure;
+import androidx.appsearch.app.StorageInfo;
+import androidx.appsearch.app.VisibilityPermissionConfig;
+import androidx.appsearch.observer.ObserverSpec;
 import androidx.appsearch.safeparcel.PropertyConfigParcel;
-import androidx.appsearch.safeparcel.PropertyParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.DocumentIndexingConfigParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.EmbeddingIndexingConfigParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.IntegerIndexingConfigParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.JoinableConfigParcel;
+import androidx.appsearch.safeparcel.PropertyConfigParcel.StringIndexingConfigParcel;
+import androidx.appsearch.stats.SchemaMigrationStats;
 
 /**
  * Stub creators for any classes extending
@@ -29,61 +54,145 @@
  * be provided for code sync purpose.
  */
 // @exportToFramework:skipFile()
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RestrictTo(RestrictTo.Scope.LIBRARY)
 public class StubCreators {
     /** Stub creator for {@link androidx.appsearch.app.StorageInfo}. */
-    public static class StorageInfoCreator extends AbstractCreator {
-    }
-
-    /** Stub creator for {@link PropertyParcel}. */
-    public static class PropertyParcelCreator extends AbstractCreator {
+    public static class StorageInfoCreator extends AbstractCreator<StorageInfo> {
     }
 
     /** Stub creator for {@link PropertyConfigParcel}. */
-    public static class PropertyConfigParcelCreator extends AbstractCreator {
+    public static class PropertyConfigParcelCreator extends AbstractCreator<PropertyConfigParcel> {
     }
 
     /**
      * Stub creator for
      * {@link PropertyConfigParcel.JoinableConfigParcel}.
      */
-    public static class JoinableConfigParcelCreator extends AbstractCreator {
+    public static class JoinableConfigParcelCreator extends AbstractCreator<JoinableConfigParcel> {
     }
 
     /**
      * Stub creator for
      * {@link PropertyConfigParcel.StringIndexingConfigParcel}.
      */
-    public static class StringIndexingConfigParcelCreator extends AbstractCreator {
+    public static class StringIndexingConfigParcelCreator extends
+            AbstractCreator<StringIndexingConfigParcel> {
     }
 
     /**
      * Stub creator for
      * {@link PropertyConfigParcel.IntegerIndexingConfigParcel}.
      */
-    public static class IntegerIndexingConfigParcelCreator extends AbstractCreator {
+    public static class IntegerIndexingConfigParcelCreator extends
+            AbstractCreator<IntegerIndexingConfigParcel> {
     }
 
     /**
      * Stub creator for
      * {@link PropertyConfigParcel.DocumentIndexingConfigParcel}.
      */
-    public static class DocumentIndexingConfigParcelCreator extends AbstractCreator {
+    public static class DocumentIndexingConfigParcelCreator extends
+            AbstractCreator<DocumentIndexingConfigParcel> {
     }
 
-    /** Stub creator for {@link GenericDocumentParcel}. */
-    public static class GenericDocumentParcelCreator extends AbstractCreator {
+    /** Stub creator for {@link SchemaVisibilityConfig}. */
+    public static class VisibilityConfigCreator extends AbstractCreator<SchemaVisibilityConfig> {
     }
 
-    /** Stub creator for {@link androidx.appsearch.app.VisibilityPermissionDocument}. */
-    public static class VisibilityPermissionDocumentCreator extends AbstractCreator {
+    /**
+     * Stub creator for {@link EmbeddingIndexingConfigParcel}.
+     */
+    public static class EmbeddingIndexingConfigParcelCreator extends
+            AbstractCreator<EmbeddingIndexingConfigParcel> {
     }
 
-    /** Stub creator for {@link androidx.appsearch.app.VisibilityDocument}. */
-    public static class VisibilityDocumentCreator extends AbstractCreator {
+    /** Stub creator for {@link InternalVisibilityConfig}. */
+    public static class InternalVisibilityConfigCreator
+            extends AbstractCreator<InternalVisibilityConfig> {
+    }
+
+    /** Stub creator for {@link VisibilityPermissionConfig}. */
+    public static class VisibilityPermissionConfigCreator extends
+            AbstractCreator<VisibilityPermissionConfig> {
     }
 
     /** Stub creator for {@link androidx.appsearch.stats.SchemaMigrationStats}. */
-    public static class SchemaMigrationStatsCreator extends AbstractCreator {
+    public static class SchemaMigrationStatsCreator extends AbstractCreator<SchemaMigrationStats> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.SearchSuggestionResult}. */
+    public static class SearchSuggestionResultCreator extends
+            AbstractCreator<SearchSuggestionResult> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.SearchSuggestionSpec}. */
+    public static class SearchSuggestionSpecCreator extends AbstractCreator<SearchSuggestionSpec> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.observer.ObserverSpec}. */
+    public static class ObserverSpecCreator extends AbstractCreator<ObserverSpec> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.SetSchemaResponse}. */
+    public static class SetSchemaResponseCreator extends
+            AbstractCreator<SetSchemaResponse> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.SetSchemaResponse.MigrationFailure}. */
+    public static class MigrationFailureCreator extends
+            AbstractCreator<MigrationFailure> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.InternalSetSchemaResponse}. */
+    public static class InternalSetSchemaResponseCreator extends
+            AbstractCreator<InternalSetSchemaResponse> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.SearchSpec}. */
+    public static class SearchSpecCreator extends AbstractCreator<SearchSpec> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.JoinSpec}. */
+    public static class JoinSpecCreator extends AbstractCreator<JoinSpec> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.GetSchemaResponse}. */
+    public static class GetSchemaResponseCreator extends AbstractCreator<GetSchemaResponse> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.AppSearchSchema}. */
+    public static class AppSearchSchemaCreator extends
+            AbstractCreator<AppSearchSchema> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.SearchResult}. */
+    public static class SearchResultCreator extends AbstractCreator<SearchResult> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.MatchInfo}. */
+    public static class MatchInfoCreator extends AbstractCreator<MatchInfo> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.SearchResultPage}. */
+    public static class SearchResultPageCreator extends AbstractCreator<SearchResultPage> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.RemoveByDocumentIdRequest}. */
+    public static class RemoveByDocumentIdRequestCreator extends
+            AbstractCreator<RemoveByDocumentIdRequest> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.ReportUsageRequest}. */
+    public static class ReportUsageRequestCreator extends AbstractCreator<ReportUsageRequest> {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.GetByDocumentIdRequest}. */
+    public static class GetByDocumentIdRequestCreator extends
+            AbstractCreator<GetByDocumentIdRequest> {
+    }
+
+    /** Stub creator for {@link EmbeddingVector}. */
+    public static class EmbeddingVectorCreator extends
+            AbstractCreator<EmbeddingVector> {
     }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java b/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
index 0a24293..91b74e7 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
@@ -17,6 +17,7 @@
 package androidx.appsearch.stats;
 
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -40,10 +41,10 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @SafeParcelable.Class(creator = "SchemaMigrationStatsCreator")
 public final class SchemaMigrationStats extends AbstractSafeParcelable {
-    @NonNull public static final SchemaMigrationStatsCreator CREATOR =
+    @NonNull public static final Parcelable.Creator<SchemaMigrationStats> CREATOR =
             new SchemaMigrationStatsCreator();
 
-    // Indicate the how a SetSchema call relative to SchemaMigration case.
+    /** Indicate the SetSchema call type relative to SchemaMigration case. */
     @IntDef(
             value = {
                     NO_MIGRATION,
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/ActionConstants.java b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/ActionConstants.java
new file mode 100644
index 0000000..0fadd2c
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/ActionConstants.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.usagereporting;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * Wrapper class for action constants.
+ *
+ * @exportToFramework:hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class ActionConstants {
+    /**
+     * Unknown action type.
+     *
+     * <p>It is defined for abstract action class and compatibility, so it should not be used in any
+     * concrete instances.
+     */
+    public static final int ACTION_TYPE_UNKNOWN = 0;
+
+    /**
+     * Search action type.
+     *
+     * <!--@exportToFramework:ifJetpack()-->
+     * <p>It is the action type for {@link SearchAction}.
+     * <!--@exportToFramework:else()-->
+     */
+    public static final int ACTION_TYPE_SEARCH = 1;
+
+    /**
+     * Click action type.
+     *
+     * <!--@exportToFramework:ifJetpack()-->
+     * <p>It is the action type for {@link ClickAction}.
+     * <!--@exportToFramework:else()-->
+     */
+    public static final int ACTION_TYPE_CLICK = 2;
+
+    private ActionConstants() {}
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/ClickAction.java b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/ClickAction.java
new file mode 100644
index 0000000..e69320a
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/ClickAction.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+
+package androidx.appsearch.usagereporting;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresFeature;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.annotation.Document;
+import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+import androidx.appsearch.app.Features;
+import androidx.core.util.Preconditions;
+
+/**
+ * {@link ClickAction} is a built-in AppSearch document type that contains different metrics.
+ * Clients can report the user's click actions on a {@link androidx.appsearch.app.SearchResult}
+ * document.
+ *
+ * <p>In order to use this document type, the client must explicitly set this schema type via
+ * {@link androidx.appsearch.app.SetSchemaRequest.Builder#addDocumentClasses}.
+ *
+ * <p>Click actions can be used as signals to boost ranking via
+ * {@link androidx.appsearch.app.JoinSpec} API in future search requests.
+ *
+ * <p>Since {@link ClickAction} is an AppSearch document, the client can handle deletion via
+ * {@link androidx.appsearch.app.AppSearchSession#removeAsync} or document time-to-live (TTL). The
+ * default TTL is 60 days.
+ */
+// In ClickAction document, there is a joinable property "referencedQualifiedId" for reporting the
+// qualified id of the clicked document. The client can create personal navboost with click action
+// signals by join query with this property. Therefore, ClickAction document class requires join
+// feature.
+@RequiresFeature(
+        enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+        name = Features.JOIN_SPEC_AND_QUALIFIED_ID)
+@Document(name = "builtin:ClickAction")
+public class ClickAction extends TakenAction {
+    @Nullable
+    @Document.StringProperty(indexingType = StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+    private final String mQuery;
+
+    @Nullable
+    @Document.StringProperty(joinableValueType =
+            StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+    private final String mReferencedQualifiedId;
+
+    @Document.LongProperty
+    private final int mResultRankInBlock;
+
+    @Document.LongProperty
+    private final int mResultRankGlobal;
+
+    @Document.LongProperty
+    private final long mTimeStayOnResultMillis;
+
+    ClickAction(@NonNull String namespace, @NonNull String id, long documentTtlMillis,
+            long actionTimestampMillis, @TakenAction.ActionType int actionType,
+            @Nullable String query, @Nullable String referencedQualifiedId, int resultRankInBlock,
+            int resultRankGlobal, long timeStayOnResultMillis) {
+        super(namespace, id, documentTtlMillis, actionTimestampMillis, actionType);
+
+        mQuery = query;
+        mReferencedQualifiedId = referencedQualifiedId;
+        mResultRankInBlock = resultRankInBlock;
+        mResultRankGlobal = resultRankGlobal;
+        mTimeStayOnResultMillis = timeStayOnResultMillis;
+    }
+
+    /**
+     * Returns the user-entered search input (without any operators or rewriting) that yielded the
+     * {@link androidx.appsearch.app.SearchResult} on which the user clicked.
+     */
+    @Nullable
+    public String getQuery() {
+        return mQuery;
+    }
+
+    /**
+     * Returns the qualified id of the {@link androidx.appsearch.app.SearchResult} document that the
+     * user clicked on.
+     *
+     * <p>A qualified id is a string generated by package, database, namespace, and document id. See
+     * {@link androidx.appsearch.util.DocumentIdUtil#createQualifiedId(String,String,String,String)}
+     * for more details.
+     */
+    @Nullable
+    public String getReferencedQualifiedId() {
+        return mReferencedQualifiedId;
+    }
+
+    /**
+     * Returns the rank of the {@link androidx.appsearch.app.SearchResult} document among the
+     * user-defined block.
+     *
+     * <p>The client can define its own custom definition for block, e.g. corpus name, group, etc.
+     *
+     * <p>For example, a client defines the block as corpus, and AppSearch returns 5 documents with
+     * corpus = ["corpus1", "corpus1", "corpus2", "corpus3", "corpus2"]. Then the block ranks of
+     * them = [1, 2, 1, 1, 2].
+     *
+     * <p>If the client is not presenting the results in multiple blocks, they should set this value
+     * to match {@link #getResultRankGlobal}.
+     *
+     * <p>If unset, then the block rank of the {@link androidx.appsearch.app.SearchResult} document
+     * will be set to -1 to mark invalid.
+     */
+    public int getResultRankInBlock() {
+        return mResultRankInBlock;
+    }
+
+    /**
+     * Returns the global rank of the {@link androidx.appsearch.app.SearchResult} document.
+     *
+     * <p>Global rank reflects the order of {@link androidx.appsearch.app.SearchResult} documents
+     * returned by AppSearch.
+     *
+     * <p>For example, AppSearch returns 2 pages with 10 {@link androidx.appsearch.app.SearchResult}
+     * documents for each page. Then the global ranks of them will be 1 to 10 for the first page,
+     * and 11 to 20 for the second page.
+     *
+     * <p>If unset, then the global rank of the {@link androidx.appsearch.app.SearchResult} document
+     * will be set to -1 to mark invalid.
+     */
+    public int getResultRankGlobal() {
+        return mResultRankGlobal;
+    }
+
+    /**
+     * Returns the time in milliseconds that user stays on the
+     * {@link androidx.appsearch.app.SearchResult} document after clicking it.
+     */
+    public long getTimeStayOnResultMillis() {
+        return mTimeStayOnResultMillis;
+    }
+
+    // TODO(b/314026345): redesign builder to enable inheritance for ClickAction.
+    /** Builder for {@link ClickAction}. */
+    @Document.BuilderProducer
+    public static final class Builder extends BuilderImpl<Builder> {
+        private String mQuery;
+        private String mReferencedQualifiedId;
+        private int mResultRankInBlock;
+        private int mResultRankGlobal;
+        private long mTimeStayOnResultMillis;
+
+        /**
+         * Constructor for {@link ClickAction.Builder}.
+         *
+         * @param namespace             Namespace for the Document. See {@link Document.Namespace}.
+         * @param id                    Unique identifier for the Document. See {@link Document.Id}.
+         * @param actionTimestampMillis The timestamp when the user took the action, in milliseconds
+         *                              since Unix epoch.
+         */
+        public Builder(@NonNull String namespace, @NonNull String id, long actionTimestampMillis) {
+            this(namespace, id, actionTimestampMillis, ActionConstants.ACTION_TYPE_CLICK);
+        }
+
+        /**
+         * Constructs {@link ClickAction.Builder} by copying existing values from the given
+         * {@link ClickAction}.
+         *
+         * @param clickAction an existing {@link ClickAction} object.
+         */
+        public Builder(@NonNull ClickAction clickAction) {
+            super(Preconditions.checkNotNull(clickAction));
+
+            mQuery = clickAction.getQuery();
+            mReferencedQualifiedId = clickAction.getReferencedQualifiedId();
+            mResultRankInBlock = clickAction.getResultRankInBlock();
+            mResultRankGlobal = clickAction.getResultRankGlobal();
+            mTimeStayOnResultMillis = clickAction.getTimeStayOnResultMillis();
+        }
+
+        /**
+         * Constructor for {@link ClickAction.Builder}.
+         *
+         * <p>It is required by {@link Document.BuilderProducer}.
+         *
+         * @param namespace             Namespace for the Document. See {@link Document.Namespace}.
+         * @param id                    Unique identifier for the Document. See {@link Document.Id}.
+         * @param actionTimestampMillis The timestamp when the user took the action, in milliseconds
+         *                              since Unix epoch.
+         * @param actionType            Action type enum for the Document. See
+         *                              {@link TakenAction.ActionType}.
+         */
+        Builder(@NonNull String namespace, @NonNull String id, long actionTimestampMillis,
+                @TakenAction.ActionType int actionType) {
+            super(namespace, id, actionTimestampMillis, actionType);
+
+            // Default for unset result rank fields. Since negative number is invalid for ranking,
+            // -1 is used as an unset value and AppSearch will ignore it.
+            mResultRankInBlock = -1;
+            mResultRankGlobal = -1;
+
+            // Default for unset timeStayOnResultMillis. Since negative number is invalid for
+            // time in millis, -1 is used as an unset value and AppSearch will ignore it.
+            mTimeStayOnResultMillis = -1;
+        }
+
+        /**
+         * Sets the user-entered search input (without any operators or rewriting) that yielded
+         * the {@link androidx.appsearch.app.SearchResult} on which the user clicked.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setQuery(@Nullable String query) {
+            mQuery = query;
+            return this;
+        }
+
+        /**
+         * Sets the qualified id of the {@link androidx.appsearch.app.SearchResult} document that
+         * the user takes action on.
+         *
+         * <p>A qualified id is a string generated by package, database, namespace, and document id.
+         * See {@link androidx.appsearch.util.DocumentIdUtil#createQualifiedId(
+         * String,String,String,String)} for more details.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setReferencedQualifiedId(@Nullable String referencedQualifiedId) {
+            mReferencedQualifiedId = referencedQualifiedId;
+            return this;
+        }
+
+        /**
+         * Sets the rank of the {@link androidx.appsearch.app.SearchResult} document among the
+         * user-defined block.
+         *
+         * @see ClickAction#getResultRankInBlock
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setResultRankInBlock(int resultRankInBlock) {
+            mResultRankInBlock = resultRankInBlock;
+            return this;
+        }
+
+        /**
+         * Sets the global rank of the {@link androidx.appsearch.app.SearchResult} document.
+         *
+         * @see ClickAction#getResultRankGlobal
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setResultRankGlobal(int resultRankGlobal) {
+            mResultRankGlobal = resultRankGlobal;
+            return this;
+        }
+
+        /**
+         * Sets the time in milliseconds that user stays on the
+         * {@link androidx.appsearch.app.SearchResult} document after clicking it.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setTimeStayOnResultMillis(long timeStayOnResultMillis) {
+            mTimeStayOnResultMillis = timeStayOnResultMillis;
+            return this;
+        }
+
+        /** Builds a {@link ClickAction}. */
+        @Override
+        @NonNull
+        public ClickAction build() {
+            return new ClickAction(mNamespace, mId, mDocumentTtlMillis, mActionTimestampMillis,
+                    mActionType, mQuery, mReferencedQualifiedId, mResultRankInBlock,
+                    mResultRankGlobal, mTimeStayOnResultMillis);
+        }
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/SearchAction.java b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/SearchAction.java
new file mode 100644
index 0000000..67cb4ea
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/SearchAction.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+
+package androidx.appsearch.usagereporting;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.annotation.Document;
+import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+import androidx.core.util.Preconditions;
+
+/**
+ * {@link SearchAction} is a built-in AppSearch document type that contains different metrics.
+ * <ul>
+ *     <li>Clients can report the user's search actions.
+ *     <li>Usually {@link SearchAction} is reported together with {@link ClickAction}, since the
+ *     user clicks on {@link androidx.appsearch.app.SearchResult} documents after searching.
+ * </ul>
+ *
+ * <p>In order to use this document type, the client must explicitly set this schema type via
+ * {@link androidx.appsearch.app.SetSchemaRequest.Builder#addDocumentClasses}.
+ *
+ * <p>Since {@link SearchAction} is an AppSearch document, the client can handle deletion via
+ * {@link androidx.appsearch.app.AppSearchSession#removeAsync} or document time-to-live (TTL). The
+ * default TTL is 60 days.
+ */
+@Document(name = "builtin:SearchAction")
+public class SearchAction extends TakenAction {
+    @Nullable
+    @Document.StringProperty(indexingType = StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+    private final String mQuery;
+
+    @Document.LongProperty
+    private final int mFetchedResultCount;
+
+    SearchAction(@NonNull String namespace, @NonNull String id, long documentTtlMillis,
+            long actionTimestampMillis, @TakenAction.ActionType int actionType,
+            @Nullable String query, int fetchedResultCount) {
+        super(namespace, id, documentTtlMillis, actionTimestampMillis, actionType);
+
+        mQuery = query;
+        mFetchedResultCount = fetchedResultCount;
+    }
+
+    /** Returns the user-entered search input (without any operators or rewriting). */
+    @Nullable
+    public String getQuery() {
+        return mQuery;
+    }
+
+    /**
+     * Returns total number of results fetched from AppSearch by the client in this
+     * {@link SearchAction}.
+     *
+     * <p>If unset, then it will be set to -1 to mark invalid.
+     */
+    public int getFetchedResultCount() {
+        return mFetchedResultCount;
+    }
+
+    // TODO(b/314026345): redesign builder to enable inheritance for SearchAction.
+    /** Builder for {@link SearchAction}. */
+    @Document.BuilderProducer
+    public static final class Builder extends BuilderImpl<Builder> {
+        private String mQuery;
+        private int mFetchedResultCount;
+
+        /**
+         * Constructor for {@link SearchAction.Builder}.
+         *
+         * @param namespace             Namespace for the Document. See {@link Document.Namespace}.
+         * @param id                    Unique identifier for the Document. See {@link Document.Id}.
+         * @param actionTimestampMillis The timestamp when the user took the action, in milliseconds
+         *                              since Unix epoch.
+         */
+        public Builder(@NonNull String namespace, @NonNull String id, long actionTimestampMillis) {
+            this(namespace, id, actionTimestampMillis, ActionConstants.ACTION_TYPE_SEARCH);
+        }
+
+        /**
+         * Constructor for {@link Builder} with all the existing values.
+         */
+        public Builder(@NonNull SearchAction searchAction) {
+            super(Preconditions.checkNotNull(searchAction));
+
+            mQuery = searchAction.getQuery();
+            mFetchedResultCount = searchAction.getFetchedResultCount();
+        }
+
+        /**
+         * Constructor for {@link SearchAction.Builder}.
+         *
+         * <p>It is required by {@link Document.BuilderProducer}.
+         *
+         * @param namespace             Namespace for the Document. See {@link Document.Namespace}.
+         * @param id                    Unique identifier for the Document. See {@link Document.Id}.
+         * @param actionTimestampMillis The timestamp when the user took the action, in milliseconds
+         *                              since Unix epoch.
+         * @param actionType            Action type enum for the Document. See
+         *                              {@link TakenAction.ActionType}.
+         */
+        Builder(@NonNull String namespace, @NonNull String id, long actionTimestampMillis,
+                @TakenAction.ActionType int actionType) {
+            super(namespace, id, actionTimestampMillis, actionType);
+
+            // Default for unset fetchedResultCount. Since negative number is invalid for fetched
+            // result count, -1 is used as an unset value and AppSearch will ignore it.
+            mFetchedResultCount = -1;
+        }
+
+        /** Sets the user-entered search input (without any operators or rewriting). */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setQuery(@Nullable String query) {
+            mQuery = query;
+            return this;
+        }
+
+        /**
+         * Sets total number of results fetched from AppSearch by the client in this
+         * {@link SearchAction}.
+         *
+         * @see SearchAction#getFetchedResultCount
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setFetchedResultCount(int fetchedResultCount) {
+            mFetchedResultCount = fetchedResultCount;
+            return this;
+        }
+
+        /** Builds a {@link SearchAction}. */
+        @Override
+        @NonNull
+        public SearchAction build() {
+            return new SearchAction(mNamespace, mId, mDocumentTtlMillis, mActionTimestampMillis,
+                    mActionType, mQuery, mFetchedResultCount);
+        }
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/TakenAction.java b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/TakenAction.java
new file mode 100644
index 0000000..da35743
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/usagereporting/TakenAction.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// @exportToFramework:skipFile()
+
+package androidx.appsearch.usagereporting;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.annotation.Document;
+import androidx.core.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * {@link TakenAction} is an abstract class which holds common fields of other AppSearch built-in
+ * action types (e.g. {@link SearchAction}, {@link ClickAction}).
+ *
+ * <p>Clients can report the user's actions by creating concrete actions with
+ * {@link androidx.appsearch.app.PutDocumentsRequest.Builder#addTakenActions} API.
+ */
+@Document(name = "builtin:TakenAction")
+public abstract class TakenAction {
+    /** Default TTL for all related {@link TakenAction} documents: 60 days. */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final long DEFAULT_DOCUMENT_TTL_MILLIS = 60L * 24 * 60 * 60 * 1000;
+
+    /** AppSearch taken action type. */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @IntDef(value = {
+            ActionConstants.ACTION_TYPE_UNKNOWN,
+            ActionConstants.ACTION_TYPE_SEARCH,
+            ActionConstants.ACTION_TYPE_CLICK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ActionType {
+    }
+
+    @NonNull
+    @Document.Namespace
+    private final String mNamespace;
+
+    @NonNull
+    @Document.Id
+    private final String mId;
+
+    @Document.TtlMillis
+    private final long mDocumentTtlMillis;
+
+    @Document.CreationTimestampMillis
+    private final long mActionTimestampMillis;
+
+    @Document.LongProperty
+    @ActionType
+    private final int mActionType;
+
+    TakenAction(@NonNull String namespace, @NonNull String id, long documentTtlMillis,
+            long actionTimestampMillis, @ActionType int actionType) {
+        mNamespace = Preconditions.checkNotNull(namespace);
+        mId = Preconditions.checkNotNull(id);
+        mDocumentTtlMillis = documentTtlMillis;
+        mActionTimestampMillis = actionTimestampMillis;
+        mActionType = actionType;
+    }
+
+    /** Returns the namespace of the {@link TakenAction}. */
+    @NonNull
+    public String getNamespace() {
+        return mNamespace;
+    }
+
+    /** Returns the unique identifier of the {@link TakenAction}. */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the time-to-live (TTL) of the {@link TakenAction} document as a duration in
+     * milliseconds.
+     *
+     * <p>The document will be automatically deleted when the TTL expires (since
+     * {@link #getActionTimestampMillis()}).
+     *
+     * <p>The default TTL for {@link TakenAction} document is 60 days.
+     *
+     * <p>See {@link androidx.appsearch.annotation.Document.TtlMillis} for more information on TTL.
+     */
+    public long getDocumentTtlMillis() {
+        return mDocumentTtlMillis;
+    }
+
+    /**
+     * Returns the timestamp when the user took the action, in milliseconds since Unix epoch.
+     *
+     * <p>The action timestamp will be used together with {@link #getDocumentTtlMillis()} as the
+     * document retention.
+     */
+    public long getActionTimestampMillis() {
+        return mActionTimestampMillis;
+    }
+
+    /**
+     * Returns the action type of the {@link TakenAction}.
+     *
+     * @see TakenAction.ActionType
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @ActionType
+    public int getActionType() {
+        return mActionType;
+    }
+
+    // TODO(b/330777270): improve AnnotationProcessor for abstract document class, and remove this
+    //                    builder.
+    /** Builder for {@link TakenAction}. */
+    @Document.BuilderProducer
+    static final class Builder extends BuilderImpl<Builder> {
+        /**
+         * Constructor for {@link TakenAction.Builder}.
+         *
+         * @param namespace             Namespace for the Document. See {@link Document.Namespace}.
+         * @param id                    Unique identifier for the Document. See {@link Document.Id}.
+         * @param actionTimestampMillis The timestamp when the user took the action, in milliseconds
+         *                              since Unix epoch.
+         * @param actionType            Action type enum for the Document. See
+         *                              {@link TakenAction.ActionType}.
+         */
+        Builder(@NonNull String namespace, @NonNull String id, long actionTimestampMillis,
+                @TakenAction.ActionType int actionType) {
+            super(namespace, id, actionTimestampMillis, actionType);
+        }
+
+        /** Constructor for {@link TakenAction.Builder} with all the existing values. */
+        Builder(@NonNull TakenAction takenAction) {
+            super(takenAction);
+        }
+    }
+
+    // Use templated BuilderImpl to resolve base class setter return type issue for child class
+    // builder instances.
+    @SuppressWarnings("unchecked")
+    static class BuilderImpl<T extends BuilderImpl<T>> {
+        protected final String mNamespace;
+        protected final String mId;
+        protected long mDocumentTtlMillis;
+        protected long mActionTimestampMillis;
+        @ActionType
+        protected int mActionType;
+
+        /**
+         * Constructs {@link TakenAction.BuilderImpl} with given {@code namespace}, {@code id},
+         * {@code actionTimestampMillis} and {@code actionType}.
+         *
+         * @param namespace             The namespace of the {@link TakenAction} document.
+         * @param id                    The id of the {@link TakenAction} document.
+         * @param actionTimestampMillis The timestamp when the user took the action, in milliseconds
+         *                              since Unix epoch.
+         * @param actionType            The action type enum of the Document.
+         */
+        BuilderImpl(@NonNull String namespace, @NonNull String id, long actionTimestampMillis,
+                @TakenAction.ActionType int actionType) {
+            mNamespace = Preconditions.checkNotNull(namespace);
+            mId = Preconditions.checkNotNull(id);
+            mActionTimestampMillis = actionTimestampMillis;
+            mActionType = actionType;
+
+            // Default for documentTtlMillis.
+            mDocumentTtlMillis = TakenAction.DEFAULT_DOCUMENT_TTL_MILLIS;
+        }
+
+        /**
+         * Constructs {@link TakenAction.BuilderImpl} by copying existing values from the given
+         * {@link TakenAction}.
+         *
+         * @param takenAction an existing {@link TakenAction} object.
+         */
+        BuilderImpl(@NonNull TakenAction takenAction) {
+            this(takenAction.getNamespace(), takenAction.getId(),
+                    takenAction.getActionTimestampMillis(), takenAction.getActionType());
+            mDocumentTtlMillis = takenAction.getDocumentTtlMillis();
+        }
+
+        /**
+         * Sets the time-to-live (TTL) of the {@link TakenAction} document as a duration in
+         * milliseconds.
+         *
+         * <p>The document will be automatically deleted when the TTL expires (since
+         * {@link TakenAction#getActionTimestampMillis()}).
+         *
+         * <p>The default TTL for {@link TakenAction} document is 60 days.
+         *
+         * <p>See {@link androidx.appsearch.annotation.Document.TtlMillis} for more information on
+         * TTL.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public T setDocumentTtlMillis(long documentTtlMillis) {
+            mDocumentTtlMillis = documentTtlMillis;
+            return (T) this;
+        }
+
+        // TODO(b/330777270): improve AnnotationProcessor for abstract document class builder, and
+        //                    make it an abstract method.
+        /**
+         * For AppSearch annotation processor requirement only. The client should never call it
+         * since it is impossible to instantiate an abstract class.
+         *
+         * @throws UnsupportedOperationException
+         */
+        @NonNull
+        public TakenAction build() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/util/BundleUtil.java b/appsearch/appsearch/src/main/java/androidx/appsearch/util/BundleUtil.java
index 9d50086..7604db5 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/util/BundleUtil.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/util/BundleUtil.java
@@ -248,7 +248,7 @@
             // Read bundle from bytes
             parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
             parcel.setDataPosition(0);
-            return parcel.readBundle();
+            return parcel.readBundle(BundleUtil.class.getClassLoader());
         } finally {
             parcel.recycle();
         }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/util/ExceptionUtil.java b/appsearch/appsearch/src/main/java/androidx/appsearch/util/ExceptionUtil.java
new file mode 100644
index 0000000..c09ac7a
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/util/ExceptionUtil.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.util;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+/**
+ * Utilities for handling exceptions.
+ */
+// This file has different behavior in Framework as compared to JetPack (like it is okay to rethrow
+// exception and log instead of rethrowing from SystemServer in case of RemoteException). This file
+// is not synced to Framework, as it maintains its own environment specific copy.
+// @exportToFramework:skipFile()
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class ExceptionUtil {
+    private static final String TAG = "AppSearchExceptionUtil";
+
+    /**
+     * {@link RuntimeException} will be rethrown if {@link #isItOkayToRethrowException()} returns
+     * true.
+     */
+    public static void handleException(@NonNull Exception e) {
+        if (isItOkayToRethrowException() && e instanceof RuntimeException) {
+            rethrowRuntimeException((RuntimeException) e);
+        }
+    }
+
+    /** Returns whether it is OK to rethrow exceptions from this entrypoint. */
+    private static boolean isItOkayToRethrowException() {
+        return true;
+    }
+
+    /** Rethrow exception from SystemServer in Framework code. */
+    public static void handleRemoteException(@NonNull RemoteException e) {
+        Log.w(TAG, "Unable to make a call to AppSearchManagerService!", e);
+    }
+
+    /**
+     * A helper method to rethrow {@link RuntimeException}.
+     *
+     * <p>We use this to enforce exception type and assure the compiler/linter that the exception is
+     * indeed {@link RuntimeException} and can be rethrown safely.
+     */
+    private static void rethrowRuntimeException(RuntimeException e) {
+        throw e;
+    }
+
+    private ExceptionUtil() {}
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/util/LogUtil.java b/appsearch/appsearch/src/main/java/androidx/appsearch/util/LogUtil.java
index cc52cdd..0f3e100 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/util/LogUtil.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/util/LogUtil.java
@@ -22,6 +22,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.Size;
+import androidx.appsearch.app.AppSearchEnvironmentFactory;
 
 /**
  * Utilities for logging to logcat.
@@ -33,6 +34,8 @@
     // TODO(b/232285376): If it becomes possible to detect an eng build, turn this on by default
     //  for eng builds.
     public static final boolean DEBUG = false;
+    public static final boolean INFO = AppSearchEnvironmentFactory.getEnvironmentInstance()
+            .isInfoLoggingEnabled();
 
     /**
      * The {@link #piiTrace} logs are intended for sensitive data that can't be enabled in
@@ -92,7 +95,7 @@
             @NonNull String message,
             @Nullable Object fastTraceObj,
             @Nullable Object fullTraceObj) {
-        if (PII_TRACE_LEVEL == 0) {
+        if (PII_TRACE_LEVEL == 0 || !INFO) {
             return;
         }
         StringBuilder builder = new StringBuilder("(trace) ").append(message);
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.java
index dd8ae1d..39dd6c7 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.java
@@ -527,6 +527,13 @@
                         env,
                         /* allowRepeated= */true);
                 break;
+            case EMBEDDING_PROPERTY:
+                requireTypeIsOneOf(
+                        getterOrField,
+                        List.of(helper.mEmbeddingType),
+                        env,
+                        /* allowRepeated= */true);
+                break;
             default:
                 throw new IllegalStateException("Unhandled annotation: " + annotation);
         }
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/FromGenericDocumentCodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/FromGenericDocumentCodeGenerator.java
index 9f11508..7296edd 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/FromGenericDocumentCodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/FromGenericDocumentCodeGenerator.java
@@ -203,8 +203,9 @@
         //       unboxing.
         //
         //   1b: ListCallArraysAsList
-        //       List contains String. We have to convert this from an array of String[], but no
-        //       conversion of the collection elements is needed. We can use Arrays#asList for this.
+        //       List contains String or EmbeddingVector. We have to convert this from
+        //       an array of String[] or EmbeddingVector[], but no conversion of the
+        //       collection elements is needed. We can use Arrays#asList for this.
         //
         //   1c: ListForLoopCallFromGenericDocument
         //       List contains a class which is annotated with @Document.
@@ -225,7 +226,8 @@
         //       of unboxing.
         //
         //   2b: ArrayUseDirectly
-        //       Array is of type String[], long[], double[], boolean[], byte[][].
+        //       Array is of type String[], long[], double[], boolean[], byte[][] or
+        //       EmbeddingVector[].
         //       We can directly use this field with no conversion.
         //
         //   2c: ArrayForLoopCallFromGenericDocument
@@ -243,7 +245,8 @@
 
         // Scenario 3: Single valued fields
         //   3a: FieldUseDirectlyWithNullCheck
-        //       Field is of type String, Long, Integer, Double, Float, Boolean, byte[].
+        //       Field is of type String, Long, Integer, Double, Float, Boolean, byte[] or
+        //       EmbeddingVector.
         //       We can use this field directly, after testing for null. The java compiler will box
         //       or unbox as needed.
         //
@@ -393,6 +396,19 @@
                     default:
                         throw new IllegalStateException("Unhandled type-category: " + typeCategory);
                 }
+            case EMBEDDING_PROPERTY:
+                switch (typeCategory) {
+                    case COLLECTION: // List<EmbeddingVector>: 1b
+                        return listCallArraysAsList(annotation, getterOrField);
+                    case ARRAY:
+                        // EmbeddingVector[]: 2b
+                        return arrayUseDirectly(annotation, getterOrField);
+                    case SINGLE:
+                        // EmbeddingVector: 3a
+                        return fieldUseDirectlyWithNullCheck(annotation, getterOrField);
+                    default:
+                        throw new IllegalStateException("Unhandled type-category: " + typeCategory);
+                }
             default:
                 throw new IllegalStateException("Unhandled annotation: " + annotation);
         }
@@ -433,8 +449,9 @@
     }
 
     // 1b: ListCallArraysAsList
-    //     List contains String. We have to convert this from an array of String[], but no
-    //     conversion of the collection elements is needed. We can use Arrays#asList for this.
+    //     List contains String or EmbeddingVector. We have to convert this from
+    //     an array of String[] or EmbeddingVector[], but no conversion of the
+    //     collection elements is needed. We can use Arrays#asList for this.
     @NonNull
     private CodeBlock listCallArraysAsList(
             @NonNull DataPropertyAnnotation annotation,
@@ -559,8 +576,8 @@
     }
 
     // 2b: ArrayUseDirectly
-    //     Array is of type String[], long[], double[], boolean[], byte[][].
-    //     We can directly use this field with no conversion.
+    //     Array is of type String[], long[], double[], boolean[], byte[][] or
+    //     EmbeddingVector[].
     @NonNull
     private CodeBlock arrayUseDirectly(
             @NonNull DataPropertyAnnotation annotation,
@@ -646,7 +663,8 @@
     }
 
     // 3a: FieldUseDirectlyWithNullCheck
-    //     Field is of type String, Long, Integer, Double, Float, Boolean, byte[].
+    //     Field is of type String, Long, Integer, Double, Float, Boolean, byte[] or
+    //     EmbeddingVector.
     //     We can use this field directly, after testing for null. The java compiler will box
     //     or unbox as needed.
     @NonNull
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
index 8ad865d..dba768a 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
@@ -90,6 +90,9 @@
     public static final ClassName GENERIC_DOCUMENT_CLASS =
             ClassName.get(APPSEARCH_PKG, "GenericDocument");
 
+    public static final ClassName EMBEDDING_VECTOR_CLASS =
+            ClassName.get(APPSEARCH_PKG, "EmbeddingVector");
+
     public static final ClassName BUILDER_PRODUCER_CLASS =
             DOCUMENT_ANNOTATION_CLASS.nestedClass("BuilderProducer");
 
@@ -108,6 +111,7 @@
     public final TypeMirror mBooleanPrimitiveType;
     public final TypeMirror mBytePrimitiveArrayType;
     public final TypeMirror mGenericDocumentType;
+    public final TypeMirror mEmbeddingType;
     public final TypeMirror mDoublePrimitiveType;
     final TypeMirror mCollectionType;
     final TypeMirror mListType;
@@ -151,6 +155,8 @@
         mBytePrimitiveArrayType = mTypeUtils.getArrayType(mBytePrimitiveType);
         mGenericDocumentType =
                 mElementUtils.getTypeElement(GENERIC_DOCUMENT_CLASS.canonicalName()).asType();
+        mEmbeddingType = mElementUtils.getTypeElement(
+                EMBEDDING_VECTOR_CLASS.canonicalName()).asType();
     }
 
     /**
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
index 9199c47..42c6b31 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
@@ -28,6 +28,7 @@
 import androidx.annotation.NonNull;
 import androidx.appsearch.compiler.annotationwrapper.DataPropertyAnnotation;
 import androidx.appsearch.compiler.annotationwrapper.DocumentPropertyAnnotation;
+import androidx.appsearch.compiler.annotationwrapper.EmbeddingPropertyAnnotation;
 import androidx.appsearch.compiler.annotationwrapper.LongPropertyAnnotation;
 import androidx.appsearch.compiler.annotationwrapper.StringPropertyAnnotation;
 
@@ -229,6 +230,12 @@
                 LongPropertyAnnotation longPropertyAnnotation = (LongPropertyAnnotation) annotation;
                 codeBlock.add(createSetIndexingTypeExpr(longPropertyAnnotation, getterOrField));
                 break;
+            case EMBEDDING_PROPERTY:
+                EmbeddingPropertyAnnotation embeddingPropertyAnnotation =
+                        (EmbeddingPropertyAnnotation) annotation;
+                codeBlock.add(
+                        createSetIndexingTypeExpr(embeddingPropertyAnnotation, getterOrField));
+                break;
             case DOUBLE_PROPERTY: // fall-through
             case BOOLEAN_PROPERTY: // fall-through
             case BYTES_PROPERTY:
@@ -418,6 +425,31 @@
 
     /**
      * Creates an expr like
+     * {@code .setIndexingType(EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)}.
+     */
+    @NonNull
+    private static CodeBlock createSetIndexingTypeExpr(
+            @NonNull EmbeddingPropertyAnnotation annotation,
+            @NonNull AnnotatedGetterOrField getterOrField) throws ProcessingException {
+        String enumName;
+        switch (annotation.getIndexingType()) {
+            case 0:
+                enumName = "INDEXING_TYPE_NONE";
+                break;
+            case 1:
+                enumName = "INDEXING_TYPE_SIMILARITY";
+                break;
+            default:
+                throw new ProcessingException(
+                        "Unknown indexing type " + annotation.getIndexingType(),
+                        getterOrField.getElement());
+        }
+        return CodeBlock.of("\n.setIndexingType($T.$N)",
+                EmbeddingPropertyAnnotation.CONFIG_CLASS, enumName);
+    }
+
+    /**
+     * Creates an expr like
      * {@code .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)}.
      */
     @NonNull
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java
index 96bba93..90ebb78 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java
@@ -165,10 +165,10 @@
         //       care of unboxing and widening where necessary.
         //
         //   1b: CollectionCallToArray
-        //       Collection contains String or GenericDocument.
-        //       We have to convert this into an array of String[] or GenericDocument[], but no
-        //       conversion of the collection elements is needed. We can use Collection#toArray for
-        //       this.
+        //       Collection contains String, GenericDocument or EmbeddingVector.
+        //       We have to convert this into an array of String[], GenericDocument[] or
+        //       EmbeddingVector[], but no conversion of the collection elements is
+        //       needed. We can use Collection#toArray for this.
         //
         //   1c: CollectionForLoopCallToGenericDocument
         //       Collection contains a class which is annotated with @Document.
@@ -188,8 +188,8 @@
         //       unboxing and widening where necessary.
         //
         //   2b: ArrayUseDirectly
-        //       Array is of type String[], long[], double[], boolean[], byte[][] or
-        //       GenericDocument[].
+        //       Array is of type String[], long[], double[], boolean[], byte[][],
+        //       GenericDocument[] or EmbeddingVector[].
         //       We can directly use this field with no conversion.
         //
         //   2c: ArrayForLoopCallToGenericDocument
@@ -207,7 +207,8 @@
 
         // Scenario 3: Single valued fields
         //   3a: FieldUseDirectlyWithNullCheck
-        //       Field is of type String, Long, Integer, Double, Float, Boolean.
+        //       Field is of type String, Long, Integer, Double, Float, Boolean or
+        //       EmbeddingVector.
         //       We can use this field directly, after testing for null. The java compiler will box
         //       or unbox as needed.
         //
@@ -375,6 +376,20 @@
                     default:
                         throw new IllegalStateException("Unhandled type-category: " + typeCategory);
                 }
+            case EMBEDDING_PROPERTY:
+                switch (typeCategory) {
+                    case COLLECTION:
+                        // List<EmbeddingVector>: 1b
+                        return collectionCallToArray(annotation, getterOrField);
+                    case ARRAY:
+                        // EmbeddingVector[]: 2b
+                        return arrayUseDirectly(annotation, getterOrField);
+                    case SINGLE:
+                        // EmbeddingVector: 3a
+                        return fieldUseDirectlyWithNullCheck(annotation, getterOrField);
+                    default:
+                        throw new IllegalStateException("Unhandled type-category: " + typeCategory);
+                }
             default:
                 throw new IllegalStateException("Unhandled annotation: " + annotation);
         }
@@ -417,10 +432,10 @@
     }
 
     // 1b: CollectionCallToArray
-    //     Collection contains String or GenericDocument.
-    //     We have to convert this into an array of String[] or GenericDocument[], but no
-    //     conversion of the collection elements is needed. We can use Collection#toArray for
-    //     this.
+    //     Collection contains String, GenericDocument or EmbeddingVector.
+    //     We have to convert this into an array of String[], GenericDocument[] or
+    //     EmbeddingVector[], but no conversion of the collection elements is
+    //     needed. We can use Collection#toArray for this.
     @NonNull
     private CodeBlock collectionCallToArray(
             @NonNull DataPropertyAnnotation annotation,
@@ -536,8 +551,8 @@
     }
 
     // 2b: ArrayUseDirectly
-    //     Array is of type String[], long[], double[], boolean[], byte[][] or
-    //     GenericDocument[].
+    //     Array is of type String[], long[], double[], boolean[], byte[][],
+    //     GenericDocument[] or EmbeddingVector[].
     //     We can directly use this field with no conversion.
     @NonNull
     private CodeBlock arrayUseDirectly(
@@ -613,7 +628,8 @@
     }
 
     // 3a: FieldUseDirectlyWithNullCheck
-    //     Field is of type String, Long, Integer, Double, Float, Boolean.
+    //     Field is of type String, Long, Integer, Double, Float, Boolean or
+    //     EmbeddingVector.
     //     We can use this field directly, after testing for null. The java compiler will box
     //     or unbox as needed.
     @NonNull
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DataPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DataPropertyAnnotation.java
index 38621c4..1c78f19 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DataPropertyAnnotation.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DataPropertyAnnotation.java
@@ -38,12 +38,13 @@
  *     <li>{@link DoublePropertyAnnotation}</li>
  *     <li>{@link BooleanPropertyAnnotation}</li>
  *     <li>{@link BytesPropertyAnnotation}</li>
+ *     <li>{@link EmbeddingPropertyAnnotation}</li>
  * </ul>
  */
 public abstract class DataPropertyAnnotation implements PropertyAnnotation {
     public enum Kind {
         STRING_PROPERTY, DOCUMENT_PROPERTY, LONG_PROPERTY, DOUBLE_PROPERTY, BOOLEAN_PROPERTY,
-        BYTES_PROPERTY
+        BYTES_PROPERTY, EMBEDDING_PROPERTY
     }
 
     @NonNull
@@ -103,6 +104,9 @@
             return LongPropertyAnnotation.parse(annotationParams, defaultName);
         } else if (qualifiedClassName.equals(StringPropertyAnnotation.CLASS_NAME.canonicalName())) {
             return StringPropertyAnnotation.parse(annotationParams, defaultName);
+        } else if (qualifiedClassName.equals(
+                EmbeddingPropertyAnnotation.CLASS_NAME.canonicalName())) {
+            return EmbeddingPropertyAnnotation.parse(annotationParams, defaultName);
         }
         return null;
     }
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/EmbeddingPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/EmbeddingPropertyAnnotation.java
new file mode 100644
index 0000000..882c737
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/EmbeddingPropertyAnnotation.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.APPSEARCH_SCHEMA_CLASS;
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+import androidx.appsearch.compiler.IntrospectionHelper;
+import androidx.appsearch.compiler.ProcessingException;
+
+import com.google.auto.value.AutoValue;
+import com.squareup.javapoet.ClassName;
+
+import java.util.Map;
+
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * An instance of the {@code @Document.EmbeddingProperty} annotation.
+ */
+@AutoValue
+public abstract class EmbeddingPropertyAnnotation extends DataPropertyAnnotation {
+    public static final ClassName CLASS_NAME =
+            DOCUMENT_ANNOTATION_CLASS.nestedClass("EmbeddingProperty");
+
+    public static final ClassName CONFIG_CLASS =
+            APPSEARCH_SCHEMA_CLASS.nestedClass("EmbeddingPropertyConfig");
+
+    public EmbeddingPropertyAnnotation() {
+        super(
+                CLASS_NAME,
+                CONFIG_CLASS,
+                /* genericDocGetterName= */"getPropertyEmbedding",
+                /* genericDocArrayGetterName= */"getPropertyEmbeddingArray",
+                /* genericDocSetterName= */"setPropertyEmbedding");
+    }
+
+    /**
+     * @param defaultName The name to use for the annotated property in case the annotation
+     *                    params do not mention an explicit name.
+     * @throws ProcessingException If the annotation points to an Illegal serializer class.
+     */
+    @NonNull
+    static EmbeddingPropertyAnnotation parse(
+            @NonNull Map<String, Object> annotationParams,
+            @NonNull String defaultName) throws ProcessingException {
+        String name = (String) annotationParams.get("name");
+        return new AutoValue_EmbeddingPropertyAnnotation(
+                name.isEmpty() ? defaultName : name,
+                (boolean) annotationParams.get("required"),
+                (int) annotationParams.get("indexingType"));
+    }
+
+    /**
+     * Specifies how a property should be indexed.
+     */
+    public abstract int getIndexingType();
+
+    @NonNull
+    @Override
+    public final Kind getDataPropertyKind() {
+        return Kind.EMBEDDING_PROPERTY;
+    }
+
+    @NonNull
+    @Override
+    public TypeMirror getUnderlyingTypeWithinGenericDoc(@NonNull IntrospectionHelper helper) {
+        return helper.mEmbeddingType;
+    }
+}
diff --git a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
index 57cbd28..fa7b7aa 100644
--- a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
+++ b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
@@ -1048,18 +1048,20 @@
         // TODO(b/156296904): Uncomment Gift in this test when it's supported
         Compilation compilation = compile(
                 "import java.util.*;\n"
+                        + "import androidx.appsearch.app.EmbeddingVector;\n"
                         + "@Document\n"
                         + "public class Gift {\n"
                         + "  @Document.Namespace String namespace;\n"
                         + "  @Document.Id String id;\n"
-                        + "  @Document.StringProperty String stringProp;\n"
-                        + "  @Document.LongProperty Integer integerProp;\n"
-                        + "  @Document.LongProperty Long longProp;\n"
-                        + "  @Document.DoubleProperty Float floatProp;\n"
-                        + "  @Document.DoubleProperty Double doubleProp;\n"
-                        + "  @Document.BooleanProperty Boolean booleanProp;\n"
-                        + "  @Document.BytesProperty byte[] bytesProp;\n"
-                        //+ "  @Document.Property Gift documentProp;\n"
+                        + "  @StringProperty String stringProp;\n"
+                        + "  @LongProperty Integer integerProp;\n"
+                        + "  @LongProperty Long longProp;\n"
+                        + "  @DoubleProperty Float floatProp;\n"
+                        + "  @DoubleProperty Double doubleProp;\n"
+                        + "  @BooleanProperty Boolean booleanProp;\n"
+                        + "  @BytesProperty byte[] bytesProp;\n"
+                        + "  @EmbeddingProperty EmbeddingVector vectorProp;\n"
+                        //+ "  @DocumentProperty Gift documentProp;\n"
                         + "}\n");
 
         assertThat(compilation).succeededWithoutWarnings();
@@ -1260,6 +1262,7 @@
         Compilation compilation = compile(
                 "import java.util.*;\n"
                         + "import androidx.appsearch.app.GenericDocument;\n"
+                        + "import androidx.appsearch.app.EmbeddingVector;\n"
                         + "@Document\n"
                         + "public class Gift {\n"
                         + "  @Namespace String namespace;\n"
@@ -1274,6 +1277,7 @@
                         + "  @BytesProperty Collection<byte[]> collectByteArr;\n"    // 1a
                         + "  @StringProperty Collection<String> collectString;\n"     // 1b
                         + "  @DocumentProperty Collection<Gift> collectGift;\n"         // 1c
+                        + "  @EmbeddingProperty Collection<EmbeddingVector> collectVec;\n"   // 1b
                         + "\n"
                         + "  // Arrays\n"
                         + "  @LongProperty Long[] arrBoxLong;\n"         // 2a
@@ -1289,6 +1293,7 @@
                         + "  @BytesProperty byte[][] arrUnboxByteArr;\n"  // 2b
                         + "  @StringProperty String[] arrString;\n"        // 2b
                         + "  @DocumentProperty Gift[] arrGift;\n"            // 2c
+                        + "  @EmbeddingProperty EmbeddingVector[] arrVec;\n"         // 2b
                         + "\n"
                         + "  // Single values\n"
                         + "  @StringProperty String string;\n"        // 3a
@@ -1304,6 +1309,7 @@
                         + "  @BooleanProperty boolean unboxBoolean;\n" // 3b
                         + "  @BytesProperty byte[] unboxByteArr;\n"  // 3a
                         + "  @DocumentProperty Gift gift;\n"            // 3c
+                        + "  @EmbeddingProperty EmbeddingVector vec;\n"        // 3a
                         + "}\n");
 
         assertThat(compilation).succeededWithoutWarnings();
@@ -3480,6 +3486,38 @@
         checkEqualsGolden("Gift.java");
     }
 
+    @Test
+    public void testEmbeddingFields() throws Exception {
+        Compilation compilation = compile(
+                "import java.util.*;\n"
+                        + "import androidx.appsearch.app.EmbeddingVector;\n"
+                        + "@Document\n"
+                        + "public class Gift {\n"
+                        + "  @Document.Namespace String namespace;\n"
+                        + "  @Document.Id String id;\n"
+                        + "  @Document.StringProperty String name;\n"
+                        // Embedding properties
+                        + "  @EmbeddingProperty EmbeddingVector defaultIndexNone;\n"
+                        + "  @EmbeddingProperty(indexingType=0) EmbeddingVector indexNone;\n"
+                        + "  @EmbeddingProperty(indexingType=1) EmbeddingVector vec;\n"
+                        + "  @EmbeddingProperty(indexingType=1) List<EmbeddingVector> listVec;\n"
+                        + "  @EmbeddingProperty(indexingType=1)"
+                        + "  Collection<EmbeddingVector> collectVec;\n"
+                        + "  @EmbeddingProperty(indexingType=1) EmbeddingVector[] arrVec;\n"
+                        + "}\n");
+
+        assertThat(compilation).succeededWithoutWarnings();
+        checkResultContains("Gift.java",
+                "new AppSearchSchema.EmbeddingPropertyConfig.Builder");
+        checkResultContains("Gift.java",
+                "AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY");
+        checkResultContains("Gift.java",
+                "AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE");
+        checkResultContains("Gift.java",
+                "EmbeddingVector");
+        checkEqualsGolden("Gift.java");
+    }
+
     private Compilation compile(String classBody) {
         return compile("Gift", classBody, /* restrictGeneratedCodeToLibrary= */false);
     }
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
index 4d65cc9..387d97d 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
@@ -2,6 +2,7 @@
 
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.DocumentClassFactory;
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import java.lang.Boolean;
@@ -55,6 +56,10 @@
           .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder("bytesProp")
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
             .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("vectorProp")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE)
+            .build())
           .build();
   }
 
@@ -95,6 +100,10 @@
     if (bytesPropCopy != null) {
       builder.setPropertyBytes("bytesProp", bytesPropCopy);
     }
+    EmbeddingVector vectorPropCopy = document.vectorProp;
+    if (vectorPropCopy != null) {
+      builder.setPropertyEmbedding("vectorProp", vectorPropCopy);
+    }
     return builder.build();
   }
 
@@ -138,6 +147,11 @@
     if (bytesPropCopy != null && bytesPropCopy.length != 0) {
       bytesPropConv = bytesPropCopy[0];
     }
+    EmbeddingVector[] vectorPropCopy = genericDoc.getPropertyEmbeddingArray("vectorProp");
+    EmbeddingVector vectorPropConv = null;
+    if (vectorPropCopy != null && vectorPropCopy.length != 0) {
+      vectorPropConv = vectorPropCopy[0];
+    }
     Gift document = new Gift();
     document.namespace = namespaceConv;
     document.id = idConv;
@@ -148,6 +162,7 @@
     document.doubleProp = doublePropConv;
     document.booleanProp = booleanPropConv;
     document.bytesProp = bytesPropConv;
+    document.vectorProp = vectorPropConv;
     return document;
   }
 }
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testEmbeddingFields.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testEmbeddingFields.JAVA
new file mode 100644
index 0000000..9447804
--- /dev/null
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testEmbeddingFields.JAVA
@@ -0,0 +1,153 @@
+package com.example.appsearch;
+
+import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.DocumentClassFactory;
+import androidx.appsearch.app.EmbeddingVector;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.exceptions.AppSearchException;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.appsearch.compiler.AppSearchCompiler")
+public final class $$__AppSearch__Gift implements DocumentClassFactory<Gift> {
+  public static final String SCHEMA_NAME = "Gift";
+
+  @Override
+  public String getSchemaName() {
+    return SCHEMA_NAME;
+  }
+
+  @Override
+  public AppSearchSchema getSchema() throws AppSearchException {
+    return new AppSearchSchema.Builder(SCHEMA_NAME)
+          .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("name")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+            .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+            .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+            .setJoinableValueType(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE)
+            .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("defaultIndexNone")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE)
+            .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("indexNone")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE)
+            .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("vec")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+            .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("listVec")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+            .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("collectVec")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+            .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("arrVec")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
+            .build())
+          .build();
+  }
+
+  @Override
+  public List<Class<?>> getDependencyDocumentClasses() throws AppSearchException {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public GenericDocument toGenericDocument(Gift document) throws AppSearchException {
+    GenericDocument.Builder<?> builder =
+        new GenericDocument.Builder<>(document.namespace, document.id, SCHEMA_NAME);
+    String nameCopy = document.name;
+    if (nameCopy != null) {
+      builder.setPropertyString("name", nameCopy);
+    }
+    EmbeddingVector defaultIndexNoneCopy = document.defaultIndexNone;
+    if (defaultIndexNoneCopy != null) {
+      builder.setPropertyEmbedding("defaultIndexNone", defaultIndexNoneCopy);
+    }
+    EmbeddingVector indexNoneCopy = document.indexNone;
+    if (indexNoneCopy != null) {
+      builder.setPropertyEmbedding("indexNone", indexNoneCopy);
+    }
+    EmbeddingVector vecCopy = document.vec;
+    if (vecCopy != null) {
+      builder.setPropertyEmbedding("vec", vecCopy);
+    }
+    List<EmbeddingVector> listVecCopy = document.listVec;
+    if (listVecCopy != null) {
+      EmbeddingVector[] listVecConv = listVecCopy.toArray(new EmbeddingVector[0]);
+      builder.setPropertyEmbedding("listVec", listVecConv);
+    }
+    Collection<EmbeddingVector> collectVecCopy = document.collectVec;
+    if (collectVecCopy != null) {
+      EmbeddingVector[] collectVecConv = collectVecCopy.toArray(new EmbeddingVector[0]);
+      builder.setPropertyEmbedding("collectVec", collectVecConv);
+    }
+    EmbeddingVector[] arrVecCopy = document.arrVec;
+    if (arrVecCopy != null) {
+      builder.setPropertyEmbedding("arrVec", arrVecCopy);
+    }
+    return builder.build();
+  }
+
+  @Override
+  public Gift fromGenericDocument(GenericDocument genericDoc,
+      Map<String, List<String>> documentClassMap) throws AppSearchException {
+    String namespaceConv = genericDoc.getNamespace();
+    String idConv = genericDoc.getId();
+    String[] nameCopy = genericDoc.getPropertyStringArray("name");
+    String nameConv = null;
+    if (nameCopy != null && nameCopy.length != 0) {
+      nameConv = nameCopy[0];
+    }
+    EmbeddingVector[] defaultIndexNoneCopy = genericDoc.getPropertyEmbeddingArray("defaultIndexNone");
+    EmbeddingVector defaultIndexNoneConv = null;
+    if (defaultIndexNoneCopy != null && defaultIndexNoneCopy.length != 0) {
+      defaultIndexNoneConv = defaultIndexNoneCopy[0];
+    }
+    EmbeddingVector[] indexNoneCopy = genericDoc.getPropertyEmbeddingArray("indexNone");
+    EmbeddingVector indexNoneConv = null;
+    if (indexNoneCopy != null && indexNoneCopy.length != 0) {
+      indexNoneConv = indexNoneCopy[0];
+    }
+    EmbeddingVector[] vecCopy = genericDoc.getPropertyEmbeddingArray("vec");
+    EmbeddingVector vecConv = null;
+    if (vecCopy != null && vecCopy.length != 0) {
+      vecConv = vecCopy[0];
+    }
+    EmbeddingVector[] listVecCopy = genericDoc.getPropertyEmbeddingArray("listVec");
+    List<EmbeddingVector> listVecConv = null;
+    if (listVecCopy != null) {
+      listVecConv = Arrays.asList(listVecCopy);
+    }
+    EmbeddingVector[] collectVecCopy = genericDoc.getPropertyEmbeddingArray("collectVec");
+    List<EmbeddingVector> collectVecConv = null;
+    if (collectVecCopy != null) {
+      collectVecConv = Arrays.asList(collectVecCopy);
+    }
+    EmbeddingVector[] arrVecConv = genericDoc.getPropertyEmbeddingArray("arrVec");
+    Gift document = new Gift();
+    document.namespace = namespaceConv;
+    document.id = idConv;
+    document.name = nameConv;
+    document.defaultIndexNone = defaultIndexNoneConv;
+    document.indexNone = indexNoneConv;
+    document.vec = vecConv;
+    document.listVec = listVecConv;
+    document.collectVec = collectVecConv;
+    document.arrVec = arrVecConv;
+    return document;
+  }
+}
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA
index ee36e9d6..6ac5c27 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_allSupportedTypes.JAVA
@@ -2,6 +2,7 @@
 
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.DocumentClassFactory;
+import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.GenericDocument;
 import androidx.appsearch.exceptions.AppSearchException;
 import java.lang.Boolean;
@@ -61,6 +62,10 @@
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
             .setShouldIndexNestedProperties(false)
             .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("collectVec")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE)
+            .build())
           .addProperty(new AppSearchSchema.LongPropertyConfig.Builder("arrBoxLong")
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
             .setIndexingType(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE)
@@ -108,6 +113,10 @@
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
             .setShouldIndexNestedProperties(false)
             .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("arrVec")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE)
+            .build())
           .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("string")
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
             .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
@@ -155,6 +164,10 @@
             .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
             .setShouldIndexNestedProperties(false)
             .build())
+          .addProperty(new AppSearchSchema.EmbeddingPropertyConfig.Builder("vec")
+            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE)
+            .build())
           .build();
   }
 
@@ -237,6 +250,11 @@
       }
       builder.setPropertyDocument("collectGift", collectGiftConv);
     }
+    Collection<EmbeddingVector> collectVecCopy = document.collectVec;
+    if (collectVecCopy != null) {
+      EmbeddingVector[] collectVecConv = collectVecCopy.toArray(new EmbeddingVector[0]);
+      builder.setPropertyEmbedding("collectVec", collectVecConv);
+    }
     Long[] arrBoxLongCopy = document.arrBoxLong;
     if (arrBoxLongCopy != null) {
       long[] arrBoxLongConv = new long[arrBoxLongCopy.length];
@@ -321,6 +339,10 @@
       }
       builder.setPropertyDocument("arrGift", arrGiftConv);
     }
+    EmbeddingVector[] arrVecCopy = document.arrVec;
+    if (arrVecCopy != null) {
+      builder.setPropertyEmbedding("arrVec", arrVecCopy);
+    }
     String stringCopy = document.string;
     if (stringCopy != null) {
       builder.setPropertyString("string", stringCopy);
@@ -359,6 +381,10 @@
       GenericDocument giftConv = GenericDocument.fromDocumentClass(giftCopy);
       builder.setPropertyDocument("gift", giftConv);
     }
+    EmbeddingVector vecCopy = document.vec;
+    if (vecCopy != null) {
+      builder.setPropertyEmbedding("vec", vecCopy);
+    }
     return builder.build();
   }
 
@@ -428,6 +454,11 @@
         collectGiftConv.add(collectGiftCopy[i].toDocumentClass(Gift.class, documentClassMap));
       }
     }
+    EmbeddingVector[] collectVecCopy = genericDoc.getPropertyEmbeddingArray("collectVec");
+    List<EmbeddingVector> collectVecConv = null;
+    if (collectVecCopy != null) {
+      collectVecConv = Arrays.asList(collectVecCopy);
+    }
     long[] arrBoxLongCopy = genericDoc.getPropertyLongArray("arrBoxLong");
     Long[] arrBoxLongConv = null;
     if (arrBoxLongCopy != null) {
@@ -497,6 +528,7 @@
         arrGiftConv[i] = arrGiftCopy[i].toDocumentClass(Gift.class, documentClassMap);
       }
     }
+    EmbeddingVector[] arrVecConv = genericDoc.getPropertyEmbeddingArray("arrVec");
     String[] stringCopy = genericDoc.getPropertyStringArray("string");
     String stringConv = null;
     if (stringCopy != null && stringCopy.length != 0) {
@@ -542,6 +574,11 @@
     if (giftCopy != null) {
       giftConv = giftCopy.toDocumentClass(Gift.class, documentClassMap);
     }
+    EmbeddingVector[] vecCopy = genericDoc.getPropertyEmbeddingArray("vec");
+    EmbeddingVector vecConv = null;
+    if (vecCopy != null && vecCopy.length != 0) {
+      vecConv = vecCopy[0];
+    }
     Gift document = new Gift();
     document.namespace = namespaceConv;
     document.id = idConv;
@@ -553,6 +590,7 @@
     document.collectByteArr = collectByteArrConv;
     document.collectString = collectStringConv;
     document.collectGift = collectGiftConv;
+    document.collectVec = collectVecConv;
     document.arrBoxLong = arrBoxLongConv;
     document.arrUnboxLong = arrUnboxLongConv;
     document.arrBoxInteger = arrBoxIntegerConv;
@@ -566,6 +604,7 @@
     document.arrUnboxByteArr = arrUnboxByteArrConv;
     document.arrString = arrStringConv;
     document.arrGift = arrGiftConv;
+    document.arrVec = arrVecConv;
     document.string = stringConv;
     document.boxLong = boxLongConv;
     document.unboxLong = unboxLongConv;
@@ -579,6 +618,7 @@
     document.unboxBoolean = unboxBooleanConv;
     document.unboxByteArr = unboxByteArrConv;
     document.gift = giftConv;
+    document.vec = vecConv;
     return document;
   }
 }
diff --git a/appsearch/exportToFramework.py b/appsearch/exportToFramework.py
index 8b3a414..b483b72 100755
--- a/appsearch/exportToFramework.py
+++ b/appsearch/exportToFramework.py
@@ -64,7 +64,8 @@
 FRAMEWORK_API_TEST_ROOT = 'testing/coretests/src/android/app/appsearch/external'
 FRAMEWORK_IMPL_ROOT = 'service/java/com/android/server/appsearch/external'
 FRAMEWORK_IMPL_TEST_ROOT = 'testing/servicestests/src/com/android/server/appsearch/external'
-FRAMEWORK_TEST_UTIL_ROOT = '../../../cts/tests/appsearch/testutils/src/android/app/appsearch/testutil/external'
+FRAMEWORK_TEST_UTIL_ROOT = (
+    '../../../cts/tests/appsearch/testutils/src/android/app/appsearch/testutil/external')
 FRAMEWORK_TEST_UTIL_TEST_ROOT = 'testing/servicestests/src/android/app/appsearch/testutil/external'
 FRAMEWORK_CTS_TEST_ROOT = '../../../cts/tests/appsearch/src/com/android/cts/appsearch/external'
 GOOGLE_JAVA_FORMAT = (
@@ -88,19 +89,34 @@
                 os.remove(abs_path)
 
     def _TransformAndCopyFile(
-            self, source_path, dest_path, transform_func=None, ignore_skips=False):
+            self, source_path, default_dest_path, transform_func=None, ignore_skips=False):
+        """
+        Transforms the file located at 'source_path' and writes it into 'default_dest_path'.
+
+        An @exportToFramework:skip() directive will skip the copy process.
+        An @exportToFramework:copyToPath() directive will override default_dest_path with another
+          path relative to framework_appsearch_root (which is usually packages/modules/AppSearch)
+        """
         with open(source_path, 'r') as fh:
             contents = fh.read()
 
         if not ignore_skips and '@exportToFramework:skipFile()' in contents:
-            print('Skipping: "%s" -> "%s"' % (source_path, dest_path), file=sys.stderr)
+            print('Skipping: "%s" -> "%s"' % (source_path, default_dest_path), file=sys.stderr)
             return
 
-        copyToPath = re.search(r'@exportToFramework:copyToPath\(([^)]+)\)', contents)
-        if copyToPath:
-          dest_path = os.path.join(self._framework_appsearch_root, copyToPath.group(1))
+        copy_to_path = re.search(r'@exportToFramework:copyToPath\(([^)]+)\)', contents)
+        if copy_to_path:
+            dest_path = os.path.join(self._framework_appsearch_root, copy_to_path.group(1))
+        else:
+            dest_path = default_dest_path
 
+        self._TransformAndCopyFileToPath(source_path, dest_path, transform_func)
+
+    def _TransformAndCopyFileToPath(self, source_path, dest_path, transform_func=None):
+        """Transforms the file located at 'source_path' and writes it into 'dest_path'."""
         print('Copy: "%s" -> "%s"' % (source_path, dest_path), file=sys.stderr)
+        with open(source_path, 'r') as fh:
+            contents = fh.read()
         if transform_func:
             contents = transform_func(contents)
         os.makedirs(os.path.dirname(dest_path), exist_ok=True)
@@ -149,6 +165,8 @@
             .replace(
                     'androidx.appsearch.localstorage.',
                     'com.android.server.appsearch.external.localstorage.')
+            .replace('androidx.appsearch.flags.FlaggedApi', 'android.annotation.FlaggedApi')
+            .replace('androidx.appsearch.flags.Flags', 'com.android.appsearch.flags.Flags')
             .replace('androidx.appsearch', 'android.app.appsearch')
             .replace(
                     'androidx.annotation.GuardedBy',
@@ -180,6 +198,7 @@
             .replace('// @exportToFramework:skipFile()', '')
         )
         contents = re.sub(r'\/\/ @exportToFramework:copyToPath\([^)]+\)', '', contents)
+        contents = re.sub(r'@RequiresFeature\([^)]*\)', '', contents, flags=re.DOTALL)
 
         # Jetpack methods have the Async suffix, but framework doesn't. Strip the Async suffix
         # to allow the same documentation to compile for both.
@@ -190,16 +209,27 @@
 
     def _TransformTestCode(self, contents):
         contents = (contents
+            .replace(
+                    'androidx.appsearch.flags.CheckFlagsRule',
+                    'android.platform.test.flag.junit.CheckFlagsRule')
+            .replace(
+                    'androidx.appsearch.flags.DeviceFlagsValueProvider',
+                    'android.platform.test.flag.junit.DeviceFlagsValueProvider')
+            .replace(
+                    'androidx.appsearch.flags.RequiresFlagsEnabled',
+                    'android.platform.test.annotations.RequiresFlagsEnabled')
             .replace('androidx.appsearch.testutil.', 'android.app.appsearch.testutil.')
             .replace(
                     'package androidx.appsearch.testutil;',
                     'package android.app.appsearch.testutil;')
             .replace(
-                    'androidx.appsearch.localstorage.LocalStorage',
-                    'android.app.appsearch.AppSearchManager')
+                    'import androidx.appsearch.localstorage.LocalStorage;',
+                    'import android.app.appsearch.AppSearchManager;')
             .replace('LocalStorage.', 'AppSearchManager.')
         )
-        for shim in ['AppSearchSession', 'GlobalSearchSession', 'SearchResults']:
+        for shim in [
+                'AppSearchSession', 'GlobalSearchSession', 'EnterpriseGlobalSearchSession',
+                'SearchResults']:
             contents = re.sub(r"([^a-zA-Z])(%s)([^a-zA-Z0-9])" % shim, r'\1\2Shim\3', contents)
         return self._TransformCommonCode(contents)
 
@@ -277,7 +307,8 @@
         self._TransformAndCopyFolder(
                 test_util_source_dir, test_util_dest_dir, transform_func=self._TransformTestCode)
         for iface_file in (
-                'AppSearchSession.java', 'GlobalSearchSession.java', 'SearchResults.java'):
+                'AppSearchSession.java', 'GlobalSearchSession.java',
+                'EnterpriseGlobalSearchSession.java', 'SearchResults.java'):
             dest_file_name = os.path.splitext(iface_file)[0] + 'Shim.java'
             self._TransformAndCopyFile(
                     os.path.join(api_source_dir, 'app/' + iface_file),
@@ -318,6 +349,8 @@
                             'package com.android.server.appsearch.external')
                     .replace('com.google.android.icing.proto.',
                             'com.android.server.appsearch.icing.proto.')
+                    .replace('com.google.android.appsearch.proto.',
+                            'com.android.server.appsearch.appsearch.proto.')
                     .replace('com.google.android.icing.protobuf.',
                             'com.android.server.appsearch.protobuf.')
             )
@@ -360,6 +393,32 @@
         print('Wrote "%s"' % file_path)
         return old_sha
 
+    def FormatCommitMessage(self, old_sha, new_sha):
+        print('\nCommand to diff old version to new version:')
+        print('  git log --pretty=format:"* %h %s" {}..{} -- appsearch/'.format(old_sha, new_sha))
+        pretty_log = subprocess.check_output([
+            'git',
+            'log',
+            '--pretty=format:* %h %s',
+            '{}..{}'.format(old_sha, new_sha),
+            '--',
+            'appsearch/'
+        ]).decode("utf-8")
+        bug_output = subprocess.check_output([
+            '/bin/sh',
+            '-c',
+            'git log {}..{} -- appsearch/ | grep Bug: | sort | uniq'.format(old_sha, new_sha)
+        ]).decode("utf-8")
+
+        print('\n--------------------------------------------------')
+        print('Update Framework from Jetpack.\n')
+        print(pretty_log)
+        print()
+        for line in bug_output.splitlines():
+            print(line.strip())
+        print('Test: Presubmit\n')
+        print('--------------------------------------------------\n')
+
 
 if __name__ == '__main__':
     if len(sys.argv) != 3:
@@ -388,5 +447,4 @@
     new_sha = sys.argv[2]
     old_sha = exporter.WriteShaFile(new_sha)
     if old_sha and old_sha != new_sha:
-      print('Command to diff old version to new version:')
-      print('  git log %s..%s -- appsearch/' % (old_sha, new_sha))
+        exporter.FormatCommitMessage(old_sha, new_sha)
diff --git a/arch/core/core-common/build.gradle b/arch/core/core-common/build.gradle
index ecec73c..dcde4b3 100644
--- a/arch/core/core-common/build.gradle
+++ b/arch/core/core-common/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
@@ -40,5 +40,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Arch-Common"
-    metalavaK2UastEnabled = true
 }
diff --git a/arch/core/core-runtime/build.gradle b/arch/core/core-runtime/build.gradle
index cf6b8aa..65c0d7c 100644
--- a/arch/core/core-runtime/build.gradle
+++ b/arch/core/core-runtime/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":arch:core:core-common"))
 }
 
@@ -38,7 +38,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Arch-Runtime"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/arch/core/core-testing/build.gradle b/arch/core/core-testing/build.gradle
index 1838375..dda5131 100644
--- a/arch/core/core-testing/build.gradle
+++ b/arch/core/core-testing/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     api(project(":arch:core:core-runtime"))
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.junit)
     api(libs.mockitoCore, excludes.bytebuddy)
 
@@ -48,7 +48,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Core-Testing"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/asynclayoutinflater/asynclayoutinflater-appcompat/build.gradle b/asynclayoutinflater/asynclayoutinflater-appcompat/build.gradle
index 61be78a..37f331f 100644
--- a/asynclayoutinflater/asynclayoutinflater-appcompat/build.gradle
+++ b/asynclayoutinflater/asynclayoutinflater-appcompat/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
     implementation(project(":appcompat:appcompat"))
     implementation(project(":asynclayoutinflater:asynclayoutinflater"))
@@ -25,7 +25,6 @@
     inceptionYear = "2022"
     description = "A thread-safe LayoutInflater Factory that provides compatibility between " +
             "AsyncLayoutInflater and AppCompat."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/asynclayoutinflater/asynclayoutinflater/build.gradle b/asynclayoutinflater/asynclayoutinflater/build.gradle
index 9db6751..f0484b3 100644
--- a/asynclayoutinflater/asynclayoutinflater/build.gradle
+++ b/asynclayoutinflater/asynclayoutinflater/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
     androidTestImplementation(project(":appcompat:appcompat"))
     androidTestImplementation(project(":asynclayoutinflater:asynclayoutinflater-appcompat"))
@@ -30,7 +30,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Provides support for inflating layouts off the UI thread."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/autofill/autofill/build.gradle b/autofill/autofill/build.gradle
index 3e30cba5..df1bf78 100644
--- a/autofill/autofill/build.gradle
+++ b/autofill/autofill/build.gradle
@@ -45,7 +45,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2019"
     description = "AndroidX Autofill"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPlugin.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPlugin.kt
index 9e0f2ba..c683663 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPlugin.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPlugin.kt
@@ -19,6 +19,7 @@
 import androidx.baselineprofile.gradle.configuration.ConfigurationManager
 import androidx.baselineprofile.gradle.producer.tasks.CollectBaselineProfileTask
 import androidx.baselineprofile.gradle.producer.tasks.InstrumentationTestTaskWrapper
+import androidx.baselineprofile.gradle.utils.AgpFeature.CONFIGURATION_CACHE_FIX_B348136774
 import androidx.baselineprofile.gradle.utils.AgpFeature.TEST_MODULE_SUPPORTS_MULTIPLE_BUILD_TYPES
 import androidx.baselineprofile.gradle.utils.AgpFeature.TEST_VARIANT_SUPPORTS_INSTRUMENTATION_RUNNER_ARGUMENTS
 import androidx.baselineprofile.gradle.utils.AgpFeature.TEST_VARIANT_TESTED_APKS
@@ -272,7 +273,9 @@
         // app as an instrumentation runner argument. BaselineProfileRule and MacrobenchmarkRule
         // can pick that up during the test execution.
         if (
-            addTargetPackageNameInstrumentationArgument && supportsFeature(TEST_VARIANT_TESTED_APKS)
+            addTargetPackageNameInstrumentationArgument &&
+                supportsFeature(TEST_VARIANT_TESTED_APKS) &&
+                supportsFeature(CONFIGURATION_CACHE_FIX_B348136774)
         ) {
             InstrumentationTestRunnerArgumentsAgp82.set(
                 variant = variant,
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Agp.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Agp.kt
index 1a16adb..283a361 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Agp.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Agp.kt
@@ -42,7 +42,8 @@
     TEST_MODULE_SUPPORTS_MULTIPLE_BUILD_TYPES(AndroidPluginVersion(8, 1, 0).alpha(7)),
     TEST_VARIANT_SUPPORTS_INSTRUMENTATION_RUNNER_ARGUMENTS(AndroidPluginVersion(8, 2, 0).alpha(3)),
     LIBRARY_MODULE_SUPPORTS_BASELINE_PROFILE_SOURCE_SETS(AndroidPluginVersion(8, 3, 0).alpha(15)),
-    TEST_VARIANT_TESTED_APKS(AndroidPluginVersion(8, 3, 0))
+    TEST_VARIANT_TESTED_APKS(AndroidPluginVersion(8, 3, 0)),
+    CONFIGURATION_CACHE_FIX_B348136774(AndroidPluginVersion(8, 4, 0)),
 }
 
 /**
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Utils.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Utils.kt
index 9d51a3b..740261b 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Utils.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/Utils.kt
@@ -31,7 +31,7 @@
         .toString()
 }
 
-private fun CharSequence.capitalized(): String =
+internal fun CharSequence.capitalized(): String =
     when {
         isEmpty() -> ""
         else ->
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPluginTest.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPluginTest.kt
index 25addd7..5ab636c 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPluginTest.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/producer/BaselineProfileProducerPluginTest.kt
@@ -21,6 +21,7 @@
 import androidx.baselineprofile.gradle.utils.TestAgpVersion.TEST_AGP_VERSION_8_1_0
 import androidx.baselineprofile.gradle.utils.TestAgpVersion.TEST_AGP_VERSION_8_2_0
 import androidx.baselineprofile.gradle.utils.TestAgpVersion.TEST_AGP_VERSION_8_3_1
+import androidx.baselineprofile.gradle.utils.TestAgpVersion.TEST_AGP_VERSION_8_4_2
 import androidx.baselineprofile.gradle.utils.VariantProfile
 import androidx.baselineprofile.gradle.utils.build
 import androidx.baselineprofile.gradle.utils.buildAndAssertThatOutput
@@ -370,31 +371,6 @@
         VariantProfile(flavor = null, buildType = "release", profileFileLines = mapOf())
 
     @Test
-    fun verifyTargetPackageNamePassedAsInstrumentationRunnerArgument() {
-        projectSetup.appTarget.setup()
-        projectSetup.producer.setup(
-            variantProfiles = listOf(emptyReleaseVariantProfile),
-            targetProject = projectSetup.appTarget,
-            additionalGradleCodeBlock = GRADLE_PRINT_ARGS_TASK
-        )
-        arrayOf(
-                Pair(
-                    "benchmarkReleaseArguments",
-                    "androidx.benchmark.targetPackageName=com.example.namespace"
-                ),
-                Pair(
-                    "nonMinifiedReleaseArguments",
-                    "androidx.benchmark.targetPackageName=com.example.namespace"
-                ),
-            )
-            .forEach {
-                projectSetup.producer.gradleRunner.buildAndAssertThatOutput(it.first) {
-                    contains(it.second)
-                }
-            }
-    }
-
-    @Test
     fun verifyTargetPackageNamePassedAsInstrumentationRunnerArgumentWithOverride() {
         projectSetup.appTarget.setup()
         projectSetup.producer.setup(
@@ -427,3 +403,44 @@
             }
     }
 }
+
+@RunWith(Parameterized::class)
+class BaselineProfileProducerPluginTestWithAgp84AndAbove(agpVersion: TestAgpVersion) {
+
+    companion object {
+        @Parameterized.Parameters(name = "agpVersion={0}")
+        @JvmStatic
+        fun parameters() = TestAgpVersion.atLeast(TEST_AGP_VERSION_8_4_2)
+    }
+
+    @get:Rule
+    val projectSetup = BaselineProfileProjectSetupRule(forceAgpVersion = agpVersion.versionString)
+
+    private val emptyReleaseVariantProfile =
+        VariantProfile(flavor = null, buildType = "release", profileFileLines = mapOf())
+
+    @Test
+    fun verifyTargetPackageNamePassedAsInstrumentationRunnerArgument() {
+        projectSetup.appTarget.setup()
+        projectSetup.producer.setup(
+            variantProfiles = listOf(emptyReleaseVariantProfile),
+            targetProject = projectSetup.appTarget,
+            additionalGradleCodeBlock = GRADLE_PRINT_ARGS_TASK
+        )
+        arrayOf(
+                Pair(
+                    "benchmarkReleaseArguments",
+                    "androidx.benchmark.targetPackageName=com.example.namespace"
+                ),
+                Pair(
+                    "nonMinifiedReleaseArguments",
+                    "androidx.benchmark.targetPackageName=com.example.namespace"
+                ),
+            )
+            .forEach {
+                projectSetup.producer.gradleRunner.buildAndAssertThatOutput(it.first) {
+                    contains(it.second)
+                }
+            }
+    }
+}
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt
index 628591e..0fc0d5f 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt
@@ -26,7 +26,6 @@
 import com.google.testing.platform.proto.api.core.TestSuiteResultProto
 import java.io.File
 import java.util.Properties
-import org.gradle.configurationcache.extensions.capitalized
 import org.gradle.testkit.runner.GradleRunner
 import org.junit.rules.ExternalResource
 import org.junit.rules.RuleChain
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Constants.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Constants.kt
index 3e45f65..19ee4ca 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Constants.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Constants.kt
@@ -21,6 +21,7 @@
     TEST_AGP_VERSION_8_1_0("8.1.0"),
     TEST_AGP_VERSION_8_2_0("8.2.0"),
     TEST_AGP_VERSION_8_3_1("8.3.1"),
+    TEST_AGP_VERSION_8_4_2("8.4.2"),
     TEST_AGP_VERSION_CURRENT(null);
 
     fun isAtLeast(version: TestAgpVersion) = this in TestAgpVersion.atLeast(version)
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/TestUtils.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/TestUtils.kt
index c16d2aa..5392918 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/TestUtils.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/TestUtils.kt
@@ -18,7 +18,6 @@
 
 import com.google.common.truth.StringSubject
 import com.google.common.truth.Truth.assertThat
-import org.gradle.configurationcache.extensions.capitalized
 import org.gradle.testkit.runner.GradleRunner
 
 internal val GRADLE_CODE_PRINT_TASK =
diff --git a/benchmark/benchmark-common/api/1.3.0-beta01.txt b/benchmark/benchmark-common/api/1.3.0-beta01.txt
deleted file mode 100644
index 9df6fae..0000000
--- a/benchmark/benchmark-common/api/1.3.0-beta01.txt
+++ /dev/null
@@ -1,139 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark {
-
-  public final class BenchmarkState {
-    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public BenchmarkState(optional Integer? warmupCount, optional Integer? repeatCount);
-    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public java.util.List<java.lang.Double> getMeasurementTimeNs();
-    method public boolean keepRunning();
-    method public void pauseTiming();
-    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public static void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
-    method public void resumeTiming();
-    field public static final androidx.benchmark.BenchmarkState.Companion Companion;
-  }
-
-  public static final class BenchmarkState.Companion {
-    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBlackHoleApi public final class BlackHole {
-    method public static void consume(boolean value);
-    method public static void consume(byte value);
-    method public static void consume(char value);
-    method public static void consume(double value);
-    method public static void consume(float value);
-    method public static void consume(int value);
-    method public static void consume(Object value);
-    method public static void consume(long value);
-    method public static void consume(short value);
-    field public static final androidx.benchmark.BlackHole INSTANCE;
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkConfigApi {
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkStateApi {
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBlackHoleApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
-    ctor public MetricCapture(java.util.List<java.lang.String> names);
-    method public abstract void capturePaused();
-    method public abstract void captureResumed();
-    method public abstract void captureStart(long timeNs);
-    method public abstract void captureStop(long timeNs, long[] output, int offset);
-    method public final java.util.List<java.lang.String> getNames();
-    property public final java.util.List<java.lang.String> names;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
-    ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler);
-    method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
-    method public androidx.benchmark.ProfilerConfig? getProfiler();
-    method public boolean isPerfettoSdkTracingEnabled();
-    method public boolean isTraceAppTagEnabled();
-    property public final java.util.List<androidx.benchmark.MetricCapture> metrics;
-    property public final boolean perfettoSdkTracingEnabled;
-    property public final androidx.benchmark.ProfilerConfig? profiler;
-    property public final boolean traceAppTagEnabled;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract sealed class ProfilerConfig {
-  }
-
-  public static final class ProfilerConfig.MethodTracing extends androidx.benchmark.ProfilerConfig {
-    ctor public ProfilerConfig.MethodTracing();
-    field public static final boolean AFFECTS_MEASUREMENTS_ON_THIS_DEVICE;
-    field public static final androidx.benchmark.ProfilerConfig.MethodTracing.Companion Companion;
-  }
-
-  public static final class ProfilerConfig.MethodTracing.Companion {
-  }
-
-  public static final class ProfilerConfig.StackSampling extends androidx.benchmark.ProfilerConfig {
-    ctor public ProfilerConfig.StackSampling();
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
-    ctor public TimeCapture();
-    ctor public TimeCapture(optional String name);
-    method public void capturePaused();
-    method public void captureResumed();
-    method public void captureStart(long timeNs);
-    method public void captureStop(long timeNs, long[] output, int offset);
-  }
-
-}
-
-package androidx.benchmark.perfetto {
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoCaptureApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public abstract sealed class PerfettoConfig {
-  }
-
-  public static final class PerfettoConfig.Binary extends androidx.benchmark.perfetto.PerfettoConfig {
-    ctor public PerfettoConfig.Binary(byte[] bytes);
-    method public byte[] getBytes();
-    property public final byte[] bytes;
-  }
-
-  public static final class PerfettoConfig.Text extends androidx.benchmark.perfetto.PerfettoConfig {
-    ctor public PerfettoConfig.Text(String text);
-    method public String getText();
-    property public final String text;
-  }
-
-  @SuppressCompatibility @RequiresApi(23) @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTrace {
-    ctor public PerfettoTrace(String path);
-    method public String getPath();
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    property public final String path;
-    field public static final androidx.benchmark.perfetto.PerfettoTrace.Companion Companion;
-  }
-
-  public static final class PerfettoTrace.Companion {
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-  }
-
-}
-
diff --git a/benchmark/benchmark-common/api/current.txt b/benchmark/benchmark-common/api/current.txt
index 9df6fae..a1c6e7b 100644
--- a/benchmark/benchmark-common/api/current.txt
+++ b/benchmark/benchmark-common/api/current.txt
@@ -40,6 +40,15 @@
   @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBlackHoleApi {
   }
 
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class ExperimentalConfig {
+    ctor public ExperimentalConfig();
+    ctor public ExperimentalConfig(optional androidx.benchmark.perfetto.PerfettoConfig? perfettoConfig, optional androidx.benchmark.StartupInsightsConfig? startupInsightsConfig);
+    method public androidx.benchmark.perfetto.PerfettoConfig? getPerfettoConfig();
+    method public androidx.benchmark.StartupInsightsConfig? getStartupInsightsConfig();
+    property public final androidx.benchmark.perfetto.PerfettoConfig? perfettoConfig;
+    property public final androidx.benchmark.StartupInsightsConfig? startupInsightsConfig;
+  }
+
   @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
     ctor public MetricCapture(java.util.List<java.lang.String> names);
     method public abstract void capturePaused();
@@ -51,6 +60,7 @@
   }
 
   @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
+    ctor public MicrobenchmarkConfig();
     ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler);
     method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
     method public androidx.benchmark.ProfilerConfig? getProfiler();
@@ -78,6 +88,12 @@
     ctor public ProfilerConfig.StackSampling();
   }
 
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class StartupInsightsConfig {
+    ctor public StartupInsightsConfig(boolean isEnabled);
+    method public boolean isEnabled();
+    property public final boolean isEnabled;
+  }
+
   @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
     ctor public TimeCapture();
     ctor public TimeCapture(optional String name);
diff --git a/benchmark/benchmark-common/api/restricted_1.3.0-beta01.txt b/benchmark/benchmark-common/api/restricted_1.3.0-beta01.txt
deleted file mode 100644
index e2bfb40..0000000
--- a/benchmark/benchmark-common/api/restricted_1.3.0-beta01.txt
+++ /dev/null
@@ -1,141 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark {
-
-  public final class BenchmarkState {
-    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public BenchmarkState(optional Integer? warmupCount, optional Integer? repeatCount);
-    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public java.util.List<java.lang.Double> getMeasurementTimeNs();
-    method public boolean keepRunning();
-    method @kotlin.PublishedApi internal boolean keepRunningInternal();
-    method public void pauseTiming();
-    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public static void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
-    method public void resumeTiming();
-    field public static final androidx.benchmark.BenchmarkState.Companion Companion;
-    field @kotlin.PublishedApi internal int iterationsRemaining;
-  }
-
-  public static final class BenchmarkState.Companion {
-    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBlackHoleApi public final class BlackHole {
-    method public static void consume(boolean value);
-    method public static void consume(byte value);
-    method public static void consume(char value);
-    method public static void consume(double value);
-    method public static void consume(float value);
-    method public static void consume(int value);
-    method public static void consume(Object value);
-    method public static void consume(long value);
-    method public static void consume(short value);
-    field public static final androidx.benchmark.BlackHole INSTANCE;
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkConfigApi {
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkStateApi {
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBlackHoleApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
-    ctor public MetricCapture(java.util.List<java.lang.String> names);
-    method public abstract void capturePaused();
-    method public abstract void captureResumed();
-    method public abstract void captureStart(long timeNs);
-    method public abstract void captureStop(long timeNs, long[] output, int offset);
-    method public final java.util.List<java.lang.String> getNames();
-    property public final java.util.List<java.lang.String> names;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
-    ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler);
-    method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
-    method public androidx.benchmark.ProfilerConfig? getProfiler();
-    method public boolean isPerfettoSdkTracingEnabled();
-    method public boolean isTraceAppTagEnabled();
-    property public final java.util.List<androidx.benchmark.MetricCapture> metrics;
-    property public final boolean perfettoSdkTracingEnabled;
-    property public final androidx.benchmark.ProfilerConfig? profiler;
-    property public final boolean traceAppTagEnabled;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract sealed class ProfilerConfig {
-  }
-
-  public static final class ProfilerConfig.MethodTracing extends androidx.benchmark.ProfilerConfig {
-    ctor public ProfilerConfig.MethodTracing();
-    field public static final boolean AFFECTS_MEASUREMENTS_ON_THIS_DEVICE;
-    field public static final androidx.benchmark.ProfilerConfig.MethodTracing.Companion Companion;
-  }
-
-  public static final class ProfilerConfig.MethodTracing.Companion {
-  }
-
-  public static final class ProfilerConfig.StackSampling extends androidx.benchmark.ProfilerConfig {
-    ctor public ProfilerConfig.StackSampling();
-  }
-
-  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
-    ctor public TimeCapture();
-    ctor public TimeCapture(optional String name);
-    method public void capturePaused();
-    method public void captureResumed();
-    method public void captureStart(long timeNs);
-    method public void captureStop(long timeNs, long[] output, int offset);
-  }
-
-}
-
-package androidx.benchmark.perfetto {
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoCaptureApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public abstract sealed class PerfettoConfig {
-  }
-
-  public static final class PerfettoConfig.Binary extends androidx.benchmark.perfetto.PerfettoConfig {
-    ctor public PerfettoConfig.Binary(byte[] bytes);
-    method public byte[] getBytes();
-    property public final byte[] bytes;
-  }
-
-  public static final class PerfettoConfig.Text extends androidx.benchmark.perfetto.PerfettoConfig {
-    ctor public PerfettoConfig.Text(String text);
-    method public String getText();
-    property public final String text;
-  }
-
-  @SuppressCompatibility @RequiresApi(23) @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTrace {
-    ctor public PerfettoTrace(String path);
-    method public String getPath();
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public static void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    property public final String path;
-    field public static final androidx.benchmark.perfetto.PerfettoTrace.Companion Companion;
-  }
-
-  public static final class PerfettoTrace.Companion {
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-    method public void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
-  }
-
-}
-
diff --git a/benchmark/benchmark-common/api/restricted_current.txt b/benchmark/benchmark-common/api/restricted_current.txt
index e2bfb40..d661084 100644
--- a/benchmark/benchmark-common/api/restricted_current.txt
+++ b/benchmark/benchmark-common/api/restricted_current.txt
@@ -42,6 +42,15 @@
   @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBlackHoleApi {
   }
 
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class ExperimentalConfig {
+    ctor public ExperimentalConfig();
+    ctor public ExperimentalConfig(optional androidx.benchmark.perfetto.PerfettoConfig? perfettoConfig, optional androidx.benchmark.StartupInsightsConfig? startupInsightsConfig);
+    method public androidx.benchmark.perfetto.PerfettoConfig? getPerfettoConfig();
+    method public androidx.benchmark.StartupInsightsConfig? getStartupInsightsConfig();
+    property public final androidx.benchmark.perfetto.PerfettoConfig? perfettoConfig;
+    property public final androidx.benchmark.StartupInsightsConfig? startupInsightsConfig;
+  }
+
   @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
     ctor public MetricCapture(java.util.List<java.lang.String> names);
     method public abstract void capturePaused();
@@ -53,6 +62,7 @@
   }
 
   @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
+    ctor public MicrobenchmarkConfig();
     ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler);
     method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
     method public androidx.benchmark.ProfilerConfig? getProfiler();
@@ -80,6 +90,12 @@
     ctor public ProfilerConfig.StackSampling();
   }
 
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class StartupInsightsConfig {
+    ctor public StartupInsightsConfig(boolean isEnabled);
+    method public boolean isEnabled();
+    property public final boolean isEnabled;
+  }
+
   @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
     ctor public TimeCapture();
     ctor public TimeCapture(optional String name);
diff --git a/benchmark/benchmark-common/build.gradle b/benchmark/benchmark-common/build.gradle
index 024ae02..6646ae9 100644
--- a/benchmark/benchmark-common/build.gradle
+++ b/benchmark/benchmark-common/build.gradle
@@ -74,7 +74,7 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.7.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.annotation:annotation-experimental:1.4.1")
     implementation("androidx.tracing:tracing-ktx:1.0.0")
     implementation("androidx.tracing:tracing-perfetto-handshake:1.0.0")
@@ -93,7 +93,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Benchmark - Common"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/benchmark/benchmark-common/lint-baseline.xml b/benchmark/benchmark-common/lint-baseline.xml
index 0568301..412113c 100644
--- a/benchmark/benchmark-common/lint-baseline.xml
+++ b/benchmark/benchmark-common/lint-baseline.xml
@@ -1,19 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.ExperimentalBenchmarkConfigApi` or `@OptIn(markerClass = androidx.benchmark.ExperimentalBenchmarkConfigApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/MetricsContainer.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.ExperimentalBenchmarkConfigApi` or `@OptIn(markerClass = androidx.benchmark.ExperimentalBenchmarkConfigApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ObsoleteSdkInt"
@@ -36,8 +22,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="        Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    if (sampled &amp;&amp; Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/benchmark/Profiler.kt"/>
     </issue>
@@ -72,267 +58,6 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(21)"
-        errorLine2="    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/benchmark/Shell.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
         errorLine1="        if (Build.VERSION.SDK_INT >= 21) {"
         errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/ExperimentalBenchmarkConfigApi.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ExperimentalBenchmarkConfigApi.kt
index 4fa79924..f7a26ad 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/ExperimentalBenchmarkConfigApi.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ExperimentalBenchmarkConfigApi.kt
@@ -16,11 +16,21 @@
 
 package androidx.benchmark
 
+import androidx.benchmark.perfetto.PerfettoConfig
+
 /**
- * Annotation indicating experimental API primarily intended to allow configuration of
- * microbenchmarks from code, overriding instrumentation arguments like
- * `androidx.benchmark.profiling.mode`, and custom microbenchmark metrics.
+ * Annotates declarations that are considered experimental within the Benchmark API, and are likely
+ * to change before becoming stable. Using experimental features can potentially break your code if
+ * the design or behavior changes.
  */
 @RequiresOptIn
 @Retention(AnnotationRetention.BINARY)
 public annotation class ExperimentalBenchmarkConfigApi
+
+@ExperimentalBenchmarkConfigApi
+public class ExperimentalConfig(
+    val perfettoConfig: PerfettoConfig? = null,
+    val startupInsightsConfig: StartupInsightsConfig? = null
+)
+
+@ExperimentalBenchmarkConfigApi public class StartupInsightsConfig(val isEnabled: Boolean)
diff --git a/benchmark/benchmark-darwin-gradle-plugin/lint-baseline.xml b/benchmark/benchmark-darwin-gradle-plugin/lint-baseline.xml
index dcdc3f1..9ce75c2 100644
--- a/benchmark/benchmark-darwin-gradle-plugin/lint-baseline.xml
+++ b/benchmark/benchmark-darwin-gradle-plugin/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="GradleProjectIsolation"
         message="Use providers.gradleProperty instead of findProperty"
-        errorLine1="        val xcodeGenUri = when (val uri = project.findProperty(XCODEGEN_DOWNLOAD_URI)) {"
-        errorLine2="                                                  ~~~~~~~~~~~~">
+        errorLine1="            when (val uri = project.findProperty(XCODEGEN_DOWNLOAD_URI)) {"
+        errorLine2="                                    ~~~~~~~~~~~~">
         <location
             file="src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt"/>
     </issue>
diff --git a/benchmark/benchmark-junit4/api/1.3.0-beta01.txt b/benchmark/benchmark-junit4/api/1.3.0-beta01.txt
deleted file mode 100644
index fc4b392..0000000
--- a/benchmark/benchmark-junit4/api/1.3.0-beta01.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark.junit4 {
-
-  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
-    ctor public AndroidBenchmarkRunner();
-  }
-
-  public final class BenchmarkRule implements org.junit.rules.TestRule {
-    ctor public BenchmarkRule();
-    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public BenchmarkRule(androidx.benchmark.MicrobenchmarkConfig config);
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public androidx.benchmark.BenchmarkState getState();
-  }
-
-  public final class BenchmarkRule.Scope {
-    method public inline <T> T runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
-  }
-
-  public final class BenchmarkRuleKt {
-    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
-    method public static inline void measureRepeatedOnMainThread(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTraceRule implements org.junit.rules.TestRule {
-    ctor public PerfettoTraceRule(optional boolean enableAppTagTracing, optional boolean enableUserspaceTracing, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback);
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public boolean getEnableAppTagTracing();
-    method public boolean getEnableUserspaceTracing();
-    method public kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? getTraceCallback();
-    property public final boolean enableAppTagTracing;
-    property public final boolean enableUserspaceTracing;
-    property public final kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback;
-  }
-
-}
-
diff --git a/benchmark/benchmark-junit4/api/current.txt b/benchmark/benchmark-junit4/api/current.txt
index fc4b392..a1baf9b 100644
--- a/benchmark/benchmark-junit4/api/current.txt
+++ b/benchmark/benchmark-junit4/api/current.txt
@@ -22,6 +22,7 @@
   }
 
   @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTraceRule implements org.junit.rules.TestRule {
+    ctor public PerfettoTraceRule();
     ctor public PerfettoTraceRule(optional boolean enableAppTagTracing, optional boolean enableUserspaceTracing, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public boolean getEnableAppTagTracing();
diff --git a/benchmark/benchmark-junit4/api/res-1.3.0-beta01.txt b/benchmark/benchmark-junit4/api/res-1.3.0-beta01.txt
deleted file mode 100644
index e69de29..0000000
--- a/benchmark/benchmark-junit4/api/res-1.3.0-beta01.txt
+++ /dev/null
diff --git a/benchmark/benchmark-junit4/api/restricted_1.3.0-beta01.txt b/benchmark/benchmark-junit4/api/restricted_1.3.0-beta01.txt
deleted file mode 100644
index 6aac49d..0000000
--- a/benchmark/benchmark-junit4/api/restricted_1.3.0-beta01.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark.junit4 {
-
-  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
-    ctor public AndroidBenchmarkRunner();
-  }
-
-  public final class BenchmarkRule implements org.junit.rules.TestRule {
-    ctor public BenchmarkRule();
-    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public BenchmarkRule(androidx.benchmark.MicrobenchmarkConfig config);
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public androidx.benchmark.BenchmarkState getState();
-  }
-
-  public final class BenchmarkRule.Scope {
-    method @kotlin.PublishedApi internal androidx.benchmark.BenchmarkState getOuterState();
-    method public inline <T> T runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
-  }
-
-  public final class BenchmarkRuleKt {
-    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
-    method public static inline void measureRepeatedOnMainThread(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTraceRule implements org.junit.rules.TestRule {
-    ctor public PerfettoTraceRule(optional boolean enableAppTagTracing, optional boolean enableUserspaceTracing, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback);
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public boolean getEnableAppTagTracing();
-    method public boolean getEnableUserspaceTracing();
-    method public kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? getTraceCallback();
-    property public final boolean enableAppTagTracing;
-    property public final boolean enableUserspaceTracing;
-    property public final kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback;
-  }
-
-}
-
diff --git a/benchmark/benchmark-junit4/api/restricted_current.txt b/benchmark/benchmark-junit4/api/restricted_current.txt
index 6aac49d..60b3cc5 100644
--- a/benchmark/benchmark-junit4/api/restricted_current.txt
+++ b/benchmark/benchmark-junit4/api/restricted_current.txt
@@ -23,6 +23,7 @@
   }
 
   @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTraceRule implements org.junit.rules.TestRule {
+    ctor public PerfettoTraceRule();
     ctor public PerfettoTraceRule(optional boolean enableAppTagTracing, optional boolean enableUserspaceTracing, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public boolean getEnableAppTagTracing();
diff --git a/benchmark/benchmark-junit4/build.gradle b/benchmark/benchmark-junit4/build.gradle
index 20b9709..4a0d751 100644
--- a/benchmark/benchmark-junit4/build.gradle
+++ b/benchmark/benchmark-junit4/build.gradle
@@ -49,7 +49,7 @@
     implementation("androidx.test:rules:1.5.0")
     implementation("androidx.test:runner:1.5.2")
     implementation("androidx.tracing:tracing-ktx:1.1.0")
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(project(":internal-testutils-ktx"))
     androidTestImplementation(libs.testCore)
@@ -62,7 +62,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2019"
     description = "Android Benchmark - JUnit4"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/benchmark/benchmark-macro-junit4/api/1.3.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/1.3.0-beta01.txt
deleted file mode 100644
index e06bf57..0000000
--- a/benchmark/benchmark-macro-junit4/api/1.3.0-beta01.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark.macro.junit4 {
-
-  @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
-    ctor public BaselineProfileRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-  }
-
-  public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
-    ctor public MacrobenchmarkRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-  }
-
-}
-
diff --git a/benchmark/benchmark-macro-junit4/api/current.txt b/benchmark/benchmark-macro-junit4/api/current.txt
index e06bf57..8728e57 100644
--- a/benchmark/benchmark-macro-junit4/api/current.txt
+++ b/benchmark/benchmark-macro-junit4/api/current.txt
@@ -19,10 +19,14 @@
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
diff --git a/benchmark/benchmark-macro-junit4/api/res-1.3.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/res-1.3.0-beta01.txt
deleted file mode 100644
index e69de29..0000000
--- a/benchmark/benchmark-macro-junit4/api/res-1.3.0-beta01.txt
+++ /dev/null
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.3.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.3.0-beta01.txt
deleted file mode 100644
index e06bf57..0000000
--- a/benchmark/benchmark-macro-junit4/api/restricted_1.3.0-beta01.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark.macro.junit4 {
-
-  @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
-    ctor public BaselineProfileRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, optional int maxIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-    method public void collect(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
-  }
-
-  public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
-    ctor public MacrobenchmarkRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-  }
-
-}
-
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_current.txt b/benchmark/benchmark-macro-junit4/api/restricted_current.txt
index e06bf57..8728e57 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_current.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_current.txt
@@ -19,10 +19,14 @@
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.ExperimentalConfig experimentalConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, optional androidx.benchmark.macro.CompilationMode compilationMode, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method @Deprecated @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, androidx.benchmark.perfetto.PerfettoConfig perfettoConfig, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
     method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
diff --git a/benchmark/benchmark-macro-junit4/build.gradle b/benchmark/benchmark-macro-junit4/build.gradle
index 489cd84..5b0de69 100644
--- a/benchmark/benchmark-macro-junit4/build.gradle
+++ b/benchmark/benchmark-macro-junit4/build.gradle
@@ -40,7 +40,7 @@
 dependencies {
     api(libs.junit)
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":benchmark:benchmark-macro"))
     api("androidx.test.uiautomator:uiautomator:2.3.0-rc01")
     implementation(project(":benchmark:benchmark-common"))
@@ -74,6 +74,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Benchmark - Macrobenchmark JUnit4"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
index 45405ba..d3dd114 100644
--- a/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
+++ b/benchmark/benchmark-macro-junit4/src/main/java/androidx/benchmark/macro/junit4/MacrobenchmarkRule.kt
@@ -19,12 +19,13 @@
 import android.Manifest
 import androidx.annotation.IntRange
 import androidx.benchmark.Arguments
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.ExperimentalConfig
 import androidx.benchmark.macro.CompilationMode
 import androidx.benchmark.macro.MacrobenchmarkScope
 import androidx.benchmark.macro.Metric
 import androidx.benchmark.macro.StartupMode
 import androidx.benchmark.macro.macrobenchmarkWithStartupMode
-import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
 import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.test.rule.GrantPermissionRule
 import org.junit.Assume.assumeTrue
@@ -157,19 +158,18 @@
      * @param iterations Number of times the [measureBlock] will be run during measurement. Note
      *   that total iteration count may not match, due to warmup iterations needed for the
      *   [compilationMode].
-     * @param perfettoConfig Configuration for Perfetto trace capture during each iteration. Note
-     *   that insufficient or invalid configs may result in built-in [Metric]s not working.
+     * @param experimentalConfig Configuration for experimental features.
      * @param setupBlock The block performing app actions each iteration, prior to the
      *   [measureBlock]. For example, navigating to a UI where scrolling will be measured.
      * @param measureBlock The block performing app actions to benchmark each iteration.
      */
-    @ExperimentalPerfettoCaptureApi
+    @ExperimentalBenchmarkConfigApi
     @JvmOverloads
     fun measureRepeated(
         packageName: String,
         metrics: List<Metric>,
         @IntRange(from = 1) iterations: Int,
-        perfettoConfig: PerfettoConfig,
+        experimentalConfig: ExperimentalConfig,
         compilationMode: CompilationMode = CompilationMode.DEFAULT,
         startupMode: StartupMode? = null,
         setupBlock: MacrobenchmarkScope.() -> Unit = {},
@@ -183,13 +183,49 @@
             metrics = metrics,
             compilationMode = compilationMode,
             iterations = iterations,
-            perfettoConfig = perfettoConfig,
+            perfettoConfig = experimentalConfig.perfettoConfig,
             startupMode = startupMode,
             setupBlock = setupBlock,
             measureBlock = measureBlock
         )
     }
 
+    @ExperimentalBenchmarkConfigApi
+    @JvmOverloads
+    @Deprecated(
+        "Deprecated in favour of a variant that accepts experimental config",
+        replaceWith =
+            ReplaceWith(
+                "measureRepeated(packageName, metrics, iterations,experimentalConfig, " +
+                    "compilationMode, startupMode, setupBlock, measureBlock)"
+            ),
+        level = DeprecationLevel.WARNING
+    )
+    /**
+     * @param perfettoConfig Configuration for Perfetto trace capture during each iteration. Note
+     *   that insufficient or invalid configs may result in built-in [Metric]s not working.
+     */
+    fun measureRepeated(
+        packageName: String,
+        metrics: List<Metric>,
+        @IntRange(from = 1) iterations: Int,
+        perfettoConfig: PerfettoConfig,
+        compilationMode: CompilationMode = CompilationMode.DEFAULT,
+        startupMode: StartupMode? = null,
+        setupBlock: MacrobenchmarkScope.() -> Unit = {},
+        measureBlock: MacrobenchmarkScope.() -> Unit,
+    ) =
+        measureRepeated(
+            packageName,
+            metrics,
+            iterations,
+            ExperimentalConfig(perfettoConfig = perfettoConfig),
+            compilationMode,
+            startupMode,
+            setupBlock,
+            measureBlock
+        )
+
     override fun apply(base: Statement, description: Description): Statement {
         // Grant external storage, as it may be needed for test output directory.
         return RuleChain.outerRule(
diff --git a/benchmark/benchmark-macro/api/1.3.0-beta01.txt b/benchmark/benchmark-macro/api/1.3.0-beta01.txt
deleted file mode 100644
index ad9fb40..0000000
--- a/benchmark/benchmark-macro/api/1.3.0-beta01.txt
+++ /dev/null
@@ -1,281 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark.macro {
-
-  public enum BaselineProfileMode {
-    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Disable;
-    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Require;
-    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
-  }
-
-  public abstract sealed class CompilationMode {
-    field public static final androidx.benchmark.macro.CompilationMode.Companion Companion;
-    field public static final androidx.benchmark.macro.CompilationMode DEFAULT;
-  }
-
-  public static final class CompilationMode.Companion {
-  }
-
-  public static final class CompilationMode.Full extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.Full();
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMacrobenchmarkApi public static final class CompilationMode.Ignore extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.Ignore();
-  }
-
-  @RequiresApi(24) public static final class CompilationMode.None extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.None();
-  }
-
-  @RequiresApi(24) public static final class CompilationMode.Partial extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.Partial();
-    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode);
-    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode, optional @IntRange(from=0L) int warmupIterations);
-    method public androidx.benchmark.macro.BaselineProfileMode getBaselineProfileMode();
-    method public int getWarmupIterations();
-    property public final androidx.benchmark.macro.BaselineProfileMode baselineProfileMode;
-    property public final int warmupIterations;
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Macrobenchmark API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMacrobenchmarkApi {
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Metric API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMetricApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class FrameTimingGfxInfoMetric extends androidx.benchmark.macro.Metric {
-    ctor public FrameTimingGfxInfoMetric();
-  }
-
-  public final class FrameTimingMetric extends androidx.benchmark.macro.Metric {
-    ctor public FrameTimingMetric();
-  }
-
-  public final class MacrobenchmarkScope {
-    ctor public MacrobenchmarkScope(String packageName, boolean launchWithClearTask);
-    method public void dropKernelPageCache();
-    method public void dropShaderCache();
-    method public androidx.test.uiautomator.UiDevice getDevice();
-    method public Integer? getIteration();
-    method public String getPackageName();
-    method public void killProcess();
-    method @Deprecated public void killProcess(optional boolean useKillAll);
-    method public void pressHome();
-    method public void pressHome(optional long delayDurationMs);
-    method public void startActivityAndWait();
-    method public void startActivityAndWait(android.content.Intent intent);
-    method public void startActivityAndWait(optional kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit> block);
-    property public final androidx.test.uiautomator.UiDevice device;
-    property public final Integer? iteration;
-    property public final String packageName;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryCountersMetric extends androidx.benchmark.macro.TraceMetric {
-    ctor public MemoryCountersMetric();
-    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getMeasurements(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryUsageMetric extends androidx.benchmark.macro.TraceMetric {
-    ctor public MemoryUsageMetric(androidx.benchmark.macro.MemoryUsageMetric.Mode mode, optional java.util.List<? extends androidx.benchmark.macro.MemoryUsageMetric.SubMetric> subMetrics);
-    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getMeasurements(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
-  }
-
-  public enum MemoryUsageMetric.Mode {
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Last;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Max;
-  }
-
-  public enum MemoryUsageMetric.SubMetric {
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric Gpu;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric HeapSize;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssAnon;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssFile;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssShmem;
-  }
-
-  public abstract sealed class Metric {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.CaptureInfo {
-    ctor public Metric.CaptureInfo(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
-    method public int component1();
-    method public String component2();
-    method public String component3();
-    method public androidx.benchmark.macro.StartupMode? component4();
-    method public androidx.benchmark.macro.Metric.CaptureInfo copy(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
-    method public int getApiLevel();
-    method public androidx.benchmark.macro.StartupMode? getStartupMode();
-    method public String getTargetPackageName();
-    method public String getTestPackageName();
-    property public final int apiLevel;
-    property public final androidx.benchmark.macro.StartupMode? startupMode;
-    property public final String targetPackageName;
-    property public final String testPackageName;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.Measurement {
-    ctor public Metric.Measurement(String name, double data);
-    ctor public Metric.Measurement(String name, java.util.List<java.lang.Double> dataSamples);
-    method public String component1();
-    method public java.util.List<java.lang.Double> component2();
-    method public boolean component3();
-    method public androidx.benchmark.macro.Metric.Measurement copy(String name, java.util.List<java.lang.Double> data, boolean requireSingleValue);
-    method public java.util.List<java.lang.Double> getData();
-    method public String getName();
-    method public boolean getRequireSingleValue();
-    property public final java.util.List<java.lang.Double> data;
-    property public final String name;
-    property public final boolean requireSingleValue;
-  }
-
-  public final class MetricResultExtensionsKt {
-    method @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static void assertEqualMeasurements(java.util.List<androidx.benchmark.macro.Metric.Measurement> expected, java.util.List<androidx.benchmark.macro.Metric.Measurement> observed, double threshold);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategory {
-    enum_constant public static final androidx.benchmark.macro.PowerCategory CPU;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory DISPLAY;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory GPS;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory GPU;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory MACHINE_LEARNING;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory MEMORY;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory NETWORK;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory UNCATEGORIZED;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategoryDisplayLevel {
-    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel BREAKDOWN;
-    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel TOTAL;
-  }
-
-  @SuppressCompatibility @RequiresApi(29) @androidx.benchmark.macro.ExperimentalMetricApi public final class PowerMetric extends androidx.benchmark.macro.Metric {
-    ctor public PowerMetric(androidx.benchmark.macro.PowerMetric.Type type);
-    method public static androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
-    method public static androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public static androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public static boolean deviceBatteryHasMinimumCharge();
-    method public static boolean deviceSupportsHighPrecisionTracking();
-    field public static final androidx.benchmark.macro.PowerMetric.Companion Companion;
-  }
-
-  public static final class PowerMetric.Companion {
-    method public androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
-    method public androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public boolean deviceBatteryHasMinimumCharge();
-    method public boolean deviceSupportsHighPrecisionTracking();
-  }
-
-  public abstract static sealed class PowerMetric.Type {
-    method public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> getCategories();
-    method public final void setCategories(java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel>);
-    property public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> categories;
-  }
-
-  public static final class PowerMetric.Type.Battery extends androidx.benchmark.macro.PowerMetric.Type {
-    ctor public PowerMetric.Type.Battery();
-  }
-
-  public static final class PowerMetric.Type.Energy extends androidx.benchmark.macro.PowerMetric.Type {
-    ctor public PowerMetric.Type.Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> energyCategories);
-  }
-
-  public static final class PowerMetric.Type.Power extends androidx.benchmark.macro.PowerMetric.Type {
-    ctor public PowerMetric.Type.Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> powerCategories);
-  }
-
-  public enum StartupMode {
-    enum_constant public static final androidx.benchmark.macro.StartupMode COLD;
-    enum_constant public static final androidx.benchmark.macro.StartupMode HOT;
-    enum_constant public static final androidx.benchmark.macro.StartupMode WARM;
-  }
-
-  public final class StartupTimingMetric extends androidx.benchmark.macro.Metric {
-    ctor public StartupTimingMetric();
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public abstract class TraceMetric extends androidx.benchmark.macro.Metric {
-    ctor public TraceMetric();
-    method public abstract java.util.List<androidx.benchmark.macro.Metric.Measurement> getMeasurements(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
-    ctor public TraceSectionMetric(String sectionName);
-    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
-    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional String label);
-    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional String label, optional boolean targetPackageOnly);
-  }
-
-  public abstract static sealed class TraceSectionMetric.Mode {
-  }
-
-  public static final class TraceSectionMetric.Mode.Average extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Average INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Count extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Count INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.First extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.First INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Max extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Max INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Min extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Min INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Sum extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Sum INSTANCE;
-  }
-
-}
-
-package androidx.benchmark.perfetto {
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoTraceProcessorApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class PerfettoTraceProcessor {
-    ctor public PerfettoTraceProcessor();
-    method public <T> T loadTrace(androidx.benchmark.perfetto.PerfettoTrace trace, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor.Session,? extends T> block);
-    method public static <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-    method public static <T> T runServer(long timeout, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-    field public static final androidx.benchmark.perfetto.PerfettoTraceProcessor.Companion Companion;
-  }
-
-  public static final class PerfettoTraceProcessor.Companion {
-    method public <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-    method public static <T> T runServer(long timeout, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-  }
-
-  public static final class PerfettoTraceProcessor.Session {
-    method public kotlin.sequences.Sequence<androidx.benchmark.perfetto.Row> query(@org.intellij.lang.annotations.Language("sql") String query);
-    method public String queryMetricsJson(java.util.List<java.lang.String> metrics);
-    method public byte[] queryMetricsProtoBinary(java.util.List<java.lang.String> metrics);
-    method public String queryMetricsProtoText(java.util.List<java.lang.String> metrics);
-    method public byte[] rawQuery(@org.intellij.lang.annotations.Language("sql") String query);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class Row implements kotlin.jvm.internal.markers.KMappedMarker java.util.Map<java.lang.String,java.lang.Object?> {
-    ctor public Row(java.util.Map<java.lang.String,? extends java.lang.Object?> map);
-    method public byte[] bytes(String columnName);
-    method public double double(String columnName);
-    method public long long(String columnName);
-    method public byte[]? nullableBytes(String columnName);
-    method public Double? nullableDouble(String columnName);
-    method public Long? nullableLong(String columnName);
-    method public String? nullableString(String columnName);
-    method public String string(String columnName);
-  }
-
-  public final class RowKt {
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public static androidx.benchmark.perfetto.Row rowOf(kotlin.Pair<java.lang.String,? extends java.lang.Object?>... pairs);
-  }
-
-}
-
diff --git a/benchmark/benchmark-macro/api/current.txt b/benchmark/benchmark-macro/api/current.txt
index ad9fb40..e82e017 100644
--- a/benchmark/benchmark-macro/api/current.txt
+++ b/benchmark/benchmark-macro/api/current.txt
@@ -177,10 +177,12 @@
   }
 
   public static final class PowerMetric.Type.Energy extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Energy();
     ctor public PowerMetric.Type.Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> energyCategories);
   }
 
   public static final class PowerMetric.Type.Power extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Power();
     ctor public PowerMetric.Type.Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> powerCategories);
   }
 
diff --git a/benchmark/benchmark-macro/api/res-1.3.0-beta01.txt b/benchmark/benchmark-macro/api/res-1.3.0-beta01.txt
deleted file mode 100644
index e69de29..0000000
--- a/benchmark/benchmark-macro/api/res-1.3.0-beta01.txt
+++ /dev/null
diff --git a/benchmark/benchmark-macro/api/restricted_1.3.0-beta01.txt b/benchmark/benchmark-macro/api/restricted_1.3.0-beta01.txt
deleted file mode 100644
index 4c883fb..0000000
--- a/benchmark/benchmark-macro/api/restricted_1.3.0-beta01.txt
+++ /dev/null
@@ -1,303 +0,0 @@
-// Signature format: 4.0
-package androidx.benchmark.macro {
-
-  public enum BaselineProfileMode {
-    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Disable;
-    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Require;
-    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class BatteryCharge {
-    method public boolean hasMinimumCharge(optional boolean throwOnMissingMetrics);
-    field public static final androidx.benchmark.macro.BatteryCharge INSTANCE;
-  }
-
-  public abstract sealed class CompilationMode {
-    field public static final androidx.benchmark.macro.CompilationMode.Companion Companion;
-    field public static final androidx.benchmark.macro.CompilationMode DEFAULT;
-  }
-
-  public static final class CompilationMode.Companion {
-  }
-
-  public static final class CompilationMode.Full extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.Full();
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMacrobenchmarkApi public static final class CompilationMode.Ignore extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.Ignore();
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final class CompilationMode.Interpreted extends androidx.benchmark.macro.CompilationMode {
-    field public static final androidx.benchmark.macro.CompilationMode.Interpreted INSTANCE;
-  }
-
-  @RequiresApi(24) public static final class CompilationMode.None extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.None();
-  }
-
-  @RequiresApi(24) public static final class CompilationMode.Partial extends androidx.benchmark.macro.CompilationMode {
-    ctor public CompilationMode.Partial();
-    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode);
-    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode, optional @IntRange(from=0L) int warmupIterations);
-    method public androidx.benchmark.macro.BaselineProfileMode getBaselineProfileMode();
-    method public int getWarmupIterations();
-    property public final androidx.benchmark.macro.BaselineProfileMode baselineProfileMode;
-    property public final int warmupIterations;
-  }
-
-  public final class CompilationModeKt {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static boolean isSupportedWithVmSettings(androidx.benchmark.macro.CompilationMode);
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Macrobenchmark API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMacrobenchmarkApi {
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Metric API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMetricApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class FrameTimingGfxInfoMetric extends androidx.benchmark.macro.Metric {
-    ctor public FrameTimingGfxInfoMetric();
-  }
-
-  public final class FrameTimingMetric extends androidx.benchmark.macro.Metric {
-    ctor public FrameTimingMetric();
-  }
-
-  public final class MacrobenchmarkScope {
-    ctor public MacrobenchmarkScope(String packageName, boolean launchWithClearTask);
-    method public void dropKernelPageCache();
-    method public void dropShaderCache();
-    method public androidx.test.uiautomator.UiDevice getDevice();
-    method public Integer? getIteration();
-    method public String getPackageName();
-    method public void killProcess();
-    method @Deprecated public void killProcess(optional boolean useKillAll);
-    method public void pressHome();
-    method public void pressHome(optional long delayDurationMs);
-    method public void startActivityAndWait();
-    method public void startActivityAndWait(android.content.Intent intent);
-    method public void startActivityAndWait(optional kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit> block);
-    property public final androidx.test.uiautomator.UiDevice device;
-    property public final Integer? iteration;
-    property public final String packageName;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryCountersMetric extends androidx.benchmark.macro.TraceMetric {
-    ctor public MemoryCountersMetric();
-    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getMeasurements(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryUsageMetric extends androidx.benchmark.macro.TraceMetric {
-    ctor public MemoryUsageMetric(androidx.benchmark.macro.MemoryUsageMetric.Mode mode, optional java.util.List<? extends androidx.benchmark.macro.MemoryUsageMetric.SubMetric> subMetrics);
-    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getMeasurements(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
-  }
-
-  public enum MemoryUsageMetric.Mode {
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Last;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Max;
-  }
-
-  public enum MemoryUsageMetric.SubMetric {
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric Gpu;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric HeapSize;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssAnon;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssFile;
-    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssShmem;
-  }
-
-  public abstract sealed class Metric {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.CaptureInfo {
-    ctor public Metric.CaptureInfo(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
-    method public int component1();
-    method public String component2();
-    method public String component3();
-    method public androidx.benchmark.macro.StartupMode? component4();
-    method public androidx.benchmark.macro.Metric.CaptureInfo copy(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
-    method public int getApiLevel();
-    method public androidx.benchmark.macro.StartupMode? getStartupMode();
-    method public String getTargetPackageName();
-    method public String getTestPackageName();
-    property public final int apiLevel;
-    property public final androidx.benchmark.macro.StartupMode? startupMode;
-    property public final String targetPackageName;
-    property public final String testPackageName;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.Measurement {
-    ctor public Metric.Measurement(String name, double data);
-    ctor public Metric.Measurement(String name, java.util.List<java.lang.Double> dataSamples);
-    method public String component1();
-    method public java.util.List<java.lang.Double> component2();
-    method public boolean component3();
-    method public androidx.benchmark.macro.Metric.Measurement copy(String name, java.util.List<java.lang.Double> data, boolean requireSingleValue);
-    method public java.util.List<java.lang.Double> getData();
-    method public String getName();
-    method public boolean getRequireSingleValue();
-    property public final java.util.List<java.lang.Double> data;
-    property public final String name;
-    property public final boolean requireSingleValue;
-  }
-
-  public final class MetricResultExtensionsKt {
-    method @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static void assertEqualMeasurements(java.util.List<androidx.benchmark.macro.Metric.Measurement> expected, java.util.List<androidx.benchmark.macro.Metric.Measurement> observed, double threshold);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategory {
-    enum_constant public static final androidx.benchmark.macro.PowerCategory CPU;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory DISPLAY;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory GPS;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory GPU;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory MACHINE_LEARNING;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory MEMORY;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory NETWORK;
-    enum_constant public static final androidx.benchmark.macro.PowerCategory UNCATEGORIZED;
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategoryDisplayLevel {
-    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel BREAKDOWN;
-    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel TOTAL;
-  }
-
-  @SuppressCompatibility @RequiresApi(29) @androidx.benchmark.macro.ExperimentalMetricApi public final class PowerMetric extends androidx.benchmark.macro.Metric {
-    ctor public PowerMetric(androidx.benchmark.macro.PowerMetric.Type type);
-    method public static androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
-    method public static androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public static androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public static boolean deviceBatteryHasMinimumCharge();
-    method public static boolean deviceSupportsHighPrecisionTracking();
-    field public static final androidx.benchmark.macro.PowerMetric.Companion Companion;
-  }
-
-  public static final class PowerMetric.Companion {
-    method public androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
-    method public androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
-    method public boolean deviceBatteryHasMinimumCharge();
-    method public boolean deviceSupportsHighPrecisionTracking();
-  }
-
-  public abstract static sealed class PowerMetric.Type {
-    method public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> getCategories();
-    method public final void setCategories(java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel>);
-    property public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> categories;
-  }
-
-  public static final class PowerMetric.Type.Battery extends androidx.benchmark.macro.PowerMetric.Type {
-    ctor public PowerMetric.Type.Battery();
-  }
-
-  public static final class PowerMetric.Type.Energy extends androidx.benchmark.macro.PowerMetric.Type {
-    ctor public PowerMetric.Type.Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> energyCategories);
-  }
-
-  public static final class PowerMetric.Type.Power extends androidx.benchmark.macro.PowerMetric.Type {
-    ctor public PowerMetric.Type.Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> powerCategories);
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class PowerRail {
-    method public boolean hasMetrics(optional boolean throwOnMissingMetrics);
-    field public static final androidx.benchmark.macro.PowerRail INSTANCE;
-  }
-
-  public enum StartupMode {
-    enum_constant public static final androidx.benchmark.macro.StartupMode COLD;
-    enum_constant public static final androidx.benchmark.macro.StartupMode HOT;
-    enum_constant public static final androidx.benchmark.macro.StartupMode WARM;
-  }
-
-  @RequiresApi(29) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class StartupTimingLegacyMetric extends androidx.benchmark.macro.Metric {
-    ctor public StartupTimingLegacyMetric();
-  }
-
-  public final class StartupTimingMetric extends androidx.benchmark.macro.Metric {
-    ctor public StartupTimingMetric();
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public abstract class TraceMetric extends androidx.benchmark.macro.Metric {
-    ctor public TraceMetric();
-    method public abstract java.util.List<androidx.benchmark.macro.Metric.Measurement> getMeasurements(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
-    ctor public TraceSectionMetric(String sectionName);
-    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
-    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional String label);
-    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional String label, optional boolean targetPackageOnly);
-  }
-
-  public abstract static sealed class TraceSectionMetric.Mode {
-  }
-
-  public static final class TraceSectionMetric.Mode.Average extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Average INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Count extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Count INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.First extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.First INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Max extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Max INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Min extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Min INSTANCE;
-  }
-
-  public static final class TraceSectionMetric.Mode.Sum extends androidx.benchmark.macro.TraceSectionMetric.Mode {
-    field public static final androidx.benchmark.macro.TraceSectionMetric.Mode.Sum INSTANCE;
-  }
-
-}
-
-package androidx.benchmark.perfetto {
-
-  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoTraceProcessorApi {
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class PerfettoTraceProcessor {
-    ctor public PerfettoTraceProcessor();
-    method public <T> T loadTrace(androidx.benchmark.perfetto.PerfettoTrace trace, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor.Session,? extends T> block);
-    method public static <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-    method public static <T> T runServer(long timeout, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-    field public static final androidx.benchmark.perfetto.PerfettoTraceProcessor.Companion Companion;
-  }
-
-  public static final class PerfettoTraceProcessor.Companion {
-    method public <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-    method public static <T> T runServer(long timeout, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
-  }
-
-  public static final class PerfettoTraceProcessor.Session {
-    method public kotlin.sequences.Sequence<androidx.benchmark.perfetto.Row> query(@org.intellij.lang.annotations.Language("sql") String query);
-    method public String queryMetricsJson(java.util.List<java.lang.String> metrics);
-    method public byte[] queryMetricsProtoBinary(java.util.List<java.lang.String> metrics);
-    method public String queryMetricsProtoText(java.util.List<java.lang.String> metrics);
-    method public byte[] rawQuery(@org.intellij.lang.annotations.Language("sql") String query);
-  }
-
-  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class Row implements kotlin.jvm.internal.markers.KMappedMarker java.util.Map<java.lang.String,java.lang.Object?> {
-    ctor public Row(java.util.Map<java.lang.String,? extends java.lang.Object?> map);
-    method public byte[] bytes(String columnName);
-    method public double double(String columnName);
-    method public long long(String columnName);
-    method public byte[]? nullableBytes(String columnName);
-    method public Double? nullableDouble(String columnName);
-    method public Long? nullableLong(String columnName);
-    method public String? nullableString(String columnName);
-    method public String string(String columnName);
-  }
-
-  public final class RowKt {
-    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public static androidx.benchmark.perfetto.Row rowOf(kotlin.Pair<java.lang.String,? extends java.lang.Object?>... pairs);
-  }
-
-}
-
diff --git a/benchmark/benchmark-macro/api/restricted_current.txt b/benchmark/benchmark-macro/api/restricted_current.txt
index 4c883fb..6693555 100644
--- a/benchmark/benchmark-macro/api/restricted_current.txt
+++ b/benchmark/benchmark-macro/api/restricted_current.txt
@@ -190,10 +190,12 @@
   }
 
   public static final class PowerMetric.Type.Energy extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Energy();
     ctor public PowerMetric.Type.Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> energyCategories);
   }
 
   public static final class PowerMetric.Type.Power extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Power();
     ctor public PowerMetric.Type.Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> powerCategories);
   }
 
diff --git a/benchmark/benchmark-macro/build.gradle b/benchmark/benchmark-macro/build.gradle
index cd94221..113312a 100644
--- a/benchmark/benchmark-macro/build.gradle
+++ b/benchmark/benchmark-macro/build.gradle
@@ -64,7 +64,7 @@
     api(project(":benchmark:benchmark-common"))
     api(libs.junit)
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("androidx.core:core:1.9.0")
     implementation("androidx.profileinstaller:profileinstaller:1.3.1")
@@ -92,7 +92,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Benchmark - Macrobenchmark"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/benchmark/benchmark-macro/lint-baseline.xml b/benchmark/benchmark-macro/lint-baseline.xml
index ad8812d..030d1971 100644
--- a/benchmark/benchmark-macro/lint-baseline.xml
+++ b/benchmark/benchmark-macro/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                    Thread.sleep(50)"
-        errorLine2="                           ~~~~~">
+        errorLine1="                        Thread.sleep(50)"
+        errorLine2="                               ~~~~~">
         <location
             file="src/androidTest/java/androidx/benchmark/macro/perfetto/AndroidxTracingTraceTest.kt"/>
     </issue>
@@ -13,8 +13,8 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                trace(label) { Thread.sleep(50) }"
-        errorLine2="                                      ~~~~~">
+        errorLine1="                    trace(label) { Thread.sleep(50) }"
+        errorLine2="                                          ~~~~~">
         <location
             file="src/androidTest/java/androidx/benchmark/macro/perfetto/AndroidxTracingTraceTest.kt"/>
     </issue>
@@ -28,46 +28,4 @@
             file="src/androidTest/java/androidx/benchmark/macro/ConfigurableActivity.kt"/>
     </issue>
 
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi` or `@OptIn(markerClass = androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/macro/perfetto/MemoryCountersQuery.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.macro.ExperimentalMetricApi` or `@OptIn(markerClass = androidx.benchmark.macro.ExperimentalMetricApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/macro/perfetto/MemoryUsageQuery.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.macro.ExperimentalMetricApi` or `@OptIn(markerClass = androidx.benchmark.macro.ExperimentalMetricApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.macro.ExperimentalMetricApi` or `@OptIn(markerClass = androidx.benchmark.macro.ExperimentalMetricApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi` or `@OptIn(markerClass = androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/macro/perfetto/PowerQuery.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi` or `@OptIn(markerClass = androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi.class)`">
-        <location
-            file="src/main/java/androidx/benchmark/perfetto/Slice.kt"/>
-    </issue>
-
 </issues>
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
index 0388589..4fb285b 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
@@ -20,7 +20,6 @@
 import android.os.Build
 import android.security.NetworkSecurityPolicy
 import android.util.Log
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.benchmark.Shell
 import androidx.benchmark.ShellScript
@@ -314,7 +313,6 @@
 
 @RequiresApi(24)
 private object Api24Impl {
-    @DoNotInline
     fun isCleartextTrafficPermittedForLocalhost() =
         NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("localhost")
 }
diff --git a/benchmark/benchmark/build.gradle b/benchmark/benchmark/build.gradle
index 4391007..cb95e6d 100644
--- a/benchmark/benchmark/build.gradle
+++ b/benchmark/benchmark/build.gradle
@@ -50,6 +50,8 @@
             testProguardFiles "proguard-rules.pro"
         }
     }
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false 
 }
 
 androidx {
diff --git a/benchmark/gradle-plugin/lint-baseline.xml b/benchmark/gradle-plugin/lint-baseline.xml
index b42bedc..671e628 100644
--- a/benchmark/gradle-plugin/lint-baseline.xml
+++ b/benchmark/gradle-plugin/lint-baseline.xml
@@ -1,23 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
-
-    <issue
-        id="EagerGradleConfiguration"
-        message="Avoid using method findByName"
-        errorLine1="        if (project.rootProject.tasks.findByName(&quot;lockClocks&quot;) == null) {"
-        errorLine2="                                      ~~~~~~~~~~">
-        <location
-            file="src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt"/>
-    </issue>
-
-    <issue
-        id="EagerGradleConfiguration"
-        message="Avoid using method findByName"
-        errorLine1="        if (project.rootProject.tasks.findByName(&quot;unlockClocks&quot;) == null) {"
-        errorLine2="                                      ~~~~~~~~~~">
-        <location
-            file="src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="GradleProjectIsolation"
diff --git a/benchmark/integration-tests/macrobenchmark-target/build.gradle b/benchmark/integration-tests/macrobenchmark-target/build.gradle
index fbe028a..f038350 100644
--- a/benchmark/integration-tests/macrobenchmark-target/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark-target/build.gradle
@@ -28,6 +28,7 @@
             proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
         }
     }
+    compileSdkVersion 35
     namespace "androidx.benchmark.integration.macrobenchmark.target"
 }
 
diff --git a/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/SingleColorActivity.kt b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/SingleColorActivity.kt
index 6aea7c0..6cf2a31 100644
--- a/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/SingleColorActivity.kt
+++ b/benchmark/integration-tests/macrobenchmark-target/src/main/java/androidx/benchmark/integration/macrobenchmark/target/SingleColorActivity.kt
@@ -21,7 +21,6 @@
 import android.os.Bundle
 import android.view.Window
 import android.view.WindowInsets
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.appcompat.app.AppCompatActivity
 import androidx.constraintlayout.widget.ConstraintLayout
@@ -46,7 +45,6 @@
     @RequiresApi(30)
     private object Api30Fullscreen {
         @JvmStatic
-        @DoNotInline
         fun hideStatusBar(window: Window) {
             window.insetsController?.hide(WindowInsets.Type.statusBars())
         }
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensions.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensions.kt
index 63f316f..88064fc 100644
--- a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensions.kt
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/main/java/androidx/binarycompatibilityvalidator/KlibParsingCursorExtensions.kt
@@ -436,7 +436,7 @@
 private val closeSquareBracketRegex = Regex("^\\]")
 private val openSquareBracketRegex = Regex("^\\[")
 private val targetsRegex = Regex("^Targets:")
-private val defaultArgSymbolRegex = Regex("^=\\.\\.\\.")
+private val defaultArgSymbolRegex = Regex("^=(\\s)?\\.\\.\\.")
 private val varargSymbolRegex = Regex("^\\.\\.\\.")
 private val openParenRegex = Regex("\\(")
 private val reifiedRegex = Regex("reified")
diff --git a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KLibDumpParserTest.kt b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KLibDumpParserTest.kt
index 0178f86..2cf3454 100644
--- a/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KLibDumpParserTest.kt
+++ b/binarycompatibilityvalidator/binarycompatibilityvalidator/src/test/java/androidx/binarycompatibilityvalidator/KLibDumpParserTest.kt
@@ -170,6 +170,21 @@
     }
 
     @Test
+    fun parseAComplexFunctionWithK2Formatting() {
+        val input =
+            "final inline fun <#A: kotlin/Any, #B: kotlin/Any> androidx.collection/" +
+                "lruCache(kotlin/Int, crossinline kotlin/Function2<#A, #B, kotlin/Int> = ..., " +
+                "crossinline kotlin/Function1<#A, #B?> = ..., " +
+                "crossinline kotlin/Function4<kotlin/Boolean, #A, #B, #B?, kotlin/Unit> = ...): " +
+                "androidx.collection/LruCache<#A, #B>"
+        val parsed = KlibDumpParser(input).parseFunction()
+        assertThat(parsed.modality).isEqualTo(AbiModality.FINAL)
+        assertThat(parsed.typeParameters).hasSize(2)
+        assertThat(parsed.qualifiedName.toString()).isEqualTo("androidx.collection/lruCache")
+        assertThat(parsed.valueParameters).hasSize(4)
+    }
+
+    @Test
     fun parseANestedValProperty() {
         val input = "final val size\n        final fun <get-size>(): kotlin/Int"
         val parsed =
diff --git a/biometric/biometric-ktx/build.gradle b/biometric/biometric-ktx/build.gradle
index 0a615c2..77d810c 100644
--- a/biometric/biometric-ktx/build.gradle
+++ b/biometric/biometric-ktx/build.gradle
@@ -42,10 +42,10 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2020"
     description = "Kotlin extensions for the Biometric Library."
-    metalavaK2UastEnabled = true
     samples(project(":biometric:biometric-ktx-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.biometric.ktx"
 }
diff --git a/biometric/biometric-ktx/samples/build.gradle b/biometric/biometric-ktx/samples/build.gradle
index 0e63ec0..c750a94 100644
--- a/biometric/biometric-ktx/samples/build.gradle
+++ b/biometric/biometric-ktx/samples/build.gradle
@@ -42,5 +42,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.biometric.samples"
 }
diff --git a/biometric/biometric/api/current.txt b/biometric/biometric/api/current.txt
index 6a60ed1..eb18aba 100644
--- a/biometric/biometric/api/current.txt
+++ b/biometric/biometric/api/current.txt
@@ -43,6 +43,7 @@
     field public static final int ERROR_HW_UNAVAILABLE = 1; // 0x1
     field public static final int ERROR_LOCKOUT = 7; // 0x7
     field public static final int ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+    field public static final int ERROR_MORE_OPTIONS_BUTTON = 16; // 0x10
     field public static final int ERROR_NEGATIVE_BUTTON = 13; // 0xd
     field public static final int ERROR_NO_BIOMETRICS = 11; // 0xb
     field public static final int ERROR_NO_DEVICE_CREDENTIAL = 14; // 0xe
@@ -72,16 +73,22 @@
     ctor public BiometricPrompt.CryptoObject(java.security.Signature);
     ctor public BiometricPrompt.CryptoObject(javax.crypto.Cipher);
     ctor public BiometricPrompt.CryptoObject(javax.crypto.Mac);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public BiometricPrompt.CryptoObject(long);
     method public javax.crypto.Cipher? getCipher();
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public android.security.identity.IdentityCredential? getIdentityCredential();
     method public javax.crypto.Mac? getMac();
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public long getOperationHandle();
     method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public android.security.identity.PresentationSession? getPresentationSession();
     method public java.security.Signature? getSignature();
   }
 
   public static class BiometricPrompt.PromptInfo {
     method public int getAllowedAuthenticators();
+    method public androidx.biometric.PromptContentView? getContentView();
     method public CharSequence? getDescription();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap? getLogoBitmap();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String? getLogoDescription();
+    method @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
     method public CharSequence getNegativeButtonText();
     method public CharSequence? getSubtitle();
     method public CharSequence getTitle();
@@ -94,13 +101,54 @@
     method public androidx.biometric.BiometricPrompt.PromptInfo build();
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setAllowedAuthenticators(int);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setContentView(androidx.biometric.PromptContentView);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setDescription(CharSequence?);
     method @Deprecated public androidx.biometric.BiometricPrompt.PromptInfo.Builder setDeviceCredentialAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.BiometricPrompt.PromptInfo.Builder setLogoBitmap(android.graphics.Bitmap);
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.BiometricPrompt.PromptInfo.Builder setLogoDescription(String);
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.BiometricPrompt.PromptInfo.Builder setLogoRes(@DrawableRes int);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setNegativeButtonText(CharSequence);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setSubtitle(CharSequence?);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setTitle(CharSequence);
   }
 
+  public interface PromptContentItem {
+  }
+
+  public final class PromptContentItemBulletedText implements androidx.biometric.PromptContentItem {
+    ctor public PromptContentItemBulletedText(String);
+  }
+
+  public final class PromptContentItemPlainText implements androidx.biometric.PromptContentItem {
+    ctor public PromptContentItemPlainText(String);
+  }
+
+  public interface PromptContentView {
+  }
+
+  public final class PromptContentViewWithMoreOptionsButton implements androidx.biometric.PromptContentView {
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String? getDescription();
+  }
+
+  public static final class PromptContentViewWithMoreOptionsButton.Builder {
+    ctor public PromptContentViewWithMoreOptionsButton.Builder();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.PromptContentViewWithMoreOptionsButton build();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.PromptContentViewWithMoreOptionsButton.Builder setDescription(String);
+  }
+
+  public final class PromptVerticalListContentView implements androidx.biometric.PromptContentView {
+    method public String? getDescription();
+    method public java.util.List<androidx.biometric.PromptContentItem!> getListItems();
+  }
+
+  public static final class PromptVerticalListContentView.Builder {
+    ctor public PromptVerticalListContentView.Builder();
+    method public androidx.biometric.PromptVerticalListContentView.Builder addListItem(androidx.biometric.PromptContentItem);
+    method public androidx.biometric.PromptVerticalListContentView.Builder addListItem(androidx.biometric.PromptContentItem, int);
+    method public androidx.biometric.PromptVerticalListContentView build();
+    method public androidx.biometric.PromptVerticalListContentView.Builder setDescription(String);
+  }
+
 }
 
 package androidx.biometric.auth {
diff --git a/biometric/biometric/api/restricted_current.txt b/biometric/biometric/api/restricted_current.txt
index 6a60ed1..eb18aba 100644
--- a/biometric/biometric/api/restricted_current.txt
+++ b/biometric/biometric/api/restricted_current.txt
@@ -43,6 +43,7 @@
     field public static final int ERROR_HW_UNAVAILABLE = 1; // 0x1
     field public static final int ERROR_LOCKOUT = 7; // 0x7
     field public static final int ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+    field public static final int ERROR_MORE_OPTIONS_BUTTON = 16; // 0x10
     field public static final int ERROR_NEGATIVE_BUTTON = 13; // 0xd
     field public static final int ERROR_NO_BIOMETRICS = 11; // 0xb
     field public static final int ERROR_NO_DEVICE_CREDENTIAL = 14; // 0xe
@@ -72,16 +73,22 @@
     ctor public BiometricPrompt.CryptoObject(java.security.Signature);
     ctor public BiometricPrompt.CryptoObject(javax.crypto.Cipher);
     ctor public BiometricPrompt.CryptoObject(javax.crypto.Mac);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public BiometricPrompt.CryptoObject(long);
     method public javax.crypto.Cipher? getCipher();
     method @RequiresApi(android.os.Build.VERSION_CODES.R) public android.security.identity.IdentityCredential? getIdentityCredential();
     method public javax.crypto.Mac? getMac();
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public long getOperationHandle();
     method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public android.security.identity.PresentationSession? getPresentationSession();
     method public java.security.Signature? getSignature();
   }
 
   public static class BiometricPrompt.PromptInfo {
     method public int getAllowedAuthenticators();
+    method public androidx.biometric.PromptContentView? getContentView();
     method public CharSequence? getDescription();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap? getLogoBitmap();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String? getLogoDescription();
+    method @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
     method public CharSequence getNegativeButtonText();
     method public CharSequence? getSubtitle();
     method public CharSequence getTitle();
@@ -94,13 +101,54 @@
     method public androidx.biometric.BiometricPrompt.PromptInfo build();
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setAllowedAuthenticators(int);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setConfirmationRequired(boolean);
+    method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setContentView(androidx.biometric.PromptContentView);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setDescription(CharSequence?);
     method @Deprecated public androidx.biometric.BiometricPrompt.PromptInfo.Builder setDeviceCredentialAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.BiometricPrompt.PromptInfo.Builder setLogoBitmap(android.graphics.Bitmap);
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.BiometricPrompt.PromptInfo.Builder setLogoDescription(String);
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.BiometricPrompt.PromptInfo.Builder setLogoRes(@DrawableRes int);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setNegativeButtonText(CharSequence);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setSubtitle(CharSequence?);
     method public androidx.biometric.BiometricPrompt.PromptInfo.Builder setTitle(CharSequence);
   }
 
+  public interface PromptContentItem {
+  }
+
+  public final class PromptContentItemBulletedText implements androidx.biometric.PromptContentItem {
+    ctor public PromptContentItemBulletedText(String);
+  }
+
+  public final class PromptContentItemPlainText implements androidx.biometric.PromptContentItem {
+    ctor public PromptContentItemPlainText(String);
+  }
+
+  public interface PromptContentView {
+  }
+
+  public final class PromptContentViewWithMoreOptionsButton implements androidx.biometric.PromptContentView {
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String? getDescription();
+  }
+
+  public static final class PromptContentViewWithMoreOptionsButton.Builder {
+    ctor public PromptContentViewWithMoreOptionsButton.Builder();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.PromptContentViewWithMoreOptionsButton build();
+    method @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public androidx.biometric.PromptContentViewWithMoreOptionsButton.Builder setDescription(String);
+  }
+
+  public final class PromptVerticalListContentView implements androidx.biometric.PromptContentView {
+    method public String? getDescription();
+    method public java.util.List<androidx.biometric.PromptContentItem!> getListItems();
+  }
+
+  public static final class PromptVerticalListContentView.Builder {
+    ctor public PromptVerticalListContentView.Builder();
+    method public androidx.biometric.PromptVerticalListContentView.Builder addListItem(androidx.biometric.PromptContentItem);
+    method public androidx.biometric.PromptVerticalListContentView.Builder addListItem(androidx.biometric.PromptContentItem, int);
+    method public androidx.biometric.PromptVerticalListContentView build();
+    method public androidx.biometric.PromptVerticalListContentView.Builder setDescription(String);
+  }
+
 }
 
 package androidx.biometric.auth {
diff --git a/biometric/biometric/build.gradle b/biometric/biometric/build.gradle
index c7b5373..49370d4 100644
--- a/biometric/biometric/build.gradle
+++ b/biometric/biometric/build.gradle
@@ -28,9 +28,13 @@
     id("com.android.library")
 }
 
+android {
+    compileSdk = 35
+}
+
 dependencies {
     // Public API dependencies
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.3.2")
     api("androidx.fragment:fragment:1.2.5")
 
@@ -69,6 +73,7 @@
     }
     testOptions.unitTests.includeAndroidResources = true
     namespace "androidx.biometric"
+    compileSdk = 35
 }
 
 androidx {
@@ -76,5 +81,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "The Biometric library is a static library that you can add to your Android application. It invokes BiometricPrompt on devices running P and greater, and on older devices will show a compat dialog. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
index c29b0bb..36b6817 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricFragment.java
@@ -16,11 +16,13 @@
 
 package androidx.biometric;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -81,13 +83,20 @@
     static final int CANCELED_FROM_CLIENT = 3;
 
     /**
+     * Authentication was canceled by the user by pressing the more options button on the prompt
+     * content.
+     */
+    static final int CANCELED_FROM_MORE_OPTIONS_BUTTON = 4;
+
+    /**
      * Where authentication was canceled from.
      */
     @IntDef({
         CANCELED_FROM_INTERNAL,
         CANCELED_FROM_USER,
         CANCELED_FROM_NEGATIVE_BUTTON,
-        CANCELED_FROM_CLIENT
+        CANCELED_FROM_CLIENT,
+        CANCELED_FROM_MORE_OPTIONS_BUTTON
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface CanceledFrom {}
@@ -352,6 +361,15 @@
                     }
                 });
 
+        mViewModel.isMoreOptionsButtonPressPending().observe(this,
+                moreOptionsButtonPressPending -> {
+                    if (moreOptionsButtonPressPending) {
+                        onMoreOptionsButtonPressed();
+                        mViewModel.setMoreOptionsButtonPressPending(false);
+                    }
+                }
+        );
+
         mViewModel.isFingerprintDialogCancelPending().observe(this,
                 fingerprintDialogCancelPending -> {
                     if (fingerprintDialogCancelPending) {
@@ -514,6 +532,29 @@
                     builder, AuthenticatorUtils.isDeviceCredentialAllowed(authenticators));
         }
 
+        // Set the custom biometric prompt features introduced in Android 15 (API 35).
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            final int logoRes = mViewModel.getLogoRes();
+            final Bitmap logoBitmap = mViewModel.getLogoBitmap();
+            final String logoDescription = mViewModel.getLogoDescription();
+            final android.hardware.biometrics.PromptContentView contentView =
+                    PromptContentViewUtils.wrapForBiometricPrompt(mViewModel.getContentView(),
+                            mViewModel.getClientExecutor(),
+                            mViewModel.getMoreOptionsButtonListener());
+            if (logoRes != -1) {
+                Api35Impl.setLogoRes(builder, logoRes);
+            }
+            if (logoBitmap != null) {
+                Api35Impl.setLogoBitmap(builder, logoBitmap);
+            }
+            if (logoDescription != null && !logoDescription.isEmpty()) {
+                Api35Impl.setLogoDescription(builder, logoDescription);
+            }
+            if (contentView != null) {
+                Api35Impl.setContentView(builder, contentView);
+            }
+        }
+
         authenticateWithBiometricPrompt(Api28Impl.buildPrompt(builder), getContext());
     }
 
@@ -779,6 +820,16 @@
     }
 
     /**
+     * Callback that is run when the view model reports that the more options button has been
+     * pressed on the prompt content.
+     */
+    void onMoreOptionsButtonPressed() {
+        sendErrorAndDismiss(BiometricPrompt.ERROR_MORE_OPTIONS_BUTTON,
+                "More options button in the content view is clicked.");
+        cancelAuthentication(BiometricFragment.CANCELED_FROM_MORE_OPTIONS_BUTTON);
+    }
+
+    /**
      * Launches the confirm device credential Settings activity, where the user can authenticate
      * using their PIN, pattern, or password.
      */
@@ -1097,6 +1148,72 @@
     }
 
     /**
+     * Nested class to avoid verification errors for methods introduced in Android 15.0 (API 35).
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @SuppressLint("MissingPermission")
+    private static class Api35Impl {
+        // Prevent instantiation.
+        private Api35Impl() {
+        }
+
+        /**
+         * Sets the prompt content view for the given framework prompt builder.
+         *
+         * @param builder An instance of
+         *                {@link android.hardware.biometrics.BiometricPrompt.Builder}.
+         * @param logoRes A drawable resource of the logo that will be shown on the prompt.
+         */
+        @DoNotInline
+        static void setLogoRes(
+                @NonNull android.hardware.biometrics.BiometricPrompt.Builder builder, int logoRes) {
+            builder.setLogoRes(logoRes);
+        }
+
+        /**
+         * Sets the prompt content view for the given framework prompt builder.
+         *
+         * @param builder    An instance of
+         *                   {@link android.hardware.biometrics.BiometricPrompt.Builder}.
+         * @param logoBitmap A bitmap drawable of the logo that will be shown on the prompt.
+         */
+        @DoNotInline
+        static void setLogoBitmap(
+                @NonNull android.hardware.biometrics.BiometricPrompt.Builder builder,
+                @NonNull Bitmap logoBitmap) {
+            builder.setLogoBitmap(logoBitmap);
+        }
+
+        /**
+         * Sets the prompt content view for the given framework prompt builder.
+         *
+         * @param builder         An instance of
+         *                        {@link android.hardware.biometrics.BiometricPrompt.Builder}.
+         * @param logoDescription The content view for the prompt.
+         */
+        @DoNotInline
+        static void setLogoDescription(
+                @NonNull android.hardware.biometrics.BiometricPrompt.Builder builder,
+                String logoDescription) {
+            builder.setLogoDescription(logoDescription);
+        }
+
+        /**
+         * Sets the prompt content view for the given framework prompt builder.
+         *
+         * @param builder     An instance of
+         *                    {@link android.hardware.biometrics.BiometricPrompt.Builder}.
+         * @param contentView The content view for the prompt.
+         */
+        @DoNotInline
+        static void setContentView(
+                @NonNull android.hardware.biometrics.BiometricPrompt.Builder builder,
+                @NonNull android.hardware.biometrics.PromptContentView contentView) {
+            builder.setContentView(contentView);
+        }
+    }
+
+    /**
      * Nested class to avoid verification errors for methods introduced in Android 11 (API 30).
      */
     @RequiresApi(Build.VERSION_CODES.R)
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java
index ed361eb..01ff4b7 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricPrompt.java
@@ -16,15 +16,20 @@
 
 package androidx.biometric;
 
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
+
 import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RequiresPermission;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.biometric.BiometricManager.Authenticators;
@@ -160,6 +165,11 @@
     public static final int ERROR_SECURITY_UPDATE_REQUIRED = 15;
 
     /**
+     * The user pressed the more options button on prompt content.
+     */
+    public static final int ERROR_MORE_OPTIONS_BUTTON = 16;
+
+    /**
      * An error code that may be returned during authentication.
      */
     @IntDef({
@@ -175,7 +185,8 @@
         ERROR_NO_BIOMETRICS,
         ERROR_HW_NOT_PRESENT,
         ERROR_NEGATIVE_BUTTON,
-        ERROR_NO_DEVICE_CREDENTIAL
+        ERROR_NO_DEVICE_CREDENTIAL,
+        ERROR_MORE_OPTIONS_BUTTON
     })
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     @Retention(RetentionPolicy.SOURCE)
@@ -230,6 +241,7 @@
         @Nullable private final Mac mMac;
         @Nullable private final android.security.identity.IdentityCredential mIdentityCredential;
         @Nullable private final android.security.identity.PresentationSession mPresentationSession;
+        private final long mOperationHandle;
 
         /**
          * Creates a crypto object that wraps the given signature object.
@@ -242,6 +254,7 @@
             mMac = null;
             mIdentityCredential = null;
             mPresentationSession = null;
+            mOperationHandle = 0;
         }
 
         /**
@@ -255,6 +268,7 @@
             mMac = null;
             mIdentityCredential = null;
             mPresentationSession = null;
+            mOperationHandle = 0;
         }
 
         /**
@@ -268,6 +282,7 @@
             mMac = mac;
             mIdentityCredential = null;
             mPresentationSession = null;
+            mOperationHandle = 0;
         }
 
         /**
@@ -284,6 +299,7 @@
             mMac = null;
             mIdentityCredential = identityCredential;
             mPresentationSession = null;
+            mOperationHandle = 0;
         }
 
         /**
@@ -300,9 +316,27 @@
             mMac = null;
             mIdentityCredential = null;
             mPresentationSession = presentationSession;
+            mOperationHandle = 0;
         }
 
         /**
+         * Create from an operation handle.
+         * @see CryptoObject#getOperationHandle()
+         *
+         * @param operationHandle the operation handle associated with this object.
+         */
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        public CryptoObject(long operationHandle) {
+            mSignature = null;
+            mCipher = null;
+            mMac = null;
+            mIdentityCredential = null;
+            mPresentationSession = null;
+            mOperationHandle = operationHandle;
+        }
+
+
+        /**
          * Gets the signature object associated with this crypto object.
          *
          * @return The signature, or {@code null} if none is associated with this object.
@@ -353,6 +387,34 @@
         public android.security.identity.PresentationSession getPresentationSession() {
             return mPresentationSession;
         }
+
+        /**
+         * Returns the {@code operationHandle} associated with this object or 0 if none.
+         * The {@code operationHandle} is the underlying identifier associated with
+         * the {@code CryptoObject}.
+         *
+         * <p> The {@code operationHandle} can be used to reconstruct a {@code CryptoObject}
+         * instance. This is useful for any cross-process communication as the {@code CryptoObject}
+         * class is not {@link android.os.Parcelable}. Hence, if the {@code CryptoObject} is
+         * constructed in one process, and needs to be propagated to another process,
+         * before calling the {@code authenticate()} API in the second process, the
+         * recommendation is to retrieve the {@code operationHandle} using this API, and then
+         * reconstruct the {@code CryptoObject}using the constructor that takes in an {@code
+         * operationHandle}, and pass that in to the {@code authenticate} API mentioned above.
+         */
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        public long getOperationHandle() {
+            return CryptoObjectUtils.getOperationHandle(this);
+        }
+
+        /**
+         * Returns the {@code operationHandle} from the constructor. This is only for wrapping
+         * this {@link androidx.biometric.BiometricPrompt.CryptoObject} to
+         * {@link android.hardware.biometrics.BiometricPrompt}.
+         */
+        long getOperationHandleCryptoObject() {
+            return mOperationHandle;
+        }
     }
 
     /**
@@ -438,15 +500,75 @@
          */
         public static class Builder {
             // Mutable options to be set on the builder.
+            @DrawableRes private int mLogoRes = -1;
+            @Nullable private Bitmap mLogoBitmap = null;
+            @Nullable private String mLogoDescription = null;
             @Nullable private CharSequence mTitle = null;
             @Nullable private CharSequence mSubtitle = null;
             @Nullable private CharSequence mDescription = null;
+            @Nullable private PromptContentView mPromptContentView = null;
             @Nullable private CharSequence mNegativeButtonText = null;
             private boolean mIsConfirmationRequired = true;
             private boolean mIsDeviceCredentialAllowed = false;
             @BiometricManager.AuthenticatorTypes private int mAllowedAuthenticators = 0;
 
             /**
+             * Optional: Sets the drawable resource of the logo that will be shown on the prompt.
+             *
+             * <p> Note that using this method is not recommended in most scenarios because the
+             * calling application's icon will be used by default. Setting the logo is intended
+             * for large bundled applications that perform a wide range of functions and need to
+             * show distinct icons for each function.
+             *
+             * @param logoRes A drawable resource of the logo that will be shown on the prompt.
+             * @return This builder.
+             */
+            @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+            @NonNull
+            public Builder setLogoRes(@DrawableRes int logoRes) {
+                mLogoRes = logoRes;
+                return this;
+            }
+
+            /**
+             * Optional: Sets the bitmap drawable of the logo that will be shown on the prompt.
+             *
+             * <p> Note that using this method is not recommended in most scenarios because the
+             * calling application's icon will be used by default. Setting the logo is intended
+             * for large bundled applications that perform a wide range of functions and need to
+             * show distinct icons for each function.
+             *
+             * @param logoBitmap A bitmap drawable of the logo that will be shown on the prompt.
+             * @return This builder.
+             */
+            @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+            @NonNull
+            public Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
+                mLogoBitmap = logoBitmap;
+                return this;
+            }
+
+            /**
+             * Optional: Sets logo description text that will be shown on the prompt.
+             *
+             * <p> Note that using this method is not recommended in most scenarios because the
+             * calling application's name will be used by default. Setting the logo description
+             * is intended for large bundled applications that perform a wide range of functions
+             * and need to show distinct description for each function.
+             *
+             * @param logoDescription The logo description text that will be shown on the prompt.
+             * @return This builder.
+             * @throws IllegalArgumentException If logo description is null or exceeds certain
+             *                                  character limit.
+             */
+            @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+            @NonNull
+            public Builder setLogoDescription(@NonNull String logoDescription) {
+                mLogoDescription = logoDescription;
+                return this;
+            }
+
+            /**
              * Required: Sets the title for the prompt.
              *
              * @param title The title to be displayed on the prompt.
@@ -471,9 +593,14 @@
             }
 
             /**
-             * Optional: Sets the description for the prompt.
+             * Optional: Sets a description that will be shown on the prompt.
              *
-             * @param description The description to be displayed on the prompt.
+             * <p> Note that the description set by {@link Builder#setDescription(CharSequence)}
+             * will be overridden by {@link Builder#setContentView(PromptContentView)}. The view
+             * provided to {@link Builder#setContentView(PromptContentView)} will be used if both
+             * methods are called.
+             *
+             * @param description The description to display.
              * @return This builder.
              */
             @NonNull
@@ -483,6 +610,23 @@
             }
 
             /**
+             * Optional: Sets application customized content view that will be shown on the prompt.
+             *
+             * <p> Note that the description set by {@link Builder#setDescription(CharSequence)}
+             * will be overridden by {@link Builder#setContentView(PromptContentView)}. The view
+             * provided to {@link Builder#setContentView(PromptContentView)} will be used if both
+             * methods are called.
+             *
+             * @param view The customized view information.
+             * @return This builder.
+             */
+            @NonNull
+            public Builder setContentView(@NonNull PromptContentView view) {
+                mPromptContentView = view;
+                return this;
+            }
+
+            /**
              * Required: Sets the text for the negative button on the prompt.
              *
              * <p>Note that this option is incompatible with device credential authentication and
@@ -623,9 +767,13 @@
                 }
 
                 return new PromptInfo(
+                        mLogoRes,
+                        mLogoBitmap,
+                        mLogoDescription,
                         mTitle,
                         mSubtitle,
                         mDescription,
+                        mPromptContentView,
                         mNegativeButtonText,
                         mIsConfirmationRequired,
                         mIsDeviceCredentialAllowed,
@@ -634,9 +782,13 @@
         }
 
         // Immutable fields for the prompt info object.
+        @DrawableRes private int mLogoRes;
+        @Nullable private Bitmap mLogoBitmap;
+        @Nullable private String mLogoDescription;
         @NonNull private final CharSequence mTitle;
         @Nullable private final CharSequence mSubtitle;
         @Nullable private final CharSequence mDescription;
+        @Nullable private PromptContentView mPromptContentView;
         @Nullable private final CharSequence mNegativeButtonText;
         private final boolean mIsConfirmationRequired;
         private final boolean mIsDeviceCredentialAllowed;
@@ -645,16 +797,24 @@
         // Prevent direct instantiation.
         @SuppressWarnings("WeakerAccess") /* synthetic access */
         PromptInfo(
+                int logoRes,
+                @Nullable Bitmap logoBitmap,
+                @Nullable String logoDescription,
                 @NonNull CharSequence title,
                 @Nullable CharSequence subtitle,
                 @Nullable CharSequence description,
+                @Nullable PromptContentView promptContentView,
                 @Nullable CharSequence negativeButtonText,
                 boolean confirmationRequired,
                 boolean deviceCredentialAllowed,
                 @BiometricManager.AuthenticatorTypes int allowedAuthenticators) {
+            mLogoRes = logoRes;
+            mLogoBitmap = logoBitmap;
+            mLogoDescription = logoDescription;
             mTitle = title;
             mSubtitle = subtitle;
             mDescription = description;
+            mPromptContentView = promptContentView;
             mNegativeButtonText = negativeButtonText;
             mIsConfirmationRequired = confirmationRequired;
             mIsDeviceCredentialAllowed = deviceCredentialAllowed;
@@ -662,6 +822,44 @@
         }
 
         /**
+         * Gets the drawable resource of the logo for the prompt, as set by
+         * {@link Builder#setLogoRes(int)}. Currently for system applications use only.
+         *
+         * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set.
+         */
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        @DrawableRes
+        public int getLogoRes() {
+            return mLogoRes;
+        }
+
+        /**
+         * Gets the logo bitmap for the prompt, as set by {@link Builder#setLogoBitmap(Bitmap)}.
+         * Currently for system applications use only.
+         *
+         * @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set.
+         */
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        @Nullable
+        public Bitmap getLogoBitmap() {
+            return mLogoBitmap;
+        }
+
+        /**
+         * Gets the logo description for the prompt, as set by
+         * {@link Builder#setLogoDescription(String)}.
+         * Currently for system applications use only.
+         *
+         * @return The logo description of the prompt, or null if the prompt has no logo description
+         * set.
+         */
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        @Nullable
+        public String getLogoDescription() {
+            return mLogoDescription;
+        }
+
+        /**
          * Gets the title for the prompt.
          *
          * @return The title to be displayed on the prompt.
@@ -698,6 +896,17 @@
         }
 
         /**
+         * Gets the content view for the prompt, as set by
+         * {@link Builder#setContentView(PromptContentView)}.
+         *
+         * @return The content view for the prompt, or null if the prompt has no content view.
+         */
+        @Nullable
+        public PromptContentView getContentView() {
+            return mPromptContentView;
+        }
+
+        /**
          * Gets the text for the negative button on the prompt.
          *
          * @return The label to be used for the negative button on the prompt, or an empty string if
diff --git a/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java b/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
index 56f8782..eb6970f 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/BiometricViewModel.java
@@ -16,7 +16,9 @@
 
 package androidx.biometric;
 
+import android.annotation.SuppressLint;
 import android.content.DialogInterface;
+import android.graphics.Bitmap;
 import android.os.Handler;
 import android.os.Looper;
 
@@ -139,6 +141,29 @@
     }
 
     /**
+     * The dialog listener that is returned by {@link #getMoreOptionsButtonListener()} ()}.
+     */
+    private static class MoreOptionsButtonListener implements DialogInterface.OnClickListener {
+        @NonNull private final WeakReference<BiometricViewModel> mViewModelRef;
+
+        /**
+         * Creates a more options button listener with a weak reference to the given view model.
+         *
+         * @param viewModel The view model instance to hold a weak reference to.
+         */
+        MoreOptionsButtonListener(@Nullable BiometricViewModel viewModel) {
+            mViewModelRef = new WeakReference<>(viewModel);
+        }
+
+        @Override
+        public void onClick(DialogInterface dialogInterface, int which) {
+            if (mViewModelRef.get() != null) {
+                mViewModelRef.get().setMoreOptionsButtonPressPending(true);
+            }
+        }
+    }
+
+    /**
      * The executor that will run authentication callback methods.
      *
      * <p>If unset, callbacks are invoked on the main thread with {@link Looper#getMainLooper()}.
@@ -181,6 +206,11 @@
     @Nullable private DialogInterface.OnClickListener mNegativeButtonListener;
 
     /**
+     * A dialog listener for the more options button shown on the prompt content.
+     */
+    @Nullable private DialogInterface.OnClickListener mMoreOptionsButtonListener;
+
+    /**
      * A label for the negative button shown on the prompt.
      *
      * <p>If set, this value overrides the one returned by
@@ -251,6 +281,11 @@
     @Nullable private MutableLiveData<Boolean> mIsNegativeButtonPressPending;
 
     /**
+     * Whether the user has pressed the more options button on the prompt content.
+     */
+    @Nullable private MutableLiveData<Boolean> mIsMoreOptionsButtonPressPending;
+
+    /**
      * Whether the fingerprint dialog should always be dismissed instantly.
      */
     private boolean mIsFingerprintDialogDismissedInstantly = true;
@@ -327,6 +362,47 @@
     }
 
     /**
+     * Gets the logo res to be shown on the biometric prompt.
+     *
+     * <p>This method relies on the {@link BiometricPrompt.PromptInfo} set by
+     * {@link #setPromptInfo(BiometricPrompt.PromptInfo)}.
+     *
+     * @return The logo res for the prompt, or -1 if not set.
+     */
+    @SuppressLint("MissingPermission")
+    int getLogoRes() {
+        return mPromptInfo != null ? mPromptInfo.getLogoRes() : -1;
+    }
+
+    /**
+     * Gets the logo bitmap to be shown on the biometric prompt.
+     *
+     * <p>This method relies on the {@link BiometricPrompt.PromptInfo} set by
+     * {@link #setPromptInfo(BiometricPrompt.PromptInfo)}.
+     *
+     * @return The logo bitmap for the prompt, or null if not set.
+     */
+    @SuppressLint("MissingPermission")
+    @Nullable
+    Bitmap getLogoBitmap() {
+        return mPromptInfo != null ? mPromptInfo.getLogoBitmap() : null;
+    }
+
+    /**
+     * Gets the logo description to be shown on the biometric prompt.
+     *
+     * <p>This method relies on the {@link BiometricPrompt.PromptInfo} set by
+     * {@link #setPromptInfo(BiometricPrompt.PromptInfo)}.
+     *
+     * @return The logo description for the prompt, or null if not set.
+     */
+    @SuppressLint("MissingPermission")
+    @Nullable
+    String getLogoDescription() {
+        return mPromptInfo != null ? mPromptInfo.getLogoDescription() : null;
+    }
+
+    /**
      * Gets the title to be shown on the biometric prompt.
      *
      * <p>This method relies on the {@link BiometricPrompt.PromptInfo} set by
@@ -366,6 +442,19 @@
     }
 
     /**
+     * Gets the prompt content view to be shown on the biometric prompt.
+     *
+     * <p>This method relies on the {@link BiometricPrompt.PromptInfo} set by
+     * {@link #setPromptInfo(BiometricPrompt.PromptInfo)}.
+     *
+     * @return The prompt content view for the prompt, or {@code null} if not set.
+     */
+    @Nullable
+    PromptContentView getContentView() {
+        return mPromptInfo != null ? mPromptInfo.getContentView() : null;
+    }
+
+    /**
      * Gets the text that should be shown for the negative button on the biometric prompt.
      *
      * <p>If non-null, the value set by {@link #setNegativeButtonTextOverride(CharSequence)} is
@@ -454,6 +543,14 @@
         return mNegativeButtonListener;
     }
 
+    @NonNull
+    DialogInterface.OnClickListener getMoreOptionsButtonListener() {
+        if (mMoreOptionsButtonListener == null) {
+            mMoreOptionsButtonListener = new MoreOptionsButtonListener(this);
+        }
+        return mMoreOptionsButtonListener;
+    }
+
     void setNegativeButtonTextOverride(@Nullable CharSequence negativeButtonTextOverride) {
         mNegativeButtonTextOverride = negativeButtonTextOverride;
     }
@@ -593,6 +690,22 @@
         updateValue(mIsNegativeButtonPressPending, negativeButtonPressPending);
     }
 
+    @NonNull
+    LiveData<Boolean> isMoreOptionsButtonPressPending() {
+        if (mIsMoreOptionsButtonPressPending == null) {
+            mIsMoreOptionsButtonPressPending = new MutableLiveData<>();
+        }
+        return mIsMoreOptionsButtonPressPending;
+    }
+
+    void setMoreOptionsButtonPressPending(boolean moreOptionsButtonPressPending) {
+        if (mIsMoreOptionsButtonPressPending == null) {
+            mIsMoreOptionsButtonPressPending = new MutableLiveData<>();
+        }
+        updateValue(mIsMoreOptionsButtonPressPending, moreOptionsButtonPressPending);
+    }
+
+
     boolean isFingerprintDialogDismissedInstantly() {
         return mIsFingerprintDialogDismissedInstantly;
     }
diff --git a/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java b/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java
index 4cc0c30..871135d 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/CryptoObjectUtils.java
@@ -112,6 +112,17 @@
             }
         }
 
+        // Operation handle is only supported on API 35 and above.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // This should be the bottom one and only be reachable when cryptoObject was
+            // constructed with operation handle. cryptoObject from other constructors should
+            // already be unwrapped and returned above.
+            final long operationHandle = Api35Impl.getOperationHandle(cryptoObject);
+            if (operationHandle != 0) {
+                return new BiometricPrompt.CryptoObject(operationHandle);
+            }
+        }
+
         return null;
     }
 
@@ -164,10 +175,39 @@
             }
         }
 
+        // Operation handle is only supported on API 35 and above.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            final long operationHandle = cryptoObject.getOperationHandleCryptoObject();
+            if (operationHandle != 0) {
+                return Api35Impl.create(operationHandle);
+            }
+        }
+
         return null;
     }
 
     /**
+     * Get the {@code operationHandle} associated with this object or 0 if none. This needs to be
+     * achieved by getting the corresponding
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} and then get its
+     * operation handle.
+     *
+     * @param cryptoObject An instance of {@link androidx.biometric.BiometricPrompt.CryptoObject}.
+     * @return The {@code operationHandle} associated with this object or 0 if none.
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    static long getOperationHandle(@Nullable BiometricPrompt.CryptoObject cryptoObject) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            final android.hardware.biometrics.BiometricPrompt.CryptoObject wrappedCryptoObject =
+                    CryptoObjectUtils.wrapForBiometricPrompt(cryptoObject);
+            if (wrappedCryptoObject != null) {
+                return Api35Impl.getOperationHandle(wrappedCryptoObject);
+            }
+        }
+        return 0;
+    }
+
+    /**
      * Unwraps a crypto object returned by
      * {@link androidx.core.hardware.fingerprint.FingerprintManagerCompat}.
      *
@@ -250,6 +290,11 @@
             return null;
         }
 
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            Log.e(TAG, "Operation handle is not supported by FingerprintManager.");
+            return null;
+        }
+
         return null;
     }
 
@@ -298,6 +343,41 @@
     }
 
     /**
+     * Nested class to avoid verification errors for methods introduced in Android 15.0 (API 35).
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static class Api35Impl {
+        // Prevent instantiation.
+        private Api35Impl() {}
+
+        /**
+         * Creates an instance of the framework class
+         * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} from the given
+         * operation handle.
+         *
+         * @param operationHandle The operation handle to be wrapped.
+         * @return An instance of {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}.
+         */
+        @NonNull
+        static android.hardware.biometrics.BiometricPrompt.CryptoObject create(
+                long operationHandle) {
+            return new android.hardware.biometrics.BiometricPrompt.CryptoObject(operationHandle);
+        }
+
+        /**
+         * Gets the operation handle associated with the given crypto object, if any.
+         *
+         * @param crypto An instance of
+         *               {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}.
+         * @return The wrapped operation handle object, or {@code null}.
+         */
+        static long getOperationHandle(
+                @NonNull android.hardware.biometrics.BiometricPrompt.CryptoObject crypto) {
+            return crypto.getOperationHandle();
+        }
+    }
+
+    /**
      * Nested class to avoid verification errors for methods introduced in Android 13.0 (API 33).
      */
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptContentItem.java b/biometric/biometric/src/main/java/androidx/biometric/PromptContentItem.java
new file mode 100644
index 0000000..099094a
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptContentItem.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometric;
+
+/**
+ * An item shown on {@link PromptContentView}.
+ */
+public interface PromptContentItem {
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptContentItemBulletedText.java b/biometric/biometric/src/main/java/androidx/biometric/PromptContentItemBulletedText.java
new file mode 100644
index 0000000..6ec55e2
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptContentItemBulletedText.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometric;
+
+import androidx.annotation.NonNull;
+
+/**
+ * A list item with bulleted text shown on {@link PromptVerticalListContentView}.
+ */
+public final class PromptContentItemBulletedText implements PromptContentItem {
+    private final String mText;
+
+    /**
+     * A list item with bulleted text shown on {@link PromptVerticalListContentView}.
+     *
+     * @param text The text of this list item.
+     */
+    public PromptContentItemBulletedText(@NonNull String text) {
+        mText = text;
+    }
+
+    @NonNull
+    String getText() {
+        return mText;
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptContentItemPlainText.java b/biometric/biometric/src/main/java/androidx/biometric/PromptContentItemPlainText.java
new file mode 100644
index 0000000..c7f2b6f
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptContentItemPlainText.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometric;
+
+import androidx.annotation.NonNull;
+
+/**
+ * A list item with plain text shown on {@link PromptVerticalListContentView}.
+ */
+public final class PromptContentItemPlainText implements PromptContentItem {
+    private final String mText;
+
+    /**
+     * A list item with plain text shown on {@link PromptVerticalListContentView}.
+     *
+     * @param text The text of this list item.
+     */
+    public PromptContentItemPlainText(@NonNull String text) {
+        mText = text;
+    }
+
+    @NonNull
+    String getText() {
+        return mText;
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptContentView.java b/biometric/biometric/src/main/java/androidx/biometric/PromptContentView.java
new file mode 100644
index 0000000..090960b
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptContentView.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometric;
+
+/**
+ * Contains the information of the template of content view for Biometric Prompt.
+ */
+public interface PromptContentView {
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewUtils.java b/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewUtils.java
new file mode 100644
index 0000000..cb6679a8
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewUtils.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometric;
+
+import android.annotation.SuppressLint;
+import android.content.DialogInterface;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Utility class for creating and converting between different types of prompt content view that may
+ * be used internally by {@link BiometricPrompt}
+ */
+class PromptContentViewUtils {
+    // Prevent instantiation.
+    private PromptContentViewUtils() {
+    }
+
+    /**
+     * Wraps a prompt content view to be passed to {@link BiometricPrompt}.
+     *
+     * @param contentView               An instance of {@link PromptContentView}.
+     * @param executor                  An executor for the more options button callback.
+     * @param moreOptionsButtonListener A listener for the more options button press event.
+     * @return An equivalent prompt content view that is compatible with
+     * {@link android.hardware.biometrics.PromptContentView}.
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @Nullable
+    static android.hardware.biometrics.PromptContentView wrapForBiometricPrompt(
+            @Nullable PromptContentView contentView, @NonNull Executor executor,
+            @NonNull DialogInterface.OnClickListener moreOptionsButtonListener) {
+
+        if (contentView == null) {
+            return null;
+        }
+
+        // Prompt content view is only supported on API 35 and above.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            if (contentView instanceof PromptVerticalListContentView) {
+                return Api35Impl.createPromptVerticalListContentView(
+                        (PromptVerticalListContentView) contentView);
+            } else if (contentView instanceof PromptContentViewWithMoreOptionsButton) {
+                return Api35Impl.createPromptContentViewWithMoreOptionsButton(
+                        (PromptContentViewWithMoreOptionsButton) contentView, executor,
+                        moreOptionsButtonListener);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Nested class to avoid verification errors for methods introduced in Android 15.0 (API 35).
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @SuppressLint("MissingPermission")
+    private static class Api35Impl {
+        // Prevent instantiation.
+        private Api35Impl() {
+        }
+
+        /**
+         * Creates an instance of the framework class
+         * {@link android.hardware.biometrics.PromptVerticalListContentView} from the given
+         * content view.
+         *
+         * @param contentView The prompt content view to be wrapped.
+         * @return An instance of {@link android.hardware.biometrics.PromptVerticalListContentView}.
+         */
+        @NonNull
+        static android.hardware.biometrics.PromptContentView createPromptVerticalListContentView(
+                @NonNull PromptVerticalListContentView contentView) {
+            android.hardware.biometrics.PromptVerticalListContentView.Builder
+                    contentViewBuilder =
+                    new android.hardware.biometrics.PromptVerticalListContentView.Builder();
+            if (contentView.getDescription() != null) {
+                contentViewBuilder.setDescription(contentView.getDescription());
+            }
+            contentView.getListItems().forEach(
+                    it -> {
+                        if (it instanceof PromptContentItemPlainText) {
+                            contentViewBuilder.addListItem(
+                                    new android.hardware.biometrics.PromptContentItemPlainText(
+                                            ((PromptContentItemPlainText) it).getText()));
+                        } else if (it instanceof PromptContentItemBulletedText) {
+                            contentViewBuilder.addListItem(
+                                    new android.hardware.biometrics.PromptContentItemBulletedText(
+                                            ((PromptContentItemBulletedText) it).getText()));
+                        }
+                    });
+            return contentViewBuilder.build();
+        }
+
+        /**
+         * Creates an instance of the framework class
+         * {@link android.hardware.biometrics.PromptContentViewWithMoreOptionsButton} from the
+         * given content view.
+         *
+         * @param contentView               The prompt content view to be wrapped.
+         * @param executor                  An executor for the more options button callback.
+         * @param moreOptionsButtonListener A listener for the more options button press event.
+         * @return An instance of
+         * {@link android.hardware.biometrics.PromptContentViewWithMoreOptionsButton}.
+         */
+        @NonNull
+        static android.hardware.biometrics.PromptContentView
+                createPromptContentViewWithMoreOptionsButton(
+                        @NonNull PromptContentViewWithMoreOptionsButton contentView,
+                        @NonNull Executor executor,
+                        @NonNull DialogInterface.OnClickListener moreOptionsButtonListener) {
+            android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.Builder
+                    contentViewBuilder =
+                    new android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
+                            .Builder();
+            if (contentView.getDescription() != null) {
+                contentViewBuilder.setDescription(contentView.getDescription());
+            }
+            contentViewBuilder.setMoreOptionsButtonListener(executor, moreOptionsButtonListener);
+            return contentViewBuilder.build();
+        }
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewWithMoreOptionsButton.java b/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewWithMoreOptionsButton.java
new file mode 100644
index 0000000..eb4f9aa
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptContentViewWithMoreOptionsButton.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometric;
+
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
+/**
+ * Contains the information of the template of content view with a more options button for
+ * Biometric Prompt.
+ * <p>
+ * This button should be used to provide more options for sign in or other purposes, such as when a
+ * user needs to select between multiple app-specific accounts or profiles that are available for
+ * sign in.
+ * <p>
+ * Apps should avoid using this when possible because it will create additional steps that the user
+ * must navigate through - clicking the more options button will dismiss the prompt, provide the app
+ * an opportunity to ask the user for the correct option, and finally allow the app to decide how to
+ * proceed once selected.
+ *
+ * <p>
+ * Here's how you'd set a <code>PromptContentViewWithMoreOptionsButton</code> on a Biometric
+ * Prompt:
+ * <pre class="prettyprint">
+ * BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
+ *     .setTitle(...)
+ *     .setSubTitle(...)
+ *     .setContentView(
+ *         new PromptContentViewWithMoreOptionsButton.Builder()
+ *             .setDescription("test description")
+ *             .setMoreOptionsButtonListener(executor, listener)
+ *             .build()
+ *      )
+ *     .build();
+ * </pre>
+ */
+public final class PromptContentViewWithMoreOptionsButton implements PromptContentView {
+    static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225;
+
+    private final String mDescription;
+
+    private PromptContentViewWithMoreOptionsButton(@NonNull String description) {
+        mDescription = description;
+    }
+
+    /**
+     * Gets the description for the content view, as set by
+     * {@link PromptContentViewWithMoreOptionsButton.Builder#setDescription(String)}.
+     *
+     * @return The description for the content view, or null if the content view has no description.
+     */
+    @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+    @Nullable
+    public String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * A builder used to set individual options for the
+     * {@link PromptContentViewWithMoreOptionsButton} class.
+     */
+    public static final class Builder {
+        private String mDescription;
+
+        /**
+         * Optional: Sets a description that will be shown on the content view.
+         *
+         * @param description The description to display.
+         * @return This builder.
+         * @throws IllegalArgumentException If description exceeds certain character limit.
+         */
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        @NonNull
+        public Builder setDescription(@NonNull String description) {
+            if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) {
+                throw new IllegalArgumentException("The character number of description exceeds "
+                        + MAX_DESCRIPTION_CHARACTER_NUMBER);
+            }
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Creates a {@link PromptContentViewWithMoreOptionsButton}.
+         *
+         * @return An instance of {@link PromptContentViewWithMoreOptionsButton}.
+         */
+        @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+        @NonNull
+        public PromptContentViewWithMoreOptionsButton build() {
+            return new PromptContentViewWithMoreOptionsButton(mDescription);
+        }
+    }
+}
diff --git a/biometric/biometric/src/main/java/androidx/biometric/PromptVerticalListContentView.java b/biometric/biometric/src/main/java/androidx/biometric/PromptVerticalListContentView.java
new file mode 100644
index 0000000..58d7be2
--- /dev/null
+++ b/biometric/biometric/src/main/java/androidx/biometric/PromptVerticalListContentView.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.biometric;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains the information of the template of vertical list content view for Biometric Prompt.
+ * <p>
+ * Here's how you'd set a <code>PromptVerticalListContentView</code> on a Biometric Prompt:
+ * <pre class="prettyprint">
+ * BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
+ *     .setTitle(...)
+ *     .setSubTitle(...)
+ *     .setContentView(
+ *         new PromptVerticalListContentView.Builder()
+ *             .setDescription("test description")
+ *             .addListItem(new PromptContentItemPlainText("test item 1"))
+ *             .addListItem(new PromptContentItemPlainText("test item 2"))
+ *             .addListItem(new PromptContentItemBulletedText("test item 3"))
+ *             .build()
+ *      )
+ *     .build();
+ * </pre>
+ */
+public final class PromptVerticalListContentView implements PromptContentView {
+    static final int MAX_ITEM_NUMBER = 20;
+    static final int MAX_EACH_ITEM_CHARACTER_NUMBER = 640;
+    static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225;
+
+    private final List<PromptContentItem> mContentList;
+    private final String mDescription;
+
+    private PromptVerticalListContentView(@NonNull List<PromptContentItem> contentList,
+            @NonNull String description) {
+        mContentList = contentList;
+        mDescription = description;
+    }
+
+    /**
+     * Gets the description for the content view, as set by
+     * {@link PromptVerticalListContentView.Builder#setDescription(String)}.
+     *
+     * @return The description for the content view, or null if the content view has no description.
+     */
+    @Nullable
+    public String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Gets the list of items on the content view, as set by
+     * {@link PromptVerticalListContentView.Builder#addListItem(PromptContentItem)}.
+     *
+     * @return The item list on the content view.
+     */
+    @NonNull
+    public List<PromptContentItem> getListItems() {
+        return new ArrayList<>(mContentList);
+    }
+
+    /**
+     * A builder used to set individual options for the {@link PromptVerticalListContentView} class.
+     */
+    public static final class Builder {
+        private final List<PromptContentItem> mContentList = new ArrayList<>();
+        private String mDescription;
+
+        /**
+         * Optional: Sets a description that will be shown on the content view.
+         *
+         * @param description The description to display.
+         * @return This builder.
+         * @throws IllegalArgumentException If description exceeds certain character limit.
+         */
+        @NonNull
+        public Builder setDescription(@NonNull String description) {
+            if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) {
+                throw new IllegalArgumentException("The character number of description exceeds "
+                        + MAX_DESCRIPTION_CHARACTER_NUMBER);
+            }
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Optional: Adds a list item in the current row.
+         *
+         * @param listItem The list item view to display
+         * @return This builder.
+         * @throws IllegalArgumentException If this list item exceeds certain character limits or
+         *                                  the number of list items exceeds certain limit.
+         */
+        @NonNull
+        public Builder addListItem(@NonNull PromptContentItem listItem) {
+            mContentList.add(listItem);
+            checkItemLimits(listItem);
+            return this;
+        }
+
+        /**
+         * Optional: Adds a list item in the current row.
+         *
+         * @param listItem The list item view to display
+         * @param index    The position at which to add the item
+         * @return This builder.
+         * @throws IllegalArgumentException If this list item exceeds certain character limits or
+         *                                  the number of list items exceeds certain limit.
+         */
+        @NonNull
+        public Builder addListItem(@NonNull PromptContentItem listItem, int index) {
+            mContentList.add(index, listItem);
+            checkItemLimits(listItem);
+            return this;
+        }
+
+        private void checkItemLimits(@NonNull PromptContentItem listItem) {
+            if (doesListItemExceedsCharLimit(listItem)) {
+                throw new IllegalArgumentException(
+                        "The character number of list item exceeds "
+                                + MAX_EACH_ITEM_CHARACTER_NUMBER);
+            }
+            if (mContentList.size() > MAX_ITEM_NUMBER) {
+                throw new IllegalArgumentException(
+                        "The number of list items exceeds " + MAX_ITEM_NUMBER);
+            }
+        }
+
+        private boolean doesListItemExceedsCharLimit(PromptContentItem listItem) {
+            if (listItem instanceof PromptContentItemPlainText) {
+                return ((PromptContentItemPlainText) listItem).getText().length()
+                        > MAX_EACH_ITEM_CHARACTER_NUMBER;
+            } else if (listItem instanceof PromptContentItemBulletedText) {
+                return ((PromptContentItemBulletedText) listItem).getText().length()
+                        > MAX_EACH_ITEM_CHARACTER_NUMBER;
+            } else {
+                return false;
+            }
+        }
+
+
+        /**
+         * Creates a {@link PromptVerticalListContentView}.
+         *
+         * @return An instance of {@link PromptVerticalListContentView}.
+         */
+        @NonNull
+        public PromptVerticalListContentView build() {
+            return new PromptVerticalListContentView(mContentList, mDescription);
+        }
+    }
+}
+
diff --git a/biometric/biometric/src/test/java/androidx/biometric/BiometricPromptTest.java b/biometric/biometric/src/test/java/androidx/biometric/BiometricPromptTest.java
index 56a5342..a43ef4c 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/BiometricPromptTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/BiometricPromptTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.graphics.Bitmap;
 import android.os.Build;
 
 import androidx.biometric.BiometricManager.Authenticators;
@@ -58,6 +59,95 @@
         assertThat(allowedAuthenticators).isEqualTo(allowedAuthenticators);
     }
 
+    @Test
+    public void testPromptInfo_CanSetAndGetOptions_logoResAndDescription() {
+        final int logoRes = R.drawable.fingerprint_dialog_fp_icon;
+        final String logoDescription = "logo description";
+        final String title = "Title";
+        final String negativeButtonText = "Negative";
+
+        final BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
+                .setLogoRes(logoRes)
+                .setLogoDescription(logoDescription)
+                .setLogoDescription(logoDescription)
+                .setTitle(title)
+                .setNegativeButtonText(negativeButtonText)
+                .build();
+
+        assertThat(info.getLogoRes()).isEqualTo(logoRes);
+        assertThat(info.getLogoDescription()).isEqualTo(logoDescription);
+    }
+
+    @Test
+    public void testPromptInfo_CanSetAndGetOptions_logoBitmap() {
+        final Bitmap logoBitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565);
+        final String logoDescription = "logo description";
+        final String title = "Title";
+        final String negativeButtonText = "Negative";
+
+        final BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
+                .setLogoBitmap(logoBitmap)
+                .setLogoDescription(logoDescription)
+                .setTitle(title)
+                .setNegativeButtonText(negativeButtonText)
+                .build();
+
+        assertThat(info.getLogoBitmap()).isEqualTo(logoBitmap);
+    }
+
+    @Test
+    public void testPromptInfo_CanSetAndGetOptions_verticalListContent() {
+        final String contentDescription = "test description";
+        final String itemOne = "content item 1";
+        final String itemTwo = "content item 2";
+        final PromptVerticalListContentView contentView =
+                new PromptVerticalListContentView.Builder()
+                        .setDescription(contentDescription)
+                        .addListItem(new PromptContentItemBulletedText(itemOne))
+                        .addListItem(new PromptContentItemBulletedText(itemTwo), 1).build();
+        final String title = "Title";
+        final String negativeButtonText = "Negative";
+
+        final BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
+                .setTitle(title)
+                .setNegativeButtonText(negativeButtonText)
+                .setContentView(contentView)
+                .build();
+
+        assertThat(info.getContentView()).isEqualTo(contentView);
+        final PromptVerticalListContentView realContentView =
+                (PromptVerticalListContentView) info.getContentView();
+        assertThat(realContentView.getDescription()).isEqualTo(contentDescription);
+        final PromptContentItemBulletedText realItemOne =
+                (PromptContentItemBulletedText) realContentView.getListItems().get(0);
+        assertThat(realItemOne.getText()).isEqualTo(itemOne);
+        final PromptContentItemBulletedText realItemTwo =
+                (PromptContentItemBulletedText) realContentView.getListItems().get(1);
+        assertThat(realItemTwo.getText()).isEqualTo(itemTwo);
+
+    }
+
+    @Test
+    public void testPromptInfo_CanSetAndGetOptions_contentViewMoreOptionsButton() {
+        final String contentDescription = "test description";
+        final PromptContentViewWithMoreOptionsButton contentView =
+                new PromptContentViewWithMoreOptionsButton.Builder().setDescription(
+                        contentDescription).build();
+        final String title = "Title";
+        final String negativeButtonText = "Negative";
+
+        final BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
+                .setTitle(title)
+                .setNegativeButtonText(negativeButtonText)
+                .setContentView(contentView)
+                .build();
+
+        assertThat(info.getContentView()).isEqualTo(contentView);
+        assertThat(
+                ((PromptContentViewWithMoreOptionsButton) info.getContentView())
+                        .getDescription()).isEqualTo(contentDescription);
+    }
+
     @Test(expected = IllegalArgumentException.class)
     public void testPromptInfo_FailsToBuild_WithNoTitle() {
         new BiometricPrompt.PromptInfo.Builder().setNegativeButtonText("Cancel").build();
diff --git a/biometric/biometric/src/test/java/androidx/biometric/BiometricViewModelTest.java b/biometric/biometric/src/test/java/androidx/biometric/BiometricViewModelTest.java
index 51e0889..f935035 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/BiometricViewModelTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/BiometricViewModelTest.java
@@ -36,13 +36,13 @@
     }
 
     @Test
-    public void testCanUpdateLiveDataValue_OnMainThread() {
+    public void testCanUpdateNegativeButtonLiveDataValue_OnMainThread() {
         mViewModel.setNegativeButtonPressPending(true);
         assertThat(mViewModel.isNegativeButtonPressPending().getValue()).isTrue();
     }
 
     @Test
-    public void testCanUpdateLiveDataValue_OnBackgroundThread() throws Exception {
+    public void testCanUpdateNegativeButtonLiveDataValue_OnBackgroundThread() throws Exception {
         final Thread backgroundThread = new Thread(new Runnable() {
             @Override
             public void run() {
@@ -54,4 +54,24 @@
         ShadowLooper.runUiThreadTasks();
         assertThat(mViewModel.isNegativeButtonPressPending().getValue()).isTrue();
     }
+
+    @Test
+    public void testCanUpdateMoreOptionsButtonLiveDataValue_OnMainThread() {
+        mViewModel.setMoreOptionsButtonPressPending(true);
+        assertThat(mViewModel.isMoreOptionsButtonPressPending().getValue()).isTrue();
+    }
+
+    @Test
+    public void testCanUpdateMoreOptionsButtonLiveDataValue_OnBackgroundThread() throws Exception {
+        final Thread backgroundThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                mViewModel.setMoreOptionsButtonPressPending(true);
+            }
+        });
+        backgroundThread.start();
+        backgroundThread.join();
+        ShadowLooper.runUiThreadTasks();
+        assertThat(mViewModel.isMoreOptionsButtonPressPending().getValue()).isTrue();
+    }
 }
diff --git a/biometric/integration-tests/testapp/build.gradle b/biometric/integration-tests/testapp/build.gradle
index e36cafe..d435a00 100644
--- a/biometric/integration-tests/testapp/build.gradle
+++ b/biometric/integration-tests/testapp/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         applicationId "androidx.biometric.integration.testapp"
     }
@@ -39,7 +40,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.8.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation(project(":biometric:biometric-ktx"))
     implementation("androidx.activity:activity-ktx:1.1.0")
     implementation("androidx.core:core-ktx:1.3.2")
diff --git a/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/CryptoUtils.kt b/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/CryptoUtils.kt
index 0671610..2564268 100644
--- a/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/CryptoUtils.kt
+++ b/biometric/integration-tests/testapp/src/main/java/androidx/biometric/integration/testapp/CryptoUtils.kt
@@ -20,7 +20,6 @@
 import android.os.Build
 import android.security.keystore.KeyGenParameterSpec
 import android.security.keystore.KeyProperties
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.biometric.BiometricPrompt
 import java.security.KeyStore
@@ -104,7 +103,6 @@
 /** Nested class to avoid verification errors for methods introduced in Android 11 (API 30). */
 @RequiresApi(Build.VERSION_CODES.R)
 private object Api30Impl {
-    @DoNotInline
     fun setUserAuthenticationParameters(
         builder: KeyGenParameterSpec.Builder,
         timeout: Int,
@@ -127,23 +125,19 @@
 /** Nested class to avoid verification errors for methods introduced in Android 6.0 (API 23). */
 @RequiresApi(Build.VERSION_CODES.M)
 private object Api23Impl {
-    @DoNotInline
     fun createKeyGenParameterSpecBuilder(
         keyName: String,
         keyPurpose: Int
     ): KeyGenParameterSpec.Builder = KeyGenParameterSpec.Builder(keyName, keyPurpose)
 
-    @DoNotInline
     fun setBlockModeCBC(builder: KeyGenParameterSpec.Builder) {
         builder.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
     }
 
-    @DoNotInline
     fun setEncryptionPaddingPKCS7(builder: KeyGenParameterSpec.Builder) {
         builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
     }
 
-    @DoNotInline
     fun setUserAuthenticationRequired(
         builder: KeyGenParameterSpec.Builder,
         userAuthenticationRequired: Boolean
@@ -152,7 +146,6 @@
     }
 
     @Suppress("DEPRECATION")
-    @DoNotInline
     fun setUserAuthenticationValidityDurationSeconds(
         builder: KeyGenParameterSpec.Builder,
         userAuthenticationValidityDurationSeconds: Int
@@ -162,7 +155,6 @@
         )
     }
 
-    @DoNotInline
     fun buildKeyGenParameterSpec(builder: KeyGenParameterSpec.Builder): KeyGenParameterSpec {
         return builder.build()
     }
diff --git a/bluetooth/bluetooth-testing/build.gradle b/bluetooth/bluetooth-testing/build.gradle
index 0f5e897..2cae81b 100644
--- a/bluetooth/bluetooth-testing/build.gradle
+++ b/bluetooth/bluetooth-testing/build.gradle
@@ -46,7 +46,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Test utilities for AndroidX Bluetooth"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/bluetooth/bluetooth/api/current.txt b/bluetooth/bluetooth/api/current.txt
index 00b1ea0..e0fa050 100644
--- a/bluetooth/bluetooth/api/current.txt
+++ b/bluetooth/bluetooth/api/current.txt
@@ -15,6 +15,7 @@
   }
 
   public final class AdvertiseParams {
+    ctor public AdvertiseParams();
     ctor public AdvertiseParams(optional boolean shouldIncludeDeviceAddress, optional boolean shouldIncludeDeviceName, optional boolean isConnectable, optional boolean isDiscoverable, optional @IntRange(from=0L, to=180000L) long durationMillis, optional java.util.Map<java.lang.Integer,byte[]> manufacturerData, optional java.util.Map<java.util.UUID,byte[]> serviceData, optional java.util.List<java.util.UUID> serviceUuids, optional java.util.List<java.util.UUID> serviceSolicitationUuids);
     method public long getDurationMillis();
     method public java.util.Map<java.lang.Integer,byte[]> getManufacturerData();
@@ -194,6 +195,7 @@
   }
 
   public final class ScanFilter {
+    ctor public ScanFilter();
     ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional String? deviceName, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask, optional java.util.UUID? serviceSolicitationUuid, optional java.util.UUID? serviceSolicitationUuidMask);
     method public androidx.bluetooth.BluetoothAddress? getDeviceAddress();
     method public String? getDeviceName();
diff --git a/bluetooth/bluetooth/api/restricted_current.txt b/bluetooth/bluetooth/api/restricted_current.txt
index 00b1ea0..e0fa050 100644
--- a/bluetooth/bluetooth/api/restricted_current.txt
+++ b/bluetooth/bluetooth/api/restricted_current.txt
@@ -15,6 +15,7 @@
   }
 
   public final class AdvertiseParams {
+    ctor public AdvertiseParams();
     ctor public AdvertiseParams(optional boolean shouldIncludeDeviceAddress, optional boolean shouldIncludeDeviceName, optional boolean isConnectable, optional boolean isDiscoverable, optional @IntRange(from=0L, to=180000L) long durationMillis, optional java.util.Map<java.lang.Integer,byte[]> manufacturerData, optional java.util.Map<java.util.UUID,byte[]> serviceData, optional java.util.List<java.util.UUID> serviceUuids, optional java.util.List<java.util.UUID> serviceSolicitationUuids);
     method public long getDurationMillis();
     method public java.util.Map<java.lang.Integer,byte[]> getManufacturerData();
@@ -194,6 +195,7 @@
   }
 
   public final class ScanFilter {
+    ctor public ScanFilter();
     ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional String? deviceName, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask, optional java.util.UUID? serviceSolicitationUuid, optional java.util.UUID? serviceSolicitationUuidMask);
     method public androidx.bluetooth.BluetoothAddress? getDeviceAddress();
     method public String? getDeviceName();
diff --git a/bluetooth/bluetooth/build.gradle b/bluetooth/bluetooth/build.gradle
index 2803d4f..1065e03 100644
--- a/bluetooth/bluetooth/build.gradle
+++ b/bluetooth/bluetooth/build.gradle
@@ -33,7 +33,7 @@
     implementation(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesCore)
 
-    implementation("androidx.annotation:annotation:1.7.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testExtTruth)
@@ -49,7 +49,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "AndroidX Bluetooth Library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/AdvertiseParams.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/AdvertiseParams.kt
index 4a944e4..cd09fca 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/AdvertiseParams.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/AdvertiseParams.kt
@@ -21,7 +21,6 @@
 import android.bluetooth.le.AdvertisingSetParameters as FwkAdvertisingSetParameters
 import android.os.Build
 import android.os.ParcelUuid
-import androidx.annotation.DoNotInline
 import androidx.annotation.IntRange
 import androidx.annotation.RequiresApi
 import java.util.UUID
@@ -69,13 +68,11 @@
     @RequiresApi(34)
     private object AdvertiseParamsApi34Impl {
         @JvmStatic
-        @DoNotInline
         fun setDiscoverable(builder: FwkAdvertiseSettings.Builder, isDiscoverable: Boolean) {
             builder.setDiscoverable(isDiscoverable)
         }
 
         @JvmStatic
-        @DoNotInline
         fun setDiscoverable(builder: FwkAdvertisingSetParameters.Builder, isDiscoverable: Boolean) {
             builder.setDiscoverable(isDiscoverable)
         }
@@ -84,7 +81,6 @@
     @RequiresApi(31)
     private object AdvertiseParamsApi31Impl {
         @JvmStatic
-        @DoNotInline
         fun addServiceSolicitationUuid(builder: FwkAdvertiseData.Builder, parcelUuid: ParcelUuid) {
             builder.addServiceSolicitationUuid(parcelUuid)
         }
@@ -93,7 +89,6 @@
     @RequiresApi(26)
     private object AdvertiseParamsApi26Impl {
         @JvmStatic
-        @DoNotInline
         fun fwkAdvertiseSetParams(
             isConnectable: Boolean,
             isDiscoverable: Boolean
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
index 3cfd5bd..fe8a30d 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
@@ -19,7 +19,6 @@
 import android.bluetooth.le.ScanFilter as FwkScanFilter
 import android.os.Build
 import android.os.ParcelUuid
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import java.util.UUID
 
@@ -90,7 +89,6 @@
     @RequiresApi(29)
     private object ScanFilterApi29Impl {
         @JvmStatic
-        @DoNotInline
         fun setServiceSolicitationUuid(
             builder: FwkScanFilter.Builder,
             serviceSolicitationUuid: UUID,
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
index 4bcbdcb..e786a94 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
@@ -19,7 +19,6 @@
 import android.bluetooth.le.ScanResult as FwkScanResult
 import android.os.Build
 import android.os.ParcelUuid
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.bluetooth.utils.addressType
@@ -51,7 +50,6 @@
     @RequiresApi(29)
     private object ScanResultApi29Impl {
         @JvmStatic
-        @DoNotInline
         fun serviceSolicitationUuids(fwkScanResult: FwkScanResult): List<ParcelUuid> =
             fwkScanResult.scanRecord?.serviceSolicitationUuids.orEmpty()
     }
@@ -59,11 +57,9 @@
     @RequiresApi(26)
     private object ScanResultApi26Impl {
         @JvmStatic
-        @DoNotInline
         fun isConnectable(fwkScanResult: FwkScanResult): Boolean = fwkScanResult.isConnectable
 
         @JvmStatic
-        @DoNotInline
         fun periodicAdvertisingInterval(fwkScanResult: FwkScanResult): Long =
             (fwkScanResult.periodicAdvertisingInterval * 1.25).toLong()
     }
diff --git a/browser/browser/build.gradle b/browser/browser/build.gradle
index aa9834f..5a5ee24 100644
--- a/browser/browser/build.gradle
+++ b/browser/browser/build.gradle
@@ -28,7 +28,7 @@
 
 dependencies {
     api("androidx.core:core:1.1.0")
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.annotation:annotation-experimental:1.4.1")
     api(libs.guavaListenableFuture)
 
@@ -60,5 +60,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2015"
     description = "Provides support for embedding Custom Tabs in an app."
-    metalavaK2UastEnabled = true
 }
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/Api33Impl.java b/browser/browser/src/main/java/androidx/browser/customtabs/Api33Impl.java
index ca13623..8785b2c 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/Api33Impl.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/Api33Impl.java
@@ -20,7 +20,6 @@
 
 import android.os.Bundle;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -31,7 +30,6 @@
         // This class is non-instantiable.
     }
 
-    @DoNotInline
     static <T> T getParcelable(@NonNull Bundle in, @Nullable String key,
             @NonNull Class<T> clazz) {
         return in.getParcelable(key, clazz);
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
index dc9e030..71bbefaf 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
@@ -40,7 +40,6 @@
 import androidx.annotation.AnimRes;
 import androidx.annotation.ColorInt;
 import androidx.annotation.Dimension;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -1788,12 +1787,10 @@
 
     @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
     private static class Api21Impl {
-        @DoNotInline
         static void setLanguageTag(Intent intent, Locale locale) {
             intent.putExtra(EXTRA_TRANSLATE_LANGUAGE_TAG, locale.toLanguageTag());
         }
 
-        @DoNotInline
         @Nullable
         static Locale getLocaleForLanguageTag(Intent intent) {
             String languageTag = intent.getStringExtra(EXTRA_TRANSLATE_LANGUAGE_TAG);
@@ -1803,7 +1800,6 @@
 
     @RequiresApi(api = Build.VERSION_CODES.M)
     private static class Api23Impl {
-        @DoNotInline
         static ActivityOptions makeBasicActivityOptions() {
             return ActivityOptions.makeBasic();
         }
@@ -1811,7 +1807,6 @@
 
     @RequiresApi(api = Build.VERSION_CODES.N)
     private static class Api24Impl {
-        @DoNotInline
         @Nullable
         static String getDefaultLocale() {
             LocaleList defaultLocaleList = LocaleList.getAdjustedDefault();
@@ -1821,7 +1816,6 @@
 
     @RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     private static class Api34Impl {
-        @DoNotInline
         static void setShareIdentityEnabled(ActivityOptions activityOptions, boolean enabled) {
             activityOptions.setShareIdentityEnabled(enabled);
         }
diff --git a/buildSrc-tests/lint-baseline.xml b/buildSrc-tests/lint-baseline.xml
index 82aca5e..b638fec 100644
--- a/buildSrc-tests/lint-baseline.xml
+++ b/buildSrc-tests/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="EagerGradleConfiguration"
@@ -67,15 +67,6 @@
     <issue
         id="EagerGradleConfiguration"
         message="Avoid using method findByName"
-        errorLine1="        if (project.tasks.findByName(&quot;check&quot;) != null) {"
-        errorLine2="                          ~~~~~~~~~~">
-        <location
-            file="${:buildSrc-tests*main*MAIN*sourceProvider*0*javaDir*4}/androidx/build/Ktlint.kt"/>
-    </issue>
-
-    <issue
-        id="EagerGradleConfiguration"
-        message="Avoid using method findByName"
         errorLine1="    tasks.findByName(taskName)"
         errorLine2="          ~~~~~~~~~~">
         <location
diff --git a/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt b/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt
index a3a86aa..23c9ff9 100644
--- a/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/KmpPlatformsTest.kt
@@ -23,111 +23,135 @@
 
     @Test
     fun withAnEmptyFlag_itReturnsTheDefaultValue() {
-        assertThat(parseTargetPlatformsFlag("")).isEqualTo(
-            setOf(
-                PlatformGroup.JVM,
-                PlatformGroup.MAC,
-                PlatformGroup.LINUX,
-                PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE
-            )
-        )
+        assertThat(parseTargetPlatformsFlag(""))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.JVM,
+                    PlatformGroup.WASM,
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE))
     }
 
     @Test
     fun withANullFlag_itReturnsTheDefaultValue() {
-        assertThat(parseTargetPlatformsFlag(null)).isEqualTo(
-            setOf(
-                PlatformGroup.JVM,
-                PlatformGroup.MAC,
-                PlatformGroup.LINUX,
-                PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE,
-            )
-        )
+        assertThat(parseTargetPlatformsFlag(null))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.JVM,
+                    PlatformGroup.WASM,
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE,
+                ))
     }
 
     @Test
     fun withASingleDefaultPlatform_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("+jvm")).isEqualTo(
-            setOf(
-                PlatformGroup.JVM,
-                PlatformGroup.MAC,
-                PlatformGroup.LINUX,
-                PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE,
-            )
-        )
+        assertThat(parseTargetPlatformsFlag("+jvm"))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.JVM,
+                    PlatformGroup.WASM,
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE,
+                ))
     }
 
     @Test
     fun withNoPlatforms_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("-jvm,-desktop,-native")).isEqualTo(
-            emptySet<PlatformGroup>()
-        )
+        assertThat(parseTargetPlatformsFlag("-jvm,-desktop,-native,-wasm"))
+            .isEqualTo(emptySet<PlatformGroup>())
     }
 
     @Test
     fun withASingleNonDefaultPlatform_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("+js")).isEqualTo(
-            setOf(
-                PlatformGroup.JVM,
-                PlatformGroup.JS,
-                PlatformGroup.MAC,
-                PlatformGroup.LINUX,
-                PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE,
-            )
-        )
+        assertThat(parseTargetPlatformsFlag("+js"))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.JVM,
+                    PlatformGroup.JS,
+                    PlatformGroup.WASM,
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE,
+                ))
     }
 
     @Test
     fun withAMultiplePlatforms_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("+js,+mac")).isEqualTo(
-            setOf(
-                PlatformGroup.JVM,
-                PlatformGroup.JS,
-                PlatformGroup.MAC,
-                PlatformGroup.LINUX,
-                PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE,
-            )
-        )
+        assertThat(parseTargetPlatformsFlag("+js,+mac"))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.JVM,
+                    PlatformGroup.JS,
+                    PlatformGroup.WASM,
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE,
+                ))
     }
 
     @Test
     fun withNegativeFlags_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("-jvm,+mac")).isEqualTo(
-            setOf(
-                PlatformGroup.MAC,
-                PlatformGroup.LINUX,
-                PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE,
-            )
-        )
+        assertThat(parseTargetPlatformsFlag("-jvm,+mac,-wasm"))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE,
+                ))
     }
 
     @Test
     fun withTheNativeFlag_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("+native")).isEqualTo(
-            setOf(PlatformGroup.JVM, PlatformGroup.MAC, PlatformGroup.LINUX, PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE)
-        )
+        assertThat(parseTargetPlatformsFlag("+native"))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.JVM,
+                    PlatformGroup.WASM,
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE))
     }
 
     @Test
     fun withMultipleFlagsIncludingTheNativeFlag_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("-jvm,+native,+js")).isEqualTo(
-            setOf(PlatformGroup.JS, PlatformGroup.MAC, PlatformGroup.LINUX, PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE)
-        )
+        assertThat(parseTargetPlatformsFlag("-jvm,+native,+js,-wasm"))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.JS,
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE))
     }
 
     @Test
     fun withRedundentFlags_itParsesTheFlagCorrectly() {
-        assertThat(parseTargetPlatformsFlag("-jvm,+native,+linux,+mac,+linux")).isEqualTo(
-            setOf(PlatformGroup.MAC, PlatformGroup.LINUX, PlatformGroup.DESKTOP,
-                PlatformGroup.ANDROID_NATIVE)
-        )
+        assertThat(parseTargetPlatformsFlag("-wasm,-jvm,+native,+linux,+mac,+linux,-wasm"))
+            .isEqualTo(
+                setOf(
+                    PlatformGroup.MAC,
+                    PlatformGroup.WINDOWS,
+                    PlatformGroup.LINUX,
+                    PlatformGroup.DESKTOP,
+                    PlatformGroup.ANDROID_NATIVE))
     }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
index b812f87..6b043c7 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
@@ -354,7 +354,7 @@
 
     var bypassCoordinateValidation = false
 
-    var metalavaK2UastEnabled = false
+    var metalavaK2UastEnabled = true
 
     val additionalDeviceTestApkKeys = mutableListOf<String>()
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index bc6c9bb..dd1eb8b 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -145,6 +145,9 @@
  */
 const val MIGRATE_ARRAY_ANNOTATIONS = "androidx.migrateArrayAnnotations"
 
+/** If true, yarn dependencies are fetched from an offline mirror */
+const val YARN_OFFLINE_MODE = "androidx.yarnOfflineMode"
+
 val ALL_ANDROIDX_PROPERTIES =
     setOf(
         ADD_GROUP_CONSTRAINTS,
@@ -179,6 +182,7 @@
         FilteredAnchorTask.PROP_PATH_PREFIX,
         INCLUDE_OPTIONAL_PROJECTS,
         MIGRATE_ARRAY_ANNOTATIONS,
+        YARN_OFFLINE_MODE,
     ) + AndroidConfigImpl.GRADLE_PROPERTIES
 
 /**
@@ -260,6 +264,9 @@
 fun Project.enableComposeCompilerReports() =
     findBooleanProperty(ENABLE_COMPOSE_COMPILER_REPORTS) ?: false
 
+/** Returns whether we should use the offline mirror for dependencies */
+fun Project.useYarnOffline() = findBooleanProperty(YARN_OFFLINE_MODE) ?: false
+
 /**
  * Returns whether this is an integration test that is allowing lint checks to be skipped to save
  * configuration time.
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index e453f50..c91b733 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -293,7 +293,7 @@
         task: AbstractTestTask,
         anchorTask: Task,
     ) {
-        anchorTask.dependsOn(task)
+        if (task.name !in listOf("jvmStubsTest")) anchorTask.dependsOn(task)
         val ignoreFailuresProperty =
             project.providers.gradleProperty(TEST_FAILURES_DO_NOT_FAIL_TEST_TASK)
         val ignoreFailures = ignoreFailuresProperty.isPresent
@@ -670,6 +670,9 @@
         }
         project.setUpCheckDocsTask(androidXExtension)
         project.writeBlankPublicTxtToAar(kotlinMultiplatformAndroidComponentsExtension)
+        kotlinMultiplatformAndroidComponentsExtension.onVariant {
+            project.validateKotlinModuleFiles(it.name, it.artifacts.get(SingleArtifact.AAR))
+        }
     }
 
     private fun Project.writeBlankPublicTxtToAar(
@@ -841,6 +844,10 @@
             onVariants { variant ->
                 variant.configureTests()
                 variant.enableLongMethodTracingInMicrobenchmark(project)
+                project.validateKotlinModuleFiles(
+                    variant.name,
+                    variant.artifacts.get(SingleArtifact.AAR)
+                )
             }
         }
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index f96abcd..30d9a7d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -36,12 +36,16 @@
 import org.gradle.api.file.FileCollection
 import org.gradle.api.plugins.ExtensionAware
 import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Copy
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.options.Option
+import org.gradle.kotlin.dsl.findByType
+import org.gradle.kotlin.dsl.the
+import org.gradle.kotlin.dsl.withType
 import org.gradle.work.DisableCachingByDefault
 import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
@@ -54,7 +58,14 @@
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
 import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
+import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWasmTargetDsl
+import org.jetbrains.kotlin.gradle.targets.js.ir.DefaultIncrementalSyncTask
+import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
+import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
+import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
+import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
 import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
 
 /**
@@ -65,7 +76,7 @@
  */
 open class AndroidXMultiplatformExtension(val project: Project) {
 
-    var enableBinaryCompatibilityValidator = false
+    var enableBinaryCompatibilityValidator = true
 
     // Kotlin multiplatform plugin is only applied if at least one target / sourceset is added.
     private val kotlinExtensionDelegate = lazy {
@@ -74,11 +85,11 @@
         project.multiplatformExtension!!
     }
     private val kotlinExtension: KotlinMultiplatformExtension by kotlinExtensionDelegate
-    val agpKmpExtensionDelegate = lazy {
+    private val agpKmpExtensionDelegate = lazy {
+        // make sure to initialize the kotlin extension by accessing the property
+        val extension = (kotlinExtension as ExtensionAware)
         project.plugins.apply(KotlinMultiplatformAndroidPlugin::class.java)
-        (kotlinExtension as ExtensionAware)
-            .extensions
-            .getByType(KotlinMultiplatformAndroidTarget::class.java)
+        extension.extensions.getByType(KotlinMultiplatformAndroidTarget::class.java)
     }
 
     val agpKmpExtension: KotlinMultiplatformAndroidTarget by agpKmpExtensionDelegate
@@ -379,7 +390,7 @@
     fun androidNativeX86(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.ANDROID_NATIVE_X86)
         return if (project.enableAndroidNative()) {
-            kotlinExtension.androidNativeX86().also { block?.execute(it) }
+            kotlinExtension.androidNativeX86 { block?.execute(this) }
         } else {
             null
         }
@@ -389,7 +400,7 @@
     fun androidNativeX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.ANDROID_NATIVE_X64)
         return if (project.enableAndroidNative()) {
-            kotlinExtension.androidNativeX64().also { block?.execute(it) }
+            kotlinExtension.androidNativeX64 { block?.execute(this) }
         } else {
             null
         }
@@ -399,7 +410,7 @@
     fun androidNativeArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.ANDROID_NATIVE_ARM64)
         return if (project.enableAndroidNative()) {
-            kotlinExtension.androidNativeArm64().also { block?.execute(it) }
+            kotlinExtension.androidNativeArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -409,7 +420,7 @@
     fun androidNativeArm32(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.ANDROID_NATIVE_ARM32)
         return if (project.enableAndroidNative()) {
-            kotlinExtension.androidNativeArm32().also { block?.execute(it) }
+            kotlinExtension.androidNativeArm32 { block?.execute(this) }
         } else {
             null
         }
@@ -437,6 +448,16 @@
         }
     }
 
+    @JvmOverloads
+    fun mingwX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
+        supportedPlatforms.add(PlatformIdentifier.MINGW_X_64)
+        return if (project.enableWindows()) {
+            kotlinExtension.mingwX64 { block?.execute(this) }
+        } else {
+            null
+        }
+    }
+
     /** Configures all mac targets supported by AndroidX. */
     @JvmOverloads
     fun mac(block: Action<KotlinNativeTarget>? = null): List<KotlinNativeTarget> {
@@ -447,7 +468,7 @@
     fun macosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
         supportedPlatforms.add(PlatformIdentifier.MAC_OSX_64)
         return if (project.enableMac()) {
-            kotlinExtension.macosX64().also { block?.execute(it) }
+            kotlinExtension.macosX64 { block?.execute(this) }
         } else {
             null
         }
@@ -457,7 +478,7 @@
     fun macosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
         supportedPlatforms.add(PlatformIdentifier.MAC_ARM_64)
         return if (project.enableMac()) {
-            kotlinExtension.macosArm64().also { block?.execute(it) }
+            kotlinExtension.macosArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -473,7 +494,7 @@
     fun iosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.IOS_ARM_64)
         return if (project.enableMac()) {
-            kotlinExtension.iosArm64().also { block?.execute(it) }
+            kotlinExtension.iosArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -483,7 +504,7 @@
     fun iosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.IOS_X_64)
         return if (project.enableMac()) {
-            kotlinExtension.iosX64().also { block?.execute(it) }
+            kotlinExtension.iosX64 { block?.execute(this) }
         } else {
             null
         }
@@ -493,7 +514,7 @@
     fun iosSimulatorArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.IOS_SIMULATOR_ARM_64)
         return if (project.enableMac()) {
-            kotlinExtension.iosSimulatorArm64().also { block?.execute(it) }
+            kotlinExtension.iosSimulatorArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -514,7 +535,7 @@
     fun watchosArm32(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.WATCHOS_ARM_32)
         return if (project.enableMac()) {
-            kotlinExtension.watchosArm32().also { block?.execute(it) }
+            kotlinExtension.watchosArm32 { block?.execute(this) }
         } else {
             null
         }
@@ -524,7 +545,7 @@
     fun watchosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.WATCHOS_ARM_64)
         return if (project.enableMac()) {
-            kotlinExtension.watchosArm64().also { block?.execute(it) }
+            kotlinExtension.watchosArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -534,7 +555,7 @@
     fun watchosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.WATCHOS_X_64)
         return if (project.enableMac()) {
-            kotlinExtension.watchosX64().also { block?.execute(it) }
+            kotlinExtension.watchosX64 { block?.execute(this) }
         } else {
             null
         }
@@ -544,7 +565,7 @@
     fun watchosSimulatorArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.WATCHOS_SIMULATOR_ARM_64)
         return if (project.enableMac()) {
-            kotlinExtension.watchosSimulatorArm64().also { block?.execute(it) }
+            kotlinExtension.watchosSimulatorArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -560,7 +581,7 @@
     fun tvosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.TVOS_ARM_64)
         return if (project.enableMac()) {
-            kotlinExtension.tvosArm64().also { block?.execute(it) }
+            kotlinExtension.tvosArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -570,7 +591,7 @@
     fun tvosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.TVOS_X_64)
         return if (project.enableMac()) {
-            kotlinExtension.tvosX64().also { block?.execute(it) }
+            kotlinExtension.tvosX64 { block?.execute(this) }
         } else {
             null
         }
@@ -580,7 +601,7 @@
     fun tvosSimulatorArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.TVOS_SIMULATOR_ARM_64)
         return if (project.enableMac()) {
-            kotlinExtension.tvosSimulatorArm64().also { block?.execute(it) }
+            kotlinExtension.tvosSimulatorArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -589,7 +610,7 @@
     @JvmOverloads
     fun linux(block: Action<KotlinNativeTarget>? = null): List<KotlinNativeTarget> {
         return listOfNotNull(
-            // TODO linuxArm64(block),
+            linuxArm64(block),
             linuxX64(block),
         )
     }
@@ -598,7 +619,7 @@
     fun linuxArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.LINUX_ARM_64)
         return if (project.enableLinux()) {
-            kotlinExtension.linuxArm64().also { block?.execute(it) }
+            kotlinExtension.linuxArm64 { block?.execute(this) }
         } else {
             null
         }
@@ -608,7 +629,7 @@
     fun linuxX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
         supportedPlatforms.add(PlatformIdentifier.LINUX_X_64)
         return if (project.enableLinux()) {
-            kotlinExtension.linuxX64().also { block?.execute(it) }
+            kotlinExtension.linuxX64 { block?.execute(this) }
         } else {
             null
         }
@@ -616,9 +637,17 @@
 
     @JvmOverloads
     fun linuxX64Stubs(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
+        // don't enable binary compatibility validator for stubs
+        enableBinaryCompatibilityValidator = false
         supportedPlatforms.add(PlatformIdentifier.LINUX_X_64_STUBS)
         return if (project.enableLinux()) {
-            kotlinExtension.linuxX64("linuxx64Stubs").also { block?.execute(it) }
+            kotlinExtension.linuxX64("linuxx64Stubs") {
+                block?.execute(this)
+                project.tasks.named("linuxx64StubsTest").configure {
+                    // don't try running common tests for stubs target
+                    it.enabled = false
+                }
+            }
         } else {
             null
         }
@@ -628,7 +657,23 @@
     fun js(block: Action<KotlinJsTargetDsl>? = null): KotlinJsTargetDsl? {
         supportedPlatforms.add(PlatformIdentifier.JS)
         return if (project.enableJs()) {
-            kotlinExtension.js().also { block?.execute(it) }
+            kotlinExtension.js { block?.execute(this) }
+        } else {
+            null
+        }
+    }
+
+    @OptIn(ExperimentalWasmDsl::class)
+    @JvmOverloads
+    fun wasmJs(block: Action<KotlinJsTargetDsl>? = null): KotlinWasmTargetDsl? {
+        supportedPlatforms.add(PlatformIdentifier.WASM_JS)
+        return if (project.enableWasmJs()) {
+            kotlinExtension.wasmJs("wasmJs") {
+                block?.execute(this)
+                binaries.executable()
+                browser {}
+                project.configureWasm()
+            }
         } else {
             null
         }
@@ -639,6 +684,79 @@
     }
 }
 
+private fun Project.configureWasm() {
+    rootProject.extensions.findByType<NodeJsRootExtension>()?.nodeVersion = getVersionByName("node")
+    rootProject.extensions.findByType<YarnRootExtension>()?.version = getVersionByName("yarn")
+    rootProject.plugins.withType<YarnPlugin> {
+        rootProject.the<YarnRootExtension>().lockFileDirectory =
+            File(project.getPrebuiltsRoot(), "androidx/external/wasm/yarn-offline-mirror")
+    }
+
+    val offlineMirrorStorage =
+        File(getPrebuiltsRoot(), "androidx/external/wasm/yarn-offline-mirror")
+    val createYarnRcFileTask =
+        rootProject.tasks.register("createYarnRcFile", CreateYarnRcFileTask::class.java) {
+            it.offlineMirrorStorage.set(offlineMirrorStorage)
+            it.yarnrcFile.set(rootProject.layout.buildDirectory.file("js/.yarnrc"))
+        }
+    rootProject.tasks.withType<KotlinNpmInstallTask>().configureEach {
+        it.dependsOn(createYarnRcFileTask)
+        it.args.addAll(listOf("--ignore-engines", "--verbose"))
+
+        println(
+            """
+             Yarn packages will be fetched from the offline mirror: ${offlineMirrorStorage.path}.
+             If yarn has a dependency that is not there, your build will fail. To fix, re-run your
+             Gradle task with -Pandroidx.yarnOfflineMode=false to download the dependencies from the
+             internet into the offline mirror. Don't forget to upload the changes from that repo to
+             Gerrit as well!   
+            """
+                .trimIndent()
+                .replace("\n", " ")
+        )
+
+        if (project.useYarnOffline()) {
+            it.args.add("--offline")
+        }
+    }
+
+    // Use DSL API when https://youtrack.jetbrains.com/issue/KT-70029 is closed for all tasks below
+    tasks.named("wasmJsDevelopmentExecutableCompileSync", DefaultIncrementalSyncTask::class.java) {
+        it.destinationDirectory.set(
+            file(layout.buildDirectory.dir("js/packages/wasm-js/dev/kotlin"))
+        )
+    }
+    tasks.named("wasmJsProductionExecutableCompileSync", DefaultIncrementalSyncTask::class.java) {
+        it.destinationDirectory.set(
+            file(layout.buildDirectory.dir("js/packages/wasm-js/prod/kotlin"))
+        )
+    }
+    tasks.named(
+        "wasmJsTestTestDevelopmentExecutableCompileSync",
+        DefaultIncrementalSyncTask::class.java
+    ) {
+        it.destinationDirectory.set(
+            file(layout.buildDirectory.dir("js/packages/wasm-js-test/dev/kotlin"))
+        )
+    }
+    tasks.named(
+        "wasmJsTestTestProductionExecutableCompileSync",
+        DefaultIncrementalSyncTask::class.java
+    ) {
+        it.destinationDirectory.set(
+            file(layout.buildDirectory.dir("js/packages/wasm-js-test/prod/kotlin"))
+        )
+    }
+    tasks.named("wasmJsBrowserDevelopmentExecutableDistributeResources", Copy::class.java) {
+        it.destinationDir =
+            file(layout.buildDirectory.dir("dist/wasm-js/developmentExecutable/resources"))
+    }
+    tasks.named("wasmJsBrowserProductionExecutableDistributeResources", Copy::class.java) {
+        it.destinationDir =
+            file(layout.buildDirectory.dir("dist/wasm-js/productionExecutable/resources"))
+    }
+}
+
 fun Project.validatePublishedMultiplatformHasDefault() {
     val extension = project.extensions.getByType(AndroidXMultiplatformExtension::class.java)
     if (extension.defaultPlatform == null && extension.supportedPlatforms.isNotEmpty()) {
@@ -756,7 +874,7 @@
      * does not appear in this list will use its [KotlinPlatformType] name.
      */
     private val allowedTargetNameSuffixes =
-        setOf("android", "desktop", "jvm", "jvmStubs", "linuxx64Stubs")
+        setOf("android", "desktop", "jvm", "commonStubs", "jvmStubs", "linuxx64Stubs", "wasmJs")
 
     /** The preferred source file suffix for the target's platform type. */
     private val KotlinTarget.preferredSourceFileSuffix: String
@@ -772,4 +890,4 @@
  * Set of targets are there to serve as stubs, but are not expected to be consumed by library
  * consumers.
  */
-internal val setOfStubTargets = setOf("jvmStubs", "linuxx64Stubs")
+internal val setOfStubTargets = setOf("commonStubs", "jvmStubs", "linuxx64Stubs")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index 0f41141..93b6662 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -59,7 +59,6 @@
         setDependencyVersions()
         configureKtfmtCheckFile()
         tasks.register(CheckExternalDependencyLicensesTask.TASK_NAME)
-
         maybeRegisterFilterableTask()
 
         // If we're running inside Studio, validate the Android Gradle Plugin version.
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/CreateYarnRcTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/CreateYarnRcTask.kt
new file mode 100644
index 0000000..42ddf85
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/CreateYarnRcTask.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
+
+/**
+ * Creates an `.yarnrc` file in a specified directory. The `.yarnrc` file will contain the path to
+ * the offline storage of the required dependencies.
+ */
+@DisableCachingByDefault(because = "not worth caching")
+abstract class CreateYarnRcFileTask : DefaultTask() {
+
+    @get:InputDirectory
+    @get:PathSensitive(PathSensitivity.ABSOLUTE)
+    abstract val offlineMirrorStorage: DirectoryProperty
+
+    @get:OutputFile abstract val yarnrcFile: RegularFileProperty
+
+    @TaskAction
+    fun createFile() {
+        val offlineStoragePath = offlineMirrorStorage.get().asFile.absolutePath
+        yarnrcFile.get().asFile.let {
+            it.parentFile.mkdirs()
+            it.writeText("yarn-offline-mirror \"$offlineStoragePath\"")
+        }
+    }
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt
index e1bff78..016ea66 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ListTaskOutputsTask.kt
@@ -118,10 +118,30 @@
         "transformIosTestCInteropDependenciesMetadataForIde",
         "transformNativeTestCInteropDependenciesMetadataForIde",
         "transformNativeMainCInteropDependenciesMetadataForIde",
+        "transformLinuxMainCInteropDependenciesMetadataForIde",
+        "transformNonJvmCommonMainCInteropDependenciesMetadataForIde",
 
         // The following tests intentionally have the same output of golden images
         "updateGoldenDesktopTest",
-        "updateGoldenDebugUnitTest"
+        "updateGoldenDebugUnitTest",
+
+        // The following tasks have the same output file:
+        // ../../prebuilts/androidx/external/wasm/yarn-offline-mirror/yarn.lock
+        "kotlinRestoreYarnLock",
+        "kotlinNpmInstall",
+        "kotlinUpgradePackageLock",
+        "kotlinUpgradeYarnLock",
+        "kotlinStorePackageLock",
+        "kotlinStoreYarnLock",
+
+        // The following tasks have the same output configFile file:
+        // projectBuildDir/js/packages/projectName-wasm-js/webpack.config.js
+        // Remove when https://youtrack.jetbrains.com/issue/KT-70029 is resolved
+        // and set configFile location for each task
+        "wasmJsBrowserDevelopmentWebpack",
+        "wasmJsBrowserDevelopmentRun",
+        "wasmJsBrowserProductionWebpack",
+        "wasmJsBrowserProductionRun",
     )
 
 fun shouldValidateTaskOutput(task: Task): Boolean {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
index 3d7f576..5a347ab 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
@@ -465,7 +465,15 @@
                 ?.asMap
                 ?.filterValues { it.publishable }
                 ?.keys
-                ?.map { it.lowercase() } ?: emptySet()
+                ?.map {
+                    it.lowercase()
+                        // Remove when https://youtrack.jetbrains.com/issue/KT-70072 is fixed.
+                        // MultiplatformExtension.targets includes `wasmjs` in its list, however,
+                        // the publication folder for this target is named `wasm-js`. Not having
+                        // this replace causes the verifyInputscreateProjectZip task to fail
+                        // as it is looking for a file named wasmjs
+                        .replace("wasmjs", "wasm-js")
+                } ?: emptySet()
         val declaredTargets = potentialTargets.filter { it != "metadata" }
         return declaredTargets.toList()
     }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ValidateKotlinModuleFiles.kt b/buildSrc/private/src/main/kotlin/androidx/build/ValidateKotlinModuleFiles.kt
new file mode 100644
index 0000000..e9ece07
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ValidateKotlinModuleFiles.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build
+
+import androidx.build.uptodatedness.cacheEvenIfNoOutputs
+import com.android.SdkConstants.DOT_KOTLIN_MODULE
+import com.android.utils.appendCapitalized
+import javax.inject.Inject
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.Project
+import org.gradle.api.file.ArchiveOperations
+import org.gradle.api.file.RegularFile
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
+import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
+
+internal fun Project.validateKotlinModuleFiles(variantName: String, aar: Provider<RegularFile>) {
+    if (
+        !project.plugins.hasPlugin(KotlinBasePluginWrapper::class.java) &&
+            !project.plugins.hasPlugin(KotlinMultiplatformPluginWrapper::class.java)
+    ) {
+        return
+    }
+    val validateKotlinModuleFiles =
+        tasks.register(
+            "validateKotlinModuleFilesFor".appendCapitalized(variantName),
+            ValidateModuleFilesTask::class.java
+        ) {
+            it.aar.set(aar)
+            it.cacheEvenIfNoOutputs()
+        }
+    project.addToBuildOnServer(validateKotlinModuleFiles)
+}
+
+@CacheableTask
+abstract class ValidateModuleFilesTask() : DefaultTask() {
+
+    @get:Inject abstract val archiveOperations: ArchiveOperations
+
+    @get:PathSensitive(PathSensitivity.NONE) @get:InputFile abstract val aar: RegularFileProperty
+
+    @get:Internal
+    val fileName: String
+        get() = aar.get().asFile.name
+
+    @TaskAction
+    fun execute() {
+        val fileTree = archiveOperations.zipTree(aar)
+        val classesJar =
+            fileTree.find { it.name == "classes.jar" }
+                ?: throw GradleException("Could not classes.jar in $fileName")
+        val jarContents = archiveOperations.zipTree(classesJar)
+        if (jarContents.files.size <= 1) {
+            // only version file, stub project with no sources.
+            return
+        }
+        jarContents.find { it.name.endsWith(DOT_KOTLIN_MODULE) }
+            ?: throw GradleException("Could not find .kotlin_module file in $fileName")
+    }
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt b/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt
index cc9782a..dff7821 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt
@@ -20,11 +20,14 @@
 
 import androidx.build.AndroidXMultiplatformExtension
 import androidx.build.Version
+import androidx.build.addToBuildOnServer
+import androidx.build.addToCheckTask
 import androidx.build.checkapi.ApiType
 import androidx.build.checkapi.getBcvFileDirectory
 import androidx.build.checkapi.getBuiltBcvFileDirectory
 import androidx.build.checkapi.getRequiredCompatibilityApiFileFromDir
 import androidx.build.checkapi.shouldWriteVersionedApiFile
+import androidx.build.uptodatedness.cacheEvenIfNoOutputs
 import androidx.build.version
 import com.android.utils.appendCapitalized
 import java.io.File
@@ -79,6 +82,8 @@
             val updateAll: TaskProvider<Task> = project.tasks.register(UPDATE_NAME)
             configureKlibTasks(project, checkAll, updateAll)
             project.tasks.named("check").configure { it.dependsOn(checkAll) }
+            project.addToCheckTask(checkAll)
+            project.addToBuildOnServer(checkAll)
         }
 
     private fun configureKlibTasks(
@@ -138,14 +143,16 @@
 
     /* Check that the current ABI definition is up to date. */
     private fun Project.checkKlibAbiTask(projectApiFile: File, generatedApiFile: File) =
-        project.tasks.register(
-            CHECK_NAME.appendCapitalized(NATIVE_SUFFIX),
-            KotlinApiCompareTask::class.java
-        ) {
-            it.projectApiFile = projectApiFile
-            it.generatedApiFile = generatedApiFile
-            it.group = ABI_GROUP_NAME
-        }
+        project.tasks
+            .register(
+                CHECK_NAME.appendCapitalized(NATIVE_SUFFIX),
+                KotlinApiCompareTask::class.java
+            ) {
+                it.projectApiFile = projectApiFile
+                it.generatedApiFile = generatedApiFile
+                it.group = ABI_GROUP_NAME
+            }
+            .also { task -> task.configure { it.cacheEvenIfNoOutputs() } }
 
     /* Check that the current ABI definition is compatible with most recently released version */
     private fun Project.checkKlibAbiReleaseTask(
@@ -205,7 +212,7 @@
      */
     private fun Project.extractKlibAbiTask(klibApiDir: File, extractDir: File) =
         project.tasks.register(EXTRACT_NAME, KotlinKlibExtractSupportedTargetsAbiTask::class.java) {
-            it.strictValidation = true
+            it.strictValidation = HostManager.hostIsMac
             it.supportedTargets = project.provider { supportedNativeTargetNames() }
             it.inputAbiFile = klibApiDir.resolve(CURRENT_API_FILE_NAME)
             it.outputAbiFile = extractDir.resolve(CURRENT_API_FILE_NAME)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
index b9f09fd..70390ec 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
@@ -30,7 +30,7 @@
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
 import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration
+import org.gradle.api.internal.artifacts.ivyservice.TypedResolveException
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Input
@@ -162,7 +162,7 @@
         val inputs: JavaCompileInputs?
         try {
             inputs = getFiles(runnerProject, mavenId)
-        } catch (e: DefaultLenientConfiguration.ArtifactResolveException) {
+        } catch (e: TypedResolveException) {
             runnerProject.logger.info("Ignoring missing artifact $mavenId: $e")
             return
         }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
index f8e6fb0..3031575 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
@@ -124,7 +124,10 @@
         ":wear:tiles:tiles-proto:extractIncludeTestProto",
 
         // https://youtrack.jetbrains.com/issue/KT-61931
-        "checkKotlinGradlePluginConfigurationErrors"
+        "checkKotlinGradlePluginConfigurationErrors",
+
+        // https://youtrack.jetbrains.com/issue/KT-70008
+        "kotlinNpmCachesSetup",
     )
 
 // Additional tasks that are expected to be temporarily out-of-date after running once
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt b/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt
index f592083..59be856b 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/AndroidXConfig.kt
@@ -34,7 +34,7 @@
     }
 
     override val minSdk: Int = 21
-    override val ndkVersion: String = "25.2.9519653"
+    override val ndkVersion: String = "27.0.12077973"
 
     override val targetSdk: Int by lazy {
         project.providers.gradleProperty(TARGET_SDK_VERSION).get().toInt()
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
index c3893a7..e2c376d 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/KmpPlatforms.kt
@@ -34,14 +34,16 @@
 enum class PlatformGroup {
     JVM,
     JS,
+    WASM,
     MAC,
+    WINDOWS,
     LINUX,
     DESKTOP,
     ANDROID_NATIVE;
 
     companion object {
         /** Target platform groups which require native compilation (e.g. LLVM). */
-        val native = listOf(MAC, LINUX, ANDROID_NATIVE)
+        val native = listOf(MAC, LINUX, WINDOWS, ANDROID_NATIVE)
 
         /**
          * Target platform groups which are enabled by default.
@@ -49,7 +51,7 @@
          * Do *not* enable [JS] unless you have read and understand this:
          * https://blog.jetbrains.com/kotlin/2021/10/important-ua-parser-js-exploit-and-kotlin-js/
          */
-        val enabledByDefault = listOf(JVM, DESKTOP, MAC, LINUX, ANDROID_NATIVE)
+        val enabledByDefault = listOf(JVM, DESKTOP, MAC, LINUX, WINDOWS, ANDROID_NATIVE, WASM)
     }
 }
 
@@ -61,6 +63,7 @@
     JVM("jvm", PlatformGroup.JVM),
     JVM_STUBS("jvmStubs", PlatformGroup.JVM),
     JS("js", PlatformGroup.JS),
+    WASM_JS("wasmJs", PlatformGroup.WASM),
     ANDROID("android", PlatformGroup.JVM),
     ANDROID_NATIVE_ARM32("androidNativeArm32", PlatformGroup.ANDROID_NATIVE),
     ANDROID_NATIVE_ARM64("androidNativeArm64", PlatformGroup.ANDROID_NATIVE),
@@ -68,6 +71,7 @@
     ANDROID_NATIVE_X64("androidNativeX64", PlatformGroup.ANDROID_NATIVE),
     MAC_ARM_64("macosarm64", PlatformGroup.MAC),
     MAC_OSX_64("macosx64", PlatformGroup.MAC),
+    MINGW_X_64("mingwx64", PlatformGroup.WINDOWS),
     LINUX_ARM_64("linuxarm64", PlatformGroup.LINUX),
     LINUX_X_64("linuxx64", PlatformGroup.LINUX),
     LINUX_X_64_STUBS("linuxx64Stubs", PlatformGroup.LINUX),
@@ -138,8 +142,12 @@
 
 fun Project.enableMac(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.MAC)
 
+fun Project.enableWindows(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.WINDOWS)
+
 fun Project.enableLinux(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.LINUX)
 
 fun Project.enableJvm(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.JVM)
 
 fun Project.enableDesktop(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.DESKTOP)
+
+fun Project.enableWasmJs(): Boolean = enabledKmpPlatforms.contains(PlatformGroup.WASM)
diff --git a/busytown/androidx_host_tests_max_dep_versions.sh b/busytown/androidx_host_tests_max_dep_versions.sh
index c979e17..1b57f2b 100755
--- a/busytown/androidx_host_tests_max_dep_versions.sh
+++ b/busytown/androidx_host_tests_max_dep_versions.sh
@@ -6,6 +6,7 @@
 cd "$(dirname $0)"
 
 impl/build.sh test allHostTests zipOwnersFiles createModuleInfo \
+    -Pandroid.experimental.disableCompileSdkChecks=true \
     -Pandroidx.useMaxDepVersions \
     -Pandroidx.displayTestOutput=false \
     -Pandroidx.ignoreTestFailures "$@"
diff --git a/busytown/androidx_max_dep_versions.sh b/busytown/androidx_max_dep_versions.sh
index ca9e1d1..a9a2216 100755
--- a/busytown/androidx_max_dep_versions.sh
+++ b/busytown/androidx_max_dep_versions.sh
@@ -7,7 +7,7 @@
 
 impl/build.sh assembleRelease assembleAndroidTest \
     -Pandroidx.useMaxDepVersions \
-    -Dandroid.experimental.disableCompileSdkChecks=true \
+    -Pandroid.experimental.disableCompileSdkChecks=true \
     "$@"
 
 echo "Completing $0 at $(date)"
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index b71053d..7500b67 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -34,7 +34,7 @@
     implementation("androidx.core:core:1.3.2")
 
     // Classes and types that are needed at compile & runtime
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":camera:camera-core"))
 
     // Classes and types that are only needed at runtime
@@ -107,7 +107,6 @@
     description = "A Camera2 Pipe implementation of CameraX, a library providing a consistent " +
             "and reliable camera foundation that enables great camera driven experiences across " +
             "all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "Not shipped externally"
 }
diff --git a/camera/camera-camera2-pipe-integration/lint-baseline.xml b/camera/camera-camera2-pipe-integration/lint-baseline.xml
index 7726617..6049b5d 100644
--- a/camera/camera-camera2-pipe-integration/lint-baseline.xml
+++ b/camera/camera-camera2-pipe-integration/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="CameraXQuirksClassDetector"
@@ -49,7 +49,7 @@
     <issue
         id="SupportAnnotationUsage"
         message="Did you mean `@get:VisibleForTesting`? Without `get:` this annotates the constructor parameter itself instead of the associated getter."
-        errorLine1="    @VisibleForTesting internal val requestListener:"
+        errorLine1="    @VisibleForTesting internal val requestListener: ComboRequestListener,"
         errorLine2="    ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt"/>
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
index ccbaa84..a15ada77 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
@@ -62,10 +62,10 @@
 
     @Test
     fun canReturnSupportedOutputFormats() {
-        val formats = cameraInfoAdapter.supportedOutputFormats.toList()
+        val formats = cameraInfoAdapter.supportedOutputFormats
         val cameraCharacteristics = CameraUtil.getCameraCharacteristics(lensFacing)!!
         val streamConfigurationMap = cameraCharacteristics.get(SCALER_STREAM_CONFIGURATION_MAP)!!
 
-        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toList())
+        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toSet())
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
index 70dba7c..e9e6568 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
@@ -174,6 +174,11 @@
 
     override var runningUseCases = useCases.toSet()
 
+    override var isPrimary: Boolean = true
+        set(value) {
+            field = value
+        }
+
     override fun <T> setParameterAsync(
         key: CaptureRequest.Key<T>,
         value: T,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
index 0f83e75..00c2e89 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
@@ -18,12 +18,12 @@
 
 import androidx.annotation.VisibleForTesting
 import androidx.camera.camera2.pipe.CameraDevices
+import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraPipe
 import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter.Companion.cameraId
 import androidx.camera.camera2.pipe.integration.internal.CameraCompatibilityFilter.isBackwardCompatible
-import androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo
-import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
 import androidx.camera.core.CameraInfo
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.InitializationException
@@ -87,7 +87,6 @@
             cameraInternal as CameraInternalAdapter
     }
 
-    @OptIn(ExperimentalCamera2Interop::class)
     override fun getConcurrentCameraSelectors(): MutableList<MutableList<CameraSelector>> {
         return concurrentCameraIdsSet
             .map { concurrentCameraIds ->
@@ -95,9 +94,7 @@
                     .map { cameraId ->
                         CameraSelector.Builder()
                             .addCameraFilter { cameraInfos ->
-                                cameraInfos.filter {
-                                    cameraId.value == Camera2CameraInfo.from(it).getCameraId()
-                                }
+                                cameraInfos.filter { cameraInfo -> cameraId == cameraInfo.cameraId }
                             }
                             .build()
                     }
@@ -112,21 +109,32 @@
 
     override fun setActiveConcurrentCameraInfos(cameraInfos: MutableList<CameraInfo>) {
         activeConcurrentCameraInfosList = cameraInfos
+
         val graphConfigs =
-            cameraInternalMap.values.map {
-                checkNotNull(it.getDeferredCameraGraphConfig()) {
-                    "Every CameraInternal instance is expected to have a deferred CameraGraph config " +
-                        "when the active concurrent CameraInfos are set!"
+            cameraInternalMap.values
+                .filter { cameraInternalAdapter ->
+                    cameraInfos.any { cameraInfo ->
+                        cameraInfo.cameraId?.value ==
+                            cameraInternalAdapter.cameraInfoInternal.cameraId
+                    }
                 }
-            }
-        val cameraGraphs = checkNotNull(cameraPipe).createCameraGraphs(graphConfigs)
-        check(cameraGraphs.size == cameraInternalMap.size)
+                .map {
+                    checkNotNull(it.getDeferredCameraGraphConfig()) {
+                        "Every CameraInternal instance is expected to have a deferred CameraGraph " +
+                            "config when the active concurrent CameraInfos are set!"
+                    }
+                }
+
+        // Create paired CameraGraphs based on the set of graphConfigs
+        val cameraGraphs =
+            checkNotNull(cameraPipe).createCameraGraphs(CameraGraph.ConcurrentConfig(graphConfigs))
+        check(cameraGraphs.size == graphConfigs.size)
+
         for ((cameraInternalAdapter, cameraGraph) in cameraInternalMap.values.zip(cameraGraphs)) {
             cameraInternalAdapter.resumeDeferredCameraGraphCreation(cameraGraph)
         }
     }
 
-    @OptIn(ExperimentalCamera2Interop::class)
     override fun getPairedConcurrentCameraId(cameraId: String): String? {
         if (!concurrentCameraIdMap.containsKey(cameraId)) {
             return null
@@ -134,7 +142,7 @@
 
         for (pairedCameraId in concurrentCameraIdMap[cameraId]!!) {
             for (cameraInfo in activeConcurrentCameraInfos) {
-                if (pairedCameraId == Camera2CameraInfo.from(cameraInfo).getCameraId()) {
+                if (pairedCameraId == cameraInfo.cameraId?.value) {
                     return pairedCameraId
                 }
             }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
index a699357..355cabe 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
@@ -20,16 +20,17 @@
 import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_ON
 import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
-import android.hardware.camera2.CameraMetadata
 import android.hardware.camera2.params.DynamicRangeProfiles
 import android.os.Build
 import android.util.Range
 import android.util.Size
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.CameraMetadata.Companion.supportsLogicalMultiCamera
 import androidx.camera.camera2.pipe.CameraMetadata.Companion.supportsPrivateReprocessing
 import androidx.camera.camera2.pipe.CameraPipe
+import androidx.camera.camera2.pipe.UnsafeWrapper
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.integration.compat.DynamicRangeProfilesCompat
 import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
@@ -70,6 +71,7 @@
 import androidx.lifecycle.LiveData
 import java.util.concurrent.Executor
 import javax.inject.Inject
+import kotlin.reflect.KClass
 
 /** Adapt the [CameraInfoInternal] interface to [CameraPipe]. */
 @SuppressLint(
@@ -89,7 +91,7 @@
     private val encoderProfilesProviderAdapter: EncoderProfilesProviderAdapter,
     private val streamConfigurationMapCompat: StreamConfigurationMapCompat,
     private val cameraFovInfo: CameraFovInfo,
-) : CameraInfoInternal {
+) : CameraInfoInternal, UnsafeWrapper {
     init {
         DeviceInfoLogger.logDeviceInfo(cameraProperties)
     }
@@ -200,8 +202,8 @@
         val timeSource =
             cameraProperties.metadata[CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE]!!
         return when (timeSource) {
-            CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME -> Timebase.REALTIME
-            CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN -> Timebase.UPTIME
+            CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME -> Timebase.REALTIME
+            CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN -> Timebase.UPTIME
             else -> Timebase.UPTIME
         }
     }
@@ -221,6 +223,16 @@
             ?: emptyList()
     }
 
+    @Suppress("UNCHECKED_CAST")
+    @OptIn(ExperimentalCamera2Interop::class)
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? =
+        when (type) {
+            Camera2CameraInfo::class -> camera2CameraInfo as T
+            CameraProperties::class -> cameraProperties as T
+            CameraMetadata::class -> cameraProperties.metadata as T
+            else -> cameraProperties.metadata.unwrapAs(type)
+        }
+
     override fun toString(): String = "CameraInfoAdapter<$cameraConfig.cameraId>"
 
     override fun getCameraQuirks(): Quirks {
@@ -312,5 +324,21 @@
                 DynamicRangeProfiles.DOLBY_VISION_8B_HDR_REF to DOLBY_VISION_8_BIT,
                 DynamicRangeProfiles.DOLBY_VISION_8B_HDR_REF_PO to DOLBY_VISION_8_BIT,
             )
+
+        fun <T : Any> CameraInfo.unwrapAs(type: KClass<T>): T? =
+            when (this) {
+                is UnsafeWrapper -> this.unwrapAs(type)
+                is CameraInfoInternal -> {
+                    if (this.implementation !== this) {
+                        this.implementation.unwrapAs(type)
+                    } else {
+                        null
+                    }
+                }
+                else -> null
+            }
+
+        val CameraInfo.cameraId: CameraId?
+            get() = this.unwrapAs(CameraMetadata::class)?.camera
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
index ad25703..21b16df 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
@@ -57,7 +57,6 @@
         CameraConfigs.defaultConfig()
     private val debugId = cameraAdapterIds.incrementAndGet()
     private var sessionProcessor: SessionProcessor? = null
-    private var isPrimary = true
 
     init {
         debug { "Created $this for $cameraId" }
@@ -85,7 +84,7 @@
     }
 
     override fun setPrimary(isPrimary: Boolean) {
-        this.isPrimary = isPrimary
+        useCaseManager.setPrimary(isPrimary)
     }
 
     override fun setActiveResumingMode(enabled: Boolean) {
@@ -142,5 +141,5 @@
         useCaseManager.sessionProcessor = sessionProcessor
     }
 
-    override fun toString(): String = "CameraInternalAdapter<$cameraId>"
+    override fun toString(): String = "CameraInternalAdapter<$cameraId($debugId)>"
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt
index a7b6fc5..295b087 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt
@@ -22,7 +22,6 @@
 import android.media.EncoderProfiles
 import android.os.Build
 import android.util.Size
-import androidx.annotation.DoNotInline
 import androidx.annotation.Nullable
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraPipe
@@ -184,7 +183,6 @@
 
     @RequiresApi(31)
     internal object Api31Impl {
-        @DoNotInline
         fun getAll(cameraId: String, quality: Int): EncoderProfiles? {
             return CamcorderProfile.getAll(cameraId, quality)
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
index 26002a9..c347ad8 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
@@ -18,6 +18,7 @@
 
 import android.annotation.SuppressLint
 import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraMetadata
 import android.util.Range
 import android.view.Surface
 import androidx.camera.camera2.pipe.UnsafeWrapper
@@ -139,12 +140,13 @@
 
     @OptIn(ExperimentalCamera2Interop::class)
     @Suppress("UNCHECKED_CAST")
-    override fun <T : Any> unwrapAs(type: KClass<T>): T? {
-        return when (type) {
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? =
+        when (type) {
             Camera2CameraInfo::class -> camera2CameraInfo as T
+            CameraProperties::class -> cameraProperties as T
+            CameraMetadata::class -> cameraProperties.metadata as T
             else -> cameraProperties.metadata.unwrapAs(type)
         }
-    }
 
     private fun getCameraSelectorLensFacing(lensFacingInt: Int): @CameraSelector.LensFacing Int {
         return when (lensFacingInt) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt
index 43e1835..6bd2e52 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt
@@ -22,6 +22,7 @@
 import androidx.camera.camera2.pipe.OutputStream
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.core.Log.debug
+import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter.Companion.getSessionConfig
 import androidx.camera.camera2.pipe.integration.impl.Camera2ImplConfig
 import androidx.camera.camera2.pipe.integration.impl.STREAM_USE_HINT_OPTION
 import androidx.camera.camera2.pipe.integration.internal.StreamUseCaseUtil
@@ -44,26 +45,27 @@
 class SessionConfigAdapter(
     private val useCases: Collection<UseCase>,
     private val sessionProcessorConfig: SessionConfig? = null,
+    private val isPrimary: Boolean = true,
 ) {
     val isSessionProcessorEnabled = sessionProcessorConfig != null
     val surfaceToStreamUseCaseMap: Map<DeferrableSurface, Long> by lazy {
         val sessionConfigs = mutableListOf<SessionConfig>()
         val useCaseConfigs = mutableListOf<UseCaseConfig<*>>()
         for (useCase in useCases) {
-            sessionConfigs.add(useCase.sessionConfig)
+            sessionConfigs.add(useCase.getSessionConfig(isPrimary))
             useCaseConfigs.add(useCase.currentConfig)
         }
         getSurfaceToStreamUseCaseMapping(sessionConfigs, useCaseConfigs)
     }
     val surfaceToStreamUseHintMap: Map<DeferrableSurface, Long> by lazy {
-        val sessionConfigs = useCases.map { it.sessionConfig }
+        val sessionConfigs = useCases.map { it.getSessionConfig(isPrimary) }
         getSurfaceToStreamUseHintMapping(sessionConfigs)
     }
     private val validatingBuilder: SessionConfig.ValidatingBuilder by lazy {
         val validatingBuilder = SessionConfig.ValidatingBuilder()
 
         for (useCase in useCases) {
-            validatingBuilder.add(useCase.sessionConfig)
+            validatingBuilder.add(useCase.getSessionConfig(isPrimary))
         }
 
         if (sessionProcessorConfig != null) {
@@ -102,7 +104,8 @@
         val sessionConfig =
             useCases
                 .firstOrNull { useCase ->
-                    useCase.sessionConfig.surfaces.contains(deferrableSurface)
+                    val sessionConfig = useCase.getSessionConfig(isPrimary)
+                    sessionConfig.surfaces.contains(deferrableSurface)
                 }
                 ?.sessionConfig
 
@@ -195,5 +198,9 @@
         fun SessionConfig.toCamera2ImplConfig(): Camera2ImplConfig {
             return Camera2ImplConfig(implementationOptions)
         }
+
+        fun UseCase.getSessionConfig(isPrimary: Boolean): SessionConfig {
+            return if (isPrimary) sessionConfig else secondarySessionConfig
+        }
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt
index 0c2c088..9c3d7bf 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.ImageFormat
 import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraDevice
 import android.hardware.camera2.params.InputConfiguration
 import android.hardware.camera2.params.StreamConfigurationMap
 import android.os.Build
@@ -136,15 +137,18 @@
         // regular capture request when taking pictures. So when user switches flash mode, we
         // could create reprocessing capture request if flash mode allows.
         if (isZslDisabledByUseCaseConfig) {
+            sessionConfigBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
             return
         }
 
         if (isZslDisabledByQuirks) {
+            sessionConfigBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
             return
         }
 
         if (!cameraMetadata.supportsPrivateReprocessing) {
             Log.info { "ZslControlImpl: Private reprocessing isn't supported" }
+            sessionConfigBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW)
             return
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ApiCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ApiCompat.kt
index 1ca65cb..5657c07 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ApiCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ApiCompat.kt
@@ -20,12 +20,10 @@
 import android.hardware.camera2.CaptureRequest
 import android.os.Build
 import android.view.Surface
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 @RequiresApi(Build.VERSION_CODES.N)
 internal object Api24Compat {
-    @DoNotInline
     @JvmStatic
     fun onCaptureBufferLost(
         callback: CameraCaptureSession.CaptureCallback,
@@ -40,7 +38,6 @@
 
 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 internal object Api34Compat {
-    @DoNotInline
     @JvmStatic
     fun onReadoutStarted(
         callback: CameraCaptureSession.CaptureCallback,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
index 9c3d743..5fffcd7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
@@ -71,12 +71,17 @@
         private val LEVEL_3_LEVEL_PRIV_PRIV_YUV_SUBSET_CONFIGURATION =
             createLevel3PrivPrivYuvSubsetConfiguration()
         private val SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_GOOGLE_MODELS: Set<String> =
-            setOf("PIXEL 6", "PIXEL 6 PRO", "PIXEL 7", "PIXEL 7 PRO")
+            setOf("PIXEL 6", "PIXEL 6 PRO", "PIXEL 7", "PIXEL 7 PRO", "PIXEL 8", "PIXEL 8 PRO")
 
         private val SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_SAMSUNG_MODELS: Set<String> =
             setOf(
-                "SM-S926B", // Galaxy S24+
-                "SM-S928U" // Galaxy S24 Ultra
+                "SM-S921", // Galaxy S24
+                "SC-51E", // Galaxy S24
+                "SCG25", // Galaxy S24
+                "SM-S926", // Galaxy S24+
+                "SM-S928", // Galaxy S24 Ultra
+                "SC-52E", // Galaxy S24 Ultra
+                "SCG26", // Galaxy S24 Ultra
             )
 
         fun isEnabled(): Boolean {
@@ -105,7 +110,13 @@
 
             val capitalModelName = Build.MODEL.uppercase()
 
-            return SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_SAMSUNG_MODELS.contains(capitalModelName)
+            // Check if the device model starts with the one of the predefined models
+            for (supportedModel in SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_SAMSUNG_MODELS) {
+                if (capitalModelName.startsWith(supportedModel)) {
+                    return true
+                }
+            }
+            return false
         }
 
         internal fun createFullYuvPrivYuvConfiguration(): SurfaceCombination {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt
index a57232b..9464cca 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt
@@ -20,7 +20,6 @@
 import android.hardware.camera2.CameraDevice
 import android.os.Build
 import android.view.Surface
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.core.impl.SessionConfig
@@ -147,7 +146,6 @@
 
         @RequiresApi(Build.VERSION_CODES.M)
         private object Api23CompatImpl {
-            @DoNotInline
             @JvmStatic
             fun onSurfacePrepared(
                 session: CameraCaptureSession,
@@ -162,7 +160,6 @@
 
         @RequiresApi(Build.VERSION_CODES.O)
         private object Api26CompatImpl {
-            @DoNotInline
             @JvmStatic
             fun onCaptureQueueEmpty(
                 session: CameraCaptureSession,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt
index 9ff8ec5..12911e0 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt
@@ -184,14 +184,14 @@
             // all beyond this point.
             val processorSessionConfig =
                 synchronized(lock) {
-                    if (isClosed()) return@launch configure(null)
+                    if (isClosed()) return@synchronized null
                     try {
                         val surfacesToIncrement = ArrayList(deferrableSurfaces)
                         postviewDeferrableSurface?.let { surfacesToIncrement.add(it) }
                         DeferrableSurfaces.incrementAll(surfacesToIncrement)
                     } catch (exception: DeferrableSurface.SurfaceClosedException) {
                         sessionConfigAdapter.reportSurfaceInvalid(exception.deferrableSurface)
-                        return@launch configure(null)
+                        return@synchronized null
                     }
                     try {
                         Log.debug { "Invoking $sessionProcessor SessionProcessor#initSession" }
@@ -212,7 +212,7 @@
                         postviewDeferrableSurface?.decrementUseCount()
                         throw throwable
                     }
-                }
+                } ?: return@launch configure(null)
 
             // DecrementAll the output surfaces when ProcessorSurface terminates.
             processorSessionConfig.surfaces
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
index e0cb40c..e60f9ba 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
@@ -51,6 +51,8 @@
     // UseCases
     var runningUseCases: Set<UseCase>
 
+    var isPrimary: Boolean
+
     interface RunningUseCasesChangeListener {
         /** Invoked when value of [UseCaseCamera.runningUseCases] has been changed. */
         fun onRunningUseCasesChanged()
@@ -101,7 +103,7 @@
 
             // Note: This may be called with the same set of values that was previously set. This
             // is used as a signal to indicate the properties of the UseCase may have changed.
-            SessionConfigAdapter(value).getValidSessionConfigOrNull()?.let {
+            SessionConfigAdapter(value, isPrimary = isPrimary).getValidSessionConfigOrNull()?.let {
                 requestControl.setSessionConfigAsync(it)
             }
                 ?: run {
@@ -118,6 +120,11 @@
             }
         }
 
+    override var isPrimary: Boolean = true
+        set(value) {
+            field = value
+        }
+
     init {
         debug { "Configured $this for $useCases" }
         useCaseGraphConfig.apply { cameraStateAdapter.onGraphUpdated(graph) }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index aaefadf..b832f7b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -153,6 +153,8 @@
 
     @GuardedBy("lock") private var pendingSessionProcessorInitialization = false
 
+    @GuardedBy("lock") private var isPrimary = true
+
     @GuardedBy("lock")
     private val pendingUseCasesToNotifyCameraControlReady = mutableSetOf<UseCase>()
 
@@ -314,6 +316,10 @@
             }
         }
 
+    fun setPrimary(isPrimary: Boolean) {
+        synchronized(lock) { this.isPrimary = isPrimary }
+    }
+
     fun setActiveResumeMode(enabled: Boolean) =
         synchronized(lock) {
             activeResumeEnabled = enabled
@@ -347,7 +353,10 @@
         when {
             shouldAddRepeatingUseCase(runningUseCases) -> addRepeatingUseCase()
             shouldRemoveRepeatingUseCase(runningUseCases) -> removeRepeatingUseCase()
-            else -> camera?.runningUseCases = runningUseCases
+            else -> {
+                camera?.isPrimary = isPrimary
+                camera?.runningUseCases = runningUseCases
+            }
         }
     }
 
@@ -436,7 +445,7 @@
                     }
             return
         } else {
-            val sessionConfigAdapter = SessionConfigAdapter(useCases)
+            val sessionConfigAdapter = SessionConfigAdapter(useCases, isPrimary = isPrimary)
             val streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
             val graphConfig = createCameraGraphConfig(sessionConfigAdapter, streamConfigMap)
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeConversions.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeConversions.kt
index b50116c..8f92ba0 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeConversions.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeConversions.kt
@@ -17,7 +17,6 @@
 package androidx.camera.camera2.pipe.integration.internal
 
 import android.hardware.camera2.params.DynamicRangeProfiles
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.camera.core.DynamicRange
 
@@ -77,7 +76,6 @@
     }
 
     /** Converts Camera2 dynamic range profile constants to [DynamicRange]. */
-    @DoNotInline
     fun profileToDynamicRange(profile: Long): DynamicRange? {
         return PROFILE_TO_DR_MAP[profile]
     }
@@ -93,7 +91,6 @@
      * returned by [DynamicRange.getEncoding] is [DynamicRange.ENCODING_HDR_UNSPECIFIED], this will
      * return `null`.
      */
-    @DoNotInline
     fun dynamicRangeToFirstSupportedProfile(
         dynamicRange: DynamicRange,
         dynamicRangeProfiles: DynamicRangeProfiles
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt
index db6bc9f..3e4e55a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt
@@ -2,7 +2,6 @@
 
 import android.hardware.camera2.CameraCharacteristics
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.core.Log
@@ -454,7 +453,6 @@
 
     @RequiresApi(33)
     internal object Api33Impl {
-        @DoNotInline
         fun getRecommended10BitDynamicRange(cameraMetadata: CameraMetadata): DynamicRange? {
             val recommendedProfile =
                 cameraMetadata[
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
index 74e5ee6..48bbef7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
@@ -18,13 +18,10 @@
 
 import android.hardware.camera2.CameraCharacteristics
 import androidx.annotation.RestrictTo
-import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter
-import androidx.camera.camera2.pipe.integration.adapter.PhysicalCameraInfoAdapter
+import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter.Companion.unwrapAs
 import androidx.camera.camera2.pipe.integration.compat.workaround.getSafely
 import androidx.camera.camera2.pipe.integration.impl.CameraProperties
 import androidx.camera.core.CameraInfo
-import androidx.camera.core.impl.CameraInfoInternal
-import androidx.core.util.Preconditions
 
 /** An interface for retrieving Camera2-related camera information. */
 @ExperimentalCamera2Interop
@@ -77,19 +74,12 @@
          *   [androidx.camera.camera2.Camera2Config]).
          */
         @JvmStatic
-        fun from(@Suppress("UNUSED_PARAMETER") cameraInfo: CameraInfo): Camera2CameraInfo {
-            // Physical camera
-            if (cameraInfo is PhysicalCameraInfoAdapter) {
-                return cameraInfo.unwrapAs(Camera2CameraInfo::class)!!
+        fun from(cameraInfo: CameraInfo): Camera2CameraInfo {
+            val camera2CameraInfo = cameraInfo.unwrapAs(Camera2CameraInfo::class)
+            requireNotNull(camera2CameraInfo) {
+                "Could not unwrap $cameraInfo as Camera2CameraInfo!"
             }
-
-            // Logical camera
-            var cameraInfoImpl = (cameraInfo as CameraInfoInternal).implementation
-            Preconditions.checkArgument(
-                cameraInfoImpl is CameraInfoAdapter,
-                "CameraInfo doesn't contain Camera2 implementation."
-            )
-            return (cameraInfoImpl as CameraInfoAdapter).camera2CameraInfo
+            return camera2CameraInfo
         }
 
         /** This is the workaround to prevent constructor from being added to public API. */
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
index 08d4951..fe7acac 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
@@ -32,6 +32,7 @@
 import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE
 import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
 import androidx.camera.core.impl.CameraInfoInternal
+import androidx.camera.testing.fakes.FakeCameraInfoInternal
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -51,7 +52,6 @@
 @DoNotInstrument
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class CameraCoordinatorAdapterTest {
-
     private val cameraMetadata0 =
         FakeCameraMetadata(
             cameraId = CameraId("0"),
@@ -74,6 +74,7 @@
                         ),
                 )
         )
+
     private val cameraMetadata2 = FakeCameraMetadata(cameraId = CameraId("2"))
 
     private val cameraDevices =
@@ -128,6 +129,13 @@
         // REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE will be filtered.
         ReflectionHelpers.setStaticField(Build::class.java, "FINGERPRINT", "fake-fingerprint")
         cameraCoordinatorAdapter = CameraCoordinatorAdapter(cameraPipe, cameraDevices)
+
+        whenever(mockCameraInternalAdapter0.cameraInfoInternal)
+            .thenReturn(FakeCameraInfoInternal("0"))
+        whenever(mockCameraInternalAdapter1.cameraInfoInternal)
+            .thenReturn(FakeCameraInfoInternal("1"))
+        whenever(mockCameraInternalAdapter2.cameraInfoInternal)
+            .thenReturn(FakeCameraInfoInternal("2"))
         cameraCoordinatorAdapter.registerCamera("0", mockCameraInternalAdapter0)
         cameraCoordinatorAdapter.registerCamera("1", mockCameraInternalAdapter1)
         cameraCoordinatorAdapter.registerCamera("2", mockCameraInternalAdapter2)
@@ -187,6 +195,26 @@
     }
 
     @Test
+    fun getPairedConcurrentCameraId_IgnoresCameraAdaptersWithoutGraphConfig() {
+        whenever(mockCameraInternalAdapter0.getDeferredCameraGraphConfig())
+            .thenReturn(mockCameraGraphConfig0)
+        whenever(mockCameraInternalAdapter1.getDeferredCameraGraphConfig())
+            .thenReturn(mockCameraGraphConfig1)
+        // When one of the adapters doesn't have a graph config, it is filtered and ignored.
+        whenever(mockCameraInternalAdapter2.getDeferredCameraGraphConfig()).thenReturn(null)
+
+        assertThat(cameraCoordinatorAdapter.getPairedConcurrentCameraId("0")).isNull()
+
+        cameraCoordinatorAdapter.activeConcurrentCameraInfos =
+            mutableListOf(
+                FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("0")),
+                FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("1"))
+            )
+
+        assertThat(cameraCoordinatorAdapter.getPairedConcurrentCameraId("0")).isEqualTo("1")
+    }
+
+    @Test
     fun setAndGetCameraOperatingMode() {
         cameraCoordinatorAdapter.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
 
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
index d212ce9..af16f8f 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
@@ -21,13 +21,14 @@
 import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_OFF
 import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_ON
 import android.hardware.camera2.CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
-import android.hardware.camera2.CameraMetadata
 import android.os.Build
 import android.util.Range
 import android.util.Size
 import android.util.SizeF
 import androidx.camera.camera2.pipe.CameraBackendId
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter.Companion.unwrapAs
 import androidx.camera.camera2.pipe.integration.impl.ZoomControl
 import androidx.camera.camera2.pipe.integration.internal.DOLBY_VISION_10B_UNCONSTRAINED
 import androidx.camera.camera2.pipe.integration.internal.HLG10_UNCONSTRAINED
@@ -52,6 +53,8 @@
 import androidx.camera.core.ZoomState
 import androidx.camera.core.impl.CameraInfoInternal
 import androidx.camera.core.impl.ImageFormatConstants
+import androidx.camera.core.impl.RestrictedCameraInfo
+import androidx.camera.testing.impl.fakes.FakeCameraConfig
 import androidx.testutils.MainDispatcherRule
 import androidx.testutils.assertThrows
 import com.google.common.truth.Truth.assertThat
@@ -79,7 +82,7 @@
     private val defaultCameraId = "0"
     private val defaultCameraCharacteristics =
         mapOf(
-            CameraCharacteristics.LENS_FACING to CameraMetadata.LENS_FACING_BACK,
+            CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_BACK,
             CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS to floatArrayOf(1.0f),
             CameraCharacteristics.SENSOR_ORIENTATION to 0,
             CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE to Size(10, 10),
@@ -713,4 +716,13 @@
 
         assertThat(cameraInfo.intrinsicZoomRatio).isEqualTo(CameraInfo.INTRINSIC_ZOOM_RATIO_UNKNOWN)
     }
+
+    @Test
+    fun canUnwrapRestrictedCameraInfoAsCameraMetadata() {
+        val fakeCameraConfig = FakeCameraConfig()
+        val restrictedCameraInfo = RestrictedCameraInfo(cameraInfoAdapter, fakeCameraConfig)
+
+        val cameraMetadata = restrictedCameraInfo.unwrapAs(CameraMetadata::class)
+        assertThat(cameraMetadata).isNotNull()
+    }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
index b5bcfe9..fce6cf6 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/FocusMeteringControlTest.kt
@@ -1552,6 +1552,11 @@
             override val requestControl: UseCaseCameraRequestControl
                 get() = fakeRequestControl
 
+            override var isPrimary: Boolean = true
+                set(value) {
+                    field = value
+                }
+
             override fun <T> setParameterAsync(
                 key: CaptureRequest.Key<T>,
                 value: T,
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapterTest.kt
index 80f3758..a933a0a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapterTest.kt
@@ -111,7 +111,7 @@
     }
 
     private fun initialize(scope: TestScope) {
-        cameraGraphSimulator =
+        val simulator =
             CameraGraphSimulator.create(
                     scope,
                     context,
@@ -119,24 +119,24 @@
                     graphConfig,
                 )
                 .also {
-                    it.cameraGraph.start()
+                    it.start()
                     it.simulateCameraStarted()
-                    it.simulateFakeSurfaceConfiguration()
+                    it.initializeSurfaces()
                 }
-        val cameraGraph = cameraGraphSimulator!!.cameraGraph
+        cameraGraphSimulator = simulator
         val surfaceToStreamMap =
             buildMap<DeferrableSurface, StreamId> {
                 put(
                     previewProcessorSurface,
-                    checkNotNull(cameraGraph.streams[previewStreamConfig]).id
+                    checkNotNull(simulator.streams[previewStreamConfig]).id
                 )
                 put(
                     imageCaptureProcessorSurface,
-                    checkNotNull(cameraGraph.streams[imageCaptureStreamConfig]).id
+                    checkNotNull(simulator.streams[imageCaptureStreamConfig]).id
                 )
             }
         val useCaseGraphConfig =
-            UseCaseGraphConfig(cameraGraph, surfaceToStreamMap, CameraStateAdapter())
+            UseCaseGraphConfig(simulator, surfaceToStreamMap, CameraStateAdapter())
 
         requestProcessorAdapter =
             RequestProcessorAdapter(
@@ -172,9 +172,7 @@
         val request = frame.request
         assertThat(request.streams.size).isEqualTo(1)
         assertThat(request.streams.first())
-            .isEqualTo(
-                checkNotNull(cameraGraphSimulator!!.cameraGraph.streams[previewStreamConfig]).id
-            )
+            .isEqualTo(checkNotNull(cameraGraphSimulator!!.streams[previewStreamConfig]).id)
 
         verify(callback, times(1)).onCaptureStarted(eq(requestToSet), any(), any())
 
@@ -215,10 +213,7 @@
         val request = frame.request
         assertThat(request.streams.size).isEqualTo(1)
         assertThat(request.streams.first())
-            .isEqualTo(
-                checkNotNull(cameraGraphSimulator!!.cameraGraph.streams[imageCaptureStreamConfig])
-                    .id
-            )
+            .isEqualTo(checkNotNull(cameraGraphSimulator!!.streams[imageCaptureStreamConfig]).id)
         assertThat(request.parameters[CaptureRequest.CONTROL_AE_MODE])
             .isEqualTo(CONTROL_AE_MODE_OFF)
 
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/ZslControlTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/ZslControlTest.kt
index 928d5b0..0ffca9b 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/ZslControlTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/ZslControlTest.kt
@@ -19,6 +19,8 @@
 import android.graphics.ImageFormat
 import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
+import android.hardware.camera2.CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
 import android.hardware.camera2.params.StreamConfigurationMap
 import android.os.Build
 import android.util.Size
@@ -77,6 +79,7 @@
         assertThat(zslControlImpl.reprocessingImageReader!!.height)
             .isEqualTo(PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
         assertThat(zslControlImpl.zslRingBuffer.maxCapacity).isEqualTo(RING_BUFFER_CAPACITY)
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_ZERO_SHUTTER_LAG)
     }
 
     @Test
@@ -94,6 +97,7 @@
         zslControlImpl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControlImpl.reprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -111,6 +115,7 @@
         zslControlImpl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControlImpl.reprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -128,6 +133,7 @@
         zslControlImpl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControlImpl.reprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -146,6 +152,7 @@
         zslControlImpl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControlImpl.reprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -172,6 +179,7 @@
         assertThat(zslControlImpl.reprocessingImageReader!!.height)
             .isEqualTo(PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
         assertThat(zslControlImpl.zslRingBuffer.maxCapacity).isEqualTo(RING_BUFFER_CAPACITY)
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_ZERO_SHUTTER_LAG)
     }
 
     @Test
@@ -192,6 +200,7 @@
         zslControlImpl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControlImpl.reprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -212,6 +221,7 @@
         zslControlImpl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControlImpl.reprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -240,6 +250,7 @@
         assertThat(zslControlImpl.reprocessingImageReader!!.height)
             .isEqualTo(PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
         assertThat(zslControlImpl.zslRingBuffer.maxCapacity).isEqualTo(RING_BUFFER_CAPACITY)
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_ZERO_SHUTTER_LAG)
     }
 
     private fun createCameraProperties(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirkTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirkTest.kt
index ac3edb6..c3a0808 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirkTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirkTest.kt
@@ -133,7 +133,7 @@
                 Config(
                     "Google",
                     null,
-                    "Pixel 6 Pro",
+                    "Pixel 7",
                     "1",
                     createLevel3PrivPrivYuvSubsetConfiguration()
                 ),
@@ -151,32 +151,68 @@
                     "1",
                     createLevel3PrivPrivYuvSubsetConfiguration()
                 ),
+                Config(
+                    "Google",
+                    null,
+                    "Pixel 8",
+                    "0",
+                    createLevel3PrivPrivYuvSubsetConfiguration()
+                ),
+                Config(
+                    "Google",
+                    null,
+                    "Pixel 8",
+                    "1",
+                    createLevel3PrivPrivYuvSubsetConfiguration()
+                ),
+                Config(
+                    "Google",
+                    null,
+                    "Pixel 8 Pro",
+                    "0",
+                    createLevel3PrivPrivYuvSubsetConfiguration()
+                ),
+                Config(
+                    "Google",
+                    null,
+                    "Pixel 8 Pro",
+                    "1",
+                    createLevel3PrivPrivYuvSubsetConfiguration()
+                ),
                 // Tests for FULL Samsung devices
+                Config("Samsung", null, "SCG25", "0", createLevel3PrivPrivYuvSubsetConfiguration()),
                 Config(
                     "Samsung",
                     null,
-                    "SM-S926B",
-                    "0",
-                    createLevel3PrivPrivYuvSubsetConfiguration()
-                ),
-                Config(
-                    "Samsung",
-                    null,
-                    "SM-S926B",
+                    "SM-S9210",
                     "1",
                     createLevel3PrivPrivYuvSubsetConfiguration()
                 ),
                 Config(
                     "Samsung",
                     null,
-                    "SM-S928U",
+                    "SM-S926B",
                     "0",
                     createLevel3PrivPrivYuvSubsetConfiguration()
                 ),
                 Config(
                     "Samsung",
                     null,
-                    "SM-S928U",
+                    "SM-S926U",
+                    "1",
+                    createLevel3PrivPrivYuvSubsetConfiguration()
+                ),
+                Config(
+                    "Samsung",
+                    null,
+                    "SM-S928U1",
+                    "0",
+                    createLevel3PrivPrivYuvSubsetConfiguration()
+                ),
+                Config(
+                    "Samsung",
+                    null,
+                    "SM-S928B",
                     "1",
                     createLevel3PrivPrivYuvSubsetConfiguration()
                 ),
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt
index 451db585..60b565d 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraph.kt
@@ -20,6 +20,7 @@
 import androidx.camera.camera2.pipe.AudioRestrictionMode
 import androidx.camera.camera2.pipe.AudioRestrictionMode.Companion.AUDIO_RESTRICTION_NONE
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.GraphState
 import androidx.camera.camera2.pipe.StreamGraph
 import androidx.camera.camera2.pipe.StreamId
@@ -36,6 +37,8 @@
 
     val setSurfaceResults = mutableMapOf<StreamId, Surface?>()
     private var isClosed = false
+    override val id: CameraGraphId
+        get() = throw NotImplementedError("Not used in testing")
 
     override val streams: StreamGraph
         get() = throw NotImplementedError("Not used in testing")
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
index c7eb629..312045f 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
@@ -237,6 +237,11 @@
     override var requestControl: UseCaseCameraRequestControl = FakeUseCaseCameraRequestControl(),
 ) : UseCaseCamera {
 
+    override var isPrimary: Boolean = true
+        set(value) {
+            field = value
+        }
+
     override fun <T> setParameterAsync(
         key: CaptureRequest.Key<T>,
         value: T,
diff --git a/camera/camera-camera2-pipe-testing/build.gradle b/camera/camera-camera2-pipe-testing/build.gradle
index 8c265a6..6ca4bf4 100644
--- a/camera/camera-camera2-pipe-testing/build.gradle
+++ b/camera/camera-camera2-pipe-testing/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     // Classes and types that are needed at compile & runtime
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     // Classes and types that are only needed at runtime
     implementation(libs.atomicFu)
@@ -73,7 +73,6 @@
     description = "Testing components for the Camera2 Pipe Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "Not shipped externally"
 }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
index c23f556..3450164 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
@@ -20,6 +20,7 @@
 import androidx.camera.camera2.pipe.CameraContext
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraStatusMonitor
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
@@ -41,13 +42,17 @@
  */
 class CameraControllerSimulator(
     cameraContext: CameraContext,
+    private val graphId: CameraGraphId,
     private val graphConfig: CameraGraph.Config,
     private val graphListener: GraphListener,
-    private val streamGraph: StreamGraph
+    private val streamGraph: StreamGraph,
 ) : CameraController {
     override val cameraId: CameraId
         get() = graphConfig.camera
 
+    override val cameraGraphId: CameraGraphId
+        get() = graphId
+
     override var isForeground = false
 
     private val lock = Any()
@@ -68,12 +73,8 @@
             _started = value
         }
 
-    private var _currentCaptureSequenceProcessor: FakeCaptureSequenceProcessor? = null
-    var currentCaptureSequenceProcessor: FakeCaptureSequenceProcessor?
-        get() = _currentCaptureSequenceProcessor
-        private set(value) {
-            _currentCaptureSequenceProcessor = value
-        }
+    var currentCaptureSequenceProcessor: FakeCaptureSequenceProcessor? = null
+        private set
 
     init {
         check(cameraContext.cameraBackends.allIds.isNotEmpty()) {
@@ -97,6 +98,7 @@
             val captureSequenceProcessor =
                 FakeCaptureSequenceProcessor(graphConfig.camera, graphConfig.defaultTemplate)
             val graphRequestProcessor = GraphRequestProcessor.from(captureSequenceProcessor)
+            captureSequenceProcessor.surfaceMap = currentSurfaceMap
             currentCaptureSequenceProcessor = captureSequenceProcessor
             currentGraphRequestProcessor = graphRequestProcessor
 
@@ -109,7 +111,7 @@
             check(!closed) {
                 "Attempted to invoke simulateCameraStopped after the CameraController was closed."
             }
-            val captureSequenceProcessor = _currentCaptureSequenceProcessor
+            val captureSequenceProcessor = currentCaptureSequenceProcessor
             val graphRequestProcessor = currentGraphRequestProcessor
 
             currentCaptureSequenceProcessor = null
@@ -123,7 +125,7 @@
 
     fun simulateCameraModified() {
         synchronized(lock) {
-            val captureSequenceProcessor = _currentCaptureSequenceProcessor
+            val captureSequenceProcessor = currentCaptureSequenceProcessor
             val graphRequestProcessor = currentGraphRequestProcessor
 
             currentCaptureSequenceProcessor = null
@@ -179,7 +181,7 @@
         synchronized(lock) {
             currentSurfaceMap = surfaceMap
 
-            val captureSequenceProcessor = _currentCaptureSequenceProcessor
+            val captureSequenceProcessor = currentCaptureSequenceProcessor
             val graphRequestProcessor = currentGraphRequestProcessor
             if (captureSequenceProcessor != null && graphRequestProcessor != null) {
                 captureSequenceProcessor.surfaceMap = surfaceMap
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
index 7e4fea2..7aa12ed 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
@@ -18,22 +18,23 @@
 
 import android.content.Context
 import android.hardware.camera2.CaptureResult
+import android.media.ImageReader
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.CameraPipe
-import androidx.camera.camera2.pipe.CameraPipe.CameraBackendConfig
 import androidx.camera.camera2.pipe.CameraTimestamp
 import androidx.camera.camera2.pipe.CaptureSequences.invokeOnRequest
 import androidx.camera.camera2.pipe.FrameMetadata
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.Metadata
+import androidx.camera.camera2.pipe.OutputId
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.media.ImageSource
 import kotlinx.atomicfu.atomic
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.withTimeout
 
@@ -48,19 +49,25 @@
  * The simulator does not make (many) assumptions about how the simulator will be used, and for this
  * reason it does not automatically put the underlying graph into a "started" state. In most cases,
  * the test will need start the [CameraGraph], [simulateCameraStarted], and either configure
- * surfaces for the [CameraGraph] or call [simulateFakeSurfaceConfiguration] to put the graph into a
- * state where it is able to send and simulate interactions with the camera. This mirrors the normal
- * lifecycle of a [CameraGraph]. Tests using CameraGraphSimulators should also close them after
- * they've completed their use of the simulator.
+ * surfaces for the [CameraGraph] or call [initializeSurfaces] to put the graph into a state where
+ * it is able to send and simulate interactions with the camera. This mirrors the normal lifecycle
+ * of a [CameraGraph]. Tests using CameraGraphSimulators should also close them after they've
+ * completed their use of the simulator.
  */
 class CameraGraphSimulator
-private constructor(
-    val context: Context,
-    val cameraMetadata: CameraMetadata,
-    val graphConfig: CameraGraph.Config,
-    val cameraGraph: CameraGraph,
-    private val cameraController: CameraControllerSimulator
-) : AutoCloseable {
+internal constructor(
+    private val cameraMetadata: CameraMetadata,
+    private val cameraController: CameraControllerSimulator,
+    private val fakeImageReaders: FakeImageReaders,
+    private val fakeImageSources: FakeImageSources,
+    private val realCameraGraph: CameraGraph,
+    val config: CameraGraph.Config,
+) : CameraGraph by realCameraGraph, AutoCloseable {
+
+    @Deprecated("CameraGraphSimulator directly implements CameraGraph")
+    val cameraGraph: CameraGraph
+        get() = this
+
     companion object {
         /**
          * Create a CameraGraphSimulator using the current [TestScope] provided by a Kotlin
@@ -70,46 +77,21 @@
          * interactions.
          */
         fun create(
-            scope: TestScope,
-            context: Context,
+            testScope: TestScope,
+            testContext: Context,
             cameraMetadata: CameraMetadata,
             graphConfig: CameraGraph.Config
         ): CameraGraphSimulator {
-            val fakeCameraBackend =
-                FakeCameraBackend(fakeCameras = mapOf(cameraMetadata.camera to cameraMetadata))
-            val cameraPipe =
-                CameraPipe(
-                    CameraPipe.Config(
-                        context,
-                        cameraBackendConfig =
-                            CameraBackendConfig(internalBackend = fakeCameraBackend),
-                        threadConfig =
-                            CameraPipe.ThreadConfig(
-                                testOnlyDispatcher = StandardTestDispatcher(scope.testScheduler),
-                                testOnlyScope = scope,
-                            )
-                    )
-                )
-            val cameraGraph = cameraPipe.create(graphConfig)
-            val cameraController =
-                checkNotNull(fakeCameraBackend.cameraControllers.lastOrNull()) {
-                    "Expected cameraPipe.create to create a CameraController instance from " +
-                        "$fakeCameraBackend as part of its initialization."
-                }
-            return CameraGraphSimulator(
-                context,
-                cameraMetadata,
-                graphConfig,
-                cameraGraph,
-                cameraController
-            )
+            val cameraPipeSimulator =
+                CameraPipeSimulator.create(testScope, testContext, listOf(cameraMetadata))
+            return cameraPipeSimulator.createCameraGraphSimulator(graphConfig)
         }
     }
 
     init {
-        check(graphConfig.camera == cameraMetadata.camera) {
+        check(config.camera == cameraMetadata.camera) {
             "CameraGraphSimulator must be creating with a camera id that matches the provided " +
-                "cameraMetadata! Received ${graphConfig.camera}, but expected " +
+                "cameraMetadata! Received ${config.camera}, but expected " +
                 "${cameraMetadata.camera}"
         }
     }
@@ -121,9 +103,13 @@
     private val pendingFrameQueue = mutableListOf<FrameSimulator>()
     private val fakeSurfaces = FakeSurfaces()
 
+    /** Return true if this [CameraGraphSimulator] has been closed. */
+    val isClosed: Boolean
+        get() = closed.value
+
     override fun close() {
         if (closed.compareAndSet(expect = false, update = true)) {
-            cameraGraph.close()
+            realCameraGraph.close()
             fakeSurfaces.close()
         }
     }
@@ -152,15 +138,30 @@
      * Configure all streams in the CameraGraph with fake surfaces that match the size of the first
      * output stream.
      */
-    fun simulateFakeSurfaceConfiguration() {
+    fun initializeSurfaces() {
         check(!closed.value) {
             "Cannot call simulateFakeSurfaceConfiguration on $this after close."
         }
-        for (stream in cameraGraph.streams.streams) {
-            // Pick an output -- most will only have one.
-            val output = stream.outputs.first()
-            val surface = fakeSurfaces.createFakeSurface(output.size)
-            cameraGraph.setSurface(stream.id, surface)
+        for (stream in streams.streams) {
+            val imageSource = fakeImageSources[stream.id]
+            if (imageSource != null) {
+                println("Using FakeImageSource ${imageSource.surface} for ${stream.id}")
+                continue
+            }
+
+            val imageReader = fakeImageReaders[stream.id]
+            if (imageReader != null) {
+                println("Using FakeImageReader ${imageReader.surface} for ${stream.id}")
+                realCameraGraph.setSurface(stream.id, imageReader.surface)
+                continue
+            }
+
+            // Pick the smallest output (This matches the behavior of MultiResolutionImageReader)
+            val minOutput = stream.outputs.minBy { it.size.width * it.size.height }
+            val surface = fakeSurfaces.createFakeSurface(minOutput.size)
+
+            println("Using Fake $surface for ${stream.id}")
+            realCameraGraph.setSurface(stream.id, surface)
         }
     }
 
@@ -193,6 +194,69 @@
         return pendingFrameQueue.removeFirst()
     }
 
+    /** Utility function to simulate the production of a [FakeImage]s for one or more streams. */
+    fun simulateImage(
+        streamId: StreamId,
+        imageTimestamp: Long,
+        outputId: OutputId? = null,
+    ) {
+        check(simulateImageInternal(streamId, outputId, imageTimestamp)) {
+            "Failed to simulate image for $streamId on $this!"
+        }
+    }
+
+    /**
+     * Utility function to simulate the production of [FakeImage]s for all outputs on a specific
+     * [request]. Use [simulateImage] to directly control simulation of individual outputs.
+     * [physicalCameraId] should be used to select the correct output id when simulating images from
+     * multi-resolution [ImageReader]s and [ImageSource]s
+     */
+    fun simulateImages(request: Request, imageTimestamp: Long, physicalCameraId: CameraId? = null) {
+        var imageSimulated = false
+        for (streamId in request.streams) {
+            val outputId =
+                if (physicalCameraId == null) {
+                    streams.outputs.single().id
+                } else {
+                    streams[streamId]?.outputs?.find { it.camera == physicalCameraId }?.id
+                }
+            val success = simulateImageInternal(streamId, outputId, imageTimestamp)
+            imageSimulated = imageSimulated || success
+        }
+
+        check(imageSimulated) {
+            "Failed to simulate images for $request!" +
+                "No matching FakeImageReaders or FakeImageSources were found."
+        }
+    }
+
+    private fun simulateImageInternal(
+        streamId: StreamId,
+        outputId: OutputId?,
+        imageTimestamp: Long
+    ): Boolean {
+        val stream = streams[streamId]
+        checkNotNull(stream) { "Cannot simulate an image for invalid $streamId on $this!" }
+        // Prefer to simulate images directly on the imageReader if possible, and then
+        // defer to the imageSource if an imageReader does not exist.
+        val imageReader = fakeImageReaders[streamId]
+        if (imageReader != null) {
+            imageReader.simulateImage(imageTimestamp = imageTimestamp, outputId = outputId)
+            return true
+        } else {
+            val fakeImageSource = fakeImageSources[streamId]
+            if (fakeImageSource != null) {
+                fakeImageSource.simulateImage(timestamp = imageTimestamp, outputId = outputId)
+                return true
+            }
+        }
+        return false
+    }
+
+    override fun toString(): String {
+        return "CameraGraphSimulator($realCameraGraph)"
+    }
+
     /**
      * A [FrameSimulator] allows a test to synchronously invoke callbacks. A single request can
      * generate multiple captures (eg, if used as a repeating request). A [FrameSimulator] allows a
@@ -299,6 +363,32 @@
             requestSequence.invokeOnRequest(requestMetadata) { it.onAborted(request) }
         }
 
+        fun simulateImage(
+            streamId: StreamId,
+            imageTimestamp: Long? = null,
+            outputId: OutputId? = null,
+        ) {
+            val timestamp = imageTimestamp ?: timestampNanos
+            checkNotNull(timestamp) {
+                "Cannot simulate an image without a timestamp! Provide an " +
+                    "imageTimestamp or call simulateStarted before simulateImage."
+            }
+            [email protected](streamId, timestamp, outputId)
+        }
+
+        /**
+         * Utility function to simulate the production of [FakeImage]s for all outputs on a Frame.
+         * Use [simulateImage] to directly control simulation of each individual image.
+         */
+        fun simulateImages(imageTimestamp: Long? = null, physicalCameraId: CameraId? = null) {
+            val timestamp = imageTimestamp ?: timestampNanos
+            checkNotNull(timestamp) {
+                "Cannot simulate an image without a timestamp! Provide an " +
+                    "imageTimestamp or call simulateStarted before simulateImage."
+            }
+            [email protected](request, timestamp, physicalCameraId)
+        }
+
         private fun createFakePhysicalMetadata(
             physicalResultMetadata: Map<CameraId, Map<CaptureResult.Key<*>, Any?>>
         ): Map<CameraId, FrameMetadata> {
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulator.kt
new file mode 100644
index 0000000..89c2da0
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulator.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.testing
+
+import android.content.Context
+import androidx.camera.camera2.pipe.AudioRestrictionMode
+import androidx.camera.camera2.pipe.CameraDevices
+import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.CameraPipe
+import androidx.camera.camera2.pipe.CameraPipe.CameraBackendConfig
+import androidx.camera.camera2.pipe.CameraSurfaceManager
+import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+
+/**
+ * This class enables a developer to simulate interactions with [CameraPipe].
+ *
+ * This simulator is a realistic fake of a real CameraPipe object with methods that enable a
+ * developer to query and interact with the simulated camera subsystem(s). This is primarily used to
+ * test complicated interactions with [CameraPipe] and [CameraGraph] and to simulate how code
+ * responds to a range of behaviors by the underlying camera within unit tests.
+ */
+class CameraPipeSimulator
+private constructor(
+    private val cameraPipeInternal: CameraPipe,
+    private val fakeCameraBackend: FakeCameraBackend,
+    val fakeSurfaces: FakeSurfaces,
+    val fakeImageReaders: FakeImageReaders,
+    val fakeImageSources: FakeImageSources,
+) : CameraPipe, AutoCloseable {
+    private val closed = atomic(false)
+    private val _cameraGraphs = mutableListOf<CameraGraphSimulator>()
+    val cameraGraphs: List<CameraGraphSimulator>
+        get() = _cameraGraphs
+
+    override fun create(config: CameraGraph.Config): CameraGraphSimulator {
+        check(!closed.value) { "Cannot interact with CameraPipeSimulator after close!" }
+
+        val cameraGraph = cameraPipeInternal.create(config)
+        val fakeCameraController =
+            checkNotNull(fakeCameraBackend.cameraControllers.lastOrNull()) {
+                "Expected cameraPipe.create to create a CameraController instance from " +
+                    "$fakeCameraBackend as part of its initialization."
+            }
+        val cameraMetadata = cameraPipeInternal.cameras().awaitCameraMetadata(config.camera)!!
+        val cameraGraphSimulator =
+            CameraGraphSimulator(
+                cameraMetadata,
+                fakeCameraController,
+                fakeImageReaders,
+                fakeImageSources,
+                cameraGraph,
+                config,
+            )
+        _cameraGraphs.add(cameraGraphSimulator)
+        return cameraGraphSimulator
+    }
+
+    override fun createCameraGraphs(
+        config: CameraGraph.ConcurrentConfig
+    ): List<CameraGraphSimulator> {
+        check(!closed.value) { "Cannot interact with CameraPipeSimulator after close!" }
+        return config.graphConfigs.map { create(it) }
+    }
+
+    override fun cameras(): CameraDevices = cameraPipeInternal.cameras()
+
+    override fun cameraSurfaceManager(): CameraSurfaceManager =
+        cameraPipeInternal.cameraSurfaceManager()
+
+    override var globalAudioRestrictionMode: AudioRestrictionMode
+        get() = cameraPipeInternal.globalAudioRestrictionMode
+        set(value) {
+            cameraPipeInternal.globalAudioRestrictionMode = value
+        }
+
+    /** Directly create and return a new [CameraGraph] and [CameraGraphSimulator]. */
+    fun createCameraGraphSimulator(graphConfig: CameraGraph.Config): CameraGraphSimulator {
+        check(!closed.value) { "Cannot interact with CameraPipeSimulator after close!" }
+        val cameraGraph = cameraPipeInternal.create(graphConfig)
+        val cameraController =
+            fakeCameraBackend.cameraControllers.first { it.cameraGraphId == cameraGraph.id }
+        val cameraGraphSimulator =
+            createCameraGraphSimulator(cameraGraph, graphConfig, cameraController)
+        _cameraGraphs.add(cameraGraphSimulator)
+        return cameraGraphSimulator
+    }
+
+    /** Directly create and return a new set of [CameraGraph]s and [CameraGraphSimulator]s. */
+    fun createCameraGraphSimulators(
+        config: CameraGraph.ConcurrentConfig
+    ): List<CameraGraphSimulator> = config.graphConfigs.map { createCameraGraphSimulator(it) }
+
+    private fun createCameraGraphSimulator(
+        graph: CameraGraph,
+        graphConfig: CameraGraph.Config,
+        cameraController: CameraControllerSimulator
+    ): CameraGraphSimulator {
+        check(!closed.value) { "Cannot interact with CameraPipeSimulator after close!" }
+        val cameraId = cameraController.cameraId
+        val cameraMetadata = fakeCameraBackend.awaitCameraMetadata(cameraController.cameraId)
+        checkNotNull(cameraMetadata) { "Failed to retrieve metadata for $cameraId!" }
+
+        val cameraGraphSimulator =
+            CameraGraphSimulator(
+                cameraMetadata,
+                cameraController,
+                fakeImageReaders,
+                fakeImageSources,
+                graph,
+                graphConfig,
+            )
+        return cameraGraphSimulator
+    }
+
+    fun checkImageReadersClosed() {
+        fakeImageSources.checkImageSourcesClosed()
+        fakeImageReaders.checkImageReadersClosed()
+    }
+
+    fun checkImagesClosed() {
+        fakeImageSources.checkImagesClosed()
+        fakeImageReaders.checkImagesClosed()
+    }
+
+    fun checkCameraGraphsClosed() {
+        for (cameraGraph in _cameraGraphs) {
+            check(cameraGraph.isClosed) { "$cameraGraph was not closed!" }
+        }
+    }
+
+    override fun close() {
+        if (closed.compareAndSet(expect = false, update = true)) {
+            fakeSurfaces.close()
+        }
+    }
+
+    override fun toString(): String {
+        return "CameraPipeSimulator($cameraPipeInternal)"
+    }
+
+    companion object {
+        fun create(
+            testScope: TestScope,
+            testContext: Context,
+            fakeCameras: List<CameraMetadata> = listOf(FakeCameraMetadata())
+        ): CameraPipeSimulator {
+            val fakeCameraBackend =
+                FakeCameraBackend(fakeCameras = fakeCameras.associateBy { it.camera })
+
+            val testScopeDispatcher =
+                StandardTestDispatcher(testScope.testScheduler, "CXCP-TestScope")
+            val testScopeThreadConfig =
+                CameraPipe.ThreadConfig(
+                    testOnlyDispatcher = testScopeDispatcher,
+                    testOnlyScope = testScope,
+                )
+
+            val fakeSurfaces = FakeSurfaces()
+            val fakeImageReaders = FakeImageReaders(fakeSurfaces)
+            val fakeImageSources = FakeImageSources(fakeImageReaders)
+
+            val cameraPipe =
+                CameraPipe(
+                    CameraPipe.Config(
+                        testContext,
+                        cameraBackendConfig =
+                            CameraBackendConfig(internalBackend = fakeCameraBackend),
+                        threadConfig = testScopeThreadConfig,
+                        imageSources = fakeImageSources
+                    )
+                )
+            return CameraPipeSimulator(
+                cameraPipe,
+                fakeCameraBackend,
+                fakeSurfaces,
+                fakeImageReaders,
+                fakeImageSources
+            )
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
index f800e66..c5f8d26 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
@@ -21,6 +21,7 @@
 import androidx.camera.camera2.pipe.CameraContext
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.CameraStatusMonitor
@@ -64,12 +65,19 @@
 
     override fun createCameraController(
         cameraContext: CameraContext,
+        graphId: CameraGraphId,
         graphConfig: CameraGraph.Config,
         graphListener: GraphListener,
         streamGraph: StreamGraph
     ): CameraController {
         val cameraController =
-            CameraControllerSimulator(cameraContext, graphConfig, graphListener, streamGraph)
+            CameraControllerSimulator(
+                cameraContext,
+                graphId,
+                graphConfig,
+                graphListener,
+                streamGraph
+            )
         synchronized(lock) { _cameraControllers.add(cameraController) }
         return cameraController
     }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt
index 76b0c9f..2647ab5 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt
@@ -66,7 +66,11 @@
     private var _surfaceMap: Map<StreamId, Surface> = emptyMap()
     var surfaceMap: Map<StreamId, Surface>
         get() = synchronized(lock) { _surfaceMap }
-        set(value) = synchronized(lock) { _surfaceMap = value }
+        set(value) =
+            synchronized(lock) {
+                _surfaceMap = value
+                println("Configured surfaceMap for $this")
+            }
 
     override fun build(
         isRepeating: Boolean,
@@ -86,6 +90,7 @@
     }
 
     override fun submit(captureSequence: FakeCaptureSequence): Int {
+        println("submit $captureSequence")
         synchronized(lock) {
             if (rejectRequests) {
                 check(
@@ -175,6 +180,11 @@
         defaultListeners: List<Request.Listener>,
     ): FakeCaptureSequence? {
         val surfaceMap = surfaceMap
+        if (surfaceMap.isEmpty()) {
+            println("No surfaces configured for $this! Cannot build CaptureSequence for $requests")
+            return null
+        }
+
         val requestInfoMap = mutableMapOf<Request, RequestMetadata>()
         val requestInfoList = mutableListOf<RequestMetadata>()
         for (request in requests) {
@@ -204,15 +214,22 @@
 
             val requestNumber = RequestNumber(requestCounter.incrementAndGet())
             val streamMap = mutableMapOf<StreamId, Surface>()
+            var hasSurface = false
             for (stream in request.streams) {
                 val surface = surfaceMap[stream]
                 if (surface == null) {
-                    println("No surface was set for $stream while building request $request")
-                    return null
+                    println("Failed to find surface for $stream on $request")
+                    continue
                 }
+                hasSurface = true
                 streamMap[stream] = surface
             }
 
+            if (!hasSurface) {
+                println("No surfaces configured for $request! Cannot build CaptureSequence.")
+                return null
+            }
+
             val requestMetadata =
                 FakeRequestMetadata(
                     request = request,
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt
index 9c4df35..ee4eb1a 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt
@@ -28,6 +28,7 @@
     override val format: Int,
     override val timestamp: Long
 ) : ImageWrapper {
+    private val debugId = debugIds.incrementAndGet()
     private val closed = atomic(false)
     val isClosed: Boolean
         get() = closed.value
@@ -45,4 +46,10 @@
             // FakeImage close is a NoOp
         }
     }
+
+    override fun toString(): String = "FakeImage-$debugId"
+
+    companion object {
+        private val debugIds = atomic(0)
+    }
 }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt
index bbd23b0..18e17d8 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt
@@ -32,34 +32,33 @@
     private val format: StreamFormat,
     override val capacity: Int,
     override val surface: Surface,
-    private val streamId: StreamId,
+    val streamId: StreamId,
     private val outputs: Map<OutputId, Size>
 ) : ImageReaderWrapper {
+    private val debugId = debugIds.incrementAndGet()
     private val closed = atomic(false)
     private val onImageListener = atomic<ImageReaderWrapper.OnImageListener?>(null)
 
+    private val lock = Any()
+    private val _images = mutableListOf<FakeImage>()
+
+    /** Retrieve a list of every image that has been created from this FakeImageReader. */
+    val images: List<FakeImage>
+        get() = synchronized(lock) { _images.toMutableList() }
+
     val isClosed: Boolean
         get() = closed.value
 
     /**
-     * Simulate an image at a specific [timestamp]. The timebase for an imageReader is undefined.
+     * Simulate an image at a specific [imageTimestamp] for a particular (optional) [OutputId]. The
+     * timebase for an imageReader is left undefined.
      */
-    fun simulateImage(timestamp: Long): FakeImage {
-        val outputId = outputs.keys.single()
-        return simulateImage(outputId, timestamp)
-    }
-
-    /**
-     * Simulate an image using a specific [outputId] and [timestamp]. The timebase for an
-     * imageReader is undefined.
-     */
-    fun simulateImage(outputId: OutputId, timestamp: Long): FakeImage {
+    fun simulateImage(imageTimestamp: Long, outputId: OutputId? = null): FakeImage {
+        val output = outputId ?: outputs.keys.single()
         val size =
-            checkNotNull(outputs[outputId]) {
-                "Unexpected $outputId! Available outputs are $outputs"
-            }
-        val image = FakeImage(size.width, size.height, format.value, timestamp)
-        simulateImage(outputId, image)
+            checkNotNull(outputs[output]) { "Unexpected $output! Available outputs are $outputs" }
+        val image = FakeImage(size.width, size.height, format.value, imageTimestamp)
+        simulateImage(image, output)
         return image
     }
 
@@ -67,13 +66,19 @@
      * Simulate an image using a specific [ImageWrapper] for the given outputId. The size must
      * match.
      */
-    fun simulateImage(outputId: OutputId, image: ImageWrapper) {
+    fun simulateImage(image: ImageWrapper, outputId: OutputId) {
         val size =
             checkNotNull(outputs[outputId]) {
                 "Unexpected $outputId! Available outputs are $outputs"
             }
         check(image.width == size.width)
         check(image.height == size.height)
+
+        synchronized(lock) {
+            if (image is FakeImage) {
+                _images.add(image)
+            }
+        }
         onImageListener.value?.onImage(streamId, outputId, image)
     }
 
@@ -96,7 +101,19 @@
         }
     }
 
+    override fun toString(): String = "FakeImageReader-$debugId"
+
+    /** [check] that all images produced by this [FakeImageReader] have been closed. */
+    fun checkImagesClosed() {
+        for ((i, fakeImage) in images.withIndex()) {
+            check(fakeImage.isClosed) {
+                "Failed to close image $i / ${images.size} $fakeImage from $this"
+            }
+        }
+    }
+
     companion object {
+        private val debugIds = atomic(0)
 
         /** Create a [FakeImageReader] that can simulate images. */
         fun create(
@@ -104,21 +121,26 @@
             streamId: StreamId,
             outputId: OutputId,
             size: Size,
-            capacity: Int
-        ): FakeImageReader = create(format, streamId, mapOf(outputId to size), capacity)
+            capacity: Int,
+            fakeSurfaces: FakeSurfaces? = null
+        ): FakeImageReader =
+            create(format, streamId, mapOf(outputId to size), capacity, fakeSurfaces)
 
         /** Create a [FakeImageReader] that can simulate different sized images. */
         fun create(
             format: StreamFormat,
             streamId: StreamId,
             outputIdMap: Map<OutputId, Size>,
-            capacity: Int
+            capacity: Int,
+            fakeSurfaces: FakeSurfaces? = null
         ): FakeImageReader {
 
             // Find smallest by areas to pick the default surface size. This matches the behavior of
             // MultiResolutionImageReader.
             val smallestOutput = outputIdMap.values.minBy { it.width * it.height }
-            val surface = FakeSurfaces.create(smallestOutput)
+            val surface =
+                fakeSurfaces?.createFakeSurface(smallestOutput)
+                    ?: FakeSurfaces.create(smallestOutput)
             return FakeImageReader(format, capacity, surface, streamId, outputIdMap)
         }
     }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReaders.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReaders.kt
new file mode 100644
index 0000000..29253bc
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReaders.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.testing
+
+import android.util.Size
+import android.view.Surface
+import androidx.annotation.GuardedBy
+import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.ImageSourceConfig
+import androidx.camera.camera2.pipe.OutputId
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.media.ImageReaderWrapper
+
+/**
+ * Utility class for creating, tracking, and simulating [FakeImageReader]s. ImageReaders can be
+ * retrieved based on [Surface] or by [StreamId], and supports both single and
+ * MultiResolutionImageReader-like implementations.
+ */
+class FakeImageReaders(private val fakeSurfaces: FakeSurfaces) {
+    private val lock = Any()
+
+    @GuardedBy("lock") private val fakeImageReaders = mutableListOf<FakeImageReader>()
+
+    operator fun get(surface: Surface): FakeImageReader? {
+        return synchronized(lock) { fakeImageReaders.find { it.surface == surface } }
+    }
+
+    operator fun get(streamId: StreamId): FakeImageReader? {
+        return synchronized(lock) { fakeImageReaders.find { it.streamId == streamId } }
+    }
+
+    /** Create a [FakeImageReader] based on a single [CameraStream]. */
+    fun create(cameraStream: CameraStream, capacity: Int) =
+        create(
+            cameraStream.outputs.first().format,
+            cameraStream.id,
+            cameraStream.outputs.associate { it.id to it.size },
+            capacity
+        )
+
+    /** Create a [FakeImageReader] from its properties. */
+    fun create(
+        format: StreamFormat,
+        streamId: StreamId,
+        outputIdMap: Map<OutputId, Size>,
+        capacity: Int,
+        fakeSurfaces: FakeSurfaces? = null
+    ): FakeImageReader {
+        check(this[streamId] == null) {
+            "Cannot create multiple ImageReader(s) from the same $streamId!"
+        }
+
+        val fakeImageReader =
+            FakeImageReader.create(format, streamId, outputIdMap, capacity, fakeSurfaces)
+        synchronized(lock) { fakeImageReaders.add(fakeImageReader) }
+        return fakeImageReader
+    }
+
+    /** Create a [FakeImageReader] based on a [CameraStream] and an [ImageSourceConfig]. */
+    fun create(
+        cameraStream: CameraStream,
+        imageSourceConfig: ImageSourceConfig
+    ): ImageReaderWrapper =
+        create(
+            cameraStream.outputs.first().format,
+            cameraStream.id,
+            cameraStream.outputs.associate { it.id to it.size },
+            imageSourceConfig.capacity,
+            fakeSurfaces
+        )
+
+    /** [check] that all [FakeImageReader]s are closed. */
+    fun checkImageReadersClosed() {
+        for (fakeImageReader in fakeImageReaders) {
+            check(fakeImageReader.isClosed) { "Failed to close ImageReader: $fakeImageReader" }
+        }
+    }
+
+    /** [check] that all images from all [FakeImageReader]s are closed. */
+    fun checkImagesClosed() {
+        for (fakeImageReader in fakeImageReaders) {
+            fakeImageReader.checkImagesClosed()
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSource.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSource.kt
new file mode 100644
index 0000000..a51af8d
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSource.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.testing
+
+import android.util.Size
+import androidx.camera.camera2.pipe.OutputId
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.media.ImageReaderImageSource
+import androidx.camera.camera2.pipe.media.ImageSource
+import kotlinx.atomicfu.atomic
+
+class FakeImageSource
+private constructor(
+    private val fakeImageReader: FakeImageReader,
+    private val imageSource: ImageSource
+) : ImageSource by imageSource {
+    private val debugId = debugIds.incrementAndGet()
+    private val closed = atomic<Boolean>(false)
+    val isClosed: Boolean
+        get() = closed.value
+
+    val streamId: StreamId
+        get() = fakeImageReader.streamId
+
+    /** Retrieve a list of every image that has been created from this FakeImageSource. */
+    val images: List<FakeImage>
+        get() = fakeImageReader.images
+
+    fun simulateImage(timestamp: Long, outputId: OutputId? = null): FakeImage {
+        return fakeImageReader.simulateImage(timestamp, outputId)
+    }
+
+    override fun close() {
+        if (closed.compareAndSet(expect = false, update = true)) {
+            imageSource.close()
+        }
+    }
+
+    override fun toString(): String = "FakeImageSource-$debugId"
+
+    companion object {
+        private val debugIds = atomic(0)
+
+        fun create(
+            streamFormat: StreamFormat,
+            streamId: StreamId,
+            outputs: Map<OutputId, Size>,
+            capacity: Int,
+            fakeImageReaders: FakeImageReaders
+        ): FakeImageSource {
+            val fakeImageReader = fakeImageReaders.create(streamFormat, streamId, outputs, capacity)
+
+            val imageReaderImageSource = ImageReaderImageSource.create(fakeImageReader)
+            return FakeImageSource(fakeImageReader, imageReaderImageSource)
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSources.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSources.kt
new file mode 100644
index 0000000..ae74060
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSources.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.testing
+
+import android.view.Surface
+import androidx.annotation.GuardedBy
+import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.ImageSourceConfig
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.media.ImageSource
+import androidx.camera.camera2.pipe.media.ImageSources
+
+/**
+ * Utility class for creating, tracking, and simulating [FakeImageSource]s. [FakeImageSource](s) can
+ * be retrieved based on [Surface] or by [StreamId], and supports both single and
+ * MultiResolutionImageReader-like implementations.
+ */
+class FakeImageSources(private val fakeImageReaders: FakeImageReaders) : ImageSources {
+    private val lock = Any()
+
+    @GuardedBy("lock") private val fakeImageSources = mutableListOf<FakeImageSource>()
+
+    operator fun get(surface: Surface): FakeImageSource? {
+        return synchronized(lock) { fakeImageSources.find { it.surface == surface } }
+    }
+
+    operator fun get(streamId: StreamId): FakeImageSource? {
+        return synchronized(lock) { fakeImageSources.find { it.streamId == streamId } }
+    }
+
+    override fun createImageSource(
+        cameraStream: CameraStream,
+        imageSourceConfig: ImageSourceConfig
+    ): ImageSource {
+        check(this[cameraStream.id] == null) {
+            "Cannot create multiple ImageSource(s) from the same $cameraStream!"
+        }
+        val fakeImageSource =
+            FakeImageSource.create(
+                cameraStream.outputs.first().format,
+                cameraStream.id,
+                cameraStream.outputs.associate { it.id to it.size },
+                imageSourceConfig.capacity,
+                fakeImageReaders
+            )
+        synchronized(lock) { fakeImageSources.add(fakeImageSource) }
+        return fakeImageSource
+    }
+
+    /** [check] that all [FakeImageSource]s are closed. */
+    fun checkImageSourcesClosed() =
+        synchronized(lock) {
+            for (fakeImageSource in fakeImageSources) {
+                check(fakeImageSource.isClosed) { "Failed to close ImageSource!: $fakeImageSource" }
+            }
+        }
+
+    /** [check] that all images from all [FakeImageReader]s are closed. */
+    fun checkImagesClosed() =
+        synchronized(lock) {
+            for ((i, fakeImageSource) in fakeImageSources.withIndex()) {
+                for ((j, fakeImage) in fakeImageSource.images.withIndex()) {
+                    check(fakeImage.isClosed) {
+                        "Failed to close $fakeImage ($j / " +
+                            "${fakeImageSource.images.size}) from $fakeImageSource " +
+                            "($i / ${fakeImageSources.size})"
+                    }
+                }
+            }
+        }
+}
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt
index b47f1b8..fcf69f6 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt
@@ -27,12 +27,12 @@
 
 object FakeThreads {
     fun fromDispatcher(dispatcher: CoroutineDispatcher): Threads {
-        val scope = CoroutineScope(dispatcher.plus(CoroutineName("CXCP-TestScope")))
+        val scope = CoroutineScope(dispatcher + CoroutineName("CXCP-TestScope"))
         return create(scope, dispatcher)
     }
 
     fun fromTestScope(scope: TestScope): Threads {
-        val dispatcher = StandardTestDispatcher(scope.testScheduler)
+        val dispatcher = StandardTestDispatcher(scope.testScheduler, "CXCP-TestScope")
         return create(scope, dispatcher)
     }
 
diff --git a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt
index a30a1e04..f0cf2e8 100644
--- a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt
+++ b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt
@@ -72,13 +72,13 @@
     @Test
     fun simulatorCanSimulateRepeatingFrames() =
         testScope.runTest {
-            val stream = simulator.cameraGraph.streams[streamConfig]!!
+            val stream = simulator.streams[streamConfig]!!
             val listener = FakeRequestListener()
             val request = Request(streams = listOf(stream.id), listeners = listOf(listener))
-            simulator.cameraGraph.acquireSession().use { it.startRepeating(request) }
-            simulator.cameraGraph.start()
+            simulator.acquireSession().use { it.startRepeating(request) }
+            simulator.start()
             simulator.simulateCameraStarted()
-            simulator.simulateFakeSurfaceConfiguration()
+            simulator.initializeSurfaces()
             advanceUntilIdle()
 
             val frame = simulator.simulateNextFrame()
@@ -160,12 +160,12 @@
     @Test
     fun simulatorAbortsRequests() =
         testScope.runTest {
-            val stream = simulator.cameraGraph.streams[streamConfig]!!
+            val stream = simulator.streams[streamConfig]!!
             val listener = FakeRequestListener()
             val request = Request(streams = listOf(stream.id), listeners = listOf(listener))
 
-            simulator.cameraGraph.acquireSession().use { it.submit(request = request) }
-            simulator.cameraGraph.close()
+            simulator.acquireSession().use { it.submit(request = request) }
+            simulator.close()
 
             val abortedEvent = listener.onAbortedFlow.first()
             assertThat(abortedEvent.request).isSameInstanceAs(request)
@@ -174,15 +174,15 @@
     @Test
     fun simulatorCanIssueBufferLoss() =
         testScope.runTest {
-            val stream = simulator.cameraGraph.streams[streamConfig]!!
+            val stream = simulator.streams[streamConfig]!!
             val listener = FakeRequestListener()
             val request = Request(streams = listOf(stream.id), listeners = listOf(listener))
 
-            simulator.cameraGraph.acquireSession().use { it.submit(request = request) }
+            simulator.acquireSession().use { it.submit(request = request) }
 
-            simulator.cameraGraph.start()
+            simulator.start()
             simulator.simulateCameraStarted()
-            simulator.simulateFakeSurfaceConfiguration()
+            simulator.initializeSurfaces()
             advanceUntilIdle()
 
             val frame = simulator.simulateNextFrame()
@@ -198,14 +198,14 @@
     @Test
     fun simulatorCanIssueMultipleFrames() =
         testScope.runTest {
-            val stream = simulator.cameraGraph.streams[streamConfig]!!
+            val stream = simulator.streams[streamConfig]!!
             val listener = FakeRequestListener()
             val request = Request(streams = listOf(stream.id), listeners = listOf(listener))
 
-            simulator.cameraGraph.acquireSession().use { it.startRepeating(request = request) }
-            simulator.cameraGraph.start()
+            simulator.acquireSession().use { it.startRepeating(request = request) }
+            simulator.start()
             simulator.simulateCameraStarted()
-            simulator.simulateFakeSurfaceConfiguration()
+            simulator.initializeSurfaces()
             advanceUntilIdle()
 
             val frame1 = simulator.simulateNextFrame()
@@ -283,19 +283,19 @@
     @Test
     fun simulatorCanSimulateGraphState() =
         testScope.runTest {
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStopped)
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStopped)
 
-            simulator.cameraGraph.start()
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStarting)
+            simulator.start()
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStarting)
 
             simulator.simulateCameraStarted()
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStarted)
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStarted)
 
-            simulator.cameraGraph.stop()
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStopping)
+            simulator.stop()
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStopping)
 
             simulator.simulateCameraStopped()
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStopped)
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStopped)
         }
 
     @Test
@@ -305,22 +305,22 @@
 
             simulator.simulateCameraError(error)
             // The CameraGraph is stopped at this point, so the errors should be ignored.
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStopped)
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStopped)
 
-            simulator.cameraGraph.start()
+            simulator.start()
             simulator.simulateCameraError(error)
-            val graphState = simulator.cameraGraph.graphState.value
+            val graphState = simulator.graphState.value
             assertThat(graphState).isInstanceOf(GraphStateError::class.java)
             val graphStateError = graphState as GraphStateError
             assertThat(graphStateError.cameraError).isEqualTo(error.cameraError)
             assertThat(graphStateError.willAttemptRetry).isEqualTo(error.willAttemptRetry)
 
             simulator.simulateCameraStarted()
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStarted)
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStarted)
 
-            simulator.cameraGraph.stop()
+            simulator.stop()
             simulator.simulateCameraStopped()
             simulator.simulateCameraError(error)
-            assertThat(simulator.cameraGraph.graphState.value).isEqualTo(GraphStateStopped)
+            assertThat(simulator.graphState.value).isEqualTo(GraphStateStopped)
         }
 }
diff --git a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulatorTest.kt b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulatorTest.kt
new file mode 100644
index 0000000..d7373f1
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulatorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.testing
+
+import android.content.Context
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import android.util.Size
+import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class CameraPipeSimulatorTest {
+    private val testScope = TestScope()
+    private val frontCameraMetadata =
+        FakeCameraMetadata(
+            mapOf(CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_FRONT)
+        )
+    private val backCameraMetadata =
+        FakeCameraMetadata(
+            mapOf(CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_BACK)
+        )
+
+    private val streamConfig = CameraStream.Config.create(Size(640, 480), StreamFormat.YUV_420_888)
+    private val graphConfig =
+        CameraGraph.Config(camera = frontCameraMetadata.camera, streams = listOf(streamConfig))
+
+    private val context = ApplicationProvider.getApplicationContext() as Context
+    private val cameraPipe =
+        CameraPipeSimulator.create(
+            testScope,
+            context,
+            listOf(frontCameraMetadata, backCameraMetadata)
+        )
+
+    @Test
+    fun cameraPipeSimulatorCanCreateCameraGraphSimulators() =
+        testScope.runTest {
+            val cameraGraph1 = cameraPipe.create(graphConfig)
+            val cameraGraphSimulator1 = cameraPipe.cameraGraphs.find { it == cameraGraph1 }
+
+            assertThat(cameraGraph1).isInstanceOf(CameraGraphSimulator::class.java)
+            assertThat(cameraGraph1).isSameInstanceAs(cameraGraphSimulator1)
+
+            // Assert that a new CameraGraph can be created with the same graphConfig and that they
+            // produce different CameraGraph instances and simulators.
+            val cameraGraphSimulator3 = cameraPipe.createCameraGraphSimulator(graphConfig)
+            assertThat(cameraGraphSimulator3).isNotSameInstanceAs(cameraGraphSimulator1)
+        }
+
+    @Test
+    fun cameraPipeSimulatorHasMetadataViaCameraPipe() =
+        testScope.runTest {
+            val cameraIds = cameraPipe.cameras().getCameraIds()
+
+            assertThat(cameraIds).isNotNull()
+            assertThat(cameraIds!!.size).isEqualTo(2)
+
+            val firstCameraId = cameraIds.first()
+            val firstMetadata = cameraPipe.cameras().getCameraMetadata(firstCameraId)
+
+            assertThat(firstMetadata).isNotNull()
+            assertThat(firstMetadata).isSameInstanceAs(frontCameraMetadata)
+            assertThat(firstMetadata!!.camera).isEqualTo(firstCameraId)
+
+            val lastCameraId = cameraIds.last()
+            val lastCameraMetadata = cameraPipe.cameras().getCameraMetadata(lastCameraId)
+
+            assertThat(lastCameraMetadata).isNotNull()
+            assertThat(lastCameraMetadata).isSameInstanceAs(backCameraMetadata)
+            assertThat(lastCameraMetadata!!.camera).isEqualTo(lastCameraId)
+        }
+
+    @Test
+    fun cameraPipeSimulatorCanCreateDualCameraGraphs() =
+        testScope.runTest {
+            val cameraIds = cameraPipe.cameras().getCameraIds()
+
+            assertThat(cameraIds).isNotNull()
+            assertThat(cameraIds!!.size).isEqualTo(2)
+
+            val firstCameraId = cameraIds.first()
+            val firstMetadata = cameraPipe.cameras().getCameraMetadata(firstCameraId)
+
+            assertThat(firstMetadata).isNotNull()
+            assertThat(firstMetadata).isSameInstanceAs(frontCameraMetadata)
+            assertThat(firstMetadata!!.camera).isEqualTo(firstCameraId)
+        }
+
+    @Test
+    fun cameraPipeSimulatorCanVerifyCameraGraphConstructionOrder() {
+        val graphConfig1 =
+            CameraGraph.Config(camera = frontCameraMetadata.camera, streams = listOf(streamConfig))
+        val graphConfig2 =
+            CameraGraph.Config(camera = backCameraMetadata.camera, streams = listOf(streamConfig))
+        val graphConfig3 =
+            CameraGraph.Config(camera = frontCameraMetadata.camera, streams = listOf(streamConfig))
+
+        val cameraGraph1 = cameraPipe.create(graphConfig1)
+        val cameraGraph2 = cameraPipe.create(graphConfig2)
+        val cameraGraph3 = cameraPipe.create(graphConfig3)
+
+        assertThat(cameraPipe.cameraGraphs)
+            .containsExactly(cameraGraph1, cameraGraph2, cameraGraph3)
+            .inOrder()
+
+        cameraGraph1.close()
+        cameraGraph2.close()
+        cameraGraph3.close()
+    }
+
+    @Test
+    fun cameraPipeSimulatorCanCheckForUnclosedResources() {
+        val cameraGraph = cameraPipe.create(graphConfig)
+        val fakeImageReader =
+            cameraPipe.fakeImageReaders.create(cameraGraph.streams[streamConfig]!!, 1)
+        val fakeImage = fakeImageReader.simulateImage(123)
+
+        // Assert that these throw exceptions
+        assertThrows(IllegalStateException::class.java) { cameraPipe.checkImageReadersClosed() }
+        assertThrows(IllegalStateException::class.java) { cameraPipe.checkImagesClosed() }
+        assertThrows(IllegalStateException::class.java) { cameraPipe.checkCameraGraphsClosed() }
+
+        // Close everything
+        fakeImage.close()
+        fakeImageReader.close()
+        cameraGraph.close()
+
+        // Assert that these no longer throw exceptions.
+        cameraPipe.checkCameraGraphsClosed()
+        cameraPipe.checkImageReadersClosed()
+        cameraPipe.checkImagesClosed()
+    }
+
+    @Test
+    fun cameraPipeSimulatorCanCreateConcurrentCameraGraphs() {
+        val config1 =
+            CameraGraph.Config(
+                camera = frontCameraMetadata.camera,
+                streams = listOf(streamConfig),
+            )
+        val config2 =
+            CameraGraph.Config(
+                camera = backCameraMetadata.camera,
+                streams = listOf(streamConfig),
+            )
+        val concurrentCameras = listOf(config1, config2)
+
+        val cameraGraphs =
+            cameraPipe.createCameraGraphs(CameraGraph.ConcurrentConfig(concurrentCameras))
+
+        assertThat(cameraGraphs.size).isEqualTo(2)
+        assertThat(cameraGraphs[0].config.camera).isEqualTo(frontCameraMetadata.camera)
+        assertThat(cameraGraphs[1].config.camera).isEqualTo(backCameraMetadata.camera)
+
+        val config1Stream1 = cameraGraphs[0].streams[streamConfig]
+        val config2Stream1 = cameraGraphs[1].streams[streamConfig]
+        assertThat(config1Stream1).isNotEqualTo(config2Stream1)
+    }
+}
diff --git a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FakeThreadsTest.kt b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FakeThreadsTest.kt
new file mode 100644
index 0000000..db747e0
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FakeThreadsTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.testing
+
+import android.os.Build
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class FakeThreadsTest {
+    private val testScope = TestScope()
+    private val fakeThreads = FakeThreads.fromTestScope(testScope)
+
+    @Test
+    fun fakeThreadsUseDelaySkipping() =
+        testScope.runTest {
+            launch(fakeThreads.backgroundDispatcher) { delay(1000000) }.join()
+            launch(fakeThreads.blockingDispatcher) { delay(1000000) }.join()
+            launch(fakeThreads.lightweightDispatcher) { delay(1000000) }.join()
+            fakeThreads.globalScope.launch { delay(1000000) }.join()
+
+            var backgroundTaskExecuted = false
+            var blockingTaskExecuted = false
+            var lightweightTaskExecuted = false
+            fakeThreads.backgroundExecutor.execute { backgroundTaskExecuted = true }
+            fakeThreads.blockingExecutor.execute { blockingTaskExecuted = true }
+            fakeThreads.lightweightExecutor.execute { lightweightTaskExecuted = true }
+            advanceUntilIdle()
+
+            assertThat(backgroundTaskExecuted).isTrue()
+            assertThat(blockingTaskExecuted).isTrue()
+            assertThat(lightweightTaskExecuted).isTrue()
+        }
+
+    @Test
+    fun exceptionsInDispatcherPropagateToTestScopeFailure() {
+
+        // Exceptions in GlobalScope is propagated out of the test.
+        assertThrows(RuntimeException::class.java) {
+            val scope = TestScope()
+            val localFakeThreads = FakeThreads.fromTestScope(scope)
+            scope.runTest {
+                localFakeThreads.globalScope.launch { throw RuntimeException("globalScope") }
+            }
+        }
+
+        // Exceptions in Dispatchers are propagated out of the test.
+        assertThrows(RuntimeException::class.java) {
+            val scope = TestScope()
+            val localFakeThreads = FakeThreads.fromTestScope(scope)
+            scope.runTest {
+                launch(localFakeThreads.backgroundDispatcher) {
+                    throw RuntimeException("backgroundDispatcher")
+                }
+            }
+        }
+
+        // Exceptions in Executors are propagated out of the test.
+        assertThrows(RuntimeException::class.java) {
+            val scope = TestScope()
+            val localFakeThreads = FakeThreads.fromTestScope(scope)
+            scope.runTest {
+                localFakeThreads.backgroundExecutor.execute {
+                    throw RuntimeException("backgroundExecutor")
+                }
+            }
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FrameCaptureTests.kt b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FrameCaptureTests.kt
new file mode 100644
index 0000000..a513cea
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FrameCaptureTests.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.testing
+
+import android.content.Context
+import android.os.Build
+import android.util.Size
+import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.Frame.Companion.isFrameInfoAvailable
+import androidx.camera.camera2.pipe.GraphState.GraphStateStarted
+import androidx.camera.camera2.pipe.GraphState.GraphStateStarting
+import androidx.camera.camera2.pipe.GraphState.GraphStateStopped
+import androidx.camera.camera2.pipe.ImageSourceConfig
+import androidx.camera.camera2.pipe.OutputStatus
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class FrameCaptureTests {
+    private val testScope = TestScope()
+    private val testContext = ApplicationProvider.getApplicationContext() as Context
+    private val cameraPipeSimulator = CameraPipeSimulator.create(testScope, testContext)
+    private val cameraId = cameraPipeSimulator.cameras().awaitCameraIds()!!.first()
+    private val cameraMetadata = cameraPipeSimulator.cameras().awaitCameraMetadata(cameraId)!!
+
+    private val viewfinderStreamConfig =
+        CameraStream.Config.create(Size(640, 480), StreamFormat.UNKNOWN)
+
+    private val jpegStreamConfig =
+        CameraStream.Config.create(
+            Size(640, 480),
+            StreamFormat.YUV_420_888,
+            imageSourceConfig = ImageSourceConfig(capacity = 10)
+        )
+
+    private val graphConfig =
+        CameraGraph.Config(
+            camera = cameraMetadata.camera,
+            streams = listOf(viewfinderStreamConfig, jpegStreamConfig)
+        )
+
+    private val cameraGraphSimulator = cameraPipeSimulator.createCameraGraphSimulator(graphConfig)
+    private val cameraGraph: CameraGraph = cameraGraphSimulator
+
+    private val viewfinderStream = cameraGraph.streams[viewfinderStreamConfig]!!
+    private val jpegStream = cameraGraph.streams[jpegStreamConfig]!!
+
+    private suspend fun startCameraGraph() {
+        assertThat(cameraGraph.graphState.value).isEqualTo(GraphStateStopped)
+
+        cameraGraph.start() // Tell the cameraGraph to start
+        assertThat(cameraGraph.graphState.value).isEqualTo(GraphStateStarting)
+
+        cameraGraphSimulator.initializeSurfaces()
+        cameraGraphSimulator.simulateCameraStarted() // Simulate the camera starting successfully
+        assertThat(cameraGraph.graphState.value).isEqualTo(GraphStateStarted)
+    }
+
+    @Test
+    fun frameCaptureCanBeSimulated() =
+        testScope.runTest {
+            startCameraGraph()
+
+            // Capture an image using the cameraGraph
+            val frameCapture =
+                cameraGraph.useSession { session ->
+                    session.capture(Request(streams = listOf(jpegStream.id)))
+                }
+            advanceUntilIdle()
+
+            // Verify a capture sequence with all of the frame interactions
+            val frameCaptureJob = launch {
+                // TODO: Should awaitFrame be called awaitFrameStarted?
+                // TODO: Should there be an awaitComplete() function?
+                val frame = frameCapture.awaitFrame()
+                assertThat(frame).isNotNull()
+
+                assertThat(frame!!.frameId.value).isGreaterThan(0)
+                assertThat(frame.frameTimestamp.value).isGreaterThan(0)
+
+                val image = frame.awaitImage(jpegStream.id)
+                assertThat(frame.imageStatus(jpegStream.id)).isEqualTo(OutputStatus.AVAILABLE)
+                assertThat(frame.imageStatus(viewfinderStream.id))
+                    .isEqualTo(OutputStatus.UNAVAILABLE)
+                assertThat(image).isNotNull()
+                assertThat(image!!.timestamp).isEqualTo(frame.frameTimestamp.value)
+
+                image.close()
+
+                assertThat(frame.imageStatus(jpegStream.id)).isEqualTo(OutputStatus.AVAILABLE)
+                assertThat(frame.imageStatus(viewfinderStream.id))
+                    .isEqualTo(OutputStatus.UNAVAILABLE)
+
+                println("frame.awaitFrameInfo()")
+                val frameInfo = frame.awaitFrameInfo()
+
+                assertThat(frame.isFrameInfoAvailable).isTrue()
+                assertThat(frameInfo).isNotNull()
+                assertThat(frameInfo!!.frameNumber).isEqualTo(frame.frameNumber)
+
+                println("frame.close()")
+                frame.close()
+
+                assertThat(frame.imageStatus(jpegStream.id)).isEqualTo(OutputStatus.UNAVAILABLE)
+                assertThat(frame.imageStatus(viewfinderStream.id))
+                    .isEqualTo(OutputStatus.UNAVAILABLE)
+                assertThat(frame.isFrameInfoAvailable).isFalse()
+            }
+
+            // Simulate camera interactions:
+            // TODO: simulateFrameStarted?
+            val frameSimulator = cameraGraphSimulator.simulateNextFrame()
+            frameSimulator.simulateImage(jpegStream.id)
+            frameSimulator.simulateComplete(emptyMap())
+
+            // TODO: should this have a way to check to make sure all frames are closed?
+            // cameraGraph?
+
+            advanceUntilIdle()
+            assertThat(frameCaptureJob.isCompleted) // Ensure verification is complete
+            cameraGraphSimulator.close()
+        }
+}
diff --git a/camera/camera-camera2-pipe/build.gradle b/camera/camera-camera2-pipe/build.gradle
index 52bfbab..8e3af81 100644
--- a/camera/camera-camera2-pipe/build.gradle
+++ b/camera/camera-camera2-pipe/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesAndroid)
 
@@ -79,7 +79,6 @@
     inceptionYear = "2020"
     description = "A set of opinionated camera interfaces and implementations on top of Camera2 " +
             "that will form a flexible shim layer to power Frameserver and CameraX."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "Not shipped externally"
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
index df87e19..a034faa 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
@@ -128,6 +128,7 @@
      */
     fun createCameraController(
         cameraContext: CameraContext,
+        graphId: CameraGraphId,
         graphConfig: CameraGraph.Config,
         graphListener: GraphListener,
         streamGraph: StreamGraph
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
index c8d1862..21e0146 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
@@ -38,8 +38,12 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 interface CameraController {
+    /** Represents the primary CameraId this CameraController is associated with. */
     val cameraId: CameraId
 
+    /** Represents the primary CameraGraphId that this [CameraController] is associated with. */
+    val cameraGraphId: CameraGraphId
+
     /**
      * Whether the camera is being used in a foreground setting, and thus should be kept open on a
      * best-effort basis, for example continuously retrying on a longer timeout.
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
index 517a846..b6cd0dc 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
@@ -43,6 +43,13 @@
 /** A [CameraGraph] represents the combined configuration and state of a camera. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 interface CameraGraph : AutoCloseable {
+    /**
+     * A unique identifier for this CameraGraph instance. This can be used to identify the graph
+     * without holding a hard reference to the CameraGraph itself.
+     */
+    val id: CameraGraphId
+
+    /** The [StreamGraph] for this CameraGraph instance. */
     val streams: StreamGraph
 
     /**
@@ -219,6 +226,32 @@
         }
     }
 
+    class ConcurrentConfig(graphConfigs: List<Config>) {
+        val graphConfigs: List<Config>
+
+        init {
+            check(graphConfigs.size >= 2) {
+                "Cannot create ConcurrentGraphConfig without 2 or more CameraGraph.Config(s)"
+            }
+            val firstConfig = graphConfigs.first()
+            check(graphConfigs.all { it.cameraBackendId == firstConfig.cameraBackendId }) {
+                "Each CameraGraph.Config must use the same camera backend!"
+            }
+
+            val distinctCameraIds = graphConfigs.map { it.camera }.distinct()
+            check(distinctCameraIds.size == graphConfigs.size) {
+                "Each CameraGraph.Config must have a distinct camera id!"
+            }
+
+            this.graphConfigs =
+                graphConfigs.map { config ->
+                    config.apply {
+                        sharedCameraIds = distinctCameraIds.filter { it != config.camera }
+                    }
+                }
+        }
+    }
+
     /**
      * Flags define boolean values that are used to adjust the behavior and interactions with
      * camera2. These flags should default to the ideal behavior and should be overridden on
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraphId.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraphId.kt
new file mode 100644
index 0000000..27a35ab
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraphId.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe
+
+import androidx.annotation.RestrictTo
+import kotlinx.atomicfu.atomic
+
+/**
+ * Identifier for a specific [CameraGraph] that can be used to standardize toString methods and as a
+ * key in maps without holding a reference to a [CameraGraph] object, which could lead to accidental
+ * memory leaks and circular dependencies.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class CameraGraphId private constructor(private val name: String) {
+    override fun toString(): String = name
+
+    companion object {
+        private val cameraGraphIds = atomic(0)
+
+        /**
+         * Create the next CameraGraphId based on a global incrementing counter. This is
+         * intentionally worded as "CameraGraph" instead of "CameraGraphId" since it is used
+         * directly as the toString representation for a [CameraGraph].
+         */
+        @JvmStatic
+        fun nextId(): CameraGraphId {
+            return CameraGraphId("CameraGraph-${cameraGraphIds.incrementAndGet()}")
+        }
+    }
+}
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 60249ac..57c90726 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
@@ -22,6 +22,7 @@
 import android.hardware.camera2.CameraDevice
 import android.os.HandlerThread
 import androidx.annotation.RestrictTo
+import androidx.camera.camera2.pipe.CameraPipe.Config
 import androidx.camera.camera2.pipe.compat.AudioRestrictionController
 import androidx.camera.camera2.pipe.config.CameraGraphConfigModule
 import androidx.camera.camera2.pipe.config.CameraPipeComponent
@@ -34,6 +35,7 @@
 import androidx.camera.camera2.pipe.config.ThreadConfigModule
 import androidx.camera.camera2.pipe.core.Debug
 import androidx.camera.camera2.pipe.core.DurationNs
+import androidx.camera.camera2.pipe.media.ImageSources
 import java.util.concurrent.Executor
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.CoroutineDispatcher
@@ -51,93 +53,49 @@
  * the [CameraGraph] interface.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class CameraPipe(config: Config) {
-    private val debugId = cameraPipeIds.incrementAndGet()
-    private val component: CameraPipeComponent =
-        Debug.trace("CameraPipe") {
-            DaggerCameraPipeComponent.builder()
-                .cameraPipeConfigModule(CameraPipeConfigModule(config))
-                .threadConfigModule(ThreadConfigModule(config.threadConfig))
-                .build()
-        }
+interface CameraPipe {
 
     /**
      * This creates a new [CameraGraph] that can be used to interact with a single Camera on the
      * device. Multiple [CameraGraph]s can be created, but only one should be active at a time.
      */
-    fun create(config: CameraGraph.Config): CameraGraph =
-        Debug.trace("CXCP#CameraGraph-${config.camera}") {
-            component
-                .cameraGraphComponentBuilder()
-                .cameraGraphConfigModule(CameraGraphConfigModule(config))
-                .build()
-                .cameraGraph()
-        }
+    fun create(config: CameraGraph.Config): CameraGraph
 
     /**
      * This creates a list of [CameraGraph]s that can be used to interact with multiple cameras on
      * the device concurrently. Device-specific constraints may apply, such as the set of cameras
      * that can be operated concurrently, or the combination of sizes we're allowed to configure.
      */
-    fun createCameraGraphs(concurrentConfigs: List<CameraGraph.Config>): List<CameraGraph> {
-        check(concurrentConfigs.isNotEmpty())
-        if (concurrentConfigs.size == 1) {
-            return listOf(create(concurrentConfigs.first()))
-        }
-        check(
-            concurrentConfigs.all {
-                it.cameraBackendId == concurrentConfigs.first().cameraBackendId
-            }
-        ) {
-            "All concurrent CameraGraph configs should have the same camera backend ID!"
-        }
-        val allCameraIds = concurrentConfigs.map { it.camera }
-        check(allCameraIds.size == allCameraIds.toSet().size) {
-            "All camera IDs specified should be distinct!"
-        }
-
-        val configs =
-            concurrentConfigs.map { config ->
-                config.apply { sharedCameraIds = allCameraIds.filter { it != config.camera } }
-            }
-        return configs.map { create(it) }
-    }
+    fun createCameraGraphs(config: CameraGraph.ConcurrentConfig): List<CameraGraph>
 
     /** This provides access to information about the available cameras on the device. */
-    fun cameras(): CameraDevices {
-        return component.cameras()
-    }
+    fun cameras(): CameraDevices
 
     /** This returns [CameraSurfaceManager] which tracks the lifetime of Surfaces in CameraPipe. */
-    fun cameraSurfaceManager(): CameraSurfaceManager {
-        return component.cameraSurfaceManager()
-    }
+    fun cameraSurfaceManager(): CameraSurfaceManager
 
     /**
      * This gets and sets the global [AudioRestrictionMode] tracked by [AudioRestrictionController].
      */
     var globalAudioRestrictionMode: AudioRestrictionMode
-        get(): AudioRestrictionMode =
-            component.cameraAudioRestrictionController().globalAudioRestrictionMode
-        set(value: AudioRestrictionMode) {
-            component.cameraAudioRestrictionController().globalAudioRestrictionMode = value
-        }
 
     /**
      * Application level configuration for [CameraPipe]. Nullable values are optional and reasonable
      * defaults will be provided if values are not specified.
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     data class Config(
         val appContext: Context,
         val threadConfig: ThreadConfig = ThreadConfig(),
         val cameraMetadataConfig: CameraMetadataConfig = CameraMetadataConfig(),
         val cameraBackendConfig: CameraBackendConfig = CameraBackendConfig(),
-        val cameraInteropConfig: CameraInteropConfig = CameraInteropConfig()
+        val cameraInteropConfig: CameraInteropConfig = CameraInteropConfig(),
+        val imageSources: ImageSources? = null,
     )
 
     /**
      * Application level configuration for Camera2Interop callbacks. If set, these callbacks will be
-     * triggered at the appropriate places in Camera-Pipe.
+     * triggered at the appropriate places in CameraPipe.
      */
     data class CameraInteropConfig(
         val cameraDeviceStateCallback: CameraDevice.StateCallback? = null,
@@ -207,8 +165,6 @@
         }
     }
 
-    override fun toString(): String = "CameraPipe-$debugId"
-
     /**
      * External may be used if the underlying implementation needs to delegate to another library or
      * system.
@@ -246,7 +202,65 @@
                         ExternalCameraGraphConfigModule(config, cameraMetadata, requestProcessor)
                     )
                     .build()
+
             return component.cameraGraph()
         }
     }
+
+    companion object {
+        fun create(config: Config): CameraPipe {
+            val cameraPipeComponent =
+                Debug.trace("CameraPipe") {
+                    DaggerCameraPipeComponent.builder()
+                        .cameraPipeConfigModule(CameraPipeConfigModule(config))
+                        .threadConfigModule(ThreadConfigModule(config.threadConfig))
+                        .build()
+                }
+
+            return CameraPipeImpl(cameraPipeComponent)
+        }
+    }
+}
+
+/** Utility constructor for existing classes that construct CameraPipe directly */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun CameraPipe(config: Config): CameraPipe = CameraPipe.create(config)
+
+internal class CameraPipeImpl(private val component: CameraPipeComponent) : CameraPipe {
+    private val debugId = cameraPipeIds.incrementAndGet()
+
+    override fun create(config: CameraGraph.Config): CameraGraph =
+        Debug.trace("CXCP#CameraGraph-${config.camera}") {
+            component
+                .cameraGraphComponentBuilder()
+                .cameraGraphConfigModule(CameraGraphConfigModule(config))
+                .build()
+                .cameraGraph()
+        }
+
+    override fun createCameraGraphs(config: CameraGraph.ConcurrentConfig): List<CameraGraph> {
+        return config.graphConfigs.map { create(it) }
+    }
+
+    /** This provides access to information about the available cameras on the device. */
+    override fun cameras(): CameraDevices {
+        return component.cameras()
+    }
+
+    /** This returns [CameraSurfaceManager] which tracks the lifetime of Surfaces in CameraPipe. */
+    override fun cameraSurfaceManager(): CameraSurfaceManager {
+        return component.cameraSurfaceManager()
+    }
+
+    /**
+     * This gets and sets the global [AudioRestrictionMode] tracked by [AudioRestrictionController].
+     */
+    override var globalAudioRestrictionMode: AudioRestrictionMode
+        get(): AudioRestrictionMode =
+            component.cameraAudioRestrictionController().globalAudioRestrictionMode
+        set(value) {
+            component.cameraAudioRestrictionController().globalAudioRestrictionMode = value
+        }
+
+    override fun toString(): String = "CameraPipe-$debugId"
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt
index dad605b..324363a 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt
@@ -31,4 +31,8 @@
     val outputs: List<OutputStream>
 
     operator fun get(config: CameraStream.Config): CameraStream?
+
+    operator fun get(streamId: StreamId): CameraStream? = streams.find { it.id == streamId }
+
+    operator fun get(outputId: OutputId): OutputStream? = outputs.find { it.id == outputId }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
index 580a7dd..781ba85 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
@@ -38,7 +38,6 @@
 import android.os.Handler
 import android.util.Size
 import android.view.Surface
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RequiresPermission
 import androidx.camera.camera2.pipe.CameraMetadata
@@ -47,7 +46,6 @@
 @RequiresApi(Build.VERSION_CODES.M)
 internal object Api23Compat {
     @JvmStatic
-    @DoNotInline
     @Throws(CameraAccessException::class)
     @Suppress("deprecation")
     fun createReprocessableCaptureSession(
@@ -61,7 +59,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     @Throws(CameraAccessException::class)
     @Suppress("deprecation")
     fun createConstrainedHighSpeedCaptureSession(
@@ -74,7 +71,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     @Throws(CameraAccessException::class)
     fun createReprocessCaptureRequest(
         cameraDevice: CameraDevice,
@@ -84,25 +80,21 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun isReprocessable(cameraCaptureSession: CameraCaptureSession): Boolean {
         return cameraCaptureSession.isReprocessable
     }
 
     @JvmStatic
-    @DoNotInline
     fun getInputSurface(cameraCaptureSession: CameraCaptureSession): Surface? {
         return cameraCaptureSession.inputSurface
     }
 
     @JvmStatic
-    @DoNotInline
     fun newInputConfiguration(width: Int, height: Int, format: Int): InputConfiguration {
         return InputConfiguration(width, height, format)
     }
 
     @JvmStatic
-    @DoNotInline
     fun checkSelfPermission(context: Context, permission: String): Int {
         return context.checkSelfPermission(permission)
     }
@@ -111,7 +103,6 @@
 @RequiresApi(Build.VERSION_CODES.N)
 internal object Api24Compat {
     @JvmStatic
-    @DoNotInline
     @Throws(CameraAccessException::class)
     @Suppress("deprecation")
     fun createCaptureSessionByOutputConfigurations(
@@ -128,7 +119,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     @Throws(CameraAccessException::class)
     @Suppress("deprecation")
     fun createReprocessableCaptureSessionByConfigurations(
@@ -147,7 +137,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun getSurfaceGroupId(outputConfiguration: OutputConfiguration): Int {
         return outputConfiguration.surfaceGroupId
     }
@@ -156,7 +145,6 @@
 @RequiresApi(Build.VERSION_CODES.O)
 internal object Api26Compat {
     @JvmStatic
-    @DoNotInline
     @Throws(CameraAccessException::class)
     fun finalizeOutputConfigurations(
         cameraCaptureSession: CameraCaptureSession,
@@ -166,25 +154,21 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun newOutputConfiguration(size: Size, klass: Class<*>): OutputConfiguration {
         return OutputConfiguration(size, klass)
     }
 
     @JvmStatic
-    @DoNotInline
     fun enableSurfaceSharing(outputConfig: OutputConfiguration) {
         outputConfig.enableSurfaceSharing()
     }
 
     @JvmStatic
-    @DoNotInline
     fun getSurfaces(outputConfig: OutputConfiguration): List<Surface> {
         return outputConfig.surfaces
     }
 
     @JvmStatic
-    @DoNotInline
     fun addSurfaces(outputConfig: OutputConfiguration, surface: Surface) {
         return outputConfig.addSurface(surface)
     }
@@ -195,7 +179,6 @@
 internal object Api28Compat {
     @JvmStatic
     @Throws(CameraAccessException::class)
-    @DoNotInline
     fun createCaptureSession(
         cameraDevice: CameraDevice,
         sessionConfig: SessionConfiguration,
@@ -204,7 +187,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun getAvailablePhysicalCameraRequestKeys(
         cameraCharacteristics: CameraCharacteristics
     ): List<CaptureRequest.Key<*>>? {
@@ -212,7 +194,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun getAvailableSessionKeys(
         cameraCharacteristics: CameraCharacteristics
     ): List<CaptureRequest.Key<*>>? {
@@ -220,13 +201,11 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun getPhysicalCameraIds(cameraCharacteristics: CameraCharacteristics): Set<String> {
         return cameraCharacteristics.physicalCameraIds
     }
 
     @JvmStatic
-    @DoNotInline
     fun getPhysicalCaptureResults(
         totalCaptureResult: TotalCaptureResult
     ): Map<String, CaptureResult>? {
@@ -234,7 +213,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun newSessionConfiguration(
         sessionType: Int,
         outputs: List<OutputConfiguration?>,
@@ -245,7 +223,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun setInputConfiguration(
         sessionConfig: SessionConfiguration,
         inputConfig: InputConfiguration
@@ -254,32 +231,27 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun setSessionParameters(sessionConfig: SessionConfiguration, params: CaptureRequest) {
         sessionConfig.sessionParameters = params
     }
 
     @JvmStatic
-    @DoNotInline
     fun getMaxSharedSurfaceCount(outputConfig: OutputConfiguration): Int {
         return outputConfig.maxSharedSurfaceCount
     }
 
     @JvmStatic
-    @DoNotInline
     fun setPhysicalCameraId(outputConfig: OutputConfiguration, cameraId: String?) {
         outputConfig.setPhysicalCameraId(cameraId)
     }
 
     @JvmStatic
-    @DoNotInline
     fun removeSurface(outputConfig: OutputConfiguration, surface: Surface) {
         return outputConfig.removeSurface(surface)
     }
 
     @JvmStatic
     @Throws(CameraAccessException::class)
-    @DoNotInline
     @RequiresPermission(android.Manifest.permission.CAMERA)
     fun openCamera(
         cameraManager: CameraManager,
@@ -291,7 +263,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun registerAvailabilityCallback(
         cameraManager: CameraManager,
         executor: Executor,
@@ -301,7 +272,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun discardFreeBuffers(imageReader: ImageReader) {
         imageReader.discardFreeBuffers()
     }
@@ -310,7 +280,6 @@
 @RequiresApi(Build.VERSION_CODES.Q)
 internal object Api29Compat {
     @JvmStatic
-    @DoNotInline
     fun imageReaderNewInstance(
         width: Int,
         height: Int,
@@ -322,7 +291,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun imageWriterNewInstance(surface: Surface, maxImages: Int, format: Int): ImageWriter {
         return ImageWriter.newInstance(surface, maxImages, format)
     }
@@ -331,19 +299,16 @@
 @RequiresApi(Build.VERSION_CODES.R)
 internal object Api30Compat {
     @JvmStatic
-    @DoNotInline
     fun getConcurrentCameraIds(cameraManager: CameraManager): Set<Set<String>> {
         return cameraManager.concurrentCameraIds
     }
 
     @JvmStatic
-    @DoNotInline
     fun getCameraAudioRestriction(cameraDevice: CameraDevice): Int {
         return cameraDevice.cameraAudioRestriction
     }
 
     @JvmStatic
-    @DoNotInline
     fun setCameraAudioRestriction(cameraDevice: CameraDevice, mode: Int) {
         cameraDevice.cameraAudioRestriction = mode
     }
@@ -352,7 +317,6 @@
 @RequiresApi(Build.VERSION_CODES.S)
 internal object Api31Compat {
     @JvmStatic
-    @DoNotInline
     fun newInputConfiguration(
         inputConfigData: List<InputConfigData>,
         cameraId: String
@@ -373,7 +337,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun newMultiResolutionStreamInfo(
         streamWidth: Int,
         streamHeight: Int,
@@ -383,7 +346,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun getPhysicalCameraTotalResults(
         totalCaptureResult: TotalCaptureResult
     ): Map<String, CaptureResult>? {
@@ -391,7 +353,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun addSensorPixelModeUsed(
         outputConfiguration: OutputConfiguration,
         sensorPixelMode: Int,
@@ -400,7 +361,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun createExtensionCaptureSession(
         cameraDevice: CameraDevice,
         extensionConfiguration: ExtensionSessionConfiguration
@@ -409,14 +369,12 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun getCameraExtensionCharacteristics(
         cameraManager: CameraManager,
         cameraId: String
     ): CameraExtensionCharacteristics = cameraManager.getCameraExtensionCharacteristics(cameraId)
 
     @JvmStatic
-    @DoNotInline
     fun newExtensionSessionConfiguration(
         extensionMode: Int,
         outputs: List<OutputConfiguration?>,
@@ -427,13 +385,11 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun getSupportedExtensions(
         extensionCharacteristics: CameraExtensionCharacteristics
     ): List<Int> = extensionCharacteristics.supportedExtensions
 
     @JvmStatic
-    @DoNotInline
     fun getExtensionSupportedSizes(
         extensionCharacteristics: CameraExtensionCharacteristics,
         extension: Int,
@@ -441,7 +397,6 @@
     ): List<Size> = extensionCharacteristics.getExtensionSupportedSizes(extension, imageFormat)
 
     @JvmStatic
-    @DoNotInline
     fun getExtensionSupportedSizes(
         extensionCharacteristics: CameraExtensionCharacteristics,
         extension: Int,
@@ -452,61 +407,51 @@
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 internal object Api33Compat {
     @JvmStatic
-    @DoNotInline
     fun setDynamicRangeProfile(outputConfig: OutputConfiguration, dynamicRangeProfile: Long) {
         outputConfig.dynamicRangeProfile = dynamicRangeProfile
     }
 
     @JvmStatic
-    @DoNotInline
     fun getDynamicRangeProfile(outputConfig: OutputConfiguration): Long {
         return outputConfig.dynamicRangeProfile
     }
 
     @JvmStatic
-    @DoNotInline
     fun setMirrorMode(outputConfig: OutputConfiguration, mirrorMode: Int) {
         outputConfig.mirrorMode = mirrorMode
     }
 
     @JvmStatic
-    @DoNotInline
     fun getMirrorMode(outputConfig: OutputConfiguration): Int {
         return outputConfig.mirrorMode
     }
 
     @JvmStatic
-    @DoNotInline
     fun setStreamUseCase(outputConfig: OutputConfiguration, streamUseCase: Long) {
         outputConfig.streamUseCase = streamUseCase
     }
 
     @JvmStatic
-    @DoNotInline
     fun getAvailableStreamUseCases(cameraMetadata: CameraMetadata): LongArray? {
         return cameraMetadata[CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES]
     }
 
     @JvmStatic
-    @DoNotInline
     fun getStreamUseCase(outputConfig: OutputConfiguration): Long {
         return outputConfig.streamUseCase
     }
 
     @JvmStatic
-    @DoNotInline
     fun setTimestampBase(outputConfig: OutputConfiguration, timestampBase: Int) {
         outputConfig.timestampBase = timestampBase
     }
 
     @JvmStatic
-    @DoNotInline
     fun getTimestampBase(outputConfig: OutputConfiguration): Int {
         return outputConfig.timestampBase
     }
 
     @JvmStatic
-    @DoNotInline
     fun getAvailableCaptureRequestKeys(
         extensionCharacteristics: CameraExtensionCharacteristics,
         extension: Int
@@ -514,7 +459,6 @@
         extensionCharacteristics.getAvailableCaptureRequestKeys(extension)
 
     @JvmStatic
-    @DoNotInline
     fun getAvailableCaptureResultKeys(
         extensionCharacteristics: CameraExtensionCharacteristics,
         extension: Int
@@ -522,7 +466,6 @@
         extensionCharacteristics.getAvailableCaptureResultKeys(extension)
 
     @JvmStatic
-    @DoNotInline
     fun newImageReaderFromImageReaderBuilder(
         width: Int,
         height: Int,
@@ -548,14 +491,12 @@
 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 internal object Api34Compat {
     @JvmStatic
-    @DoNotInline
     fun isPostviewAvailable(
         extensionCharacteristics: CameraExtensionCharacteristics,
         extension: Int
     ): Boolean = extensionCharacteristics.isPostviewAvailable(extension)
 
     @JvmStatic
-    @DoNotInline
     fun getPostviewSupportedSizes(
         extensionCharacteristics: CameraExtensionCharacteristics,
         extension: Int,
@@ -565,7 +506,6 @@
         extensionCharacteristics.getPostviewSupportedSizes(extension, captureSize, format)
 
     @JvmStatic
-    @DoNotInline
     fun setPostviewOutputConfiguration(
         extensionSessionConfiguration: ExtensionSessionConfiguration,
         postviewOutputConfiguration: OutputConfiguration
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt
index c2eadfc..67a2ed2 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Backend.kt
@@ -21,6 +21,7 @@
 import androidx.camera.camera2.pipe.CameraContext
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.CameraStatusMonitor.CameraStatus
@@ -96,6 +97,7 @@
 
     override fun createCameraController(
         cameraContext: CameraContext,
+        graphId: CameraGraphId,
         graphConfig: CameraGraph.Config,
         graphListener: GraphListener,
         streamGraph: StreamGraph
@@ -106,6 +108,7 @@
                 .camera2ControllerConfig(
                     Camera2ControllerConfig(
                         this,
+                        graphId,
                         graphConfig,
                         graphListener,
                         streamGraph as StreamGraphImpl
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
index a18ec53..f8473a0 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraController.kt
@@ -23,6 +23,7 @@
 import androidx.camera.camera2.pipe.CameraController.ControllerState
 import androidx.camera.camera2.pipe.CameraError
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraStatusMonitor.CameraStatus
 import androidx.camera.camera2.pipe.CameraSurfaceManager
@@ -54,16 +55,17 @@
 constructor(
     private val scope: CoroutineScope,
     private val threads: Threads,
-    private val config: CameraGraph.Config,
+    private val graphConfig: CameraGraph.Config,
     private val graphListener: GraphListener,
     private val captureSessionFactory: CaptureSessionFactory,
     private val captureSequenceProcessorFactory: Camera2CaptureSequenceProcessorFactory,
     private val virtualCameraManager: VirtualCameraManager,
     private val cameraSurfaceManager: CameraSurfaceManager,
     private val timeSource: TimeSource,
+    override val cameraGraphId: CameraGraphId
 ) : CameraController {
     override val cameraId: CameraId
-        get() = config.camera
+        get() = graphConfig.camera
 
     private val lock = Any()
 
@@ -95,8 +97,8 @@
             lastCameraError = null
             val camera =
                 virtualCameraManager.open(
-                    config.camera,
-                    config.sharedCameraIds,
+                    graphConfig.camera,
+                    graphConfig.sharedCameraIds,
                     graphListener,
                 ) { _ ->
                     isForeground
@@ -119,7 +121,7 @@
                     captureSequenceProcessorFactory,
                     cameraSurfaceManager,
                     timeSource,
-                    config.flags,
+                    graphConfig.flags,
                     scope
                 )
             currentSession = session
@@ -207,7 +209,7 @@
             currentCameraStateJob = null
 
             disconnectSessionAndCamera(session, camera)
-            if (config.flags.quirkCloseCameraDeviceOnClose) {
+            if (graphConfig.flags.quirkCloseCameraDeviceOnClose) {
                 Log.debug { "Quirk: Closing all camera devices" }
                 virtualCameraManager.closeAll()
             }
@@ -291,14 +293,12 @@
                 session?.disconnect()
                 camera?.disconnect()
             }
-        if (config.flags.quirkCloseCaptureSessionOnDisconnect) {
+        if (graphConfig.flags.quirkCloseCaptureSessionOnDisconnect) {
             // It seems that on certain devices, CameraCaptureSession.close() can block for an
             // extended period of time [1]. Wrap the await call with a timeout to prevent us from
             // getting blocked for too long.
             //
-            // [1] b/307594946 - [ANR] at
-            //
-            // androidx.camera.camera2.pipe.compat.Camera2CameraController.disconnectSessionAndCamera
+            // [1] b/307594946 - [ANR] at Camera2CameraController.disconnectSessionAndCamera
             runBlockingWithTimeout(threads.backgroundDispatcher, CLOSE_CAPTURE_SESSION_TIMEOUT_MS) {
                 deferred.await()
             }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
index bfefa22..89b4e1e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
@@ -196,7 +196,8 @@
 
             // Create high speed capture requests if session is a high speed session
             if (session is CameraConstrainedHighSpeedCaptureSessionWrapper) {
-                val highSpeedRequestList = session.createHighSpeedRequestList(captureRequest)
+                val highSpeedRequestList =
+                    session.createHighSpeedRequestList(captureRequest) ?: return null
 
                 // Check if video stream use case or hint is present
                 val containsVideoStream =
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
index aa8dfd6..8f405d7 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
@@ -23,7 +23,6 @@
 import android.os.Build
 import android.os.Handler
 import android.view.Surface
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.UnsafeWrapper
 import androidx.camera.camera2.pipe.core.Debug
@@ -140,8 +139,7 @@
      * @param request A capture list.
      * @return A list of high speed requests.
      */
-    @Throws(ObjectUnavailableException::class)
-    fun createHighSpeedRequestList(request: CaptureRequest): List<CaptureRequest>
+    fun createHighSpeedRequestList(request: CaptureRequest): List<CaptureRequest>?
 }
 
 internal class AndroidCaptureSessionStateCallback(
@@ -245,7 +243,6 @@
 
     @RequiresApi(Build.VERSION_CODES.O)
     private object Api26CompatImpl {
-        @DoNotInline
         @JvmStatic
         fun onCaptureQueueEmpty(
             session: CameraCaptureSession,
@@ -370,8 +367,7 @@
 ) :
     AndroidCameraCaptureSession(device, session, cameraErrorListener, callbackHandler),
     CameraConstrainedHighSpeedCaptureSessionWrapper {
-    @Throws(ObjectUnavailableException::class)
-    override fun createHighSpeedRequestList(request: CaptureRequest): List<CaptureRequest> =
+    override fun createHighSpeedRequestList(request: CaptureRequest): List<CaptureRequest>? =
         try {
             // This converts a single CaptureRequest into a list of CaptureRequest(s) that must be
             // submitted together during high speed recording.
@@ -385,7 +381,7 @@
             // happen during normal operation of the camera, log and rethrow the error as a standard
             // exception that can be ignored.
             Log.warn { "Failed to createHighSpeedRequestList. $device may be closed." }
-            throw ObjectUnavailableException(e)
+            null
         } catch (e: IllegalArgumentException) {
 
             // b/111749845: If the surface (such as the viewfinder) is destroyed before calling
@@ -396,7 +392,7 @@
                 "Failed to createHighSpeedRequestList from $device because the output surface" +
                     " was destroyed before calling createHighSpeedRequestList."
             }
-            throw ObjectUnavailableException(e)
+            null
         }
 
     @Suppress("UNCHECKED_CAST")
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
index c8c6c9f..7a824d9 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
@@ -22,6 +22,7 @@
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraStatusMonitor
 import androidx.camera.camera2.pipe.CaptureSequence
@@ -40,6 +41,7 @@
 import kotlinx.atomicfu.atomic
 
 class ExternalCameraController(
+    private val graphId: CameraGraphId,
     private val graphConfig: CameraGraph.Config,
     private val graphListener: GraphListener,
     private val requestProcessor: RequestProcessor
@@ -52,6 +54,9 @@
     override val cameraId: CameraId
         get() = graphConfig.camera
 
+    override val cameraGraphId: CameraGraphId
+        get() = graphId
+
     override var isForeground = false
 
     override fun start() {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt
index 1690d1e..589e929 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/Camera2Component.kt
@@ -19,6 +19,7 @@
 import androidx.camera.camera2.pipe.CameraBackend
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraStatusMonitor
 import androidx.camera.camera2.pipe.StreamGraph
 import androidx.camera.camera2.pipe.compat.AudioRestrictionController
@@ -114,12 +115,15 @@
 @Module
 internal class Camera2ControllerConfig(
     private val cameraBackend: CameraBackend,
+    private val graphId: CameraGraphId,
     private val graphConfig: CameraGraph.Config,
     private val graphListener: GraphListener,
     private val streamGraph: StreamGraph,
 ) {
     @Provides fun provideCameraGraphConfig() = graphConfig
 
+    @Provides fun provideCameraGraphId() = graphId
+
     @Provides fun provideCameraBackend() = cameraBackend
 
     @Provides fun provideStreamGraph() = streamGraph as StreamGraphImpl
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraGraphComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraGraphComponent.kt
index 6ed7df6..06ee0f1 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraGraphComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraGraphComponent.kt
@@ -22,12 +22,12 @@
 import androidx.camera.camera2.pipe.CameraContext
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.CameraSurfaceManager
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.StreamGraph
 import androidx.camera.camera2.pipe.core.Threads
-import androidx.camera.camera2.pipe.graph.CameraGraphId
 import androidx.camera.camera2.pipe.graph.CameraGraphImpl
 import androidx.camera.camera2.pipe.graph.GraphListener
 import androidx.camera.camera2.pipe.graph.GraphProcessor
@@ -196,6 +196,7 @@
         @CameraGraphScope
         @Provides
         fun provideCameraController(
+            graphId: CameraGraphId,
             graphConfig: CameraGraph.Config,
             cameraBackend: CameraBackend,
             cameraContext: CameraContext,
@@ -204,6 +205,7 @@
         ): CameraController {
             return cameraBackend.createCameraController(
                 cameraContext,
+                graphId,
                 graphConfig,
                 graphProcessor,
                 streamGraph
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt
index e7e36d6..6c16c76 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/CameraPipeComponent.kt
@@ -38,6 +38,8 @@
 import androidx.camera.camera2.pipe.core.TimeSource
 import androidx.camera.camera2.pipe.internal.CameraBackendsImpl
 import androidx.camera.camera2.pipe.internal.CameraDevicesImpl
+import androidx.camera.camera2.pipe.media.ImageReaderImageSources
+import androidx.camera.camera2.pipe.media.ImageSources
 import dagger.Binds
 import dagger.Component
 import dagger.Module
@@ -159,6 +161,17 @@
             return CameraBackendsImpl(defaultBackendId, allBackends, cameraPipeContext, threads)
         }
 
+        @Provides
+        fun configureImageSources(
+            imageReaderImageSources: ImageReaderImageSources,
+            cameraPipeConfig: CameraPipe.Config
+        ): ImageSources {
+            if (cameraPipeConfig.imageSources != null) {
+                return cameraPipeConfig.imageSources
+            }
+            return imageReaderImageSources
+        }
+
         @Singleton @Provides fun provideCameraSurfaceManager() = CameraSurfaceManager()
 
         @Singleton
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
index 8d08dd9..09dbcec 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
@@ -24,6 +24,7 @@
 import androidx.camera.camera2.pipe.CameraContext
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.CameraStatusMonitor
@@ -53,7 +54,7 @@
 
 @Module
 internal class ExternalCameraGraphConfigModule(
-    private val config: CameraGraph.Config,
+    private val graphConfig: CameraGraph.Config,
     private val cameraMetadata: CameraMetadata,
     private val requestProcessor: RequestProcessor
 ) {
@@ -91,6 +92,7 @@
 
             override fun createCameraController(
                 cameraContext: CameraContext,
+                graphId: CameraGraphId,
                 graphConfig: CameraGraph.Config,
                 graphListener: GraphListener,
                 streamGraph: StreamGraph
@@ -118,14 +120,17 @@
                 throw UnsupportedOperationException("External CameraPipe should not use backends")
         }
 
-    @Provides fun provideCameraGraphConfig(): CameraGraph.Config = config
+    @Provides fun provideCameraGraphConfig(): CameraGraph.Config = graphConfig
 
     @Provides fun provideCameraMetadata(): CameraMetadata = cameraMetadata
 
     @CameraGraphScope
     @Provides
-    fun provideGraphController(graphListener: GraphListener): CameraController =
-        ExternalCameraController(config, graphListener, requestProcessor)
+    fun provideGraphController(
+        graphId: CameraGraphId,
+        graphListener: GraphListener
+    ): CameraController =
+        ExternalCameraController(graphId, graphConfig, graphListener, requestProcessor)
 
     @CameraGraphScope @Provides fun provideCameraBackend(): CameraBackend = externalCameraBackend
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraPipeComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraPipeComponent.kt
index a6c06dd..1528c71 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraPipeComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraPipeComponent.kt
@@ -19,6 +19,8 @@
 import androidx.camera.camera2.pipe.CameraSurfaceManager
 import androidx.camera.camera2.pipe.compat.AudioRestrictionController
 import androidx.camera.camera2.pipe.compat.AudioRestrictionControllerImpl
+import androidx.camera.camera2.pipe.media.ImageReaderImageSources
+import androidx.camera.camera2.pipe.media.ImageSources
 import dagger.Binds
 import dagger.Component
 import dagger.Module
@@ -41,4 +43,7 @@
     abstract fun bindAudioRestrictionController(
         audioRestrictionController: AudioRestrictionControllerImpl
     ): AudioRestrictionController
+
+    @Binds
+    abstract fun bindImageSources(imageReaderImageSources: ImageReaderImageSources): ImageSources
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphId.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphId.kt
deleted file mode 100644
index 80040f7..0000000
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphId.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.camera2.pipe.graph
-
-import androidx.camera.camera2.pipe.CameraGraph
-import kotlinx.atomicfu.atomic
-
-/**
- * Identifier for a specific [CameraGraph] that can be used to standardize toString methods and as a
- * key in maps without holding a reference to a [CameraGraph] object, which could lead to accidental
- * memory leaks and circular dependencies.
- */
-internal class CameraGraphId private constructor(private val name: String) {
-    override fun toString(): String = name
-
-    companion object {
-        private val cameraGraphIds = atomic(0)
-
-        /**
-         * Create the next CameraGraphId based on a global incrementing counter. This is
-         * intentionally worded as "CameraGraph" instead of "CameraGraphId" since it is used
-         * directly as the toString representation for a [CameraGraph].
-         */
-        fun nextId(): CameraGraphId {
-            return CameraGraphId("CameraGraph-${cameraGraphIds.incrementAndGet()}")
-        }
-    }
-}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt
index 3b93611..9a75783 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphImpl.kt
@@ -22,6 +22,7 @@
 import androidx.camera.camera2.pipe.CameraBackend
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.GraphState
 import androidx.camera.camera2.pipe.StreamGraph
@@ -55,7 +56,6 @@
 constructor(
     graphConfig: CameraGraph.Config,
     metadata: CameraMetadata,
-    private val cameraGraphId: CameraGraphId,
     private val graphLifecycleManager: GraphLifecycleManager,
     private val graphProcessor: GraphProcessor,
     private val graphListener: GraphListener,
@@ -67,7 +67,8 @@
     private val listener3A: Listener3A,
     private val frameDistributor: FrameDistributor,
     private val frameCaptureQueue: FrameCaptureQueue,
-    private val audioRestrictionController: AudioRestrictionController
+    private val audioRestrictionController: AudioRestrictionController,
+    override val id: CameraGraphId
 ) : CameraGraph {
     private val sessionMutex = Mutex()
     private val controller3A = Controller3A(graphProcessor, metadata, graphState3A, listener3A)
@@ -237,5 +238,5 @@
         }
     }
 
-    override fun toString(): String = cameraGraphId.toString()
+    override fun toString(): String = id.toString()
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
index d21e542..858f8e2 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import androidx.annotation.GuardedBy
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CaptureSequenceProcessor
 import androidx.camera.camera2.pipe.FrameInfo
 import androidx.camera.camera2.pipe.FrameNumber
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt
index cd98d81..76885a4 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/SurfaceGraph.kt
@@ -32,7 +32,7 @@
  * most up to date version to the [CameraController] instance.
  */
 internal class SurfaceGraph(
-    private val streamGraph: StreamGraphImpl,
+    private val streamGraphImpl: StreamGraphImpl,
     private val cameraController: CameraController,
     private val surfaceManager: CameraSurfaceManager,
     private val imageSources: Map<StreamId, ImageSource>
@@ -70,8 +70,7 @@
 
                 if (surface == null) {
                     // TODO: Tell the graph processor that it should resubmit the repeating request
-                    // or
-                    //  reconfigure the camera2 captureSession
+                    // or reconfigure the camera2 captureSession
                     val oldSurface = surfaceMap.remove(streamId)
                     if (oldSurface != null) {
                         oldSurfaceToken = surfaceUsageMap.remove(oldSurface)
@@ -129,14 +128,13 @@
     private fun buildSurfaceMap(): Map<StreamId, Surface> =
         synchronized(lock) {
             val surfaces = mutableMapOf<StreamId, Surface>()
-            for (outputConfig in streamGraph.outputConfigs) {
+            for (outputConfig in streamGraphImpl.outputConfigs) {
                 for (stream in outputConfig.streamBuilder) {
                     val surface = surfaceMap[stream.id]
                     if (surface == null) {
                         if (!outputConfig.deferrable) {
                             // If output is non-deferrable, a surface must be available or the
-                            // config
-                            // is not yet valid. Exit now with an empty map.
+                            // config is not yet valid. Exit now with an empty map.
                             return emptyMap()
                         }
                     } else {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/ImageSourceMap.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/ImageSourceMap.kt
index c3fa23f..d2c8973 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/ImageSourceMap.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/ImageSourceMap.kt
@@ -20,34 +20,21 @@
 import androidx.camera.camera2.pipe.StreamGraph
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.config.CameraGraphScope
-import androidx.camera.camera2.pipe.core.Threads
 import androidx.camera.camera2.pipe.media.ImageSource
+import androidx.camera.camera2.pipe.media.ImageSources
 import javax.inject.Inject
 
 @CameraGraphScope
 internal class ImageSourceMap
 @Inject
-constructor(graphConfig: CameraGraph.Config, streamGraph: StreamGraph, threads: Threads) {
-    val imageSources: Map<StreamId, ImageSource>
+constructor(graphConfig: CameraGraph.Config, streamGraph: StreamGraph, imageSources: ImageSources) {
+    val imageSources: Map<StreamId, ImageSource> = buildMap {
+        for (config in graphConfig.streams) {
+            val imageSourceConfig = config.imageSourceConfig ?: continue
 
-    init {
-        imageSources = buildMap {
-            for (config in graphConfig.streams) {
-                val imageStream = config.imageSourceConfig ?: continue
-                val cameraStream = streamGraph[config]!!
-
-                val imageSource =
-                    ImageSource.create(
-                        cameraStream,
-                        imageStream.capacity,
-                        imageStream.usageFlags,
-                        imageStream.defaultDataSpace,
-                        imageStream.defaultHardwareBufferFormat,
-                        { threads.camera2Handler },
-                        { threads.lightweightExecutor }
-                    )
-                this[cameraStream.id] = imageSource
-            }
+            val cameraStream = streamGraph[config]!!
+            val imageSource = imageSources.createImageSource(cameraStream, imageSourceConfig)
+            this[cameraStream.id] = imageSource
         }
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderImageSource.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderImageSource.kt
new file mode 100644
index 0000000..c9bad0b
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderImageSource.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.media
+
+import android.media.ImageReader
+import android.os.Build
+import android.view.Surface
+import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.ImageSourceConfig
+import androidx.camera.camera2.pipe.OutputId
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.core.Threads
+import androidx.camera.camera2.pipe.media.AndroidImageReader.Companion.IMAGEREADER_MAX_CAPACITY
+import androidx.camera.camera2.pipe.media.ImageReaderImageSource.Companion.IMAGE_SOURCE_CAPACITY
+import javax.inject.Inject
+import kotlin.reflect.KClass
+import kotlinx.atomicfu.atomic
+
+internal class ImageReaderImageSources @Inject constructor(private val threads: Threads) :
+    ImageSources {
+    override fun createImageSource(
+        cameraStream: CameraStream,
+        imageSourceConfig: ImageSourceConfig
+    ): ImageSource {
+        return create(
+            cameraStream,
+            imageSourceConfig.capacity,
+            imageSourceConfig.usageFlags,
+            imageSourceConfig.defaultDataSpace,
+            imageSourceConfig.defaultHardwareBufferFormat
+        )
+    }
+
+    fun create(
+        cameraStream: CameraStream,
+        capacity: Int,
+        usageFlags: Long?,
+        defaultDataSpace: Int?,
+        defaultHardwareBufferFormat: Int?
+    ): ImageSource {
+        require(cameraStream.outputs.isNotEmpty()) { "$cameraStream must have outputs." }
+        require(capacity > 0) { "Capacity ($capacity) must be > 0" }
+        require(capacity <= IMAGE_SOURCE_CAPACITY) {
+            "Capacity for creating new ImageReaderImageSources is restricted to " +
+                "$IMAGE_SOURCE_CAPACITY. Android has undocumented internal limits that can vary " +
+                "per device."
+        }
+
+        val handlerProvider = { threads.camera2Handler }
+        val executorProvider = { threads.lightweightExecutor }
+
+        // Increase the internal capacity of the ImageReader so that the final capacity of the
+        // ImageSource matches the requested capacity.
+        //
+        // As an example, if the consumer requests "40", the ImageReader will be created with
+        // a capacity of "42", which will allow the consumer to hold exactly 40 images without
+        // stalling the camera pipeline.
+        val imageReaderCapacity = capacity + IMAGE_SOURCE_CAPACITY
+
+        if (cameraStream.outputs.size == 1) {
+            val output = cameraStream.outputs.single()
+            val handler = handlerProvider()
+            val imageReader =
+                AndroidImageReader.create(
+                    output.size.width,
+                    output.size.height,
+                    output.format.value,
+                    imageReaderCapacity,
+                    usageFlags,
+                    defaultDataSpace,
+                    defaultHardwareBufferFormat,
+                    cameraStream.id,
+                    output.id,
+                    handler
+                )
+            return ImageReaderImageSource.create(imageReader)
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            if (usageFlags != null) {
+                Log.warn {
+                    "Ignoring usageFlags ($usageFlags) " +
+                        "for $cameraStream. MultiResolutionImageReader does not support " +
+                        "setting usage flags."
+                }
+            }
+            if (defaultDataSpace != null) {
+                Log.warn {
+                    "Ignoring DataSpace ($defaultDataSpace) " +
+                        "for $cameraStream. MultiResolutionImageReader does not support " +
+                        "setting the default DataSpace."
+                }
+            }
+            if (defaultHardwareBufferFormat != null) {
+                Log.warn {
+                    "Ignoring HardwareBufferFormat ($defaultHardwareBufferFormat) " +
+                        "for $cameraStream. MultiResolutionImageReader does not support " +
+                        "setting the default HardwareBufferFormat."
+                }
+            }
+            val imageReader =
+                AndroidMultiResolutionImageReader.create(cameraStream, capacity, executorProvider())
+            return ImageReaderImageSource.create(imageReader)
+        }
+
+        // If we reach this point, it's likely the user asked for MultiResolutionImageReader
+        // but it was not possible to create it due to the SDK the code is running on.
+        throw IllegalStateException("Failed to create an ImageSource for $cameraStream!")
+    }
+}
+
+/** An ImageReaderImageSource implements an [ImageSource] using an [ImageReader] */
+class ImageReaderImageSource(
+    private val imageReader: ImageReaderWrapper,
+    private val maxImages: Int,
+) : ImageSource {
+    companion object {
+        private const val IMAGE_SOURCE_CAPACITY_MARGIN = 2
+        const val IMAGE_SOURCE_CAPACITY = IMAGEREADER_MAX_CAPACITY - IMAGE_SOURCE_CAPACITY_MARGIN
+
+        fun create(imageReader: ImageReaderWrapper): ImageSource {
+            // Reduce the maxImages of the ImageSource relative to the ImageReader to ensure there
+            // is enough headroom to avoid acquiring too many images that could otherwise stall the
+            // camera or trigger IllegalStateExceptions from the underlying ImageReader.
+            val maxImages = imageReader.capacity - IMAGE_SOURCE_CAPACITY_MARGIN
+            return ImageReaderImageSource(imageReader, maxImages)
+        }
+    }
+
+    private val state = atomic(State.ACTIVE)
+    private val listener = atomic<ImageSourceListener?>(null)
+    private val imageCount = atomic(0)
+
+    override val surface: Surface = imageReader.surface
+
+    init {
+        imageReader.setOnImageListener(::onImage)
+    }
+
+    override fun setListener(listener: ImageSourceListener) {
+        this.listener.value = listener
+    }
+
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? = imageReader.unwrapAs(type)
+
+    override fun close() {
+        // If this is the first time this is invoked, update the state from ACTIVE to CLOSING and
+        // flush unused images from the pool. This does *not* immediately close the underlying
+        // ImageReader unless there are no outstanding images.
+        if (state.compareAndSet(expect = State.ACTIVE, update = State.CLOSING)) {
+            flushOrCloseIfEmpty()
+        }
+    }
+
+    override fun toString(): String = "ImageSource($imageReader)"
+
+    private fun onImage(streamId: StreamId, outputId: OutputId, image: ImageWrapper) {
+        // Always increment the imageCount before acquireNextImage
+        val currentImageCount = imageCount.incrementAndGet()
+
+        val outputListener = listener.value
+        if (outputListener == null) {
+            // If there is nowhere to send the image, close it and decrement the imageCount.
+            closeAndDecrementImageCount(image)
+            return
+        }
+
+        if (currentImageCount > maxImages || state.value != State.ACTIVE) {
+            // If there are too many images that are currently being held or the ImageSource is in
+            // a CLOSING or CLOSED state: close the image, decrement the imageCount, and let the
+            // outputListener know that an image was received but that it was dropped (by passing
+            // null for the image).
+            val outputTimestamp = image.timestamp
+            closeAndDecrementImageCount(image)
+            outputListener.onImage(streamId, outputId, outputTimestamp, null)
+            return
+        }
+
+        // Wrap and track the image, and pass it along to the outputListener, which is now
+        // responsible for closing the image when it is done with it.
+        outputListener.onImage(
+            streamId,
+            outputId,
+            image.timestamp,
+            TrackedOutputImage(image, streamId, outputId)
+        )
+    }
+
+    internal fun closeAndDecrementImageCount(image: ImageWrapper) {
+        // This must called *exactly* once for each image that is closed.
+        image.close()
+        imageCount.decrementAndGet()
+        if (state.value != State.ACTIVE) {
+            flushOrCloseIfEmpty()
+        }
+    }
+
+    private fun flushOrCloseIfEmpty() {
+        // Assumption: This method is ONLY called if the current state is CLOSING or CLOSED.
+        if (state.value == State.CLOSED) {
+            return
+        }
+
+        // If the imageCount is zero, or has just reached zero, update the state to CLOSED and call
+        // close on the imageReader exactly once.
+        if (imageCount.value == 0) {
+            if (state.compareAndSet(State.CLOSING, State.CLOSED)) {
+                imageReader.close()
+            }
+            return
+        }
+
+        // If we reach this point, this ImageSource is CLOSING. Actively flush and discard free
+        // buffers to reduce memory usage as individual images are closed.
+        imageReader.flush()
+    }
+
+    private inner class TrackedOutputImage(
+        private val image: ImageWrapper,
+        override val streamId: StreamId,
+        override val outputId: OutputId
+    ) : ImageWrapper by image, OutputImage {
+        private val closed = atomic(false)
+
+        override fun close() {
+            if (closed.compareAndSet(expect = false, update = true)) {
+                // Close underlying image exactly once, and close it *before* decrementImageCount
+                // to ensure the imageCount does not get out of sync.
+                closeAndDecrementImageCount(image)
+            }
+        }
+
+        protected fun finalize() {
+            // https://kotlinlang.org/docs/java-interop.html#finalize
+            // Wrapper images that are no longer reachable should be closed to avoid memory leaks.
+            close()
+        }
+    }
+
+    private enum class State {
+        ACTIVE,
+        CLOSING,
+        CLOSED
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt
index 270b53e..cf2082c 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,21 +18,15 @@
 
 import android.hardware.camera2.MultiResolutionImageReader
 import android.media.ImageReader
-import android.os.Build
-import android.os.Handler
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.ImageSourceConfig
 import androidx.camera.camera2.pipe.OutputId
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.UnsafeWrapper
-import androidx.camera.camera2.pipe.core.Log
-import androidx.camera.camera2.pipe.media.AndroidImageReader.Companion.IMAGEREADER_MAX_CAPACITY
-import java.util.concurrent.Executor
-import kotlin.reflect.KClass
-import kotlinx.atomicfu.atomic
 
 /**
- * An ImageSource produces images from a camera via the [ImageSourceListener].
+ * An ImageSource produces images from a CameraStream via an [ImageSourceListener].
  *
  * This interface is an abstraction over [ImageReader] and [MultiResolutionImageReader] that is
  * designed to eliminate several subtle and difficult to avoid pitfalls that can occur when using
@@ -62,219 +56,23 @@
     val surface: Surface
 
     fun setListener(listener: ImageSourceListener)
-
-    companion object {
-        private const val IMAGE_CAPACITY_MARGIN = 2
-        private const val IMAGE_SOURCE_CAPACITY = IMAGEREADER_MAX_CAPACITY - IMAGE_CAPACITY_MARGIN
-
-        fun create(imageReader: ImageReaderWrapper): ImageSource {
-            // Reduce the maxImages of the ImageSource relative to the ImageReader to ensure there
-            // is enough headroom to avoid acquiring too many images that could otherwise stall the
-            // camera or trigger IllegalStateExceptions from the underlying ImageReader.
-            val maxImages = imageReader.capacity - IMAGE_CAPACITY_MARGIN
-            return ImageSourceImpl(imageReader, maxImages)
-        }
-
-        fun create(
-            cameraStream: CameraStream,
-            capacity: Int,
-            usageFlags: Long?,
-            defaultDataSpace: Int?,
-            defaultHardwareBufferFormat: Int?,
-            handlerProvider: () -> Handler,
-            executorProvider: () -> Executor
-        ): ImageSource {
-            require(cameraStream.outputs.isNotEmpty()) { "$cameraStream must have outputs." }
-            require(capacity > 0) { "Capacity ($capacity) must be > 0" }
-            require(capacity <= IMAGE_SOURCE_CAPACITY) {
-                "Capacity for creating new ImageSources is restricted to $IMAGE_SOURCE_CAPACITY." +
-                    "Android has undocumented internal limits that can vary per device."
-            }
-
-            // Increase the internal capacity of the ImageReader so that the final capacity of the
-            // ImageSource matches the requested capacity.
-            //
-            // As an example, if the consumer requests "40", the ImageReader will be created with
-            // a capacity of "42", which will allow the consumer to hold exactly 40 images without
-            // stalling the camera pipeline.
-            val imageReaderCapacity = capacity + IMAGE_CAPACITY_MARGIN
-
-            if (cameraStream.outputs.size == 1) {
-                val output = cameraStream.outputs.single()
-                val handler = handlerProvider()
-                val imageReader =
-                    AndroidImageReader.create(
-                        output.size.width,
-                        output.size.height,
-                        output.format.value,
-                        imageReaderCapacity,
-                        usageFlags,
-                        defaultDataSpace,
-                        defaultHardwareBufferFormat,
-                        cameraStream.id,
-                        output.id,
-                        handler
-                    )
-                return create(imageReader)
-            }
-
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
-                if (usageFlags != null) {
-                    Log.warn {
-                        "Ignoring usageFlags ($usageFlags) " +
-                            "for $cameraStream. MultiResolutionImageReader does not support " +
-                            "setting usage flags."
-                    }
-                }
-                if (defaultDataSpace != null) {
-                    Log.warn {
-                        "Ignoring DataSpace ($defaultDataSpace) " +
-                            "for $cameraStream. MultiResolutionImageReader does not support " +
-                            "setting the default DataSpace."
-                    }
-                }
-                if (defaultHardwareBufferFormat != null) {
-                    Log.warn {
-                        "Ignoring HardwareBufferFormat ($defaultHardwareBufferFormat) " +
-                            "for $cameraStream. MultiResolutionImageReader does not support " +
-                            "setting the default HardwareBufferFormat."
-                    }
-                }
-                val imageReader =
-                    AndroidMultiResolutionImageReader.create(
-                        cameraStream,
-                        capacity,
-                        executorProvider()
-                    )
-                return create(imageReader)
-            }
-
-            // If we reach this point, it's likely the user asked for MultiResolutionImageReader
-            // but it was not possible to create it due to the SDK the code is running on.
-            throw IllegalStateException("Failed to create an ImageSource for $cameraStream!")
-        }
-    }
 }
 
 /** Listener for handling [ImageWrapper]s as they are produced. */
 fun interface ImageSourceListener {
     /**
      * Handle the next image from the [ImageSource]. Implementations *must* close the [image] when
-     * they are done with it. Receiving a null [image] indicates the underlying ImageSource is full,
-     * and that the image was dropped to avoid stalling the pipeline.
+     * they are done with it. Receiving a null [image] indicates the that an image was produced, but
+     * that this image source is at capacity and that the image was dropped and closed to avoid
+     * stalling the camera.
      */
     fun onImage(streamId: StreamId, outputId: OutputId, outputTimestamp: Long, image: ImageWrapper?)
 }
 
-/** An ImageReaderImageSource implements an [ImageSource] using an [ImageReader] */
-internal class ImageSourceImpl(
-    private val imageReader: ImageReaderWrapper,
-    private val maxImages: Int,
-) : ImageSource {
-    private val state = atomic(State.ACTIVE)
-    private val listener = atomic<ImageSourceListener?>(null)
-    private val imageCount = atomic(0)
-
-    override val surface: Surface = imageReader.surface
-
-    init {
-        imageReader.setOnImageListener(::onImage)
-    }
-
-    override fun setListener(listener: ImageSourceListener) {
-        this.listener.value = listener
-    }
-
-    override fun <T : Any> unwrapAs(type: KClass<T>): T? = imageReader.unwrapAs(type)
-
-    override fun close() {
-        // If this is the first time this is invoked, update the state from ACTIVE to CLOSING and
-        // flush unused images from the pool. This does *not* immediately close the underlying
-        // ImageReader unless there are no outstanding images.
-        if (state.compareAndSet(expect = State.ACTIVE, update = State.CLOSING)) {
-            flushOrCloseIfEmpty()
-        }
-    }
-
-    override fun toString(): String = "ImageSource($imageReader)"
-
-    private fun onImage(streamId: StreamId, outputId: OutputId, image: ImageWrapper) {
-        // Always increment the imageCount before acquireNextImage
-        val currentImageCount = imageCount.incrementAndGet()
-
-        val outputListener = listener.value
-        if (outputListener == null) {
-            // If there is nowhere to send the image, close it and decrement the imageCount.
-            closeAndDecrementImageCount(image)
-            return
-        }
-
-        if (currentImageCount > maxImages || state.value != State.ACTIVE) {
-            // If there are too many images that are currently being held or the ImageSource is in
-            // a CLOSING or CLOSED state: close the image, decrement the imageCount, and let the
-            // outputListener know that an image was received but that it was dropped (by passing
-            // null for the image).
-            val outputTimestamp = image.timestamp
-            closeAndDecrementImageCount(image)
-            outputListener.onImage(streamId, outputId, outputTimestamp, null)
-            return
-        }
-
-        // Wrap and track the image, and pass it along to the outputListener, which is now
-        // responsible for closing the image when it is done with it.
-        outputListener.onImage(streamId, outputId, image.timestamp, TrackedImage(image))
-    }
-
-    internal fun closeAndDecrementImageCount(image: ImageWrapper) {
-        // This must called *exactly* once for each image that is closed.
-        image.close()
-        imageCount.decrementAndGet()
-        if (state.value != State.ACTIVE) {
-            flushOrCloseIfEmpty()
-        }
-    }
-
-    private fun flushOrCloseIfEmpty() {
-        // Assumption: This method is ONLY called if the current state is CLOSING or CLOSED.
-        if (state.value == State.CLOSED) {
-            return
-        }
-
-        // If the imageCount is zero, or has just reached zero, update the state to CLOSED and call
-        // close on the imageReader exactly once.
-        if (imageCount.value == 0) {
-            if (state.compareAndSet(State.CLOSING, State.CLOSED)) {
-                imageReader.close()
-            }
-            return
-        }
-
-        // If we reach this point, this ImageSource is CLOSING. Actively flush and discard free
-        // buffers to reduce memory usage as individual images are closed.
-        imageReader.flush()
-    }
-
-    private inner class TrackedImage(private val image: ImageWrapper) : ImageWrapper by image {
-        private val closed = atomic(false)
-
-        override fun close() {
-            if (closed.compareAndSet(expect = false, update = true)) {
-                // Close underlying image exactly once, and close it *before* decrementImageCount
-                // to ensure the imageCount does not get out of sync.
-                closeAndDecrementImageCount(image)
-            }
-        }
-
-        protected fun finalize() {
-            // https://kotlinlang.org/docs/java-interop.html#finalize
-            // Wrapper images that are no longer reachable should be closed to avoid memory leaks.
-            close()
-        }
-    }
-
-    private enum class State {
-        ACTIVE,
-        CLOSING,
-        CLOSED
-    }
+/** Provider for creating an [ImageSource] based on an [ImageSourceConfig] */
+fun interface ImageSources {
+    fun createImageSource(
+        cameraStream: CameraStream,
+        imageSourceConfig: ImageSourceConfig,
+    ): ImageSource
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt
index 98c6d0d..804b580 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt
@@ -30,6 +30,7 @@
 
     companion object {
         fun from(streamId: StreamId, outputId: OutputId, image: ImageWrapper): OutputImage {
+            if (image is OutputImage) return image
             return OutputImageImpl(streamId, outputId, image)
         }
 
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt
index 78ebf5d..d507cda 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/CameraGraphImplTest.kt
@@ -25,6 +25,7 @@
 import android.util.Size
 import androidx.camera.camera2.pipe.CameraBackendFactory
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraStream
 import androidx.camera.camera2.pipe.CameraSurfaceManager
 import androidx.camera.camera2.pipe.Request
@@ -34,6 +35,7 @@
 import androidx.camera.camera2.pipe.internal.FrameDistributor
 import androidx.camera.camera2.pipe.internal.GraphLifecycleManager
 import androidx.camera.camera2.pipe.internal.ImageSourceMap
+import androidx.camera.camera2.pipe.media.ImageReaderImageSources
 import androidx.camera.camera2.pipe.testing.CameraControllerSimulator
 import androidx.camera.camera2.pipe.testing.FakeAudioRestrictionController
 import androidx.camera.camera2.pipe.testing.FakeCameraBackend
@@ -89,6 +91,7 @@
     private val stream2Config =
         CameraStream.Config.create(Size(1920, 1080), StreamFormat.YUV_420_888)
 
+    private val graphId = CameraGraphId.nextId()
     private val graphConfig =
         CameraGraph.Config(
             camera = metadata.camera,
@@ -106,22 +109,27 @@
     private val cameraContext = CameraBackendsImpl.CameraBackendContext(context, threads, backends)
     private val graphLifecycleManager = GraphLifecycleManager(threads)
     private val streamGraph = StreamGraphImpl(metadata, graphConfig)
-    private val imageSourceMap = ImageSourceMap(graphConfig, streamGraph, threads)
+    private val imageSources = ImageReaderImageSources(threads)
+    private val imageSourceMap = ImageSourceMap(graphConfig, streamGraph, imageSources)
     private val frameCaptureQueue = FrameCaptureQueue()
     private val frameDistributor =
         FrameDistributor(imageSourceMap.imageSources, frameCaptureQueue) {}
     private val cameraController =
-        CameraControllerSimulator(cameraContext, graphConfig, fakeGraphProcessor, streamGraph)
+        CameraControllerSimulator(
+            cameraContext,
+            graphId,
+            graphConfig,
+            fakeGraphProcessor,
+            streamGraph
+        )
 
     private val surfaceGraph =
         SurfaceGraph(streamGraph, cameraController, cameraSurfaceManager, emptyMap())
     private val audioRestriction = FakeAudioRestrictionController()
-    private val cameraGraphId = CameraGraphId.nextId()
     private val cameraGraph =
         CameraGraphImpl(
             graphConfig,
             metadata,
-            cameraGraphId,
             graphLifecycleManager,
             fakeGraphProcessor,
             fakeGraphProcessor,
@@ -133,7 +141,8 @@
             Listener3A(),
             frameDistributor,
             frameCaptureQueue,
-            audioRestriction
+            audioRestriction,
+            graphId,
         )
     private val stream1: CameraStream =
         checkNotNull(cameraGraph.streams[stream1Config]) {
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/GraphProcessorTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/GraphProcessorTest.kt
index 9c5c504..d95aa17 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/GraphProcessorTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/GraphProcessorTest.kt
@@ -22,6 +22,7 @@
 import android.os.Build
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraError
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.GraphState.GraphStateStopped
 import androidx.camera.camera2.pipe.Request
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/SurfaceGraphTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/SurfaceGraphTest.kt
index 1d7be95..c1aac90 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/SurfaceGraphTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/SurfaceGraphTest.kt
@@ -19,6 +19,7 @@
 import android.graphics.SurfaceTexture
 import android.os.Build
 import android.view.Surface
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraSurfaceManager
 import androidx.camera.camera2.pipe.testing.FakeCameraController
 import androidx.camera.camera2.pipe.testing.FakeGraphConfigs
@@ -41,14 +42,16 @@
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class SurfaceGraphTest {
     private val config = FakeGraphConfigs
-    private val controller = FakeCameraController()
+    private val graphId = CameraGraphId.nextId()
+    private val fakeCameraController = FakeCameraController(graphId)
 
     private val streamMap = StreamGraphImpl(config.fakeMetadata, config.graphConfig)
 
     private val fakeSurfaceListener: CameraSurfaceManager.SurfaceListener = mock()
     private val cameraSurfaceManager =
         CameraSurfaceManager().also { it.addListener(fakeSurfaceListener) }
-    private val surfaceGraph = SurfaceGraph(streamMap, controller, cameraSurfaceManager, emptyMap())
+    private val surfaceGraph =
+        SurfaceGraph(streamMap, fakeCameraController, cameraSurfaceManager, emptyMap())
 
     private val stream1 = streamMap[config.streamConfig1]!!
     private val stream2 = streamMap[config.streamConfig2]!!
@@ -99,20 +102,20 @@
         surfaceGraph[stream9.id] = fakeSurface9
         surfaceGraph[stream10.id] = fakeSurface10
 
-        assertThat(controller.surfaceMap).isNotNull()
-        assertThat(controller.surfaceMap?.get(stream1.id)).isEqualTo(fakeSurface1)
-        assertThat(controller.surfaceMap?.get(stream2.id)).isEqualTo(fakeSurface2)
-        assertThat(controller.surfaceMap?.get(stream3.id)).isEqualTo(fakeSurface3)
-        assertThat(controller.surfaceMap?.get(stream4.id)).isEqualTo(fakeSurface4)
-        assertThat(controller.surfaceMap?.get(stream5.id)).isEqualTo(fakeSurface5)
-        assertThat(controller.surfaceMap?.get(stream6.id)).isEqualTo(fakeSurface6)
-        assertThat(controller.surfaceMap?.get(stream7.id)).isEqualTo(fakeSurface7)
-        assertThat(controller.surfaceMap?.get(stream8.id)).isEqualTo(fakeSurface8)
+        assertThat(fakeCameraController.surfaceMap).isNotNull()
+        assertThat(fakeCameraController.surfaceMap?.get(stream1.id)).isEqualTo(fakeSurface1)
+        assertThat(fakeCameraController.surfaceMap?.get(stream2.id)).isEqualTo(fakeSurface2)
+        assertThat(fakeCameraController.surfaceMap?.get(stream3.id)).isEqualTo(fakeSurface3)
+        assertThat(fakeCameraController.surfaceMap?.get(stream4.id)).isEqualTo(fakeSurface4)
+        assertThat(fakeCameraController.surfaceMap?.get(stream5.id)).isEqualTo(fakeSurface5)
+        assertThat(fakeCameraController.surfaceMap?.get(stream6.id)).isEqualTo(fakeSurface6)
+        assertThat(fakeCameraController.surfaceMap?.get(stream7.id)).isEqualTo(fakeSurface7)
+        assertThat(fakeCameraController.surfaceMap?.get(stream8.id)).isEqualTo(fakeSurface8)
     }
 
     @Test
     fun outputSurfacesArePassedToListenerWhenAvailable() {
-        assertThat(controller.surfaceMap).isNull()
+        assertThat(fakeCameraController.surfaceMap).isNull()
 
         surfaceGraph[stream1.id] = fakeSurface1
         surfaceGraph[stream2.id] = fakeSurface2
@@ -122,22 +125,22 @@
         surfaceGraph[stream6.id] = fakeSurface6
         surfaceGraph[stream7.id] = fakeSurface7
         surfaceGraph[stream8.id] = fakeSurface8
-        assertThat(controller.surfaceMap).isNull()
+        assertThat(fakeCameraController.surfaceMap).isNull()
 
         surfaceGraph[stream9.id] = fakeSurface9
         surfaceGraph[stream10.id] = fakeSurface10
 
-        assertThat(controller.surfaceMap).isNotNull()
-        assertThat(controller.surfaceMap?.get(stream1.id)).isEqualTo(fakeSurface1)
-        assertThat(controller.surfaceMap?.get(stream2.id)).isEqualTo(fakeSurface2)
-        assertThat(controller.surfaceMap?.get(stream3.id)).isEqualTo(fakeSurface3)
-        assertThat(controller.surfaceMap?.get(stream4.id)).isEqualTo(fakeSurface4)
-        assertThat(controller.surfaceMap?.get(stream5.id)).isEqualTo(fakeSurface5)
-        assertThat(controller.surfaceMap?.get(stream6.id)).isEqualTo(fakeSurface6)
-        assertThat(controller.surfaceMap?.get(stream7.id)).isEqualTo(fakeSurface7)
-        assertThat(controller.surfaceMap?.get(stream8.id)).isEqualTo(fakeSurface8)
-        assertThat(controller.surfaceMap?.get(stream9.id)).isEqualTo(fakeSurface9)
-        assertThat(controller.surfaceMap?.get(stream10.id)).isEqualTo(fakeSurface10)
+        assertThat(fakeCameraController.surfaceMap).isNotNull()
+        assertThat(fakeCameraController.surfaceMap?.get(stream1.id)).isEqualTo(fakeSurface1)
+        assertThat(fakeCameraController.surfaceMap?.get(stream2.id)).isEqualTo(fakeSurface2)
+        assertThat(fakeCameraController.surfaceMap?.get(stream3.id)).isEqualTo(fakeSurface3)
+        assertThat(fakeCameraController.surfaceMap?.get(stream4.id)).isEqualTo(fakeSurface4)
+        assertThat(fakeCameraController.surfaceMap?.get(stream5.id)).isEqualTo(fakeSurface5)
+        assertThat(fakeCameraController.surfaceMap?.get(stream6.id)).isEqualTo(fakeSurface6)
+        assertThat(fakeCameraController.surfaceMap?.get(stream7.id)).isEqualTo(fakeSurface7)
+        assertThat(fakeCameraController.surfaceMap?.get(stream8.id)).isEqualTo(fakeSurface8)
+        assertThat(fakeCameraController.surfaceMap?.get(stream9.id)).isEqualTo(fakeSurface9)
+        assertThat(fakeCameraController.surfaceMap?.get(stream10.id)).isEqualTo(fakeSurface10)
     }
 
     @Test
@@ -147,7 +150,7 @@
 
         surfaceGraph[stream1.id] = fakeSurface1A
         surfaceGraph[stream1.id] = fakeSurface1B
-        assertThat(controller.surfaceMap).isNull()
+        assertThat(fakeCameraController.surfaceMap).isNull()
 
         surfaceGraph[stream2.id] = fakeSurface2
         surfaceGraph[stream3.id] = fakeSurface3
@@ -159,17 +162,17 @@
         surfaceGraph[stream9.id] = fakeSurface9
         surfaceGraph[stream10.id] = fakeSurface10
 
-        assertThat(controller.surfaceMap).isNotNull()
-        assertThat(controller.surfaceMap?.get(stream1.id)).isEqualTo(fakeSurface1B)
-        assertThat(controller.surfaceMap?.get(stream2.id)).isEqualTo(fakeSurface2)
-        assertThat(controller.surfaceMap?.get(stream3.id)).isEqualTo(fakeSurface3)
-        assertThat(controller.surfaceMap?.get(stream4.id)).isEqualTo(fakeSurface4)
-        assertThat(controller.surfaceMap?.get(stream5.id)).isEqualTo(fakeSurface5)
-        assertThat(controller.surfaceMap?.get(stream6.id)).isEqualTo(fakeSurface6)
-        assertThat(controller.surfaceMap?.get(stream7.id)).isEqualTo(fakeSurface7)
-        assertThat(controller.surfaceMap?.get(stream8.id)).isEqualTo(fakeSurface8)
-        assertThat(controller.surfaceMap?.get(stream9.id)).isEqualTo(fakeSurface9)
-        assertThat(controller.surfaceMap?.get(stream10.id)).isEqualTo(fakeSurface10)
+        assertThat(fakeCameraController.surfaceMap).isNotNull()
+        assertThat(fakeCameraController.surfaceMap?.get(stream1.id)).isEqualTo(fakeSurface1B)
+        assertThat(fakeCameraController.surfaceMap?.get(stream2.id)).isEqualTo(fakeSurface2)
+        assertThat(fakeCameraController.surfaceMap?.get(stream3.id)).isEqualTo(fakeSurface3)
+        assertThat(fakeCameraController.surfaceMap?.get(stream4.id)).isEqualTo(fakeSurface4)
+        assertThat(fakeCameraController.surfaceMap?.get(stream5.id)).isEqualTo(fakeSurface5)
+        assertThat(fakeCameraController.surfaceMap?.get(stream6.id)).isEqualTo(fakeSurface6)
+        assertThat(fakeCameraController.surfaceMap?.get(stream7.id)).isEqualTo(fakeSurface7)
+        assertThat(fakeCameraController.surfaceMap?.get(stream8.id)).isEqualTo(fakeSurface8)
+        assertThat(fakeCameraController.surfaceMap?.get(stream9.id)).isEqualTo(fakeSurface9)
+        assertThat(fakeCameraController.surfaceMap?.get(stream10.id)).isEqualTo(fakeSurface10)
 
         fakeSurface1A.release()
         fakeSurface1B.release()
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/ImageReaderImageSourceTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/ImageReaderImageSourceTest.kt
new file mode 100644
index 0000000..804016b
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/ImageReaderImageSourceTest.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.media
+
+import android.os.Build
+import android.util.Size
+import androidx.camera.camera2.pipe.OutputId
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.camera.camera2.pipe.StreamId
+import androidx.camera.camera2.pipe.testing.FakeImage
+import androidx.camera.camera2.pipe.testing.FakeImageReader
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+/** Tests for [OutputImage] and [SharedOutputImage] */
+@RunWith(RobolectricTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class ImageSourceTest {
+    private val streamId = StreamId(32)
+    private val outputId = OutputId(42)
+    private val fakeImageSize = Size(640, 480)
+    private val fakeImageFormat = StreamFormat.YUV_420_888
+    private val fakeImageReader =
+        FakeImageReader.create(
+            format = fakeImageFormat,
+            streamId = streamId,
+            outputId = outputId,
+            size = fakeImageSize,
+            capacity = 10
+        )
+    private val imageSource = ImageReaderImageSource(fakeImageReader, fakeImageReader.capacity - 2)
+
+    @After
+    fun cleanup() {
+        fakeImageReader.close()
+    }
+
+    @Test
+    fun testImageSourceForwardsImagesFromImageReader() {
+        val testListener = TestImageSourceListener()
+        imageSource.setListener(testListener)
+        fakeImageReader.simulateImage(12345)
+
+        assertThat(testListener.onImageEvents.size).isEqualTo(1)
+        assertThat(testListener.onImageEvents[0].outputId).isEqualTo(outputId)
+        assertThat(testListener.onImageEvents[0].outputTimestamp).isEqualTo(12345)
+        assertThat(testListener.onImageEvents[0].image).isNotNull()
+    }
+
+    @Test
+    fun testImageSourceForwardsEmptyImagesAfterReachingCapacity() {
+        val testListener = TestImageSourceListener()
+        imageSource.setListener(testListener)
+
+        for (i in 0..99) {
+            fakeImageReader.simulateImage(12345 + (i * 10000L))
+        }
+
+        assertThat(testListener.onImageEvents.size).isEqualTo(100)
+        // Spot check 11th index
+        assertThat(testListener.onImageEvents[10].outputId).isEqualTo(outputId)
+        assertThat(testListener.onImageEvents[10].image).isNull()
+
+        // Spot check last index
+        assertThat(testListener.onImageEvents[99].outputId).isEqualTo(outputId)
+        assertThat(testListener.onImageEvents[99].image).isNull()
+    }
+
+    @Test
+    fun closingImagesAllowsAllImagesToBeProduced() {
+        val testListener = TestImageSourceListener()
+        imageSource.setListener(testListener)
+
+        for (i in 0..99) {
+            fakeImageReader.simulateImage(12345 + (i * 10000L))
+            testListener.onImageEvents.last().image!!.close()
+        }
+
+        assertThat(testListener.onImageEvents.size).isEqualTo(100)
+        // Spot check 11th index
+        assertThat(testListener.onImageEvents[10].outputId).isEqualTo(outputId)
+        assertThat(testListener.onImageEvents[10].image).isNotNull()
+
+        // Spot check last index
+        assertThat(testListener.onImageEvents[99].outputId).isEqualTo(outputId)
+        assertThat(testListener.onImageEvents[99].image).isNotNull()
+    }
+
+    @Test
+    fun imagesWithoutAListenerAreClosed() {
+        val image =
+            FakeImage(fakeImageSize.width, fakeImageSize.height, fakeImageFormat.value, 12345)
+
+        fakeImageReader.simulateImage(image, outputId)
+        assertThat(image.isClosed).isTrue()
+    }
+
+    @Test
+    fun closingImageSourceClosesImageReader() {
+        imageSource.close()
+        assertThat(fakeImageReader.isClosed)
+    }
+
+    @Test
+    fun closingImageSourceAfterClosingImagesClosesImageReader() {
+        val testListener = TestImageSourceListener()
+        imageSource.setListener(testListener)
+
+        // Simulate 3 images.
+        fakeImageReader.simulateImage(12345)
+        fakeImageReader.simulateImage(12345)
+        fakeImageReader.simulateImage(12345)
+
+        // Close all the images
+        testListener.onImageEvents.forEach { it.image!!.close() }
+
+        // Close the image source
+        imageSource.close()
+
+        // Check that the image reader is closed.
+        assertThat(fakeImageReader.isClosed).isTrue()
+    }
+
+    @Test
+    fun closingImageSourceBeforeClosingImagesClosesImageReader() {
+        val testListener = TestImageSourceListener()
+        imageSource.setListener(testListener)
+
+        // Simulate 3 images.
+        fakeImageReader.simulateImage(12345)
+        fakeImageReader.simulateImage(12345)
+        fakeImageReader.simulateImage(12345)
+
+        // Close the image source before closing images.
+        imageSource.close()
+
+        // Check that the image reader is *NOT* closed.
+        assertThat(fakeImageReader.isClosed).isFalse()
+
+        // Close all the images
+        testListener.onImageEvents.forEach { it.image!!.close() }
+
+        // Check that the image reader is now closed.
+        assertThat(fakeImageReader.isClosed).isTrue()
+    }
+
+    @Test
+    fun imagesAfterCloseAreClosed() {
+        val testListener = TestImageSourceListener()
+        imageSource.setListener(testListener)
+
+        // Simulate 3 images.
+        fakeImageReader.simulateImage(12345)
+        fakeImageReader.simulateImage(12346)
+        fakeImageReader.simulateImage(12347)
+
+        // Close the image source before closing images.
+        imageSource.close()
+
+        // Check that the image reader is *NOT* closed.
+        assertThat(fakeImageReader.isClosed).isFalse()
+
+        // Now simulate the imageReader producing images after the imageSource is closed
+        val fakeImage =
+            FakeImage(fakeImageSize.width, fakeImageSize.height, fakeImageFormat.value, 54321)
+        fakeImageReader.simulateImage(fakeImage, outputId)
+        // Image is immediately closed
+        assertThat(fakeImage.isClosed)
+
+        // Event is fired, but the image is *not* passed down
+        assertThat(testListener.onImageEvents.size).isEqualTo(4)
+        assertThat(testListener.onImageEvents.last().image).isNull()
+        assertThat(testListener.onImageEvents.last().outputTimestamp).isEqualTo(fakeImage.timestamp)
+
+        // Close all the images
+        testListener.onImageEvents.forEach { it.image?.close() }
+
+        // Make sure the image reader gets closed.
+        assertThat(fakeImageReader.isClosed).isTrue()
+    }
+
+    private class TestImageSourceListener : ImageSourceListener {
+        val onImageEvents = mutableListOf<OnImage>()
+
+        data class OnImage(
+            val streamId: StreamId,
+            val outputId: OutputId,
+            val outputTimestamp: Long,
+            val image: ImageWrapper?,
+        )
+
+        override fun onImage(
+            streamId: StreamId,
+            outputId: OutputId,
+            outputTimestamp: Long,
+            image: ImageWrapper?
+        ) {
+            onImageEvents.add(OnImage(streamId, outputId, outputTimestamp, image))
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/ImageSourceTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/ImageSourceTest.kt
deleted file mode 100644
index b41fbef..0000000
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/media/ImageSourceTest.kt
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.camera2.pipe.media
-
-import android.os.Build
-import android.util.Size
-import androidx.camera.camera2.pipe.OutputId
-import androidx.camera.camera2.pipe.StreamFormat
-import androidx.camera.camera2.pipe.StreamId
-import androidx.camera.camera2.pipe.testing.FakeImage
-import androidx.camera.camera2.pipe.testing.FakeImageReader
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
-import org.robolectric.annotation.Config
-
-/** Tests for [OutputImage] and [SharedOutputImage] */
-@RunWith(RobolectricTestRunner::class)
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-class ImageSourceTest {
-    private val streamId = StreamId(32)
-    private val outputId = OutputId(42)
-    private val fakeImageSize = Size(640, 480)
-    private val fakeImageFormat = StreamFormat.YUV_420_888
-    private val fakeImageReader =
-        FakeImageReader.create(
-            format = fakeImageFormat,
-            streamId = streamId,
-            outputId = outputId,
-            size = fakeImageSize,
-            capacity = 10
-        )
-
-    private val imageSource = ImageSource.create(fakeImageReader)
-
-    @After
-    fun cleanup() {
-        fakeImageReader.close()
-    }
-
-    @Test
-    fun testImageSourceForwardsImagesFromImageReader() {
-        val testListener = TestImageSourceListener()
-        imageSource.setListener(testListener)
-        fakeImageReader.simulateImage(outputId, 12345)
-
-        assertThat(testListener.onImageEvents.size).isEqualTo(1)
-        assertThat(testListener.onImageEvents[0].outputId).isEqualTo(outputId)
-        assertThat(testListener.onImageEvents[0].outputTimestamp).isEqualTo(12345)
-        assertThat(testListener.onImageEvents[0].image).isNotNull()
-    }
-
-    @Test
-    fun testImageSourceForwardsEmptyImagesAfterReachingCapacity() {
-        val testListener = TestImageSourceListener()
-        imageSource.setListener(testListener)
-
-        for (i in 0..99) {
-            fakeImageReader.simulateImage(outputId, 12345 + (i * 10000L))
-        }
-
-        assertThat(testListener.onImageEvents.size).isEqualTo(100)
-        // Spot check 11th index
-        assertThat(testListener.onImageEvents[10].outputId).isEqualTo(outputId)
-        assertThat(testListener.onImageEvents[10].image).isNull()
-
-        // Spot check last index
-        assertThat(testListener.onImageEvents[99].outputId).isEqualTo(outputId)
-        assertThat(testListener.onImageEvents[99].image).isNull()
-    }
-
-    @Test
-    fun closingImagesAllowsAllImagesToBeProduced() {
-        val testListener = TestImageSourceListener()
-        imageSource.setListener(testListener)
-
-        for (i in 0..99) {
-            fakeImageReader.simulateImage(outputId, 12345 + (i * 10000L))
-            testListener.onImageEvents.last().image!!.close()
-        }
-
-        assertThat(testListener.onImageEvents.size).isEqualTo(100)
-        // Spot check 11th index
-        assertThat(testListener.onImageEvents[10].outputId).isEqualTo(outputId)
-        assertThat(testListener.onImageEvents[10].image).isNotNull()
-
-        // Spot check last index
-        assertThat(testListener.onImageEvents[99].outputId).isEqualTo(outputId)
-        assertThat(testListener.onImageEvents[99].image).isNotNull()
-    }
-
-    @Test
-    fun imagesWithoutAListenerAreClosed() {
-        val image =
-            FakeImage(fakeImageSize.width, fakeImageSize.height, fakeImageFormat.value, 12345)
-
-        fakeImageReader.simulateImage(outputId, image)
-        assertThat(image.isClosed).isTrue()
-    }
-
-    @Test
-    fun closingImageSourceClosesImageReader() {
-        imageSource.close()
-        assertThat(fakeImageReader.isClosed)
-    }
-
-    @Test
-    fun closingImageSourceAfterClosingImagesClosesImageReader() {
-        val testListener = TestImageSourceListener()
-        imageSource.setListener(testListener)
-
-        // Simulate 3 images.
-        fakeImageReader.simulateImage(outputId, 12345)
-        fakeImageReader.simulateImage(outputId, 12345)
-        fakeImageReader.simulateImage(outputId, 12345)
-
-        // Close all the images
-        testListener.onImageEvents.forEach { it.image!!.close() }
-
-        // Close the image source
-        imageSource.close()
-
-        // Check that the image reader is closed.
-        assertThat(fakeImageReader.isClosed).isTrue()
-    }
-
-    @Test
-    fun closingImageSourceBeforeClosingImagesClosesImageReader() {
-        val testListener = TestImageSourceListener()
-        imageSource.setListener(testListener)
-
-        // Simulate 3 images.
-        fakeImageReader.simulateImage(outputId, 12345)
-        fakeImageReader.simulateImage(outputId, 12345)
-        fakeImageReader.simulateImage(outputId, 12345)
-
-        // Close the image source before closing images.
-        imageSource.close()
-
-        // Check that the image reader is *NOT* closed.
-        assertThat(fakeImageReader.isClosed).isFalse()
-
-        // Close all the images
-        testListener.onImageEvents.forEach { it.image!!.close() }
-
-        // Check that the image reader is now closed.
-        assertThat(fakeImageReader.isClosed).isTrue()
-    }
-
-    @Test
-    fun imagesAfterCloseAreClosed() {
-        val testListener = TestImageSourceListener()
-        imageSource.setListener(testListener)
-
-        // Simulate 3 images.
-        fakeImageReader.simulateImage(outputId, 12345)
-        fakeImageReader.simulateImage(outputId, 12346)
-        fakeImageReader.simulateImage(outputId, 12347)
-
-        // Close the image source before closing images.
-        imageSource.close()
-
-        // Check that the image reader is *NOT* closed.
-        assertThat(fakeImageReader.isClosed).isFalse()
-
-        // Now simulate the imageReader producing images after the imageSource is closed
-        val fakeImage =
-            FakeImage(fakeImageSize.width, fakeImageSize.height, fakeImageFormat.value, 54321)
-        fakeImageReader.simulateImage(outputId, fakeImage)
-        // Image is immediately closed
-        assertThat(fakeImage.isClosed)
-
-        // Event is fired, but the image is *not* passed down
-        assertThat(testListener.onImageEvents.size).isEqualTo(4)
-        assertThat(testListener.onImageEvents.last().image).isNull()
-        assertThat(testListener.onImageEvents.last().outputTimestamp).isEqualTo(fakeImage.timestamp)
-
-        // Close all the images
-        testListener.onImageEvents.forEach { it.image?.close() }
-
-        // Make sure the image reader gets closed.
-        assertThat(fakeImageReader.isClosed).isTrue()
-    }
-
-    private class TestImageSourceListener : ImageSourceListener {
-        val onImageEvents = mutableListOf<OnImage>()
-
-        data class OnImage(
-            val streamId: StreamId,
-            val outputId: OutputId,
-            val outputTimestamp: Long,
-            val image: ImageWrapper?,
-        )
-
-        override fun onImage(
-            streamId: StreamId,
-            outputId: OutputId,
-            outputTimestamp: Long,
-            image: ImageWrapper?
-        ) {
-            onImageEvents.add(OnImage(streamId, outputId, outputTimestamp, image))
-        }
-    }
-}
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt
index 1302981..4690516 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraController.kt
@@ -18,11 +18,12 @@
 
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraController
+import androidx.camera.camera2.pipe.CameraGraphId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraStatusMonitor
 import androidx.camera.camera2.pipe.StreamId
 
-internal class FakeCameraController : CameraController {
+internal class FakeCameraController(override val cameraGraphId: CameraGraphId) : CameraController {
     var started = false
     var closed = false
     var surfaceMap: Map<StreamId, Surface>? = null
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 9c33c0e..4d4aa8a 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(project(":camera:camera-core"))
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.tracing:tracing:1.2.0")
@@ -104,6 +104,5 @@
     description = "Camera2 implementation and extensions for the Jetpack Camera Library, a " +
             "library providing a consistent and reliable camera foundation that enables great " +
             "camera driven experiences across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-camera2/lint-baseline.xml b/camera/camera-camera2/lint-baseline.xml
index 815bea7..8e783a1 100644
--- a/camera/camera-camera2/lint-baseline.xml
+++ b/camera/camera-camera2/lint-baseline.xml
@@ -1,14 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
-
-    <issue
-        id="NewApi"
-        message="Field requires API level 30 (current min is 21): `android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE`"
-        errorLine1="                    CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE).getUpper();"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/androidTest/java/androidx/camera/camera2/internal/ZoomControlDeviceTest.java"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.kt
index dbb27ed..61946c0 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.kt
@@ -56,10 +56,10 @@
 
     @Test
     fun canReturnSupportedOutputFormats() {
-        val formats = camera2CameraInfo.supportedOutputFormats.toList()
+        val formats = camera2CameraInfo.supportedOutputFormats
         val cameraCharacteristics = CameraUtil.getCameraCharacteristics(lensFacing)!!
         val streamConfigurationMap = cameraCharacteristics.get(SCALER_STREAM_CONFIGURATION_MAP)!!
 
-        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toList())
+        assertThat(formats).containsExactlyElementsIn(streamConfigurationMap.outputFormats.toSet())
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
index c9058f1..70decc2 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 import android.view.Surface;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
@@ -231,6 +230,7 @@
         if (device == null) {
             return null;
         }
+        Logger.d(TAG, "template type = " + captureConfig.getTemplateType());
         CaptureRequest.Builder builder = device.createCaptureRequest(
                 captureConfig.getTemplateType());
 
@@ -252,7 +252,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static CaptureRequest.Builder createReprocessCaptureRequest(
                 @NonNull CameraDevice cameraDevice,
                 @NonNull TotalCaptureResult totalCaptureResult)
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProvider.java
index 76e803cb..80805f4 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2EncoderProfilesProvider.java
@@ -24,7 +24,6 @@
 import android.os.Build;
 import android.util.Size;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -194,7 +193,6 @@
 
     @RequiresApi(31)
     static class Api31Impl {
-        @DoNotInline
         static EncoderProfiles getAll(String cameraId, int quality) {
             return CamcorderProfile.getAll(cameraId, quality);
         }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DynamicRangeResolver.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DynamicRangeResolver.java
index 622046b..3b237d3 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DynamicRangeResolver.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DynamicRangeResolver.java
@@ -27,7 +27,6 @@
 import android.os.Build;
 import android.text.TextUtils;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -409,7 +408,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         @Nullable
         static DynamicRange getRecommended10BitDynamicRange(
                 @NonNull CameraCharacteristicsCompat characteristics) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
index 929edd0..2f89dcf 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
@@ -39,7 +39,6 @@
 import android.util.Rational;
 import android.util.Size;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -1542,7 +1541,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Size[] getHighResolutionOutputSizes(StreamConfigurationMap streamConfigurationMap,
                 int format) {
             return streamConfigurationMap.getHighResolutionOutputSizes(format);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseImpl.java
index 55fed20..47e7def 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SynchronizedCaptureSessionBaseImpl.java
@@ -24,7 +24,6 @@
 import android.os.Handler;
 import android.view.Surface;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -638,7 +637,6 @@
         private Api23Impl() {
         }
 
-        @DoNotInline
         static Surface getInputSurface(CameraCaptureSession cameraCaptureSession) {
             return cameraCaptureSession.getInputSurface();
         }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
index d69d972..f193b1b 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
@@ -17,6 +17,7 @@
 package androidx.camera.camera2.internal;
 
 import static android.graphics.ImageFormat.PRIVATE;
+import static android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW;
 import static android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
 
 import static androidx.camera.camera2.internal.ZslUtil.isCapabilitySupported;
@@ -137,10 +138,12 @@
         // regular capture request when taking pictures. So when user switches flash mode, we
         // could create reprocessing capture request if flash mode allows.
         if (mIsZslDisabledByUseCaseConfig) {
+            sessionConfigBuilder.setTemplateType(TEMPLATE_PREVIEW);
             return;
         }
 
         if (mShouldZslDisabledByQuirks) {
+            sessionConfigBuilder.setTemplateType(TEMPLATE_PREVIEW);
             return;
         }
 
@@ -153,6 +156,7 @@
                 || mReprocessingInputSizeMap.isEmpty()
                 || !mReprocessingInputSizeMap.containsKey(PRIVATE)
                 || !isJpegValidOutputForInputFormat(mCameraCharacteristicsCompat, PRIVATE)) {
+            sessionConfigBuilder.setTemplateType(TEMPLATE_PREVIEW);
             return;
         }
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/ApiCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/ApiCompat.java
index d0b19fd..a8f054d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/ApiCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/ApiCompat.java
@@ -24,7 +24,6 @@
 import android.util.Size;
 import android.view.Surface;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -49,7 +48,6 @@
         /**
          * @see CameraDevice#close()
          */
-        @DoNotInline
         public static void close(@NonNull CameraDevice cameraDevice) {
             cameraDevice.close();
         }
@@ -67,7 +65,6 @@
         /**
          * @see CameraCaptureSession.StateCallback#onSurfacePrepared(CameraCaptureSession, Surface)
          */
-        @DoNotInline
         public static void onSurfacePrepared(
                 @NonNull CameraCaptureSession.StateCallback callback,
                 @NonNull CameraCaptureSession session,
@@ -89,7 +86,6 @@
          * @see CameraCaptureSession.CaptureCallback#onCaptureBufferLost(CameraCaptureSession,
          * CaptureRequest, Surface, long)
          */
-        @DoNotInline
         public static void onCaptureBufferLost(
                 @NonNull CameraCaptureSession.CaptureCallback callback,
                 @NonNull CameraCaptureSession session,
@@ -112,7 +108,6 @@
         /**
          * @see CameraCaptureSession.StateCallback#onCaptureQueueEmpty(CameraCaptureSession)
          */
-        @DoNotInline
         public static void onCaptureQueueEmpty(
                 @NonNull CameraCaptureSession.StateCallback callback,
                 @NonNull CameraCaptureSession session) {
@@ -122,7 +117,6 @@
         /**
          * @see OutputConfiguration
          */
-        @DoNotInline
         @NonNull
         public static <T> OutputConfiguration newOutputConfiguration(@NonNull Size surfaceSize,
                 @NonNull Class<T> klass) {
@@ -142,7 +136,6 @@
         /**
          * @see CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged()
          */
-        @DoNotInline
         public static void onCameraAccessPrioritiesChanged(
                 @NonNull CameraManager.AvailabilityCallback callback) {
             callback.onCameraAccessPrioritiesChanged();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
index 38d0e68..3b97170 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
@@ -21,7 +21,6 @@
 import android.os.Build;
 import android.util.Size;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -86,7 +85,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Size[] getHighResolutionOutputSizes(StreamConfigurationMap streamConfigurationMap,
                 int format) {
             return streamConfigurationMap.getHighResolutionOutputSizes(format);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangeConversions.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangeConversions.java
index 91bee96..c86258a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangeConversions.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/DynamicRangeConversions.java
@@ -31,7 +31,6 @@
 
 import android.hardware.camera2.params.DynamicRangeProfiles;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -95,7 +94,6 @@
     /**
      * Converts Camera2 dynamic range profile constants to {@link DynamicRange}.
      */
-    @DoNotInline
     @Nullable
     public static DynamicRange profileToDynamicRange(long profile) {
         return PROFILE_TO_DR_MAP.get(profile);
@@ -112,7 +110,6 @@
      * format returned by {@link DynamicRange#getEncoding()} is
      * {@link DynamicRange#ENCODING_HDR_UNSPECIFIED}, this will return {@code null}.
      */
-    @DoNotInline
     @Nullable
     public static Long dynamicRangeToFirstSupportedProfile(@NonNull DynamicRange dynamicRange,
             @NonNull DynamicRangeProfiles dynamicRangeProfiles) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.java
index 46a5d06..d28b4b5 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.java
@@ -56,12 +56,19 @@
                     "PIXEL 6",
                     "PIXEL 6 PRO",
                     "PIXEL 7",
-                    "PIXEL 7 PRO"));
+                    "PIXEL 7 PRO",
+                    "PIXEL 8",
+                    "PIXEL 8 PRO"));
 
     private static final Set<String> SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_SAMSUNG_MODELS =
             new HashSet<>(Arrays.asList(
-                    "SM-S926B", // Galaxy S24+
-                    "SM-S928U" // Galaxy S24 Ultra
+                    "SM-S921", // Galaxy S24
+                    "SC-51E",  // Galaxy S24
+                    "SCG25",   // Galaxy S24
+                    "SM-S926", // Galaxy S24+
+                    "SM-S928", // Galaxy S24 Ultra
+                    "SC-52E",  // Galaxy S24 Ultra
+                    "SCG26"   // Galaxy S24 Ultra
               ));
 
     static boolean load() {
@@ -91,7 +98,13 @@
 
         String capitalModelName = Build.MODEL.toUpperCase(Locale.US);
 
-        return SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_SAMSUNG_MODELS.contains(capitalModelName);
+        // Check if the device model starts with the one of the predefined models
+        for (String supportedModel : SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_SAMSUNG_MODELS) {
+            if (capitalModelName.startsWith(supportedModel)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt
index 7daff71..8e63102 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZslControlImplTest.kt
@@ -21,6 +21,8 @@
 import android.graphics.ImageFormat.YUV_420_888
 import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
+import android.hardware.camera2.CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
 import android.hardware.camera2.params.StreamConfigurationMap
 import android.os.Build
 import android.util.Size
@@ -84,6 +86,7 @@
         assertThat(zslControl.mReprocessingImageReader.height)
             .isEqualTo(PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
         assertThat(zslControl.mImageRingBuffer.maxCapacity).isEqualTo(RING_BUFFER_CAPACITY)
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_ZERO_SHUTTER_LAG)
     }
 
     @Test
@@ -101,6 +104,7 @@
         zslControl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControl.mReprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -118,6 +122,7 @@
         zslControl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControl.mReprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -135,6 +140,7 @@
         zslControl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControl.mReprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -153,6 +159,7 @@
         zslControl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControl.mReprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -178,6 +185,7 @@
         assertThat(zslControl.mReprocessingImageReader.height)
             .isEqualTo(PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
         assertThat(zslControl.mImageRingBuffer.maxCapacity).isEqualTo(RING_BUFFER_CAPACITY)
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_ZERO_SHUTTER_LAG)
     }
 
     @Test
@@ -218,6 +226,7 @@
         zslControl.addZslConfig(sessionConfigBuilder)
 
         assertThat(zslControl.mReprocessingImageReader).isNull()
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_PREVIEW)
     }
 
     @Test
@@ -245,6 +254,7 @@
         assertThat(zslControl.mReprocessingImageReader.height)
             .isEqualTo(PRIVATE_REPROCESSING_MAXIMUM_SIZE.height)
         assertThat(zslControl.mImageRingBuffer.maxCapacity).isEqualTo(RING_BUFFER_CAPACITY)
+        assertThat(sessionConfigBuilder.build().templateType).isEqualTo(TEMPLATE_ZERO_SHUTTER_LAG)
     }
 
     private fun createCameraCharacteristicsCompat(
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerTest.java
index 61368e8..6746aed 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerTest.java
@@ -73,15 +73,27 @@
                 createLevel3PrivPrivYuvSubsetConfiguration())});
         data.add(new Object[]{new Config("Google", null, "Pixel 7 Pro", "1",
                 createLevel3PrivPrivYuvSubsetConfiguration())});
+        data.add(new Object[]{new Config("Google", null, "Pixel 8", "0",
+                createLevel3PrivPrivYuvSubsetConfiguration())});
+        data.add(new Object[]{new Config("Google", null, "Pixel 8", "1",
+                createLevel3PrivPrivYuvSubsetConfiguration())});
+        data.add(new Object[]{new Config("Google", null, "Pixel 8 Pro", "0",
+                createLevel3PrivPrivYuvSubsetConfiguration())});
+        data.add(new Object[]{new Config("Google", null, "Pixel 8 Pro", "1",
+                createLevel3PrivPrivYuvSubsetConfiguration())});
 
         // Tests for FULL Samsung devices
+        data.add(new Object[]{new Config("Samsung", null, "SCG25", "0",
+                createLevel3PrivPrivYuvSubsetConfiguration())});
+        data.add(new Object[]{new Config("Samsung", null, "SM-S9210", "1",
+                createLevel3PrivPrivYuvSubsetConfiguration())});
         data.add(new Object[]{new Config("Samsung", null, "SM-S926B", "0",
                 createLevel3PrivPrivYuvSubsetConfiguration())});
-        data.add(new Object[]{new Config("Samsung", null, "SM-S926B", "1",
+        data.add(new Object[]{new Config("Samsung", null, "SM-S926U", "1",
                 createLevel3PrivPrivYuvSubsetConfiguration())});
-        data.add(new Object[]{new Config("Samsung", null, "SM-S928U", "0",
+        data.add(new Object[]{new Config("Samsung", null, "SM-S928U1", "0",
                 createLevel3PrivPrivYuvSubsetConfiguration())});
-        data.add(new Object[]{new Config("Samsung", null, "SM-S928U", "1",
+        data.add(new Object[]{new Config("Samsung", null, "SM-S928B", "1",
                 createLevel3PrivPrivYuvSubsetConfiguration())});
 
         // Other cases
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index 7277d2d..d523b9f 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.lifecycle:lifecycle-livedata:2.1.0")
     api(libs.guavaListenableFuture)
     api("androidx.annotation:annotation-experimental:1.4.1")
@@ -92,7 +92,8 @@
                         "-fomit-frame-pointer", "-fdata-sections", "-ffunction-sections"
                 arguments "-DCMAKE_VERBOSE_MAKEFILE=ON",
                         "-DCMAKE_SHARED_LINKER_FLAGS=-Wl,--gc-sections " +
-                                "-Wl,--version-script=${versionScript}"
+                                "-Wl,--version-script=${versionScript} " +
+                                "-Wl,--undefined-version"
             }
         }
     }
@@ -134,7 +135,6 @@
     description = "Core components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     extraLicense {
         name = "BSD License"
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
index c70ec60..0095250 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/DefaultSurfaceProcessorTest.kt
@@ -35,6 +35,7 @@
 import androidx.camera.core.impl.ImageReaderProxy
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
+import androidx.camera.core.processing.util.GLUtils.InputFormat
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.impl.CameraUtil
 import androidx.camera.testing.impl.HandlerUtil
@@ -319,20 +320,30 @@
     @SdkSuppress(minSdkVersion = 23)
     @Test
     fun renderByCustomShader(): Unit = runBlocking {
-        testRender(OutputType.IMAGE_READER, createCustomShaderProvider())
+        val shaderProviderOverride = createCustomShaderProvider()
+        testRender(
+            OutputType.IMAGE_READER,
+            shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProviderOverride)
+        )
     }
 
     @SdkSuppress(minSdkVersion = 21, maxSdkVersion = 22)
     @Test
     fun renderByCustomShaderBelowApi23(): Unit = runBlocking {
-        testRender(OutputType.SURFACE_TEXTURE, createCustomShaderProvider())
+        val shaderProviderOverride = createCustomShaderProvider()
+        testRender(
+            OutputType.SURFACE_TEXTURE,
+            shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProviderOverride)
+        )
     }
 
     @Test
     fun createByInvalidShaderString_throwException() {
         val shaderProvider = createCustomShaderProvider(shaderString = "Invalid shader")
         assertThrows(IllegalArgumentException::class.java) {
-            createSurfaceProcessor(shaderProvider = shaderProvider)
+            createSurfaceProcessor(
+                shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+            )
         }
     }
 
@@ -341,7 +352,9 @@
         val shaderProvider =
             createCustomShaderProvider(exceptionToThrow = RuntimeException("Failed Shader"))
         assertThrows(IllegalArgumentException::class.java) {
-            createSurfaceProcessor(shaderProvider = shaderProvider)
+            createSurfaceProcessor(
+                shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+            )
         }
     }
 
@@ -349,7 +362,9 @@
     fun createByIncorrectSamplerName_throwException() {
         val shaderProvider = createCustomShaderProvider(samplerVarName = "_mySampler_")
         assertThrows(IllegalArgumentException::class.java) {
-            createSurfaceProcessor(shaderProvider = shaderProvider)
+            createSurfaceProcessor(
+                shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+            )
         }
     }
 
@@ -357,15 +372,17 @@
     fun createByIncorrectFragCoordsName_throwException() {
         val shaderProvider = createCustomShaderProvider(fragCoordsVarName = "_myFragCoords_")
         assertThrows(IllegalArgumentException::class.java) {
-            createSurfaceProcessor(shaderProvider = shaderProvider)
+            createSurfaceProcessor(
+                shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+            )
         }
     }
 
     private suspend fun testRender(
         outputType: OutputType,
-        shaderProvider: ShaderProvider = ShaderProvider.DEFAULT
+        shaderProviderOverrides: Map<InputFormat, ShaderProvider> = emptyMap()
     ) {
-        createSurfaceProcessor(shaderProvider = shaderProvider)
+        createSurfaceProcessor(shaderProviderOverrides = shaderProviderOverrides)
         // Prepare input
         val inputSurfaceRequest = createInputSurfaceRequest()
         surfaceProcessor.onInputSurface(inputSurfaceRequest)
@@ -394,9 +411,9 @@
 
     private fun createSurfaceProcessor(
         dynamicRange: DynamicRange = DynamicRange.SDR,
-        shaderProvider: ShaderProvider = ShaderProvider.DEFAULT
+        shaderProviderOverrides: Map<InputFormat, ShaderProvider> = emptyMap()
     ) {
-        surfaceProcessor = DefaultSurfaceProcessor(dynamicRange, shaderProvider)
+        surfaceProcessor = DefaultSurfaceProcessor(dynamicRange, shaderProviderOverrides)
     }
 
     private fun createInputSurfaceRequest(): SurfaceRequest {
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt
index 54a2af9..3a10b97 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/processing/OpenGlRendererTest.kt
@@ -34,6 +34,7 @@
 import androidx.camera.core.CameraSelector.LENS_FACING_BACK
 import androidx.camera.core.DynamicRange
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.processing.util.GLUtils.InputFormat
 import androidx.camera.core.processing.util.GraphicDeviceInfo
 import androidx.camera.testing.impl.CameraUtil
 import androidx.camera.testing.impl.TestImageUtil.createBitmap
@@ -269,7 +270,9 @@
         val shaderProvider = createCustomShaderProvider(shaderString = "Invalid shader")
         assertThrows(IllegalArgumentException::class.java) {
             runBlocking(glDispatcher) {
-                createOpenGlRendererAndInit(shaderProvider = shaderProvider)
+                createOpenGlRendererAndInit(
+                    shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+                )
             }
         }
     }
@@ -280,7 +283,9 @@
             createCustomShaderProvider(exceptionToThrow = RuntimeException("Failed Shader"))
         assertThrows(IllegalArgumentException::class.java) {
             runBlocking(glDispatcher) {
-                createOpenGlRendererAndInit(shaderProvider = shaderProvider)
+                createOpenGlRendererAndInit(
+                    shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+                )
             }
         }
     }
@@ -290,7 +295,9 @@
         val shaderProvider = createCustomShaderProvider(samplerVarName = "_mySampler_")
         assertThrows(IllegalArgumentException::class.java) {
             runBlocking(glDispatcher) {
-                createOpenGlRendererAndInit(shaderProvider = shaderProvider)
+                createOpenGlRendererAndInit(
+                    shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+                )
             }
         }
     }
@@ -300,7 +307,9 @@
         val shaderProvider = createCustomShaderProvider(fragCoordsVarName = "_myFragCoords_")
         assertThrows(IllegalArgumentException::class.java) {
             runBlocking(glDispatcher) {
-                createOpenGlRendererAndInit(shaderProvider = shaderProvider)
+                createOpenGlRendererAndInit(
+                    shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProvider)
+                )
             }
         }
     }
@@ -319,7 +328,7 @@
         runBlocking(glDispatcher) {
             createOpenGlRendererAndInit()
             glRenderer.release()
-            glRenderer.init(DynamicRange.SDR, ShaderProvider.DEFAULT)
+            glRenderer.init(DynamicRange.SDR)
             assertThat(glRenderer.textureName).isNotEqualTo(0L)
         }
 
@@ -362,14 +371,22 @@
     @Test
     fun renderByCustomShader(): Unit =
         runBlocking(glDispatcher) {
-            testRender(OutputType.IMAGE_READER, shaderProvider = createCustomShaderProvider())
+            val shaderProviderOverride = createCustomShaderProvider()
+            testRender(
+                OutputType.IMAGE_READER,
+                shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProviderOverride)
+            )
         }
 
     @SdkSuppress(minSdkVersion = 21, maxSdkVersion = 22)
     @Test
     fun renderByCustomShaderBelowApi23(): Unit =
         runBlocking(glDispatcher) {
-            testRender(OutputType.SURFACE_TEXTURE, shaderProvider = createCustomShaderProvider())
+            val shaderProviderOverride = createCustomShaderProvider()
+            testRender(
+                OutputType.SURFACE_TEXTURE,
+                shaderProviderOverrides = mapOf(InputFormat.DEFAULT to shaderProviderOverride)
+            )
         }
 
     @Test
@@ -390,27 +407,27 @@
 
     private suspend fun initRender(
         dynamicRange: DynamicRange = DynamicRange.SDR,
-        shaderProvider: ShaderProvider = ShaderProvider.DEFAULT,
+        shaderProviderOverrides: Map<InputFormat, ShaderProvider> = emptyMap()
     ): GraphicDeviceInfo {
         prepareCamera()
         assumeDynamicRange(dynamicRange)
         return createOpenGlRendererAndInit(
             dynamicRange = dynamicRange,
-            shaderProvider = shaderProvider
+            shaderProviderOverrides = shaderProviderOverrides
         )
     }
 
     private suspend fun testRender(
         outputType: OutputType,
         dynamicRange: DynamicRange = DynamicRange.SDR,
-        shaderProvider: ShaderProvider = ShaderProvider.DEFAULT,
+        shaderProviderOverrides: Map<InputFormat, ShaderProvider> = emptyMap(),
         shouldInit: Boolean = true,
         expectedStandard: Int? = null,
         expectedTransfer: Int? = null
     ) {
         // Arrange.
         if (shouldInit) {
-            initRender(dynamicRange, shaderProvider)
+            initRender(dynamicRange, shaderProviderOverrides)
         }
 
         // Prepare input
@@ -460,16 +477,16 @@
 
     private suspend fun createOpenGlRendererAndInit(
         dynamicRange: DynamicRange = DynamicRange.SDR,
-        shaderProvider: ShaderProvider = ShaderProvider.DEFAULT
+        shaderProviderOverrides: Map<InputFormat, ShaderProvider> = emptyMap()
     ): GraphicDeviceInfo {
         createOpenGlRenderer()
 
         return if (currentCoroutineContext()[ContinuationInterceptor] == glDispatcher) {
             // same dispatcher, init directly
-            glRenderer.init(dynamicRange, shaderProvider)
+            glRenderer.init(dynamicRange, shaderProviderOverrides)
         } else {
             runBlocking(glDispatcher) {
-                return@runBlocking glRenderer.init(dynamicRange, shaderProvider)
+                return@runBlocking glRenderer.init(dynamicRange, shaderProviderOverrides)
             }
         }
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Bitmap2JpegBytes.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Bitmap2JpegBytes.java
index b0b81cd..d4ab73e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Bitmap2JpegBytes.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/Bitmap2JpegBytes.java
@@ -22,7 +22,6 @@
 import android.graphics.ImageFormat;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.ImageCaptureException;
@@ -67,7 +66,6 @@
 
     @RequiresApi(34)
     private static class Api34Impl {
-        @DoNotInline
         static boolean hasGainmap(@NonNull Bitmap bitmap) {
             return bitmap.hasGainmap();
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraValidator.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraValidator.java
index 4e65cb4..d65107e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraValidator.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraValidator.java
@@ -20,7 +20,6 @@
 import android.content.pm.PackageManager;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
@@ -166,7 +165,6 @@
         private Api34Impl() {
         }
 
-        @DoNotInline
         static int getDeviceId(@NonNull Context context) {
             return context.getDeviceId();
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ContextUtil.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ContextUtil.java
index f979139..7e8a567 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ContextUtil.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ContextUtil.java
@@ -21,7 +21,6 @@
 import android.content.ContextWrapper;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -98,14 +97,12 @@
         private Api30Impl() {
         }
 
-        @DoNotInline
         @NonNull
         static Context createAttributionContext(@NonNull Context context,
                 @Nullable String attributeTag) {
             return context.createAttributionContext(attributeTag);
         }
 
-        @DoNotInline
         @Nullable
         static String getAttributionTag(@NonNull Context context) {
             return context.getAttributionTag();
@@ -117,13 +114,11 @@
         private Api34Impl() {
         }
 
-        @DoNotInline
         @NonNull
         static Context createDeviceContext(@NonNull Context context, int deviceId) {
             return context.createDeviceContext(deviceId);
         }
 
-        @DoNotInline
         static int getDeviceId(@NonNull Context context) {
             return context.getDeviceId();
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 37910ae..b233749 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -169,6 +169,9 @@
     @NonNull
     private final RestrictedCameraInfo mAdapterCameraInfo;
 
+    @Nullable
+    private final RestrictedCameraInfo mAdapterSecondaryCameraInfo;
+
     @NonNull
     private final LayoutSettings mLayoutSettings;
     @NonNull
@@ -242,6 +245,7 @@
         mAdapterCameraControl = new RestrictedCameraControl(
                 mCameraInternal.getCameraControlInternal(), sessionProcessor);
         mAdapterCameraInfo = restrictedCameraInfo;
+        mAdapterSecondaryCameraInfo = secondaryRestrictedCameraInfo;
         mId = generateCameraId(restrictedCameraInfo, secondaryRestrictedCameraInfo);
     }
 
@@ -1121,6 +1125,11 @@
         return mAdapterCameraInfo;
     }
 
+    @Nullable
+    public CameraInfo getSecondaryCameraInfo() {
+        return mAdapterSecondaryCameraInfo;
+    }
+
     @Override
     @NonNull
     public CameraConfig getExtendedConfig() {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
index baa04da..1cdc173 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/DefaultSurfaceProcessor.java
@@ -56,6 +56,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -97,24 +98,25 @@
 
     /** Constructs {@link DefaultSurfaceProcessor} with default shaders. */
     DefaultSurfaceProcessor(@NonNull DynamicRange dynamicRange) {
-        this(dynamicRange, ShaderProvider.DEFAULT);
+        this(dynamicRange, Collections.emptyMap());
     }
 
     /**
      * Constructs {@link DefaultSurfaceProcessor} with custom shaders.
      *
-     * @param shaderProvider custom shader provider for OpenGL rendering.
-     * @throws IllegalArgumentException if the shaderProvider provides invalid shader.
+     * @param shaderProviderOverrides custom shader providers for OpenGL rendering, for each input
+     *                                format.
+     * @throws IllegalArgumentException if any shaderProvider override provides invalid shader.
      */
     DefaultSurfaceProcessor(@NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider) {
+            @NonNull Map<InputFormat, ShaderProvider> shaderProviderOverrides) {
         mGlThread = new HandlerThread("GL Thread");
         mGlThread.start();
         mGlHandler = new Handler(mGlThread.getLooper());
         mGlExecutor = CameraXExecutors.newHandlerExecutor(mGlHandler);
         mGlRenderer = new OpenGlRenderer();
         try {
-            initGlRenderer(dynamicRange, shaderProvider);
+            initGlRenderer(dynamicRange, shaderProviderOverrides);
         } catch (RuntimeException e) {
             release();
             throw e;
@@ -355,11 +357,11 @@
     }
 
     private void initGlRenderer(@NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider) {
+            @NonNull Map<InputFormat, ShaderProvider> shaderProviderOverrides) {
         ListenableFuture<Void> initFuture = CallbackToFutureAdapter.getFuture(completer -> {
             executeSafely(() -> {
                 try {
-                    mGlRenderer.init(dynamicRange, shaderProvider);
+                    mGlRenderer.init(dynamicRange, shaderProviderOverrides);
                     completer.set(null);
                 } catch (RuntimeException e) {
                     completer.setException(e);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/OpenGlRenderer.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/OpenGlRenderer.java
index 63e29226..f11e758 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/OpenGlRenderer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/OpenGlRenderer.java
@@ -22,20 +22,14 @@
 import static androidx.camera.core.processing.util.GLUtils.EMPTY_ATTRIBS;
 import static androidx.camera.core.processing.util.GLUtils.NO_OUTPUT_SURFACE;
 import static androidx.camera.core.processing.util.GLUtils.PIXEL_STRIDE;
-import static androidx.camera.core.processing.util.GLUtils.TEX_BUF;
-import static androidx.camera.core.processing.util.GLUtils.VAR_TEXTURE;
-import static androidx.camera.core.processing.util.GLUtils.VAR_TEXTURE_YUV;
-import static androidx.camera.core.processing.util.GLUtils.VERTEX_BUF;
 import static androidx.camera.core.processing.util.GLUtils.checkEglErrorOrLog;
 import static androidx.camera.core.processing.util.GLUtils.checkEglErrorOrThrow;
 import static androidx.camera.core.processing.util.GLUtils.checkGlErrorOrThrow;
 import static androidx.camera.core.processing.util.GLUtils.checkGlThreadOrThrow;
 import static androidx.camera.core.processing.util.GLUtils.checkInitializedOrThrow;
-import static androidx.camera.core.processing.util.GLUtils.checkLocationOrThrow;
 import static androidx.camera.core.processing.util.GLUtils.chooseSurfaceAttrib;
-import static androidx.camera.core.processing.util.GLUtils.create4x4IdentityMatrix;
 import static androidx.camera.core.processing.util.GLUtils.createPBufferSurface;
-import static androidx.camera.core.processing.util.GLUtils.createProgram;
+import static androidx.camera.core.processing.util.GLUtils.createPrograms;
 import static androidx.camera.core.processing.util.GLUtils.createTexture;
 import static androidx.camera.core.processing.util.GLUtils.createWindowSurface;
 import static androidx.camera.core.processing.util.GLUtils.deleteFbo;
@@ -44,7 +38,6 @@
 import static androidx.camera.core.processing.util.GLUtils.generateTexture;
 import static androidx.camera.core.processing.util.GLUtils.getGlVersionNumber;
 import static androidx.camera.core.processing.util.GLUtils.getSurfaceSize;
-import static androidx.camera.core.processing.util.GLUtils.getTexNumUnits;
 import static androidx.core.util.Preconditions.checkArgument;
 
 import static java.util.Objects.requireNonNull;
@@ -68,12 +61,15 @@
 import androidx.camera.core.Logger;
 import androidx.camera.core.SurfaceOutput;
 import androidx.camera.core.processing.util.GLUtils.InputFormat;
+import androidx.camera.core.processing.util.GLUtils.Program2D;
+import androidx.camera.core.processing.util.GLUtils.SamplerShaderProgram;
 import androidx.camera.core.processing.util.GraphicDeviceInfo;
 import androidx.camera.core.processing.util.OutputSurface;
 import androidx.core.util.Pair;
 import androidx.core.util.Preconditions;
 
 import java.nio.ByteBuffer;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -85,7 +81,7 @@
  * OpenGLRenderer renders texture image to the output surface.
  *
  * <p>OpenGLRenderer's methods must run on the same thread, so called GL thread. The GL thread is
- * locked as the thread running the {@link #init(DynamicRange, ShaderProvider)} method, otherwise an
+ * locked as the thread running the {@link #init(DynamicRange, Map)} method, otherwise an
  * {@link IllegalStateException} will be thrown when other methods are called.
  */
 @WorkerThread
@@ -109,17 +105,10 @@
     protected EGLSurface mTempSurface = EGL14.EGL_NO_SURFACE;
     @Nullable
     protected Surface mCurrentSurface;
-    protected int mProgramHandle = -1;
-    protected int mTexMatrixLoc = -1;
-    protected int mTransMatrixLoc = -1;
-    protected int mAlphaScaleLoc = -1;
-    protected int mPositionLoc = -1;
-    protected int mTexCoordLoc = -1;
-    protected int mSamplerDefaultLoc = -1;
-    protected int mSamplerYuvLoc = -1;
-    protected int mSamplerSelectorLoc = -1;
-    protected int mExternalTexNumUnits = -1;
-    protected boolean mIsDefaultHdrShader = false;
+    @NonNull
+    protected Map<InputFormat, Program2D> mProgramHandles = Collections.emptyMap();
+    @Nullable
+    protected Program2D mCurrentProgram = null;
     @NonNull
     protected InputFormat mCurrentInputformat = InputFormat.UNKNOWN;
 
@@ -128,20 +117,34 @@
     /**
      * Initializes the OpenGLRenderer
      *
+     * <p>This is equivalent to calling {@link #init(DynamicRange, Map)} without providing any
+     * shader overrides. Default shaders will be used for the dynamic range specified.
+     */
+    @NonNull
+    public GraphicDeviceInfo init(@NonNull DynamicRange dynamicRange) {
+        return init(dynamicRange, Collections.emptyMap());
+    }
+
+    /**
+     * Initializes the OpenGLRenderer
+     *
      * <p>Initialization must be done before calling other methods, otherwise an
      * {@link IllegalStateException} will be thrown. Following methods must run on the same
      * thread as this method, so called GL thread, otherwise an {@link IllegalStateException}
      * will be thrown.
      *
+     * @param dynamicRange    the dynamic range used to select default shaders.
+     * @param shaderOverrides specific shader overrides for fragment shaders
+     *                        per {@link InputFormat}.
+     * @return Info about the initialized graphics device.
      * @throws IllegalStateException    if the renderer is already initialized or failed to be
      *                                  initialized.
      * @throws IllegalArgumentException if the ShaderProvider fails to create shader or provides
      *                                  invalid shader string.
-     * @return Info about the initialized graphics device.
      */
     @NonNull
     public GraphicDeviceInfo init(@NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider) {
+            @NonNull Map<InputFormat, ShaderProvider> shaderOverrides) {
         checkInitializedOrThrow(mInitialized, false);
         GraphicDeviceInfo.Builder infoBuilder = GraphicDeviceInfo.builder();
         try {
@@ -161,12 +164,9 @@
             createTempSurface();
             makeCurrent(mTempSurface);
             infoBuilder.setGlVersion(getGlVersionNumber());
-            mProgramHandle = createProgram(dynamicRange, shaderProvider);
-            mIsDefaultHdrShader = dynamicRange.is10BitHdr();
-            loadLocations();
+            mProgramHandles = createPrograms(dynamicRange, shaderOverrides);
             mExternalTextureId = createTexture();
-            mExternalTexNumUnits = getTexNumUnits();
-            useAndConfigureProgram(mExternalTextureId);
+            useAndConfigureProgramWithTexture(mExternalTextureId);
         } catch (IllegalStateException | IllegalArgumentException e) {
             releaseInternal();
             throw e;
@@ -246,23 +246,12 @@
 
         if (mCurrentInputformat != inputFormat) {
             mCurrentInputformat = inputFormat;
-            activateExternalTexture(mExternalTextureId);
+            useAndConfigureProgramWithTexture(mExternalTextureId);
         }
     }
 
     private void activateExternalTexture(int externalTextureId) {
-        GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
-
-        int texUnit = GLES20.GL_TEXTURE0;
-        if (mIsDefaultHdrShader) {
-            GLES20.glUniform1i(mSamplerSelectorLoc, mCurrentInputformat.getSamplerSelector());
-            checkGlErrorOrThrow("glUniform1i " + mCurrentInputformat);
-
-            if (mCurrentInputformat == InputFormat.YUV) {
-                texUnit = GLES20.GL_TEXTURE0 + mExternalTexNumUnits;
-            }
-        }
-        GLES20.glActiveTexture(texUnit);
+        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
         checkGlErrorOrThrow("glActiveTexture");
 
         GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, externalTextureId);
@@ -302,10 +291,11 @@
         }
 
         // TODO(b/245855601): Upload the matrix to GPU when textureTransform is changed.
-        // Copy the texture transformation matrix over.
-        GLES20.glUniformMatrix4fv(mTexMatrixLoc, /*count=*/1, /*transpose=*/false, textureTransform,
-                /*offset=*/0);
-        checkGlErrorOrThrow("glUniformMatrix4fv");
+        Program2D program = Preconditions.checkNotNull(mCurrentProgram);
+        if (program instanceof SamplerShaderProgram) {
+            // Copy the texture transformation matrix over.
+            ((SamplerShaderProgram) program).updateTextureMatrix(textureTransform);
+        }
 
         // Draw the rect.
         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*firstVertex=*/0, /*vertexCount=*/4);
@@ -397,10 +387,11 @@
         GLES20.glViewport(0, 0, size.getWidth(), size.getHeight());
         GLES20.glScissor(0, 0, size.getWidth(), size.getHeight());
 
-        // Upload transform matrix.
-        GLES20.glUniformMatrix4fv(mTexMatrixLoc, /*count=*/1, /*transpose=*/false, textureTransform,
-                /*offset=*/0);
-        checkGlErrorOrThrow("glUniformMatrix4fv");
+        Program2D program = Preconditions.checkNotNull(mCurrentProgram);
+        if (program instanceof SamplerShaderProgram) {
+            // Upload transform matrix.
+            ((SamplerShaderProgram) program).updateTextureMatrix(textureTransform);
+        }
 
         // Draw the external texture to the intermediate texture.
         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*firstVertex=*/0, /*vertexCount=*/4);
@@ -515,79 +506,30 @@
         }
     }
 
-    protected void useAndConfigureProgram(int textureId) {
-        // Select the program.
-        GLES20.glUseProgram(mProgramHandle);
-        checkGlErrorOrThrow("glUseProgram");
-
-        // Initialize the samplers to the correct texture unit offsets
-        GLES20.glUniform1i(mSamplerDefaultLoc, 0);
-        if (mIsDefaultHdrShader) {
-            GLES20.glUniform1i(mSamplerYuvLoc, mExternalTexNumUnits);
+    protected void useAndConfigureProgramWithTexture(int textureId) {
+        Program2D program = mProgramHandles.get(mCurrentInputformat);
+        if (program == null) {
+            throw new IllegalStateException(
+                    "Unable to configure program for input format: " + mCurrentInputformat);
         }
-
-        // Enable the "aPosition" vertex attribute.
-        GLES20.glEnableVertexAttribArray(mPositionLoc);
-        checkGlErrorOrThrow("glEnableVertexAttribArray");
-
-        // Connect vertexBuffer to "aPosition".
-        int coordsPerVertex = 2;
-        int vertexStride = 0;
-        GLES20.glVertexAttribPointer(mPositionLoc, coordsPerVertex, GLES20.GL_FLOAT,
-                /*normalized=*/false, vertexStride, VERTEX_BUF);
-        checkGlErrorOrThrow("glVertexAttribPointer");
-
-        // Enable the "aTextureCoord" vertex attribute.
-        GLES20.glEnableVertexAttribArray(mTexCoordLoc);
-        checkGlErrorOrThrow("glEnableVertexAttribArray");
-
-        // Connect texBuffer to "aTextureCoord".
-        int coordsPerTex = 2;
-        int texStride = 0;
-        GLES20.glVertexAttribPointer(mTexCoordLoc, coordsPerTex, GLES20.GL_FLOAT,
-                /*normalized=*/false, texStride, TEX_BUF);
-        checkGlErrorOrThrow("glVertexAttribPointer");
-
-        // Set to default value for single camera case
-        GLES20.glUniformMatrix4fv(mTransMatrixLoc,
-                /*count=*/1, /*transpose=*/false, create4x4IdentityMatrix(),
-                /*offset=*/0);
-        checkGlErrorOrThrow("glUniformMatrix4fv");
-
-        GLES20.glUniform1f(mAlphaScaleLoc, 1.0f);
-        checkGlErrorOrThrow("glUniform1f");
+        if (mCurrentProgram != program) {
+            mCurrentProgram = program;
+            mCurrentProgram.use();
+            Log.d(TAG, "Using program for input format " + mCurrentInputformat + ": "
+                    + mCurrentProgram);
+        }
 
         // Activate the texture
         activateExternalTexture(textureId);
     }
 
-    private void loadLocations() {
-        mPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
-        checkLocationOrThrow(mPositionLoc, "aPosition");
-        mTexCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord");
-        checkLocationOrThrow(mTexCoordLoc, "aTextureCoord");
-        mTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix");
-        checkLocationOrThrow(mTexMatrixLoc, "uTexMatrix");
-        mTransMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTransMatrix");
-        checkLocationOrThrow(mTransMatrixLoc, "uTransMatrix");
-        mAlphaScaleLoc = GLES20.glGetUniformLocation(mProgramHandle, "uAlphaScale");
-        checkLocationOrThrow(mAlphaScaleLoc, "uAlphaScale");
-        mSamplerDefaultLoc = GLES20.glGetUniformLocation(mProgramHandle, VAR_TEXTURE);
-        checkLocationOrThrow(mSamplerDefaultLoc, VAR_TEXTURE);
-        if (mIsDefaultHdrShader) {
-            mSamplerYuvLoc = GLES20.glGetUniformLocation(mProgramHandle, VAR_TEXTURE_YUV);
-            checkLocationOrThrow(mSamplerYuvLoc, VAR_TEXTURE_YUV);
-            mSamplerSelectorLoc = GLES20.glGetUniformLocation(mProgramHandle, "uSamplerSelector");
-            checkLocationOrThrow(mSamplerSelectorLoc, "uSamplerSelector");
-        }
-    }
-
     private void releaseInternal() {
         // Delete program
-        if (mProgramHandle != -1) {
-            GLES20.glDeleteProgram(mProgramHandle);
-            mProgramHandle = -1;
+        for (Program2D program : mProgramHandles.values()) {
+            program.delete();
         }
+        mProgramHandles = Collections.emptyMap();
+        mCurrentProgram = null;
 
         if (!Objects.equals(mEglDisplay, EGL14.EGL_NO_DISPLAY)) {
             EGL14.eglMakeCurrent(mEglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
@@ -621,21 +563,10 @@
 
         // Reset other members
         mEglConfig = null;
-        mProgramHandle = -1;
-        mTexMatrixLoc = -1;
-        mPositionLoc = -1;
-        mTexCoordLoc = -1;
-        mTransMatrixLoc = -1;
-        mAlphaScaleLoc = -1;
-        mSamplerDefaultLoc = -1;
-        mSamplerYuvLoc = -1;
-        mSamplerSelectorLoc = -1;
         mExternalTextureId = -1;
-        mExternalTexNumUnits = -1;
         mCurrentInputformat = InputFormat.UNKNOWN;
         mCurrentSurface = null;
         mGlThread = null;
-        mIsDefaultHdrShader = false;
     }
 
     @NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/ShaderProvider.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/ShaderProvider.java
index 24b4782..138a578 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/ShaderProvider.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/ShaderProvider.java
@@ -57,9 +57,4 @@
             @NonNull String fragCoordsVarName) {
         return null;
     }
-
-    /** A default provider that will use the default shader code without any post-processing. */
-    ShaderProvider DEFAULT = new ShaderProvider() {
-        // Use default implementation.
-    };
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualOpenGlRenderer.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualOpenGlRenderer.java
index 8576549..fc7fe62 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualOpenGlRenderer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualOpenGlRenderer.java
@@ -40,8 +40,14 @@
 import androidx.camera.core.SurfaceOutput;
 import androidx.camera.core.processing.OpenGlRenderer;
 import androidx.camera.core.processing.ShaderProvider;
+import androidx.camera.core.processing.util.GLUtils;
+import androidx.camera.core.processing.util.GLUtils.InputFormat;
+import androidx.camera.core.processing.util.GLUtils.SamplerShaderProgram;
 import androidx.camera.core.processing.util.GraphicDeviceInfo;
 import androidx.camera.core.processing.util.OutputSurface;
+import androidx.core.util.Preconditions;
+
+import java.util.Map;
 
 /**
  * An internal augmented {@link OpenGlRenderer} for dual concurrent cameras.
@@ -69,8 +75,8 @@
     @NonNull
     @Override
     public GraphicDeviceInfo init(@NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider) {
-        GraphicDeviceInfo graphicDeviceInfo = super.init(dynamicRange, shaderProvider);
+            @NonNull Map<InputFormat, ShaderProvider> shaderProviderOverrides) {
+        GraphicDeviceInfo graphicDeviceInfo = super.init(dynamicRange, shaderProviderOverrides);
         mPrimaryExternalTextureId = createTexture();
         mSecondaryExternalTextureId = createTexture();
         return graphicDeviceInfo;
@@ -154,7 +160,7 @@
             @NonNull LayoutSettings layoutSettings,
             int externalTextureId,
             boolean isPrimary) {
-        useAndConfigureProgram(externalTextureId);
+        useAndConfigureProgramWithTexture(externalTextureId);
         GLES20.glViewport(0, 0, outputSurface.getWidth(),
                 outputSurface.getHeight());
         GLES20.glScissor(0, 0, outputSurface.getWidth(),
@@ -167,22 +173,19 @@
         surfaceOutput.updateTransformMatrix(
                 surfaceOutputMatrix, textureTransform, isPrimary);
 
-        GLES20.glUniformMatrix4fv(mTexMatrixLoc,
-                /*count=*/1, /*transpose=*/false, surfaceOutputMatrix,
-                /*offset=*/0);
-        checkGlErrorOrThrow("glUniformMatrix4fv");
+        GLUtils.Program2D currentProgram = Preconditions.checkNotNull(mCurrentProgram);
+        if (currentProgram instanceof SamplerShaderProgram) {
+            ((SamplerShaderProgram) currentProgram).updateTextureMatrix(surfaceOutputMatrix);
+        }
 
         float[] transTransform = getTransformMatrix(
                 new Size((int) (outputSurface.getWidth() * layoutSettings.getWidth()),
                         (int) (outputSurface.getHeight() * layoutSettings.getHeight())),
                 new Size(outputSurface.getWidth(), outputSurface.getHeight()),
                 layoutSettings);
-        GLES20.glUniformMatrix4fv(mTransMatrixLoc,
-                /*count=*/1, /*transpose=*/false, transTransform,
-                /*offset=*/0);
+        currentProgram.updateTransformMatrix(transTransform);
 
-        GLES20.glUniform1f(mAlphaScaleLoc, layoutSettings.getAlpha());
-        checkGlErrorOrThrow("glUniform1f");
+        currentProgram.updateAlpha(layoutSettings.getAlpha());
 
         GLES20.glEnable(GLES20.GL_BLEND);
         GLES20.glBlendFuncSeparate(
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java
index d7fd7b7..afa52f8 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java
@@ -38,12 +38,14 @@
 import androidx.camera.core.processing.DefaultSurfaceProcessor;
 import androidx.camera.core.processing.ShaderProvider;
 import androidx.camera.core.processing.SurfaceProcessorInternal;
+import androidx.camera.core.processing.util.GLUtils.InputFormat;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
 import kotlin.jvm.functions.Function3;
 
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -81,12 +83,12 @@
     DualSurfaceProcessor(@NonNull DynamicRange dynamicRange,
             @NonNull LayoutSettings primaryLayoutSettings,
             @NonNull LayoutSettings secondaryLayoutSettings) {
-        this(dynamicRange, ShaderProvider.DEFAULT, primaryLayoutSettings, secondaryLayoutSettings);
+        this(dynamicRange, Collections.emptyMap(), primaryLayoutSettings, secondaryLayoutSettings);
     }
 
     DualSurfaceProcessor(
             @NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider,
+            @NonNull Map<InputFormat, ShaderProvider> shaderProviderOverrides,
             @NonNull LayoutSettings primaryLayoutSettings,
             @NonNull LayoutSettings secondaryLayoutSettings) {
         mGlThread = new HandlerThread("GL Thread");
@@ -95,7 +97,7 @@
         mGlExecutor = CameraXExecutors.newHandlerExecutor(mGlHandler);
         mGlRenderer = new DualOpenGlRenderer(primaryLayoutSettings, secondaryLayoutSettings);
         try {
-            initGlRenderer(dynamicRange, shaderProvider);
+            initGlRenderer(dynamicRange, shaderProviderOverrides);
         } catch (RuntimeException e) {
             release();
             throw e;
@@ -125,10 +127,10 @@
             });
             if (surfaceRequest.isPrimary()) {
                 mPrimarySurfaceTexture = surfaceTexture;
-                // Only render when primary camera frames come in
-                surfaceTexture.setOnFrameAvailableListener(this, mGlHandler);
             } else {
                 mSecondarySurfaceTexture = surfaceTexture;
+                // Only render when secondary camera frames come in
+                surfaceTexture.setOnFrameAvailableListener(this, mGlHandler);
             }
         }, surfaceRequest::willNotProvideSurface);
     }
@@ -197,11 +199,11 @@
 
     private void initGlRenderer(
             @NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider) {
+            @NonNull Map<InputFormat, ShaderProvider> shaderProviderOverrides) {
         ListenableFuture<Void> initFuture = CallbackToFutureAdapter.getFuture(completer -> {
             executeSafely(() -> {
                 try {
-                    mGlRenderer.init(dynamicRange, shaderProvider);
+                    mGlRenderer.init(dynamicRange, shaderProviderOverrides);
                     completer.set(null);
                 } catch (RuntimeException e) {
                     completer.setException(e);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/util/GLUtils.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/util/GLUtils.java
index c60f0dc..0ab26da 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/util/GLUtils.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/util/GLUtils.java
@@ -22,9 +22,7 @@
 import android.opengl.EGLConfig;
 import android.opengl.EGLDisplay;
 import android.opengl.EGLSurface;
-import android.opengl.GLES11Ext;
 import android.opengl.GLES20;
-import android.opengl.GLES30;
 import android.opengl.Matrix;
 import android.util.Log;
 import android.util.Size;
@@ -41,7 +39,9 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.FloatBuffer;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -62,11 +62,6 @@
 
     public static final String VAR_TEXTURE_COORD = "vTextureCoord";
     public static final String VAR_TEXTURE = "sTexture";
-    public static final String VAR_TEXTURE_YUV = "sTextureYuv";
-    // SAMPLER_SELECTOR_UNKNOWN must be 0 to correctly initialize HDR shader uniform selector
-    public static final int SAMPLER_SELECTOR_UNKNOWN = 0;
-    public static final int SAMPLER_SELECTOR_DEFAULT = 1;
-    public static final int SAMPLER_SELECTOR_YUV = 2;
     public static final int PIXEL_STRIDE = 4;
     public static final int[] EMPTY_ATTRIBS = {EGL14.EGL_NONE};
     public static final int[] HLG_SURFACE_ATTRIBS = {
@@ -97,59 +92,93 @@
                     + "  %s = (uTexMatrix * aTextureCoord).xy;\n"
                     + "}\n", VAR_TEXTURE_COORD, VAR_TEXTURE_COORD);
 
-    public static final String DEFAULT_FRAGMENT_SHADER = String.format(Locale.US,
-            "#extension GL_OES_EGL_image_external : require\n"
-                    + "precision mediump float;\n"
-                    + "varying vec2 %s;\n"
-                    + "uniform samplerExternalOES %s;\n"
-                    + "uniform float uAlphaScale;\n"
+    public static final String BLANK_VERTEX_SHADER =
+            "uniform mat4 uTransMatrix;\n"
+                    + "attribute vec4 aPosition;\n"
                     + "void main() {\n"
-                    + "    vec4 src = texture2D(%s, %s);\n"
-                    + "    gl_FragColor = vec4(src.rgb, src.a * uAlphaScale);\n"
-                    + "}\n", VAR_TEXTURE_COORD, VAR_TEXTURE, VAR_TEXTURE, VAR_TEXTURE_COORD);
+                    + "    gl_Position = uTransMatrix * aPosition;\n"
+                    + "}\n";
 
-    public static final String HDR_FRAGMENT_SHADER = String.format(Locale.US,
-            "#version 300 es\n"
-                    + "#extension GL_OES_EGL_image_external_essl3 : require\n"
-                    + "#extension GL_EXT_YUV_target : require\n"
-                    + "precision mediump float;\n"
-                    + "uniform samplerExternalOES %s;\n"
-                    + "uniform __samplerExternal2DY2YEXT %s;\n"
-                    + "uniform int uSamplerSelector;\n"
+    public static final String BLANK_FRAGMENT_SHADER =
+            "precision mediump float;\n"
                     + "uniform float uAlphaScale;\n"
-                    + "in vec2 %s;\n"
-                    + "out vec4 outColor;\n"
-                    + "\n"
-                    + "vec3 yuvToRgb(vec3 yuv) {\n"
-                    + "  const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);\n"
-                    + "  const mat3 yuvToRgbColorTransform = mat3(\n"
-                    + "    1.1689f, 1.1689f, 1.1689f,\n"
-                    + "    0.0000f, -0.1881f, 2.1502f,\n"
-                    + "    1.6853f, -0.6530f, 0.0000f\n"
-                    + "  );\n"
-                    + "  return clamp(yuvToRgbColorTransform * (yuv - yuvOffset), 0.0, 1.0);\n"
-                    + "}\n"
-                    + "\n"
                     + "void main() {\n"
-                    + "  vec4 src = vec4(0.0);\n"
-                    + "  if (uSamplerSelector == %d) {\n"
-                    + "    src = texture(%s, %s);\n"
-                    + "  } else if (uSamplerSelector == %d) {\n"
-                    + "    vec3 srcYuv = texture(%s, %s).xyz;\n"
-                    + "    src = vec4(yuvToRgb(srcYuv), 1.0);\n"
-                    + "  }\n"
-                    + "  outColor = vec4(src.rgb, src.a * uAlphaScale);\n"
-                    + "}",
-            VAR_TEXTURE,
-            VAR_TEXTURE_YUV,
-            VAR_TEXTURE_COORD,
-            SAMPLER_SELECTOR_DEFAULT,
-            VAR_TEXTURE,
-            VAR_TEXTURE_COORD,
-            SAMPLER_SELECTOR_YUV,
-            VAR_TEXTURE_YUV,
-            VAR_TEXTURE_COORD
-    );
+                    + "    gl_FragColor = vec4(0.0, 0.0, 0.0, uAlphaScale);\n"
+                    + "}\n";
+
+    private static final ShaderProvider SHADER_PROVIDER_DEFAULT = new ShaderProvider() {
+        @NonNull
+        @Override
+        public String createFragmentShader(@NonNull String samplerVarName,
+                @NonNull String fragCoordsVarName) {
+            return String.format(Locale.US,
+                    "#extension GL_OES_EGL_image_external : require\n"
+                            + "precision mediump float;\n"
+                            + "varying vec2 %s;\n"
+                            + "uniform samplerExternalOES %s;\n"
+                            + "uniform float uAlphaScale;\n"
+                            + "void main() {\n"
+                            + "    vec4 src = texture2D(%s, %s);\n"
+                            + "    gl_FragColor = vec4(src.rgb, src.a * uAlphaScale);\n"
+                            + "}\n",
+                    fragCoordsVarName, samplerVarName, samplerVarName, fragCoordsVarName);
+        }
+    };
+
+    private static final ShaderProvider SHADER_PROVIDER_HDR_DEFAULT = new ShaderProvider() {
+        @NonNull
+        @Override
+        public String createFragmentShader(@NonNull String samplerVarName,
+                @NonNull String fragCoordsVarName) {
+            return String.format(Locale.US,
+                    "#version 300 es\n"
+                            + "#extension GL_OES_EGL_image_external_essl3 : require\n"
+                            + "precision mediump float;\n"
+                            + "uniform samplerExternalOES %s;\n"
+                            + "uniform float uAlphaScale;\n"
+                            + "in vec2 %s;\n"
+                            + "out vec4 outColor;\n"
+                            + "\n"
+                            + "void main() {\n"
+                            + "  vec4 src = texture(%s, %s);\n"
+                            + "  outColor = vec4(src.rgb, src.a * uAlphaScale);\n"
+                            + "}",
+                    samplerVarName, fragCoordsVarName, samplerVarName, fragCoordsVarName);
+        }
+    };
+
+    private static final ShaderProvider SHADER_PROVIDER_HDR_YUV = new ShaderProvider() {
+        @NonNull
+        @Override
+        public String createFragmentShader(@NonNull String samplerVarName,
+                @NonNull String fragCoordsVarName) {
+            return String.format(Locale.US,
+                    "#version 300 es\n"
+                            + "#extension GL_EXT_YUV_target : require\n"
+                            + "precision mediump float;\n"
+                            + "uniform __samplerExternal2DY2YEXT %s;\n"
+                            + "uniform float uAlphaScale;\n"
+                            + "in vec2 %s;\n"
+                            + "out vec4 outColor;\n"
+                            + "\n"
+                            + "vec3 yuvToRgb(vec3 yuv) {\n"
+                            + "  const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);\n"
+                            + "  const mat3 yuvToRgbColorMat = mat3(\n"
+                            + "    1.1689f, 1.1689f, 1.1689f,\n"
+                            + "    0.0000f, -0.1881f, 2.1502f,\n"
+                            + "    1.6853f, -0.6530f, 0.0000f\n"
+                            + "  );\n"
+                            + "  return clamp(yuvToRgbColorMat * (yuv - yuvOffset), 0.0, 1.0);\n"
+                            + "}\n"
+                            + "\n"
+                            + "void main() {\n"
+                            + "  vec3 srcYuv = texture(%s, %s).xyz;\n"
+                            + "  vec3 srcRgb = yuvToRgb(srcYuv);\n"
+                            + "  outColor = vec4(srcRgb, uAlphaScale);\n"
+                            + "}",
+                    samplerVarName, fragCoordsVarName, samplerVarName, fragCoordsVarName);
+        }
+    };
 
     public static final float[] VERTEX_COORDS = {
             -1.0f, -1.0f,   // 0 bottom left
@@ -172,24 +201,212 @@
             OutputSurface.of(EGL14.EGL_NO_SURFACE, 0, 0);
 
     public enum InputFormat {
-        UNKNOWN(SAMPLER_SELECTOR_UNKNOWN),
-        DEFAULT(SAMPLER_SELECTOR_DEFAULT),
-        YUV(SAMPLER_SELECTOR_YUV);
-
-        private final int mSamplerSelector;
-
-        InputFormat(int samplerSelector) {
-            mSamplerSelector = samplerSelector;
-        }
-
-        public int getSamplerSelector() {
-            return mSamplerSelector;
-        }
+        /**
+         * Input texture format is unknown.
+         *
+         * <p>When the input format is unknown, HDR content may require rendering blank frames
+         * since we are not sure what type of sampler can be used. For SDR content, it is
+         * typically safe to use samplerExternalOES since this can handle both RGB and YUV inputs
+         * for SDR content.
+         */
+        UNKNOWN,
+        /**
+         * Input texture format is the default format.
+         *
+         * <p>The texture format may be RGB or YUV. For SDR content, using samplerExternalOES is
+         * safe since it will be able to convert YUV to RGB automatically within the shader. For
+         * HDR content, the input is expected to be RGB.
+         */
+        DEFAULT,
+        /**
+         * Input format is explicitly YUV.
+         *
+         * <p>This needs to be specified for HDR content. Only __samplerExternal2DY2YEXT should be
+         * used for HDR YUV content as samplerExternalOES may not correctly convert to RGB.
+         */
+        YUV
     }
 
     private GLUtils() {
     }
 
+    public abstract static class Program2D {
+        protected int mProgramHandle;
+        protected int mTransMatrixLoc = -1;
+        protected int mAlphaScaleLoc = -1;
+        protected int mPositionLoc = -1;
+
+        protected Program2D(@NonNull String vertexShaderSource,
+                @NonNull String fragmentShaderSource) {
+            int vertexShader = -1;
+            int fragmentShader = -1;
+            int program = -1;
+            try {
+                vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderSource);
+                fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderSource);
+                program = GLES20.glCreateProgram();
+                checkGlErrorOrThrow("glCreateProgram");
+                GLES20.glAttachShader(program, vertexShader);
+                checkGlErrorOrThrow("glAttachShader");
+                GLES20.glAttachShader(program, fragmentShader);
+                checkGlErrorOrThrow("glAttachShader");
+                GLES20.glLinkProgram(program);
+                int[] linkStatus = new int[1];
+                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, /*offset=*/0);
+                if (linkStatus[0] != GLES20.GL_TRUE) {
+                    throw new IllegalStateException(
+                            "Could not link program: " + GLES20.glGetProgramInfoLog(program));
+                }
+                mProgramHandle = program;
+            } catch (IllegalStateException | IllegalArgumentException e) {
+                if (vertexShader != -1) {
+                    GLES20.glDeleteShader(vertexShader);
+                }
+                if (fragmentShader != -1) {
+                    GLES20.glDeleteShader(fragmentShader);
+                }
+                if (program != -1) {
+                    GLES20.glDeleteProgram(program);
+                }
+                throw e;
+            }
+
+            loadLocations();
+        }
+
+        /** Use this shader program */
+        public void use() {
+            // Select the program.
+            GLES20.glUseProgram(mProgramHandle);
+            checkGlErrorOrThrow("glUseProgram");
+
+            // Enable the "aPosition" vertex attribute.
+            GLES20.glEnableVertexAttribArray(mPositionLoc);
+            checkGlErrorOrThrow("glEnableVertexAttribArray");
+
+            // Connect vertexBuffer to "aPosition".
+            int coordsPerVertex = 2;
+            int vertexStride = 0;
+            GLES20.glVertexAttribPointer(mPositionLoc, coordsPerVertex, GLES20.GL_FLOAT,
+                    /*normalized=*/false, vertexStride, VERTEX_BUF);
+            checkGlErrorOrThrow("glVertexAttribPointer");
+
+            // Set to default value for single camera case
+            updateTransformMatrix(create4x4IdentityMatrix());
+            updateAlpha(1.0f);
+        }
+
+        /** Updates the global transform matrix */
+        public void updateTransformMatrix(@NonNull float[] transformMat) {
+            GLES20.glUniformMatrix4fv(mTransMatrixLoc,
+                    /*count=*/1, /*transpose=*/false, transformMat,
+                    /*offset=*/0);
+            checkGlErrorOrThrow("glUniformMatrix4fv");
+        }
+
+        /** Updates the alpha of the drawn frame */
+        public void updateAlpha(float alpha) {
+            GLES20.glUniform1f(mAlphaScaleLoc, alpha);
+            checkGlErrorOrThrow("glUniform1f");
+        }
+
+        /**
+         * Delete the shader program
+         *
+         * <p>Once called, this program should no longer be used.
+         */
+        public void delete() {
+            GLES20.glDeleteProgram(mProgramHandle);
+        }
+
+        private void loadLocations() {
+            mPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
+            checkLocationOrThrow(mPositionLoc, "aPosition");
+            mTransMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTransMatrix");
+            checkLocationOrThrow(mTransMatrixLoc, "uTransMatrix");
+            mAlphaScaleLoc = GLES20.glGetUniformLocation(mProgramHandle, "uAlphaScale");
+            checkLocationOrThrow(mAlphaScaleLoc, "uAlphaScale");
+        }
+    }
+
+    public static class SamplerShaderProgram extends Program2D {
+        private int mSamplerLoc = -1;
+        private int mTexMatrixLoc = -1;
+        private int mTexCoordLoc = -1;
+
+        public SamplerShaderProgram(
+                @NonNull DynamicRange dynamicRange,
+                @NonNull InputFormat inputFormat
+        ) {
+            this(dynamicRange, resolveDefaultShaderProvider(dynamicRange, inputFormat));
+        }
+
+        public SamplerShaderProgram(
+                @NonNull DynamicRange dynamicRange,
+                @NonNull ShaderProvider shaderProvider) {
+            super(dynamicRange.is10BitHdr() ? HDR_VERTEX_SHADER : DEFAULT_VERTEX_SHADER,
+                    getFragmentShaderSource(shaderProvider));
+
+            loadLocations();
+        }
+
+        @Override
+        public void use() {
+            super.use();
+            // Initialize the sampler to the correct texture unit offset
+            GLES20.glUniform1i(mSamplerLoc, 0);
+
+            // Enable the "aTextureCoord" vertex attribute.
+            GLES20.glEnableVertexAttribArray(mTexCoordLoc);
+            checkGlErrorOrThrow("glEnableVertexAttribArray");
+
+            // Connect texBuffer to "aTextureCoord".
+            int coordsPerTex = 2;
+            int texStride = 0;
+            GLES20.glVertexAttribPointer(mTexCoordLoc, coordsPerTex, GLES20.GL_FLOAT,
+                    /*normalized=*/false, texStride, TEX_BUF);
+            checkGlErrorOrThrow("glVertexAttribPointer");
+        }
+
+        /** Updates the texture transform matrix */
+        public void updateTextureMatrix(@NonNull float[] textureMat) {
+            GLES20.glUniformMatrix4fv(mTexMatrixLoc, /*count=*/1, /*transpose=*/false,
+                    textureMat, /*offset=*/0);
+            checkGlErrorOrThrow("glUniformMatrix4fv");
+        }
+
+        private void loadLocations() {
+            super.loadLocations();
+            mSamplerLoc = GLES20.glGetUniformLocation(mProgramHandle, VAR_TEXTURE);
+            checkLocationOrThrow(mSamplerLoc, VAR_TEXTURE);
+            mTexCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord");
+            checkLocationOrThrow(mTexCoordLoc, "aTextureCoord");
+            mTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix");
+            checkLocationOrThrow(mTexMatrixLoc, "uTexMatrix");
+        }
+
+        private static ShaderProvider resolveDefaultShaderProvider(
+                @NonNull DynamicRange dynamicRange,
+                @Nullable InputFormat inputFormat) {
+            if (dynamicRange.is10BitHdr()) {
+                Preconditions.checkArgument(inputFormat != InputFormat.UNKNOWN,
+                        "No default sampler shader available for" + inputFormat);
+                if (inputFormat == InputFormat.YUV) {
+                    return SHADER_PROVIDER_HDR_YUV;
+                }
+                return SHADER_PROVIDER_HDR_DEFAULT;
+            } else {
+                return SHADER_PROVIDER_DEFAULT;
+            }
+        }
+    }
+
+    public static class BlankShaderProgram extends Program2D {
+        public BlankShaderProgram() {
+            super(BLANK_VERTEX_SHADER, BLANK_FRAGMENT_SHADER);
+        }
+    }
+
     /**
      * Creates an {@link EGLSurface}.
      */
@@ -282,43 +499,52 @@
     }
 
     /**
-     * Creates a program object based on shaders.
+     * Creates program objects based on shaders which are appropriate for each input format.
+     *
+     * <p>Each {@link InputFormat} may have different sampler requirements based on the dynamic
+     * range. For that reason, we create a separate program for each input format, and will switch
+     * to the program when the input format changes so we correctly sample the input texture
+     * (or no-op, in some cases).
      */
-    public static int createProgram(@NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider) {
-        int vertexShader = -1;
-        int fragmentShader = -1;
-        int program = -1;
-        try {
-            vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
-                    dynamicRange.is10BitHdr() ? HDR_VERTEX_SHADER : DEFAULT_VERTEX_SHADER);
-            fragmentShader = loadFragmentShader(dynamicRange, shaderProvider);
-            program = GLES20.glCreateProgram();
-            checkGlErrorOrThrow("glCreateProgram");
-            GLES20.glAttachShader(program, vertexShader);
-            checkGlErrorOrThrow("glAttachShader");
-            GLES20.glAttachShader(program, fragmentShader);
-            checkGlErrorOrThrow("glAttachShader");
-            GLES20.glLinkProgram(program);
-            int[] linkStatus = new int[1];
-            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, /*offset=*/0);
-            if (linkStatus[0] != GLES20.GL_TRUE) {
-                throw new IllegalStateException(
-                        "Could not link program: " + GLES20.glGetProgramInfoLog(program));
+    @NonNull
+    public static Map<InputFormat, Program2D> createPrograms(@NonNull DynamicRange dynamicRange,
+            @NonNull Map<InputFormat, ShaderProvider> shaderProviderOverrides) {
+        HashMap<InputFormat, Program2D> programs = new HashMap<>();
+        for (InputFormat inputFormat : InputFormat.values()) {
+            ShaderProvider shaderProviderOverride = shaderProviderOverrides.get(inputFormat);
+            Program2D program;
+            if (shaderProviderOverride != null) {
+                // Always use the overridden shader provider if present
+                program = new SamplerShaderProgram(dynamicRange, shaderProviderOverride);
+            } else if (inputFormat == InputFormat.YUV || inputFormat == InputFormat.DEFAULT) {
+                // Use a default sampler shader for DEFAULT or YUV
+                program = new SamplerShaderProgram(dynamicRange, inputFormat);
+            } else {
+                Preconditions.checkState(inputFormat == InputFormat.UNKNOWN,
+                        "Unhandled input format: " + inputFormat);
+                if (dynamicRange.is10BitHdr()) {
+                    // InputFormat is UNKNOWN and we don't know if we need to use a
+                    // YUV-specific sampler for HDR. Use a blank shader program.
+                    program = new BlankShaderProgram();
+                } else {
+                    // If we're not rendering HDR content, we can use the default sampler shader
+                    // program since it can handle both YUV and DEFAULT inputs when the format
+                    // is UNKNOWN.
+                    ShaderProvider defaultShaderProviderOverride =
+                            shaderProviderOverrides.get(InputFormat.DEFAULT);
+                    if (defaultShaderProviderOverride != null) {
+                        program = new SamplerShaderProgram(dynamicRange,
+                                defaultShaderProviderOverride);
+                    } else {
+                        program = new SamplerShaderProgram(dynamicRange, InputFormat.DEFAULT);
+                    }
+                }
             }
-            return program;
-        } catch (IllegalStateException | IllegalArgumentException e) {
-            if (vertexShader != -1) {
-                GLES20.glDeleteShader(vertexShader);
-            }
-            if (fragmentShader != -1) {
-                GLES20.glDeleteShader(fragmentShader);
-            }
-            if (program != -1) {
-                GLES20.glDeleteProgram(program);
-            }
-            throw e;
+            Log.d(TAG, "Shader program for input format " + inputFormat + " created: "
+                    + program);
+            programs.put(inputFormat, program);
         }
+        return programs;
     }
 
     /**
@@ -333,9 +559,9 @@
         GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, texId);
         checkGlErrorOrThrow("glBindTexture " + texId);
 
-        GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
+        GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
                 GLES20.GL_NEAREST);
-        GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
+        GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
                 GLES20.GL_LINEAR);
         GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
                 GLES20.GL_CLAMP_TO_EDGE);
@@ -346,36 +572,6 @@
     }
 
     /**
-     * Gets texture required image unites.
-     */
-    public static int getTexNumUnits() {
-        // Collect the required texture units for GL_TEXTURE_EXTERNAL_OES so we know
-        // which texture unit we can use to bind the YUV sampler. The documentation says
-        // GL_TEXTURE_EXTERNAL_OES can only use a maximum of 3 texture units, so default
-        // to that if we can't query the required units
-        int requiredUnits = -1;
-        try {
-            int[] texParams = new int[1];
-            GLES30.glGetTexParameteriv(
-                    GL_TEXTURE_EXTERNAL_OES,
-                    GLES11Ext.GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES,
-                    texParams, 0
-            );
-            checkGlErrorOrThrow("glGetTexParameteriv");
-            if (texParams[0] >= 0 && texParams[0] <= 3) {
-                requiredUnits = texParams[0];
-            } else {
-                Log.e(TAG, "Query for GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES returned out of"
-                        + " bounds size: " + texParams[0] + ". Defaulting to 3.");
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Unable to query GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES", e);
-        }
-
-        return requiredUnits != -1 ? requiredUnits : 3;
-    }
-
-    /**
      * Creates a 4x4 identity matrix.
      */
     @NonNull
@@ -521,30 +717,23 @@
         checkGlErrorOrThrow("glDeleteFramebuffers");
     }
 
-    private static int loadFragmentShader(@NonNull DynamicRange dynamicRange,
-            @NonNull ShaderProvider shaderProvider) {
-        if (shaderProvider == ShaderProvider.DEFAULT) {
-            return loadShader(GLES20.GL_FRAGMENT_SHADER,
-                    dynamicRange.is10BitHdr() ? HDR_FRAGMENT_SHADER : DEFAULT_FRAGMENT_SHADER);
-        } else {
-            // Throw IllegalArgumentException if the shader provider can not provide a valid
-            // fragment shader.
-            String source;
-            try {
-                source = shaderProvider.createFragmentShader(VAR_TEXTURE, VAR_TEXTURE_COORD);
-                // A simple check to workaround custom shader doesn't contain required variable.
-                // See b/241193761.
-                if (source == null || !source.contains(VAR_TEXTURE_COORD) || !source.contains(
-                        VAR_TEXTURE)) {
-                    throw new IllegalArgumentException("Invalid fragment shader");
-                }
-                return loadShader(GLES20.GL_FRAGMENT_SHADER, source);
-            } catch (Throwable t) {
-                if (t instanceof IllegalArgumentException) {
-                    throw t;
-                }
-                throw new IllegalArgumentException("Unable to compile fragment shader", t);
+    private static String getFragmentShaderSource(@NonNull ShaderProvider shaderProvider) {
+        // Throw IllegalArgumentException if the shader provider can not provide a valid
+        // fragment shader.
+        try {
+            String source = shaderProvider.createFragmentShader(VAR_TEXTURE, VAR_TEXTURE_COORD);
+            // A simple check to workaround custom shader doesn't contain required variable.
+            // See b/241193761.
+            if (source == null || !source.contains(VAR_TEXTURE_COORD) || !source.contains(
+                    VAR_TEXTURE)) {
+                throw new IllegalArgumentException("Invalid fragment shader");
             }
+            return source;
+        } catch (Throwable t) {
+            if (t instanceof IllegalArgumentException) {
+                throw t;
+            }
+            throw new IllegalArgumentException("Unable retrieve fragment shader source", t);
         }
     }
 }
diff --git a/camera/camera-effects-still-portrait/build.gradle b/camera/camera-effects-still-portrait/build.gradle
index 2e9f9d0..35b7c44 100644
--- a/camera/camera-effects-still-portrait/build.gradle
+++ b/camera/camera-effects-still-portrait/build.gradle
@@ -35,6 +35,5 @@
     runApiTasks = new RunApiTasks.Yes()
     description = "A post-processing effect that works with CameraX Library, providing a portrait" +
             " mode effect that applies to still image captures."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-effects/build.gradle b/camera/camera-effects/build.gradle
index 5eae11a..56fec95 100644
--- a/camera/camera-effects/build.gradle
+++ b/camera/camera-effects/build.gradle
@@ -57,6 +57,5 @@
     inceptionYear = "2023"
     description = "Camera effects components for the Jetpack Camera Library, a library providing " +
             "camera post-processing features such as drawing overlay with the CameraX library."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-effects/src/main/java/androidx/camera/effects/internal/Utils.java b/camera/camera-effects/src/main/java/androidx/camera/effects/internal/Utils.java
index 2fe2753..71b21c4 100644
--- a/camera/camera-effects/src/main/java/androidx/camera/effects/internal/Utils.java
+++ b/camera/camera-effects/src/main/java/androidx/camera/effects/internal/Utils.java
@@ -19,7 +19,6 @@
 import android.graphics.Canvas;
 import android.view.Surface;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -49,7 +48,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Canvas lockHardwareCanvas(Surface surface) {
             return surface.lockHardwareCanvas();
         }
diff --git a/camera/camera-extensions-stub/build.gradle b/camera/camera-extensions-stub/build.gradle
index efca9c0..0ad02fb 100644
--- a/camera/camera-extensions-stub/build.gradle
+++ b/camera/camera-extensions-stub/build.gradle
@@ -21,7 +21,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.0.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 2f09630..97acbb4 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -57,6 +57,7 @@
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(libs.testUiautomator)
     androidTestImplementation(libs.truth)
     androidTestImplementation(project(":camera:camera-camera2"))
     androidTestImplementation(project(":camera:camera-camera2-pipe-integration"))
@@ -69,6 +70,7 @@
     androidTestImplementation(project(":internal-testutils-truth"))
     androidTestImplementation(project(":camera:camera-testlib-extensions"))
     androidTestImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
+    androidTestImplementation("androidx.test.espresso:espresso-core:3.3.0")
     // To use the testlib to have the implementation of the extensions-stub interface.
 }
 
@@ -92,6 +94,5 @@
     inceptionYear = "2019"
     description = "OEM Extensions for the Jetpack Camera Library, a library providing interfaces" +
             " to integrate with OEM specific camera features."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
index 3edbbbb..7619a785 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureTest.kt
@@ -579,6 +579,24 @@
         assertThat(hasError).isFalse()
     }
 
+    /**
+     * Test Bind and then unbind immediately to ensure there is not race conditions or deadlocks.
+     */
+    @Test
+    fun canBindAndUnbindImmediately(): Unit = runBlocking {
+        val imageCapture = ImageCapture.Builder().build()
+
+        withContext(Dispatchers.Main) {
+            cameraProvider.bindToLifecycle(
+                fakeLifecycleOwner,
+                extensionsCameraSelector,
+                imageCapture
+            )
+        }
+
+        withContext(Dispatchers.Main) { cameraProvider.unbindAll() }
+    }
+
     @Test
     fun highResolutionDisabled_whenExtensionsEnabled(): Unit = runBlocking {
         val imageCapture = ImageCapture.Builder().build()
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/proguard/ReleaseApkTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/proguard/ReleaseApkTest.kt
new file mode 100644
index 0000000..8923dc8
--- /dev/null
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/proguard/ReleaseApkTest.kt
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.proguard
+
+import android.content.Context
+import android.content.Intent
+import android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_AUTOMATIC
+import android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_BOKEH
+import android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH
+import android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_HDR
+import android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_NIGHT
+import android.hardware.camera2.CameraManager
+import android.os.Build
+import android.os.Bundle
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.core.CameraFilter
+import androidx.camera.core.CameraInfo
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraXConfig
+import androidx.camera.core.impl.CameraInfoInternal
+import androidx.camera.extensions.ExtensionMode.AUTO
+import androidx.camera.extensions.ExtensionMode.BOKEH
+import androidx.camera.extensions.ExtensionMode.FACE_RETOUCH
+import androidx.camera.extensions.ExtensionMode.HDR
+import androidx.camera.extensions.ExtensionMode.NIGHT
+import androidx.camera.extensions.ExtensionsManager
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.impl.CameraPipeConfigTestRule
+import androidx.camera.testing.impl.CameraUtil
+import androidx.camera.testing.impl.CameraUtil.PreTestCameraIdList
+import androidx.camera.testing.impl.CoreAppTestUtil
+import androidx.camera.testing.impl.LabTestRule
+import androidx.camera.testing.impl.activity.RequestResultTestActivity
+import androidx.camera.testing.impl.activity.RequestResultTestActivity.INTENT_EXTRA_BUNDLE
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso
+import androidx.test.espresso.IdlingRegistry
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.google.common.truth.Truth.assertWithMessage
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class ReleaseApkTest(private val config: CameraXExtensionTestParams) {
+
+    @get:Rule
+    val cameraPipeConfigTestRule =
+        CameraPipeConfigTestRule(
+            active = config.implName == CameraPipeConfig::class.simpleName,
+        )
+
+    @get:Rule
+    val cameraRule =
+        CameraUtil.grantCameraPermissionAndPreTestAndPostTest(
+            PreTestCameraIdList(config.cameraXConfig)
+        )
+
+    @get:Rule val labTestRule = LabTestRule()
+
+    private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    private lateinit var requestResultActivityScenario: ActivityScenario<RequestResultTestActivity>
+
+    @Before
+    fun setUp() {
+        // Clear the device UI and check if there is no dialog or lock screen on the top of the
+        // window before start the test.
+        CoreAppTestUtil.prepareDeviceUI(InstrumentationRegistry.getInstrumentation())
+    }
+
+    @After
+    fun tearDown(): Unit = runBlocking {
+        if (::requestResultActivityScenario.isInitialized) {
+            requestResultActivityScenario.close()
+        }
+
+        // Unfreeze rotation so the device can choose the orientation via its own policy. Be nice
+        // to other tests :)
+        device.unfreezeRotation()
+        device.pressHome()
+        device.waitForIdle(3000)
+    }
+
+    @LabTestRule.LabTestOnly
+    @Test
+    fun canLaunchReleaseVersionApk() {
+        // Launches the RequestResultTestActivity to invoke startActivityForResult to the
+        // extensions test app's ReleaseTestActivity.
+        val startIntent =
+            Intent(Intent.ACTION_MAIN).apply {
+                setClassName(context.packageName, RequestResultTestActivity::class.java.name)
+                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                // Specify the intent action string for launching the ReleaseTestActivity.
+                putExtra(
+                    RequestResultTestActivity.INTENT_EXTRA_INTENT_ACTION,
+                    "androidx.camera.integration.extensions.release_test"
+                )
+                // Specifies the target impl mode, running mode, camera id and extension mode info.
+                putExtra(
+                    INTENT_EXTRA_BUNDLE,
+                    Bundle().apply {
+                        putString(
+                            INTENT_EXTRA_CAMERA_IMPLEMENTATION,
+                            if (config.implName == CameraPipeConfig::class.simpleName) {
+                                CAMERA_PIPE_IMPLEMENTATION_OPTION
+                            } else {
+                                CAMERA2_IMPLEMENTATION_OPTION
+                            }
+                        )
+                        putString(INTENT_EXTRA_RUNNING_MODE_CHECK, "release")
+                        putString(INTENT_EXTRA_KEY_CAMERA_ID, config.cameraId)
+                        putInt(INTENT_EXTRA_KEY_EXTENSION_MODE, config.extensionMode)
+                    }
+                )
+            }
+        val activityRef =
+            RequestResultTestActivity::class
+                .java
+                .cast(InstrumentationRegistry.getInstrumentation().startActivitySync(startIntent))!!
+        try {
+            IdlingRegistry.getInstance().register(activityRef.requestResultReadyIdlingResource)
+
+            // The {@link Espresso#onIdle()} throws timeout exception if the
+            // RequestResultTestActivity
+            // cannot get onActivityResult. The default timeout in espresso is 26 sec.
+            Espresso.onIdle()
+            assertWithMessage(activityRef.resultErrorMessage)
+                .that(activityRef.resultErrorCode)
+                .isEqualTo(RESULT_ERROR_NONE)
+        } finally {
+            IdlingRegistry.getInstance().unregister(activityRef.requestResultReadyIdlingResource)
+        }
+    }
+
+    companion object {
+        /** Launches the activity with the specified CameraX implementation. */
+        private const val INTENT_EXTRA_CAMERA_IMPLEMENTATION = "camera_implementation"
+
+        /** Launches the activity with the specified id of camera. */
+        private const val INTENT_EXTRA_KEY_CAMERA_ID = "camera_id"
+
+        /** Launches the activity with the specified extension mode. */
+        private const val INTENT_EXTRA_KEY_EXTENSION_MODE = "extension_mode"
+
+        /** Used to pass the running mode to check. The valid values are debug and release. */
+        private const val INTENT_EXTRA_RUNNING_MODE_CHECK = "running_mode_check"
+
+        /** Result error code - no error */
+        private const val RESULT_ERROR_NONE = 0
+
+        private const val CAMERA2_IMPLEMENTATION_OPTION: String = "camera2"
+        private const val CAMERA_PIPE_IMPLEMENTATION_OPTION: String = "camera_pipe"
+
+        private val context = ApplicationProvider.getApplicationContext<Context>()!!
+
+        @Parameterized.Parameters(name = "config = {0}")
+        @JvmStatic
+        fun parameters() = getAllCameraIdExtensionModeCombinations()
+
+        data class CameraXExtensionTestParams(
+            val implName: String,
+            val cameraXConfig: CameraXConfig,
+            val cameraId: String,
+            val extensionMode: Int,
+        )
+
+        /** Gets a list of all camera id and extension mode combinations. */
+        @JvmStatic
+        fun getAllCameraIdExtensionModeCombinations(
+            context: Context = ApplicationProvider.getApplicationContext()
+        ): List<CameraXExtensionTestParams> =
+            filterOutUnavailableMode(
+                context,
+                CameraUtil.getBackwardCompatibleCameraIdListOrThrow().flatMap { cameraId ->
+                    AVAILABLE_EXTENSION_MODES.flatMap { extensionMode ->
+                        CAMERAX_CONFIGS.map { config ->
+                            CameraXExtensionTestParams(
+                                config.first!!,
+                                config.second,
+                                cameraId,
+                                extensionMode
+                            )
+                        }
+                    }
+                }
+            )
+
+        @JvmStatic
+        private fun filterOutUnavailableMode(
+            context: Context,
+            list: List<CameraXExtensionTestParams>
+        ): List<CameraXExtensionTestParams> {
+            var extensionsManager: ExtensionsManager? = null
+            var cameraProvider: ProcessCameraProvider? = null
+            try {
+                cameraProvider = ProcessCameraProvider.getInstance(context)[2, TimeUnit.SECONDS]
+                extensionsManager =
+                    ExtensionsManager.getInstanceAsync(context, cameraProvider)[2, TimeUnit.SECONDS]
+
+                val result: MutableList<CameraXExtensionTestParams> = mutableListOf()
+                for (item in list) {
+                    val cameraSelector = createCameraSelectorById(item.cameraId)
+                    // For CameraX extensions, minification might cause a default ExtensionsManager
+                    // instance returned. In that case, no item will be added to the testing target
+                    // list. Therefore, adding the items according to Camera2Extensions support
+                    // list. Then, the test will fail when ReleaseTestActivity checks whether the
+                    // target extensions mode is supported or not. This can avoid the false-positive
+                    // test result situation.
+                    if (
+                        (isCamera2ExtensionsSupported(context, item.cameraId, item.extensionMode) &&
+                            !isDeviceOnlySupportedInCamera2Extensions()) ||
+                            extensionsManager.isExtensionAvailable(
+                                cameraSelector,
+                                item.extensionMode
+                            )
+                    ) {
+                        result.add(item)
+                    }
+                }
+                return result
+            } catch (e: Exception) {
+                return list
+            } finally {
+                try {
+                    cameraProvider?.shutdownAsync()?.get()
+                    extensionsManager?.shutdown()?.get()
+                } catch (_: Exception) {}
+            }
+        }
+
+        @JvmStatic
+        private fun createCameraSelectorById(cameraId: String) =
+            CameraSelector.Builder()
+                .addCameraFilter(
+                    CameraFilter { cameraInfos ->
+                        cameraInfos.forEach {
+                            if ((it as CameraInfoInternal).cameraId.equals(cameraId)) {
+                                return@CameraFilter listOf<CameraInfo>(it)
+                            }
+                        }
+
+                        return@CameraFilter emptyList()
+                    }
+                )
+                .build()
+
+        /** Checks whether the corresponding extensions mode is supported in Camera2Extensions. */
+        @JvmStatic
+        private fun isCamera2ExtensionsSupported(
+            context: Context,
+            cameraId: String,
+            cameraXExtensionMode: Int
+        ): Boolean {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+                return false
+            }
+
+            val correspondingMode =
+                when (cameraXExtensionMode) {
+                    BOKEH -> EXTENSION_BOKEH
+                    HDR -> EXTENSION_HDR
+                    NIGHT -> EXTENSION_NIGHT
+                    FACE_RETOUCH -> EXTENSION_FACE_RETOUCH
+                    AUTO -> EXTENSION_AUTOMATIC
+                    else ->
+                        throw IllegalArgumentException(
+                            "No matching Camera2Extensions mode can be found!"
+                        )
+                }
+
+            val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+            val characteristics = cameraManager.getCameraExtensionCharacteristics(cameraId)
+
+            return characteristics.supportedExtensions.contains(correspondingMode)
+        }
+
+        /**
+         * Some devices only support Camera2Extensions but do not support CameraXExtensions. For
+         * these devices, do not force add to the testing target list.
+         */
+        @JvmStatic
+        private fun isDeviceOnlySupportedInCamera2Extensions() =
+            Build.BRAND.equals("samsung", true) && Build.MODEL.equals("sm-n975u1", true)
+
+        @JvmStatic
+        private val AVAILABLE_EXTENSION_MODES = arrayOf(BOKEH, HDR, NIGHT, FACE_RETOUCH, AUTO)
+
+        /** A list of supported implementation options and their respective [CameraXConfig]. */
+        private val CAMERAX_CONFIGS =
+            listOf(
+                Pair(Camera2Config::class.simpleName, Camera2Config.defaultConfig()),
+                Pair(CameraPipeConfig::class.simpleName, CameraPipeConfig.defaultConfig())
+            )
+    }
+}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/Api21Impl.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/Api21Impl.kt
index 81d097c..99104e5 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/Api21Impl.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/Api21Impl.kt
@@ -17,18 +17,16 @@
 package androidx.camera.extensions.util
 
 import android.hardware.camera2.CameraDevice
-import androidx.annotation.DoNotInline
 
 /** Helper class to prevent class verification failures at API level 21. */
 object Api21Impl {
 
-    @DoNotInline @JvmStatic fun CameraDevice.toCameraDeviceWrapper() = CameraDeviceWrapper(this)
+    @JvmStatic fun CameraDevice.toCameraDeviceWrapper() = CameraDeviceWrapper(this)
 
     class CameraDeviceWrapper(private val cameraDevice: CameraDevice) {
 
-        @DoNotInline fun unwrap(): CameraDevice = cameraDevice
+        fun unwrap(): CameraDevice = cameraDevice
 
-        @DoNotInline
         fun close() {
             cameraDevice.close()
         }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUtils.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUtils.java
index cd7f6c8..99c73a6 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUtils.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUtils.java
@@ -19,7 +19,6 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.CameraInfoInternal;
@@ -81,7 +80,6 @@
         private Api28Impl() {
         }
 
-        @DoNotInline
         static Set<String> getPhysicalCameraIds(
                 @NonNull CameraCharacteristics cameraCharacteristics) {
             try {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/CaptureOutputSurfaceForCaptureProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/CaptureOutputSurfaceForCaptureProcessor.java
index edf011b..cd5687c 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/CaptureOutputSurfaceForCaptureProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/compat/workaround/CaptureOutputSurfaceForCaptureProcessor.java
@@ -23,7 +23,6 @@
 import android.util.Size;
 import android.view.Surface;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.OptIn;
@@ -152,7 +151,6 @@
          * Creates a {@link ImageWriter} instance.
          */
         @NonNull
-        @DoNotInline
         static ImageWriter newInstance(@NonNull Surface surface, int maxImages, int imageFormat) {
             return ImageWriter.newInstance(surface, maxImages, imageFormat);
         }
@@ -161,7 +159,6 @@
             imageWriter.queueInputImage(image);
         }
 
-        @DoNotInline
         static void close(ImageWriter imageWriter) {
             imageWriter.close();
         }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverter.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverter.java
index cb733cc..e33ea65 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverter.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverter.java
@@ -46,7 +46,10 @@
             }
         }
 
-        if (impl instanceof SurfaceOutputConfigImpl) {
+        // Workaround to avoid the R8 issue (b/350689668)
+        // Force cast the input Camera2OutputConfigImpl instance to all possible types in sequence
+        // to know what is its real type.
+        try {
             SurfaceOutputConfigImpl surfaceImpl = (SurfaceOutputConfigImpl) impl;
             return SurfaceOutputConfig.create(
                     surfaceImpl.getId(),
@@ -54,7 +57,11 @@
                     surfaceImpl.getPhysicalCameraId(),
                     sharedOutputConfigs,
                     surfaceImpl.getSurface());
-        } else if (impl instanceof ImageReaderOutputConfigImpl) {
+        } catch (ClassCastException e) {
+            // Not type of SurfaceOutputConfigImpl! Go to try the next type.
+        }
+
+        try {
             ImageReaderOutputConfigImpl imageReaderImpl = (ImageReaderOutputConfigImpl) impl;
             return ImageReaderOutputConfig.create(
                     imageReaderImpl.getId(),
@@ -64,7 +71,11 @@
                     imageReaderImpl.getSize(),
                     imageReaderImpl.getImageFormat(),
                     imageReaderImpl.getMaxImages());
-        } else if (impl instanceof MultiResolutionImageReaderOutputConfigImpl) {
+        } catch (ClassCastException e) {
+            // Not type of ImageReaderOutputConfigImpl! Go to try the next type.
+        }
+
+        try {
             MultiResolutionImageReaderOutputConfigImpl multiResolutionImageReaderImpl =
                     (MultiResolutionImageReaderOutputConfigImpl) impl;
             return MultiResolutionImageReaderOutputConfig.create(
@@ -74,7 +85,10 @@
                     sharedOutputConfigs,
                     multiResolutionImageReaderImpl.getImageFormat(),
                     multiResolutionImageReaderImpl.getMaxImages());
+        } catch (ClassCastException e) {
+            // Not type of MultiResolutionImageReaderOutputConfigImpl!
         }
+
         throw new IllegalArgumentException(
                 "Not supported Camera2OutputConfigImpl: " + impl.getClass());
     }
diff --git a/camera/camera-feature-combination-query-play-services/build.gradle b/camera/camera-feature-combination-query-play-services/build.gradle
index 721b314..b3e6ba6 100644
--- a/camera/camera-feature-combination-query-play-services/build.gradle
+++ b/camera/camera-feature-combination-query-play-services/build.gradle
@@ -24,6 +24,7 @@
 
 dependencies {
     api(libs.androidx.annotation)
+    project(":camera:camera-feature-combination-query")
     implementation(project(":camera:camera-feature-combination-query"))
 
     testImplementation(libs.testRunner)
@@ -32,9 +33,16 @@
     testImplementation(libs.truth)
     testImplementation(libs.testRules)
     testImplementation(libs.testCore)
+
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
 }
 
 android {
+    compileSdk 35
     lintOptions {
         enable 'CameraXQuirksClassDetector'
     }
@@ -51,6 +59,5 @@
     runApiTasks = new RunApiTasks.Yes()
     description = "Camera feature combination components for the Jetpack Camera Library, a " +
             "library providing camera feature combination with Google Play Services dependencies."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
-}
\ No newline at end of file
+}
diff --git a/camera/camera-feature-combination-query-play-services/src/androidTest/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompatTest.kt b/camera/camera-feature-combination-query-play-services/src/androidTest/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompatTest.kt
new file mode 100644
index 0000000..e2c097b
--- /dev/null
+++ b/camera/camera-feature-combination-query-play-services/src/androidTest/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompatTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery.playservices
+
+import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.params.SessionConfiguration
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.SOURCE_PLAY_SERVICES
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompatFactory
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 28)
+class PlayServicesCameraDeviceSetupCompatTest {
+
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+
+    @Test
+    fun queryResult_resultSourceIsPlayServices() {
+        // Arrange.
+        val factory = CameraDeviceSetupCompatFactory(instrumentation.context)
+        val impl = factory.getCameraDeviceSetupCompat("1")
+        val sessionConfiguration =
+            SessionConfiguration(
+                SessionConfiguration.SESSION_REGULAR,
+                listOf(),
+                directExecutor(),
+                object : CameraCaptureSession.StateCallback() {
+                    override fun onConfigured(p0: CameraCaptureSession) {
+                        // no-op
+                    }
+
+                    override fun onConfigureFailed(p0: CameraCaptureSession) {
+                        // no-op
+                    }
+                }
+            )
+        // Act.
+        val result = impl.isSessionConfigurationSupported(sessionConfiguration)
+        // Assert.
+        assertThat(result.source).isEqualTo(SOURCE_PLAY_SERVICES)
+        assertThat(result.supported).isEqualTo(SupportQueryResult.RESULT_UNSUPPORTED)
+        assertThat(result.timestampMillis).isEqualTo(0)
+    }
+}
diff --git a/camera/camera-feature-combination-query-play-services/src/main/AndroidManifest.xml b/camera/camera-feature-combination-query-play-services/src/main/AndroidManifest.xml
index a5918e6..9a67ab2 100644
--- a/camera/camera-feature-combination-query-play-services/src/main/AndroidManifest.xml
+++ b/camera/camera-feature-combination-query-play-services/src/main/AndroidManifest.xml
@@ -1,4 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <application>
+        <service
+            android:name="androidx.camera.featurecombinationquery.playservices.MetadataHolderService"
+            android:enabled="true"
+            android:exported="false"
+            tools:ignore="MissingServiceExportedEqualsTrue">
+            <meta-data
+                android:name="androidx.camera.featurecombinationquery.PLAY_SERVICES_IMPL_PROVIDER_KEY"
+                android:value="androidx.camera.featurecombinationquery.playservices.PlayServicesCameraDeviceSetupCompatProvider" />
+        </service>
+    </application>
 </manifest>
\ No newline at end of file
diff --git a/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/MetadataHolderService.java b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/MetadataHolderService.java
new file mode 100644
index 0000000..48e84db
--- /dev/null
+++ b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/MetadataHolderService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery.playservices;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A Service that holds metadata for the Play Services CameraDeviceSetup implementation.
+ */
+public class MetadataHolderService extends Service {
+
+    @Nullable
+    @Override
+    public IBinder onBind(@Nullable Intent intent) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompat.java b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompat.java
new file mode 100644
index 0000000..9e53d6a
--- /dev/null
+++ b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompat.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery.playservices;
+
+import static androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.RESULT_UNSUPPORTED;
+import static androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.SOURCE_PLAY_SERVICES;
+
+import android.hardware.camera2.params.SessionConfiguration;
+
+import androidx.annotation.NonNull;
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompat;
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompatFactory;
+
+/**
+ * A Google Play Services based {@link CameraDeviceSetupCompat} implementation.
+ *
+ * <p>This class is internal only and app cannot instantiate it directly. Instead app will
+ * depend on the camera-feature-combination-query-play-services artifact to get an instance of
+ * this class via the {@link CameraDeviceSetupCompatFactory#getCameraDeviceSetupCompat} API.
+ */
+public class PlayServicesCameraDeviceSetupCompat implements CameraDeviceSetupCompat {
+
+    public PlayServicesCameraDeviceSetupCompat(@NonNull String cameraId) {
+        // TODO: Implement this once Google Play Services CameraDeviceSetup is available.
+    }
+
+    @NonNull
+    @Override
+    public SupportQueryResult isSessionConfigurationSupported(
+            @NonNull SessionConfiguration sessionConfig) {
+        // TODO: Implement this once Google Play Services CameraDeviceSetup is available.
+        return new SupportQueryResult(RESULT_UNSUPPORTED, SOURCE_PLAY_SERVICES, 0);
+    }
+}
diff --git a/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompatProvider.java b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompatProvider.java
new file mode 100644
index 0000000..367ca54
--- /dev/null
+++ b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/PlayServicesCameraDeviceSetupCompatProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery.playservices;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompat;
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompatFactory;
+import androidx.camera.featurecombinationquery.CameraDeviceSetupCompatProvider;
+
+/**
+ * A Google Play Services based {@link CameraDeviceSetupCompat} implementation.
+ *
+ * <p>This class is internal only and app cannot instantiate it directly. Instead app will
+ * depend on the camera-feature-combination-query-play-services artifact to get an instance of
+ * this class via the {@link CameraDeviceSetupCompatFactory#getCameraDeviceSetupCompat} API.
+ */
+public class PlayServicesCameraDeviceSetupCompatProvider implements
+        CameraDeviceSetupCompatProvider {
+
+    public PlayServicesCameraDeviceSetupCompatProvider(@NonNull Context context) {
+        // TODO: Implement this once Google Play Services CameraDeviceSetup is available.
+    }
+
+    @NonNull
+    @Override
+    public CameraDeviceSetupCompat getCameraDeviceSetupCompat(@NonNull String cameraId) {
+        return new PlayServicesCameraDeviceSetupCompat(cameraId);
+    }
+}
diff --git a/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/package-info.java b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/package-info.java
new file mode 100644
index 0000000..4d36705
--- /dev/null
+++ b/camera/camera-feature-combination-query-play-services/src/main/java/androidx/camera/featurecombinationquery/playservices/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+package androidx.camera.featurecombinationquery.playservices;
+
+import androidx.annotation.RestrictTo;
diff --git a/camera/camera-feature-combination-query/api/1.4.0-beta03.txt b/camera/camera-feature-combination-query/api/1.4.0-beta03.txt
index e6f50d0..fd3b5ae 100644
--- a/camera/camera-feature-combination-query/api/1.4.0-beta03.txt
+++ b/camera/camera-feature-combination-query/api/1.4.0-beta03.txt
@@ -1 +1,30 @@
 // Signature format: 4.0
+package @androidx.camera.featurecombinationquery.ExperimentalFeatureCombinationQuery androidx.camera.featurecombinationquery {
+
+  public interface CameraDeviceSetupCompat {
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult isSessionConfigurationSupported(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  public static final class CameraDeviceSetupCompat.SupportQueryResult {
+    ctor public CameraDeviceSetupCompat.SupportQueryResult(int, int, long);
+    method public int getSource();
+    method public int getSupported();
+    method public long getTimestampMillis();
+    field public static final int RESULT_SUPPORTED = 1; // 0x1
+    field public static final int RESULT_UNDEFINED = 0; // 0x0
+    field public static final int RESULT_UNSUPPORTED = 2; // 0x2
+    field public static final int SOURCE_ANDROID_FRAMEWORK = 2; // 0x2
+    field public static final int SOURCE_PLAY_SERVICES = 1; // 0x1
+    field public static final int SOURCE_UNDEFINED = 0; // 0x0
+  }
+
+  public class CameraDeviceSetupCompatFactory {
+    ctor public CameraDeviceSetupCompatFactory(android.content.Context);
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat getCameraDeviceSetupCompat(String) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  @SuppressCompatibility @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @kotlin.RequiresOptIn public @interface ExperimentalFeatureCombinationQuery {
+  }
+
+}
+
diff --git a/camera/camera-feature-combination-query/api/current.txt b/camera/camera-feature-combination-query/api/current.txt
index e6f50d0..fd3b5ae 100644
--- a/camera/camera-feature-combination-query/api/current.txt
+++ b/camera/camera-feature-combination-query/api/current.txt
@@ -1 +1,30 @@
 // Signature format: 4.0
+package @androidx.camera.featurecombinationquery.ExperimentalFeatureCombinationQuery androidx.camera.featurecombinationquery {
+
+  public interface CameraDeviceSetupCompat {
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult isSessionConfigurationSupported(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  public static final class CameraDeviceSetupCompat.SupportQueryResult {
+    ctor public CameraDeviceSetupCompat.SupportQueryResult(int, int, long);
+    method public int getSource();
+    method public int getSupported();
+    method public long getTimestampMillis();
+    field public static final int RESULT_SUPPORTED = 1; // 0x1
+    field public static final int RESULT_UNDEFINED = 0; // 0x0
+    field public static final int RESULT_UNSUPPORTED = 2; // 0x2
+    field public static final int SOURCE_ANDROID_FRAMEWORK = 2; // 0x2
+    field public static final int SOURCE_PLAY_SERVICES = 1; // 0x1
+    field public static final int SOURCE_UNDEFINED = 0; // 0x0
+  }
+
+  public class CameraDeviceSetupCompatFactory {
+    ctor public CameraDeviceSetupCompatFactory(android.content.Context);
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat getCameraDeviceSetupCompat(String) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  @SuppressCompatibility @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @kotlin.RequiresOptIn public @interface ExperimentalFeatureCombinationQuery {
+  }
+
+}
+
diff --git a/camera/camera-feature-combination-query/api/restricted_1.4.0-beta03.txt b/camera/camera-feature-combination-query/api/restricted_1.4.0-beta03.txt
index e6f50d0..fd3b5ae 100644
--- a/camera/camera-feature-combination-query/api/restricted_1.4.0-beta03.txt
+++ b/camera/camera-feature-combination-query/api/restricted_1.4.0-beta03.txt
@@ -1 +1,30 @@
 // Signature format: 4.0
+package @androidx.camera.featurecombinationquery.ExperimentalFeatureCombinationQuery androidx.camera.featurecombinationquery {
+
+  public interface CameraDeviceSetupCompat {
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult isSessionConfigurationSupported(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  public static final class CameraDeviceSetupCompat.SupportQueryResult {
+    ctor public CameraDeviceSetupCompat.SupportQueryResult(int, int, long);
+    method public int getSource();
+    method public int getSupported();
+    method public long getTimestampMillis();
+    field public static final int RESULT_SUPPORTED = 1; // 0x1
+    field public static final int RESULT_UNDEFINED = 0; // 0x0
+    field public static final int RESULT_UNSUPPORTED = 2; // 0x2
+    field public static final int SOURCE_ANDROID_FRAMEWORK = 2; // 0x2
+    field public static final int SOURCE_PLAY_SERVICES = 1; // 0x1
+    field public static final int SOURCE_UNDEFINED = 0; // 0x0
+  }
+
+  public class CameraDeviceSetupCompatFactory {
+    ctor public CameraDeviceSetupCompatFactory(android.content.Context);
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat getCameraDeviceSetupCompat(String) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  @SuppressCompatibility @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @kotlin.RequiresOptIn public @interface ExperimentalFeatureCombinationQuery {
+  }
+
+}
+
diff --git a/camera/camera-feature-combination-query/api/restricted_current.txt b/camera/camera-feature-combination-query/api/restricted_current.txt
index e6f50d0..fd3b5ae 100644
--- a/camera/camera-feature-combination-query/api/restricted_current.txt
+++ b/camera/camera-feature-combination-query/api/restricted_current.txt
@@ -1 +1,30 @@
 // Signature format: 4.0
+package @androidx.camera.featurecombinationquery.ExperimentalFeatureCombinationQuery androidx.camera.featurecombinationquery {
+
+  public interface CameraDeviceSetupCompat {
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult isSessionConfigurationSupported(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  public static final class CameraDeviceSetupCompat.SupportQueryResult {
+    ctor public CameraDeviceSetupCompat.SupportQueryResult(int, int, long);
+    method public int getSource();
+    method public int getSupported();
+    method public long getTimestampMillis();
+    field public static final int RESULT_SUPPORTED = 1; // 0x1
+    field public static final int RESULT_UNDEFINED = 0; // 0x0
+    field public static final int RESULT_UNSUPPORTED = 2; // 0x2
+    field public static final int SOURCE_ANDROID_FRAMEWORK = 2; // 0x2
+    field public static final int SOURCE_PLAY_SERVICES = 1; // 0x1
+    field public static final int SOURCE_UNDEFINED = 0; // 0x0
+  }
+
+  public class CameraDeviceSetupCompatFactory {
+    ctor public CameraDeviceSetupCompatFactory(android.content.Context);
+    method public androidx.camera.featurecombinationquery.CameraDeviceSetupCompat getCameraDeviceSetupCompat(String) throws android.hardware.camera2.CameraAccessException;
+  }
+
+  @SuppressCompatibility @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @kotlin.RequiresOptIn public @interface ExperimentalFeatureCombinationQuery {
+  }
+
+}
+
diff --git a/camera/camera-feature-combination-query/build.gradle b/camera/camera-feature-combination-query/build.gradle
index 12d1d72..8e2b743 100644
--- a/camera/camera-feature-combination-query/build.gradle
+++ b/camera/camera-feature-combination-query/build.gradle
@@ -24,6 +24,7 @@
 
 dependencies {
     api(libs.androidx.annotation)
+    implementation(libs.autoValueAnnotations)
 
     testImplementation(libs.testRunner)
     testImplementation(libs.robolectric)
@@ -31,6 +32,8 @@
     testImplementation(libs.truth)
     testImplementation(libs.testRules)
     testImplementation(libs.testCore)
+
+    annotationProcessor(libs.autoValue)
 }
 
 android {
@@ -38,6 +41,7 @@
         enable 'CameraXQuirksClassDetector'
     }
 
+    compileSdk 35
     testOptions.unitTests.includeAndroidResources = true
     namespace "androidx.camera.featurecombinationquery"
 }
@@ -49,6 +53,5 @@
     runApiTasks = new RunApiTasks.Yes()
     description = "Camera feature combination components for the Jetpack Camera Library, a library " +
             "providing a seamless experience for querying camera features across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
\ No newline at end of file
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/AggregatedCameraDeviceSetupCompat.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/AggregatedCameraDeviceSetupCompat.java
new file mode 100644
index 0000000..6fc6e84
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/AggregatedCameraDeviceSetupCompat.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery;
+
+import static androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.RESULT_UNDEFINED;
+import static androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.SOURCE_UNDEFINED;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.params.SessionConfiguration;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+/**
+ * A {@link CameraDeviceSetupCompat} implementation that combines multiple
+ * {@link CameraDeviceSetupCompat}.
+ *
+ * <p>This class checks if a {@link SessionConfiguration} is supported in the order of the
+ * provided implementation list, and returns the first non-undefined result. If all results are
+ * undefined, it will return a undefined result.
+ */
+final class AggregatedCameraDeviceSetupCompat implements CameraDeviceSetupCompat {
+
+    private final List<CameraDeviceSetupCompat> mCameraDeviceSetupImpls;
+
+    AggregatedCameraDeviceSetupCompat(List<CameraDeviceSetupCompat> cameraDeviceSetupImpls) {
+        mCameraDeviceSetupImpls = cameraDeviceSetupImpls;
+    }
+
+    @NonNull
+    @Override
+    public SupportQueryResult isSessionConfigurationSupported(
+            @NonNull SessionConfiguration sessionConfig)
+            throws CameraAccessException {
+        for (CameraDeviceSetupCompat impl : mCameraDeviceSetupImpls) {
+            SupportQueryResult result = impl.isSessionConfigurationSupported(sessionConfig);
+            if (result.getSupported() != RESULT_UNDEFINED) {
+                return result;
+            }
+        }
+        return new SupportQueryResult(RESULT_UNDEFINED, SOURCE_UNDEFINED, 0);
+    }
+}
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/Camera2CameraDeviceSetupCompat.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/Camera2CameraDeviceSetupCompat.java
new file mode 100644
index 0000000..dc4fce8
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/Camera2CameraDeviceSetupCompat.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery;
+
+import static androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.RESULT_SUPPORTED;
+import static androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.RESULT_UNSUPPORTED;
+import static androidx.camera.featurecombinationquery.CameraDeviceSetupCompat.SupportQueryResult.SOURCE_ANDROID_FRAMEWORK;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.params.SessionConfiguration;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+/**
+ * A Android framework based {@link CameraDeviceSetupCompat} implementation.
+ */
+@RequiresApi(api = 35)
+class Camera2CameraDeviceSetupCompat implements CameraDeviceSetupCompat {
+
+    private final CameraDevice.CameraDeviceSetup mCameraDeviceSetup;
+
+    Camera2CameraDeviceSetupCompat(@NonNull CameraManager cameraManager, @NonNull String cameraId)
+            throws CameraAccessException {
+        mCameraDeviceSetup = cameraManager.getCameraDeviceSetup(cameraId);
+    }
+
+    @NonNull
+    @Override
+    public SupportQueryResult isSessionConfigurationSupported(
+            @NonNull SessionConfiguration sessionConfig)
+            throws CameraAccessException {
+        return new SupportQueryResult(
+                mCameraDeviceSetup.isSessionConfigurationSupported(sessionConfig) ? RESULT_SUPPORTED
+                        : RESULT_UNSUPPORTED,
+                SOURCE_ANDROID_FRAMEWORK,
+                getBuildTimeEpochMillis());
+    }
+
+    public static long getBuildTimeEpochMillis() {
+        String value = System.getProperty("ro.build.date.utc");
+        if (value != null) {
+            try {
+                return Long.parseLong(value) * 1000;
+            } catch (NumberFormatException e) {
+                // Fall through
+            }
+        }
+        return 0;
+    }
+}
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/Camera2CameraDeviceSetupCompatProvider.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/Camera2CameraDeviceSetupCompatProvider.java
new file mode 100644
index 0000000..aa8eceb
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/Camera2CameraDeviceSetupCompatProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery;
+
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+/**
+ * A Android framework based {@link CameraDeviceSetupCompat} implementation.
+ */
+@RequiresApi(api = 35)
+class Camera2CameraDeviceSetupCompatProvider implements CameraDeviceSetupCompatProvider {
+
+    private final CameraManager mCameraManager;
+
+    Camera2CameraDeviceSetupCompatProvider(@NonNull Context context) {
+        mCameraManager = context.getSystemService(CameraManager.class);
+    }
+
+    @NonNull
+    @Override
+    public CameraDeviceSetupCompat getCameraDeviceSetupCompat(@NonNull String cameraId)
+            throws CameraAccessException {
+        return new Camera2CameraDeviceSetupCompat(mCameraManager, cameraId);
+    }
+}
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompat.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompat.java
new file mode 100644
index 0000000..e856e7e
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompat.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.params.SessionConfiguration;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for checking if a {@link SessionConfiguration} is supported by the device.
+ *
+ * <p>This interface is a compatible version of the {@link CameraDevice.CameraDeviceSetup}
+ * class.
+ *
+ * <p>Implementations of this interface must be able to check if a {@link SessionConfiguration}
+ * is supported. They will check both the output streams and the session parameters, then return
+ * whether the combination works for the given camera. For example, a camera device may support
+ * HDR and 60FPS frame rate, but not both at the same time.
+ *
+ * @see CameraDevice.CameraDeviceSetup
+ */
+public interface CameraDeviceSetupCompat {
+
+    /**
+     * Checks if the {@link SessionConfiguration} is supported.
+     *
+     * @param sessionConfig The {@link SessionConfiguration} to check.
+     * @return a {@link SupportQueryResult} indicating if the {@link SessionConfiguration} is
+     * supported.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error.
+     * @see CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported
+     */
+    @NonNull
+    SupportQueryResult isSessionConfigurationSupported(@NonNull SessionConfiguration sessionConfig)
+            throws CameraAccessException;
+
+    /**
+     * Result of a {@link CameraDeviceSetupCompat#isSessionConfigurationSupported} query.
+     */
+    final class SupportQueryResult {
+
+        /**
+         * Source of the result is undefined. This is always accompanied by
+         * {@link #RESULT_UNDEFINED}.
+         */
+        public static final int SOURCE_UNDEFINED = 0;
+        /**
+         * Source of the result is Google Play Services.
+         */
+        public static final int SOURCE_PLAY_SERVICES = 1;
+        /**
+         * Source of the result is Android framework.
+         */
+        public static final int SOURCE_ANDROID_FRAMEWORK = 2;
+
+        /**
+         * The library cannot determine if the {@link SessionConfiguration} is supported.
+         *
+         * <p>For API levels 29 to 34 inclusive, the app may continue to call
+         * {@link CameraDevice#isSessionConfigurationSupported} to check if the session
+         * configuration is supported.
+         *
+         * @see CameraDeviceSetupCompatFactory#getCameraDeviceSetupCompat for sample code.
+         */
+        public static final int RESULT_UNDEFINED = 0;
+        /**
+         * The {@link SessionConfiguration} is supported by the camera.
+         */
+        public static final int RESULT_SUPPORTED = 1;
+        /**
+         * The {@link SessionConfiguration} is not supported by the camera.
+         */
+        public static final int RESULT_UNSUPPORTED = 2;
+
+        /**
+         * Options for where the result is coming from.
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @IntDef(value = {RESULT_UNDEFINED, RESULT_SUPPORTED, RESULT_UNSUPPORTED})
+        public @interface Supported {
+        }
+
+        /**
+         * Options for where the result is coming from.
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @IntDef(value = {SOURCE_UNDEFINED, SOURCE_PLAY_SERVICES, SOURCE_ANDROID_FRAMEWORK})
+        @interface Sources {
+        }
+
+        // Whether the configuration is supported.
+        @Supported
+        private final int mSupported;
+        // The source of the result.
+        @Sources
+        private final int mSource;
+        // The timestamp of when the result was updated.
+        private final long mTimestampMillis;
+
+        /**
+         * Creates a new instance of {@link SupportQueryResult}.
+         *
+         * @param supported       Whether the {@link SessionConfiguration} is supported.
+         * @param source          The source of the result.
+         * @param timestampMillis The epoch timestamp of when the result was updated.
+         */
+        public SupportQueryResult(int supported, int source, long timestampMillis) {
+            mSupported = supported;
+            mSource = source;
+            mTimestampMillis = timestampMillis;
+        }
+
+        /**
+         * Whether the {@link SessionConfiguration} is supported.
+         *
+         * <p> If the value is {@link #RESULT_SUPPORTED}, the configuration is
+         * supported by the camera; if the value is {@link #RESULT_UNSUPPORTED}, the
+         * configuration is not supported by the camera; if the value is
+         * {@link #RESULT_UNDEFINED}, then the library cannot determine if the configuration is
+         * supported or not.
+         */
+        @Supported
+        public int getSupported() {
+            return mSupported;
+        }
+
+        /**
+         * Returns the source of the result.
+         *
+         * <p> If the source of the information is Play Services, the value is
+         * {@link #SOURCE_PLAY_SERVICES}; if the source is Android framework, the value is
+         * {@link #SOURCE_ANDROID_FRAMEWORK}; otherwise, the value is {@link #SOURCE_UNDEFINED}.
+         */
+        @Sources
+        public int getSource() {
+            return mSource;
+        }
+
+        /**
+         * Returns the epoch timestamp of when the result was updated.
+         *
+         * <p> If the source is {@link #SOURCE_PLAY_SERVICES}, the value is the time when the
+         * data is updated on the Play Services server; if the source is
+         * {@link #SOURCE_ANDROID_FRAMEWORK}, the value is the build property "ro.build.date.utc"
+         * if available; otherwise, it will return 0.
+         */
+        public long getTimestampMillis() {
+            return mTimestampMillis;
+        }
+    }
+
+}
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompatFactory.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompatFactory.java
new file mode 100644
index 0000000..34fb0d0
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompatFactory.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.params.SessionConfiguration;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Factory for creating {@link CameraDeviceSetupCompat} instances.
+ */
+public class CameraDeviceSetupCompatFactory {
+
+    private static final String PLAY_SERVICES_IMPL_KEY =
+            "androidx.camera.featurecombinationquery.PLAY_SERVICES_IMPL_PROVIDER_KEY";
+
+    private final Context mContext;
+
+    // Cached provider for Play Services implementation.
+    @Nullable
+    private CameraDeviceSetupCompatProvider mPlayServicesProvider;
+    // Cached provider for Camera2 implementation.
+    @Nullable
+    private CameraDeviceSetupCompatProvider mCamera2Provider;
+
+    /**
+     * Creates a new instance of {@link CameraDeviceSetupCompatFactory}.
+     *
+     * @param context The context to use for creating {@link CameraDeviceSetupCompat} instances.
+     */
+    public CameraDeviceSetupCompatFactory(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Gets a new instance of {@link CameraDeviceSetupCompat} for the given camera ID.
+     *
+     * <p> The returned instance aggregates the results from both the Play Services and the
+     * Android framework. It first checks if a Play Services implementation exists, and if so,
+     * return the query result from the Play Services implementation. If no Play Services
+     * implementation exists or the result from Play Services is undefined, the returned instance
+     * will then query Android framework for the result, if running on a new enough version.
+     * Sample code:
+     *
+     * <pre><code>
+     * // Query the compatibility before opening the camera.
+     * CameraDeviceSetupCompatFactory factory = new CameraDeviceSetupCompatFactory(context);
+     * CameraDeviceSetupCompat cameraDeviceSetupCompat
+     *     = factory.getCameraDeviceSetupCompat(cameraId);
+     * Result result
+     *     = cameraDeviceSetupCompat.isSessionConfigurationSupported(sessionConfiguration);
+     * boolean supported = result.getValue() == CameraDeviceSetupCompat.RESULT_SUPPORTED;
+     * </code></pre>
+     *
+     * <p> To include the Play Services as a source, the app must depend on the
+     * camera-feature-combination-query-play-services artifact.
+     *
+     * <p> If the return value is
+     * {@link CameraDeviceSetupCompat.SupportQueryResult#RESULT_UNDEFINED}, on API level between
+     * 29 and 34 inclusive, it's also possible to further check if the session
+     * configuration is supported by querying the
+     * {@link CameraDevice#isSessionConfigurationSupported}. This approach requires
+     * opening the camera first which may introduce latency, so the AndroidX implementation does not
+     * include this code path by default. Additionally, this approach does not check the values
+     * set in {@link SessionConfiguration#setSessionParameters}. For example, the FPS range is
+     * ignored. Sample code:
+     *
+     * <pre><code>
+     * if (SDK_INT <= 34 && SDK_INT >= 29) {
+     *   // Check if the session configuration is supported with an opened CameraDevice.
+     *   try {
+     *     supported = supported || (result.getValue() == RESULT_UNDEFINED &&
+     *         cameraDevice.isSessionConfigurationSupported(sessionConfiguration));
+     *   } catch (UnsupportedOperationException unsupportedException) {
+     *     // CameraDevice may throw UnsupportedOperationException when the config is not supported.
+     *   }
+     * }
+     * </code></pre>
+     *
+     * @throws IllegalStateException    If the Play Services implementation exists but the library
+     *                                  fails to instantiate it. For example, if there are multiple
+     *                                  Play Services implementations in the manifest.
+     * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not
+     *                                  match any device in {@link CameraManager#getCameraIdList()}.
+     * @throws CameraAccessException    If the camera has encountered a fatal error.
+     * @see CameraDevice#isSessionConfigurationSupported
+     * @see CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported
+     */
+    @NonNull
+    public CameraDeviceSetupCompat getCameraDeviceSetupCompat(@NonNull String cameraId)
+            throws CameraAccessException {
+        List<CameraDeviceSetupCompat> impls = new ArrayList<>();
+        if (mPlayServicesProvider == null) {
+            // Create Play Services implementation if there isn't a cached one.
+            mPlayServicesProvider = getPlayServicesCameraDeviceSetupCompatProvider();
+        }
+        if (mPlayServicesProvider != null) {
+            // Add the Play Services implementation if the app contains that dependency.
+            impls.add(mPlayServicesProvider.getCameraDeviceSetupCompat(cameraId));
+        }
+        if (SDK_INT >= 35) {
+            try {
+                if (mCamera2Provider == null) {
+                    // Create the camera2 implementation if there isn't a cached one.
+                    mCamera2Provider = new Camera2CameraDeviceSetupCompatProvider(mContext);
+                }
+                impls.add(mCamera2Provider.getCameraDeviceSetupCompat(cameraId));
+            } catch (UnsupportedOperationException e) {
+                // This can throw UnsupportedOperationException for Android V upgrade devices. In
+                // that case, we treat it as SDK_INT < 35 and ignore.
+            }
+        }
+        return new AggregatedCameraDeviceSetupCompat(impls);
+    }
+
+    /**
+     * Returns a new instance of {@link CameraDeviceSetupCompatProvider}.
+     *
+     * @return The Play Services CameraDeviceSetupCompat implementation, or null if not found.
+     * @throws IllegalStateException if multiple Play Services CameraDeviceSetupCompat
+     *                               implementations are found, or failed to instantiate the
+     *                               implementation.
+     */
+    @Nullable
+    private CameraDeviceSetupCompatProvider getPlayServicesCameraDeviceSetupCompatProvider()
+            throws IllegalStateException {
+        PackageInfo packageInfo;
+        try {
+            packageInfo = mContext.getPackageManager().getPackageInfo(
+                    mContext.getPackageName(),
+                    PackageManager.GET_META_DATA | PackageManager.GET_SERVICES);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+
+        String playServiceImplClassName = null;
+        if (packageInfo.services == null) {
+            return null;
+        }
+        for (ServiceInfo serviceInfo : packageInfo.services) {
+            if (serviceInfo.metaData == null) {
+                continue;
+            }
+            // Try to load the play services impl class name from the Service metadata
+            // in camera-feature-combination-query-play-services manifest.
+            String className = serviceInfo.metaData.getString(PLAY_SERVICES_IMPL_KEY);
+            if (className != null) {
+                if (playServiceImplClassName != null) {
+                    throw new IllegalStateException(
+                            "Multiple Play Services CameraDeviceSetupCompat implementations"
+                                    + " found in the manifest.");
+                }
+                playServiceImplClassName = className;
+            }
+        }
+        if (playServiceImplClassName == null) {
+            // The app does not depend on a Play Services implementation.
+            return null;
+        }
+        return instantiatePlayServicesImplProvider(playServiceImplClassName);
+    }
+
+    /**
+     * Instantiates a Play Services CameraDeviceSetupCompat provider implementation.
+     *
+     * @param className The class name of the implementation.
+     * @return The instantiated implementation.
+     */
+    private CameraDeviceSetupCompatProvider instantiatePlayServicesImplProvider(
+            @NonNull String className) {
+        try {
+            Class<?> clazz = Class.forName(className);
+            return (CameraDeviceSetupCompatProvider) clazz.getConstructor(Context.class)
+                    .newInstance(mContext);
+        } catch (Exception e) {
+            throw new IllegalStateException(
+                    "Failed to instantiate Play Services CameraDeviceSetupCompat implementation",
+                    e);
+        }
+    }
+}
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompatProvider.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompatProvider.java
new file mode 100644
index 0000000..b61d909
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/CameraDeviceSetupCompatProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery;
+
+import android.hardware.camera2.CameraAccessException;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+/**
+ * Interface for providing a {@link CameraDeviceSetupCompat} for a camera device.
+ *
+ * <p> Getting a provider usually needs Binder calls which is costly. This interface allows the
+ * to be cached and reused.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public interface CameraDeviceSetupCompatProvider {
+
+    /**
+     * Get a {@link CameraDeviceSetupCompat} for the camera device with the given cameraId.
+     *
+     * @param cameraId the cameraId of the camera device.
+     * @return a {@link CameraDeviceSetupCompat} for the camera device with the given cameraId.
+     */
+    @NonNull
+    CameraDeviceSetupCompat getCameraDeviceSetupCompat(@NonNull String cameraId)
+            throws CameraAccessException;
+}
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/ExperimentalFeatureCombinationQuery.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/ExperimentalFeatureCombinationQuery.java
new file mode 100644
index 0000000..cf686ce
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/ExperimentalFeatureCombinationQuery.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.featurecombinationquery;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import kotlin.RequiresOptIn;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Indicates that the annotated API uses the experimental feature combination query API.
+ *
+ * <p> This feature will move to official API in 1.5.0.
+ */
+@Retention(CLASS)
+@RequiresOptIn
+public @interface ExperimentalFeatureCombinationQuery {
+}
diff --git a/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/package-info.java b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/package-info.java
new file mode 100644
index 0000000..fdf1063
--- /dev/null
+++ b/camera/camera-feature-combination-query/src/main/java/androidx/camera/featurecombinationquery/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@ExperimentalFeatureCombinationQuery
+package androidx.camera.featurecombinationquery;
+
diff --git a/camera/camera-lifecycle/build.gradle b/camera/camera-lifecycle/build.gradle
index 6de8011..c6aea3a 100644
--- a/camera/camera-lifecycle/build.gradle
+++ b/camera/camera-lifecycle/build.gradle
@@ -75,7 +75,6 @@
     description = "Lifecycle components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":camera:camera-lifecycle:camera-lifecycle-samples"))
 }
diff --git a/camera/camera-lifecycle/samples/build.gradle b/camera/camera-lifecycle/samples/build.gradle
index 1410588..5885e79 100644
--- a/camera/camera-lifecycle/samples/build.gradle
+++ b/camera/camera-lifecycle/samples/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(libs.kotlinStdlib)
     implementation(project(":camera:camera-camera2"))
     implementation(project(":camera:camera-lifecycle"))
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
index d579d3c..bc97c35 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
@@ -21,6 +21,7 @@
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.Camera;
@@ -269,6 +270,11 @@
         return mCameraUseCaseAdapter.getCameraInfo();
     }
 
+    @Nullable
+    CameraInfo getSecondaryCameraInfo() {
+        return mCameraUseCaseAdapter.getSecondaryCameraInfo();
+    }
+
     @NonNull
     @Override
     public CameraConfig getExtendedConfig() {
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
index c1d50f1..7b6e1e2 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
@@ -175,7 +175,8 @@
             LifecycleOwner lifecycleOwner = lifecycleCamera.getLifecycleOwner();
             Key key = Key.create(lifecycleOwner,
                     CameraUseCaseAdapter.generateCameraId(
-                            (RestrictedCameraInfo) lifecycleCamera.getCameraInfo(), null));
+                            (RestrictedCameraInfo) lifecycleCamera.getCameraInfo(),
+                            (RestrictedCameraInfo) lifecycleCamera.getSecondaryCameraInfo()));
 
             LifecycleCameraRepositoryObserver observer =
                     getLifecycleCameraRepositoryObserver(lifecycleOwner);
diff --git a/camera/camera-mlkit-vision/build.gradle b/camera/camera-mlkit-vision/build.gradle
index 0854ab8..770f44e 100644
--- a/camera/camera-mlkit-vision/build.gradle
+++ b/camera/camera-mlkit-vision/build.gradle
@@ -24,7 +24,7 @@
 dependencies {
     api(project(":camera:camera-core"))
     api(project(":camera:camera-view"))
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api('com.google.mlkit:vision-interfaces:16.0.0') {
         version {
             prefer '16.0.0'
@@ -59,6 +59,5 @@
     description = "MLKit vision components for the Jetpack Camera Library, a library providing a " +
             "seamless integration that enables camera driven computer vision features " +
             "across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 03c370b..358d69f 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -40,7 +40,7 @@
     implementation("androidx.test:runner:$testRunnerVersion")
 
     implementation(libs.testUiautomator)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(libs.guavaListenableFuture)
     implementation("androidx.appcompat:appcompat:1.1.0")
     api(project(":camera:camera-core"))
diff --git a/camera/camera-testing/src/main/AndroidManifest.xml b/camera/camera-testing/src/main/AndroidManifest.xml
index a8e142c..0ad8afa 100644
--- a/camera/camera-testing/src/main/AndroidManifest.xml
+++ b/camera/camera-testing/src/main/AndroidManifest.xml
@@ -26,6 +26,11 @@
             android:exported="true"
             android:label="ForegroundTestActivity" />
         <activity
+            android:name="androidx.camera.testing.impl.activity.RequestResultTestActivity"
+            android:configChanges="orientation|screenSize"
+            android:exported="true"
+            android:label="RequestResultTestActivity" />
+        <activity
             android:name="androidx.camera.testing.impl.activity.Camera2TestActivity"
             android:exported="true"
             android:label="Camera2 TestActivity" />
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java
index c7302a3..dd5fe93 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraUtil.java
@@ -42,7 +42,6 @@
 import android.util.Log;
 import android.view.Surface;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -182,7 +181,6 @@
 
     @RequiresApi(28)
     private static class Api28Impl {
-        @DoNotInline
         static Set<String> getPhysicalCameraId(CameraCharacteristics cameraCharacteristics) {
             return cameraCharacteristics.getPhysicalCameraIds();
         }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java
index 61b08a5..91d37ce 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/TestImageUtil.java
@@ -33,7 +33,6 @@
 import android.graphics.Rect;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
@@ -334,12 +333,10 @@
 
     @RequiresApi(34)
     private static class Api34Impl {
-        @DoNotInline
         static Gainmap createGainmap(@NonNull Bitmap bitmap) {
             return new Gainmap(bitmap);
         }
 
-        @DoNotInline
         static void setGainmap(@NonNull Bitmap bitmap, @NonNull Gainmap gainmap) {
             bitmap.setGainmap(gainmap);
         }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
index 81bc7e5..9e7c9ef 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
@@ -21,7 +21,6 @@
 import android.content.Intent
 import android.os.Build
 import android.view.WindowManager
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.camera.core.Logger
 import androidx.camera.testing.impl.Api27Impl.setShowWhenLocked
@@ -103,12 +102,10 @@
 
 @RequiresApi(Build.VERSION_CODES.O_MR1)
 object Api27Impl {
-    @DoNotInline
     fun Activity.setShowWhenLocked() {
         setShowWhenLocked(true)
     }
 
-    @DoNotInline
     fun Activity.setTurnScreenOn() {
         setTurnScreenOn(true)
     }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/activity/RequestResultTestActivity.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/activity/RequestResultTestActivity.java
new file mode 100644
index 0000000..89d8bec
--- /dev/null
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/activity/RequestResultTestActivity.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.testing.impl.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.espresso.idling.CountingIdlingResource;
+
+/**
+ * A intermediate activity to launch another activity and wait for result.
+ */
+public class RequestResultTestActivity extends Activity {
+
+    /** Used to pass the action intent of the target activity. */
+    public static final String INTENT_EXTRA_INTENT_ACTION = "intent_action";
+    /** Used to pass the extra bundle info to launch the target activity. */
+    public static final String INTENT_EXTRA_BUNDLE = "intent_extra_bundle";
+    private static final String INTENT_EXTRA_RESULT_ERROR_MESSAGE = "result_error_message";
+    private static final int REQUEST_CODE_DEFAULT = 0;
+    private final CountingIdlingResource mRequestResultReady = new CountingIdlingResource(
+            "RequestResultReady");
+
+    private int mResultErrorCode = 0;
+    @Nullable
+    private String mResultErrorMessage = null;
+
+    /**
+     * Retrieves the CountingIdlingResource to know whether the result has been returned or not.
+     */
+    @NonNull
+    public CountingIdlingResource getRequestResultReadyIdlingResource() {
+        return mRequestResultReady;
+    }
+
+    /**
+     * Retrieves the result error code.
+     */
+    public int getResultErrorCode() {
+        return mResultErrorCode;
+    }
+
+    /**
+     * Retrieves the result error message.
+     */
+    @Nullable
+    public String getResultErrorMessage() {
+        return mResultErrorMessage;
+    }
+
+    @SuppressWarnings("deprecation") // Bundle.get
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mRequestResultReady.increment();
+        String intentAction = getIntent().getStringExtra(INTENT_EXTRA_INTENT_ACTION);
+        Intent intent = new Intent(intentAction);
+        Bundle bundle = getIntent().getBundleExtra(INTENT_EXTRA_BUNDLE);
+        // Uses Bundle to bring the extra settings to the target activity. instanceof is used to
+        // correctly cast the extra values. Needs to add new types and verify the results when new
+        // types are needed here.
+        if (bundle != null) {
+            for (String key : bundle.keySet()) {
+                Object value = bundle.get(key);
+                if (value instanceof String) {
+                    intent.putExtra(key, (String) value);
+                } else if (value instanceof Boolean) {
+                    intent.putExtra(key, (Boolean) value);
+                } else if (value instanceof Integer) {
+                    intent.putExtra(key, (Integer) value);
+                }
+            }
+        }
+        startActivityForResult(intent, REQUEST_CODE_DEFAULT);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        mResultErrorCode = resultCode;
+        mResultErrorMessage = data.getStringExtra(INTENT_EXTRA_RESULT_ERROR_MESSAGE);
+        mRequestResultReady.decrement();
+    }
+}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/compat/LooperCompat.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/compat/LooperCompat.java
index 36fe971..869612a 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/compat/LooperCompat.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/compat/LooperCompat.java
@@ -20,7 +20,6 @@
 import android.os.Looper;
 import android.os.MessageQueue;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -60,7 +59,6 @@
         private Api23Impl() {
         }
 
-        @DoNotInline
         @NonNull
         static MessageQueue getQueue(@NonNull Looper looper) {
             return looper.getQueue();
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java
index 581eb24..f1e25e3 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java
@@ -23,6 +23,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.camera.testing.impl.mocks.helpers.ArgumentCaptor;
+import androidx.camera.testing.impl.mocks.helpers.ArgumentMatcher;
 import androidx.camera.testing.impl.mocks.helpers.CallTimes;
 import androidx.camera.testing.impl.mocks.helpers.CallTimesAtLeast;
 import androidx.core.util.Consumer;
@@ -55,9 +56,9 @@
     private final Map<Integer, Boolean> mIsEventVerifiedByIndex = new HashMap<>();
     private int mIndexLastVerifiedInOrder = -1;
 
-    private Class<?> mClassTypeToVerify;
     private CallTimes mCallTimes;
     private boolean mInOrder = false;
+    private ArgumentMatcher<T> mMatcher;
 
     private int getMatchingEventCount() {
         return getMatchingEventCount(mVerifyingEventList);
@@ -67,7 +68,7 @@
         int count = 0;
         int startIndex = mInOrder ? mIndexLastVerifiedInOrder + 1 : 0;
         for (int i = startIndex; i < eventList.size(); i++) {
-            if (mClassTypeToVerify.isInstance(eventList.get(i))) {
+            if (mMatcher.matches(eventList.get(i))) {
                 count++;
             }
         }
@@ -77,7 +78,7 @@
     private int getLastVerifiedEventInOrder() {
         int count = 0;
         for (int i = mIndexLastVerifiedInOrder + 1; i < mVerifyingEventList.size(); i++) {
-            if (mClassTypeToVerify.isInstance(mVerifyingEventList.get(i))) {
+            if (mMatcher.matches(mVerifyingEventList.get(i))) {
                 count++;
             }
 
@@ -95,7 +96,7 @@
         }
 
         for (int i = 0; i < mVerifyingEventList.size(); i++) {
-            if (mClassTypeToVerify.isInstance(mVerifyingEventList.get(i))) {
+            if (mMatcher.matches(mVerifyingEventList.get(i))) {
                 mIsEventVerifiedByIndex.put(i, true);
             }
         }
@@ -151,7 +152,7 @@
      */
     public void verifyAcceptCall(@NonNull Class<?> classType, boolean inOrder,
             @IntRange(from = 0) long timeoutInMillis, @NonNull CallTimes callTimes) {
-        verifyAcceptCall(classType, inOrder, timeoutInMillis, callTimes, null);
+        verifyAcceptCall(classType, inOrder, timeoutInMillis, callTimes, (ArgumentCaptor<T>) null);
     }
 
     /**
@@ -173,14 +174,37 @@
     public void verifyAcceptCall(@NonNull Class<?> classType, boolean inOrder,
             @IntRange(from = 0) long timeoutInMillis, @NonNull CallTimes callTimes,
             @Nullable ArgumentCaptor<T> captor) {
+        verifyAcceptCallInternal(classType, null, inOrder, timeoutInMillis, callTimes, captor);
+    }
+
+    /** Verifies if {@link #accept} method was invoked properly during test. */
+    public void verifyAcceptCall(@NonNull Class<?> classType, boolean inOrder,
+            @IntRange(from = 0) long timeoutInMillis, @NonNull CallTimes callTimes,
+            @NonNull ArgumentMatcher<T> matcher) {
+        verifyAcceptCall(classType, inOrder, timeoutInMillis, callTimes, null, matcher);
+    }
+
+    /** Verifies if {@link #accept} method was invoked properly during test. */
+    public void verifyAcceptCall(@NonNull Class<?> classType, boolean inOrder,
+            @IntRange(from = 0) long timeoutInMillis, @NonNull CallTimes callTimes,
+            @Nullable ArgumentCaptor<T> captor, @NonNull ArgumentMatcher<T> matcher
+    ) {
+        verifyAcceptCallInternal(classType, matcher, inOrder, timeoutInMillis, callTimes, captor);
+    }
+
+    private void verifyAcceptCallInternal(@NonNull Class<?> classType,
+            @Nullable ArgumentMatcher<T> matcher, boolean inOrder,
+            @IntRange(from = 0) long timeoutInMillis, @NonNull CallTimes callTimes,
+            @Nullable ArgumentCaptor<T> captor) {
         Preconditions.checkNotNull(classType, "The class type can not be null.");
         Preconditions.checkState(timeoutInMillis >= 0,
                 "Timeout can not be negative: " + timeoutInMillis);
         Preconditions.checkNotNull(callTimes, "The call times criteria can not be null.");
 
-        mClassTypeToVerify = classType;
         mCallTimes = callTimes;
         mInOrder = inOrder;
+        mMatcher = argument -> classType.isInstance(argument)
+                && (matcher == null || matcher.matches(argument));
 
         CountDownLatch latch = null;
         boolean isVerified;
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt
index 824a02cb8..d3dd4cf 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt
@@ -25,6 +25,7 @@
 import androidx.camera.testing.impl.hasVideo
 import androidx.camera.testing.impl.mocks.MockConsumer
 import androidx.camera.testing.impl.mocks.helpers.ArgumentCaptor
+import androidx.camera.testing.impl.mocks.helpers.ArgumentMatcher
 import androidx.camera.testing.impl.mocks.helpers.CallTimes
 import androidx.camera.testing.impl.mocks.helpers.CallTimesAtLeast
 import androidx.camera.testing.impl.useAndRelease
@@ -226,8 +227,8 @@
         try {
             val expectedAudioState =
                 if (muted) AudioStats.AUDIO_STATE_MUTED else AudioStats.AUDIO_STATE_ACTIVE
-            val captor =
-                ArgumentCaptor<VideoRecordEvent> {
+            val matcher =
+                ArgumentMatcher<VideoRecordEvent> {
                     it.recordingStats.audioStats.audioState == expectedAudioState
                 }
             listener.verifyAcceptCall(
@@ -235,7 +236,7 @@
                 /*inOrder=*/ true,
                 defaultVerifyStatusTimeoutMs,
                 CallTimesAtLeast(1),
-                captor
+                matcher
             )
         } catch (t: Throwable) {
             throw AssertionError("Failed on #verifyMute", t)
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockConsumerTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockConsumerTest.java
index 29026f7..2e2b263 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockConsumerTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/mocks/MockConsumerTest.java
@@ -23,6 +23,7 @@
 
 import androidx.camera.testing.impl.mocks.MockConsumer;
 import androidx.camera.testing.impl.mocks.helpers.ArgumentCaptor;
+import androidx.camera.testing.impl.mocks.helpers.ArgumentMatcher;
 import androidx.camera.testing.impl.mocks.helpers.CallTimes;
 import androidx.camera.testing.impl.mocks.helpers.CallTimesAtLeast;
 
@@ -96,6 +97,42 @@
     }
 
     @Test
+    public void verifiedWithArgumentMatcher() {
+        // Arrange.
+        Integer[] integers = {0, 1, 0, 2, 0};
+        MockConsumer<Integer> mockConsumer = new MockConsumer<>();
+        ArgumentMatcher<Integer> matcher = argument -> argument > 0;
+
+        // Act.
+        for (int b : integers) {
+            mockConsumer.accept(b);
+        }
+
+        // Verify.
+        mockConsumer.verifyAcceptCall(Integer.class, false, 0, new CallTimes(2), matcher);
+    }
+
+    @Test
+    public void verifiedWithArgumentMatcherAndArgumentCaptor() {
+        // Arrange.
+        Integer[] integers = {0, 1, 2, 0, 4};
+        MockConsumer<Integer> mockConsumer = new MockConsumer<>();
+        ArgumentMatcher<Integer> matcher = argument -> argument > 0;
+
+        // Act.
+        for (int b : integers) {
+            mockConsumer.accept(b);
+        }
+
+        // Verify.
+        ArgumentCaptor<Integer> captor = new ArgumentCaptor<>(argument -> argument >= 2);
+        mockConsumer.verifyAcceptCall(Integer.class, false, 0, new CallTimes(3), captor, matcher);
+        assertThat(captor.getAllValues()).hasSize(2);
+        assertThat(captor.getAllValues().get(0)).isEqualTo(2);
+        assertThat(captor.getAllValues().get(1)).isEqualTo(4);
+    }
+
+    @Test
     public void acceptCalledWithinTimeout_verifyAcceptCallPasses() {
         new Thread(() -> {
             try {
diff --git a/camera/camera-testlib-extensions/build.gradle b/camera/camera-testlib-extensions/build.gradle
index 2d3c393..34bc14d 100644
--- a/camera/camera-testlib-extensions/build.gradle
+++ b/camera/camera-testlib-extensions/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(project(":camera:camera-core"))
 }
 
diff --git a/camera/camera-video/build.gradle b/camera/camera-video/build.gradle
index 22fafd6..5fd497e 100644
--- a/camera/camera-video/build.gradle
+++ b/camera/camera-video/build.gradle
@@ -24,7 +24,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":camera:camera-core"))
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
@@ -90,6 +90,5 @@
     description = "Video components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
index d326de5..d2b1913 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
@@ -963,6 +963,23 @@
         }
     }
 
+    @Test
+    fun canSetAudioSource() {
+        // Arrange.
+        val recorder = createRecorder(audioSource = MediaRecorder.AudioSource.VOICE_RECOGNITION)
+
+        // Assert.
+        assertThat(recorder.audioSource).isEqualTo(MediaRecorder.AudioSource.VOICE_RECOGNITION)
+
+        // Act: ensure the value is correctly propagated to the internal AudioSource instance.
+        // Start recording to create the AudioSource instance.
+        recordingSession.createRecording(recorder = recorder).startAndVerify(statusCount = 1)
+
+        // Assert.
+        assertThat(recorder.mAudioSource.mAudioSource)
+            .isEqualTo(MediaRecorder.AudioSource.VOICE_RECOGNITION)
+    }
+
     private fun testRecorderIsConfiguredBasedOnTargetVideoEncodingBitrate(targetBitrate: Int) {
         // Arrange.
         val recorder = createRecorder(targetBitrate = targetBitrate)
@@ -1000,6 +1017,7 @@
         targetBitrate: Int? = null,
         retrySetupVideoMaxCount: Int? = null,
         retrySetupVideoDelayMs: Long? = null,
+        audioSource: Int? = null,
     ): Recorder {
         val recorder =
             Recorder.Builder()
@@ -1009,7 +1027,8 @@
                     executor?.let { setExecutor(it) }
                     videoEncoderFactory?.let { setVideoEncoderFactory(it) }
                     audioEncoderFactory?.let { setAudioEncoderFactory(it) }
-                    targetBitrate?.let { setTargetVideoEncodingBitRate(targetBitrate) }
+                    targetBitrate?.let { setTargetVideoEncodingBitRate(it) }
+                    audioSource?.let { setAudioSource(it) }
                 }
                 .build()
                 .apply {
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index a0f11ae..6cb8513 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -66,6 +66,7 @@
 import androidx.camera.testing.impl.getRotation
 import androidx.camera.testing.impl.mocks.MockScreenFlash
 import androidx.camera.testing.impl.useAndRelease
+import androidx.camera.testing.impl.video.Recording
 import androidx.camera.testing.impl.video.RecordingSession
 import androidx.camera.video.VideoRecordEvent.Finalize.ERROR_SOURCE_INACTIVE
 import androidx.lifecycle.LifecycleOwner
@@ -403,13 +404,16 @@
         // Act: Ensure the Recorder is initialized before start test.
         recordingSession.createRecording().startAndVerify().stop()
 
-        instrumentation.runOnMainSync { lifecycleOwner.pauseAndStop() }
-        recordingSession.createRecording().apply {
-            start()
+        lateinit var recording: Recording
+        instrumentation.runOnMainSync {
+            lifecycleOwner.pauseAndStop()
 
-            // Verify.
-            verifyFinalize(error = ERROR_SOURCE_INACTIVE)
+            // TODO(b/353578694): call start() in main thread to workaround the race condition.
+            recording = recordingSession.createRecording().start()
         }
+
+        // Verify.
+        recording.verifyFinalize(error = ERROR_SOURCE_INACTIVE)
     }
 
     @Test
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
index aee88cb..1250761 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
@@ -701,8 +701,9 @@
      * create this recorder, or the default value of {@link AudioSpec#SOURCE_AUTO} if no source was
      * set.
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
     @AudioSpec.Source
-    int getAudioSource() {
+    public int getAudioSource() {
         return getObservableData(mMediaSpec).getAudioSpec().getSource();
     }
 
@@ -3553,8 +3554,9 @@
          *               {@link AudioSpec#SOURCE_CAMCORDER}. Default is
          *               {@link AudioSpec#SOURCE_AUTO}.
          */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
         @NonNull
-        Builder setAudioSource(@AudioSpec.Source int source) {
+        public Builder setAudioSource(@AudioSpec.Source int source) {
             mMediaSpecBuilder.configureAudio(builder -> builder.setSource(source));
             return this;
         }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java
index 70e0f58..38cda9f 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java
@@ -144,6 +144,8 @@
             double mAudioAmplitude;
     long mAmplitudeTimestamp = 0;
     private final int mAudioFormat;
+    @VisibleForTesting
+    public final int mAudioSource;
 
     /**
      * Creates an AudioSource for the given settings.
@@ -192,6 +194,7 @@
         mAudioStream.setCallback(new AudioStreamCallback(), mExecutor);
         mSilentAudioStream = new SilentAudioStream(settings);
         mAudioFormat = settings.getAudioFormat();
+        mAudioSource = settings.getAudioSource();
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api23Impl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api23Impl.java
index 2bfd854..a595636 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api23Impl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api23Impl.java
@@ -20,7 +20,6 @@
 import android.media.AudioFormat;
 import android.media.AudioRecord;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
@@ -35,7 +34,6 @@
     }
 
     /** Creates an {@link AudioRecord.Builder}. */
-    @DoNotInline
     @NonNull
     public static AudioRecord.Builder createAudioRecordBuilder() {
         return new AudioRecord.Builder();
@@ -45,7 +43,6 @@
      * Sets the {@linkplain AudioRecord.Builder#setAudioSource(int) audio source} on an
      * {@link AudioRecord.Builder}.
      */
-    @DoNotInline
     public static void setAudioSource(@NonNull AudioRecord.Builder audioRecordBuilder,
             int audioSource) {
         audioRecordBuilder.setAudioSource(audioSource);
@@ -55,7 +52,6 @@
      * Sets the {@linkplain AudioRecord.Builder#setAudioFormat(AudioFormat) audio format} on an
      * {@link AudioRecord.Builder}.
      */
-    @DoNotInline
     public static void setAudioFormat(@NonNull AudioRecord.Builder audioRecordBuilder,
             @NonNull AudioFormat audioFormat) {
         audioRecordBuilder.setAudioFormat(audioFormat);
@@ -65,7 +61,6 @@
      * Sets the {@linkplain AudioRecord.Builder#setBufferSizeInBytes(int) buffer size} on an
      * {@link AudioRecord.Builder}.
      */
-    @DoNotInline
     public static void setBufferSizeInBytes(@NonNull AudioRecord.Builder audioRecordBuilder,
             int bufferSizeInBytes) {
         audioRecordBuilder.setBufferSizeInBytes(bufferSizeInBytes);
@@ -75,7 +70,6 @@
     /**
      * Builds an {@link AudioRecord} from an {@link AudioRecord.Builder}.
      */
-    @DoNotInline
     @NonNull
     @RequiresPermission(Manifest.permission.RECORD_AUDIO)
     public static AudioRecord build(@NonNull AudioRecord.Builder audioRecordBuilder) {
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api24Impl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api24Impl.java
index ceaf2e8..1f61acc 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api24Impl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api24Impl.java
@@ -20,7 +20,6 @@
 import android.media.AudioRecordingConfiguration;
 import android.media.AudioTimestamp;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -36,7 +35,6 @@
     /**
      * Gets the audio timestamp from a {@link AudioRecord}.
      */
-    @DoNotInline
     public static int getTimestamp(@NonNull AudioRecord audioRecord,
             @NonNull AudioTimestamp audioTimestamp, int timeBase) {
         return audioRecord.getTimestamp(audioTimestamp, timeBase);
@@ -45,7 +43,6 @@
     /**
      * Gets the audio session ID from a {@link AudioRecordingConfiguration}.
      */
-    @DoNotInline
     public static int getClientAudioSessionId(
             @NonNull AudioRecordingConfiguration audioRecordingConfiguration) {
         return audioRecordingConfiguration.getClientAudioSessionId();
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api26Impl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api26Impl.java
index 901bca0..64c31db 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api26Impl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api26Impl.java
@@ -18,7 +18,6 @@
 
 import android.media.MediaMuxer;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -37,7 +36,6 @@
     /**
      * Uses a {@link FileDescriptor} as output destination to create a {@link MediaMuxer}.
      */
-    @DoNotInline
     @NonNull
     public static MediaMuxer createMediaMuxer(@NonNull FileDescriptor fileDescriptor, int format)
             throws IOException {
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api28Impl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api28Impl.java
index d7c8a08..50d7c94 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api28Impl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api28Impl.java
@@ -19,7 +19,6 @@
 import android.media.MediaCodecInfo;
 import android.util.Range;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -37,7 +36,6 @@
      * {@link MediaCodecInfo.EncoderCapabilities}.
      */
     @NonNull
-    @DoNotInline
     public static Range<Integer> getQualityRange(@NonNull MediaCodecInfo.EncoderCapabilities caps) {
         return caps.getQualityRange();
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api29Impl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api29Impl.java
index f15301e..1735443 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api29Impl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api29Impl.java
@@ -20,7 +20,6 @@
 import android.media.AudioRecord;
 import android.media.AudioRecordingConfiguration;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -43,7 +42,6 @@
      * @return a valid AudioRecordingConfiguration if this recorder is active or null otherwise.
      */
     @Nullable
-    @DoNotInline
     public static AudioRecordingConfiguration getActiveRecordingConfiguration(
             @NonNull AudioRecord audioRecord) {
         return audioRecord.getActiveRecordingConfiguration();
@@ -53,7 +51,6 @@
      * Registers a {@link android.media.AudioManager.AudioRecordingCallback} to a
      * {@link AudioRecord}.
      */
-    @DoNotInline
     public static void registerAudioRecordingCallback(@NonNull AudioRecord audioRecord,
             @NonNull Executor executor,
             @NonNull AudioManager.AudioRecordingCallback callback) {
@@ -64,7 +61,6 @@
      * Unregisters a {@link android.media.AudioManager.AudioRecordingCallback} previously
      * registered from a {@link AudioRecord}.
      */
-    @DoNotInline
     public static void unregisterAudioRecordingCallback(@NonNull AudioRecord audioRecord,
             @NonNull AudioManager.AudioRecordingCallback callback) {
         audioRecord.unregisterAudioRecordingCallback(callback);
@@ -73,7 +69,6 @@
     /**
      * Checks whether a {@link AudioRecordingConfiguration} shows that the client is silenced.
      */
-    @DoNotInline
     public static boolean isClientSilenced(@NonNull AudioRecordingConfiguration configuration) {
         return configuration.isClientSilenced();
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api31Impl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api31Impl.java
index 7e60887..593a749 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api31Impl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/Api31Impl.java
@@ -21,7 +21,6 @@
 import android.media.MediaCodecInfo;
 import android.util.Range;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -38,7 +37,6 @@
      * Returns the minimum number of input channels supported for
      * {@link MediaCodecInfo.AudioCapabilities}.
      */
-    @DoNotInline
     public static int getMinInputChannelCount(@NonNull MediaCodecInfo.AudioCapabilities caps) {
         return caps.getMinInputChannelCount();
     }
@@ -47,7 +45,6 @@
      * Returns an array of ranges representing the number of input channels supported for
      * {@link MediaCodecInfo.AudioCapabilities}.
      */
-    @DoNotInline
     @NonNull
     public static Range<Integer>[] getInputChannelCountRanges(
             @NonNull MediaCodecInfo.AudioCapabilities caps) {
@@ -57,7 +54,6 @@
     /**
      * Sets the context used for attribution on an {@link AudioRecord}.
      */
-    @DoNotInline
     public static void setContext(@NonNull AudioRecord.Builder builder, @NonNull Context context) {
         builder.setContext(context);
     }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
index 7893c56..7f4f2ef 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderImpl.java
@@ -41,7 +41,6 @@
 import android.util.Range;
 import android.view.Surface;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -1720,13 +1719,11 @@
         private Api23Impl() {
         }
 
-        @DoNotInline
         @NonNull
         static Surface createPersistentInputSurface() {
             return MediaCodec.createPersistentInputSurface();
         }
 
-        @DoNotInline
         static void setInputSurface(@NonNull MediaCodec mediaCodec, @NonNull Surface surface) {
             mediaCodec.setInputSurface(surface);
         }
diff --git a/camera/camera-view/build.gradle b/camera/camera-view/build.gradle
index 77d81b0..640ce6fa 100644
--- a/camera/camera-view/build.gradle
+++ b/camera/camera-view/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api("androidx.lifecycle:lifecycle-common:2.0.0")
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":camera:camera-core"))
     api(project(":camera:camera-video"))
 
@@ -89,6 +89,5 @@
     description = "UI tools for the Jetpack Camera Library, a library providing a consistent and " +
             "reliable camera foundation that enables great camera driven experiences across all " +
             "of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
index 8e8933e..a06b39d 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
@@ -125,13 +125,13 @@
  * tap-to-focus and pinch-to-zoom features.
  *
  * <p> This class provides features of 4 {@link UseCase}s: {@link Preview}, {@link ImageCapture},
- * {@link ImageAnalysis} and an experimental video capture. {@link Preview} is required and always
- * enabled. {@link ImageCapture} and {@link ImageAnalysis} are enabled by default. The video
- * capture feature is experimental. It's disabled by default because it might conflict with other
- * use cases, especially on lower end devices. It might be necessary to disable {@link ImageCapture}
- * and/or {@link ImageAnalysis} before the video capture feature can be enabled. Disabling/enabling
- * {@link UseCase}s freezes the preview for a short period of time. To avoid the glitch, the
- * {@link UseCase}s need to be enabled/disabled before the controller is set on {@link PreviewView}.
+ * {@link ImageAnalysis} and {@link VideoCapture}. {@link Preview} is required and always enabled.
+ * {@link ImageCapture} and {@link ImageAnalysis} are enabled by default. The video capture is
+ * disabled by default because it might affect other use cases, especially on lower end devices.
+ * It might be necessary to disable {@link ImageCapture} and/or {@link ImageAnalysis} before the
+ * video capture feature can be enabled. Disabling/enabling {@link UseCase}s freezes the preview for
+ * a short period of time. To avoid the glitch, the {@link UseCase}s need to be enabled/disabled
+ * before the controller is set on {@link PreviewView}.
  */
 public abstract class CameraController {
 
@@ -210,17 +210,20 @@
 
     /**
      * Bitmask option to enable {@link ImageCapture}. In {@link #setEnabledUseCases}, if
-     * (enabledUseCases & IMAGE_CAPTURE) != 0, then controller will enable image capture features.
+     * {@code (enabledUseCases & IMAGE_CAPTURE) != 0}, then controller will enable image capture
+     * features.
      */
     public static final int IMAGE_CAPTURE = 1;
     /**
      * Bitmask option to enable {@link ImageAnalysis}. In {@link #setEnabledUseCases}, if
-     * (enabledUseCases & IMAGE_ANALYSIS) != 0, then controller will enable image analysis features.
+     * {@code (enabledUseCases & IMAGE_ANALYSIS) != 0}, then controller will enable image
+     * analysis features.
      */
     public static final int IMAGE_ANALYSIS = 1 << 1;
     /**
      * Bitmask option to enable video capture use case. In {@link #setEnabledUseCases}, if
-     * (enabledUseCases & VIDEO_CAPTURE) != 0, then controller will enable video capture features.
+     * {@code (enabledUseCases & VIDEO_CAPTURE) != 0}, then controller will enable video capture
+     * features.
      */
     public static final int VIDEO_CAPTURE = 1 << 2;
 
@@ -403,16 +406,16 @@
     /**
      * Gets a {@link ListenableFuture} that completes when camera initialization completes.
      *
-     * <p> This future may fail with an {@link InitializationException} and associated cause that
+     * <p>This future may fail with an {@link InitializationException} and associated cause that
      * can be retrieved by {@link Throwable#getCause()}. The cause will be a
      * {@link CameraUnavailableException} if it fails to access any camera during initialization.
      *
-     * <p> In the rare case that the future fails with {@link CameraUnavailableException}, the
+     * <p>In the rare case that the future fails with {@link CameraUnavailableException}, the
      * camera will become unusable. This could happen for various reasons, for example hardware
      * failure or the camera being held by another process. If the failure is temporary, killing
      * and restarting the app might fix the issue.
      *
-     * <p> The initialization also try to bind use cases before completing the
+     * <p>The initialization also try to bind use cases before completing the
      * {@link ListenableFuture}. The {@link ListenableFuture} will complete successfully
      * regardless of whether the use cases are ready to be bound, e.g. it will complete
      * successfully even if the controller is not set on a {@link PreviewView}. However the
@@ -429,8 +432,8 @@
     /**
      * Implemented by children to refresh after {@link UseCase} is changed.
      *
-     * @throws IllegalStateException for invalid {@link UseCase} combinations.
-     * @throws RuntimeException      for invalid {@link CameraEffect} combinations.
+     * @throws IllegalStateException For invalid {@link UseCase} combinations.
+     * @throws RuntimeException For invalid {@link CameraEffect} combinations.
      */
     @Nullable
     abstract Camera startCamera();
@@ -450,15 +453,13 @@
     /**
      * Enables or disables use cases.
      *
-     * <p> Use cases need to be enabled before they can be used. By default, {@link #IMAGE_CAPTURE}
+     * <p>Use cases need to be enabled before they can be used. By default, {@link #IMAGE_CAPTURE}
      * and {@link #IMAGE_ANALYSIS} are enabled, and {@link #VIDEO_CAPTURE} is disabled. This is
-     * necessary because {@link #VIDEO_CAPTURE} is an experimental feature that might not work
-     * with other use cases, especially on lower end devices. When that happens, this method will
-     * fail with an {@link IllegalStateException}.
+     * necessary because {@link #VIDEO_CAPTURE} may affect the available resolutions for other use
+     * cases, especially on lower end devices.
      *
-     * <p> To make sure {@link #VIDEO_CAPTURE} works, {@link #IMAGE_CAPTURE} and
-     * {@link #IMAGE_ANALYSIS} needs to be disabled when enabling {@link #VIDEO_CAPTURE}. For
-     * example:
+     * <p>To make sure getting the maximum resolution, only enable {@link #VIDEO_CAPTURE} when
+     * shooting video. For example:
      *
      * <pre><code>
      * // By default, image capture is enabled. Taking picture works.
@@ -475,10 +476,9 @@
      * </code></pre>
      *
      * @param enabledUseCases one or more of the following use cases, bitwise-OR-ed together:
-     *                        {@link #IMAGE_CAPTURE}, {@link #IMAGE_ANALYSIS} and/or
-     *                        {@link #VIDEO_CAPTURE}.
-     * @throws IllegalStateException If the current camera selector is unable to resolve a
-     *                               camera to be used for the enabled use cases.
+     * {@link #IMAGE_CAPTURE}, {@link #IMAGE_ANALYSIS} and/or {@link #VIDEO_CAPTURE}.
+     * @throws IllegalStateException If the current camera selector is unable to resolve a camera
+     * to be used for the enabled use cases.
      * @see UseCase
      * @see ImageCapture
      * @see ImageAnalysis
@@ -501,23 +501,28 @@
      * Checks if the given use case mask is enabled.
      *
      * @param useCaseMask One of the {@link #IMAGE_CAPTURE}, {@link #IMAGE_ANALYSIS} or
-     *                    {@link #VIDEO_CAPTURE}
-     * @return true if the use case is enabled.
+     * {@link #VIDEO_CAPTURE}.
+     * @return {@code true} if the use case is enabled.
      */
     private boolean isUseCaseEnabled(int useCaseMask) {
         return (mEnabledUseCases & useCaseMask) != 0;
     }
 
     /**
-     * Sets the {@link ResolutionSelector} on the config.
+     * Configures the resolution on the config.
      *
-     * <p>If the given resolution selector is {@code null}, the {@link AspectRatioStrategy} will
-     * be override to match the {@link ViewPort}.
+     * <p>If the {@link ResolutionSelector} and the {@link OutputSize} are set at the same time,
+     * the {@link ResolutionSelector} takes precedence.
+     *
+     * <p>If the given {@link ResolutionSelector} is {@code null} and {@link OutputSize}, the
+     * {@link AspectRatioStrategy} will be override to match the {@link ViewPort}.
      */
-    private void setResolutionSelector(@NonNull ImageOutputConfig.Builder<?> builder,
-            @Nullable ResolutionSelector resolutionSelector) {
+    private void configureResolution(@NonNull ImageOutputConfig.Builder<?> builder,
+            @Nullable ResolutionSelector resolutionSelector, @Nullable OutputSize outputSize) {
         if (resolutionSelector != null) {
             builder.setResolutionSelector(resolutionSelector);
+        } else if (outputSize != null) {
+            setTargetOutputSize(builder, outputSize);
         } else if (mViewPort != null) {
             // Override the aspect ratio strategy if viewport is set and there's no resolution
             // selector explicitly set by the user.
@@ -586,7 +591,7 @@
     }
 
     /**
-     * Clear {@link PreviewView} to remove the UI reference.
+     * Clears {@link PreviewView} to remove the UI reference.
      */
     @MainThread
     void clearPreviewSurface() {
@@ -614,13 +619,14 @@
     /**
      * Sets the intended output size for {@link Preview}.
      *
-     * <p> The value is used as a hint when determining the resolution and aspect ratio of the
+     * <p>The value is used as a hint when determining the resolution and aspect ratio of the
      * preview. The actual output may differ from the requested value due to device constraints.
      *
-     * <p> When set to null, the output will be based on the default config of {@link Preview}.
+     * <p>When set to {@code null}, the output will be based on the default config of
+     * {@link Preview}.
      *
-     * <p> Changing the value will reconfigure the camera which will cause additional latency.
-     * To avoid this, set the value before controller is bound to lifecycle.
+     * <p>Changing the value will reconfigure the camera which will cause additional latency. To
+     * avoid this, set the value before controller is bound to lifecycle.
      *
      * @param targetSize the intended output size for {@link Preview}.
      * @see Preview.Builder#setTargetAspectRatio(int)
@@ -641,7 +647,7 @@
 
     /**
      * Returns the intended output size for {@link Preview} set by
-     * {@link #setPreviewTargetSize(OutputSize)}, or null if not set.
+     * {@link #setPreviewTargetSize(OutputSize)}, or {@code null} if not set.
      *
      * @deprecated Use {@link #getPreviewResolutionSelector()} instead.
      */
@@ -657,10 +663,10 @@
      * Sets the {@link ResolutionSelector} for {@link Preview}.
      *
      * <p>CameraX uses this value as a hint to select the resolution for preview. The actual
-     * output may differ from the requested value due to device constraints. When set to null,
-     * CameraX will use the default config of {@link Preview}. By default, the selected resolution
-     * will be limited by the {@code PREVIEW} size which is defined as the best size match to the
-     * device's screen resolution, or to 1080p (1920x1080), whichever is smaller.
+     * output may differ from the requested value due to device constraints. When set to
+     * {@code null}, CameraX will use the default config of {@link Preview}. By default, the
+     * selected resolution will be limited by the {@code PREVIEW} size which is defined as the best
+     * size match to the device's screen resolution, or to 1080p (1920x1080), whichever is smaller.
      *
      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
      * avoid this, set the value before controller is bound to lifecycle.
@@ -699,7 +705,7 @@
      * are displayed on a display. Some dynamic ranges will allow the preview to make full use of
      * the extended range of brightness of a display.
      *
-     * <p> The supported dynamic ranges of the camera can be queried using
+     * <p>The supported dynamic ranges of the camera can be queried using
      * {@link CameraInfo#querySupportedDynamicRanges(Set)}.
      *
      * <p>It is possible to choose a high dynamic range (HDR) with unspecified encoding by providing
@@ -748,8 +754,7 @@
 
     private Preview createPreview() {
         Preview.Builder builder = new Preview.Builder();
-        setTargetOutputSize(builder, mPreviewTargetSize);
-        setResolutionSelector(builder, mPreviewResolutionSelector);
+        configureResolution(builder, mPreviewResolutionSelector, mPreviewTargetSize);
         builder.setDynamicRange(mPreviewDynamicRange);
         return builder.build();
     }
@@ -761,7 +766,7 @@
     /**
      * Checks if {@link ImageCapture} is enabled.
      *
-     * <p> {@link ImageCapture} is enabled by default. It has to be enabled before
+     * <p>{@link ImageCapture} is enabled by default. It has to be enabled before
      * {@link #takePicture} can be called.
      *
      * @see ImageCapture
@@ -800,8 +805,8 @@
      * still set). Otherwise, {@code FLASH_MODE_OFF} will be set.
      *
      * @param flashMode the flash mode for {@link ImageCapture}.
-     * @throws IllegalArgumentException If flash mode is invalid or FLASH_MODE_SCREEN is used
-     *                                  without a front camera.
+     * @throws IllegalArgumentException If flash mode is invalid or
+     * {@link ImageCapture#FLASH_MODE_SCREEN} is used without a front camera.
      * @see PreviewView#setScreenFlashWindow(Window)
      * @see ScreenFlashView#setScreenFlashWindow(Window)
      */
@@ -862,9 +867,10 @@
      * Returns a {@link ScreenFlashUiInfo} by prioritizing {@link ScreenFlashView} over
      * {@link PreviewView}.
      *
-     * <p> PreviewView always has a ScreenFlashView internally and does not know if user is
-     * using another ScreenFlashView themselves. This API prioritizes user's ScreenFlashView over
-     * the internal one in PreviewView and provides the ScreenFlashUiInfo accordingly.
+     * <p>{@link PreviewView} always has a {@link ScreenFlashView} internally and does not know
+     * if user is using another {@link ScreenFlashView} themselves. This API prioritizes user's
+     * {@link ScreenFlashView} over the internal one in {@link PreviewView} and provides the
+     * {@link ScreenFlashUiInfo} accordingly.
      */
     @Nullable
     @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -893,14 +899,13 @@
      * {@link PreviewView}'s aspect ratio matches the max JPEG resolution supported by the camera.
      *
      * @param outputFileOptions  Options to store the newly captured image.
-     * @param executor           The executor in which the callback methods will be run.
+     * @param executor The executor in which the callback methods will be run.
      * @param imageSavedCallback Callback to be called for the newly captured image.
      * @throws IllegalStateException If {@link ImageCapture#FLASH_MODE_SCREEN} is set to the
-     *                               {@link CameraController}, but a non-null {@link Window}
-     *                               instance has not been set with
-     *                               {@link PreviewView#setScreenFlashWindow}.
+     * {@link CameraController}, but a non-null {@link Window} instance has not been set with
+     * {@link PreviewView#setScreenFlashWindow}.
      * @see ImageCapture#takePicture(
-     *ImageCapture.OutputFileOptions, Executor, ImageCapture.OnImageSavedCallback)
+     * ImageCapture.OutputFileOptions, Executor, ImageCapture.OnImageSavedCallback)
      */
     @MainThread
     public void takePicture(
@@ -918,9 +923,9 @@
     }
 
     /**
-     * Update {@link ImageCapture.OutputFileOptions} based on config.
+     * Updates {@link ImageCapture.OutputFileOptions} based on config.
      *
-     * <p> Mirror the output image if front camera is used and if the flag is not set explicitly by
+     * <p>Mirror the output image if front camera is used and if the flag is not set explicitly by
      * the app.
      */
     @VisibleForTesting
@@ -942,9 +947,8 @@
      * @param executor The executor in which the callback methods will be run.
      * @param callback Callback to be invoked for the newly captured image
      * @throws IllegalStateException If {@link ImageCapture#FLASH_MODE_SCREEN} is set to the
-     *                               {@link CameraController}, but a non-null {@link Window}
-     *                               instance has not been set with
-     *                               {@link PreviewView#setScreenFlashWindow}.
+     * {@link CameraController}, but a non-null {@link Window} instance has not been set with
+     * {@link PreviewView#setScreenFlashWindow}.
      * @see ImageCapture#takePicture(Executor, ImageCapture.OnImageCapturedCallback)
      */
     @MainThread
@@ -979,10 +983,10 @@
      * {@link ImageCapture.CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY},
      * which prioritizes image quality over latency.
      *
-     * <p> Changing the value will reconfigure the camera which will cause additional latency.
-     * To avoid this, set the value before controller is bound to lifecycle.
+     * <p>Changing the value will reconfigure the camera which will cause additional latency. To
+     * avoid this, set the value before controller is bound to lifecycle.
      *
-     * @param captureMode the requested image capture mode.
+     * @param captureMode The requested image capture mode.
      */
     @MainThread
     public void setImageCaptureMode(@ImageCapture.CaptureMode int captureMode) {
@@ -1008,16 +1012,17 @@
     /**
      * Sets the intended image size for {@link ImageCapture}.
      *
-     * <p> The value is used as a hint when determining the resolution and aspect ratio of
-     * the captured image. The actual output may differ from the requested value due to device
+     * <p>The value is used as a hint when determining the resolution and aspect ratio of the
+     * captured image. The actual output may differ from the requested value due to device
      * constraints.
      *
-     * <p> When set to null, the output will be based on the default config of {@link ImageCapture}.
+     * <p>When set to {@code null}, the output will be based on the default config of
+     * {@link ImageCapture}.
      *
-     * <p> Changing the value will reconfigure the camera which will cause additional latency.
-     * To avoid this, set the value before controller is bound to lifecycle.
+     * <p>Changing the value will reconfigure the camera which will cause additional latency. To
+     * avoid this, set the value before controller is bound to lifecycle.
      *
-     * @param targetSize the intended image size for {@link ImageCapture}.
+     * @param targetSize The intended image size for {@link ImageCapture}.
      * @deprecated Use {@link #setImageCaptureResolutionSelector(ResolutionSelector)} instead.
      */
     @MainThread
@@ -1034,7 +1039,7 @@
 
     /**
      * Returns the intended output size for {@link ImageCapture} set by
-     * {@link #setImageCaptureTargetSize(OutputSize)}, or null if not set.
+     * {@link #setImageCaptureTargetSize(OutputSize)}, or {@code null} if not set.
      *
      * @deprecated Use {@link #getImageCaptureResolutionSelector()} instead.
      */
@@ -1050,10 +1055,11 @@
      * Sets the {@link ResolutionSelector} for {@link ImageCapture}.
      *
      * <p>CameraX uses this value as a hint to select the resolution for captured images. The actual
-     * output may differ from the requested value due to device constraints. When set to null,
-     * CameraX will use the default config of {@link ImageCapture}. The default resolution
-     * strategy for ImageCapture is {@link ResolutionStrategy#HIGHEST_AVAILABLE_STRATEGY}, which
-     * will select the largest available resolution to use.
+     * output may differ from the requested value due to device constraints. When set to
+     * {@code null}, CameraX will use the default config of {@link ImageCapture}. The default
+     * resolution strategy for ImageCapture is
+     * {@link ResolutionStrategy#HIGHEST_AVAILABLE_STRATEGY}, which will select the largest
+     * available resolution to use.
      *
      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
      * avoid this, set the value before controller is bound to lifecycle.
@@ -1088,12 +1094,12 @@
     /**
      * Sets the default executor that will be used for {@link ImageCapture} IO tasks.
      *
-     * <p> This executor will be used for any IO tasks specifically for {@link ImageCapture},
+     * <p>This executor will be used for any IO tasks specifically for {@link ImageCapture},
      * such as {@link #takePicture(ImageCapture.OutputFileOptions, Executor,
      * ImageCapture.OnImageSavedCallback)}. If no executor is set, then a default Executor
      * specifically for IO will be used instead.
      *
-     * <p> Changing the value will reconfigure the camera which will cause additional latency.
+     * <p>Changing the value will reconfigure the camera which will cause additional latency.
      * To avoid this, set the value before controller is bound to lifecycle.
      *
      * @param executor The executor which will be used for IO tasks.
@@ -1137,8 +1143,7 @@
         if (imageCaptureMode != null) {
             builder.setCaptureMode(imageCaptureMode);
         }
-        setTargetOutputSize(builder, mImageCaptureTargetSize);
-        setResolutionSelector(builder, mImageCaptureResolutionSelector);
+        configureResolution(builder, mImageCaptureResolutionSelector, mImageCaptureTargetSize);
         if (mImageCaptureIoExecutor != null) {
             builder.setIoExecutor(mImageCaptureIoExecutor);
         }
@@ -1177,12 +1182,12 @@
      * coordinates from the {@link ImageAnalysis}'s coordinate system to the {@link PreviewView}'s
      * coordinate system.
      *
-     * <p> If the {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns a non-null
+     * <p>If the {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns a non-null
      * value, calling this method will reconfigure the camera which might cause additional
      * latency. To avoid this, set the value before controller is bound to the lifecycle.
      *
      * @param executor The executor in which the
-     *                 {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} will be run.
+     * {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} will be run.
      * @param analyzer of the images.
      * @see ImageAnalysis#setAnalyzer(Executor, ImageAnalysis.Analyzer)
      */
@@ -1205,7 +1210,7 @@
      *
      * <p>This will stop data from streaming to the {@link ImageAnalysis}.
      *
-     * <p> If the current {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns
+     * <p>If the current {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns
      * non-null value, calling this method will reconfigure the camera which might cause additional
      * latency. To avoid this, call this method when the lifecycle is not active.
      *
@@ -1239,7 +1244,7 @@
     /**
      * Returns the mode with which images are acquired.
      *
-     * <p> If not set, it defaults to {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}.
+     * <p>If not set, it defaults to {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}.
      *
      * @return The backpressure strategy applied to the image producer.
      * @see ImageAnalysis.Builder#getBackpressureStrategy()
@@ -1259,7 +1264,7 @@
      * {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}. If not set, the backpressure strategy
      * will default to {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}.
      *
-     * <p> Changing the value will reconfigure the camera which will cause additional latency. To
+     * <p>Changing the value will reconfigure the camera which will cause additional latency. To
      * avoid this, set the value before controller is bound to lifecycle.
      *
      * @param strategy The strategy to use.
@@ -1281,11 +1286,11 @@
     /**
      * Sets the image queue depth of {@link ImageAnalysis}.
      *
-     * <p> This sets the number of images available in parallel to {@link ImageAnalysis.Analyzer}
-     * . The value is only used if the backpressure strategy is
+     * <p>This sets the number of images available in parallel to {@link ImageAnalysis.Analyzer}.
+     * The value is only used if the backpressure strategy is
      * {@link ImageAnalysis.BackpressureStrategy#STRATEGY_BLOCK_PRODUCER}.
      *
-     * <p> Changing the value will reconfigure the camera which will cause additional latency. To
+     * <p>Changing the value will reconfigure the camera which will cause additional latency. To
      * avoid this, set the value before controller is bound to lifecycle.
      *
      * @param depth The total number of images available.
@@ -1316,17 +1321,17 @@
     /**
      * Sets the intended output size for {@link ImageAnalysis}.
      *
-     * <p> The value is used as a hint when determining the resolution and aspect ratio of
+     * <p>The value is used as a hint when determining the resolution and aspect ratio of
      * the output buffer. The actual output may differ from the requested value due to device
      * constraints.
      *
-     * <p> When set to null, the output will be based on the default config of
+     * <p>When set to {@code null}, the output will be based on the default config of
      * {@link ImageAnalysis}.
      *
-     * <p> Changing the value will reconfigure the camera which will cause additional latency.
+     * <p>Changing the value will reconfigure the camera which will cause additional latency.
      * To avoid this, set the value before controller is bound to lifecycle.
      *
-     * @param targetSize the intended output size for {@link ImageAnalysis}.
+     * @param targetSize The intended output size for {@link ImageAnalysis}.
      * @see ImageAnalysis.Builder#setTargetAspectRatio(int)
      * @see ImageAnalysis.Builder#setTargetResolution(Size)
      * @deprecated Use {@link #setImageAnalysisResolutionSelector(ResolutionSelector)} instead.
@@ -1348,7 +1353,7 @@
 
     /**
      * Returns the intended output size for {@link ImageAnalysis} set by
-     * {@link #setImageAnalysisTargetSize(OutputSize)}, or null if not set.
+     * {@link #setImageAnalysisTargetSize(OutputSize)}, or {@code null} if not set.
      *
      * @deprecated Use {@link #getImageAnalysisResolutionSelector()} instead.
      */
@@ -1364,9 +1369,9 @@
      * Sets the {@link ResolutionSelector} for {@link ImageAnalysis}.
      *
      * <p>CameraX uses this value as a hint to select the resolution for images. The actual
-     * output may differ from the requested value due to device constraints. When set to null,
-     * CameraX will use the default config of {@link ImageAnalysis}. ImageAnalysis has a default
-     * {@link ResolutionStrategy} with bound size as 640x480 and fallback rule of
+     * output may differ from the requested value due to device constraints. When set to
+     * {@code null}, CameraX will use the default config of {@link ImageAnalysis}. ImageAnalysis has
+     * a default {@link ResolutionStrategy} with bound size as 640x480 and fallback rule of
      * {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}.
      *
      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
@@ -1409,10 +1414,10 @@
      * <p>If not set, the background executor will default to an automatically generated
      * {@link Executor}.
      *
-     * <p> Changing the value will reconfigure the camera, which will cause additional latency. To
+     * <p>Changing the value will reconfigure the camera, which will cause additional latency. To
      * avoid this, set the value before controller is bound to lifecycle.
      *
-     * @param executor the executor for {@link ImageAnalysis} background tasks.
+     * @param executor The executor for {@link ImageAnalysis} background tasks.
      * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor)
      */
     @MainThread
@@ -1519,8 +1524,7 @@
         if (outputFormat != null) {
             builder.setOutputImageFormat(outputFormat);
         }
-        setTargetOutputSize(builder, mImageAnalysisTargetSize);
-        setResolutionSelector(builder, mImageAnalysisResolutionSelector);
+        configureResolution(builder, mImageAnalysisResolutionSelector, mImageAnalysisTargetSize);
         if (mAnalysisBackgroundExecutor != null) {
             builder.setBackgroundExecutor(mAnalysisBackgroundExecutor);
         }
@@ -1548,8 +1552,8 @@
     /**
      * Checks if video capture is enabled.
      *
-     * <p> Video capture is disabled by default. It has to be enabled before
-     * {@link #startRecording} can be called.
+     * <p>Video capture is disabled by default. It has to be enabled before {@link #startRecording}
+     * can be called.
      */
     @MainThread
     public boolean isVideoCaptureEnabled() {
@@ -1560,28 +1564,27 @@
     /**
      * Takes a video to a given file.
      *
-     * <p> Only a single recording can be active at a time, so if {@link #isRecording()} is true,
-     * this will throw an {@link IllegalStateException}.
+     * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is
+     * {@code true}, this will throw an {@link IllegalStateException}.
      *
-     * <p> Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
+     * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
      * be the first event sent to the provided event listener.
      *
-     * <p> If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
+     * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
      * will be the first event sent to the provided listener, and information about the error can
      * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method.
      *
-     * <p> Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
+     * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
      * permission; without it, starting a recording will fail with a {@link SecurityException}.
      *
-     * @param outputOptions the options to store the newly captured video.
-     * @param audioConfig   the configuration of audio.
-     * @param executor      the executor that the event listener will be run on.
-     * @param listener      the event listener to handle video record events.
+     * @param outputOptions The options to store the newly captured video.
+     * @param audioConfig The configuration of audio.
+     * @param executor The executor that the event listener will be run on.
+     * @param listener The event listener to handle video record events.
      * @return a {@link Recording} that provides controls for new active recordings.
      * @throws IllegalStateException if there is an unfinished active recording.
-     * @throws SecurityException     if the audio config specifies audio should be enabled but the
-     *                               {@link android.Manifest.permission#RECORD_AUDIO} permission
-     *                               is denied.
+     * @throws SecurityException if the audio config specifies audio should be enabled but the
+     * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied.
      */
     @SuppressLint("MissingPermission")
     @MainThread
@@ -1597,31 +1600,30 @@
     /**
      * Takes a video to a given file descriptor.
      *
-     * <p> Currently, file descriptors as output destinations are not supported on pre-Android O
+     * <p>Currently, file descriptors as output destinations are not supported on pre-Android O
      * (API 26) devices.
      *
-     * <p> Only a single recording can be active at a time, so if {@link #isRecording()} is true,
+     * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is true,
      * this will throw an {@link IllegalStateException}.
      *
-     * <p> Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
+     * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
      * be the first event sent to the provided event listener.
      *
-     * <p> If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
+     * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
      * will be the first event sent to the provided listener, and information about the error can
      * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method.
      *
-     * <p> Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
+     * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
      * permission; without it, starting a recording will fail with a {@link SecurityException}.
      *
-     * @param outputOptions the options to store the newly captured video.
-     * @param audioConfig   the configuration of audio.
-     * @param executor      the executor that the event listener will be run on.
-     * @param listener      the event listener to handle video record events.
+     * @param outputOptions The options to store the newly captured video.
+     * @param audioConfig The configuration of audio.
+     * @param executor The executor that the event listener will be run on.
+     * @param listener The event listener to handle video record events.
      * @return a {@link Recording} that provides controls for new active recordings.
      * @throws IllegalStateException if there is an unfinished active recording.
-     * @throws SecurityException     if the audio config specifies audio should be enabled but the
-     *                               {@link android.Manifest.permission#RECORD_AUDIO} permission
-     *                               is denied.
+     * @throws SecurityException if the audio config specifies audio should be enabled but the
+     * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied.
      */
     @SuppressLint("MissingPermission")
     @RequiresApi(26)
@@ -1638,28 +1640,27 @@
     /**
      * Takes a video to MediaStore.
      *
-     * <p> Only a single recording can be active at a time, so if {@link #isRecording()} is true,
-     * this will throw an {@link IllegalStateException}.
+     * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is
+     * {@code true}, this will throw an {@link IllegalStateException}.
      *
-     * <p> Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
+     * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
      * be the first event sent to the provided event listener.
      *
-     * <p> If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
+     * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
      * will be the first event sent to the provided listener, and information about the error can
      * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method.
      *
-     * <p> Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
+     * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
      * permission; without it, starting a recording will fail with a {@link SecurityException}.
      *
-     * @param outputOptions the options to store the newly captured video.
-     * @param audioConfig   the configuration of audio.
-     * @param executor      the executor that the event listener will be run on.
-     * @param listener      the event listener to handle video record events.
+     * @param outputOptions The options to store the newly captured video.
+     * @param audioConfig The configuration of audio.
+     * @param executor The executor that the event listener will be run on.
+     * @param listener The event listener to handle video record events.
      * @return a {@link Recording} that provides controls for new active recordings.
      * @throws IllegalStateException if there is an unfinished active recording.
-     * @throws SecurityException     if the audio config specifies audio should be enabled but the
-     *                               {@link android.Manifest.permission#RECORD_AUDIO} permission
-     *                               is denied.
+     * @throws SecurityException if the audio config specifies audio should be enabled but the
+     * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied.
      */
     @SuppressLint("MissingPermission")
     @MainThread
@@ -1710,11 +1711,11 @@
     /**
      * Generates a {@link PendingRecording} instance for starting a recording.
      *
-     * <p> This method handles {@code prepareRecording()} methods for different output formats,
+     * <p>This method handles {@code prepareRecording()} methods for different output formats,
      * and makes {@link #startRecordingInternal(OutputOptions, AudioConfig, Executor, Consumer)}
      * only handle the general flow.
      *
-     * <p> This method uses the parent class {@link OutputOptions} as the parameter. On the other
+     * <p>This method uses the parent class {@link OutputOptions} as the parameter. On the other
      * hand, the public {@code startRecording()} is overloaded with subclasses. The reason is to
      * enforce compile-time check for API levels.
      */
@@ -1786,9 +1787,9 @@
     /**
      * Stops an in-progress video recording.
      *
-     * <p> Once the current recording has been stopped, the next recording can be started.
+     * <p>Once the current recording has been stopped, the next recording can be started.
      *
-     * <p> If the recording completes successfully, a {@link VideoRecordEvent.Finalize} event with
+     * <p>If the recording completes successfully, a {@link VideoRecordEvent.Finalize} event with
      * {@link VideoRecordEvent.Finalize#ERROR_NONE} will be sent to the provided listener.
      */
     @MainThread
@@ -1819,10 +1820,10 @@
      * <p>If no quality selector is provided, the default is
      * {@link Recorder#DEFAULT_QUALITY_SELECTOR}.
      *
-     * <p>Changing the value will reconfigure the camera which will cause video capture to stop.
-     * To avoid this, set the value before controller is bound to lifecycle.
+     * <p>Changing the value will reconfigure the camera which will cause video capture to stop. To
+     * avoid this, set the value before controller is bound to lifecycle.
      *
-     * @param qualitySelector the quality selector for {@link #VIDEO_CAPTURE}.
+     * @param qualitySelector The quality selector for {@link #VIDEO_CAPTURE}.
      * @see QualitySelector
      */
     @MainThread
@@ -2035,15 +2036,15 @@
     /**
      * Sets the {@link CameraSelector}.
      *
-     * <p> Calling this method with a {@link CameraSelector} that resolves to a different camera
+     * <p>Calling this method with a {@link CameraSelector} that resolves to a different camera
      * will change the camera being used by the controller. If camera initialization is complete,
      * the controller will immediately rebind use cases with the new {@link CameraSelector};
      * otherwise, the new {@link CameraSelector} will be used when the camera becomes ready.
      *
      * <p>The default value is {@link CameraSelector#DEFAULT_BACK_CAMERA}.
      *
-     * @throws IllegalStateException If the provided camera selector is unable to resolve a
-     *                               camera to be used for the enabled use cases.
+     * @throws IllegalStateException If the provided camera selector is unable to resolve a camera
+     * to be used for the enabled use cases.
      * @see CameraSelector
      */
     @MainThread
@@ -2072,11 +2073,11 @@
     /**
      * Checks if the given {@link CameraSelector} can be resolved to a camera.
      *
-     * <p> Use this method to check if the device has the given camera.
+     * <p>Use this method to check if the device has the given camera.
      *
-     * <p> Only call this method after camera is initialized. e.g. after the
-     * {@link ListenableFuture} from {@link #getInitializationFuture()} is finished. Calling it
-     * prematurely throws {@link IllegalStateException}. Example:
+     * <p>Only call this method after camera is initialized. e.g. after the {@link ListenableFuture}
+     * from {@link #getInitializationFuture()} is finished. Calling it prematurely throws
+     * {@link IllegalStateException}. Example:
      *
      * <pre><code>
      * controller.getInitializationFuture().addListener(() -> {
@@ -2090,7 +2091,7 @@
      * }, ContextCompat.getMainExecutor(requireContext()));
      * </code></pre>
      *
-     * @return true if the {@link CameraSelector} can be resolved to a camera.
+     * @return {@code true} if the {@link CameraSelector} can be resolved to a camera.
      * @throws IllegalStateException if the camera is not initialized.
      */
     @MainThread
@@ -2128,9 +2129,9 @@
     /**
      * Returns whether pinch-to-zoom is enabled.
      *
-     * <p> By default pinch-to-zoom is enabled.
+     * <p>By default pinch-to-zoom is enabled.
      *
-     * @return True if pinch-to-zoom is enabled.
+     * @return {@code true} if pinch-to-zoom is enabled.
      */
     @MainThread
     public boolean isPinchToZoomEnabled() {
@@ -2144,7 +2145,7 @@
      * <p>Once enabled, end user can pinch on the {@link PreviewView} to zoom in/out if the bound
      * camera supports zooming.
      *
-     * @param enabled True to enable pinch-to-zoom.
+     * @param enabled {@code true} to enable pinch-to-zoom.
      */
     @MainThread
     public void setPinchToZoomEnabled(boolean enabled) {
@@ -2235,9 +2236,9 @@
     /**
      * Returns whether tap-to-focus is enabled.
      *
-     * <p> By default tap-to-focus is enabled.
+     * <p>By default tap-to-focus is enabled.
      *
-     * @return True if tap-to-focus is enabled.
+     * @return {@code true} if tap-to-focus is enabled.
      */
     @MainThread
     public boolean isTapToFocusEnabled() {
@@ -2250,7 +2251,7 @@
      *
      * <p>Once enabled, end user can tap on the {@link PreviewView} to set focus point.
      *
-     * @param enabled True to enable tap-to-focus.
+     * @param enabled {@code true} to enable tap-to-focus.
      */
     @MainThread
     public void setTapToFocusEnabled(boolean enabled) {
@@ -2261,7 +2262,7 @@
     /**
      * Returns a {@link LiveData} with the latest tap-to-focus state.
      *
-     * <p> When tap-to-focus feature is enabled, the {@link LiveData} will receive updates of
+     * <p>When tap-to-focus feature is enabled, the {@link LiveData} will receive updates of
      * focusing states. This happens when the end user taps on {@link PreviewView}, and then again
      * when focusing is finished either successfully or unsuccessfully. The following table
      * displays the states the {@link LiveData} can be in, and the possible transitions between
@@ -2338,13 +2339,14 @@
     /**
      * Gets the {@link CameraInfo} of the currently attached camera.
      *
-     * <p> For info available directly through CameraController as well as {@link CameraInfo},
-     * it's recommended to use the ones with CameraController, e.g. {@link #getTorchState()} v.s.
-     * {@link CameraInfo#getTorchState()}. {@link CameraInfo} is a lower-layer API and may
-     * require more steps to achieve the same effect, and will not maintain values when switching
-     * between cameras.
+     * <p>For info available directly through CameraController as well as {@link CameraInfo}, it's
+     * recommended to use the ones with CameraController, e.g. {@link #getTorchState()} v.s.
+     * {@link CameraInfo#getTorchState()}. {@link CameraInfo} is a lower-layer API and may require
+     * more steps to achieve the same effect, and will not maintain values when switching between
+     * cameras.
      *
-     * @return the {@link CameraInfo} of the current camera. Returns null if camera is not ready.
+     * @return The {@link CameraInfo} of the current camera. Returns {@code null} if camera is not
+     * ready.
      * @see Camera#getCameraInfo()
      */
     @Nullable
@@ -2357,13 +2359,14 @@
     /**
      * Gets the {@link CameraControl} of the currently attached camera.
      *
-     * <p> For controls available directly through CameraController as well as
+     * <p>For controls available directly through CameraController as well as
      * {@link CameraControl}, it's recommended to use the ones with CameraController, e.g.
      * {@link #setLinearZoom(float)} v.s. {@link CameraControl#setLinearZoom(float)}.
      * CameraControl is a lower-layer API and may require more steps to achieve the same effect,
      * and will not maintain control values when switching between cameras.
      *
-     * @return the {@link CameraControl} of the current camera. Returns null if camera is not ready.
+     * @return The {@link CameraControl} of the current camera. Returns {@code null} if camera is
+     * not ready.
      * @see Camera#getCameraControl()
      */
     @Nullable
@@ -2383,7 +2386,7 @@
      * camera to be ready and then sets the zoom ratio.
      *
      * @param zoomRatio The requested zoom ratio.
-     * @return a {@link ListenableFuture} which is finished when camera is set to the given ratio.
+     * @return A {@link ListenableFuture} which is finished when camera is set to the given ratio.
      * It fails with {@link CameraControl.OperationCanceledException} if there is newer value
      * being set or camera is closed. If the ratio is out of range, it fails with
      * {@link IllegalArgumentException}. Cancellation of this future is a no-op.
@@ -2411,7 +2414,7 @@
      * <p>If the value is set before the camera is ready, {@link CameraController} waits for the
      * camera to be ready and then sets the linear zoom.
      *
-     * @return a {@link ListenableFuture} which is finished when camera is set to the given ratio.
+     * @return A {@link ListenableFuture} which is finished when camera is set to the given ratio.
      * It fails with {@link CameraControl.OperationCanceledException} if there is newer value
      * being set or camera is closed. If the ratio is out of range, it fails with
      * {@link IllegalArgumentException}. Cancellation of this future is a no-op.
@@ -2430,10 +2433,10 @@
     /**
      * Returns a {@link LiveData} of current {@link TorchState}.
      *
-     * <p>The torch can be turned on and off via {@link #enableTorch(boolean)} which
-     * will trigger the change event to the returned {@link LiveData}.
+     * <p>The torch can be turned on and off via {@link #enableTorch(boolean)} which will trigger
+     * the change event to the returned {@link LiveData}.
      *
-     * @return a {@link LiveData} containing current torch state.
+     * @return A {@link LiveData} containing current torch state.
      * @see CameraInfo#getTorchState()
      */
     @NonNull
@@ -2449,7 +2452,7 @@
      * <p>If the value is set before the camera is ready, {@link CameraController} waits for the
      * camera to be ready and then enables the torch.
      *
-     * @param torchEnabled true to turn on the torch, false to turn it off.
+     * @param torchEnabled {@code true} to turn on the torch, {@code false} to turn it off.
      * @return A {@link ListenableFuture} which is successful when the torch was changed to the
      * value specified. It fails when it is unable to change the torch state. Cancellation of
      * this future is a no-op.
@@ -2481,7 +2484,7 @@
      * supported by CameraX. Please see the Javadoc of {@link UseCaseGroup.Builder#addEffect} to
      * see the supported effects combinations.
      *
-     * @param effects the effects applied to camera output.
+     * @param effects The effects applied to camera output.
      * @throws IllegalArgumentException if the combination of effects is not supported by CameraX.
      * @see UseCaseGroup.Builder#addEffect
      */
@@ -2529,10 +2532,10 @@
     }
 
     /**
-     * @param restoreStateRunnable runnable to restore the controller to the previous good state if
-     *                             the binding fails.
+     * @param restoreStateRunnable {@link Runnable} to restore the controller to the previous good
+     *                                            state if the binding fails.
      * @throws IllegalStateException for invalid {@link UseCase} combinations.
-     * @throws RuntimeException      for invalid {@link CameraEffect} combinations.
+     * @throws RuntimeException for invalid {@link CameraEffect} combinations.
      */
     void startCameraAndTrackStates(@Nullable Runnable restoreStateRunnable) {
         try {
@@ -2558,8 +2561,8 @@
     /**
      * Creates {@link UseCaseGroup} from all the use cases.
      *
-     * <p> Preview is required. If it is null, then controller is not ready. Return null and ignore
-     * other use cases.
+     * <p>Preview is required. If it is {@code null}, then controller is not ready. Return
+     * {@code null} and ignore other use cases.
      */
     @Nullable
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -2604,7 +2607,7 @@
     /**
      * Represents the output size of a {@link UseCase}.
      *
-     * <p> This class is a preferred output size to be used with {@link CameraController}. The
+     * <p>This class is a preferred output size to be used with {@link CameraController}. The
      * preferred output size can be based on either resolution or aspect ratio, but not both.
      *
      * @see #setImageAnalysisTargetSize(OutputSize)
@@ -2672,7 +2675,7 @@
         /**
          * Gets the value of resolution.
          *
-         * @return null if the size is not based on resolution.
+         * @return {@code null} if the size is not based on resolution.
          */
         @Nullable
         public Size getResolution() {
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/SurfaceViewImplementation.java b/camera/camera-view/src/main/java/androidx/camera/view/SurfaceViewImplementation.java
index b080483..a510afc 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/SurfaceViewImplementation.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/SurfaceViewImplementation.java
@@ -29,7 +29,6 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -365,7 +364,6 @@
         private Api24Impl() {
         }
 
-        @DoNotInline
         static void pixelCopyRequest(@NonNull SurfaceView source, @NonNull Bitmap dest,
                 @NonNull PixelCopy.OnPixelCopyFinishedListener listener, @NonNull Handler handler) {
             PixelCopy.request(source, dest, listener, handler);
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
index 8a1ebb0..7dff925 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
+++ b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
@@ -24,7 +24,6 @@
 import android.util.Rational
 import android.util.Size
 import android.view.Surface
-import androidx.camera.core.AspectRatio
 import androidx.camera.core.AspectRatio.RATIO_16_9
 import androidx.camera.core.AspectRatio.RATIO_4_3
 import androidx.camera.core.CameraSelector
@@ -86,7 +85,7 @@
     private lateinit var controller: LifecycleCameraController
 
     @Suppress("deprecation")
-    private val targetSizeWithAspectRatio = CameraController.OutputSize(AspectRatio.RATIO_16_9)
+    private val targetSizeWithAspectRatio = CameraController.OutputSize(RATIO_16_9)
     private val resolutionSelector =
         ResolutionSelector.Builder()
             .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY)
@@ -624,8 +623,31 @@
         assertThat(controller.imageCaptureFlashMode).isEqualTo(FLASH_MODE_ON)
     }
 
+    @Suppress("deprecation")
     @Test
-    fun setViewport_overrideUseCasesAspectRatioIfNotSetYet() {
+    fun setResolutionSelectorAndOutputSizeAtTheSameTime() {
+        // Arrange & Act: Set resolution selector and target size together.
+        controller.previewResolutionSelector = resolutionSelector
+        controller.imageCaptureResolutionSelector = resolutionSelector
+        controller.imageAnalysisResolutionSelector = resolutionSelector
+        controller.previewTargetSize = targetSizeWithResolution
+        controller.imageCaptureTargetSize = targetSizeWithResolution
+        controller.imageAnalysisTargetSize = targetSizeWithResolution
+
+        // Assert: The resolution selector should be set, while the target resolution should not.
+        val previewConfig = controller.mPreview.currentConfig as ImageOutputConfig
+        assertThat(previewConfig.resolutionSelector).isEqualTo(resolutionSelector)
+        assertThat(previewConfig.getTargetResolution(null)).isNull()
+        val imageCaptureConfig = controller.mImageCapture.currentConfig as ImageOutputConfig
+        assertThat(imageCaptureConfig.resolutionSelector).isEqualTo(resolutionSelector)
+        assertThat(imageCaptureConfig.getTargetResolution(null)).isNull()
+        val imageAnalysisConfig = controller.mImageAnalysis.currentConfig as ImageOutputConfig
+        assertThat(imageAnalysisConfig.resolutionSelector).isEqualTo(resolutionSelector)
+        assertThat(imageAnalysisConfig.getTargetResolution(null)).isNull()
+    }
+
+    @Test
+    fun setViewport_overrideUseCasesAspectRatio() {
         // Arrange & Act: Set a 16:9 viewport.
         controller.attachPreviewSurface(
             {},
@@ -647,7 +669,7 @@
     }
 
     @Test
-    fun setViewport_notOverrideUseCasesAspectRatioIfAlreadySet() {
+    fun setViewport_notOverrideUseCasesAspectRatioIfResolutionSelectorAlreadySet() {
         // Arrange: Set a 4:3 viewport.
         controller.attachPreviewSurface(
             {},
@@ -672,4 +694,27 @@
             .isNotEqualTo(RATIO_4_3)
         assertThat(controller.mVideoCapture.output.aspectRatio).isNotEqualTo(RATIO_4_3)
     }
+
+    @Suppress("deprecation")
+    @Test
+    fun setViewport_notOverrideUseCasesAspectRatioIfOutputSizeAlreadySet() {
+        // Arrange: Set a 4:3 viewport.
+        controller.attachPreviewSurface(
+            {},
+            ViewPort.Builder(Rational(4, 3), Surface.ROTATION_0).build()
+        )
+
+        // Act: Explicitly set a 16:9 target size.
+        controller.previewTargetSize = targetSizeWithAspectRatio
+        controller.imageCaptureTargetSize = targetSizeWithAspectRatio
+        controller.imageAnalysisTargetSize = targetSizeWithAspectRatio
+
+        // Assert: The resolution selector should not exist in the config.
+        val previewConfig = controller.mPreview.currentConfig as ImageOutputConfig
+        assertThat(previewConfig.getResolutionSelector(null)).isNull()
+        val imageCaptureConfig = controller.mImageCapture.currentConfig as ImageOutputConfig
+        assertThat(imageCaptureConfig.getResolutionSelector(null)).isNull()
+        val imageAnalysisConfig = controller.mImageAnalysis.currentConfig as ImageOutputConfig
+        assertThat(imageAnalysisConfig.getResolutionSelector(null)).isNull()
+    }
 }
diff --git a/camera/camera-viewfinder-compose/build.gradle b/camera/camera-viewfinder-compose/build.gradle
index 38fad1e..1ce6918 100644
--- a/camera/camera-viewfinder-compose/build.gradle
+++ b/camera/camera-viewfinder-compose/build.gradle
@@ -49,6 +49,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.camera.viewfinder.compose"
 
     sourceSets.androidTest.assets.srcDirs +=
@@ -61,6 +62,5 @@
     inceptionYear = "2023"
     mavenVersion = LibraryVersions.CAMERA_VIEWFINDER_COMPOSE
     description = "Composable ViewFinder implementation for CameraX"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-viewfinder-core/build.gradle b/camera/camera-viewfinder-core/build.gradle
index 664b6ab..2fdbc6c 100644
--- a/camera/camera-viewfinder-core/build.gradle
+++ b/camera/camera-viewfinder-core/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.annotation:annotation-experimental:1.4.1")
     implementation(libs.guavaListenableFuture)
     implementation("androidx.core:core:1.7.0")
@@ -54,7 +54,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2023"
     description = "Core dependencies for ViewFinder"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":camera:camera-viewfinder-core:camera-viewfinder-core-samples"))
 }
diff --git a/camera/camera-viewfinder-core/samples/build.gradle b/camera/camera-viewfinder-core/samples/build.gradle
index 8d9dbaf..8666bda 100644
--- a/camera/camera-viewfinder-core/samples/build.gradle
+++ b/camera/camera-viewfinder-core/samples/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(libs.kotlinStdlib)
     implementation(project(":camera:camera-core"))
     implementation(project(":camera:camera-viewfinder-core"))
diff --git a/camera/camera-viewfinder/build.gradle b/camera/camera-viewfinder/build.gradle
index 11a7321..f8a1f30 100644
--- a/camera/camera-viewfinder/build.gradle
+++ b/camera/camera-viewfinder/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(':camera:camera-viewfinder-core'))
     implementation("androidx.annotation:annotation-experimental:1.4.1")
     implementation(libs.guavaListenableFuture)
@@ -83,6 +83,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Standalone Viewfinder for Camera2 and CameraX"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/SurfaceViewImplementation.java b/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/SurfaceViewImplementation.java
index 4704692..93f0bd8 100644
--- a/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/SurfaceViewImplementation.java
+++ b/camera/camera-viewfinder/src/main/java/androidx/camera/viewfinder/SurfaceViewImplementation.java
@@ -26,7 +26,6 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -269,7 +268,6 @@
         private Api24Impl() {
         }
 
-        @DoNotInline
         static void pixelCopyRequest(@NonNull SurfaceView source, @NonNull Bitmap dest,
                 @NonNull PixelCopy.OnPixelCopyFinishedListener listener, @NonNull Handler handler) {
             PixelCopy.request(source, dest, listener, handler);
diff --git a/camera/integration-tests/avsynctestapp/build.gradle b/camera/integration-tests/avsynctestapp/build.gradle
index c64700e..ebd2c4e 100644
--- a/camera/integration-tests/avsynctestapp/build.gradle
+++ b/camera/integration-tests/avsynctestapp/build.gradle
@@ -24,6 +24,8 @@
 android {
     namespace 'androidx.camera.integration.avsync'
 
+    compileSdk 35
+
     defaultConfig {
         applicationId "androidx.camera.integration.avsync"
     }
@@ -41,6 +43,7 @@
 
     // Internal library
     implementation(project(":camera:camera-camera2"))
+    implementation(project(":camera:camera-camera2-pipe-integration"))
     implementation(project(":camera:camera-lifecycle"))
     implementation(project(":camera:camera-video"))
 
@@ -48,6 +51,7 @@
     def compose_version = "1.4.0"
     implementation("androidx.activity:activity-compose:1.5.0")
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation("androidx.compose.ui:ui:$compose_version")
     implementation("androidx.compose.ui:ui-tooling-preview:$compose_version")
     implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
@@ -68,6 +72,8 @@
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
+    androidTestImplementation(project(":concurrent:concurrent-futures"))
+    androidTestImplementation(project(":concurrent:concurrent-futures-ktx"))
     androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
     androidTestImplementation(libs.kotlinCoroutinesTest)
     androidTestImplementation(libs.testExtJunit)
diff --git a/camera/integration-tests/avsynctestapp/lint-baseline.xml b/camera/integration-tests/avsynctestapp/lint-baseline.xml
index 3544f4b..0dd664f0 100644
--- a/camera/integration-tests/avsynctestapp/lint-baseline.xml
+++ b/camera/integration-tests/avsynctestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="RestrictedApiAndroidX"
@@ -23,7 +23,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.i can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="            Logger.i(TAG, &quot;Will not start audio generation, since AudioGenerator is disabled.&quot;)"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/AudioGenerator.kt"/>
     </issue>
@@ -50,7 +50,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.i can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.i(TAG, &quot;start audio generation&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/AudioGenerator.kt"/>
     </issue>
@@ -77,7 +77,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.i can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="            Logger.i(TAG, &quot;Will not stop audio generation, since AudioGenerator is disabled.&quot;)"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/AudioGenerator.kt"/>
     </issue>
@@ -104,7 +104,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.i can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.i(TAG, &quot;stop audio generation&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/AudioGenerator.kt"/>
     </issue>
@@ -185,7 +185,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.i can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="            Logger.i(TAG, &quot;Will not initial audio track, since AudioGenerator is disabled.&quot;)"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/AudioGenerator.kt"/>
     </issue>
@@ -310,8 +310,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="FileUtil.generateVideoFileOutputOptions can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                    generateVideoFileOutputOptions(fileName)"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                recorder.prepareRecording(context, generateVideoFileOutputOptions(fileName))"
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt"/>
     </issue>
@@ -319,8 +319,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="FileUtil.generateVideoFileOutputOptions can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                    generateVideoFileOutputOptions(fileName)"
-        errorLine2="                                                   ~~~~~~~~">
+        errorLine1="                recorder.prepareRecording(context, generateVideoFileOutputOptions(fileName))"
+        errorLine2="                                                                                  ~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt"/>
     </issue>
@@ -347,7 +347,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.d(TAG, &quot;Start signal generation.&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt"/>
     </issue>
@@ -374,7 +374,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.d(TAG, &quot;Stop signal generation.&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt"/>
     </issue>
@@ -401,7 +401,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.d(TAG, &quot;Start recording.&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt"/>
     </issue>
@@ -428,7 +428,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.d(TAG, &quot;Stop recording.&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt"/>
     </issue>
@@ -455,7 +455,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.d(TAG, &quot;Pause recording.&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt"/>
     </issue>
@@ -482,7 +482,7 @@
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
         errorLine1="        Logger.d(TAG, &quot;Resume recording.&quot;)"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~">
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt"/>
     </issue>
@@ -508,8 +508,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable res with type List&lt;Double>: replace with FloatList"
-        errorLine1="        val res = mutableListOf&lt;Double>()"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            val res = mutableListOf&lt;Double>()"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/AudioGenerator.kt"/>
     </issue>
@@ -517,8 +517,8 @@
     <issue
         id="PrimitiveInCollection"
         message="method toSamples has parameter data with type List&lt;Double>: replace with FloatList"
-        errorLine1="        data: List&lt;Double>,"
-        errorLine2="              ~~~~~~~~~~~~">
+        errorLine1="    suspend fun toSamples(data: List&lt;Double>, sampleWidth: Int): List&lt;Byte> ="
+        errorLine2="                                ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/avsync/model/AudioGenerator.kt"/>
     </issue>
diff --git a/camera/integration-tests/avsynctestapp/src/androidTest/java/androidx/camera/integration/avsync/SignalGeneratorViewModelTest.kt b/camera/integration-tests/avsynctestapp/src/androidTest/java/androidx/camera/integration/avsync/SignalGeneratorViewModelTest.kt
index 593f415..e81de28 100644
--- a/camera/integration-tests/avsynctestapp/src/androidTest/java/androidx/camera/integration/avsync/SignalGeneratorViewModelTest.kt
+++ b/camera/integration-tests/avsynctestapp/src/androidTest/java/androidx/camera/integration/avsync/SignalGeneratorViewModelTest.kt
@@ -36,6 +36,7 @@
 import android.os.Build
 import androidx.camera.camera2.Camera2Config
 import androidx.camera.core.CameraSelector
+import androidx.camera.integration.avsync.model.CameraHelper.Companion.CameraImplementation
 import androidx.camera.testing.impl.CameraUtil
 import androidx.camera.testing.impl.CameraXUtil
 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
@@ -127,7 +128,7 @@
     fun initialRecorder_canMakeRecorderReady(): Unit = runBlocking {
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
 
-        viewModel.initialRecorder(context, lifecycleOwner)
+        viewModel.initialRecorder(context, lifecycleOwner, CameraImplementation.CAMERA2)
 
         assertThat(viewModel.isRecorderReady).isTrue()
     }
@@ -176,7 +177,7 @@
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
 
         // Arrange.
-        viewModel.initialRecorder(context, lifecycleOwner)
+        viewModel.initialRecorder(context, lifecycleOwner, CameraImplementation.CAMERA2)
 
         assertThat(viewModel.isRecorderReady).isTrue()
 
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/MainActivity.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/MainActivity.kt
index fd27ccc..5d3f7e0 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/MainActivity.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/MainActivity.kt
@@ -16,24 +16,34 @@
 
 package androidx.camera.integration.avsync
 
+import android.annotation.SuppressLint
 import android.os.Build
 import android.os.Bundle
 import android.view.WindowManager
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import androidx.annotation.DoNotInline
+import androidx.annotation.OptIn
 import androidx.annotation.RequiresApi
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.integration.avsync.model.CameraHelper.Companion.CameraImplementation
+import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration
+import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.compose.material.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.core.util.Preconditions
 
 private const val KEY_BEEP_FREQUENCY = "beep_frequency"
 private const val KEY_BEEP_ENABLED = "beep_enabled"
+private const val KEY_CAMERA_IMPLEMENTATION = "camera_implementation"
 private const val DEFAULT_BEEP_FREQUENCY = 1500
 private const val DEFAULT_BEEP_ENABLED = true
+private const val CAMERA_IMPLEMENTATION_CAMERA2 = "camera2"
+private const val CAMERA_IMPLEMENTATION_CAMERA_PIPE = "camera_pipe"
 private const val MIN_SCREEN_BRIGHTNESS = 0F
 private const val MAX_SCREEN_BRIGHTNESS = 1F
 private const val DEFAULT_SCREEN_BRIGHTNESS = 0.8F
+private val DEFAULT_CAMERA_IMPLEMENTATION = CameraImplementation.CAMERA2
 
 class MainActivity : ComponentActivity() {
 
@@ -42,7 +52,15 @@
 
         handleScreenLock()
         setScreenBrightness()
-        setContent { App(getBeepFrequency(), getBeepEnabled()) }
+
+        // Config CameraX.
+        val cameraImplementation = getCameraImplementation()
+        if (!isCameraXConfigured) {
+            isCameraXConfigured = true
+            configureCameraProvider(cameraImplementation)
+        }
+
+        setContent { App(getBeepFrequency(), getBeepEnabled(), cameraImplementation) }
     }
 
     private fun handleScreenLock() {
@@ -78,6 +96,28 @@
         return intent.getBooleanExtra(KEY_BEEP_ENABLED, DEFAULT_BEEP_ENABLED)
     }
 
+    private fun getCameraImplementation(): CameraImplementation {
+        val implementation = intent.getStringExtra(KEY_CAMERA_IMPLEMENTATION)
+        return if (CAMERA_IMPLEMENTATION_CAMERA2.equals(implementation, true)) {
+            CameraImplementation.CAMERA2
+        } else if (CAMERA_IMPLEMENTATION_CAMERA_PIPE.equals(implementation, true)) {
+            CameraImplementation.CAMERA_PIPE
+        } else {
+            DEFAULT_CAMERA_IMPLEMENTATION
+        }
+    }
+
+    @SuppressLint("NullAnnotationGroup")
+    @OptIn(ExperimentalCameraProviderConfiguration::class)
+    private fun configureCameraProvider(cameraImplementation: CameraImplementation) {
+        val config =
+            when (cameraImplementation) {
+                CameraImplementation.CAMERA2 -> Camera2Config.defaultConfig()
+                CameraImplementation.CAMERA_PIPE -> CameraPipeConfig.defaultConfig()
+            }
+        ProcessCameraProvider.configureInstance(config)
+    }
+
     private fun setScreenBrightness(brightness: Float = DEFAULT_SCREEN_BRIGHTNESS) {
         Preconditions.checkArgument(brightness in MIN_SCREEN_BRIGHTNESS..MAX_SCREEN_BRIGHTNESS)
 
@@ -88,17 +128,19 @@
 
     @RequiresApi(27)
     private object Api27Impl {
-        @DoNotInline
         fun setShowWhenLocked(activity: ComponentActivity, showWhenLocked: Boolean) =
             activity.setShowWhenLocked(showWhenLocked)
 
-        @DoNotInline
         fun setTurnScreenOn(activity: ComponentActivity, turnScreenOn: Boolean) =
             activity.setTurnScreenOn(turnScreenOn)
     }
+
+    companion object {
+        private var isCameraXConfigured: Boolean = false
+    }
 }
 
 @Composable
-fun App(beepFrequency: Int, beepEnabled: Boolean) {
-    MaterialTheme { SignalGeneratorScreen(beepFrequency, beepEnabled) }
+fun App(beepFrequency: Int, beepEnabled: Boolean, cameraImplementation: CameraImplementation) {
+    MaterialTheme { SignalGeneratorScreen(beepFrequency, beepEnabled, cameraImplementation) }
 }
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
index 5263698..3e1f767 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorScreen.kt
@@ -16,6 +16,7 @@
 
 package androidx.camera.integration.avsync
 
+import androidx.camera.integration.avsync.model.CameraHelper.Companion.CameraImplementation
 import androidx.camera.integration.avsync.ui.theme.LightOff
 import androidx.camera.integration.avsync.ui.theme.LightOn
 import androidx.camera.integration.avsync.ui.widget.AdvancedFloatingActionButton
@@ -48,13 +49,14 @@
 fun SignalGeneratorScreen(
     beepFrequency: Int,
     beepEnabled: Boolean,
+    cameraImplementation: CameraImplementation,
     viewModel: SignalGeneratorViewModel = viewModel()
 ) {
     val context = LocalContext.current
     val lifecycleOwner = LocalLifecycleOwner.current
 
     LaunchedEffect(true) {
-        viewModel.initialRecorder(context, lifecycleOwner)
+        viewModel.initialRecorder(context, lifecycleOwner, cameraImplementation)
         viewModel.initialSignalGenerator(context, beepFrequency, beepEnabled)
     }
 
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
index a859158..41b45e4 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/SignalGeneratorViewModel.kt
@@ -22,6 +22,7 @@
 import androidx.camera.core.Logger
 import androidx.camera.integration.avsync.model.AudioGenerator
 import androidx.camera.integration.avsync.model.CameraHelper
+import androidx.camera.integration.avsync.model.CameraHelper.Companion.CameraImplementation
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -53,7 +54,7 @@
 
     private var signalGenerationJob: Job? = null
     private lateinit var audioGenerator: AudioGenerator
-    private val cameraHelper = CameraHelper()
+    private lateinit var cameraHelper: CameraHelper
     private lateinit var audioManager: AudioManager
     private var originalVolume: Int = 0
 
@@ -75,7 +76,12 @@
     var isPaused: Boolean by mutableStateOf(false)
         private set
 
-    suspend fun initialRecorder(context: Context, lifecycleOwner: LifecycleOwner) {
+    suspend fun initialRecorder(
+        context: Context,
+        lifecycleOwner: LifecycleOwner,
+        cameraImplementation: CameraImplementation
+    ) {
+        cameraHelper = CameraHelper(cameraImplementation)
         withContext(Dispatchers.Main) {
             isRecorderReady = cameraHelper.bindCamera(context, lifecycleOwner)
         }
diff --git a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt
index 0b23d70..b53de02 100644
--- a/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt
+++ b/camera/integration-tests/avsynctestapp/src/main/java/androidx/camera/integration/avsync/model/CameraHelper.kt
@@ -25,9 +25,11 @@
 import android.util.Range
 import androidx.annotation.MainThread
 import androidx.annotation.OptIn
-import androidx.camera.camera2.interop.Camera2CameraInfo
-import androidx.camera.camera2.interop.Camera2Interop
+import androidx.camera.camera2.interop.Camera2CameraInfo as C2CameraInfo
+import androidx.camera.camera2.interop.Camera2Interop as C2Interop
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop
+import androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo as CPCameraInfo
+import androidx.camera.camera2.pipe.integration.interop.Camera2Interop as CPInterop
 import androidx.camera.core.CameraInfo
 import androidx.camera.core.CameraSelector
 import androidx.camera.lifecycle.ProcessCameraProvider
@@ -46,7 +48,7 @@
 
 private const val TAG = "CameraHelper"
 
-class CameraHelper {
+class CameraHelper(private val cameraImplementation: CameraImplementation) {
 
     private val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
     private var videoCapture: VideoCapture<Recorder>? = null
@@ -99,40 +101,71 @@
         activeRecording!!.resume()
     }
 
-    companion object {
-        private val FPS_30 = Range(30, 30)
-
-        private fun createVideoCapture(cameraInfo: CameraInfo): VideoCapture<Recorder> {
-            val recorder = Recorder.Builder().build()
-            val videoCaptureBuilder = VideoCapture.Builder<Recorder>(recorder)
-            if (isLegacyDevice(cameraInfo)) {
-                // Set target FPS to 30 on legacy devices. Legacy devices use lower FPS to
-                // workaround exposure issues, but this makes the video timestamp info become
-                // fewer and causes A/V sync test to false alarm. See AeFpsRangeLegacyQuirk.
-                videoCaptureBuilder.setTargetFpsRange(FPS_30)
-            }
-
-            return videoCaptureBuilder.build()
+    private fun createVideoCapture(cameraInfo: CameraInfo): VideoCapture<Recorder> {
+        val recorder = Recorder.Builder().build()
+        val videoCaptureBuilder = VideoCapture.Builder<Recorder>(recorder)
+        if (isLegacyDevice(cameraInfo, cameraImplementation)) {
+            // Set target FPS to 30 on legacy devices. Legacy devices use lower FPS to
+            // workaround exposure issues, but this makes the video timestamp info become
+            // fewer and causes A/V sync test to false alarm. See AeFpsRangeLegacyQuirk.
+            videoCaptureBuilder.setTargetFpsRange(FPS_30, cameraImplementation)
         }
 
+        return videoCaptureBuilder.build()
+    }
+
+    companion object {
+        enum class CameraImplementation {
+            CAMERA2,
+            CAMERA_PIPE
+        }
+
+        private val FPS_30 = Range(30, 30)
+
         @SuppressLint("NullAnnotationGroup")
         @OptIn(ExperimentalCamera2Interop::class)
+        @kotlin.OptIn(
+            androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop::class
+        )
         private fun VideoCapture.Builder<Recorder>.setTargetFpsRange(
-            range: Range<Int>
+            range: Range<Int>,
+            cameraImplementation: CameraImplementation
         ): VideoCapture.Builder<Recorder> {
             Log.d(TAG, "Set target fps to $range")
+            when (cameraImplementation) {
+                CameraImplementation.CAMERA2 -> {
+                    C2Interop.Extender(this)
+                        .setCaptureRequestOption(CONTROL_AE_TARGET_FPS_RANGE, range)
+                }
+                CameraImplementation.CAMERA_PIPE -> {
+                    CPInterop.Extender(this)
+                        .setCaptureRequestOption(CONTROL_AE_TARGET_FPS_RANGE, range)
+                }
+            }
 
-            val camera2InterOp = Camera2Interop.Extender(this)
-            camera2InterOp.setCaptureRequestOption(CONTROL_AE_TARGET_FPS_RANGE, range)
             return this
         }
 
         @SuppressLint("NullAnnotationGroup")
         @OptIn(ExperimentalCamera2Interop::class)
-        private fun isLegacyDevice(cameraInfo: CameraInfo): Boolean {
-            return Camera2CameraInfo.from(cameraInfo)
-                .getCameraCharacteristic(INFO_SUPPORTED_HARDWARE_LEVEL) ==
-                INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
+        @kotlin.OptIn(
+            androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop::class
+        )
+        private fun isLegacyDevice(
+            cameraInfo: CameraInfo,
+            cameraImplementation: CameraImplementation
+        ): Boolean {
+            val hardwareLevel =
+                when (cameraImplementation) {
+                    CameraImplementation.CAMERA2 ->
+                        C2CameraInfo.from(cameraInfo)
+                            .getCameraCharacteristic(INFO_SUPPORTED_HARDWARE_LEVEL)
+                    CameraImplementation.CAMERA_PIPE ->
+                        CPCameraInfo.from(cameraInfo)
+                            .getCameraCharacteristic(INFO_SUPPORTED_HARDWARE_LEVEL)
+                }
+
+            return hardwareLevel == INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
         }
 
         private fun prepareRecording(context: Context, recorder: Recorder): PendingRecording {
@@ -152,7 +185,7 @@
         }
 
         private fun generateVideoRecordEventListener(): Consumer<VideoRecordEvent> {
-            return Consumer<VideoRecordEvent> { videoRecordEvent ->
+            return Consumer { videoRecordEvent ->
                 if (videoRecordEvent is VideoRecordEvent.Finalize) {
                     val uri = videoRecordEvent.outputResults.outputUri
                     if (videoRecordEvent.error == VideoRecordEvent.Finalize.ERROR_NONE) {
diff --git a/camera/integration-tests/camerapipetestapp/lint-baseline.xml b/camera/integration-tests/camerapipetestapp/lint-baseline.xml
index 88e59e8..6331700 100644
--- a/camera/integration-tests/camerapipetestapp/lint-baseline.xml
+++ b/camera/integration-tests/camerapipetestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -11,15 +11,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.camera.integration.camera2.pipe.CameraPipeApplication is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val elapsedRealtime = Process.getStartElapsedRealtime()"
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/camera/integration/camera2/pipe/CameraPipeApplication.kt"/>
-    </issue>
-
-    <issue
         id="PermissionImpliesUnsupportedChromeOsHardware"
         message="Permission exists without corresponding hardware `&lt;uses-feature android:name=&quot;android.hardware.camera&quot; required=&quot;false&quot;>` tag"
         errorLine1="    &lt;uses-permission android:name=&quot;android.permission.CAMERA&quot; />"
diff --git a/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt b/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt
index 90768a5..87de31e 100644
--- a/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt
+++ b/camera/integration-tests/camerapipetestapp/src/main/java/androidx/camera/integration/camera2/pipe/SimpleCamera.kt
@@ -29,7 +29,6 @@
 import android.util.Range
 import android.util.Size
 import android.view.Surface
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
@@ -320,7 +319,7 @@
                 }
             check(cameraIds.size == configs.size)
 
-            val cameraGraphs = cameraPipe.createCameraGraphs(configs)
+            val cameraGraphs = cameraPipe.createCameraGraphs(CameraGraph.ConcurrentConfig(configs))
 
             val viewfinderStreams =
                 cameraGraphs.zip(viewfinderSteamConfigs).map { (cameraGraph, viewfinderStreamConfig)
@@ -410,7 +409,6 @@
 
     @RequiresApi(Build.VERSION_CODES.Q)
     private object Api29CompatImpl {
-        @DoNotInline
         fun newImageReaderInstance(
             width: Int,
             height: Int,
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CoroutineCopyEffect.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CoroutineCopyEffect.kt
index 08c2044..6d84239 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CoroutineCopyEffect.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/CoroutineCopyEffect.kt
@@ -25,7 +25,6 @@
 import androidx.camera.core.SurfaceProcessor
 import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.processing.OpenGlRenderer
-import androidx.camera.core.processing.ShaderProvider
 import androidx.camera.core.processing.util.GLUtils.InputFormat
 import java.util.concurrent.Executors
 import kotlinx.atomicfu.atomic
@@ -70,7 +69,7 @@
                 val newDynamicRange = surfaceRequest.dynamicRange
                 if (dynamicRange != newDynamicRange) {
                     dynamicRange?.let { glRenderer.release() }
-                    glRenderer.init(newDynamicRange, ShaderProvider.DEFAULT)
+                    glRenderer.init(newDynamicRange)
                     dynamicRange = newDynamicRange
                 }
 
diff --git a/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp b/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp
index 33b4d9f..250bc53 100644
--- a/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp
+++ b/camera/integration-tests/coretestapp/src/main/cpp/opengl_renderer_jni.cpp
@@ -145,8 +145,6 @@
         RENDERER_DYN_RNG_HDR_HLG = 3  // Equivalent to DynamicRange.ENCODING_HLG
     };
 
-    constexpr GLint EGL_GL_COLORSPACE_BT2020_HLG_EXT = 0x3540;
-
     constexpr char VERTEX_SHADER_SRC[] = R"SRC(
       attribute vec4 position;
       attribute vec4 texCoords;
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index 62f607a..457f05d 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -91,7 +91,6 @@
 
 import androidx.activity.result.ActivityResultLauncher;
 import androidx.activity.result.contract.ActivityResultContracts;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -2757,7 +2756,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setColorMode(@NonNull Window window, int colorMode) {
             window.setColorMode(colorMode);
         }
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXService.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXService.java
index 7102352..50a926c 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXService.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXService.java
@@ -49,7 +49,6 @@
 import android.widget.RemoteViews;
 import android.widget.Toast;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -537,14 +536,12 @@
         }
 
         /** @noinspection SameParameterValue */
-        @DoNotInline
         @NonNull
         static NotificationChannel newNotificationChannel(@NonNull String id,
                 @NonNull CharSequence name, int importance) {
             return new NotificationChannel(id, name, importance);
         }
 
-        @DoNotInline
         static void createNotificationChannel(@NonNull NotificationManager manager,
                 @NonNull NotificationChannel channel) {
             manager.createNotificationChannel(channel);
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java
index 9a3ec50..35a4e1f 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java
@@ -79,6 +79,8 @@
 import androidx.camera.core.MirrorMode;
 import androidx.camera.core.Preview;
 import androidx.camera.core.UseCaseGroup;
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
 import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration;
 import androidx.camera.lifecycle.ProcessCameraProvider;
 import androidx.camera.video.ExperimentalPersistentRecording;
@@ -174,7 +176,7 @@
         mDualRecordButton = findViewById(R.id.dual_record);
 
         Recorder recorder = new Recorder.Builder()
-                .setQualitySelector(QualitySelector.from(Quality.HD))
+                .setQualitySelector(QualitySelector.from(Quality.FHD))
                 .build();
         mVideoCapture = new VideoCapture.Builder<>(recorder)
                 .setMirrorMode(MirrorMode.MIRROR_MODE_ON_FRONT_ONLY)
@@ -464,7 +466,13 @@
                 mFrontPreviewViewForPip.removeAllViews();
                 mFrontPreviewViewForPip.addView(mSinglePreviewView);
                 mBackPreviewViewForPip.setVisibility(GONE);
+
+                ResolutionSelector resolutionSelector = new ResolutionSelector.Builder()
+                        .setAspectRatioStrategy(
+                                AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY)
+                        .build();
                 Preview preview = new Preview.Builder()
+                        .setResolutionSelector(resolutionSelector)
                         .build();
                 preview.setSurfaceProvider(mSinglePreviewView.getSurfaceProvider());
                 UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLActivity.java
index 842fee3..c7412b4 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/OpenGLActivity.java
@@ -43,7 +43,6 @@
 import androidx.activity.result.ActivityResultCallback;
 import androidx.activity.result.ActivityResultLauncher;
 import androidx.activity.result.contract.ActivityResultContracts;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -354,7 +353,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Set<DynamicRange> getHighDynamicRangesSupportedByDisplay(
                 @NonNull Display display) {
             Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities();
@@ -377,7 +375,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Display getDisplay(ContextWrapper contextWrapper) {
             return contextWrapper.getDisplay();
         }
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/SurfaceViewRenderSurface.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/SurfaceViewRenderSurface.java
index a7893ae..ae75152 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/SurfaceViewRenderSurface.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/SurfaceViewRenderSurface.java
@@ -27,7 +27,6 @@
 import android.view.SurfaceView;
 import android.view.ViewStub;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -208,7 +207,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static SurfaceControl createSurfaceControl(int format, int width, int height) {
             return new SurfaceControl.Builder()
                     .setName("ChildSurfaceControl")
@@ -217,17 +215,14 @@
                     .build();
         }
 
-        @DoNotInline
         static Surface createSurface(SurfaceControl from) {
             return new Surface(from);
         }
 
-        @DoNotInline
         static SurfaceControl getSurfaceControl(SurfaceView surfaceView) {
             return surfaceView.getSurfaceControl();
         }
 
-        @DoNotInline
         static void reparent(SurfaceControl sc, SurfaceControl newParent) {
             SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
             transaction.reparent(sc, newParent).setVisibility(sc, true).apply();
diff --git a/camera/integration-tests/extensionstestapp/lint-baseline.xml b/camera/integration-tests/extensionstestapp/lint-baseline.xml
index 1f2a8e8..5899a3e 100644
--- a/camera/integration-tests/extensionstestapp/lint-baseline.xml
+++ b/camera/integration-tests/extensionstestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="RestrictedApiAndroidX"
@@ -76,8 +76,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.mainThreadExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                CameraXExecutors.mainThreadExecutor()"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~">
+        errorLine1="            Futures.addCallback(future, evFutureCallback, CameraXExecutors.mainThreadExecutor())"
+        errorLine2="                                                                           ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/extensions/validation/ImageCaptureActivity.kt"/>
     </issue>
@@ -85,8 +85,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.mainThreadExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                CameraXExecutors.mainThreadExecutor()"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~">
+        errorLine1="            Futures.addCallback(future, evFutureCallback, CameraXExecutors.mainThreadExecutor())"
+        errorLine2="                                                                           ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/extensions/validation/ImageCaptureActivity.kt"/>
     </issue>
diff --git a/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml b/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml
index b1a60cf..6bc33ee 100644
--- a/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml
+++ b/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml
@@ -83,6 +83,16 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+
+        <activity
+            android:name=".proguard.ReleaseTestActivity"
+            android:configChanges="orientation|keyboardHidden|screenSize"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="androidx.camera.integration.extensions.release_test" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
 
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
index 1e14622..5ca6d34 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Camera2ExtensionsActivity.kt
@@ -57,7 +57,6 @@
 import android.widget.Switch
 import android.widget.TextView
 import android.widget.Toast
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.appcompat.app.AppCompatActivity
@@ -1432,7 +1431,6 @@
     @RequiresApi(33)
     private object ZoomUtilExtensions {
         @JvmStatic
-        @DoNotInline
         fun hasZoomSupport(
             cameraId: String,
             cameraManager: CameraManager,
@@ -1446,7 +1444,6 @@
 
     @RequiresApi(31)
     private object ZoomUtil {
-        @DoNotInline
         fun hasZoomSupport(cameraId: String, cameraManager: CameraManager): Boolean {
             val characteristics = cameraManager.getCameraCharacteristics(cameraId)
             val availableCaptureRequestKeys = characteristics.availableCaptureRequestKeys
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index 88439b2..31bb389 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -24,10 +24,12 @@
 import static androidx.camera.core.ImageCapture.ERROR_UNKNOWN;
 import static androidx.camera.integration.extensions.CameraDirection.BACKWARD;
 import static androidx.camera.integration.extensions.CameraDirection.FORWARD;
+import static androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_CAMERA_IMPLEMENTATION;
 import static androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_KEY_CAMERA_DIRECTION;
 import static androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_KEY_CAMERA_ID;
 import static androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_KEY_DELETE_CAPTURED_IMAGE;
 import static androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_KEY_EXTENSION_MODE;
+import static androidx.camera.integration.extensions.utils.PermissionUtil.setupPermissions;
 import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_DURATION_LIMIT_REACHED;
 import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_FILE_SIZE_LIMIT_REACHED;
 import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_INSUFFICIENT_STORAGE;
@@ -37,13 +39,10 @@
 
 import static java.util.concurrent.TimeUnit.NANOSECONDS;
 
-import android.Manifest;
 import android.annotation.SuppressLint;
 import android.content.ContentValues;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CaptureRequest;
@@ -55,6 +54,7 @@
 import android.os.SystemClock;
 import android.provider.MediaStore;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -131,7 +131,6 @@
 
     private static final String TAG = "CameraExtensionActivity";
     private static final int PERMISSIONS_REQUEST_CODE = 42;
-    public static final String INTENT_EXTRA_CAMERA_IMPLEMENTATION = "camera_implementation";
     public static final String CAMERA2_IMPLEMENTATION_OPTION = "camera2";
     public static final String CAMERA_PIPE_IMPLEMENTATION_OPTION = "camera_pipe";
 
@@ -618,7 +617,10 @@
         setupPinchToZoomAndTapToFocus(mPreviewView);
         String cameraImplementation =
                 getIntent().getStringExtra(INTENT_EXTRA_CAMERA_IMPLEMENTATION);
-        Futures.addCallback(setupPermissions(), new FutureCallback<Boolean>() {
+        Pair<ListenableFuture<Boolean>, CallbackToFutureAdapter.Completer<Boolean>>
+                futureCompleter = setupPermissions(this);
+        mPermissionCompleter = futureCompleter.second;
+        Futures.addCallback(futureCompleter.first, new FutureCallback<Boolean>() {
             @Override
             public void onSuccess(@Nullable Boolean result) {
                 mPermissionsGranted = Preconditions.checkNotNull(result);
@@ -807,76 +809,6 @@
         });
     }
 
-    private ListenableFuture<Boolean> setupPermissions() {
-        return CallbackToFutureAdapter.getFuture(completer -> {
-            mPermissionCompleter = completer;
-            if (!allPermissionsGranted()) {
-                makePermissionRequest();
-            } else {
-                mPermissionCompleter.set(true);
-            }
-
-            return "get_permissions";
-        });
-    }
-
-    private void makePermissionRequest() {
-        ActivityCompat.requestPermissions(this, getRequiredPermissions(), PERMISSIONS_REQUEST_CODE);
-    }
-
-    /** Returns true if all the necessary permissions have been granted already. */
-    private boolean allPermissionsGranted() {
-        for (String permission : getRequiredPermissions()) {
-            if (ContextCompat.checkSelfPermission(this, permission)
-                    != PackageManager.PERMISSION_GRANTED) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** Tries to acquire all the necessary permissions through a dialog. */
-    @SuppressWarnings("deprecation")
-    private String[] getRequiredPermissions() {
-        PackageInfo info;
-        try {
-            info = getPackageManager().getPackageInfo(getPackageName(),
-                    PackageManager.GET_PERMISSIONS);
-        } catch (NameNotFoundException exception) {
-            Log.e(TAG, "Failed to obtain all required permissions.", exception);
-            return new String[0];
-        }
-
-        if (info.requestedPermissions == null || info.requestedPermissions.length == 0) {
-            return new String[0];
-        }
-
-        List<String> requiredPermissions = new ArrayList<>();
-
-        // From Android T, skips the permission check of WRITE_EXTERNAL_STORAGE since it won't be
-        // granted any more. When querying the requested permissions from PackageManager,
-        // READ_EXTERNAL_STORAGE will also be included if we specify WRITE_EXTERNAL_STORAGE
-        // requirement in AndroidManifest.xml. Therefore, also need to skip the permission check
-        // of READ_EXTERNAL_STORAGE.
-        for (String permission : info.requestedPermissions) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (
-                    Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)
-                            || Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission))) {
-                continue;
-            }
-
-            requiredPermissions.add(permission);
-        }
-
-        String[] permissions = requiredPermissions.toArray(new String[0]);
-
-        if (permissions.length > 0) {
-            return permissions;
-        } else {
-            return new String[0];
-        }
-    }
-
     @Nullable
     public Preview getPreview() {
         return mPreview;
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Constants.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Constants.kt
index 89bb808..7a03b48 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Constants.kt
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/Constants.kt
@@ -25,8 +25,14 @@
 /** Invalid lens facing */
 const val INVALID_LENS_FACING = -1
 
+/** Permissions request code */
+const val PERMISSIONS_REQUEST_CODE = 42
+
 /** Intent extra keys to pass necessary information between the caller and callee activities. */
 object IntentExtraKey {
+    /** Launches the activity with the specified CameraX implementation. */
+    const val INTENT_EXTRA_CAMERA_IMPLEMENTATION = "camera_implementation"
+
     /**
      * Launches the activity with the specified direction of camera.
      *
@@ -65,7 +71,7 @@
     const val INTENT_EXTRA_KEY_IMAGE_URI = "ImageUri"
 
     /**
-     * Used to pass the rotation degrees fo the captured image to the caller activity to show the
+     * Used to pass the rotation degrees of the captured image to the caller activity to show the
      * image in correct orientation.
      */
     const val INTENT_EXTRA_KEY_IMAGE_ROTATION_DEGREES = "ImageRotationDegrees"
@@ -75,6 +81,20 @@
 
     /** Used to pass the error code to the caller activity. */
     const val INTENT_EXTRA_KEY_ERROR_CODE = "ErrorCode"
+
+    /** Used to pass the running mode to check. The valid values are debug and release. */
+    const val INTENT_EXTRA_RUNNING_MODE_CHECK = "running_mode_check"
+
+    /** Used to pass the result error message. */
+    const val INTENT_EXTRA_RESULT_ERROR_MESSAGE = "result_error_message"
+}
+
+/** Implementation options */
+object ImplementationOption {
+    /** CameraX implementation option */
+    const val CAMERA2_IMPLEMENTATION_OPTION: String = "camera2"
+    /** Camera-pipe implementation option */
+    const val CAMERA_PIPE_IMPLEMENTATION_OPTION: String = "camera_pipe"
 }
 
 /** Camera directions */
@@ -129,3 +149,21 @@
     /** All tests have been run and some items are failed */
     const val TEST_RESULT_FAILED = 3
 }
+
+/** Request result error codes for release apk test. */
+object RequestResultErrorCode {
+    /** None error happens. */
+    const val RESULT_SUCCESS = 0
+    /** Running mode (debug or release) incorrect. */
+    const val RESULT_ERROR_RUNNING_MODE_INCORRECT = 1
+    /** Permission requirements are not satisfied. */
+    const val RESULT_ERROR_PERMISSION_NOT_SATISFIED = 2
+    /** Failed to retrieve ExtensionsManager. */
+    const val RESULT_ERROR_FAILED_TO_RETRIEVE_EXTENSIONS_MANAGER = 3
+    /** Target testing extension mode is not supported on the target camera device. */
+    const val RESULT_ERROR_EXTENSION_MOD_NOT_SUPPORTED = 4
+    /** Incorrect camera implementation. */
+    const val RESULT_ERROR_INCORRECT_CAMERA_IMPLEMENTATION = 5
+    /** Failed to take a picture. */
+    const val RESULT_ERROR_TAKE_PICTURE_FAILED = 6
+}
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/proguard/ReleaseTestActivity.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/proguard/ReleaseTestActivity.kt
new file mode 100644
index 0000000..5791b24
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/proguard/ReleaseTestActivity.kt
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.extensions.proguard
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.StrictMode
+import android.os.StrictMode.VmPolicy
+import android.util.Log
+import android.view.ViewStub
+import android.widget.Toast
+import androidx.annotation.OptIn
+import androidx.appcompat.app.AppCompatActivity
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
+import androidx.camera.core.Camera
+import androidx.camera.core.CameraInfo
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCaptureException
+import androidx.camera.core.ImageProxy
+import androidx.camera.core.Preview
+import androidx.camera.extensions.ExtensionMode
+import androidx.camera.extensions.ExtensionsManager
+import androidx.camera.integration.extensions.ExtensionsApplication
+import androidx.camera.integration.extensions.ImplementationOption.CAMERA2_IMPLEMENTATION_OPTION
+import androidx.camera.integration.extensions.ImplementationOption.CAMERA_PIPE_IMPLEMENTATION_OPTION
+import androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_CAMERA_IMPLEMENTATION
+import androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_KEY_CAMERA_ID
+import androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_KEY_EXTENSION_MODE
+import androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_RESULT_ERROR_MESSAGE
+import androidx.camera.integration.extensions.IntentExtraKey.INTENT_EXTRA_RUNNING_MODE_CHECK
+import androidx.camera.integration.extensions.PERMISSIONS_REQUEST_CODE
+import androidx.camera.integration.extensions.R
+import androidx.camera.integration.extensions.RequestResultErrorCode.RESULT_ERROR_EXTENSION_MOD_NOT_SUPPORTED
+import androidx.camera.integration.extensions.RequestResultErrorCode.RESULT_ERROR_FAILED_TO_RETRIEVE_EXTENSIONS_MANAGER
+import androidx.camera.integration.extensions.RequestResultErrorCode.RESULT_ERROR_INCORRECT_CAMERA_IMPLEMENTATION
+import androidx.camera.integration.extensions.RequestResultErrorCode.RESULT_ERROR_PERMISSION_NOT_SATISFIED
+import androidx.camera.integration.extensions.RequestResultErrorCode.RESULT_ERROR_RUNNING_MODE_INCORRECT
+import androidx.camera.integration.extensions.RequestResultErrorCode.RESULT_ERROR_TAKE_PICTURE_FAILED
+import androidx.camera.integration.extensions.RequestResultErrorCode.RESULT_SUCCESS
+import androidx.camera.integration.extensions.utils.CameraSelectorUtil.createCameraSelectorById
+import androidx.camera.integration.extensions.utils.PermissionUtil
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.lifecycle.ProcessCameraProvider.Companion.getInstance
+import androidx.camera.view.PreviewView
+import androidx.concurrent.futures.CallbackToFutureAdapter
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import com.google.common.base.Preconditions
+import com.google.common.util.concurrent.FutureCallback
+import com.google.common.util.concurrent.Futures
+
+private const val TAG = "ReleaseTestActivity"
+// Stage that is waiting for the preview stream state becoming STREAMING.
+private const val STAGE_WAIT_FOR_PREVIEW_STREAMING = 0
+// Stage that is waiting for a still image is captured and saved successfully.
+private const val STAGE_WAIT_FOR_STILL_IMAGE_CAPTURE = 1
+
+/**
+ * An activity is specially implemented for release apk test.
+ *
+ * After the activity is launched, the following steps will be executed:
+ * <ul>
+ * <li> Checks whether the target running mode is correct if it is specified.
+ * <li> Checks whether the target extension mode is supported for the target camera device.
+ * <li> Wait for the preview STREAMING state after binding Preview and ImageCapture UseCases.
+ * <li> Wait for still image capture result after preview becomes STREAMING state.
+ * <li> Finish the activity with RESULT_ERROR_NONE if all above steps can run successfully.
+ * </ul>
+ */
+class ReleaseTestActivity : AppCompatActivity() {
+    private var permissionsGranted: Boolean = false
+    private lateinit var permissionCompleter: CallbackToFutureAdapter.Completer<Boolean>
+
+    private var cameraImplementation: String = CAMERA2_IMPLEMENTATION_OPTION
+    // When the retrieved implementation is incorrect, one time retry will be allowed.
+    private var hasRetried = false
+    private lateinit var cameraProvider: ProcessCameraProvider
+    private lateinit var extensionsManager: ExtensionsManager
+
+    private var currentCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+    @ExtensionMode.Mode private var currentExtensionMode = ExtensionMode.BOKEH
+    private lateinit var camera: Camera
+
+    private lateinit var preview: Preview
+    private lateinit var previewView: PreviewView
+    private lateinit var imageCapture: ImageCapture
+
+    private var currentStreamState: PreviewView.StreamState? = null
+    private val resultIntent = Intent()
+    private var runningStage = STAGE_WAIT_FOR_PREVIEW_STREAMING
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        // Sets the default result code if no any problem occurs when the activity is finished
+        setResult(RESULT_SUCCESS)
+        // Checks whether the running mode matches the specified one
+        checkRunningMode()
+
+        setContentView(R.layout.activity_release_test)
+        setTitle(R.string.camerax_extensions)
+
+        intent.getStringExtra(INTENT_EXTRA_CAMERA_IMPLEMENTATION)?.let { cameraImplementation = it }
+
+        intent.getStringExtra(INTENT_EXTRA_KEY_CAMERA_ID)?.let {
+            currentCameraSelector = createCameraSelectorById(it)
+        }
+
+        currentExtensionMode =
+            intent.getIntExtra(INTENT_EXTRA_KEY_EXTENSION_MODE, currentExtensionMode)
+
+        val policy = VmPolicy.Builder().detectAll().penaltyLog().build()
+        StrictMode.setVmPolicy(policy)
+
+        val viewFinderStub = findViewById<ViewStub>(R.id.viewFinderStub)
+        viewFinderStub.layoutResource = R.layout.full_previewview
+        previewView = viewFinderStub.inflate() as PreviewView
+        previewView.setImplementationMode(PreviewView.ImplementationMode.COMPATIBLE)
+
+        val futureCompleter = PermissionUtil.setupPermissions(this)
+        permissionCompleter = futureCompleter.second
+        Futures.addCallback<Boolean>(
+            futureCompleter.first,
+            object : FutureCallback<Boolean?> {
+                override fun onSuccess(result: Boolean?) {
+                    permissionsGranted = Preconditions.checkNotNull(result)
+
+                    if (!permissionsGranted) {
+                        Log.d(TAG, "Required permissions are not all granted!")
+                        Toast.makeText(
+                                this@ReleaseTestActivity,
+                                "Required permissions are not " + "all granted!",
+                                Toast.LENGTH_LONG
+                            )
+                            .show()
+                        setResultAndFinishActivity(
+                            RESULT_ERROR_PERMISSION_NOT_SATISFIED,
+                            "Permission requirements are not satisfied."
+                        )
+                        return
+                    }
+
+                    configAndRetrieveCameraProvider()
+                }
+
+                override fun onFailure(t: Throwable) {
+                    setResultAndFinishActivity(
+                        RESULT_ERROR_PERMISSION_NOT_SATISFIED,
+                        "Permission requirements are not satisfied."
+                    )
+                }
+            },
+            ContextCompat.getMainExecutor(this)
+        )
+    }
+
+    private fun checkRunningMode() {
+        val runningModeCheck = intent.getStringExtra(INTENT_EXTRA_RUNNING_MODE_CHECK) ?: return
+
+        val inDebug = (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
+
+        if (
+            ("release" == runningModeCheck && !inDebug) || ("debug" == runningModeCheck && inDebug)
+        ) {
+            return
+        }
+
+        setResultAndFinishActivity(
+            RESULT_ERROR_RUNNING_MODE_INCORRECT,
+            "Running mode check failed! The target running mode is $runningModeCheck"
+        )
+    }
+
+    private fun configAndRetrieveCameraProvider() {
+        (application as ExtensionsApplication).cameraXConfig =
+            if (cameraImplementation.equals(CAMERA_PIPE_IMPLEMENTATION_OPTION)) {
+                CameraPipeConfig.defaultConfig()
+            } else {
+                Camera2Config.defaultConfig()
+            }
+
+        val cameraProviderFuture = getInstance(this@ReleaseTestActivity)
+
+        Futures.addCallback<ProcessCameraProvider>(
+            cameraProviderFuture,
+            object : FutureCallback<ProcessCameraProvider> {
+                @SuppressLint("VisibleForTests")
+                override fun onSuccess(result: ProcessCameraProvider) {
+                    cameraProvider = result
+
+                    if (!checkCameraImplementation()) {
+                        if (hasRetried) {
+                            setResultAndFinishActivity(
+                                RESULT_ERROR_INCORRECT_CAMERA_IMPLEMENTATION,
+                                "Can not setup implementation correctly."
+                            )
+                            return
+                        }
+                        cameraProvider
+                            .shutdownAsync()
+                            .addListener(
+                                {
+                                    if (hasRetried) {
+                                        return@addListener
+                                    }
+                                    hasRetried = true
+                                    configAndRetrieveCameraProvider()
+                                },
+                                ContextCompat.getMainExecutor(this@ReleaseTestActivity)
+                            )
+                        return
+                    }
+
+                    setupCamera()
+                }
+
+                override fun onFailure(t: Throwable) {
+                    throw RuntimeException("Failed to get camera provider", t)
+                }
+            },
+            ContextCompat.getMainExecutor(this@ReleaseTestActivity)
+        )
+    }
+
+    /** Checks whether the camera implementation mode is correct. */
+    private fun checkCameraImplementation(): Boolean {
+        camera = cameraProvider.bindToLifecycle(this, currentCameraSelector)
+
+        if (cameraImplementation.equals(CAMERA_PIPE_IMPLEMENTATION_OPTION)) {
+            if (!isCameraPipeImplementation(camera.cameraInfo)) {
+                return false
+            }
+        } else {
+            if (!isCamera2Implementation(camera.cameraInfo)) {
+                return false
+            }
+        }
+
+        return true
+    }
+
+    @OptIn(androidx.camera.camera2.interop.ExperimentalCamera2Interop::class)
+    private fun isCamera2Implementation(cameraInfo: CameraInfo): Boolean {
+        try {
+            androidx.camera.camera2.interop.Camera2CameraInfo.from(cameraInfo)
+        } catch (e: IllegalArgumentException) {
+            return false
+        }
+        return true
+    }
+
+    @kotlin.OptIn(ExperimentalCamera2Interop::class)
+    private fun isCameraPipeImplementation(cameraInfo: CameraInfo): Boolean {
+        try {
+            androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo.from(cameraInfo)
+        } catch (e: IllegalArgumentException) {
+            return false
+        }
+        return true
+    }
+
+    private fun setResultAndFinishActivity(resultErrorCode: Int, errorMessage: String? = null) {
+        errorMessage?.let { resultIntent.putExtra(INTENT_EXTRA_RESULT_ERROR_MESSAGE, errorMessage) }
+        setResult(resultErrorCode, resultIntent)
+        runOnUiThread(::finish)
+    }
+
+    override fun onRequestPermissionsResult(
+        requestCode: Int,
+        permissions: Array<out String>,
+        grantResults: IntArray
+    ) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+        if (requestCode != PERMISSIONS_REQUEST_CODE) {
+            return
+        }
+
+        // If request is cancelled, the result arrays are empty.
+        if (grantResults.isEmpty()) {
+            permissionCompleter.set(false)
+            return
+        }
+
+        var allPermissionGranted = true
+
+        for (grantResult in grantResults) {
+            if (grantResult != PackageManager.PERMISSION_GRANTED) {
+                allPermissionGranted = false
+                break
+            }
+        }
+
+        Log.d(TAG, "All permissions granted: $allPermissionGranted")
+        permissionCompleter.set(allPermissionGranted)
+    }
+
+    private fun setupCamera() {
+        if (!permissionsGranted) {
+            Log.d(TAG, "Permissions denied.")
+            return
+        }
+        if (isDestroyed) {
+            Log.d(TAG, "Activity is destroyed, not to create LifecycleCamera.")
+            return
+        }
+
+        camera = cameraProvider.bindToLifecycle(this, currentCameraSelector)
+
+        val extensionsManagerFuture =
+            ExtensionsManager.getInstanceAsync(applicationContext, cameraProvider)
+
+        Futures.addCallback<ExtensionsManager>(
+            extensionsManagerFuture,
+            object : FutureCallback<ExtensionsManager> {
+                override fun onSuccess(extensionsManager: ExtensionsManager) {
+                    // There might be timing issue that the activity has been destroyed when
+                    // the onSuccess callback is received. Skips the afterward flow when the
+                    // situation happens.
+                    if (
+                        [email protected] == Lifecycle.State.DESTROYED
+                    ) {
+                        return
+                    }
+                    [email protected] = extensionsManager
+                    bindUseCases()
+                }
+
+                override fun onFailure(throwable: Throwable) {
+                    setResultAndFinishActivity(
+                        RESULT_ERROR_FAILED_TO_RETRIEVE_EXTENSIONS_MANAGER,
+                        "Failed to retrieve ExtensionsManager."
+                    )
+                }
+            },
+            ContextCompat.getMainExecutor(this@ReleaseTestActivity)
+        )
+    }
+
+    private fun bindUseCases() {
+        if (!extensionsManager.isExtensionAvailable(currentCameraSelector, currentExtensionMode)) {
+            setResultAndFinishActivity(
+                RESULT_ERROR_EXTENSION_MOD_NOT_SUPPORTED,
+                "Mode $currentExtensionMode is not supported!"
+            )
+            return
+        }
+
+        cameraProvider.unbindAll()
+
+        val cameraSelector =
+            extensionsManager.getExtensionEnabledCameraSelector(
+                currentCameraSelector,
+                currentExtensionMode
+            )
+
+        camera = cameraProvider.bindToLifecycle(this, cameraSelector)
+
+        imageCapture = ImageCapture.Builder().setTargetName("ImageCapture").build()
+
+        preview = Preview.Builder().setTargetName("Preview").build()
+        currentStreamState = null
+        preview.surfaceProvider = previewView.surfaceProvider
+
+        // Observes the stream state for the unit tests to know the preview status.
+        previewView.previewStreamState.removeObservers(this)
+        previewView.previewStreamState.observeForever { streamState: PreviewView.StreamState ->
+            currentStreamState = streamState
+            if (
+                streamState == PreviewView.StreamState.STREAMING &&
+                    runningStage == STAGE_WAIT_FOR_PREVIEW_STREAMING
+            ) {
+                runningStage = STAGE_WAIT_FOR_STILL_IMAGE_CAPTURE
+                // Trigger photo-taking flow
+                takePicture()
+            }
+        }
+
+        camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
+    }
+
+    private fun takePicture() {
+        imageCapture.takePicture(
+            ContextCompat.getMainExecutor(this@ReleaseTestActivity),
+            object : ImageCapture.OnImageCapturedCallback() {
+                override fun onCaptureSuccess(image: ImageProxy) {
+                    Log.d(TAG, "Still image is captured successfully!")
+                    image.close()
+                    // Force finish if the still image can be captured successfully
+                    setResultAndFinishActivity(RESULT_SUCCESS)
+                }
+
+                override fun onError(exception: ImageCaptureException) {
+                    val errorMessage =
+                        "Failed to capture image - ${exception.message}, ${exception.cause}"
+                    Log.e(TAG, errorMessage)
+                    setResultAndFinishActivity(RESULT_ERROR_TAKE_PICTURE_FAILED, errorMessage)
+                }
+            }
+        )
+    }
+}
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/Camera2ExtensionsUtil.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/Camera2ExtensionsUtil.kt
index 97cfdb7..22ad5ce 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/Camera2ExtensionsUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/Camera2ExtensionsUtil.kt
@@ -138,15 +138,27 @@
         }
         val displayArRatio = displaySize.x.toFloat() / displaySize.y
         val previewSizes = ArrayList<Size>()
+        var previewSize: Size? = null
+        var currentDistance = Int.MAX_VALUE
         for (sz in textureSizes) {
             val arRatio = sz.width.toFloat() / sz.height
             if (Math.abs(arRatio - displayArRatio) <= .2f) {
                 previewSizes.add(sz)
             }
+            val distance = Math.abs(sz.width * sz.height - displaySize.x * displaySize.y)
+            if (currentDistance > distance) {
+                currentDistance = distance
+                previewSize = sz
+            }
         }
 
-        var previewSize = previewSizes[0]
-        var currentDistance = Int.MAX_VALUE
+        if (previewSizes.isEmpty()) {
+            previewSize?.let { previewSizes.add(it) }
+                ?: throw IllegalStateException("No preview size was found")
+        } else {
+            previewSize = previewSizes[0]
+        }
+
         if (extensionMode == EXTENSION_MODE_NONE) {
             for (sz in previewSizes) {
                 val distance = Math.abs(sz.width * sz.height - displaySize.x * displaySize.y)
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/PermissionUtil.kt b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/PermissionUtil.kt
new file mode 100644
index 0000000..1ed34c4
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/utils/PermissionUtil.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.extensions.utils
+
+import android.Manifest
+import android.app.Activity
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.Build
+import android.util.Log
+import android.util.Pair
+import androidx.camera.integration.extensions.PERMISSIONS_REQUEST_CODE
+import androidx.concurrent.futures.CallbackToFutureAdapter
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import com.google.common.util.concurrent.ListenableFuture
+
+private const val TAG = "PermissionUtil"
+
+/** Permissions setup utility. */
+object PermissionUtil {
+    /** Setup required permissions. */
+    @JvmStatic
+    fun setupPermissions(
+        activity: Activity
+    ): Pair<ListenableFuture<Boolean>, CallbackToFutureAdapter.Completer<Boolean>> {
+        var permissionCompleter: CallbackToFutureAdapter.Completer<Boolean>? = null
+        val future =
+            CallbackToFutureAdapter.getFuture {
+                completer: CallbackToFutureAdapter.Completer<Boolean> ->
+                permissionCompleter = completer
+                if (!allPermissionsGranted(activity)) {
+                    makePermissionRequest(activity)
+                } else {
+                    permissionCompleter!!.set(true)
+                }
+                "get_permissions"
+            }
+        return Pair.create(future, permissionCompleter!!)
+    }
+
+    private fun makePermissionRequest(activity: Activity) {
+        ActivityCompat.requestPermissions(
+            activity,
+            getRequiredPermissions(activity),
+            PERMISSIONS_REQUEST_CODE
+        )
+    }
+
+    /** Returns true if all the necessary permissions have been granted already. */
+    private fun allPermissionsGranted(activity: Activity): Boolean {
+        for (permission in getRequiredPermissions(activity)) {
+            if (
+                ContextCompat.checkSelfPermission(activity, permission!!) !=
+                    PackageManager.PERMISSION_GRANTED
+            ) {
+                return false
+            }
+        }
+        return true
+    }
+
+    /** Tries to acquire all the necessary permissions through a dialog. */
+    private fun getRequiredPermissions(activity: Activity): Array<String?> {
+        val info: PackageInfo
+        try {
+            info =
+                activity.packageManager.getPackageInfo(
+                    activity.packageName,
+                    PackageManager.GET_PERMISSIONS
+                )
+        } catch (exception: PackageManager.NameNotFoundException) {
+            Log.e(TAG, "Failed to obtain all required permissions.", exception)
+            return arrayOfNulls(0)
+        }
+
+        if (info.requestedPermissions == null || info.requestedPermissions.isEmpty()) {
+            return arrayOfNulls(0)
+        }
+
+        val requiredPermissions: MutableList<String?> = mutableListOf()
+
+        // From Android T, skips the permission check of WRITE_EXTERNAL_STORAGE since it won't be
+        // granted any more. When querying the requested permissions from PackageManager,
+        // READ_EXTERNAL_STORAGE will also be included if we specify WRITE_EXTERNAL_STORAGE
+        // requirement in AndroidManifest.xml. Therefore, also need to skip the permission check
+        // of READ_EXTERNAL_STORAGE.
+        for (permission in info.requestedPermissions) {
+            if (
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
+                    (Manifest.permission.WRITE_EXTERNAL_STORAGE == permission ||
+                        Manifest.permission.READ_EXTERNAL_STORAGE == permission)
+            ) {
+                continue
+            }
+
+            requiredPermissions.add(permission)
+        }
+
+        val permissions = requiredPermissions.toTypedArray<String?>()
+
+        return if (permissions.isNotEmpty()) {
+            permissions
+        } else {
+            arrayOfNulls(0)
+        }
+    }
+}
diff --git a/camera/integration-tests/extensionstestapp/src/main/res/layout/activity_release_test.xml b/camera/integration-tests/extensionstestapp/src/main/res/layout/activity_release_test.xml
new file mode 100644
index 0000000..afb3ac6
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/main/res/layout/activity_release_test.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/constraintLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="androidx.camera.integration.extensions.CameraExtensionsActivity">
+
+    <ViewStub
+        android:id="@+id/viewFinderStub"
+        android:inflatedId="@id/viewFinder"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/camera/integration-tests/timingtestapp/lint-baseline.xml b/camera/integration-tests/timingtestapp/lint-baseline.xml
index c36a0d1..89ad9d4 100644
--- a/camera/integration-tests/timingtestapp/lint-baseline.xml
+++ b/camera/integration-tests/timingtestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -110,28 +110,10 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.camera.integration.antelope.CameraUtilsKt is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    physicalCameras = cameraChars.physicalCameraIds"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/camera/integration/antelope/CameraUtils.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.camera.integration.antelope.cameracontrollers.CameraXControllerKt is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        released = texture.isReleased"
-        errorLine2="                           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/camera/integration/antelope/cameracontrollers/CameraXController.kt"/>
-    </issue>
-
-    <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.directExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                    surface, CameraXExecutors.directExecutor(),"
-        errorLine2="                                              ~~~~~~~~~~~~~~">
+        errorLine1="                    CameraXExecutors.directExecutor(),"
+        errorLine2="                                     ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/antelope/cameracontrollers/CameraXController.kt"/>
     </issue>
@@ -139,8 +121,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.mainThreadExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                CameraXExecutors.mainThreadExecutor(),"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~">
+        errorLine1="            CameraXExecutors.mainThreadExecutor(),"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/antelope/cameracontrollers/CameraXController.kt"/>
     </issue>
diff --git a/camera/integration-tests/uiwidgetstestapp/build.gradle b/camera/integration-tests/uiwidgetstestapp/build.gradle
index 453f43f..33c414d 100644
--- a/camera/integration-tests/uiwidgetstestapp/build.gradle
+++ b/camera/integration-tests/uiwidgetstestapp/build.gradle
@@ -21,6 +21,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         applicationId "androidx.camera.integration.uiwidgets"
         versionCode 1
diff --git a/camera/integration-tests/uiwidgetstestapp/lint-baseline.xml b/camera/integration-tests/uiwidgetstestapp/lint-baseline.xml
index 29700ac..0a24108 100644
--- a/camera/integration-tests/uiwidgetstestapp/lint-baseline.xml
+++ b/camera/integration-tests/uiwidgetstestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="RestrictedApiAndroidX"
@@ -40,8 +40,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="UseCase.getAttachedSurfaceResolution can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="        val resolution = mImageCapture.attachedSurfaceResolution"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            mImageCapture.attachedSurfaceResolution"
+        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/uiwidgets/rotations/CameraActivity.kt"/>
     </issue>
@@ -49,8 +49,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="UseCase.getAttachedSurfaceResolution can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="        val resolution = mImageCapture.attachedSurfaceResolution"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            mImageCapture.attachedSurfaceResolution"
+        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/uiwidgets/rotations/CameraActivity.kt"/>
     </issue>
@@ -229,7 +229,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field VALID_FLASH_MODES with type List&lt;Integer>: replace with IntList"
-        errorLine1="        private val VALID_FLASH_MODES = listOf("
+        errorLine1="        private val VALID_FLASH_MODES ="
         errorLine2="        ^">
         <location
             file="src/main/java/androidx/camera/integration/uiwidgets/compose/ui/screen/imagecapture/ImageCaptureScreenState.kt"/>
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt
index 3251be1..57a3e02 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisBaseTest.kt
@@ -74,19 +74,19 @@
         )
 
     @get:Rule
-    val mCameraActivityRules: GrantPermissionRule =
+    val cameraActivityRules: GrantPermissionRule =
         GrantPermissionRule.grant(*CameraActivity.PERMISSIONS)
 
-    protected val mDevice: UiDevice =
-        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    protected lateinit var device: UiDevice
 
     protected fun setUp(lensFacing: Int) {
         CoreAppTestUtil.assumeCompatibleDevice()
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing))
 
+        device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
         // Ensure it's in a natural orientation. This change could delay around 1 sec, please
         // call this earlier before launching the test activity.
-        mDevice.setOrientationNatural()
+        device.setOrientationNatural()
 
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window before start the test.
@@ -99,7 +99,9 @@
             val cameraProvider = ProcessCameraProvider.getInstance(context)[10, TimeUnit.SECONDS]
             cameraProvider.shutdownAsync()[10, TimeUnit.SECONDS]
         }
-        mDevice.unfreezeRotation()
+        if (::device.isInitialized) {
+            device.unfreezeRotation()
+        }
     }
 
     protected inline fun <reified A : CameraActivity> verifyRotation(
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisUnlockedOrientationTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisUnlockedOrientationTest.kt
index bfa414a..731580e 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisUnlockedOrientationTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageAnalysisUnlockedOrientationTest.kt
@@ -84,7 +84,7 @@
         InstrumentationRegistry.getInstrumentation().addMonitor(monitor)
 
         // Rotate
-        rotation.rotate(mDevice)
+        rotation.rotate(device)
 
         // Wait for the activity to be recreated after rotation
         InstrumentationRegistry.getInstrumentation().waitForMonitorWithTimeout(monitor, 2000L)
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
index d934150..da903e5 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
@@ -84,11 +84,10 @@
         )
 
     @get:Rule
-    val mCameraActivityRules: GrantPermissionRule =
+    val cameraActivityRules: GrantPermissionRule =
         GrantPermissionRule.grant(*CameraActivity.PERMISSIONS)
 
-    protected val mDevice: UiDevice =
-        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    protected lateinit var device: UiDevice
 
     protected fun setUp(lensFacing: Int) {
         // TODO(b/147448711) Cuttlefish seems to have an issue handling rotation. Might be
@@ -105,9 +104,10 @@
         CoreAppTestUtil.assumeCompatibleDevice()
         assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing))
 
+        device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
         // Ensure it's in a natural orientation. This change could delay around 1 sec, please
         // call this earlier before launching the test activity.
-        mDevice.setOrientationNatural()
+        device.setOrientationNatural()
 
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window before start the test.
@@ -123,7 +123,9 @@
             val cameraProvider = ProcessCameraProvider.getInstance(context)[10, TimeUnit.SECONDS]
             cameraProvider.shutdownAsync()[10, TimeUnit.SECONDS]
         }
-        mDevice.unfreezeRotation()
+        if (::device.isInitialized) {
+            device.unfreezeRotation()
+        }
     }
 
     @Suppress("DEPRECATION")
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
index 09c1e73..6883234 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureUnlockedOrientationTest.kt
@@ -128,7 +128,7 @@
         InstrumentationRegistry.getInstrumentation().addMonitor(monitor)
 
         // Rotate
-        rotation.rotate(mDevice)
+        rotation.rotate(device)
 
         // Wait for the activity to be recreated after rotation
         InstrumentationRegistry.getInstrumentation().waitForMonitorWithTimeout(monitor, 2000L)
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
index 044f7f4..79731b2 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
@@ -120,15 +120,16 @@
             )
         )
 
-    private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    private lateinit var device: UiDevice
 
     @Before
     fun setUp() {
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing))
 
+        device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
         // Ensure it's in a natural orientation. This change could delay around 1 sec, please
         // call this earlier before launching the test activity.
-        mDevice.setOrientationNatural()
+        device.setOrientationNatural()
 
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window.
@@ -140,7 +141,9 @@
         val context = ApplicationProvider.getApplicationContext<Context>()
         val cameraProvider = ProcessCameraProvider.getInstance(context)[10, TimeUnit.SECONDS]
         cameraProvider.shutdownAsync()[10, TimeUnit.SECONDS]
-        mDevice.unfreezeRotation()
+        if (::device.isInitialized) {
+            device.unfreezeRotation()
+        }
     }
 
     // The test makes sure the camera PreviewView is in the streaming state.
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt
index c35865e..331ed44 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPagerActivityTest.kt
@@ -106,15 +106,16 @@
             )
         )
 
-    private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    private lateinit var device: UiDevice
 
     @Before
     fun setUp() {
         Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing))
 
+        device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
         // Ensure it's in a natural orientation. This change could delay around 1 sec, please
         // call this earlier before launching the test activity.
-        mDevice.setOrientationNatural()
+        device.setOrientationNatural()
 
         // Clear the device UI and check if there is no dialog or lock screen on the top of the
         // window.
@@ -126,7 +127,9 @@
         val context = ApplicationProvider.getApplicationContext<Context>()
         val cameraProvider = ProcessCameraProvider.getInstance(context)[10, TimeUnit.SECONDS]
         cameraProvider.shutdownAsync()[10, TimeUnit.SECONDS]
-        mDevice.unfreezeRotation()
+        if (::device.isInitialized) {
+            device.unfreezeRotation()
+        }
     }
 
     // The test makes sure the camera PreviewView is in the streaming state.
@@ -183,7 +186,7 @@
 
             scenario.moveToState(State.CREATED)
             scenario.moveToState(State.RESUMED)
-            mDevice.waitForIdle(ACTION_IDLE_TIMEOUT)
+            device.waitForIdle(ACTION_IDLE_TIMEOUT)
 
             // After resume, switch back to CameraFragment, to check Preview in stream state
             onView(withText(ViewPagerActivity.CAMERA_FRAGMENT_TAB_TITLE)).perform(click())
diff --git a/camera/integration-tests/viewfindertestapp/lint-baseline.xml b/camera/integration-tests/viewfindertestapp/lint-baseline.xml
index 15b245a..820ca23 100644
--- a/camera/integration-tests/viewfindertestapp/lint-baseline.xml
+++ b/camera/integration-tests/viewfindertestapp/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-alpha15" type="baseline" client="gradle" dependencies="false" name="AGP (8.2.0-alpha15)" variant="all" version="8.2.0-alpha15">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="RestrictedApiAndroidX"
         message="CompareSizesByArea can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                    /* comp = */ CompareSizesByArea()"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~">
+        errorLine1="                        /* comp = */ CompareSizesByArea()"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/viewfinder/CameraViewfinderFoldableFragment.kt"/>
     </issue>
diff --git a/camera/integration-tests/viewtestapp/lint-baseline.xml b/camera/integration-tests/viewtestapp/lint-baseline.xml
index c6b452f..a6ded29 100644
--- a/camera/integration-tests/viewtestapp/lint-baseline.xml
+++ b/camera/integration-tests/viewtestapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="RestrictedApiAndroidX"
@@ -148,8 +148,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.directExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="            directExecutor(),"
-        errorLine2="            ~~~~~~~~~~~~~~">
+        errorLine1="        cameraController.takePicture(outputFileOptions, directExecutor(), onImageSavedCallback)"
+        errorLine2="                                                        ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/EffectsFragment.kt"/>
     </issue>
@@ -157,8 +157,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.directExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="            directExecutor()"
-        errorLine2="            ~~~~~~~~~~~~~~">
+        errorLine1="            cameraController.startRecording(outputOptions, audioConfig, directExecutor()) {"
+        errorLine2="                                                                        ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/EffectsFragment.kt"/>
     </issue>
@@ -166,8 +166,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.mainThreadExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="        cameraController.setImageAnalysisAnalyzer(mainThreadExecutor(),"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~">
+        errorLine1="            mainThreadExecutor(),"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/MlKitFragment.kt"/>
     </issue>
@@ -184,8 +184,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.e can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="            Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
-        errorLine2="                   ~">
+        errorLine1="                Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
+        errorLine2="                       ~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -193,8 +193,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.e can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="            Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
-        errorLine2="                     ~~~">
+        errorLine1="                Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
+        errorLine2="                         ~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -202,8 +202,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.e can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="            Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
+        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -211,8 +211,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.e can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="            Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
-        errorLine2="                                                       ~~~~~~~~~">
+        errorLine1="                Logger.e(TAG, &quot;Failed to bind use cases.&quot;, exception)"
+        errorLine2="                                                           ~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -229,8 +229,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.directExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                CameraXExecutors.directExecutor(),"
-        errorLine2="                                 ~~~~~~~~~~~~~~">
+        errorLine1="                    .start(CameraXExecutors.directExecutor(), generateVideoRecordEventListener())"
+        errorLine2="                                            ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -319,8 +319,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                    VideoRecordEvent.Finalize.ERROR_SOURCE_INACTIVE -> Logger.d("
-        errorLine2="                                                                              ~">
+        errorLine1="                        Logger.d(TAG, &quot;Video saved to: $uri&quot;)"
+        errorLine2="                               ~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -328,8 +328,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                        TAG,"
-        errorLine2="                        ~~~">
+        errorLine1="                        Logger.d(TAG, &quot;Video saved to: $uri&quot;)"
+        errorLine2="                                 ~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -337,8 +337,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.d can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                        &quot;Video saved to: $uri&quot;"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                        Logger.d(TAG, &quot;Video saved to: $uri&quot;)"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -346,8 +346,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.e can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                    else -> Logger.e("
-        errorLine2="                                   ~">
+        errorLine1="                        Logger.e(TAG, &quot;Failed to save video: uri $uri with code (${event.error})&quot;)"
+        errorLine2="                               ~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -355,8 +355,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.e can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                        TAG,"
-        errorLine2="                        ~~~">
+        errorLine1="                        Logger.e(TAG, &quot;Failed to save video: uri $uri with code (${event.error})&quot;)"
+        errorLine2="                                 ~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -364,8 +364,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Logger.e can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="                        &quot;Failed to save video: uri $uri with code (${event.error})&quot;"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                        Logger.e(TAG, &quot;Failed to save video: uri $uri with code (${event.error})&quot;)"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt"/>
     </issue>
@@ -373,8 +373,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="CameraXExecutors.mainThreadExecutor can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="    IMAGE_CAPTURE, mainThreadExecutor(), ToneMappingImageProcessor(), {}"
-        errorLine2="                   ~~~~~~~~~~~~~~~~~~">
+        errorLine1="    CameraEffect(IMAGE_CAPTURE, mainThreadExecutor(), ToneMappingImageProcessor(), {}) {"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingImageEffect.kt"/>
     </issue>
@@ -463,8 +463,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="ShaderProvider can only be accessed from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="        private val TONE_MAPPING_SHADER_PROVIDER = object : ShaderProvider {"
-        errorLine2="                                                            ~~~~~~~~~~~~~~">
+        errorLine1="            object : ShaderProvider {"
+        errorLine2="                     ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
     </issue>
@@ -472,8 +472,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="ShaderProvider.createFragmentShader can only be called from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
-        errorLine1="            override fun createFragmentShader(sampler: String, fragCoords: String): String {"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                override fun createFragmentShader(sampler: String, fragCoords: String): String {"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
     </issue>
@@ -517,7 +517,7 @@
     <issue
         id="RestrictedApiAndroidX"
         message="OpenGlRenderer.init can only be called from within the same library (androidx.camera:camera-core)"
-        errorLine1="            glRenderer.init(DynamicRange.SDR, TONE_MAPPING_SHADER_PROVIDER)"
+        errorLine1="            glRenderer.init("
         errorLine2="                       ~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
@@ -526,8 +526,17 @@
     <issue
         id="RestrictedApiAndroidX"
         message="OpenGlRenderer.init can only be called from within the same library (androidx.camera:camera-core)"
-        errorLine1="            glRenderer.init(DynamicRange.SDR, TONE_MAPPING_SHADER_PROVIDER)"
-        errorLine2="                            ~~~~~~~~~~~~~~~~">
+        errorLine1="                DynamicRange.SDR,"
+        errorLine2="                ~~~~~~~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
+    </issue>
+
+    <issue
+        id="RestrictedApiAndroidX"
+        message="InputFormat.DEFAULT can only be accessed from within the same library group (referenced groupId=`androidx.camera` from groupId=`androidx.camera.integration-tests`)"
+        errorLine1="                mapOf(InputFormat.DEFAULT to TONE_MAPPING_SHADER_PROVIDER)"
+        errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
     </issue>
@@ -535,8 +544,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="OpenGlRenderer.init can only be called from within the same library (androidx.camera:camera-core)"
-        errorLine1="            glRenderer.init(DynamicRange.SDR, TONE_MAPPING_SHADER_PROVIDER)"
-        errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                mapOf(InputFormat.DEFAULT to TONE_MAPPING_SHADER_PROVIDER)"
+        errorLine2="                ~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
     </issue>
@@ -562,8 +571,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="OpenGlRenderer.unregisterOutputSurface can only be called from within the same library (androidx.camera:camera-core)"
-        errorLine1="                glRenderer.unregisterOutputSurface(removedSurface)"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                    glRenderer.unregisterOutputSurface(removedSurface)"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
     </issue>
@@ -571,8 +580,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="OpenGlRenderer.unregisterOutputSurface can only be called from within the same library (androidx.camera:camera-core)"
-        errorLine1="                glRenderer.unregisterOutputSurface(removedSurface)"
-        errorLine2="                                                   ~~~~~~~~~~~~~~">
+        errorLine1="                    glRenderer.unregisterOutputSurface(removedSurface)"
+        errorLine2="                                                       ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt"/>
     </issue>
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt
index 8f32ea2..ad98da7 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/StreamSharingActivity.kt
@@ -22,9 +22,13 @@
 import android.os.Bundle
 import android.view.OrientationEventListener
 import android.widget.Button
+import androidx.annotation.OptIn
 import androidx.appcompat.app.AppCompatActivity
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
 import androidx.camera.core.Camera
 import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraXConfig
 import androidx.camera.core.ImageAnalysis
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.Logger
@@ -32,6 +36,7 @@
 import androidx.camera.core.Preview
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.impl.FileUtil.canDeviceWriteToMediaStore
 import androidx.camera.testing.impl.FileUtil.generateVideoFileOutputOptions
@@ -68,6 +73,11 @@
 private const val PREVIEW_VIEW_COMPATIBLE_MODE = "compatible"
 private const val PREVIEW_VIEW_PERFORMANCE_MODE = "performance"
 
+// Possible values for this intent key (case-insensitive): "camera2", "camera_pipe".
+private const val INTENT_EXTRA_CAMERA_IMPLEMENTATION = "camera_implementation"
+private const val CAMERA_IMPLEMENTATION_CAMERA2 = "camera2"
+private const val CAMERA_IMPLEMENTATION_CAMERA_PIPE = "camera_pipe"
+
 class StreamSharingActivity : AppCompatActivity() {
 
     private lateinit var previewView: PreviewView
@@ -75,6 +85,7 @@
     private lateinit var recordButton: Button
     private lateinit var useCases: Array<UseCase>
     private var cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+    private var cameraXConfig: CameraXConfig = Camera2Config.defaultConfig()
     private var camera: Camera? = null
     private var previewViewMode: ImplementationMode = ImplementationMode.PERFORMANCE
     private var previewViewScaleType = PreviewView.ScaleType.FILL_CENTER
@@ -99,6 +110,7 @@
         if (bundle != null) {
             parseScreenOrientationAndSetValueIfNeed(bundle)
             parseCameraSelector(bundle)
+            parseCameraImplementation(bundle)
             parsePreviewViewMode(bundle)
         }
 
@@ -113,6 +125,10 @@
             if (activeRecording == null) startRecording() else stopRecording()
         }
 
+        if (!isCameraXConfigured) {
+            isCameraXConfigured = true
+            configureCameraProvider()
+        }
         startCamera()
     }
 
@@ -144,6 +160,15 @@
         }
     }
 
+    private fun parseCameraImplementation(bundle: Bundle) {
+        val implementation = bundle.getString(INTENT_EXTRA_CAMERA_IMPLEMENTATION)
+        if (CAMERA_IMPLEMENTATION_CAMERA2.equals(implementation, true)) {
+            cameraXConfig = Camera2Config.defaultConfig()
+        } else if (CAMERA_IMPLEMENTATION_CAMERA_PIPE.equals(implementation, true)) {
+            cameraXConfig = CameraPipeConfig.defaultConfig()
+        }
+    }
+
     private fun parsePreviewViewMode(bundle: Bundle) {
         val mode = bundle.getString(INTENT_PREVIEW_VIEW_MODE)
         if (PREVIEW_VIEW_COMPATIBLE_MODE.equals(mode, true)) {
@@ -153,6 +178,12 @@
         }
     }
 
+    @SuppressLint("NullAnnotationGroup")
+    @OptIn(ExperimentalCameraProviderConfiguration::class)
+    private fun configureCameraProvider() {
+        ProcessCameraProvider.configureInstance(cameraXConfig)
+    }
+
     private fun startCamera() {
         val cameraProviderFuture = ProcessCameraProvider.getInstance(applicationContext)
         cameraProviderFuture.addListener(
@@ -286,4 +317,8 @@
             }
         }
     }
+
+    companion object {
+        private var isCameraXConfigured: Boolean = false
+    }
 }
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt
index f31da72..8f2d566 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/ToneMappingSurfaceProcessor.kt
@@ -29,6 +29,7 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.newHandlerExecutor
 import androidx.camera.core.processing.OpenGlRenderer
 import androidx.camera.core.processing.ShaderProvider
+import androidx.camera.core.processing.util.GLUtils.InputFormat
 import androidx.core.util.Preconditions.checkState
 import java.util.concurrent.Executor
 
@@ -85,7 +86,12 @@
         glThread.start()
         glHandler = Handler(glThread.looper)
         glExecutor = newHandlerExecutor(glHandler)
-        glExecutor.execute { glRenderer.init(DynamicRange.SDR, TONE_MAPPING_SHADER_PROVIDER) }
+        glExecutor.execute {
+            glRenderer.init(
+                DynamicRange.SDR,
+                mapOf(InputFormat.DEFAULT to TONE_MAPPING_SHADER_PROVIDER)
+            )
+        }
     }
 
     override fun onInputSurface(surfaceRequest: SurfaceRequest) {
diff --git a/car/app/app-automotive/build.gradle b/car/app/app-automotive/build.gradle
index dd4116b..2434027 100644
--- a/car/app/app-automotive/build.gradle
+++ b/car/app/app-automotive/build.gradle
@@ -36,7 +36,7 @@
     implementation("androidx.concurrent:concurrent-futures:1.1.0")
     implementation("androidx.fragment:fragment:1.3.0")
     implementation("androidx.lifecycle:lifecycle-common-java8:2.2.0")
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.7.0")
     implementation(libs.autoValueAnnotations)
 
@@ -79,5 +79,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Automotive OS specific functionality to build navigation, parking, and charging apps for cars"
-    metalavaK2UastEnabled = true
 }
diff --git a/car/app/app-automotive/lint-baseline.xml b/car/app/app-automotive/lint-baseline.xml
index 67c3ab1..6260021 100644
--- a/car/app/app-automotive/lint-baseline.xml
+++ b/car/app/app-automotive/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="UnsafeOptInUsageError"
@@ -343,13 +343,4 @@
             file="src/main/java/androidx/car/app/hardware/common/PropertyUtils.java"/>
     </issue>
 
-    <issue
-        id="SupportAnnotationUsage"
-        message="This annotation does not apply for type com.google.common.collect.ImmutableMap&lt;java.util.Set&lt;androidx.car.app.hardware.common.CarZone>,java.util.Set&lt;java.lang.Integer>>; expected int"
-        errorLine1="    @HvacFanDirection"
-        errorLine2="    ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/car/app/hardware/common/CarPropertyProfile.java"/>
-    </issue>
-
 </issues>
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 9444524..58d00ab 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,7 +40,6 @@
 import android.view.WindowManager;
 import android.widget.ImageView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -254,18 +253,15 @@
         private Api30Impl() {
         }
 
-        @DoNotInline
         static Insets getInsets(WindowInsets windowInsets) {
             return windowInsets.getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.ime());
         }
 
-        @DoNotInline
         static WindowInsets getDecorViewInsets(WindowInsets insets) {
             return new WindowInsets.Builder(insets).setInsets(
                     WindowInsets.Type.displayCutout(), Insets.NONE).build();
         }
 
-        @DoNotInline
         static void setDecorFitsSystemWindows(BaseCarAppActivity activity, Window window,
                 boolean decorFitsSystemWindows) {
             // Set mDecorFitsSystemWindows so we can retrieve its value for testing.
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/TemplateSurfaceView.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/TemplateSurfaceView.java
index c10bcfc..f76ee3e 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/TemplateSurfaceView.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/renderer/surface/TemplateSurfaceView.java
@@ -40,7 +40,6 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -341,17 +340,14 @@
         private Api30Impl() {
         }
 
-        @DoNotInline
         static IBinder getHostToken(TemplateSurfaceView view) {
             return view.getHostToken();
         }
 
-        @DoNotInline
         static void setSurfacePackage(TemplateSurfaceView view, SurfacePackage surfacePackage) {
             view.setChildSurfacePackage(surfacePackage);
         }
 
-        @DoNotInline
         public static void releaseSurfacePackage(SurfacePackage surfacePackage) {
             surfacePackage.release();
         }
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
index de431f2..2cd57df 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/hardware/info/AutomotiveCarInfo.java
@@ -43,7 +43,6 @@
 import android.os.Build;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
@@ -411,7 +410,6 @@
 
     @RequiresApi(31)
     private static class Api31Impl {
-        @DoNotInline
         static void addTollListener(Executor executor,
                 OnCarDataAvailableListener<TollCard> listener, PropertyManager propertyManager,
                 Map<OnCarDataAvailableListener<?>, OnCarPropertyResponseListener> listenerMap) {
@@ -421,7 +419,6 @@
             listenerMap.put(listener, tollListener);
         }
 
-        @DoNotInline
         static void removeTollListener(OnCarPropertyResponseListener listener,
                 PropertyManager propertyManager) {
             propertyManager.submitUnregisterListenerRequest(listener);
@@ -430,7 +427,6 @@
 
     @RequiresApi(30)
     private static class Api30Impl {
-        @DoNotInline
         static void populateExteriorDimensionsData(
                 @NonNull Executor executor,
                 OnCarDataAvailableListener<ExteriorDimensions> listener,
diff --git a/car/app/app-automotive/src/main/res/values-fr-rCA/strings.xml b/car/app/app-automotive/src/main/res/values-fr-rCA/strings.xml
index 1935a74..acc5bc5 100644
--- a/car/app/app-automotive/src/main/res/values-fr-rCA/strings.xml
+++ b/car/app/app-automotive/src/main/res/values-fr-rCA/strings.xml
@@ -17,10 +17,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="error_action_finish" msgid="7621130025103996211">"Fermer l\'application"</string>
+    <string name="error_action_finish" msgid="7621130025103996211">"Fermer l\'appli"</string>
     <string name="error_action_update_host" msgid="4802951804749609593">"Rechercher des mises à jour"</string>
     <string name="error_action_retry" msgid="985347670495166517">"Réessayer"</string>
-    <string name="error_message_client_side_error" msgid="3323186720368387787">"Erreur d\'application Veuillez signaler cette erreur au développeur de l\'application"</string>
+    <string name="error_message_client_side_error" msgid="3323186720368387787">"Erreur d\'appli Veuillez signaler cette erreur au développeur de l\'appli"</string>
     <string name="error_message_host_error" msgid="5484419926049675696">"Erreur système"</string>
     <string name="error_message_host_connection_lost" msgid="5723205987837759151">"Système momentanément inaccessible"</string>
     <string name="error_message_host_not_found" msgid="3241065067065670113">"Mise à jour du système requise"</string>
diff --git a/car/app/app-projected/build.gradle b/car/app/app-projected/build.gradle
index ce471c8..ca1efcf 100644
--- a/car/app/app-projected/build.gradle
+++ b/car/app/app-projected/build.gradle
@@ -33,7 +33,7 @@
     implementation(project(":car:app:app"))
     annotationProcessor(libs.nullaway)
 
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     testImplementation(libs.junit)
     testImplementation(libs.testCore)
@@ -66,5 +66,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Android Auto Projected specific funationaltiy to build navigation, parking, and charging apps for cars"
-    metalavaK2UastEnabled = true
 }
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
index 96ad8b9..0c12ecf 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
@@ -27,7 +27,7 @@
 
   <uses-sdk
       android:minSdkVersion="29"
-      android:targetSdkVersion="33" />
+      android:targetSdkVersion="34" />
 
   <uses-permission android:name="android.permission.INTERNET"/>
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
index 8261bab..c78bb4d 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fr-rCA/strings.xml
@@ -110,10 +110,10 @@
     <string name="route_list_limit" msgid="505793441615134116">"Limite de la liste d\'itinéraires"</string>
     <string name="content_limits" msgid="5726880972110281095">"Limites de contenu"</string>
     <string name="content_limits_demo_title" msgid="3207211638386727610">"Démo des limites de contenu"</string>
-    <string name="finish_app_msg" msgid="8354334557053141891">"L\'application sera fermée, et un écran d\'autorisation sera ajouté quand vous la rouvrirez"</string>
-    <string name="finish_app_title" msgid="9013328479438745074">"Terminer la démo de l\'application"</string>
+    <string name="finish_app_msg" msgid="8354334557053141891">"L\'appli sera fermée, et un écran d\'autorisation sera ajouté quand vous la rouvrirez"</string>
+    <string name="finish_app_title" msgid="9013328479438745074">"Terminer la démo de l\'appli"</string>
     <string name="finish_app_demo_title" msgid="8223819062053448384">"Ajout de l\'écran d\'autorisation lors de la prochaine exécution de la démo"</string>
-    <string name="preseed_permission_app_title" msgid="182847662545676962">"Ajout d\'autorisations à l\'application de démonstration"</string>
+    <string name="preseed_permission_app_title" msgid="182847662545676962">"Ajout d\'autorisations à l\'appli de démonstration"</string>
     <string name="preseed_permission_demo_title" msgid="5476541421753978071">"Ajout de l\'écran d\'autorisation lors de la prochaine exécution de la démo"</string>
     <string name="loading_demo_title" msgid="1086529475809143517">"Chargement de la démo en cours…"</string>
     <string name="loading_demo_row_title" msgid="8933049915126088142">"Chargement terminé!"</string>
@@ -124,7 +124,7 @@
     <string name="pop_to_title" msgid="3924696281273379455">"Aller à la démo"</string>
     <string name="package_not_found_error_msg" msgid="7525619456883627939">"Ensemble non trouvé."</string>
     <string name="permissions_granted_msg" msgid="2348556088141992714">"Toutes les autorisations ont été accordées. Veuillez révoquer les autorisations dans Paramètres."</string>
-    <string name="needs_access_msg_prefix" msgid="2204136858798832382">"L\'application a besoin d\'accéder aux autorisations suivantes :\n"</string>
+    <string name="needs_access_msg_prefix" msgid="2204136858798832382">"L\'appli a besoin d\'accéder aux autorisations suivantes :\n"</string>
     <string name="phone_screen_permission_msg" msgid="3599815596923367256">"Accorder l\'autorisation sur l\'écran du téléphone"</string>
     <string name="enable_location_permission_on_device_msg" msgid="472752487966156897">"Activer les autorisations de localisation sur l\'appareil"</string>
     <string name="enable_location_permission_on_phone_msg" msgid="5082615523959139121">"Activer la position sur l\'écran du téléphone"</string>
@@ -133,8 +133,8 @@
     <string name="cancel_reservation_title" msgid="1374986823057959608">"Écran d\'annulation de la réservation"</string>
     <string name="reservation_cancelled_msg" msgid="6334213670275547875">"Réservation annulée"</string>
     <string name="result_demo_title" msgid="3900525190662148290">"Démo du résultat"</string>
-    <string name="not_started_for_result_msg" msgid="7498800528148447270">"Cette application n\'a pas été lancée à partir du résultat"</string>
-    <string name="started_for_result_msg" msgid="4225260243713833974">"Cette application a été lancée à partir du résultat de %s. Veuillez sélectionner le résultat à renvoyer à l\'appelant"</string>
+    <string name="not_started_for_result_msg" msgid="7498800528148447270">"Cette appli n\'a pas été lancée à partir du résultat"</string>
+    <string name="started_for_result_msg" msgid="4225260243713833974">"Cette appli a été lancée à partir du résultat de %s. Veuillez sélectionner le résultat à renvoyer à l\'appelant"</string>
     <string name="arrived_exclamation_msg" msgid="9132265698563096988">"Arrivée!"</string>
     <string name="travel_est_trip_text" msgid="5134365408383171144">"Aller chercher Alice"</string>
     <string name="send_notification_title" msgid="4731688444696028193">"Envoyer une notification"</string>
@@ -271,14 +271,14 @@
     <string name="icon_title_prefix" msgid="8487026131229541244">"Icône"</string>
     <string name="content_provider_icons_demo_title" msgid="5708602618664311097">"Démo des icônes du fournisseur de contenu"</string>
     <string name="icons_demo_title" msgid="4082976685262307357">"Démo d\'icônes"</string>
-    <string name="app_icon_title" msgid="7534936683349723423">"L\'icône de l\'application"</string>
+    <string name="app_icon_title" msgid="7534936683349723423">"L\'icône de l\'appli"</string>
     <string name="vector_no_tint_title" msgid="874591632279039146">"Une vectorielle dessinable, sans teinte"</string>
     <string name="vector_with_tint_title" msgid="1022346419829696827">"Une vectorielle dessinable, avec teinte"</string>
-    <string name="vector_with_app_theme_attr_title" msgid="4890094482708376219">"Une vectorielle dessinable, avec comme couleur l\'attribut du thème d\'une application"</string>
+    <string name="vector_with_app_theme_attr_title" msgid="4890094482708376219">"Une vectorielle dessinable, avec comme couleur l\'attribut du thème d\'une appli"</string>
     <string name="png_res_title" msgid="7437083018336747544">"Un PNG, envoyé comme ressource"</string>
     <string name="png_bitmap_title" msgid="3385912074130977032">"Un PNG, envoyé comme table de bits"</string>
     <string name="just_row_title" msgid="965700021568970725">"Juste un titre"</string>
-    <string name="title_with_app_icon_row_title" msgid="6294250714820740520">"Titre avec icône d\'application"</string>
+    <string name="title_with_app_icon_row_title" msgid="6294250714820740520">"Titre avec icône d\'appli"</string>
     <string name="title_with_res_id_image_row_title" msgid="3813134904602875778">"Titre avec image d\'identifiant de ressource"</string>
     <string name="title_with_svg_image_row_title" msgid="6109092343637263755">"Titre avec image SVG"</string>
     <string name="colored_secondary_row_title" msgid="5173288934252528929">"Texte secondaire en couleur"</string>
@@ -337,11 +337,11 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Écran d\'autorisation à l\'accès vocal"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interactions avec l\'utilisateur"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demande d\'autorisation pour les démos."</string>
-    <string name="application_overflow_title" msgid="396427940886169325">"Outil de validation de menu à développer d\'application"</string>
+    <string name="application_overflow_title" msgid="396427940886169325">"Outil de validation de menu à développer d\'appli"</string>
     <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Veuillez tester les modèles suivants lorsque vous basculez"</string>
     <string name="application_overflow_subtitle2" msgid="4385123036846369714">"l\'état du véhicule stationné à celui de conduite"</string>
     <string name="perm_group" msgid="3834918337351876270">"Groupe d\'autorisations"</string>
-    <string name="perm_group_description" msgid="7348847631139139024">"Groupe d\'autorisations pour l\'application Showcase"</string>
+    <string name="perm_group_description" msgid="7348847631139139024">"Groupe d\'autorisations pour l\'appli Showcase"</string>
     <string name="perm_fine_location" msgid="5438874642600304118">"Accès à la localisation précise"</string>
     <string name="perm_fine_location_desc" msgid="3549183883787912516">"Autorisation pour l\'accès à la localisation précise"</string>
     <string name="perm_coarse_location" msgid="6140337431619481015">"Accès à la localisation imprécise"</string>
diff --git a/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml b/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
index 6f205ae..a5b0cdd 100644
--- a/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
@@ -27,7 +27,7 @@
 
   <uses-sdk
       android:minSdkVersion="23"
-      android:targetSdkVersion="33" />
+      android:targetSdkVersion="34" />
 
   <uses-permission android:name="android.permission.INTERNET"/>
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
diff --git a/car/app/app-testing/api/1.7.0-beta01.txt b/car/app/app-testing/api/1.7.0-beta01.txt
index 57cf025..b478e9b 100644
--- a/car/app/app-testing/api/1.7.0-beta01.txt
+++ b/car/app/app-testing/api/1.7.0-beta01.txt
@@ -42,6 +42,12 @@
     method public java.util.List<java.lang.String!> getPermissionsRequested();
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class TestDelegateInvoker {
+    method public <T> java.util.List<T> requestAllItemsForTest(androidx.car.app.serialization.ListDelegate<? extends T>);
+    method public <T> java.util.List<T> requestItemRangeForTest(androidx.car.app.serialization.ListDelegate<? extends T>, int startIndex, int endIndex);
+    field public static final androidx.car.app.testing.TestDelegateInvoker INSTANCE;
+  }
+
   public class TestScreenManager extends androidx.car.app.ScreenManager {
     method public java.util.List<androidx.car.app.Screen!> getScreensPushed();
     method public java.util.List<androidx.car.app.Screen!> getScreensRemoved();
diff --git a/car/app/app-testing/api/current.txt b/car/app/app-testing/api/current.txt
index 57cf025..b478e9b 100644
--- a/car/app/app-testing/api/current.txt
+++ b/car/app/app-testing/api/current.txt
@@ -42,6 +42,12 @@
     method public java.util.List<java.lang.String!> getPermissionsRequested();
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class TestDelegateInvoker {
+    method public <T> java.util.List<T> requestAllItemsForTest(androidx.car.app.serialization.ListDelegate<? extends T>);
+    method public <T> java.util.List<T> requestItemRangeForTest(androidx.car.app.serialization.ListDelegate<? extends T>, int startIndex, int endIndex);
+    field public static final androidx.car.app.testing.TestDelegateInvoker INSTANCE;
+  }
+
   public class TestScreenManager extends androidx.car.app.ScreenManager {
     method public java.util.List<androidx.car.app.Screen!> getScreensPushed();
     method public java.util.List<androidx.car.app.Screen!> getScreensRemoved();
diff --git a/car/app/app-testing/api/restricted_1.7.0-beta01.txt b/car/app/app-testing/api/restricted_1.7.0-beta01.txt
index 57cf025..b478e9b 100644
--- a/car/app/app-testing/api/restricted_1.7.0-beta01.txt
+++ b/car/app/app-testing/api/restricted_1.7.0-beta01.txt
@@ -42,6 +42,12 @@
     method public java.util.List<java.lang.String!> getPermissionsRequested();
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class TestDelegateInvoker {
+    method public <T> java.util.List<T> requestAllItemsForTest(androidx.car.app.serialization.ListDelegate<? extends T>);
+    method public <T> java.util.List<T> requestItemRangeForTest(androidx.car.app.serialization.ListDelegate<? extends T>, int startIndex, int endIndex);
+    field public static final androidx.car.app.testing.TestDelegateInvoker INSTANCE;
+  }
+
   public class TestScreenManager extends androidx.car.app.ScreenManager {
     method public java.util.List<androidx.car.app.Screen!> getScreensPushed();
     method public java.util.List<androidx.car.app.Screen!> getScreensRemoved();
diff --git a/car/app/app-testing/api/restricted_current.txt b/car/app/app-testing/api/restricted_current.txt
index 57cf025..b478e9b 100644
--- a/car/app/app-testing/api/restricted_current.txt
+++ b/car/app/app-testing/api/restricted_current.txt
@@ -42,6 +42,12 @@
     method public java.util.List<java.lang.String!> getPermissionsRequested();
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class TestDelegateInvoker {
+    method public <T> java.util.List<T> requestAllItemsForTest(androidx.car.app.serialization.ListDelegate<? extends T>);
+    method public <T> java.util.List<T> requestItemRangeForTest(androidx.car.app.serialization.ListDelegate<? extends T>, int startIndex, int endIndex);
+    field public static final androidx.car.app.testing.TestDelegateInvoker INSTANCE;
+  }
+
   public class TestScreenManager extends androidx.car.app.ScreenManager {
     method public java.util.List<androidx.car.app.Screen!> getScreensPushed();
     method public java.util.List<androidx.car.app.Screen!> getScreensRemoved();
diff --git a/car/app/app-testing/build.gradle b/car/app/app-testing/build.gradle
index 9220169..c3d9073 100644
--- a/car/app/app-testing/build.gradle
+++ b/car/app/app-testing/build.gradle
@@ -26,15 +26,19 @@
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
+    id("org.jetbrains.kotlin.android")
 }
 
 dependencies {
     api(project(":car:app:app"))
+
     implementation "androidx.lifecycle:lifecycle-runtime:2.2.0"
     implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
     implementation 'androidx.annotation:annotation:1.1.0'
     implementation(libs.robolectric)
     implementation("androidx.annotation:annotation-experimental:1.4.1")
+    api(libs.kotlinStdlib)
+    implementation(libs.kotlinStdlibCommon)
 
     testImplementation(project(":car:app:app-projected"))
     testImplementation(libs.junit)
@@ -60,5 +64,4 @@
     type = LibraryType.PUBLISHED_TEST_LIBRARY
     inceptionYear = "2021"
     description = "androidx.car.app:app-testing"
-    metalavaK2UastEnabled = true
 }
diff --git a/car/app/app-testing/src/main/java/androidx/car/app/testing/TestDelegateInvoker.kt b/car/app/app-testing/src/main/java/androidx/car/app/testing/TestDelegateInvoker.kt
new file mode 100644
index 0000000..44660ce
--- /dev/null
+++ b/car/app/app-testing/src/main/java/androidx/car/app/testing/TestDelegateInvoker.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright (C) 2024 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.app.testing
+
+import android.annotation.SuppressLint
+import androidx.car.app.OnDoneCallback
+import androidx.car.app.annotations.ExperimentalCarApi
+import androidx.car.app.serialization.Bundleable
+import androidx.car.app.serialization.ListDelegate
+
+/** Provides a simplified interface for invoking AIDL/delegate APIs in tests */
+@SuppressLint("NullAnnotationGroup")
+@ExperimentalCarApi
+public object TestDelegateInvoker {
+    public fun <T> ListDelegate<T>.requestAllItemsForTest(): List<T> =
+        requestItemRangeForTest(0, size - 1)
+
+    public fun <T> ListDelegate<T>.requestItemRangeForTest(
+        startIndex: Int,
+        endIndex: Int
+    ): List<T> = runForResult {
+        [email protected](startIndex, endIndex, it)
+    }
+
+    private fun <T> runForResult(f: (OnDoneCallback) -> Unit): T {
+        val callback = ResponseCapturingOnDoneCallback<T>()
+        f(callback)
+
+        // Generally, tests run in a single process.
+        // Therefore, Host/Client AIDL logic runs synchronously.
+        // Therefore, we assume the callback was fulfilled, without waiting.
+        return callback.getResponseOrCrash()
+    }
+
+    /**
+     * [OnDoneCallback] implementation for testing
+     *
+     * This class captures and stores the [Bundleable] response (if any), and unmarshalls it to the
+     * specified type.
+     */
+    private class ResponseCapturingOnDoneCallback<TResponse> : OnDoneCallback {
+        // "null" is a valid response
+        private var hasResponse = false
+        private var response: Bundleable? = null
+
+        override fun onSuccess(response: Bundleable?) {
+            check(!hasResponse) {
+                "Callback was invoked multiple times. Please create a new callback for each API call."
+            }
+            hasResponse = true
+            this.response = response
+        }
+
+        override fun onFailure(response: Bundleable) {
+            error("OnDone callbacks should never fail in tests")
+        }
+
+        fun getResponseOrCrash(): TResponse {
+            check(hasResponse) { "Callback was never invoked." }
+
+            @Suppress("UNCHECKED_CAST") return response?.get() as TResponse
+        }
+    }
+}
diff --git a/car/app/app-testing/src/test/java/androidx/car/app/testing/TestDelegateInvokerTest.kt b/car/app/app-testing/src/test/java/androidx/car/app/testing/TestDelegateInvokerTest.kt
new file mode 100644
index 0000000..5e3374f
--- /dev/null
+++ b/car/app/app-testing/src/test/java/androidx/car/app/testing/TestDelegateInvokerTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.app.testing
+
+import androidx.car.app.serialization.ListDelegateImpl
+import androidx.car.app.testing.TestDelegateInvoker.requestAllItemsForTest
+import androidx.car.app.testing.TestDelegateInvoker.requestItemRangeForTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+/** Tests for {@link TestDelegateInvoker}. */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+class TestDelegateInvokerTest {
+    @Test
+    fun requestAllItemsForTest() {
+        val numbers = List(10) { it }
+        val listDelegate = ListDelegateImpl(numbers)
+
+        assertThat(listDelegate.requestAllItemsForTest()).containsExactlyElementsIn(numbers)
+    }
+
+    @Test
+    fun requestItemRangeForTest() {
+        val numbers = List(10) { it }
+        val listDelegate = ListDelegateImpl(numbers)
+
+        assertThat(listDelegate.requestItemRangeForTest(3, 5))
+            .containsExactlyElementsIn(numbers.subList(3, 6)) // indices 3, 4, 5
+    }
+}
diff --git a/car/app/app/api/1.7.0-beta01.txt b/car/app/app/api/1.7.0-beta01.txt
index 958295c..3b4c1e5 100644
--- a/car/app/app/api/1.7.0-beta01.txt
+++ b/car/app/app/api/1.7.0-beta01.txt
@@ -1350,7 +1350,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class GridSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.GridItem!,androidx.car.app.model.GridSection.Builder!> {
     ctor public GridSection.Builder();
-    ctor public GridSection.Builder(androidx.car.app.model.GridSection);
     method public androidx.car.app.model.GridSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemImageShape(int);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemSize(int);
@@ -1694,7 +1693,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class RowSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.Row!,androidx.car.app.model.RowSection.Builder!> {
     ctor public RowSection.Builder();
-    ctor public RowSection.Builder(androidx.car.app.model.RowSection);
     method public androidx.car.app.model.RowSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder clearSelectionGroup();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder setAsSelectionGroup(int);
@@ -1736,14 +1734,13 @@
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public abstract class Section<T extends androidx.car.app.model.Item> {
     ctor protected Section();
     ctor protected Section(androidx.car.app.model.Section.BaseBuilder<T!,? extends java.lang.Object!>);
-    method public java.util.List<T!> getItems();
+    method public androidx.car.app.serialization.ListDelegate<T!> getItemsDelegate();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
     method public androidx.car.app.model.CarText? getTitle();
   }
 
   protected abstract static class Section.BaseBuilder<T extends androidx.car.app.model.Item, B> {
     ctor protected Section.BaseBuilder();
-    ctor protected Section.BaseBuilder(androidx.car.app.model.Section<T!>);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B addItem(T);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B clearItems();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B setItems(java.util.List<T!>);
@@ -2384,6 +2381,12 @@
     ctor public BundlerException(String?, Throwable);
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public interface ListDelegate<T> {
+    method public int getSize();
+    method public void requestItemRange(int startIndex, int endIndex, androidx.car.app.OnDoneCallback callback);
+    property public abstract int size;
+  }
+
 }
 
 package androidx.car.app.suggestion {
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 958295c..3b4c1e5 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -1350,7 +1350,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class GridSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.GridItem!,androidx.car.app.model.GridSection.Builder!> {
     ctor public GridSection.Builder();
-    ctor public GridSection.Builder(androidx.car.app.model.GridSection);
     method public androidx.car.app.model.GridSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemImageShape(int);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemSize(int);
@@ -1694,7 +1693,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class RowSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.Row!,androidx.car.app.model.RowSection.Builder!> {
     ctor public RowSection.Builder();
-    ctor public RowSection.Builder(androidx.car.app.model.RowSection);
     method public androidx.car.app.model.RowSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder clearSelectionGroup();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder setAsSelectionGroup(int);
@@ -1736,14 +1734,13 @@
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public abstract class Section<T extends androidx.car.app.model.Item> {
     ctor protected Section();
     ctor protected Section(androidx.car.app.model.Section.BaseBuilder<T!,? extends java.lang.Object!>);
-    method public java.util.List<T!> getItems();
+    method public androidx.car.app.serialization.ListDelegate<T!> getItemsDelegate();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
     method public androidx.car.app.model.CarText? getTitle();
   }
 
   protected abstract static class Section.BaseBuilder<T extends androidx.car.app.model.Item, B> {
     ctor protected Section.BaseBuilder();
-    ctor protected Section.BaseBuilder(androidx.car.app.model.Section<T!>);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B addItem(T);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B clearItems();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B setItems(java.util.List<T!>);
@@ -2384,6 +2381,12 @@
     ctor public BundlerException(String?, Throwable);
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public interface ListDelegate<T> {
+    method public int getSize();
+    method public void requestItemRange(int startIndex, int endIndex, androidx.car.app.OnDoneCallback callback);
+    property public abstract int size;
+  }
+
 }
 
 package androidx.car.app.suggestion {
diff --git a/car/app/app/api/restricted_1.7.0-beta01.txt b/car/app/app/api/restricted_1.7.0-beta01.txt
index 958295c..3b4c1e5 100644
--- a/car/app/app/api/restricted_1.7.0-beta01.txt
+++ b/car/app/app/api/restricted_1.7.0-beta01.txt
@@ -1350,7 +1350,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class GridSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.GridItem!,androidx.car.app.model.GridSection.Builder!> {
     ctor public GridSection.Builder();
-    ctor public GridSection.Builder(androidx.car.app.model.GridSection);
     method public androidx.car.app.model.GridSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemImageShape(int);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemSize(int);
@@ -1694,7 +1693,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class RowSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.Row!,androidx.car.app.model.RowSection.Builder!> {
     ctor public RowSection.Builder();
-    ctor public RowSection.Builder(androidx.car.app.model.RowSection);
     method public androidx.car.app.model.RowSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder clearSelectionGroup();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder setAsSelectionGroup(int);
@@ -1736,14 +1734,13 @@
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public abstract class Section<T extends androidx.car.app.model.Item> {
     ctor protected Section();
     ctor protected Section(androidx.car.app.model.Section.BaseBuilder<T!,? extends java.lang.Object!>);
-    method public java.util.List<T!> getItems();
+    method public androidx.car.app.serialization.ListDelegate<T!> getItemsDelegate();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
     method public androidx.car.app.model.CarText? getTitle();
   }
 
   protected abstract static class Section.BaseBuilder<T extends androidx.car.app.model.Item, B> {
     ctor protected Section.BaseBuilder();
-    ctor protected Section.BaseBuilder(androidx.car.app.model.Section<T!>);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B addItem(T);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B clearItems();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B setItems(java.util.List<T!>);
@@ -2384,6 +2381,12 @@
     ctor public BundlerException(String?, Throwable);
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public interface ListDelegate<T> {
+    method public int getSize();
+    method public void requestItemRange(int startIndex, int endIndex, androidx.car.app.OnDoneCallback callback);
+    property public abstract int size;
+  }
+
 }
 
 package androidx.car.app.suggestion {
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 958295c..3b4c1e5 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -1350,7 +1350,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class GridSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.GridItem!,androidx.car.app.model.GridSection.Builder!> {
     ctor public GridSection.Builder();
-    ctor public GridSection.Builder(androidx.car.app.model.GridSection);
     method public androidx.car.app.model.GridSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemImageShape(int);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.GridSection.Builder setItemSize(int);
@@ -1694,7 +1693,6 @@
 
   @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final class RowSection.Builder extends androidx.car.app.model.Section.BaseBuilder<androidx.car.app.model.Row!,androidx.car.app.model.RowSection.Builder!> {
     ctor public RowSection.Builder();
-    ctor public RowSection.Builder(androidx.car.app.model.RowSection);
     method public androidx.car.app.model.RowSection build();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder clearSelectionGroup();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public androidx.car.app.model.RowSection.Builder setAsSelectionGroup(int);
@@ -1736,14 +1734,13 @@
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public abstract class Section<T extends androidx.car.app.model.Item> {
     ctor protected Section();
     ctor protected Section(androidx.car.app.model.Section.BaseBuilder<T!,? extends java.lang.Object!>);
-    method public java.util.List<T!> getItems();
+    method public androidx.car.app.serialization.ListDelegate<T!> getItemsDelegate();
     method public androidx.car.app.model.CarText? getNoItemsMessage();
     method public androidx.car.app.model.CarText? getTitle();
   }
 
   protected abstract static class Section.BaseBuilder<T extends androidx.car.app.model.Item, B> {
     ctor protected Section.BaseBuilder();
-    ctor protected Section.BaseBuilder(androidx.car.app.model.Section<T!>);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B addItem(T);
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B clearItems();
     method @com.google.errorprone.annotations.CanIgnoreReturnValue public B setItems(java.util.List<T!>);
@@ -2384,6 +2381,12 @@
     ctor public BundlerException(String?, Throwable);
   }
 
+  @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public interface ListDelegate<T> {
+    method public int getSize();
+    method public void requestItemRange(int startIndex, int endIndex, androidx.car.app.OnDoneCallback callback);
+    property public abstract int size;
+  }
+
 }
 
 package androidx.car.app.suggestion {
diff --git a/car/app/app/build.gradle b/car/app/app/build.gradle
index 1c26d21..e689bf5 100644
--- a/car/app/app/build.gradle
+++ b/car/app/app/build.gradle
@@ -56,7 +56,7 @@
     // OnBackPressedDispatcher is part of the API
     api("androidx.activity:activity:1.2.0")
     implementation(libs.guavaAndroid)
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.7.0")
     implementation("androidx.lifecycle:lifecycle-viewmodel:2.2.0")
     implementation ("androidx.media:media:1.6.0")
@@ -110,7 +110,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Build navigation, parking, and charging apps for Android Auto"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/car/app/app/lint-baseline.xml b/car/app/app/lint-baseline.xml
index e10674d..c9b0d1d 100644
--- a/car/app/app/lint-baseline.xml
+++ b/car/app/app/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="MissingPermission"
@@ -57,7 +57,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.ZonedDateTime#parse`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.ZonedDateTime#parse`"
         errorLine1="        ZonedDateTime zonedDateTime = ZonedDateTime.parse(&quot;2020-05-14T19:57:00-07:00[US/Pacific]&quot;);"
         errorLine2="                                                    ~~~~~">
         <location
@@ -93,7 +93,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#getSeconds`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#getSeconds`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                                                                            ~~~~~~~~~~">
         <location
@@ -102,7 +102,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#ofMillis`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#ofMillis`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                         ~~~~~~~~">
         <location
@@ -111,7 +111,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#getSeconds`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#getSeconds`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                                                                            ~~~~~~~~~~">
         <location
@@ -120,7 +120,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#ofMillis`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#ofMillis`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                         ~~~~~~~~">
         <location
@@ -129,7 +129,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#getSeconds`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#getSeconds`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                                                                            ~~~~~~~~~~">
         <location
@@ -138,7 +138,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#ofMillis`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#ofMillis`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                         ~~~~~~~~">
         <location
@@ -147,7 +147,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#getSeconds`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#getSeconds`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                                                                            ~~~~~~~~~~">
         <location
@@ -156,7 +156,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#ofMillis`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#ofMillis`"
         errorLine1="                Duration.ofMillis(timeZone.getOffset(timeSinceEpochMillis)).getSeconds();"
         errorLine2="                         ~~~~~~~~">
         <location
@@ -165,7 +165,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Collection#stream`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Collection#stream`"
         errorLine1="                .stream()"
         errorLine2="                 ~~~~~~">
         <location
@@ -174,7 +174,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.stream.Stream#mapToLong`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.stream.Stream#mapToLong`"
         errorLine1="                .mapToLong(List::size)"
         errorLine2="                 ~~~~~~~~~">
         <location
@@ -183,7 +183,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.stream.LongStream#sum`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.stream.LongStream#sum`"
         errorLine1="                .sum()).isEqualTo(6);"
         errorLine2="                 ~~~">
         <location
@@ -228,7 +228,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.ZonedDateTime#parse`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.ZonedDateTime#parse`"
         errorLine1="        ZonedDateTime arrivalTime = ZonedDateTime.parse(&quot;2020-05-14T19:57:00-07:00[US/Pacific]&quot;);"
         errorLine2="                                                  ~~~~~">
         <location
@@ -237,7 +237,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#ofHours`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#ofHours`"
         errorLine1="        Duration remainingTime = Duration.ofHours(10);"
         errorLine2="                                          ~~~~~~~">
         <location
@@ -264,7 +264,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#getSeconds`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#getSeconds`"
         errorLine1="        assertThat(travelEstimate.getRemainingTimeSeconds()).isEqualTo(remainingTime.getSeconds());"
         errorLine2="                                                                                     ~~~~~~~~~~">
         <location
@@ -282,7 +282,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.ZonedDateTime#parse`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.ZonedDateTime#parse`"
         errorLine1="        ZonedDateTime arrivalTime = ZonedDateTime.parse(&quot;2020-05-14T19:57:00-07:00[US/Pacific]&quot;);"
         errorLine2="                                                  ~~~~~">
         <location
@@ -309,7 +309,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 26 (current min is 21): `java.time.Duration#ofSeconds`"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Duration#ofSeconds`"
         errorLine1="                        Duration.ofSeconds(REMAINING_TIME_UNKNOWN)).build();"
         errorLine2="                                 ~~~~~~~~~">
         <location
@@ -327,7 +327,7 @@
 
     <issue
         id="WrongConstant"
-        message="Must be one of: CarAppApiLevels.UNKNOWN, CarAppApiLevels.LEVEL_1, CarAppApiLevels.LEVEL_2, CarAppApiLevels.LEVEL_3, CarAppApiLevels.LEVEL_4, CarAppApiLevels.LEVEL_5, CarAppApiLevels.LEVEL_6, CarAppApiLevels.LEVEL_7"
+        message="Must be one of: CarAppApiLevels.UNKNOWN, CarAppApiLevels.LEVEL_1, CarAppApiLevels.LEVEL_2, CarAppApiLevels.LEVEL_3, CarAppApiLevels.LEVEL_4, CarAppApiLevels.LEVEL_5, CarAppApiLevels.LEVEL_6, CarAppApiLevels.LEVEL_7, CarAppApiLevels.LEVEL_8"
         errorLine1="        mCarAppApiLevel = handshakeInfo.getHostCarAppApiLevel();"
         errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java b/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java
index 2ba356d..35bdabb 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppMetadataHolderService.java
@@ -25,7 +25,6 @@
 import android.os.Build;
 import android.os.IBinder;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -69,7 +68,6 @@
 
     @RequiresApi(24)
     private static class Api24Impl {
-        @DoNotInline
         static int getDisabledComponentFlag() {
             return PackageManager.MATCH_DISABLED_COMPONENTS;
         }
diff --git a/car/app/app/src/main/java/androidx/car/app/CarContext.java b/car/app/app/src/main/java/androidx/car/app/CarContext.java
index 3a52126..1e97f2d 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarContext.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarContext.java
@@ -42,7 +42,6 @@
 
 import androidx.activity.OnBackPressedCallback;
 import androidx.activity.OnBackPressedDispatcher;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -770,7 +769,6 @@
     @RequiresApi(api = VERSION_CODES.O)
     private static class Api26Impl {
 
-        @DoNotInline
         static Bundle makeBasicActivityOptionsBundle() {
             return ActivityOptions.makeBasic()
                     .setLaunchDisplayId(Display.DEFAULT_DISPLAY).toBundle();
diff --git a/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java b/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java
index 0ee4c24..f26fb64 100644
--- a/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java
+++ b/car/app/app/src/main/java/androidx/car/app/SessionInfoIntentEncoder.java
@@ -20,7 +20,6 @@
 import android.os.Build;
 import android.os.Bundle;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -95,7 +94,6 @@
         }
 
         /** 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/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java b/car/app/app/src/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java
index f8f6a3c..1619b6c 100644
--- a/car/app/app/src/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java
+++ b/car/app/app/src/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java
@@ -31,7 +31,6 @@
 import android.os.Build;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 import androidx.car.app.connection.CarConnection.ConnectionType;
@@ -134,7 +133,6 @@
             // Not instantiable
         }
 
-        @DoNotInline
         static void registerExportedReceiver(Context context, BroadcastReceiver broadcastReceiver,
                 IntentFilter intentFilter) {
             context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java b/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
index 4807010..a30414b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DateTimeWithZone.java
@@ -23,7 +23,6 @@
 
 import android.annotation.SuppressLint;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -212,7 +211,6 @@
         private Api26Impl() {
         }
 
-        @DoNotInline
         @NonNull
         public static DateTimeWithZone create(@NonNull ZonedDateTime zonedDateTime) {
             LocalDateTime localDateTime = requireNonNull(zonedDateTime).toLocalDateTime();
diff --git a/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java b/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
index baf74f7..eb90838 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/DurationSpan.java
@@ -20,7 +20,6 @@
 
 import android.annotation.SuppressLint;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -120,7 +119,6 @@
         private Api26Impl() {
         }
 
-        @DoNotInline
         @NonNull
         public static DurationSpan create(@NonNull Duration duration) {
             return new DurationSpan(requireNonNull(duration).getSeconds());
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridSection.java b/car/app/app/src/main/java/androidx/car/app/model/GridSection.java
index 9718f2a..6f25eb1 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridSection.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridSection.java
@@ -160,13 +160,6 @@
             super();
         }
 
-        /** Create a new {@link GridSection} builder, copying values from an existing instance. */
-        public Builder(@NonNull GridSection gridSection) {
-            super(gridSection);
-            mItemSize = gridSection.mItemSize;
-            mItemImageShape = gridSection.mItemImageShape;
-        }
-
         /** Sets the size of the items within this section. */
         @NonNull
         @CanIgnoreReturnValue
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Row.java b/car/app/app/src/main/java/androidx/car/app/model/Row.java
index c1b5a4e..8503cfb 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Row.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Row.java
@@ -559,13 +559,13 @@
 
         /**
          * Adds an additional action to the end of the row.
+         * Note: From Car API 7 onwards, Rows are allowed to have 2 max actions to be set.
          *
          * @throws NullPointerException     if {@code action} is {@code null}
          * @throws IllegalArgumentException if {@code action} contains unsupported Action types,
          *                                  exceeds the maximum number of allowed actions or does
          *                                  not contain a valid {@link CarIcon}.
          */
-        //TODO(b/260557014): Update docs when half-list UX is defined
         @NonNull
         @RequiresCarApi(6)
         public Builder addAction(@NonNull Action action) {
diff --git a/car/app/app/src/main/java/androidx/car/app/model/RowSection.java b/car/app/app/src/main/java/androidx/car/app/model/RowSection.java
index 7603d2a..a2220d0 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/RowSection.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/RowSection.java
@@ -51,7 +51,8 @@
     }
 
     /**
-     * When set to a value that correlates to an index in {@link #getItems()}, this entire row
+     * When set to a value that correlates to an index in {@link #getItemsDelegate()}, this
+     * entire row
      * section should be treated as a selection group (eg. radio group). Otherwise this will be a
      * negative value to denote that this row section should not be transformed into a selection
      * group.
@@ -119,12 +120,6 @@
             super();
         }
 
-        /** Create a new {@link RowSection} builder, copying values from an existing instance. */
-        public Builder(@NonNull RowSection rowSection) {
-            super(rowSection);
-            mInitialSelectedIndex = rowSection.mInitialSelectedIndex;
-        }
-
         /**
          * Sets this entire {@link RowSection} as a selection group when passed a non-negative
          * integer correlating to a valid index within the list of items added. The UI behaves
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Section.java b/car/app/app/src/main/java/androidx/car/app/model/Section.java
index e000a0d..212006b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Section.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Section.java
@@ -22,6 +22,8 @@
 import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.model.constraints.CarTextConstraints;
+import androidx.car.app.serialization.ListDelegate;
+import androidx.car.app.serialization.ListDelegateImpl;
 
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 
@@ -41,7 +43,7 @@
 @ExperimentalCarApi
 public abstract class Section<T extends Item> {
     @NonNull
-    private final List<T> mItems;
+    private final ListDelegate<T> mItemsDelegate;
     @Nullable
     private final CarText mTitle;
     @Nullable
@@ -49,22 +51,22 @@
 
     // Empty constructor for serialization
     protected Section() {
-        mItems = Collections.emptyList();
+        mItemsDelegate = new ListDelegateImpl<>(Collections.emptyList());
         mTitle = null;
         mNoItemsMessage = null;
     }
 
     /** Constructor that fills out fields from any section builder. */
     protected Section(@NonNull BaseBuilder<T, ?> builder) {
-        mItems = Collections.unmodifiableList(builder.mItems);
+        mItemsDelegate = new ListDelegateImpl<>(Collections.unmodifiableList(builder.mItems));
         mTitle = builder.mHeader;
         mNoItemsMessage = builder.mNoItemsMessage;
     }
 
     /** Returns the items added to this section. */
     @NonNull
-    public List<T> getItems() {
-        return mItems;
+    public ListDelegate<T> getItemsDelegate() {
+        return mItemsDelegate;
     }
 
     /** Returns the optional text that should appear with the items in this section. */
@@ -91,20 +93,20 @@
         }
         Section<?> section = (Section<?>) other;
 
-        return Objects.equals(mItems, section.mItems) && Objects.equals(mTitle, section.mTitle)
+        return Objects.equals(mItemsDelegate, section.mItemsDelegate) && Objects.equals(mTitle,
+                section.mTitle)
                 && Objects.equals(mNoItemsMessage, section.mNoItemsMessage);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mItems, mTitle, mNoItemsMessage);
+        return Objects.hash(mItemsDelegate, mTitle, mNoItemsMessage);
     }
 
     @NonNull
     @Override
     public String toString() {
-        return "Section { items: " + mItems + ", title: " + mTitle + ", noItemsMessage: "
-                + mNoItemsMessage + " }";
+        return "Section";
     }
 
     /**
@@ -125,12 +127,6 @@
         protected BaseBuilder() {
         }
 
-        protected BaseBuilder(@NonNull Section<T> section) {
-            mItems = section.mItems;
-            mHeader = section.mTitle;
-            mNoItemsMessage = section.mNoItemsMessage;
-        }
-
         /** Sets the items for this section, overwriting any other previously set items. */
         @NonNull
         @CanIgnoreReturnValue
@@ -175,7 +171,7 @@
             CarText carText = CarText.create(title);
             CarTextConstraints.TEXT_ONLY.validateOrThrow(carText);
             mHeader = carText;
-            return  (B) this;
+            return (B) this;
         }
 
         /**
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index 67c55f7..46d8500 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -136,15 +136,15 @@
 
     /**
      * Constraints for additional row actions. Only allows custom actions.
+     * Note: From Car API 7 onwards, Rows are allowed to have 2 max actions to be set.
      */
-    //TODO(b/249225370): Allow multiple actions in the row.
     @NonNull
     public static final ActionsConstraints ACTIONS_CONSTRAINTS_ROW =
             new ActionsConstraints.Builder()
-                    .setMaxActions(1)
-                    .setMaxCustomTitles(1)
+                    .setMaxActions(2)
+                    .setMaxCustomTitles(2)
+                    .setMaxPrimaryActions(1)
                     .addAllowedActionType(Action.TYPE_CUSTOM)
-                    .setRequireActionIcons(true)
                     .setOnClickListenerAllowed(true)
                     .build();
 
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
index cd20b95..4051da3 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/TravelEstimate.java
@@ -20,12 +20,12 @@
 
 import android.annotation.SuppressLint;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.CarColor;
 import androidx.car.app.model.CarIcon;
@@ -35,7 +35,6 @@
 import androidx.car.app.model.constraints.CarColorConstraints;
 import androidx.car.app.model.constraints.CarIconConstraints;
 import androidx.car.app.model.constraints.CarTextConstraints;
-import androidx.car.app.annotations.KeepFields;
 
 import java.time.Duration;
 import java.time.ZonedDateTime;
@@ -387,7 +386,6 @@
             private Api26Impl() {
             }
 
-            @DoNotInline
             @NonNull
             public static Builder setRemainingTime(Builder builder,
                     @NonNull Duration remainingTime) {
diff --git a/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java b/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java
index 840a3d7..f1eddd4 100644
--- a/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/notification/CarNotificationManager.java
@@ -42,7 +42,6 @@
 import android.os.Build;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -504,7 +503,6 @@
          * Convert the list of {@link Notification.Action} to {@link NotificationCompat.Action} and
          * add them to the input {@code notification}.
          */
-        @DoNotInline
         static void convertActionsToCompatActions(@NonNull NotificationCompat.Builder notification,
                 @NonNull List<Notification.Action> actions) {
             if (actions.isEmpty()) {
diff --git a/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegate.kt b/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegate.kt
index 8e87b8c..4e99e87 100644
--- a/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegate.kt
+++ b/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegate.kt
@@ -16,8 +16,6 @@
 package androidx.car.app.serialization
 
 import android.annotation.SuppressLint
-import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope.LIBRARY
 import androidx.car.app.OnDoneCallback
 import androidx.car.app.annotations.ExperimentalCarApi
 
@@ -27,8 +25,7 @@
  * <p> Long lists are stored on the client for performance reasons.
  */
 @ExperimentalCarApi
-@RestrictTo(LIBRARY)
-interface ListDelegate<T> {
+interface ListDelegate<out T> {
     /** The size of the underlying [List] */
     val size: Int
 
diff --git a/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegateImpl.kt b/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegateImpl.kt
index 196397a..628be63 100644
--- a/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegateImpl.kt
+++ b/car/app/app/src/main/java/androidx/car/app/serialization/ListDelegateImpl.kt
@@ -31,10 +31,20 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 class ListDelegateImpl<T> : ListDelegate<T> {
     private var _size: Int = -1
+
+    /**
+     * The hash of the underlying list.
+     *
+     * This hash is used to determine whether two [ListDelegate]s contain the same items, without
+     * needing to load every item in the list.
+     */
+    private var listHashCode: Int = -1
+
     private lateinit var mStub: IRemoteList
 
     constructor(content: List<T>) {
         _size = content.size
+        listHashCode = content.hashCode()
         mStub = RemoteListStub<T>(content)
     }
 
@@ -60,6 +70,11 @@
         }
     }
 
+    override fun equals(other: Any?) =
+        other is ListDelegateImpl<*> && other.listHashCode == listHashCode
+
+    override fun hashCode(): Int = listHashCode
+
     private class RemoteListStub<T>(private val mContent: List<T>) : IRemoteList.Stub() {
         @Throws(RemoteException::class)
         override fun requestItemRange(startIndex: Int, endIndex: Int, callback: IOnDoneCallback) {
diff --git a/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java b/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java
index bb8fcd2..497619d 100644
--- a/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java
+++ b/car/app/app/src/main/java/androidx/car/app/validation/HostValidator.java
@@ -32,7 +32,6 @@
 import android.util.Pair;
 
 import androidx.annotation.ArrayRes;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -325,7 +324,6 @@
         private Api28Impl() {
         }
 
-        @DoNotInline
         @Nullable
         static Signature[] getSignatures(@NonNull PackageInfo packageInfo) {
             if (packageInfo.signingInfo == null) {
@@ -334,7 +332,6 @@
             return packageInfo.signingInfo.getSigningCertificateHistory();
         }
 
-        @DoNotInline
         @NonNull
         @SuppressWarnings("deprecation")
         static PackageInfo getPackageInfo(@NonNull PackageManager packageManager,
diff --git a/car/app/app/src/test/java/androidx/car/app/model/GridSectionTest.kt b/car/app/app/src/test/java/androidx/car/app/model/GridSectionTest.kt
index e6fe370..8d20b10 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/GridSectionTest.kt
+++ b/car/app/app/src/test/java/androidx/car/app/model/GridSectionTest.kt
@@ -46,26 +46,6 @@
     }
 
     @Test
-    fun builderFromObject() {
-        val section =
-            GridSection.Builder()
-                .setItemSize(GridSection.ITEM_SIZE_LARGE)
-                .setItemImageShape(GridSection.ITEM_IMAGE_SHAPE_CIRCLE)
-                .setItems(testItemList)
-                .setTitle(testHeader)
-                .setNoItemsMessage("Test no items message")
-                .build()
-
-        val result = GridSection.Builder(section).build()
-
-        assertThat(result.itemSize).isEqualTo(GridSection.ITEM_SIZE_LARGE)
-        assertThat(result.itemImageShape).isEqualTo(GridSection.ITEM_IMAGE_SHAPE_CIRCLE)
-        assertThat(result.items).containsExactlyElementsIn(testItemList)
-        assertThat(result.title).isEqualTo(testHeader)
-        assertThat(result.noItemsMessage).isEqualTo(CarText.create("Test no items message"))
-    }
-
-    @Test
     fun equals_returnsFalse_whenPassedNull() {
         val section = GridSection.Builder().build()
 
diff --git a/car/app/app/src/test/java/androidx/car/app/model/RowSectionTest.kt b/car/app/app/src/test/java/androidx/car/app/model/RowSectionTest.kt
index dd4680a..66aad825 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/RowSectionTest.kt
+++ b/car/app/app/src/test/java/androidx/car/app/model/RowSectionTest.kt
@@ -102,25 +102,6 @@
     }
 
     @Test
-    fun builderFromObject() {
-        val section =
-            RowSection.Builder()
-                .setItems(testItemList)
-                .setAsSelectionGroup(1)
-                .setTitle(testHeader)
-                .setNoItemsMessage(testNoItemsMessage)
-                .build()
-
-        val result = RowSection.Builder(section).build()
-
-        assertThat(result.items).containsExactlyElementsIn(testItemList)
-        assertThat(result.isSelectionGroup).isTrue()
-        assertThat(result.initialSelectedIndex).isEqualTo(1)
-        assertThat(result.title).isEqualTo(testHeader)
-        assertThat(result.noItemsMessage).isEqualTo(testNoItemsMessage)
-    }
-
-    @Test
     fun equals_returnsFalse_whenPassedNull() {
         val section = RowSection.Builder().build()
 
diff --git a/car/app/app/src/test/java/androidx/car/app/model/RowTest.java b/car/app/app/src/test/java/androidx/car/app/model/RowTest.java
index 533642f..45f65d5 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/RowTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/RowTest.java
@@ -272,14 +272,27 @@
     }
 
     @Test
-    public void addAction_invalidActionNullIcon_throws() {
-        Action customAction = TestUtils.createAction("Title", null);
+    public void addAction_twoActionsWithOnePrimary_doesNotThrow() {
+        CarIcon carIcon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        Action primaryAction = new Action.Builder().setTitle("Title").setFlags(
+                Action.FLAG_PRIMARY).build();
+        Action customAction = TestUtils.createAction("Title", carIcon);
+        Row row = new Row.Builder().setTitle("Title")
+                .addAction(customAction)
+                .addAction(primaryAction)
+                .build();
 
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> new Row.Builder().setTitle("Title")
-                        .addAction(customAction)
-                        .build());
+        assertThat(row.getActions().size()).isEqualTo(2);
+    }
+
+    @Test
+    public void addAction_textOnlyActionNullIcon_doesNotThrow() {
+        Action customAction = TestUtils.createAction("Title", null);
+        Row row = new Row.Builder().setTitle("Title")
+                .addAction(customAction)
+                .build();
+        assertThat(row.getActions().get(0)).isEqualTo(customAction);
     }
 
     public void addAction_browsableRow_throws() {
diff --git a/car/app/app/src/test/java/androidx/car/app/model/SectionTest.kt b/car/app/app/src/test/java/androidx/car/app/model/SectionTest.kt
index bccf051..318c749 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/SectionTest.kt
+++ b/car/app/app/src/test/java/androidx/car/app/model/SectionTest.kt
@@ -18,6 +18,7 @@
 
 import android.text.SpannableString
 import android.text.Spanned
+import androidx.car.app.testing.TestDelegateInvoker.requestAllItemsForTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Test
@@ -27,7 +28,9 @@
 @RunWith(RobolectricTestRunner::class)
 class SectionTest {
     /** An example item containing a uniquely identifying field. */
-    private data class TestItem(val someUniquelyIdentifyingField: Int) : Item
+    private data class TestItem(val someUniquelyIdentifyingField: Int) : Item {
+        constructor() : this(-1)
+    }
 
     /** An empty section implementation to test the base class. */
     private class TestSection(builder: Builder) : Section<TestItem>(builder) {
@@ -42,7 +45,7 @@
         val item = TestItem(1)
         val section = TestSection.Builder().addItem(item).build()
 
-        assertThat(section.items).containsExactly(item)
+        assertThat(section.itemsDelegate.requestAllItemsForTest()).containsExactly(item)
     }
 
     @Test
diff --git a/car/app/app/src/test/java/androidx/car/app/serialization/ListDelegateTest.kt b/car/app/app/src/test/java/androidx/car/app/serialization/ListDelegateTest.kt
index 7014c15..b5f8108 100644
--- a/car/app/app/src/test/java/androidx/car/app/serialization/ListDelegateTest.kt
+++ b/car/app/app/src/test/java/androidx/car/app/serialization/ListDelegateTest.kt
@@ -54,6 +54,22 @@
         assertInvalidIndices(2, 1) // end before start
     }
 
+    @Test
+    fun equalsAndHashCode_sameInstance_areEqual() =
+        ListDelegateImpl(testList).let { assertEqual(it, it) }
+
+    @Test
+    fun equalsAndHashCode_equivalentItems_areEqual() =
+        testList.let { assertEqual(ListDelegateImpl(it), ListDelegateImpl(it)) }
+
+    @Test
+    fun equalsAndHashCode_marshalledItem_areEqual() =
+        ListDelegateImpl(testList).let { assertEqual(it, marshallUnmarshall(it)) }
+
+    @Test
+    fun equalsAndHashCode_differentItems_areNotEqual() =
+        assertNotEqual(ListDelegateImpl((10..19).toList()), ListDelegateImpl((20..29).toList()))
+
     private fun assertInvalidIndices(startIndex: Int, endIndex: Int) {
         assertThrows(AssertionError::class.java) { requestItemRange(startIndex, endIndex) }
     }
@@ -70,4 +86,20 @@
 
         @Suppress("UNCHECKED_CAST") return resultCaptor.lastValue.get() as List<Int>
     }
+
+    private fun <T : Any> assertEqual(a: T, b: T) {
+        assertThat(a).isEqualTo(b)
+        assertThat(b).isEqualTo(a)
+        assertThat(a.hashCode()).isEqualTo(b.hashCode())
+    }
+
+    private fun <T : Any> assertNotEqual(a: T, b: T) {
+        assertThat(a).isNotEqualTo(b)
+        assertThat(b).isNotEqualTo(a)
+        assertThat(a.hashCode()).isNotEqualTo(b.hashCode())
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    private fun <T : Any> marshallUnmarshall(obj: T): T =
+        Bundler.fromBundle(Bundler.toBundle(obj)) as T
 }
diff --git a/cardview/cardview/build.gradle b/cardview/cardview/build.gradle
index 26d9aa8..eaafddc 100644
--- a/cardview/cardview/build.gradle
+++ b/cardview/cardview/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies { 
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.3.0")
 }
 
@@ -23,7 +23,6 @@
     inceptionYear = "2011"
     description = "Android Support CardView"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/collection/collection-ktx/build.gradle b/collection/collection-ktx/build.gradle
index 7bdd853..a99e33a0 100644
--- a/collection/collection-ktx/build.gradle
+++ b/collection/collection-ktx/build.gradle
@@ -37,5 +37,4 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'collection' artifact"
-    metalavaK2UastEnabled = true
 }
diff --git a/collection/collection/api/current.txt b/collection/collection/api/current.txt
index b5f715c..88bc7b3 100644
--- a/collection/collection/api/current.txt
+++ b/collection/collection/api/current.txt
@@ -1184,7 +1184,7 @@
   }
 
   public class LruCache<K, V> {
-    ctor public LruCache(@IntRange(from=1L, to=kotlin.jvm.internal.LongCompanionObject.MAX_VALUE) int maxSize);
+    ctor public LruCache(@IntRange(from=1L, to=androidx.collection.LruCacheKt.MAX_SIZE) int maxSize);
     method protected V? create(K key);
     method public final int createCount();
     method protected void entryRemoved(boolean evicted, K key, V oldValue, V? newValue);
@@ -1197,7 +1197,7 @@
     method public final V? put(K key, V value);
     method public final int putCount();
     method public final V? remove(K key);
-    method public void resize(@IntRange(from=1L, to=kotlin.jvm.internal.LongCompanionObject.MAX_VALUE) int maxSize);
+    method public void resize(@IntRange(from=1L, to=androidx.collection.LruCacheKt.MAX_SIZE) int maxSize);
     method public final int size();
     method protected int sizeOf(K key, V value);
     method public final java.util.Map<K,V> snapshot();
@@ -1209,6 +1209,7 @@
   }
 
   public final class MutableDoubleList extends androidx.collection.DoubleList {
+    ctor public MutableDoubleList();
     ctor public MutableDoubleList(optional int initialCapacity);
     method public boolean add(double element);
     method public void add(@IntRange(from=0L) int index, double element);
@@ -1240,6 +1241,7 @@
   }
 
   public final class MutableFloatFloatMap extends androidx.collection.FloatFloatMap {
+    ctor public MutableFloatFloatMap();
     ctor public MutableFloatFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(float key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1259,6 +1261,7 @@
   }
 
   public final class MutableFloatIntMap extends androidx.collection.FloatIntMap {
+    ctor public MutableFloatIntMap();
     ctor public MutableFloatIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(float key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1278,6 +1281,7 @@
   }
 
   public final class MutableFloatList extends androidx.collection.FloatList {
+    ctor public MutableFloatList();
     ctor public MutableFloatList(optional int initialCapacity);
     method public boolean add(float element);
     method public void add(@IntRange(from=0L) int index, float element);
@@ -1309,6 +1313,7 @@
   }
 
   public final class MutableFloatLongMap extends androidx.collection.FloatLongMap {
+    ctor public MutableFloatLongMap();
     ctor public MutableFloatLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(float key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1328,6 +1333,7 @@
   }
 
   public final class MutableFloatObjectMap<V> extends androidx.collection.FloatObjectMap<V> {
+    ctor public MutableFloatObjectMap();
     ctor public MutableFloatObjectMap(optional int initialCapacity);
     method public void clear();
     method public inline V getOrPut(float key, kotlin.jvm.functions.Function0<? extends V> defaultValue);
@@ -1346,6 +1352,7 @@
   }
 
   public final class MutableFloatSet extends androidx.collection.FloatSet {
+    ctor public MutableFloatSet();
     ctor public MutableFloatSet(optional int initialCapacity);
     method public boolean add(float element);
     method public boolean addAll(androidx.collection.FloatSet elements);
@@ -1364,6 +1371,7 @@
   }
 
   public final class MutableIntFloatMap extends androidx.collection.IntFloatMap {
+    ctor public MutableIntFloatMap();
     ctor public MutableIntFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(int key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1383,6 +1391,7 @@
   }
 
   public final class MutableIntIntMap extends androidx.collection.IntIntMap {
+    ctor public MutableIntIntMap();
     ctor public MutableIntIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1402,6 +1411,7 @@
   }
 
   public final class MutableIntList extends androidx.collection.IntList {
+    ctor public MutableIntList();
     ctor public MutableIntList(optional int initialCapacity);
     method public boolean add(int element);
     method public void add(@IntRange(from=0L) int index, int element);
@@ -1433,6 +1443,7 @@
   }
 
   public final class MutableIntLongMap extends androidx.collection.IntLongMap {
+    ctor public MutableIntLongMap();
     ctor public MutableIntLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1452,6 +1463,7 @@
   }
 
   public final class MutableIntObjectMap<V> extends androidx.collection.IntObjectMap<V> {
+    ctor public MutableIntObjectMap();
     ctor public MutableIntObjectMap(optional int initialCapacity);
     method public void clear();
     method public inline V getOrPut(int key, kotlin.jvm.functions.Function0<? extends V> defaultValue);
@@ -1470,6 +1482,7 @@
   }
 
   public final class MutableIntSet extends androidx.collection.IntSet {
+    ctor public MutableIntSet();
     ctor public MutableIntSet(optional int initialCapacity);
     method public boolean add(int element);
     method public boolean addAll(androidx.collection.IntSet elements);
@@ -1488,6 +1501,7 @@
   }
 
   public final class MutableLongFloatMap extends androidx.collection.LongFloatMap {
+    ctor public MutableLongFloatMap();
     ctor public MutableLongFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(long key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1507,6 +1521,7 @@
   }
 
   public final class MutableLongIntMap extends androidx.collection.LongIntMap {
+    ctor public MutableLongIntMap();
     ctor public MutableLongIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(long key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1526,6 +1541,7 @@
   }
 
   public final class MutableLongList extends androidx.collection.LongList {
+    ctor public MutableLongList();
     ctor public MutableLongList(optional int initialCapacity);
     method public void add(@IntRange(from=0L) int index, long element);
     method public boolean add(long element);
@@ -1557,6 +1573,7 @@
   }
 
   public final class MutableLongLongMap extends androidx.collection.LongLongMap {
+    ctor public MutableLongLongMap();
     ctor public MutableLongLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(long key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1576,6 +1593,7 @@
   }
 
   public final class MutableLongObjectMap<V> extends androidx.collection.LongObjectMap<V> {
+    ctor public MutableLongObjectMap();
     ctor public MutableLongObjectMap(optional int initialCapacity);
     method public void clear();
     method public inline V getOrPut(long key, kotlin.jvm.functions.Function0<? extends V> defaultValue);
@@ -1594,6 +1612,7 @@
   }
 
   public final class MutableLongSet extends androidx.collection.LongSet {
+    ctor public MutableLongSet();
     ctor public MutableLongSet(optional int initialCapacity);
     method public boolean add(long element);
     method public boolean addAll(androidx.collection.LongSet elements);
@@ -1612,6 +1631,7 @@
   }
 
   public final class MutableObjectFloatMap<K> extends androidx.collection.ObjectFloatMap<K> {
+    ctor public MutableObjectFloatMap();
     ctor public MutableObjectFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(K key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1632,6 +1652,7 @@
   }
 
   public final class MutableObjectIntMap<K> extends androidx.collection.ObjectIntMap<K> {
+    ctor public MutableObjectIntMap();
     ctor public MutableObjectIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(K key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1652,6 +1673,7 @@
   }
 
   public final class MutableObjectList<E> extends androidx.collection.ObjectList<E> {
+    ctor public MutableObjectList();
     ctor public MutableObjectList(optional int initialCapacity);
     method public boolean add(E element);
     method public void add(@IntRange(from=0L) int index, E element);
@@ -1704,6 +1726,7 @@
   }
 
   public final class MutableObjectLongMap<K> extends androidx.collection.ObjectLongMap<K> {
+    ctor public MutableObjectLongMap();
     ctor public MutableObjectLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(K key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1724,6 +1747,7 @@
   }
 
   public final class MutableScatterMap<K, V> extends androidx.collection.ScatterMap<K,V> {
+    ctor public MutableScatterMap();
     ctor public MutableScatterMap(optional int initialCapacity);
     method public java.util.Map<K,V> asMutableMap();
     method public void clear();
@@ -1755,6 +1779,7 @@
   }
 
   public final class MutableScatterSet<E> extends androidx.collection.ScatterSet<E> {
+    ctor public MutableScatterSet();
     ctor public MutableScatterSet(optional int initialCapacity);
     method public boolean add(E element);
     method public boolean addAll(androidx.collection.ObjectList<E> elements);
@@ -2089,6 +2114,60 @@
     method public static <E> androidx.collection.ScatterSet<E> scatterSetOf(E... elements);
   }
 
+  public final class SieveCache<K, V> {
+    ctor public SieveCache(@IntRange(from=1L, to=androidx.collection.SieveCacheKt.MaxSize) int maxSize, optional @IntRange(from=0L, to=androidx.collection.SieveCacheKt.MaxSize) int initialCapacity, optional kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf, optional kotlin.jvm.functions.Function1<? super K,? extends V?> createValueFromKey, optional kotlin.jvm.functions.Function4<? super K,? super V,? super V?,? super java.lang.Boolean,kotlin.Unit> onEntryRemoved);
+    method public inline boolean all(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public boolean any();
+    method public inline boolean any(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public operator boolean contains(K key);
+    method public boolean containsKey(K key);
+    method public boolean containsValue(V value);
+    method public int count();
+    method public inline int count(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public void evictAll();
+    method public inline void forEach(kotlin.jvm.functions.Function2<? super K,? super V,kotlin.Unit> block);
+    method public inline void forEachKey(kotlin.jvm.functions.Function1<? super K,kotlin.Unit> block);
+    method public inline void forEachValue(kotlin.jvm.functions.Function1<? super V,kotlin.Unit> block);
+    method public operator V? get(K key);
+    method public int getCapacity();
+    method public int getCount();
+    method public int getMaxSize();
+    method public int getSize();
+    method public boolean isEmpty();
+    method public boolean isNotEmpty();
+    method public inline operator void minusAssign(androidx.collection.ObjectList<K> keys);
+    method public inline operator void minusAssign(androidx.collection.ScatterSet<K> keys);
+    method public inline operator void minusAssign(Iterable<? extends K> keys);
+    method public inline operator void minusAssign(K key);
+    method public inline operator void minusAssign(K[] keys);
+    method public inline operator void minusAssign(kotlin.sequences.Sequence<? extends K> keys);
+    method public boolean none();
+    method public inline operator void plusAssign(androidx.collection.ScatterMap<K,V> from);
+    method public inline operator void plusAssign(androidx.collection.SieveCache<K,V> from);
+    method public inline operator void plusAssign(Iterable<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public inline operator void plusAssign(java.util.Map<K,? extends V> from);
+    method public inline operator void plusAssign(kotlin.Pair<? extends K,? extends V> pair);
+    method public inline operator void plusAssign(kotlin.Pair<? extends K,? extends V>[] pairs);
+    method public inline operator void plusAssign(kotlin.sequences.Sequence<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public V? put(K key, V value);
+    method public void putAll(androidx.collection.ScatterMap<K,V> from);
+    method public void putAll(androidx.collection.SieveCache<K,V> from);
+    method public void putAll(Iterable<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public void putAll(java.util.Map<K,? extends V> from);
+    method public void putAll(kotlin.Pair<? extends K,? extends V>[] pairs);
+    method public void putAll(kotlin.sequences.Sequence<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public V? remove(K key);
+    method public boolean remove(K key, V value);
+    method public void removeIf(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public void resize(@IntRange(from=1L, to=androidx.collection.SieveCacheKt.MaxSize) int maxSize);
+    method public inline operator void set(K key, V value);
+    method public void trimToSize(int maxSize);
+    property public final int capacity;
+    property public final int count;
+    property public final int maxSize;
+    property public final int size;
+  }
+
   public class SimpleArrayMap<K, V> {
     ctor public SimpleArrayMap();
     ctor public SimpleArrayMap(androidx.collection.SimpleArrayMap<? extends K,? extends V>? map);
diff --git a/collection/collection/api/restricted_current.txt b/collection/collection/api/restricted_current.txt
index a8aa1fa..de5f4d1 100644
--- a/collection/collection/api/restricted_current.txt
+++ b/collection/collection/api/restricted_current.txt
@@ -1258,7 +1258,7 @@
   }
 
   public class LruCache<K, V> {
-    ctor public LruCache(@IntRange(from=1L, to=kotlin.jvm.internal.LongCompanionObject.MAX_VALUE) int maxSize);
+    ctor public LruCache(@IntRange(from=1L, to=androidx.collection.LruCacheKt.MAX_SIZE) int maxSize);
     method protected V? create(K key);
     method public final int createCount();
     method protected void entryRemoved(boolean evicted, K key, V oldValue, V? newValue);
@@ -1271,7 +1271,7 @@
     method public final V? put(K key, V value);
     method public final int putCount();
     method public final V? remove(K key);
-    method public void resize(@IntRange(from=1L, to=kotlin.jvm.internal.LongCompanionObject.MAX_VALUE) int maxSize);
+    method public void resize(@IntRange(from=1L, to=androidx.collection.LruCacheKt.MAX_SIZE) int maxSize);
     method public final int size();
     method protected int sizeOf(K key, V value);
     method public final java.util.Map<K,V> snapshot();
@@ -1283,6 +1283,7 @@
   }
 
   public final class MutableDoubleList extends androidx.collection.DoubleList {
+    ctor public MutableDoubleList();
     ctor public MutableDoubleList(optional int initialCapacity);
     method public boolean add(double element);
     method public void add(@IntRange(from=0L) int index, double element);
@@ -1314,6 +1315,7 @@
   }
 
   public final class MutableFloatFloatMap extends androidx.collection.FloatFloatMap {
+    ctor public MutableFloatFloatMap();
     ctor public MutableFloatFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(float key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1334,6 +1336,7 @@
   }
 
   public final class MutableFloatIntMap extends androidx.collection.FloatIntMap {
+    ctor public MutableFloatIntMap();
     ctor public MutableFloatIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(float key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1354,6 +1357,7 @@
   }
 
   public final class MutableFloatList extends androidx.collection.FloatList {
+    ctor public MutableFloatList();
     ctor public MutableFloatList(optional int initialCapacity);
     method public boolean add(float element);
     method public void add(@IntRange(from=0L) int index, float element);
@@ -1385,6 +1389,7 @@
   }
 
   public final class MutableFloatLongMap extends androidx.collection.FloatLongMap {
+    ctor public MutableFloatLongMap();
     ctor public MutableFloatLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(float key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1405,6 +1410,7 @@
   }
 
   public final class MutableFloatObjectMap<V> extends androidx.collection.FloatObjectMap<V> {
+    ctor public MutableFloatObjectMap();
     ctor public MutableFloatObjectMap(optional int initialCapacity);
     method public void clear();
     method public inline V getOrPut(float key, kotlin.jvm.functions.Function0<? extends V> defaultValue);
@@ -1424,6 +1430,7 @@
   }
 
   public final class MutableFloatSet extends androidx.collection.FloatSet {
+    ctor public MutableFloatSet();
     ctor public MutableFloatSet(optional int initialCapacity);
     method public boolean add(float element);
     method public boolean addAll(androidx.collection.FloatSet elements);
@@ -1442,6 +1449,7 @@
   }
 
   public final class MutableIntFloatMap extends androidx.collection.IntFloatMap {
+    ctor public MutableIntFloatMap();
     ctor public MutableIntFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(int key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1462,6 +1470,7 @@
   }
 
   public final class MutableIntIntMap extends androidx.collection.IntIntMap {
+    ctor public MutableIntIntMap();
     ctor public MutableIntIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1482,6 +1491,7 @@
   }
 
   public final class MutableIntList extends androidx.collection.IntList {
+    ctor public MutableIntList();
     ctor public MutableIntList(optional int initialCapacity);
     method public boolean add(int element);
     method public void add(@IntRange(from=0L) int index, int element);
@@ -1513,6 +1523,7 @@
   }
 
   public final class MutableIntLongMap extends androidx.collection.IntLongMap {
+    ctor public MutableIntLongMap();
     ctor public MutableIntLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1533,6 +1544,7 @@
   }
 
   public final class MutableIntObjectMap<V> extends androidx.collection.IntObjectMap<V> {
+    ctor public MutableIntObjectMap();
     ctor public MutableIntObjectMap(optional int initialCapacity);
     method public void clear();
     method public inline V getOrPut(int key, kotlin.jvm.functions.Function0<? extends V> defaultValue);
@@ -1552,6 +1564,7 @@
   }
 
   public final class MutableIntSet extends androidx.collection.IntSet {
+    ctor public MutableIntSet();
     ctor public MutableIntSet(optional int initialCapacity);
     method public boolean add(int element);
     method public boolean addAll(androidx.collection.IntSet elements);
@@ -1570,6 +1583,7 @@
   }
 
   public final class MutableLongFloatMap extends androidx.collection.LongFloatMap {
+    ctor public MutableLongFloatMap();
     ctor public MutableLongFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(long key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1590,6 +1604,7 @@
   }
 
   public final class MutableLongIntMap extends androidx.collection.LongIntMap {
+    ctor public MutableLongIntMap();
     ctor public MutableLongIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(long key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1610,6 +1625,7 @@
   }
 
   public final class MutableLongList extends androidx.collection.LongList {
+    ctor public MutableLongList();
     ctor public MutableLongList(optional int initialCapacity);
     method public void add(@IntRange(from=0L) int index, long element);
     method public boolean add(long element);
@@ -1641,6 +1657,7 @@
   }
 
   public final class MutableLongLongMap extends androidx.collection.LongLongMap {
+    ctor public MutableLongLongMap();
     ctor public MutableLongLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(long key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1661,6 +1678,7 @@
   }
 
   public final class MutableLongObjectMap<V> extends androidx.collection.LongObjectMap<V> {
+    ctor public MutableLongObjectMap();
     ctor public MutableLongObjectMap(optional int initialCapacity);
     method public void clear();
     method public inline V getOrPut(long key, kotlin.jvm.functions.Function0<? extends V> defaultValue);
@@ -1680,6 +1698,7 @@
   }
 
   public final class MutableLongSet extends androidx.collection.LongSet {
+    ctor public MutableLongSet();
     ctor public MutableLongSet(optional int initialCapacity);
     method public boolean add(long element);
     method public boolean addAll(androidx.collection.LongSet elements);
@@ -1698,6 +1717,7 @@
   }
 
   public final class MutableObjectFloatMap<K> extends androidx.collection.ObjectFloatMap<K> {
+    ctor public MutableObjectFloatMap();
     ctor public MutableObjectFloatMap(optional int initialCapacity);
     method public void clear();
     method public inline float getOrPut(K key, kotlin.jvm.functions.Function0<java.lang.Float> defaultValue);
@@ -1719,6 +1739,7 @@
   }
 
   public final class MutableObjectIntMap<K> extends androidx.collection.ObjectIntMap<K> {
+    ctor public MutableObjectIntMap();
     ctor public MutableObjectIntMap(optional int initialCapacity);
     method public void clear();
     method public inline int getOrPut(K key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
@@ -1740,6 +1761,7 @@
   }
 
   public final class MutableObjectList<E> extends androidx.collection.ObjectList<E> {
+    ctor public MutableObjectList();
     ctor public MutableObjectList(optional int initialCapacity);
     method public boolean add(E element);
     method public void add(@IntRange(from=0L) int index, E element);
@@ -1792,6 +1814,7 @@
   }
 
   public final class MutableObjectLongMap<K> extends androidx.collection.ObjectLongMap<K> {
+    ctor public MutableObjectLongMap();
     ctor public MutableObjectLongMap(optional int initialCapacity);
     method public void clear();
     method public inline long getOrPut(K key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
@@ -1813,6 +1836,7 @@
   }
 
   public final class MutableScatterMap<K, V> extends androidx.collection.ScatterMap<K,V> {
+    ctor public MutableScatterMap();
     ctor public MutableScatterMap(optional int initialCapacity);
     method public java.util.Map<K,V> asMutableMap();
     method public void clear();
@@ -1846,6 +1870,7 @@
   }
 
   public final class MutableScatterSet<E> extends androidx.collection.ScatterSet<E> {
+    ctor public MutableScatterSet();
     ctor public MutableScatterSet(optional int initialCapacity);
     method public boolean add(E element);
     method public boolean addAll(androidx.collection.ObjectList<E> elements);
@@ -2213,6 +2238,64 @@
     method public static <E> androidx.collection.ScatterSet<E> scatterSetOf(E... elements);
   }
 
+  public final class SieveCache<K, V> {
+    ctor public SieveCache(@IntRange(from=1L, to=androidx.collection.SieveCacheKt.MaxSize) int maxSize, optional @IntRange(from=0L, to=androidx.collection.SieveCacheKt.MaxSize) int initialCapacity, optional kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf, optional kotlin.jvm.functions.Function1<? super K,? extends V?> createValueFromKey, optional kotlin.jvm.functions.Function4<? super K,? super V,? super V?,? super java.lang.Boolean,kotlin.Unit> onEntryRemoved);
+    method public inline boolean all(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public boolean any();
+    method public inline boolean any(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public operator boolean contains(K key);
+    method public boolean containsKey(K key);
+    method public boolean containsValue(V value);
+    method public int count();
+    method public inline int count(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public void evictAll();
+    method public inline void forEach(kotlin.jvm.functions.Function2<? super K,? super V,kotlin.Unit> block);
+    method @kotlin.PublishedApi internal inline void forEachIndexed(kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> block);
+    method public inline void forEachKey(kotlin.jvm.functions.Function1<? super K,kotlin.Unit> block);
+    method public inline void forEachValue(kotlin.jvm.functions.Function1<? super V,kotlin.Unit> block);
+    method public operator V? get(K key);
+    method public int getCapacity();
+    method public int getCount();
+    method public int getMaxSize();
+    method public int getSize();
+    method public boolean isEmpty();
+    method public boolean isNotEmpty();
+    method public inline operator void minusAssign(androidx.collection.ObjectList<K> keys);
+    method public inline operator void minusAssign(androidx.collection.ScatterSet<K> keys);
+    method public inline operator void minusAssign(Iterable<? extends K> keys);
+    method public inline operator void minusAssign(K key);
+    method public inline operator void minusAssign(K[] keys);
+    method public inline operator void minusAssign(kotlin.sequences.Sequence<? extends K> keys);
+    method public boolean none();
+    method public inline operator void plusAssign(androidx.collection.ScatterMap<K,V> from);
+    method public inline operator void plusAssign(androidx.collection.SieveCache<K,V> from);
+    method public inline operator void plusAssign(Iterable<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public inline operator void plusAssign(java.util.Map<K,? extends V> from);
+    method public inline operator void plusAssign(kotlin.Pair<? extends K,? extends V> pair);
+    method public inline operator void plusAssign(kotlin.Pair<? extends K,? extends V>[] pairs);
+    method public inline operator void plusAssign(kotlin.sequences.Sequence<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public V? put(K key, V value);
+    method public void putAll(androidx.collection.ScatterMap<K,V> from);
+    method public void putAll(androidx.collection.SieveCache<K,V> from);
+    method public void putAll(Iterable<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public void putAll(java.util.Map<K,? extends V> from);
+    method public void putAll(kotlin.Pair<? extends K,? extends V>[] pairs);
+    method public void putAll(kotlin.sequences.Sequence<? extends kotlin.Pair<? extends K,? extends V>> pairs);
+    method public V? remove(K key);
+    method public boolean remove(K key, V value);
+    method public void removeIf(kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Boolean> predicate);
+    method public void resize(@IntRange(from=1L, to=androidx.collection.SieveCacheKt.MaxSize) int maxSize);
+    method public inline operator void set(K key, V value);
+    method public void trimToSize(int maxSize);
+    property public final int capacity;
+    property public final int count;
+    property public final int maxSize;
+    property public final int size;
+    field @kotlin.PublishedApi internal Object?[] keys;
+    field @kotlin.PublishedApi internal long[] metadata;
+    field @kotlin.PublishedApi internal Object?[] values;
+  }
+
   public class SimpleArrayMap<K, V> {
     ctor public SimpleArrayMap();
     ctor public SimpleArrayMap(androidx.collection.SimpleArrayMap<? extends K,? extends V>? map);
diff --git a/collection/collection/bcv/native/current.txt b/collection/collection/bcv/native/current.txt
new file mode 100644
index 0000000..e63db68
--- /dev/null
+++ b/collection/collection/bcv/native/current.txt
@@ -0,0 +1,2195 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.collection:collection>
+final class <#A: kotlin/Any, #B: kotlin/Any> androidx.collection/SieveCache { // androidx.collection/SieveCache|null[0]
+    constructor <init>(kotlin/Int, kotlin/Int =..., kotlin/Function2<#A, #B, kotlin/Int> =..., kotlin/Function1<#A, #B?> =..., kotlin/Function4<#A, #B, #B?, kotlin/Boolean, kotlin/Unit> =...) // androidx.collection/SieveCache.<init>|<init>(kotlin.Int;kotlin.Int;kotlin.Function2<1:0,1:1,kotlin.Int>;kotlin.Function1<1:0,1:1?>;kotlin.Function4<1:0,1:1,1:1?,kotlin.Boolean,kotlin.Unit>){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/SieveCache.any|any(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/SieveCache.contains|contains(1:0){}[0]
+    final fun containsKey(#A): kotlin/Boolean // androidx.collection/SieveCache.containsKey|containsKey(1:0){}[0]
+    final fun containsValue(#B): kotlin/Boolean // androidx.collection/SieveCache.containsValue|containsValue(1:1){}[0]
+    final fun count(): kotlin/Int // androidx.collection/SieveCache.count|count(){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/SieveCache.equals|equals(kotlin.Any?){}[0]
+    final fun evictAll() // androidx.collection/SieveCache.evictAll|evictAll(){}[0]
+    final fun get(#A): #B? // androidx.collection/SieveCache.get|get(1:0){}[0]
+    final fun hashCode(): kotlin/Int // androidx.collection/SieveCache.hashCode|hashCode(){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/SieveCache.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/SieveCache.isNotEmpty|isNotEmpty(){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/SieveCache.none|none(){}[0]
+    final fun put(#A, #B): #B? // androidx.collection/SieveCache.put|put(1:0;1:1){}[0]
+    final fun putAll(androidx.collection/ScatterMap<#A, #B>) // androidx.collection/SieveCache.putAll|putAll(androidx.collection.ScatterMap<1:0,1:1>){}[0]
+    final fun putAll(androidx.collection/SieveCache<#A, #B>) // androidx.collection/SieveCache.putAll|putAll(androidx.collection.SieveCache<1:0,1:1>){}[0]
+    final fun putAll(kotlin.collections/Iterable<kotlin/Pair<#A, #B>>) // androidx.collection/SieveCache.putAll|putAll(kotlin.collections.Iterable<kotlin.Pair<1:0,1:1>>){}[0]
+    final fun putAll(kotlin.collections/Map<#A, #B>) // androidx.collection/SieveCache.putAll|putAll(kotlin.collections.Map<1:0,1:1>){}[0]
+    final fun putAll(kotlin.sequences/Sequence<kotlin/Pair<#A, #B>>) // androidx.collection/SieveCache.putAll|putAll(kotlin.sequences.Sequence<kotlin.Pair<1:0,1:1>>){}[0]
+    final fun putAll(kotlin/Array<out kotlin/Pair<#A, #B>>) // androidx.collection/SieveCache.putAll|putAll(kotlin.Array<out|kotlin.Pair<1:0,1:1>>){}[0]
+    final fun remove(#A): #B? // androidx.collection/SieveCache.remove|remove(1:0){}[0]
+    final fun remove(#A, #B): kotlin/Boolean // androidx.collection/SieveCache.remove|remove(1:0;1:1){}[0]
+    final fun removeIf(kotlin/Function2<#A, #B, kotlin/Boolean>) // androidx.collection/SieveCache.removeIf|removeIf(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+    final fun resize(kotlin/Int) // androidx.collection/SieveCache.resize|resize(kotlin.Int){}[0]
+    final fun toString(): kotlin/String // androidx.collection/SieveCache.toString|toString(){}[0]
+    final fun trimToSize(kotlin/Int) // androidx.collection/SieveCache.trimToSize|trimToSize(kotlin.Int){}[0]
+    final inline fun all(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Boolean // androidx.collection/SieveCache.all|all(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Boolean // androidx.collection/SieveCache.any|any(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Int // androidx.collection/SieveCache.count|count(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<#A, #B, kotlin/Unit>) // androidx.collection/SieveCache.forEach|forEach(kotlin.Function2<1:0,1:1,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/SieveCache.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/SieveCache.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<#B, kotlin/Unit>) // androidx.collection/SieveCache.forEachValue|forEachValue(kotlin.Function1<1:1,kotlin.Unit>){}[0]
+    final inline fun minusAssign(#A) // androidx.collection/SieveCache.minusAssign|minusAssign(1:0){}[0]
+    final inline fun minusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/SieveCache.minusAssign|minusAssign(androidx.collection.ObjectList<1:0>){}[0]
+    final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/SieveCache.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/SieveCache.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/SieveCache.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/SieveCache.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+    final inline fun plusAssign(androidx.collection/ScatterMap<#A, #B>) // androidx.collection/SieveCache.plusAssign|plusAssign(androidx.collection.ScatterMap<1:0,1:1>){}[0]
+    final inline fun plusAssign(androidx.collection/SieveCache<#A, #B>) // androidx.collection/SieveCache.plusAssign|plusAssign(androidx.collection.SieveCache<1:0,1:1>){}[0]
+    final inline fun plusAssign(kotlin.collections/Iterable<kotlin/Pair<#A, #B>>) // androidx.collection/SieveCache.plusAssign|plusAssign(kotlin.collections.Iterable<kotlin.Pair<1:0,1:1>>){}[0]
+    final inline fun plusAssign(kotlin.collections/Map<#A, #B>) // androidx.collection/SieveCache.plusAssign|plusAssign(kotlin.collections.Map<1:0,1:1>){}[0]
+    final inline fun plusAssign(kotlin.sequences/Sequence<kotlin/Pair<#A, #B>>) // androidx.collection/SieveCache.plusAssign|plusAssign(kotlin.sequences.Sequence<kotlin.Pair<1:0,1:1>>){}[0]
+    final inline fun plusAssign(kotlin/Array<out kotlin/Pair<#A, #B>>) // androidx.collection/SieveCache.plusAssign|plusAssign(kotlin.Array<out|kotlin.Pair<1:0,1:1>>){}[0]
+    final inline fun plusAssign(kotlin/Pair<#A, #B>) // androidx.collection/SieveCache.plusAssign|plusAssign(kotlin.Pair<1:0,1:1>){}[0]
+    final inline fun set(#A, #B) // androidx.collection/SieveCache.set|set(1:0;1:1){}[0]
+    final val capacity // androidx.collection/SieveCache.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/SieveCache.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val count // androidx.collection/SieveCache.count|{}count[0]
+        final fun <get-count>(): kotlin/Int // androidx.collection/SieveCache.count.<get-count>|<get-count>(){}[0]
+    final val maxSize // androidx.collection/SieveCache.maxSize|{}maxSize[0]
+        final fun <get-maxSize>(): kotlin/Int // androidx.collection/SieveCache.maxSize.<get-maxSize>|<get-maxSize>(){}[0]
+    final val size // androidx.collection/SieveCache.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/SieveCache.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/SieveCache.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/SieveCache.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/SieveCache.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+    final var metadata // androidx.collection/SieveCache.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/SieveCache.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/SieveCache.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/SieveCache.values|{}values[0]
+        final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/SieveCache.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/SieveCache.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+}
+final class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/MutableScatterMap : androidx.collection/ScatterMap<#A, #B> { // androidx.collection/MutableScatterMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableScatterMap.<init>|<init>(kotlin.Int){}[0]
+    final fun asMutableMap(): kotlin.collections/MutableMap<#A, #B> // androidx.collection/MutableScatterMap.asMutableMap|asMutableMap(){}[0]
+    final fun clear() // androidx.collection/MutableScatterMap.clear|clear(){}[0]
+    final fun findInsertIndex(#A): kotlin/Int // androidx.collection/MutableScatterMap.findInsertIndex|findInsertIndex(1:0){}[0]
+    final fun put(#A, #B): #B? // androidx.collection/MutableScatterMap.put|put(1:0;1:1){}[0]
+    final fun putAll(androidx.collection/ScatterMap<#A, #B>) // androidx.collection/MutableScatterMap.putAll|putAll(androidx.collection.ScatterMap<1:0,1:1>){}[0]
+    final fun putAll(kotlin.collections/Iterable<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.collections.Iterable<kotlin.Pair<1:0,1:1>>){}[0]
+    final fun putAll(kotlin.collections/Map<#A, #B>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.collections.Map<1:0,1:1>){}[0]
+    final fun putAll(kotlin.sequences/Sequence<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.sequences.Sequence<kotlin.Pair<1:0,1:1>>){}[0]
+    final fun putAll(kotlin/Array<out kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.putAll|putAll(kotlin.Array<out|kotlin.Pair<1:0,1:1>>){}[0]
+    final fun remove(#A): #B? // androidx.collection/MutableScatterMap.remove|remove(1:0){}[0]
+    final fun remove(#A, #B): kotlin/Boolean // androidx.collection/MutableScatterMap.remove|remove(1:0;1:1){}[0]
+    final fun removeValueAt(kotlin/Int): #B? // androidx.collection/MutableScatterMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(#A, #B) // androidx.collection/MutableScatterMap.set|set(1:0;1:1){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableScatterMap.trim|trim(){}[0]
+    final inline fun compute(#A, kotlin/Function2<#A, #B?, #B>): #B // androidx.collection/MutableScatterMap.compute|compute(1:0;kotlin.Function2<1:0,1:1?,1:1>){}[0]
+    final inline fun getOrPut(#A, kotlin/Function0<#B>): #B // androidx.collection/MutableScatterMap.getOrPut|getOrPut(1:0;kotlin.Function0<1:1>){}[0]
+    final inline fun minusAssign(#A) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(1:0){}[0]
+    final inline fun minusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(androidx.collection.ObjectList<1:0>){}[0]
+    final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableScatterMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+    final inline fun plusAssign(androidx.collection/ScatterMap<#A, #B>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(androidx.collection.ScatterMap<1:0,1:1>){}[0]
+    final inline fun plusAssign(kotlin.collections/Iterable<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.collections.Iterable<kotlin.Pair<1:0,1:1>>){}[0]
+    final inline fun plusAssign(kotlin.collections/Map<#A, #B>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.collections.Map<1:0,1:1>){}[0]
+    final inline fun plusAssign(kotlin.sequences/Sequence<kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.sequences.Sequence<kotlin.Pair<1:0,1:1>>){}[0]
+    final inline fun plusAssign(kotlin/Array<out kotlin/Pair<#A, #B>>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.Array<out|kotlin.Pair<1:0,1:1>>){}[0]
+    final inline fun plusAssign(kotlin/Pair<#A, #B>) // androidx.collection/MutableScatterMap.plusAssign|plusAssign(kotlin.Pair<1:0,1:1>){}[0]
+    final inline fun removeIf(kotlin/Function2<#A, #B, kotlin/Boolean>) // androidx.collection/MutableScatterMap.removeIf|removeIf(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/ArraySet : kotlin.collections/MutableCollection<#A>, kotlin.collections/MutableSet<#A> { // androidx.collection/ArraySet|null[0]
+    constructor <init>(androidx.collection/ArraySet<out #A>?) // androidx.collection/ArraySet.<init>|<init>(androidx.collection.ArraySet<out|1:0>?){}[0]
+    constructor <init>(kotlin.collections/Collection<#A>?) // androidx.collection/ArraySet.<init>|<init>(kotlin.collections.Collection<1:0>?){}[0]
+    constructor <init>(kotlin/Array<out #A>?) // androidx.collection/ArraySet.<init>|<init>(kotlin.Array<out|1:0>?){}[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/ArraySet.<init>|<init>(kotlin.Int){}[0]
+    final fun add(#A): kotlin/Boolean // androidx.collection/ArraySet.add|add(1:0){}[0]
+    final fun addAll(androidx.collection/ArraySet<out #A>) // androidx.collection/ArraySet.addAll|addAll(androidx.collection.ArraySet<out|1:0>){}[0]
+    final fun addAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.addAll|addAll(kotlin.collections.Collection<1:0>){}[0]
+    final fun clear() // androidx.collection/ArraySet.clear|clear(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/ArraySet.contains|contains(1:0){}[0]
+    final fun containsAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.containsAll|containsAll(kotlin.collections.Collection<1:0>){}[0]
+    final fun ensureCapacity(kotlin/Int) // androidx.collection/ArraySet.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ArraySet.equals|equals(kotlin.Any?){}[0]
+    final fun hashCode(): kotlin/Int // androidx.collection/ArraySet.hashCode|hashCode(){}[0]
+    final fun indexOf(kotlin/Any?): kotlin/Int // androidx.collection/ArraySet.indexOf|indexOf(kotlin.Any?){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/ArraySet.isEmpty|isEmpty(){}[0]
+    final fun iterator(): kotlin.collections/MutableIterator<#A> // androidx.collection/ArraySet.iterator|iterator(){}[0]
+    final fun remove(#A): kotlin/Boolean // androidx.collection/ArraySet.remove|remove(1:0){}[0]
+    final fun removeAll(androidx.collection/ArraySet<out #A>): kotlin/Boolean // androidx.collection/ArraySet.removeAll|removeAll(androidx.collection.ArraySet<out|1:0>){}[0]
+    final fun removeAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.removeAll|removeAll(kotlin.collections.Collection<1:0>){}[0]
+    final fun removeAt(kotlin/Int): #A // androidx.collection/ArraySet.removeAt|removeAt(kotlin.Int){}[0]
+    final fun retainAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/ArraySet.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0]
+    final fun toString(): kotlin/String // androidx.collection/ArraySet.toString|toString(){}[0]
+    final fun valueAt(kotlin/Int): #A // androidx.collection/ArraySet.valueAt|valueAt(kotlin.Int){}[0]
+    final val size // androidx.collection/ArraySet.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/ArraySet.size.<get-size>|<get-size>(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/CircularArray { // androidx.collection/CircularArray|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/CircularArray.<init>|<init>(kotlin.Int){}[0]
+    final fun addFirst(#A) // androidx.collection/CircularArray.addFirst|addFirst(1:0){}[0]
+    final fun addLast(#A) // androidx.collection/CircularArray.addLast|addLast(1:0){}[0]
+    final fun clear() // androidx.collection/CircularArray.clear|clear(){}[0]
+    final fun get(kotlin/Int): #A // androidx.collection/CircularArray.get|get(kotlin.Int){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/CircularArray.isEmpty|isEmpty(){}[0]
+    final fun popFirst(): #A // androidx.collection/CircularArray.popFirst|popFirst(){}[0]
+    final fun popLast(): #A // androidx.collection/CircularArray.popLast|popLast(){}[0]
+    final fun removeFromEnd(kotlin/Int) // androidx.collection/CircularArray.removeFromEnd|removeFromEnd(kotlin.Int){}[0]
+    final fun removeFromStart(kotlin/Int) // androidx.collection/CircularArray.removeFromStart|removeFromStart(kotlin.Int){}[0]
+    final fun size(): kotlin/Int // androidx.collection/CircularArray.size|size(){}[0]
+    final val first // androidx.collection/CircularArray.first|{}first[0]
+        final fun <get-first>(): #A // androidx.collection/CircularArray.first.<get-first>|<get-first>(){}[0]
+    final val last // androidx.collection/CircularArray.last|{}last[0]
+        final fun <get-last>(): #A // androidx.collection/CircularArray.last.<get-last>|<get-last>(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableFloatObjectMap : androidx.collection/FloatObjectMap<#A> { // androidx.collection/MutableFloatObjectMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatObjectMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableFloatObjectMap.clear|clear(){}[0]
+    final fun put(kotlin/Float, #A): #A? // androidx.collection/MutableFloatObjectMap.put|put(kotlin.Float;1:0){}[0]
+    final fun putAll(androidx.collection/FloatObjectMap<#A>) // androidx.collection/MutableFloatObjectMap.putAll|putAll(androidx.collection.FloatObjectMap<1:0>){}[0]
+    final fun remove(kotlin/Float): #A? // androidx.collection/MutableFloatObjectMap.remove|remove(kotlin.Float){}[0]
+    final fun remove(kotlin/Float, #A): kotlin/Boolean // androidx.collection/MutableFloatObjectMap.remove|remove(kotlin.Float;1:0){}[0]
+    final fun removeValueAt(kotlin/Int): #A? // androidx.collection/MutableFloatObjectMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Float, #A) // androidx.collection/MutableFloatObjectMap.set|set(kotlin.Float;1:0){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableFloatObjectMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Float, kotlin/Function0<#A>): #A // androidx.collection/MutableFloatObjectMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<1:0>){}[0]
+    final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+    final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+    final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(kotlin.Float){}[0]
+    final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatObjectMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+    final inline fun plusAssign(androidx.collection/FloatObjectMap<#A>) // androidx.collection/MutableFloatObjectMap.plusAssign|plusAssign(androidx.collection.FloatObjectMap<1:0>){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>) // androidx.collection/MutableFloatObjectMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableIntObjectMap : androidx.collection/IntObjectMap<#A> { // androidx.collection/MutableIntObjectMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntObjectMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableIntObjectMap.clear|clear(){}[0]
+    final fun put(kotlin/Int, #A): #A? // androidx.collection/MutableIntObjectMap.put|put(kotlin.Int;1:0){}[0]
+    final fun putAll(androidx.collection/IntObjectMap<#A>) // androidx.collection/MutableIntObjectMap.putAll|putAll(androidx.collection.IntObjectMap<1:0>){}[0]
+    final fun remove(kotlin/Int): #A? // androidx.collection/MutableIntObjectMap.remove|remove(kotlin.Int){}[0]
+    final fun remove(kotlin/Int, #A): kotlin/Boolean // androidx.collection/MutableIntObjectMap.remove|remove(kotlin.Int;1:0){}[0]
+    final fun removeValueAt(kotlin/Int): #A? // androidx.collection/MutableIntObjectMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Int, #A) // androidx.collection/MutableIntObjectMap.set|set(kotlin.Int;1:0){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableIntObjectMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Int, kotlin/Function0<#A>): #A // androidx.collection/MutableIntObjectMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<1:0>){}[0]
+    final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+    final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+    final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntObjectMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+    final inline fun plusAssign(androidx.collection/IntObjectMap<#A>) // androidx.collection/MutableIntObjectMap.plusAssign|plusAssign(androidx.collection.IntObjectMap<1:0>){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>) // androidx.collection/MutableIntObjectMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableLongObjectMap : androidx.collection/LongObjectMap<#A> { // androidx.collection/MutableLongObjectMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongObjectMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableLongObjectMap.clear|clear(){}[0]
+    final fun put(kotlin/Long, #A): #A? // androidx.collection/MutableLongObjectMap.put|put(kotlin.Long;1:0){}[0]
+    final fun putAll(androidx.collection/LongObjectMap<#A>) // androidx.collection/MutableLongObjectMap.putAll|putAll(androidx.collection.LongObjectMap<1:0>){}[0]
+    final fun remove(kotlin/Long): #A? // androidx.collection/MutableLongObjectMap.remove|remove(kotlin.Long){}[0]
+    final fun remove(kotlin/Long, #A): kotlin/Boolean // androidx.collection/MutableLongObjectMap.remove|remove(kotlin.Long;1:0){}[0]
+    final fun removeValueAt(kotlin/Int): #A? // androidx.collection/MutableLongObjectMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Long, #A) // androidx.collection/MutableLongObjectMap.set|set(kotlin.Long;1:0){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableLongObjectMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Long, kotlin/Function0<#A>): #A // androidx.collection/MutableLongObjectMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<1:0>){}[0]
+    final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+    final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+    final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(kotlin.Long){}[0]
+    final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongObjectMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+    final inline fun plusAssign(androidx.collection/LongObjectMap<#A>) // androidx.collection/MutableLongObjectMap.plusAssign|plusAssign(androidx.collection.LongObjectMap<1:0>){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>) // androidx.collection/MutableLongObjectMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectFloatMap : androidx.collection/ObjectFloatMap<#A> { // androidx.collection/MutableObjectFloatMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectFloatMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableObjectFloatMap.clear|clear(){}[0]
+    final fun put(#A, kotlin/Float) // androidx.collection/MutableObjectFloatMap.put|put(1:0;kotlin.Float){}[0]
+    final fun put(#A, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableObjectFloatMap.put|put(1:0;kotlin.Float;kotlin.Float){}[0]
+    final fun putAll(androidx.collection/ObjectFloatMap<#A>) // androidx.collection/MutableObjectFloatMap.putAll|putAll(androidx.collection.ObjectFloatMap<1:0>){}[0]
+    final fun remove(#A) // androidx.collection/MutableObjectFloatMap.remove|remove(1:0){}[0]
+    final fun remove(#A, kotlin/Float): kotlin/Boolean // androidx.collection/MutableObjectFloatMap.remove|remove(1:0;kotlin.Float){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableObjectFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(#A, kotlin/Float) // androidx.collection/MutableObjectFloatMap.set|set(1:0;kotlin.Float){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableObjectFloatMap.trim|trim(){}[0]
+    final inline fun getOrPut(#A, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableObjectFloatMap.getOrPut|getOrPut(1:0;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun minusAssign(#A) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(1:0){}[0]
+    final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableObjectFloatMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+    final inline fun plusAssign(androidx.collection/ObjectFloatMap<#A>) // androidx.collection/MutableObjectFloatMap.plusAssign|plusAssign(androidx.collection.ObjectFloatMap<1:0>){}[0]
+    final inline fun removeIf(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableObjectFloatMap.removeIf|removeIf(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectIntMap : androidx.collection/ObjectIntMap<#A> { // androidx.collection/MutableObjectIntMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectIntMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableObjectIntMap.clear|clear(){}[0]
+    final fun put(#A, kotlin/Int) // androidx.collection/MutableObjectIntMap.put|put(1:0;kotlin.Int){}[0]
+    final fun put(#A, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableObjectIntMap.put|put(1:0;kotlin.Int;kotlin.Int){}[0]
+    final fun putAll(androidx.collection/ObjectIntMap<#A>) // androidx.collection/MutableObjectIntMap.putAll|putAll(androidx.collection.ObjectIntMap<1:0>){}[0]
+    final fun remove(#A) // androidx.collection/MutableObjectIntMap.remove|remove(1:0){}[0]
+    final fun remove(#A, kotlin/Int): kotlin/Boolean // androidx.collection/MutableObjectIntMap.remove|remove(1:0;kotlin.Int){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableObjectIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(#A, kotlin/Int) // androidx.collection/MutableObjectIntMap.set|set(1:0;kotlin.Int){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableObjectIntMap.trim|trim(){}[0]
+    final inline fun getOrPut(#A, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableObjectIntMap.getOrPut|getOrPut(1:0;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun minusAssign(#A) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(1:0){}[0]
+    final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableObjectIntMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+    final inline fun plusAssign(androidx.collection/ObjectIntMap<#A>) // androidx.collection/MutableObjectIntMap.plusAssign|plusAssign(androidx.collection.ObjectIntMap<1:0>){}[0]
+    final inline fun removeIf(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableObjectIntMap.removeIf|removeIf(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectList : androidx.collection/ObjectList<#A> { // androidx.collection/MutableObjectList|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectList.<init>|<init>(kotlin.Int){}[0]
+    final fun add(#A): kotlin/Boolean // androidx.collection/MutableObjectList.add|add(1:0){}[0]
+    final fun add(kotlin/Int, #A) // androidx.collection/MutableObjectList.add|add(kotlin.Int;1:0){}[0]
+    final fun addAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(androidx.collection.ObjectList<1:0>){}[0]
+    final fun addAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun addAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.collections.Iterable<1:0>){}[0]
+    final fun addAll(kotlin.collections/List<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.collections.List<1:0>){}[0]
+    final fun addAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun addAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Array<1:0>){}[0]
+    final fun addAll(kotlin/Int, androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Int;androidx.collection.ObjectList<1:0>){}[0]
+    final fun addAll(kotlin/Int, kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Int;kotlin.collections.Collection<1:0>){}[0]
+    final fun addAll(kotlin/Int, kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.addAll|addAll(kotlin.Int;kotlin.Array<1:0>){}[0]
+    final fun asList(): kotlin.collections/List<#A> // androidx.collection/MutableObjectList.asList|asList(){}[0]
+    final fun asMutableList(): kotlin.collections/MutableList<#A> // androidx.collection/MutableObjectList.asMutableList|asMutableList(){}[0]
+    final fun clear() // androidx.collection/MutableObjectList.clear|clear(){}[0]
+    final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableObjectList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+    final fun minusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(androidx.collection.ObjectList<1:0>){}[0]
+    final fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final fun minusAssign(kotlin.collections/List<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.collections.List<1:0>){}[0]
+    final fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun minusAssign(kotlin/Array<#A>) // androidx.collection/MutableObjectList.minusAssign|minusAssign(kotlin.Array<1:0>){}[0]
+    final fun plusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(androidx.collection.ObjectList<1:0>){}[0]
+    final fun plusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun plusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final fun plusAssign(kotlin.collections/List<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.collections.List<1:0>){}[0]
+    final fun plusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun plusAssign(kotlin/Array<#A>) // androidx.collection/MutableObjectList.plusAssign|plusAssign(kotlin.Array<1:0>){}[0]
+    final fun remove(#A): kotlin/Boolean // androidx.collection/MutableObjectList.remove|remove(1:0){}[0]
+    final fun removeAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(androidx.collection.ObjectList<1:0>){}[0]
+    final fun removeAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun removeAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.collections.Iterable<1:0>){}[0]
+    final fun removeAll(kotlin.collections/List<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.collections.List<1:0>){}[0]
+    final fun removeAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun removeAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.removeAll|removeAll(kotlin.Array<1:0>){}[0]
+    final fun removeAt(kotlin/Int): #A // androidx.collection/MutableObjectList.removeAt|removeAt(kotlin.Int){}[0]
+    final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableObjectList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+    final fun retainAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(androidx.collection.ObjectList<1:0>){}[0]
+    final fun retainAll(kotlin.collections/Collection<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0]
+    final fun retainAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.collections.Iterable<1:0>){}[0]
+    final fun retainAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun retainAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/MutableObjectList.retainAll|retainAll(kotlin.Array<1:0>){}[0]
+    final fun set(kotlin/Int, #A): #A // androidx.collection/MutableObjectList.set|set(kotlin.Int;1:0){}[0]
+    final fun trim(kotlin/Int =...) // androidx.collection/MutableObjectList.trim|trim(kotlin.Int){}[0]
+    final inline fun minusAssign(#A) // androidx.collection/MutableObjectList.minusAssign|minusAssign(1:0){}[0]
+    final inline fun plusAssign(#A) // androidx.collection/MutableObjectList.plusAssign|plusAssign(1:0){}[0]
+    final inline fun removeIf(kotlin/Function1<#A, kotlin/Boolean>) // androidx.collection/MutableObjectList.removeIf|removeIf(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final val capacity // androidx.collection/MutableObjectList.capacity|{}capacity[0]
+        final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableObjectList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableObjectLongMap : androidx.collection/ObjectLongMap<#A> { // androidx.collection/MutableObjectLongMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableObjectLongMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableObjectLongMap.clear|clear(){}[0]
+    final fun put(#A, kotlin/Long) // androidx.collection/MutableObjectLongMap.put|put(1:0;kotlin.Long){}[0]
+    final fun put(#A, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableObjectLongMap.put|put(1:0;kotlin.Long;kotlin.Long){}[0]
+    final fun putAll(androidx.collection/ObjectLongMap<#A>) // androidx.collection/MutableObjectLongMap.putAll|putAll(androidx.collection.ObjectLongMap<1:0>){}[0]
+    final fun remove(#A) // androidx.collection/MutableObjectLongMap.remove|remove(1:0){}[0]
+    final fun remove(#A, kotlin/Long): kotlin/Boolean // androidx.collection/MutableObjectLongMap.remove|remove(1:0;kotlin.Long){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableObjectLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(#A, kotlin/Long) // androidx.collection/MutableObjectLongMap.set|set(1:0;kotlin.Long){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableObjectLongMap.trim|trim(){}[0]
+    final inline fun getOrPut(#A, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableObjectLongMap.getOrPut|getOrPut(1:0;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun minusAssign(#A) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(1:0){}[0]
+    final inline fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final inline fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final inline fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final inline fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableObjectLongMap.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+    final inline fun plusAssign(androidx.collection/ObjectLongMap<#A>) // androidx.collection/MutableObjectLongMap.plusAssign|plusAssign(androidx.collection.ObjectLongMap<1:0>){}[0]
+    final inline fun removeIf(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableObjectLongMap.removeIf|removeIf(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class <#A: kotlin/Any?> androidx.collection/MutableScatterSet : androidx.collection/ScatterSet<#A> { // androidx.collection/MutableScatterSet|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableScatterSet.<init>|<init>(kotlin.Int){}[0]
+    final fun add(#A): kotlin/Boolean // androidx.collection/MutableScatterSet.add|add(1:0){}[0]
+    final fun addAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(androidx.collection.ObjectList<1:0>){}[0]
+    final fun addAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun addAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(kotlin.collections.Iterable<1:0>){}[0]
+    final fun addAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun addAll(kotlin/Array<out #A>): kotlin/Boolean // androidx.collection/MutableScatterSet.addAll|addAll(kotlin.Array<out|1:0>){}[0]
+    final fun asMutableSet(): kotlin.collections/MutableSet<#A> // androidx.collection/MutableScatterSet.asMutableSet|asMutableSet(){}[0]
+    final fun clear() // androidx.collection/MutableScatterSet.clear|clear(){}[0]
+    final fun minusAssign(#A) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(1:0){}[0]
+    final fun minusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(androidx.collection.ObjectList<1:0>){}[0]
+    final fun minusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun minusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final fun minusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun minusAssign(kotlin/Array<out #A>) // androidx.collection/MutableScatterSet.minusAssign|minusAssign(kotlin.Array<out|1:0>){}[0]
+    final fun plusAssign(#A) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(1:0){}[0]
+    final fun plusAssign(androidx.collection/ObjectList<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(androidx.collection.ObjectList<1:0>){}[0]
+    final fun plusAssign(androidx.collection/ScatterSet<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun plusAssign(kotlin.collections/Iterable<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(kotlin.collections.Iterable<1:0>){}[0]
+    final fun plusAssign(kotlin.sequences/Sequence<#A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun plusAssign(kotlin/Array<out #A>) // androidx.collection/MutableScatterSet.plusAssign|plusAssign(kotlin.Array<out|1:0>){}[0]
+    final fun remove(#A): kotlin/Boolean // androidx.collection/MutableScatterSet.remove|remove(1:0){}[0]
+    final fun removeAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(androidx.collection.ObjectList<1:0>){}[0]
+    final fun removeAll(androidx.collection/ScatterSet<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(androidx.collection.ScatterSet<1:0>){}[0]
+    final fun removeAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(kotlin.collections.Iterable<1:0>){}[0]
+    final fun removeAll(kotlin.sequences/Sequence<#A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(kotlin.sequences.Sequence<1:0>){}[0]
+    final fun removeAll(kotlin/Array<out #A>): kotlin/Boolean // androidx.collection/MutableScatterSet.removeAll|removeAll(kotlin.Array<out|1:0>){}[0]
+    final fun removeElementAt(kotlin/Int) // androidx.collection/MutableScatterSet.removeElementAt|removeElementAt(kotlin.Int){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableScatterSet.trim|trim(){}[0]
+    final inline fun removeIf(kotlin/Function1<#A, kotlin/Boolean>) // androidx.collection/MutableScatterSet.removeIf|removeIf(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/CircularIntArray { // androidx.collection/CircularIntArray|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/CircularIntArray.<init>|<init>(kotlin.Int){}[0]
+    final fun addFirst(kotlin/Int) // androidx.collection/CircularIntArray.addFirst|addFirst(kotlin.Int){}[0]
+    final fun addLast(kotlin/Int) // androidx.collection/CircularIntArray.addLast|addLast(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/CircularIntArray.clear|clear(){}[0]
+    final fun get(kotlin/Int): kotlin/Int // androidx.collection/CircularIntArray.get|get(kotlin.Int){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/CircularIntArray.isEmpty|isEmpty(){}[0]
+    final fun popFirst(): kotlin/Int // androidx.collection/CircularIntArray.popFirst|popFirst(){}[0]
+    final fun popLast(): kotlin/Int // androidx.collection/CircularIntArray.popLast|popLast(){}[0]
+    final fun removeFromEnd(kotlin/Int) // androidx.collection/CircularIntArray.removeFromEnd|removeFromEnd(kotlin.Int){}[0]
+    final fun removeFromStart(kotlin/Int) // androidx.collection/CircularIntArray.removeFromStart|removeFromStart(kotlin.Int){}[0]
+    final fun size(): kotlin/Int // androidx.collection/CircularIntArray.size|size(){}[0]
+    final val first // androidx.collection/CircularIntArray.first|{}first[0]
+        final fun <get-first>(): kotlin/Int // androidx.collection/CircularIntArray.first.<get-first>|<get-first>(){}[0]
+    final val last // androidx.collection/CircularIntArray.last|{}last[0]
+        final fun <get-last>(): kotlin/Int // androidx.collection/CircularIntArray.last.<get-last>|<get-last>(){}[0]
+}
+final class androidx.collection/LongLongPair { // androidx.collection/LongLongPair|null[0]
+    constructor <init>(kotlin/Long, kotlin/Long) // androidx.collection/LongLongPair.<init>|<init>(kotlin.Long;kotlin.Long){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongLongPair.equals|equals(kotlin.Any?){}[0]
+    final fun hashCode(): kotlin/Int // androidx.collection/LongLongPair.hashCode|hashCode(){}[0]
+    final fun toString(): kotlin/String // androidx.collection/LongLongPair.toString|toString(){}[0]
+    final inline fun component1(): kotlin/Long // androidx.collection/LongLongPair.component1|component1(){}[0]
+    final inline fun component2(): kotlin/Long // androidx.collection/LongLongPair.component2|component2(){}[0]
+    final val first // androidx.collection/LongLongPair.first|{}first[0]
+        final fun <get-first>(): kotlin/Long // androidx.collection/LongLongPair.first.<get-first>|<get-first>(){}[0]
+    final val second // androidx.collection/LongLongPair.second|{}second[0]
+        final fun <get-second>(): kotlin/Long // androidx.collection/LongLongPair.second.<get-second>|<get-second>(){}[0]
+}
+final class androidx.collection/MutableDoubleList : androidx.collection/DoubleList { // androidx.collection/MutableDoubleList|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableDoubleList.<init>|<init>(kotlin.Int){}[0]
+    final fun add(kotlin/Double): kotlin/Boolean // androidx.collection/MutableDoubleList.add|add(kotlin.Double){}[0]
+    final fun add(kotlin/Int, kotlin/Double) // androidx.collection/MutableDoubleList.add|add(kotlin.Int;kotlin.Double){}[0]
+    final fun addAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(androidx.collection.DoubleList){}[0]
+    final fun addAll(kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(kotlin.DoubleArray){}[0]
+    final fun addAll(kotlin/Int, androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(kotlin.Int;androidx.collection.DoubleList){}[0]
+    final fun addAll(kotlin/Int, kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.addAll|addAll(kotlin.Int;kotlin.DoubleArray){}[0]
+    final fun clear() // androidx.collection/MutableDoubleList.clear|clear(){}[0]
+    final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableDoubleList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+    final fun minusAssign(androidx.collection/DoubleList) // androidx.collection/MutableDoubleList.minusAssign|minusAssign(androidx.collection.DoubleList){}[0]
+    final fun minusAssign(kotlin/DoubleArray) // androidx.collection/MutableDoubleList.minusAssign|minusAssign(kotlin.DoubleArray){}[0]
+    final fun plusAssign(androidx.collection/DoubleList) // androidx.collection/MutableDoubleList.plusAssign|plusAssign(androidx.collection.DoubleList){}[0]
+    final fun plusAssign(kotlin/DoubleArray) // androidx.collection/MutableDoubleList.plusAssign|plusAssign(kotlin.DoubleArray){}[0]
+    final fun remove(kotlin/Double): kotlin/Boolean // androidx.collection/MutableDoubleList.remove|remove(kotlin.Double){}[0]
+    final fun removeAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.removeAll|removeAll(androidx.collection.DoubleList){}[0]
+    final fun removeAll(kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.removeAll|removeAll(kotlin.DoubleArray){}[0]
+    final fun removeAt(kotlin/Int): kotlin/Double // androidx.collection/MutableDoubleList.removeAt|removeAt(kotlin.Int){}[0]
+    final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableDoubleList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+    final fun retainAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/MutableDoubleList.retainAll|retainAll(androidx.collection.DoubleList){}[0]
+    final fun retainAll(kotlin/DoubleArray): kotlin/Boolean // androidx.collection/MutableDoubleList.retainAll|retainAll(kotlin.DoubleArray){}[0]
+    final fun set(kotlin/Int, kotlin/Double): kotlin/Double // androidx.collection/MutableDoubleList.set|set(kotlin.Int;kotlin.Double){}[0]
+    final fun sort() // androidx.collection/MutableDoubleList.sort|sort(){}[0]
+    final fun sortDescending() // androidx.collection/MutableDoubleList.sortDescending|sortDescending(){}[0]
+    final fun trim(kotlin/Int =...) // androidx.collection/MutableDoubleList.trim|trim(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/Double) // androidx.collection/MutableDoubleList.minusAssign|minusAssign(kotlin.Double){}[0]
+    final inline fun plusAssign(kotlin/Double) // androidx.collection/MutableDoubleList.plusAssign|plusAssign(kotlin.Double){}[0]
+    final val capacity // androidx.collection/MutableDoubleList.capacity|{}capacity[0]
+        final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableDoubleList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableFloatFloatMap : androidx.collection/FloatFloatMap { // androidx.collection/MutableFloatFloatMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatFloatMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableFloatFloatMap.clear|clear(){}[0]
+    final fun put(kotlin/Float, kotlin/Float) // androidx.collection/MutableFloatFloatMap.put|put(kotlin.Float;kotlin.Float){}[0]
+    final fun put(kotlin/Float, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableFloatFloatMap.put|put(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+    final fun putAll(androidx.collection/FloatFloatMap) // androidx.collection/MutableFloatFloatMap.putAll|putAll(androidx.collection.FloatFloatMap){}[0]
+    final fun remove(kotlin/Float) // androidx.collection/MutableFloatFloatMap.remove|remove(kotlin.Float){}[0]
+    final fun remove(kotlin/Float, kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatFloatMap.remove|remove(kotlin.Float;kotlin.Float){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableFloatFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Float, kotlin/Float) // androidx.collection/MutableFloatFloatMap.set|set(kotlin.Float;kotlin.Float){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableFloatFloatMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Float, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableFloatFloatMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+    final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+    final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(kotlin.Float){}[0]
+    final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatFloatMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+    final inline fun plusAssign(androidx.collection/FloatFloatMap) // androidx.collection/MutableFloatFloatMap.plusAssign|plusAssign(androidx.collection.FloatFloatMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableFloatFloatMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableFloatIntMap : androidx.collection/FloatIntMap { // androidx.collection/MutableFloatIntMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatIntMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableFloatIntMap.clear|clear(){}[0]
+    final fun put(kotlin/Float, kotlin/Int) // androidx.collection/MutableFloatIntMap.put|put(kotlin.Float;kotlin.Int){}[0]
+    final fun put(kotlin/Float, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableFloatIntMap.put|put(kotlin.Float;kotlin.Int;kotlin.Int){}[0]
+    final fun putAll(androidx.collection/FloatIntMap) // androidx.collection/MutableFloatIntMap.putAll|putAll(androidx.collection.FloatIntMap){}[0]
+    final fun remove(kotlin/Float) // androidx.collection/MutableFloatIntMap.remove|remove(kotlin.Float){}[0]
+    final fun remove(kotlin/Float, kotlin/Int): kotlin/Boolean // androidx.collection/MutableFloatIntMap.remove|remove(kotlin.Float;kotlin.Int){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableFloatIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Float, kotlin/Int) // androidx.collection/MutableFloatIntMap.set|set(kotlin.Float;kotlin.Int){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableFloatIntMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Float, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableFloatIntMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+    final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+    final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(kotlin.Float){}[0]
+    final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatIntMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+    final inline fun plusAssign(androidx.collection/FloatIntMap) // androidx.collection/MutableFloatIntMap.plusAssign|plusAssign(androidx.collection.FloatIntMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableFloatIntMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableFloatList : androidx.collection/FloatList { // androidx.collection/MutableFloatList|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatList.<init>|<init>(kotlin.Int){}[0]
+    final fun add(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatList.add|add(kotlin.Float){}[0]
+    final fun add(kotlin/Int, kotlin/Float) // androidx.collection/MutableFloatList.add|add(kotlin.Int;kotlin.Float){}[0]
+    final fun addAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(androidx.collection.FloatList){}[0]
+    final fun addAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(kotlin.FloatArray){}[0]
+    final fun addAll(kotlin/Int, androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(kotlin.Int;androidx.collection.FloatList){}[0]
+    final fun addAll(kotlin/Int, kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.addAll|addAll(kotlin.Int;kotlin.FloatArray){}[0]
+    final fun clear() // androidx.collection/MutableFloatList.clear|clear(){}[0]
+    final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableFloatList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+    final fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatList.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+    final fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatList.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+    final fun plusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatList.plusAssign|plusAssign(androidx.collection.FloatList){}[0]
+    final fun plusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatList.plusAssign|plusAssign(kotlin.FloatArray){}[0]
+    final fun remove(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatList.remove|remove(kotlin.Float){}[0]
+    final fun removeAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.removeAll|removeAll(androidx.collection.FloatList){}[0]
+    final fun removeAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.removeAll|removeAll(kotlin.FloatArray){}[0]
+    final fun removeAt(kotlin/Int): kotlin/Float // androidx.collection/MutableFloatList.removeAt|removeAt(kotlin.Int){}[0]
+    final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableFloatList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+    final fun retainAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/MutableFloatList.retainAll|retainAll(androidx.collection.FloatList){}[0]
+    final fun retainAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatList.retainAll|retainAll(kotlin.FloatArray){}[0]
+    final fun set(kotlin/Int, kotlin/Float): kotlin/Float // androidx.collection/MutableFloatList.set|set(kotlin.Int;kotlin.Float){}[0]
+    final fun sort() // androidx.collection/MutableFloatList.sort|sort(){}[0]
+    final fun sortDescending() // androidx.collection/MutableFloatList.sortDescending|sortDescending(){}[0]
+    final fun trim(kotlin/Int =...) // androidx.collection/MutableFloatList.trim|trim(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatList.minusAssign|minusAssign(kotlin.Float){}[0]
+    final inline fun plusAssign(kotlin/Float) // androidx.collection/MutableFloatList.plusAssign|plusAssign(kotlin.Float){}[0]
+    final val capacity // androidx.collection/MutableFloatList.capacity|{}capacity[0]
+        final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableFloatList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableFloatLongMap : androidx.collection/FloatLongMap { // androidx.collection/MutableFloatLongMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatLongMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableFloatLongMap.clear|clear(){}[0]
+    final fun put(kotlin/Float, kotlin/Long) // androidx.collection/MutableFloatLongMap.put|put(kotlin.Float;kotlin.Long){}[0]
+    final fun put(kotlin/Float, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableFloatLongMap.put|put(kotlin.Float;kotlin.Long;kotlin.Long){}[0]
+    final fun putAll(androidx.collection/FloatLongMap) // androidx.collection/MutableFloatLongMap.putAll|putAll(androidx.collection.FloatLongMap){}[0]
+    final fun remove(kotlin/Float) // androidx.collection/MutableFloatLongMap.remove|remove(kotlin.Float){}[0]
+    final fun remove(kotlin/Float, kotlin/Long): kotlin/Boolean // androidx.collection/MutableFloatLongMap.remove|remove(kotlin.Float;kotlin.Long){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableFloatLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Float, kotlin/Long) // androidx.collection/MutableFloatLongMap.set|set(kotlin.Float;kotlin.Long){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableFloatLongMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Float, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableFloatLongMap.getOrPut|getOrPut(kotlin.Float;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun minusAssign(androidx.collection/FloatList) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(androidx.collection.FloatList){}[0]
+    final inline fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+    final inline fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(kotlin.Float){}[0]
+    final inline fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatLongMap.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+    final inline fun plusAssign(androidx.collection/FloatLongMap) // androidx.collection/MutableFloatLongMap.plusAssign|plusAssign(androidx.collection.FloatLongMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableFloatLongMap.removeIf|removeIf(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableFloatSet : androidx.collection/FloatSet { // androidx.collection/MutableFloatSet|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableFloatSet.<init>|<init>(kotlin.Int){}[0]
+    final fun add(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatSet.add|add(kotlin.Float){}[0]
+    final fun addAll(androidx.collection/FloatSet): kotlin/Boolean // androidx.collection/MutableFloatSet.addAll|addAll(androidx.collection.FloatSet){}[0]
+    final fun addAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatSet.addAll|addAll(kotlin.FloatArray){}[0]
+    final fun clear() // androidx.collection/MutableFloatSet.clear|clear(){}[0]
+    final fun minusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatSet.minusAssign|minusAssign(androidx.collection.FloatSet){}[0]
+    final fun minusAssign(kotlin/Float) // androidx.collection/MutableFloatSet.minusAssign|minusAssign(kotlin.Float){}[0]
+    final fun minusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatSet.minusAssign|minusAssign(kotlin.FloatArray){}[0]
+    final fun plusAssign(androidx.collection/FloatSet) // androidx.collection/MutableFloatSet.plusAssign|plusAssign(androidx.collection.FloatSet){}[0]
+    final fun plusAssign(kotlin/Float) // androidx.collection/MutableFloatSet.plusAssign|plusAssign(kotlin.Float){}[0]
+    final fun plusAssign(kotlin/FloatArray) // androidx.collection/MutableFloatSet.plusAssign|plusAssign(kotlin.FloatArray){}[0]
+    final fun remove(kotlin/Float): kotlin/Boolean // androidx.collection/MutableFloatSet.remove|remove(kotlin.Float){}[0]
+    final fun removeAll(androidx.collection/FloatSet): kotlin/Boolean // androidx.collection/MutableFloatSet.removeAll|removeAll(androidx.collection.FloatSet){}[0]
+    final fun removeAll(kotlin/FloatArray): kotlin/Boolean // androidx.collection/MutableFloatSet.removeAll|removeAll(kotlin.FloatArray){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableFloatSet.trim|trim(){}[0]
+}
+final class androidx.collection/MutableIntFloatMap : androidx.collection/IntFloatMap { // androidx.collection/MutableIntFloatMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntFloatMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableIntFloatMap.clear|clear(){}[0]
+    final fun put(kotlin/Int, kotlin/Float) // androidx.collection/MutableIntFloatMap.put|put(kotlin.Int;kotlin.Float){}[0]
+    final fun put(kotlin/Int, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableIntFloatMap.put|put(kotlin.Int;kotlin.Float;kotlin.Float){}[0]
+    final fun putAll(androidx.collection/IntFloatMap) // androidx.collection/MutableIntFloatMap.putAll|putAll(androidx.collection.IntFloatMap){}[0]
+    final fun remove(kotlin/Int) // androidx.collection/MutableIntFloatMap.remove|remove(kotlin.Int){}[0]
+    final fun remove(kotlin/Int, kotlin/Float): kotlin/Boolean // androidx.collection/MutableIntFloatMap.remove|remove(kotlin.Int;kotlin.Float){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableIntFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Int, kotlin/Float) // androidx.collection/MutableIntFloatMap.set|set(kotlin.Int;kotlin.Float){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableIntFloatMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Int, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableIntFloatMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+    final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+    final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntFloatMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+    final inline fun plusAssign(androidx.collection/IntFloatMap) // androidx.collection/MutableIntFloatMap.plusAssign|plusAssign(androidx.collection.IntFloatMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableIntFloatMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableIntIntMap : androidx.collection/IntIntMap { // androidx.collection/MutableIntIntMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntIntMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableIntIntMap.clear|clear(){}[0]
+    final fun put(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntIntMap.put|put(kotlin.Int;kotlin.Int){}[0]
+    final fun put(kotlin/Int, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableIntIntMap.put|put(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+    final fun putAll(androidx.collection/IntIntMap) // androidx.collection/MutableIntIntMap.putAll|putAll(androidx.collection.IntIntMap){}[0]
+    final fun remove(kotlin/Int) // androidx.collection/MutableIntIntMap.remove|remove(kotlin.Int){}[0]
+    final fun remove(kotlin/Int, kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntIntMap.remove|remove(kotlin.Int;kotlin.Int){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableIntIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntIntMap.set|set(kotlin.Int;kotlin.Int){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableIntIntMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Int, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableIntIntMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+    final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+    final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntIntMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+    final inline fun plusAssign(androidx.collection/IntIntMap) // androidx.collection/MutableIntIntMap.plusAssign|plusAssign(androidx.collection.IntIntMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableIntIntMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableIntList : androidx.collection/IntList { // androidx.collection/MutableIntList|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntList.<init>|<init>(kotlin.Int){}[0]
+    final fun add(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntList.add|add(kotlin.Int){}[0]
+    final fun add(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntList.add|add(kotlin.Int;kotlin.Int){}[0]
+    final fun addAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(androidx.collection.IntList){}[0]
+    final fun addAll(kotlin/Int, androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(kotlin.Int;androidx.collection.IntList){}[0]
+    final fun addAll(kotlin/Int, kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(kotlin.Int;kotlin.IntArray){}[0]
+    final fun addAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.addAll|addAll(kotlin.IntArray){}[0]
+    final fun clear() // androidx.collection/MutableIntList.clear|clear(){}[0]
+    final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableIntList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+    final fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntList.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+    final fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntList.minusAssign|minusAssign(kotlin.IntArray){}[0]
+    final fun plusAssign(androidx.collection/IntList) // androidx.collection/MutableIntList.plusAssign|plusAssign(androidx.collection.IntList){}[0]
+    final fun plusAssign(kotlin/IntArray) // androidx.collection/MutableIntList.plusAssign|plusAssign(kotlin.IntArray){}[0]
+    final fun remove(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntList.remove|remove(kotlin.Int){}[0]
+    final fun removeAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.removeAll|removeAll(androidx.collection.IntList){}[0]
+    final fun removeAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.removeAll|removeAll(kotlin.IntArray){}[0]
+    final fun removeAt(kotlin/Int): kotlin/Int // androidx.collection/MutableIntList.removeAt|removeAt(kotlin.Int){}[0]
+    final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableIntList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+    final fun retainAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/MutableIntList.retainAll|retainAll(androidx.collection.IntList){}[0]
+    final fun retainAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntList.retainAll|retainAll(kotlin.IntArray){}[0]
+    final fun set(kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableIntList.set|set(kotlin.Int;kotlin.Int){}[0]
+    final fun sort() // androidx.collection/MutableIntList.sort|sort(){}[0]
+    final fun sortDescending() // androidx.collection/MutableIntList.sortDescending|sortDescending(){}[0]
+    final fun trim(kotlin/Int =...) // androidx.collection/MutableIntList.trim|trim(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntList.minusAssign|minusAssign(kotlin.Int){}[0]
+    final inline fun plusAssign(kotlin/Int) // androidx.collection/MutableIntList.plusAssign|plusAssign(kotlin.Int){}[0]
+    final val capacity // androidx.collection/MutableIntList.capacity|{}capacity[0]
+        final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableIntList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableIntLongMap : androidx.collection/IntLongMap { // androidx.collection/MutableIntLongMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntLongMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableIntLongMap.clear|clear(){}[0]
+    final fun put(kotlin/Int, kotlin/Long) // androidx.collection/MutableIntLongMap.put|put(kotlin.Int;kotlin.Long){}[0]
+    final fun put(kotlin/Int, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableIntLongMap.put|put(kotlin.Int;kotlin.Long;kotlin.Long){}[0]
+    final fun putAll(androidx.collection/IntLongMap) // androidx.collection/MutableIntLongMap.putAll|putAll(androidx.collection.IntLongMap){}[0]
+    final fun remove(kotlin/Int) // androidx.collection/MutableIntLongMap.remove|remove(kotlin.Int){}[0]
+    final fun remove(kotlin/Int, kotlin/Long): kotlin/Boolean // androidx.collection/MutableIntLongMap.remove|remove(kotlin.Int;kotlin.Long){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableIntLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Int, kotlin/Long) // androidx.collection/MutableIntLongMap.set|set(kotlin.Int;kotlin.Long){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableIntLongMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Int, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableIntLongMap.getOrPut|getOrPut(kotlin.Int;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun minusAssign(androidx.collection/IntList) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(androidx.collection.IntList){}[0]
+    final inline fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+    final inline fun minusAssign(kotlin/Int) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntLongMap.minusAssign|minusAssign(kotlin.IntArray){}[0]
+    final inline fun plusAssign(androidx.collection/IntLongMap) // androidx.collection/MutableIntLongMap.plusAssign|plusAssign(androidx.collection.IntLongMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableIntLongMap.removeIf|removeIf(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableIntSet : androidx.collection/IntSet { // androidx.collection/MutableIntSet|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableIntSet.<init>|<init>(kotlin.Int){}[0]
+    final fun add(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntSet.add|add(kotlin.Int){}[0]
+    final fun addAll(androidx.collection/IntSet): kotlin/Boolean // androidx.collection/MutableIntSet.addAll|addAll(androidx.collection.IntSet){}[0]
+    final fun addAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntSet.addAll|addAll(kotlin.IntArray){}[0]
+    final fun clear() // androidx.collection/MutableIntSet.clear|clear(){}[0]
+    final fun minusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntSet.minusAssign|minusAssign(androidx.collection.IntSet){}[0]
+    final fun minusAssign(kotlin/Int) // androidx.collection/MutableIntSet.minusAssign|minusAssign(kotlin.Int){}[0]
+    final fun minusAssign(kotlin/IntArray) // androidx.collection/MutableIntSet.minusAssign|minusAssign(kotlin.IntArray){}[0]
+    final fun plusAssign(androidx.collection/IntSet) // androidx.collection/MutableIntSet.plusAssign|plusAssign(androidx.collection.IntSet){}[0]
+    final fun plusAssign(kotlin/Int) // androidx.collection/MutableIntSet.plusAssign|plusAssign(kotlin.Int){}[0]
+    final fun plusAssign(kotlin/IntArray) // androidx.collection/MutableIntSet.plusAssign|plusAssign(kotlin.IntArray){}[0]
+    final fun remove(kotlin/Int): kotlin/Boolean // androidx.collection/MutableIntSet.remove|remove(kotlin.Int){}[0]
+    final fun removeAll(androidx.collection/IntSet): kotlin/Boolean // androidx.collection/MutableIntSet.removeAll|removeAll(androidx.collection.IntSet){}[0]
+    final fun removeAll(kotlin/IntArray): kotlin/Boolean // androidx.collection/MutableIntSet.removeAll|removeAll(kotlin.IntArray){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableIntSet.trim|trim(){}[0]
+}
+final class androidx.collection/MutableLongFloatMap : androidx.collection/LongFloatMap { // androidx.collection/MutableLongFloatMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongFloatMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableLongFloatMap.clear|clear(){}[0]
+    final fun put(kotlin/Long, kotlin/Float) // androidx.collection/MutableLongFloatMap.put|put(kotlin.Long;kotlin.Float){}[0]
+    final fun put(kotlin/Long, kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/MutableLongFloatMap.put|put(kotlin.Long;kotlin.Float;kotlin.Float){}[0]
+    final fun putAll(androidx.collection/LongFloatMap) // androidx.collection/MutableLongFloatMap.putAll|putAll(androidx.collection.LongFloatMap){}[0]
+    final fun remove(kotlin/Long) // androidx.collection/MutableLongFloatMap.remove|remove(kotlin.Long){}[0]
+    final fun remove(kotlin/Long, kotlin/Float): kotlin/Boolean // androidx.collection/MutableLongFloatMap.remove|remove(kotlin.Long;kotlin.Float){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableLongFloatMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Long, kotlin/Float) // androidx.collection/MutableLongFloatMap.set|set(kotlin.Long;kotlin.Float){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableLongFloatMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Long, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/MutableLongFloatMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+    final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+    final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(kotlin.Long){}[0]
+    final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongFloatMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+    final inline fun plusAssign(androidx.collection/LongFloatMap) // androidx.collection/MutableLongFloatMap.plusAssign|plusAssign(androidx.collection.LongFloatMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>) // androidx.collection/MutableLongFloatMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableLongIntMap : androidx.collection/LongIntMap { // androidx.collection/MutableLongIntMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongIntMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableLongIntMap.clear|clear(){}[0]
+    final fun put(kotlin/Long, kotlin/Int) // androidx.collection/MutableLongIntMap.put|put(kotlin.Long;kotlin.Int){}[0]
+    final fun put(kotlin/Long, kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/MutableLongIntMap.put|put(kotlin.Long;kotlin.Int;kotlin.Int){}[0]
+    final fun putAll(androidx.collection/LongIntMap) // androidx.collection/MutableLongIntMap.putAll|putAll(androidx.collection.LongIntMap){}[0]
+    final fun remove(kotlin/Long) // androidx.collection/MutableLongIntMap.remove|remove(kotlin.Long){}[0]
+    final fun remove(kotlin/Long, kotlin/Int): kotlin/Boolean // androidx.collection/MutableLongIntMap.remove|remove(kotlin.Long;kotlin.Int){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableLongIntMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Long, kotlin/Int) // androidx.collection/MutableLongIntMap.set|set(kotlin.Long;kotlin.Int){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableLongIntMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Long, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/MutableLongIntMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+    final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+    final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(kotlin.Long){}[0]
+    final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongIntMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+    final inline fun plusAssign(androidx.collection/LongIntMap) // androidx.collection/MutableLongIntMap.plusAssign|plusAssign(androidx.collection.LongIntMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>) // androidx.collection/MutableLongIntMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableLongList : androidx.collection/LongList { // androidx.collection/MutableLongList|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongList.<init>|<init>(kotlin.Int){}[0]
+    final fun add(kotlin/Int, kotlin/Long) // androidx.collection/MutableLongList.add|add(kotlin.Int;kotlin.Long){}[0]
+    final fun add(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongList.add|add(kotlin.Long){}[0]
+    final fun addAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(androidx.collection.LongList){}[0]
+    final fun addAll(kotlin/Int, androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(kotlin.Int;androidx.collection.LongList){}[0]
+    final fun addAll(kotlin/Int, kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(kotlin.Int;kotlin.LongArray){}[0]
+    final fun addAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.addAll|addAll(kotlin.LongArray){}[0]
+    final fun clear() // androidx.collection/MutableLongList.clear|clear(){}[0]
+    final fun ensureCapacity(kotlin/Int) // androidx.collection/MutableLongList.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+    final fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongList.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+    final fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongList.minusAssign|minusAssign(kotlin.LongArray){}[0]
+    final fun plusAssign(androidx.collection/LongList) // androidx.collection/MutableLongList.plusAssign|plusAssign(androidx.collection.LongList){}[0]
+    final fun plusAssign(kotlin/LongArray) // androidx.collection/MutableLongList.plusAssign|plusAssign(kotlin.LongArray){}[0]
+    final fun remove(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongList.remove|remove(kotlin.Long){}[0]
+    final fun removeAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.removeAll|removeAll(androidx.collection.LongList){}[0]
+    final fun removeAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.removeAll|removeAll(kotlin.LongArray){}[0]
+    final fun removeAt(kotlin/Int): kotlin/Long // androidx.collection/MutableLongList.removeAt|removeAt(kotlin.Int){}[0]
+    final fun removeRange(kotlin/Int, kotlin/Int) // androidx.collection/MutableLongList.removeRange|removeRange(kotlin.Int;kotlin.Int){}[0]
+    final fun retainAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/MutableLongList.retainAll|retainAll(androidx.collection.LongList){}[0]
+    final fun retainAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongList.retainAll|retainAll(kotlin.LongArray){}[0]
+    final fun set(kotlin/Int, kotlin/Long): kotlin/Long // androidx.collection/MutableLongList.set|set(kotlin.Int;kotlin.Long){}[0]
+    final fun sort() // androidx.collection/MutableLongList.sort|sort(){}[0]
+    final fun sortDescending() // androidx.collection/MutableLongList.sortDescending|sortDescending(){}[0]
+    final fun trim(kotlin/Int =...) // androidx.collection/MutableLongList.trim|trim(kotlin.Int){}[0]
+    final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongList.minusAssign|minusAssign(kotlin.Long){}[0]
+    final inline fun plusAssign(kotlin/Long) // androidx.collection/MutableLongList.plusAssign|plusAssign(kotlin.Long){}[0]
+    final val capacity // androidx.collection/MutableLongList.capacity|{}capacity[0]
+        final inline fun <get-capacity>(): kotlin/Int // androidx.collection/MutableLongList.capacity.<get-capacity>|<get-capacity>(){}[0]
+}
+final class androidx.collection/MutableLongLongMap : androidx.collection/LongLongMap { // androidx.collection/MutableLongLongMap|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongLongMap.<init>|<init>(kotlin.Int){}[0]
+    final fun clear() // androidx.collection/MutableLongLongMap.clear|clear(){}[0]
+    final fun put(kotlin/Long, kotlin/Long) // androidx.collection/MutableLongLongMap.put|put(kotlin.Long;kotlin.Long){}[0]
+    final fun put(kotlin/Long, kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/MutableLongLongMap.put|put(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+    final fun putAll(androidx.collection/LongLongMap) // androidx.collection/MutableLongLongMap.putAll|putAll(androidx.collection.LongLongMap){}[0]
+    final fun remove(kotlin/Long) // androidx.collection/MutableLongLongMap.remove|remove(kotlin.Long){}[0]
+    final fun remove(kotlin/Long, kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongLongMap.remove|remove(kotlin.Long;kotlin.Long){}[0]
+    final fun removeValueAt(kotlin/Int) // androidx.collection/MutableLongLongMap.removeValueAt|removeValueAt(kotlin.Int){}[0]
+    final fun set(kotlin/Long, kotlin/Long) // androidx.collection/MutableLongLongMap.set|set(kotlin.Long;kotlin.Long){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableLongLongMap.trim|trim(){}[0]
+    final inline fun getOrPut(kotlin/Long, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/MutableLongLongMap.getOrPut|getOrPut(kotlin.Long;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun minusAssign(androidx.collection/LongList) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(androidx.collection.LongList){}[0]
+    final inline fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+    final inline fun minusAssign(kotlin/Long) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(kotlin.Long){}[0]
+    final inline fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongLongMap.minusAssign|minusAssign(kotlin.LongArray){}[0]
+    final inline fun plusAssign(androidx.collection/LongLongMap) // androidx.collection/MutableLongLongMap.plusAssign|plusAssign(androidx.collection.LongLongMap){}[0]
+    final inline fun removeIf(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>) // androidx.collection/MutableLongLongMap.removeIf|removeIf(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+}
+final class androidx.collection/MutableLongSet : androidx.collection/LongSet { // androidx.collection/MutableLongSet|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/MutableLongSet.<init>|<init>(kotlin.Int){}[0]
+    final fun add(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongSet.add|add(kotlin.Long){}[0]
+    final fun addAll(androidx.collection/LongSet): kotlin/Boolean // androidx.collection/MutableLongSet.addAll|addAll(androidx.collection.LongSet){}[0]
+    final fun addAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongSet.addAll|addAll(kotlin.LongArray){}[0]
+    final fun clear() // androidx.collection/MutableLongSet.clear|clear(){}[0]
+    final fun minusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongSet.minusAssign|minusAssign(androidx.collection.LongSet){}[0]
+    final fun minusAssign(kotlin/Long) // androidx.collection/MutableLongSet.minusAssign|minusAssign(kotlin.Long){}[0]
+    final fun minusAssign(kotlin/LongArray) // androidx.collection/MutableLongSet.minusAssign|minusAssign(kotlin.LongArray){}[0]
+    final fun plusAssign(androidx.collection/LongSet) // androidx.collection/MutableLongSet.plusAssign|plusAssign(androidx.collection.LongSet){}[0]
+    final fun plusAssign(kotlin/Long) // androidx.collection/MutableLongSet.plusAssign|plusAssign(kotlin.Long){}[0]
+    final fun plusAssign(kotlin/LongArray) // androidx.collection/MutableLongSet.plusAssign|plusAssign(kotlin.LongArray){}[0]
+    final fun remove(kotlin/Long): kotlin/Boolean // androidx.collection/MutableLongSet.remove|remove(kotlin.Long){}[0]
+    final fun removeAll(androidx.collection/LongSet): kotlin/Boolean // androidx.collection/MutableLongSet.removeAll|removeAll(androidx.collection.LongSet){}[0]
+    final fun removeAll(kotlin/LongArray): kotlin/Boolean // androidx.collection/MutableLongSet.removeAll|removeAll(kotlin.LongArray){}[0]
+    final fun trim(): kotlin/Int // androidx.collection/MutableLongSet.trim|trim(){}[0]
+}
+final const val androidx.collection/BitmaskLsb // androidx.collection/BitmaskLsb|<get-BitmaskLsb>(){}[0]
+    final fun <get-BitmaskLsb>(): kotlin/Long // androidx.collection/BitmaskLsb.<get-BitmaskLsb>|<get-BitmaskLsb>(){}[0]
+final const val androidx.collection/BitmaskMsb // androidx.collection/BitmaskMsb|<get-BitmaskMsb>(){}[0]
+    final fun <get-BitmaskMsb>(): kotlin/Long // androidx.collection/BitmaskMsb.<get-BitmaskMsb>|<get-BitmaskMsb>(){}[0]
+final const val androidx.collection/Sentinel // androidx.collection/Sentinel|{}Sentinel[0]
+    final fun <get-Sentinel>(): kotlin/Long // androidx.collection/Sentinel.<get-Sentinel>|<get-Sentinel>(){}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/emptyScatterMap(): androidx.collection/ScatterMap<#A, #B> // androidx.collection/emptyScatterMap|emptyScatterMap(){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/mutableScatterMapOf(): androidx.collection/MutableScatterMap<#A, #B> // androidx.collection/mutableScatterMapOf|mutableScatterMapOf(){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/mutableScatterMapOf(kotlin/Array<out kotlin/Pair<#A, #B>>...): androidx.collection/MutableScatterMap<#A, #B> // androidx.collection/mutableScatterMapOf|mutableScatterMapOf(kotlin.Array<out|kotlin.Pair<0:0,0:1>>...){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/keyIterator(): kotlin.collections/LongIterator // androidx.collection/keyIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/plus(androidx.collection/LongSparseArray<#A>): androidx.collection/LongSparseArray<#A> // androidx.collection/plus|[email protected]<0:0>(androidx.collection.LongSparseArray<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/remove(kotlin/Long, #A): kotlin/Boolean // androidx.collection/remove|[email protected]<0:0>(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/valueIterator(): kotlin.collections/Iterator<#A> // androidx.collection/valueIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/keyIterator(): kotlin.collections/IntIterator // androidx.collection/keyIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/plus(androidx.collection/SparseArrayCompat<#A>): androidx.collection/SparseArrayCompat<#A> // androidx.collection/plus|[email protected]<0:0>(androidx.collection.SparseArrayCompat<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/remove(kotlin/Int, #A): kotlin/Boolean // androidx.collection/remove|[email protected]<0:0>(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/valueIterator(): kotlin.collections/Iterator<#A> // androidx.collection/valueIterator|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/arraySetOf(kotlin/Array<out #A>...): androidx.collection/ArraySet<#A> // androidx.collection/arraySetOf|arraySetOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyFloatObjectMap(): androidx.collection/FloatObjectMap<#A> // androidx.collection/emptyFloatObjectMap|emptyFloatObjectMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyIntObjectMap(): androidx.collection/IntObjectMap<#A> // androidx.collection/emptyIntObjectMap|emptyIntObjectMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyLongObjectMap(): androidx.collection/LongObjectMap<#A> // androidx.collection/emptyLongObjectMap|emptyLongObjectMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectFloatMap(): androidx.collection/ObjectFloatMap<#A> // androidx.collection/emptyObjectFloatMap|emptyObjectFloatMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectIntMap(): androidx.collection/ObjectIntMap<#A> // androidx.collection/emptyObjectIntMap|emptyObjectIntMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectList(): androidx.collection/ObjectList<#A> // androidx.collection/emptyObjectList|emptyObjectList(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyObjectLongMap(): androidx.collection/ObjectLongMap<#A> // androidx.collection/emptyObjectLongMap|emptyObjectLongMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/emptyScatterSet(): androidx.collection/ScatterSet<#A> // androidx.collection/emptyScatterSet|emptyScatterSet(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/floatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/FloatObjectMap<#A> // androidx.collection/floatObjectMapOf|floatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/intObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/IntObjectMap<#A> // androidx.collection/intObjectMapOf|intObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/longObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/LongObjectMap<#A> // androidx.collection/longObjectMapOf|longObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableFloatObjectMapOf(kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A): androidx.collection/MutableFloatObjectMap<#A> // androidx.collection/mutableFloatObjectMapOf|mutableFloatObjectMapOf(kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableIntObjectMapOf(kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A): androidx.collection/MutableIntObjectMap<#A> // androidx.collection/mutableIntObjectMapOf|mutableIntObjectMapOf(kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableLongObjectMapOf(kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A): androidx.collection/MutableLongObjectMap<#A> // androidx.collection/mutableLongObjectMapOf|mutableLongObjectMapOf(kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectFloatMapOf(): androidx.collection/MutableObjectFloatMap<#A> // androidx.collection/mutableObjectFloatMapOf|mutableObjectFloatMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectIntMapOf(): androidx.collection/MutableObjectIntMap<#A> // androidx.collection/mutableObjectIntMapOf|mutableObjectIntMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(#A): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(#A, #A): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(#A, #A, #A): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableObjectLongMapOf(): androidx.collection/MutableObjectLongMap<#A> // androidx.collection/mutableObjectLongMapOf|mutableObjectLongMapOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(#A): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(#A, #A): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(#A, #A, #A): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/mutableScatterSetOf(kotlin/Array<out #A>...): androidx.collection/MutableScatterSet<#A> // androidx.collection/mutableScatterSetOf|mutableScatterSetOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMap(): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMap|objectFloatMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectFloatMapOf(#A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float, #A, kotlin/Float): androidx.collection/ObjectFloatMap<#A> // androidx.collection/objectFloatMapOf|objectFloatMapOf(0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float;0:0;kotlin.Float){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMap(): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMap|objectIntMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectIntMapOf(#A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int, #A, kotlin/Int): androidx.collection/ObjectIntMap<#A> // androidx.collection/objectIntMapOf|objectIntMapOf(0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int;0:0;kotlin.Int){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(#A): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(#A, #A): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(#A, #A, #A): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectListOf(kotlin/Array<out #A>...): androidx.collection/ObjectList<#A> // androidx.collection/objectListOf|objectListOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMap(): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMap|objectLongMap(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/objectLongMapOf(#A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long, #A, kotlin/Long): androidx.collection/ObjectLongMap<#A> // androidx.collection/objectLongMapOf|objectLongMapOf(0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long;0:0;kotlin.Long){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(#A): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(#A, #A): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(#A, #A, #A): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(0:0;0:0;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.collection/scatterSetOf(kotlin/Array<out #A>...): androidx.collection/ScatterSet<#A> // androidx.collection/scatterSetOf|scatterSetOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final fun androidx.collection/doubleListOf(): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(){}[0]
+final fun androidx.collection/doubleListOf(kotlin/Double): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.Double){}[0]
+final fun androidx.collection/doubleListOf(kotlin/Double, kotlin/Double): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/doubleListOf(kotlin/Double, kotlin/Double, kotlin/Double): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.Double;kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/doubleListOf(kotlin/DoubleArray...): androidx.collection/DoubleList // androidx.collection/doubleListOf|doubleListOf(kotlin.DoubleArray...){}[0]
+final fun androidx.collection/emptyDoubleList(): androidx.collection/DoubleList // androidx.collection/emptyDoubleList|emptyDoubleList(){}[0]
+final fun androidx.collection/emptyFloatFloatMap(): androidx.collection/FloatFloatMap // androidx.collection/emptyFloatFloatMap|emptyFloatFloatMap(){}[0]
+final fun androidx.collection/emptyFloatIntMap(): androidx.collection/FloatIntMap // androidx.collection/emptyFloatIntMap|emptyFloatIntMap(){}[0]
+final fun androidx.collection/emptyFloatList(): androidx.collection/FloatList // androidx.collection/emptyFloatList|emptyFloatList(){}[0]
+final fun androidx.collection/emptyFloatLongMap(): androidx.collection/FloatLongMap // androidx.collection/emptyFloatLongMap|emptyFloatLongMap(){}[0]
+final fun androidx.collection/emptyFloatSet(): androidx.collection/FloatSet // androidx.collection/emptyFloatSet|emptyFloatSet(){}[0]
+final fun androidx.collection/emptyIntFloatMap(): androidx.collection/IntFloatMap // androidx.collection/emptyIntFloatMap|emptyIntFloatMap(){}[0]
+final fun androidx.collection/emptyIntIntMap(): androidx.collection/IntIntMap // androidx.collection/emptyIntIntMap|emptyIntIntMap(){}[0]
+final fun androidx.collection/emptyIntList(): androidx.collection/IntList // androidx.collection/emptyIntList|emptyIntList(){}[0]
+final fun androidx.collection/emptyIntLongMap(): androidx.collection/IntLongMap // androidx.collection/emptyIntLongMap|emptyIntLongMap(){}[0]
+final fun androidx.collection/emptyIntSet(): androidx.collection/IntSet // androidx.collection/emptyIntSet|emptyIntSet(){}[0]
+final fun androidx.collection/emptyLongFloatMap(): androidx.collection/LongFloatMap // androidx.collection/emptyLongFloatMap|emptyLongFloatMap(){}[0]
+final fun androidx.collection/emptyLongIntMap(): androidx.collection/LongIntMap // androidx.collection/emptyLongIntMap|emptyLongIntMap(){}[0]
+final fun androidx.collection/emptyLongList(): androidx.collection/LongList // androidx.collection/emptyLongList|emptyLongList(){}[0]
+final fun androidx.collection/emptyLongLongMap(): androidx.collection/LongLongMap // androidx.collection/emptyLongLongMap|emptyLongLongMap(){}[0]
+final fun androidx.collection/emptyLongSet(): androidx.collection/LongSet // androidx.collection/emptyLongSet|emptyLongSet(){}[0]
+final fun androidx.collection/floatFloatMapOf(): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatFloatMap // androidx.collection/floatFloatMapOf|floatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatIntMapOf(): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/FloatIntMap // androidx.collection/floatIntMapOf|floatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/floatListOf(): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(){}[0]
+final fun androidx.collection/floatListOf(kotlin/Float): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.Float){}[0]
+final fun androidx.collection/floatListOf(kotlin/Float, kotlin/Float): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatListOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatListOf(kotlin/FloatArray...): androidx.collection/FloatList // androidx.collection/floatListOf|floatListOf(kotlin.FloatArray...){}[0]
+final fun androidx.collection/floatLongMapOf(): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/FloatLongMap // androidx.collection/floatLongMapOf|floatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/floatSetOf(): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(){}[0]
+final fun androidx.collection/floatSetOf(kotlin/Float): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.Float){}[0]
+final fun androidx.collection/floatSetOf(kotlin/Float, kotlin/Float): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatSetOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/floatSetOf(kotlin/FloatArray...): androidx.collection/FloatSet // androidx.collection/floatSetOf|floatSetOf(kotlin.FloatArray...){}[0]
+final fun androidx.collection/intFloatMapOf(): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/IntFloatMap // androidx.collection/intFloatMapOf|intFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/intIntMapOf(): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntIntMap // androidx.collection/intIntMapOf|intIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intListOf(): androidx.collection/IntList // androidx.collection/intListOf|intListOf(){}[0]
+final fun androidx.collection/intListOf(kotlin/Int): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.Int){}[0]
+final fun androidx.collection/intListOf(kotlin/Int, kotlin/Int): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intListOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intListOf(kotlin/IntArray...): androidx.collection/IntList // androidx.collection/intListOf|intListOf(kotlin.IntArray...){}[0]
+final fun androidx.collection/intLongMapOf(): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/IntLongMap // androidx.collection/intLongMapOf|intLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/intSetOf(): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(){}[0]
+final fun androidx.collection/intSetOf(kotlin/Int): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.Int){}[0]
+final fun androidx.collection/intSetOf(kotlin/Int, kotlin/Int): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intSetOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/intSetOf(kotlin/IntArray...): androidx.collection/IntSet // androidx.collection/intSetOf|intSetOf(kotlin.IntArray...){}[0]
+final fun androidx.collection/longFloatMapOf(): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/LongFloatMap // androidx.collection/longFloatMapOf|longFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/longIntMapOf(): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/LongIntMap // androidx.collection/longIntMapOf|longIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/longListOf(): androidx.collection/LongList // androidx.collection/longListOf|longListOf(){}[0]
+final fun androidx.collection/longListOf(kotlin/Long): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.Long){}[0]
+final fun androidx.collection/longListOf(kotlin/Long, kotlin/Long): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longListOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longListOf(kotlin/LongArray...): androidx.collection/LongList // androidx.collection/longListOf|longListOf(kotlin.LongArray...){}[0]
+final fun androidx.collection/longLongMapOf(): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongLongMap // androidx.collection/longLongMapOf|longLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(){}[0]
+final fun androidx.collection/longSetOf(kotlin/Long): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(kotlin/Long, kotlin/Long): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/longSetOf(kotlin/LongArray...): androidx.collection/LongSet // androidx.collection/longSetOf|longSetOf(kotlin.LongArray...){}[0]
+final fun androidx.collection/mutableDoubleListOf(kotlin/Double): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.Double){}[0]
+final fun androidx.collection/mutableDoubleListOf(kotlin/Double, kotlin/Double): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/mutableDoubleListOf(kotlin/Double, kotlin/Double, kotlin/Double): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.Double;kotlin.Double;kotlin.Double){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatFloatMapOf(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatFloatMap // androidx.collection/mutableFloatFloatMapOf|mutableFloatFloatMapOf(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatIntMapOf(kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int): androidx.collection/MutableFloatIntMap // androidx.collection/mutableFloatIntMapOf|mutableFloatIntMapOf(kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int){}[0]
+final fun androidx.collection/mutableFloatListOf(kotlin/Float): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatListOf(kotlin/Float, kotlin/Float): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatListOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatLongMapOf(kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long): androidx.collection/MutableFloatLongMap // androidx.collection/mutableFloatLongMapOf|mutableFloatLongMapOf(kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long){}[0]
+final fun androidx.collection/mutableFloatSetOf(): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/Float): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/Float, kotlin/Float): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/Float, kotlin/Float, kotlin/Float): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.collection/mutableFloatSetOf(kotlin/FloatArray...): androidx.collection/MutableFloatSet // androidx.collection/mutableFloatSetOf|mutableFloatSetOf(kotlin.FloatArray...){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntFloatMapOf(kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float, kotlin/Int, kotlin/Float): androidx.collection/MutableIntFloatMap // androidx.collection/mutableIntFloatMapOf|mutableIntFloatMapOf(kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float;kotlin.Int;kotlin.Float){}[0]
+final fun androidx.collection/mutableIntIntMapOf(): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntIntMapOf(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntIntMap // androidx.collection/mutableIntIntMapOf|mutableIntIntMapOf(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntListOf(kotlin/Int): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.Int){}[0]
+final fun androidx.collection/mutableIntListOf(kotlin/Int, kotlin/Int): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntListOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntLongMapOf(): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntLongMapOf(kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long): androidx.collection/MutableIntLongMap // androidx.collection/mutableIntLongMapOf|mutableIntLongMapOf(kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long){}[0]
+final fun androidx.collection/mutableIntSetOf(): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/Int): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.Int){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/Int, kotlin/Int): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/Int, kotlin/Int, kotlin/Int): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.Int;kotlin.Int;kotlin.Int){}[0]
+final fun androidx.collection/mutableIntSetOf(kotlin/IntArray...): androidx.collection/MutableIntSet // androidx.collection/mutableIntSetOf|mutableIntSetOf(kotlin.IntArray...){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongFloatMapOf(kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float, kotlin/Long, kotlin/Float): androidx.collection/MutableLongFloatMap // androidx.collection/mutableLongFloatMapOf|mutableLongFloatMapOf(kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float;kotlin.Long;kotlin.Float){}[0]
+final fun androidx.collection/mutableLongIntMapOf(): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongIntMapOf(kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int, kotlin/Long, kotlin/Int): androidx.collection/MutableLongIntMap // androidx.collection/mutableLongIntMapOf|mutableLongIntMapOf(kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int;kotlin.Long;kotlin.Int){}[0]
+final fun androidx.collection/mutableLongListOf(kotlin/Long): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.Long){}[0]
+final fun androidx.collection/mutableLongListOf(kotlin/Long, kotlin/Long): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongListOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongLongMapOf(kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongLongMap // androidx.collection/mutableLongLongMapOf|mutableLongLongMapOf(kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/Long): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/Long, kotlin/Long): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/Long, kotlin/Long, kotlin/Long): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.Long;kotlin.Long;kotlin.Long){}[0]
+final fun androidx.collection/mutableLongSetOf(kotlin/LongArray...): androidx.collection/MutableLongSet // androidx.collection/mutableLongSetOf|mutableLongSetOf(kotlin.LongArray...){}[0]
+final inline fun (kotlin/Long).androidx.collection/lowestBitSet(): kotlin/Int // androidx.collection/lowestBitSet|[email protected](){}[0]
+final inline fun (kotlin/Long).androidx.collection/maskEmptyOrDeleted(): kotlin/Long // androidx.collection/maskEmptyOrDeleted|[email protected](){}[0]
+final inline fun (kotlin/Long).androidx.collection/match(kotlin/Int): kotlin/Long // androidx.collection/match|[email protected](kotlin.Int){}[0]
+final inline fun <#A: kotlin/Any, #B: kotlin/Any> androidx.collection/lruCache(kotlin/Int, crossinline kotlin/Function2<#A, #B, kotlin/Int> =..., crossinline kotlin/Function1<#A, #B?> =..., crossinline kotlin/Function4<kotlin/Boolean, #A, #B, #B?, kotlin/Unit> =...): androidx.collection/LruCache<#A, #B> // androidx.collection/lruCache|lruCache(kotlin.Int;kotlin.Function2<0:0,0:1,kotlin.Int>;kotlin.Function1<0:0,0:1?>;kotlin.Function4<kotlin.Boolean,0:0,0:1,0:1?,kotlin.Unit>){0§<kotlin.Any>;1§<kotlin.Any>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/contains(kotlin/Long): kotlin/Boolean // androidx.collection/contains|[email protected]<0:0>(kotlin.Long){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/forEach(kotlin/Function2<kotlin/Long, #A, kotlin/Unit>) // androidx.collection/forEach|[email protected]<0:0>(kotlin.Function2<kotlin.Long,0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/getOrDefault(kotlin/Long, #A): #A // androidx.collection/getOrDefault|[email protected]<0:0>(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/getOrElse(kotlin/Long, kotlin/Function0<#A>): #A // androidx.collection/getOrElse|[email protected]<0:0>(kotlin.Long;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/isNotEmpty(): kotlin/Boolean // androidx.collection/isNotEmpty|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/LongSparseArray<#A>).androidx.collection/set(kotlin/Long, #A) // androidx.collection/set|[email protected]<0:0>(kotlin.Long;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/contains(kotlin/Int): kotlin/Boolean // androidx.collection/contains|[email protected]<0:0>(kotlin.Int){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/forEach(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/forEach|[email protected]<0:0>(kotlin.Function2<kotlin.Int,0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/getOrDefault(kotlin/Int, #A): #A // androidx.collection/getOrDefault|[email protected]<0:0>(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/getOrElse(kotlin/Int, kotlin/Function0<#A>): #A // androidx.collection/getOrElse|[email protected]<0:0>(kotlin.Int;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/isNotEmpty(): kotlin/Boolean // androidx.collection/isNotEmpty|[email protected]<0:0>(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A>).androidx.collection/set(kotlin/Int, #A) // androidx.collection/set|[email protected]<0:0>(kotlin.Int;0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> androidx.collection/arraySetOf(): androidx.collection/ArraySet<#A> // androidx.collection/arraySetOf|arraySetOf(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> androidx.collection/mutableObjectListOf(kotlin/Array<out #A>...): androidx.collection/MutableObjectList<#A> // androidx.collection/mutableObjectListOf|mutableObjectListOf(kotlin.Array<out|0:0>...){0§<kotlin.Any?>}[0]
+final inline fun androidx.collection.internal/floatFromBits(kotlin/Int): kotlin/Float // androidx.collection.internal/floatFromBits|floatFromBits(kotlin.Int){}[0]
+final inline fun androidx.collection/isFull(kotlin/Long): kotlin/Boolean // androidx.collection/isFull|isFull(kotlin.Long){}[0]
+final inline fun androidx.collection/mutableDoubleListOf(): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(){}[0]
+final inline fun androidx.collection/mutableDoubleListOf(kotlin/DoubleArray...): androidx.collection/MutableDoubleList // androidx.collection/mutableDoubleListOf|mutableDoubleListOf(kotlin.DoubleArray...){}[0]
+final inline fun androidx.collection/mutableFloatListOf(): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(){}[0]
+final inline fun androidx.collection/mutableFloatListOf(kotlin/FloatArray...): androidx.collection/MutableFloatList // androidx.collection/mutableFloatListOf|mutableFloatListOf(kotlin.FloatArray...){}[0]
+final inline fun androidx.collection/mutableIntListOf(): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(){}[0]
+final inline fun androidx.collection/mutableIntListOf(kotlin/IntArray...): androidx.collection/MutableIntList // androidx.collection/mutableIntListOf|mutableIntListOf(kotlin.IntArray...){}[0]
+final inline fun androidx.collection/mutableLongListOf(): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(){}[0]
+final inline fun androidx.collection/mutableLongListOf(kotlin/LongArray...): androidx.collection/MutableLongList // androidx.collection/mutableLongListOf|mutableLongListOf(kotlin.LongArray...){}[0]
+final inline fun androidx.collection/readRawMetadata(kotlin/LongArray, kotlin/Int): kotlin/Long // androidx.collection/readRawMetadata|readRawMetadata(kotlin.LongArray;kotlin.Int){}[0]
+final val androidx.collection/size // androidx.collection/size|@androidx.collection.LongSparseArray<0:0>{0§<kotlin.Any?>}size[0]
+    final inline fun <#A1: kotlin/Any?> (androidx.collection/LongSparseArray<#A1>).<get-size>(): kotlin/Int // androidx.collection/size.<get-size>|<get-size>@androidx.collection.LongSparseArray<0:0>(){0§<kotlin.Any?>}[0]
+final val androidx.collection/size // androidx.collection/size|@androidx.collection.SparseArrayCompat<0:0>{0§<kotlin.Any?>}size[0]
+    final inline fun <#A1: kotlin/Any?> (androidx.collection/SparseArrayCompat<#A1>).<get-size>(): kotlin/Int // androidx.collection/size.<get-size>|<get-size>@androidx.collection.SparseArrayCompat<0:0>(){0§<kotlin.Any?>}[0]
+final value class androidx.collection/FloatFloatPair { // androidx.collection/FloatFloatPair|null[0]
+    constructor <init>(kotlin/Float, kotlin/Float) // androidx.collection/FloatFloatPair.<init>|<init>(kotlin.Float;kotlin.Float){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatFloatPair.equals|equals(kotlin.Any?){}[0]
+    final fun hashCode(): kotlin/Int // androidx.collection/FloatFloatPair.hashCode|hashCode(){}[0]
+    final fun toString(): kotlin/String // androidx.collection/FloatFloatPair.toString|toString(){}[0]
+    final inline fun component1(): kotlin/Float // androidx.collection/FloatFloatPair.component1|component1(){}[0]
+    final inline fun component2(): kotlin/Float // androidx.collection/FloatFloatPair.component2|component2(){}[0]
+    final val first // androidx.collection/FloatFloatPair.first|{}first[0]
+        final inline fun <get-first>(): kotlin/Float // androidx.collection/FloatFloatPair.first.<get-first>|<get-first>(){}[0]
+    final val packedValue // androidx.collection/FloatFloatPair.packedValue|{}packedValue[0]
+        final fun <get-packedValue>(): kotlin/Long // androidx.collection/FloatFloatPair.packedValue.<get-packedValue>|<get-packedValue>(){}[0]
+    final val second // androidx.collection/FloatFloatPair.second|{}second[0]
+        final inline fun <get-second>(): kotlin/Float // androidx.collection/FloatFloatPair.second.<get-second>|<get-second>(){}[0]
+}
+final value class androidx.collection/IntIntPair { // androidx.collection/IntIntPair|null[0]
+    constructor <init>(kotlin/Int, kotlin/Int) // androidx.collection/IntIntPair.<init>|<init>(kotlin.Int;kotlin.Int){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntIntPair.equals|equals(kotlin.Any?){}[0]
+    final fun hashCode(): kotlin/Int // androidx.collection/IntIntPair.hashCode|hashCode(){}[0]
+    final fun toString(): kotlin/String // androidx.collection/IntIntPair.toString|toString(){}[0]
+    final inline fun component1(): kotlin/Int // androidx.collection/IntIntPair.component1|component1(){}[0]
+    final inline fun component2(): kotlin/Int // androidx.collection/IntIntPair.component2|component2(){}[0]
+    final val first // androidx.collection/IntIntPair.first|{}first[0]
+        final fun <get-first>(): kotlin/Int // androidx.collection/IntIntPair.first.<get-first>|<get-first>(){}[0]
+    final val packedValue // androidx.collection/IntIntPair.packedValue|{}packedValue[0]
+        final fun <get-packedValue>(): kotlin/Long // androidx.collection/IntIntPair.packedValue.<get-packedValue>|<get-packedValue>(){}[0]
+    final val second // androidx.collection/IntIntPair.second|{}second[0]
+        final fun <get-second>(): kotlin/Int // androidx.collection/IntIntPair.second.<get-second>|<get-second>(){}[0]
+}
+open class <#A: kotlin/Any, #B: kotlin/Any> androidx.collection/LruCache { // androidx.collection/LruCache|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/LruCache.<init>|<init>(kotlin.Int){}[0]
+    final fun createCount(): kotlin/Int // androidx.collection/LruCache.createCount|createCount(){}[0]
+    final fun evictAll() // androidx.collection/LruCache.evictAll|evictAll(){}[0]
+    final fun evictionCount(): kotlin/Int // androidx.collection/LruCache.evictionCount|evictionCount(){}[0]
+    final fun get(#A): #B? // androidx.collection/LruCache.get|get(1:0){}[0]
+    final fun hitCount(): kotlin/Int // androidx.collection/LruCache.hitCount|hitCount(){}[0]
+    final fun maxSize(): kotlin/Int // androidx.collection/LruCache.maxSize|maxSize(){}[0]
+    final fun missCount(): kotlin/Int // androidx.collection/LruCache.missCount|missCount(){}[0]
+    final fun put(#A, #B): #B? // androidx.collection/LruCache.put|put(1:0;1:1){}[0]
+    final fun putCount(): kotlin/Int // androidx.collection/LruCache.putCount|putCount(){}[0]
+    final fun remove(#A): #B? // androidx.collection/LruCache.remove|remove(1:0){}[0]
+    final fun size(): kotlin/Int // androidx.collection/LruCache.size|size(){}[0]
+    final fun snapshot(): kotlin.collections/MutableMap<#A, #B> // androidx.collection/LruCache.snapshot|snapshot(){}[0]
+    open fun create(#A): #B? // androidx.collection/LruCache.create|create(1:0){}[0]
+    open fun entryRemoved(kotlin/Boolean, #A, #B, #B?) // androidx.collection/LruCache.entryRemoved|entryRemoved(kotlin.Boolean;1:0;1:1;1:1?){}[0]
+    open fun resize(kotlin/Int) // androidx.collection/LruCache.resize|resize(kotlin.Int){}[0]
+    open fun sizeOf(#A, #B): kotlin/Int // androidx.collection/LruCache.sizeOf|sizeOf(1:0;1:1){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LruCache.toString|toString(){}[0]
+    open fun trimToSize(kotlin/Int) // androidx.collection/LruCache.trimToSize|trimToSize(kotlin.Int){}[0]
+}
+open class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/SimpleArrayMap { // androidx.collection/SimpleArrayMap|null[0]
+    constructor <init>(androidx.collection/SimpleArrayMap<out #A, out #B>?) // androidx.collection/SimpleArrayMap.<init>|<init>(androidx.collection.SimpleArrayMap<out|1:0,out|1:1>?){}[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/SimpleArrayMap.<init>|<init>(kotlin.Int){}[0]
+    open fun clear() // androidx.collection/SimpleArrayMap.clear|clear(){}[0]
+    open fun containsKey(#A): kotlin/Boolean // androidx.collection/SimpleArrayMap.containsKey|containsKey(1:0){}[0]
+    open fun containsValue(#B): kotlin/Boolean // androidx.collection/SimpleArrayMap.containsValue|containsValue(1:1){}[0]
+    open fun ensureCapacity(kotlin/Int) // androidx.collection/SimpleArrayMap.ensureCapacity|ensureCapacity(kotlin.Int){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/SimpleArrayMap.equals|equals(kotlin.Any?){}[0]
+    open fun get(#A): #B? // androidx.collection/SimpleArrayMap.get|get(1:0){}[0]
+    open fun getOrDefault(kotlin/Any?, #B): #B // androidx.collection/SimpleArrayMap.getOrDefault|getOrDefault(kotlin.Any?;1:1){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/SimpleArrayMap.hashCode|hashCode(){}[0]
+    open fun indexOfKey(#A): kotlin/Int // androidx.collection/SimpleArrayMap.indexOfKey|indexOfKey(1:0){}[0]
+    open fun isEmpty(): kotlin/Boolean // androidx.collection/SimpleArrayMap.isEmpty|isEmpty(){}[0]
+    open fun keyAt(kotlin/Int): #A // androidx.collection/SimpleArrayMap.keyAt|keyAt(kotlin.Int){}[0]
+    open fun put(#A, #B): #B? // androidx.collection/SimpleArrayMap.put|put(1:0;1:1){}[0]
+    open fun putAll(androidx.collection/SimpleArrayMap<out #A, out #B>) // androidx.collection/SimpleArrayMap.putAll|putAll(androidx.collection.SimpleArrayMap<out|1:0,out|1:1>){}[0]
+    open fun putIfAbsent(#A, #B): #B? // androidx.collection/SimpleArrayMap.putIfAbsent|putIfAbsent(1:0;1:1){}[0]
+    open fun remove(#A): #B? // androidx.collection/SimpleArrayMap.remove|remove(1:0){}[0]
+    open fun remove(#A, #B): kotlin/Boolean // androidx.collection/SimpleArrayMap.remove|remove(1:0;1:1){}[0]
+    open fun removeAt(kotlin/Int): #B // androidx.collection/SimpleArrayMap.removeAt|removeAt(kotlin.Int){}[0]
+    open fun replace(#A, #B): #B? // androidx.collection/SimpleArrayMap.replace|replace(1:0;1:1){}[0]
+    open fun replace(#A, #B, #B): kotlin/Boolean // androidx.collection/SimpleArrayMap.replace|replace(1:0;1:1;1:1){}[0]
+    open fun setValueAt(kotlin/Int, #B): #B // androidx.collection/SimpleArrayMap.setValueAt|setValueAt(kotlin.Int;1:1){}[0]
+    open fun size(): kotlin/Int // androidx.collection/SimpleArrayMap.size|size(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/SimpleArrayMap.toString|toString(){}[0]
+    open fun valueAt(kotlin/Int): #B // androidx.collection/SimpleArrayMap.valueAt|valueAt(kotlin.Int){}[0]
+}
+open class <#A: kotlin/Any?> androidx.collection/LongSparseArray { // androidx.collection/LongSparseArray|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/LongSparseArray.<init>|<init>(kotlin.Int){}[0]
+    open fun append(kotlin/Long, #A) // androidx.collection/LongSparseArray.append|append(kotlin.Long;1:0){}[0]
+    open fun clear() // androidx.collection/LongSparseArray.clear|clear(){}[0]
+    open fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongSparseArray.containsKey|containsKey(kotlin.Long){}[0]
+    open fun containsValue(#A): kotlin/Boolean // androidx.collection/LongSparseArray.containsValue|containsValue(1:0){}[0]
+    open fun delete(kotlin/Long) // androidx.collection/LongSparseArray.delete|delete(kotlin.Long){}[0]
+    open fun get(kotlin/Long): #A? // androidx.collection/LongSparseArray.get|get(kotlin.Long){}[0]
+    open fun get(kotlin/Long, #A): #A // androidx.collection/LongSparseArray.get|get(kotlin.Long;1:0){}[0]
+    open fun indexOfKey(kotlin/Long): kotlin/Int // androidx.collection/LongSparseArray.indexOfKey|indexOfKey(kotlin.Long){}[0]
+    open fun indexOfValue(#A): kotlin/Int // androidx.collection/LongSparseArray.indexOfValue|indexOfValue(1:0){}[0]
+    open fun isEmpty(): kotlin/Boolean // androidx.collection/LongSparseArray.isEmpty|isEmpty(){}[0]
+    open fun keyAt(kotlin/Int): kotlin/Long // androidx.collection/LongSparseArray.keyAt|keyAt(kotlin.Int){}[0]
+    open fun put(kotlin/Long, #A) // androidx.collection/LongSparseArray.put|put(kotlin.Long;1:0){}[0]
+    open fun putAll(androidx.collection/LongSparseArray<out #A>) // androidx.collection/LongSparseArray.putAll|putAll(androidx.collection.LongSparseArray<out|1:0>){}[0]
+    open fun putIfAbsent(kotlin/Long, #A): #A? // androidx.collection/LongSparseArray.putIfAbsent|putIfAbsent(kotlin.Long;1:0){}[0]
+    open fun remove(kotlin/Long) // androidx.collection/LongSparseArray.remove|remove(kotlin.Long){}[0]
+    open fun remove(kotlin/Long, #A): kotlin/Boolean // androidx.collection/LongSparseArray.remove|remove(kotlin.Long;1:0){}[0]
+    open fun removeAt(kotlin/Int) // androidx.collection/LongSparseArray.removeAt|removeAt(kotlin.Int){}[0]
+    open fun replace(kotlin/Long, #A): #A? // androidx.collection/LongSparseArray.replace|replace(kotlin.Long;1:0){}[0]
+    open fun replace(kotlin/Long, #A, #A): kotlin/Boolean // androidx.collection/LongSparseArray.replace|replace(kotlin.Long;1:0;1:0){}[0]
+    open fun setValueAt(kotlin/Int, #A) // androidx.collection/LongSparseArray.setValueAt|setValueAt(kotlin.Int;1:0){}[0]
+    open fun size(): kotlin/Int // androidx.collection/LongSparseArray.size|size(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LongSparseArray.toString|toString(){}[0]
+    open fun valueAt(kotlin/Int): #A // androidx.collection/LongSparseArray.valueAt|valueAt(kotlin.Int){}[0]
+}
+open class <#A: kotlin/Any?> androidx.collection/SparseArrayCompat { // androidx.collection/SparseArrayCompat|null[0]
+    constructor <init>(kotlin/Int =...) // androidx.collection/SparseArrayCompat.<init>|<init>(kotlin.Int){}[0]
+    open fun append(kotlin/Int, #A) // androidx.collection/SparseArrayCompat.append|append(kotlin.Int;1:0){}[0]
+    open fun clear() // androidx.collection/SparseArrayCompat.clear|clear(){}[0]
+    open fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/SparseArrayCompat.containsKey|containsKey(kotlin.Int){}[0]
+    open fun containsValue(#A): kotlin/Boolean // androidx.collection/SparseArrayCompat.containsValue|containsValue(1:0){}[0]
+    open fun get(kotlin/Int): #A? // androidx.collection/SparseArrayCompat.get|get(kotlin.Int){}[0]
+    open fun get(kotlin/Int, #A): #A // androidx.collection/SparseArrayCompat.get|get(kotlin.Int;1:0){}[0]
+    open fun indexOfKey(kotlin/Int): kotlin/Int // androidx.collection/SparseArrayCompat.indexOfKey|indexOfKey(kotlin.Int){}[0]
+    open fun indexOfValue(#A): kotlin/Int // androidx.collection/SparseArrayCompat.indexOfValue|indexOfValue(1:0){}[0]
+    open fun isEmpty(): kotlin/Boolean // androidx.collection/SparseArrayCompat.isEmpty|isEmpty(){}[0]
+    open fun keyAt(kotlin/Int): kotlin/Int // androidx.collection/SparseArrayCompat.keyAt|keyAt(kotlin.Int){}[0]
+    open fun put(kotlin/Int, #A) // androidx.collection/SparseArrayCompat.put|put(kotlin.Int;1:0){}[0]
+    open fun putAll(androidx.collection/SparseArrayCompat<out #A>) // androidx.collection/SparseArrayCompat.putAll|putAll(androidx.collection.SparseArrayCompat<out|1:0>){}[0]
+    open fun putIfAbsent(kotlin/Int, #A): #A? // androidx.collection/SparseArrayCompat.putIfAbsent|putIfAbsent(kotlin.Int;1:0){}[0]
+    open fun remove(kotlin/Int) // androidx.collection/SparseArrayCompat.remove|remove(kotlin.Int){}[0]
+    open fun remove(kotlin/Int, kotlin/Any?): kotlin/Boolean // androidx.collection/SparseArrayCompat.remove|remove(kotlin.Int;kotlin.Any?){}[0]
+    open fun removeAt(kotlin/Int) // androidx.collection/SparseArrayCompat.removeAt|removeAt(kotlin.Int){}[0]
+    open fun removeAtRange(kotlin/Int, kotlin/Int) // androidx.collection/SparseArrayCompat.removeAtRange|removeAtRange(kotlin.Int;kotlin.Int){}[0]
+    open fun replace(kotlin/Int, #A): #A? // androidx.collection/SparseArrayCompat.replace|replace(kotlin.Int;1:0){}[0]
+    open fun replace(kotlin/Int, #A, #A): kotlin/Boolean // androidx.collection/SparseArrayCompat.replace|replace(kotlin.Int;1:0;1:0){}[0]
+    open fun setValueAt(kotlin/Int, #A) // androidx.collection/SparseArrayCompat.setValueAt|setValueAt(kotlin.Int;1:0){}[0]
+    open fun size(): kotlin/Int // androidx.collection/SparseArrayCompat.size|size(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/SparseArrayCompat.toString|toString(){}[0]
+    open fun valueAt(kotlin/Int): #A // androidx.collection/SparseArrayCompat.valueAt|valueAt(kotlin.Int){}[0]
+}
+sealed class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.collection/ScatterMap { // androidx.collection/ScatterMap|null[0]
+    constructor <init>() // androidx.collection/ScatterMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/ScatterMap.any|any(){}[0]
+    final fun asMap(): kotlin.collections/Map<#A, #B> // androidx.collection/ScatterMap.asMap|asMap(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/ScatterMap.contains|contains(1:0){}[0]
+    final fun containsKey(#A): kotlin/Boolean // androidx.collection/ScatterMap.containsKey|containsKey(1:0){}[0]
+    final fun containsValue(#B): kotlin/Boolean // androidx.collection/ScatterMap.containsValue|containsValue(1:1){}[0]
+    final fun count(): kotlin/Int // androidx.collection/ScatterMap.count|count(){}[0]
+    final fun get(#A): #B? // androidx.collection/ScatterMap.get|get(1:0){}[0]
+    final fun getOrDefault(#A, #B): #B // androidx.collection/ScatterMap.getOrDefault|getOrDefault(1:0;1:1){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/ScatterMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ScatterMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., kotlin/Function2<#A, #B, kotlin/CharSequence>? =...): kotlin/String // androidx.collection/ScatterMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,1:1,kotlin.CharSequence>?){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/ScatterMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterMap.all|all(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterMap.any|any(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<#A, #B, kotlin/Boolean>): kotlin/Int // androidx.collection/ScatterMap.count|count(kotlin.Function2<1:0,1:1,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<#A, #B, kotlin/Unit>) // androidx.collection/ScatterMap.forEach|forEach(kotlin.Function2<1:0,1:1,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ScatterMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ScatterMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<#B, kotlin/Unit>) // androidx.collection/ScatterMap.forEachValue|forEachValue(kotlin.Function1<1:1,kotlin.Unit>){}[0]
+    final inline fun getOrElse(#A, kotlin/Function0<#B>): #B // androidx.collection/ScatterMap.getOrElse|getOrElse(1:0;kotlin.Function0<1:1>){}[0]
+    final val capacity // androidx.collection/ScatterMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/ScatterMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/ScatterMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/ScatterMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ScatterMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ScatterMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+    final var metadata // androidx.collection/ScatterMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ScatterMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ScatterMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/ScatterMap.values|{}values[0]
+        final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/ScatterMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/ScatterMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ScatterMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/ScatterMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/ScatterMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/FloatObjectMap { // androidx.collection/FloatObjectMap|null[0]
+    constructor <init>() // androidx.collection/FloatObjectMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/FloatObjectMap.any|any(){}[0]
+    final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatObjectMap.contains|contains(kotlin.Float){}[0]
+    final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatObjectMap.containsKey|containsKey(kotlin.Float){}[0]
+    final fun containsValue(#A): kotlin/Boolean // androidx.collection/FloatObjectMap.containsValue|containsValue(1:0){}[0]
+    final fun count(): kotlin/Int // androidx.collection/FloatObjectMap.count|count(){}[0]
+    final fun get(kotlin/Float): #A? // androidx.collection/FloatObjectMap.get|get(kotlin.Float){}[0]
+    final fun getOrDefault(kotlin/Float, #A): #A // androidx.collection/FloatObjectMap.getOrDefault|getOrDefault(kotlin.Float;1:0){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatObjectMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatObjectMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/FloatObjectMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatObjectMap.all|all(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatObjectMap.any|any(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Float, #A, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatObjectMap.count|count(kotlin.Function2<kotlin.Float,1:0,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Float, #A, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEach|forEach(kotlin.Function2<kotlin.Float,1:0,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/FloatObjectMap.forEachValue|forEachValue(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Float, kotlin/Function0<#A>): #A // androidx.collection/FloatObjectMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<1:0>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, #A, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,1:0,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/FloatObjectMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/FloatObjectMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/FloatObjectMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/FloatObjectMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatObjectMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatObjectMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+    final var metadata // androidx.collection/FloatObjectMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatObjectMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatObjectMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/FloatObjectMap.values|{}values[0]
+        final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/FloatObjectMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/FloatObjectMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatObjectMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/FloatObjectMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/FloatObjectMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/IntObjectMap { // androidx.collection/IntObjectMap|null[0]
+    constructor <init>() // androidx.collection/IntObjectMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/IntObjectMap.any|any(){}[0]
+    final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntObjectMap.contains|contains(kotlin.Int){}[0]
+    final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntObjectMap.containsKey|containsKey(kotlin.Int){}[0]
+    final fun containsValue(#A): kotlin/Boolean // androidx.collection/IntObjectMap.containsValue|containsValue(1:0){}[0]
+    final fun count(): kotlin/Int // androidx.collection/IntObjectMap.count|count(){}[0]
+    final fun get(kotlin/Int): #A? // androidx.collection/IntObjectMap.get|get(kotlin.Int){}[0]
+    final fun getOrDefault(kotlin/Int, #A): #A // androidx.collection/IntObjectMap.getOrDefault|getOrDefault(kotlin.Int;1:0){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/IntObjectMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntObjectMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/IntObjectMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntObjectMap.all|all(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntObjectMap.any|any(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Int, #A, kotlin/Boolean>): kotlin/Int // androidx.collection/IntObjectMap.count|count(kotlin.Function2<kotlin.Int,1:0,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/IntObjectMap.forEach|forEach(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntObjectMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntObjectMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/IntObjectMap.forEachValue|forEachValue(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Int, kotlin/Function0<#A>): #A // androidx.collection/IntObjectMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<1:0>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, #A, kotlin/CharSequence>): kotlin/String // androidx.collection/IntObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,1:0,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/IntObjectMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/IntObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/IntObjectMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/IntObjectMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/IntObjectMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntObjectMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntObjectMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+    final var metadata // androidx.collection/IntObjectMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntObjectMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntObjectMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/IntObjectMap.values|{}values[0]
+        final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/IntObjectMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/IntObjectMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntObjectMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/IntObjectMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/IntObjectMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/LongObjectMap { // androidx.collection/LongObjectMap|null[0]
+    constructor <init>() // androidx.collection/LongObjectMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/LongObjectMap.any|any(){}[0]
+    final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongObjectMap.contains|contains(kotlin.Long){}[0]
+    final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongObjectMap.containsKey|containsKey(kotlin.Long){}[0]
+    final fun containsValue(#A): kotlin/Boolean // androidx.collection/LongObjectMap.containsValue|containsValue(1:0){}[0]
+    final fun count(): kotlin/Int // androidx.collection/LongObjectMap.count|count(){}[0]
+    final fun get(kotlin/Long): #A? // androidx.collection/LongObjectMap.get|get(kotlin.Long){}[0]
+    final fun getOrDefault(kotlin/Long, #A): #A // androidx.collection/LongObjectMap.getOrDefault|getOrDefault(kotlin.Long;1:0){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/LongObjectMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongObjectMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/LongObjectMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongObjectMap.all|all(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongObjectMap.any|any(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Long, #A, kotlin/Boolean>): kotlin/Int // androidx.collection/LongObjectMap.count|count(kotlin.Function2<kotlin.Long,1:0,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Long, #A, kotlin/Unit>) // androidx.collection/LongObjectMap.forEach|forEach(kotlin.Function2<kotlin.Long,1:0,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongObjectMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongObjectMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/LongObjectMap.forEachValue|forEachValue(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Long, kotlin/Function0<#A>): #A // androidx.collection/LongObjectMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<1:0>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, #A, kotlin/CharSequence>): kotlin/String // androidx.collection/LongObjectMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,1:0,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/LongObjectMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/LongObjectMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/LongObjectMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/LongObjectMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/LongObjectMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongObjectMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongObjectMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+    final var metadata // androidx.collection/LongObjectMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongObjectMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongObjectMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/LongObjectMap.values|{}values[0]
+        final fun <get-values>(): kotlin/Array<kotlin/Any?> // androidx.collection/LongObjectMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/Array<kotlin/Any?>) // androidx.collection/LongObjectMap.values.<set-values>|<set-values>(kotlin.Array<kotlin.Any?>){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongObjectMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/LongObjectMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LongObjectMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectFloatMap { // androidx.collection/ObjectFloatMap|null[0]
+    constructor <init>() // androidx.collection/ObjectFloatMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/ObjectFloatMap.any|any(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectFloatMap.contains|contains(1:0){}[0]
+    final fun containsKey(#A): kotlin/Boolean // androidx.collection/ObjectFloatMap.containsKey|containsKey(1:0){}[0]
+    final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/ObjectFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+    final fun count(): kotlin/Int // androidx.collection/ObjectFloatMap.count|count(){}[0]
+    final fun findKeyIndex(#A): kotlin/Int // androidx.collection/ObjectFloatMap.findKeyIndex|findKeyIndex(1:0){}[0]
+    final fun get(#A): kotlin/Float // androidx.collection/ObjectFloatMap.get|get(1:0){}[0]
+    final fun getOrDefault(#A, kotlin/Float): kotlin/Float // androidx.collection/ObjectFloatMap.getOrDefault|getOrDefault(1:0;kotlin.Float){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectFloatMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectFloatMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/ObjectFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/ObjectFloatMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectFloatMap.all|all(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectFloatMap.any|any(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<#A, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectFloatMap.count|count(kotlin.Function2<1:0,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<#A, kotlin/Float, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEach|forEach(kotlin.Function2<1:0,kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/ObjectFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun getOrElse(#A, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/ObjectFloatMap.getOrElse|getOrElse(1:0;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<#A, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/ObjectFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,kotlin.Float,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/ObjectFloatMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/ObjectFloatMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/ObjectFloatMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/ObjectFloatMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectFloatMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+    final var metadata // androidx.collection/ObjectFloatMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ObjectFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ObjectFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/ObjectFloatMap.values|{}values[0]
+        final fun <get-values>(): kotlin/FloatArray // androidx.collection/ObjectFloatMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/FloatArray) // androidx.collection/ObjectFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectFloatMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/ObjectFloatMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/ObjectFloatMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectIntMap { // androidx.collection/ObjectIntMap|null[0]
+    constructor <init>() // androidx.collection/ObjectIntMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/ObjectIntMap.any|any(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectIntMap.contains|contains(1:0){}[0]
+    final fun containsKey(#A): kotlin/Boolean // androidx.collection/ObjectIntMap.containsKey|containsKey(1:0){}[0]
+    final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/ObjectIntMap.containsValue|containsValue(kotlin.Int){}[0]
+    final fun count(): kotlin/Int // androidx.collection/ObjectIntMap.count|count(){}[0]
+    final fun findKeyIndex(#A): kotlin/Int // androidx.collection/ObjectIntMap.findKeyIndex|findKeyIndex(1:0){}[0]
+    final fun get(#A): kotlin/Int // androidx.collection/ObjectIntMap.get|get(1:0){}[0]
+    final fun getOrDefault(#A, kotlin/Int): kotlin/Int // androidx.collection/ObjectIntMap.getOrDefault|getOrDefault(1:0;kotlin.Int){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectIntMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectIntMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/ObjectIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/ObjectIntMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectIntMap.all|all(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectIntMap.any|any(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<#A, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectIntMap.count|count(kotlin.Function2<1:0,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<#A, kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEach|forEach(kotlin.Function2<1:0,kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun getOrElse(#A, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/ObjectIntMap.getOrElse|getOrElse(1:0;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<#A, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/ObjectIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,kotlin.Int,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/ObjectIntMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/ObjectIntMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/ObjectIntMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/ObjectIntMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectIntMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectIntMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+    final var metadata // androidx.collection/ObjectIntMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ObjectIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ObjectIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/ObjectIntMap.values|{}values[0]
+        final fun <get-values>(): kotlin/IntArray // androidx.collection/ObjectIntMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/IntArray) // androidx.collection/ObjectIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectIntMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/ObjectIntMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/ObjectIntMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectList { // androidx.collection/ObjectList|null[0]
+    abstract fun asList(): kotlin.collections/List<#A> // androidx.collection/ObjectList.asList|asList(){}[0]
+    constructor <init>(kotlin/Int) // androidx.collection/ObjectList.<init>|<init>(kotlin.Int){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/ObjectList.any|any(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectList.contains|contains(1:0){}[0]
+    final fun containsAll(androidx.collection/ObjectList<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(androidx.collection.ObjectList<1:0>){}[0]
+    final fun containsAll(kotlin.collections/Iterable<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(kotlin.collections.Iterable<1:0>){}[0]
+    final fun containsAll(kotlin.collections/List<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(kotlin.collections.List<1:0>){}[0]
+    final fun containsAll(kotlin/Array<#A>): kotlin/Boolean // androidx.collection/ObjectList.containsAll|containsAll(kotlin.Array<1:0>){}[0]
+    final fun count(): kotlin/Int // androidx.collection/ObjectList.count|count(){}[0]
+    final fun elementAt(kotlin/Int): #A // androidx.collection/ObjectList.elementAt|elementAt(kotlin.Int){}[0]
+    final fun first(): #A // androidx.collection/ObjectList.first|first(){}[0]
+    final fun get(kotlin/Int): #A // androidx.collection/ObjectList.get|get(kotlin.Int){}[0]
+    final fun indexOf(#A): kotlin/Int // androidx.collection/ObjectList.indexOf|indexOf(1:0){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectList.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectList.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., kotlin/Function1<#A, kotlin/CharSequence>? =...): kotlin/String // androidx.collection/ObjectList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<1:0,kotlin.CharSequence>?){}[0]
+    final fun last(): #A // androidx.collection/ObjectList.last|last(){}[0]
+    final fun lastIndexOf(#A): kotlin/Int // androidx.collection/ObjectList.lastIndexOf|lastIndexOf(1:0){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/ObjectList.none|none(){}[0]
+    final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, #A, #A1>): #A1 // androidx.collection/ObjectList.fold|fold(0:0;kotlin.Function2<0:0,1:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, #A, #A1>): #A1 // androidx.collection/ObjectList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,1:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<#A, #A1, #A1>): #A1 // androidx.collection/ObjectList.foldRight|foldRight(0:0;kotlin.Function2<1:0,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, #A, #A1, #A1>): #A1 // androidx.collection/ObjectList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,1:0,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun any(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectList.any|any(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectList.count|count(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, #A>): #A // androidx.collection/ObjectList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,1:0>){}[0]
+    final inline fun first(kotlin/Function1<#A, kotlin/Boolean>): #A // androidx.collection/ObjectList.first|first(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun firstOrNull(): #A? // androidx.collection/ObjectList.firstOrNull|firstOrNull(){}[0]
+    final inline fun firstOrNull(kotlin/Function1<#A, kotlin/Boolean>): #A? // androidx.collection/ObjectList.firstOrNull|firstOrNull(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectList.forEach|forEach(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/ObjectList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
+    final inline fun forEachReversed(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectList.forEachReversed|forEachReversed(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.collection/ObjectList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
+    final inline fun indexOfFirst(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectList.indexOfFirst|indexOfFirst(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun indexOfLast(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectList.indexOfLast|indexOfLast(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun last(kotlin/Function1<#A, kotlin/Boolean>): #A // androidx.collection/ObjectList.last|last(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun lastOrNull(): #A? // androidx.collection/ObjectList.lastOrNull|lastOrNull(){}[0]
+    final inline fun lastOrNull(kotlin/Function1<#A, kotlin/Boolean>): #A? // androidx.collection/ObjectList.lastOrNull|lastOrNull(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun reversedAny(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectList.reversedAny|reversedAny(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final val indices // androidx.collection/ObjectList.indices|{}indices[0]
+        final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/ObjectList.indices.<get-indices>|<get-indices>(){}[0]
+    final val lastIndex // androidx.collection/ObjectList.lastIndex|{}lastIndex[0]
+        final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/ObjectList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+    final val size // androidx.collection/ObjectList.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/ObjectList.size.<get-size>|<get-size>(){}[0]
+    final var _size // androidx.collection/ObjectList._size|{}_size[0]
+        final fun <get-_size>(): kotlin/Int // androidx.collection/ObjectList._size.<get-_size>|<get-_size>(){}[0]
+        final fun <set-_size>(kotlin/Int) // androidx.collection/ObjectList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+    final var content // androidx.collection/ObjectList.content|{}content[0]
+        final fun <get-content>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectList.content.<get-content>|<get-content>(){}[0]
+        final fun <set-content>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectList.content.<set-content>|<set-content>(kotlin.Array<kotlin.Any?>){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectList.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/ObjectList.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/ObjectList.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ObjectLongMap { // androidx.collection/ObjectLongMap|null[0]
+    constructor <init>() // androidx.collection/ObjectLongMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/ObjectLongMap.any|any(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/ObjectLongMap.contains|contains(1:0){}[0]
+    final fun containsKey(#A): kotlin/Boolean // androidx.collection/ObjectLongMap.containsKey|containsKey(1:0){}[0]
+    final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/ObjectLongMap.containsValue|containsValue(kotlin.Long){}[0]
+    final fun count(): kotlin/Int // androidx.collection/ObjectLongMap.count|count(){}[0]
+    final fun findKeyIndex(#A): kotlin/Int // androidx.collection/ObjectLongMap.findKeyIndex|findKeyIndex(1:0){}[0]
+    final fun get(#A): kotlin/Long // androidx.collection/ObjectLongMap.get|get(1:0){}[0]
+    final fun getOrDefault(#A, kotlin/Long): kotlin/Long // androidx.collection/ObjectLongMap.getOrDefault|getOrDefault(1:0;kotlin.Long){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/ObjectLongMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ObjectLongMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/ObjectLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/ObjectLongMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectLongMap.all|all(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ObjectLongMap.any|any(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<#A, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/ObjectLongMap.count|count(kotlin.Function2<1:0,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<#A, kotlin/Long, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEach|forEach(kotlin.Function2<1:0,kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEachKey|forEachKey(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/ObjectLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun getOrElse(#A, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/ObjectLongMap.getOrElse|getOrElse(1:0;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<#A, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/ObjectLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<1:0,kotlin.Long,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/ObjectLongMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/ObjectLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/ObjectLongMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/ObjectLongMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/ObjectLongMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/Array<kotlin/Any?> // androidx.collection/ObjectLongMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/Array<kotlin/Any?>) // androidx.collection/ObjectLongMap.keys.<set-keys>|<set-keys>(kotlin.Array<kotlin.Any?>){}[0]
+    final var metadata // androidx.collection/ObjectLongMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ObjectLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ObjectLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/ObjectLongMap.values|{}values[0]
+        final fun <get-values>(): kotlin/LongArray // androidx.collection/ObjectLongMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/LongArray) // androidx.collection/ObjectLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ObjectLongMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/ObjectLongMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/ObjectLongMap.toString|toString(){}[0]
+}
+sealed class <#A: kotlin/Any?> androidx.collection/ScatterSet { // androidx.collection/ScatterSet|null[0]
+    constructor <init>() // androidx.collection/ScatterSet.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/ScatterSet.any|any(){}[0]
+    final fun asSet(): kotlin.collections/Set<#A> // androidx.collection/ScatterSet.asSet|asSet(){}[0]
+    final fun contains(#A): kotlin/Boolean // androidx.collection/ScatterSet.contains|contains(1:0){}[0]
+    final fun count(): kotlin/Int // androidx.collection/ScatterSet.count|count(){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/ScatterSet.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/ScatterSet.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., kotlin/Function1<#A, kotlin/CharSequence>? =...): kotlin/String // androidx.collection/ScatterSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<1:0,kotlin.CharSequence>?){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/ScatterSet.none|none(){}[0]
+    final inline fun all(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterSet.all|all(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Boolean // androidx.collection/ScatterSet.any|any(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<#A, kotlin/Boolean>): kotlin/Int // androidx.collection/ScatterSet.count|count(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun first(): #A // androidx.collection/ScatterSet.first|first(){}[0]
+    final inline fun first(kotlin/Function1<#A, kotlin/Boolean>): #A // androidx.collection/ScatterSet.first|first(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun firstOrNull(kotlin/Function1<#A, kotlin/Boolean>): #A? // androidx.collection/ScatterSet.firstOrNull|firstOrNull(kotlin.Function1<1:0,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<#A, kotlin/Unit>) // androidx.collection/ScatterSet.forEach|forEach(kotlin.Function1<1:0,kotlin.Unit>){}[0]
+    final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/ScatterSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final val capacity // androidx.collection/ScatterSet.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/ScatterSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/ScatterSet.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/ScatterSet.size.<get-size>|<get-size>(){}[0]
+    final var elements // androidx.collection/ScatterSet.elements|{}elements[0]
+        final fun <get-elements>(): kotlin/Array<kotlin/Any?> // androidx.collection/ScatterSet.elements.<get-elements>|<get-elements>(){}[0]
+        final fun <set-elements>(kotlin/Array<kotlin/Any?>) // androidx.collection/ScatterSet.elements.<set-elements>|<set-elements>(kotlin.Array<kotlin.Any?>){}[0]
+    final var metadata // androidx.collection/ScatterSet.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/ScatterSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/ScatterSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/ScatterSet.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/ScatterSet.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/ScatterSet.toString|toString(){}[0]
+}
+sealed class androidx.collection/DoubleList { // androidx.collection/DoubleList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/DoubleList.<init>|<init>(kotlin.Int){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/DoubleList.any|any(){}[0]
+    final fun contains(kotlin/Double): kotlin/Boolean // androidx.collection/DoubleList.contains|contains(kotlin.Double){}[0]
+    final fun containsAll(androidx.collection/DoubleList): kotlin/Boolean // androidx.collection/DoubleList.containsAll|containsAll(androidx.collection.DoubleList){}[0]
+    final fun count(): kotlin/Int // androidx.collection/DoubleList.count|count(){}[0]
+    final fun elementAt(kotlin/Int): kotlin/Double // androidx.collection/DoubleList.elementAt|elementAt(kotlin.Int){}[0]
+    final fun first(): kotlin/Double // androidx.collection/DoubleList.first|first(){}[0]
+    final fun get(kotlin/Int): kotlin/Double // androidx.collection/DoubleList.get|get(kotlin.Int){}[0]
+    final fun indexOf(kotlin/Double): kotlin/Int // androidx.collection/DoubleList.indexOf|indexOf(kotlin.Double){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/DoubleList.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/DoubleList.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/DoubleList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun last(): kotlin/Double // androidx.collection/DoubleList.last|last(){}[0]
+    final fun lastIndexOf(kotlin/Double): kotlin/Int // androidx.collection/DoubleList.lastIndexOf|lastIndexOf(kotlin.Double){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/DoubleList.none|none(){}[0]
+    final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Double, #A1>): #A1 // androidx.collection/DoubleList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Double,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Double, #A1>): #A1 // androidx.collection/DoubleList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Double,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Double, #A1, #A1>): #A1 // androidx.collection/DoubleList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Double,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Double, #A1, #A1>): #A1 // androidx.collection/DoubleList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Double,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun any(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Boolean // androidx.collection/DoubleList.any|any(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Int // androidx.collection/DoubleList.count|count(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+    final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Double>): kotlin/Double // androidx.collection/DoubleList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Double>){}[0]
+    final inline fun first(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Double // androidx.collection/DoubleList.first|first(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEach|forEach(kotlin.Function1<kotlin.Double,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Double,kotlin.Unit>){}[0]
+    final inline fun forEachReversed(kotlin/Function1<kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Double,kotlin.Unit>){}[0]
+    final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Double, kotlin/Unit>) // androidx.collection/DoubleList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Double,kotlin.Unit>){}[0]
+    final inline fun indexOfFirst(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Int // androidx.collection/DoubleList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+    final inline fun indexOfLast(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Int // androidx.collection/DoubleList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Double, kotlin/CharSequence>): kotlin/String // androidx.collection/DoubleList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Double,kotlin.CharSequence>){}[0]
+    final inline fun last(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Double // androidx.collection/DoubleList.last|last(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+    final inline fun reversedAny(kotlin/Function1<kotlin/Double, kotlin/Boolean>): kotlin/Boolean // androidx.collection/DoubleList.reversedAny|reversedAny(kotlin.Function1<kotlin.Double,kotlin.Boolean>){}[0]
+    final val indices // androidx.collection/DoubleList.indices|{}indices[0]
+        final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/DoubleList.indices.<get-indices>|<get-indices>(){}[0]
+    final val lastIndex // androidx.collection/DoubleList.lastIndex|{}lastIndex[0]
+        final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/DoubleList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+    final val size // androidx.collection/DoubleList.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/DoubleList.size.<get-size>|<get-size>(){}[0]
+    final var _size // androidx.collection/DoubleList._size|{}_size[0]
+        final fun <get-_size>(): kotlin/Int // androidx.collection/DoubleList._size.<get-_size>|<get-_size>(){}[0]
+        final fun <set-_size>(kotlin/Int) // androidx.collection/DoubleList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+    final var content // androidx.collection/DoubleList.content|{}content[0]
+        final fun <get-content>(): kotlin/DoubleArray // androidx.collection/DoubleList.content.<get-content>|<get-content>(){}[0]
+        final fun <set-content>(kotlin/DoubleArray) // androidx.collection/DoubleList.content.<set-content>|<set-content>(kotlin.DoubleArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/DoubleList.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/DoubleList.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/DoubleList.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatFloatMap { // androidx.collection/FloatFloatMap|null[0]
+    constructor <init>() // androidx.collection/FloatFloatMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/FloatFloatMap.any|any(){}[0]
+    final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatFloatMap.contains|contains(kotlin.Float){}[0]
+    final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatFloatMap.containsKey|containsKey(kotlin.Float){}[0]
+    final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/FloatFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+    final fun count(): kotlin/Int // androidx.collection/FloatFloatMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Float): kotlin/Int // androidx.collection/FloatFloatMap.findKeyIndex|findKeyIndex(kotlin.Float){}[0]
+    final fun get(kotlin/Float): kotlin/Float // androidx.collection/FloatFloatMap.get|get(kotlin.Float){}[0]
+    final fun getOrDefault(kotlin/Float, kotlin/Float): kotlin/Float // androidx.collection/FloatFloatMap.getOrDefault|getOrDefault(kotlin.Float;kotlin.Float){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatFloatMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatFloatMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/FloatFloatMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatFloatMap.all|all(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatFloatMap.any|any(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatFloatMap.count|count(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEach|forEach(kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Float, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/FloatFloatMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,kotlin.Float,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/FloatFloatMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/FloatFloatMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/FloatFloatMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/FloatFloatMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatFloatMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+    final var metadata // androidx.collection/FloatFloatMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/FloatFloatMap.values|{}values[0]
+        final fun <get-values>(): kotlin/FloatArray // androidx.collection/FloatFloatMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/FloatArray) // androidx.collection/FloatFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatFloatMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/FloatFloatMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/FloatFloatMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatIntMap { // androidx.collection/FloatIntMap|null[0]
+    constructor <init>() // androidx.collection/FloatIntMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/FloatIntMap.any|any(){}[0]
+    final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatIntMap.contains|contains(kotlin.Float){}[0]
+    final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatIntMap.containsKey|containsKey(kotlin.Float){}[0]
+    final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/FloatIntMap.containsValue|containsValue(kotlin.Int){}[0]
+    final fun count(): kotlin/Int // androidx.collection/FloatIntMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Float): kotlin/Int // androidx.collection/FloatIntMap.findKeyIndex|findKeyIndex(kotlin.Float){}[0]
+    final fun get(kotlin/Float): kotlin/Int // androidx.collection/FloatIntMap.get|get(kotlin.Float){}[0]
+    final fun getOrDefault(kotlin/Float, kotlin/Int): kotlin/Int // androidx.collection/FloatIntMap.getOrDefault|getOrDefault(kotlin.Float;kotlin.Int){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatIntMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatIntMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/FloatIntMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatIntMap.all|all(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatIntMap.any|any(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatIntMap.count|count(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/Unit>) // androidx.collection/FloatIntMap.forEach|forEach(kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatIntMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Float, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/FloatIntMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,kotlin.Int,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/FloatIntMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/FloatIntMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/FloatIntMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/FloatIntMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatIntMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatIntMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+    final var metadata // androidx.collection/FloatIntMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/FloatIntMap.values|{}values[0]
+        final fun <get-values>(): kotlin/IntArray // androidx.collection/FloatIntMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/IntArray) // androidx.collection/FloatIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatIntMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/FloatIntMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/FloatIntMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatList { // androidx.collection/FloatList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/FloatList.<init>|<init>(kotlin.Int){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/FloatList.any|any(){}[0]
+    final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatList.contains|contains(kotlin.Float){}[0]
+    final fun containsAll(androidx.collection/FloatList): kotlin/Boolean // androidx.collection/FloatList.containsAll|containsAll(androidx.collection.FloatList){}[0]
+    final fun count(): kotlin/Int // androidx.collection/FloatList.count|count(){}[0]
+    final fun elementAt(kotlin/Int): kotlin/Float // androidx.collection/FloatList.elementAt|elementAt(kotlin.Int){}[0]
+    final fun first(): kotlin/Float // androidx.collection/FloatList.first|first(){}[0]
+    final fun get(kotlin/Int): kotlin/Float // androidx.collection/FloatList.get|get(kotlin.Int){}[0]
+    final fun indexOf(kotlin/Float): kotlin/Int // androidx.collection/FloatList.indexOf|indexOf(kotlin.Float){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatList.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatList.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun last(): kotlin/Float // androidx.collection/FloatList.last|last(){}[0]
+    final fun lastIndexOf(kotlin/Float): kotlin/Int // androidx.collection/FloatList.lastIndexOf|lastIndexOf(kotlin.Float){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/FloatList.none|none(){}[0]
+    final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Float, #A1>): #A1 // androidx.collection/FloatList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Float,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Float, #A1>): #A1 // androidx.collection/FloatList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Float,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Float, #A1, #A1>): #A1 // androidx.collection/FloatList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Float,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Float, #A1, #A1>): #A1 // androidx.collection/FloatList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Float,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun any(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatList.any|any(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatList.count|count(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Float>): kotlin/Float // androidx.collection/FloatList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Float>){}[0]
+    final inline fun first(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Float // androidx.collection/FloatList.first|first(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEach|forEach(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachReversed(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Unit>) // androidx.collection/FloatList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun indexOfFirst(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun indexOfLast(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Float,kotlin.CharSequence>){}[0]
+    final inline fun last(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Float // androidx.collection/FloatList.last|last(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun reversedAny(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatList.reversedAny|reversedAny(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final val indices // androidx.collection/FloatList.indices|{}indices[0]
+        final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/FloatList.indices.<get-indices>|<get-indices>(){}[0]
+    final val lastIndex // androidx.collection/FloatList.lastIndex|{}lastIndex[0]
+        final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/FloatList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+    final val size // androidx.collection/FloatList.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/FloatList.size.<get-size>|<get-size>(){}[0]
+    final var _size // androidx.collection/FloatList._size|{}_size[0]
+        final fun <get-_size>(): kotlin/Int // androidx.collection/FloatList._size.<get-_size>|<get-_size>(){}[0]
+        final fun <set-_size>(kotlin/Int) // androidx.collection/FloatList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+    final var content // androidx.collection/FloatList.content|{}content[0]
+        final fun <get-content>(): kotlin/FloatArray // androidx.collection/FloatList.content.<get-content>|<get-content>(){}[0]
+        final fun <set-content>(kotlin/FloatArray) // androidx.collection/FloatList.content.<set-content>|<set-content>(kotlin.FloatArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatList.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/FloatList.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/FloatList.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatLongMap { // androidx.collection/FloatLongMap|null[0]
+    constructor <init>() // androidx.collection/FloatLongMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/FloatLongMap.any|any(){}[0]
+    final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatLongMap.contains|contains(kotlin.Float){}[0]
+    final fun containsKey(kotlin/Float): kotlin/Boolean // androidx.collection/FloatLongMap.containsKey|containsKey(kotlin.Float){}[0]
+    final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/FloatLongMap.containsValue|containsValue(kotlin.Long){}[0]
+    final fun count(): kotlin/Int // androidx.collection/FloatLongMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Float): kotlin/Int // androidx.collection/FloatLongMap.findKeyIndex|findKeyIndex(kotlin.Float){}[0]
+    final fun get(kotlin/Float): kotlin/Long // androidx.collection/FloatLongMap.get|get(kotlin.Float){}[0]
+    final fun getOrDefault(kotlin/Float, kotlin/Long): kotlin/Long // androidx.collection/FloatLongMap.getOrDefault|getOrDefault(kotlin.Float;kotlin.Long){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatLongMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatLongMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/FloatLongMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatLongMap.all|all(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatLongMap.any|any(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatLongMap.count|count(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/Unit>) // androidx.collection/FloatLongMap.forEach|forEach(kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatLongMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/FloatLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Float, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/FloatLongMap.getOrElse|getOrElse(kotlin.Float;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Float, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Float,kotlin.Long,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/FloatLongMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/FloatLongMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/FloatLongMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/FloatLongMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/FloatArray // androidx.collection/FloatLongMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/FloatArray) // androidx.collection/FloatLongMap.keys.<set-keys>|<set-keys>(kotlin.FloatArray){}[0]
+    final var metadata // androidx.collection/FloatLongMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/FloatLongMap.values|{}values[0]
+        final fun <get-values>(): kotlin/LongArray // androidx.collection/FloatLongMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/LongArray) // androidx.collection/FloatLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatLongMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/FloatLongMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/FloatLongMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/FloatSet { // androidx.collection/FloatSet|null[0]
+    constructor <init>() // androidx.collection/FloatSet.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/FloatSet.any|any(){}[0]
+    final fun contains(kotlin/Float): kotlin/Boolean // androidx.collection/FloatSet.contains|contains(kotlin.Float){}[0]
+    final fun count(): kotlin/Int // androidx.collection/FloatSet.count|count(){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/FloatSet.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/FloatSet.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/FloatSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/FloatSet.none|none(){}[0]
+    final inline fun all(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatSet.all|all(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/FloatSet.any|any(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/FloatSet.count|count(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun first(): kotlin/Float // androidx.collection/FloatSet.first|first(){}[0]
+    final inline fun first(kotlin/Function1<kotlin/Float, kotlin/Boolean>): kotlin/Float // androidx.collection/FloatSet.first|first(kotlin.Function1<kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/FloatSet.forEach|forEach(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/FloatSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/FloatSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Float,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/FloatSet.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/FloatSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/FloatSet.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/FloatSet.size.<get-size>|<get-size>(){}[0]
+    final var elements // androidx.collection/FloatSet.elements|{}elements[0]
+        final fun <get-elements>(): kotlin/FloatArray // androidx.collection/FloatSet.elements.<get-elements>|<get-elements>(){}[0]
+        final fun <set-elements>(kotlin/FloatArray) // androidx.collection/FloatSet.elements.<set-elements>|<set-elements>(kotlin.FloatArray){}[0]
+    final var metadata // androidx.collection/FloatSet.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/FloatSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/FloatSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/FloatSet.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/FloatSet.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/FloatSet.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntFloatMap { // androidx.collection/IntFloatMap|null[0]
+    constructor <init>() // androidx.collection/IntFloatMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/IntFloatMap.any|any(){}[0]
+    final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntFloatMap.contains|contains(kotlin.Int){}[0]
+    final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntFloatMap.containsKey|containsKey(kotlin.Int){}[0]
+    final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/IntFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+    final fun count(): kotlin/Int // androidx.collection/IntFloatMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Int): kotlin/Int // androidx.collection/IntFloatMap.findKeyIndex|findKeyIndex(kotlin.Int){}[0]
+    final fun get(kotlin/Int): kotlin/Float // androidx.collection/IntFloatMap.get|get(kotlin.Int){}[0]
+    final fun getOrDefault(kotlin/Int, kotlin/Float): kotlin/Float // androidx.collection/IntFloatMap.getOrDefault|getOrDefault(kotlin.Int;kotlin.Float){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/IntFloatMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntFloatMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/IntFloatMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntFloatMap.all|all(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntFloatMap.any|any(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/IntFloatMap.count|count(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/Unit>) // androidx.collection/IntFloatMap.forEach|forEach(kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntFloatMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/IntFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Int, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/IntFloatMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/IntFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,kotlin.Float,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/IntFloatMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/IntFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/IntFloatMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/IntFloatMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/IntFloatMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntFloatMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+    final var metadata // androidx.collection/IntFloatMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/IntFloatMap.values|{}values[0]
+        final fun <get-values>(): kotlin/FloatArray // androidx.collection/IntFloatMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/FloatArray) // androidx.collection/IntFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntFloatMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/IntFloatMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/IntFloatMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntIntMap { // androidx.collection/IntIntMap|null[0]
+    constructor <init>() // androidx.collection/IntIntMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/IntIntMap.any|any(){}[0]
+    final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntIntMap.contains|contains(kotlin.Int){}[0]
+    final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntIntMap.containsKey|containsKey(kotlin.Int){}[0]
+    final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/IntIntMap.containsValue|containsValue(kotlin.Int){}[0]
+    final fun count(): kotlin/Int // androidx.collection/IntIntMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Int): kotlin/Int // androidx.collection/IntIntMap.findKeyIndex|findKeyIndex(kotlin.Int){}[0]
+    final fun get(kotlin/Int): kotlin/Int // androidx.collection/IntIntMap.get|get(kotlin.Int){}[0]
+    final fun getOrDefault(kotlin/Int, kotlin/Int): kotlin/Int // androidx.collection/IntIntMap.getOrDefault|getOrDefault(kotlin.Int;kotlin.Int){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/IntIntMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntIntMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/IntIntMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntIntMap.all|all(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntIntMap.any|any(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntIntMap.count|count(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEach|forEach(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Int, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/IntIntMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/IntIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/IntIntMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/IntIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/IntIntMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/IntIntMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/IntIntMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntIntMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntIntMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+    final var metadata // androidx.collection/IntIntMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/IntIntMap.values|{}values[0]
+        final fun <get-values>(): kotlin/IntArray // androidx.collection/IntIntMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/IntArray) // androidx.collection/IntIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntIntMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/IntIntMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/IntIntMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntList { // androidx.collection/IntList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/IntList.<init>|<init>(kotlin.Int){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/IntList.any|any(){}[0]
+    final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntList.contains|contains(kotlin.Int){}[0]
+    final fun containsAll(androidx.collection/IntList): kotlin/Boolean // androidx.collection/IntList.containsAll|containsAll(androidx.collection.IntList){}[0]
+    final fun count(): kotlin/Int // androidx.collection/IntList.count|count(){}[0]
+    final fun elementAt(kotlin/Int): kotlin/Int // androidx.collection/IntList.elementAt|elementAt(kotlin.Int){}[0]
+    final fun first(): kotlin/Int // androidx.collection/IntList.first|first(){}[0]
+    final fun get(kotlin/Int): kotlin/Int // androidx.collection/IntList.get|get(kotlin.Int){}[0]
+    final fun indexOf(kotlin/Int): kotlin/Int // androidx.collection/IntList.indexOf|indexOf(kotlin.Int){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/IntList.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntList.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun last(): kotlin/Int // androidx.collection/IntList.last|last(){}[0]
+    final fun lastIndexOf(kotlin/Int): kotlin/Int // androidx.collection/IntList.lastIndexOf|lastIndexOf(kotlin.Int){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/IntList.none|none(){}[0]
+    final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Int, #A1>): #A1 // androidx.collection/IntList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Int,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Int, #A1>): #A1 // androidx.collection/IntList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Int,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Int, #A1, #A1>): #A1 // androidx.collection/IntList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Int,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Int, #A1, #A1>): #A1 // androidx.collection/IntList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Int,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun any(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntList.any|any(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.count|count(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Int>): kotlin/Int // androidx.collection/IntList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Int>){}[0]
+    final inline fun first(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.first|first(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEach|forEach(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachReversed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Int, kotlin/Unit>) // androidx.collection/IntList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun indexOfFirst(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun indexOfLast(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/IntList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Int,kotlin.CharSequence>){}[0]
+    final inline fun last(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntList.last|last(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun reversedAny(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntList.reversedAny|reversedAny(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final val indices // androidx.collection/IntList.indices|{}indices[0]
+        final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/IntList.indices.<get-indices>|<get-indices>(){}[0]
+    final val lastIndex // androidx.collection/IntList.lastIndex|{}lastIndex[0]
+        final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/IntList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+    final val size // androidx.collection/IntList.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/IntList.size.<get-size>|<get-size>(){}[0]
+    final var _size // androidx.collection/IntList._size|{}_size[0]
+        final fun <get-_size>(): kotlin/Int // androidx.collection/IntList._size.<get-_size>|<get-_size>(){}[0]
+        final fun <set-_size>(kotlin/Int) // androidx.collection/IntList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+    final var content // androidx.collection/IntList.content|{}content[0]
+        final fun <get-content>(): kotlin/IntArray // androidx.collection/IntList.content.<get-content>|<get-content>(){}[0]
+        final fun <set-content>(kotlin/IntArray) // androidx.collection/IntList.content.<set-content>|<set-content>(kotlin.IntArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntList.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/IntList.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/IntList.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntLongMap { // androidx.collection/IntLongMap|null[0]
+    constructor <init>() // androidx.collection/IntLongMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/IntLongMap.any|any(){}[0]
+    final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntLongMap.contains|contains(kotlin.Int){}[0]
+    final fun containsKey(kotlin/Int): kotlin/Boolean // androidx.collection/IntLongMap.containsKey|containsKey(kotlin.Int){}[0]
+    final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/IntLongMap.containsValue|containsValue(kotlin.Long){}[0]
+    final fun count(): kotlin/Int // androidx.collection/IntLongMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Int): kotlin/Int // androidx.collection/IntLongMap.findKeyIndex|findKeyIndex(kotlin.Int){}[0]
+    final fun get(kotlin/Int): kotlin/Long // androidx.collection/IntLongMap.get|get(kotlin.Int){}[0]
+    final fun getOrDefault(kotlin/Int, kotlin/Long): kotlin/Long // androidx.collection/IntLongMap.getOrDefault|getOrDefault(kotlin.Int;kotlin.Long){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/IntLongMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntLongMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/IntLongMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntLongMap.all|all(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntLongMap.any|any(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/IntLongMap.count|count(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Unit>) // androidx.collection/IntLongMap.forEach|forEach(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntLongMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/IntLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Int, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/IntLongMap.getOrElse|getOrElse(kotlin.Int;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/IntLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/IntLongMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/IntLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/IntLongMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/IntLongMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/IntLongMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/IntArray // androidx.collection/IntLongMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/IntArray) // androidx.collection/IntLongMap.keys.<set-keys>|<set-keys>(kotlin.IntArray){}[0]
+    final var metadata // androidx.collection/IntLongMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/IntLongMap.values|{}values[0]
+        final fun <get-values>(): kotlin/LongArray // androidx.collection/IntLongMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/LongArray) // androidx.collection/IntLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntLongMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/IntLongMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/IntLongMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/IntSet { // androidx.collection/IntSet|null[0]
+    constructor <init>() // androidx.collection/IntSet.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/IntSet.any|any(){}[0]
+    final fun contains(kotlin/Int): kotlin/Boolean // androidx.collection/IntSet.contains|contains(kotlin.Int){}[0]
+    final fun count(): kotlin/Int // androidx.collection/IntSet.count|count(){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/IntSet.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/IntSet.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/IntSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/IntSet.none|none(){}[0]
+    final inline fun all(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntSet.all|all(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/IntSet.any|any(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntSet.count|count(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun first(): kotlin/Int // androidx.collection/IntSet.first|first(){}[0]
+    final inline fun first(kotlin/Function1<kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/IntSet.first|first(kotlin.Function1<kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntSet.forEach|forEach(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/IntSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/IntSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Int,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/IntSet.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/IntSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/IntSet.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/IntSet.size.<get-size>|<get-size>(){}[0]
+    final var elements // androidx.collection/IntSet.elements|{}elements[0]
+        final fun <get-elements>(): kotlin/IntArray // androidx.collection/IntSet.elements.<get-elements>|<get-elements>(){}[0]
+        final fun <set-elements>(kotlin/IntArray) // androidx.collection/IntSet.elements.<set-elements>|<set-elements>(kotlin.IntArray){}[0]
+    final var metadata // androidx.collection/IntSet.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/IntSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/IntSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/IntSet.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/IntSet.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/IntSet.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongFloatMap { // androidx.collection/LongFloatMap|null[0]
+    constructor <init>() // androidx.collection/LongFloatMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/LongFloatMap.any|any(){}[0]
+    final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongFloatMap.contains|contains(kotlin.Long){}[0]
+    final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongFloatMap.containsKey|containsKey(kotlin.Long){}[0]
+    final fun containsValue(kotlin/Float): kotlin/Boolean // androidx.collection/LongFloatMap.containsValue|containsValue(kotlin.Float){}[0]
+    final fun count(): kotlin/Int // androidx.collection/LongFloatMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Long): kotlin/Int // androidx.collection/LongFloatMap.findKeyIndex|findKeyIndex(kotlin.Long){}[0]
+    final fun get(kotlin/Long): kotlin/Float // androidx.collection/LongFloatMap.get|get(kotlin.Long){}[0]
+    final fun getOrDefault(kotlin/Long, kotlin/Float): kotlin/Float // androidx.collection/LongFloatMap.getOrDefault|getOrDefault(kotlin.Long;kotlin.Float){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/LongFloatMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongFloatMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/LongFloatMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongFloatMap.all|all(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongFloatMap.any|any(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Boolean>): kotlin/Int // androidx.collection/LongFloatMap.count|count(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/Unit>) // androidx.collection/LongFloatMap.forEach|forEach(kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongFloatMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongFloatMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Float, kotlin/Unit>) // androidx.collection/LongFloatMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Float,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Long, kotlin/Function0<kotlin/Float>): kotlin/Float // androidx.collection/LongFloatMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<kotlin.Float>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, kotlin/Float, kotlin/CharSequence>): kotlin/String // androidx.collection/LongFloatMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,kotlin.Float,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/LongFloatMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/LongFloatMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/LongFloatMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/LongFloatMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/LongFloatMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongFloatMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongFloatMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+    final var metadata // androidx.collection/LongFloatMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongFloatMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongFloatMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/LongFloatMap.values|{}values[0]
+        final fun <get-values>(): kotlin/FloatArray // androidx.collection/LongFloatMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/FloatArray) // androidx.collection/LongFloatMap.values.<set-values>|<set-values>(kotlin.FloatArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongFloatMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/LongFloatMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LongFloatMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongIntMap { // androidx.collection/LongIntMap|null[0]
+    constructor <init>() // androidx.collection/LongIntMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/LongIntMap.any|any(){}[0]
+    final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongIntMap.contains|contains(kotlin.Long){}[0]
+    final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongIntMap.containsKey|containsKey(kotlin.Long){}[0]
+    final fun containsValue(kotlin/Int): kotlin/Boolean // androidx.collection/LongIntMap.containsValue|containsValue(kotlin.Int){}[0]
+    final fun count(): kotlin/Int // androidx.collection/LongIntMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Long): kotlin/Int // androidx.collection/LongIntMap.findKeyIndex|findKeyIndex(kotlin.Long){}[0]
+    final fun get(kotlin/Long): kotlin/Int // androidx.collection/LongIntMap.get|get(kotlin.Long){}[0]
+    final fun getOrDefault(kotlin/Long, kotlin/Int): kotlin/Int // androidx.collection/LongIntMap.getOrDefault|getOrDefault(kotlin.Long;kotlin.Int){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/LongIntMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongIntMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/LongIntMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongIntMap.all|all(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongIntMap.any|any(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Boolean>): kotlin/Int // androidx.collection/LongIntMap.count|count(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/Unit>) // androidx.collection/LongIntMap.forEach|forEach(kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongIntMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongIntMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongIntMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Long, kotlin/Function0<kotlin/Int>): kotlin/Int // androidx.collection/LongIntMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<kotlin.Int>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, kotlin/Int, kotlin/CharSequence>): kotlin/String // androidx.collection/LongIntMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,kotlin.Int,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/LongIntMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/LongIntMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/LongIntMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/LongIntMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/LongIntMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongIntMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongIntMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+    final var metadata // androidx.collection/LongIntMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongIntMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongIntMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/LongIntMap.values|{}values[0]
+        final fun <get-values>(): kotlin/IntArray // androidx.collection/LongIntMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/IntArray) // androidx.collection/LongIntMap.values.<set-values>|<set-values>(kotlin.IntArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongIntMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/LongIntMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LongIntMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongList { // androidx.collection/LongList|null[0]
+    constructor <init>(kotlin/Int) // androidx.collection/LongList.<init>|<init>(kotlin.Int){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/LongList.any|any(){}[0]
+    final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongList.contains|contains(kotlin.Long){}[0]
+    final fun containsAll(androidx.collection/LongList): kotlin/Boolean // androidx.collection/LongList.containsAll|containsAll(androidx.collection.LongList){}[0]
+    final fun count(): kotlin/Int // androidx.collection/LongList.count|count(){}[0]
+    final fun elementAt(kotlin/Int): kotlin/Long // androidx.collection/LongList.elementAt|elementAt(kotlin.Int){}[0]
+    final fun first(): kotlin/Long // androidx.collection/LongList.first|first(){}[0]
+    final fun get(kotlin/Int): kotlin/Long // androidx.collection/LongList.get|get(kotlin.Int){}[0]
+    final fun indexOf(kotlin/Long): kotlin/Int // androidx.collection/LongList.indexOf|indexOf(kotlin.Long){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/LongList.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongList.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun last(): kotlin/Long // androidx.collection/LongList.last|last(){}[0]
+    final fun lastIndexOf(kotlin/Long): kotlin/Int // androidx.collection/LongList.lastIndexOf|lastIndexOf(kotlin.Long){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/LongList.none|none(){}[0]
+    final inline fun <#A1: kotlin/Any?> fold(#A1, kotlin/Function2<#A1, kotlin/Long, #A1>): #A1 // androidx.collection/LongList.fold|fold(0:0;kotlin.Function2<0:0,kotlin.Long,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldIndexed(#A1, kotlin/Function3<kotlin/Int, #A1, kotlin/Long, #A1>): #A1 // androidx.collection/LongList.foldIndexed|foldIndexed(0:0;kotlin.Function3<kotlin.Int,0:0,kotlin.Long,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRight(#A1, kotlin/Function2<kotlin/Long, #A1, #A1>): #A1 // androidx.collection/LongList.foldRight|foldRight(0:0;kotlin.Function2<kotlin.Long,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: kotlin/Any?> foldRightIndexed(#A1, kotlin/Function3<kotlin/Int, kotlin/Long, #A1, #A1>): #A1 // androidx.collection/LongList.foldRightIndexed|foldRightIndexed(0:0;kotlin.Function3<kotlin.Int,kotlin.Long,0:0,0:0>){0§<kotlin.Any?>}[0]
+    final inline fun any(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongList.any|any(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongList.count|count(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun elementAtOrElse(kotlin/Int, kotlin/Function1<kotlin/Int, kotlin/Long>): kotlin/Long // androidx.collection/LongList.elementAtOrElse|elementAtOrElse(kotlin.Int;kotlin.Function1<kotlin.Int,kotlin.Long>){}[0]
+    final inline fun first(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Long // androidx.collection/LongList.first|first(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEach|forEach(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachReversed(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEachReversed|forEachReversed(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachReversedIndexed(kotlin/Function2<kotlin/Int, kotlin/Long, kotlin/Unit>) // androidx.collection/LongList.forEachReversedIndexed|forEachReversedIndexed(kotlin.Function2<kotlin.Int,kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun indexOfFirst(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongList.indexOfFirst|indexOfFirst(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun indexOfLast(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongList.indexOfLast|indexOfLast(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/LongList.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Long,kotlin.CharSequence>){}[0]
+    final inline fun last(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Long // androidx.collection/LongList.last|last(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun reversedAny(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongList.reversedAny|reversedAny(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final val indices // androidx.collection/LongList.indices|{}indices[0]
+        final inline fun <get-indices>(): kotlin.ranges/IntRange // androidx.collection/LongList.indices.<get-indices>|<get-indices>(){}[0]
+    final val lastIndex // androidx.collection/LongList.lastIndex|{}lastIndex[0]
+        final inline fun <get-lastIndex>(): kotlin/Int // androidx.collection/LongList.lastIndex.<get-lastIndex>|<get-lastIndex>(){}[0]
+    final val size // androidx.collection/LongList.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/LongList.size.<get-size>|<get-size>(){}[0]
+    final var _size // androidx.collection/LongList._size|{}_size[0]
+        final fun <get-_size>(): kotlin/Int // androidx.collection/LongList._size.<get-_size>|<get-_size>(){}[0]
+        final fun <set-_size>(kotlin/Int) // androidx.collection/LongList._size.<set-_size>|<set-_size>(kotlin.Int){}[0]
+    final var content // androidx.collection/LongList.content|{}content[0]
+        final fun <get-content>(): kotlin/LongArray // androidx.collection/LongList.content.<get-content>|<get-content>(){}[0]
+        final fun <set-content>(kotlin/LongArray) // androidx.collection/LongList.content.<set-content>|<set-content>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongList.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/LongList.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LongList.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongLongMap { // androidx.collection/LongLongMap|null[0]
+    constructor <init>() // androidx.collection/LongLongMap.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/LongLongMap.any|any(){}[0]
+    final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongLongMap.contains|contains(kotlin.Long){}[0]
+    final fun containsKey(kotlin/Long): kotlin/Boolean // androidx.collection/LongLongMap.containsKey|containsKey(kotlin.Long){}[0]
+    final fun containsValue(kotlin/Long): kotlin/Boolean // androidx.collection/LongLongMap.containsValue|containsValue(kotlin.Long){}[0]
+    final fun count(): kotlin/Int // androidx.collection/LongLongMap.count|count(){}[0]
+    final fun findKeyIndex(kotlin/Long): kotlin/Int // androidx.collection/LongLongMap.findKeyIndex|findKeyIndex(kotlin.Long){}[0]
+    final fun get(kotlin/Long): kotlin/Long // androidx.collection/LongLongMap.get|get(kotlin.Long){}[0]
+    final fun getOrDefault(kotlin/Long, kotlin/Long): kotlin/Long // androidx.collection/LongLongMap.getOrDefault|getOrDefault(kotlin.Long;kotlin.Long){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/LongLongMap.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongLongMap.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/LongLongMap.none|none(){}[0]
+    final inline fun all(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongLongMap.all|all(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongLongMap.any|any(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongLongMap.count|count(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/Unit>) // androidx.collection/LongLongMap.forEach|forEach(kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachIndexed(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongLongMap.forEachIndexed|forEachIndexed(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun forEachKey(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongLongMap.forEachKey|forEachKey(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachValue(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongLongMap.forEachValue|forEachValue(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun getOrElse(kotlin/Long, kotlin/Function0<kotlin/Long>): kotlin/Long // androidx.collection/LongLongMap.getOrElse|getOrElse(kotlin.Long;kotlin.Function0<kotlin.Long>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function2<kotlin/Long, kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/LongLongMap.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function2<kotlin.Long,kotlin.Long,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/LongLongMap.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/LongLongMap.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/LongLongMap.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/LongLongMap.size.<get-size>|<get-size>(){}[0]
+    final var keys // androidx.collection/LongLongMap.keys|{}keys[0]
+        final fun <get-keys>(): kotlin/LongArray // androidx.collection/LongLongMap.keys.<get-keys>|<get-keys>(){}[0]
+        final fun <set-keys>(kotlin/LongArray) // androidx.collection/LongLongMap.keys.<set-keys>|<set-keys>(kotlin.LongArray){}[0]
+    final var metadata // androidx.collection/LongLongMap.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongLongMap.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongLongMap.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    final var values // androidx.collection/LongLongMap.values|{}values[0]
+        final fun <get-values>(): kotlin/LongArray // androidx.collection/LongLongMap.values.<get-values>|<get-values>(){}[0]
+        final fun <set-values>(kotlin/LongArray) // androidx.collection/LongLongMap.values.<set-values>|<set-values>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongLongMap.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/LongLongMap.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LongLongMap.toString|toString(){}[0]
+}
+sealed class androidx.collection/LongSet { // androidx.collection/LongSet|null[0]
+    constructor <init>() // androidx.collection/LongSet.<init>|<init>(){}[0]
+    final fun any(): kotlin/Boolean // androidx.collection/LongSet.any|any(){}[0]
+    final fun contains(kotlin/Long): kotlin/Boolean // androidx.collection/LongSet.contains|contains(kotlin.Long){}[0]
+    final fun count(): kotlin/Int // androidx.collection/LongSet.count|count(){}[0]
+    final fun isEmpty(): kotlin/Boolean // androidx.collection/LongSet.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty(): kotlin/Boolean // androidx.collection/LongSet.isNotEmpty|isNotEmpty(){}[0]
+    final fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =...): kotlin/String // androidx.collection/LongSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence){}[0]
+    final fun none(): kotlin/Boolean // androidx.collection/LongSet.none|none(){}[0]
+    final inline fun all(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongSet.all|all(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun any(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Boolean // androidx.collection/LongSet.any|any(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun count(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Int // androidx.collection/LongSet.count|count(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun first(): kotlin/Long // androidx.collection/LongSet.first|first(){}[0]
+    final inline fun first(kotlin/Function1<kotlin/Long, kotlin/Boolean>): kotlin/Long // androidx.collection/LongSet.first|first(kotlin.Function1<kotlin.Long,kotlin.Boolean>){}[0]
+    final inline fun forEach(kotlin/Function1<kotlin/Long, kotlin/Unit>) // androidx.collection/LongSet.forEach|forEach(kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0]
+    final inline fun forEachIndex(kotlin/Function1<kotlin/Int, kotlin/Unit>) // androidx.collection/LongSet.forEachIndex|forEachIndex(kotlin.Function1<kotlin.Int,kotlin.Unit>){}[0]
+    final inline fun joinToString(kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/CharSequence =..., kotlin/Int =..., kotlin/CharSequence =..., crossinline kotlin/Function1<kotlin/Long, kotlin/CharSequence>): kotlin/String // androidx.collection/LongSet.joinToString|joinToString(kotlin.CharSequence;kotlin.CharSequence;kotlin.CharSequence;kotlin.Int;kotlin.CharSequence;kotlin.Function1<kotlin.Long,kotlin.CharSequence>){}[0]
+    final val capacity // androidx.collection/LongSet.capacity|{}capacity[0]
+        final fun <get-capacity>(): kotlin/Int // androidx.collection/LongSet.capacity.<get-capacity>|<get-capacity>(){}[0]
+    final val size // androidx.collection/LongSet.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // androidx.collection/LongSet.size.<get-size>|<get-size>(){}[0]
+    final var elements // androidx.collection/LongSet.elements|{}elements[0]
+        final fun <get-elements>(): kotlin/LongArray // androidx.collection/LongSet.elements.<get-elements>|<get-elements>(){}[0]
+        final fun <set-elements>(kotlin/LongArray) // androidx.collection/LongSet.elements.<set-elements>|<set-elements>(kotlin.LongArray){}[0]
+    final var metadata // androidx.collection/LongSet.metadata|{}metadata[0]
+        final fun <get-metadata>(): kotlin/LongArray // androidx.collection/LongSet.metadata.<get-metadata>|<get-metadata>(){}[0]
+        final fun <set-metadata>(kotlin/LongArray) // androidx.collection/LongSet.metadata.<set-metadata>|<set-metadata>(kotlin.LongArray){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.collection/LongSet.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.collection/LongSet.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.collection/LongSet.toString|toString(){}[0]
+}
diff --git a/collection/collection/build.gradle b/collection/collection/build.gradle
index 84812dc..396aea9 100644
--- a/collection/collection/build.gradle
+++ b/collection/collection/build.gradle
@@ -21,6 +21,7 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
+import androidx.build.KotlinTarget
 import androidx.build.LibraryType
 import androidx.build.PlatformIdentifier
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -37,7 +38,6 @@
     }
     mac()
     linux()
-    linuxArm64()
     ios()
     watchos()
     tvos()
@@ -62,6 +62,7 @@
                 implementation(libs.kotlinTest)
                 implementation(libs.kotlinTestAnnotationsCommon)
                 implementation(libs.kotlinCoroutinesCore)
+                implementation(libs.kotlinCoroutinesTest)
             }
         }
 
@@ -131,6 +132,8 @@
 androidx {
     name = "collections"
     type = LibraryType.PUBLISHED_LIBRARY
+    kotlinTarget = KotlinTarget.KOTLIN_1_9
     inceptionYear = "2018"
     description = "Standalone efficient collections."
+    metalavaK2UastEnabled = false
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/CircularArray.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/CircularArray.kt
index 5ff1abc..05b767c 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/CircularArray.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/CircularArray.kt
@@ -17,6 +17,7 @@
 package androidx.collection
 
 import androidx.collection.CollectionPlatformUtils.createIndexOutOfBoundsException
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmOverloads
 
 /**
@@ -34,8 +35,8 @@
     private var capacityBitmask: Int
 
     init {
-        require(minCapacity >= 1) { "capacity must be >= 1" }
-        require(minCapacity <= 2 shl 29) { "capacity must be <= 2^30" }
+        requirePrecondition(minCapacity >= 1) { "capacity must be >= 1" }
+        requirePrecondition(minCapacity <= 2 shl 29) { "capacity must be <= 2^30" }
 
         // If minCapacity isn't a power of 2, round up to the next highest
         // power of 2.
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/CircularIntArray.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/CircularIntArray.kt
index c41cc85..a6dbcf8 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/CircularIntArray.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/CircularIntArray.kt
@@ -16,6 +16,7 @@
 package androidx.collection
 
 import androidx.collection.CollectionPlatformUtils.createIndexOutOfBoundsException
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmOverloads
 
 /**
@@ -33,8 +34,8 @@
     private var capacityBitmask: Int
 
     init {
-        require(minCapacity >= 1) { "capacity must be >= 1" }
-        require(minCapacity <= 2 shl 29) { "capacity must be <= 2^30" }
+        requirePrecondition(minCapacity >= 1) { "capacity must be >= 1" }
+        requirePrecondition(minCapacity <= 2 shl 29) { "capacity must be <= 2^30" }
 
         // If minCapacity isn't a power of 2, round up to the next highest
         // power of 2.
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt
index 5afa7a9..734b95b5 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
@@ -55,19 +56,19 @@
     @Suppress("PropertyName") @JvmField @PublishedApi internal var _size: Int = 0
 
     /** The number of elements in the [DoubleList]. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
     /**
      * Returns the last valid index in the [DoubleList]. This can be `-1` when the list is empty.
      */
-    @get:androidx.annotation.IntRange(from = -1)
+    @get:IntRange(from = -1)
     public inline val lastIndex: Int
         get() = _size - 1
 
     /** Returns an [IntRange] of the valid indices for this [DoubleList]. */
-    public inline val indices: IntRange
+    public inline val indices: kotlin.ranges.IntRange
         get() = 0 until _size
 
     /** Returns `true` if the collection has no elements in it. */
@@ -284,7 +285,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public operator fun get(@androidx.annotation.IntRange(from = 0) index: Int): Double {
+    public operator fun get(@IntRange(from = 0) index: Int): Double {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -295,7 +296,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public fun elementAt(@androidx.annotation.IntRange(from = 0) index: Int): Double {
+    public fun elementAt(@IntRange(from = 0) index: Int): Double {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -311,7 +312,7 @@
      *   index not in the list.
      */
     public inline fun elementAtOrElse(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         defaultValue: (index: Int) -> Double
     ): Double {
         if (index !in 0 until _size) {
@@ -533,7 +534,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun add(@androidx.annotation.IntRange(from = 0) index: Int, element: Double) {
+    public fun add(@IntRange(from = 0) index: Int, element: Double) {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -558,10 +559,7 @@
      * @return `true` if the [MutableDoubleList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive.
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: DoubleArray
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: DoubleArray): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -588,10 +586,7 @@
      * @return `true` if the [MutableDoubleList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: DoubleList
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: DoubleList): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -740,7 +735,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public fun removeAt(@androidx.annotation.IntRange(from = 0) index: Int): Double {
+    public fun removeAt(@IntRange(from = 0) index: Int): Double {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -764,10 +759,7 @@
      * @throws IndexOutOfBoundsException if [start] or [end] isn't between 0 and [size], inclusive
      * @throws IllegalArgumentException if [start] is greater than [end]
      */
-    public fun removeRange(
-        @androidx.annotation.IntRange(from = 0) start: Int,
-        @androidx.annotation.IntRange(from = 0) end: Int
-    ) {
+    public fun removeRange(@IntRange(from = 0) start: Int, @IntRange(from = 0) end: Int) {
         if (start !in 0.._size || end !in 0.._size) {
             throw IndexOutOfBoundsException("Start ($start) and end ($end) must be in 0..$_size")
         }
@@ -827,10 +819,7 @@
      * @return the previous value set at [index]
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public operator fun set(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        element: Double
-    ): Double {
+    public operator fun set(@IntRange(from = 0) index: Int, element: Double): Double {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("set index $index must be between 0 .. $lastIndex")
         }
@@ -842,11 +831,15 @@
 
     /** Sorts the [MutableDoubleList] elements in ascending order. */
     public fun sort() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sort(fromIndex = 0, toIndex = _size)
     }
 
     /** Sorts the [MutableDoubleList] elements in descending order. */
     public fun sortDescending() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sortDescending(fromIndex = 0, toIndex = _size)
     }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleSet.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleSet.kt
index dd5d348..eac2af5 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleSet.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleSet.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,6 @@
  * limitations under the License.
  */
 
-@file:Suppress(
-    "RedundantVisibilityModifier",
-    "KotlinRedundantDiagnosticSuppress",
-    "KotlinConstantConditions",
-    "PropertyName",
-    "ConstPropertyName",
-    "PrivatePropertyName",
-    "NOTHING_TO_INLINE"
-)
-
 package androidx.collection
 
 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatMap.kt
index 6824d27..fb2cf07 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0f
+
+                values[targetIndex] = values[index]
+                values[index] = 0f
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatIntMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatIntMap.kt
index 324e0d7..528fbda 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatIntMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatIntMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0f
+
+                values[targetIndex] = values[index]
+                values[index] = 0
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt
index cb71c1e..edee44c7 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
@@ -55,17 +56,17 @@
     @Suppress("PropertyName") @JvmField @PublishedApi internal var _size: Int = 0
 
     /** The number of elements in the [FloatList]. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
     /** Returns the last valid index in the [FloatList]. This can be `-1` when the list is empty. */
-    @get:androidx.annotation.IntRange(from = -1)
+    @get:IntRange(from = -1)
     public inline val lastIndex: Int
         get() = _size - 1
 
     /** Returns an [IntRange] of the valid indices for this [FloatList]. */
-    public inline val indices: IntRange
+    public inline val indices: kotlin.ranges.IntRange
         get() = 0 until _size
 
     /** Returns `true` if the collection has no elements in it. */
@@ -282,7 +283,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public operator fun get(@androidx.annotation.IntRange(from = 0) index: Int): Float {
+    public operator fun get(@IntRange(from = 0) index: Int): Float {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -293,7 +294,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public fun elementAt(@androidx.annotation.IntRange(from = 0) index: Int): Float {
+    public fun elementAt(@IntRange(from = 0) index: Int): Float {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -309,7 +310,7 @@
      *   index not in the list.
      */
     public inline fun elementAtOrElse(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         defaultValue: (index: Int) -> Float
     ): Float {
         if (index !in 0 until _size) {
@@ -530,7 +531,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun add(@androidx.annotation.IntRange(from = 0) index: Int, element: Float) {
+    public fun add(@IntRange(from = 0) index: Int, element: Float) {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -555,10 +556,7 @@
      * @return `true` if the [MutableFloatList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive.
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: FloatArray
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: FloatArray): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -585,10 +583,7 @@
      * @return `true` if the [MutableFloatList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: FloatList
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: FloatList): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -737,7 +732,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public fun removeAt(@androidx.annotation.IntRange(from = 0) index: Int): Float {
+    public fun removeAt(@IntRange(from = 0) index: Int): Float {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -761,10 +756,7 @@
      * @throws IndexOutOfBoundsException if [start] or [end] isn't between 0 and [size], inclusive
      * @throws IllegalArgumentException if [start] is greater than [end]
      */
-    public fun removeRange(
-        @androidx.annotation.IntRange(from = 0) start: Int,
-        @androidx.annotation.IntRange(from = 0) end: Int
-    ) {
+    public fun removeRange(@IntRange(from = 0) start: Int, @IntRange(from = 0) end: Int) {
         if (start !in 0.._size || end !in 0.._size) {
             throw IndexOutOfBoundsException("Start ($start) and end ($end) must be in 0..$_size")
         }
@@ -824,10 +816,7 @@
      * @return the previous value set at [index]
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public operator fun set(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        element: Float
-    ): Float {
+    public operator fun set(@IntRange(from = 0) index: Int, element: Float): Float {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("set index $index must be between 0 .. $lastIndex")
         }
@@ -839,11 +828,15 @@
 
     /** Sorts the [MutableFloatList] elements in ascending order. */
     public fun sort() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sort(fromIndex = 0, toIndex = _size)
     }
 
     /** Sorts the [MutableFloatList] elements in descending order. */
     public fun sortDescending() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sortDescending(fromIndex = 0, toIndex = _size)
     }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatLongMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatLongMap.kt
index 2f2512f..dec4db5 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatLongMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatLongMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0f
+
+                values[targetIndex] = values[index]
+                values[index] = 0L
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatObjectMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatObjectMap.kt
index 8582631..f6b324f 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatObjectMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatObjectMap.kt
@@ -19,6 +19,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -595,7 +596,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -741,7 +742,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         val oldValue = values[index]
         values[index] = null
 
@@ -801,7 +802,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -849,15 +850,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0f
+
+                values[targetIndex] = values[index]
+                values[index] = null
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -865,8 +966,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -874,42 +977,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatSet.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatSet.kt
index f8562c57..ee0343c 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatSet.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatSet.kt
@@ -26,6 +26,8 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
+import androidx.collection.internal.requirePrecondition
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
@@ -128,7 +130,7 @@
      * Returns the number of elements that can be stored in this set without requiring internal
      * storage reallocation.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val capacity: Int
         get() = _capacity
 
@@ -137,7 +139,7 @@
     @JvmField internal var _size: Int = 0
 
     /** Returns the number of elements in this set. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
@@ -248,7 +250,7 @@
     }
 
     /** Returns the number of elements in this set. */
-    @androidx.annotation.IntRange(from = 0) public fun count(): Int = _size
+    @IntRange(from = 0) public fun count(): Int = _size
 
     /**
      * Returns the number of elements matching the given [predicate].
@@ -256,7 +258,7 @@
      * @param predicate Called for all elements in the set to count the number for which it returns
      *   `true`.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(predicate: (element: Float) -> Boolean): Int {
         contract { callsInPlace(predicate) }
         var count = 0
@@ -439,7 +441,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -617,7 +619,7 @@
 
         // TODO: We could just mark the element as empty if there's a group
         //       window around this element that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all elements from this set. */
@@ -672,7 +674,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -703,7 +705,7 @@
      * Returns the number of empty elements removed from this set's storage. Returns 0 if no
      * trimming is necessary or possible.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public fun trim(): Int {
         val previousCapacity = _capacity
         val newCapacity = normalizeCapacity(unloadedCapacity(_size))
@@ -720,22 +722,116 @@
      * place" occurs when the current size is <= 25/32 of the set capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_map`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val elements = elements
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(elements[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                elements[targetIndex] = elements[index]
+                elements[index] = 0f
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                elements[swapIndex] = elements[targetIndex]
+                elements[targetIndex] = elements[index]
+                elements[index] = elements[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousElements = elements
         val previousCapacity = _capacity
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newElements = elements
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -743,43 +839,11 @@
                 val hash = hash(previousElement)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newElements[index] = previousElement
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
 
 /**
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IndexBasedArrayIterator.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IndexBasedArrayIterator.kt
index eb25daf..ad93ea9 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IndexBasedArrayIterator.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IndexBasedArrayIterator.kt
@@ -15,6 +15,8 @@
  */
 package androidx.collection
 
+import androidx.collection.internal.checkPrecondition
+
 internal abstract class IndexBasedArrayIterator<T>(startingSize: Int) : MutableIterator<T> {
 
     private var size = startingSize
@@ -39,7 +41,7 @@
     }
 
     override fun remove() {
-        check(canRemove) { "Call next() before removing an element." }
+        checkPrecondition(canRemove) { "Call next() before removing an element." }
         // Attempt removal first so an UnsupportedOperationException retains a valid state.
         removeAt(--index)
         size--
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IntFloatMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IntFloatMap.kt
index 71d6bb4..f19abb2 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IntFloatMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IntFloatMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0
+
+                values[targetIndex] = values[index]
+                values[index] = 0f
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IntIntMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IntIntMap.kt
index 3974053..4cd3dc4 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IntIntMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IntIntMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0
+
+                values[targetIndex] = values[index]
+                values[index] = 0
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt
index 85ad97a..da859f9 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
@@ -55,17 +56,17 @@
     @Suppress("PropertyName") @JvmField @PublishedApi internal var _size: Int = 0
 
     /** The number of elements in the [IntList]. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
     /** Returns the last valid index in the [IntList]. This can be `-1` when the list is empty. */
-    @get:androidx.annotation.IntRange(from = -1)
+    @get:IntRange(from = -1)
     public inline val lastIndex: Int
         get() = _size - 1
 
     /** Returns an [IntRange] of the valid indices for this [IntList]. */
-    public inline val indices: IntRange
+    public inline val indices: kotlin.ranges.IntRange
         get() = 0 until _size
 
     /** Returns `true` if the collection has no elements in it. */
@@ -282,7 +283,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public operator fun get(@androidx.annotation.IntRange(from = 0) index: Int): Int {
+    public operator fun get(@IntRange(from = 0) index: Int): Int {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -293,7 +294,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public fun elementAt(@androidx.annotation.IntRange(from = 0) index: Int): Int {
+    public fun elementAt(@IntRange(from = 0) index: Int): Int {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -309,7 +310,7 @@
      *   index not in the list.
      */
     public inline fun elementAtOrElse(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         defaultValue: (index: Int) -> Int
     ): Int {
         if (index !in 0 until _size) {
@@ -527,7 +528,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun add(@androidx.annotation.IntRange(from = 0) index: Int, element: Int) {
+    public fun add(@IntRange(from = 0) index: Int, element: Int) {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -552,10 +553,7 @@
      * @return `true` if the [MutableIntList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive.
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: IntArray
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: IntArray): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -582,10 +580,7 @@
      * @return `true` if the [MutableIntList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: IntList
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: IntList): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -731,7 +726,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public fun removeAt(@androidx.annotation.IntRange(from = 0) index: Int): Int {
+    public fun removeAt(@IntRange(from = 0) index: Int): Int {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -755,10 +750,7 @@
      * @throws IndexOutOfBoundsException if [start] or [end] isn't between 0 and [size], inclusive
      * @throws IllegalArgumentException if [start] is greater than [end]
      */
-    public fun removeRange(
-        @androidx.annotation.IntRange(from = 0) start: Int,
-        @androidx.annotation.IntRange(from = 0) end: Int
-    ) {
+    public fun removeRange(@IntRange(from = 0) start: Int, @IntRange(from = 0) end: Int) {
         if (start !in 0.._size || end !in 0.._size) {
             throw IndexOutOfBoundsException("Start ($start) and end ($end) must be in 0..$_size")
         }
@@ -818,7 +810,7 @@
      * @return the previous value set at [index]
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public operator fun set(@androidx.annotation.IntRange(from = 0) index: Int, element: Int): Int {
+    public operator fun set(@IntRange(from = 0) index: Int, element: Int): Int {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("set index $index must be between 0 .. $lastIndex")
         }
@@ -830,11 +822,15 @@
 
     /** Sorts the [MutableIntList] elements in ascending order. */
     public fun sort() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sort(fromIndex = 0, toIndex = _size)
     }
 
     /** Sorts the [MutableIntList] elements in descending order. */
     public fun sortDescending() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sortDescending(fromIndex = 0, toIndex = _size)
     }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IntLongMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IntLongMap.kt
index b3877d5..7afe108 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IntLongMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IntLongMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0
+
+                values[targetIndex] = values[index]
+                values[index] = 0L
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IntObjectMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IntObjectMap.kt
index 313d924..30e2aa5 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IntObjectMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IntObjectMap.kt
@@ -19,6 +19,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -595,7 +596,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -741,7 +742,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         val oldValue = values[index]
         values[index] = null
 
@@ -801,7 +802,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -849,15 +850,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0
+
+                values[targetIndex] = values[index]
+                values[index] = null
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -865,8 +966,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -874,42 +977,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IntSet.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IntSet.kt
index 8dfe391..a226286 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IntSet.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IntSet.kt
@@ -26,6 +26,8 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
+import androidx.collection.internal.requirePrecondition
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
@@ -126,7 +128,7 @@
      * Returns the number of elements that can be stored in this set without requiring internal
      * storage reallocation.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val capacity: Int
         get() = _capacity
 
@@ -135,7 +137,7 @@
     @JvmField internal var _size: Int = 0
 
     /** Returns the number of elements in this set. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
@@ -246,7 +248,7 @@
     }
 
     /** Returns the number of elements in this set. */
-    @androidx.annotation.IntRange(from = 0) public fun count(): Int = _size
+    @IntRange(from = 0) public fun count(): Int = _size
 
     /**
      * Returns the number of elements matching the given [predicate].
@@ -254,7 +256,7 @@
      * @param predicate Called for all elements in the set to count the number for which it returns
      *   `true`.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(predicate: (element: Int) -> Boolean): Int {
         contract { callsInPlace(predicate) }
         var count = 0
@@ -437,7 +439,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -615,7 +617,7 @@
 
         // TODO: We could just mark the element as empty if there's a group
         //       window around this element that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all elements from this set. */
@@ -670,7 +672,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -701,7 +703,7 @@
      * Returns the number of empty elements removed from this set's storage. Returns 0 if no
      * trimming is necessary or possible.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public fun trim(): Int {
         val previousCapacity = _capacity
         val newCapacity = normalizeCapacity(unloadedCapacity(_size))
@@ -718,22 +720,116 @@
      * place" occurs when the current size is <= 25/32 of the set capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_map`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val elements = elements
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(elements[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                elements[targetIndex] = elements[index]
+                elements[index] = 0
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                elements[swapIndex] = elements[targetIndex]
+                elements[targetIndex] = elements[index]
+                elements[index] = elements[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousElements = elements
         val previousCapacity = _capacity
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newElements = elements
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -741,43 +837,11 @@
                 val hash = hash(previousElement)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newElements[index] = previousElement
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
 
 /**
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongFloatMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongFloatMap.kt
index ba21fb5..249291c 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongFloatMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongFloatMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0L
+
+                values[targetIndex] = values[index]
+                values[index] = 0f
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongIntMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongIntMap.kt
index 9e986ac..2fbfc65 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongIntMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongIntMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0L
+
+                values[targetIndex] = values[index]
+                values[index] = 0
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt
index 8d0497b..5c45ff0 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
@@ -55,17 +56,17 @@
     @Suppress("PropertyName") @JvmField @PublishedApi internal var _size: Int = 0
 
     /** The number of elements in the [LongList]. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
     /** Returns the last valid index in the [LongList]. This can be `-1` when the list is empty. */
-    @get:androidx.annotation.IntRange(from = -1)
+    @get:IntRange(from = -1)
     public inline val lastIndex: Int
         get() = _size - 1
 
     /** Returns an [IntRange] of the valid indices for this [LongList]. */
-    public inline val indices: IntRange
+    public inline val indices: kotlin.ranges.IntRange
         get() = 0 until _size
 
     /** Returns `true` if the collection has no elements in it. */
@@ -282,7 +283,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public operator fun get(@androidx.annotation.IntRange(from = 0) index: Int): Long {
+    public operator fun get(@IntRange(from = 0) index: Int): Long {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -293,7 +294,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if the [index]
      * is out of bounds of this collection.
      */
-    public fun elementAt(@androidx.annotation.IntRange(from = 0) index: Int): Long {
+    public fun elementAt(@IntRange(from = 0) index: Int): Long {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -309,7 +310,7 @@
      *   index not in the list.
      */
     public inline fun elementAtOrElse(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         defaultValue: (index: Int) -> Long
     ): Long {
         if (index !in 0 until _size) {
@@ -528,7 +529,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun add(@androidx.annotation.IntRange(from = 0) index: Int, element: Long) {
+    public fun add(@IntRange(from = 0) index: Int, element: Long) {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -553,10 +554,7 @@
      * @return `true` if the [MutableLongList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive.
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: LongArray
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: LongArray): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -583,10 +581,7 @@
      * @return `true` if the [MutableLongList] was changed or `false` if [elements] was empty
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        elements: LongList
-    ): Boolean {
+    public fun addAll(@IntRange(from = 0) index: Int, elements: LongList): Boolean {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -733,7 +728,7 @@
      *
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public fun removeAt(@androidx.annotation.IntRange(from = 0) index: Int): Long {
+    public fun removeAt(@IntRange(from = 0) index: Int): Long {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -757,10 +752,7 @@
      * @throws IndexOutOfBoundsException if [start] or [end] isn't between 0 and [size], inclusive
      * @throws IllegalArgumentException if [start] is greater than [end]
      */
-    public fun removeRange(
-        @androidx.annotation.IntRange(from = 0) start: Int,
-        @androidx.annotation.IntRange(from = 0) end: Int
-    ) {
+    public fun removeRange(@IntRange(from = 0) start: Int, @IntRange(from = 0) end: Int) {
         if (start !in 0.._size || end !in 0.._size) {
             throw IndexOutOfBoundsException("Start ($start) and end ($end) must be in 0..$_size")
         }
@@ -820,10 +812,7 @@
      * @return the previous value set at [index]
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public operator fun set(
-        @androidx.annotation.IntRange(from = 0) index: Int,
-        element: Long
-    ): Long {
+    public operator fun set(@IntRange(from = 0) index: Int, element: Long): Long {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("set index $index must be between 0 .. $lastIndex")
         }
@@ -835,11 +824,15 @@
 
     /** Sorts the [MutableLongList] elements in ascending order. */
     public fun sort() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sort(fromIndex = 0, toIndex = _size)
     }
 
     /** Sorts the [MutableLongList] elements in descending order. */
     public fun sortDescending() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sortDescending(fromIndex = 0, toIndex = _size)
     }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongLongMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongLongMap.kt
index 52ad2ef..f0437fa 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongLongMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongLongMap.kt
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -597,7 +598,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -763,7 +764,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all mappings from this map. */
@@ -818,7 +819,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -866,15 +867,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0L
+
+                values[targetIndex] = values[index]
+                values[index] = 0L
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -882,8 +983,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -891,42 +994,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongObjectMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongObjectMap.kt
index aed8484..93b5b7c 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongObjectMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongObjectMap.kt
@@ -19,6 +19,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -595,7 +596,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -741,7 +742,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         val oldValue = values[index]
         values[index] = null
 
@@ -801,7 +802,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -849,15 +850,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0L
+
+                values[targetIndex] = values[index]
+                values[index] = null
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -865,8 +966,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -874,42 +977,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongSet.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongSet.kt
index cdf0b3e..b633354 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongSet.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongSet.kt
@@ -26,6 +26,8 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
+import androidx.collection.internal.requirePrecondition
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
@@ -127,7 +129,7 @@
      * Returns the number of elements that can be stored in this set without requiring internal
      * storage reallocation.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val capacity: Int
         get() = _capacity
 
@@ -136,7 +138,7 @@
     @JvmField internal var _size: Int = 0
 
     /** Returns the number of elements in this set. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
@@ -247,7 +249,7 @@
     }
 
     /** Returns the number of elements in this set. */
-    @androidx.annotation.IntRange(from = 0) public fun count(): Int = _size
+    @IntRange(from = 0) public fun count(): Int = _size
 
     /**
      * Returns the number of elements matching the given [predicate].
@@ -255,7 +257,7 @@
      * @param predicate Called for all elements in the set to count the number for which it returns
      *   `true`.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(predicate: (element: Long) -> Boolean): Int {
         contract { callsInPlace(predicate) }
         var count = 0
@@ -438,7 +440,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -616,7 +618,7 @@
 
         // TODO: We could just mark the element as empty if there's a group
         //       window around this element that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /** Removes all elements from this set. */
@@ -671,7 +673,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -702,7 +704,7 @@
      * Returns the number of empty elements removed from this set's storage. Returns 0 if no
      * trimming is necessary or possible.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public fun trim(): Int {
         val previousCapacity = _capacity
         val newCapacity = normalizeCapacity(unloadedCapacity(_size))
@@ -719,22 +721,116 @@
      * place" occurs when the current size is <= 25/32 of the set capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_map`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val elements = elements
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(elements[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                elements[targetIndex] = elements[index]
+                elements[index] = 0L
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                elements[swapIndex] = elements[targetIndex]
+                elements[targetIndex] = elements[index]
+                elements[index] = elements[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousElements = elements
         val previousCapacity = _capacity
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newElements = elements
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -742,43 +838,11 @@
                 val hash = hash(previousElement)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newElements[index] = previousElement
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
 
 /**
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongSparseArray.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongSparseArray.kt
index d08b354..732c229 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongSparseArray.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongSparseArray.kt
@@ -18,6 +18,7 @@
 
 import androidx.collection.internal.binarySearch
 import androidx.collection.internal.idealLongArraySize
+import androidx.collection.internal.requirePrecondition
 import kotlin.DeprecationLevel.HIDDEN
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
@@ -399,7 +400,9 @@
 
 @Suppress("NOTHING_TO_INLINE")
 internal inline fun <E> LongSparseArray<E>.commonKeyAt(index: Int): Long {
-    require(index in 0 until size) { "Expected index to be within 0..size()-1, but was $index" }
+    requirePrecondition(index in 0 until size) {
+        "Expected index to be within 0..size()-1, but was $index"
+    }
 
     if (garbage) {
         commonGc()
@@ -409,7 +412,9 @@
 
 @Suppress("NOTHING_TO_INLINE")
 internal inline fun <E> LongSparseArray<E>.commonValueAt(index: Int): E {
-    require(index in 0 until size) { "Expected index to be within 0..size()-1, but was $index" }
+    requirePrecondition(index in 0 until size) {
+        "Expected index to be within 0..size()-1, but was $index"
+    }
 
     if (garbage) {
         commonGc()
@@ -420,7 +425,9 @@
 
 @Suppress("NOTHING_TO_INLINE")
 internal inline fun <E> LongSparseArray<E>.commonSetValueAt(index: Int, value: E) {
-    require(index in 0 until size) { "Expected index to be within 0..size()-1, but was $index" }
+    requirePrecondition(index in 0 until size) {
+        "Expected index to be within 0..size()-1, but was $index"
+    }
 
     if (garbage) {
         commonGc()
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt
index c2b3c02..19eb5b2 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LruCache.kt
@@ -19,14 +19,37 @@
 import androidx.annotation.IntRange
 import androidx.collection.internal.Lock
 import androidx.collection.internal.LruHashMap
+import androidx.collection.internal.checkPrecondition
+import androidx.collection.internal.requirePrecondition
 import androidx.collection.internal.synchronized
-import kotlin.Long.Companion.MAX_VALUE
+
+private const val MAX_SIZE = Int.MAX_VALUE.toLong()
 
 /**
- * Static library version of `android.util.LruCache`. Used to write apps that run on API levels
- * prior to 12. When running on API level 12 or above, this implementation is still used; it does
- * not try to switch to the framework's implementation. See the framework SDK documentation for a
- * class overview.
+ * A cache that holds strong references to a limited number of values. Each time a value is
+ * accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at
+ * the end of that queue is evicted and may become eligible for garbage collection.
+ *
+ * If your cached values hold resources that need to be explicitly released, override
+ * [entryRemoved].
+ *
+ * If a cache miss should be computed on demand for the corresponding keys, override [create]. This
+ * simplifies the calling code, allowing it to assume a value will always be returned, even when
+ * there's a cache miss.
+ *
+ * By default, the cache size is measured in the number of entries. Override [sizeOf] to size the
+ * cache in different units. For example, this cache is limited to 4MiB of bitmaps:
+ * ```
+ * val cacheSize = 4 * 1024 * 1024; // 4MiB
+ * val bitmapCache = LruCache<String, Bitmap>(cacheSize) {
+ *     override fun sizeOf(key: String, value: Bitmap) = value.byteCount
+ * }
+ * ```
+ *
+ * This class is thread-safe.
+ *
+ * This class does not allow null to be used as a key or value. A return value of null from [get],
+ * [put] or [remove] is unambiguous: the key was not in the cache.
  *
  * @param maxSize for caches that do not override [sizeOf], this is the maximum number of entries in
  *   the cache. For all other caches, this is the maximum sum of the sizes of the entries in this
@@ -34,10 +57,9 @@
  * @constructor Creates a new [LruCache]
  */
 public open class LruCache<K : Any, V : Any>
-public constructor(@IntRange(from = 1, to = MAX_VALUE) private var maxSize: Int) {
-
+public constructor(@IntRange(from = 1, to = MAX_SIZE) private var maxSize: Int) {
     init {
-        require(maxSize > 0) { "maxSize <= 0" }
+        requirePrecondition(maxSize > 0) { "maxSize <= 0" }
     }
 
     private val map = LruHashMap<K, V>(0, 0.75f)
@@ -57,8 +79,8 @@
      *
      * @param maxSize The new maximum size.
      */
-    public open fun resize(@IntRange(from = 1, to = MAX_VALUE) maxSize: Int) {
-        require(maxSize > 0) { "maxSize <= 0" }
+    public open fun resize(@IntRange(from = 1, to = MAX_SIZE) maxSize: Int) {
+        requirePrecondition(maxSize > 0) { "maxSize <= 0" }
 
         lock.synchronized { this.maxSize = maxSize }
         trimToSize(maxSize)
@@ -145,7 +167,7 @@
             var value: V
 
             lock.synchronized {
-                check(!(size < 0 || (map.isEmpty && size != 0))) {
+                checkPrecondition(!(size < 0 || (map.isEmpty && size != 0))) {
                     ("LruCache.sizeOf() is reporting inconsistent results!")
                 }
 
@@ -220,7 +242,7 @@
 
     private fun safeSizeOf(key: K, value: V): Int {
         val result = sizeOf(key, value)
-        check(result >= 0) { "Negative size: $key=$value" }
+        checkPrecondition(result >= 0) { "Negative size: $key=$value" }
         return result
     }
 
@@ -273,9 +295,11 @@
     public fun snapshot(): MutableMap<K, V> {
         // order and mutability is important for backwards compatibility so we intentionally use
         // a LinkedHashMap here.
-        val copy = LinkedHashMap<K, V>()
-        lock.synchronized { map.entries.forEach { (key, value) -> copy[key] = value } }
-        return copy
+        lock.synchronized {
+            val copy = LinkedHashMap<K, V>(map.entries.size)
+            map.entries.forEach { (key, value) -> copy[key] = value }
+            return copy
+        }
     }
 
     override fun toString(): String {
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectFloatMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectFloatMap.kt
index 2928542..22a67ae 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectFloatMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectFloatMap.kt
@@ -19,6 +19,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -603,7 +604,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -777,7 +778,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         keys[index] = null
     }
 
@@ -834,7 +835,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -882,15 +883,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = null
+
+                values[targetIndex] = values[index]
+                values[index] = 0f
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -898,8 +999,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -907,42 +1010,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectIntMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectIntMap.kt
index 4d416d88..34831d4 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectIntMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectIntMap.kt
@@ -19,6 +19,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -603,7 +604,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -777,7 +778,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         keys[index] = null
     }
 
@@ -834,7 +835,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -882,15 +883,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = null
+
+                values[targetIndex] = values[index]
+                values[index] = 0
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -898,8 +999,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -907,42 +1010,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectLongMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectLongMap.kt
index 9fe0b65..ca70012 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectLongMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/ObjectLongMap.kt
@@ -19,6 +19,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -603,7 +604,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -777,7 +778,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         keys[index] = null
     }
 
@@ -834,7 +835,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -882,15 +883,115 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = null
+
+                values[targetIndex] = values[index]
+                values[index] = 0L
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -898,8 +999,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -907,42 +1010,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterMap.kt
index 023e31a..b14e44a 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterMap.kt
@@ -27,6 +27,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 import kotlin.math.max
@@ -158,7 +159,7 @@
 
 // Indicates that all the slot in a [Group] are empty
 // 0x8080808080808080UL, see explanation in [BitmaskMsb]
-internal const val AllEmpty = -0x7f7f7f7f7f7f7f80L
+internal const val AllEmpty = -0x7f7f7f7f_7f7f7f80L
 
 internal const val Empty = 0b10000000L
 internal const val Deleted = 0b11111110L
@@ -175,7 +176,7 @@
 internal val EmptyGroup =
     longArrayOf(
         // NOTE: the first byte in the array's logical order is in the LSB
-        -0x7f7f7f7f7f7f7f01L, // Sentinel, Empty, Empty... or 0x80808080808080FFUL
+        -0x7f7f7f7f_7f7f7f01L, // Sentinel, Empty, Empty... or 0xFF80808080808080UL
         -1L // 0xFFFFFFFFFFFFFFFFUL
     )
 
@@ -337,7 +338,7 @@
                 // 0 when i < lastIndex, 1 otherwise.
                 val bitCount = 8 - ((i - lastIndex).inv() ushr 31)
                 for (j in 0 until bitCount) {
-                    if (isFull(slot and 0xFFL)) {
+                    if (isFull(slot and 0xffL)) {
                         val index = (i shl 3) + j
                         block(index)
                     }
@@ -718,7 +719,7 @@
  * automatically shrinks its storage to avoid using more memory than necessary. You can also control
  * memory usage with [MutableScatterMap] by manually calling [MutableScatterMap.trim].
  *
- * @param initialCapacity The initial desired capacity for this container. the container will honor
+ * @param initialCapacity The initial desired capacity for this container. The container will honor
  *   this value by guaranteeing its internal structures can hold that many entries without requiring
  *   any allocations. The initial capacity can be set to 0.
  * @constructor Creates a new [MutableScatterMap]
@@ -730,7 +731,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -983,7 +984,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         keys[index] = null
         val oldValue = values[index]
         values[index] = null
@@ -1046,7 +1047,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -1094,15 +1095,114 @@
      * place" occurs when the current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            resizeStorage(_capacity)
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] = metadata[0]
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = null
+
+                values[targetIndex] = values[index]
+                values[index] = null
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = metadata[0]
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -1110,8 +1210,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -1119,45 +1221,13 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
 
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
-
     /**
      * Wraps this [ScatterMap] with a [MutableMap] interface. The [MutableMap] is backed by the
      * [ScatterMap], so changes to the [ScatterMap] are reflected in the [MutableMap] and
@@ -1467,6 +1537,31 @@
     }
 }
 
+internal inline fun convertMetadataForCleanup(metadata: LongArray, capacity: Int) {
+    val end = (capacity + 7) shr 3
+    for (i in 0 until end) {
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        val maskedGroup = metadata[i] and BitmaskMsb
+        metadata[i] = (maskedGroup.inv() + (maskedGroup ushr 7)) and BitmaskLsb.inv()
+    }
+
+    val lastIndex = metadata.lastIndex
+    // Restores the sentinel that we overwrote above
+    metadata[lastIndex - 1] =
+        (Sentinel shl 56) or (metadata[lastIndex - 1] and 0x00ffffff_ffffffffL)
+    // Copies the metadata into the clone area
+    metadata[lastIndex] = metadata[0]
+}
+
+internal fun findEmptySlot(metadata: LongArray, start: Int, end: Int): Int {
+    for (i in start until end) {
+        if (readRawMetadata(metadata, i) == Empty) {
+            return i
+        }
+    }
+    return -1
+}
+
 /**
  * Returns the hash code of [k]. The hash spreads low bits to to minimize collisions in high 25-bits
  * that are used for probing.
@@ -1487,7 +1582,7 @@
 
 // Returns the "H2" part of the specified hash code. In our implementation,
 // this corresponds to the lower 7 bits
-internal inline fun h2(hash: Int) = hash and 0x7F
+internal inline fun h2(hash: Int) = hash and 0x7f
 
 // Assumes [capacity] was normalized with [normalizedCapacity].
 // Returns the next 2^m - 1
@@ -1500,7 +1595,7 @@
 
 // n -> nearest 2^m - 1
 internal fun normalizeCapacity(n: Int) =
-    if (n > 0) (0xFFFFFFFF.toInt() ushr n.countLeadingZeroBits()) else 0
+    if (n > 0) (0xffffffff.toInt() ushr n.countLeadingZeroBits()) else 0
 
 // Computes the growth based on a load factor of 7/8 for the general case.
 // When capacity is < GroupWidth - 1, we use a load factor of 1 instead
@@ -1528,12 +1623,36 @@
 internal inline fun readRawMetadata(data: LongArray, offset: Int): Long {
     // Take the Long at index `offset / 8` and shift by `offset % 8`
     // A longer explanation can be found in [group()].
-    return (data[offset shr 3] shr ((offset and 0x7) shl 3)) and 0xFF
+    return (data[offset shr 3] shr ((offset and 0x7) shl 3)) and 0xff
 }
 
 /**
- * Writes a single byte into the long array at the specified [offset] in *bytes*. NOTE: [value] must
- * be a single byte, accepted here as a Long to avoid unnecessary conversions.
+ * Writes a single byte into the long array at the specified [offset] in *bytes* and copies it, if
+ * necessary, into the cloned bytes section at the end of the array.
+ *
+ * NOTE: [value] must be a single byte, accepted here as a Long to avoid unnecessary conversions.
+ */
+internal inline fun writeMetadata(data: LongArray, capacity: Int, offset: Int, value: Long) {
+    writeRawMetadata(data, offset, value)
+
+    // Mirroring
+    // We could/should write a single byte when cloning the metadata by calling
+    // writeRawMetadata(), but since our implementation uses Longs for storage,
+    // we always write a full Long. We can skip a bit of unnecessary work by just
+    // copying the whole group the index falls into.
+    // When index is in 0..7, we copy the group over the control bytes at the end of
+    // the array, otherwise the group is copied onto itself (cloneIndex shr 3 == index shr 3)
+    // TODO: We could further reduce the work we do by always copying index 0 to
+    //       lastIndex, but is it interesting in terms of data caches?
+    val cloneIndex =
+        ((offset - ClonedMetadataCount) and capacity) + (ClonedMetadataCount and capacity)
+    data[cloneIndex shr 3] = data[offset shr 3]
+}
+
+/**
+ * Writes a single byte into the long array at the specified [offset] in *bytes*.
+ *
+ * NOTE: [value] must be a single byte, accepted here as a Long to avoid unnecessary conversions.
  */
 internal inline fun writeRawMetadata(data: LongArray, offset: Int, value: Long) {
     // See [group()] for details. First find the index i in the LongArray,
@@ -1542,7 +1661,7 @@
     val b = (offset and 0x7) shl 3
     // Mask the source data with 0xFF in the right place, then and [value]
     // moved to the right spot
-    data[i] = (data[i] and (0xFFL shl b).inv()) or (value shl b)
+    data[i] = (data[i] and (0xffL shl b).inv()) or (value shl b)
 }
 
 internal inline fun isEmpty(metadata: LongArray, index: Int) =
@@ -1599,7 +1718,7 @@
 internal inline fun Bitmask.hasNext() = this != 0L
 
 // Least significant bits in the bitmask, one for each metadata in the group
-@PublishedApi internal const val BitmaskLsb: Long = 0x0101010101010101L
+@PublishedApi internal const val BitmaskLsb: Long = 0x01010101_01010101L
 
 // Most significant bits in the bitmask, one for each metadata in the group
 //
@@ -1608,7 +1727,7 @@
 // a Long. And since Kotlin hates signed constants, we have to use
 // -0x7f7f7f7f7f7f7f80L instead of the more sensible 0x8080808080808080L (and
 // 0x8080808080808080UL.toLong() isn't considered a constant)
-@PublishedApi internal const val BitmaskMsb: Long = -0x7f7f7f7f7f7f7f80L // srsly Kotlin @#!
+@PublishedApi internal const val BitmaskMsb: Long = -0x7f7f7f7f_7f7f7f80L // srsly Kotlin @#!
 
 /**
  * Creates a [Group] from a metadata array, starting at the specified offset. [offset] must be a
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterSet.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterSet.kt
index c393eef..c3eb566 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterSet.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/ScatterSet.kt
@@ -26,7 +26,9 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
@@ -125,7 +127,7 @@
      * Returns the number of elements that can be stored in this set without requiring internal
      * storage reallocation.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val capacity: Int
         get() = _capacity
 
@@ -134,7 +136,7 @@
     @JvmField internal var _size: Int = 0
 
     /** Returns the number of elements in this set. */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
@@ -255,7 +257,7 @@
     }
 
     /** Returns the number of elements in this set. */
-    @androidx.annotation.IntRange(from = 0) public fun count(): Int = size
+    @IntRange(from = 0) public fun count(): Int = size
 
     /**
      * Returns the number of elements matching the given [predicate].
@@ -263,7 +265,7 @@
      * @param predicate Called for all elements in the set to count the number for which it returns
      *   `true`.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(predicate: (element: E) -> Boolean): Int {
         contract { callsInPlace(predicate) }
         var count = 0
@@ -475,7 +477,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -794,7 +796,7 @@
 
         // TODO: We could just mark the element as empty if there's a group
         //       window around this element that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         elements[index] = null
     }
 
@@ -851,7 +853,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -882,7 +884,7 @@
      * Returns the number of empty elements removed from this set's storage. Returns 0 if no
      * trimming is necessary or possible.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public fun trim(): Int {
         val previousCapacity = _capacity
         val newCapacity = normalizeCapacity(unloadedCapacity(_size))
@@ -899,22 +901,116 @@
      * place" occurs when the current size is <= 25/32 of the set capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_map`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val elements = elements
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(elements[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                elements[targetIndex] = elements[index]
+                elements[index] = null
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                elements[swapIndex] = elements[targetIndex]
+                elements[targetIndex] = elements[index]
+                elements[index] = elements[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] = (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousElements = elements
         val previousCapacity = _capacity
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newElements = elements
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -922,44 +1018,12 @@
                 val hash = hash(previousElement)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newElements[index] = previousElement
             }
         }
     }
 
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified [index]. The index
-     * must be a valid index. This function ensures the metadata is also written in the clone area
-     * at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) + (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
-
     /**
      * Wraps this [ScatterSet] with a [MutableSet] interface. The [MutableSet] is backed by the
      * [ScatterSet], so changes to the [ScatterSet] are reflected in the [MutableSet] and
@@ -987,13 +1051,12 @@
         override fun iterator(): MutableIterator<E> =
             object : MutableIterator<E> {
                 var current = -1
-                val iterator =
-                    iterator<E> {
-                        [email protected] { index ->
-                            current = index
-                            @Suppress("UNCHECKED_CAST") yield(elements[index] as E)
-                        }
+                val iterator = iterator {
+                    [email protected] { index ->
+                        current = index
+                        @Suppress("UNCHECKED_CAST") yield(elements[index] as E)
                     }
+                }
 
                 override fun hasNext(): Boolean = iterator.hasNext()
 
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/SieveCache.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/SieveCache.kt
new file mode 100644
index 0000000..7fe3443
--- /dev/null
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/SieveCache.kt
@@ -0,0 +1,1039 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("KotlinRedundantDiagnosticSuppress", "NOTHING_TO_INLINE")
+
+package androidx.collection
+
+import androidx.annotation.IntRange
+import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
+import kotlin.jvm.JvmField
+import kotlin.math.max
+
+private const val MaxSize = Int.MAX_VALUE.toLong() - 1
+
+private const val NodeInvalidLink = 0x7fff_ffff
+private const val NodeLinkMask = 0x7fff_ffffL
+private const val NodeLinksMask = 0x3fffffff_ffffffffL
+private const val NodeVisitedBit = 0x40000000_00000000L
+private const val NodeMetaMask = -0x40000000_00000000L // 0xc0000000_00000000UL.toLong()
+private const val NodeMetaAndNextMask = -0x3fffffff_80000001L // 0xc0000000_7fffffffUL.toLong()
+private const val NodeMetaAndPreviousMask = -0x00000000_80000000L // 0xffffffff_80000000UL.toLong()
+
+private const val EmptyNode = 0x3fffffff_ffffffffL
+private val EmptyNodes = LongArray(0)
+
+/**
+ * [SieveCache] is an in-memory cache that holds strong references to a limited number of values
+ * determined by the cache's [maxSize] and the size of each value. When a value is added to a full
+ * cache, one or more existing values are evicted from the cache using the
+ * [SIEVE algorithm](https://cachemon.github.io/SIEVE-website/). Complete details about the
+ * algorithm can be found in Zhang et al., 2024, SIEVE is Simpler than LRU: an Efficient Turn-Key
+ * Eviction Algorithm for Web Caches, NSDI'24
+ * ([paper](https://www.usenix.org/system/files/nsdi24-zhang-yazhuo.pdf)).
+ *
+ * Contrary to [LruCache], [SieveCache] does not maintain a list of entries based on their access
+ * order, but on their insertion order. Eviction candidates are found by keeping track of the
+ * "visited" status of each entry. This means that reading a value using [get] prevents that entry
+ * from becoming an eviction candidate. In practice, [SieveCache] offers better hit ratio compared
+ * to [LruCache].
+ *
+ * The underlying implementation is also designed to avoid all allocations on insertion, removal,
+ * retrieval, and iteration. Allocations may still happen on insertion when the underlying storage
+ * needs to grow to accommodate newly added entries to the table. In addition, this implementation
+ * minimizes memory usage by avoiding the use of separate objects to hold key/value pairs. The
+ * implementation follows the implementation of [ScatterMap].
+ *
+ * By default, the size of the cache is measured in number of entries. The caller can choose the
+ * size and size unit of the values by passing their own [sizeOf] lambda, invoked whenever the cache
+ * needs to query the size of a value.
+ *
+ * The [createValueFromKey] lambda can be used to compute values on demand from a key when querying
+ * for an entry that does not exist in the cache.
+ *
+ * When a cached value is removed, either directly by the caller or via the eviction mechanism, you
+ * can use the [onEntryRemoved] lambda to execute any side effect or perform any necessary cleanup.
+ *
+ * This implementation is not thread-safe: if multiple threads access this container concurrently,
+ * and one or more threads modify the structure of the map (insertion or removal for instance), the
+ * calling code must provide the appropriate synchronization. Multiple threads are safe to read from
+ * this map concurrently if no write is happening.
+ *
+ * A [SieveCache] can hold a maximum of `Int.MAX_VALUE - 1` entries, independent of their computed
+ * size.
+ *
+ * @param maxSize For caches that do not override [sizeOf], this is the maximum number of entries in
+ *   the cache. For all other caches, this is the maximum sum of the sizes of the entries in this
+ *   cache. The maximum size must be strictly greater than 0 and must be less than or equal to
+ *   `Int.MAX_VALUE - 1`.
+ * @param initialCapacity The initial desired capacity for this cache. The cache will honor this
+ *   value by guaranteeing its internal structures can hold that many entries without requiring any
+ *   allocations. The initial capacity can be set to 0.
+ * @param sizeOf Returns the size of the entry for the specified key and value. The size of an entry
+ *   cannot change after it was added to the cache, and must be >= 0.
+ * @param createValueFromKey Called after a cache miss to compute a value for the specified key.
+ *   Returning null from this lambda indicates that no value can be computed.
+ * @param onEntryRemoved Called for entries that have been removed by the user of the cache, or
+ *   automatically evicted. The lambda is supplied with multiple parameters. The `key` of the entry
+ *   being removed or evicted. The original value (`oldValue`) of the entry if the entry is being
+ *   evicted or replaced. The new value (`newValue`) for the key, if it exists. If non-null, the
+ *   removal was caused by a `put()` or a `set()`, otherwise it was caused by an eviction or a
+ *   removal. A boolean (`evicted`) set to `true` if the entry was evicted to make space in the
+ *   cache, or set to `false` if the removal happened on demand or while replacing an existing value
+ *   with [put].
+ * @constructor Creates a new [SieveCache].
+ */
+public class SieveCache<K : Any, V : Any>
+public constructor(
+    @IntRange(from = 1, to = MaxSize) maxSize: Int,
+    @IntRange(from = 0, to = MaxSize) initialCapacity: Int = DefaultScatterCapacity,
+    private val sizeOf: (key: K, value: V) -> Int = { _, _ -> 1 },
+    private val createValueFromKey: (key: K) -> V? = { null },
+    private val onEntryRemoved: (key: K, oldValue: V, newValue: V?, evicted: Boolean) -> Unit =
+        { _, _, _, _ ->
+        }
+) {
+    @PublishedApi @JvmField internal var metadata: LongArray = EmptyGroup
+    @PublishedApi @JvmField internal var keys: Array<Any?> = EMPTY_OBJECTS
+    @PublishedApi @JvmField internal var values: Array<Any?> = EMPTY_OBJECTS
+    private var nodes: LongArray = EmptyNodes
+
+    private var _capacity: Int = 0
+    private var growthLimit = 0
+    private var _count: Int = 0
+
+    private var _maxSize: Int = 0
+    private var _size: Int = 0
+
+    private var head = NodeInvalidLink
+    private var tail = NodeInvalidLink
+    private var hand = NodeInvalidLink
+
+    init {
+        requirePrecondition(maxSize > 0) { "maxSize must be > 0" }
+        _maxSize = maxSize
+        initializeStorage(unloadedCapacity(initialCapacity))
+    }
+
+    /**
+     * Size of the cache in the unit defined by the implementation of [sizeOf] (by default, the
+     * number of elements).
+     *
+     * @see maxSize
+     */
+    public val size: Int
+        get() = _size
+
+    /**
+     * Return the maximum size of the cache before adding new elements causes existing elements to
+     * be evicted. The unit of [maxSize] is defined by the implementation of [sizeOf]. Using the
+     * default implementation of [sizeOf], [maxSize] indicates the a maximum number of elements.
+     *
+     * @see size
+     */
+    public val maxSize: Int
+        get() = _maxSize
+
+    /**
+     * Returns the number of elements held in the cache.
+     *
+     * @see capacity
+     */
+    public val count: Int
+        get() = _count
+
+    /**
+     * Returns the number of entries that can be stored in this cache without requiring internal
+     * storage reallocation.
+     *
+     * @see count
+     */
+    public val capacity: Int
+        get() = _capacity
+
+    /** Returns `true` if this cache has at least one entry. */
+    public fun any(): Boolean = _count != 0
+
+    /** Returns `true` if this cache has no entries. */
+    public fun none(): Boolean = _count == 0
+
+    /** Indicates whether this cache is empty. */
+    public fun isEmpty(): Boolean = _count == 0
+
+    /** Returns `true` if this cache is not empty. */
+    public fun isNotEmpty(): Boolean = _count != 0
+
+    private fun initializeStorage(initialCapacity: Int) {
+        val newCapacity =
+            if (initialCapacity > 0) {
+                // Since we use longs for storage, our capacity is never < 7, enforce
+                // it here. We do have a special case for 0 to create small empty maps
+                max(7, normalizeCapacity(initialCapacity))
+            } else {
+                0
+            }
+        _capacity = newCapacity
+        initializeMetadata(newCapacity)
+        keys = if (newCapacity == 0) EMPTY_OBJECTS else arrayOfNulls(newCapacity)
+        values = if (newCapacity == 0) EMPTY_OBJECTS else arrayOfNulls(newCapacity)
+        nodes =
+            if (newCapacity == 0) EmptyNodes else LongArray(newCapacity).apply { fill(EmptyNode) }
+    }
+
+    private fun initializeMetadata(capacity: Int) {
+        metadata =
+            if (capacity == 0) {
+                EmptyGroup
+            } else {
+                // Round up to the next multiple of 8 and find how many longs we need
+                val size = (((capacity + 1 + ClonedMetadataCount) + 7) and 0x7.inv()) shr 3
+                LongArray(size).apply {
+                    fill(AllEmpty)
+                    writeRawMetadata(this, capacity, Sentinel)
+                }
+            }
+        initializeGrowth()
+    }
+
+    private fun initializeGrowth() {
+        growthLimit = loadedCapacity(_capacity) - count
+    }
+
+    /**
+     * Returns the value for [key] if it exists in the cache or can be created by
+     * [createValueFromKey]. Return null if a value is not present in the cache and cannot be
+     * created.
+     */
+    public operator fun get(key: K): V? {
+        val index = findKeyIndex(key)
+        if (index >= 0) {
+            markNodeVisited(index)
+            @Suppress("UNCHECKED_CAST") return values[index] as V?
+        }
+
+        val createdValue = createValueFromKey(key) ?: return null
+        put(key, createdValue)
+
+        return createdValue
+    }
+
+    /**
+     * Adds [value] to the cache using the specific [key]. If [key] is already present in the cache,
+     * the association is modified and the previously associated value is replaced with [value]. If
+     * [key] is not present, a new entry is added to the map. If an existing value is replaced,
+     * [onEntryRemoved] will be invoked with the `evicted` parameter set to `false`.
+     *
+     * When [value] is added to the cache, [sizeOf] is invoked to query its size. If the total size
+     * of the cache, including the new value's size, is greater than [maxSize], existing entries
+     * will be evicted. On each removal due to an eviction, [onEntryRemoved] will be invoked with
+     * the `evicted` parameter set to `true`.
+     */
+    public inline operator fun set(key: K, value: V) {
+        put(key, value)
+    }
+
+    /**
+     * Adds [value] to the cache using the specific [key]. If [key] is already present in the map,
+     * the association is modified and the previously associated value is replaced with [value]. If
+     * [key] is not present, a new entry is added to the map. If an existing value is replaced,
+     * [onEntryRemoved] will be invoked with the `evicted` parameter set to `false`.
+     *
+     * When [value] is added to the cache, [sizeOf] is invoked to query its size. If the total size
+     * of the cache, including the new value's size, is greater than [maxSize], existing entries
+     * will be evicted. On each removal due to an eviction, [onEntryRemoved] will be invoked with
+     * the `evicted` parameter set to `true`.
+     *
+     * Return the previous value associated with the [key], or `null` if the key was not present in
+     * the cache.
+     */
+    public fun put(key: K, value: V): V? {
+        val index = findInsertIndex(key).let { index -> if (index < 0) index.inv() else index }
+        @Suppress("UNCHECKED_CAST") val previousValue = values[index] as V?
+
+        values[index] = value
+        keys[index] = key
+
+        moveNodeToHead(index)
+
+        _size += sizeOf(key, value)
+
+        if (previousValue != null) {
+            _size -= sizeOf(key, previousValue)
+            onEntryRemoved(key, previousValue, value, false)
+        }
+
+        // TODO: We should trim to size before doing the insertion. The insertion might cause
+        //       the underlying storage to resize unnecessarily.
+        trimToSize(_maxSize)
+
+        return previousValue
+    }
+
+    /**
+     * Puts all the [pairs] into this cache, using the first component of the pair as the key, and
+     * the second component as the value. Calling this method is equivalent to calling [put] for
+     * each input pair. See [put] for more details about the behavior of each insertion.
+     */
+    public fun putAll(@Suppress("ArrayReturn") pairs: Array<out Pair<K, V>>) {
+        for ((key, value) in pairs) {
+            this[key] = value
+        }
+    }
+
+    /**
+     * Puts all the [pairs] into this cache, using the first component of the pair as the key, and
+     * the second component as the value. Calling this method is equivalent to calling [put] for
+     * each input pair. See [put] for more details about the behavior of each insertion.
+     */
+    public fun putAll(pairs: Iterable<Pair<K, V>>) {
+        for ((key, value) in pairs) {
+            this[key] = value
+        }
+    }
+
+    /**
+     * Puts all the [pairs] into this cache, using the first component of the pair as the key, and
+     * the second component as the value. Calling this method is equivalent to calling [put] for
+     * each input pair. See [put] for more details about the behavior of each insertion.
+     */
+    public fun putAll(pairs: Sequence<Pair<K, V>>) {
+        for ((key, value) in pairs) {
+            this[key] = value
+        }
+    }
+
+    /**
+     * Puts all the key/value mappings in the [from] map into this cache. Calling this method is
+     * equivalent to calling [put] for each input pair. See [put] for more details about the
+     * behavior of each insertion.
+     */
+    public fun putAll(from: Map<K, V>) {
+        from.forEach { (key, value) -> this[key] = value }
+    }
+
+    /**
+     * Puts all the key/value mappings in the [from] map into this cache. Calling this method is
+     * equivalent to calling [put] for each input pair. See [put] for more details about the
+     * behavior of each insertion.
+     */
+    public fun putAll(from: ScatterMap<K, V>) {
+        from.forEach { key, value -> this[key] = value }
+    }
+
+    /**
+     * Puts all the key/value mappings in the [from] cache into this cache. Calling this method is
+     * equivalent to calling [put] for each input pair. See [put] for more details about the
+     * behavior of each insertion.
+     */
+    public fun putAll(from: SieveCache<K, V>) {
+        from.forEach { key, value -> this[key] = value }
+    }
+
+    /**
+     * Puts the key/value mapping from the [pair] in this cache, using the first element as the key,
+     * and the second element as the value. See [put] for more details about the insertion behavior.
+     */
+    public inline operator fun plusAssign(pair: Pair<K, V>) {
+        this[pair.first] = pair.second
+    }
+
+    /**
+     * Puts all the [pairs] into this map, using the first component of the pair as the key, and the
+     * second component as the value. Calling this * method is equivalent to calling [put] for each
+     * input pair. See [put] for more details about the behavior of each insertion.
+     */
+    public inline operator fun plusAssign(
+        @Suppress("ArrayReturn") pairs: Array<out Pair<K, V>>
+    ): Unit = putAll(pairs)
+
+    /**
+     * Puts all the [pairs] into this map, using the first component of the pair as the key, and the
+     * second component as the value. Calling this * method is equivalent to calling [put] for each
+     * input pair. See [put] for more details about the behavior of each insertion.
+     */
+    public inline operator fun plusAssign(pairs: Iterable<Pair<K, V>>): Unit = putAll(pairs)
+
+    /**
+     * Puts all the [pairs] into this map, using the first component of the pair as the key, and the
+     * second component as the value. Calling this * method is equivalent to calling [put] for each
+     * input pair. See [put] for more details about the behavior of each insertion.
+     */
+    public inline operator fun plusAssign(pairs: Sequence<Pair<K, V>>): Unit = putAll(pairs)
+
+    /**
+     * Puts all the key/value mappings in the [from] map into this map. Calling this method is
+     * equivalent to calling [put] for each input pair. See [put] for more details about the
+     * behavior of each insertion.
+     */
+    public inline operator fun plusAssign(from: Map<K, V>): Unit = putAll(from)
+
+    /**
+     * Puts all the key/value mappings in the [from] map into this map. Calling this method is
+     * equivalent to calling [put] for each input pair. See [put] for more details about the
+     * behavior of each insertion.
+     */
+    public inline operator fun plusAssign(from: ScatterMap<K, V>): Unit = putAll(from)
+
+    /**
+     * Puts all the key/value mappings in the [from] map into this map. Calling this method is
+     * equivalent to calling [put] for each input pair. See [put] for more details about the
+     * behavior of each insertion.
+     */
+    public inline operator fun plusAssign(from: SieveCache<K, V>): Unit = putAll(from)
+
+    /**
+     * Removes the specified [key] and its associated value from the cache. If the [key] was present
+     * in the cache, this function returns the value that was present before removal, otherwise it
+     * returns `null`. On successful removal, [sizeOf] will be invoked to query the size of the
+     * removed element, and [onEntryRemoved] will be invoked with the `evicted` parameter set to
+     * `false`.
+     */
+    public fun remove(key: K): V? {
+        val index = findKeyIndex(key)
+        if (index >= 0) {
+            // Better codegen, and can only happen if the data structure is internally inconsistent
+            val previousValue = removeValueAt(index) ?: return null
+            _size -= sizeOf(key, previousValue)
+            onEntryRemoved(key, previousValue, null, false)
+            return previousValue
+        }
+
+        return null
+    }
+
+    /**
+     * Removes the specified [key] and its associated value from the cache if the associated value
+     * equals [value]. If the [key] was present in the cache, this function returns true, otherwise
+     * it returns false. On successful removal, [sizeOf] will be invoked to query the size of the
+     * removed element, and [onEntryRemoved] will be invoked with the `evicted` parameter set to
+     * `false`.
+     */
+    public fun remove(key: K, value: V): Boolean {
+        val index = findKeyIndex(key)
+        if (index >= 0) {
+            if (values[index] == value) {
+                val previousValue = removeValueAt(index) ?: return false
+                _size -= sizeOf(key, previousValue)
+                onEntryRemoved(key, previousValue, null, false)
+                return true
+            }
+        }
+        return false
+    }
+
+    /** Removes any mapping for which the specified [predicate] returns true. */
+    public fun removeIf(predicate: (K, V) -> Boolean) {
+        forEachIndexed { index ->
+            val key = keys[index]
+            @Suppress("UNCHECKED_CAST")
+            if (predicate(key as K, values[index] as V)) {
+                val previousValue = removeValueAt(index) ?: return
+                _size -= sizeOf(key, previousValue)
+                onEntryRemoved(key, previousValue, null, false)
+            }
+        }
+    }
+
+    /** Removes the specified [key] and its associated value from the map. */
+    public inline operator fun minusAssign(key: K) {
+        remove(key)
+    }
+
+    /** Removes the specified [keys] and their associated value from the map. */
+    public inline operator fun minusAssign(@Suppress("ArrayReturn") keys: Array<out K>) {
+        for (key in keys) {
+            remove(key)
+        }
+    }
+
+    /** Removes the specified [keys] and their associated value from the map. */
+    public inline operator fun minusAssign(keys: Iterable<K>) {
+        for (key in keys) {
+            remove(key)
+        }
+    }
+
+    /** Removes the specified [keys] and their associated value from the map. */
+    public inline operator fun minusAssign(keys: Sequence<K>) {
+        for (key in keys) {
+            remove(key)
+        }
+    }
+
+    /** Removes the specified [keys] and their associated value from the map. */
+    public inline operator fun minusAssign(keys: ScatterSet<K>) {
+        keys.forEach { key -> remove(key) }
+    }
+
+    /** Removes the specified [keys] and their associated value from the map. */
+    public inline operator fun minusAssign(keys: ObjectList<K>) {
+        keys.forEach { key -> remove(key) }
+    }
+
+    /**
+     * Removes all the entries from this cache. Upon each removal, [onEntryRemoved] is invoked with
+     * the `evicted` parameter set to `true`.
+     */
+    public fun evictAll() {
+        trimToSize(-1)
+    }
+
+    /**
+     * Sets the maximum size of the cache to [maxSize], in the unit defined by the implementation of
+     * [sizeOf]. The size must be strictly greater than 0. If the current total size of the entries
+     * in the cache is greater than the new [maxSize], entries will be removed until the total size
+     * is less than or equal to [maxSize]. Upon each removal, [onEntryRemoved] is invoked with the
+     * `evicted` parameter set to `true`.
+     */
+    public fun resize(@IntRange(from = 1, to = MaxSize) maxSize: Int) {
+        _maxSize = maxSize
+        trimToSize(maxSize)
+    }
+
+    /**
+     * Remove entries until the total size of the remaining entries is less than or equal to
+     * [maxSize]. The size of the entries is defined by the implementation of [sizeOf]. Upon each
+     * removal, [onEntryRemoved] is invoked with the `evicted` parameter set to `true`.
+     *
+     * If [maxSize] is set to -1 (or any negative value), all entries are removed.
+     */
+    public fun trimToSize(maxSize: Int) {
+        while (true) {
+            if (_size <= maxSize || count == 0) {
+                return
+            }
+
+            val candidate = findEvictionCandidate()
+            if (candidate == NodeInvalidLink) return
+
+            @Suppress("UNCHECKED_CAST") val key = keys[candidate] as K
+            // Better codegen compared to !!, and the continue can only happen if the data structure
+            // has become internally inconsistent
+            val value = removeValueAt(candidate) ?: continue
+
+            _size -= sizeOf(key, value)
+            onEntryRemoved(key, value, null, true)
+        }
+    }
+
+    /**
+     * Iterates over every key/value pair stored in this cache by invoking the specified [block]
+     * lambda. The iteration order is not specified.
+     *
+     * **NOTE**: Iterating over the content of the cache does *not* mark entries as recently
+     * visited, and therefore does not affect which entries get evicted first.
+     */
+    public inline fun forEach(block: (key: K, value: V) -> Unit) {
+        val k = keys
+        val v = values
+
+        forEachIndexed { index -> @Suppress("UNCHECKED_CAST") block(k[index] as K, v[index] as V) }
+    }
+
+    /**
+     * Iterates over every key stored in this cache by invoking the specified [block] lambda.
+     *
+     * **NOTE**: Iterating over the content of the cache does *not* mark entries as recently
+     * visited, and therefore does not affect which entries get evicted first.
+     */
+    public inline fun forEachKey(block: (key: K) -> Unit) {
+        val k = keys
+
+        forEachIndexed { index -> @Suppress("UNCHECKED_CAST") block(k[index] as K) }
+    }
+
+    /**
+     * Iterates over every value stored in this cache by invoking the specified [block] lambda.
+     *
+     * **NOTE**: Iterating over the content of the cache does *not* mark entries as recently
+     * visited, and therefore does not affect which entries get evicted first.
+     */
+    public inline fun forEachValue(block: (value: V) -> Unit) {
+        val v = values
+
+        forEachIndexed { index -> @Suppress("UNCHECKED_CAST") block(v[index] as V) }
+    }
+
+    /** Returns true if all entries match the given [predicate]. */
+    public inline fun all(predicate: (K, V) -> Boolean): Boolean {
+        forEach { key, value -> if (!predicate(key, value)) return false }
+        return true
+    }
+
+    /** Returns true if at least one entry matches the given [predicate]. */
+    public inline fun any(predicate: (K, V) -> Boolean): Boolean {
+        forEach { key, value -> if (predicate(key, value)) return true }
+        return false
+    }
+
+    /** Returns the number of entries in this cache. */
+    public fun count(): Int = size
+
+    /** Returns the number of entries matching the given [predicate]. */
+    public inline fun count(predicate: (K, V) -> Boolean): Int {
+        var count = 0
+        forEach { key, value -> if (predicate(key, value)) count++ }
+        return count
+    }
+
+    /** Returns true if the specified [key] is present in this cache, false otherwise. */
+    public operator fun contains(key: K): Boolean = findKeyIndex(key) >= 0
+
+    /** Returns true if the specified [key] is present in this cache, false otherwise. */
+    public fun containsKey(key: K): Boolean = findKeyIndex(key) >= 0
+
+    /** Returns true if the specified [value] is present in this hash map, false otherwise. */
+    public fun containsValue(value: V): Boolean {
+        val v = values
+        forEachIndexed { index ->
+            @Suppress("UNCHECKED_CAST") if (value == v[index] as V) return true
+        }
+        return false
+    }
+
+    private fun findEvictionCandidate(): Int {
+        val nodes = nodes
+
+        var candidate = if (hand != NodeInvalidLink) hand else tail
+        while (candidate != NodeInvalidLink && nodes[candidate].visited != 0) {
+            val node = nodes[candidate]
+            val previousIndex = node.previousNode
+            nodes[candidate] = clearVisitedBit(node)
+            candidate = if (previousIndex != NodeInvalidLink) previousIndex else tail
+        }
+
+        val previousIndex = nodes[candidate].previousNode
+        hand = if (previousIndex != NodeInvalidLink) previousIndex else NodeInvalidLink
+
+        return candidate
+    }
+
+    private inline fun moveNodeToHead(index: Int) {
+        nodes[index] = createLinkToNext(head)
+
+        if (head != NodeInvalidLink) {
+            nodes[head] = setLinkToPrevious(nodes[head], index)
+        }
+        head = index
+
+        if (tail == NodeInvalidLink) {
+            tail = index
+        }
+    }
+
+    private fun removeValueAt(index: Int): V? {
+        _count -= 1
+
+        writeMetadata(metadata, _capacity, index, Deleted)
+
+        keys[index] = null
+        val previousValue = values[index]
+        values[index] = null
+
+        removeNode(index)
+
+        @Suppress("UNCHECKED_CAST") return previousValue as V?
+    }
+
+    private inline fun removeNode(index: Int) {
+        val nodes = nodes
+        val node = nodes[index]
+        val previousIndex = node.previousNode
+        val nextIndex = node.nextNode
+
+        if (previousIndex != NodeInvalidLink) {
+            nodes[previousIndex] = setLinkToNext(nodes[previousIndex], nextIndex)
+        } else {
+            head = nextIndex
+        }
+
+        if (nextIndex != NodeInvalidLink) {
+            nodes[nextIndex] = setLinkToPrevious(nodes[nextIndex], previousIndex)
+        } else {
+            tail = previousIndex
+        }
+
+        nodes[index] = EmptyNode
+    }
+
+    private inline fun markNodeVisited(index: Int) {
+        nodes[index] = (nodes[index] and NodeLinksMask) or NodeVisitedBit
+    }
+
+    private fun findKeyIndex(key: K): Int {
+        val hash = hash(key)
+        val hash2 = h2(hash)
+
+        val probeMask = _capacity
+        var probeOffset = h1(hash) and probeMask
+        var probeIndex = 0
+
+        while (true) {
+            val g = group(metadata, probeOffset)
+            var m = g.match(hash2)
+            while (m.hasNext()) {
+                val index = (probeOffset + m.get()) and probeMask
+                if (keys[index] == key) {
+                    return index
+                }
+                m = m.next()
+            }
+
+            if (g.maskEmpty() != 0L) {
+                break
+            }
+
+            probeIndex += GroupWidth
+            probeOffset = (probeOffset + probeIndex) and probeMask
+        }
+
+        return -1
+    }
+
+    private fun findInsertIndex(key: K): Int {
+        val hash = hash(key)
+        val hash1 = h1(hash)
+        val hash2 = h2(hash)
+
+        val probeMask = _capacity
+        var probeOffset = hash1 and probeMask
+        var probeIndex = 0
+
+        while (true) {
+            val g = group(metadata, probeOffset)
+            var m = g.match(hash2)
+            while (m.hasNext()) {
+                val index = (probeOffset + m.get()) and probeMask
+                if (keys[index] == key) {
+                    return index
+                }
+                m = m.next()
+            }
+
+            if (g.maskEmpty() != 0L) {
+                break
+            }
+
+            probeIndex += GroupWidth
+            probeOffset = (probeOffset + probeIndex) and probeMask
+        }
+
+        var index = findFirstAvailableSlot(hash1)
+        if (growthLimit == 0 && !isDeleted(metadata, index)) {
+            adjustStorage()
+            index = findFirstAvailableSlot(hash1)
+        }
+
+        _count += 1
+        growthLimit -= if (isEmpty(metadata, index)) 1 else 0
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
+
+        return index.inv()
+    }
+
+    private fun findFirstAvailableSlot(hash1: Int): Int {
+        val probeMask = _capacity
+        var probeOffset = hash1 and probeMask
+        var probeIndex = 0
+
+        while (true) {
+            val g = group(metadata, probeOffset)
+            val m = g.maskEmptyOrDeleted()
+            if (m != 0L) {
+                return (probeOffset + m.lowestBitSet()) and probeMask
+            }
+            probeIndex += GroupWidth
+            probeOffset = (probeOffset + probeIndex) and probeMask
+        }
+    }
+
+    // Internal to prevent inlining
+    internal fun adjustStorage() {
+        if (_capacity > GroupWidth && count.toULong() * 32UL <= _capacity.toULong() * 25UL) {
+            dropDeletes()
+        } else {
+            resizeStorage(nextCapacity(_capacity))
+        }
+    }
+
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        // TODO: This shouldn't be required, but without it the compiler generates an extra
+        //       200+ aarch64 instructions to generate a NullPointerException.
+        @Suppress("SENSELESS_COMPARISON") if (metadata == null) return
+
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+        val nodes = nodes
+
+        val indexMapping = IntArray(capacity)
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                indexMapping[index] = index
+
+                // Copies the metadata into the clone area
+                metadata[metadata.size - 1] = metadata[0]
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = null
+
+                values[targetIndex] = values[index]
+                values[index] = null
+
+                nodes[targetIndex] = nodes[index]
+                nodes[index] = EmptyNode
+
+                indexMapping[index] = targetIndex
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                nodes[swapIndex] = nodes[targetIndex]
+                nodes[targetIndex] = nodes[index]
+                nodes[index] = nodes[swapIndex]
+
+                indexMapping[index] = targetIndex
+                indexMapping[targetIndex] = index
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.size - 1] = metadata[0]
+
+            index++
+        }
+
+        initializeGrowth()
+
+        fixupNodes(indexMapping)
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
+        val previousMetadata = metadata
+        val previousKeys = keys
+        val previousValues = values
+        val previousNodes = nodes
+        val previousCapacity = _capacity
+
+        val indexMapping = IntArray(previousCapacity)
+
+        initializeStorage(newCapacity)
+
+        val newMetadata = metadata
+        val newKeys = keys
+        val newValues = values
+        val newNodes = nodes
+        val capacity = _capacity
+
+        for (i in 0 until previousCapacity) {
+            if (isFull(previousMetadata, i)) {
+                val previousKey = previousKeys[i]
+                val hash = hash(previousKey)
+                val index = findFirstAvailableSlot(h1(hash))
+
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
+                newKeys[index] = previousKey
+                newValues[index] = previousValues[i]
+                newNodes[index] = previousNodes[i]
+
+                indexMapping[i] = index
+            }
+        }
+
+        fixupNodes(indexMapping)
+    }
+
+    private fun fixupNodes(mapping: IntArray) {
+        val nodes = nodes
+        for (i in nodes.indices) {
+            val node = nodes[i]
+            val previous = node.previousNode
+            val next = node.nextNode
+            nodes[i] = createLinks(node, previous, next, mapping)
+        }
+        if (head != NodeInvalidLink) head = mapping[head]
+        if (tail != NodeInvalidLink) tail = mapping[tail]
+        if (hand != NodeInvalidLink) hand = mapping[hand]
+    }
+
+    @PublishedApi
+    internal inline fun forEachIndexed(block: (index: Int) -> Unit) {
+        val m = metadata
+        val lastIndex = m.size - 2 // We always have 0 or at least 2 entries
+
+        for (i in 0..lastIndex) {
+            var slot = m[i]
+            if (slot.maskEmptyOrDeleted() != BitmaskMsb) {
+                // Branch-less if (i == lastIndex) 7 else 8
+                // i - lastIndex returns a negative value when i < lastIndex,
+                // so 1 is set as the MSB. By inverting and shifting we get
+                // 0 when i < lastIndex, 1 otherwise.
+                val bitCount = 8 - ((i - lastIndex).inv() ushr 31)
+                for (j in 0 until bitCount) {
+                    if (isFull(slot and 0xffL)) {
+                        val index = (i shl 3) + j
+                        block(index)
+                    }
+                    slot = slot shr 8
+                }
+                if (bitCount != 8) return
+            }
+        }
+    }
+
+    /**
+     * Returns the hash code value for this cache. The hash code the sum of the hash codes of each
+     * key/value pair.
+     */
+    public override fun hashCode(): Int {
+        var hash = 0
+
+        forEach { key, value -> hash += key.hashCode() xor value.hashCode() }
+
+        return hash
+    }
+
+    /**
+     * Compares the specified object [other] with this cache for equality. The two objects are
+     * considered equal if [other]:
+     * - Is a [SieveCache]
+     * - Has the same [size] and [count] as this cache
+     * - Contains key/value pairs equal to this cache's pair
+     */
+    public override fun equals(other: Any?): Boolean {
+        if (other === this) {
+            return true
+        }
+
+        if (other !is SieveCache<*, *>) {
+            return false
+        }
+        if (other.size != size || other._count != _count) {
+            return false
+        }
+
+        @Suppress("UNCHECKED_CAST") val o = other as SieveCache<Any, Any>
+
+        forEach { key, value ->
+            if (value != o[key]) {
+                return false
+            }
+        }
+
+        return true
+    }
+
+    override fun toString(): String {
+        return "SieveCache[maxSize=$_maxSize, size=$_size, capacity=$_capacity, count=$_count]"
+    }
+}
+
+private inline fun createLinks(node: Long, previous: Int, next: Int, mapping: IntArray): Long {
+    return (node and NodeMetaMask) or
+        (if (previous == NodeInvalidLink) NodeInvalidLink else mapping[previous]).toLong() shl
+        31 or
+        (if (next == NodeInvalidLink) NodeInvalidLink else mapping[next]).toLong()
+}
+
+// set meta to 0 (visited = false) and previous to NodeInvalidLink
+private inline fun createLinkToNext(next: Int) =
+    0x3fffffff_80000000L or (next.toLong() and NodeLinkMask)
+
+private inline fun setLinkToPrevious(node: Long, previous: Int) =
+    (node and NodeMetaAndNextMask) or ((previous.toLong() and NodeLinkMask) shl 31)
+
+private inline fun setLinkToNext(node: Long, next: Int) =
+    (node and NodeMetaAndPreviousMask) or (next.toLong() and NodeLinkMask)
+
+private inline fun clearVisitedBit(node: Long) = node and NodeLinksMask
+
+private inline val Long.previousNode: Int
+    get() = ((this shr 31) and NodeLinkMask).toInt()
+private inline val Long.nextNode: Int
+    get() = (this and NodeLinkMask).toInt()
+private inline val Long.visited: Int
+    get() = ((this shr 62) and 0x1).toInt()
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/SimpleArrayMap.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/SimpleArrayMap.kt
index 5069164..e00d445 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/SimpleArrayMap.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/SimpleArrayMap.kt
@@ -19,6 +19,7 @@
 import androidx.collection.internal.EMPTY_INTS
 import androidx.collection.internal.EMPTY_OBJECTS
 import androidx.collection.internal.binarySearch
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmName
 import kotlin.jvm.JvmOverloads
 
@@ -309,7 +310,9 @@
      * @throws IllegalArgumentException if [index] is not between 0 and [size]-1
      */
     public open fun keyAt(index: Int): K {
-        require(index in 0 until size) { "Expected index to be within 0..size()-1, but was $index" }
+        requirePrecondition(index in 0 until size) {
+            "Expected index to be within 0..size()-1, but was $index"
+        }
 
         @Suppress("UNCHECKED_CAST") return array[index shl 1] as K
     }
@@ -322,7 +325,9 @@
      * @throws IllegalArgumentException if [index] is not between 0 and [size]-1
      */
     public open fun valueAt(index: Int): V {
-        require(index in 0 until size) { "Expected index to be within 0..size()-1, but was $index" }
+        requirePrecondition(index in 0 until size) {
+            "Expected index to be within 0..size()-1, but was $index"
+        }
 
         @Suppress("UNCHECKED_CAST") return array[(index shl 1) + 1] as V
     }
@@ -336,7 +341,9 @@
      * @throws IllegalArgumentException if [index] is not between 0 and [size]-1
      */
     public open fun setValueAt(index: Int, value: V): V {
-        require(index in 0 until size) { "Expected index to be within 0..size()-1, but was $index" }
+        requirePrecondition(index in 0 until size) {
+            "Expected index to be within 0..size()-1, but was $index"
+        }
 
         val indexInArray = (index shl 1) + 1
 
@@ -500,7 +507,9 @@
      * @throws IllegalArgumentException if [index] is not between 0 and [size]-1
      */
     public open fun removeAt(index: Int): V {
-        require(index in 0 until size) { "Expected index to be within 0..size()-1, but was $index" }
+        requirePrecondition(index in 0 until size) {
+            "Expected index to be within 0..size()-1, but was $index"
+        }
 
         val old = array[(index shl 1) + 1]
         val osize = size
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/internal/RuntimeHelpers.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/internal/RuntimeHelpers.kt
new file mode 100644
index 0000000..90ec883
--- /dev/null
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/internal/RuntimeHelpers.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.collection.internal
+
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
+
+// This function exists so we do *not* inline the throw. It keeps
+// the call site much smaller and since it's the slow path anyway,
+// we don't mind the extra function call
+internal fun throwIllegalStateException(message: String) {
+    throw IllegalStateException(message)
+}
+
+// Like Kotlin's require() but without the .toString() call
+@OptIn(ExperimentalContracts::class)
+internal inline fun checkPrecondition(value: Boolean, lazyMessage: () -> String) {
+    contract { returns() implies value }
+    if (!value) {
+        throwIllegalStateException(lazyMessage())
+    }
+}
+
+internal fun throwIllegalArgumentException(message: String) {
+    throw IllegalArgumentException(message)
+}
+
+// Like Kotlin's require() but without the .toString() call
+@Suppress("BanInlineOptIn") // same opt-in as using Kotlin's require()
+@OptIn(ExperimentalContracts::class)
+internal inline fun requirePrecondition(value: Boolean, lazyMessage: () -> String) {
+    contract { returns() implies value }
+    if (!value) {
+        throwIllegalArgumentException(lazyMessage())
+    }
+}
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/ArraySetTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/ArraySetTest.kt
index 46a4ab9..ccae670 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/ArraySetTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/ArraySetTest.kt
@@ -17,6 +17,7 @@
 
 import androidx.collection.internal.Lock
 import androidx.collection.internal.synchronized
+import kotlin.coroutines.CoroutineContext
 import kotlin.random.Random
 import kotlin.test.Test
 import kotlin.test.assertEquals
@@ -24,15 +25,12 @@
 import kotlin.test.assertNotEquals
 import kotlin.test.assertTrue
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DelicateCoroutinesApi
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.newFixedThreadPoolContext
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
 
 internal class ArraySetTest {
     private val set = ArraySet<String>()
@@ -45,19 +43,14 @@
      * catch this and throw ConcurrentModificationException instead of crashing somewhere in its
      * internals.
      */
-    @Suppress("UnnecessaryOptInAnnotation")
-    @OptIn(
-        ExperimentalCoroutinesApi::class, // newFixedThreadPoolContext is experimental in common
-        DelicateCoroutinesApi::class, // newFixedThreadPoolContext is delicate in jvm
-    )
     @Test
-    fun testConcurrentModificationException() {
+    fun testConcurrentModificationException() = runTest {
         var error: Throwable? = null
         val nThreads = 20
         var nActiveThreads = 0
         val lock = Lock()
-        val dispatcher = newFixedThreadPoolContext(nThreads = nThreads, name = "ArraySetTest")
-        val scope = CoroutineScope(dispatcher)
+        val context: CoroutineContext = Dispatchers.Default
+        val scope = CoroutineScope(context)
 
         repeat(nThreads) {
             scope.launch {
@@ -86,22 +79,19 @@
             }
         }
 
-        runBlocking(Dispatchers.Default) {
-            // Wait until all worker threads are started
-            for (i in 0 until 100) {
-                if (lock.synchronized { nActiveThreads == nThreads }) {
-                    break
-                } else {
-                    delay(timeMillis = 10L)
-                }
+        // Wait until all worker threads are started
+        for (i in 0 until 100) {
+            if (lock.synchronized { nActiveThreads == nThreads }) {
+                break
+            } else {
+                delay(timeMillis = 10L)
             }
-
-            // Allow the worker threads to run concurrently for some time
-            delay(timeMillis = 100L)
         }
 
+        // Allow the worker threads to run concurrently for some time
+        delay(timeMillis = 100L)
+
         scope.cancel()
-        dispatcher.close()
 
         error?.also { throw it }
     }
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/DoubleListTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/DoubleListTest.kt
index f02a99f..4bc3b68 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/DoubleListTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/DoubleListTest.kt
@@ -623,6 +623,14 @@
     }
 
     @Test
+    fun sortEmpty() {
+        val l = MutableDoubleList(0)
+        l.sort()
+        l.sortDescending()
+        assertEquals(MutableDoubleList(0), l)
+    }
+
+    @Test
     fun testEmptyDoubleList() {
         val l = emptyDoubleList()
         assertEquals(0, l.size)
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/FloatListTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/FloatListTest.kt
index ca3a23f..cdc37e9 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/FloatListTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/FloatListTest.kt
@@ -623,6 +623,14 @@
     }
 
     @Test
+    fun sortEmpty() {
+        val l = MutableFloatList(0)
+        l.sort()
+        l.sortDescending()
+        assertEquals(MutableFloatList(0), l)
+    }
+
+    @Test
     fun testEmptyFloatList() {
         val l = emptyFloatList()
         assertEquals(0, l.size)
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/IntListTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/IntListTest.kt
index 37fc96d..ae3bc8f4 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/IntListTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/IntListTest.kt
@@ -623,6 +623,14 @@
     }
 
     @Test
+    fun sortEmpty() {
+        val l = MutableIntList(0)
+        l.sort()
+        l.sortDescending()
+        assertEquals(MutableIntList(0), l)
+    }
+
+    @Test
     fun testEmptyIntList() {
         val l = emptyIntList()
         assertEquals(0, l.size)
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/LongListTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/LongListTest.kt
index 0f01be7..9fdd870 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/LongListTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/LongListTest.kt
@@ -623,6 +623,14 @@
     }
 
     @Test
+    fun sortEmpty() {
+        val l = MutableLongList(0)
+        l.sort()
+        l.sortDescending()
+        assertEquals(MutableLongList(0), l)
+    }
+
+    @Test
     fun testEmptyLongList() {
         val l = emptyLongList()
         assertEquals(0, l.size)
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/LruCacheTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/LruCacheTest.kt
index 0445018..b081b99 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/LruCacheTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/LruCacheTest.kt
@@ -21,11 +21,10 @@
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertNull
-import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
 
 internal class LruCacheTest {
 
@@ -382,7 +381,7 @@
     }
 
     @Test
-    fun testAbleToUpdateFromAnotherThreadWithBlockedEntryRemoved() {
+    fun testAbleToUpdateFromAnotherThreadWithBlockedEntryRemoved() = runTest {
         val cache =
             object : LruCache<String, String>(3) {
                 override fun entryRemoved(
@@ -392,7 +391,7 @@
                     newValue: String?
                 ) {
                     if (key in setOf("a", "b", "c", "d")) {
-                        runBlocking(Dispatchers.Default) { put("x", "X") }
+                        launch { put("x", "X") }
                     }
                 }
             }
@@ -407,9 +406,8 @@
     }
 
     /** Makes sure that LruCache operations are correctly synchronized to guarantee consistency. */
-    @OptIn(DelicateCoroutinesApi::class) // Using GlobalScope in tests
     @Test
-    fun consistentMultithreadedAccess() {
+    fun consistentMultithreadedAccess() = runTest {
         var nonNullValues = 0
         var nullValues = 0
         var valuesPut = 0
@@ -424,8 +422,10 @@
                 override fun create(key: String): Int = value
             }
 
+        val scope = CoroutineScope(Dispatchers.Default)
+
         val t0 =
-            GlobalScope.launch(Dispatchers.Default) {
+            scope.launch {
                 repeat(rounds) {
                     if (cache[key] != null) {
                         nonNullValues++
@@ -436,7 +436,7 @@
             }
 
         val t1 =
-            GlobalScope.launch(Dispatchers.Default) {
+            scope.launch {
                 repeat(rounds) { i ->
                     if (i % 2 == 0) {
                         if (cache.put(key, value) != null) {
@@ -451,10 +451,8 @@
                 }
             }
 
-        runBlocking {
-            t0.join()
-            t1.join()
-        }
+        t0.join()
+        t1.join()
 
         assertEquals(rounds, nonNullValues)
         assertEquals(0, nullValues)
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterMapTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterMapTest.kt
index dd370fc..108548e 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterMapTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterMapTest.kt
@@ -51,7 +51,7 @@
     }
 
     @Test
-    fun zeroCapacityHashMap() {
+    fun zeroCapacityMap() {
         val map = MutableScatterMap<String, String>(0)
         assertEquals(0, map.capacity)
         assertEquals(0, map.size)
@@ -419,6 +419,35 @@
     }
 
     @Test
+    fun removeDoesNotCauseGrowthOnInsert() {
+        val map = MutableScatterMap<String, String>(10) // Must be > GroupWidth (8)
+        assertEquals(15, map.capacity)
+
+        map["Hello"] = "World"
+        map["Bonjour"] = "Monde"
+        map["Hallo"] = "Welt"
+        map["Konnichiwa"] = "Sekai"
+        map["Ciao"] = "Mondo"
+        map["Annyeong"] = "Sesang"
+
+        // Reach the upper limit of what we can store without increasing the map size
+        for (i in 0..7) {
+            map[i.toString()] = i.toString()
+        }
+
+        // Delete a few items
+        for (i in 0..5) {
+            map.remove(i.toString())
+        }
+
+        // Inserting a new item shouldn't cause growth, but the deleted markers to be purged
+        map["Foo"] = "Bar"
+        assertEquals(15, map.capacity)
+
+        assertEquals("Bar", map["Foo"])
+    }
+
+    @Test
     fun minus() {
         val map = MutableScatterMap<String, String>()
         map["Hello"] = "World"
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterSetTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterSetTest.kt
index 721d993..5185166 100644
--- a/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterSetTest.kt
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/ScatterSetTest.kt
@@ -376,6 +376,35 @@
     }
 
     @Test
+    fun removeDoesNotCauseGrowthOnInsert() {
+        val set = MutableScatterSet<String>(10) // Must be > GroupWidth (8)
+        assertEquals(15, set.capacity)
+
+        set += "Hello"
+        set += "Bonjour"
+        set += "Hallo"
+        set += "Konnichiwa"
+        set += "Ciao"
+        set += "Annyeong"
+
+        // Reach the upper limit of what we can store without increasing the map size
+        for (i in 0..7) {
+            set += i.toString()
+        }
+
+        // Delete a few items
+        for (i in 0..5) {
+            set.remove(i.toString())
+        }
+
+        // Inserting a new item shouldn't cause growth, but the deleted markers to be purged
+        set += "Foo"
+        assertEquals(15, set.capacity)
+
+        assertTrue(set.contains("Foo"))
+    }
+
+    @Test
     fun minusAssignArray() {
         val set = mutableScatterSetOf("Hello", "World")
         set -= arrayOf("Hola", "Bonjour")
diff --git a/collection/collection/src/commonTest/kotlin/androidx/collection/SieveCacheTest.kt b/collection/collection/src/commonTest/kotlin/androidx/collection/SieveCacheTest.kt
new file mode 100644
index 0000000..31622e5
--- /dev/null
+++ b/collection/collection/src/commonTest/kotlin/androidx/collection/SieveCacheTest.kt
@@ -0,0 +1,885 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.collection
+
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+
+class SieveCacheTest {
+    @Test
+    fun sizeMustBeGreaterThan0() {
+        assertFailsWith<IllegalArgumentException> { SieveCache<String, String>(-1) }
+        assertFailsWith<IllegalArgumentException> { SieveCache<String, String>(0) }
+    }
+
+    @Test
+    fun emptyCache() {
+        val cache = createStandardCache()
+        assertEquals(0, cache.size)
+        assertEquals(4, cache.maxSize)
+        assertEquals(0, cache.count)
+        assertEquals(7, cache.capacity)
+    }
+
+    @Test
+    fun zeroCapacityCache() {
+        val cache = SieveCache<String, String>(4, 0)
+        assertEquals(0, cache.capacity)
+        assertEquals(0, cache.size)
+    }
+
+    @Test
+    fun cacheWithCapacity() {
+        // When unloading the suggested capacity, we'll fall outside of the
+        // expected bucket of 2047 entries, and we'll get 4095 instead
+        val cache = SieveCache<String, String>(4, 1800)
+        assertEquals(4095, cache.capacity)
+        assertEquals(0, cache.size)
+    }
+
+    @Test
+    fun addEntry() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+
+        assertEquals(1, cache.size)
+        assertEquals(1, cache.count)
+        assertEquals(7, cache.capacity)
+        assertEquals("World", cache["Hello"])
+    }
+
+    @Test
+    fun addEntryCustomSize() {
+        val cache = createCacheWithCustomSize()
+        cache["Hello"] = "World"
+
+        assertEquals("World".length, cache.size)
+        assertEquals(1, cache.count)
+        assertEquals(7, cache.capacity)
+        assertEquals("World", cache["Hello"])
+    }
+
+    @Test
+    fun addEntryReplacesOldValue() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createCacheWithCustomSize(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Hello"] = "Goodbye"
+
+        assertEquals("Goodbye".length, cache.size)
+        assertContentEquals(listOf("World"), removedEntries)
+        assertContentEquals(listOf(), evictedEntries)
+    }
+
+    @Test
+    fun addEntryTrimsSize() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+        cache["Ciao"] = "Mondo"
+
+        assertEquals(4, cache.size)
+        assertEquals(4, cache.count)
+        assertEquals(7, cache.capacity)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World"), evictedEntries)
+    }
+
+    @Test
+    fun addIncreaseCapacity() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createCacheWithCustomSize(removedEntries, evictedEntries)
+
+        assertEquals(7, cache.capacity)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+        cache["Ciao"] = "Mondo"
+        cache["Annyeong"] = "Sesang"
+        cache["Goodbye"] = "World"
+
+        assertEquals(7, cache.count)
+        assertEquals(15, cache.capacity)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf(), evictedEntries)
+    }
+
+    @Test
+    fun addToZeroCapacityCache() {
+        val cache = SieveCache<String, String>(4, 0)
+        cache["Hello"] = "World"
+
+        assertEquals(1, cache.size)
+        assertEquals(1, cache.count)
+        assertEquals("World", cache["Hello"])
+    }
+
+    @Test
+    fun get() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+
+        assertEquals("World", cache["Hello"])
+    }
+
+    @Test
+    fun getReturnsNull() {
+        val cache = createStandardCache()
+        assertNull(cache["Hello"])
+    }
+
+    @Test
+    fun getCreatesValue() {
+        val cache = createCreatingCache()
+        val value = cache["Hello"]
+
+        assertEquals(cache.size, 1)
+        assertEquals(cache.count, 1)
+        assertEquals("created-Hello", value)
+    }
+
+    @Test
+    fun contains() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+
+        assertTrue("Hello" in cache)
+        assertFalse("World" in cache)
+    }
+
+    @Test
+    fun containsKey() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+
+        assertTrue(cache.containsKey("Hello"))
+        assertFalse(cache.containsKey("World"))
+    }
+
+    @Test
+    fun containsValue() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+
+        assertTrue(cache.containsValue("World"))
+        assertFalse(cache.containsValue("Hello"))
+    }
+
+    @Test
+    fun empty() {
+        val cache = createStandardCache()
+        assertTrue(cache.isEmpty())
+        assertFalse(cache.isNotEmpty())
+        assertTrue(cache.none())
+        assertFalse(cache.any())
+
+        cache["Hello"] = "World"
+
+        assertFalse(cache.isEmpty())
+        assertTrue(cache.isNotEmpty())
+        assertTrue(cache.any())
+        assertFalse(cache.none())
+    }
+
+    @Test
+    fun count() {
+        val cache = createStandardCache()
+        assertEquals(0, cache.count())
+
+        cache["Hello"] = "World"
+        assertEquals(1, cache.count())
+
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        assertEquals(2, cache.count { key, _ -> key.startsWith("H") })
+        assertEquals(0, cache.count { key, _ -> key.startsWith("W") })
+    }
+
+    @Test
+    fun any() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+        cache["Ciao"] = "Mondo"
+        cache["Annyeong"] = "Sesang"
+
+        assertTrue(cache.any { key, _ -> key.startsWith("K") })
+        assertFalse(cache.any { key, _ -> key.startsWith("W") })
+    }
+
+    @Test
+    fun all() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+        cache["Ciao"] = "Mondo"
+        cache["Annyeong"] = "Sesang"
+
+        assertTrue(cache.any { key, value -> key.length >= 5 && value.length >= 4 })
+        assertFalse(cache.all { key, _ -> key.startsWith("W") })
+    }
+
+    @Test
+    fun putReturnsNull() {
+        val cache = createStandardCache()
+        val previousValue = cache.put("Hello", "World")
+        assertNull(previousValue)
+    }
+
+    @Test
+    fun putReturnsPreviousValue() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        val previousValue = cache.put("Hello", "Goodbye")
+        assertEquals("World", previousValue)
+    }
+
+    @Test
+    fun readPreventsEviction() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        // Preserve Hello=World
+        assertEquals("World", cache["Hello"])
+        cache["Ciao"] = "Mondo"
+
+        assertEquals(4, cache.size)
+        assertContentEquals(listOf(), removedEntries)
+        // Reading "Hello" kept it, the next eviction candidate is "Bonjour"
+        assertContentEquals(listOf("Monde"), evictedEntries)
+    }
+
+    @Test
+    fun plus() {
+        val cache = createStandardCache()
+        cache += "Hello" to "World"
+
+        assertEquals(1, cache.size)
+        assertEquals("World", cache["Hello"])
+    }
+
+    @Test
+    fun plusMap() {
+        val cache = createStandardCache()
+        cache += mapOf("Hallo" to "Welt", "Hola" to "Mundo")
+
+        assertEquals(2, cache.size)
+        assertEquals("Welt", cache["Hallo"])
+        assertEquals("Mundo", cache["Hola"])
+    }
+
+    @Test
+    fun plusArray() {
+        val cache = createStandardCache()
+        cache += arrayOf("Hallo" to "Welt", "Hola" to "Mundo")
+
+        assertEquals(2, cache.size)
+        assertEquals("Welt", cache["Hallo"])
+        assertEquals("Mundo", cache["Hola"])
+    }
+
+    @Test
+    fun plusIterable() {
+        val cache = createStandardCache()
+        cache += listOf("Hallo" to "Welt", "Hola" to "Mundo")
+
+        assertEquals(2, cache.size)
+        assertEquals("Welt", cache["Hallo"])
+        assertEquals("Mundo", cache["Hola"])
+    }
+
+    @Test
+    fun plusSequence() {
+        val cache = createStandardCache()
+        cache += listOf("Hallo" to "Welt", "Hola" to "Mundo").asSequence()
+
+        assertEquals(2, cache.size)
+        assertEquals("Welt", cache["Hallo"])
+        assertEquals("Mundo", cache["Hola"])
+    }
+
+    @Test
+    fun remove() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createCacheWithCustomSize(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        val previousValue = cache.remove("Hello")
+
+        assertEquals(0, cache.size)
+        assertEquals("World", previousValue)
+        assertContentEquals(listOf("World"), removedEntries)
+        assertContentEquals(listOf(), evictedEntries)
+    }
+
+    @Test
+    fun removeUnknownEntry() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+
+        assertNull(cache.remove("Bonjour"))
+        assertEquals(1, cache.size)
+        assertEquals(1, cache.count)
+    }
+
+    @Test
+    fun removeFromEmptyCache() {
+        val cache = createStandardCache()
+
+        assertNull(cache.remove("Bonjour"))
+        assertEquals(0, cache.size)
+        assertEquals(0, cache.count)
+    }
+
+    @Test
+    fun removeThenAdd() {
+        // Use a size of 6 to fit in a single entry in the metadata table
+        val cache = SieveCache<String, String>(2048, 6)
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+        cache["Ciao"] = "Mondo"
+        cache["Annyeong"] = "Sesang"
+
+        // Removing all the entries will mark the metadata as deleted
+        cache.remove("Hello")
+        cache.remove("Bonjour")
+        cache.remove("Hallo")
+        cache.remove("Konnichiwa")
+        cache.remove("Ciao")
+        cache.remove("Annyeong")
+
+        assertEquals(0, cache.count)
+
+        val capacity = cache.capacity
+
+        // Make sure reinserting an entry after filling the table
+        // with "Deleted" markers works
+        cache["Hello"] = "World"
+
+        assertEquals(1, cache.count)
+        assertEquals(capacity, cache.capacity)
+    }
+
+    @Test
+    fun removeIf() {
+        val removedEntries = mutableListOf<String>()
+        val cache = createCacheWithCustomSize(removedEntries)
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+        cache["Ciao"] = "Mondo"
+        cache["Annyeong"] = "Sesang"
+
+        cache.removeIf { key, value -> key.startsWith('H') || value.startsWith('S') }
+
+        assertEquals(2, cache.count)
+        assertEquals("Monde", cache["Bonjour"])
+        assertEquals("Mondo", cache["Ciao"])
+        assertContentEquals(listOf("Sekai", "Sesang", "World", "Welt"), removedEntries)
+    }
+
+    @Test
+    fun removeDoesNotCauseGrowthOnInsert() {
+        val cache = SieveCache<String, String>(2048, 10) // Must be > GroupWidth (8)
+        assertEquals(15, cache.capacity)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+        cache["Ciao"] = "Mondo"
+        cache["Annyeong"] = "Sesang"
+
+        // Reach the upper limit of what we can store without increasing the cache size
+        for (i in 0..7) {
+            cache[i.toString()] = i.toString()
+        }
+
+        // Delete a few items
+        for (i in 0..5) {
+            cache.remove(i.toString())
+        }
+
+        // Inserting a new item shouldn't cause growth, but the deleted markers to be purged
+        cache["Foo"] = "Bar"
+        assertEquals(15, cache.capacity)
+
+        assertEquals("Bar", cache["Foo"])
+    }
+
+    @Test
+    fun conditionalRemove() {
+        val removedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries)
+        assertFalse(cache.remove("Hello", "World"))
+        assertContentEquals(listOf(), removedEntries)
+
+        cache["Hello"] = "World"
+        assertTrue(cache.remove("Hello", "World"))
+        assertEquals(0, cache.count)
+        assertContentEquals(listOf("World"), removedEntries)
+    }
+
+    @Test
+    fun minus() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+
+        cache -= "Hello"
+
+        assertEquals(2, cache.count)
+        assertNull(cache["Hello"])
+    }
+
+    @Test
+    fun minusArray() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+
+        cache -= arrayOf("Hallo", "Bonjour")
+
+        assertEquals(1, cache.count)
+        assertNull(cache["Hallo"])
+        assertNull(cache["Bonjour"])
+    }
+
+    @Test
+    fun minusIterable() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+
+        cache -= listOf("Hallo", "Bonjour")
+
+        assertEquals(1, cache.count)
+        assertNull(cache["Hallo"])
+        assertNull(cache["Bonjour"])
+    }
+
+    @Test
+    fun minusSequence() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+
+        cache -= listOf("Hallo", "Bonjour").asSequence()
+
+        assertEquals(1, cache.count)
+        assertNull(cache["Hallo"])
+        assertNull(cache["Bonjour"])
+    }
+
+    @Test
+    fun minusScatterSet() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+
+        cache -= scatterSetOf("Hallo", "Bonjour")
+
+        assertEquals(1, cache.count)
+        assertNull(cache["Hallo"])
+        assertNull(cache["Bonjour"])
+    }
+
+    @Test
+    fun minusObjectList() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+
+        cache -= objectListOf("Hallo", "Bonjour")
+
+        assertEquals(1, cache.count)
+        assertNull(cache["Hallo"])
+        assertNull(cache["Bonjour"])
+    }
+
+    @Test
+    fun evictAllFromEmptyCache() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries, evictedEntries)
+
+        cache.evictAll()
+
+        assertEquals(0, cache.size)
+        assertEquals(0, cache.count)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf(), evictedEntries)
+    }
+
+    @Test
+    fun evictAll() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        cache.evictAll()
+
+        assertEquals(0, cache.size)
+        assertEquals(0, cache.count)
+        assertEquals(7, cache.capacity)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World", "Monde", "Welt", "Sekai"), evictedEntries)
+    }
+
+    @Test
+    fun evictAllEvictsZeroSizeElements() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createZeroSizeCache(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        cache.evictAll()
+
+        assertEquals(0, cache.size)
+        assertEquals(0, cache.count)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World", "Monde", "Welt", "Sekai"), evictedEntries)
+    }
+
+    @Test
+    fun resize() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createCacheWithCustomSize(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        cache.resize(6)
+
+        assertEquals(6, cache.maxSize)
+        assertEquals(5, cache.size)
+        assertEquals(1, cache.count)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World", "Monde", "Welt"), evictedEntries)
+    }
+
+    @Test
+    fun trimToSize() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createCacheWithCustomSize(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        val maxSize = cache.maxSize
+        cache.trimToSize(6)
+
+        assertEquals(maxSize, cache.maxSize)
+        assertEquals(5, cache.size)
+        assertEquals(1, cache.count)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World", "Monde", "Welt"), evictedEntries)
+    }
+
+    @Test
+    fun equals() {
+        val cache = createStandardCache()
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+
+        assertFalse(cache.equals(null))
+        assertEquals(cache, cache)
+
+        val cache2 = createStandardCache()
+        cache2["Bonjour"] = "Monde"
+        cache2["Hello"] = "Monde"
+
+        assertNotEquals(cache, cache2)
+
+        cache2["Hello"] = "World"
+        assertEquals(cache, cache2)
+    }
+
+    @Test
+    fun forEach() {
+        for (i in 0..48) {
+            val cache = createCacheWithCustomSize()
+
+            for (j in 0 until i) {
+                val s = j.toString()
+                cache[s] = s
+            }
+
+            var counter = 0
+            cache.forEach { key, value ->
+                assertEquals(key, value)
+                counter++
+            }
+
+            assertEquals(i, counter)
+        }
+    }
+
+    @Test
+    fun forEachDoesNotPreventsEviction() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        var count = 0
+        cache.forEach { _, _ -> count++ }
+        assertEquals(4, count)
+
+        cache["Ciao"] = "Mondo"
+
+        assertEquals(4, cache.size)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World"), evictedEntries)
+    }
+
+    @Test
+    fun forEachKey() {
+        for (i in 0..48) {
+            val cache = createCacheWithCustomSize()
+
+            for (j in 0 until i) {
+                val s = j.toString()
+                cache[s] = s
+            }
+
+            var counter = 0
+            cache.forEachKey { key ->
+                assertNotNull(key.toIntOrNull())
+                counter++
+            }
+
+            assertEquals(i, counter)
+        }
+    }
+
+    @Test
+    fun forEachKeyDoesNotPreventsEviction() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        var count = 0
+        cache.forEachKey { _ -> count++ }
+        assertEquals(4, count)
+
+        cache["Ciao"] = "Mondo"
+
+        assertEquals(4, cache.size)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World"), evictedEntries)
+    }
+
+    @Test
+    fun forEachValue() {
+        for (i in 0..48) {
+            val cache = createCacheWithCustomSize()
+
+            for (j in 0 until i) {
+                val s = j.toString()
+                cache[s] = s
+            }
+
+            var counter = 0
+            cache.forEachValue { value ->
+                assertNotNull(value.toIntOrNull())
+                counter++
+            }
+
+            assertEquals(i, counter)
+        }
+    }
+
+    @Test
+    fun forEachValueDoesNotPreventsEviction() {
+        val removedEntries = mutableListOf<String>()
+        val evictedEntries = mutableListOf<String>()
+        val cache = createStandardCache(removedEntries, evictedEntries)
+
+        cache["Hello"] = "World"
+        cache["Bonjour"] = "Monde"
+        cache["Hallo"] = "Welt"
+        cache["Konnichiwa"] = "Sekai"
+
+        var count = 0
+        cache.forEachKey { _ -> count++ }
+        assertEquals(4, count)
+
+        cache["Ciao"] = "Mondo"
+
+        assertEquals(4, cache.size)
+        assertContentEquals(listOf(), removedEntries)
+        assertContentEquals(listOf("World"), evictedEntries)
+    }
+
+    @Test
+    fun insertOneRemoveOne() {
+        val map = SieveCache<Int, String>(4)
+
+        for (i in 0..1000000) {
+            map[i] = i.toString()
+            map.remove(i)
+            assertTrue(map.capacity < 16, "Map grew larger than 16 after step $i")
+        }
+    }
+
+    @Test
+    fun insertManyRemoveMany() {
+        val map = SieveCache<Int, String>(1024)
+
+        for (i in 0..100) {
+            map[i] = i.toString()
+        }
+
+        for (i in 0..100) {
+            if (i % 2 == 0) {
+                map.remove(i)
+            }
+        }
+
+        for (i in 0..100) {
+            if (i % 2 == 0) {
+                map[i] = i.toString()
+            }
+        }
+
+        for (i in 0..100) {
+            if (i % 2 != 0) {
+                map.remove(i)
+            }
+        }
+
+        for (i in 0..100) {
+            if (i % 2 != 0) {
+                map[i] = i.toString()
+            }
+        }
+
+        assertEquals(127, map.capacity)
+        for (i in 0..100) {
+            assertTrue(map.contains(i), "Map should contain element $i")
+        }
+    }
+
+    private fun createCreatingCache(): SieveCache<String, String> {
+        return SieveCache(4, createValueFromKey = { key -> "created-$key" })
+    }
+
+    private fun createStandardCache(
+        removedEntries: MutableList<String> = mutableListOf(),
+        evictedEntries: MutableList<String> = mutableListOf()
+    ): SieveCache<String, String> {
+        return SieveCache(
+            4,
+            onEntryRemoved = { _, old, _, evicted ->
+                (if (evicted) evictedEntries else removedEntries).add(old)
+            }
+        )
+    }
+
+    private fun createCacheWithCustomSize(
+        removedEntries: MutableList<String> = mutableListOf(),
+        evictedEntries: MutableList<String> = mutableListOf()
+    ): SieveCache<String, String> {
+        return SieveCache(
+            4096,
+            sizeOf = { _, v -> v.length },
+            onEntryRemoved = { _, old, _, evicted ->
+                (if (evicted) evictedEntries else removedEntries).add(old)
+            }
+        )
+    }
+
+    private fun createZeroSizeCache(
+        removedEntries: MutableList<String> = mutableListOf(),
+        evictedEntries: MutableList<String> = mutableListOf()
+    ): SieveCache<String, String> {
+        return SieveCache(
+            4,
+            sizeOf = { _, _ -> 0 },
+            onEntryRemoved = { _, old, _, evicted ->
+                (if (evicted) evictedEntries else removedEntries).add(old)
+            }
+        )
+    }
+}
diff --git a/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt b/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt
index 2f9a255..63e4bc3 100644
--- a/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt
+++ b/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt
@@ -16,7 +16,8 @@
 
 package androidx.collection.internal
 
-import kotlin.native.internal.createCleaner
+import kotlin.experimental.ExperimentalNativeApi
+import kotlin.native.ref.createCleaner
 import kotlinx.cinterop.Arena
 import kotlinx.cinterop.ExperimentalForeignApi
 import kotlinx.cinterop.alloc
@@ -43,8 +44,8 @@
 
     private val resources = Resources()
 
+    @OptIn(ExperimentalNativeApi::class)
     @Suppress("unused") // The returned Cleaner must be assigned to a property
-    @ExperimentalStdlibApi
     private val cleaner = createCleaner(resources, Resources::destroy)
 
     actual inline fun <T> synchronizedImpl(block: () -> T): T {
diff --git a/collection/collection/template/ObjectPValueMap.kt.template b/collection/collection/template/ObjectPValueMap.kt.template
index 5376d2f..2a997d2 100644
--- a/collection/collection/template/ObjectPValueMap.kt.template
+++ b/collection/collection/template/ObjectPValueMap.kt.template
@@ -22,6 +22,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -697,7 +698,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -897,7 +898,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         keys[index] = null
     }
 
@@ -957,7 +958,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -1006,15 +1007,116 @@
      * current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes()  {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = null
+
+                values[targetIndex] = values[index]
+                values[index] = 0ValueSuffix
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] =
+                (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -1022,8 +1124,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -1031,43 +1135,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified
-     * [index]. The index must be a valid index. This function ensures the
-     * metadata is also written in the clone area at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) +
-            (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/template/PKeyList.kt.template b/collection/collection/template/PKeyList.kt.template
index dacc266..3835d49 100644
--- a/collection/collection/template/PKeyList.kt.template
+++ b/collection/collection/template/PKeyList.kt.template
@@ -18,6 +18,7 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
@@ -60,20 +61,20 @@
     /**
      * The number of elements in the [PKeyList].
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
     /**
      * Returns the last valid index in the [PKeyList]. This can be `-1` when the list is empty.
      */
-    @get:androidx.annotation.IntRange(from = -1)
+    @get:IntRange(from = -1)
     public inline val lastIndex: Int get() = _size - 1
 
     /**
      * Returns an [IntRange] of the valid indices for this [PKeyList].
      */
-    public inline val indices: IntRange get() = 0 until _size
+    public inline val indices: kotlin.ranges.IntRange get() = 0 until _size
 
     /**
      * Returns `true` if the collection has no elements in it.
@@ -302,7 +303,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if
      * the [index] is out of bounds of this collection.
      */
-    public operator fun get(@androidx.annotation.IntRange(from = 0) index: Int): PKey {
+    public operator fun get(@IntRange(from = 0) index: Int): PKey {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -313,7 +314,7 @@
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if
      * the [index] is out of bounds of this collection.
      */
-    public fun elementAt(@androidx.annotation.IntRange(from = 0) index: Int): PKey {
+    public fun elementAt(@IntRange(from = 0) index: Int): PKey {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -328,7 +329,7 @@
      * an index not in the list.
      */
     public inline fun elementAtOrElse(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         defaultValue: (index: Int) -> PKey
     ): PKey {
         if (index !in 0 until _size) {
@@ -564,7 +565,7 @@
      * elements at [index] and after, if any.
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
-    public fun add(@androidx.annotation.IntRange(from = 0) index: Int, element: PKey) {
+    public fun add(@IntRange(from = 0) index: Int, element: PKey) {
         if (index !in 0.._size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$_size")
         }
@@ -589,7 +590,7 @@
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive.
      */
     public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         elements: PKeyArray
     ): Boolean {
         if (index !in 0.._size) {
@@ -618,7 +619,7 @@
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
     public fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         elements: PKeyList
     ): Boolean {
         if (index !in 0.._size) {
@@ -779,7 +780,7 @@
      * Removes the element at the given [index] and returns it.
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public fun removeAt(@androidx.annotation.IntRange(from = 0) index: Int): PKey {
+    public fun removeAt(@IntRange(from = 0) index: Int): PKey {
         if (index !in 0 until _size) {
             throw IndexOutOfBoundsException("Index $index must be in 0..$lastIndex")
         }
@@ -803,8 +804,8 @@
      * @throws IllegalArgumentException if [start] is greater than [end]
      */
     public fun removeRange(
-        @androidx.annotation.IntRange(from = 0) start: Int,
-        @androidx.annotation.IntRange(from = 0) end: Int
+        @IntRange(from = 0) start: Int,
+        @IntRange(from = 0) end: Int
     ) {
         if (start !in 0.._size || end !in 0.._size) {
             throw IndexOutOfBoundsException("Start ($start) and end ($end) must be in 0..$_size")
@@ -863,7 +864,7 @@
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
     public operator fun set(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         element: PKey
     ): PKey {
         if (index !in 0 until _size) {
@@ -879,6 +880,8 @@
      * Sorts the [MutablePKeyList] elements in ascending order.
      */
     public fun sort() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sort(fromIndex = 0, toIndex = _size)
     }
 
@@ -886,6 +889,8 @@
      * Sorts the [MutablePKeyList] elements in descending order.
      */
     public fun sortDescending() {
+        // TODO: remove a return after https://youtrack.jetbrains.com/issue/KT-70005 is fixed
+        if (_size == 0) return
         content.sortDescending(fromIndex = 0, toIndex = _size)
     }
 }
diff --git a/collection/collection/template/PKeyListTest.kt.template b/collection/collection/template/PKeyListTest.kt.template
index cd430c9..f316360 100644
--- a/collection/collection/template/PKeyListTest.kt.template
+++ b/collection/collection/template/PKeyListTest.kt.template
@@ -659,6 +659,14 @@
     }
 
     @Test
+    fun sortEmpty() {
+        val l = MutablePKeyList(0)
+        l.sort()
+        l.sortDescending()
+        assertEquals(MutablePKeyList(0), l)
+    }
+
+    @Test
     fun testEmptyPKeyList() {
         val l = emptyPKeyList()
         assertEquals(0, l.size)
diff --git a/collection/collection/template/PKeyObjectMap.kt.template b/collection/collection/template/PKeyObjectMap.kt.template
index 5cf7ee5..b81707a 100644
--- a/collection/collection/template/PKeyObjectMap.kt.template
+++ b/collection/collection/template/PKeyObjectMap.kt.template
@@ -22,6 +22,7 @@
 package androidx.collection
 
 import androidx.collection.internal.EMPTY_OBJECTS
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -682,7 +683,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -853,7 +854,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
         val oldValue = values[index]
         values[index] = null
 
@@ -917,7 +918,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -966,15 +967,116 @@
      * current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes()  {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0KeySuffix
+
+                values[targetIndex] = values[index]
+                values[index] = null
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] =
+                (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -982,8 +1084,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -991,43 +1095,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified
-     * [index]. The index must be a valid index. This function ensures the
-     * metadata is also written in the clone area at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) +
-            (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/template/PKeyPValueMap.kt.template b/collection/collection/template/PKeyPValueMap.kt.template
index d8bff84..8c194f7 100644
--- a/collection/collection/template/PKeyPValueMap.kt.template
+++ b/collection/collection/template/PKeyPValueMap.kt.template
@@ -21,6 +21,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.requirePrecondition
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
 
@@ -680,7 +681,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -872,7 +873,7 @@
 
         // TODO: We could just mark the entry as empty if there's a group
         //       window around this entry that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /**
@@ -930,7 +931,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index.inv()
     }
@@ -979,15 +980,116 @@
      * current size is <= 25/32 of the table capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_set`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes()  {
+        val metadata = metadata
+        val capacity = _capacity
+        val keys = keys
+        val values = values
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(keys[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                keys[targetIndex] = keys[index]
+                keys[index] = 0KeySuffix
+
+                values[targetIndex] = values[index]
+                values[index] = 0ValueSuffix
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                keys[swapIndex] = keys[targetIndex]
+                keys[targetIndex] = keys[index]
+                keys[index] = keys[swapIndex]
+
+                values[swapIndex] = values[targetIndex]
+                values[targetIndex] = values[index]
+                values[index] = values[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] =
+                (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousKeys = keys
         val previousValues = values
@@ -995,8 +1097,10 @@
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newKeys = keys
         val newValues = values
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -1004,43 +1108,10 @@
                 val hash = hash(previousKey)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newKeys[index] = previousKey
                 newValues[index] = previousValues[i]
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified
-     * [index]. The index must be a valid index. This function ensures the
-     * metadata is also written in the clone area at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) +
-            (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
diff --git a/collection/collection/template/PKeySet.kt.template b/collection/collection/template/PKeySet.kt.template
index ea71ba3..7b4383f 100644
--- a/collection/collection/template/PKeySet.kt.template
+++ b/collection/collection/template/PKeySet.kt.template
@@ -26,6 +26,8 @@
 
 package androidx.collection
 
+import androidx.annotation.IntRange
+import androidx.collection.internal.requirePrecondition
 import kotlin.contracts.contract
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmOverloads
@@ -159,7 +161,7 @@
      * Returns the number of elements that can be stored in this set
      * without requiring internal storage reallocation.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val capacity: Int
         get() = _capacity
 
@@ -171,7 +173,7 @@
     /**
      * Returns the number of elements in this set.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public val size: Int
         get() = _size
 
@@ -296,7 +298,7 @@
     /**
      * Returns the number of elements in this set.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public fun count(): Int = _size
 
     /**
@@ -304,7 +306,7 @@
      * @param predicate Called for all elements in the set to count the number for which it returns
      * `true`.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(predicate: (element: PKey) -> Boolean): Int {
         contract { callsInPlace(predicate) }
         var count = 0
@@ -496,7 +498,7 @@
     private var growthLimit = 0
 
     init {
-        require(initialCapacity >= 0) { "Capacity must be a positive value." }
+        requirePrecondition(initialCapacity >= 0) { "Capacity must be a positive value." }
         initializeStorage(unloadedCapacity(initialCapacity))
     }
 
@@ -670,7 +672,7 @@
 
         // TODO: We could just mark the element as empty if there's a group
         //       window around this element that was already empty
-        writeMetadata(index, Deleted)
+        writeMetadata(metadata, _capacity, index, Deleted)
     }
 
     /**
@@ -728,7 +730,7 @@
 
         _size += 1
         growthLimit -= if (isEmpty(metadata, index)) 1 else 0
-        writeMetadata(index, hash2.toLong())
+        writeMetadata(metadata, _capacity, index, hash2.toLong())
 
         return index
     }
@@ -759,7 +761,7 @@
      * Returns the number of empty elements removed from this set's storage.
      * Returns 0 if no trimming is necessary or possible.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public fun trim(): Int {
         val previousCapacity = _capacity
         val newCapacity = normalizeCapacity(unloadedCapacity(_size))
@@ -777,22 +779,117 @@
      * current size is <= 25/32 of the set capacity. The choice of 25/32 is
      * detailed in the implementation of abseil's `raw_hash_map`.
      */
-    private fun adjustStorage() {
+    internal fun adjustStorage() { // Internal to prevent inlining
         if (_capacity > GroupWidth && _size.toULong() * 32UL <= _capacity.toULong() * 25UL) {
-            removeDeletedMarkers()
+            dropDeletes()
         } else {
             resizeStorage(nextCapacity(_capacity))
         }
     }
 
-    private fun resizeStorage(newCapacity: Int) {
+    // Internal to prevent inlining
+    internal fun dropDeletes() {
+        val metadata = metadata
+        val capacity = _capacity
+        val elements = elements
+
+        // Converts Sentinel and Deleted to Empty, and Full to Deleted
+        convertMetadataForCleanup(metadata, capacity)
+
+        var swapIndex = -1
+        var index = 0
+
+        // Drop deleted items and re-hashes surviving entries
+        while (index != capacity) {
+            var m = readRawMetadata(metadata, index)
+            // Formerly Deleted entry, we can use it as a swap spot
+            if (m == Empty) {
+                swapIndex = index
+                index++
+                continue
+            }
+
+            // Formerly Full entries are now marked Deleted. If we see an
+            // entry that's not marked Deleted, we can ignore it completely
+            if (m != Deleted) {
+                index++
+                continue
+            }
+
+            val hash = hash(elements[index])
+            val hash1 = h1(hash)
+            val targetIndex = findFirstAvailableSlot(hash1)
+
+            // Test if the current index (i) and the new index (targetIndex) fall
+            // within the same group based on the hash. If the group doesn't change,
+            // we don't move the entry
+            val probeOffset = hash1 and capacity
+            val newProbeIndex = ((targetIndex - probeOffset) and capacity) / GroupWidth
+            val oldProbeIndex = ((index - probeOffset) and capacity) / GroupWidth
+
+            if (newProbeIndex == oldProbeIndex) {
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, index, hash2.toLong())
+
+                // Copies the metadata into the clone area
+                metadata[metadata.lastIndex] =
+                    (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+                index++
+                continue
+            }
+
+            m = readRawMetadata(metadata, targetIndex)
+            if (m == Empty) {
+                // The target is empty so we can transfer directly
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+                writeRawMetadata(metadata, index, Empty)
+
+                elements[targetIndex] = elements[index]
+                elements[index] = 0KeySuffix
+
+                swapIndex = index
+            } else /* m == Deleted */ {
+                // The target isn't empty so we use an empty slot denoted by
+                // swapIndex to perform the swap
+                val hash2 = h2(hash)
+                writeRawMetadata(metadata, targetIndex, hash2.toLong())
+
+                if (swapIndex == -1) {
+                    swapIndex = findEmptySlot(metadata, index + 1, capacity)
+                }
+
+                elements[swapIndex] = elements[targetIndex]
+                elements[targetIndex] = elements[index]
+                elements[index] = elements[swapIndex]
+
+                // Since we exchanged two slots we must repeat the process with
+                // element we just moved in the current location
+                index--
+            }
+
+            // Copies the metadata into the clone area
+            metadata[metadata.lastIndex] =
+                (Empty shl 56) or (metadata[0] and 0x00ffffff_ffffffffL)
+
+            index++
+        }
+
+        initializeGrowth()
+    }
+
+    // Internal to prevent inlining
+    internal fun resizeStorage(newCapacity: Int) {
         val previousMetadata = metadata
         val previousElements = elements
         val previousCapacity = _capacity
 
         initializeStorage(newCapacity)
 
+        val newMetadata = metadata
         val newElements = elements
+        val capacity = _capacity
 
         for (i in 0 until previousCapacity) {
             if (isFull(previousMetadata, i)) {
@@ -800,44 +897,11 @@
                 val hash = hash(previousElement)
                 val index = findFirstAvailableSlot(h1(hash))
 
-                writeMetadata(index, h2(hash).toLong())
+                writeMetadata(newMetadata, capacity, index, h2(hash).toLong())
                 newElements[index] = previousElement
             }
         }
     }
-
-    private fun removeDeletedMarkers() {
-        val m = metadata
-        val capacity = _capacity
-        var removedDeletes = 0
-
-        // TODO: this can be done in a more efficient way
-        for (i in 0 until capacity) {
-            val slot = readRawMetadata(m, i)
-            if (slot == Deleted) {
-                writeMetadata(i, Empty)
-                removedDeletes++
-            }
-        }
-
-        growthLimit += removedDeletes
-    }
-
-    /**
-     * Writes the "H2" part of an entry into the metadata array at the specified
-     * [index]. The index must be a valid index. This function ensures the
-     * metadata is also written in the clone area at the end.
-     */
-    private inline fun writeMetadata(index: Int, value: Long) {
-        val m = metadata
-        writeRawMetadata(m, index, value)
-
-        // Mirroring
-        val c = _capacity
-        val cloneIndex = ((index - ClonedMetadataCount) and c) +
-            (ClonedMetadataCount and c)
-        writeRawMetadata(m, cloneIndex, value)
-    }
 }
 
 /**
diff --git a/collection/collection/template/ValueClassList.kt.template b/collection/collection/template/ValueClassList.kt.template
index 083abd8..4ce50e6 100644
--- a/collection/collection/template/ValueClassList.kt.template
+++ b/collection/collection/template/ValueClassList.kt.template
@@ -27,6 +27,7 @@
 
 package PACKAGE
 
+import androidx.annotation.IntRange
 import androidx.collection.PRIMITIVEList
 import androidx.collection.MutablePRIMITIVEList
 import androidx.collection.emptyPRIMITIVEList
@@ -62,19 +63,19 @@
     /**
      * The number of elements in the [VALUE_CLASSList].
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public inline val size: Int get() = list.size
 
     /**
      * Returns the last valid index in the [VALUE_CLASSList]. This can be `-1` when the list is empty.
      */
-    @get:androidx.annotation.IntRange(from = -1)
+    @get:IntRange(from = -1)
     public inline val lastIndex: Int get() = list.lastIndex
 
     /**
      * Returns an [IntRange] of the valid indices for this [VALUE_CLASSList].
      */
-    public inline val indices: IntRange get() = list.indices
+    public inline val indices: kotlin.ranges.IntRange get() = list.indices
 
     /**
      * Returns `true` if the collection has no elements in it.
@@ -261,14 +262,14 @@
      * the [index] is out of bounds of this collection.
      */
     public inline operator fun get(
-        @androidx.annotation.IntRange(from = 0) index: Int
+        @IntRange(from = 0) index: Int
     ): VALUE_CLASS = VALUE_CLASS(list[index]TO_PARAM)
 
     /**
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if
      * the [index] is out of bounds of this collection.
      */
-    public inline fun elementAt(@androidx.annotation.IntRange(from = 0) index: Int): VALUE_CLASS =
+    public inline fun elementAt(@IntRange(from = 0) index: Int): VALUE_CLASS =
         VALUE_CLASS(list[index]TO_PARAM)
 
     /**
@@ -279,7 +280,7 @@
      * an index not in the list.
      */
     public inline fun elementAtOrElse(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         defaultValue: (index: Int) -> VALUE_CLASS
     ): VALUE_CLASS =
         VALUE_CLASS(list.elementAtOrElse(index) { defaultValue(it).BACKING_PROPERTY }TO_PARAM)
@@ -384,19 +385,19 @@
     /**
      * The number of elements in the [VALUE_CLASSList].
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public inline val size: Int get() = list.size
 
     /**
      * Returns the last valid index in the [VALUE_CLASSList]. This can be `-1` when the list is empty.
      */
-    @get:androidx.annotation.IntRange(from = -1)
+    @get:IntRange(from = -1)
     public inline val lastIndex: Int get() = list.lastIndex
 
     /**
      * Returns an [IntRange] of the valid indices for this [VALUE_CLASSList].
      */
-    public inline val indices: IntRange get() = list.indices
+    public inline val indices: kotlin.ranges.IntRange get() = list.indices
 
     /**
      * Returns `true` if the collection has no elements in it.
@@ -583,14 +584,14 @@
      * the [index] is out of bounds of this collection.
      */
     public inline operator fun get(
-        @androidx.annotation.IntRange(from = 0) index: Int
+        @IntRange(from = 0) index: Int
     ): VALUE_CLASS = VALUE_CLASS(list[index]TO_PARAM)
 
     /**
      * Returns the element at the given [index] or throws [IndexOutOfBoundsException] if
      * the [index] is out of bounds of this collection.
      */
-    public inline fun elementAt(@androidx.annotation.IntRange(from = 0) index: Int): VALUE_CLASS =
+    public inline fun elementAt(@IntRange(from = 0) index: Int): VALUE_CLASS =
         VALUE_CLASS(list[index]TO_PARAM)
 
     /**
@@ -601,7 +602,7 @@
      * an index not in the list.
      */
     public inline fun elementAtOrElse(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         defaultValue: (index: Int) -> VALUE_CLASS
     ): VALUE_CLASS =
         VALUE_CLASS(list.elementAtOrElse(index) { defaultValue(it).BACKING_PROPERTY }TO_PARAM)
@@ -695,7 +696,7 @@
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
     public inline fun add(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         element: VALUE_CLASS
     ) = list.add(index, element.BACKING_PROPERTY)
 
@@ -706,7 +707,7 @@
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
     public inline fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         elements: VALUE_CLASSList
     ): Boolean = list.addAll(index, elements.list)
 
@@ -717,7 +718,7 @@
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [size], inclusive
      */
     public inline fun addAll(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         elements: MutableVALUE_CLASSList
     ): Boolean = list.addAll(index, elements.list)
 
@@ -812,7 +813,7 @@
      * Removes the element at the given [index] and returns it.
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
-    public inline fun removeAt(@androidx.annotation.IntRange(from = 0) index: Int): VALUE_CLASS =
+    public inline fun removeAt(@IntRange(from = 0) index: Int): VALUE_CLASS =
         VALUE_CLASS(list.removeAt(index)TO_PARAM)
 
     /**
@@ -821,8 +822,8 @@
      * @throws IllegalArgumentException if [start] is greater than [end]
      */
     public inline fun removeRange(
-        @androidx.annotation.IntRange(from = 0) start: Int,
-        @androidx.annotation.IntRange(from = 0) end: Int
+        @IntRange(from = 0) start: Int,
+        @IntRange(from = 0) end: Int
     ) = list.removeRange(start, end)
 
     /**
@@ -845,7 +846,7 @@
      * @throws IndexOutOfBoundsException if [index] isn't between 0 and [lastIndex], inclusive
      */
     public inline operator fun set(
-        @androidx.annotation.IntRange(from = 0) index: Int,
+        @IntRange(from = 0) index: Int,
         element: VALUE_CLASS
     ): VALUE_CLASS = VALUE_CLASS(list.set(index, element.BACKING_PROPERTY)TO_PARAM)
 }
diff --git a/collection/collection/template/ValueClassSet.kt.template b/collection/collection/template/ValueClassSet.kt.template
index 2c2679b..de43cdf 100644
--- a/collection/collection/template/ValueClassSet.kt.template
+++ b/collection/collection/template/ValueClassSet.kt.template
@@ -27,6 +27,7 @@
 
 package PACKAGE
 
+import androidx.annotation.IntRange
 import androidx.collection.PRIMITIVESet
 import androidx.collection.MutablePRIMITIVESet
 import androidx.collection.emptyPRIMITIVESet
@@ -157,14 +158,14 @@
      * Returns the number of elements that can be stored in this set
      * without requiring internal storage reallocation.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public inline val capacity: Int
         get() = set.capacity
 
     /**
      * Returns the number of elements in this set.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public inline val size: Int
         get() = set.size
 
@@ -243,7 +244,7 @@
     /**
      * Returns the number of elements in this set.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(): Int = set.count()
 
     /**
@@ -251,7 +252,7 @@
      * @param predicate Called for all elements in the set to count the number for which it returns
      * `true`.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(predicate: (element: VALUE_CLASS) -> Boolean): Int {
         contract { callsInPlace(predicate) }
         return set.count { predicate(VALUE_CLASS(itTO_PARAM)) }
@@ -308,14 +309,14 @@
      * Returns the number of elements that can be stored in this set
      * without requiring internal storage reallocation.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public inline val capacity: Int
         get() = set.capacity
 
     /**
      * Returns the number of elements in this set.
      */
-    @get:androidx.annotation.IntRange(from = 0)
+    @get:IntRange(from = 0)
     public inline val size: Int
         get() = set.size
 
@@ -394,7 +395,7 @@
     /**
      * Returns the number of elements in this set.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(): Int = set.count()
 
     /**
@@ -402,7 +403,7 @@
      * @param predicate Called for all elements in the set to count the number for which it returns
      * `true`.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun count(predicate: (element: VALUE_CLASS) -> Boolean): Int {
         contract { callsInPlace(predicate) }
         return set.count { predicate(VALUE_CLASS(itTO_PARAM)) }
@@ -537,6 +538,6 @@
      * Returns the number of empty elements removed from this set's storage.
      * Returns 0 if no trimming is necessary or possible.
      */
-    @androidx.annotation.IntRange(from = 0)
+    @IntRange(from = 0)
     public inline fun trim(): Int = set.trim()
 }
diff --git a/collection/collection/template/generateCollections.sh b/collection/collection/template/generateCollections.sh
index 94667f7f..27bd553 100755
--- a/collection/collection/template/generateCollections.sh
+++ b/collection/collection/template/generateCollections.sh
@@ -1,7 +1,12 @@
 #!/bin/bash
 
-primitives=("Double" "Float" "Long" "Int")
-suffixes=(".0" "f" "L" "")
+# Create maps, sets, and lists
+primitives=("Float" "Long" "Int")
+suffixes=("f" "L" "")
+
+# Create lists only
+listOnlyPrimitives=("Double")
+listOnlySuffixes=(".0")
 
 # Note: Had to use `dirname ${0}` on Linux
 scriptDir=`dirname ${PWD}/${0}`
@@ -11,19 +16,20 @@
   primitive=${primitives[$index]}
   firstLower=`echo ${primitive:0:1} | tr '[:upper:]' '[:lower:]'`
   lower="${firstLower}${primitive:1}"
-  echo "generating ${primitive}ObjectMap.kt"
-  sed -e "s/PKey/${primitive}/g" -e "s/pKey/${lower}/g" ${scriptDir}/PKeyObjectMap.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/${primitive}ObjectMap.kt
-  echo "generating ${primitive}ObjectMapTest.kt"
-  sed -e "s/PValue/${primitive}/g" ${scriptDir}/ObjectPValueMap.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/Object${primitive}Map.kt
-
   suffix=${suffixes[$index]}
+
+  echo "generating ${primitive}ObjectMap.kt"
+  sed -e "s/PKey/${primitive}/g" -e "s/pKey/${lower}/g" -e "s/KeySuffix/${suffix}/g" ${scriptDir}/PKeyObjectMap.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/${primitive}ObjectMap.kt
   echo "generating Object${primitive}Map.kt"
+  sed -e "s/PValue/${primitive}/g" -e "s/ValueSuffix/${suffix}/g" ${scriptDir}/ObjectPValueMap.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/Object${primitive}Map.kt
+
+  echo "generating ${primitive}ObjectMapTest.kt"
   sed -e "s/PValue/${primitive}/g" -e "s/ValueSuffix/${suffix}/g" ${scriptDir}/ObjectPValueMapTest.kt.template > ${scriptDir}/../src/commonTest/kotlin/androidx/collection/Object${primitive}MapTest.kt
   echo "generating Object${primitive}MapTest.kt"
   sed -e "s/PKey/${primitive}/g" -e"s/pKey/${lower}/g" -e "s/KeySuffix/${suffix}/g" ${scriptDir}/PKeyObjectMapTest.kt.template > ${scriptDir}/../src/commonTest/kotlin/androidx/collection/${primitive}ObjectMapTest.kt
 
   echo "generating ${primitive}Set.kt"
-  sed -e "s/PKey/${primitive}/g" -e"s/pKey/${lower}/g" ${scriptDir}/PKeySet.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/${primitive}Set.kt
+  sed -e "s/PKey/${primitive}/g" -e"s/pKey/${lower}/g" -e "s/KeySuffix/${suffix}/g" ${scriptDir}/PKeySet.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/${primitive}Set.kt
   echo "generating ${primitive}SetTest.kt"
   sed -e "s/PKey/${primitive}/g" -e"s/pKey/${lower}/g" -e "s/KeySuffix/${suffix}/g" ${scriptDir}/PKeySetTest.kt.template > ${scriptDir}/../src/commonTest/kotlin/androidx/collection/${primitive}SetTest.kt
 
@@ -44,8 +50,22 @@
     value=${primitives[$valueIndex]}
     valueSuffix=${suffixes[$valueIndex]}
     echo "generating ${key}${value}Map.kt"
-    sed -e "s/PKey/${key}/g" -e "s/pKey/${lowerKey}/g" -e "s/PValue/${value}/g" ${scriptDir}/PKeyPValueMap.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/${key}${value}Map.kt
+    sed -e "s/PKey/${key}/g" -e "s/pKey/${lowerKey}/g" -e "s/PValue/${value}/g" -e "s/ValueSuffix/${valueSuffix}/g" -e "s/KeySuffix/${keySuffix}/g" ${scriptDir}/PKeyPValueMap.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/${key}${value}Map.kt
     echo "generating ${key}${value}MapTest.kt"
     sed -e "s/PKey/${key}/g" -e "s/pKey/${lowerKey}/g" -e "s/PValue/${value}/g" -e "s/ValueSuffix/${valueSuffix}/g" -e "s/KeySuffix/${keySuffix}/g" ${scriptDir}/PKeyPValueMapTest.kt.template > ${scriptDir}/../src/commonTest/kotlin/androidx/collection/${key}${value}MapTest.kt
   done
 done
+
+for index in ${!listOnlyPrimitives[@]}
+do
+  primitive=${listOnlyPrimitives[$index]}
+  firstLower=`echo ${primitive:0:1} | tr '[:upper:]' '[:lower:]'`
+  lower="${firstLower}${primitive:1}"
+
+  suffix=${listOnlySuffixes[$index]}
+
+  echo "generating ${primitive}List.kt"
+  sed -e "s/PKey/${primitive}/g" -e"s/pKey/${lower}/g" ${scriptDir}/PKeyList.kt.template > ${scriptDir}/../src/commonMain/kotlin/androidx/collection/${primitive}List.kt
+  echo "generating ${primitive}ListTest.kt"
+  sed -e "s/PKey/${primitive}/g" -e"s/pKey/${lower}/g" -e "s/KeySuffix/${suffix}/g" ${scriptDir}/PKeyListTest.kt.template > ${scriptDir}/../src/commonTest/kotlin/androidx/collection/${primitive}ListTest.kt
+done
\ No newline at end of file
diff --git a/collection/integration-tests/testapp/build.gradle b/collection/integration-tests/testapp/build.gradle
index 14410da..8adca75 100644
--- a/collection/integration-tests/testapp/build.gradle
+++ b/collection/integration-tests/testapp/build.gradle
@@ -31,7 +31,7 @@
 dependencies {
     implementation(project(":collection:collection"))
     implementation(libs.kotlinStdlib)
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index 350a33b..b19fbe0 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -208,6 +208,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi @androidx.compose.runtime.Immutable public final class ArcAnimationSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public ArcAnimationSpec();
     ctor public ArcAnimationSpec(optional int mode, optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
     method public int getDelayMillis();
     method public int getDurationMillis();
@@ -399,6 +400,7 @@
   }
 
   public final class FloatExponentialDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
+    ctor public FloatExponentialDecaySpec();
     ctor public FloatExponentialDecaySpec(optional @FloatRange(from=0.0, fromInclusive=false) float frictionMultiplier, optional @FloatRange(from=0.0, fromInclusive=false) float absVelocityThreshold);
     method public float getAbsVelocityThreshold();
     method public long getDurationNanos(float initialValue, float initialVelocity);
@@ -409,6 +411,7 @@
   }
 
   public final class FloatSpringSpec implements androidx.compose.animation.core.FloatAnimationSpec {
+    ctor public FloatSpringSpec();
     ctor public FloatSpringSpec(optional float dampingRatio, optional float stiffness, optional float visibilityThreshold);
     method public float getDampingRatio();
     method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
@@ -420,6 +423,7 @@
   }
 
   public final class FloatTweenSpec implements androidx.compose.animation.core.FloatAnimationSpec {
+    ctor public FloatTweenSpec();
     ctor public FloatTweenSpec(optional int duration, optional int delay, optional androidx.compose.animation.core.Easing easing);
     method public int getDelay();
     method public int getDuration();
@@ -573,6 +577,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public SnapSpec();
     ctor public SnapSpec(optional int delay);
     method public int getDelay();
     method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
@@ -594,6 +599,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
+    ctor public SpringSpec();
     ctor public SpringSpec(optional float dampingRatio, optional float stiffness, optional T? visibilityThreshold);
     method public float getDampingRatio();
     method public float getStiffness();
@@ -713,6 +719,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TweenSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public TweenSpec();
     ctor public TweenSpec(optional int durationMillis, optional int delay, optional androidx.compose.animation.core.Easing easing);
     method public int getDelay();
     method public int getDurationMillis();
@@ -810,6 +817,7 @@
   }
 
   public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
+    ctor public VectorizedSnapSpec();
     ctor public VectorizedSnapSpec(optional int delayMillis);
     method public int getDelayMillis();
     method public int getDurationMillis();
@@ -828,6 +836,7 @@
   }
 
   public final class VectorizedTweenSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
+    ctor public VectorizedTweenSpec();
     ctor public VectorizedTweenSpec(optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
     method public int getDelayMillis();
     method public int getDurationMillis();
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index 0bf428d..99d7a37 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -208,6 +208,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.animation.core.ExperimentalAnimationSpecApi @androidx.compose.runtime.Immutable public final class ArcAnimationSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public ArcAnimationSpec();
     ctor public ArcAnimationSpec(optional int mode, optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
     method public int getDelayMillis();
     method public int getDurationMillis();
@@ -399,6 +400,7 @@
   }
 
   public final class FloatExponentialDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
+    ctor public FloatExponentialDecaySpec();
     ctor public FloatExponentialDecaySpec(optional @FloatRange(from=0.0, fromInclusive=false) float frictionMultiplier, optional @FloatRange(from=0.0, fromInclusive=false) float absVelocityThreshold);
     method public float getAbsVelocityThreshold();
     method public long getDurationNanos(float initialValue, float initialVelocity);
@@ -409,6 +411,7 @@
   }
 
   public final class FloatSpringSpec implements androidx.compose.animation.core.FloatAnimationSpec {
+    ctor public FloatSpringSpec();
     ctor public FloatSpringSpec(optional float dampingRatio, optional float stiffness, optional float visibilityThreshold);
     method public float getDampingRatio();
     method public long getDurationNanos(float initialValue, float targetValue, float initialVelocity);
@@ -420,6 +423,7 @@
   }
 
   public final class FloatTweenSpec implements androidx.compose.animation.core.FloatAnimationSpec {
+    ctor public FloatTweenSpec();
     ctor public FloatTweenSpec(optional int duration, optional int delay, optional androidx.compose.animation.core.Easing easing);
     method public int getDelay();
     method public int getDuration();
@@ -573,6 +577,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SnapSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public SnapSpec();
     ctor public SnapSpec(optional int delay);
     method public int getDelay();
     method public <V extends androidx.compose.animation.core.AnimationVector> androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> vectorize(androidx.compose.animation.core.TwoWayConverter<T,V> converter);
@@ -594,6 +599,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class SpringSpec<T> implements androidx.compose.animation.core.FiniteAnimationSpec<T> {
+    ctor public SpringSpec();
     ctor public SpringSpec(optional float dampingRatio, optional float stiffness, optional T? visibilityThreshold);
     method public float getDampingRatio();
     method public float getStiffness();
@@ -718,6 +724,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TweenSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
+    ctor public TweenSpec();
     ctor public TweenSpec(optional int durationMillis, optional int delay, optional androidx.compose.animation.core.Easing easing);
     method public int getDelay();
     method public int getDurationMillis();
@@ -815,6 +822,7 @@
   }
 
   public final class VectorizedSnapSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
+    ctor public VectorizedSnapSpec();
     ctor public VectorizedSnapSpec(optional int delayMillis);
     method public int getDelayMillis();
     method public int getDurationMillis();
@@ -833,6 +841,7 @@
   }
 
   public final class VectorizedTweenSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
+    ctor public VectorizedTweenSpec();
     ctor public VectorizedTweenSpec(optional int durationMillis, optional int delayMillis, optional androidx.compose.animation.core.Easing easing);
     method public int getDelayMillis();
     method public int getDurationMillis();
diff --git a/compose/animation/animation-core/benchmark/build.gradle b/compose/animation/animation-core/benchmark/build.gradle
index 2b93098..788f98d 100644
--- a/compose/animation/animation-core/benchmark/build.gradle
+++ b/compose/animation/animation-core/benchmark/build.gradle
@@ -35,5 +35,7 @@
 }
 
 android {
+    compileSdk 35
+
     namespace "androidx.compose.animation.core.benchmark"
 }
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index d108b31..595aef5 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -34,6 +34,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -41,11 +42,11 @@
         commonMain {
             dependencies {
                 implementation(project(":compose:runtime:runtime"))
-                implementation("androidx.compose.ui:ui:1.6.0")
-                implementation("androidx.compose.ui:ui-unit:1.6.0")
+                implementation(project(":compose:ui:ui"))
+                implementation(project(":compose:ui:ui-unit"))
                 implementation(project(":compose:ui:ui-graphics"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation("androidx.collection:collection:1.4.0")
+                implementation(project(":collection:collection"))
                 implementation(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
             }
@@ -65,14 +66,20 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -88,6 +95,8 @@
                 implementation("androidx.compose.ui:ui-test-junit4:1.2.1")
                 implementation(project(":compose:test-utils"))
                 implementation("androidx.compose.material3:material3:1.2.1")
+                implementation(libs.leakcanary)
+                implementation(libs.leakcanaryInstrumentation)
             }
         }
 
@@ -113,10 +122,11 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2019"
     description = "Animation engine and animation primitives that are the building blocks of the Compose animation library"
-    legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:animation:animation-core:animation-core-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.animation.core"
 }
diff --git a/compose/animation/animation-core/lint-baseline.xml b/compose/animation/animation-core/lint-baseline.xml
index 352cbc1..6687669 100644
--- a/compose/animation/animation-core/lint-baseline.xml
+++ b/compose/animation/animation-core/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
@@ -13,7 +13,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field visibilityThresholdMap with type Map&lt;TwoWayConverter&lt;?, ?>, Float>: replace with ObjectFloatMap"
-        errorLine1="internal val visibilityThresholdMap: Map&lt;TwoWayConverter&lt;*, *>, Float> = mapOf("
+        errorLine1="internal val visibilityThresholdMap: Map&lt;TwoWayConverter&lt;*, *>, Float> ="
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt"/>
@@ -22,7 +22,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type Map&lt;TwoWayConverter&lt;?, ?>, Float> of getVisibilityThresholdMap: replace with ObjectFloatMap"
-        errorLine1="internal val visibilityThresholdMap: Map&lt;TwoWayConverter&lt;*, *>, Float> = mapOf("
+        errorLine1="internal val visibilityThresholdMap: Map&lt;TwoWayConverter&lt;*, *>, Float> ="
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt"/>
diff --git a/compose/animation/animation-core/samples/build.gradle b/compose/animation/animation-core/samples/build.gradle
index f566a97..d6e8a00 100644
--- a/compose/animation/animation-core/samples/build.gradle
+++ b/compose/animation/animation-core/samples/build.gradle
@@ -41,6 +41,7 @@
     implementation("androidx.compose.foundation:foundation:1.2.1")
     implementation("androidx.compose.foundation:foundation-layout:1.2.1")
     implementation("androidx.compose.material:material:1.2.1")
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
 }
 
 androidx {
@@ -51,5 +52,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.animation.core.samples"
 }
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
index ca3382b..939ca37 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
@@ -27,15 +27,20 @@
 import androidx.test.filters.LargeTest
 import junit.framework.TestCase.assertEquals
 import junit.framework.TestCase.assertFalse
+import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.Rule
 import org.junit.Test
+import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @LargeTest
 class InfiniteTransitionTest {
+    private val rule = createComposeRule()
 
-    @get:Rule val rule = createComposeRule()
+    // Detect leaks BEFORE and AFTER compose rule work
+    @get:Rule
+    val ruleChain: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()).around(rule)
 
     @Test
     fun transitionTest() {
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
index 1566c9e..8f9d455 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
-import java.lang.IllegalStateException
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
index b41a9fd..32d573f 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/SeekableTransitionStateTest.kt
@@ -68,17 +68,23 @@
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
+import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
+import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 class SeekableTransitionStateTest {
-    @get:Rule val rule = createComposeRule()
+    private val rule = createComposeRule()
+
+    // Detect leaks BEFORE and AFTER compose rule work
+    @get:Rule
+    val ruleChain: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()).around(rule)
 
     private enum class AnimStates {
         From,
@@ -171,7 +177,8 @@
         }
 
         rule.mainClock.advanceTimeByFrame() // wait for composition after seekTo()
-        val deferred1 = coroutineScope.async { seekableTransitionState.animateTo() }
+        val deferred1 =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
         rule.mainClock.advanceTimeByFrame() // one frame to set the start time
         rule.mainClock.advanceTimeByFrame()
 
@@ -201,7 +208,8 @@
         }
 
         // continue from the same place
-        val deferred2 = coroutineScope.async { seekableTransitionState.animateTo() }
+        val deferred2 =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
         rule.waitForIdle() // wait for coroutine to run
         rule.mainClock.advanceTimeByFrame() // one frame to set the start time
         rule.mainClock.advanceTimeByFrame()
@@ -318,12 +326,16 @@
                     .value
         }
 
-        val deferred1 = coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+        val deferred1 =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame() // one frame to set the start time
         rule.mainClock.advanceTimeByFrame()
 
         // Running the same animation again should cancel the existing one
-        val deferred2 = coroutineScope.async { seekableTransitionState.animateTo() }
+        val deferred2 =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
 
         rule.waitForIdle() // wait for coroutine to run
         rule.mainClock.advanceTimeByFrame()
@@ -332,7 +344,10 @@
         assertFalse(deferred2.isCancelled)
 
         // seeking should cancel the animation
-        val deferred3 = coroutineScope.async { seekableTransitionState.seekTo(fraction = 0.25f) }
+        val deferred3 =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.seekTo(fraction = 0.25f) }
+            }
 
         rule.waitForIdle() // wait for coroutine to run
         rule.mainClock.advanceTimeByFrame()
@@ -342,7 +357,8 @@
         assertTrue(deferred3.isCompleted)
 
         // start the animation again
-        val deferred4 = coroutineScope.async { seekableTransitionState.animateTo() }
+        val deferred4 =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
 
         rule.waitForIdle() // wait for coroutine to run
         rule.mainClock.advanceTimeByFrame()
@@ -465,8 +481,10 @@
         // Start seek to new state. It won't complete until the initial state is
         // animated to "To"
         val seekTo =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(0f, targetState = AnimStates.Other)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(0f, targetState = AnimStates.Other)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // must recompose to Other
         rule.runOnIdle {
@@ -486,9 +504,11 @@
             assertEquals(500 + (500f * 80f / 150f), animatedValue3.toFloat(), 1f)
         }
         val seekToFraction =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(fraction = 0.5f)
-                assertEquals(0.5f, seekableTransitionState.fraction)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(fraction = 0.5f)
+                    assertEquals(0.5f, seekableTransitionState.fraction)
+                }
             }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle {
@@ -600,9 +620,11 @@
         }
 
         val seekTo =
-            coroutineScope.async {
-                // seek to Other. This won't finish until the animation finishes
-                seekableTransitionState.seekTo(0f, targetState = AnimStates.Other)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    // seek to Other. This won't finish until the animation finishes
+                    seekableTransitionState.seekTo(0f, targetState = AnimStates.Other)
+                }
             }
 
         rule.runOnIdle {
@@ -622,9 +644,11 @@
             assertEquals(640f, animatedValue3.toFloat(), 1f)
         }
         val seekToHalf =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(fraction = 0.5f)
-                assertEquals(0.5f, seekableTransitionState.fraction)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(fraction = 0.5f)
+                    assertEquals(0.5f, seekableTransitionState.fraction)
+                }
             }
         rule.runOnIdle { assertEquals(500, animatedValue2) }
 
@@ -640,9 +664,11 @@
             assertEquals(500, animatedValue2)
             assertEquals(1500, animatedValue3)
         }
-        coroutineScope.launch {
-            seekableTransitionState.seekTo(fraction = 1f)
-            assertEquals(1f, seekableTransitionState.fraction, 0f)
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.seekTo(fraction = 1f)
+                assertEquals(1f, seekableTransitionState.fraction, 0f)
+            }
         }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle {
@@ -728,7 +754,9 @@
             assertEquals(533f, animatedValue3.toFloat(), 1f)
         }
         val animateToOther =
-            coroutineScope.async { seekableTransitionState.animateTo(AnimStates.Other) }
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.Other) }
+            }
 
         rule.mainClock.advanceTimeBy(16) // composition after animateTo()
 
@@ -936,16 +964,32 @@
             )
         }
         rule.waitForIdle()
-        coroutineScope.launch { seekableTransitionState.seekTo(0f, targetState = AnimStates.To) }
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.seekTo(0f, targetState = AnimStates.To)
+            }
+        }
         rule.waitForIdle()
-        coroutineScope.launch { seekableTransitionState.seekTo(fraction = 0.5f) }
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.seekTo(fraction = 0.5f) }
+        }
         rule.waitForIdle()
-        coroutineScope.launch { seekableTransitionState.seekTo(0f, targetState = AnimStates.Other) }
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.seekTo(0f, targetState = AnimStates.Other)
+            }
+        }
         rule.waitForIdle()
         rule.mainClock.advanceTimeByFrame() // lock in the initial value animation start time
-        coroutineScope.launch { seekableTransitionState.seekTo(fraction = 0.5f) }
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.seekTo(fraction = 0.5f) }
+        }
         rule.waitForIdle()
-        coroutineScope.launch { seekableTransitionState.seekTo(0f, targetState = AnimStates.From) }
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.seekTo(0f, targetState = AnimStates.From)
+            }
+        }
         rule.waitForIdle()
 
         // Now we have two initial value animations running. One is for animating
@@ -982,7 +1026,10 @@
             }
         }
         rule.waitForIdle()
-        val seekTo = coroutineScope.async { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+        val seekTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle {
             assertTrue(seekTo.isCompleted)
@@ -1013,14 +1060,17 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        val deferred = coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+        val deferred =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+            }
         rule.mainClock.advanceTimeBy(10_000L) // complete the animation
         rule.waitForIdle()
         assertTrue(deferred.isCompleted)
         assertEquals(1000, animatedValue1)
 
         // seeking after the animation has completed should not change any value
-        coroutineScope.launch { seekableTransitionState.seekTo(fraction = 0.5f) }
+        rule.runOnIdle { coroutineScope.launch { seekableTransitionState.seekTo(fraction = 0.5f) } }
         rule.waitForIdle()
         rule.mainClock.advanceTimeByFrame()
         assertEquals(1000, animatedValue1)
@@ -1049,12 +1099,16 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        coroutineScope.launch { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+        }
         rule.waitForIdle()
         rule.mainClock.advanceTimeByFrame()
         val deferred =
-            coroutineScope.async {
-                seekableTransitionState.animateTo(animationSpec = tween(1000, 0, LinearEasing))
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.animateTo(animationSpec = tween(1000, 0, LinearEasing))
+                }
             }
         rule.mainClock.advanceTimeByFrame() // lock in the start time
         rule.mainClock.advanceTimeBy(64)
@@ -1102,9 +1156,11 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        coroutineScope.launch {
-            seekableTransitionState.seekTo(1f, AnimStates.To)
-            seekableTransitionState.animateTo(AnimStates.From)
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.seekTo(1f, AnimStates.To)
+                seekableTransitionState.animateTo(AnimStates.From)
+            }
         }
         rule.mainClock.advanceTimeByFrame() // let the composition happen after seekTo
         rule.runOnIdle { // seekTo() should run now, setting the animated value
@@ -1147,14 +1203,18 @@
         }
         rule.waitForIdle()
         val defer1 =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(1f, AnimStates.To)
-                seekableTransitionState.animateTo(AnimStates.From)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(1f, AnimStates.To)
+                    seekableTransitionState.animateTo(AnimStates.From)
+                }
             }
         val defer2 =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(1f, AnimStates.Other)
-                seekableTransitionState.animateTo(AnimStates.From)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(1f, AnimStates.Other)
+                    seekableTransitionState.animateTo(AnimStates.From)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // let the composition happen after seekTo
         rule.runOnIdle {
@@ -1200,14 +1260,18 @@
         }
         rule.waitForIdle()
         val defer1 =
-            coroutineScope.async {
-                seekableTransitionState.snapTo(AnimStates.To)
-                seekableTransitionState.animateTo(AnimStates.From)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.snapTo(AnimStates.To)
+                    seekableTransitionState.animateTo(AnimStates.From)
+                }
             }
         val defer2 =
-            coroutineScope.async {
-                seekableTransitionState.snapTo(AnimStates.Other)
-                seekableTransitionState.animateTo(AnimStates.From)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.snapTo(AnimStates.Other)
+                    seekableTransitionState.animateTo(AnimStates.From)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // let the composition happen after seekTo
         rule.runOnIdle {
@@ -1256,13 +1320,15 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        coroutineScope.launch {
-            seekableTransitionState.seekTo(1f, AnimStates.From)
-            seekableTransitionState.animateTo(AnimStates.To)
-        }
-        coroutineScope.launch {
-            seekableTransitionState.seekTo(1f, AnimStates.Other)
-            seekableTransitionState.animateTo(AnimStates.From)
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.seekTo(1f, AnimStates.From)
+                seekableTransitionState.animateTo(AnimStates.To)
+            }
+            coroutineScope.launch {
+                seekableTransitionState.seekTo(1f, AnimStates.Other)
+                seekableTransitionState.animateTo(AnimStates.From)
+            }
         }
         rule.mainClock.advanceTimeByFrame() // let the composition happen after seekTo
         rule.runOnIdle { assertEquals(2000, animatedValue1) }
@@ -1304,13 +1370,15 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        coroutineScope.launch {
-            seekableTransitionState.snapTo(AnimStates.From)
-            seekableTransitionState.animateTo(AnimStates.To)
-        }
-        coroutineScope.launch {
-            seekableTransitionState.snapTo(AnimStates.Other)
-            seekableTransitionState.animateTo(AnimStates.From)
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.snapTo(AnimStates.From)
+                seekableTransitionState.animateTo(AnimStates.To)
+            }
+            coroutineScope.launch {
+                seekableTransitionState.snapTo(AnimStates.Other)
+                seekableTransitionState.animateTo(AnimStates.From)
+            }
         }
         rule.mainClock.advanceTimeByFrame() // let the composition happen after snapTo
         rule.runOnIdle { assertEquals(2000, animatedValue1) }
@@ -1348,13 +1416,21 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        coroutineScope.launch { seekableTransitionState.seekTo(1f, AnimStates.To) }
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.seekTo(1f, AnimStates.To) }
+        }
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
-        val animation = coroutineScope.async { seekableTransitionState.animateTo(AnimStates.Other) }
+        val animation =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.Other) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
-        val snapTo = coroutineScope.async { seekableTransitionState.snapTo(AnimStates.From) }
+        val snapTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.snapTo(AnimStates.From) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle {
             assertTrue(animation.isCancelled)
@@ -1389,10 +1465,16 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        val seekTo = coroutineScope.async { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+        val seekTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle { assertTrue(seekTo.isCompleted) }
-        val snapTo = coroutineScope.async { seekableTransitionState.snapTo(AnimStates.To) }
+        val snapTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.snapTo(AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle {
             assertTrue(snapTo.isCompleted)
@@ -1426,10 +1508,16 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        val seekTo = coroutineScope.async { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+        val seekTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.seekTo(0.5f, AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle { assertTrue(seekTo.isCompleted) }
-        val snapTo = coroutineScope.async { seekableTransitionState.snapTo(AnimStates.From) }
+        val snapTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.snapTo(AnimStates.From) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle {
             assertTrue(snapTo.isCompleted)
@@ -1463,17 +1551,22 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        val snapTo = coroutineScope.async { seekableTransitionState.snapTo(AnimStates.From) }
+        val snapTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.snapTo(AnimStates.From) }
+            }
         rule.mainClock.advanceTimeByFrame()
         rule.runOnIdle {
             assertTrue(snapTo.isCompleted)
             assertEquals(0, animatedValue1)
         }
         val seekAndSnap =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(0.5f, AnimStates.To)
-                seekableTransitionState.snapTo(AnimStates.From)
-                seekableTransitionState.snapTo(AnimStates.From)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(0.5f, AnimStates.To)
+                    seekableTransitionState.snapTo(AnimStates.From)
+                    seekableTransitionState.snapTo(AnimStates.From)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // seekTo
         rule.mainClock.advanceTimeByFrame() // snapTo
@@ -1510,17 +1603,21 @@
         }
         rule.waitForIdle()
         val seekTo =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // wait for composition after seekTo
         rule.runOnIdle { assertTrue(seekTo.isCompleted) }
-        val animateTo = coroutineScope.async { seekableTransitionState.animateTo() }
+        val animateTo =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
         rule.mainClock.advanceTimeByFrame() // lock animation clock
         rule.mainClock.advanceTimeBy(160)
         rule.runOnIdle { assertEquals(160, animatedValue1) }
 
-        val animateTo2 = coroutineScope.async { seekableTransitionState.animateTo() }
+        val animateTo2 =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
 
         rule.runOnIdle { assertTrue(animateTo.isCancelled) }
 
@@ -1563,21 +1660,26 @@
         }
         rule.waitForIdle()
         val seekTo =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // wait for composition after seekTo
         rule.runOnIdle { assertTrue(seekTo.isCompleted) }
-        val animateTo = coroutineScope.async { seekableTransitionState.animateTo() }
+        val animateTo =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
         rule.mainClock.advanceTimeByFrame() // lock animation clock
         rule.mainClock.advanceTimeBy(160)
         rule.runOnIdle { assertEquals(160, animatedValue1) }
 
         val animateTo2 =
-            coroutineScope.async {
-                seekableTransitionState.animateTo(
-                    animationSpec = tween(durationMillis = 200, easing = LinearEasing)
-                )
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.animateTo(
+                        animationSpec = tween(durationMillis = 200, easing = LinearEasing)
+                    )
+                }
             }
 
         rule.runOnIdle { assertTrue(animateTo.isCancelled) }
@@ -1624,21 +1726,26 @@
         }
         rule.waitForIdle()
         val seekTo =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // wait for composition after seekTo
         rule.runOnIdle { assertTrue(seekTo.isCompleted) }
-        val animateTo = coroutineScope.async { seekableTransitionState.animateTo() }
+        val animateTo =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.animateTo() } }
         rule.mainClock.advanceTimeByFrame() // lock animation clock
         rule.mainClock.advanceTimeBy(800) // half way
         rule.runOnIdle { assertEquals(500, animatedValue1) }
 
-        coroutineScope.launch {
-            seekableTransitionState.animateTo(
-                animationSpec =
-                    spring(visibilityThreshold = 0.01f, stiffness = Spring.StiffnessVeryLow)
-            )
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.animateTo(
+                    animationSpec =
+                        spring(visibilityThreshold = 0.01f, stiffness = Spring.StiffnessVeryLow)
+                )
+            }
         }
 
         rule.runOnIdle { assertTrue(animateTo.isCancelled) }
@@ -1679,15 +1786,21 @@
         }
         rule.waitForIdle()
         val seekTo =
-            coroutineScope.async {
-                seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.seekTo(fraction = 0f, targetState = AnimStates.To)
+                }
             }
         rule.mainClock.advanceTimeByFrame() // wait for composition after seekTo
         rule.runOnIdle { assertTrue(seekTo.isCompleted) }
         val springSpec = spring<Float>(dampingRatio = 2f)
         val vecSpringSpec = springSpec.vectorize(Float.VectorConverter)
         val animateTo =
-            coroutineScope.async { seekableTransitionState.animateTo(animationSpec = springSpec) }
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.animateTo(animationSpec = springSpec)
+                }
+            }
         rule.mainClock.advanceTimeByFrame() // lock animation clock
 
         // find how long it takes to get to about half way:
@@ -1725,15 +1838,17 @@
                     initialVelocity = zeroVector
                 )[0]
 
-        coroutineScope.launch {
-            seekableTransitionState.animateTo(
-                animationSpec =
-                    spring(
-                        visibilityThreshold = 0.01f,
-                        stiffness = Spring.StiffnessVeryLow,
-                        dampingRatio = Spring.DampingRatioHighBouncy
-                    )
-            )
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.animateTo(
+                    animationSpec =
+                        spring(
+                            visibilityThreshold = 0.01f,
+                            stiffness = Spring.StiffnessVeryLow,
+                            dampingRatio = Spring.DampingRatioHighBouncy
+                        )
+                )
+            }
         }
 
         rule.runOnIdle { assertTrue(animateTo.isCancelled) }
@@ -1772,9 +1887,13 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        coroutineScope.launch { seekableTransitionState.animateTo(AnimStates.To) }
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.animateTo(AnimStates.To) }
+        }
         rule.mainClock.advanceTimeBy(1700)
-        coroutineScope.launch { seekableTransitionState.animateTo(AnimStates.From) }
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.animateTo(AnimStates.From) }
+        }
         rule.mainClock.advanceTimeByFrame() // lock in the clock
         rule.runOnIdle { assertEquals(1000, animatedValue1) }
         rule.mainClock.advanceTimeByFrame()
@@ -1823,14 +1942,18 @@
             )
         }
         rule.waitForIdle()
-        coroutineScope.launch {
-            seekableTransitionState.animateTo(
-                AnimStates.To,
-                animationSpec = tween(durationMillis = 160, easing = LinearEasing)
-            )
+        rule.runOnUiThread {
+            coroutineScope.launch {
+                seekableTransitionState.animateTo(
+                    AnimStates.To,
+                    animationSpec = tween(durationMillis = 160, easing = LinearEasing)
+                )
+            }
         }
         rule.mainClock.advanceTimeByFrame() // lock in the clock
-        coroutineScope.launch { seekableTransitionState.animateTo(AnimStates.Other) }
+        rule.runOnUiThread {
+            coroutineScope.launch { seekableTransitionState.animateTo(AnimStates.Other) }
+        }
         rule.mainClock.advanceTimeByFrame() // advance one frame toward To and compose to Other
         rule.runOnIdle {
             assertEquals(100, animatedValue1)
@@ -1879,13 +2002,19 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        val seekTo = coroutineScope.async { seekableTransitionState.seekTo(1f, AnimStates.To) }
+        val seekTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.seekTo(1f, AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame() // wait for composition
         rule.runOnIdle {
             assertTrue(seekTo.isCompleted)
             assertEquals(1000, animatedValue1)
         }
-        val anim = coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+        val anim =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame() // compose to current state = target state
         rule.runOnIdle {
             assertTrue(anim.isCompleted)
@@ -1917,7 +2046,8 @@
             Box(Modifier.fillMaxSize().drawBehind { animatedValue1 = val1.value })
         }
         rule.waitForIdle()
-        val seekTo = coroutineScope.async { seekableTransitionState.seekTo(0.5f) }
+        val seekTo =
+            rule.runOnUiThread { coroutineScope.async { seekableTransitionState.seekTo(0.5f) } }
         rule.runOnIdle {
             assertTrue(seekTo.isCompleted)
             assertEquals(0, animatedValue1)
@@ -1964,17 +2094,24 @@
             )
         }
         rule.waitForIdle()
-        val seekTo = coroutineScope.async { seekableTransitionState.seekTo(0f, AnimStates.To) }
+        val seekTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.seekTo(0f, AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame() // compose to To
         assertTrue(seekTo.isCompleted)
         val seekOther =
-            coroutineScope.async { seekableTransitionState.seekTo(1f, AnimStates.Other) }
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.seekTo(1f, AnimStates.Other) }
+            }
         rule.mainClock.advanceTimeByFrame() // compose to Other
         assertFalse(seekOther.isCompleted) // should be animating animatedValue2
         val animateOther =
-            coroutineScope.async {
-                // already at the end (1f), but it should continue the animatedValue2 animation
-                seekableTransitionState.animateTo(AnimStates.Other)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    // already at the end (1f), but it should continue the animatedValue2 animation
+                    seekableTransitionState.animateTo(AnimStates.Other)
+                }
             }
         assertTrue(seekOther.isCancelled)
         assertTrue(animateOther.isActive)
@@ -2029,7 +2166,10 @@
             )
         }
         rule.waitForIdle()
-        val anim = coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+        val anim =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame() // wait for composition
         rule.mainClock.advanceTimeBy(800) // half way through
         rule.runOnIdle {
@@ -2101,7 +2241,10 @@
             )
         }
         rule.waitForIdle()
-        val animateTo = coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+        val animateTo =
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+            }
         rule.mainClock.advanceTimeByFrame() // wait for composition
         rule.mainClock.advanceTimeBy(800) // half way through
 
@@ -2120,7 +2263,9 @@
 
         // now seek to third state
         val animateOther =
-            coroutineScope.async { seekableTransitionState.animateTo(AnimStates.Other) }
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.Other) }
+            }
         assertTrue(animateTo.isCancelled)
         rule.mainClock.advanceTimeByFrame() // wait for composition
         rule.runOnIdle {
@@ -2203,10 +2348,12 @@
         }
         rule.waitForIdle()
         val initialAnimateAndSeek =
-            coroutineScope.async {
-                seekableTransitionState.animateTo(AnimStates.To)
-                seekableTransitionState.seekTo(0.5f, targetState = AnimStates.From)
-                seekableTransitionState.seekTo(0f, targetState = AnimStates.From)
+            rule.runOnUiThread {
+                coroutineScope.async {
+                    seekableTransitionState.animateTo(AnimStates.To)
+                    seekableTransitionState.seekTo(0.5f, targetState = AnimStates.From)
+                    seekableTransitionState.seekTo(0f, targetState = AnimStates.From)
+                }
             }
         rule.mainClock.advanceTimeBy(5000)
         rule.runOnIdle {
@@ -2222,7 +2369,9 @@
             }
         }
         val secondAnimate =
-            coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+            rule.runOnUiThread {
+                coroutineScope.async { seekableTransitionState.animateTo(AnimStates.To) }
+            }
         rule.waitForIdle()
         // This waits for the initial state animation to finish, since we changed the initial state
         // when going from seeking to animating.
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/TransitionTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
index ae20509..618516f 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
@@ -40,15 +40,20 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
+import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.Rule
 import org.junit.Test
+import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @LargeTest
 class TransitionTest {
+    private val rule = createComposeRule()
 
-    @get:Rule val rule = createComposeRule()
+    // Detect leaks BEFORE and AFTER compose rule work
+    @get:Rule
+    val ruleChain: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()).around(rule)
 
     private enum class AnimStates {
         From,
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt
index 86ba661..816bec4 100644
--- a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/EasingTest.kt
@@ -28,8 +28,8 @@
 
 @RunWith(JUnit4::class)
 class EasingTest {
-    private val ZeroEpsilon = -(1.0f.ulp)
-    private val OneEpsilon = 1.0f + 1.0f.ulp
+    private val ZeroEpsilon = -(1.0f.ulp * 2.0f)
+    private val OneEpsilon = 1.0f + 1.0f.ulp * 2.0f
 
     @Test
     fun cubicBezierStartsAt0() {
@@ -108,12 +108,49 @@
 
     @Test
     fun canSolveCubicForFractionsCloseToOne() {
-        val curve = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
+        // Only test curves defined in [0..1]
+        // For instance, EaseInOutBack is defined in a larger domain, so exclude it from the list
+        val curves =
+            listOf(
+                CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f),
+                Ease,
+                EaseIn,
+                EaseInBack,
+                EaseInCirc,
+                EaseInCubic,
+                EaseInExpo,
+                EaseInOut,
+                EaseInOutCirc,
+                EaseInOutCubic,
+                EaseInOutExpo,
+                EaseInOutQuad,
+                EaseInOutQuart,
+                EaseInOutQuint,
+                EaseInOutSine,
+                EaseInOutQuad,
+                EaseInOutQuart,
+                EaseInOutQuint,
+                EaseInSine,
+                EaseOut,
+                EaseOutCirc,
+                EaseOutCubic,
+                EaseOutExpo,
+                EaseOutQuad,
+                EaseOutQuart,
+                EaseOutQuint,
+                EaseOutSine
+            )
 
-        // Test the last 16 ulps until 1.0f
-        for (i in 0x3f7ffff0..0x3f7fffff) {
-            val t = curve.transform(floatFromBits(i))
-            assertTrue(t in -ZeroEpsilon..OneEpsilon)
+        for (curve in curves) {
+            // Test the last 16 ulps until 1.0f
+            for (i in 0x3f7ffff0..0x3f7fffff) {
+                val fraction = floatFromBits(i)
+                val t = curve.transform(fraction)
+                assertTrue(
+                    "f($fraction) = $t out of range for $curve",
+                    t in -ZeroEpsilon..OneEpsilon
+                )
+            }
         }
     }
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
index 2972e55..e55b79a 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animatable.kt
@@ -46,11 +46,11 @@
  * @see animateDecay
  */
 @Suppress("NotCloseable")
-class Animatable<T, V : AnimationVector>(
+public class Animatable<T, V : AnimationVector>(
     initialValue: T,
-    val typeConverter: TwoWayConverter<T, V>,
+    public val typeConverter: TwoWayConverter<T, V>,
     private val visibilityThreshold: T? = null,
-    val label: String = "Animatable"
+    public val label: String = "Animatable"
 ) {
 
     @Deprecated(
@@ -61,7 +61,7 @@
             ),
         DeprecationLevel.HIDDEN
     )
-    constructor(
+    public constructor(
         initialValue: T,
         typeConverter: TwoWayConverter<T, V>,
         visibilityThreshold: T? = null
@@ -71,26 +71,26 @@
         AnimationState(typeConverter = typeConverter, initialValue = initialValue)
 
     /** Current value of the animation. */
-    val value: T
+    public val value: T
         get() = internalState.value
 
     /** Velocity vector of the animation (in the form of [AnimationVector]. */
-    val velocityVector: V
+    public val velocityVector: V
         get() = internalState.velocityVector
 
     /** Returns the velocity, converted from [velocityVector]. */
-    val velocity: T
+    public val velocity: T
         get() = typeConverter.convertFromVector(velocityVector)
 
     /** Indicates whether the animation is running. */
-    var isRunning: Boolean by mutableStateOf(false)
+    public var isRunning: Boolean by mutableStateOf(false)
         private set
 
     /**
      * The target of the current animation. If the animation finishes un-interrupted, it will reach
      * this target value.
      */
-    var targetValue: T by mutableStateOf(initialValue)
+    public var targetValue: T by mutableStateOf(initialValue)
         private set
 
     /**
@@ -101,7 +101,7 @@
      * example: For an Animatable<Offset> with an [lowerBound] set to Offset(100f, 200f), when the
      * [value].x drops below 100f *or* [value].y drops below 200f, the animation will stop.
      */
-    var lowerBound: T? = null
+    public var lowerBound: T? = null
         private set
 
     /**
@@ -112,7 +112,7 @@
      * example: For an Animatable<Offset> with an [upperBound] set to Offset(100f, 200f), when the
      * [value].x exceeds 100f *or* [value].y exceeds 200f, the animation will stop.
      */
-    var upperBound: T? = null
+    public var upperBound: T? = null
         private set
 
     private val mutatorMutex = MutatorMutex()
@@ -159,7 +159,7 @@
      * @throws [IllegalStateException] if the [lowerBound] is greater than [upperBound] in any
      *   dimension.
      */
-    fun updateBounds(lowerBound: T? = this.lowerBound, upperBound: T? = this.upperBound) {
+    public fun updateBounds(lowerBound: T? = this.lowerBound, upperBound: T? = this.upperBound) {
         val lowerBoundVector =
             lowerBound?.run { typeConverter.convertToVector(this) } ?: negativeInfinityBounds
 
@@ -221,7 +221,7 @@
      *
      * @sample androidx.compose.animation.core.samples.AnimatableFadeIn
      */
-    suspend fun animateTo(
+    public suspend fun animateTo(
         targetValue: T,
         animationSpec: AnimationSpec<T> = defaultSpringSpec,
         initialVelocity: T = velocity,
@@ -269,7 +269,7 @@
      *
      * @sample androidx.compose.animation.core.samples.AnimatableDecayAndAnimateToSample
      */
-    suspend fun animateDecay(
+    public suspend fun animateDecay(
         initialVelocity: T,
         animationSpec: DecayAnimationSpec<T>,
         block: (Animatable<T, V>.() -> Unit)? = null
@@ -373,7 +373,7 @@
      * @see animateDecay
      * @see stop
      */
-    suspend fun snapTo(targetValue: T) {
+    public suspend fun snapTo(targetValue: T) {
         mutatorMutex.mutate {
             endAnimation()
             val clampedValue = clampToBounds(targetValue)
@@ -396,7 +396,7 @@
      * @see animateDecay
      * @see snapTo
      */
-    suspend fun stop() {
+    public suspend fun stop() {
         mutatorMutex.mutate { endAnimation() }
     }
 
@@ -405,7 +405,7 @@
      * the animation's current value without causing unnecessary recompositions when the value
      * changes.
      */
-    fun asState(): State<T> = internalState
+    public fun asState(): State<T> = internalState
 }
 
 /**
@@ -426,10 +426,11 @@
  * @param visibilityThreshold Threshold at which the animation may round off to its target value.
  *   [Spring.DefaultDisplacementThreshold] by default.
  */
-fun Animatable(
+public fun Animatable(
     initialValue: Float,
     visibilityThreshold: Float = Spring.DefaultDisplacementThreshold
-) = Animatable(initialValue, Float.VectorConverter, visibilityThreshold)
+): Animatable<Float, AnimationVector1D> =
+    Animatable(initialValue, Float.VectorConverter, visibilityThreshold)
 
 // TODO: Consider some version of @Composable fun<T, V: AnimationVector> Animatable<T, V>.animateTo
 /**
@@ -444,13 +445,13 @@
  *
  * @sample androidx.compose.animation.core.samples.AnimatableAnimationResultSample
  */
-class AnimationResult<T, V : AnimationVector>(
+public class AnimationResult<T, V : AnimationVector>(
     /**
      * The state of the animation in its last frame before it's canceled or reset. This captures the
      * animation value/velocity/frame time, etc at the point of interruption, or before the velocity
      * is reset when the animation finishes successfully.
      */
-    val endState: AnimationState<T, V>,
+    public val endState: AnimationState<T, V>,
     /**
      * The reason why the animation has ended. Could be either of the following:
      * - [Finished], when the animation finishes successfully without any interruption
@@ -458,7 +459,7 @@
      *   [upperBound][Animatable.upperBound] in any dimension, the animation will end with
      *   [BoundReached] being the end reason.
      */
-    val endReason: AnimationEndReason
+    public val endReason: AnimationEndReason
 ) {
     override fun toString(): String = "AnimationResult(endReason=$endReason, endState=$endState)"
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimateAsState.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimateAsState.kt
index 250187a..0a68448 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimateAsState.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimateAsState.kt
@@ -59,7 +59,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateFloatAsState(
+public fun animateFloatAsState(
     targetValue: Float,
     animationSpec: AnimationSpec<Float> = defaultAnimation,
     visibilityThreshold: Float = 0.01f,
@@ -104,7 +104,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateDpAsState(
+public fun animateDpAsState(
     targetValue: Dp,
     animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
     label: String = "DpAnimation",
@@ -145,7 +145,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateSizeAsState(
+public fun animateSizeAsState(
     targetValue: Size,
     animationSpec: AnimationSpec<Size> = sizeDefaultSpring,
     label: String = "SizeAnimation",
@@ -184,7 +184,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateOffsetAsState(
+public fun animateOffsetAsState(
     targetValue: Offset,
     animationSpec: AnimationSpec<Offset> = offsetDefaultSpring,
     label: String = "OffsetAnimation",
@@ -225,7 +225,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateRectAsState(
+public fun animateRectAsState(
     targetValue: Rect,
     animationSpec: AnimationSpec<Rect> = rectDefaultSpring,
     label: String = "RectAnimation",
@@ -263,7 +263,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateIntAsState(
+public fun animateIntAsState(
     targetValue: Int,
     animationSpec: AnimationSpec<Int> = intDefaultSpring,
     label: String = "IntAnimation",
@@ -302,7 +302,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateIntOffsetAsState(
+public fun animateIntOffsetAsState(
     targetValue: IntOffset,
     animationSpec: AnimationSpec<IntOffset> = intOffsetDefaultSpring,
     label: String = "IntOffsetAnimation",
@@ -340,7 +340,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun animateIntSizeAsState(
+public fun animateIntSizeAsState(
     targetValue: IntSize,
     animationSpec: AnimationSpec<IntSize> = intSizeDefaultSpring,
     label: String = "IntSizeAnimation",
@@ -384,7 +384,7 @@
  * @return A [State] object, the value of which is updated by animation.
  */
 @Composable
-fun <T, V : AnimationVector> animateValueAsState(
+public fun <T, V : AnimationVector> animateValueAsState(
     targetValue: T,
     typeConverter: TwoWayConverter<T, V>,
     animationSpec: AnimationSpec<T> = remember { spring() },
@@ -436,7 +436,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateFloatAsState(
+public fun animateFloatAsState(
     targetValue: Float,
     animationSpec: AnimationSpec<Float> = defaultAnimation,
     visibilityThreshold: Float = 0.01f,
@@ -454,7 +454,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateDpAsState(
+public fun animateDpAsState(
     targetValue: Dp,
     animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
     finishedListener: ((Dp) -> Unit)? = null
@@ -472,7 +472,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateSizeAsState(
+public fun animateSizeAsState(
     targetValue: Size,
     animationSpec: AnimationSpec<Size> = sizeDefaultSpring,
     finishedListener: ((Size) -> Unit)? = null
@@ -490,7 +490,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateOffsetAsState(
+public fun animateOffsetAsState(
     targetValue: Offset,
     animationSpec: AnimationSpec<Offset> = offsetDefaultSpring,
     finishedListener: ((Offset) -> Unit)? = null
@@ -508,7 +508,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateRectAsState(
+public fun animateRectAsState(
     targetValue: Rect,
     animationSpec: AnimationSpec<Rect> = rectDefaultSpring,
     finishedListener: ((Rect) -> Unit)? = null
@@ -526,7 +526,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateIntAsState(
+public fun animateIntAsState(
     targetValue: Int,
     animationSpec: AnimationSpec<Int> = intDefaultSpring,
     finishedListener: ((Int) -> Unit)? = null
@@ -544,7 +544,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateIntOffsetAsState(
+public fun animateIntOffsetAsState(
     targetValue: IntOffset,
     animationSpec: AnimationSpec<IntOffset> = intOffsetDefaultSpring,
     finishedListener: ((IntOffset) -> Unit)? = null
@@ -562,7 +562,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateIntSizeAsState(
+public fun animateIntSizeAsState(
     targetValue: IntSize,
     animationSpec: AnimationSpec<IntSize> = intSizeDefaultSpring,
     finishedListener: ((IntSize) -> Unit)? = null
@@ -580,7 +580,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun <T, V : AnimationVector> animateValueAsState(
+public fun <T, V : AnimationVector> animateValueAsState(
     targetValue: T,
     typeConverter: TwoWayConverter<T, V>,
     animationSpec: AnimationSpec<T> = remember { spring() },
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
index c2d4513..dd84983 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
@@ -17,6 +17,7 @@
 package androidx.compose.animation.core
 
 import androidx.annotation.RestrictTo
+import androidx.compose.animation.core.internal.JvmDefaultWithCompatibility
 
 /**
  * This interface provides a convenient way to query from an [VectorizedAnimationSpec] or
@@ -37,47 +38,47 @@
  * @see [updateTransition]
  */
 @JvmDefaultWithCompatibility
-interface Animation<T, V : AnimationVector> {
+public interface Animation<T, V : AnimationVector> {
     /** This amount of time in nanoseconds that the animation will run before it finishes */
-    @get:Suppress("MethodNameUnits") val durationNanos: Long
+    @get:Suppress("MethodNameUnits") public val durationNanos: Long
 
     /**
      * The [TwoWayConverter] that will be used to convert value/velocity from any arbitrary data
      * type to [AnimationVector]. This makes it possible to animate different dimensions of the data
      * object independently (e.g. x/y dimensions of the position data).
      */
-    val typeConverter: TwoWayConverter<T, V>
+    public val typeConverter: TwoWayConverter<T, V>
 
     /** This is the value that the [Animation] will reach when it finishes uninterrupted. */
-    val targetValue: T
+    public val targetValue: T
 
     /**
      * Whether or not the [Animation] represents an infinite animation. That is, one that will not
      * finish by itself, one that needs an external action to stop. For examples, an indeterminate
      * progress bar, which will only stop when it is removed from the composition.
      */
-    val isInfinite: Boolean
+    public val isInfinite: Boolean
 
     /**
      * Returns the value of the animation at the given play time.
      *
      * @param playTimeNanos the play time that is used to determine the value of the animation.
      */
-    fun getValueFromNanos(playTimeNanos: Long): T
+    public fun getValueFromNanos(playTimeNanos: Long): T
 
     /**
      * Returns the velocity (in [AnimationVector] form) of the animation at the given play time.
      *
      * @param playTimeNanos the play time that is used to calculate the velocity of the animation.
      */
-    fun getVelocityVectorFromNanos(playTimeNanos: Long): V
+    public fun getVelocityVectorFromNanos(playTimeNanos: Long): V
 
     /**
      * Returns whether the animation is finished at the given play time.
      *
      * @param playTimeNanos the play time used to determine whether the animation is finished.
      */
-    fun isFinishedFromNanos(playTimeNanos: Long): Boolean {
+    public fun isFinishedFromNanos(playTimeNanos: Long): Boolean {
         return playTimeNanos >= durationNanos
     }
 }
@@ -94,7 +95,7 @@
  *
  * @param playTimeNanos the play time that is used to calculate the velocity of the animation.
  */
-fun <T, V : AnimationVector> Animation<T, V>.getVelocityFromNanos(playTimeNanos: Long): T =
+public fun <T, V : AnimationVector> Animation<T, V>.getVelocityFromNanos(playTimeNanos: Long): T =
     typeConverter.convertFromVector(getVelocityVectorFromNanos(playTimeNanos))
 
 /**
@@ -107,7 +108,7 @@
  * @param initialVelocity the initial velocity to start the animation at
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-fun <V : AnimationVector> VectorizedAnimationSpec<V>.createAnimation(
+public fun <V : AnimationVector> VectorizedAnimationSpec<V>.createAnimation(
     initialValue: V,
     targetValue: V,
     initialVelocity: V
@@ -141,13 +142,13 @@
  * @param initialVelocity the start velocity (of type [T] of the animation
  * @param typeConverter the [TwoWayConverter] that is used to convert animation type [T] from/to [V]
  */
-fun <T, V : AnimationVector> TargetBasedAnimation(
+public fun <T, V : AnimationVector> TargetBasedAnimation(
     animationSpec: AnimationSpec<T>,
     typeConverter: TwoWayConverter<T, V>,
     initialValue: T,
     targetValue: T,
     initialVelocity: T
-) =
+): TargetBasedAnimation<T, V> =
     TargetBasedAnimation(
         animationSpec,
         typeConverter,
@@ -180,7 +181,7 @@
  * @see [updateTransition]
  * @see [Animatable]
  */
-class TargetBasedAnimation<T, V : AnimationVector>
+public class TargetBasedAnimation<T, V : AnimationVector>
 internal constructor(
     internal val animationSpec: VectorizedAnimationSpec<V>,
     override val typeConverter: TwoWayConverter<T, V>,
@@ -208,7 +209,7 @@
             }
         }
 
-    val initialValue: T
+    public val initialValue: T
         get() = mutableInitialValue
 
     override val targetValue: T
@@ -236,7 +237,7 @@
      * @param targetValue the end value of the animation
      * @param initialVelocityVector the start velocity vector, null by default (meaning 0 velocity).
      */
-    constructor(
+    public constructor(
         animationSpec: AnimationSpec<T>,
         typeConverter: TwoWayConverter<T, V>,
         initialValue: T,
@@ -345,15 +346,15 @@
  * @see Animatable.animateDecay
  * @see AnimationState.animateDecay
  */
-class DecayAnimation<T, V : AnimationVector> /*@VisibleForTesting*/
+public class DecayAnimation<T, V : AnimationVector> /*@VisibleForTesting*/
 constructor(
     private val animationSpec: VectorizedDecayAnimationSpec<V>,
     override val typeConverter: TwoWayConverter<T, V>,
-    val initialValue: T,
+    public val initialValue: T,
     initialVelocityVector: V
 ) : Animation<T, V> {
     private val initialValueVector: V = typeConverter.convertToVector(initialValue)
-    val initialVelocityVector: V = initialVelocityVector.copy()
+    public val initialVelocityVector: V = initialVelocityVector.copy()
     private val endVelocity: V
 
     override val targetValue: T =
@@ -383,7 +384,7 @@
      * @see Animatable.animateDecay
      * @see AnimationState.animateDecay
      */
-    constructor(
+    public constructor(
         animationSpec: DecayAnimationSpec<T>,
         typeConverter: TwoWayConverter<T, V>,
         initialValue: T,
@@ -413,7 +414,7 @@
      * @see Animatable.animateDecay
      * @see AnimationState.animateDecay
      */
-    constructor(
+    public constructor(
         animationSpec: DecayAnimationSpec<T>,
         typeConverter: TwoWayConverter<T, V>,
         initialValue: T,
@@ -482,11 +483,11 @@
  * @param initialValue starting value that will be passed to the decay animation
  * @param initialVelocity starting velocity for the decay animation, 0f by default
  */
-fun DecayAnimation(
+public fun DecayAnimation(
     animationSpec: FloatDecayAnimationSpec,
     initialValue: Float,
     initialVelocity: Float = 0f
-) =
+): DecayAnimation<Float, AnimationVector1D> =
     DecayAnimation(
         animationSpec.generateDecayAnimationSpec(),
         Float.VectorConverter,
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationEndReason.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationEndReason.kt
index 1406399..e27f985 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationEndReason.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationEndReason.kt
@@ -17,7 +17,7 @@
 package androidx.compose.animation.core
 
 /** Possible reasons for [Animatable]s to end. */
-enum class AnimationEndReason {
+public enum class AnimationEndReason {
     /**
      * Animation will be forced to end when its value reaches upper/lower bound (if they have been
      * defined, e.g. via [Animatable.updateBounds])
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
index 61e8977..173f3af 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
@@ -34,12 +34,12 @@
 import androidx.compose.ui.util.fastRoundToInt
 import kotlin.math.abs
 
-object AnimationConstants {
+public object AnimationConstants {
     /** The default duration used in [VectorizedAnimationSpec]s and [AnimationSpec]. */
-    const val DefaultDurationMillis: Int = 300
+    public const val DefaultDurationMillis: Int = 300
 
     /** The value that is used when the animation time is not yet set. */
-    const val UnspecifiedTime: Long = Long.MIN_VALUE
+    public const val UnspecifiedTime: Long = Long.MIN_VALUE
 }
 
 /**
@@ -56,7 +56,7 @@
  * multi-dimensional way. It is particularly useful for smoothly handling animation interruptions
  * (such as when the target changes during the animation).
  */
-interface AnimationSpec<T> {
+public interface AnimationSpec<T> {
     /**
      * Creates a [VectorizedAnimationSpec] with the given [TwoWayConverter].
      *
@@ -68,7 +68,7 @@
      *
      * @param converter converts the type [T] from and to [AnimationVector] type
      */
-    fun <V : AnimationVector> vectorize(
+    public fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
     ): VectorizedAnimationSpec<V>
 }
@@ -80,7 +80,7 @@
  *
  * @see [InfiniteRepeatableSpec]
  */
-interface FiniteAnimationSpec<T> : AnimationSpec<T> {
+public interface FiniteAnimationSpec<T> : AnimationSpec<T> {
     override fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
     ): VectorizedFiniteAnimationSpec<V>
@@ -94,14 +94,15 @@
  * @param easing the easing curve used by the animation. [FastOutSlowInEasing] by default.
  */
 @Immutable
-class TweenSpec<T>(
-    val durationMillis: Int = DefaultDurationMillis,
-    val delay: Int = 0,
-    val easing: Easing = FastOutSlowInEasing
+public class TweenSpec<T>(
+    public val durationMillis: Int = DefaultDurationMillis,
+    public val delay: Int = 0,
+    public val easing: Easing = FastOutSlowInEasing
 ) : DurationBasedAnimationSpec<T> {
 
-    override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>) =
-        VectorizedTweenSpec<V>(durationMillis, delay, easing)
+    override fun <V : AnimationVector> vectorize(
+        converter: TwoWayConverter<T, V>
+    ): VectorizedTweenSpec<V> = VectorizedTweenSpec<V>(durationMillis, delay, easing)
 
     override fun equals(other: Any?): Boolean =
         if (other is TweenSpec<*>) {
@@ -122,7 +123,7 @@
  * [TweenSpec], and [SnapSpec]. These duration based specs can repeated when put into a
  * [RepeatableSpec].
  */
-interface DurationBasedAnimationSpec<T> : FiniteAnimationSpec<T> {
+public interface DurationBasedAnimationSpec<T> : FiniteAnimationSpec<T> {
     override fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
     ): VectorizedDurationBasedAnimationSpec<V>
@@ -139,13 +140,15 @@
  */
 // TODO: annotate damping/stiffness with FloatRange
 @Immutable
-class SpringSpec<T>(
-    val dampingRatio: Float = Spring.DampingRatioNoBouncy,
-    val stiffness: Float = Spring.StiffnessMedium,
-    val visibilityThreshold: T? = null
+public class SpringSpec<T>(
+    public val dampingRatio: Float = Spring.DampingRatioNoBouncy,
+    public val stiffness: Float = Spring.StiffnessMedium,
+    public val visibilityThreshold: T? = null
 ) : FiniteAnimationSpec<T> {
 
-    override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>) =
+    override fun <V : AnimationVector> vectorize(
+        converter: TwoWayConverter<T, V>
+    ): VectorizedSpringSpec<V> =
         VectorizedSpringSpec(dampingRatio, stiffness, converter.convert(visibilityThreshold))
 
     override fun equals(other: Any?): Boolean =
@@ -199,11 +202,11 @@
  */
 @ExperimentalAnimationSpecApi
 @Immutable
-class ArcAnimationSpec<T>(
-    val mode: ArcMode = ArcBelow,
-    val durationMillis: Int = DefaultDurationMillis,
-    val delayMillis: Int = 0,
-    val easing: Easing = FastOutSlowInEasing // Same default as tween()
+public class ArcAnimationSpec<T>(
+    public val mode: ArcMode = ArcBelow,
+    public val durationMillis: Int = DefaultDurationMillis,
+    public val delayMillis: Int = 0,
+    public val easing: Easing = FastOutSlowInEasing // Same default as tween()
 ) : DurationBasedAnimationSpec<T> {
     override fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
@@ -247,13 +250,13 @@
  * @see StartOffset
  */
 @kotlin.jvm.JvmInline
-value class StartOffsetType private constructor(internal val value: Int) {
-    companion object {
+public value class StartOffsetType private constructor(internal val value: Int) {
+    public companion object {
         /** Delays the start of the animation. */
-        val Delay = StartOffsetType(-1)
+        public val Delay: StartOffsetType = StartOffsetType(-1)
 
         /** Fast forwards the animation to a given play time, and starts it immediately. */
-        val FastForward = StartOffsetType(1)
+        public val FastForward: StartOffsetType = StartOffsetType(1)
     }
 }
 
@@ -268,7 +271,7 @@
 // This is an inline of Long so that when adding a StartOffset param to the end of constructor
 // param list, it won't be confused with/clash with the mask param generated by constructors.
 @kotlin.jvm.JvmInline
-value class StartOffset private constructor(internal val value: Long) {
+public value class StartOffset private constructor(internal val value: Long) {
     /**
      * This creates a start offset for [repeatable] and [infiniteRepeatable]. [offsetType] can be
      * either of the following: [StartOffsetType.Delay] and [StartOffsetType.FastForward].
@@ -278,17 +281,17 @@
      * [StartOffsetType.FastForward] starts the animation right away from [offsetMillis] in the
      * animation.
      */
-    constructor(
+    public constructor(
         offsetMillis: Int,
         offsetType: StartOffsetType = StartOffsetType.Delay
     ) : this((offsetMillis * offsetType.value).toLong())
 
     /** Returns the number of milliseconds to offset the start of the animation. */
-    val offsetMillis: Int
+    public val offsetMillis: Int
         get() = abs(this.value.toInt())
 
     /** Returns the offset type of the provided [StartOffset]. */
-    val offsetType: StartOffsetType
+    public val offsetType: StartOffsetType
         get() =
             when (this.value > 0) {
                 true -> StartOffsetType.FastForward
@@ -318,15 +321,15 @@
  * @see infiniteRepeatable
  */
 @Immutable
-class RepeatableSpec<T>(
-    val iterations: Int,
-    val animation: DurationBasedAnimationSpec<T>,
-    val repeatMode: RepeatMode = RepeatMode.Restart,
-    val initialStartOffset: StartOffset = StartOffset(0)
+public class RepeatableSpec<T>(
+    public val iterations: Int,
+    public val animation: DurationBasedAnimationSpec<T>,
+    public val repeatMode: RepeatMode = RepeatMode.Restart,
+    public val initialStartOffset: StartOffset = StartOffset(0)
 ) : FiniteAnimationSpec<T> {
 
     @Deprecated(level = DeprecationLevel.HIDDEN, message = "This constructor has been deprecated")
-    constructor(
+    public constructor(
         iterations: Int,
         animation: DurationBasedAnimationSpec<T>,
         repeatMode: RepeatMode = RepeatMode.Restart
@@ -379,14 +382,14 @@
  * @see infiniteRepeatable
  */
 // TODO: Consider supporting repeating spring specs
-class InfiniteRepeatableSpec<T>(
-    val animation: DurationBasedAnimationSpec<T>,
-    val repeatMode: RepeatMode = RepeatMode.Restart,
-    val initialStartOffset: StartOffset = StartOffset(0)
+public class InfiniteRepeatableSpec<T>(
+    public val animation: DurationBasedAnimationSpec<T>,
+    public val repeatMode: RepeatMode = RepeatMode.Restart,
+    public val initialStartOffset: StartOffset = StartOffset(0)
 ) : AnimationSpec<T> {
 
     @Deprecated(level = DeprecationLevel.HIDDEN, message = "This constructor has been deprecated")
-    constructor(
+    public constructor(
         animation: DurationBasedAnimationSpec<T>,
         repeatMode: RepeatMode = RepeatMode.Restart
     ) : this(animation, repeatMode, StartOffset(0))
@@ -417,7 +420,7 @@
 }
 
 /** Repeat mode for [RepeatableSpec] and [VectorizedRepeatableSpec]. */
-enum class RepeatMode {
+public enum class RepeatMode {
     /** [Restart] will restart the animation and animate from the start value to the end value. */
     Restart,
 
@@ -433,7 +436,7 @@
  *   starts. Defaults to 0.
  */
 @Immutable
-class SnapSpec<T>(val delay: Int = 0) : DurationBasedAnimationSpec<T> {
+public class SnapSpec<T>(public val delay: Int = 0) : DurationBasedAnimationSpec<T> {
     override fun <V : AnimationVector> vectorize(
         converter: TwoWayConverter<T, V>
     ): VectorizedDurationBasedAnimationSpec<V> = VectorizedSnapSpec(delay)
@@ -451,20 +454,20 @@
 }
 
 /** Shared configuration class used as DSL for keyframe based animations. */
-sealed class KeyframesSpecBaseConfig<T, E : KeyframeBaseEntity<T>> {
+public sealed class KeyframesSpecBaseConfig<T, E : KeyframeBaseEntity<T>> {
     /**
      * Duration of the animation in milliseconds. The minimum is `0` and defaults to
      * [DefaultDurationMillis]
      */
     @get:IntRange(from = 0L)
     @setparam:IntRange(from = 0L)
-    var durationMillis: Int = DefaultDurationMillis
+    public var durationMillis: Int = DefaultDurationMillis
 
     /**
      * The amount of time that the animation should be delayed. The minimum is `0` and defaults
      * to 0.
      */
-    @get:IntRange(from = 0L) @setparam:IntRange(from = 0L) var delayMillis: Int = 0
+    @get:IntRange(from = 0L) @setparam:IntRange(from = 0L) public var delayMillis: Int = 0
 
     internal val keyframes = mutableIntObjectMapOf<E>()
 
@@ -480,7 +483,7 @@
      * @return an instance of [E] so a custom [Easing] can be added by the [using] method.
      */
     // needed as `open` to guarantee binary compatibility in KeyframesSpecConfig
-    open infix fun T.at(@IntRange(from = 0) timeStamp: Int): E {
+    public open infix fun T.at(@IntRange(from = 0) timeStamp: Int): E {
         val entity = createEntityFor(this)
         keyframes[timeStamp] = entity
         return entity
@@ -496,7 +499,7 @@
      * @return an instance of [E] so a custom [Easing] can be added by the [using] method
      */
     // needed as `open` to guarantee binary compatibility in KeyframesSpecConfig
-    open infix fun T.atFraction(@FloatRange(from = 0.0, to = 1.0) fraction: Float): E {
+    public open infix fun T.atFraction(@FloatRange(from = 0.0, to = 1.0) fraction: Float): E {
         return at((durationMillis * fraction).fastRoundToInt())
     }
 
@@ -508,14 +511,14 @@
      * @param easing [Easing] to be used for the next interval.
      * @return the same [E] instance so that other implementations can expand on the builder pattern
      */
-    infix fun E.using(easing: Easing): E {
+    public infix fun E.using(easing: Easing): E {
         this.easing = easing
         return this
     }
 }
 
 /** Base holder class for building a keyframes animation. */
-sealed class KeyframeBaseEntity<T>(internal val value: T, internal var easing: Easing) {
+public sealed class KeyframeBaseEntity<T>(internal val value: T, internal var easing: Easing) {
     internal fun <V : AnimationVector> toPair(convertToVector: (T) -> V) =
         convertToVector.invoke(value) to easing
 }
@@ -530,18 +533,23 @@
  *
  * @sample androidx.compose.animation.core.samples.FloatKeyframesBuilder
  *
- * You can also provide a custom [Easing] for the interval with use of [with] function applied for
- * the interval starting keyframe.
+ * For each interval, you may provide a custom [Easing] by use of the [KeyframesSpecConfig.using]
+ * function.
  *
  * @sample androidx.compose.animation.core.samples.KeyframesBuilderWithEasing
  *
- * Values can be animated using arcs of quarter of an Ellipse with [KeyframesSpecConfig.using] and
- * [ArcMode]:
+ * By default, values are animated linearly from one interval to the next (similar to [tween]),
+ * however for 2-dimensional values you may animate them using arcs of quarter of an Ellipse with
+ * [KeyframesSpecConfig.using] and [ArcMode]:
  *
  * @sample androidx.compose.animation.core.samples.OffsetKeyframesWithArcsBuilder
+ *
+ * If instead, you wish to have a smooth curvy animation across all intervals, consider using
+ * [KeyframesWithSplineSpec].
  */
 @Immutable
-class KeyframesSpec<T>(val config: KeyframesSpecConfig<T>) : DurationBasedAnimationSpec<T> {
+public class KeyframesSpec<T>(public val config: KeyframesSpecConfig<T>) :
+    DurationBasedAnimationSpec<T> {
     /**
      * [KeyframesSpecConfig] stores a mutable configuration of the key frames, including
      * [durationMillis], [delayMillis], and all the key frames. Each key frame defines what the
@@ -551,7 +559,7 @@
      * @sample androidx.compose.animation.core.samples.KeyframesBuilderForPosition
      * @see keyframes
      */
-    class KeyframesSpecConfig<T> : KeyframesSpecBaseConfig<T, KeyframeEntity<T>>() {
+    public class KeyframesSpecConfig<T> : KeyframesSpecBaseConfig<T, KeyframeEntity<T>>() {
         @OptIn(ExperimentalAnimationSpecApi::class)
         override fun createEntityFor(value: T): KeyframeEntity<T> = KeyframeEntity(value)
 
@@ -601,7 +609,7 @@
                     " in other keyframe builders.",
             replaceWith = ReplaceWith("this using easing") // Expected usage pattern
         )
-        infix fun KeyframeEntity<T>.with(easing: Easing) {
+        public infix fun KeyframeEntity<T>.with(easing: Easing) {
             this.easing = easing
         }
 
@@ -620,7 +628,7 @@
          * E.g.: [RectToVector] assigns its values as `[left, top, right, bottom]` so the pairs of
          * dimensions animated as arcs are: `[left, top]` and `[right, bottom]`.
          */
-        infix fun KeyframeEntity<T>.using(arcMode: ArcMode): KeyframeEntity<T> {
+        public infix fun KeyframeEntity<T>.using(arcMode: ArcMode): KeyframeEntity<T> {
             this.arcMode = arcMode
             return this
         }
@@ -662,7 +670,7 @@
     }
 
     /** Holder class for building a keyframes animation. */
-    class KeyframeEntity<T>
+    public class KeyframeEntity<T>
     internal constructor(
         value: T,
         easing: Easing = LinearEasing,
@@ -689,11 +697,12 @@
  * [KeyframesWithSplineSpec] creates a keyframe based [DurationBasedAnimationSpec] using the
  * Monotone cubic Hermite spline to interpolate between the values in [config].
  *
- * [KeyframesWithSplineSpec] is best used with 2D values such as [Offset]. For example:
+ * [KeyframesWithSplineSpec] may be used to animate any n-dimensional values, but you'll likely use
+ * it most to animate positional 2D values such as [Offset]. For example:
  *
  * @sample androidx.compose.animation.core.samples.KeyframesBuilderForOffsetWithSplines
  *
- * You may however, provide a [periodicBias] value (between 0f and 1f) to make a periodic spline.
+ * You may also provide a [periodicBias] value (between 0f and 1f) to make a periodic spline.
  * Periodic splines adjust the initial and final velocity to be the same. This is useful to create
  * smooth repeatable animations. Such as an infinite pulsating animation:
  *
@@ -710,8 +719,8 @@
  * @see keyframesWithSpline
  */
 @Immutable
-class KeyframesWithSplineSpec<T>(
-    val config: KeyframesWithSplineSpecConfig<T>,
+public class KeyframesWithSplineSpec<T>(
+    public val config: KeyframesWithSplineSpecConfig<T>,
 ) : DurationBasedAnimationSpec<T> {
     // Periodic bias property, NaN by default. Only meant to be set by secondary constructor
     private var periodicBias: Float = Float.NaN
@@ -724,14 +733,21 @@
      * @param periodicBias A value from 0f to 1f, indicating how much the starting or ending
      *   velocities are modified respectively to achieve periodicity.
      */
-    constructor(
+    public constructor(
         config: KeyframesWithSplineSpecConfig<T>,
         @FloatRange(0.0, 1.0) periodicBias: Float
     ) : this(config) {
         this.periodicBias = periodicBias
     }
 
-    class KeyframesWithSplineSpecConfig<T> :
+    /**
+     * Keyframe configuration class for [KeyframesWithSplineSpec].
+     *
+     * Since [keyframesWithSpline] uses the values across all the given intervals to calculate the
+     * shape of the animation, [KeyframesWithSplineSpecConfig] does not allow setting a specific
+     * [ArcMode] between intervals (compared to [KeyframesSpecConfig] used for [keyframes]).
+     */
+    public class KeyframesWithSplineSpecConfig<T> :
         KeyframesSpecBaseConfig<T, KeyframesSpec.KeyframeEntity<T>>() {
 
         override fun createEntityFor(value: T): KeyframesSpec.KeyframeEntity<T> =
@@ -773,7 +789,7 @@
  * @param easing the easing curve that will be used to interpolate between start and end
  */
 @Stable
-fun <T> tween(
+public fun <T> tween(
     durationMillis: Int = DefaultDurationMillis,
     delayMillis: Int = 0,
     easing: Easing = FastOutSlowInEasing
@@ -789,7 +805,7 @@
  * @param visibilityThreshold optionally specifies the visibility threshold.
  */
 @Stable
-fun <T> spring(
+public fun <T> spring(
     dampingRatio: Float = Spring.DampingRatioNoBouncy,
     stiffness: Float = Spring.StiffnessMedium,
     visibilityThreshold: T? = null
@@ -808,11 +824,15 @@
  * [ArcMode]:
  *
  * @sample androidx.compose.animation.core.samples.OffsetKeyframesWithArcsBuilder
+ *
+ * For a smooth, curvy animation across all the intervals in the keyframes, consider using
+ * [keyframesWithSpline] instead.
+ *
  * @param init Initialization function for the [KeyframesSpec] animation
  * @see KeyframesSpec.KeyframesSpecConfig
  */
 @Stable
-fun <T> keyframes(init: KeyframesSpec.KeyframesSpecConfig<T>.() -> Unit): KeyframesSpec<T> {
+public fun <T> keyframes(init: KeyframesSpec.KeyframesSpecConfig<T>.() -> Unit): KeyframesSpec<T> {
     return KeyframesSpec(KeyframesSpec.KeyframesSpecConfig<T>().apply(init))
 }
 
@@ -831,7 +851,7 @@
  * @sample androidx.compose.animation.core.samples.KeyframesBuilderForDpOffsetWithSplines
  * @see KeyframesWithSplineSpec.KeyframesWithSplineSpecConfig
  */
-fun <T> keyframesWithSpline(
+public fun <T> keyframesWithSpline(
     init: KeyframesWithSplineSpec.KeyframesWithSplineSpecConfig<T>.() -> Unit
 ): KeyframesWithSplineSpec<T> =
     KeyframesWithSplineSpec(
@@ -859,7 +879,7 @@
  * @param init Initialization function for the [KeyframesWithSplineSpec] animation
  * @see KeyframesWithSplineSpec.KeyframesWithSplineSpecConfig
  */
-fun <T> keyframesWithSpline(
+public fun <T> keyframesWithSpline(
     @FloatRange(0.0, 1.0) periodicBias: Float,
     init: KeyframesWithSplineSpec.KeyframesWithSplineSpecConfig<T>.() -> Unit
 ): KeyframesWithSplineSpec<T> =
@@ -890,7 +910,7 @@
  * @param initialStartOffset offsets the start of the animation
  */
 @Stable
-fun <T> repeatable(
+public fun <T> repeatable(
     iterations: Int,
     animation: DurationBasedAnimationSpec<T>,
     repeatMode: RepeatMode = RepeatMode.Restart,
@@ -904,11 +924,11 @@
         "This method has been deprecated in favor of the repeatable function that accepts" +
             " start offset."
 )
-fun <T> repeatable(
+public fun <T> repeatable(
     iterations: Int,
     animation: DurationBasedAnimationSpec<T>,
     repeatMode: RepeatMode = RepeatMode.Restart
-) = RepeatableSpec(iterations, animation, repeatMode, StartOffset(0))
+): RepeatableSpec<T> = RepeatableSpec(iterations, animation, repeatMode, StartOffset(0))
 
 /**
  * Creates a [InfiniteRepeatableSpec] that plays a [DurationBasedAnimationSpec] (e.g. [TweenSpec],
@@ -927,7 +947,7 @@
  * @param initialStartOffset offsets the start of the animation
  */
 @Stable
-fun <T> infiniteRepeatable(
+public fun <T> infiniteRepeatable(
     animation: DurationBasedAnimationSpec<T>,
     repeatMode: RepeatMode = RepeatMode.Restart,
     initialStartOffset: StartOffset = StartOffset(0)
@@ -940,17 +960,17 @@
         "This method has been deprecated in favor of the infinite repeatable function that" +
             " accepts start offset."
 )
-fun <T> infiniteRepeatable(
+public fun <T> infiniteRepeatable(
     animation: DurationBasedAnimationSpec<T>,
     repeatMode: RepeatMode = RepeatMode.Restart
-) = InfiniteRepeatableSpec(animation, repeatMode, StartOffset(0))
+): InfiniteRepeatableSpec<T> = InfiniteRepeatableSpec(animation, repeatMode, StartOffset(0))
 
 /**
  * Creates a Snap animation for immediately switching the animating value to the end value.
  *
  * @param delayMillis the number of milliseconds to wait before the animation runs. 0 by default.
  */
-@Stable fun <T> snap(delayMillis: Int = 0) = SnapSpec<T>(delayMillis)
+@Stable public fun <T> snap(delayMillis: Int = 0): SnapSpec<T> = SnapSpec<T>(delayMillis)
 
 /**
  * Returns an [AnimationSpec] that is the same as [animationSpec] with a delay of [startDelayNanos].
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt
index 508db27..269c8bd 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationState.kt
@@ -37,8 +37,8 @@
  * @param isRunning whether the [AnimationState] is currently being updated by an animation. False
  *   by default
  */
-class AnimationState<T, V : AnimationVector>(
-    val typeConverter: TwoWayConverter<T, V>,
+public class AnimationState<T, V : AnimationVector>(
+    public val typeConverter: TwoWayConverter<T, V>,
     initialValue: T,
     initialVelocityVector: V? = null,
     lastFrameTimeNanos: Long = AnimationConstants.UnspecifiedTime,
@@ -50,7 +50,7 @@
         internal set
 
     /** Current velocity vector of the [AnimationState]. */
-    var velocityVector: V =
+    public var velocityVector: V =
         initialVelocityVector?.copy() ?: typeConverter.createZeroVectorFrom(initialValue)
         internal set
 
@@ -64,7 +64,7 @@
      * animation to set its start time to when the previous animation is interrupted or finished.
      */
     @get:Suppress("MethodNameUnits")
-    var lastFrameTimeNanos: Long = lastFrameTimeNanos
+    public var lastFrameTimeNanos: Long = lastFrameTimeNanos
         internal set
 
     /**
@@ -75,15 +75,15 @@
      * [AnimationState] constructor.
      */
     @get:Suppress("MethodNameUnits")
-    var finishedTimeNanos: Long = finishedTimeNanos
+    public var finishedTimeNanos: Long = finishedTimeNanos
         internal set
 
     /** Indicates whether the animation is currently running. */
-    var isRunning: Boolean = isRunning
+    public var isRunning: Boolean = isRunning
         internal set
 
     /** Velocity of type [T], converted from [velocityVector]. */
-    val velocity: T
+    public val velocity: T
         get() = typeConverter.convertFromVector(velocityVector)
 
     override fun toString(): String {
@@ -101,7 +101,7 @@
  * Indicates whether the given [AnimationState] is for an animation that has finished, indicated by
  * [AnimationState.finishedTimeNanos] having a specified value.
  */
-val AnimationState<*, *>.isFinished
+public val AnimationState<*, *>.isFinished: Boolean
     get() = finishedTimeNanos != AnimationConstants.UnspecifiedTime
 
 /**
@@ -110,27 +110,27 @@
  *
  * @see [AnimationState.animateTo]
  */
-class AnimationScope<T, V : AnimationVector>
+public class AnimationScope<T, V : AnimationVector>
 internal constructor(
     initialValue: T,
     /** [TwoWayConverter] to convert type [T] from and to [AnimationVector]. */
-    val typeConverter: TwoWayConverter<T, V>,
+    public val typeConverter: TwoWayConverter<T, V>,
     initialVelocityVector: V,
     lastFrameTimeNanos: Long,
     /** Target value of the animation. */
-    val targetValue: T,
+    public val targetValue: T,
     /** Start time of the animation in the [System.nanoTime] timebase. */
-    @get:Suppress("MethodNameUnits") val startTimeNanos: Long,
+    @get:Suppress("MethodNameUnits") public val startTimeNanos: Long,
     isRunning: Boolean,
     private val onCancel: () -> Unit
 ) {
     // Externally immutable fields
     /** Current value of the [AnimationScope]. */
-    var value: T by mutableStateOf(initialValue)
+    public var value: T by mutableStateOf(initialValue)
         internal set
 
     /** Current velocity vector of the [AnimationScope]. */
-    var velocityVector: V = initialVelocityVector.copy()
+    public var velocityVector: V = initialVelocityVector.copy()
         internal set
 
     /**
@@ -143,7 +143,7 @@
      * animation to set its start time to when the previous animation is interrupted or finished.
      */
     @get:Suppress("MethodNameUnits")
-    var lastFrameTimeNanos: Long = lastFrameTimeNanos
+    public var lastFrameTimeNanos: Long = lastFrameTimeNanos
         internal set
 
     /**
@@ -154,22 +154,22 @@
      * [AnimationState] constructor.
      */
     @get:Suppress("MethodNameUnits")
-    var finishedTimeNanos: Long = AnimationConstants.UnspecifiedTime
+    public var finishedTimeNanos: Long = AnimationConstants.UnspecifiedTime
         internal set
 
     /** Indicates whether the animation is currently running. */
-    var isRunning: Boolean by mutableStateOf(isRunning)
+    public var isRunning: Boolean by mutableStateOf(isRunning)
         internal set
 
     /** Velocity of type [T], converted from [velocityVector]. */
-    val velocity
+    public val velocity: T
         get() = typeConverter.convertFromVector(velocityVector)
 
     /**
      * Cancels the animation that this [AnimationScope] corresponds to. The scope will not be
      * updated any more after [cancelAnimation] is called.
      */
-    fun cancelAnimation() {
+    public fun cancelAnimation() {
         isRunning = false
         onCancel()
     }
@@ -178,7 +178,7 @@
      * Creates an [AnimationState] that populates all the fields in [AnimationState] from
      * [AnimationScope].
      */
-    fun toAnimationState() =
+    public fun toAnimationState(): AnimationState<T, V> =
         AnimationState(
             typeConverter,
             value,
@@ -207,7 +207,7 @@
  * @return A new [AnimationState] instance copied from the given instance, with some fields
  *   optionally altered
  */
-fun <T, V : AnimationVector> AnimationState<T, V>.copy(
+public fun <T, V : AnimationVector> AnimationState<T, V>.copy(
     value: T = this.value,
     velocityVector: V? = this.velocityVector.copy(),
     lastFrameTimeNanos: Long = this.lastFrameTimeNanos,
@@ -240,7 +240,7 @@
  * @return A new [AnimationState] instance copied from the given instance, with some fields
  *   optionally altered
  */
-fun AnimationState<Float, AnimationVector1D>.copy(
+public fun AnimationState<Float, AnimationVector1D>.copy(
     value: Float = this.value,
     velocity: Float = this.velocityVector.value,
     lastFrameTimeNanos: Long = this.lastFrameTimeNanos,
@@ -269,7 +269,7 @@
  *   by default
  * @return A new [AnimationState] instance
  */
-fun AnimationState(
+public fun AnimationState(
     initialValue: Float,
     initialVelocity: Float = 0f,
     lastFrameTimeNanos: Long = AnimationConstants.UnspecifiedTime,
@@ -300,7 +300,7 @@
  *   by default
  * @return A new [AnimationState] instance
  */
-fun <T, V : AnimationVector> AnimationState(
+public fun <T, V : AnimationVector> AnimationState(
     typeConverter: TwoWayConverter<T, V>,
     initialValue: T,
     initialVelocity: T,
@@ -324,5 +324,5 @@
  *
  * @return a new AnimationVector instance of type [V].
  */
-fun <T, V : AnimationVector> TwoWayConverter<T, V>.createZeroVectorFrom(value: T) =
+public fun <T, V : AnimationVector> TwoWayConverter<T, V>.createZeroVectorFrom(value: T): V =
     convertToVector(value).also { it.reset() }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
index 5d574f1..1ab050e 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
@@ -25,7 +25,7 @@
  * object should be converted to [AnimationVector2D], whereas an object that describes rectangle
  * bounds should convert to [AnimationVector4D].
  */
-sealed class AnimationVector {
+public sealed class AnimationVector {
     internal abstract fun reset()
 
     internal abstract fun newVector(): AnimationVector
@@ -42,7 +42,7 @@
  *
  * @param v1 value to set on the value field of [AnimationVector1D]
  */
-fun AnimationVector(v1: Float) = AnimationVector1D(v1)
+public fun AnimationVector(v1: Float): AnimationVector1D = AnimationVector1D(v1)
 
 /**
  * Factory method to create an [AnimationVector2D]
@@ -50,7 +50,7 @@
  * @param v1 value to set on the first dimension
  * @param v2 value to set on the second dimension
  */
-fun AnimationVector(v1: Float, v2: Float) = AnimationVector2D(v1, v2)
+public fun AnimationVector(v1: Float, v2: Float): AnimationVector2D = AnimationVector2D(v1, v2)
 
 /**
  * Factory method to create an [AnimationVector3D]
@@ -59,7 +59,8 @@
  * @param v2 value to set on the second dimension
  * @param v3 value to set on the third dimension
  */
-fun AnimationVector(v1: Float, v2: Float, v3: Float) = AnimationVector3D(v1, v2, v3)
+public fun AnimationVector(v1: Float, v2: Float, v3: Float): AnimationVector3D =
+    AnimationVector3D(v1, v2, v3)
 
 /**
  * Factory method to create an [AnimationVector4D]
@@ -69,7 +70,8 @@
  * @param v3 value to set on the third dimension
  * @param v4 value to set on the fourth dimension
  */
-fun AnimationVector(v1: Float, v2: Float, v3: Float, v4: Float) = AnimationVector4D(v1, v2, v3, v4)
+public fun AnimationVector(v1: Float, v2: Float, v3: Float, v4: Float): AnimationVector4D =
+    AnimationVector4D(v1, v2, v3, v4)
 
 internal fun <T : AnimationVector> T.newInstance(): T {
     @Suppress("UNCHECKED_CAST") return this.newVector() as T
@@ -95,9 +97,9 @@
  *
  * @param initVal initial value to set the [value] field to.
  */
-class AnimationVector1D(initVal: Float) : AnimationVector() {
+public class AnimationVector1D(initVal: Float) : AnimationVector() {
     /** This field holds the only Float value in this [AnimationVector1D] object. */
-    var value: Float = initVal
+    public var value: Float = initVal
         internal set
 
     // internal
@@ -138,13 +140,13 @@
  * @param v1 initial value to set on the first dimension
  * @param v2 initial value to set on the second dimension
  */
-class AnimationVector2D(v1: Float, v2: Float) : AnimationVector() {
+public class AnimationVector2D(v1: Float, v2: Float) : AnimationVector() {
     /** Float value field for the first dimension of the 2D vector. */
-    var v1: Float = v1
+    public var v1: Float = v1
         internal set
 
     /** Float value field for the second dimension of the 2D vector. */
-    var v2: Float = v2
+    public var v2: Float = v2
         internal set
 
     // internal
@@ -189,18 +191,18 @@
  * @param v2 initial value to set on the second dimension
  * @param v3 initial value to set on the third dimension
  */
-class AnimationVector3D(v1: Float, v2: Float, v3: Float) : AnimationVector() {
+public class AnimationVector3D(v1: Float, v2: Float, v3: Float) : AnimationVector() {
     // Internally mutable, so we don't have to create a number of small objects per anim frame
     /** Float value field for the first dimension of the 3D vector. */
-    var v1: Float = v1
+    public var v1: Float = v1
         internal set
 
     /** Float value field for the second dimension of the 3D vector. */
-    var v2: Float = v2
+    public var v2: Float = v2
         internal set
 
     /** Float value field for the third dimension of the 3D vector. */
-    var v3: Float = v3
+    public var v3: Float = v3
         internal set
 
     // internal
@@ -249,22 +251,22 @@
  * @param v3 initial value to set on the third dimension
  * @param v4 initial value to set on the fourth dimension
  */
-class AnimationVector4D(v1: Float, v2: Float, v3: Float, v4: Float) : AnimationVector() {
+public class AnimationVector4D(v1: Float, v2: Float, v3: Float, v4: Float) : AnimationVector() {
     // Internally mutable, so we don't have to create a number of small objects per anim frame
     /** Float value field for the first dimension of the 4D vector. */
-    var v1: Float = v1
+    public var v1: Float = v1
         internal set
 
     /** Float value field for the second dimension of the 4D vector. */
-    var v2: Float = v2
+    public var v2: Float = v2
         internal set
 
     /** Float value field for the third dimension of the 4D vector. */
-    var v3: Float = v3
+    public var v3: Float = v3
         internal set
 
     /** Float value field for the fourth dimension of the 4D vector. */
-    var v4: Float = v4
+    public var v4: Float = v4
         internal set
 
     override fun reset() {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ArcSpline.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ArcSpline.kt
index a816ef1..740faa1 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ArcSpline.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ArcSpline.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.animation.core
 
+import kotlin.math.PI
 import kotlin.math.abs
 import kotlin.math.cos
 import kotlin.math.hypot
@@ -241,7 +242,7 @@
 
         fun setPoint(time: Float) {
             val percent = (if (isVertical) time2 - time else time - time1) * oneOverDeltaTime
-            val angle = Math.PI.toFloat() * 0.5f * lookup(percent)
+            val angle = PI.toFloat() * 0.5f * lookup(percent)
             tmpSinAngle = sin(angle)
             tmpCosAngle = cos(angle)
         }
@@ -308,7 +309,7 @@
             var ly = 0f
             var dist = 0f
             for (i in ourPercent.indices) {
-                val angle = Math.toRadians(90.0 * i / (ourPercent.size - 1)).toFloat()
+                val angle = toRadians(90.0 * i / (ourPercent.size - 1)).toFloat()
                 val s = sin(angle)
                 val c = cos(angle)
                 val px = a * s
@@ -326,7 +327,7 @@
             }
             for (i in lut.indices) {
                 val pos = i / (lut.size - 1).toFloat()
-                val index = ourPercent.binarySearch(pos)
+                val index = binarySearch(ourPercent, pos)
                 if (index >= 0) {
                     lut[i] = index / (ourPercent.size - 1).toFloat()
                 } else if (index == -1) {
@@ -371,3 +372,7 @@
         private const val UpArc = 5
     }
 }
+
+internal expect inline fun toRadians(value: Double): Double
+
+internal expect inline fun binarySearch(array: FloatArray, position: Float): Int
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
index 964a3c0..ae50da6 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
@@ -33,7 +33,7 @@
  * multi-dimensional way. It is particularly useful for smoothly handling animation interruptions
  * (such as when the target changes during the animation).
  */
-interface DecayAnimationSpec<T> {
+public interface DecayAnimationSpec<T> {
 
     /**
      * Creates a [VectorizedDecayAnimationSpec] with the given [TwoWayConverter].
@@ -44,7 +44,7 @@
      *
      * @param typeConverter converts the type [T] from and to [AnimationVector] type
      */
-    fun <V : AnimationVector> vectorize(
+    public fun <V : AnimationVector> vectorize(
         typeConverter: TwoWayConverter<T, V>
     ): VectorizedDecayAnimationSpec<V>
 }
@@ -55,7 +55,7 @@
  *
  * @return target value where the animation will come to a natural stop
  */
-fun <T, V : AnimationVector> DecayAnimationSpec<T>.calculateTargetValue(
+public fun <T, V : AnimationVector> DecayAnimationSpec<T>.calculateTargetValue(
     typeConverter: TwoWayConverter<T, V>,
     initialValue: T,
     initialVelocity: T
@@ -75,7 +75,7 @@
  *
  * @return target value where the animation will come to a natural stop
  */
-fun DecayAnimationSpec<Float>.calculateTargetValue(
+public fun DecayAnimationSpec<Float>.calculateTargetValue(
     initialValue: Float,
     initialVelocity: Float
 ): Float {
@@ -101,7 +101,7 @@
  * @param absVelocityThreshold The minimum speed, below which the animation is considered finished.
  *   Must be greater than `0`.
  */
-fun <T> exponentialDecay(
+public fun <T> exponentialDecay(
     @FloatRange(from = 0.0, fromInclusive = false) frictionMultiplier: Float = 1f,
     @FloatRange(from = 0.0, fromInclusive = false) absVelocityThreshold: Float = 0.1f
 ): DecayAnimationSpec<T> =
@@ -111,7 +111,7 @@
  * Creates a [DecayAnimationSpec] from a [FloatDecayAnimationSpec] by applying the given
  * [FloatDecayAnimationSpec] on every dimension of the [AnimationVector] that [T] converts to.
  */
-fun <T> FloatDecayAnimationSpec.generateDecayAnimationSpec(): DecayAnimationSpec<T> {
+public fun <T> FloatDecayAnimationSpec.generateDecayAnimationSpec(): DecayAnimationSpec<T> {
     return DecayAnimationSpecImpl(this)
 }
 
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DeferredTargetAnimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DeferredTargetAnimation.kt
index 3b59987..a1dabac 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DeferredTargetAnimation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DeferredTargetAnimation.kt
@@ -26,7 +26,7 @@
     message = "This is an experimental animation API for Transition. It may change in the future."
 )
 @Retention(AnnotationRetention.BINARY)
-annotation class ExperimentalAnimatableApi
+public annotation class ExperimentalAnimatableApi
 
 /**
  * [DeferredTargetAnimation] is intended for animations where the target is unknown at the time of
@@ -41,11 +41,11 @@
  * @sample androidx.compose.animation.core.samples.DeferredTargetAnimationSample
  */
 @ExperimentalAnimatableApi
-class DeferredTargetAnimation<T, V : AnimationVector>(
+public class DeferredTargetAnimation<T, V : AnimationVector>(
     private val vectorConverter: TwoWayConverter<T, V>
 ) {
     /** Returns the target value from the most recent [updateTarget] call. */
-    val pendingTarget: T?
+    public val pendingTarget: T?
         get() = _pendingTarget
 
     private var _pendingTarget: T? by mutableStateOf(null)
@@ -64,7 +64,7 @@
      *
      * @return current value of the animation
      */
-    fun updateTarget(
+    public fun updateTarget(
         target: T,
         coroutineScope: CoroutineScope,
         animationSpec: FiniteAnimationSpec<T> = spring()
@@ -84,6 +84,6 @@
      * [pendingTarget], or when the animation has not been set up (i.e. [updateTarget] has never
      * been called).
      */
-    val isIdle: Boolean
+    public val isIdle: Boolean
         get() = _pendingTarget == target && animatable?.isRunning != true
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
index ceea13f..9ff5f0f 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
@@ -33,8 +33,8 @@
  * An [Easing] must map fraction=0.0 to 0.0 and fraction=1.0 to 1.0.
  */
 @Stable
-fun interface Easing {
-    fun transform(fraction: Float): Float
+public fun interface Easing {
+    public fun transform(fraction: Float): Float
 }
 
 /**
@@ -46,7 +46,7 @@
  *
  * This is equivalent to the Android `FastOutSlowInInterpolator`
  */
-val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
+public val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
 
 /**
  * Incoming elements are animated using deceleration easing, which starts a transition at peak
@@ -54,7 +54,7 @@
  *
  * This is equivalent to the Android `LinearOutSlowInInterpolator`
  */
-val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
+public val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
 
 /**
  * Elements exiting a screen use acceleration easing, where they start at rest and end at peak
@@ -62,13 +62,13 @@
  *
  * This is equivalent to the Android `FastOutLinearInInterpolator`
  */
-val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
+public val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
 
 /**
  * It returns fraction unmodified. This is useful as a default value for cases where a [Easing] is
  * required but no actual easing is desired.
  */
-val LinearEasing: Easing = Easing { fraction -> fraction }
+public val LinearEasing: Easing = Easing { fraction -> fraction }
 
 /**
  * A cubic polynomial easing.
@@ -96,7 +96,7 @@
  * @see FastOutLinearInEasing
  */
 @Immutable
-class CubicBezierEasing(
+public class CubicBezierEasing(
     private val a: Float,
     private val b: Float,
     private val c: Float,
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/EasingFunctions.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/EasingFunctions.kt
index edfc39c..37219ac 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/EasingFunctions.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/EasingFunctions.kt
@@ -26,7 +26,7 @@
  * ![Ease
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease.gif)
  */
-val Ease: Easing = CubicBezierEasing(0.25f, 0.1f, 0.25f, 1.0f)
+public val Ease: Easing = CubicBezierEasing(0.25f, 0.1f, 0.25f, 1.0f)
 
 /**
  * Easing Curve that starts quickly and ends slowly.
@@ -34,7 +34,7 @@
  * ![EaseOut
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out.gif)
  */
-val EaseOut: Easing = CubicBezierEasing(0f, 0f, 0.58f, 1f)
+public val EaseOut: Easing = CubicBezierEasing(0f, 0f, 0.58f, 1f)
 
 /**
  * Easing Curve that starts slowly and ends quickly.
@@ -42,7 +42,7 @@
  * ![EaseIn
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in.gif)
  */
-val EaseIn: Easing = CubicBezierEasing(0.42f, 0f, 1f, 1f)
+public val EaseIn: Easing = CubicBezierEasing(0.42f, 0f, 1f, 1f)
 
 /**
  * Easing Curve that starts slowly, speeds up and then ends slowly.
@@ -50,7 +50,7 @@
  * ![EaseInOut
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out.gif)
  */
-val EaseInOut: Easing = CubicBezierEasing(0.42f, 0.0f, 0.58f, 1.0f)
+public val EaseInOut: Easing = CubicBezierEasing(0.42f, 0.0f, 0.58f, 1.0f)
 
 /**
  * Easing Curve that starts slowly and ends quickly. Similar to EaseIn, but with slightly less
@@ -59,151 +59,151 @@
  * ![EaseInSine
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_sine.gif)
  */
-val EaseInSine: Easing = CubicBezierEasing(0.12f, 0f, 0.39f, 0f)
+public val EaseInSine: Easing = CubicBezierEasing(0.12f, 0f, 0.39f, 0f)
 
 /**
  * ![EaseOutSine
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_sine.gif)
  */
-val EaseOutSine: Easing = CubicBezierEasing(0.61f, 1f, 0.88f, 1f)
+public val EaseOutSine: Easing = CubicBezierEasing(0.61f, 1f, 0.88f, 1f)
 
 /**
  * ![EaseInOutSine
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_sine.gif)
  */
-val EaseInOutSine: Easing = CubicBezierEasing(0.37f, 0f, 0.63f, 1f)
+public val EaseInOutSine: Easing = CubicBezierEasing(0.37f, 0f, 0.63f, 1f)
 
 /**
  * ![EaseInCubic
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_cubic.gif)
  */
-val EaseInCubic: Easing = CubicBezierEasing(0.32f, 0f, 0.67f, 0f)
+public val EaseInCubic: Easing = CubicBezierEasing(0.32f, 0f, 0.67f, 0f)
 
 /**
  * ![EaseOutCubic
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_cubic.gif)
  */
-val EaseOutCubic: Easing = CubicBezierEasing(0.33f, 1f, 0.68f, 1f)
+public val EaseOutCubic: Easing = CubicBezierEasing(0.33f, 1f, 0.68f, 1f)
 
 /**
  * ![EaseInOutCubic
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_cubic.gif)
  */
-val EaseInOutCubic: Easing = CubicBezierEasing(0.65f, 0f, 0.35f, 1f)
+public val EaseInOutCubic: Easing = CubicBezierEasing(0.65f, 0f, 0.35f, 1f)
 
 /**
  * ![EaseInQuint
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_quint.gif)
  */
-val EaseInQuint: Easing = CubicBezierEasing(0.64f, 0f, 0.78f, 0f)
+public val EaseInQuint: Easing = CubicBezierEasing(0.64f, 0f, 0.78f, 0f)
 
 /**
  * ![EaseOutQuint
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_quint.gif)
  */
-val EaseOutQuint: Easing = CubicBezierEasing(0.22f, 1f, 0.36f, 1f)
+public val EaseOutQuint: Easing = CubicBezierEasing(0.22f, 1f, 0.36f, 1f)
 
 /**
  * ![EaseInOutQuint
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_quint.gif)
  */
-val EaseInOutQuint: Easing = CubicBezierEasing(0.83f, 0f, 0.17f, 1f)
+public val EaseInOutQuint: Easing = CubicBezierEasing(0.83f, 0f, 0.17f, 1f)
 
 /**
  * ![EaseInCirc
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_circ.gif)
  */
-val EaseInCirc: Easing = CubicBezierEasing(0.55f, 0f, 1f, 0.45f)
+public val EaseInCirc: Easing = CubicBezierEasing(0.55f, 0f, 1f, 0.45f)
 
 /**
  * ![EaseOutCirc
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_circ.gif)
  */
-val EaseOutCirc: Easing = CubicBezierEasing(0f, 0.55f, 0.45f, 1f)
+public val EaseOutCirc: Easing = CubicBezierEasing(0f, 0.55f, 0.45f, 1f)
 
 /**
  * ![EaseInOutCirc
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_circ.gif)
  */
-val EaseInOutCirc: Easing = CubicBezierEasing(0.85f, 0f, 0.15f, 1f)
+public val EaseInOutCirc: Easing = CubicBezierEasing(0.85f, 0f, 0.15f, 1f)
 
 /**
  * ![EaseInQuad
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_quad.gif)
  */
-val EaseInQuad: Easing = CubicBezierEasing(0.11f, 0f, 0.5f, 0f)
+public val EaseInQuad: Easing = CubicBezierEasing(0.11f, 0f, 0.5f, 0f)
 
 /**
  * ![EaseOutQuad
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_quad.gif)
  */
-val EaseOutQuad: Easing = CubicBezierEasing(0.5f, 1f, 0.89f, 1f)
+public val EaseOutQuad: Easing = CubicBezierEasing(0.5f, 1f, 0.89f, 1f)
 
 /**
  * ![EaseInOutQuad
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_quad.gif)
  */
-val EaseInOutQuad: Easing = CubicBezierEasing(0.45f, 0f, 0.55f, 1f)
+public val EaseInOutQuad: Easing = CubicBezierEasing(0.45f, 0f, 0.55f, 1f)
 
 /**
  * ![EaseInQuart
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_quart.gif)
  */
-val EaseInQuart: Easing = CubicBezierEasing(0.5f, 0f, 0.75f, 0f)
+public val EaseInQuart: Easing = CubicBezierEasing(0.5f, 0f, 0.75f, 0f)
 
 /**
  * ![EaseOutQuart
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_quart.gif)
  */
-val EaseOutQuart: Easing = CubicBezierEasing(0.25f, 1f, 0.5f, 1f)
+public val EaseOutQuart: Easing = CubicBezierEasing(0.25f, 1f, 0.5f, 1f)
 
 /**
  * ![EaseInOutQuart
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_quart.gif)
  */
-val EaseInOutQuart: Easing = CubicBezierEasing(0.76f, 0f, 0.24f, 1f)
+public val EaseInOutQuart: Easing = CubicBezierEasing(0.76f, 0f, 0.24f, 1f)
 
 /**
  * ![EaseInExpo
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_expo.gif)
  */
-val EaseInExpo: Easing = CubicBezierEasing(0.7f, 0f, 0.84f, 0f)
+public val EaseInExpo: Easing = CubicBezierEasing(0.7f, 0f, 0.84f, 0f)
 
 /**
  * ![EaseOutExpo
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_expo.gif)
  */
-val EaseOutExpo: Easing = CubicBezierEasing(0.16f, 1f, 0.3f, 1f)
+public val EaseOutExpo: Easing = CubicBezierEasing(0.16f, 1f, 0.3f, 1f)
 
 /**
  * ![EaseInOutExpo
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_expo.gif)
  */
-val EaseInOutExpo: Easing = CubicBezierEasing(0.87f, 0f, 0.13f, 1f)
+public val EaseInOutExpo: Easing = CubicBezierEasing(0.87f, 0f, 0.13f, 1f)
 
 /**
  * ![EaseInBack
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_back.gif)
  */
-val EaseInBack: Easing = CubicBezierEasing(0.36f, 0f, 0.66f, -0.56f)
+public val EaseInBack: Easing = CubicBezierEasing(0.36f, 0f, 0.66f, -0.56f)
 
 /**
  * ![EaseOutBack
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_back.gif)
  */
-val EaseOutBack: Easing = CubicBezierEasing(0.34f, 1.56f, 0.64f, 1f)
+public val EaseOutBack: Easing = CubicBezierEasing(0.34f, 1.56f, 0.64f, 1f)
 
 /**
  * ![EaseInOutBack
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_back.gif)
  */
-val EaseInOutBack: Easing = CubicBezierEasing(0.68f, -0.6f, 0.32f, 1.6f)
+public val EaseInOutBack: Easing = CubicBezierEasing(0.68f, -0.6f, 0.32f, 1.6f)
 
 /**
  * ![EaseInElastic
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_elastic.gif)
  */
-val EaseInElastic: Easing = Easing { fraction: Float ->
+public val EaseInElastic: Easing = Easing { fraction: Float ->
     val c4 = (2f * PI) / 3f
 
     return@Easing when (fraction) {
@@ -218,7 +218,7 @@
  * ![EaseOutElastic
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_elastic.gif)
  */
-val EaseOutElastic: Easing = Easing { fraction ->
+public val EaseOutElastic: Easing = Easing { fraction ->
     val c4 = (2f * PI) / 3f
 
     return@Easing when (fraction) {
@@ -232,7 +232,7 @@
  * ![EaseInOutElastic
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_elastic.gif)
  */
-val EaseInOutElastic: Easing = Easing { fraction ->
+public val EaseInOutElastic: Easing = Easing { fraction ->
     val c5 = (2f * PI) / 4.5f
     return@Easing when (fraction) {
         0f -> 0f
@@ -250,7 +250,7 @@
  * ![EaseOutBounce
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_out_bounce.gif)
  */
-val EaseOutBounce: Easing = Easing { fraction ->
+public val EaseOutBounce: Easing = Easing { fraction ->
     val n1 = 7.5625f
     val d1 = 2.75f
     var newFraction = fraction
@@ -273,7 +273,7 @@
  * ![EaseInBounce
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_bounce.gif)
  */
-val EaseInBounce: Easing = Easing { fraction ->
+public val EaseInBounce: Easing = Easing { fraction ->
     return@Easing 1 - EaseOutBounce.transform(1f - fraction)
 }
 
@@ -281,7 +281,7 @@
  * ![EaseInOutBounce
  * Curve](https://developer.android.com/images/reference/androidx/compose/animation-core/ease_in_out_bounce.gif)
  */
-val EaseInOutBounce: Easing = Easing { fraction ->
+public val EaseInOutBounce: Easing = Easing { fraction ->
     return@Easing if (fraction < 0.5) {
         (1 - EaseOutBounce.transform(1f - 2f * fraction)) / 2f
     } else {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalAnimationSpecApi.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalAnimationSpecApi.kt
index ff854454..c0b09aad 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalAnimationSpecApi.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalAnimationSpecApi.kt
@@ -21,4 +21,4 @@
         "This is an experimental animation API for AnimationSpec. " + "It may change in the future."
 )
 @Retention(AnnotationRetention.BINARY)
-annotation class ExperimentalAnimationSpecApi
+public annotation class ExperimentalAnimationSpecApi
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalTransitionApi.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalTransitionApi.kt
index 7aeadaa..b65978e 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalTransitionApi.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ExperimentalTransitionApi.kt
@@ -20,4 +20,4 @@
     message = "This is an experimental animation API for Transition. It may change in the future."
 )
 @Retention(AnnotationRetention.BINARY)
-annotation class ExperimentalTransitionApi
+public annotation class ExperimentalTransitionApi
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
index 553ef30..e0c04a3 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
@@ -34,7 +34,7 @@
  * @see [VectorizedAnimationSpec]
  */
 @JvmDefaultWithCompatibility
-interface FloatAnimationSpec : AnimationSpec<Float> {
+public interface FloatAnimationSpec : AnimationSpec<Float> {
     /**
      * Calculates the value of the animation at given the playtime, with the provided start/end
      * values, and start velocity.
@@ -44,7 +44,7 @@
      * @param targetValue end value of the animation
      * @param initialVelocity start velocity of the animation
      */
-    fun getValueFromNanos(
+    public fun getValueFromNanos(
         playTimeNanos: Long,
         initialValue: Float,
         targetValue: Float,
@@ -60,7 +60,7 @@
      * @param targetValue end value of the animation
      * @param initialVelocity start velocity of the animation
      */
-    fun getVelocityFromNanos(
+    public fun getVelocityFromNanos(
         playTimeNanos: Long,
         initialValue: Float,
         targetValue: Float,
@@ -77,7 +77,11 @@
      * @param targetValue end value of the animation
      * @param initialVelocity start velocity of the animation
      */
-    fun getEndVelocity(initialValue: Float, targetValue: Float, initialVelocity: Float): Float =
+    public fun getEndVelocity(
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
+    ): Float =
         getVelocityFromNanos(
             getDurationNanos(initialValue, targetValue, initialVelocity),
             initialValue,
@@ -99,15 +103,20 @@
      * @param initialVelocity start velocity of the animation
      */
     @Suppress("MethodNameUnits")
-    fun getDurationNanos(initialValue: Float, targetValue: Float, initialVelocity: Float): Long
+    public fun getDurationNanos(
+        initialValue: Float,
+        targetValue: Float,
+        initialVelocity: Float
+    ): Long
 
     /**
      * Create an [VectorizedAnimationSpec] that animates [AnimationVector] from a
      * [FloatAnimationSpec]. Every dimension of the [AnimationVector] will be animated using the
      * given [FloatAnimationSpec].
      */
-    override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<Float, V>) =
-        VectorizedFloatAnimationSpec<V>(this)
+    override fun <V : AnimationVector> vectorize(
+        converter: TwoWayConverter<Float, V>
+    ): VectorizedFloatAnimationSpec<V> = VectorizedFloatAnimationSpec<V>(this)
 }
 
 /**
@@ -119,9 +128,9 @@
  * @param visibilityThreshold The value threshold such that the animation is no longer significant.
  *   e.g. 1px for translation animations. Defaults to [Spring.DefaultDisplacementThreshold]
  */
-class FloatSpringSpec(
-    val dampingRatio: Float = Spring.DampingRatioNoBouncy,
-    val stiffness: Float = Spring.StiffnessMedium,
+public class FloatSpringSpec(
+    public val dampingRatio: Float = Spring.DampingRatioNoBouncy,
+    public val stiffness: Float = Spring.StiffnessMedium,
     private val visibilityThreshold: Float = Spring.DefaultDisplacementThreshold
 ) : FloatAnimationSpec {
 
@@ -189,9 +198,9 @@
  * @param easing the easing function that will be used to interoplate between the start and end
  *   value of the animation. Defaults to [FastOutSlowInEasing].
  */
-class FloatTweenSpec(
-    val duration: Int = DefaultDurationMillis,
-    val delay: Int = 0,
+public class FloatTweenSpec(
+    public val duration: Int = DefaultDurationMillis,
+    public val delay: Int = 0,
     private val easing: Easing = FastOutSlowInEasing
 ) : FloatAnimationSpec {
     private val durationNanos: Long = duration * MillisToNanos
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
index cc1989c..87dfda9 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
@@ -27,12 +27,12 @@
  * Animation<T>, DecayAnimation does not have an end value defined. The end value is a result of the
  * animation rather than an input.
  */
-interface FloatDecayAnimationSpec {
+public interface FloatDecayAnimationSpec {
     /**
      * This is the absolute value of a velocity threshold, below which the animation is considered
      * finished.
      */
-    val absVelocityThreshold: Float
+    public val absVelocityThreshold: Float
 
     /**
      * Returns the value of the animation at the given time.
@@ -41,7 +41,11 @@
      * @param initialValue The start value of the animation
      * @param initialVelocity The start velocity of the animation
      */
-    fun getValueFromNanos(playTimeNanos: Long, initialValue: Float, initialVelocity: Float): Float
+    public fun getValueFromNanos(
+        playTimeNanos: Long,
+        initialValue: Float,
+        initialVelocity: Float
+    ): Float
 
     /**
      * Returns the duration of the decay animation, in nanoseconds.
@@ -50,7 +54,7 @@
      * @param initialVelocity start velocity of the animation
      */
     @Suppress("MethodNameUnits")
-    fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long
+    public fun getDurationNanos(initialValue: Float, initialVelocity: Float): Long
 
     /**
      * Returns the velocity of the animation at the given time.
@@ -59,7 +63,7 @@
      * @param initialValue The start value of the animation
      * @param initialVelocity The start velocity of the animation
      */
-    fun getVelocityFromNanos(
+    public fun getVelocityFromNanos(
         playTimeNanos: Long,
         initialValue: Float,
         initialVelocity: Float
@@ -72,7 +76,7 @@
      * @param initialValue The start value of the animation
      * @param initialVelocity The start velocity of the animation
      */
-    fun getTargetValue(initialValue: Float, initialVelocity: Float): Float
+    public fun getTargetValue(initialValue: Float, initialVelocity: Float): Float
 }
 
 private const val ExponentialDecayFriction = -4.2f
@@ -89,7 +93,7 @@
  * @param absVelocityThreshold The speed at which the animation is considered close enough to rest
  *   for the animation to finish.
  */
-class FloatExponentialDecaySpec(
+public class FloatExponentialDecaySpec(
     @FloatRange(from = 0.0, fromInclusive = false) frictionMultiplier: Float = 1f,
     @FloatRange(from = 0.0, fromInclusive = false) absVelocityThreshold: Float = 0.1f
 ) : FloatDecayAnimationSpec {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt
index 819d5b9..9883d77 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteAnimationPolicy.kt
@@ -26,7 +26,7 @@
  * Like [withFrameNanos], but applies the [InfiniteAnimationPolicy] from the calling
  * [CoroutineContext] if there is one.
  */
-suspend fun <R> withInfiniteAnimationFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R =
+public suspend fun <R> withInfiniteAnimationFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R =
     when (val policy = coroutineContext[InfiniteAnimationPolicy]) {
         null -> withFrameNanos(onFrame)
         else -> policy.onInfiniteOperation { withFrameNanos(onFrame) }
@@ -37,6 +37,6 @@
  * [CoroutineContext] if there is one.
  */
 @Suppress("UnnecessaryLambdaCreation")
-suspend inline fun <R> withInfiniteAnimationFrameMillis(
+public suspend inline fun <R> withInfiniteAnimationFrameMillis(
     crossinline onFrame: (frameTimeMillis: Long) -> R
 ): R = withInfiniteAnimationFrameNanos { onFrame(it / 1_000_000L) }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
index 43c85ba..a76d0864 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
@@ -42,7 +42,7 @@
  * @sample androidx.compose.animation.core.samples.InfiniteTransitionSample
  */
 @Composable
-fun rememberInfiniteTransition(label: String = "InfiniteTransition"): InfiniteTransition {
+public fun rememberInfiniteTransition(label: String = "InfiniteTransition"): InfiniteTransition {
     val infiniteTransition = remember { InfiniteTransition(label) }
     infiniteTransition.run()
     return infiniteTransition
@@ -58,7 +58,7 @@
  * @param label A label for differentiating this animation from others in android studio.
  * @sample androidx.compose.animation.core.samples.InfiniteTransitionSample
  */
-class InfiniteTransition internal constructor(val label: String) {
+public class InfiniteTransition internal constructor(public val label: String) {
 
     /**
      * Each animation created using
@@ -68,26 +68,26 @@
      * value from/to an [AnimationVector]. [label] differentiates this animation from others in
      * android studio.
      */
-    inner class TransitionAnimationState<T, V : AnimationVector>
+    public inner class TransitionAnimationState<T, V : AnimationVector>
     internal constructor(
         internal var initialValue: T,
         internal var targetValue: T,
-        val typeConverter: TwoWayConverter<T, V>,
+        public val typeConverter: TwoWayConverter<T, V>,
         animationSpec: AnimationSpec<T>,
-        val label: String
+        public val label: String
     ) : State<T> {
-        override var value by mutableStateOf(initialValue)
+        override var value: T by mutableStateOf(initialValue)
             internal set
 
         /** [AnimationSpec] that is used for current animation run. */
-        var animationSpec: AnimationSpec<T> = animationSpec
+        public var animationSpec: AnimationSpec<T> = animationSpec
             private set
 
         /**
          * All the animation configurations including initial value/velocity & target value for
          * animating from [initialValue] to [targetValue] are captured in [animation].
          */
-        var animation =
+        public var animation: TargetBasedAnimation<T, V> =
             TargetBasedAnimation(this.animationSpec, typeConverter, initialValue, targetValue)
             internal set
 
@@ -148,7 +148,7 @@
     private var isRunning by mutableStateOf(true)
 
     /** List of [TransitionAnimationState]s that are in a [InfiniteTransition]. */
-    val animations: List<TransitionAnimationState<*, *>>
+    public val animations: List<TransitionAnimationState<*, *>>
         get() = _animations.asMutableList()
 
     internal fun addAnimation(animation: TransitionAnimationState<*, *>) {
@@ -237,7 +237,7 @@
  * @see [androidx.compose.animation.animateColor]
  */
 @Composable
-fun <T, V : AnimationVector> InfiniteTransition.animateValue(
+public fun <T, V : AnimationVector> InfiniteTransition.animateValue(
     initialValue: T,
     targetValue: T,
     typeConverter: TwoWayConverter<T, V>,
@@ -288,7 +288,7 @@
  * @see [androidx.compose.animation.animateColor]
  */
 @Composable
-fun InfiniteTransition.animateFloat(
+public fun InfiniteTransition.animateFloat(
     initialValue: Float,
     targetValue: Float,
     animationSpec: InfiniteRepeatableSpec<Float>,
@@ -301,7 +301,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun rememberInfiniteTransition(): InfiniteTransition {
+public fun rememberInfiniteTransition(): InfiniteTransition {
     return rememberInfiniteTransition("InfiniteTransition")
 }
 
@@ -310,7 +310,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun <T, V : AnimationVector> InfiniteTransition.animateValue(
+public fun <T, V : AnimationVector> InfiniteTransition.animateValue(
     initialValue: T,
     targetValue: T,
     typeConverter: TwoWayConverter<T, V>,
@@ -330,7 +330,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun InfiniteTransition.animateFloat(
+public fun InfiniteTransition.animateFloat(
     initialValue: Float,
     targetValue: Float,
     animationSpec: InfiniteRepeatableSpec<Float>
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt
index 95e59a1..5544fcb 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntListExtension.kt
@@ -17,6 +17,7 @@
 package androidx.compose.animation.core
 
 import androidx.collection.IntList
+import kotlin.jvm.JvmOverloads
 
 // TODO(b/311454748): Move to :collection as public API once it's back on alpha. Also, add versions
 //  for LongList and FloatList.
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalAnimationApi.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalAnimationApi.kt
index 4eec185..2aed1438 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalAnimationApi.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalAnimationApi.kt
@@ -25,4 +25,4 @@
     AnnotationTarget.PROPERTY_GETTER
 )
 @Retention(AnnotationRetention.BINARY)
-annotation class InternalAnimationApi
+public annotation class InternalAnimationApi
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalMutatorMutex.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalMutatorMutex.kt
index 3d93081..533f06e 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalMutatorMutex.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InternalMutatorMutex.kt
@@ -58,13 +58,7 @@
  * lookups to build the exception message and stack trace collection. Remove if these are changed in
  * kotlinx.coroutines.
  */
-private class MutationInterruptedException : CancellationException("Mutation interrupted") {
-    override fun fillInStackTrace(): Throwable {
-        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
-        stackTrace = emptyArray()
-        return this
-    }
-}
+internal expect class MutationInterruptedException() : CancellationException
 
 /**
  * Mutual exclusion for UI state mutation over time.
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
index 85b9aab..93f4a53 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
@@ -50,7 +50,7 @@
  * @param path The [Path] to use to make the curve representing the easing curve.
  */
 @Immutable
-class PathEasing(private val path: Path) : Easing {
+public class PathEasing(private val path: Path) : Easing {
     private lateinit var intervals: IntervalTree<PathSegment>
 
     override fun transform(fraction: Float): Float {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt
index 4f8007f..697eca6 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt
@@ -32,7 +32,7 @@
 
 /** Returns the estimated time that the spring will last be at [delta] */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-fun estimateAnimationDurationMillis(
+public fun estimateAnimationDurationMillis(
     stiffness: Float,
     dampingRatio: Float,
     initialVelocity: Float,
@@ -55,7 +55,7 @@
 
 /** Returns the estimated time that the spring will last be at [delta] */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-fun estimateAnimationDurationMillis(
+public fun estimateAnimationDurationMillis(
     stiffness: Double,
     dampingRatio: Double,
     initialVelocity: Double,
@@ -82,7 +82,7 @@
 
 /** Returns the estimated time that the spring will last be at [delta] */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-fun estimateAnimationDurationMillis(
+public fun estimateAnimationDurationMillis(
     springConstant: Double,
     dampingCoefficient: Double,
     mass: Double,
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
index 7b365b6..6eb3267 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
@@ -38,7 +38,7 @@
  * @sample androidx.compose.animation.core.samples.suspendAnimateFloatVariant
  * @see AnimationState.animateTo
  */
-suspend fun animate(
+public suspend fun animate(
     initialValue: Float,
     targetValue: Float,
     initialVelocity: Float = 0f,
@@ -61,7 +61,7 @@
  * @param block Will be invoked on each animation frame with up-to-date value and velocity.
  * @see AnimationState.animateDecay
  */
-suspend fun animateDecay(
+public suspend fun animateDecay(
     initialValue: Float,
     initialVelocity: Float,
     animationSpec: FloatDecayAnimationSpec,
@@ -86,7 +86,7 @@
  *
  * @see AnimationState.animateTo
  */
-suspend fun <T, V : AnimationVector> animate(
+public suspend fun <T, V : AnimationVector> animate(
     typeConverter: TwoWayConverter<T, V>,
     initialValue: T,
     targetValue: T,
@@ -131,7 +131,7 @@
  *   the animation related info can be accessed via [AnimationScope].
  * @sample androidx.compose.animation.core.samples.animateToOnAnimationState
  */
-suspend fun <T, V : AnimationVector> AnimationState<T, V>.animateTo(
+public suspend fun <T, V : AnimationVector> AnimationState<T, V>.animateTo(
     targetValue: T,
     animationSpec: AnimationSpec<T> = spring(),
     sequentialAnimation: Boolean = false,
@@ -174,7 +174,7 @@
  *   loop will exit after the [block] returns. All the animation related info can be accessed via
  *   [AnimationScope].
  */
-suspend fun <T, V : AnimationVector> AnimationState<T, V>.animateDecay(
+public suspend fun <T, V : AnimationVector> AnimationState<T, V>.animateDecay(
     animationSpec: DecayAnimationSpec<T>,
     sequentialAnimation: Boolean = false,
     block: AnimationScope<T, V>.() -> Unit = {}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index 592ef33..b61ed67 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -21,6 +21,7 @@
 import androidx.annotation.FloatRange
 import androidx.annotation.RestrictTo
 import androidx.collection.MutableObjectList
+import androidx.compose.animation.core.internal.JvmDefaultWithCompatibility
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
@@ -49,6 +50,7 @@
 import androidx.compose.ui.util.fastForEach
 import kotlin.coroutines.coroutineContext
 import kotlin.coroutines.resume
+import kotlin.jvm.JvmName
 import kotlin.math.max
 import kotlin.math.roundToLong
 import kotlinx.coroutines.CancellableContinuation
@@ -82,7 +84,7 @@
  * @see Transition.animateValue
  */
 @Composable
-fun <T> updateTransition(targetState: T, label: String? = null): Transition<T> {
+public fun <T> updateTransition(targetState: T, label: String? = null): Transition<T> {
     val transition = remember { Transition(targetState, label = label) }
     transition.animateTo(targetState)
     DisposableEffect(transition) {
@@ -101,19 +103,19 @@
  * Use with [rememberTransition] to create a [Transition] that can be dynamically targeted with
  * [MutableTransitionState] or seekable with [SeekableTransitionState].
  */
-sealed class TransitionState<S> {
+public sealed class TransitionState<S> {
     /**
      * Current state of the transition. If there is an active transition, [currentState] and
      * [targetState] are different.
      */
-    abstract var currentState: S
+    public abstract var currentState: S
         internal set
 
     /**
      * Target state of the transition. If this is the same as [currentState], no transition is
      * active.
      */
-    abstract var targetState: S
+    public abstract var targetState: S
         internal set
 
     // Updated from Transition
@@ -154,7 +156,7 @@
  * @sample androidx.compose.animation.core.samples.InitialStateSample
  * @see rememberTransition
  */
-class MutableTransitionState<S>(initialState: S) : TransitionState<S>() {
+public class MutableTransitionState<S>(initialState: S) : TransitionState<S>() {
     /**
      * Current state of the transition. [currentState] is initialized to the initialState that the
      * [MutableTransitionState] is constructed with.
@@ -183,7 +185,7 @@
      *
      * @sample androidx.compose.animation.core.samples.TransitionStateIsIdleSample
      */
-    val isIdle: Boolean
+    public val isIdle: Boolean
         get() = (currentState == targetState) && !isRunning
 
     override fun transitionConfigured(transition: Transition<S>) {}
@@ -212,7 +214,7 @@
  *
  * @sample androidx.compose.animation.core.samples.SeekingAnimationSample
  */
-class SeekableTransitionState<S>(initialState: S) : TransitionState<S>() {
+public class SeekableTransitionState<S>(initialState: S) : TransitionState<S>() {
     override var targetState: S by mutableStateOf(initialState)
         internal set
 
@@ -245,7 +247,7 @@
      * If [targetState] and [currentState] are the same, [fraction] will be 0.
      */
     @get:FloatRange(from = 0.0, to = 1.0)
-    var fraction: Float by mutableFloatStateOf(0f)
+    public var fraction: Float by mutableFloatStateOf(0f)
         private set
 
     /** The continuation used when waiting for a composition. */
@@ -426,7 +428,7 @@
      * @sample androidx.compose.animation.core.samples.SnapToSample
      * @see animateTo
      */
-    suspend fun snapTo(targetState: S) {
+    public suspend fun snapTo(targetState: S) {
         val transition = transition ?: return
         if (
             currentState == targetState && [email protected] == targetState
@@ -473,7 +475,7 @@
      * @sample androidx.compose.animation.core.samples.SeekToSample
      * @see animateTo
      */
-    suspend fun seekTo(
+    public suspend fun seekTo(
         @FloatRange(from = 0.0, to = 1.0) fraction: Float,
         targetState: S = this.targetState
     ) {
@@ -588,7 +590,7 @@
      *   transition is linearly traversed based on the duration of the transition.
      */
     @Suppress("DocumentExceptions")
-    suspend fun animateTo(
+    public suspend fun animateTo(
         targetState: S = this.targetState,
         animationSpec: FiniteAnimationSpec<Float>? = null
     ) {
@@ -792,7 +794,7 @@
  * @sample androidx.compose.animation.core.samples.DoubleTapToLikeSample
  */
 @Composable
-fun <T> rememberTransition(
+public fun <T> rememberTransition(
     transitionState: TransitionState<T>,
     label: String? = null
 ): Transition<T> {
@@ -845,7 +847,7 @@
     replaceWith = ReplaceWith("rememberTransition(transitionState, label)")
 )
 @Composable
-fun <T> updateTransition(
+public fun <T> updateTransition(
     transitionState: MutableTransitionState<T>,
     label: String? = null
 ): Transition<T> {
@@ -872,11 +874,11 @@
  */
 // TODO: Support creating Transition outside of composition and support imperative use of Transition
 @Stable
-class Transition<S>
+public class Transition<S>
 internal constructor(
     private val transitionState: TransitionState<S>,
-    @get:RestrictTo(RestrictTo.Scope.LIBRARY) val parentTransition: Transition<*>?,
-    val label: String? = null
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY) public val parentTransition: Transition<*>?,
+    public val label: String? = null
 ) {
     @PublishedApi
     internal constructor(
@@ -900,25 +902,25 @@
      * the transition is finished. Once the transition is finished, [currentState] will be set to
      * [targetState]. [currentState] is backed by a [MutableState].
      */
-    val currentState: S
+    public val currentState: S
         get() = transitionState.currentState
 
     /**
      * Target state of the transition. This will be read by all child animations to determine their
      * most up-to-date target values.
      */
-    var targetState: S by mutableStateOf(currentState)
+    public var targetState: S by mutableStateOf(currentState)
         internal set
 
     /**
      * [segment] contains the initial state and the target state of the currently on-going
      * transition.
      */
-    var segment: Segment<S> by mutableStateOf(SegmentImpl(currentState, currentState))
+    public var segment: Segment<S> by mutableStateOf(SegmentImpl(currentState, currentState))
         private set
 
     /** Indicates whether there is any animation running in the transition. */
-    val isRunning: Boolean
+    public val isRunning: Boolean
         get() = startTimeNanos != AnimationConstants.UnspecifiedTime
 
     private var _playTimeNanos by mutableLongStateOf(0L)
@@ -929,7 +931,7 @@
      */
     @get:RestrictTo(RestrictTo.Scope.LIBRARY)
     @set:RestrictTo(RestrictTo.Scope.LIBRARY)
-    var playTimeNanos: Long
+    public var playTimeNanos: Long
         get() {
             return parentTransition?.playTimeNanos ?: _playTimeNanos
         }
@@ -950,17 +952,17 @@
     private val _transitions = mutableStateListOf<Transition<*>>()
 
     /** List of child transitions in a [Transition]. */
-    val transitions: List<Transition<*>>
+    public val transitions: List<Transition<*>>
         get() = _transitions
 
     /** List of [TransitionAnimationState]s that are in a [Transition]. */
-    val animations: List<TransitionAnimationState<*, *>>
+    public val animations: List<TransitionAnimationState<*, *>>
         get() = _animations
 
     // Seeking related
     @get:RestrictTo(RestrictTo.Scope.LIBRARY)
     @set:RestrictTo(RestrictTo.Scope.LIBRARY)
-    var isSeeking: Boolean by mutableStateOf(false)
+    public var isSeeking: Boolean by mutableStateOf(false)
         internal set
 
     internal var lastSeekedTimeNanos = 0L
@@ -976,7 +978,7 @@
     @get:Suppress("GetterSetterNames") // Don't care about Java name for this property
     @InternalAnimationApi
     @get:InternalAnimationApi
-    val hasInitialValueAnimations: Boolean
+    public val hasInitialValueAnimations: Boolean
         get() =
             _animations.fastAny { it.initialValueState != null } ||
                 _transitions.fastAny { it.hasInitialValueAnimations }
@@ -989,7 +991,7 @@
      * to [Transition]. It's strongly recommended to query this *after* all the animations in the
      * [Transition] are set up.
      */
-    val totalDurationNanos: Long by derivedStateOf { calculateTotalDurationNanos() }
+    public val totalDurationNanos: Long by derivedStateOf { calculateTotalDurationNanos() }
 
     private fun calculateTotalDurationNanos(): Long {
         var maxDurationNanos = 0L
@@ -1094,7 +1096,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
     @OptIn(InternalAnimationApi::class)
     @JvmName("seek")
-    fun setPlaytimeAfterInitialAndTargetStateEstablished(
+    public fun setPlaytimeAfterInitialAndTargetStateEstablished(
         initialState: S,
         targetState: S,
         playTimeNanos: Long
@@ -1287,12 +1289,12 @@
      * [TransitionAnimationState] in [Transition].
      */
     @Stable
-    inner class TransitionAnimationState<T, V : AnimationVector>
+    public inner class TransitionAnimationState<T, V : AnimationVector>
     internal constructor(
         initialValue: T,
         initialVelocityVector: V,
-        val typeConverter: TwoWayConverter<T, V>,
-        val label: String
+        public val typeConverter: TwoWayConverter<T, V>,
+        public val label: String
     ) : State<T> {
 
         // Changed during composition, may rollback
@@ -1304,14 +1306,14 @@
          * [AnimationSpec] that is used for current animation run. This can change when
          * [targetState] changes.
          */
-        var animationSpec: FiniteAnimationSpec<T> by mutableStateOf(defaultSpring)
+        public var animationSpec: FiniteAnimationSpec<T> by mutableStateOf(defaultSpring)
             private set
 
         /**
          * All the animation configurations including initial value/velocity & target value for
          * animating from [currentState] to [targetState] are captured in [animation].
          */
-        var animation: TargetBasedAnimation<T, V> by
+        public var animation: TargetBasedAnimation<T, V> by
             mutableStateOf(
                 TargetBasedAnimation(
                     animationSpec,
@@ -1339,7 +1341,7 @@
         private var useOnlyInitialValue = false
 
         // Changed during animation, no concerns of rolling back
-        override var value by mutableStateOf(initialValue)
+        override var value: T by mutableStateOf(initialValue)
             internal set
 
         private var velocityVector: V = initialVelocityVector
@@ -1585,18 +1587,18 @@
      * transition from the child animations.
      */
     @JvmDefaultWithCompatibility
-    interface Segment<S> {
+    public interface Segment<S> {
         /** Initial state of a Transition Segment. This is the state that transition starts from. */
-        val initialState: S
+        public val initialState: S
 
         /** Target state of a Transition Segment. This is the state that transition will end on. */
-        val targetState: S
+        public val targetState: S
 
         /**
          * Returns whether the provided state matches the [initialState] && the provided
          * [targetState] matches [Segment.targetState].
          */
-        infix fun S.isTransitioningTo(targetState: S): Boolean {
+        public infix fun S.isTransitioningTo(targetState: S): Boolean {
             return this == initialState && targetState == [email protected]
         }
     }
@@ -1610,8 +1612,11 @@
      * [DeferredAnimation.animate] method.
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
-    inner class DeferredAnimation<T, V : AnimationVector>
-    internal constructor(val typeConverter: TwoWayConverter<T, V>, val label: String) {
+    public inner class DeferredAnimation<T, V : AnimationVector>
+    internal constructor(
+        public val typeConverter: TwoWayConverter<T, V>,
+        public val label: String
+    ) {
         internal var data: DeferredAnimationData<T, V>? by mutableStateOf(null)
 
         internal inner class DeferredAnimationData<T, V : AnimationVector>(
@@ -1648,7 +1653,7 @@
          * [transitionSpec] and [targetValueByState] for the mapping from target state to animation
          * spec and target value, respectively.
          */
-        fun animate(
+        public fun animate(
             transitionSpec: Segment<S>.() -> FiniteAnimationSpec<T>,
             targetValueByState: (state: S) -> T
         ): State<T> {
@@ -1722,7 +1727,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @Composable
-fun <S, T, V : AnimationVector> Transition<S>.createDeferredAnimation(
+public fun <S, T, V : AnimationVector> Transition<S>.createDeferredAnimation(
     typeConverter: TwoWayConverter<T, V>,
     label: String = "DeferredAnimation"
 ): Transition<S>.DeferredAnimation<T, V> {
@@ -1751,7 +1756,7 @@
  */
 @ExperimentalTransitionApi
 @Composable
-inline fun <S, T> Transition<S>.createChildTransition(
+public inline fun <S, T> Transition<S>.createChildTransition(
     label: String = "ChildTransition",
     transformToChildState: @Composable (parentState: S) -> T,
 ): Transition<T> {
@@ -1818,7 +1823,7 @@
  * @see androidx.compose.animation.animateColor
  */
 @Composable
-inline fun <S, T, V : AnimationVector> Transition<S>.animateValue(
+public inline fun <S, T, V : AnimationVector> Transition<S>.animateValue(
     typeConverter: TwoWayConverter<T, V>,
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<T> = {
         spring()
@@ -1899,7 +1904,7 @@
  * @see androidx.compose.animation.animateColor
  */
 @Composable
-inline fun <S> Transition<S>.animateFloat(
+public inline fun <S> Transition<S>.animateFloat(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Float> = {
         spring()
     },
@@ -1929,7 +1934,7 @@
  * @return A [State] object, the value of which is updated by animation
  */
 @Composable
-inline fun <S> Transition<S>.animateDp(
+public inline fun <S> Transition<S>.animateDp(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Dp> = {
         spring(visibilityThreshold = Dp.VisibilityThreshold)
     },
@@ -1959,7 +1964,7 @@
  * @return A [State] object, the value of which is updated by animation
  */
 @Composable
-inline fun <S> Transition<S>.animateOffset(
+public inline fun <S> Transition<S>.animateOffset(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Offset> = {
         spring(visibilityThreshold = Offset.VisibilityThreshold)
     },
@@ -1989,7 +1994,7 @@
  * @return A [State] object, the value of which is updated by animation
  */
 @Composable
-inline fun <S> Transition<S>.animateSize(
+public inline fun <S> Transition<S>.animateSize(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Size> = {
         spring(visibilityThreshold = Size.VisibilityThreshold)
     },
@@ -2019,7 +2024,7 @@
  * @return A [State] object, the value of which is updated by animation
  */
 @Composable
-inline fun <S> Transition<S>.animateIntOffset(
+public inline fun <S> Transition<S>.animateIntOffset(
     noinline transitionSpec:
         @Composable
         Transition.Segment<S>.() -> FiniteAnimationSpec<IntOffset> =
@@ -2053,7 +2058,7 @@
  * @return A [State] object, the value of which is updated by animation
  */
 @Composable
-inline fun <S> Transition<S>.animateInt(
+public inline fun <S> Transition<S>.animateInt(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Int> = {
         spring(visibilityThreshold = 1)
     },
@@ -2083,7 +2088,7 @@
  * @return A [State] object, the value of which is updated by animation
  */
 @Composable
-inline fun <S> Transition<S>.animateIntSize(
+public inline fun <S> Transition<S>.animateIntSize(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<IntSize> =
         {
             spring(visibilityThreshold = IntSize(1, 1))
@@ -2114,7 +2119,7 @@
  * @return A [State] object, the value of which is updated by animation
  */
 @Composable
-inline fun <S> Transition<S>.animateRect(
+public inline fun <S> Transition<S>.animateRect(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Rect> = {
         spring(visibilityThreshold = Rect.VisibilityThreshold)
     },
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
index 6e5f1ec..75b4aba 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorConverters.kt
@@ -31,19 +31,19 @@
  * [AnimationVector], and convert the [AnimationVector] back to the type [T]. This allows animations
  * to run on any type of objects, e.g. position, rectangle, color, etc.
  */
-interface TwoWayConverter<T, V : AnimationVector> {
+public interface TwoWayConverter<T, V : AnimationVector> {
     /**
      * Defines how a type [T] should be converted to a Vector type (i.e. [AnimationVector1D],
      * [AnimationVector2D], [AnimationVector3D] or [AnimationVector4D], depends on the dimensions of
      * type T).
      */
-    val convertToVector: (T) -> V
+    public val convertToVector: (T) -> V
     /**
      * Defines how to convert a Vector type (i.e. [AnimationVector1D], [AnimationVector2D],
      * [AnimationVector3D] or [AnimationVector4D], depends on the dimensions of type T) back to type
      * [T].
      */
-    val convertFromVector: (V) -> T
+    public val convertFromVector: (V) -> T
 }
 
 /**
@@ -53,7 +53,7 @@
  * @param convertToVector converts from type [T] to [AnimationVector]
  * @param convertFromVector converts from [AnimationVector] to type [T]
  */
-fun <T, V : AnimationVector> TwoWayConverter(
+public fun <T, V : AnimationVector> TwoWayConverter(
     convertToVector: (T) -> V,
     convertFromVector: (V) -> T
 ): TwoWayConverter<T, V> = TwoWayConverterImpl(convertToVector, convertFromVector)
@@ -68,11 +68,11 @@
     (start * (1 - fraction) + stop * fraction)
 
 /** A [TwoWayConverter] that converts [Float] from and to [AnimationVector1D] */
-val Float.Companion.VectorConverter: TwoWayConverter<Float, AnimationVector1D>
+public val Float.Companion.VectorConverter: TwoWayConverter<Float, AnimationVector1D>
     get() = FloatToVector
 
 /** A [TwoWayConverter] that converts [Int] from and to [AnimationVector1D] */
-val Int.Companion.VectorConverter: TwoWayConverter<Int, AnimationVector1D>
+public val Int.Companion.VectorConverter: TwoWayConverter<Int, AnimationVector1D>
     get() = IntToVector
 
 private val FloatToVector: TwoWayConverter<Float, AnimationVector1D> =
@@ -81,27 +81,27 @@
 private val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
     TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })
 /** A type converter that converts a [Rect] to a [AnimationVector4D], and vice versa. */
-val Rect.Companion.VectorConverter: TwoWayConverter<Rect, AnimationVector4D>
+public val Rect.Companion.VectorConverter: TwoWayConverter<Rect, AnimationVector4D>
     get() = RectToVector
 
 /** A type converter that converts a [Dp] to a [AnimationVector1D], and vice versa. */
-val Dp.Companion.VectorConverter: TwoWayConverter<Dp, AnimationVector1D>
+public val Dp.Companion.VectorConverter: TwoWayConverter<Dp, AnimationVector1D>
     get() = DpToVector
 
 /** A type converter that converts a [DpOffset] to a [AnimationVector2D], and vice versa. */
-val DpOffset.Companion.VectorConverter: TwoWayConverter<DpOffset, AnimationVector2D>
+public val DpOffset.Companion.VectorConverter: TwoWayConverter<DpOffset, AnimationVector2D>
     get() = DpOffsetToVector
 
 /** A type converter that converts a [Size] to a [AnimationVector2D], and vice versa. */
-val Size.Companion.VectorConverter: TwoWayConverter<Size, AnimationVector2D>
+public val Size.Companion.VectorConverter: TwoWayConverter<Size, AnimationVector2D>
     get() = SizeToVector
 
 /** A type converter that converts a [Offset] to a [AnimationVector2D], and vice versa. */
-val Offset.Companion.VectorConverter: TwoWayConverter<Offset, AnimationVector2D>
+public val Offset.Companion.VectorConverter: TwoWayConverter<Offset, AnimationVector2D>
     get() = OffsetToVector
 
 /** A type converter that converts a [IntOffset] to a [AnimationVector2D], and vice versa. */
-val IntOffset.Companion.VectorConverter: TwoWayConverter<IntOffset, AnimationVector2D>
+public val IntOffset.Companion.VectorConverter: TwoWayConverter<IntOffset, AnimationVector2D>
     get() = IntOffsetToVector
 
 /**
@@ -109,7 +109,7 @@
  *
  * Clamps negative values to zero when converting back to [IntSize].
  */
-val IntSize.Companion.VectorConverter: TwoWayConverter<IntSize, AnimationVector2D>
+public val IntSize.Companion.VectorConverter: TwoWayConverter<IntSize, AnimationVector2D>
     get() = IntSizeToVector
 
 /** A type converter that converts a [Dp] to a [AnimationVector1D], and vice versa. */
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
index 266e8dd..d2e83ca 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
@@ -21,6 +21,8 @@
 import androidx.collection.MutableIntList
 import androidx.collection.MutableIntObjectMap
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
+import androidx.compose.animation.core.internal.JvmDefaultWithCompatibility
+import kotlin.jvm.JvmInline
 import kotlin.math.min
 
 /**
@@ -44,13 +46,13 @@
  * @see Animation
  */
 @JvmDefaultWithCompatibility
-interface VectorizedAnimationSpec<V : AnimationVector> {
+public interface VectorizedAnimationSpec<V : AnimationVector> {
     /**
      * Whether or not the [VectorizedAnimationSpec] specifies an infinite animation. That is, one
      * that will not finish by itself, one that needs an external action to stop. For examples, an
      * indeterminate progress bar, which will only stop when it is removed from the composition.
      */
-    val isInfinite: Boolean
+    public val isInfinite: Boolean
 
     /**
      * Calculates the value of the animation at given the playtime, with the provided start/end
@@ -61,7 +63,7 @@
      * @param targetValue end value of the animation
      * @param initialVelocity start velocity of the animation
      */
-    fun getValueFromNanos(
+    public fun getValueFromNanos(
         playTimeNanos: Long,
         initialValue: V,
         targetValue: V,
@@ -77,7 +79,7 @@
      * @param targetValue end value of the animation
      * @param initialVelocity start velocity of the animation
      */
-    fun getVelocityFromNanos(
+    public fun getVelocityFromNanos(
         playTimeNanos: Long,
         initialValue: V,
         targetValue: V,
@@ -95,7 +97,7 @@
      * @param initialVelocity start velocity of the animation
      */
     @Suppress("MethodNameUnits")
-    fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long
+    public fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long
 
     /**
      * Calculates the end velocity of the animation with the provided start/end values, and start
@@ -107,7 +109,7 @@
      * @param targetValue end value of the animation
      * @param initialVelocity start velocity of the animation
      */
-    fun getEndVelocity(initialValue: V, targetValue: V, initialVelocity: V): V =
+    public fun getEndVelocity(initialValue: V, targetValue: V, initialVelocity: V): V =
         getVelocityFromNanos(
             getDurationNanos(initialValue, targetValue, initialVelocity),
             initialValue,
@@ -156,20 +158,20 @@
  * __not__ implement this is: [InfiniteRepeatableSpec].
  */
 @JvmDefaultWithCompatibility
-interface VectorizedFiniteAnimationSpec<V : AnimationVector> : VectorizedAnimationSpec<V> {
+public interface VectorizedFiniteAnimationSpec<V : AnimationVector> : VectorizedAnimationSpec<V> {
     override val isInfinite: Boolean
         get() = false
 }
 
 /** Base class for [VectorizedAnimationSpec]s that are based on a fixed [durationMillis]. */
 @JvmDefaultWithCompatibility
-interface VectorizedDurationBasedAnimationSpec<V : AnimationVector> :
+public interface VectorizedDurationBasedAnimationSpec<V : AnimationVector> :
     VectorizedFiniteAnimationSpec<V> {
     /** duration is the amount of time while animation is not yet finished. */
-    val durationMillis: Int
+    public val durationMillis: Int
 
     /** delay defines the amount of time that animation can be delayed. */
-    val delayMillis: Int
+    public val delayMillis: Int
 
     @Suppress("MethodNameUnits")
     override fun getDurationNanos(initialValue: V, targetValue: V, initialVelocity: V): Long =
@@ -210,7 +212,7 @@
  *
  * @see [KeyframesSpec]
  */
-class VectorizedKeyframesSpec<V : AnimationVector>
+public class VectorizedKeyframesSpec<V : AnimationVector>
 internal constructor(
     // List of all timestamps. Must include start (time = 0), end (time = durationMillis) and all
     // other timestamps found in [keyframes].
@@ -232,7 +234,7 @@
      * @param delayMillis the amount of the time the animation should wait before it starts.
      *   Defaults to 0.
      */
-    constructor(
+    public constructor(
         keyframes: Map<Int, Pair<V, Easing>>,
         durationMillis: Int,
         delayMillis: Int = 0
@@ -519,27 +521,27 @@
  * @see ArcAnimationSpec
  */
 @JvmInline
-value class ArcMode internal constructor(internal val value: Int) {
+public value class ArcMode internal constructor(internal val value: Int) {
 
-    companion object {
+    public companion object {
         /**
          * Interpolates using a quarter of an Ellipse where the curve is "above" the center of the
          * Ellipse.
          */
-        val ArcAbove = ArcMode(ArcSpline.ArcAbove)
+        public val ArcAbove: ArcMode = ArcMode(ArcSpline.ArcAbove)
 
         /**
          * Interpolates using a quarter of an Ellipse where the curve is "below" the center of the
          * Ellipse.
          */
-        val ArcBelow = ArcMode(ArcSpline.ArcBelow)
+        public val ArcBelow: ArcMode = ArcMode(ArcSpline.ArcBelow)
 
         /**
          * An [ArcMode] that forces linear interpolation.
          *
          * You'll likely only use this mode within a keyframe.
          */
-        val ArcLinear = ArcMode(ArcSpline.ArcStartLinear)
+        public val ArcLinear: ArcMode = ArcMode(ArcSpline.ArcStartLinear)
     }
 }
 
@@ -549,7 +551,7 @@
  * @param delayMillis the amount of time (in milliseconds) that the animation should wait before it
  *   starts. Defaults to 0.
  */
-class VectorizedSnapSpec<V : AnimationVector>(override val delayMillis: Int = 0) :
+public class VectorizedSnapSpec<V : AnimationVector>(override val delayMillis: Int = 0) :
     VectorizedDurationBasedAnimationSpec<V> {
 
     override fun getValueFromNanos(
@@ -593,7 +595,7 @@
  *   [RepeatMode.Restart]) or from the end (i.e. [RepeatMode.Reverse])
  * @param initialStartOffset offsets the start of the animation
  */
-class VectorizedInfiniteRepeatableSpec<V : AnimationVector>(
+public class VectorizedInfiniteRepeatableSpec<V : AnimationVector>(
     private val animation: VectorizedDurationBasedAnimationSpec<V>,
     private val repeatMode: RepeatMode = RepeatMode.Restart,
     initialStartOffset: StartOffset = StartOffset(0)
@@ -604,7 +606,7 @@
             "This method has been deprecated in favor of the constructor that" +
                 " accepts start offset."
     )
-    constructor(
+    public constructor(
         animation: VectorizedDurationBasedAnimationSpec<V>,
         repeatMode: RepeatMode = RepeatMode.Restart
     ) : this(animation, repeatMode, StartOffset(0))
@@ -703,7 +705,7 @@
  *   [RepeatMode.Restart]) or from the end (i.e. [RepeatMode.Reverse])
  * @param initialStartOffset offsets the start of the animation
  */
-class VectorizedRepeatableSpec<V : AnimationVector>(
+public class VectorizedRepeatableSpec<V : AnimationVector>(
     private val iterations: Int,
     private val animation: VectorizedDurationBasedAnimationSpec<V>,
     private val repeatMode: RepeatMode = RepeatMode.Restart,
@@ -715,7 +717,7 @@
             "This method has been deprecated in favor of the constructor that accepts" +
                 " start offset."
     )
-    constructor(
+    public constructor(
         iterations: Int,
         animation: VectorizedDurationBasedAnimationSpec<V>,
         repeatMode: RepeatMode = RepeatMode.Restart
@@ -795,55 +797,55 @@
 }
 
 /** Physics class contains a number of recommended configurations for physics animations. */
-object Spring {
+public object Spring {
     /** Stiffness constant for extremely stiff spring */
-    const val StiffnessHigh = 10_000f
+    public const val StiffnessHigh: Float = 10_000f
 
     /**
      * Stiffness constant for medium stiff spring. This is the default stiffness for spring force.
      */
-    const val StiffnessMedium = 1500f
+    public const val StiffnessMedium: Float = 1500f
 
     /**
      * Stiffness constant for medium-low stiff spring. This is the default stiffness for springs
      * used in enter/exit transitions.
      */
-    const val StiffnessMediumLow = 400f
+    public const val StiffnessMediumLow: Float = 400f
 
     /** Stiffness constant for a spring with low stiffness. */
-    const val StiffnessLow = 200f
+    public const val StiffnessLow: Float = 200f
 
     /** Stiffness constant for a spring with very low stiffness. */
-    const val StiffnessVeryLow = 50f
+    public const val StiffnessVeryLow: Float = 50f
 
     /**
      * Damping ratio for a very bouncy spring. Note for under-damped springs (i.e. damping ratio <
      * 1), the lower the damping ratio, the more bouncy the spring.
      */
-    const val DampingRatioHighBouncy = 0.2f
+    public const val DampingRatioHighBouncy: Float = 0.2f
 
     /**
      * Damping ratio for a medium bouncy spring. This is also the default damping ratio for spring
      * force. Note for under-damped springs (i.e. damping ratio < 1), the lower the damping ratio,
      * the more bouncy the spring.
      */
-    const val DampingRatioMediumBouncy = 0.5f
+    public const val DampingRatioMediumBouncy: Float = 0.5f
 
     /**
      * Damping ratio for a spring with low bounciness. Note for under-damped springs (i.e. damping
      * ratio < 1), the lower the damping ratio, the higher the bounciness.
      */
-    const val DampingRatioLowBouncy = 0.75f
+    public const val DampingRatioLowBouncy: Float = 0.75f
 
     /**
      * Damping ratio for a spring with no bounciness. This damping ratio will create a critically
      * damped spring that returns to equilibrium within the shortest amount of time without
      * oscillating.
      */
-    const val DampingRatioNoBouncy = 1f
+    public const val DampingRatioNoBouncy: Float = 1f
 
     /** Default cutoff for rounding off physics based animations */
-    const val DefaultDisplacementThreshold = 0.01f
+    public const val DefaultDisplacementThreshold: Float = 0.01f
 }
 
 /** Internal data structure for storing different FloatAnimations for different dimensions. */
@@ -854,9 +856,12 @@
 /**
  * [VectorizedSpringSpec] uses spring animations to animate (each dimension of) [AnimationVector]s.
  */
-class VectorizedSpringSpec<V : AnimationVector>
-private constructor(val dampingRatio: Float, val stiffness: Float, anims: Animations) :
-    VectorizedFiniteAnimationSpec<V> by VectorizedFloatAnimationSpec<V>(anims) {
+public class VectorizedSpringSpec<V : AnimationVector>
+private constructor(
+    public val dampingRatio: Float,
+    public val stiffness: Float,
+    anims: Animations
+) : VectorizedFiniteAnimationSpec<V> by VectorizedFloatAnimationSpec<V>(anims) {
 
     /**
      * Creates a [VectorizedSpringSpec] that uses the same spring constants (i.e. [dampingRatio] and
@@ -868,7 +873,7 @@
      * @param stiffness stiffness of the spring. [Spring.StiffnessMedium] by default.
      * @param visibilityThreshold specifies the visibility threshold for each dimension.
      */
-    constructor(
+    public constructor(
         dampingRatio: Float = Spring.DampingRatioNoBouncy,
         stiffness: Float = Spring.StiffnessMedium,
         visibilityThreshold: V? = null
@@ -913,10 +918,10 @@
  * @param easing the easing curve used by the animation. [FastOutSlowInEasing] by default.
  */
 // TODO: Support different tween on different dimens
-class VectorizedTweenSpec<V : AnimationVector>(
+public class VectorizedTweenSpec<V : AnimationVector>(
     override val durationMillis: Int = DefaultDurationMillis,
     override val delayMillis: Int = 0,
-    val easing: Easing = FastOutSlowInEasing
+    public val easing: Easing = FastOutSlowInEasing
 ) : VectorizedDurationBasedAnimationSpec<V> {
 
     private val anim =
@@ -946,7 +951,7 @@
  * into a multi-dimensional [VectorizedFloatAnimationSpec], by using the same [FloatAnimationSpec]
  * on each dimension of the [AnimationVector] that is being animated.
  */
-class VectorizedFloatAnimationSpec<V : AnimationVector>
+public class VectorizedFloatAnimationSpec<V : AnimationVector>
 internal constructor(private val anims: Animations) : VectorizedFiniteAnimationSpec<V> {
     private lateinit var valueVector: V
     private lateinit var velocityVector: V
@@ -958,7 +963,7 @@
      *
      * @param anim the animation spec for animating each dimension of the [AnimationVector]
      */
-    constructor(
+    public constructor(
         anim: FloatAnimationSpec
     ) : this(
         object : Animations {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
index 1fab462..c786333 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedDecayAnimationSpec.kt
@@ -34,12 +34,12 @@
  *
  * @see DecayAnimation
  */
-interface VectorizedDecayAnimationSpec<V : AnimationVector> {
+public interface VectorizedDecayAnimationSpec<V : AnimationVector> {
     /**
      * This is the absolute value of a velocity threshold, below which the animation is considered
      * finished.
      */
-    val absVelocityThreshold: Float
+    public val absVelocityThreshold: Float
 
     /**
      * Returns the value of the animation at the given time.
@@ -48,7 +48,7 @@
      * @param initialValue The initialValue value of the animation
      * @param initialVelocity The initialValue velocity of the animation
      */
-    fun getValueFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V
+    public fun getValueFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V
 
     /**
      * Returns the duration of the decay animation, in nanoseconds.
@@ -56,7 +56,8 @@
      * @param initialValue initialValue value of the animation
      * @param initialVelocity initialValue velocity of the animation
      */
-    @Suppress("MethodNameUnits") fun getDurationNanos(initialValue: V, initialVelocity: V): Long
+    @Suppress("MethodNameUnits")
+    public fun getDurationNanos(initialValue: V, initialVelocity: V): Long
 
     /**
      * Returns the velocity of the animation at the given time.
@@ -65,7 +66,7 @@
      * @param initialValue The initialValue value of the animation
      * @param initialVelocity The initialValue velocity of the animation
      */
-    fun getVelocityFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V
+    public fun getVelocityFromNanos(playTimeNanos: Long, initialValue: V, initialVelocity: V): V
 
     /**
      * Returns the target value of the animation based on the initial condition of the animation (
@@ -74,5 +75,5 @@
      * @param initialValue The initial value of the animation
      * @param initialVelocity The initial velocity of the animation
      */
-    fun getTargetValue(initialValue: V, initialVelocity: V): V
+    public fun getTargetValue(initialValue: V, initialVelocity: V): V
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
index c14d8be..8852c65 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VisibilityThresholds.kt
@@ -36,7 +36,7 @@
  * to be no longer visible. The animation system uses this to signal to some default [spring]
  * animations to stop when the value is close enough to the target.
  */
-val IntOffset.Companion.VisibilityThreshold: IntOffset
+public val IntOffset.Companion.VisibilityThreshold: IntOffset
     get() = IntOffset(1, 1)
 
 /**
@@ -44,7 +44,7 @@
  * be no longer visible. The animation system uses this to signal to some default [spring]
  * animations to stop when the value is close enough to the target.
  */
-val Offset.Companion.VisibilityThreshold: Offset
+public val Offset.Companion.VisibilityThreshold: Offset
     get() = Offset(PxVisibilityThreshold, PxVisibilityThreshold)
 
 /**
@@ -52,7 +52,7 @@
  * no longer visible. The animation system uses this to signal to some default [spring] animations
  * to stop when the value is close enough to the target.
  */
-val Int.Companion.VisibilityThreshold: Int
+public val Int.Companion.VisibilityThreshold: Int
     get() = 1
 
 /**
@@ -60,7 +60,7 @@
  * no longer visible. The animation system uses this to signal to some default [spring] animations
  * to stop when the value is close enough to the target.
  */
-val Dp.Companion.VisibilityThreshold: Dp
+public val Dp.Companion.VisibilityThreshold: Dp
     get() = DpVisibilityThreshold.dp
 
 /**
@@ -68,7 +68,7 @@
  * to be no longer visible. The animation system uses this to signal to some default [spring]
  * animations to stop when the value is close enough to the target.
  */
-val DpOffset.Companion.VisibilityThreshold: DpOffset
+public val DpOffset.Companion.VisibilityThreshold: DpOffset
     get() = DpOffset(Dp.VisibilityThreshold, Dp.VisibilityThreshold)
 
 /**
@@ -76,7 +76,7 @@
  * no longer visible. The animation system uses this to signal to some default [spring] animations
  * to stop when the value is close enough to the target.
  */
-val Size.Companion.VisibilityThreshold: Size
+public val Size.Companion.VisibilityThreshold: Size
     get() = Size(PxVisibilityThreshold, PxVisibilityThreshold)
 
 /**
@@ -84,7 +84,7 @@
  * be no longer visible. The animation system uses this to signal to some default [spring]
  * animations to stop when the value is close enough to the target.
  */
-val IntSize.Companion.VisibilityThreshold: IntSize
+public val IntSize.Companion.VisibilityThreshold: IntSize
     get() = IntSize(1, 1)
 
 /**
@@ -92,7 +92,7 @@
  * no longer visible. The animation system uses this to signal to some default [spring] animations
  * to stop when the value is close enough to the target.
  */
-val Rect.Companion.VisibilityThreshold: Rect
+public val Rect.Companion.VisibilityThreshold: Rect
     get() = rectVisibilityThreshold
 
 // TODO: Add Dp.DefaultAnimation = spring<Dp>(visibilityThreshold = Dp.VisibilityThreshold)
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/internal/JvmDefaultWithCompatibility.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/internal/JvmDefaultWithCompatibility.kt
new file mode 100644
index 0000000..0aebdc3
--- /dev/null
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/internal/JvmDefaultWithCompatibility.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core.internal
+
+internal expect annotation class JvmDefaultWithCompatibility()
diff --git a/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/ArcSpline.commonStubs.kt b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/ArcSpline.commonStubs.kt
new file mode 100644
index 0000000..d8d41dd
--- /dev/null
+++ b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/ArcSpline.commonStubs.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun toRadians(value: Double): Double = implementedInJetBrainsFork()
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun binarySearch(array: FloatArray, position: Float): Int =
+    implementedInJetBrainsFork()
diff --git a/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/Expect.commonStubs.kt b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/Expect.commonStubs.kt
new file mode 100644
index 0000000..6a96f9c
--- /dev/null
+++ b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/Expect.commonStubs.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+internal actual class AtomicReference<V> actual constructor(value: V) {
+    actual fun get(): V = implementedInJetBrainsFork()
+
+    actual fun set(value: V) {
+        implementedInJetBrainsFork()
+    }
+
+    actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+    actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/InternalMutatorMutex.commonStubs.kt b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/InternalMutatorMutex.commonStubs.kt
new file mode 100644
index 0000000..ab8b58f
--- /dev/null
+++ b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/InternalMutatorMutex.commonStubs.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class MutationInterruptedException : CancellationException("") {
+    init {
+        implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/NotImplemented.commonStubs.kt b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/NotImplemented.commonStubs.kt
new file mode 100644
index 0000000..30b49a5
--- /dev/null
+++ b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/NotImplemented.commonStubs.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun implementedInJetBrainsFork(): Nothing =
+    throw NotImplementedError(
+        """
+        Implemented only in JetBrains fork.
+        Please use `org.jetbrains.compose.animation:animation-core` package instead.
+        """
+            .trimIndent()
+    )
diff --git a/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..423a8a9
--- /dev/null
+++ b/compose/animation/animation-core/src/commonStubsMain/kotlin/androidx/compose/animation/core/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core.internal
+
+internal actual annotation class JvmDefaultWithCompatibility()
diff --git a/compose/animation/animation-core/src/jvmMain/kotlin/androidx/compose/animation/core/ArcSpline.jvm.kt b/compose/animation/animation-core/src/jvmMain/kotlin/androidx/compose/animation/core/ArcSpline.jvm.kt
new file mode 100644
index 0000000..5ade8f9
--- /dev/null
+++ b/compose/animation/animation-core/src/jvmMain/kotlin/androidx/compose/animation/core/ArcSpline.jvm.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun toRadians(value: Double): Double {
+    return Math.toRadians(value)
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun binarySearch(array: FloatArray, position: Float): Int {
+    return array.binarySearch(position)
+}
diff --git a/compose/animation/animation-core/src/jvmMain/kotlin/androidx/compose/animation/core/InternalMotatorMutex.jvm.kt b/compose/animation/animation-core/src/jvmMain/kotlin/androidx/compose/animation/core/InternalMotatorMutex.jvm.kt
new file mode 100644
index 0000000..5e2d56c
--- /dev/null
+++ b/compose/animation/animation-core/src/jvmMain/kotlin/androidx/compose/animation/core/InternalMotatorMutex.jvm.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class MutationInterruptedException : CancellationException("Mutation interrupted") {
+    override fun fillInStackTrace(): Throwable {
+        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
+        stackTrace = emptyArray()
+        return this
+    }
+}
diff --git a/compose/animation/animation-graphics/build.gradle b/compose/animation/animation-graphics/build.gradle
index c33dea6..16f1350 100644
--- a/compose/animation/animation-graphics/build.gradle
+++ b/compose/animation/animation-graphics/build.gradle
@@ -33,6 +33,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -42,17 +43,17 @@
                 implementation(libs.kotlinStdlib)
 
                 api(project(":compose:animation:animation"))
-                api("androidx.compose.foundation:foundation-layout:1.6.0")
+                api(project(":compose:foundation:foundation-layout"))
                 api(project(":compose:runtime:runtime"))
-                api("androidx.compose.ui:ui:1.6.0")
-                api("androidx.compose.ui:ui-geometry:1.6.0")
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-geometry"))
 
                 implementation(project(":compose:ui:ui-util"))
-                implementation("androidx.collection:collection:1.4.0")
+                implementation(project(":collection:collection"))
             }
         }
         androidMain.dependencies {
-            api("androidx.annotation:annotation:1.1.0")
+            api("androidx.annotation:annotation:1.8.1")
             api("androidx.annotation:annotation-experimental:1.4.1")
             implementation("androidx.core:core-ktx:1.5.0")
         }
@@ -74,10 +75,16 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -109,10 +116,11 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2021"
     description = "Compose Animation Graphics Library for using animated-vector resources in Compose"
-    legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:animation:animation-graphics:animation-graphics-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.animation.graphics"
 }
diff --git a/compose/animation/animation-graphics/lint-baseline.xml b/compose/animation/animation-graphics/lint-baseline.xml
index bdc4bc5..14141af 100644
--- a/compose/animation/animation-graphics/lint-baseline.xml
+++ b/compose/animation/animation-graphics/lint-baseline.xml
@@ -1,10 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
+
+    <issue
+        id="RestrictedApiAndroidX"
+        message="PathParser.createPathFromPathData can only be called from within the same library (androidx.core:core)"
+        errorLine1="                    PathInterpolator(PathParser.createPathFromPathData(pathData)).toEasing()"
+        errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/animation/graphics/vector/compat/XmlAnimatorParser.android.kt"/>
+    </issue>
 
     <issue
         id="PrimitiveInCollection"
         message="field builtinInterpolators with type HashMap&lt;Integer, Easing>: replace with IntObjectMap"
-        errorLine1="private val builtinInterpolators = hashMapOf("
+        errorLine1="private val builtinInterpolators ="
         errorLine2="^">
         <location
             file="src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatorResources.android.kt"/>
diff --git a/compose/animation/animation-graphics/samples/build.gradle b/compose/animation/animation-graphics/samples/build.gradle
index c794e59..dd05f1e 100644
--- a/compose/animation/animation-graphics/samples/build.gradle
+++ b/compose/animation/animation-graphics/samples/build.gradle
@@ -52,5 +52,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.animation.graphics.samples"
 }
diff --git a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
index d598db5..10476cd 100644
--- a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
+++ b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
@@ -40,7 +40,7 @@
  */
 @ExperimentalAnimationGraphicsApi
 @Composable
-fun rememberAnimatedVectorPainter(
+public fun rememberAnimatedVectorPainter(
     animatedImageVector: AnimatedImageVector,
     atEnd: Boolean
 ): Painter {
diff --git a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt
index e928d13..6d3dd01 100644
--- a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt
+++ b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt
@@ -37,7 +37,7 @@
  */
 @ExperimentalAnimationGraphicsApi
 @Composable
-fun AnimatedImageVector.Companion.animatedVectorResource(
+public fun AnimatedImageVector.Companion.animatedVectorResource(
     @DrawableRes id: Int
 ): AnimatedImageVector {
     val context = LocalContext.current
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/ExperimentalAnimationGraphicsApi.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/ExperimentalAnimationGraphicsApi.kt
index 79068b1..19111a9 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/ExperimentalAnimationGraphicsApi.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/ExperimentalAnimationGraphicsApi.kt
@@ -19,4 +19,4 @@
 @RequiresOptIn(message = "This is an experimental animation graphics API.")
 @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
 @Retention(AnnotationRetention.BINARY)
-annotation class ExperimentalAnimationGraphicsApi
+public annotation class ExperimentalAnimationGraphicsApi
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt
index f16f4b2..b8d9d02 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt
@@ -31,9 +31,9 @@
  */
 @ExperimentalAnimationGraphicsApi
 @Immutable
-class AnimatedImageVector
+public class AnimatedImageVector
 internal constructor(
-    val imageVector: ImageVector,
+    public val imageVector: ImageVector,
     // The list of [AnimatedVectorTarget]s that specify animations for each of the elements in the
     // drawable. This is represented with `<target>` elements in `<animated-vector>`. This list is
     // expected to be *immutable*.
@@ -43,11 +43,11 @@
     /**
      * The total duration of all the animations in this image, including start delays and repeats.
      */
-    val totalDuration =
+    public val totalDuration: Int =
         targets.fastMaxBy { it.animator.totalDuration }?.animator?.totalDuration ?: 0
 
     /** Provide an empty companion object to hang platform-specific companion extensions onto. */
-    companion object {}
+    public companion object {}
 }
 
 /** Definition of animation to one of the elements in a [AnimatedImageVector]. */
diff --git a/compose/animation/animation-tooling-internal/build.gradle b/compose/animation/animation-tooling-internal/build.gradle
index a5f56b2..3fdb835 100644
--- a/compose/animation/animation-tooling-internal/build.gradle
+++ b/compose/animation/animation-tooling-internal/build.gradle
@@ -36,6 +36,5 @@
     name = "Compose Animation Tooling"
     description = "Compose Animation APIs for tooling support. Internal use only."
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
-    metalavaK2UastEnabled = true
     doNotDocumentReason = "Only used externally by Android Studio"
 }
diff --git a/compose/animation/animation/build.gradle b/compose/animation/animation/build.gradle
index 5294d9d..f968628 100644
--- a/compose/animation/animation/build.gradle
+++ b/compose/animation/animation/build.gradle
@@ -34,6 +34,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -43,15 +44,15 @@
                 implementation(libs.kotlinStdlib)
 
                 api(project(":compose:animation:animation-core"))
-                api("androidx.compose.foundation:foundation-layout:1.6.0")
+                api(project(":compose:foundation:foundation-layout"))
                 api(project(":compose:runtime:runtime"))
-                api("androidx.compose.ui:ui-geometry:1.6.0")
+                api(project(":compose:ui:ui-geometry"))
 
-                implementation project(':compose:ui:ui')
+                implementation(project(":compose:ui:ui"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation project(':compose:ui:ui-graphics')
+                implementation(project(":compose:ui:ui-graphics"))
 
-                implementation("androidx.collection:collection:1.4.0")
+                implementation(project(":collection:collection"))
             }
         }
 
@@ -66,19 +67,24 @@
             }
         }
 
-
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -117,8 +123,7 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2019"
     description = "Compose animation library"
-    legacyDisableKotlinStrictApiMode = true
-    legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:animation:animation:animation-samples"))
 }
 
@@ -127,5 +132,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.animation"
 }
diff --git a/compose/animation/animation/integration-tests/animation-demos/build.gradle b/compose/animation/animation/integration-tests/animation-demos/build.gradle
index a826c38..49c87a5 100644
--- a/compose/animation/animation/integration-tests/animation-demos/build.gradle
+++ b/compose/animation/animation/integration-tests/animation-demos/build.gradle
@@ -29,6 +29,7 @@
     implementation(project(":compose:animation:animation-core:animation-core-samples"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":compose:ui:ui-tooling-preview"))
     implementation project(':compose:material3:material3')
     implementation project(":navigation:navigation-compose")
@@ -40,5 +41,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.animation.demos"
 }
diff --git a/compose/animation/animation/integration-tests/animation-demos/lint-baseline.xml b/compose/animation/animation/integration-tests/animation-demos/lint-baseline.xml
index 7ef4d21..2fb88cf 100644
--- a/compose/animation/animation/integration-tests/animation-demos/lint-baseline.xml
+++ b/compose/animation/animation/integration-tests/animation-demos/lint-baseline.xml
@@ -1,14 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
-
-    <issue
-        id="PrimitiveInCollection"
-        message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
-        errorLine2="^">
-        <location
-            file="src/main/java/androidx/compose/animation/demos/layoutanimation/AnimateEnterExitDemo.kt"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
@@ -22,7 +13,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field turquoiseColors with type List&lt;Color>: replace with LongList"
-        errorLine1="internal val turquoiseColors = listOf("
+        errorLine1="internal val turquoiseColors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedVisiblilityLazyColumnDemo.kt"/>
@@ -31,7 +22,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Color> of getTurquoiseColors: replace with LongList"
-        errorLine1="internal val turquoiseColors = listOf("
+        errorLine1="internal val turquoiseColors ="
         errorLine2="             ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/animation/demos/layoutanimation/AnimatedVisiblilityLazyColumnDemo.kt"/>
@@ -40,7 +31,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt"/>
@@ -49,7 +40,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt"/>
@@ -58,7 +49,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithIntrinsicsDemo.kt"/>
@@ -67,7 +58,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field res with type List&lt;Integer>: replace with IntList"
-        errorLine1="val res = listOf("
+        errorLine1="val res ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithLazyColumn.kt"/>
@@ -76,7 +67,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of getRes: replace with IntList"
-        errorLine1="val res = listOf("
+        errorLine1="val res ="
         errorLine2="    ~~~">
         <location
             file="src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithLazyColumn.kt"/>
@@ -85,7 +76,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithMovableContentDemo.kt"/>
@@ -94,7 +85,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithScaffold.kt"/>
@@ -103,7 +94,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithSubcompose.kt"/>
@@ -112,7 +103,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/visualinspection/SeekingDebugging.kt"/>
@@ -121,7 +112,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/gesture/SpringBackScrollingDemo.kt"/>
@@ -130,7 +121,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field vibrantColors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val vibrantColors = listOf("
+        errorLine1="private val vibrantColors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/fancy/SpringChainDemo.kt"/>
@@ -139,7 +130,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field pastelColors with type List&lt;Color>: replace with LongList"
-        errorLine1="internal val pastelColors = listOf("
+        errorLine1="internal val pastelColors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt"/>
@@ -148,7 +139,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Color> of getPastelColors: replace with LongList"
-        errorLine1="internal val pastelColors = listOf("
+        errorLine1="internal val pastelColors ="
         errorLine2="             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt"/>
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/OffsetKeyframeSplinePlaygroundDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/OffsetKeyframeSplinePlaygroundDemo.kt
index 7534afc..228b929 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/OffsetKeyframeSplinePlaygroundDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/suspendfun/OffsetKeyframeSplinePlaygroundDemo.kt
@@ -77,6 +77,7 @@
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.times
 import androidx.compose.ui.window.Popup
+import kotlin.collections.removeLast as removeLastKt
 import kotlin.math.atan2
 import kotlin.math.cos
 import kotlin.math.log
@@ -309,7 +310,7 @@
     fun removeAnchor() {
         if (anchors.size > 1) {
             scope.launch { animatedOffset.snapTo(Offset.Zero) }
-            anchors.removeLast()
+            anchors.removeLastKt()
             modificationIndicator++
         }
     }
diff --git a/compose/animation/animation/samples/build.gradle b/compose/animation/animation/samples/build.gradle
index d7aecde..a3be858 100644
--- a/compose/animation/animation/samples/build.gradle
+++ b/compose/animation/animation/samples/build.gradle
@@ -38,6 +38,7 @@
     implementation(project(":compose:animation:animation"))
     implementation("androidx.compose.foundation:foundation:1.2.1")
     implementation("androidx.compose.material:material:1.2.1")
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation("androidx.compose.runtime:runtime:1.2.1")
     implementation("androidx.compose.ui:ui-text:1.2.1")
 }
@@ -50,5 +51,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.animation.samples"
 }
diff --git a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt
index b76bd88..9604026 100644
--- a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt
+++ b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/SharedTransitionTest.kt
@@ -25,9 +25,12 @@
 import androidx.compose.animation.core.SeekableTransitionState
 import androidx.compose.animation.core.Transition
 import androidx.compose.animation.core.rememberTransition
+import androidx.compose.animation.core.snap
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
@@ -52,6 +55,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertContainsColor
 import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Alignment.Companion.BottomCenter
@@ -81,6 +85,7 @@
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
@@ -2713,6 +2718,106 @@
                 ScaleToBounds(alignment = customAlignment)
         )
     }
+
+    // Regression test for b/347520198, SharedTransitionLayout onDraw would not get invalidated
+    // in some cases.
+    @SdkSuppress(minSdkVersion = 26)
+    @Test
+    fun testSharedTransitionScopeIsInvalidated() {
+        var state by mutableIntStateOf(0)
+
+        val animDurationMillis = 500
+
+        val parentTag = "STL"
+        val clickTarget = "click-target"
+
+        rule.setContent {
+            SharedTransitionLayout(Modifier.size(100.dp).testTag(parentTag)) {
+                // This outer AnimatedContent doesn't do anything, and the issue only triggers
+                // when it's present
+                AnimatedContent(targetState = true) {
+                    @Suppress("UNUSED_EXPRESSION")
+                    it // Need to reference the unused outer AnimatedContent's target state
+
+                    AnimatedContent(
+                        targetState = state,
+                        transitionSpec = {
+                            // Add a delay to the animation just so that it takes a known time to
+                            // complete
+                            fadeIn(snap()).togetherWith(fadeOut(snap(animDurationMillis)))
+                        }
+                    ) { currentState ->
+                        val innerAnimatedContentScope = this
+                        Box(
+                            // This will cycle from Green -> Blue -> Red -> Blue -> Red...
+                            Modifier.testTag(clickTarget)
+                                .clickable(
+                                    // Don't let the clickable paint anything, it may interfere with
+                                    // the test.
+                                    interactionSource = remember { MutableInteractionSource() },
+                                    indication = null
+                                ) {
+                                    state =
+                                        when (currentState) {
+                                            0 -> 2
+                                            1 -> 2
+                                            else -> 1
+                                        }
+                                }
+                                .fillMaxSize()
+                        ) {
+                            val color =
+                                when (currentState) {
+                                    0 -> Color.Green
+                                    1 -> Color.Red
+                                    else -> Color.Blue
+                                }
+                            Box(
+                                Modifier
+                                    // Using shared bounds so that we control when the item enters
+                                    // and leaves in every case. Particularly, we want the target to
+                                    // show immediately
+                                    .sharedBounds(
+                                        rememberSharedContentState(
+                                            key =
+                                                if (currentState == 0) "no match"
+                                                else "matching key"
+                                        ),
+                                        animatedVisibilityScope = innerAnimatedContentScope,
+                                        enter = fadeIn(snap()),
+                                        exit = fadeOut(snap())
+                                    )
+                                    .background(color)
+                                    .fillMaxSize()
+                            )
+                        }
+                    }
+                }
+            }
+        }
+        rule.waitForIdle()
+        // Start off with a Green box
+        rule.onNodeWithTag(parentTag).captureToImage().assertContainsColor(Color.Green)
+
+        fun clickAndAssertColorDuringTransition(color: Color) {
+            rule.mainClock.autoAdvance = false
+            rule.onNodeWithTag(clickTarget).performClick()
+
+            rule.mainClock.advanceTimeByFrame()
+            rule.mainClock.advanceTimeBy(animDurationMillis / 2L)
+
+            rule.onNodeWithTag(parentTag).captureToImage().assertContainsColor(color)
+
+            rule.mainClock.autoAdvance = true
+            rule.waitForIdle()
+        }
+
+        // Transition into a Blue box
+        clickAndAssertColorDuringTransition(Color.Blue)
+
+        // Transition into a Red box
+        clickAndAssertColorDuringTransition(Color.Red)
+    }
 }
 
 private fun assertEquals(a: IntSize, b: IntSize, delta: IntSize) {
diff --git a/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/AndroidActualDefaultDecayAnimationSpec.android.kt b/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/AndroidActualDefaultDecayAnimationSpec.android.kt
index f1fe969..39b4c4d 100644
--- a/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/AndroidActualDefaultDecayAnimationSpec.android.kt
+++ b/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/AndroidActualDefaultDecayAnimationSpec.android.kt
@@ -21,6 +21,6 @@
 
 @Composable
 @Deprecated("Replace with rememberSplineBasedDecay<Float>")
-actual fun defaultDecayAnimationSpec(): DecayAnimationSpec<Float> {
+public actual fun defaultDecayAnimationSpec(): DecayAnimationSpec<Float> {
     return rememberSplineBasedDecay()
 }
diff --git a/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.android.kt b/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.android.kt
index 1e6ace7..4966e6a 100644
--- a/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.android.kt
+++ b/compose/animation/animation/src/androidMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.android.kt
@@ -32,13 +32,13 @@
  */
 @Deprecated("Moved to common code", level = DeprecationLevel.HIDDEN)
 @JvmName("splineBasedDecay")
-fun <T> splineBasedDecayDeprecated(density: Density): DecayAnimationSpec<T> =
+public fun <T> splineBasedDecayDeprecated(density: Density): DecayAnimationSpec<T> =
     splineBasedDecay(density)
 
 internal actual val platformFlingScrollFriction = ViewConfiguration.getScrollFriction()
 
 @Composable
-actual fun <T> rememberSplineBasedDecay(): DecayAnimationSpec<T> {
+public actual fun <T> rememberSplineBasedDecay(): DecayAnimationSpec<T> {
     // This function will internally update the calculation of fling decay when the density changes,
     // but the reference to the returned spec will not change across calls.
     val density = LocalDensity.current
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
index c5732f6..f095141 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
@@ -124,7 +124,7 @@
  * @see AnimatedContentScope
  */
 @Composable
-fun <S> AnimatedContent(
+public fun <S> AnimatedContent(
     targetState: S,
     modifier: Modifier = Modifier,
     transitionSpec: AnimatedContentTransitionScope<S>.() -> ContentTransform = {
@@ -181,9 +181,9 @@
  * @see ExitTransition
  * @see AnimatedContent
  */
-class ContentTransform(
-    val targetContentEnter: EnterTransition,
-    val initialContentExit: ExitTransition,
+public class ContentTransform(
+    public val targetContentEnter: EnterTransition,
+    public val initialContentExit: ExitTransition,
     targetContentZIndex: Float = 0f,
     sizeTransform: SizeTransform? = SizeTransform()
 ) {
@@ -192,7 +192,7 @@
      * to 0f. Content with higher zIndex will be drawn over lower `zIndex`ed content. Among content
      * with the same index, the target content will be placed on top.
      */
-    var targetContentZIndex by mutableFloatStateOf(targetContentZIndex)
+    public var targetContentZIndex: Float by mutableFloatStateOf(targetContentZIndex)
 
     /**
      * [sizeTransform] manages the expanding and shrinking of the container if there is any size
@@ -201,7 +201,7 @@
      * the animated size. Both can be customized by supplying a different [SizeTransform]. If no
      * size animation is desired, [sizeTransform] can be set to `null`.
      */
-    var sizeTransform: SizeTransform? = sizeTransform
+    public var sizeTransform: SizeTransform? = sizeTransform
         internal set
 }
 
@@ -212,7 +212,7 @@
  *
  * @sample androidx.compose.animation.samples.AnimatedContentTransitionSpecSample
  */
-fun SizeTransform(
+public fun SizeTransform(
     clip: Boolean = true,
     sizeAnimationSpec: (initialSize: IntSize, targetSize: IntSize) -> FiniteAnimationSpec<IntSize> =
         { _, _ ->
@@ -231,15 +231,18 @@
  *
  * @sample androidx.compose.animation.samples.AnimatedContentTransitionSpecSample
  */
-interface SizeTransform {
+public interface SizeTransform {
     /** Whether the content should be clipped using the animated size. */
-    val clip: Boolean
+    public val clip: Boolean
 
     /**
      * This allows [FiniteAnimationSpec] to be defined based on the [initialSize] before the size
      * animation and the [targetSize] of the animation.
      */
-    fun createAnimationSpec(initialSize: IntSize, targetSize: IntSize): FiniteAnimationSpec<IntSize>
+    public fun createAnimationSpec(
+        initialSize: IntSize,
+        targetSize: IntSize
+    ): FiniteAnimationSpec<IntSize>
 }
 
 /** Private implementation of SizeTransform interface. */
@@ -260,26 +263,28 @@
  *
  * @sample androidx.compose.animation.samples.AnimatedContentTransitionSpecSample
  */
-infix fun EnterTransition.togetherWith(exit: ExitTransition) = ContentTransform(this, exit)
+public infix fun EnterTransition.togetherWith(exit: ExitTransition): ContentTransform =
+    ContentTransform(this, exit)
 
 @ExperimentalAnimationApi
 @Deprecated(
     "Infix fun EnterTransition.with(ExitTransition) has been renamed to" + " togetherWith",
     ReplaceWith("togetherWith(exit)")
 )
-infix fun EnterTransition.with(exit: ExitTransition) = ContentTransform(this, exit)
+public infix fun EnterTransition.with(exit: ExitTransition): ContentTransform =
+    ContentTransform(this, exit)
 
 /**
  * [AnimatedContentTransitionScope] provides functions that are convenient and only applicable in
  * the context of [AnimatedContent], such as [slideIntoContainer] and [slideOutOfContainer].
  */
-sealed interface AnimatedContentTransitionScope<S> : Transition.Segment<S> {
+public sealed interface AnimatedContentTransitionScope<S> : Transition.Segment<S> {
     /**
      * Customizes the [SizeTransform] of a given [ContentTransform]. For example:
      *
      * @sample androidx.compose.animation.samples.AnimatedContentTransitionSpecSample
      */
-    infix fun ContentTransform.using(sizeTransform: SizeTransform?): ContentTransform
+    public infix fun ContentTransform.using(sizeTransform: SizeTransform?): ContentTransform
 
     /**
      * [SlideDirection] defines the direction of the slide in/out for [slideIntoContainer] and
@@ -287,14 +292,14 @@
      */
     @Immutable
     @kotlin.jvm.JvmInline
-    value class SlideDirection internal constructor(private val value: Int) {
-        companion object {
-            val Left = SlideDirection(0)
-            val Right = SlideDirection(1)
-            val Up = SlideDirection(2)
-            val Down = SlideDirection(3)
-            val Start = SlideDirection(4)
-            val End = SlideDirection(5)
+    public value class SlideDirection internal constructor(private val value: Int) {
+        public companion object {
+            public val Left: SlideDirection = SlideDirection(0)
+            public val Right: SlideDirection = SlideDirection(1)
+            public val Up: SlideDirection = SlideDirection(2)
+            public val Down: SlideDirection = SlideDirection(3)
+            public val Start: SlideDirection = SlideDirection(4)
+            public val End: SlideDirection = SlideDirection(5)
         }
 
         override fun toString(): String {
@@ -331,7 +336,7 @@
      * @see slideInHorizontally
      * @see slideInVertically
      */
-    fun slideIntoContainer(
+    public fun slideIntoContainer(
         towards: SlideDirection,
         animationSpec: FiniteAnimationSpec<IntOffset> =
             spring(visibilityThreshold = IntOffset.VisibilityThreshold),
@@ -358,7 +363,7 @@
      * @see slideOutHorizontally
      * @see slideOutVertically
      */
-    fun slideOutOfContainer(
+    public fun slideOutOfContainer(
         towards: SlideDirection,
         animationSpec: FiniteAnimationSpec<IntOffset> =
             spring(visibilityThreshold = IntOffset.VisibilityThreshold),
@@ -378,11 +383,11 @@
      *
      * @sample androidx.compose.animation.samples.SlideIntoContainerSample
      */
-    val ExitTransition.Companion.KeepUntilTransitionsFinished: ExitTransition
+    public val ExitTransition.Companion.KeepUntilTransitionsFinished: ExitTransition
         get() = KeepUntilTransitionsFinished
 
     /** This returns the [Alignment] specified on [AnimatedContent]. */
-    val contentAlignment: Alignment
+    public val contentAlignment: Alignment
 }
 
 internal class AnimatedContentTransitionScopeImpl<S>
@@ -626,7 +631,7 @@
  * [transition][AnimatedVisibilityScope.transition] can be used to observe the state of the
  * transition, or to add more enter/exit transition for the content.
  */
-sealed interface AnimatedContentScope : AnimatedVisibilityScope
+public sealed interface AnimatedContentScope : AnimatedVisibilityScope
 
 private class AnimatedContentScopeImpl
 internal constructor(animatedVisibilityScope: AnimatedVisibilityScope) :
@@ -680,7 +685,7 @@
  */
 @OptIn(ExperimentalAnimationApi::class)
 @Composable
-fun <S> Transition<S>.AnimatedContent(
+public fun <S> Transition<S>.AnimatedContent(
     modifier: Modifier = Modifier,
     transitionSpec: AnimatedContentTransitionScope<S>.() -> ContentTransform = {
         (fadeIn(animationSpec = tween(220, delayMillis = 90)) +
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
index 4938063..4c79c36 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
@@ -120,7 +120,7 @@
  * @see AnimatedVisibilityScope
  */
 @Composable
-fun AnimatedVisibility(
+public fun AnimatedVisibility(
     visible: Boolean,
     modifier: Modifier = Modifier,
     enter: EnterTransition = fadeIn() + expandIn(),
@@ -193,7 +193,7 @@
  * @see AnimatedVisibilityScope
  */
 @Composable
-fun RowScope.AnimatedVisibility(
+public fun RowScope.AnimatedVisibility(
     visible: Boolean,
     modifier: Modifier = Modifier,
     enter: EnterTransition = fadeIn() + expandHorizontally(),
@@ -265,7 +265,7 @@
  * @see AnimatedVisibilityScope
  */
 @Composable
-fun ColumnScope.AnimatedVisibility(
+public fun ColumnScope.AnimatedVisibility(
     visible: Boolean,
     modifier: Modifier = Modifier,
     enter: EnterTransition = fadeIn() + expandVertically(),
@@ -289,7 +289,7 @@
  * @sample androidx.compose.animation.samples.AnimatedVisibilityWithBooleanVisibleParamNoReceiver
  * @see AnimatedVisibility
  */
-enum class EnterExitState {
+public enum class EnterExitState {
     /** The initial state of a custom enter animation in [AnimatedVisibility].. */
     PreEnter,
 
@@ -363,7 +363,7 @@
  * @see AnimatedVisibilityScope
  */
 @Composable
-fun AnimatedVisibility(
+public fun AnimatedVisibility(
     visibleState: MutableTransitionState<Boolean>,
     modifier: Modifier = Modifier,
     enter: EnterTransition = fadeIn() + expandIn(),
@@ -434,7 +434,7 @@
  * @see AnimatedVisibilityScope
  */
 @Composable
-fun RowScope.AnimatedVisibility(
+public fun RowScope.AnimatedVisibility(
     visibleState: MutableTransitionState<Boolean>,
     modifier: Modifier = Modifier,
     enter: EnterTransition = expandHorizontally() + fadeIn(),
@@ -507,7 +507,7 @@
  * @see AnimatedVisibilityScope
  */
 @Composable
-fun ColumnScope.AnimatedVisibility(
+public fun ColumnScope.AnimatedVisibility(
     visibleState: MutableTransitionState<Boolean>,
     modifier: Modifier = Modifier,
     enter: EnterTransition = expandVertically() + fadeIn(),
@@ -579,13 +579,13 @@
  * @see Transition.AnimatedVisibility
  */
 @Composable
-fun <T> Transition<T>.AnimatedVisibility(
+public fun <T> Transition<T>.AnimatedVisibility(
     visible: (T) -> Boolean,
     modifier: Modifier = Modifier,
     enter: EnterTransition = fadeIn() + expandIn(),
     exit: ExitTransition = shrinkOut() + fadeOut(),
     content: @Composable() AnimatedVisibilityScope.() -> Unit
-) = AnimatedVisibilityImpl(this, visible, modifier, enter, exit, content = content)
+): Unit = AnimatedVisibilityImpl(this, visible, modifier, enter, exit, content = content)
 
 /**
  * This is the scope for the content of [AnimatedVisibility]. In this scope, direct and indirect
@@ -602,12 +602,12 @@
  * @sample androidx.compose.animation.samples.AVScopeAnimateEnterExit
  */
 @JvmDefaultWithCompatibility
-interface AnimatedVisibilityScope {
+public interface AnimatedVisibilityScope {
     /**
      * [transition] allows custom enter/exit animations to be specified. It will run simultaneously
      * with the built-in enter/exit transitions specified in [AnimatedVisibility].
      */
-    val transition: Transition<EnterExitState>
+    public val transition: Transition<EnterExitState>
 
     /**
      * [animateEnterExit] modifier can be used for any direct or indirect children of
@@ -632,7 +632,7 @@
      *
      * @sample androidx.compose.animation.samples.AnimateEnterExitPartialContent
      */
-    fun Modifier.animateEnterExit(
+    public fun Modifier.animateEnterExit(
         enter: EnterTransition = fadeIn(),
         exit: ExitTransition = fadeOut(),
         label: String = "animateEnterExit"
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
index bbfd272..57d5833 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
@@ -66,7 +66,7 @@
  * @param finishedListener an optional listener to be called when the content change animation is
  *   completed.
  */
-fun Modifier.animateContentSize(
+public fun Modifier.animateContentSize(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -100,7 +100,7 @@
  * @param finishedListener an optional listener to be called when the content change animation is
  *   completed.
  */
-fun Modifier.animateContentSize(
+public fun Modifier.animateContentSize(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt
index cfe379b..e939c02 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ColorVectorConverter.kt
@@ -53,6 +53,6 @@
  * [AnimationVector4D], and convert a [AnimationVector4D]) back to a [Color] in the given
  * [ColorSpace].
  */
-val Color.Companion.VectorConverter:
+public val Color.Companion.VectorConverter:
     (colorSpace: ColorSpace) -> TwoWayConverter<Color, AnimationVector4D>
     get() = ColorToVector
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
index 9470862..0efbee0 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
@@ -46,7 +46,7 @@
  */
 @OptIn(ExperimentalAnimationApi::class)
 @Composable
-fun <T> Crossfade(
+public fun <T> Crossfade(
     targetState: T,
     modifier: Modifier = Modifier,
     animationSpec: FiniteAnimationSpec<Float> = tween(),
@@ -60,7 +60,7 @@
 @Deprecated("Crossfade API now has a new label parameter added.", level = DeprecationLevel.HIDDEN)
 @OptIn(ExperimentalAnimationApi::class)
 @Composable
-fun <T> Crossfade(
+public fun <T> Crossfade(
     targetState: T,
     modifier: Modifier = Modifier,
     animationSpec: FiniteAnimationSpec<Float> = tween(),
@@ -92,7 +92,7 @@
  */
 @ExperimentalAnimationApi
 @Composable
-fun <T> Transition<T>.Crossfade(
+public fun <T> Transition<T>.Crossfade(
     modifier: Modifier = Modifier,
     animationSpec: FiniteAnimationSpec<Float> = tween(),
     contentKey: (targetState: T) -> Any? = { it },
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt
index 204fabb..e554389 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt
@@ -22,6 +22,6 @@
 /** Create default [DecayAnimationSpec] representing a default fling curve for a platform. */
 @Composable
 @Deprecated("Replace with rememberSplineBasedDecay<Float>")
-expect fun defaultDecayAnimationSpec(): DecayAnimationSpec<Float>
+public expect fun defaultDecayAnimationSpec(): DecayAnimationSpec<Float>
 
-@Composable expect fun <T> rememberSplineBasedDecay(): DecayAnimationSpec<T>
+@Composable public expect fun <T> rememberSplineBasedDecay(): DecayAnimationSpec<T>
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
index dd4d391..26aae1d 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
@@ -60,7 +60,7 @@
     AnnotationTarget.PROPERTY_GETTER,
 )
 @Retention(AnnotationRetention.BINARY)
-annotation class ExperimentalAnimationApi
+public annotation class ExperimentalAnimationApi
 
 /**
  * [EnterTransition] defines how an [AnimatedVisibility] Composable appears on screen as it becomes
@@ -90,7 +90,7 @@
  * @see AnimatedVisibility
  */
 @Immutable
-sealed class EnterTransition {
+public sealed class EnterTransition {
     internal abstract val data: TransitionData
 
     /**
@@ -103,7 +103,7 @@
      * @param enter another [EnterTransition] to be combined
      */
     @Stable
-    operator fun plus(enter: EnterTransition): EnterTransition {
+    public operator fun plus(enter: EnterTransition): EnterTransition {
         return EnterTransitionImpl(
             TransitionData(
                 fade = enter.data.fade ?: data.fade,
@@ -139,7 +139,7 @@
 
     override fun hashCode(): Int = data.hashCode()
 
-    companion object {
+    public companion object {
         /**
          * This can be used when no enter transition is desired. It can be useful in cases where
          * there are other forms of enter animation defined indirectly for an [AnimatedVisibility].
@@ -148,7 +148,7 @@
          *
          * @see [ExitTransition.None]
          */
-        val None: EnterTransition = EnterTransitionImpl(TransitionData())
+        public val None: EnterTransition = EnterTransitionImpl(TransitionData())
     }
 }
 
@@ -181,7 +181,7 @@
  * @see AnimatedVisibility
  */
 @Immutable
-sealed class ExitTransition {
+public sealed class ExitTransition {
     internal abstract val data: TransitionData
 
     /**
@@ -194,7 +194,7 @@
      * @param exit another [ExitTransition] to be combined.
      */
     @Stable
-    operator fun plus(exit: ExitTransition): ExitTransition {
+    public operator fun plus(exit: ExitTransition): ExitTransition {
         return ExitTransitionImpl(
             TransitionData(
                 fade = exit.data.fade ?: data.fade,
@@ -234,7 +234,7 @@
 
     override fun hashCode(): Int = data.hashCode()
 
-    companion object {
+    public companion object {
         /**
          * This can be used when no built-in [ExitTransition] (i.e. fade/slide, etc) is desired for
          * the [AnimatedVisibility], but rather the children are defining their own exit animation
@@ -246,7 +246,7 @@
          *
          * @sample androidx.compose.animation.samples.AVScopeAnimateEnterExit
          */
-        val None: ExitTransition = ExitTransitionImpl(TransitionData())
+        public val None: ExitTransition = ExitTransitionImpl(TransitionData())
 
         /**
          * Keep this type of exit transition internal and only expose it in AnimatedContent, as
@@ -291,7 +291,7 @@
  * @param initialAlpha the starting alpha of the enter transition, 0f by default
  */
 @Stable
-fun fadeIn(
+public fun fadeIn(
     animationSpec: FiniteAnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
     initialAlpha: Float = 0f
 ): EnterTransition {
@@ -309,7 +309,7 @@
  * @param targetAlpha the target alpha of the exit transition, 0f by default
  */
 @Stable
-fun fadeOut(
+public fun fadeOut(
     animationSpec: FiniteAnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
     targetAlpha: Float = 0f,
 ): ExitTransition {
@@ -335,7 +335,7 @@
  *   offset for the slide-in
  */
 @Stable
-fun slideIn(
+public fun slideIn(
     animationSpec: FiniteAnimationSpec<IntOffset> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -365,7 +365,7 @@
  *   offset for the slide-out
  */
 @Stable
-fun slideOut(
+public fun slideOut(
     animationSpec: FiniteAnimationSpec<IntOffset> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -396,7 +396,7 @@
  *   [TransformOrigin.Center].
  */
 @Stable
-fun scaleIn(
+public fun scaleIn(
     animationSpec: FiniteAnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
     initialScale: Float = 0f,
     transformOrigin: TransformOrigin = TransformOrigin.Center,
@@ -426,7 +426,7 @@
  *   [TransformOrigin.Center].
  */
 @Stable
-fun scaleOut(
+public fun scaleOut(
     animationSpec: FiniteAnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
     targetScale: Float = 0f,
     transformOrigin: TransformOrigin = TransformOrigin.Center
@@ -462,7 +462,7 @@
  * @param initialSize the start size of the expanding bounds, returning `IntSize(0, 0)` by default.
  */
 @Stable
-fun expandIn(
+public fun expandIn(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -502,7 +502,7 @@
  * @param targetSize returns the end size of the shrinking bounds, `IntSize(0, 0)` by default.
  */
 @Stable
-fun shrinkOut(
+public fun shrinkOut(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -540,7 +540,7 @@
  * @param initialWidth the start width of the expanding bounds, returning 0 by default.
  */
 @Stable
-fun expandHorizontally(
+public fun expandHorizontally(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -578,7 +578,7 @@
  * @param initialHeight the start height of the expanding bounds, returning 0 by default.
  */
 @Stable
-fun expandVertically(
+public fun expandVertically(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -616,7 +616,7 @@
  * @param targetWidth returns the end width of the shrinking bounds, 0 by default.
  */
 @Stable
-fun shrinkHorizontally(
+public fun shrinkHorizontally(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -655,7 +655,7 @@
  * @param targetHeight returns the end height of the shrinking bounds, 0 by default.
  */
 @Stable
-fun shrinkVertically(
+public fun shrinkVertically(
     animationSpec: FiniteAnimationSpec<IntSize> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -688,7 +688,7 @@
  *   initial offset for the slide-in, by default it returns `-fullWidth/2`
  */
 @Stable
-fun slideInHorizontally(
+public fun slideInHorizontally(
     animationSpec: FiniteAnimationSpec<IntOffset> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -718,7 +718,7 @@
  *   offset for the slide-in, by default it returns `-fullHeight/2`
  */
 @Stable
-fun slideInVertically(
+public fun slideInVertically(
     animationSpec: FiniteAnimationSpec<IntOffset> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -748,7 +748,7 @@
  *   offset for the slide-in, by default it returns `fullWidth/2`
  */
 @Stable
-fun slideOutHorizontally(
+public fun slideOutHorizontally(
     animationSpec: FiniteAnimationSpec<IntOffset> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
@@ -776,7 +776,7 @@
  *   offset for the slide-out, by default it returns `fullHeight/2`
  */
 @Stable
-fun slideOutVertically(
+public fun slideOutVertically(
     animationSpec: FiniteAnimationSpec<IntOffset> =
         spring(
             stiffness = Spring.StiffnessMediumLow,
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ExperimentalSharedTransitionApi.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ExperimentalSharedTransitionApi.kt
index 47e60b6..b233164 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ExperimentalSharedTransitionApi.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/ExperimentalSharedTransitionApi.kt
@@ -25,4 +25,4 @@
     AnnotationTarget.PROPERTY_GETTER,
 )
 @Retention(AnnotationRetention.BINARY)
-annotation class ExperimentalSharedTransitionApi
+public annotation class ExperimentalSharedTransitionApi
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt
index 9b1735d..f2b32d9 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SharedTransitionScope.kt
@@ -43,6 +43,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -105,7 +106,7 @@
  */
 @ExperimentalSharedTransitionApi
 @Composable
-fun SharedTransitionLayout(
+public fun SharedTransitionLayout(
     modifier: Modifier = Modifier,
     content: @Composable SharedTransitionScope.() -> Unit
 ) {
@@ -131,7 +132,7 @@
  */
 @ExperimentalSharedTransitionApi
 @Composable
-fun SharedTransitionScope(content: @Composable SharedTransitionScope.(Modifier) -> Unit) {
+public fun SharedTransitionScope(content: @Composable SharedTransitionScope.(Modifier) -> Unit) {
     LookaheadScope {
         val coroutineScope = rememberCoroutineScope()
         val sharedScope = remember { SharedTransitionScopeImpl(this, coroutineScope) }
@@ -164,12 +165,12 @@
  * bounds.
  */
 @ExperimentalSharedTransitionApi
-fun interface BoundsTransform {
+public fun interface BoundsTransform {
     /**
      * Returns a [FiniteAnimationSpec] for animating the bounds from [initialBounds] to
      * [targetBounds].
      */
-    fun transform(initialBounds: Rect, targetBounds: Rect): FiniteAnimationSpec<Rect>
+    public fun transform(initialBounds: Rect, targetBounds: Rect): FiniteAnimationSpec<Rect>
 }
 
 /**
@@ -193,14 +194,14 @@
  */
 @Stable
 @ExperimentalSharedTransitionApi
-interface SharedTransitionScope : LookaheadScope {
+public interface SharedTransitionScope : LookaheadScope {
 
     /**
      * PlaceHolderSize defines the size of the space that was or will be occupied by the exiting or
      * entering [sharedElement]/[sharedBounds].
      */
-    fun interface PlaceHolderSize {
-        companion object {
+    public fun interface PlaceHolderSize {
+        public companion object {
             /**
              * [animatedSize] is a pre-defined [SharedTransitionScope.PlaceHolderSize] that lets the
              * parent layout of shared elements or shared bounds observe the animated size during an
@@ -210,7 +211,9 @@
              * @see [contentSize]
              * @see [SharedTransitionScope.PlaceHolderSize]
              */
-            val animatedSize = PlaceHolderSize { _, animatedSize -> animatedSize }
+            public val animatedSize: PlaceHolderSize = PlaceHolderSize { _, animatedSize ->
+                animatedSize
+            }
 
             /**
              * [contentSize] is a pre-defined [SharedTransitionScope.PlaceHolderSize] that allows
@@ -227,7 +230,9 @@
              * @see [contentSize]
              * @see [SharedTransitionScope.PlaceHolderSize]
              */
-            val contentSize = PlaceHolderSize { contentSize, _ -> contentSize }
+            public val contentSize: PlaceHolderSize = PlaceHolderSize { contentSize, _ ->
+                contentSize
+            }
         }
 
         /**
@@ -236,7 +241,7 @@
          * content, [contentSize] is the lookahead size of the content (i.e. target size of the
          * shared transition).
          */
-        fun calculateSize(contentSize: IntSize, animatedSize: IntSize): IntSize
+        public fun calculateSize(contentSize: IntSize, animatedSize: IntSize): IntSize
     }
 
     /**
@@ -257,8 +262,8 @@
      * aspect ratios, and other layouts that adjust themselves visually nicely and efficiently to
      * size changes.
      */
-    sealed interface ResizeMode {
-        companion object {
+    public sealed interface ResizeMode {
+        public companion object {
             /**
              * In contrast to [ScaleToBounds], [RemeasureToBounds] is a [ResizeMode] that remeasures
              * and relayouts its child whenever bounds change during the bounds transform. More
@@ -273,7 +278,7 @@
              * result in overlapping children when constrained to too small of a size. In these
              * cases, it's recommended to use [ScaleToBounds] instead.
              */
-            val RemeasureToBounds: ResizeMode = RemeasureImpl
+            public val RemeasureToBounds: ResizeMode = RemeasureImpl
 
             /**
              * [ScaleToBounds] as a type of [ResizeMode] will measure the child layout with
@@ -291,7 +296,7 @@
              * used to calculate the placement of the scaled content. It is [Alignment.Center] by
              * default.
              */
-            fun ScaleToBounds(
+            public fun ScaleToBounds(
                 contentScale: ContentScale = ContentScale.FillWidth,
                 alignment: Alignment = Alignment.Center
             ): ResizeMode = ScaleToBoundsCached(contentScale, alignment)
@@ -302,13 +307,13 @@
      * Indicates whether there is any ongoing transition between matched [sharedElement] or
      * [sharedBounds].
      */
-    val isTransitionActive: Boolean
+    public val isTransitionActive: Boolean
 
     @Deprecated(
         "This EnterTransition has been deprecated. Please replace the usage with " +
             "resizeMode = ScaleToBounds(...) in sharedBounds to achieve the scale-to-bounds effect."
     )
-    fun scaleInSharedContentToBounds(
+    public fun scaleInSharedContentToBounds(
         contentScale: ContentScale = ContentScale.Fit,
         alignment: Alignment = Alignment.Center
     ): EnterTransition =
@@ -318,7 +323,7 @@
         "This ExitTransition has been deprecated.  Please replace the usage with " +
             "resizeMode = ScaleToBounds(...) in sharedBounds to achieve the scale-to-bounds effect."
     )
-    fun scaleOutSharedContentToBounds(
+    public fun scaleOutSharedContentToBounds(
         contentScale: ContentScale = ContentScale.Fit,
         alignment: Alignment = Alignment.Center
     ): ExitTransition =
@@ -335,7 +340,7 @@
      *
      * @sample androidx.compose.animation.samples.NestedSharedBoundsSample
      */
-    fun Modifier.skipToLookaheadSize(): Modifier
+    public fun Modifier.skipToLookaheadSize(): Modifier
 
     /**
      * Renders the content in the [SharedTransitionScope]'s overlay, where shared content (i.e.
@@ -361,7 +366,7 @@
      *
      * @sample androidx.compose.animation.samples.SharedElementWithFABInOverlaySample
      */
-    fun Modifier.renderInSharedTransitionScopeOverlay(
+    public fun Modifier.renderInSharedTransitionScopeOverlay(
         renderInOverlay: () -> Boolean = { isTransitionActive },
         zIndexInOverlay: Float = 0f,
         clipInOverlayDuringTransition: (LayoutDirection, Density) -> Path? =
@@ -372,7 +377,7 @@
      * [OverlayClip] defines a specific clipping that should be applied to a [sharedBounds] or
      * [sharedElement] in the overlay.
      */
-    interface OverlayClip {
+    public interface OverlayClip {
         /**
          * Creates a clip path based using current animated [bounds] of the [sharedBounds] or
          * [sharedElement], their [state] (to query parent state's bounds if needed), and
@@ -386,7 +391,7 @@
          * It is recommended to modify the same [Path] object and return it here, instead of
          * creating new [Path]s.
          */
-        fun getClipPath(
+        public fun getClipPath(
             state: SharedContentState,
             bounds: Rect,
             layoutDirection: LayoutDirection,
@@ -452,7 +457,7 @@
      * @see [sharedBounds]
      */
     @OptIn(ExperimentalAnimationApi::class)
-    fun Modifier.sharedElement(
+    public fun Modifier.sharedElement(
         state: SharedContentState,
         animatedVisibilityScope: AnimatedVisibilityScope,
         boundsTransform: BoundsTransform = DefaultBoundsTransform,
@@ -532,7 +537,7 @@
      * @see [sharedBounds]
      */
     @OptIn(ExperimentalAnimationApi::class)
-    fun Modifier.sharedBounds(
+    public fun Modifier.sharedBounds(
         sharedContentState: SharedContentState,
         animatedVisibilityScope: AnimatedVisibilityScope,
         enter: EnterTransition = fadeIn(),
@@ -609,7 +614,7 @@
      *
      * @sample androidx.compose.animation.samples.SharedElementWithMovableContentSample
      */
-    fun Modifier.sharedElementWithCallerManagedVisibility(
+    public fun Modifier.sharedElementWithCallerManagedVisibility(
         sharedContentState: SharedContentState,
         visible: Boolean,
         boundsTransform: BoundsTransform = DefaultBoundsTransform,
@@ -620,10 +625,10 @@
     ): Modifier
 
     /** Creates an [OverlayClip] based on a specific [clipShape]. */
-    fun OverlayClip(clipShape: Shape): OverlayClip
+    public fun OverlayClip(clipShape: Shape): OverlayClip
 
     /** Creates and remembers a [SharedContentState] with a given [key]. */
-    @Composable fun rememberSharedContentState(key: Any): SharedContentState
+    @Composable public fun rememberSharedContentState(key: Any): SharedContentState
 
     /**
      * [SharedContentState] is designed to allow access of the properties of
@@ -631,7 +636,7 @@
      * the [SharedTransitionScope], its [clipPathInOverlay] and [parentSharedContentState] if there
      * is a parent [sharedBounds] in the layout tree.
      */
-    class SharedContentState internal constructor(val key: Any) {
+    public class SharedContentState internal constructor(public val key: Any) {
         /**
          * Indicates whether a match of the same [key] has been found. [sharedElement] or
          * [sharedBounds] will not have any animation unless a match has been found.
@@ -641,7 +646,7 @@
          * declared in subcomposition (e.g. a LazyList) where the composition happens as a part of
          * the measure/layout pass, that's when [isMatchFound] will become true.
          */
-        val isMatchFound: Boolean
+        public val isMatchFound: Boolean
             get() = internalState?.sharedElement?.foundMatch ?: false
 
         /**
@@ -650,11 +655,11 @@
          * means it is safe to query [parentSharedContentState]'s [clipPathInOverlay] when the
          * shared content is drawn.
          */
-        val clipPathInOverlay: Path?
+        public val clipPathInOverlay: Path?
             get() = nonNullInternalState.clipPathInOverlay
 
         /** Returns the [SharedContentState] of a parent [sharedBounds], if any. */
-        val parentSharedContentState: SharedContentState?
+        public val parentSharedContentState: SharedContentState?
             get() = nonNullInternalState.parentState?.userState
 
         internal var internalState: SharedElementInternalState? by mutableStateOf(null)
@@ -1038,7 +1043,7 @@
     internal var nullableLookaheadRoot: LayoutCoordinates? = null
 
     // TODO: Use MutableObjectList and impl sort
-    private val renderers = mutableListOf<LayerRenderer>()
+    private val renderers = mutableStateListOf<LayerRenderer>()
 
     private val sharedElements = MutableScatterMap<Any, SharedElement>()
 
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
index 3f8b204..66758b7 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
@@ -53,7 +53,7 @@
  * @param finishedListener An optional listener to get notified when the animation is finished.
  */
 @Composable
-fun animateColorAsState(
+public fun animateColorAsState(
     targetValue: Color,
     animationSpec: AnimationSpec<Color> = colorDefaultSpring,
     label: String = "ColorAnimation",
@@ -75,7 +75,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun animateColorAsState(
+public fun animateColorAsState(
     targetValue: Color,
     animationSpec: AnimationSpec<Color> = colorDefaultSpring,
     finishedListener: ((Color) -> Unit)? = null
@@ -104,5 +104,5 @@
  * @sample androidx.compose.animation.samples.AnimatableColor
  * @param initialValue initial value of the [Animatable]
  */
-fun Animatable(initialValue: Color): Animatable<Color, AnimationVector4D> =
+public fun Animatable(initialValue: Color): Animatable<Color, AnimationVector4D> =
     Animatable(initialValue, (Color.VectorConverter)(initialValue.colorSpace))
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedDecay.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedDecay.kt
index 8461157..04e8097 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedDecay.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedDecay.kt
@@ -118,5 +118,5 @@
     )
 }
 
-fun <T> splineBasedDecay(density: Density): DecayAnimationSpec<T> =
+public fun <T> splineBasedDecay(density: Density): DecayAnimationSpec<T> =
     SplineBasedFloatDecayAnimationSpec(density).generateDecayAnimationSpec()
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.kt
index 686d2fc..acc8bb9 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SplineBasedFloatDecayAnimationSpec.kt
@@ -25,7 +25,7 @@
  *
  * @param density density of the display
  */
-class SplineBasedFloatDecayAnimationSpec(density: Density) : FloatDecayAnimationSpec {
+public class SplineBasedFloatDecayAnimationSpec(density: Density) : FloatDecayAnimationSpec {
 
     private val flingCalculator =
         FlingCalculator(friction = platformFlingScrollFriction, density = density)
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
index e76e066..470fdf6 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
@@ -58,7 +58,7 @@
  * @see androidx.compose.animation.core.updateTransition
  */
 @Composable
-inline fun <S> Transition<S>.animateColor(
+public inline fun <S> Transition<S>.animateColor(
     noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Color> = {
         spring()
     },
@@ -90,7 +90,7 @@
  * @see InfiniteRepeatableSpec
  */
 @Composable
-fun InfiniteTransition.animateColor(
+public fun InfiniteTransition.animateColor(
     initialValue: Color,
     targetValue: Color,
     animationSpec: InfiniteRepeatableSpec<Color>,
@@ -105,7 +105,7 @@
     level = DeprecationLevel.HIDDEN
 )
 @Composable
-fun InfiniteTransition.animateColor(
+public fun InfiniteTransition.animateColor(
     initialValue: Color,
     targetValue: Color,
     animationSpec: InfiniteRepeatableSpec<Color>
diff --git a/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.commonStubs.kt b/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.commonStubs.kt
new file mode 100644
index 0000000..c087eca
--- /dev/null
+++ b/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.commonStubs.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation
+
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.runtime.Composable
+
+@Composable
+public actual fun defaultDecayAnimationSpec(): DecayAnimationSpec<Float> =
+    implementedInJetBrainsFork()
diff --git a/compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/NotImplemented.jvmStubs.kt b/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/NotImplemented.jvmStubs.kt
rename to compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/NotImplemented.commonStubs.kt
diff --git a/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/SplineBasedDecayAnimationSpec.commonStubs.kt b/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/SplineBasedDecayAnimationSpec.commonStubs.kt
new file mode 100644
index 0000000..f3e9f43
--- /dev/null
+++ b/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/SplineBasedDecayAnimationSpec.commonStubs.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation
+
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.runtime.Composable
+
+internal actual val platformFlingScrollFriction: Float = implementedInJetBrainsFork()
+
+@Composable
+public actual fun <T> rememberSplineBasedDecay(): DecayAnimationSpec<T> =
+    implementedInJetBrainsFork()
diff --git a/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..fc8e8e8
--- /dev/null
+++ b/compose/animation/animation/src/commonStubsMain/kotlin/androidx/compose/animation/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.internal
+
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/DesktopActualDefaultDecayAnimationSpec.jvmStubs.kt b/compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/DesktopActualDefaultDecayAnimationSpec.jvmStubs.kt
deleted file mode 100644
index 5d4919c..0000000
--- a/compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/DesktopActualDefaultDecayAnimationSpec.jvmStubs.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.animation
-
-import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.runtime.Composable
-
-@Composable
-actual fun defaultDecayAnimationSpec(): DecayAnimationSpec<Float> = implementedInJetBrainsFork()
diff --git a/compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/SplineBasedDecayAnimationSpec.jvmStubs.kt b/compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/SplineBasedDecayAnimationSpec.jvmStubs.kt
deleted file mode 100644
index 3c8f6b7..0000000
--- a/compose/animation/animation/src/jvmStubsMain/kotlin/androidx/compose/animation/SplineBasedDecayAnimationSpec.jvmStubs.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.animation
-
-import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.runtime.Composable
-
-internal actual val platformFlingScrollFriction: Float = implementedInJetBrainsFork()
-
-@Composable
-actual fun <T> rememberSplineBasedDecay(): DecayAnimationSpec<T> = implementedInJetBrainsFork()
diff --git a/compose/benchmark-utils/benchmark/build.gradle b/compose/benchmark-utils/benchmark/build.gradle
index 1ff5a1a..a4ad944 100644
--- a/compose/benchmark-utils/benchmark/build.gradle
+++ b/compose/benchmark-utils/benchmark/build.gradle
@@ -34,5 +34,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.benchmarkutils.benchmark"
 }
diff --git a/compose/benchmark-utils/build.gradle b/compose/benchmark-utils/build.gradle
index 7e3d1ab..b994260 100644
--- a/compose/benchmark-utils/build.gradle
+++ b/compose/benchmark-utils/build.gradle
@@ -56,10 +56,11 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.benchmarkutils"
 
     // workarounds for b/328649293
     buildTypes.configureEach {
         consumerProguardFiles "proguard-rules.pro"
     }
-}
\ No newline at end of file
+}
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index dea6c5f..a815014 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -124,10 +124,11 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.FlowColumnScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
     method public int getIndexInLine();
     method public int getLineIndex();
     method public float getMaxHeightInLine();
@@ -139,8 +140,8 @@
   }
 
   public final class ContextualFlowLayoutKt {
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
   }
 
   @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
@@ -156,10 +157,11 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.FlowRowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.RowScope {
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
     method public int getIndexInLine();
     method public int getLineIndex();
     method public float getMaxHeight();
@@ -186,20 +188,20 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnScope {
     method public int getShownItemCount();
     method public int getTotalItemCount();
     property public abstract int shownItemCount;
     property public abstract int totalItemCount;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
   public final class FlowLayoutKt {
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
   }
 
   @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public abstract sealed class FlowLayoutOverflow {
@@ -218,14 +220,14 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowScope {
     method public int getShownItemCount();
     method public int getTotalItemCount();
     property public abstract int shownItemCount;
     property public abstract int totalItemCount;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
@@ -245,6 +247,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class MutableWindowInsets implements androidx.compose.foundation.layout.WindowInsets {
+    ctor public MutableWindowInsets();
     ctor public MutableWindowInsets(optional androidx.compose.foundation.layout.WindowInsets initialInsets);
     method public int getBottom(androidx.compose.ui.unit.Density density);
     method public androidx.compose.foundation.layout.WindowInsets getInsets();
@@ -283,6 +286,7 @@
   }
 
   @androidx.compose.runtime.Immutable public static final class PaddingValues.Absolute implements androidx.compose.foundation.layout.PaddingValues {
+    ctor public PaddingValues.Absolute();
     ctor public PaddingValues.Absolute(optional @androidx.compose.runtime.Stable float left, optional @androidx.compose.runtime.Stable float top, optional @androidx.compose.runtime.Stable float right, optional @androidx.compose.runtime.Stable float bottom);
     method public float calculateBottomPadding();
     method public float calculateLeftPadding(androidx.compose.ui.unit.LayoutDirection layoutDirection);
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index f0f519b..f8e7656 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -128,10 +128,11 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.FlowColumnScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
     method public int getIndexInLine();
     method public int getLineIndex();
     method public float getMaxHeightInLine();
@@ -143,8 +144,8 @@
   }
 
   public final class ContextualFlowLayoutKt {
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
   }
 
   @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
@@ -160,10 +161,11 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.FlowRowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.RowScope {
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
     method public int getIndexInLine();
     method public int getLineIndex();
     method public float getMaxHeight();
@@ -190,20 +192,20 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnScope {
     method public int getShownItemCount();
     method public int getTotalItemCount();
     property public abstract int shownItemCount;
     property public abstract int totalItemCount;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
   public final class FlowLayoutKt {
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy columnMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, int maxItemsInMainAxis);
     method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy rowMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, int maxItemsInMainAxis);
   }
@@ -224,14 +226,14 @@
     property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Visible;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowScope {
     method public int getShownItemCount();
     method public int getTotalItemCount();
     property public abstract int shownItemCount;
     property public abstract int totalItemCount;
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
+  @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowRowScope extends androidx.compose.foundation.layout.RowScope {
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
@@ -251,6 +253,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class MutableWindowInsets implements androidx.compose.foundation.layout.WindowInsets {
+    ctor public MutableWindowInsets();
     ctor public MutableWindowInsets(optional androidx.compose.foundation.layout.WindowInsets initialInsets);
     method public int getBottom(androidx.compose.ui.unit.Density density);
     method public androidx.compose.foundation.layout.WindowInsets getInsets();
@@ -289,6 +292,7 @@
   }
 
   @androidx.compose.runtime.Immutable public static final class PaddingValues.Absolute implements androidx.compose.foundation.layout.PaddingValues {
+    ctor public PaddingValues.Absolute();
     ctor public PaddingValues.Absolute(optional @androidx.compose.runtime.Stable float left, optional @androidx.compose.runtime.Stable float top, optional @androidx.compose.runtime.Stable float right, optional @androidx.compose.runtime.Stable float bottom);
     method public float calculateBottomPadding();
     method public float calculateLeftPadding(androidx.compose.ui.unit.LayoutDirection layoutDirection);
diff --git a/compose/foundation/foundation-layout/benchmark/build.gradle b/compose/foundation/foundation-layout/benchmark/build.gradle
index 62442df..768ee45 100644
--- a/compose/foundation/foundation-layout/benchmark/build.gradle
+++ b/compose/foundation/foundation-layout/benchmark/build.gradle
@@ -27,6 +27,7 @@
     androidTestImplementation project(":benchmark:benchmark-junit4")
     androidTestImplementation project(":compose:foundation:foundation-layout")
     androidTestImplementation project(":compose:material:material")
+    androidTestImplementation("androidx.compose.material:material-icons-core:1.6.7")
     androidTestImplementation project(":compose:runtime:runtime")
     androidTestImplementation project(":compose:benchmark-utils")
     androidTestImplementation(libs.testRules)
@@ -38,5 +39,12 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.layout.benchmark"
+
+    buildTypes {
+        release {
+            minifyEnabled true
+        }
+    }
 }
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index 33c700e..565c485 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -33,6 +33,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -40,10 +41,10 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api("androidx.compose.ui:ui:1.6.0")
+                api(project(":compose:ui:ui"))
                 implementation(project(":compose:runtime:runtime"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation("androidx.collection:collection:1.4.0")
+                implementation(project(":collection:collection"))
                 implementation(project(":compose:ui:ui-unit"))
             }
         }
@@ -62,17 +63,23 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
                 implementation("androidx.core:core:1.7.0")
                 implementation("androidx.compose.animation:animation-core:1.2.1")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -108,9 +115,11 @@
     inceptionYear = "2019"
     description = "Compose layout implementations"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:foundation:foundation-layout:foundation-layout-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.layout"
 }
diff --git a/compose/foundation/foundation-layout/integration-tests/layout-demos/build.gradle b/compose/foundation/foundation-layout/integration-tests/layout-demos/build.gradle
index e9a5acb..d003d3f 100644
--- a/compose/foundation/foundation-layout/integration-tests/layout-demos/build.gradle
+++ b/compose/foundation/foundation-layout/integration-tests/layout-demos/build.gradle
@@ -29,6 +29,7 @@
     implementation(project(":compose:foundation:foundation-layout"))
     implementation(project(":compose:foundation:foundation-layout:foundation-layout-samples"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":compose:integration-tests:demos:common"))
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:ui:ui"))
@@ -36,5 +37,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.layout.demos"
 }
diff --git a/compose/foundation/foundation-layout/samples/build.gradle b/compose/foundation/foundation-layout/samples/build.gradle
index 8718492..7b33988 100644
--- a/compose/foundation/foundation-layout/samples/build.gradle
+++ b/compose/foundation/foundation-layout/samples/build.gradle
@@ -53,5 +53,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.layout.samples"
 }
diff --git a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt
index 690d36f..3aa9880 100644
--- a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt
@@ -919,6 +919,44 @@
     }
 
     @Test
+    fun testContextualFlowRow_alignItemsCenterVertically_UsingTopLevelAPI() {
+        val totalRowHeight = 20
+        val shorterHeight = 10
+        val expectedResult = (totalRowHeight - shorterHeight) / 2
+        var positionInParentY = 0f
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    ContextualFlowRow(
+                        itemCount = 5,
+                        itemVerticalAlignment = Alignment.CenterVertically
+                    ) { index ->
+                        Box(
+                            Modifier.size(
+                                    20.toDp(),
+                                    if (index == 4) {
+                                        shorterHeight.toDp()
+                                    } else {
+                                        totalRowHeight.toDp()
+                                    }
+                                )
+                                .onPlaced {
+                                    if (index == 4) {
+                                        val positionInParent = it.positionInParent()
+                                        positionInParentY = positionInParent.y
+                                    }
+                                }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        Truth.assertThat(positionInParentY).isEqualTo(expectedResult)
+    }
+
+    @Test
     fun testContextualFlowColumn_alignItemsDefaultsToTop() {
         val shorterWidth = 10
         val expectedResult = 0f
@@ -979,6 +1017,44 @@
     }
 
     @Test
+    fun testContextualFlowColumn_alignItemsCenterHorizontally_UsingTopLevelAPI() {
+        val totalColumnWidth = 20
+        val shorterWidth = 10
+        val expectedResult = (totalColumnWidth - shorterWidth) / 2
+        var positionInParentX = 0f
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    ContextualFlowColumn(
+                        itemCount = 5,
+                        itemHorizontalAlignment = Alignment.CenterHorizontally
+                    ) { index ->
+                        Box(
+                            Modifier.size(
+                                    if (index == 4) {
+                                        shorterWidth.toDp()
+                                    } else {
+                                        totalColumnWidth.toDp()
+                                    },
+                                    20.toDp()
+                                )
+                                .onPlaced {
+                                    if (index == 4) {
+                                        val positionInParent = it.positionInParent()
+                                        positionInParentX = positionInParent.x
+                                    }
+                                }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        Truth.assertThat(positionInParentX).isEqualTo(expectedResult)
+    }
+
+    @Test
     fun testContextualFlowRow_horizontalArrangementSpaceAround() {
         val size = 200f
         val noOfItemsPerRow = 5
@@ -1687,7 +1763,6 @@
             Truth.assertThat(itemsShownCount).isEqualTo(5)
             Truth.assertThat(seeMoreShown).isTrue()
             Truth.assertThat(collapseShown).isFalse()
-            Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(collapseOnScope.shownItemCount)
             Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(itemsShownCount)
         }
         rule.onNodeWithTag(seeMoreTag).performTouchInput { click() }
@@ -1699,7 +1774,6 @@
             Truth.assertThat(finalMaxLines).isEqualTo(4)
             Truth.assertThat(seeMoreShown).isTrue()
             Truth.assertThat(collapseShown).isFalse()
-            Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(collapseOnScope.shownItemCount)
             Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(itemsShownCount)
         }
 
@@ -1711,7 +1785,6 @@
             Truth.assertThat(finalMaxLines).isEqualTo(6)
             Truth.assertThat(seeMoreShown).isTrue()
             Truth.assertThat(collapseShown).isFalse()
-            Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(collapseOnScope.shownItemCount)
             Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(itemsShownCount)
         }
 
@@ -1723,8 +1796,7 @@
             Truth.assertThat(finalMaxLines).isEqualTo(8)
             Truth.assertThat(collapseShown).isTrue()
             Truth.assertThat(seeMoreShown).isFalse()
-            Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(collapseOnScope.shownItemCount)
-            Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(itemsShownCount)
+            Truth.assertThat(collapseOnScope.shownItemCount).isEqualTo(itemsShownCount)
         }
         rule.onNodeWithTag(collapseTag).performTouchInput { click() }
 
@@ -1734,7 +1806,6 @@
             Truth.assertThat(finalMaxLines).isEqualTo(2)
             Truth.assertThat(seeMoreShown).isTrue()
             Truth.assertThat(collapseShown).isFalse()
-            Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(collapseOnScope.shownItemCount)
             Truth.assertThat(expandOnScope.shownItemCount).isEqualTo(5)
         }
     }
diff --git a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
index 39f21d5..9c2a063 100644
--- a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
@@ -773,6 +773,44 @@
     }
 
     @Test
+    fun testFlowRow_alignItemsCenterVertically_UsingTopLevelAPI() {
+
+        val totalRowHeight = 20
+        val shorterHeight = 10
+        val expectedResult = (totalRowHeight - shorterHeight) / 2
+        var positionInParentY = 0f
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    FlowRow(itemVerticalAlignment = Alignment.CenterVertically) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier.size(
+                                        20.toDp(),
+                                        if (index == 4) {
+                                            shorterHeight.toDp()
+                                        } else {
+                                            totalRowHeight.toDp()
+                                        }
+                                    )
+                                    .onPlaced {
+                                        if (index == 4) {
+                                            val positionInParent = it.positionInParent()
+                                            positionInParentY = positionInParent.y
+                                        }
+                                    }
+                            )
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        Truth.assertThat(positionInParentY).isEqualTo(expectedResult)
+    }
+
+    @Test
     fun testFlowColumn_alignItemsDefaultsToTop() {
         val totalColumnWidth = 20
         val shorterWidth = 10
@@ -849,6 +887,44 @@
     }
 
     @Test
+    fun testFlowColumn_alignItemsCenterHorizontally_UsingTopLevelAPI() {
+
+        val totalColumnWidth = 20
+        val shorterWidth = 10
+        val expectedResult = (totalColumnWidth - shorterWidth) / 2
+        var positionInParentX = 0f
+        rule.setContent {
+            with(LocalDensity.current) {
+                Box(Modifier.size(200.toDp())) {
+                    FlowColumn(itemHorizontalAlignment = Alignment.CenterHorizontally) {
+                        repeat(5) { index ->
+                            Box(
+                                Modifier.size(
+                                        if (index == 4) {
+                                            shorterWidth.toDp()
+                                        } else {
+                                            totalColumnWidth.toDp()
+                                        },
+                                        20.toDp()
+                                    )
+                                    .onPlaced {
+                                        if (index == 4) {
+                                            val positionInParent = it.positionInParent()
+                                            positionInParentX = positionInParent.x
+                                        }
+                                    }
+                            )
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        Truth.assertThat(positionInParentX).isEqualTo(expectedResult)
+    }
+
+    @Test
     fun testFlowRow_horizontalArrangementSpaceAround() {
         val size = 200f
         val noOfItemsPerRow = 5
@@ -5402,6 +5478,7 @@
                 rowMeasurementMultiContentHelper(
                     verticalArrangement = Arrangement.Top,
                     horizontalArrangement = Arrangement.Start,
+                    itemVerticalAlignment = Alignment.Top,
                     maxItemsInMainAxis = maxItemsInMainAxis,
                     maxLines = maxLines,
                     overflowState = overflowState
@@ -5434,6 +5511,7 @@
                 rowMeasurementMultiContentHelper(
                     verticalArrangement = Arrangement.Top,
                     horizontalArrangement = Arrangement.Start,
+                    itemVerticalAlignment = Alignment.Top,
                     maxItemsInMainAxis = maxItemsInMainAxis,
                     maxLines = maxLines,
                     overflowState = FlowRowOverflow.expandIndicator {}.createOverflowState()
@@ -5443,6 +5521,7 @@
                 rowMeasurementMultiContentHelper(
                     verticalArrangement = Arrangement.Top,
                     horizontalArrangement = Arrangement.Start,
+                    itemVerticalAlignment = Alignment.Top,
                     maxItemsInMainAxis = maxItemsInMainAxis,
                     maxLines = maxLines,
                     overflowState = FlowRowOverflow.expandIndicator {}.createOverflowState()
@@ -5488,6 +5567,7 @@
                 columnMeasurementMultiContentHelper(
                     verticalArrangement = Arrangement.Top,
                     horizontalArrangement = Arrangement.Start,
+                    itemHorizontalAlignment = Alignment.Start,
                     maxItemsInMainAxis = maxItemsInMainAxis,
                     maxLines = maxLines,
                     overflowState = overflowState
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
index 39633da..70e0789 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.layout
 
 import androidx.annotation.FloatRange
+import androidx.compose.foundation.layout.internal.JvmDefaultWithCompatibility
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt
index b8793bd..2504197 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt
@@ -16,9 +16,11 @@
 
 package androidx.compose.foundation.layout
 
+import androidx.annotation.FloatRange
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
@@ -47,13 +49,14 @@
  * Example:
  *
  * @sample androidx.compose.foundation.layout.samples.ContextualFlowRowMaxLineDynamicSeeMore
+ * @param itemCount The total number of item composable
  * @param modifier The modifier to be applied to the Row.
  * @param horizontalArrangement The horizontal arrangement of the layout's children.
  * @param verticalArrangement The vertical arrangement of the layout's virtual rows.
+ * @param itemVerticalAlignment The cross axis/vertical alignment of an item in the column.
  * @param maxItemsInEachRow The maximum number of items per row
  * @param maxLines The maximum number of rows
  * @param overflow The strategy to handle overflowing items
- * @param itemCount The total number of item composable
  * @param content The indexed-based content of [ContextualFlowRowScope]
  * @see FlowRow
  * @see ContextualFlowColumn
@@ -65,6 +68,7 @@
     modifier: Modifier = Modifier,
     horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
     verticalArrangement: Arrangement.Vertical = Arrangement.Top,
+    itemVerticalAlignment: Alignment.Vertical = Alignment.Top,
     maxItemsInEachRow: Int = Int.MAX_VALUE,
     maxLines: Int = Int.MAX_VALUE,
     overflow: ContextualFlowRowOverflow = ContextualFlowRowOverflow.Clip,
@@ -81,6 +85,7 @@
         contextualRowMeasurementHelper(
             horizontalArrangement,
             verticalArrangement,
+            itemVerticalAlignment,
             maxItemsInEachRow,
             maxLines,
             overflowState,
@@ -118,13 +123,14 @@
  * Example:
  *
  * @sample androidx.compose.foundation.layout.samples.ContextualFlowColMaxLineDynamicSeeMore
+ * @param itemCount The total number of item composable
  * @param modifier The modifier to be applied to the Row.
- * @param horizontalArrangement The horizontal arrangement of the layout's children.
  * @param verticalArrangement The vertical arrangement of the layout's virtual column.
+ * @param horizontalArrangement The horizontal arrangement of the layout's children.
+ * @param itemHorizontalAlignment The cross axis/horizontal alignment of an item in the column.
  * @param maxItemsInEachColumn The maximum number of items per column
  * @param maxLines The maximum number of columns
- * @param overflow The strategy to handle overflowing items
- * @param itemCount The total number of item composable
+ * @param overflow The straoadtegy to handle overflowing items
  * @param content The indexed-based content of [ContextualFlowColumnScope]
  * @see FlowColumn
  * @see ContextualFlowRow
@@ -136,6 +142,7 @@
     modifier: Modifier = Modifier,
     verticalArrangement: Arrangement.Vertical = Arrangement.Top,
     horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+    itemHorizontalAlignment: Alignment.Horizontal = Alignment.Start,
     maxItemsInEachColumn: Int = Int.MAX_VALUE,
     maxLines: Int = Int.MAX_VALUE,
     overflow: ContextualFlowColumnOverflow = ContextualFlowColumnOverflow.Clip,
@@ -152,6 +159,7 @@
         contextualColumnMeasureHelper(
             verticalArrangement,
             horizontalArrangement,
+            itemHorizontalAlignment,
             maxItemsInEachColumn,
             maxLines,
             overflowState,
@@ -173,9 +181,25 @@
 
 /** Defines the scope for items within a [ContextualFlowRow]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
-interface ContextualFlowRowScope : FlowRowScope {
+interface ContextualFlowRowScope : RowScope {
+    /**
+     * Have the item fill (possibly only partially) the max height of the tallest item in the row it
+     * was placed in, within the [FlowRow].
+     *
+     * @param fraction The fraction of the max height of the tallest item between `0` and `1`,
+     *   inclusive.
+     *
+     * Example usage:
+     *
+     * @sample androidx.compose.foundation.layout.samples.SimpleFlowRow_EqualHeight
+     */
+    @ExperimentalLayoutApi
+    fun Modifier.fillMaxRowHeight(
+        @FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f,
+    ): Modifier
+
     /**
      * Identifies the row or column index where the UI component(s) are to be placed, provided they
      * do not exceed the specified [maxWidthInLine] and [maxHeight] for that row or column.
@@ -224,21 +248,37 @@
 
 /** Scope for the overflow [ContextualFlowRow]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
 interface ContextualFlowRowOverflowScope : FlowRowOverflowScope
 
 /** Scope for the overflow [ContextualFlowColumn]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
 interface ContextualFlowColumnOverflowScope : FlowColumnOverflowScope
 
 /** Provides a scope for items within a [ContextualFlowColumn]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
-interface ContextualFlowColumnScope : FlowColumnScope {
+interface ContextualFlowColumnScope : ColumnScope {
+    /**
+     * Have the item fill (possibly only partially) the max width of the widest item in the column
+     * it was placed in, within the [FlowColumn].
+     *
+     * @param fraction The fraction of the max width of the widest item between `0` and `1`,
+     *   inclusive.
+     *
+     * Example usage:
+     *
+     * @sample androidx.compose.foundation.layout.samples.SimpleFlowColumn_EqualWidth
+     */
+    @ExperimentalLayoutApi
+    fun Modifier.fillMaxColumnWidth(
+        @FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f,
+    ): Modifier
+
     /**
      * Identifies the row or column index where the UI component(s) are to be placed, provided they
      * do not exceed the specified [maxWidth] and [maxHeightInLine] for that row or column.
@@ -291,7 +331,19 @@
     override val indexInLine: Int,
     override val maxWidthInLine: Dp,
     override val maxHeight: Dp
-) : FlowRowScope by FlowRowScopeInstance, ContextualFlowRowScope
+) : RowScope by RowScopeInstance, ContextualFlowRowScope {
+    override fun Modifier.fillMaxRowHeight(fraction: Float): Modifier {
+        require(fraction >= 0.0) {
+            "invalid fraction $fraction; must be greater than " + "or equal to zero"
+        }
+        require(fraction <= 1.0) { "invalid fraction $fraction; must not be greater " + "than 1.0" }
+        return this.then(
+            FillCrossAxisSizeElement(
+                fraction = fraction,
+            )
+        )
+    }
+}
 
 @OptIn(ExperimentalLayoutApi::class)
 internal class ContextualFlowColumnScopeImpl(
@@ -299,7 +351,19 @@
     override val indexInLine: Int,
     override val maxWidth: Dp,
     override val maxHeightInLine: Dp
-) : FlowColumnScope by FlowColumnScopeInstance, ContextualFlowColumnScope
+) : ColumnScope by ColumnScopeInstance, ContextualFlowColumnScope {
+    override fun Modifier.fillMaxColumnWidth(fraction: Float): Modifier {
+        require(fraction >= 0.0) {
+            "invalid fraction $fraction; must be greater than or " + "equal to zero"
+        }
+        require(fraction <= 1.0) { "invalid fraction $fraction; must not be greater " + "than 1.0" }
+        return this.then(
+            FillCrossAxisSizeElement(
+                fraction = fraction,
+            )
+        )
+    }
+}
 
 @ExperimentalLayoutApi
 internal class ContextualFlowRowOverflowScopeImpl(private val state: FlowLayoutOverflowState) :
@@ -314,6 +378,7 @@
 internal fun contextualRowMeasurementHelper(
     horizontalArrangement: Arrangement.Horizontal,
     verticalArrangement: Arrangement.Vertical,
+    itemVerticalAlignment: Alignment.Vertical,
     maxItemsInMainAxis: Int,
     maxLines: Int,
     overflowState: FlowLayoutOverflowState,
@@ -324,6 +389,7 @@
     return remember(
         horizontalArrangement,
         verticalArrangement,
+        itemVerticalAlignment,
         maxItemsInMainAxis,
         maxLines,
         overflowState,
@@ -334,7 +400,7 @@
                 isHorizontal = true,
                 horizontalArrangement = horizontalArrangement,
                 mainAxisSpacing = horizontalArrangement.spacing,
-                crossAxisAlignment = CROSS_AXIS_ALIGNMENT_TOP,
+                crossAxisAlignment = CrossAxisAlignment.vertical(itemVerticalAlignment),
                 verticalArrangement = verticalArrangement,
                 crossAxisArrangementSpacing = verticalArrangement.spacing,
                 maxItemsInMainAxis = maxItemsInMainAxis,
@@ -352,6 +418,7 @@
 internal fun contextualColumnMeasureHelper(
     verticalArrangement: Arrangement.Vertical,
     horizontalArrangement: Arrangement.Horizontal,
+    itemHorizontalAlignment: Alignment.Horizontal,
     maxItemsInMainAxis: Int,
     maxLines: Int,
     overflowState: FlowLayoutOverflowState,
@@ -362,6 +429,7 @@
     return remember(
         verticalArrangement,
         horizontalArrangement,
+        itemHorizontalAlignment,
         maxItemsInMainAxis,
         maxLines,
         overflowState,
@@ -372,7 +440,7 @@
                 isHorizontal = false,
                 verticalArrangement = verticalArrangement,
                 mainAxisSpacing = verticalArrangement.spacing,
-                crossAxisAlignment = CROSS_AXIS_ALIGNMENT_START,
+                crossAxisAlignment = CrossAxisAlignment.horizontal(itemHorizontalAlignment),
                 horizontalArrangement = horizontalArrangement,
                 crossAxisArrangementSpacing = horizontalArrangement.spacing,
                 maxItemsInMainAxis = maxItemsInMainAxis,
@@ -488,9 +556,7 @@
                 measurable
             }
         } else {
-            throw ArrayIndexOutOfBoundsException(
-                "No item returned at index call. Index: $itemIndex"
-            )
+            throw IndexOutOfBoundsException("No item returned at index call. Index: $itemIndex")
         }
     }
 }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
index 8a8bc1a..0900cfe 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
@@ -21,7 +21,7 @@
 import androidx.collection.mutableIntListOf
 import androidx.collection.mutableIntObjectMapOf
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
 import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.remember
@@ -71,6 +71,7 @@
  * @param modifier The modifier to be applied to the Row.
  * @param horizontalArrangement The horizontal arrangement of the layout's children.
  * @param verticalArrangement The vertical arrangement of the layout's virtual rows.
+ * @param itemVerticalAlignment The cross axis/vertical alignment of an item in the column.
  * @param maxItemsInEachRow The maximum number of items per row
  * @param maxLines The max number of rows
  * @param overflow The strategy to handle overflowing items
@@ -85,6 +86,7 @@
     modifier: Modifier = Modifier,
     horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
     verticalArrangement: Arrangement.Vertical = Arrangement.Top,
+    itemVerticalAlignment: Alignment.Vertical = Alignment.Top,
     maxItemsInEachRow: Int = Int.MAX_VALUE,
     maxLines: Int = Int.MAX_VALUE,
     overflow: FlowRowOverflow = FlowRowOverflow.Clip,
@@ -95,12 +97,13 @@
         rowMeasurementMultiContentHelper(
             horizontalArrangement,
             verticalArrangement,
+            itemVerticalAlignment,
             maxItemsInEachRow,
             maxLines,
             overflowState
         )
     val list: List<@Composable () -> Unit> =
-        remember(overflow, content) {
+        remember(overflow, content, maxLines) {
             val mutableList: MutableList<@Composable () -> Unit> = mutableListOf()
             mutableList.add { FlowRowScopeInstance.content() }
             overflow.addOverflowComposables(overflowState, mutableList)
@@ -129,6 +132,7 @@
  * @param modifier The modifier to be applied to the Row.
  * @param verticalArrangement The vertical arrangement of the layout's children.
  * @param horizontalArrangement The horizontal arrangement of the layout's virtual columns
+ * @param itemHorizontalAlignment The cross axis/horizontal alignment of an item in the column.
  * @param maxItemsInEachColumn The maximum number of items per column
  * @param maxLines The max number of rows
  * @param overflow The strategy to handle overflowing items
@@ -143,6 +147,7 @@
     modifier: Modifier = Modifier,
     verticalArrangement: Arrangement.Vertical = Arrangement.Top,
     horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+    itemHorizontalAlignment: Alignment.Horizontal = Alignment.Start,
     maxItemsInEachColumn: Int = Int.MAX_VALUE,
     maxLines: Int = Int.MAX_VALUE,
     overflow: FlowColumnOverflow = FlowColumnOverflow.Clip,
@@ -153,12 +158,13 @@
         columnMeasurementMultiContentHelper(
             verticalArrangement,
             horizontalArrangement,
+            itemHorizontalAlignment,
             maxItemsInEachColumn,
             maxLines,
             overflowState
         )
     val list: List<@Composable () -> Unit> =
-        remember(overflow, content) {
+        remember(overflow, content, maxLines) {
             val mutableList: MutableList<@Composable () -> Unit> = mutableListOf()
             mutableList.add { FlowColumnScopeInstance.content() }
             overflow.addOverflowComposables(overflowState, mutableList)
@@ -169,7 +175,7 @@
 
 /** Scope for the children of [FlowRow]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
 interface FlowRowScope : RowScope {
     /**
@@ -191,7 +197,7 @@
 
 /** Scope for the overflow [FlowRow]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
 interface FlowRowOverflowScope : FlowRowScope {
     /**
@@ -208,7 +214,7 @@
 
 /** Scope for the children of [FlowColumn]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
 interface FlowColumnScope : ColumnScope {
     /**
@@ -230,7 +236,7 @@
 
 /** Scope for the overflow [FlowColumn]. */
 @LayoutScopeMarker
-@Immutable
+@Stable
 @ExperimentalLayoutApi
 interface FlowColumnOverflowScope : FlowColumnScope {
     /**
@@ -263,21 +269,17 @@
 @OptIn(ExperimentalLayoutApi::class)
 internal class FlowRowOverflowScopeImpl(private val state: FlowLayoutOverflowState) :
     FlowRowScope by FlowRowScopeInstance, FlowRowOverflowScope {
-    override val totalItemCount: Int
-        get() = state.itemCount
+    override val totalItemCount: Int by lazyInt { state.itemCount }
 
-    override val shownItemCount: Int
-        get() = state.shownItemCount
+    override val shownItemCount: Int by lazyInt(state.shownItemLazyErrorMessage) { state.itemShown }
 }
 
 @OptIn(ExperimentalLayoutApi::class)
 internal class FlowColumnOverflowScopeImpl(private val state: FlowLayoutOverflowState) :
     FlowColumnScope by FlowColumnScopeInstance, FlowColumnOverflowScope {
-    override val totalItemCount: Int
-        get() = state.itemCount
+    override val totalItemCount: Int by lazyInt { state.itemCount }
 
-    override val shownItemCount: Int
-        get() = state.shownItemCount
+    override val shownItemCount: Int by lazyInt(state.shownItemLazyErrorMessage) { state.itemShown }
 }
 
 @OptIn(ExperimentalLayoutApi::class)
@@ -374,6 +376,7 @@
 internal fun rowMeasurementMultiContentHelper(
     horizontalArrangement: Arrangement.Horizontal,
     verticalArrangement: Arrangement.Vertical,
+    itemVerticalAlignment: Alignment.Vertical,
     maxItemsInMainAxis: Int,
     maxLines: Int,
     overflowState: FlowLayoutOverflowState,
@@ -381,6 +384,7 @@
     return remember(
         horizontalArrangement,
         verticalArrangement,
+        itemVerticalAlignment,
         maxItemsInMainAxis,
         maxLines,
         overflowState
@@ -389,7 +393,7 @@
             isHorizontal = true,
             horizontalArrangement = horizontalArrangement,
             mainAxisSpacing = horizontalArrangement.spacing,
-            crossAxisAlignment = CROSS_AXIS_ALIGNMENT_TOP,
+            crossAxisAlignment = CrossAxisAlignment.vertical(itemVerticalAlignment),
             verticalArrangement = verticalArrangement,
             crossAxisArrangementSpacing = verticalArrangement.spacing,
             maxItemsInMainAxis = maxItemsInMainAxis,
@@ -434,6 +438,7 @@
 internal fun columnMeasurementMultiContentHelper(
     verticalArrangement: Arrangement.Vertical,
     horizontalArrangement: Arrangement.Horizontal,
+    itemHorizontalAlignment: Alignment.Horizontal,
     maxItemsInMainAxis: Int,
     maxLines: Int,
     overflowState: FlowLayoutOverflowState
@@ -441,6 +446,7 @@
     return remember(
         verticalArrangement,
         horizontalArrangement,
+        itemHorizontalAlignment,
         maxItemsInMainAxis,
         maxLines,
         overflowState
@@ -449,7 +455,7 @@
             isHorizontal = false,
             verticalArrangement = verticalArrangement,
             mainAxisSpacing = verticalArrangement.spacing,
-            crossAxisAlignment = CROSS_AXIS_ALIGNMENT_START,
+            crossAxisAlignment = CrossAxisAlignment.horizontal(itemHorizontalAlignment),
             horizontalArrangement = horizontalArrangement,
             crossAxisArrangementSpacing = horizontalArrangement.spacing,
             maxItemsInMainAxis = maxItemsInMainAxis,
@@ -1385,7 +1391,7 @@
         } else {
             next()
         }
-    } catch (e: ArrayIndexOutOfBoundsException) {
+    } catch (e: IndexOutOfBoundsException) {
         null
     }
 }
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt
index ae9d963..686fcc8 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt
@@ -632,6 +632,33 @@
     }
 }
 
+internal fun lazyInt(
+    errorMessage: String = "Lazy item is not yet initialized",
+    initializer: () -> Int
+): Lazy<Int> = LazyImpl(initializer, errorMessage)
+
+private class LazyImpl(val initializer: () -> Int, val errorMessage: String) : Lazy<Int> {
+    private var _value: Int = UNINITIALIZED_VALUE
+    override val value: Int
+        get() {
+            if (_value == UNINITIALIZED_VALUE) {
+                _value = initializer()
+            }
+            if (_value == UNINITIALIZED_VALUE) {
+                throw IllegalStateException(errorMessage)
+            }
+            return _value
+        }
+
+    override fun isInitialized(): Boolean = _value != UNINITIALIZED_VALUE
+
+    override fun toString(): String = if (isInitialized()) value.toString() else errorMessage
+
+    companion object {
+        internal const val UNINITIALIZED_VALUE: Int = -1
+    }
+}
+
 /** Overflow State for managing overflow state within FlowLayouts. */
 @OptIn(ExperimentalLayoutApi::class)
 internal data class FlowLayoutOverflowState
@@ -643,18 +670,19 @@
     internal val shownItemCount: Int
         get() {
             if (itemShown == -1) {
-                throw IllegalStateException(
-                    "Accessing shownItemCount before it is set. " +
-                        "Are you calling this in the Composition phase, " +
-                        "rather than in the draw phase? " +
-                        "Consider our samples on how to use it during the draw phase " +
-                        "or consider using ContextualFlowRow/ContextualFlowColumn " +
-                        "which initializes this method in the composition phase."
-                )
+                throw IllegalStateException(shownItemLazyErrorMessage)
             }
             return itemShown
         }
 
+    internal val shownItemLazyErrorMessage =
+        "Accessing shownItemCount before it is set. " +
+            "Are you calling this in the Composition phase, " +
+            "rather than in the draw phase? " +
+            "Consider our samples on how to use it during the draw phase " +
+            "or consider using ContextualFlowRow/ContextualFlowColumn " +
+            "which initializes this method in the composition phase."
+
     internal var itemShown: Int = -1
     internal var itemCount = 0
     private var seeMoreMeasurable: Measurable? = null
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
index 430f09c..b2e98bc 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.layout
 
 import androidx.annotation.FloatRange
+import androidx.compose.foundation.layout.internal.JvmDefaultWithCompatibility
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
index 5f82c58..8051e45 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
@@ -34,6 +34,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastRoundToInt
+import kotlin.jvm.JvmInline
 import kotlin.math.max
 import kotlin.math.min
 
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.kt
index 013d42b..41febd5 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.kt
@@ -170,7 +170,8 @@
             try {
                 remainder -= weightedSize.fastRoundToInt()
             } catch (e: IllegalArgumentException) {
-                throw IllegalArgumentException(
+                throw initCause(
+                    IllegalArgumentException(
                         "This log indicates a hard-to-reproduce Compose issue, " +
                             "modified with additional debugging details. " +
                             "Please help us by adding your experiences to the bug link provided. " +
@@ -200,8 +201,9 @@
                             itemWeight +
                             "weightedSize " +
                             weightedSize
-                    )
-                    .initCause(e)
+                    ),
+                    e
+                )
             }
         }
 
@@ -241,7 +243,8 @@
                             isPrioritizing = true
                         )
                 } catch (e: IllegalArgumentException) {
-                    throw IllegalArgumentException(
+                    throw initCause(
+                        IllegalArgumentException(
                             "This log indicates a hard-to-reproduce Compose issue, " +
                                 "modified with additional debugging details. " +
                                 "Please help us by adding your experiences to the bug link provided. " +
@@ -277,8 +280,9 @@
                                 remainderUnit +
                                 "childMainAxisSize " +
                                 childMainAxisSize
-                        )
-                        .initCause(e)
+                        ),
+                        e
+                    )
                 }
                 val placeable = child.measure(childConstraints)
                 val placeableMainAxisSize = placeable.mainAxisSize()
@@ -349,3 +353,8 @@
         endIndex
     )
 }
+
+internal expect inline fun initCause(
+    exception: IllegalArgumentException,
+    cause: Exception
+): Throwable
diff --git a/compose/foundation/foundation-layout/src/jvmStubsMain/kotlin/androidx/compose/foundation/layout/NotImplemented.jvmStubs.kt b/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation-layout/src/jvmStubsMain/kotlin/androidx/compose/foundation/layout/NotImplemented.jvmStubs.kt
rename to compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/NotImplemented.commonStubs.kt
diff --git a/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.commonStubs.kt b/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.commonStubs.kt
new file mode 100644
index 0000000..263e22e
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.commonStubs.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.layout
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun initCause(
+    exception: IllegalArgumentException,
+    cause: Exception
+): Throwable = implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation-layout/src/jvmStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsets.jvmStubs.kt b/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsets.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation-layout/src/jvmStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsets.jvmStubs.kt
rename to compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsets.commonStubs.kt
diff --git a/compose/foundation/foundation-layout/src/jvmStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.jvmStubs.kt b/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation-layout/src/jvmStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.jvmStubs.kt
rename to compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/WindowInsetsPadding.commonStubs.kt
diff --git a/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..f808336
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/commonStubsMain/kotlin/androidx/compose/foundation/layout/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.layout.internal
+
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/foundation/foundation-layout/src/jvmMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.jvm.kt b/compose/foundation/foundation-layout/src/jvmMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.jvm.kt
new file mode 100644
index 0000000..c273ad3
--- /dev/null
+++ b/compose/foundation/foundation-layout/src/jvmMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurePolicy.jvm.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.layout
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun initCause(
+    exception: IllegalArgumentException,
+    cause: Exception
+): Throwable {
+    return exception.initCause(cause)
+}
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 1bd3eb3..79430fc 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -210,6 +210,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class OverscrollConfiguration {
+    ctor public OverscrollConfiguration();
     ctor public OverscrollConfiguration(optional long glowColor, optional androidx.compose.foundation.layout.PaddingValues drawPadding);
     method public androidx.compose.foundation.layout.PaddingValues getDrawPadding();
     method public long getGlowColor();
@@ -538,6 +539,9 @@
     method public suspend Object? drag(optional androidx.compose.foundation.MutatePriority dragPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.DragScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
   }
 
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API feature-flags new behavior and will be removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTapGestureDetectorBehaviorApi {
+  }
+
   @androidx.compose.runtime.Stable public interface FlingBehavior {
     method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
   }
@@ -548,6 +552,7 @@
   }
 
   public final class GestureCancellationException extends java.util.concurrent.CancellationException {
+    ctor public GestureCancellationException();
     ctor public GestureCancellationException(optional String? message);
   }
 
@@ -607,8 +612,11 @@
     method public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, optional androidx.compose.ui.input.pointer.PointerEventPass pass, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
     method @Deprecated public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
     method public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> onPress, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onTap, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @SuppressCompatibility @androidx.compose.foundation.gestures.ExperimentalTapGestureDetectorBehaviorApi public static boolean getDetectTapGesturesEnableNewDispatchingBehavior();
+    method @SuppressCompatibility @androidx.compose.foundation.gestures.ExperimentalTapGestureDetectorBehaviorApi public static void setDetectTapGesturesEnableNewDispatchingBehavior(boolean);
     method public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional androidx.compose.ui.input.pointer.PointerEventPass pass, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange?>);
     method @Deprecated public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange?>);
+    property @SuppressCompatibility @androidx.compose.foundation.gestures.ExperimentalTapGestureDetectorBehaviorApi public static final boolean DetectTapGesturesEnableNewDispatchingBehavior;
   }
 
   @androidx.compose.runtime.Stable public interface TargetedFlingBehavior extends androidx.compose.foundation.gestures.FlingBehavior {
@@ -890,6 +898,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyListState();
     ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset, optional androidx.compose.foundation.lazy.LazyListPrefetchStrategy prefetchStrategy);
     method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -1060,6 +1069,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class LazyGridState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyGridState();
     ctor public LazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset, optional androidx.compose.foundation.lazy.grid.LazyGridPrefetchStrategy prefetchStrategy);
     method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -1186,6 +1196,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class LazyLayoutPrefetchState {
+    ctor public LazyLayoutPrefetchState();
     ctor public LazyLayoutPrefetchState(optional androidx.compose.foundation.lazy.layout.PrefetchScheduler? prefetchScheduler, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.NestedPrefetchScope,kotlin.Unit>? onNestedPrefetch);
     method public androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle schedulePrefetch(int index);
     method public androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle schedulePrefetch(int index, long constraints);
@@ -1493,6 +1504,10 @@
     method public androidx.compose.ui.geometry.Rect calculateRectForParent(androidx.compose.ui.geometry.Rect localRect);
   }
 
+  public final class ScrollIntoView {
+    method public static suspend Object? scrollIntoView(androidx.compose.ui.node.DelegatableNode, optional androidx.compose.ui.geometry.Rect? rect, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
 }
 
 package androidx.compose.foundation.selection {
@@ -1666,6 +1681,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class KeyboardActions {
+    ctor public KeyboardActions();
     ctor public KeyboardActions(optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onDone, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onGo, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onNext, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onPrevious, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSearch, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSend);
     method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnDone();
     method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnGo();
@@ -1692,6 +1708,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class KeyboardOptions {
+    ctor public KeyboardOptions();
     ctor @Deprecated public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
     ctor @Deprecated public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
     ctor @Deprecated public KeyboardOptions(optional int capitalization, boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions, optional Boolean? showKeyboardOnFocus, optional androidx.compose.ui.text.intl.LocaleList? hintLocales);
@@ -1825,6 +1842,7 @@
   }
 
   @androidx.compose.runtime.Immutable public static final class TextFieldLineLimits.MultiLine implements androidx.compose.foundation.text.input.TextFieldLineLimits {
+    ctor public TextFieldLineLimits.MultiLine();
     ctor public TextFieldLineLimits.MultiLine(optional int minHeightInLines, optional int maxHeightInLines);
     method public int getMaxHeightInLines();
     method public int getMinHeightInLines();
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 52eacab..3254ecd 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -212,6 +212,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class OverscrollConfiguration {
+    ctor public OverscrollConfiguration();
     ctor public OverscrollConfiguration(optional long glowColor, optional androidx.compose.foundation.layout.PaddingValues drawPadding);
     method public androidx.compose.foundation.layout.PaddingValues getDrawPadding();
     method public long getGlowColor();
@@ -540,6 +541,9 @@
     method public suspend Object? drag(optional androidx.compose.foundation.MutatePriority dragPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.DragScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
   }
 
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API feature-flags new behavior and will be removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalTapGestureDetectorBehaviorApi {
+  }
+
   @androidx.compose.runtime.Stable public interface FlingBehavior {
     method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
   }
@@ -550,6 +554,7 @@
   }
 
   public final class GestureCancellationException extends java.util.concurrent.CancellationException {
+    ctor public GestureCancellationException();
     ctor public GestureCancellationException(optional String? message);
   }
 
@@ -609,8 +614,11 @@
     method public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, optional androidx.compose.ui.input.pointer.PointerEventPass pass, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
     method @Deprecated public static suspend Object? awaitFirstDown(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional boolean requireUnconsumed, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange>);
     method public static suspend Object? detectTapGestures(androidx.compose.ui.input.pointer.PointerInputScope, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onDoubleTap, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onLongPress, optional kotlin.jvm.functions.Function3<? super androidx.compose.foundation.gestures.PressGestureScope,? super androidx.compose.ui.geometry.Offset,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> onPress, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? onTap, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @SuppressCompatibility @androidx.compose.foundation.gestures.ExperimentalTapGestureDetectorBehaviorApi public static boolean getDetectTapGesturesEnableNewDispatchingBehavior();
+    method @SuppressCompatibility @androidx.compose.foundation.gestures.ExperimentalTapGestureDetectorBehaviorApi public static void setDetectTapGesturesEnableNewDispatchingBehavior(boolean);
     method public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, optional androidx.compose.ui.input.pointer.PointerEventPass pass, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange?>);
     method @Deprecated public static suspend Object? waitForUpOrCancellation(androidx.compose.ui.input.pointer.AwaitPointerEventScope, kotlin.coroutines.Continuation<? super androidx.compose.ui.input.pointer.PointerInputChange?>);
+    property @SuppressCompatibility @androidx.compose.foundation.gestures.ExperimentalTapGestureDetectorBehaviorApi public static final boolean DetectTapGesturesEnableNewDispatchingBehavior;
   }
 
   @androidx.compose.runtime.Stable public interface TargetedFlingBehavior extends androidx.compose.foundation.gestures.FlingBehavior {
@@ -892,6 +900,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyListState();
     ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset, optional androidx.compose.foundation.lazy.LazyListPrefetchStrategy prefetchStrategy);
     method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -1062,6 +1071,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class LazyGridState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyGridState();
     ctor public LazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     ctor @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public LazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset, optional androidx.compose.foundation.lazy.grid.LazyGridPrefetchStrategy prefetchStrategy);
     method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -1188,6 +1198,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class LazyLayoutPrefetchState {
+    ctor public LazyLayoutPrefetchState();
     ctor public LazyLayoutPrefetchState(optional androidx.compose.foundation.lazy.layout.PrefetchScheduler? prefetchScheduler, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.layout.NestedPrefetchScope,kotlin.Unit>? onNestedPrefetch);
     method public androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle schedulePrefetch(int index);
     method public androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle schedulePrefetch(int index, long constraints);
@@ -1495,6 +1506,10 @@
     method public androidx.compose.ui.geometry.Rect calculateRectForParent(androidx.compose.ui.geometry.Rect localRect);
   }
 
+  public final class ScrollIntoView {
+    method public static suspend Object? scrollIntoView(androidx.compose.ui.node.DelegatableNode, optional androidx.compose.ui.geometry.Rect? rect, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
 }
 
 package androidx.compose.foundation.selection {
@@ -1668,6 +1683,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class KeyboardActions {
+    ctor public KeyboardActions();
     ctor public KeyboardActions(optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onDone, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onGo, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onNext, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onPrevious, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSearch, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? onSend);
     method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnDone();
     method public kotlin.jvm.functions.Function1<androidx.compose.foundation.text.KeyboardActionScope,kotlin.Unit>? getOnGo();
@@ -1694,6 +1710,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class KeyboardOptions {
+    ctor public KeyboardOptions();
     ctor @Deprecated public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
     ctor @Deprecated public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
     ctor @Deprecated public KeyboardOptions(optional int capitalization, boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions, optional Boolean? showKeyboardOnFocus, optional androidx.compose.ui.text.intl.LocaleList? hintLocales);
@@ -1827,6 +1844,7 @@
   }
 
   @androidx.compose.runtime.Immutable public static final class TextFieldLineLimits.MultiLine implements androidx.compose.foundation.text.input.TextFieldLineLimits {
+    ctor public TextFieldLineLimits.MultiLine();
     ctor public TextFieldLineLimits.MultiLine(optional int minHeightInLines, optional int maxHeightInLines);
     method public int getMaxHeightInLines();
     method public int getMinHeightInLines();
diff --git a/compose/foundation/foundation/benchmark/build.gradle b/compose/foundation/foundation/benchmark/build.gradle
index 388e103..d06d861 100644
--- a/compose/foundation/foundation/benchmark/build.gradle
+++ b/compose/foundation/foundation/benchmark/build.gradle
@@ -58,6 +58,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.benchmark"
 
     // DO NOT CHECK IN! Enable experimental benchmarking with R8 - for local runs only
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 95118fd..2122e74 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -30,10 +30,10 @@
     id("AndroidXComposePlugin")
 }
 
-
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -41,13 +41,13 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api("androidx.collection:collection:1.4.0")
+                api(project(":collection:collection"))
                 api(project(":compose:animation:animation"))
                 api(project(":compose:runtime:runtime"))
                 api(project(":compose:ui:ui"))
-                implementation("androidx.compose.ui:ui-text:1.6.0")
-                implementation("androidx.compose.ui:ui-util:1.6.0")
-                implementation(project(':compose:foundation:foundation-layout'))
+                implementation(project(":compose:ui:ui-text"))
+                implementation(project(":compose:ui:ui-util"))
+                implementation(project(":compose:foundation:foundation-layout"))
             }
         }
 
@@ -67,17 +67,23 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
                 implementation("androidx.emoji2:emoji2:1.3.0")
                 implementation("androidx.core:core:1.13.1")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -131,9 +137,12 @@
 
 // Screenshot tests related setup
 android {
+    compileSdk 35
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/compose/foundation/foundation"
     namespace "androidx.compose.foundation"
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
@@ -142,5 +151,6 @@
     inceptionYear = "2018"
     description = "Higher level abstractions of the Compose UI primitives. This library is design system agnostic, providing the high-level building blocks for both application and design-system developers"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:foundation:foundation:foundation-samples"))
 }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle b/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle
index 063a475..0b306d6 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/build.gradle
@@ -34,6 +34,7 @@
     implementation(project(":compose:foundation:foundation-layout:foundation-layout-samples"))
     implementation(project(":compose:integration-tests:demos:common"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:ui:ui-util"))
@@ -47,5 +48,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.demos"
 }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml b/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml
index 2a3618a..98b4fb5 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/lint-baseline.xml
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Map#getOrDefault`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Map#getOrDefault`"
         errorLine1="                val selected = selectedIndexes.getOrDefault(item, false)"
         errorLine2="                                               ~~~~~~~~~~~~">
         <location
@@ -11,36 +11,9 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.compose.foundation.demos.ListDemosKt is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                val selected = selectedIndexes.getOrDefault(item, false)"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/foundation/demos/ListDemos.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="field RainbowColors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val RainbowColors = listOf("
-        errorLine2="^">
-        <location
-            file="src/main/java/androidx/compose/foundation/demos/text/BrushDemo.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="field RainbowStops with type List&lt;Float>: replace with FloatList"
-        errorLine1="private val RainbowStops = listOf(0f, 0.2f, 0.4f, 0.6f, 0.8f, 1f)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/foundation/demos/text/BrushDemo.kt"/>
-    </issue>
-
-    <issue
         id="PrimitiveInCollection"
         message="variable options with type List&lt;? extends Alignment>: replace with FloatList"
-        errorLine1="    val options = listOf("
+        errorLine1="    val options ="
         errorLine2="    ^">
         <location
             file="src/main/java/androidx/compose/foundation/demos/text/ComposeLineHeight.kt"/>
@@ -49,8 +22,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable options with type List&lt;? extends Trim>: replace with IntList"
-        errorLine1="    val options = listOf("
-        errorLine2="    ^">
+        errorLine1="    val options = listOf(Trim.Both, Trim.None, Trim.FirstLineTop, Trim.LastLineBottom)"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/foundation/demos/text/ComposeLineHeight.kt"/>
     </issue>
@@ -67,7 +40,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable colorList with type List&lt;? extends Color>: replace with LongList"
-        errorLine1="    val colorList = listOf("
+        errorLine1="    val colorList ="
         errorLine2="    ^">
         <location
             file="src/main/java/androidx/compose/foundation/demos/text/ComposeTextSelection.kt"/>
@@ -76,7 +49,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field blendModes with type List&lt;BlendMode>: replace with IntList"
-        errorLine1="private val blendModes = listOf("
+        errorLine1="private val blendModes ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/foundation/demos/text/DrawTextDemo.kt"/>
@@ -93,15 +66,6 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="field RainbowColors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val RainbowColors = listOf("
-        errorLine2="^">
-        <location
-            file="src/main/java/androidx/compose/foundation/demos/text/DrawTextDemo.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
         message="variable list with type List&lt;? extends Integer>: replace with IntList"
         errorLine1="    var list by remember { mutableStateOf(List(50) { it }) }"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -139,7 +103,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field colors with type List&lt;Color>: replace with LongList"
-        errorLine1="private val colors = listOf("
+        errorLine1="private val colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/foundation/demos/ListDemos.kt"/>
@@ -157,8 +121,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable heights with type List&lt;? extends Dp>: replace with FloatList"
-        errorLine1="    val heights = remember {"
-        errorLine2="    ^">
+        errorLine1="    val heights = remember { List(100) { (Random.nextInt(100) + 100).dp } }"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/foundation/demos/ListDemos.kt"/>
     </issue>
@@ -166,8 +130,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable colors with type List&lt;? extends Color>: replace with LongList"
-        errorLine1="    val colors = remember {"
-        errorLine2="    ^">
+        errorLine1="    val colors = remember { List(100) { Color.hsl(Random.nextFloat() * 360, .5f, .65f) } }"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/foundation/demos/ListDemos.kt"/>
     </issue>
@@ -175,8 +139,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable items with type SnapshotStateList&lt;Integer>: replace with IntList"
-        errorLine1="    val items = remember {"
-        errorLine2="    ^">
+        errorLine1="    val items = remember { mutableStateListOf&lt;Int>().apply { repeat(20) { add(it) } } }"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/foundation/demos/ListDemos.kt"/>
     </issue>
@@ -220,7 +184,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field modifierKeys with type Set&lt;Integer>: replace with IntSet"
-        errorLine1="private val modifierKeys = setOf("
+        errorLine1="private val modifierKeys ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/foundation/demos/text/TextFieldFocusDemo.kt"/>
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/DrawTextDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/DrawTextDemo.kt
index 6e32f3d..504de76 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/DrawTextDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/DrawTextDemo.kt
@@ -72,6 +72,7 @@
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import kotlin.collections.removeFirst as removeFirstKt
 import kotlin.math.roundToInt
 import kotlin.math.roundToLong
 import kotlin.system.measureNanoTime
@@ -453,7 +454,7 @@
     fun addMeasure(duration: Long) {
         values.add(duration)
         while (values.size > capacity) {
-            values.removeFirst()
+            values.removeFirstKt()
         }
     }
 
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/SelectionPopupDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/SelectionPopupDemo.kt
new file mode 100644
index 0000000..c9d4dd8
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/SelectionPopupDemo.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.demos.text
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.selection.SelectionContainer
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Popup
+
+private val boxColor = Color.Gray
+private val textBackgroundColor = Color.White
+private val columnColor = lerp(boxColor, textBackgroundColor, 0.5f)
+
+private val text = loremIpsum(wordCount = 50)
+
+private val modifier = Modifier.background(textBackgroundColor)
+
+@Composable
+fun SelectionPopupDemo() {
+    Box(modifier = Modifier.fillMaxSize().background(boxColor)) {
+        Popup(alignment = Alignment.Center) {
+            Column(
+                verticalArrangement = Arrangement.spacedBy(8.dp),
+                modifier = Modifier.background(columnColor)
+            ) {
+                BasicText("SelectionContainer")
+                SelectionContainer { BasicText(text = text, modifier = modifier) }
+
+                BasicText("BTF1")
+                var tfv by remember { mutableStateOf(TextFieldValue(text)) }
+                BasicTextField(tfv, { tfv = it }, modifier = modifier)
+
+                BasicText("BTF2")
+                BasicTextField(rememberTextFieldState(text), modifier = modifier)
+            }
+        }
+    }
+}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
index 8ff478f..a53882f 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
@@ -212,6 +212,7 @@
                         MinTouchTargetTextSelection()
                     },
                     ComposableDemo("Selection & DropdownMenu") { DropdownMenuSelection() },
+                    ComposableDemo("Selection in Popup") { SelectionPopupDemo() },
                 )
             ),
             DemoCategory(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldFocusDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldFocusDemo.kt
index aa3cf8b..4eb96a7 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldFocusDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextFieldFocusDemo.kt
@@ -58,6 +58,7 @@
 import androidx.compose.ui.input.key.type
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.unit.dp
+import kotlin.collections.removeLast as removeLastKt
 
 private val modifierKeys =
     setOf(
@@ -100,7 +101,7 @@
         if (keys.none { it.keyEvent.keyCode == event.keyCode && !it.isUp }) {
             keys.add(0, KeyState(event))
             if (keys.size > 10) {
-                keys.removeLast()
+                keys.removeLastKt()
             }
         }
     }
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/build.gradle b/compose/foundation/foundation/integration-tests/lazy-tests/build.gradle
index 76766bf..e86a8c9 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/build.gradle
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/build.gradle
@@ -30,6 +30,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.lazytests"
 }
 
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPinnableContainerTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPinnableContainerTest.kt
index ee75d0e..25e3419 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPinnableContainerTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridPinnableContainerTest.kt
@@ -37,6 +37,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlin.collections.removeFirst as removeFirstKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Rule
@@ -500,7 +501,7 @@
         while (handles.isNotEmpty()) {
             rule.runOnIdle {
                 assertThat(composed).contains(1)
-                handles.removeFirst().release()
+                handles.removeFirstKt().release()
             }
         }
 
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
index 5e0c8d9..d988a6a 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
@@ -24,11 +24,14 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth
@@ -56,11 +59,12 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(1) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertIsDisplayed()
     }
 
@@ -73,14 +77,16 @@
                 items(100) { Spacer(Modifier.height(itemsSizeDp).fillMaxWidth().testTag("$it")) }
             }
         }
-
+        // Semantics IDs must be fetched before scrolling.
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(2) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
         rule.onNodeWithTag("2").assertIsDisplayed()
     }
 
@@ -97,10 +103,16 @@
                 items(100) { Spacer(Modifier.height(itemsSizeDp).fillMaxWidth().testTag("$it")) }
             }
         }
+        val deactivatedIds = mutableListOf<Int>()
+        repeat(DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$it").semanticsId())
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(DefaultMaxItemsToRetain + 1) } }
 
-        repeat(DefaultMaxItemsToRetain) { rule.onNodeWithTag("$it").assertIsDeactivated() }
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
+        }
         rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${DefaultMaxItemsToRetain + 1}").assertIsDisplayed()
     }
@@ -115,6 +127,8 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
@@ -133,8 +147,8 @@
         rule.onNodeWithTag("1").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("2").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         // visible
         rule.onNodeWithTag("3").assertIsDisplayed()
@@ -154,6 +168,14 @@
             runBlocking {
                 state.scrollToItem(1) // buffer is [0]
                 state.scrollToItem(2) // 0 used, buffer is [1]
+            }
+        }
+
+        // 3 should be visible at this point, so save its ID to check later
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(3) // 1 used, buffer is [2]
                 state.scrollToItem(4) // 2 used, buffer is [3]
             }
@@ -165,7 +187,7 @@
         rule.onNodeWithTag("2").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
 
         // visible
         rule.onNodeWithTag("4").assertIsDisplayed()
@@ -181,6 +203,10 @@
                 items(100) { Spacer(Modifier.height(itemsSizeDp).fillMaxWidth().testTag("$it")) }
             }
         }
+
+        val id10 = rule.onNodeWithTag("10").semanticsId()
+        val id11 = rule.onNodeWithTag("11").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(8) // buffer is [10, 11]
@@ -188,8 +214,8 @@
         }
 
         // in buffer
-        rule.onNodeWithTag("10").assertIsDeactivated()
-        rule.onNodeWithTag("11").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id10)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id11)
 
         // visible
         rule.onNodeWithTag("8").assertIsDisplayed()
@@ -209,12 +235,18 @@
             runBlocking {
                 state.scrollToItem(9) // buffer is [11]
                 state.scrollToItem(7) // 11 reused, buffer is [9]
+            }
+        }
+        // 8 should be visible at this point, so save its ID to check later
+        val id8 = rule.onNodeWithTag("8").semanticsId()
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(6) // 9 reused, buffer is [8]
             }
         }
 
         // in buffer
-        rule.onNodeWithTag("8").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id8)
 
         // visible
         rule.onNodeWithTag("6").assertIsDisplayed()
@@ -258,6 +290,15 @@
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2) // buffer is [0, 1]
+            }
+        }
+
+        // 2 and 3 should be visible at this point, so save its ID to check later
+        val id2 = rule.onNodeWithTag("2").semanticsId()
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 counter0 = 0
                 counter1 = 0
                 state.scrollToItem(0) // scrolled back, 0 and 1 are reused back. buffer: [2, 3]
@@ -276,8 +317,8 @@
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
-        rule.onNodeWithTag("2").assertIsDeactivated()
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id2)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
     }
 
     @Test
@@ -298,24 +339,25 @@
             }
         }
 
+        val deactivatedIds = mutableListOf<Int>()
         for (i in 0 until visibleItemsCount) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").semanticsId())
             rule.onNodeWithTag("$i").assertIsDisplayed()
         }
+        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").fetchSemanticsNode().id)
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(visibleItemsCount) } }
 
         rule.onNodeWithTag("$visibleItemsCount").assertIsDisplayed()
 
-        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse
-        for (i in 0 until DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
+        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse and 7 items of type 1
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
         }
-        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
 
-        // and 7 items of type 1
-        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
-        }
+        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${startOfType1 + DefaultMaxItemsToRetain}").assertDoesNotExist()
     }
 
@@ -340,6 +382,9 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2)
@@ -347,8 +392,8 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         rule.runOnIdle {
             runBlocking {
@@ -357,12 +402,20 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertDoesNotExist()
         rule.onNodeWithTag("9").assertIsDisplayed()
         rule.onNodeWithTag("10").assertIsDisplayed()
         rule.onNodeWithTag("11").assertIsDisplayed()
     }
+
+    private fun SemanticsNode.assertLayoutDeactivatedById(id: Int) {
+        children.fastForEach {
+            if (it.id == id) {
+                assert(it.layoutInfo.isDeactivated)
+            }
+        }
+    }
 }
 
 private val DefaultMaxItemsToRetain = 7
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyColumnTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyColumnTest.kt
index 7831afe..e472e71 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyColumnTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyColumnTest.kt
@@ -72,6 +72,7 @@
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.collections.removeLast as removeLastKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Rule
 import org.junit.Test
@@ -360,7 +361,7 @@
             LazyColumn { items(items) { item -> Spacer(Modifier.size(itemSize).testTag(item)) } }
         }
 
-        rule.runOnIdle { items.removeLast() }
+        rule.runOnIdle { items.removeLastKt() }
 
         rule.onNodeWithTag("1").assertIsDisplayed()
 
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPinnableContainerTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPinnableContainerTest.kt
index 5432606..590268b 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPinnableContainerTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPinnableContainerTest.kt
@@ -42,6 +42,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlin.collections.removeFirst as removeFirstKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Rule
@@ -498,7 +499,7 @@
         while (handles.isNotEmpty()) {
             rule.runOnIdle {
                 assertThat(composed).contains(1)
-                handles.removeFirst().release()
+                handles.removeFirstKt().release()
             }
         }
 
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
index a952b29..0ab752c 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
@@ -28,11 +28,14 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth
@@ -62,11 +65,12 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(1) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertIsDisplayed()
     }
 
@@ -82,13 +86,15 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(2) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
         rule.onNodeWithTag("2").assertIsDisplayed()
     }
 
@@ -103,10 +109,17 @@
                 }
             }
         }
+        // Semantics IDs must be fetched before scrolling.
+        val deactivatedIds = mutableListOf<Int>()
+        repeat(DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$it").semanticsId())
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(DefaultMaxItemsToRetain + 1) } }
 
-        repeat(DefaultMaxItemsToRetain) { rule.onNodeWithTag("$it").assertIsDeactivated() }
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
+        }
         rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${DefaultMaxItemsToRetain + 1}").assertIsDisplayed()
     }
@@ -123,6 +136,8 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
@@ -141,8 +156,8 @@
         rule.onNodeWithTag("1").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("2").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         // visible
         rule.onNodeWithTag("3").assertIsDisplayed()
@@ -164,6 +179,14 @@
             runBlocking {
                 state.scrollToItem(1) // buffer is [0]
                 state.scrollToItem(2) // 0 used, buffer is [1]
+            }
+        }
+
+        // 3 should be visible at this point, so save its ID to check later
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(3) // 1 used, buffer is [2]
                 state.scrollToItem(4) // 2 used, buffer is [3]
             }
@@ -175,7 +198,7 @@
         rule.onNodeWithTag("2").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
 
         // visible
         rule.onNodeWithTag("4").assertIsDisplayed()
@@ -193,6 +216,10 @@
                 }
             }
         }
+
+        val id10 = rule.onNodeWithTag("10").semanticsId()
+        val id11 = rule.onNodeWithTag("11").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(8) // buffer is [10, 11]
@@ -200,8 +227,8 @@
         }
 
         // in buffer
-        rule.onNodeWithTag("10").assertIsDeactivated()
-        rule.onNodeWithTag("11").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id10)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id11)
 
         // visible
         rule.onNodeWithTag("8").assertIsDisplayed()
@@ -223,12 +250,18 @@
             runBlocking {
                 state.scrollToItem(9) // buffer is [11]
                 state.scrollToItem(7) // 11 reused, buffer is [9]
+            }
+        }
+        // 8 should be visible at this point, so save its ID to check later
+        val id8 = rule.onNodeWithTag("8").semanticsId()
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(6) // 9 reused, buffer is [8]
             }
         }
 
         // in buffer
-        rule.onNodeWithTag("8").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id8)
 
         // visible
         rule.onNodeWithTag("6").assertIsDisplayed()
@@ -277,6 +310,15 @@
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2) // buffer is [0, 1]
+            }
+        }
+
+        // 2 and 3 should be visible at this point, so save its ID to check later
+        val id2 = rule.onNodeWithTag("2").semanticsId()
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 counter0 = 0
                 counter1 = 0
                 state.scrollToItem(0) // scrolled back, 0 and 1 are reused back. buffer: [2, 3]
@@ -295,8 +337,8 @@
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
-        rule.onNodeWithTag("2").assertIsDeactivated()
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id2)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
     }
 
     @Test
@@ -313,24 +355,25 @@
             }
         }
 
+        val deactivatedIds = mutableListOf<Int>()
         for (i in 0 until visibleItemsCount) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").semanticsId())
             rule.onNodeWithTag("$i").assertIsDisplayed()
         }
+        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").fetchSemanticsNode().id)
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(visibleItemsCount) } }
 
         rule.onNodeWithTag("$visibleItemsCount").assertIsDisplayed()
 
-        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse
-        for (i in 0 until DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
+        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse and 7 items of type 1
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
         }
-        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
 
-        // and 7 items of type 1
-        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
-        }
+        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${startOfType1 + DefaultMaxItemsToRetain}").assertDoesNotExist()
     }
 
@@ -355,6 +398,9 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2)
@@ -362,8 +408,8 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         rule.runOnIdle {
             runBlocking {
@@ -372,12 +418,20 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertDoesNotExist()
         rule.onNodeWithTag("9").assertIsDisplayed()
         rule.onNodeWithTag("10").assertIsDisplayed()
         rule.onNodeWithTag("11").assertIsDisplayed()
     }
+
+    private fun SemanticsNode.assertLayoutDeactivatedById(id: Int) {
+        children.fastForEach {
+            if (it.id == id) {
+                assert(it.layoutInfo.isDeactivated)
+            }
+        }
+    }
 }
 
 private val DefaultMaxItemsToRetain = 7
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPinnableContainerTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPinnableContainerTest.kt
index 855edd6..58a6494 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPinnableContainerTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPinnableContainerTest.kt
@@ -37,6 +37,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlin.collections.removeFirst as removeFirstKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Rule
@@ -500,7 +501,7 @@
         while (handles.isNotEmpty()) {
             rule.runOnIdle {
                 assertThat(composed).contains(1)
-                handles.removeFirst().release()
+                handles.removeFirstKt().release()
             }
         }
 
diff --git a/compose/foundation/foundation/lint-baseline.xml b/compose/foundation/foundation/lint-baseline.xml
index 3978436..ba80c20 100644
--- a/compose/foundation/foundation/lint-baseline.xml
+++ b/compose/foundation/foundation/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.6.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha05)" variant="all" version="8.6.0-alpha05">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
@@ -212,42 +212,6 @@
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of getHeaderIndexes: replace with IntList"
         errorLine1="    val headerIndexes: List&lt;Int>"
-        errorLine2="        ~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListIntervalContent.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable varb332d665 with type List&lt;Integer>: replace with IntList"
-        errorLine1="        get() = _headerIndexes ?: emptyList()"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListIntervalContent.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable headersIndexes with type List&lt;Integer>: replace with IntList"
-        errorLine1="        val headersIndexes = _headerIndexes ?: mutableListOf&lt;Int>().also { _headerIndexes = it }"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListIntervalContent.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable varb3336cf1 with type List&lt;Integer>: replace with IntList"
-        errorLine1="        val headersIndexes = _headerIndexes ?: mutableListOf&lt;Int>().also { _headerIndexes = it }"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListIntervalContent.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="return type List&lt;Integer> of getHeaderIndexes: replace with IntList"
-        errorLine1="    val headerIndexes: List&lt;Int>"
         errorLine2="                       ~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt"/>
diff --git a/compose/foundation/foundation/samples/build.gradle b/compose/foundation/foundation/samples/build.gradle
index 318daf5..61e8271 100644
--- a/compose/foundation/foundation/samples/build.gradle
+++ b/compose/foundation/foundation/samples/build.gradle
@@ -40,6 +40,7 @@
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:foundation:foundation-layout"))
     implementation("androidx.compose.material:material:1.2.1")
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation("androidx.compose.runtime:runtime:1.2.1")
     implementation("androidx.compose.ui:ui:1.2.1")
     implementation("androidx.compose.ui:ui-text:1.2.1")
@@ -55,5 +56,8 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.foundation.samples"
+    // TODO(b/328001575)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
index 18065f8..7d6f1ca 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/ClickableTest.kt
@@ -29,6 +29,8 @@
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredHeight
@@ -5683,6 +5685,68 @@
             .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
             .assertOnClickLabelMatches("true")
     }
+
+    @Test // https://youtrack.jetbrains.com/issue/CMP-5069
+    fun clickableInScrollContainerWithMouse() {
+        var isClicked = false
+        rule.setContent {
+            Row(modifier = Modifier.testTag("container")) {
+                Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
+                    Box(modifier = Modifier.size(100.dp).clickable { isClicked = true })
+                }
+            }
+        }
+
+        rule.onNodeWithTag("container").performMouseInput {
+            moveBy(Offset(10f, 10f))
+            press()
+            moveBy(Offset(50f, 50f))
+            release()
+        }
+
+        rule.runOnIdle {
+            assertTrue(isClicked, "The Box is expected to receive a click when using Mouse")
+        }
+    }
+
+    @Test // https://youtrack.jetbrains.com/issue/CMP-5069
+    fun clickableInScrollContainerWithTouch() {
+        var isClicked = false
+        rule.setContent {
+            Row(modifier = Modifier.testTag("container")) {
+                Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
+                    Box(modifier = Modifier.size(100.dp).clickable { isClicked = true })
+                }
+            }
+        }
+
+        rule.onNodeWithTag("container").performTouchInput {
+            down(Offset(10f, 10f))
+            // drag a bit
+            moveBy(Offset(50f, 50f))
+            up()
+        }
+
+        rule.runOnIdle {
+            assertFalse(
+                isClicked,
+                "The Box is NOT expected to receive a Click while dragging using touch"
+            )
+        }
+
+        rule.onNodeWithTag("container").performTouchInput {
+            down(Offset(50f, 50f))
+            // no drag
+            up()
+        }
+
+        rule.runOnIdle {
+            assertTrue(
+                isClicked,
+                "The Box is expected to receive a Click, there was no dragging using touch"
+            )
+        }
+    }
 }
 
 /**
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/DraggableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/DraggableTest.kt
index 06a8a4a..716a842 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/DraggableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/DraggableTest.kt
@@ -1021,6 +1021,26 @@
         }
     }
 
+    @Test // b/355160589
+    fun onDragStopped_withSameTimeStamps_shouldNotPropagateNanVelocity() {
+        setDraggableContent {
+            Modifier.draggable(
+                Orientation.Horizontal,
+                onDragStopped = { assertThat(it).isNotNaN() }
+            ) {}
+        }
+
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 0f), 0L)
+            moveBy(Offset(100f, 0f), 0L)
+            moveBy(Offset(100f, 0f), 0L)
+            up()
+        }
+
+        rule.waitForIdle()
+    }
+
     @Test
     fun equalInputs_shouldResolveToEquals() {
         val state = DraggableState {}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/anchoredDraggable/AnchoredDraggableStateTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/anchoredDraggable/AnchoredDraggableStateTest.kt
index 7109852..f074dcf 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/anchoredDraggable/AnchoredDraggableStateTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/anchoredDraggable/AnchoredDraggableStateTest.kt
@@ -262,7 +262,7 @@
         // Swipe towards B, close after threshold
         state.dispatchRawDelta(aToBThreshold * 0.2f)
 
-        assertThat(state.currentValue).isEqualTo(A)
+        assertThat(state.currentValue).isEqualTo(B)
         assertThat(state.targetValue).isEqualTo(B)
 
         runBlocking(AutoTestFrameClock()) { performFling(flingBehavior, state, 0f) }
@@ -279,7 +279,7 @@
         // Swipe towards A, close after threshold
         state.dispatchRawDelta(-(aToBThreshold * 0.2f))
 
-        assertThat(state.currentValue).isEqualTo(B)
+        assertThat(state.currentValue).isEqualTo(A)
         assertThat(state.targetValue).isEqualTo(A)
 
         runBlocking(AutoTestFrameClock()) { performFling(flingBehavior, state, 0f) }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
index 182f9dd..0cf9953 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
@@ -42,6 +42,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlin.collections.removeFirst as removeFirstKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Rule
@@ -475,7 +476,7 @@
         while (handles.isNotEmpty()) {
             rule.runOnIdle {
                 assertThat(composed).contains(1)
-                handles.removeFirst().release()
+                handles.removeFirstKt().release()
             }
         }
 
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextHoverTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextHoverTest.kt
index c6f42a1..4db7891a 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextHoverTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextHoverTest.kt
@@ -51,6 +51,7 @@
 class BasicTextHoverTest {
     @get:Rule val rule = createComposeRule()
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenSelectableText_andDefaultIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runSelectableTest(
@@ -60,6 +61,7 @@
             expectedTextIcon = TYPE_TEXT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenSelectableText_andSetIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runSelectableTest(
@@ -69,6 +71,7 @@
             expectedTextIcon = TYPE_TEXT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenSelectableText_andSetIcon_withOverride_inBoxWithDefaultIcon_setIconIsUsed() =
         runSelectableTest(
@@ -135,6 +138,7 @@
             }
         }
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenNonSelectableText_andDefaultIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runNonSelectableTest(
@@ -144,6 +148,7 @@
             expectedTextIcon = TYPE_DEFAULT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenNonSelectableText_andSetIcon_inBoxWithDefaultIcon_setIconIsUsed() =
         runNonSelectableTest(
@@ -153,6 +158,7 @@
             expectedTextIcon = TYPE_CROSSHAIR
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenNonSelectableText_andSetIcon_withOverride_inBoxWithDefaultIcon_setIconIsUsed() =
         runNonSelectableTest(
@@ -217,6 +223,7 @@
             }
         }
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenDisabledSelectionText_andDefaultIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runDisabledSelectionText(
@@ -226,6 +233,7 @@
             expectedTextIcon = TYPE_DEFAULT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenDisabledSelectionText_andSetIcon_inBoxWithDefaultIcon_setIconIsUsed() =
         runDisabledSelectionText(
@@ -235,6 +243,7 @@
             expectedTextIcon = TYPE_CROSSHAIR
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenDisabledSelectionText_andSetIcon_withOverride_inBoxWithDefaultIcon_setIconIsUsed() =
         runDisabledSelectionText(
@@ -337,6 +346,6 @@
 
             // Exit hovering over element
             rule.onNodeWithTag(selectionContainerTag).performMouseInput { exit() }
-            assertIcon(TYPE_DEFAULT)
+            @Suppress("DEPRECATION") assertIcon(TYPE_DEFAULT)
         }
 }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
index 2050d0c..9e2b97b 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/BasicTextLinkTest.kt
@@ -888,6 +888,44 @@
         assertThat(styles).isEmpty()
     }
 
+    @Test
+    fun links_displayedWithCorrectStyle_onFirstFrame() {
+        val checked = mutableStateOf(false)
+        var calledAfterChecked = false
+
+        rule.setContent {
+            calledAfterChecked = checked.value
+            BasicText(
+                buildAnnotatedString {
+                    withLink(
+                        LinkAnnotation.Clickable(
+                            "tag",
+                            TextLinkStyles(SpanStyle(color = Color.Green))
+                        ) {}
+                    ) {
+                        append("Link")
+                    }
+                },
+                style = TextStyle(Color.Red),
+                onTextLayout = {
+                    // When the flicker happens, the BasicText is first composed and drawn
+                    // with the empty span styles. Only after the recomposition the link
+                    // style is applied. After the fix the first composition happens with
+                    // the correct link color and therefore the span styles will contain it
+                    val colors = it.layoutInput.text.spanStyles.map { it.item.color }
+                    assertThat(colors).isNotEmpty()
+                    assertThat(colors.first()).isEqualTo(Color.Green)
+                }
+            )
+        }
+
+        checked.value = true
+        rule.runOnIdle {
+            // ensures that recomposition happened after a `checked` change
+            assertThat(calledAfterChecked).isTrue()
+        }
+    }
+
     @Composable
     private fun TextWithLinks() =
         with(rule.density) {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHoverTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHoverTest.kt
index 5a27adf..7fd6761 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHoverTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/CoreTextFieldHoverTest.kt
@@ -52,6 +52,7 @@
 class CoreTextFieldHoverTest {
     @get:Rule val rule = createComposeRule()
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenDefaultIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runTest(
@@ -61,6 +62,7 @@
             expectedTextIcon = TYPE_TEXT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenSetIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runTest(
@@ -70,6 +72,7 @@
             expectedTextIcon = TYPE_TEXT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenSetIcon_withOverride_inBoxWithDefaultIcon_setIconIsUsed() =
         runTest(
@@ -148,6 +151,6 @@
 
             // Exit hovering over element
             rule.onNodeWithTag(boxTag).performMouseInput { exit() }
-            assertIcon(TYPE_DEFAULT)
+            @Suppress("DEPRECATION") assertIcon(TYPE_DEFAULT)
         }
 }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldSemanticsTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldSemanticsTest.kt
index ee93cda..37099ff 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldSemanticsTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/BasicTextFieldSemanticsTest.kt
@@ -32,6 +32,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.expectError
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalClipboardManager
 import androidx.compose.ui.platform.testTag
@@ -153,7 +154,9 @@
             BasicTextField(state = state, modifier = Modifier.testTag(Tag), readOnly = true)
         }
 
-        rule.onNodeWithTag(Tag).performTextReplacement("hello")
+        expectError<AssertionError>(expectedMessage = "Failed to perform text input.*") {
+            rule.onNodeWithTag(Tag).performTextReplacement("hello")
+        }
 
         assertThat(state.text.toString()).isEqualTo("")
     }
@@ -198,7 +201,9 @@
             BasicTextField(state = state, modifier = Modifier.testTag(Tag), readOnly = true)
         }
 
-        rule.onNodeWithTag(Tag).performTextInput("hello")
+        expectError<AssertionError>(expectedMessage = "Failed to perform text input.*") {
+            rule.onNodeWithTag(Tag).performTextInput("hello")
+        }
 
         assertThat(state.text.toString()).isEqualTo("")
     }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/FakeInputMethodManager.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/FakeInputMethodManager.kt
index ce78be4..d01acf3 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/FakeInputMethodManager.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/FakeInputMethodManager.kt
@@ -21,12 +21,13 @@
 import android.view.inputmethod.ExtractedText
 import androidx.compose.foundation.text.input.internal.ComposeInputMethodManager
 import com.google.common.truth.Truth.assertThat
+import kotlin.collections.removeFirst as removeFirstKt
 
 internal open class FakeInputMethodManager : ComposeInputMethodManager {
     private val calls = mutableListOf<String>()
 
     fun expectCall(description: String) {
-        assertThat(calls.removeFirst()).isEqualTo(description)
+        assertThat(calls.removeFirstKt()).isEqualTo(description)
     }
 
     fun expectNoMoreCalls() {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt
index 41a50b8..f3b8e93 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldCursorTest.kt
@@ -64,6 +64,7 @@
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.hasPerformImeAction
 import androidx.compose.ui.test.hasSetTextAction
 import androidx.compose.ui.test.junit4.ComposeContentTestRule
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -697,7 +698,8 @@
         rule.mainClock.advanceTimeBy(100)
         rule.mainClock.advanceTimeByFrame()
 
-        rule.onNode(hasSetTextAction()).captureToImage().assertDoesNotContainColor(cursorColor)
+        // readonly fields do not have setText action
+        rule.onNode(hasPerformImeAction()).captureToImage().assertDoesNotContainColor(cursorColor)
     }
 
     @Test
@@ -721,12 +723,13 @@
         rule.mainClock.advanceTimeBy(100)
         rule.mainClock.advanceTimeByFrame()
 
-        rule.onNode(hasSetTextAction()).captureToImage().assertDoesNotContainColor(cursorColor)
+        // readonly fields do not have setText action
+        rule.onNode(hasPerformImeAction()).captureToImage().assertDoesNotContainColor(cursorColor)
 
         readOnly = false
         rule.mainClock.advanceTimeByFrame()
 
-        rule.onNode(hasSetTextAction()).captureToImage().assertCursor(cursorTopCenterInLtr)
+        rule.onNode(hasPerformImeAction()).captureToImage().assertCursor(cursorTopCenterInLtr)
     }
 
     @Test
@@ -1024,7 +1027,7 @@
     }
 
     private fun focusAndWait() {
-        rule.onNode(hasSetTextAction()).requestFocus()
+        rule.onNode(hasPerformImeAction()).requestFocus()
         rule.mainClock.advanceTimeUntil { isFocused }
     }
 
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
index 6718e56..64c33f0 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
@@ -71,6 +71,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
@@ -382,6 +383,7 @@
         rule.onNodeWithTag("test-text-field-1").assertSelection(TextRange(2))
     }
 
+    @FlakyTest(bugId = 348380475)
     @SdkSuppress(minSdkVersion = 22) // b/266742195
     @Test
     fun basicTextField_checkFocusNavigation_onDPadRight_hardwareKeyboard() {
@@ -424,6 +426,7 @@
         rule.onNodeWithTag("test-text-field-1").assertSelection(TextRange(3))
     }
 
+    @FlakyTest(bugId = 348380475)
     @SdkSuppress(minSdkVersion = 22) // b/266742195
     @Test
     fun basicTextField_checkFocusNavigation_onDPadDown_hardwareKeyboard() {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt
index 5350717..67ef74b 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldKeyEventTest.kt
@@ -16,10 +16,13 @@
 
 package androidx.compose.foundation.text.input
 
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.text.BasicSecureTextField
 import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.text.TEST_FONT_FAMILY
 import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
 import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
@@ -29,6 +32,11 @@
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.LocalClipboardManager
 import androidx.compose.ui.platform.LocalDensity
@@ -39,11 +47,13 @@
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performKeyInput
 import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.test.requestFocus
 import androidx.compose.ui.test.withKeyDown
 import androidx.compose.ui.test.withKeysDown
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
@@ -681,6 +691,166 @@
         }
     }
 
+    @Test
+    fun textField_singleLine_pressEnter_parentClickable() {
+        var parentClickCount = 0
+        var keyboardActionCount = 0
+        rule.setContent {
+            Box(Modifier.clickable { parentClickCount++ }) {
+                BasicTextField(
+                    state = rememberTextFieldState(),
+                    textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY, fontSize = 30.sp),
+                    modifier = Modifier.testTag(tag),
+                    lineLimits = SingleLine,
+                    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
+                    onKeyboardAction = { keyboardActionCount++ }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(tag).requestFocus()
+        rule.onNodeWithTag(tag).performKeyInput { pressKey(Key.Enter) }
+
+        rule.runOnIdle {
+            assertThat(parentClickCount).isEqualTo(0)
+            assertThat(keyboardActionCount).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun textField_multiLine_pressEnter_parentClickable() {
+        var parentClickCount = 0
+        var keyboardActionCount = 0
+        val state = TextFieldState()
+        rule.setContent {
+            Box(Modifier.clickable { parentClickCount++ }) {
+                BasicTextField(
+                    state = state,
+                    textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY, fontSize = 30.sp),
+                    modifier = Modifier.testTag(tag),
+                    lineLimits = MultiLine(),
+                    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
+                    onKeyboardAction = { keyboardActionCount++ }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(tag).requestFocus()
+        rule.onNodeWithTag(tag).performKeyInput { pressKey(Key.Enter) }
+
+        rule.runOnIdle {
+            assertThat(parentClickCount).isEqualTo(0)
+            assertThat(keyboardActionCount).isEqualTo(0)
+            assertThat(state.text).isEqualTo("\n")
+        }
+    }
+
+    @Test
+    fun textField_consumedKeyDownEvent_keyUpDoesNotPropagate() {
+        val parentKeyEvents = mutableListOf<KeyEvent>()
+        val state = TextFieldState()
+        rule.setContent {
+            Box(
+                Modifier.onKeyEvent {
+                    parentKeyEvents += it
+                    true
+                }
+            ) {
+                BasicTextField(
+                    state = state,
+                    textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY, fontSize = 30.sp),
+                    modifier = Modifier.testTag(tag),
+                )
+            }
+        }
+
+        rule.onNodeWithTag(tag).requestFocus()
+        rule.onNodeWithTag(tag).performKeyInput { pressKey(Key.A) }
+
+        rule.runOnIdle {
+            // even key up even shouldn't be passed to parent listener since the down event is
+            // consumed by BasicTextField
+            assertThat(parentKeyEvents).isEmpty()
+            assertThat(state.text).isEqualTo("a")
+        }
+    }
+
+    @Test
+    fun textField_simultaneousConsumedKeyDownEvents_keyUpDoesNotPropagate() {
+        val parentKeyEvents = mutableListOf<KeyEvent>()
+        val state = TextFieldState()
+        rule.setContent {
+            Box(
+                Modifier.onKeyEvent {
+                    parentKeyEvents += it
+                    true
+                }
+            ) {
+                BasicTextField(
+                    state = state,
+                    textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY, fontSize = 30.sp),
+                    modifier = Modifier.testTag(tag),
+                )
+            }
+        }
+
+        rule.onNodeWithTag(tag).requestFocus()
+        rule.onNodeWithTag(tag).performKeyInput {
+            keyDown(Key.A)
+            keyDown(Key.B)
+            keyDown(Key.C)
+            keyUp(Key.C)
+            keyUp(Key.B)
+            keyUp(Key.A)
+        }
+
+        rule.runOnIdle {
+            // even key up even shouldn't be passed to parent listener since the down event is
+            // consumed by BasicTextField
+            assertThat(parentKeyEvents).isEmpty()
+            assertThat(state.text).isEqualTo("abc")
+        }
+    }
+
+    @Test
+    fun textField_simultaneousMetaConsumedKeyDownEvents_keyUpDoesNotPropagate() {
+        val parentKeyEvents = mutableListOf<KeyEvent>()
+        val state = TextFieldState()
+        rule.setContent {
+            Box(
+                Modifier.onKeyEvent {
+                    parentKeyEvents += it
+                    true
+                }
+            ) {
+                BasicTextField(
+                    state = state,
+                    textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY, fontSize = 30.sp),
+                    modifier = Modifier.testTag(tag),
+                )
+            }
+        }
+
+        rule.onNodeWithTag(tag).requestFocus()
+        rule.onNodeWithTag(tag).performKeyInput {
+            keyDown(Key.ShiftLeft)
+            keyDown(Key.A)
+            keyUp(Key.ShiftLeft)
+            keyUp(Key.A)
+        }
+
+        rule.runOnIdle {
+            // even key up even shouldn't be passed to parent listener since the down event is
+            // consumed by BasicTextField
+            assertThat(parentKeyEvents.size).isEqualTo(2)
+            assertThat(parentKeyEvents[0].key).isEqualTo(Key.ShiftLeft)
+            assertThat(parentKeyEvents[0].type).isEqualTo(KeyEventType.KeyDown)
+            assertThat(parentKeyEvents[1].key).isEqualTo(Key.ShiftLeft)
+            assertThat(parentKeyEvents[1].type).isEqualTo(KeyEventType.KeyUp)
+            assertThat(state.text).isEqualTo("A")
+        }
+    }
+
     private inner class SequenceScope(
         val state: TextFieldState,
         val clipboardManager: ClipboardManager,
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt
index 41a0cf14..034f2e7 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldReceiveContentTest.kt
@@ -116,7 +116,7 @@
         rule.onNodeWithTag(tag).requestFocus()
         inputMethodInterceptor.withEditorInfo {
             val contentMimeTypes = EditorInfoCompat.getContentMimeTypes(this)
-            assertThat(contentMimeTypes).isEqualTo(arrayOf("*/*"))
+            assertThat(contentMimeTypes).asList().containsAtLeastElementsIn(arrayOf("*/*"))
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt
index e873706..5c1cbcc 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/BasicTextFieldHoverTest.kt
@@ -51,6 +51,7 @@
 class BasicTextFieldHoverTest {
     @get:Rule val rule = createComposeRule()
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenDefaultIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runTest(
@@ -60,6 +61,7 @@
             expectedTextIcon = TYPE_TEXT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenSetIcon_inBoxWithDefaultIcon_textIconIsUsed() =
         runTest(
@@ -69,6 +71,7 @@
             expectedTextIcon = TYPE_TEXT
         )
 
+    @Suppress("DEPRECATION")
     @Test
     fun whenSetIcon_withOverride_inBoxWithDefaultIcon_setIconIsUsed() =
         runTest(
@@ -146,6 +149,6 @@
 
             // Exit hovering over element
             rule.onNodeWithTag(boxTag).performMouseInput { exit() }
-            assertIcon(TYPE_DEFAULT)
+            @Suppress("DEPRECATION") assertIcon(TYPE_DEFAULT)
         }
 }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnectionTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnectionTest.kt
index 0a36e88..1375112 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnectionTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnectionTest.kt
@@ -17,9 +17,19 @@
 package androidx.compose.foundation.text.input.internal
 
 import android.content.ClipDescription
+import android.graphics.Color
+import android.graphics.Typeface
 import android.net.Uri
 import android.os.Bundle
 import android.os.CancellationSignal
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import android.text.style.BackgroundColorSpan
+import android.text.style.ForegroundColorSpan
+import android.text.style.StrikethroughSpan
+import android.text.style.StyleSpan
+import android.text.style.TypefaceSpan
+import android.text.style.UnderlineSpan
 import android.view.KeyEvent
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.HandwritingGesture
@@ -31,17 +41,24 @@
 import androidx.compose.foundation.text.input.TextFieldCharSequence
 import androidx.compose.foundation.text.input.TextFieldState
 import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.platform.firstUriOrNull
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.style.TextDecoration
 import androidx.core.view.inputmethod.EditorInfoCompat
 import androidx.core.view.inputmethod.InputConnectionCompat
 import androidx.core.view.inputmethod.InputContentInfoCompat
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 import org.junit.Before
@@ -113,26 +130,26 @@
 
     @Test
     fun getTextBeforeAndAfterCursorTest() {
-        Truth.assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("")
-        Truth.assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("")
+        assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("")
+        assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("")
 
         // Set "Hello, World", and place the cursor at the beginning of the text.
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange.Zero)
 
-        Truth.assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("")
-        Truth.assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("Hello, World")
+        assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("")
+        assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("Hello, World")
 
         // Set "Hello, World", and place the cursor between "H" and "e".
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange(1))
 
-        Truth.assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("H")
-        Truth.assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("ello, World")
+        assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("H")
+        assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("ello, World")
 
         // Set "Hello, World", and place the cursor at the end of the text.
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange(12))
 
-        Truth.assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("Hello, World")
-        Truth.assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("")
+        assertThat(ic.getTextBeforeCursor(100, 0)).isEqualTo("Hello, World")
+        assertThat(ic.getTextAfterCursor(100, 0)).isEqualTo("")
     }
 
     @Test
@@ -140,20 +157,20 @@
         // Set "Hello, World", and place the cursor at the beginning of the text.
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange.Zero)
 
-        Truth.assertThat(ic.getTextBeforeCursor(5, 0)).isEqualTo("")
-        Truth.assertThat(ic.getTextAfterCursor(5, 0)).isEqualTo("Hello")
+        assertThat(ic.getTextBeforeCursor(5, 0)).isEqualTo("")
+        assertThat(ic.getTextAfterCursor(5, 0)).isEqualTo("Hello")
 
         // Set "Hello, World", and place the cursor between "H" and "e".
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange(1))
 
-        Truth.assertThat(ic.getTextBeforeCursor(5, 0)).isEqualTo("H")
-        Truth.assertThat(ic.getTextAfterCursor(5, 0)).isEqualTo("ello,")
+        assertThat(ic.getTextBeforeCursor(5, 0)).isEqualTo("H")
+        assertThat(ic.getTextAfterCursor(5, 0)).isEqualTo("ello,")
 
         // Set "Hello, World", and place the cursor at the end of the text.
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange(12))
 
-        Truth.assertThat(ic.getTextBeforeCursor(5, 0)).isEqualTo("World")
-        Truth.assertThat(ic.getTextAfterCursor(5, 0)).isEqualTo("")
+        assertThat(ic.getTextBeforeCursor(5, 0)).isEqualTo("World")
+        assertThat(ic.getTextAfterCursor(5, 0)).isEqualTo("")
     }
 
     @Test
@@ -161,17 +178,17 @@
         // Set "Hello, World", and place the cursor at the beginning of the text.
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange.Zero)
 
-        Truth.assertThat(ic.getSelectedText(0)).isNull()
+        assertThat(ic.getSelectedText(0)).isNull()
 
         // Set "Hello, World", and place the cursor between "H" and "e".
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange(0, 1))
 
-        Truth.assertThat(ic.getSelectedText(0)).isEqualTo("H")
+        assertThat(ic.getSelectedText(0)).isEqualTo("H")
 
         // Set "Hello, World", and place the cursor at the end of the text.
         value = TextFieldCharSequence(text = "Hello, World", selection = TextRange(0, 12))
 
-        Truth.assertThat(ic.getSelectedText(0)).isEqualTo("Hello, World")
+        assertThat(ic.getSelectedText(0)).isEqualTo("Hello, World")
     }
 
     @Test
@@ -187,17 +204,17 @@
         // Do not callback to listener during batch session.
         ic.beginBatchEdit()
 
-        Truth.assertThat(ic.commitText("Hello, ", 1)).isTrue()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(ic.commitText("Hello, ", 1)).isTrue()
+        assertThat(requestEditsCalled).isEqualTo(0)
 
-        Truth.assertThat(ic.commitText("World.", 1)).isTrue()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(ic.commitText("World.", 1)).isTrue()
+        assertThat(requestEditsCalled).isEqualTo(0)
 
         ic.endBatchEdit()
 
-        Truth.assertThat(requestEditsCalled).isEqualTo(1)
-        Truth.assertThat(state.mainBuffer.toString()).isEqualTo("Hello, World.")
-        Truth.assertThat(state.mainBuffer.selection).isEqualTo(TextRange(13))
+        assertThat(requestEditsCalled).isEqualTo(1)
+        assertThat(state.mainBuffer.toString()).isEqualTo("Hello, World.")
+        assertThat(state.mainBuffer.selection).isEqualTo(TextRange(13))
     }
 
     @OptIn(ExperimentalComposeUiApi::class)
@@ -220,19 +237,17 @@
                 extras
             )
 
-        Truth.assertThat(transferableContent).isNotNull()
-        Truth.assertThat(transferableContent?.clipEntry).isNotNull()
-        Truth.assertThat(transferableContent?.clipEntry?.firstUriOrNull()).isEqualTo(contentUri)
-        Truth.assertThat(transferableContent?.clipEntry?.clipData?.itemCount).isEqualTo(1)
-        Truth.assertThat(transferableContent?.clipMetadata?.clipDescription)
-            .isSameInstanceAs(description)
+        assertThat(transferableContent).isNotNull()
+        assertThat(transferableContent?.clipEntry).isNotNull()
+        assertThat(transferableContent?.clipEntry?.firstUriOrNull()).isEqualTo(contentUri)
+        assertThat(transferableContent?.clipEntry?.clipData?.itemCount).isEqualTo(1)
+        assertThat(transferableContent?.clipMetadata?.clipDescription).isSameInstanceAs(description)
 
-        Truth.assertThat(transferableContent?.source).isEqualTo(TransferableContent.Source.Keyboard)
-        Truth.assertThat(transferableContent?.platformTransferableContent?.linkUri)
-            .isEqualTo(linkUri)
-        Truth.assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
+        assertThat(transferableContent?.source).isEqualTo(TransferableContent.Source.Keyboard)
+        assertThat(transferableContent?.platformTransferableContent?.linkUri).isEqualTo(linkUri)
+        assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
             .contains("key")
-        Truth.assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
+        assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
             .contains("EXTRA_INPUT_CONTENT_INFO")
 
         assertTrue(result)
@@ -289,26 +304,155 @@
                 extras
             )
 
-        Truth.assertThat(transferableContent).isNotNull()
-        Truth.assertThat(transferableContent?.clipEntry).isNotNull()
-        Truth.assertThat(transferableContent?.clipEntry?.firstUriOrNull()).isEqualTo(contentUri)
-        Truth.assertThat(transferableContent?.clipEntry?.clipData?.itemCount).isEqualTo(1)
-        Truth.assertThat(transferableContent?.clipMetadata?.clipDescription)
-            .isSameInstanceAs(description)
+        assertThat(transferableContent).isNotNull()
+        assertThat(transferableContent?.clipEntry).isNotNull()
+        assertThat(transferableContent?.clipEntry?.firstUriOrNull()).isEqualTo(contentUri)
+        assertThat(transferableContent?.clipEntry?.clipData?.itemCount).isEqualTo(1)
+        assertThat(transferableContent?.clipMetadata?.clipDescription).isSameInstanceAs(description)
 
-        Truth.assertThat(transferableContent?.source).isEqualTo(TransferableContent.Source.Keyboard)
-        Truth.assertThat(transferableContent?.platformTransferableContent?.linkUri)
-            .isEqualTo(linkUri)
-        Truth.assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
+        assertThat(transferableContent?.source).isEqualTo(TransferableContent.Source.Keyboard)
+        assertThat(transferableContent?.platformTransferableContent?.linkUri).isEqualTo(linkUri)
+        assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
             .contains("key")
         // Permissions do not exist below SDK 25
-        Truth.assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
+        assertThat(transferableContent?.platformTransferableContent?.extras?.keySet())
             .doesNotContain("EXTRA_INPUT_CONTENT_INFO")
 
         assertTrue(result)
     }
 
     @Test
+    fun setComposingText_appliesComposingSpans() {
+        var requestEditsCalled = 0
+        state = TextFieldState("hello ")
+        onRequestEdit = { block ->
+            requestEditsCalled++
+            state.editAsUser(
+                inputTransformation = null,
+                restartImeIfContentChanges = false,
+                block = block
+            )
+        }
+
+        ic.setComposingText(
+            SpannableStringBuilder().append("world").apply {
+                setSpan(BackgroundColorSpan(Color.RED), 0, 3, 0)
+                setSpan(BackgroundColorSpan(Color.BLUE), 3, 5, 0)
+                setSpan(UnderlineSpan(), 0, 5, 0)
+            },
+            1
+        )
+
+        assertThat(requestEditsCalled).isEqualTo(1)
+        assertThat(state.composition).isEqualTo(TextRange(6, 11))
+        assertThat(state.value.composingAnnotations).isNotNull()
+        assertThat(state.value.composingAnnotations)
+            .containsExactlyElementsIn(
+                listOf(
+                    AnnotatedString.Range(
+                        SpanStyle(background = androidx.compose.ui.graphics.Color.Red),
+                        6,
+                        9
+                    ),
+                    AnnotatedString.Range(
+                        SpanStyle(background = androidx.compose.ui.graphics.Color.Blue),
+                        9,
+                        11
+                    ),
+                    AnnotatedString.Range(
+                        SpanStyle(textDecoration = TextDecoration.Underline),
+                        6,
+                        11
+                    )
+                )
+            )
+    }
+
+    @Test
+    fun verify_backgroundColorSpan() {
+        val expected =
+            listOf(
+                AnnotatedString.Range(
+                    SpanStyle(background = androidx.compose.ui.graphics.Color.Red),
+                    0,
+                    1
+                )
+            )
+        val actual =
+            buildSpannableString(
+                    BackgroundColorSpan(androidx.compose.ui.graphics.Color.Red.toArgb())
+                )
+                .toAnnotationList()
+
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @Test
+    fun verify_foregroundColorSpan() {
+        val expected =
+            listOf(
+                AnnotatedString.Range(
+                    SpanStyle(color = androidx.compose.ui.graphics.Color.Red),
+                    0,
+                    1
+                )
+            )
+        val actual =
+            buildSpannableString(
+                    ForegroundColorSpan(androidx.compose.ui.graphics.Color.Red.toArgb())
+                )
+                .toAnnotationList()
+
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @Test
+    fun verify_strikeThroughSpan() {
+        val expected =
+            listOf(
+                AnnotatedString.Range(SpanStyle(textDecoration = TextDecoration.LineThrough), 0, 1)
+            )
+        val actual = buildSpannableString(StrikethroughSpan()).toAnnotationList()
+
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @Test
+    fun verify_styleSpan() {
+        val expected =
+            listOf(
+                AnnotatedString.Range(
+                    SpanStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic),
+                    0,
+                    1
+                )
+            )
+        val actual = buildSpannableString(StyleSpan(Typeface.BOLD_ITALIC)).toAnnotationList()
+
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @Test
+    fun verify_typefaceSpan() {
+        val expected =
+            listOf(AnnotatedString.Range(SpanStyle(fontFamily = FontFamily.Monospace), 0, 1))
+        val actual = buildSpannableString(TypefaceSpan("monospace")).toAnnotationList()
+
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @Test
+    fun verify_underlineSpan() {
+        val expected =
+            listOf(
+                AnnotatedString.Range(SpanStyle(textDecoration = TextDecoration.Underline), 0, 1)
+            )
+        val actual = buildSpannableString(UnderlineSpan()).toAnnotationList()
+
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @Test
     fun mixedAPICalls_batchSession() {
         var requestEditsCalled = 0
         onRequestEdit = { block ->
@@ -320,26 +464,26 @@
         // Do not callback to listener during batch session.
         ic.beginBatchEdit()
 
-        Truth.assertThat(ic.setComposingText("Hello, ", 1)).isTrue()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(ic.setComposingText("Hello, ", 1)).isTrue()
+        assertThat(requestEditsCalled).isEqualTo(0)
 
-        Truth.assertThat(ic.finishComposingText()).isTrue()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(ic.finishComposingText()).isTrue()
+        assertThat(requestEditsCalled).isEqualTo(0)
 
-        Truth.assertThat(ic.commitText("World.", 1)).isTrue()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(ic.commitText("World.", 1)).isTrue()
+        assertThat(requestEditsCalled).isEqualTo(0)
 
-        Truth.assertThat(ic.setSelection(0, 12)).isTrue()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(ic.setSelection(0, 12)).isTrue()
+        assertThat(requestEditsCalled).isEqualTo(0)
 
-        Truth.assertThat(ic.commitText("", 1)).isTrue()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(ic.commitText("", 1)).isTrue()
+        assertThat(requestEditsCalled).isEqualTo(0)
 
         ic.endBatchEdit()
 
-        Truth.assertThat(requestEditsCalled).isEqualTo(1)
-        Truth.assertThat(state.mainBuffer.toString()).isEqualTo(".")
-        Truth.assertThat(state.mainBuffer.selection).isEqualTo(TextRange(0))
+        assertThat(requestEditsCalled).isEqualTo(1)
+        assertThat(state.mainBuffer.toString()).isEqualTo(".")
+        assertThat(state.mainBuffer.selection).isEqualTo(TextRange(0))
     }
 
     @Test
@@ -356,7 +500,7 @@
         ic.beginBatchEdit()
         ic.getSelectedText(1)
         ic.endBatchEdit()
-        Truth.assertThat(requestEditsCalled).isEqualTo(0)
+        assertThat(requestEditsCalled).isEqualTo(0)
     }
 
     @Test
@@ -368,9 +512,9 @@
         ic.sendKeyEvent(keyEvent1)
         ic.sendKeyEvent(keyEvent2)
 
-        Truth.assertThat(keyEvents.size).isEqualTo(2)
-        Truth.assertThat(keyEvents.first()).isEqualTo(keyEvent1)
-        Truth.assertThat(keyEvents.last()).isEqualTo(keyEvent2)
+        assertThat(keyEvents.size).isEqualTo(2)
+        assertThat(keyEvents.first()).isEqualTo(keyEvent1)
+        assertThat(keyEvents.last()).isEqualTo(keyEvent2)
     }
 
     @Test
@@ -387,7 +531,7 @@
         ic.performEditorAction(EditorInfo.IME_ACTION_UNSPECIFIED)
         ic.performEditorAction(-1)
 
-        Truth.assertThat(receivedImeActions)
+        assertThat(receivedImeActions)
             .isEqualTo(
                 listOf(
                     ImeAction.Done,
@@ -414,8 +558,8 @@
 
         ic.performContextMenuAction(android.R.id.selectAll)
 
-        Truth.assertThat(callCount).isEqualTo(1)
-        Truth.assertThat(state.mainBuffer.selection).isEqualTo(TextRange(0, 5))
+        assertThat(callCount).isEqualTo(1)
+        assertThat(state.mainBuffer.selection).isEqualTo(TextRange(0, 5))
     }
 
     @Test
@@ -425,11 +569,11 @@
 
         ic.performContextMenuAction(android.R.id.cut)
 
-        Truth.assertThat(keyEvents.size).isEqualTo(2)
-        Truth.assertThat(keyEvents[0].action).isEqualTo(KeyEvent.ACTION_DOWN)
-        Truth.assertThat(keyEvents[0].keyCode).isEqualTo(KeyEvent.KEYCODE_CUT)
-        Truth.assertThat(keyEvents[1].action).isEqualTo(KeyEvent.ACTION_UP)
-        Truth.assertThat(keyEvents[1].keyCode).isEqualTo(KeyEvent.KEYCODE_CUT)
+        assertThat(keyEvents.size).isEqualTo(2)
+        assertThat(keyEvents[0].action).isEqualTo(KeyEvent.ACTION_DOWN)
+        assertThat(keyEvents[0].keyCode).isEqualTo(KeyEvent.KEYCODE_CUT)
+        assertThat(keyEvents[1].action).isEqualTo(KeyEvent.ACTION_UP)
+        assertThat(keyEvents[1].keyCode).isEqualTo(KeyEvent.KEYCODE_CUT)
     }
 
     @Test
@@ -439,11 +583,11 @@
 
         ic.performContextMenuAction(android.R.id.copy)
 
-        Truth.assertThat(keyEvents.size).isEqualTo(2)
-        Truth.assertThat(keyEvents[0].action).isEqualTo(KeyEvent.ACTION_DOWN)
-        Truth.assertThat(keyEvents[0].keyCode).isEqualTo(KeyEvent.KEYCODE_COPY)
-        Truth.assertThat(keyEvents[1].action).isEqualTo(KeyEvent.ACTION_UP)
-        Truth.assertThat(keyEvents[1].keyCode).isEqualTo(KeyEvent.KEYCODE_COPY)
+        assertThat(keyEvents.size).isEqualTo(2)
+        assertThat(keyEvents[0].action).isEqualTo(KeyEvent.ACTION_DOWN)
+        assertThat(keyEvents[0].keyCode).isEqualTo(KeyEvent.KEYCODE_COPY)
+        assertThat(keyEvents[1].action).isEqualTo(KeyEvent.ACTION_UP)
+        assertThat(keyEvents[1].keyCode).isEqualTo(KeyEvent.KEYCODE_COPY)
     }
 
     @Test
@@ -453,11 +597,11 @@
 
         ic.performContextMenuAction(android.R.id.paste)
 
-        Truth.assertThat(keyEvents.size).isEqualTo(2)
-        Truth.assertThat(keyEvents[0].action).isEqualTo(KeyEvent.ACTION_DOWN)
-        Truth.assertThat(keyEvents[0].keyCode).isEqualTo(KeyEvent.KEYCODE_PASTE)
-        Truth.assertThat(keyEvents[1].action).isEqualTo(KeyEvent.ACTION_UP)
-        Truth.assertThat(keyEvents[1].keyCode).isEqualTo(KeyEvent.KEYCODE_PASTE)
+        assertThat(keyEvents.size).isEqualTo(2)
+        assertThat(keyEvents[0].action).isEqualTo(KeyEvent.ACTION_DOWN)
+        assertThat(keyEvents[0].keyCode).isEqualTo(KeyEvent.KEYCODE_PASTE)
+        assertThat(keyEvents[1].action).isEqualTo(KeyEvent.ACTION_UP)
+        assertThat(keyEvents[1].keyCode).isEqualTo(KeyEvent.KEYCODE_PASTE)
     }
 
     @Test
@@ -469,4 +613,7 @@
                 "been there. Just remember to turn it off before you deploy your code."
         )
     }
+
+    private fun buildSpannableString(span: Any) =
+        SpannableStringBuilder().also { it.append("a", span, Spanned.SPAN_INCLUSIVE_INCLUSIVE) }
 }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt
index 8a89478..378855c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCacheTest.kt
@@ -28,6 +28,8 @@
 import androidx.compose.runtime.snapshots.SnapshotStateObserver
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.createFontFamilyResolver
@@ -340,7 +342,7 @@
         val expectedVisualText = "i"
 
         fun assertVisualText() {
-            Truth.assertThat(cache.value?.layoutInput?.text?.text).isEqualTo(expectedVisualText)
+            assertThat(cache.value?.layoutInput?.text?.text).isEqualTo(expectedVisualText)
         }
 
         updateNonMeasureInputs()
@@ -364,7 +366,7 @@
             ) {
                 assertVisualText()
             }
-            Truth.assertThat(transformationInvocations).isEqualTo(1)
+            assertThat(transformationInvocations).isEqualTo(1)
 
             // This should be a full cache hit.
             secondaryObserver.observeReads(
@@ -376,7 +378,7 @@
             ) {
                 assertVisualText()
             }
-            Truth.assertThat(transformationInvocations).isEqualTo(1)
+            assertThat(transformationInvocations).isEqualTo(1)
 
             // Invalidate the transformation.
             transformationState++
@@ -386,9 +388,9 @@
         }
 
         assertVisualText()
-        Truth.assertThat(transformationInvocations).isEqualTo(2)
-        Truth.assertThat(primaryInvalidations).isEqualTo(1)
-        Truth.assertThat(secondaryInvalidations).isEqualTo(1)
+        assertThat(transformationInvocations).isEqualTo(2)
+        assertThat(primaryInvalidations).isEqualTo(1)
+        assertThat(secondaryInvalidations).isEqualTo(1)
     }
 
     @FlakyTest(bugId = 299662404)
@@ -412,7 +414,7 @@
         var expectedVisualText = "i"
 
         fun assertVisualText() {
-            Truth.assertThat(cache.value?.layoutInput?.text?.text).isEqualTo(expectedVisualText)
+            assertThat(cache.value?.layoutInput?.text?.text).isEqualTo(expectedVisualText)
         }
 
         updateNonMeasureInputs()
@@ -436,7 +438,7 @@
             ) {
                 assertVisualText()
             }
-            Truth.assertThat(transformationInvocations).isEqualTo(1)
+            assertThat(transformationInvocations).isEqualTo(1)
 
             // This should be a full cache hit.
             secondaryObserver.observeReads(
@@ -448,7 +450,7 @@
             ) {
                 assertVisualText()
             }
-            Truth.assertThat(transformationInvocations).isEqualTo(1)
+            assertThat(transformationInvocations).isEqualTo(1)
 
             // Invalidate the transformation.
             expectedVisualText = "j"
@@ -460,9 +462,9 @@
 
         assertVisualText()
         // Two more reads means two more applications of the transformation.
-        Truth.assertThat(transformationInvocations).isEqualTo(2)
-        Truth.assertThat(primaryInvalidations).isEqualTo(1)
-        Truth.assertThat(secondaryInvalidations).isEqualTo(1)
+        assertThat(transformationInvocations).isEqualTo(2)
+        assertThat(primaryInvalidations).isEqualTo(1)
+        assertThat(secondaryInvalidations).isEqualTo(1)
     }
 
     @Test
@@ -479,8 +481,8 @@
                 }
             },
         ) { old, new ->
-            Truth.assertThat(old.layoutInput.text.text).isEqualTo("h")
-            Truth.assertThat(new.layoutInput.text.text).isEqualTo("hello")
+            assertThat(old.layoutInput.text.text).isEqualTo("h")
+            assertThat(new.layoutInput.text.text).isEqualTo("hello")
         }
     }
 
@@ -495,13 +497,13 @@
                 textFieldState.editAsUser(inputTransformation = null) { setComposingRegion(2, 3) }
             },
         ) { old, new ->
-            Truth.assertThat(
+            assertThat(
                     old.multiParagraph.intrinsics.annotatedString.spanStyles.any {
                         it.item.textDecoration == TextDecoration.Underline
                     }
                 )
                 .isFalse()
-            Truth.assertThat(
+            assertThat(
                     new.multiParagraph.intrinsics.annotatedString.spanStyles.any {
                         it.item.textDecoration == TextDecoration.Underline
                     }
@@ -526,6 +528,62 @@
     }
 
     @Test
+    fun value_returnsNewLayout_whenComposingAnnotationsChanged() {
+        textFieldState.editAsUser(inputTransformation = null) {
+            replace(0, length, "hello")
+            setComposition(
+                0,
+                5,
+                listOf(AnnotatedString.Range(SpanStyle(background = Color.Blue), 0, 5))
+            )
+        }
+        // change composing region but not the annotations.
+        assertLayoutChange(
+            change = {
+                textFieldState.editAsUser(inputTransformation = null) {
+                    setComposition(
+                        0,
+                        5,
+                        listOf(AnnotatedString.Range(SpanStyle(background = Color.Red), 0, 5))
+                    )
+                }
+            },
+        ) { old, new ->
+            assertThat(
+                    old.multiParagraph.intrinsics.annotatedString.spanStyles.any {
+                        it.item.background == Color.Blue
+                    }
+                )
+                .isTrue()
+            assertThat(
+                    new.multiParagraph.intrinsics.annotatedString.spanStyles.any {
+                        it.item.background == Color.Red
+                    }
+                )
+                .isTrue()
+        }
+    }
+
+    @Test
+    fun value_returnsCachedLayout_whenComposingAnnotationsDoNotChange() {
+        textFieldState.editAsUser(inputTransformation = null) {
+            replace(0, length, "hello")
+            setSelection(0, 0)
+            setComposition(
+                0,
+                5,
+                listOf(AnnotatedString.Range(SpanStyle(background = Color.Red), 0, 5))
+            )
+        }
+        updateNonMeasureInputs()
+        updateMeasureInputs()
+        val initialLayout = cache.value
+        // this shouldn't cause a recompute
+        val secondLayout = cache.value
+        assertThat(initialLayout).isSameInstanceAs(secondLayout)
+    }
+
+    @Test
     fun value_returnsCachedLayout_whenTextSelectionChanged() {
         textFieldState.edit {
             replace(0, length, "hello")
@@ -534,7 +592,7 @@
         assertLayoutChange(change = { textFieldState.edit { placeCursorBeforeCharAt(1) } }) {
             old,
             new ->
-            Truth.assertThat(new).isSameInstanceAs(old)
+            assertThat(new).isSameInstanceAs(old)
         }
     }
 
@@ -560,8 +618,8 @@
                 updateNonMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(old.layoutInput.text.text).isEqualTo("h")
-            Truth.assertThat(new.layoutInput.text.text).isEqualTo("i")
+            assertThat(old.layoutInput.text.text).isEqualTo("h")
+            assertThat(new.layoutInput.text.text).isEqualTo("i")
         }
     }
 
@@ -587,7 +645,7 @@
                 updateNonMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(new).isSameInstanceAs(old)
+            assertThat(new).isSameInstanceAs(old)
         }
     }
 
@@ -600,9 +658,9 @@
                 updateNonMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(old.layoutInput.style.fontSize).isEqualTo(12.sp)
-            Truth.assertThat(new.layoutInput.style.fontSize).isEqualTo(23.sp)
-            Truth.assertThat(old.multiParagraph).isNotSameInstanceAs(new.multiParagraph)
+            assertThat(old.layoutInput.style.fontSize).isEqualTo(12.sp)
+            assertThat(new.layoutInput.style.fontSize).isEqualTo(23.sp)
+            assertThat(old.multiParagraph).isNotSameInstanceAs(new.multiParagraph)
         }
     }
 
@@ -616,7 +674,7 @@
             }
         ) { old, new ->
             // TextLayoutInput needs to change. We only care whether multiParagraph is reused.
-            Truth.assertThat(new.multiParagraph).isSameInstanceAs(old.multiParagraph)
+            assertThat(new.multiParagraph).isSameInstanceAs(old.multiParagraph)
         }
     }
 
@@ -628,9 +686,9 @@
                 updateNonMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(new).isNotSameInstanceAs(old)
-            Truth.assertThat(old.layoutInput.maxLines).isEqualTo(Int.MAX_VALUE)
-            Truth.assertThat(new.layoutInput.maxLines).isEqualTo(1)
+            assertThat(new).isNotSameInstanceAs(old)
+            assertThat(old.layoutInput.maxLines).isEqualTo(Int.MAX_VALUE)
+            assertThat(new.layoutInput.maxLines).isEqualTo(1)
         }
     }
 
@@ -642,8 +700,8 @@
                 updateNonMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(old.layoutInput.softWrap).isEqualTo(!softWrap)
-            Truth.assertThat(new.layoutInput.softWrap).isEqualTo(softWrap)
+            assertThat(old.layoutInput.softWrap).isEqualTo(!softWrap)
+            assertThat(new.layoutInput.softWrap).isEqualTo(softWrap)
         }
     }
 
@@ -663,7 +721,7 @@
                 updateMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(new).isNotSameInstanceAs(old)
+            assertThat(new).isNotSameInstanceAs(old)
         }
     }
 
@@ -682,7 +740,7 @@
                 updateMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(new).isNotSameInstanceAs(old)
+            assertThat(new).isNotSameInstanceAs(old)
         }
     }
 
@@ -695,7 +753,7 @@
                 updateMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(new).isSameInstanceAs(old)
+            assertThat(new).isSameInstanceAs(old)
         }
     }
 
@@ -708,8 +766,8 @@
                 updateMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(old.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Ltr)
-            Truth.assertThat(new.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
+            assertThat(old.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Ltr)
+            assertThat(new.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
         }
     }
 
@@ -722,7 +780,7 @@
                 updateMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(new).isNotSameInstanceAs(old)
+            assertThat(new).isNotSameInstanceAs(old)
         }
     }
 
@@ -732,7 +790,7 @@
         fontFamilyResolver =
             createFontFamilyResolver(InstrumentationRegistry.getInstrumentation().context)
         assertLayoutChange(change = { TODO("b/294443266: make fonts stale") }) { old, new ->
-            Truth.assertThat(new).isNotSameInstanceAs(old)
+            assertThat(new).isNotSameInstanceAs(old)
         }
     }
 
@@ -745,8 +803,8 @@
                 updateMeasureInputs()
             }
         ) { old, new ->
-            Truth.assertThat(old.layoutInput.constraints).isEqualTo(Constraints.fixed(5, 5))
-            Truth.assertThat(new.layoutInput.constraints).isEqualTo(Constraints.fixed(6, 5))
+            assertThat(old.layoutInput.constraints).isEqualTo(Constraints.fixed(5, 5))
+            assertThat(new.layoutInput.constraints).isEqualTo(Constraints.fixed(6, 5))
         }
     }
 
@@ -764,25 +822,21 @@
                 updateMeasureInputs()
 
                 val newLayout = cache.value!!
-                Truth.assertThat(initialLayout.layoutInput.layoutDirection)
-                    .isEqualTo(LayoutDirection.Ltr)
-                Truth.assertThat(newLayout.layoutInput.layoutDirection)
-                    .isEqualTo(LayoutDirection.Rtl)
-                Truth.assertThat(cache.value!!).isSameInstanceAs(newLayout)
+                assertThat(initialLayout.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Ltr)
+                assertThat(newLayout.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
+                assertThat(cache.value!!).isSameInstanceAs(newLayout)
             }
 
             // Not visible in parent yet.
-            Truth.assertThat(initialLayout.layoutInput.layoutDirection)
-                .isEqualTo(LayoutDirection.Ltr)
-            Truth.assertThat(cache.value!!).isSameInstanceAs(initialLayout)
+            assertThat(initialLayout.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Ltr)
+            assertThat(cache.value!!).isSameInstanceAs(initialLayout)
             snapshot.apply().check()
 
             // Now visible in parent.
             val newLayout = cache.value!!
-            Truth.assertThat(initialLayout.layoutInput.layoutDirection)
-                .isEqualTo(LayoutDirection.Ltr)
-            Truth.assertThat(newLayout.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
-            Truth.assertThat(cache.value!!).isSameInstanceAs(newLayout)
+            assertThat(initialLayout.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Ltr)
+            assertThat(newLayout.layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
+            assertThat(cache.value!!).isSameInstanceAs(newLayout)
         } finally {
             snapshot.dispose()
         }
@@ -805,26 +859,26 @@
                 layoutDirection = LayoutDirection.Rtl
                 updateMeasureInputs()
                 with(cache.value!!) {
-                    Truth.assertThat(layoutInput.softWrap).isEqualTo(false)
-                    Truth.assertThat(layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
-                    Truth.assertThat(cache.value!!).isSameInstanceAs(this)
+                    assertThat(layoutInput.softWrap).isEqualTo(false)
+                    assertThat(layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
+                    assertThat(cache.value!!).isSameInstanceAs(this)
                 }
             }
 
             // Parent only sees its update.
             with(cache.value!!) {
-                Truth.assertThat(layoutInput.softWrap).isEqualTo(true)
-                Truth.assertThat(layoutInput.layoutDirection).isEqualTo(LayoutDirection.Ltr)
-                Truth.assertThat(this).isSameInstanceAs(initialLayout)
-                Truth.assertThat(cache.value!!).isSameInstanceAs(this)
+                assertThat(layoutInput.softWrap).isEqualTo(true)
+                assertThat(layoutInput.layoutDirection).isEqualTo(LayoutDirection.Ltr)
+                assertThat(this).isSameInstanceAs(initialLayout)
+                assertThat(cache.value!!).isSameInstanceAs(this)
             }
             snapshot.apply().check()
 
             // Cache should now reflect merged inputs.
             with(cache.value!!) {
-                Truth.assertThat(layoutInput.softWrap).isEqualTo(true)
-                Truth.assertThat(layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
-                Truth.assertThat(cache.value!!).isSameInstanceAs(this)
+                assertThat(layoutInput.softWrap).isEqualTo(true)
+                assertThat(layoutInput.layoutDirection).isEqualTo(LayoutDirection.Rtl)
+                assertThat(cache.value!!).isSameInstanceAs(this)
             }
         } finally {
             snapshot.dispose()
@@ -846,23 +900,22 @@
         snapshot.enter {
             with(cache.value!!) {
                 layoutFromSnapshot = this
-                Truth.assertThat(initialLayout.layoutInput.maxLines).isEqualTo(1)
-                Truth.assertThat(layoutInput.maxLines).isEqualTo(Int.MAX_VALUE)
+                assertThat(initialLayout.layoutInput.maxLines).isEqualTo(1)
+                assertThat(layoutInput.maxLines).isEqualTo(Int.MAX_VALUE)
             }
         }
 
         val finalLayout = cache.value!!
 
-        Truth.assertThat(initialLayout.multiParagraph)
+        assertThat(initialLayout.multiParagraph)
             .isNotSameInstanceAs(layoutFromSnapshot.multiParagraph)
 
         // Even though the initial text layout calculation after TextStyle change was done in a
         // read-only snapshot, we still expect to get the same MultiParagraph instance when called
         // with the same measure/non-measure arguments.
-        Truth.assertThat(finalLayout.multiParagraph)
-            .isSameInstanceAs(layoutFromSnapshot.multiParagraph)
+        assertThat(finalLayout.multiParagraph).isSameInstanceAs(layoutFromSnapshot.multiParagraph)
 
-        Truth.assertThat(finalLayout.layoutInput).isEqualTo(layoutFromSnapshot.layoutInput)
+        assertThat(finalLayout.layoutInput).isEqualTo(layoutFromSnapshot.layoutInput)
     }
 
     private fun assertLayoutChange(
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt
index 1f060f6..7a25e7e 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/TextInputServiceAndroidCursorAnchorInfoTest.kt
@@ -371,6 +371,8 @@
 
         override fun localToWindow(relativeToLocal: Offset): Offset = relativeToLocal + windowOffset
 
+        override fun localToScreen(relativeToLocal: Offset): Offset = relativeToLocal + windowOffset
+
         override fun localToRoot(relativeToLocal: Offset): Offset = relativeToLocal
 
         override fun localPositionOf(
@@ -384,8 +386,6 @@
         ): Rect =
             Rect(localPositionOf(sourceCoordinates, Offset.Zero), sourceCoordinates.size.toSize())
 
-        override fun transformToScreen(matrix: Matrix) {
-            matrix.translate(windowOffset.x, windowOffset.y, 0f)
-        }
+        override fun transformFrom(sourceCoordinates: LayoutCoordinates, matrix: Matrix) {}
     }
 }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt
index 0f29ba5..764705d 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldClickToMoveCursorTest.kt
@@ -33,6 +33,7 @@
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalWindowInfo
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -267,13 +268,18 @@
             Dialog(
                 onDismissRequest = { show.value = false },
                 content = {
+                    val isWindowFocused = LocalWindowInfo.current.isWindowFocused
                     BasicTextField(
                         state = state,
                         textStyle = defaultTextStyle,
                         modifier =
                             Modifier.fillMaxWidth().testTag(TAG).focusRequester(focusRequester),
                     )
-                    LaunchedEffect(Unit) { focusRequester.requestFocus() }
+                    LaunchedEffect(isWindowFocused) {
+                        if (isWindowFocused) {
+                            focusRequester.requestFocus()
+                        }
+                    }
                 }
             )
         }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
index aad428a..9fd4053 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldCursorHandleTest.kt
@@ -47,7 +47,7 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.click
-import androidx.compose.ui.test.hasSetTextAction
+import androidx.compose.ui.test.hasPerformImeAction
 import androidx.compose.ui.test.isDisplayed
 import androidx.compose.ui.test.isNotDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -971,7 +971,7 @@
     }
 
     private fun focusAndWait() {
-        rule.onNode(hasSetTextAction()).requestFocus()
+        rule.onNode(hasPerformImeAction()).requestFocus()
     }
 
     private fun swipeToLeft(swipeDistance: Float, durationMillis: Long = 1000) =
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/matchers/BitmapSubject.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/matchers/BitmapSubject.kt
index bebdcbd..a1e4a61 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/matchers/BitmapSubject.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/matchers/BitmapSubject.kt
@@ -56,7 +56,7 @@
 
     override fun actualCustomStringRepresentation(): String {
         return if (subject != null) {
-            "($subject ${subject.width}x${subject.height} ${subject.config.name})"
+            "($subject ${subject.width}x${subject.height} ${subject.config!!.name})"
         } else {
             super.actualCustomStringRepresentation()
         }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
index ea33294..90ccd70 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
@@ -58,6 +58,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.testutils.assertPixelColor
 import androidx.compose.testutils.assertShape
+import androidx.compose.testutils.expectError
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
@@ -449,7 +450,7 @@
     }
 
     @Test
-    fun semantics_setTextAction_doesNothingWhenReadOnly() {
+    fun semantics_setTextAction_throwsAssertionErrorWhenReadOnly() {
         rule.setContent {
             var value by remember { mutableStateOf("") }
             BasicTextField(
@@ -460,7 +461,9 @@
             )
         }
 
-        rule.onNodeWithTag(Tag).performTextReplacement("hello")
+        expectError<AssertionError>(expectedMessage = "Failed to perform text input.*") {
+            rule.onNodeWithTag(Tag).performTextReplacement("hello")
+        }
         rule.onNodeWithTag(Tag).assertEditableTextEquals("")
     }
 
@@ -480,7 +483,7 @@
     }
 
     @Test
-    fun semantics_insertTextAction_doesNothingWhenReadOnly() {
+    fun semantics_insertTextAction_throwsAssertionErrorWhenReadOnly() {
         rule.setContent {
             var value by remember { mutableStateOf("") }
             BasicTextField(
@@ -491,7 +494,10 @@
             )
         }
 
-        rule.onNodeWithTag(Tag).performTextInput("hello")
+        expectError<AssertionError>(expectedMessage = "Failed to perform text input.*") {
+            rule.onNodeWithTag(Tag).performTextInput("hello")
+        }
+
         rule.onNodeWithTag(Tag).assertEditableTextEquals("")
     }
 
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.android.kt
index e30885c..a9127f3 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.android.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.geometry.center
+import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.NativeCanvas
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
@@ -431,7 +432,7 @@
     private val density: Density,
     overscrollConfig: OverscrollConfiguration
 ) : OverscrollEffect {
-    private var pointerPosition: Offset? = null
+    private var pointerPosition: Offset = Offset.Unspecified
 
     private val edgeEffectWrapper =
         EdgeEffectWrapper(context, glowColor = overscrollConfig.glowColor.toArgb())
@@ -679,11 +680,11 @@
         }
     }
 
-    private var pointerId: PointerId? = null
+    private var pointerId: PointerId = PointerId(-1L)
 
     /** @return displacement based on the last [pointerPosition] and [containerSize] */
     internal fun displacement(): Offset {
-        val pointer = pointerPosition ?: containerSize.center
+        val pointer = if (pointerPosition.isSpecified) pointerPosition else containerSize.center
         val x = pointer.x / containerSize.width
         val y = pointer.y / containerSize.height
         return Offset(x, y)
@@ -708,7 +709,7 @@
                             pointerPosition = change.position
                         }
                     } while (pressedChanges.isNotEmpty())
-                    pointerId = null
+                    pointerId = PointerId(-1L)
                     // Explicitly not resetting the pointer position until the next down, so we
                     // don't change any existing effects
                 }
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/EdgeEffectCompat.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/EdgeEffectCompat.android.kt
index f078ec9..d05c236 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/EdgeEffectCompat.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/EdgeEffectCompat.android.kt
@@ -21,7 +21,6 @@
 import android.util.AttributeSet
 import android.view.ViewConfiguration
 import android.widget.EdgeEffect
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.dp
@@ -156,7 +155,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object Api31Impl {
-    @DoNotInline
     fun create(context: Context, attrs: AttributeSet?): EdgeEffect {
         return try {
             EdgeEffect(context, attrs)
@@ -165,7 +163,6 @@
         }
     }
 
-    @DoNotInline
     fun onPullDistance(edgeEffect: EdgeEffect, deltaDistance: Float, displacement: Float): Float {
         return try {
             edgeEffect.onPullDistance(deltaDistance, displacement)
@@ -175,7 +172,6 @@
         }
     }
 
-    @DoNotInline
     fun getDistance(edgeEffect: EdgeEffect): Float {
         return try {
             edgeEffect.getDistance()
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSession.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSession.android.kt
index bd6770d..620e0af 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSession.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/AndroidTextInputSession.android.kt
@@ -197,7 +197,13 @@
     }
 }
 
-private val ALL_MIME_TYPES = arrayOf("*/*")
+/**
+ * Even though [star/star] should be enough to cover all cases, some IMEs do not like when it's the
+ * only mime type that's declared as supported. IMEs claim that they do not have the necessary
+ * explicit information that the editor will support image or video content. Instead we also add
+ * those types specifically to make sure that IMEs can send everything.
+ */
+private val ALL_MIME_TYPES = arrayOf("*/*", "image/*", "video/*")
 
 private fun logDebug(tag: String = TIA_TAG, content: () -> String) {
     if (TIA_DEBUG) {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt
index 52f2c36..b183166 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/CursorAnchorInfoController.android.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Matrix
 import androidx.compose.ui.graphics.setFrom
+import androidx.compose.ui.layout.transformToScreen
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Job
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt
index 4d90603..0f091cf 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/EditorInfo.android.kt
@@ -26,7 +26,6 @@
 import android.view.inputmethod.RemoveSpaceGesture
 import android.view.inputmethod.SelectGesture
 import android.view.inputmethod.SelectRangeGesture
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.foundation.text.handwriting.isStylusHandwritingSupported
 import androidx.compose.ui.text.TextRange
@@ -158,7 +157,6 @@
 @RequiresApi(24)
 internal object LocaleListHelper {
     @RequiresApi(24)
-    @DoNotInline
     fun setHintLocales(editorInfo: EditorInfo, localeList: LocaleList) {
         when (localeList) {
             LocaleList.Empty -> {
@@ -174,7 +172,6 @@
 
 @RequiresApi(34)
 private object EditorInfoApi34 {
-    @DoNotInline
     fun setHandwritingGestures(editorInfo: EditorInfo) {
         editorInfo.supportedHandwritingGestures =
             listOf(
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt
index 0d04cd0..3653f27 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/HandwritingGesture.android.kt
@@ -28,7 +28,6 @@
 import android.view.inputmethod.RemoveSpaceGesture
 import android.view.inputmethod.SelectGesture
 import android.view.inputmethod.SelectRangeGesture
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.foundation.text.LegacyTextFieldState
 import androidx.compose.foundation.text.input.TextHighlightType
@@ -55,7 +54,6 @@
 
 @RequiresApi(34)
 internal object HandwritingGestureApi34 {
-    @DoNotInline
     internal fun TransformedTextFieldState.performHandwritingGesture(
         handwritingGesture: HandwritingGesture,
         layoutState: TextLayoutState,
@@ -76,7 +74,6 @@
         }
     }
 
-    @DoNotInline
     internal fun TransformedTextFieldState.previewHandwritingGesture(
         handwritingGesture: PreviewableHandwritingGesture,
         layoutState: TextLayoutState,
@@ -93,7 +90,6 @@
         return true
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performSelectGesture(
         gesture: SelectGesture,
         layoutState: TextLayoutState
@@ -112,7 +108,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.previewSelectGesture(
         gesture: SelectGesture,
         layoutState: TextLayoutState
@@ -127,7 +122,6 @@
         )
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performDeleteGesture(
         gesture: DeleteGesture,
         layoutState: TextLayoutState
@@ -149,7 +143,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.previewDeleteGesture(
         gesture: DeleteGesture,
         layoutState: TextLayoutState
@@ -164,7 +157,6 @@
         )
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performSelectRangeGesture(
         gesture: SelectRangeGesture,
         layoutState: TextLayoutState
@@ -184,7 +176,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.previewSelectRangeGesture(
         gesture: SelectRangeGesture,
         layoutState: TextLayoutState
@@ -200,7 +191,6 @@
         )
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performDeleteRangeGesture(
         gesture: DeleteRangeGesture,
         layoutState: TextLayoutState
@@ -223,7 +213,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.previewDeleteRangeGesture(
         gesture: DeleteRangeGesture,
         layoutState: TextLayoutState
@@ -239,7 +228,6 @@
         )
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performJoinOrSplitGesture(
         gesture: JoinOrSplitGesture,
         layoutState: TextLayoutState,
@@ -274,7 +262,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performInsertGesture(
         gesture: InsertGesture,
         layoutState: TextLayoutState,
@@ -295,7 +282,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performRemoveSpaceGesture(
         gesture: RemoveSpaceGesture,
         layoutState: TextLayoutState,
@@ -345,7 +331,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.performDeletion(
         rangeInTransformedText: TextRange,
         adjustRange: Boolean
@@ -359,7 +344,6 @@
         replaceText("", finalRange)
     }
 
-    @DoNotInline
     private fun TransformedTextFieldState.fallback(gesture: HandwritingGesture): Int {
         editUntransformedTextAsUser { clearHighlight() }
 
@@ -384,7 +368,6 @@
         }
     }
 
-    @DoNotInline
     internal fun LegacyTextFieldState.performHandwritingGesture(
         gesture: HandwritingGesture,
         textFieldSelectionManager: TextFieldSelectionManager?,
@@ -413,7 +396,6 @@
         }
     }
 
-    @DoNotInline
     internal fun LegacyTextFieldState.previewHandwritingGesture(
         gesture: PreviewableHandwritingGesture,
         textFieldSelectionManager: TextFieldSelectionManager?,
@@ -437,7 +419,6 @@
         return true
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.performSelectGesture(
         gesture: SelectGesture,
         textSelectionManager: TextFieldSelectionManager?,
@@ -457,7 +438,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.previewSelectGesture(
         gesture: SelectGesture,
         textFieldSelectionManager: TextFieldSelectionManager?
@@ -471,7 +451,6 @@
         )
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.performDeleteGesture(
         gesture: DeleteGesture,
         text: AnnotatedString,
@@ -497,7 +476,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.previewDeleteGesture(
         gesture: DeleteGesture,
         textFieldSelectionManager: TextFieldSelectionManager?
@@ -511,7 +489,6 @@
         )
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.performSelectRangeGesture(
         gesture: SelectRangeGesture,
         textSelectionManager: TextFieldSelectionManager?,
@@ -536,7 +513,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.previewSelectRangeGesture(
         gesture: SelectRangeGesture,
         textFieldSelectionManager: TextFieldSelectionManager?
@@ -551,7 +527,6 @@
         )
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.performDeleteRangeGesture(
         gesture: DeleteRangeGesture,
         text: AnnotatedString,
@@ -577,7 +552,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.previewDeleteRangeGesture(
         gesture: DeleteRangeGesture,
         textFieldSelectionManager: TextFieldSelectionManager?
@@ -592,7 +566,6 @@
         )
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.performJoinOrSplitGesture(
         gesture: JoinOrSplitGesture,
         text: AnnotatedString,
@@ -628,7 +601,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.performInsertGesture(
         gesture: InsertGesture,
         viewConfiguration: ViewConfiguration?,
@@ -652,7 +624,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun LegacyTextFieldState.performRemoveSpaceGesture(
         gesture: RemoveSpaceGesture,
         text: AnnotatedString,
@@ -710,7 +681,6 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS
     }
 
-    @DoNotInline
     private fun performInsertionOnLegacyTextField(
         offset: Int,
         text: String,
@@ -721,7 +691,6 @@
         )
     }
 
-    @DoNotInline
     private fun performSelectionOnLegacyTextField(
         range: TextRange,
         textSelectionManager: TextFieldSelectionManager?,
@@ -731,7 +700,6 @@
         textSelectionManager?.enterSelectionMode(showFloatingToolbar = true)
     }
 
-    @DoNotInline
     private fun performDeletionOnLegacyTextField(
         range: TextRange,
         text: AnnotatedString,
@@ -756,7 +724,6 @@
         )
     }
 
-    @DoNotInline
     private fun fallbackOnLegacyTextField(
         gesture: HandwritingGesture,
         editCommandConsumer: (EditCommand) -> Unit
@@ -768,7 +735,6 @@
     }
 
     /** Convert the Platform text granularity to Compose [TextGranularity] object. */
-    @DoNotInline
     private fun Int.toTextGranularity(): TextGranularity {
         return when (this) {
             HandwritingGesture.GRANULARITY_CHARACTER -> TextGranularity.Character
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/InputMethodManager.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/InputMethodManager.android.kt
index 9fcfb95..5bc0131 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/InputMethodManager.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/InputMethodManager.android.kt
@@ -22,7 +22,6 @@
 import android.view.View
 import android.view.inputmethod.CursorAnchorInfo
 import android.view.inputmethod.ExtractedText
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.view.SoftwareKeyboardControllerCompat
 
@@ -107,7 +106,6 @@
 
 @RequiresApi(34)
 internal object Api34StartStylusHandwriting {
-    @DoNotInline
     fun startStylusHandwriting(imm: android.view.inputmethod.InputMethodManager, view: View) {
         imm.startStylusHandwriting(view)
     }
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt
index 902fefc..6e4923e 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyCursorAnchorInfoBuilder.android.kt
@@ -20,7 +20,6 @@
 import android.os.Build
 import android.view.inputmethod.CursorAnchorInfo
 import android.view.inputmethod.EditorBoundsInfo
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.toAndroidRectF
@@ -188,7 +187,6 @@
 @RequiresApi(33)
 internal object CursorAnchorInfoApi33Helper {
     @JvmStatic
-    @DoNotInline
     fun setEditorBoundsInfo(
         builder: CursorAnchorInfo.Builder,
         decorationBoxBounds: Rect
@@ -204,7 +202,6 @@
 @RequiresApi(34)
 internal object CursorAnchorInfoApi34Helper {
     @JvmStatic
-    @DoNotInline
     fun addVisibleLineBounds(
         builder: CursorAnchorInfo.Builder,
         textLayoutResult: TextLayoutResult,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt
index b26bda3..5e68e93 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.android.kt
@@ -29,6 +29,7 @@
 import androidx.compose.runtime.withFrameMillis
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.layout.transformToScreen
 import androidx.compose.ui.platform.PlatformTextInputMethodRequest
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.text.TextLayoutResult
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt
index f942faf..3b0b232 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/RecordingInputConnection.android.kt
@@ -32,7 +32,6 @@
 import android.view.inputmethod.InputConnection
 import android.view.inputmethod.InputContentInfo
 import android.view.inputmethod.PreviewableHandwritingGesture
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.foundation.text.LegacyTextFieldState
 import androidx.compose.foundation.text.input.internal.HandwritingGestureApi34.performHandwritingGesture
@@ -588,7 +587,6 @@
 @RequiresApi(34)
 private object Api34LegacyPerformHandwritingGestureImpl {
 
-    @DoNotInline
     fun performHandwritingGesture(
         legacyTextFieldState: LegacyTextFieldState?,
         textFieldSelectionManager: TextFieldSelectionManager?,
@@ -614,7 +612,6 @@
         }
     }
 
-    @DoNotInline
     fun previewHandwritingGesture(
         legacyTextFieldState: LegacyTextFieldState?,
         textFieldSelectionManager: TextFieldSelectionManager?,
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt
index e06977f..94694b3 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/StatelessInputConnection.android.kt
@@ -17,12 +17,20 @@
 package androidx.compose.foundation.text.input.internal
 
 import android.content.ClipData
+import android.graphics.Typeface
 import android.os.Build
 import android.os.Bundle
 import android.os.CancellationSignal
 import android.os.Handler
 import android.os.Parcelable
+import android.text.Spanned
 import android.text.TextUtils
+import android.text.style.BackgroundColorSpan
+import android.text.style.ForegroundColorSpan
+import android.text.style.StrikethroughSpan
+import android.text.style.StyleSpan
+import android.text.style.TypefaceSpan
+import android.text.style.UnderlineSpan
 import android.util.Log
 import android.view.KeyEvent
 import android.view.inputmethod.CompletionInfo
@@ -35,20 +43,27 @@
 import android.view.inputmethod.InputConnectionWrapper
 import android.view.inputmethod.InputContentInfo
 import android.view.inputmethod.PreviewableHandwritingGesture
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.content.PlatformTransferableContent
 import androidx.compose.foundation.content.TransferableContent
+import androidx.compose.foundation.text.input.PlacedAnnotation
 import androidx.compose.foundation.text.input.TextFieldCharSequence
 import androidx.compose.foundation.text.input.getSelectedText
 import androidx.compose.foundation.text.input.getTextAfterSelection
 import androidx.compose.foundation.text.input.getTextBeforeSelection
 import androidx.compose.runtime.collection.mutableVectorOf
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.toClipEntry
 import androidx.compose.ui.platform.toClipMetadata
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.style.TextDecoration
 import androidx.core.view.inputmethod.EditorInfoCompat
 import androidx.core.view.inputmethod.InputConnectionCompat
 import androidx.core.view.inputmethod.InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
@@ -243,6 +258,7 @@
 
     override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
         logDebug("commitText(\"$text\", $newCursorPosition)")
+        if (text == null) return true
         addEditCommandWithBatch { commitText(text.toString(), newCursorPosition) }
         return true
     }
@@ -255,7 +271,14 @@
 
     override fun setComposingText(text: CharSequence?, newCursorPosition: Int): Boolean {
         logDebug("setComposingText(\"$text\", $newCursorPosition)")
-        addEditCommandWithBatch { setComposingText(text.toString(), newCursorPosition) }
+        if (text == null) return true
+        addEditCommandWithBatch {
+            [email protected](
+                text = text.toString(),
+                newCursorPosition = newCursorPosition,
+                annotations = (text as? Spanned)?.toAnnotationList()
+            )
+        }
         return true
     }
 
@@ -509,7 +532,6 @@
 @RequiresApi(25)
 private object Api25CommitContentImpl {
 
-    @DoNotInline
     fun commitContent(
         inputConnection: InputConnection,
         inputContentInfo: InputContentInfo,
@@ -522,7 +544,6 @@
 
 @RequiresApi(34)
 private object Api34PerformHandwritingGestureImpl {
-    @DoNotInline
     fun performHandwritingGesture(
         session: TextInputSession,
         gesture: HandwritingGesture,
@@ -539,7 +560,6 @@
         }
     }
 
-    @DoNotInline
     fun previewHandwritingGesture(
         session: TextInputSession,
         gesture: PreviewableHandwritingGesture,
@@ -572,3 +592,98 @@
             PlatformTransferableContent(linkUri = linkUri, extras = extras ?: Bundle.EMPTY)
     )
 }
+
+@VisibleForTesting
+internal fun Spanned.toAnnotationList(): List<PlacedAnnotation>? {
+    var mutableAnnotationList: MutableList<PlacedAnnotation>? = null
+    val spans = getSpans(0, length, Any::class.java)
+    spans.forEach { span ->
+        span.toAnnotation()?.let { annotation ->
+            if (mutableAnnotationList == null) {
+                mutableAnnotationList = mutableListOf()
+            }
+            mutableAnnotationList?.add(
+                AnnotatedString.Range(
+                    item = annotation,
+                    start = getSpanStart(span),
+                    end = getSpanEnd(span)
+                )
+            )
+        }
+    }
+    return mutableAnnotationList
+}
+
+// The following functions were borrowed from ui-text module where they are currently private.
+private fun Any.toAnnotation(): AnnotatedString.Annotation? {
+    return when (this) {
+        is BackgroundColorSpan -> {
+            SpanStyle(background = Color(this.backgroundColor))
+        }
+        is ForegroundColorSpan -> {
+            SpanStyle(color = Color(this.foregroundColor))
+        }
+        is StrikethroughSpan -> {
+            SpanStyle(textDecoration = TextDecoration.LineThrough)
+        }
+        is StyleSpan -> {
+            this.toSpanStyle()
+        }
+        is TypefaceSpan -> {
+            this.toSpanStyle()
+        }
+        is UnderlineSpan -> {
+            SpanStyle(textDecoration = TextDecoration.Underline)
+        }
+        else -> null
+    }
+}
+
+private fun StyleSpan.toSpanStyle(): SpanStyle? {
+    /**
+     * StyleSpan doc: styles are cumulative -- if both bold and italic are set in separate spans, or
+     * if the base style is bold and a span calls for italic, you get bold italic. You can't turn
+     * off a style from the base style.
+     */
+    return when (style) {
+        Typeface.BOLD -> {
+            SpanStyle(fontWeight = FontWeight.Bold)
+        }
+        Typeface.ITALIC -> {
+            SpanStyle(fontStyle = FontStyle.Italic)
+        }
+        Typeface.BOLD_ITALIC -> {
+            SpanStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic)
+        }
+        else -> null
+    }
+}
+
+private fun TypefaceSpan.toSpanStyle(): SpanStyle {
+    val fontFamily =
+        when (family) {
+            FontFamily.Cursive.name -> FontFamily.Cursive
+            FontFamily.Monospace.name -> FontFamily.Monospace
+            FontFamily.SansSerif.name -> FontFamily.SansSerif
+            FontFamily.Serif.name -> FontFamily.Serif
+            else -> {
+                optionalFontFamilyFromName(family)
+            }
+        }
+    return SpanStyle(fontFamily = fontFamily)
+}
+
+/**
+ * Mirrors [androidx.compose.ui.text.font.PlatformTypefaces.optionalOnDeviceFontFamilyByName]
+ * behavior with both font weight and font style being Normal in this case
+ */
+private fun optionalFontFamilyFromName(familyName: String?): FontFamily? {
+    if (familyName.isNullOrEmpty()) return null
+    val typeface = Typeface.create(familyName, Typeface.NORMAL)
+    return typeface
+        .takeIf {
+            typeface != Typeface.DEFAULT &&
+                typeface != Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
+        }
+        ?.let { FontFamily(it) }
+}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.android.kt
index edce5b5..b365b31 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.android.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.text.input.internal
 
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.text.intl.PlatformLocale
 import androidx.compose.ui.text.style.TextDirection
@@ -44,7 +43,6 @@
 }
 
 private object DigitDirectionalityApi21 {
-    @DoNotInline
     fun resolve(locale: PlatformLocale): Byte {
         val symbols = java.text.DecimalFormatSymbols.getInstance(locale)
         val zero = symbols.zeroDigit
@@ -54,7 +52,6 @@
 
 @RequiresApi(Build.VERSION_CODES.N)
 private object DigitDirectionalityApi24 {
-    @DoNotInline
     fun resolve(locale: PlatformLocale): Byte {
         val symbols = android.icu.text.DecimalFormatSymbols.getInstance(locale)
         val zero = symbols.zeroDigit
@@ -64,7 +61,6 @@
 
 @RequiresApi(Build.VERSION_CODES.P)
 private object DigitDirectionalityApi28 {
-    @DoNotInline
     fun resolve(locale: PlatformLocale): Byte {
         val symbols = android.icu.text.DecimalFormatSymbols.getInstance(locale)
         val zero = symbols.digitStrings[0].codePointAt(0)
diff --git a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferChangeTrackingTest.kt b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferChangeTrackingTest.kt
index 6bf671d..110d2d7 100644
--- a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferChangeTrackingTest.kt
+++ b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferChangeTrackingTest.kt
@@ -81,6 +81,30 @@
     }
 
     @Test
+    fun tailInsertionAtTheEnd_reportedAsFullReplace_coercesToInsertion() {
+        val eb = EditingBuffer("abc", TextRange.Zero)
+
+        eb.replace(0, 3, "abcd")
+
+        assertThat(eb.toString()).isEqualTo("abcd")
+        assertThat(eb.changeTracker.changeCount).isEqualTo(1)
+        assertThat(eb.changeTracker.getRange(0)).isEqualTo(TextRange(3, 4))
+        assertThat(eb.changeTracker.getOriginalRange(0)).isEqualTo(TextRange(3))
+    }
+
+    @Test
+    fun tailInsertionAtTheEnd_reportedAsFullReplace_sameLastCharacter_coercesToInsertion() {
+        val eb = EditingBuffer("abc", TextRange.Zero)
+
+        eb.replace(0, 3, "abcc")
+
+        assertThat(eb.toString()).isEqualTo("abcc")
+        assertThat(eb.changeTracker.changeCount).isEqualTo(1)
+        assertThat(eb.changeTracker.getRange(0)).isEqualTo(TextRange(3, 4))
+        assertThat(eb.changeTracker.getOriginalRange(0)).isEqualTo(TextRange(3))
+    }
+
+    @Test
     fun tailDeletionReportedAsReplace_coercesToDeletion() {
         val eb = EditingBuffer("abcde", TextRange.Zero)
 
diff --git a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferTest.kt b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferTest.kt
index 0689506..4d0c59f 100644
--- a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferTest.kt
+++ b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/internal/EditingBufferTest.kt
@@ -16,9 +16,13 @@
 
 package androidx.compose.foundation.text.input.internal
 
+import androidx.compose.foundation.text.input.PlacedAnnotation
 import androidx.compose.foundation.text.input.TextHighlightType
 import androidx.compose.foundation.text.input.internal.matchers.assertThat
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.style.TextDecoration
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -250,6 +254,60 @@
     }
 
     @Test
+    fun setComposition_and_annotationList() {
+        val eb = EditingBuffer("ABCDE", TextRange.Zero)
+
+        val annotations: List<PlacedAnnotation> =
+            listOf(
+                AnnotatedString.Range(SpanStyle(textDecoration = TextDecoration.Underline), 0, 5)
+            )
+        eb.setComposition(0, 5, annotations)
+        assertThat(eb).hasChars("ABCDE")
+        assertThat(eb.cursor).isEqualTo(0)
+        assertThat(eb.selectionStart).isEqualTo(0)
+        assertThat(eb.selectionEnd).isEqualTo(0)
+        assertThat(eb.hasComposition()).isTrue()
+        assertThat(eb.compositionStart).isEqualTo(0)
+        assertThat(eb.compositionEnd).isEqualTo(5)
+        assertThat(eb.composingAnnotations?.size).isEqualTo(1)
+        assertThat(eb.composingAnnotations?.first()).isEqualTo(annotations.first())
+
+        eb.replace(2, 3, "X") // replace function cancel the composition text.
+        assertThat(eb).hasChars("ABXDE")
+        assertThat(eb.cursor).isEqualTo(3)
+        assertThat(eb.selectionStart).isEqualTo(3)
+        assertThat(eb.selectionEnd).isEqualTo(3)
+        assertThat(eb.hasComposition()).isFalse()
+        assertThat(eb.compositionStart).isEqualTo(-1)
+        assertThat(eb.compositionEnd).isEqualTo(-1)
+        assertThat(eb.composingAnnotations?.isEmpty()).isTrue()
+
+        eb.setComposition(2, 4) // set composition again
+        assertThat(eb).hasChars("ABXDE")
+        assertThat(eb.cursor).isEqualTo(3)
+        assertThat(eb.selectionStart).isEqualTo(3)
+        assertThat(eb.selectionEnd).isEqualTo(3)
+        assertThat(eb.hasComposition()).isTrue()
+        assertThat(eb.compositionStart).isEqualTo(2)
+        assertThat(eb.compositionEnd).isEqualTo(4)
+        assertThat(eb.composingAnnotations?.isEmpty()).isTrue()
+
+        eb.setComposition(0, 5, annotations)
+        assertThat(eb.composingAnnotations?.size).isEqualTo(1)
+        assertThat(eb.composingAnnotations?.first()).isEqualTo(annotations.first())
+
+        eb.commitComposition() // commit the composition
+        assertThat(eb).hasChars("ABXDE")
+        assertThat(eb.cursor).isEqualTo(3)
+        assertThat(eb.selectionStart).isEqualTo(3)
+        assertThat(eb.selectionEnd).isEqualTo(3)
+        assertThat(eb.hasComposition()).isFalse()
+        assertThat(eb.compositionStart).isEqualTo(-1)
+        assertThat(eb.compositionEnd).isEqualTo(-1)
+        assertThat(eb.composingAnnotations?.isEmpty()).isTrue()
+    }
+
+    @Test
     fun setCursor_and_get_cursor() {
         val eb = EditingBuffer("ABCDE", TextRange.Zero)
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
index a7a3274..8951c04 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
@@ -144,6 +144,7 @@
     private var lastLayoutDirection: LayoutDirection? = null
     private var lastOutline: Outline? = null
     private var lastShape: Shape? = null
+    private var tmpOutline: Outline? = null
 
     override fun ContentDrawScope.draw() {
         if (shape === RectangleShape) {
@@ -178,12 +179,15 @@
     }
 
     private fun ContentDrawScope.getOutline(): Outline {
-        var outline: Outline? = null
+        val outline: Outline?
         if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) {
             outline = lastOutline!!
         } else {
             // Manually observe reads so we can directly invalidate the outline when it changes
-            observeReads { outline = shape.createOutline(size, layoutDirection, this) }
+            // Use tmpOutline to avoid creating an object reference to local var outline
+            observeReads { tmpOutline = shape.createOutline(size, layoutDirection, this) }
+            outline = tmpOutline
+            tmpOutline = null
         }
         lastOutline = outline
         lastSize = size
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
index aea1f51..a3b1549 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
@@ -63,6 +63,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.unit.dp
+import kotlin.jvm.JvmInline
 import kotlin.math.absoluteValue
 import kotlin.math.ceil
 import kotlin.math.roundToInt
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
index 27d57fd..55adc64 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
@@ -216,7 +216,11 @@
             val parent = findBringIntoViewParent()
             if (parent != null) {
                 val layoutCoordinates = requireLayoutCoordinates()
-                coroutineScope.launch { parent.scrollIntoView(layoutCoordinates) }
+                coroutineScope.launch {
+                    if (isAttached) {
+                        parent.scrollIntoView(layoutCoordinates)
+                    }
+                }
             }
             val pinnableContainer = retrievePinnableContainer()
             pinnedHandle = pinnableContainer?.pin()
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt
index 8b177f3..066cb2a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt
@@ -57,13 +57,7 @@
  * lookups to build the exception message and stack trace collection. Remove if these are changed in
  * kotlinx.coroutines.
  */
-private class MutationInterruptedException : CancellationException("Mutation interrupted") {
-    override fun fillInStackTrace(): Throwable {
-        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
-        stackTrace = emptyArray()
-        return this
-    }
-}
+internal expect class MutationInterruptedException() : CancellationException
 
 /**
  * Mutual exclusion for UI state mutation over time.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt
index 5c12db2..29f7850 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.ui.platform.ClipEntry
 import androidx.compose.ui.platform.ClipMetadata
+import kotlin.jvm.JvmInline
 
 /**
  * Represents content that can be transferred between applications or processes.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
index 5ddc1e0..40708d7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
@@ -1174,9 +1174,9 @@
      */
     fun dispatchRawDelta(delta: Float): Float {
         val newOffset = newOffsetForDelta(delta)
-        val oldOffset = if (offset.isNaN()) 0f else offset
-        offset = newOffset
-        return newOffset - oldOffset
+        val consumedDelta = (newOffset - requireOffset())
+        anchoredDragScope.dragTo(newOffset)
+        return consumedDelta
     }
 
     /**
@@ -1489,12 +1489,7 @@
     return if (target > 0) coerceAtMost(target) else coerceAtLeast(target)
 }
 
-private class AnchoredDragFinishedSignal : CancellationException() {
-    override fun fillInStackTrace(): Throwable {
-        stackTrace = emptyArray()
-        return this
-    }
-}
+internal expect class AnchoredDragFinishedSignal() : CancellationException
 
 private suspend fun <I> restartable(inputs: () -> I, block: suspend (I) -> Unit) {
     try {
@@ -1527,7 +1522,7 @@
 ) : DraggableAnchors<T> {
 
     init {
-        assert(keys.size == anchors.size) {
+        assertOnJvm(keys.size == anchors.size) {
             "DraggableAnchors were constructed with " +
                 "inconsistent key-value sizes. Keys: $keys | Anchors: ${anchors.toList()}"
         }
@@ -1608,6 +1603,8 @@
     }
 }
 
+internal expect inline fun assertOnJvm(statement: Boolean, message: () -> String): Unit
+
 internal val AnchoredDraggableMinFlingVelocity = 125.dp
 
 private const val ConfigurationMovedToModifier =
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
index a0c19a9..9d07996 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
@@ -25,6 +25,7 @@
 //  functions public
 
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
@@ -230,6 +231,8 @@
     orientationLock: Orientation?,
     onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
 ) {
+    var overSlop: Offset
+
     awaitEachGesture {
         val initialDown = awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial)
         val awaitTouchSlop = shouldAwaitTouchSlop()
@@ -239,8 +242,8 @@
         }
         val down = awaitFirstDown(requireUnconsumed = false)
         var drag: PointerInputChange?
-        var overSlop = Offset.Zero
         var initialDelta = Offset.Zero
+        overSlop = Offset.Zero
 
         if (awaitTouchSlop) {
             do {
@@ -766,7 +769,7 @@
             }
         } else {
             val postSlopOffset = touchSlopDetector.addPointerInputChange(dragEvent, touchSlop)
-            if (postSlopOffset != null) {
+            if (postSlopOffset.isSpecified) {
                 onPointerSlopReached(dragEvent, postSlopOffset)
                 if (dragEvent.isConsumed) {
                     return dragEvent
@@ -803,7 +806,7 @@
      * provided by [touchSlop], this method will return the post slop offset, that is the total
      * accumulated delta change minus the touch slop value, otherwise this should return null.
      */
-    fun addPointerInputChange(dragEvent: PointerInputChange, touchSlop: Float): Offset? {
+    fun addPointerInputChange(dragEvent: PointerInputChange, touchSlop: Float): Offset {
         val currentPosition = dragEvent.position
         val previousPosition = dragEvent.previousPosition
         val positionChange = currentPosition - previousPosition
@@ -821,7 +824,7 @@
         return if (hasCrossedSlop) {
             calculatePostSlopOffset(touchSlop)
         } else {
-            null
+            Offset.Unspecified
         }
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
index 68632dc..f0490ff 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
@@ -489,7 +489,7 @@
                 val velocity =
                     velocityTracker.calculateVelocity(Velocity(maximumVelocity, maximumVelocity))
                 velocityTracker.resetTracking()
-                channel?.trySend(DragStopped(velocity))
+                channel?.trySend(DragStopped(velocity.toValidVelocity()))
             }
 
             val onDragCancel: () -> Unit = { channel?.trySend(DragCancelled) }
@@ -627,5 +627,8 @@
 private fun Velocity.toFloat(orientation: Orientation) =
     if (orientation == Orientation.Vertical) this.y else this.x
 
+private fun Velocity.toValidVelocity() =
+    Velocity(if (this.x.isNaN()) 0f else this.x, if (this.y.isNaN()) 0f else this.y)
+
 private val NoOpOnDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {}
 private val NoOpOnDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {}
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 5a8b7f5..ed11cd6 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
@@ -76,6 +76,7 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.util.fastAll
+import androidx.compose.ui.util.fastAny
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.abs
 import kotlinx.coroutines.CancellationException
@@ -468,7 +469,9 @@
         pass: PointerEventPass,
         bounds: IntSize
     ) {
-        super.onPointerEvent(pointerEvent, pass, bounds)
+        if (pointerEvent.changes.fastAny { canDrag.invoke(it) }) {
+            super.onPointerEvent(pointerEvent, pass, bounds)
+        }
         if (pass == PointerEventPass.Main && pointerEvent.type == PointerEventType.Scroll) {
             processMouseWheelEvent(pointerEvent, bounds)
         }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
index db66f42..e7eb673 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
@@ -33,6 +33,8 @@
 import androidx.compose.ui.util.fastAll
 import androidx.compose.ui.util.fastAny
 import androidx.compose.ui.util.fastForEach
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.sync.Mutex
@@ -85,6 +87,7 @@
  * If the first down event is consumed somewhere else, the entire gesture will be skipped, including
  * [onPress].
  */
+@OptIn(ExperimentalTapGestureDetectorBehaviorApi::class)
 suspend fun PointerInputScope.detectTapGestures(
     onDoubleTap: ((Offset) -> Unit)? = null,
     onLongPress: ((Offset) -> Unit)? = null,
@@ -94,30 +97,50 @@
     // special signal to indicate to the sending side that it shouldn't intercept and consume
     // cancel/up events as we're only require down events
     val pressScope = PressGestureScopeImpl(this@detectTapGestures)
-
     awaitEachGesture {
         val down = awaitFirstDown()
         down.consume()
-        launch { pressScope.reset() }
+        var resetJob =
+            launch(start = coroutineStartForCurrentDispatchBehavior) { pressScope.reset() }
+        // In some cases, coroutine cancellation of the reset job might still be processing when we
+        // are already processing an up or cancel pointer event. We need to wait for the reset job
+        // to cancel and complete so it can clean up properly (e.g. unlock the underlying mutex)
+        suspend fun awaitResetOrSkip() {
+            if (DetectTapGesturesEnableNewDispatchingBehavior) {
+                resetJob.join()
+            }
+        }
         if (onPress !== NoPressGesture) launch { pressScope.onPress(down.position) }
         val longPressTimeout =
             onLongPress?.let { viewConfiguration.longPressTimeoutMillis } ?: (Long.MAX_VALUE / 2)
         var upOrCancel: PointerInputChange? = null
+        var cancelOrReleaseJob: Job?
         try {
             // wait for first tap up or long press
             upOrCancel = withTimeout(longPressTimeout) { waitForUpOrCancellation() }
             if (upOrCancel == null) {
-                launch {
-                    pressScope.cancel() // tap-up was canceled
-                }
+                cancelOrReleaseJob =
+                    launch(start = coroutineStartForCurrentDispatchBehavior) {
+                        awaitResetOrSkip()
+                        // tap-up was canceled
+                        pressScope.cancel()
+                    }
             } else {
                 upOrCancel.consume()
-                launch { pressScope.release() }
+                cancelOrReleaseJob =
+                    launch(start = coroutineStartForCurrentDispatchBehavior) {
+                        awaitResetOrSkip()
+                        pressScope.release()
+                    }
             }
         } catch (_: PointerEventTimeoutCancellationException) {
             onLongPress?.invoke(down.position)
             consumeUntilUp()
-            launch { pressScope.release() }
+            cancelOrReleaseJob =
+                launch(start = coroutineStartForCurrentDispatchBehavior) {
+                    awaitResetOrSkip()
+                    pressScope.release()
+                }
         }
 
         if (upOrCancel != null) {
@@ -132,7 +155,11 @@
                     onTap?.invoke(upOrCancel.position) // no valid second tap started
                 } else {
                     // Second tap down detected
-                    launch { pressScope.reset() }
+                    resetJob =
+                        launch(start = coroutineStartForCurrentDispatchBehavior) {
+                            cancelOrReleaseJob?.join()
+                            pressScope.reset()
+                        }
                     if (onPress !== NoPressGesture) {
                         launch { pressScope.onPress(secondDown.position) }
                     }
@@ -143,10 +170,16 @@
                             val secondUp = waitForUpOrCancellation()
                             if (secondUp != null) {
                                 secondUp.consume()
-                                launch { pressScope.release() }
+                                launch(start = coroutineStartForCurrentDispatchBehavior) {
+                                    awaitResetOrSkip()
+                                    pressScope.release()
+                                }
                                 onDoubleTap(secondUp.position)
                             } else {
-                                launch { pressScope.cancel() }
+                                launch(start = coroutineStartForCurrentDispatchBehavior) {
+                                    awaitResetOrSkip()
+                                    pressScope.cancel()
+                                }
                                 onTap?.invoke(upOrCancel.position)
                             }
                         }
@@ -158,7 +191,11 @@
                         // notify for the long press
                         onLongPress?.invoke(secondDown.position)
                         consumeUntilUp()
-                        launch { pressScope.release() }
+
+                        launch(start = coroutineStartForCurrentDispatchBehavior) {
+                            awaitResetOrSkip()
+                            pressScope.release()
+                        }
                     }
                 }
             }
@@ -204,6 +241,7 @@
  * can be negative or larger than the element bounds if the touch target is smaller than the
  * [ViewConfiguration.minimumTouchTargetSize].
  */
+@OptIn(ExperimentalTapGestureDetectorBehaviorApi::class)
 internal suspend fun PointerInputScope.detectTapAndPress(
     onPress: suspend PressGestureScope.(Offset) -> Unit = NoPressGesture,
     onTap: ((Offset) -> Unit)? = null
@@ -211,7 +249,13 @@
     val pressScope = PressGestureScopeImpl(this)
     coroutineScope {
         awaitEachGesture {
-            launch { pressScope.reset() }
+            val resetJob =
+                launch(start = coroutineStartForCurrentDispatchBehavior) { pressScope.reset() }
+            suspend fun awaitResetOrSkip() {
+                if (DetectTapGesturesEnableNewDispatchingBehavior) {
+                    resetJob.join()
+                }
+            }
 
             val down = awaitFirstDown().also { it.consume() }
 
@@ -221,12 +265,17 @@
 
             val up = waitForUpOrCancellation()
             if (up == null) {
-                launch {
-                    pressScope.cancel() // tap-up was canceled
+                launch(start = coroutineStartForCurrentDispatchBehavior) {
+                    awaitResetOrSkip()
+                    // tap-up was canceled
+                    pressScope.cancel()
                 }
             } else {
                 up.consume()
-                launch { pressScope.release() }
+                launch(start = coroutineStartForCurrentDispatchBehavior) {
+                    awaitResetOrSkip()
+                    pressScope.release()
+                }
                 onTap?.invoke(up.position)
             }
         }
@@ -299,6 +348,32 @@
     }
 }
 
+@Retention(AnnotationRetention.BINARY)
+@RequiresOptIn("This API feature-flags new behavior and will be removed in the future.")
+annotation class ExperimentalTapGestureDetectorBehaviorApi
+
+/**
+ * Whether to use more immediate coroutine dispatching in [detectTapGestures] and
+ * [detectTapAndPress], true by default. This might affect some implicit timing guarantees. Please
+ * file a bug if this change is affecting your use case.
+ */
+// This lint does not translate well to top-level declarations
+@get:Suppress("GetterSetterNames")
+@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+@ExperimentalTapGestureDetectorBehaviorApi
+@get:ExperimentalTapGestureDetectorBehaviorApi
+@set:ExperimentalTapGestureDetectorBehaviorApi
+var DetectTapGesturesEnableNewDispatchingBehavior = false
+
+@OptIn(ExperimentalTapGestureDetectorBehaviorApi::class)
+private val coroutineStartForCurrentDispatchBehavior
+    get() =
+        if (DetectTapGesturesEnableNewDispatchingBehavior) {
+            CoroutineStart.UNDISPATCHED
+        } else {
+            CoroutineStart.DEFAULT
+        }
+
 /** [detectTapGestures]'s implementation of [PressGestureScope]. */
 internal class PressGestureScopeImpl(density: Density) : PressGestureScope, Density by density {
     private var isReleased = false
@@ -308,13 +383,17 @@
     /** Called when a gesture has been canceled. */
     fun cancel() {
         isCanceled = true
-        mutex.unlock()
+        if (mutex.isLocked) {
+            mutex.unlock()
+        }
     }
 
     /** Called when all pointers are up. */
     fun release() {
         isReleased = true
-        mutex.unlock()
+        if (mutex.isLocked) {
+            mutex.unlock()
+        }
     }
 
     /** Called when a new gesture has started. */
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.kt
new file mode 100644
index 0000000..0ca25ee
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.internal
+
+internal expect annotation class JvmSynchronized()
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt
index da839b5..12a9249 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemAnimator.kt
@@ -210,7 +210,10 @@
         movingAwayKeys.forEach { key ->
             // found an item which was in our map previously but is not a part of the
             // positionedItems now
-            val info = keyToItemInfoMap[key]!!
+            // TODO(jossiwolf): In some cases, keyToItemInfoMap and movingAwayKeys can get out of
+            //  sync. If that's the case, we can not play an animation in any case as the item is
+            //  already gone (b/352482051). Follow-up: b/354695943
+            val info = keyToItemInfoMap[key] ?: return@forEach
             val newIndex = keyIndexMap.getIndex(key)
 
             // it is possible that we are being remeasured with smaller laneCount. make sure
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
index 0565551..b05993a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
@@ -22,6 +22,7 @@
 import androidx.compose.runtime.ReusableContentHost
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.saveable.SaveableStateHolder
+import kotlin.jvm.JvmInline
 
 /**
  * This class:
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
index 1518e8e..42d7668 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
@@ -28,7 +28,6 @@
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.util.trace
-import kotlin.system.measureNanoTime
 
 /**
  * State for lazy items prefetching, used by lazy layouts to instruct the prefetcher.
@@ -211,6 +210,8 @@
     }
 }
 
+internal expect inline fun measureNanoTime(doMeasure: () -> Unit): Long
+
 @ExperimentalFoundationApi
 private object DummyHandle : PrefetchHandle {
     override fun cancel() {}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt
index dc2b9cf..d56a7ba 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.neverEqualPolicy
+import kotlin.jvm.JvmInline
 
 /**
  * Simple wrapper over a mutable state which allows to invalidate an observable scope. We might
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index 66a7fed..1c54460 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.util.packInts
 import androidx.compose.ui.util.unpackInt1
 import androidx.compose.ui.util.unpackInt2
+import kotlin.jvm.JvmInline
 import kotlin.math.abs
 import kotlin.math.min
 import kotlin.math.sign
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
index bec29c5..7cfd681 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
@@ -432,7 +432,7 @@
         source: NestedScrollSource
     ): Offset {
         if (source == NestedScrollSource.SideEffect && available.mainAxis() != 0f) {
-            throw CancellationException()
+            throw CancellationException("Scroll cancelled")
         }
         return Offset.Zero
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
index fffad28..ac2df12 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
@@ -22,6 +22,8 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.node.DelegatableNode
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
 /**
  * Platform-specific "root" of the [BringIntoViewParent] chain to call into when there are no
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
index 2edc81a..7821f49 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
@@ -24,6 +24,8 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.InspectorInfo
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
 /**
  * Can be used to send [bringIntoView] requests. Pass it as a parameter to
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
index 3896f76..dd7d949 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
@@ -29,6 +29,8 @@
 import androidx.compose.ui.node.findNearestAncestor
 import androidx.compose.ui.node.requireLayoutCoordinates
 import androidx.compose.ui.platform.InspectorInfo
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt
index dd43101..af31b5d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt
@@ -25,6 +25,8 @@
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.requireLayoutCoordinates
 import androidx.compose.ui.unit.toSize
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
 /**
  * Bring this node into bounds by making all the scrollable parents scroll appropriately.
@@ -40,8 +42,7 @@
  * @sample androidx.compose.foundation.samples.BringIntoViewSample
  * @sample androidx.compose.foundation.samples.BringPartOfComposableIntoViewSample
  */
-// TODO(b/333421581) Make public.
-internal suspend fun DelegatableNode.scrollIntoView(rect: Rect? = null) {
+suspend fun DelegatableNode.scrollIntoView(rect: Rect? = null) {
     if (!node.isAttached) return
     val layoutCoordinates = requireLayoutCoordinates()
     val parent = findBringIntoViewParent() ?: return
@@ -52,6 +53,7 @@
     layoutCoordinates: LayoutCoordinates,
     rect: Rect? = null
 ) {
+    if (!layoutCoordinates.isAttached) return
     bringChildIntoView(layoutCoordinates) {
         // If the rect is not specified, use a rectangle representing the entire composable.
         // If the coordinates are detached when this call is made, we don't bother even
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index 6b6deb5..9b787f5e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -463,7 +463,8 @@
             this.textSelectionRange = value.selection
             if (!enabled) this.disabled()
             if (isPassword) this.password()
-            isEditable = enabled && !readOnly
+            val editable = enabled && !readOnly
+            isEditable = editable
             getTextLayoutResult {
                 if (state.layoutResult != null) {
                     it.add(state.layoutResult!!.value)
@@ -472,62 +473,60 @@
                     false
                 }
             }
-            setText { text ->
-                if (readOnly || !enabled) return@setText false
-
-                // If the action is performed while in an active text editing session, treat this
-                // like
-                // an IME command and update the text by going through the buffer. This keeps the
-                // buffer
-                // state consistent if other IME commands are performed before the next
-                // recomposition,
-                // and is used for the testing code path.
-                state.inputSession?.let { session ->
-                    TextFieldDelegate.onEditCommand(
-                        ops = listOf(DeleteAllCommand(), CommitTextCommand(text, 1)),
-                        editProcessor = state.processor,
-                        state.onValueChange,
-                        session
-                    )
-                }
-                    ?: run {
-                        state.onValueChange(TextFieldValue(text.text, TextRange(text.text.length)))
+            if (editable) {
+                setText { text ->
+                    // If the action is performed while in an active text editing session, treat
+                    // this like an IME command and update the text by going through the buffer.
+                    // This keeps the buffer state consistent if other IME commands are performed
+                    // before the next recomposition, and is used for the testing code path.
+                    state.inputSession?.let { session ->
+                        TextFieldDelegate.onEditCommand(
+                            ops = listOf(DeleteAllCommand(), CommitTextCommand(text, 1)),
+                            editProcessor = state.processor,
+                            state.onValueChange,
+                            session
+                        )
                     }
-                true
-            }
-            insertTextAtCursor { text ->
-                if (readOnly || !enabled) return@insertTextAtCursor false
-
-                // If the action is performed while in an active text editing session, treat this
-                // like
-                // an IME command and update the text by going through the buffer. This keeps the
-                // buffer
-                // state consistent if other IME commands are performed before the next
-                // recomposition,
-                // and is used for the testing code path.
-                state.inputSession?.let { session ->
-                    TextFieldDelegate.onEditCommand(
-                        // Finish composing text first because when the field is focused the IME
-                        // might
-                        // set composition.
-                        ops = listOf(FinishComposingTextCommand(), CommitTextCommand(text, 1)),
-                        editProcessor = state.processor,
-                        state.onValueChange,
-                        session
-                    )
-                }
-                    ?: run {
-                        val newText =
-                            value.text.replaceRange(
-                                value.selection.start,
-                                value.selection.end,
-                                text
+                        ?: run {
+                            state.onValueChange(
+                                TextFieldValue(text.text, TextRange(text.text.length))
                             )
-                        val newCursor = TextRange(value.selection.start + text.length)
-                        state.onValueChange(TextFieldValue(newText, newCursor))
+                        }
+                    true
+                }
+
+                insertTextAtCursor { text ->
+                    if (readOnly || !enabled) return@insertTextAtCursor false
+
+                    // If the action is performed while in an active text editing session, treat
+                    // this like an IME command and update the text by going through the buffer.
+                    // This keeps the buffer state consistent if other IME commands are performed
+                    // before the next recomposition, and is used for the testing code path.
+                    state.inputSession?.let { session ->
+                        TextFieldDelegate.onEditCommand(
+                            // Finish composing text first because when the field is focused the IME
+                            // might
+                            // set composition.
+                            ops = listOf(FinishComposingTextCommand(), CommitTextCommand(text, 1)),
+                            editProcessor = state.processor,
+                            state.onValueChange,
+                            session
+                        )
                     }
-                true
+                        ?: run {
+                            val newText =
+                                value.text.replaceRange(
+                                    value.selection.start,
+                                    value.selection.end,
+                                    text
+                                )
+                            val newCursor = TextRange(value.selection.start + text.length)
+                            state.onValueChange(TextFieldValue(newText, newCursor))
+                        }
+                    true
+                }
             }
+
             setSelection { selectionStart, selectionEnd, relativeToOriginalText ->
                 // in traversal mode we get selection from the `textSelectionRange` semantics which
                 // is
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt
index 39de64f..3bcd0d1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt
@@ -16,12 +16,12 @@
 
 package androidx.compose.foundation.text
 
+import androidx.annotation.VisibleForTesting
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.input.OffsetMapping
 import androidx.compose.ui.text.input.TransformedText
 import androidx.compose.ui.text.input.VisualTransformation
 import kotlin.math.min
-import org.jetbrains.annotations.VisibleForTesting
 
 internal val ValidatingEmptyOffsetMappingIdentity: OffsetMapping =
     ValidatingOffsetMapping(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
index 43420ee..9867e6d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.text.input.internal.OffsetMappingCalculator
 import androidx.compose.foundation.text.input.internal.PartialGapBuffer
 import androidx.compose.ui.text.TextRange
+import kotlin.jvm.JvmName
 
 /**
  * A text buffer that can be edited, similar to [StringBuilder].
@@ -179,6 +180,7 @@
 
     // Doc inherited from Appendable.
     // This append overload should be first so it ends up being the target of links to this method.
+    @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
     override fun append(text: CharSequence?): Appendable = apply {
         if (text != null) {
             onTextWillChange(length, length, text.length)
@@ -187,6 +189,7 @@
     }
 
     // Doc inherited from Appendable.
+    @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
     override fun append(text: CharSequence?, start: Int, end: Int): Appendable = apply {
         if (text != null) {
             onTextWillChange(length, length, end - start)
@@ -195,6 +198,7 @@
     }
 
     // Doc inherited from Appendable.
+    @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
     override fun append(char: Char): Appendable = apply {
         onTextWillChange(length, length, 1)
         buffer.replace(buffer.length, buffer.length, char.toString())
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
index 350b5eca..df3aa81 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
@@ -17,8 +17,12 @@
 package androidx.compose.foundation.text.input
 
 import androidx.compose.foundation.text.input.internal.toCharArray
+import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.coerceIn
+import kotlin.jvm.JvmInline
+
+internal typealias PlacedAnnotation = AnnotatedString.Range<AnnotatedString.Annotation>
 
 /**
  * An immutable snapshot of the contents of a [TextFieldState].
@@ -39,7 +43,8 @@
     text: CharSequence = "",
     selection: TextRange = TextRange.Zero,
     composition: TextRange? = null,
-    highlight: Pair<TextHighlightType, TextRange>? = null
+    highlight: Pair<TextHighlightType, TextRange>? = null,
+    val composingAnnotations: List<PlacedAnnotation>? = null
 ) : CharSequence {
 
     override val length: Int
@@ -114,6 +119,7 @@
         if (selection != other.selection) return false
         if (composition != other.composition) return false
         if (highlight != other.highlight) return false
+        if (composingAnnotations != other.composingAnnotations) return false
         if (!contentEquals(other.text)) return false
 
         return true
@@ -124,6 +130,7 @@
         result = 31 * result + selection.hashCode()
         result = 31 * result + (composition?.hashCode() ?: 0)
         result = 31 * result + highlight.hashCode()
+        result = 31 * result + composingAnnotations.hashCode()
         return result
     }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
index 6337cc0..5d9b251a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.text.input.internal.undo.TextFieldEditUndoBehavior
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -32,9 +33,12 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.coerceIn
 import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.style.TextDecoration
 
 internal fun TextFieldState(initialValue: TextFieldValue): TextFieldState {
     return TextFieldState(
@@ -314,7 +318,8 @@
         ) {
             if (
                 beforeEditValue.composition != mainBuffer.composition ||
-                    beforeEditValue.highlight != mainBuffer.highlight
+                    beforeEditValue.highlight != mainBuffer.highlight ||
+                    beforeEditValue.composingAnnotations != mainBuffer.composingAnnotations
             ) {
                 // edit operation caused no change to text content or selection
                 // No need to run an existing InputTransformation, or record an undo. Only update
@@ -326,7 +331,12 @@
                             text = mainBuffer.toString(),
                             selection = mainBuffer.selection,
                             composition = mainBuffer.composition,
-                            highlight = mainBuffer.highlight
+                            highlight = mainBuffer.highlight,
+                            composingAnnotations =
+                                finalizeComposingAnnotations(
+                                    composition = mainBuffer.composition,
+                                    annotationList = mainBuffer.composingAnnotations
+                                )
                         ),
                     restartImeIfContentChanges = restartImeIfContentChanges
                 )
@@ -341,7 +351,12 @@
                 text = mainBuffer.toString(),
                 selection = mainBuffer.selection,
                 composition = mainBuffer.composition,
-                highlight = mainBuffer.highlight
+                highlight = mainBuffer.highlight,
+                composingAnnotations =
+                    finalizeComposingAnnotations(
+                        composition = mainBuffer.composition,
+                        annotationList = mainBuffer.composingAnnotations
+                    )
             )
 
         // if there's no filter; just record the undo, update the snapshot value, end.
@@ -691,3 +706,32 @@
         placeCursorAtEnd()
     }
 }
+
+/**
+ * Final deciding property for which annotations would be rendered for the composing region. If the
+ * IME has not set any composing annotations and the composing region is not collapsed, we need to
+ * add the specific underline styling.
+ */
+@Suppress("ListIterator")
+private fun finalizeComposingAnnotations(
+    composition: TextRange?,
+    annotationList: MutableVector<PlacedAnnotation>?
+): List<PlacedAnnotation> =
+    when {
+        annotationList != null && annotationList.isNotEmpty() -> {
+            // it is important to freeze the mutable list into an immutable list because
+            // mutable list is sustained inside the EditingBuffer and TextFieldCharSequence
+            // must be read-only.
+            annotationList.asMutableList().toList()
+        }
+        composition != null && !composition.collapsed -> {
+            listOf(
+                AnnotatedString.Range(
+                    SpanStyle(textDecoration = TextDecoration.Underline),
+                    start = composition.min,
+                    end = composition.max
+                )
+            )
+        }
+        else -> emptyList()
+    }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt
index 2be9695..2c0c102 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.foundation.text.input
 
+import kotlin.jvm.JvmInline
+
 /**
  * Defines how the text will be obscured in secure text fields.
  *
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditCommand.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditCommand.kt
index 5f25f0c..058ed63 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditCommand.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditCommand.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.text.findFollowingBreak
 import androidx.compose.foundation.text.findPrecedingBreak
+import androidx.compose.foundation.text.input.PlacedAnnotation
 
 /**
  * Commit final [text] to the text box and set the new cursor position.
@@ -90,14 +91,20 @@
  *
  * @param text The composing text.
  * @param newCursorPosition The cursor position after setting composing text.
+ * @param annotations Text annotations that IME attaches to the composing region. e.g. background
+ *   color or underline styling.
  */
-internal fun EditingBuffer.setComposingText(text: String, newCursorPosition: Int) {
+internal fun EditingBuffer.setComposingText(
+    text: String,
+    newCursorPosition: Int,
+    annotations: List<PlacedAnnotation>? = null
+) {
     if (hasComposition()) {
         // API doc says, if there is ongoing composing text, replace it with new text.
         val compositionStart = compositionStart
         replace(compositionStart, compositionEnd, text)
         if (text.isNotEmpty()) {
-            setComposition(compositionStart, compositionStart + text.length)
+            setComposition(compositionStart, compositionStart + text.length, annotations)
         }
     } else {
         // If there is no composing text, insert composing text into cursor position with
@@ -105,7 +112,7 @@
         val selectionStart = selectionStart
         replace(selectionStart, selectionEnd, text)
         if (text.isNotEmpty()) {
-            setComposition(selectionStart, selectionStart + text.length)
+            setComposition(selectionStart, selectionStart + text.length, annotations)
         }
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditingBuffer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditingBuffer.kt
index 05493d4..198f266 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditingBuffer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/EditingBuffer.kt
@@ -16,9 +16,13 @@
 
 package androidx.compose.foundation.text.input.internal
 
+import androidx.compose.foundation.text.input.PlacedAnnotation
 import androidx.compose.foundation.text.input.TextHighlightType
+import androidx.compose.runtime.collection.MutableVector
+import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.util.fastForEach
 
 /**
  * The editing buffer
@@ -27,7 +31,7 @@
  */
 internal class EditingBuffer(
     /** The initial text of this editing buffer */
-    text: AnnotatedString,
+    text: String,
     /**
      * The initial selection range of this buffer. If you provide collapsed selection, it is treated
      * as the cursor position. The cursor and selection cannot exists at the same time. The
@@ -40,7 +44,10 @@
         const val NOWHERE = -1
     }
 
-    private val gapBuffer = PartialGapBuffer(text.text)
+    var composingAnnotations: MutableVector<AnnotatedString.Range<AnnotatedString.Annotation>>? =
+        null
+
+    private val gapBuffer = PartialGapBuffer(text)
 
     val changeTracker = ChangeTracker()
 
@@ -120,8 +127,6 @@
     val length: Int
         get() = gapBuffer.length
 
-    constructor(text: String, selection: TextRange) : this(AnnotatedString(text), selection)
-
     init {
         checkRange(selection.start, selection.end)
     }
@@ -142,6 +147,16 @@
         // composition based typing when each keystroke may trigger a replace function that looks
         // like "abcd" => "abcde".
 
+        // b(351165334)
+        // Since we are starting from the left hand side to compare the strings, when "abc" is
+        // replaced with "aabc", it will be reported as an `a` is inserted at `TextRange(1)` instead
+        // of the more logical possibility; `TextRange(0)`. This replace call cannot differentiate
+        // between the two possible cases because we have no way of really knowing what was the
+        // intention of the user beyond this replace call. We prefer to choose the more logical
+        // explanation for right hand side since it's the more common direction of typing. This is
+        // guaranteed by the fact that we start our coercion from left hand side, and finally apply
+        // the right hand side.
+
         // coerce min
         var i = 0
         var cMin = min
@@ -152,7 +167,7 @@
         // coerce max
         var j = text.length
         var cMax = max
-        while (cMax > min && j > i && text[j - 1] == gapBuffer[cMax - 1]) {
+        while (cMax > cMin && j > i && text[j - 1] == gapBuffer[cMax - 1]) {
             j--
             cMax--
         }
@@ -174,6 +189,8 @@
         // to set composition range after replace function.
         compositionStart = NOWHERE
         compositionEnd = NOWHERE
+        // Do not deallocate an existing list. We will probably use it again.
+        composingAnnotations?.clear()
 
         highlight = null
     }
@@ -261,11 +278,15 @@
      *
      * @param start the inclusive start offset of the composition
      * @param end the exclusive end offset of the composition
-     * @throws IndexOutOfBoundsException if start or end offset is ouside of current buffer
+     * @param annotations Annotations that are attached to the composing region of text. This
+     *   function does not check whether the given annotations are inside the composing region. It
+     *   simply adds them to the current buffer while adjusting their range according to where the
+     *   new composition region is set.
+     * @throws IndexOutOfBoundsException if start or end offset is outside of current buffer
      * @throws IllegalArgumentException if start is larger than or equal to end. (reversed or
      *   collapsed range)
      */
-    fun setComposition(start: Int, end: Int) {
+    fun setComposition(start: Int, end: Int, annotations: List<PlacedAnnotation>? = null) {
         if (start < 0 || start > gapBuffer.length) {
             throw IndexOutOfBoundsException(
                 "start ($start) offset is outside of text region ${gapBuffer.length}"
@@ -282,18 +303,30 @@
 
         compositionStart = start
         compositionEnd = end
+
+        this.composingAnnotations?.clear()
+        if (!annotations.isNullOrEmpty()) {
+            if (this.composingAnnotations == null) {
+                this.composingAnnotations = mutableVectorOf()
+            }
+            annotations.fastForEach {
+                // place the annotations at the correct indices in the buffer.
+                this.composingAnnotations?.add(
+                    it.copy(start = it.start + start, end = it.end + start)
+                )
+            }
+        }
     }
 
     /** Commits the ongoing composition text and reset the composition range. */
     fun commitComposition() {
         compositionStart = NOWHERE
         compositionEnd = NOWHERE
+        composingAnnotations?.clear()
     }
 
     override fun toString(): String = gapBuffer.toString()
 
-    fun toAnnotatedString(): AnnotatedString = AnnotatedString(toString())
-
     private fun checkRange(start: Int, end: Int) {
         if (start < 0 || start > gapBuffer.length) {
             throw IndexOutOfBoundsException(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt
index 0c7fcc1..98e60df 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt
@@ -167,8 +167,8 @@
      * @param builder The output string builder
      */
     fun append(builder: StringBuilder) {
-        builder.append(buffer, 0, gapStart)
-        builder.append(buffer, gapEnd, capacity - gapEnd)
+        builder.appendRange(buffer, 0, gapStart)
+        builder.appendRange(buffer, gapEnd, capacity)
     }
 
     /**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt
index cd38a53..c962e3f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDecoratorModifier.kt
@@ -495,11 +495,21 @@
         getTextLayoutResult {
             textLayoutState.layoutResult?.let { result -> it.add(result) } ?: false
         }
-        setText { newText ->
-            if (!editable) return@setText false
+        if (editable) {
+            setText { newText ->
+                if (!editable) return@setText false
 
-            textFieldState.replaceAll(newText)
-            true
+                textFieldState.replaceAll(newText)
+                true
+            }
+            insertTextAtCursor { newText ->
+                if (!editable) return@insertTextAtCursor false
+
+                // Finish composing text first because when the field is focused the IME
+                // might set composition.
+                textFieldState.replaceSelectedText(newText, clearComposition = true)
+                true
+            }
         }
         @Suppress("NAME_SHADOWING")
         setSelection { start, end, relativeToOriginal ->
@@ -539,14 +549,6 @@
             }
             return@setSelection true
         }
-        insertTextAtCursor { newText ->
-            if (!editable) return@insertTextAtCursor false
-
-            // Finish composing text first because when the field is focused the IME
-            // might set composition.
-            textFieldState.replaceSelectedText(newText, clearComposition = true)
-            true
-        }
 
         val effectiveImeAction = keyboardOptions.imeActionOrDefault
         onImeAction(effectiveImeAction) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt
index ac8290f..242a2c4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.text.input.internal
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.content.MediaType
 import androidx.compose.ui.draganddrop.DragAndDropEvent
 import androidx.compose.ui.draganddrop.DragAndDropModifierNode
@@ -23,6 +24,7 @@
 import androidx.compose.ui.platform.ClipEntry
 import androidx.compose.ui.platform.ClipMetadata
 
+@ExperimentalFoundationApi
 internal expect fun textFieldDragAndDropNode(
     hintMediaTypes: () -> Set<MediaType>,
     onDrop: (clipEntry: ClipEntry, clipMetadata: ClipMetadata) -> Boolean,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt
index 1481d93..16c7f53 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.text.input.internal
 
+import androidx.collection.MutableLongSet
 import androidx.compose.foundation.text.DeadKeyCombiner
 import androidx.compose.foundation.text.KeyCommand
 import androidx.compose.foundation.text.appendCodePointX
@@ -30,6 +31,7 @@
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
 import androidx.compose.ui.input.key.type
 import androidx.compose.ui.platform.SoftwareKeyboardController
 import androidx.compose.ui.text.TextRange
@@ -52,6 +54,15 @@
     private val deadKeyCombiner = DeadKeyCombiner()
     private val keyMapping = platformDefaultKeyMapping
 
+    /**
+     * We hold a reference to the all key down events that we receive and consume so that we can
+     * also consume the corresponding key up events. Otherwise the up events get sent to the
+     * ancestor nodes where the behavior is unpredictable. Please refer to b/353554186 for more
+     * information.
+     */
+    // TODO(b/307580000) Factor this state out into a class to manage key inputs.
+    private var currentlyConsumedDownKeys: MutableLongSet? = null
+
     open fun onPreKeyEvent(
         event: KeyEvent,
         textFieldState: TransformedTextFieldState,
@@ -77,10 +88,52 @@
         singleLine: Boolean,
         onSubmit: () -> Unit
     ): Boolean {
-        if (event.type != KeyEventType.KeyDown) {
+        val keyCode = event.key.keyCode
+
+        if (event.type == KeyEventType.KeyUp) {
+            if (currentlyConsumedDownKeys?.contains(keyCode) == true) {
+                currentlyConsumedDownKeys?.remove(keyCode)
+                return true
+            } else {
+                return false
+            }
+        }
+
+        if (event.type == KeyEventType.Unknown) {
             return false
         }
 
+        val consumed =
+            processKeyDownEvent(
+                event = event,
+                textFieldState = textFieldState,
+                textLayoutState = textLayoutState,
+                textFieldSelectionState = textFieldSelectionState,
+                editable = editable,
+                singleLine = singleLine,
+                onSubmit = onSubmit
+            )
+
+        if (consumed) {
+            // initialize if it hasn't been initialized yet.
+            val currentlyConsumedDownKeys =
+                currentlyConsumedDownKeys
+                    ?: MutableLongSet(initialCapacity = 3).also { currentlyConsumedDownKeys = it }
+            currentlyConsumedDownKeys += keyCode
+        }
+
+        return consumed
+    }
+
+    private fun processKeyDownEvent(
+        event: KeyEvent,
+        textFieldState: TransformedTextFieldState,
+        textLayoutState: TextLayoutState,
+        textFieldSelectionState: TextFieldSelectionState,
+        editable: Boolean,
+        singleLine: Boolean,
+        onSubmit: () -> Unit
+    ): Boolean {
         if (event.isTypedEvent) {
             val codePoint = deadKeyCombiner.consume(event)
             if (codePoint != null) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt
index b6a8478..6373697 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.text.TextDelegate
+import androidx.compose.foundation.text.input.PlacedAnnotation
 import androidx.compose.foundation.text.input.TextFieldCharSequence
 import androidx.compose.foundation.text.input.TextFieldState
 import androidx.compose.foundation.text.input.internal.TextFieldLayoutStateCache.MeasureInputs
@@ -32,18 +33,16 @@
 import androidx.compose.runtime.snapshots.StateRecord
 import androidx.compose.runtime.snapshots.withCurrent
 import androidx.compose.runtime.snapshots.writable
-import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextLayoutInput
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextMeasurer
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.intl.Locale
 import androidx.compose.ui.text.intl.PlatformLocale
-import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.text.style.TextDirection
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
@@ -166,6 +165,7 @@
             if (
                 cachedResult != null &&
                     cachedRecord.visualText?.contentEquals(visualText) == true &&
+                    cachedRecord.composingAnnotations == visualText.composingAnnotations &&
                     cachedRecord.composition == visualText.composition &&
                     cachedRecord.singleLine == nonMeasureInputs.singleLine &&
                     cachedRecord.softWrap == nonMeasureInputs.softWrap &&
@@ -224,6 +224,7 @@
                 if (newResult != cachedResult) {
                     updateCacheIfWritable {
                         this.visualText = visualText
+                        this.composingAnnotations = visualText.composingAnnotations
                         this.composition = visualText.composition
                         this.singleLine = nonMeasureInputs.singleLine
                         this.softWrap = nonMeasureInputs.softWrap
@@ -293,16 +294,10 @@
 
         return textMeasurer.measure(
             text =
-                buildAnnotatedString {
-                    append(visualText.toString())
-                    if (visualText.composition != null) {
-                        addStyle(
-                            style = SpanStyle(textDecoration = TextDecoration.Underline),
-                            start = visualText.composition.min,
-                            end = visualText.composition.max
-                        )
-                    }
-                },
+                AnnotatedString(
+                    text = visualText.toString(),
+                    annotations = visualText.composingAnnotations ?: emptyList()
+                ),
             style = finalTextStyle,
             softWrap = nonMeasureInputs.softWrap,
             maxLines = if (nonMeasureInputs.singleLine) 1 else Int.MAX_VALUE,
@@ -347,6 +342,7 @@
         // re-layout. Also if the TFS object _doesn't_ change but its text _does_, we do need to
         // re-layout. That state read happens in getOrComputeLayout to invalidate correctly.
         var visualText: CharSequence? = null
+        var composingAnnotations: List<PlacedAnnotation>? = null
         // We keep composition separate from visualText because we do not want to invalidate text
         // layout when selection changes. Composition should invalidate the layout because it
         // adds an underline span.
@@ -370,6 +366,7 @@
         override fun assign(value: StateRecord) {
             value as CacheRecord
             visualText = value.visualText
+            composingAnnotations = value.composingAnnotations
             composition = value.composition
             textStyle = value.textStyle
             singleLine = value.singleLine
@@ -385,6 +382,7 @@
         override fun toString(): String =
             "CacheRecord(" +
                 "visualText=$visualText, " +
+                "composingAnnotations=$composingAnnotations, " +
                 "composition=$composition, " +
                 "textStyle=$textStyle, " +
                 "singleLine=$singleLine, " +
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/UndoManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/UndoManager.kt
index b54b8b8..1c50185 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/UndoManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/undo/UndoManager.kt
@@ -20,6 +20,8 @@
 import androidx.compose.runtime.saveable.SaverScope
 import androidx.compose.runtime.snapshots.SnapshotStateList
 import androidx.compose.ui.util.fastForEach
+import kotlin.collections.removeFirst as removeFirstKt
+import kotlin.collections.removeLast as removeLastKt
 
 /**
  * A generic purpose undo/redo stack manager.
@@ -60,7 +62,7 @@
         redoStack.clear()
 
         while (size > capacity - 1) { // leave room for the immediate `add`
-            undoStack.removeFirst()
+            undoStack.removeFirstKt()
         }
         undoStack.add(undoableAction)
     }
@@ -77,7 +79,7 @@
                 "Please first check `canUndo` value before calling the `undo` function."
         }
 
-        val topOperation = undoStack.removeLast()
+        val topOperation = undoStack.removeLastKt()
 
         redoStack.add(topOperation)
         return topOperation
@@ -95,7 +97,7 @@
                 "Please first check `canRedo` value before calling the `redo` function."
         }
 
-        val topOperation = redoStack.removeLast()
+        val topOperation = redoStack.removeLastKt()
 
         undoStack.add(topOperation)
         return topOperation
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt
index ebe89ee..f41cb97 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt
@@ -20,6 +20,7 @@
 import androidx.compose.ui.util.packFloats
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackFloat2
+import kotlin.jvm.JvmInline
 
 /**
  * [Density] is an interface, not a final class. When you want to have a snapshot of Density values
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
index e8adbcb..c4bfef6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.text.selection
 
+import androidx.compose.foundation.internal.JvmSynchronized
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.isUnspecified
@@ -44,7 +45,7 @@
      * instance check is enough to accomplish whether a text layout has changed in a meaningful way.
      */
     private val TextLayoutResult.lastVisibleOffset: Int
-        @Synchronized
+        @JvmSynchronized
         get() {
             if (_previousTextLayoutResult !== this) {
                 val lastVisibleLine =
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/Clickable.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Clickable.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/Clickable.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Clickable.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DarkTheme.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DarkTheme.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DarkTheme.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DarkTheme.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Expect.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Expect.commonStubs.kt
new file mode 100644
index 0000000..b425466
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Expect.commonStubs.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation
+
+internal actual class AtomicReference<V> actual constructor(value: V) {
+    actual fun get(): V = implementedInJetBrainsFork()
+
+    actual fun set(value: V) {
+        implementedInJetBrainsFork()
+    }
+
+    actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+    actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
+
+internal actual class AtomicLong actual constructor(value: Long) {
+    actual fun get(): Long = implementedInJetBrainsFork()
+
+    actual fun set(value: Long) {
+        implementedInJetBrainsFork()
+    }
+
+    actual fun getAndIncrement(): Long = implementedInJetBrainsFork()
+}
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/MutatorMutex.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/MutatorMutex.commonStubs.kt
new file mode 100644
index 0000000..8272b93
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/MutatorMutex.commonStubs.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class MutationInterruptedException : CancellationException("Mutation interrupted") {
+    init {
+        implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/NotImplemented.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/NotImplemented.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/MediaType.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/MediaType.commonStubs.kt
new file mode 100644
index 0000000..015092f
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/MediaType.commonStubs.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.content
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+
+actual class MediaType internal constructor() {
+    actual constructor(representation: String) : this()
+
+    actual val representation: String = implementedInJetBrainsFork()
+
+    actual companion object {
+        actual val Text: MediaType = implementedInJetBrainsFork()
+        actual val PlainText: MediaType = implementedInJetBrainsFork()
+        actual val HtmlText: MediaType = implementedInJetBrainsFork()
+        actual val Image: MediaType = implementedInJetBrainsFork()
+        actual val All: MediaType = implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.commonStubs.kt
new file mode 100644
index 0000000..3c35fdf
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.commonStubs.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.content
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.implementedInJetBrainsFork
+import androidx.compose.ui.platform.ClipEntry
+
+@ExperimentalFoundationApi
+actual class PlatformTransferableContent internal constructor() {
+    init {
+        implementedInJetBrainsFork()
+    }
+}
+
+@ExperimentalFoundationApi
+actual fun TransferableContent.hasMediaType(mediaType: MediaType): Boolean =
+    implementedInJetBrainsFork()
+
+internal actual fun ClipEntry.readPlainText(): String? = implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.commonStubs.kt
new file mode 100644
index 0000000..d9cb99e
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.commonStubs.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gestures
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal actual constructor() :
+    CancellationException("Anchored drag finished") {
+    init {
+        implementedInJetBrainsFork()
+    }
+}
+
+internal actual inline fun assertOnJvm(statement: Boolean, message: () -> String): Unit =
+    implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..4b50cb8
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.internal
+
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.commonStubs.kt
new file mode 100644
index 0000000..503ce20
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.internal
+
+internal actual annotation class JvmSynchronized()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.commonStubs.kt
new file mode 100644
index 0000000..23a886a
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.commonStubs.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.layout
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+
+internal actual inline fun measureNanoTime(doMeasure: () -> Unit): Long =
+    implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/CursorHandle.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/CursorHandle.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.commonStubs.kt
new file mode 100644
index 0000000..dd19eab
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.commonStubs.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+
+internal actual fun String.findPrecedingBreak(index: Int): Int = implementedInJetBrainsFork()
+
+internal actual fun String.findFollowingBreak(index: Int): Int = implementedInJetBrainsFork()
+
+internal actual fun StringBuilder.appendCodePointX(codePoint: Int): StringBuilder =
+    implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/UndoManager.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/UndoManager.commonStubs.kt
new file mode 100644
index 0000000..8065392
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/UndoManager.commonStubs.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+
+internal actual fun timeNowMillis(): Long = implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointHelpers.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointHelpers.commonStubs.kt
new file mode 100644
index 0000000..ef0a75e
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointHelpers.commonStubs.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text.input.internal
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+
+internal actual fun CharSequence.codePointAt(index: Int): Int = implementedInJetBrainsFork()
+
+internal actual fun charCount(codePoint: Int): Int = implementedInJetBrainsFork()
+
+internal actual fun CharSequence.codePointBefore(index: Int): Int = implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/DesktopTextInputSession.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/DesktopTextInputSession.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/DesktopTextFieldMagnifier.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/DesktopTextFieldMagnifier.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/MutatorMutex.jvm.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/MutatorMutex.jvm.kt
new file mode 100644
index 0000000..d451c90
--- /dev/null
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/MutatorMutex.jvm.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class MutationInterruptedException actual constructor() :
+    CancellationException("Mutation interrupted") {
+    override fun fillInStackTrace(): Throwable {
+        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
+        stackTrace = emptyArray()
+        return this
+    }
+}
diff --git a/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.jvm.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.jvm.kt
new file mode 100644
index 0000000..807aa9a
--- /dev/null
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.jvm.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gestures
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal actual constructor() :
+    CancellationException("Anchored drag finished") {
+    override fun fillInStackTrace(): Throwable {
+        stackTrace = emptyArray()
+        return this
+    }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun assertOnJvm(statement: Boolean, message: () -> String): Unit {
+    assert(statement, message)
+}
diff --git a/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.jvm.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.jvm.kt
new file mode 100644
index 0000000..6037e0e
--- /dev/null
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.jvm.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.internal
+
+@Suppress("ACTUAL_WITHOUT_EXPECT") // https://youtrack.jetbrains.com/issue/KT-37316
+internal actual typealias JvmSynchronized = kotlin.jvm.Synchronized
diff --git a/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.jvm.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.jvm.kt
new file mode 100644
index 0000000..9ed6841
--- /dev/null
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.jvm.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.layout
+
+internal actual inline fun measureNanoTime(doMeasure: () -> Unit): Long {
+    return kotlin.system.measureNanoTime(doMeasure)
+}
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/MediaType.jvmStubs.kt b/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/MediaType.jvmStubs.kt
deleted file mode 100644
index 9bb0b64..0000000
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/MediaType.jvmStubs.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.content
-
-import java.awt.datatransfer.DataFlavor
-
-actual class MediaType internal constructor(val dataFlavor: DataFlavor) {
-
-    actual constructor(representation: String) : this(DataFlavor(representation))
-
-    actual val representation: String = dataFlavor.mimeType
-
-    actual companion object {
-        actual val Text: MediaType = MediaType(DataFlavor.stringFlavor)
-
-        actual val PlainText: MediaType = MediaType(DataFlavor.getTextPlainUnicodeFlavor())
-
-        actual val HtmlText: MediaType = MediaType(DataFlavor.allHtmlFlavor)
-
-        actual val Image: MediaType = MediaType(DataFlavor.imageFlavor)
-
-        actual val All: MediaType = MediaType(DataFlavor("*/*"))
-    }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is MediaType) return false
-
-        return dataFlavor == other.dataFlavor
-    }
-
-    override fun hashCode(): Int {
-        return dataFlavor.hashCode()
-    }
-
-    override fun toString(): String {
-        return "MediaType(" + "dataFlavor=$dataFlavor, " + "representation='$representation')"
-    }
-}
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.jvmStubs.kt b/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.jvmStubs.kt
deleted file mode 100644
index 0f012d2..0000000
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.jvmStubs.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.content
-
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.implementedInJetBrainsFork
-import androidx.compose.ui.platform.ClipEntry
-import java.awt.datatransfer.Transferable
-
-@ExperimentalFoundationApi
-actual class PlatformTransferableContent internal constructor(val transferable: Transferable)
-
-@ExperimentalFoundationApi
-actual fun TransferableContent.hasMediaType(mediaType: MediaType): Boolean =
-    implementedInJetBrainsFork()
-
-internal actual fun ClipEntry.readPlainText(): String? = implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt b/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
deleted file mode 100644
index 18b1f9a..0000000
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.text
-
-import androidx.compose.foundation.implementedInJetBrainsFork
-
-internal actual fun String.findPrecedingBreak(index: Int): Int = implementedInJetBrainsFork()
-
-internal actual fun String.findFollowingBreak(index: Int): Int = implementedInJetBrainsFork()
diff --git a/compose/integration-tests/demos/build.gradle b/compose/integration-tests/demos/build.gradle
index 7d2fed8..8622500 100644
--- a/compose/integration-tests/demos/build.gradle
+++ b/compose/integration-tests/demos/build.gradle
@@ -46,5 +46,6 @@
 ApkCopyHelperKt.setupAppApkCopy(project, "release")
 
 android {
+    compileSdk 35
     namespace "androidx.compose.integration.demos"
 }
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
index 6210ebe..a663c70 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
@@ -75,7 +75,7 @@
         val rootDemo =
             when (val demoName = intent.getStringExtra(DEMO_NAME)) {
                 null -> AllDemosCategory
-                else -> requireDemo(demoName, Navigator.findDemo(AllDemosCategory, demoName))
+                else -> Navigator.searchAllDemos(demoName)
             }
 
         ComposeView(this)
@@ -137,11 +137,6 @@
 
     companion object {
         const val DEMO_NAME = "demoname"
-
-        internal fun requireDemo(demoName: String, demo: Demo?) =
-            requireNotNull(demo) {
-                "No demo called \"$demoName\" could be found. Note substring matches are allowed."
-            }
     }
 }
 
@@ -166,7 +161,9 @@
     SideEffect {
         WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !isDarkMode
         WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars = !isDarkMode
+        @Suppress("deprecation")
         window.statusBarColor = Color.Transparent.toArgb()
+        @Suppress("deprecation")
         window.navigationBarColor = Color.Transparent.toArgb()
     }
     MaterialTheme(colorScheme = colorScheme, content = content)
@@ -245,32 +242,58 @@
                     require(restored.isNotEmpty()) { "no restored items" }
                     val backStack =
                         restored.mapTo(mutableListOf()) {
-                            requireNotNull(findDemo(rootDemo, it, exact = true)) { "no root demo" }
+                            requireNotNull(findDemo(rootDemo, it)) { "no root demo" }
                         }
                     val initial = backStack.removeAt(backStack.lastIndex)
                     Navigator(backDispatcher, launchActivityDemo, rootDemo, initial, backStack)
                 }
             )
 
-        fun findDemo(demo: Demo, title: String, exact: Boolean = false): Demo? {
-            if (exact) {
-                if (demo.title == title) {
-                    return demo
-                }
-            } else {
-                if (demo.title.contains(title)) {
-                    return demo
-                }
-            }
+        fun findDemo(demo: Demo, title: String): Demo? {
+            if (demo.title == title) return demo
             if (demo is DemoCategory) {
                 demo.demos.forEach { child ->
-                    findDemo(child, title, exact)?.let {
+                    findDemo(child, title)?.let {
                         return it
                     }
                 }
             }
             return null
         }
+
+        fun searchAllDemos(demoName: String): Demo {
+            val demos = mutableListOf<Demo>()
+            demos.addDemos(AllDemosCategory, demoName, exact = true)
+            if (demos.size == 1) return demos.single()
+
+            require(demos.isEmpty()) {
+                "${demos.size} demos have the demo name \"$demoName\", " +
+                    "can't disambiguate between them."
+            }
+
+            demos.addDemos(AllDemosCategory, demoName, exact = false)
+            if (demos.size == 1) return demos.single()
+
+            val errorMessage =
+                if (demos.isEmpty()) {
+                    "No demo called \"$demoName\" could be found. " +
+                        "Note substring matches are allowed."
+                } else {
+                    "Found multiple demos matching the substring \"$demoName\", " +
+                        "please use a more specific substring. " +
+                        "Matching demo names: ${demos.joinToString { "\"${it.title}\"" }}"
+                }
+            throw IllegalArgumentException(errorMessage)
+        }
+
+        private fun MutableList<Demo>.addDemos(demo: Demo, title: String, exact: Boolean = false) {
+            if ((exact && demo.title == title) || (!exact && demo.title.contains(title))) {
+                add(demo)
+            }
+            if (demo is DemoCategory) {
+                demo.demos.forEach { addDemos(it, title, exact) }
+            }
+        }
     }
 }
 
diff --git a/compose/integration-tests/docs-snippets/build.gradle b/compose/integration-tests/docs-snippets/build.gradle
index e3ff228..7b9584b 100644
--- a/compose/integration-tests/docs-snippets/build.gradle
+++ b/compose/integration-tests/docs-snippets/build.gradle
@@ -70,6 +70,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.integration.docs"
 }
 
diff --git a/compose/integration-tests/hero/benchmark/build.gradle b/compose/integration-tests/hero/benchmark/build.gradle
index 86e2872..22f988f 100644
--- a/compose/integration-tests/hero/benchmark/build.gradle
+++ b/compose/integration-tests/hero/benchmark/build.gradle
@@ -56,6 +56,8 @@
 }
 
 android {
+    compileSdk 35
+
     namespace "androidx.compose.integration.hero.benchmark"
 }
 
diff --git a/compose/integration-tests/hero/hero-implementation/build.gradle b/compose/integration-tests/hero/hero-implementation/build.gradle
index 690088e..2543f87 100644
--- a/compose/integration-tests/hero/hero-implementation/build.gradle
+++ b/compose/integration-tests/hero/hero-implementation/build.gradle
@@ -22,6 +22,7 @@
 }
 
 android {
+    compileSdk 35
     namespace 'androidx.compose.integration.hero.implementation'
 
     lint {
@@ -59,4 +60,4 @@
     implementation(project(":compose:runtime:runtime-tracing"))
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:ui:ui-tooling"))
-}
\ No newline at end of file
+}
diff --git a/compose/integration-tests/hero/macrobenchmark-target/build.gradle b/compose/integration-tests/hero/macrobenchmark-target/build.gradle
index 181d642..acb2d78b 100644
--- a/compose/integration-tests/hero/macrobenchmark-target/build.gradle
+++ b/compose/integration-tests/hero/macrobenchmark-target/build.gradle
@@ -22,6 +22,7 @@
 }
 
 android {
+    compileSdkVersion 35
     namespace "androidx.compose.integration.hero.macrobenchmark.target"
 
     buildTypes {
diff --git a/compose/integration-tests/macrobenchmark-target/build.gradle b/compose/integration-tests/macrobenchmark-target/build.gradle
index 8e43d97..63e23a1 100644
--- a/compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/compose/integration-tests/macrobenchmark-target/build.gradle
@@ -13,6 +13,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.integration.macrobenchmark.target"
     buildTypes {
         release {
@@ -29,6 +30,7 @@
     implementation 'androidx.viewpager2:viewpager2:1.0.0'
 
     implementation(libs.kotlinStdlib)
+    implementation(libs.material)
     implementation(project(":activity:activity-compose"))
     implementation("androidx.appcompat:appcompat:1.4.1")
     implementation("androidx.cardview:cardview:1.0.0")
@@ -41,6 +43,7 @@
     implementation(project(":compose:foundation:foundation-layout"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":compose:material3:material3"))
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:runtime:runtime-tracing"))
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index 0eadc27..d249e08 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -20,12 +20,11 @@
     <uses-permission android:name="android.permission.INTERNET"/>
 
     <application
-        android:label="Jetpack Compose Macrobenchmark Target"
         android:allowBackup="false"
-        android:supportsRtl="true"
         android:icon="@mipmap/ic_launcher"
+        android:label="Jetpack Compose Macrobenchmark Target"
+        android:supportsRtl="true"
         tools:ignore="GoogleAppIndexingWarning">
-
         <!-- Profileable to enable macrobenchmark profiling -->
         <profileable android:shell="true"/>
 
@@ -37,8 +36,8 @@
          -->
         <activity
             android:name=".TrivialStartupActivity"
-            android:label="C Trivial"
-            android:exported="true">
+            android:exported="true"
+            android:label="C Trivial">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -50,8 +49,8 @@
         </activity>
         <activity
             android:name=".StaticScrollingContentWithChromeInitialCompositionActivity"
-            android:label="C StaticScrollingWithChrome Init"
-            android:exported="true">
+            android:exported="true"
+            android:label="C StaticScrollingWithChrome Init">
             <intent-filter>
                 <action android:name="androidx.compose.integration.macrobenchmark.target.STATIC_SCROLLING_CONTENT_WITH_CHROME_INITIAL_COMPOSITION_ACTIVITY" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -63,8 +62,8 @@
         </activity>
         <activity
             android:name=".TrivialStartupTracingActivity"
-            android:label="C TrivialTracing"
-            android:exported="true">
+            android:exported="true"
+            android:label="C TrivialTracing">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -92,8 +91,8 @@
         </activity>
         <activity
             android:name=".LazyColumnActivity"
-            android:label="C LazyColumn"
-            android:exported="true">
+            android:exported="true"
+            android:label="C LazyColumn">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -105,8 +104,8 @@
         </activity>
         <activity
             android:name=".FrameExperimentActivity"
-            android:label="FrameExp"
-            android:exported="true">
+            android:exported="true"
+            android:label="FrameExp">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -176,7 +175,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-
         <activity
             android:name=".ViewPagerActivity"
             android:exported="true"
@@ -186,7 +184,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-
         <activity
             android:name=".RecyclerViewAsCarouselActivity"
             android:exported="true"
@@ -196,7 +193,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-
         <activity
             android:name=".PagerAsCarouselActivity"
             android:exported="true"
@@ -206,7 +202,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-
         <activity
             android:name=".PagerActivity"
             android:exported="true"
@@ -216,7 +211,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-
         <activity
             android:name=".TrivialTracingActivity"
             android:exported="true">
@@ -225,7 +219,8 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity android:name=".AndroidViewListActivity"
+        <activity
+            android:name=".AndroidViewListActivity"
             android:exported="true"
             android:theme="@style/Theme.AppCompat">
             <intent-filter>
@@ -233,7 +228,8 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity android:name=".RecyclerViewListActivity"
+        <activity
+            android:name=".RecyclerViewListActivity"
             android:exported="true"
             android:theme="@style/Theme.AppCompat">
             <intent-filter>
@@ -241,11 +237,10 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        
-	<activity
+        <activity
             android:name=".VectorsListActivity"
-            android:label="Compose vectors list"
-            android:exported="true">
+            android:exported="true"
+            android:label="Compose vectors list">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -255,11 +250,10 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-
         <activity
             android:name=".CrossfadeActivity"
-            android:label="Compose Crossfade Benchmark"
-            android:exported="true">
+            android:exported="true"
+            android:label="Compose Crossfade Benchmark">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -268,9 +262,9 @@
                 <action android:name="androidx.compose.integration.macrobenchmark.target.CROSSFADE_ACTIVITY" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-	</activity>
-
-	<activity android:name=".PagerOfLazyGridActivity"
+        </activity>
+        <activity
+            android:name=".PagerOfLazyGridActivity"
             android:exported="true"
             android:theme="@style/Theme.AppCompat">
             <intent-filter>
@@ -278,5 +272,20 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".FormFillingActivity"
+            android:exported="true"
+            android:label="Compose Form Filling Benchmark"
+            android:launchMode="singleInstance"
+            android:theme="@style/Theme.AppCompat.Light">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="androidx.compose.integration.macrobenchmark.target.FORM_ACTIVITY" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/FormFillingActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/FormFillingActivity.kt
new file mode 100644
index 0000000..2958dfe
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/FormFillingActivity.kt
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark.target
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.EditText
+import android.widget.LinearLayout
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material.LocalTextStyle
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.customActions
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.fastRoundToInt
+import androidx.compose.ui.util.trace
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.Adapter
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+
+class FormFillingActivity : ComponentActivity() {
+    private lateinit var lazyListState: LazyListState
+    private lateinit var formView: FormView
+    private lateinit var type: String
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        val rowHeightDp: Dp
+        val fontSize: TextUnit
+        when (intent.getIntExtra(MODE, 0)) {
+            FRAME_MEASUREMENT_MODE -> {
+                // Larger number of rows to stress the system while measuring frame info.
+                rowHeightDp = 30.dp
+                fontSize = 5.sp
+            }
+            CREATE_ANI_MODE -> {
+                // Smaller number of rows so that we have no dropped frames.
+                rowHeightDp = 100.dp
+                fontSize = 10.sp
+            }
+            else -> error("Invalid Mode")
+        }
+
+        type = checkNotNull(intent.getStringExtra(TYPE)) { "No type specified." }
+        when (type) {
+            COMPOSE ->
+                setContent {
+                    lazyListState = rememberLazyListState()
+                    FormComposable(lazyListState, rowHeightDp, fontSize)
+                }
+            VIEW -> {
+                val rowHeightPx = rowHeightDp.value * resources.displayMetrics.densityDpi / 160f
+                formView = FormView(this, rowHeightPx, fontSize)
+                setContentView(formView)
+            }
+            else -> error("Unknown Type")
+        }
+    }
+
+    override fun onNewIntent(intent: Intent) {
+        when (type) {
+            COMPOSE -> lazyListState.requestScrollToItem(lazyListState.firstVisibleItemIndex + 100)
+            VIEW -> formView.scrollToPosition(formView.lastVisibleItemIndex + 100)
+            else -> error("Unknown Type")
+        }
+        super.onNewIntent(intent)
+    }
+
+    @Composable
+    private fun FormComposable(lazyListState: LazyListState, rowHeight: Dp, fontSize: TextUnit) {
+        val textStyle = LocalTextStyle.current.copy(fontSize = fontSize)
+        LazyColumn(state = lazyListState) {
+            items(data.size) { index ->
+                val person = data[index]
+                Row(
+                    modifier =
+                        Modifier.height(rowHeight).semantics {
+                            customActions =
+                                listOf(CustomAccessibilityAction("customAction") { false })
+                        }
+                ) {
+                    BasicTextField(
+                        value = person.title,
+                        onValueChange = { person.title = it },
+                        textStyle = textStyle
+                    )
+                    BasicTextField(
+                        value = person.firstName,
+                        onValueChange = { person.firstName = it },
+                        textStyle = textStyle
+                    )
+                    BasicTextField(
+                        value = person.middleName,
+                        onValueChange = { person.middleName = it },
+                        textStyle = textStyle
+                    )
+                    BasicTextField(
+                        value = person.lastName,
+                        onValueChange = { person.lastName = it },
+                        textStyle = textStyle
+                    )
+                    BasicTextField(
+                        value = person.age.toString(),
+                        onValueChange = { person.age = it.toInt() },
+                        textStyle = textStyle
+                    )
+                }
+            }
+        }
+    }
+
+    private class FormView(context: Context, rowHeight: Float, fontSize: TextUnit) :
+        RecyclerView(context) {
+        private val linearLayoutManager: LinearLayoutManager
+
+        init {
+            setHasFixedSize(true)
+            linearLayoutManager = LinearLayoutManager(context, VERTICAL, false)
+            layoutManager = linearLayoutManager
+            adapter = DemoAdapter(data, rowHeight, fontSize)
+        }
+
+        val lastVisibleItemIndex: Int
+            get() = linearLayoutManager.findLastVisibleItemPosition()
+
+        override fun createAccessibilityNodeInfo(): AccessibilityNodeInfo {
+            return trace(CREATE_ANI_TRACE) { super.createAccessibilityNodeInfo() }
+        }
+
+        override fun sendAccessibilityEvent(eventType: Int) {
+            return trace(ACCESSIBILITY_EVENT_TRACE) { super.sendAccessibilityEvent(eventType) }
+        }
+    }
+
+    private class RowView(context: Context, content: (RowView) -> Unit) : LinearLayout(context) {
+        init {
+            gravity = Gravity.CENTER_VERTICAL
+            content(this)
+        }
+
+        override fun createAccessibilityNodeInfo(): AccessibilityNodeInfo {
+            return trace(CREATE_ANI_TRACE) { super.createAccessibilityNodeInfo() }
+        }
+
+        override fun sendAccessibilityEvent(eventType: Int) {
+            return trace(ACCESSIBILITY_EVENT_TRACE) { super.sendAccessibilityEvent(eventType) }
+        }
+    }
+
+    @SuppressLint("AppCompatCustomView")
+    private class EditTextView(context: Context, fontSize: Float) : EditText(context) {
+        init {
+            textSize = fontSize
+            gravity = Gravity.CENTER_VERTICAL
+        }
+
+        fun replaceText(newText: String) {
+            text.replace(0, length(), newText, 0, newText.length)
+        }
+
+        override fun createAccessibilityNodeInfo(): AccessibilityNodeInfo {
+            return trace(CREATE_ANI_TRACE) { super.createAccessibilityNodeInfo() }
+        }
+
+        override fun sendAccessibilityEvent(eventType: Int) {
+            return trace(ACCESSIBILITY_EVENT_TRACE) { super.sendAccessibilityEvent(eventType) }
+        }
+    }
+
+    private class DemoAdapter(
+        val data: List<FormData>,
+        val rowHeightPx: Float,
+        textSize: TextUnit
+    ) : Adapter<DemoAdapter.DemoViewHolder>() {
+
+        private class DemoViewHolder(
+            val title: EditTextView,
+            val firstName: EditTextView,
+            val middleName: EditTextView,
+            val lastName: EditTextView,
+            val age: EditTextView,
+            itemRoot: View
+        ) : ViewHolder(itemRoot)
+
+        val textSize = textSize.value
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DemoViewHolder {
+            val title = EditTextView(parent.context, textSize)
+            val firstName = EditTextView(parent.context, textSize)
+            val middleName = EditTextView(parent.context, textSize)
+            val lastName = EditTextView(parent.context, textSize)
+            val age = EditTextView(parent.context, textSize)
+
+            return DemoViewHolder(
+                title,
+                firstName,
+                middleName,
+                lastName,
+                age,
+                RowView(parent.context) {
+                    it.minimumHeight = rowHeightPx.fastRoundToInt()
+                    it.addView(title)
+                    it.addView(firstName)
+                    it.addView(middleName)
+                    it.addView(lastName)
+                    it.addView(age)
+                }
+            )
+        }
+
+        override fun onBindViewHolder(holder: DemoViewHolder, position: Int) {
+            val formData = data.elementAt(position)
+            holder.title.replaceText(formData.title)
+            holder.firstName.replaceText(formData.firstName)
+            holder.middleName.replaceText(formData.middleName)
+            holder.lastName.replaceText(formData.lastName)
+            holder.age.replaceText(formData.age.toString())
+        }
+
+        override fun getItemCount(): Int = data.size
+    }
+
+    private data class FormData(
+        var title: String = "",
+        var firstName: String = "",
+        var middleName: String = "",
+        var lastName: String = "",
+        var age: Int = 0,
+    )
+
+    private companion object {
+        private const val TYPE = "TYPE"
+        private const val COMPOSE = "Compose"
+        private const val VIEW = "View"
+        private const val MODE = "MODE"
+        private const val CREATE_ANI_MODE = 1
+        private const val FRAME_MEASUREMENT_MODE = 2
+        private const val CREATE_ANI_TRACE = "createAccessibilityNodeInfo"
+        private const val ACCESSIBILITY_EVENT_TRACE = "sendAccessibilityEvent"
+        private val data by lazy {
+            List(200000) {
+                FormData(
+                    title = "Mr",
+                    firstName = "John $it",
+                    middleName = "Ace $it",
+                    lastName = "Doe $it",
+                    age = it
+                )
+            }
+        }
+    }
+}
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FormFillingBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FormFillingBenchmark.kt
new file mode 100644
index 0000000..9b05e73
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FormFillingBenchmark.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark
+
+import android.app.Instrumentation
+import android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES
+import android.content.Intent
+import android.os.Build.VERSION_CODES.N
+import android.provider.Settings.Secure
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.TraceSectionMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.Configurator
+import androidx.test.uiautomator.UiDevice
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@SdkSuppress(minSdkVersion = N)
+@LargeTest
+@RunWith(Parameterized::class)
+class FormFillingBenchmark(private var talkbackEnabled: Boolean, private val type: String) {
+
+    @get:Rule val benchmarkRule = MacrobenchmarkRule()
+    private lateinit var instrumentation: Instrumentation
+    private var previousTalkbackSettings: String? = null
+    private lateinit var device: UiDevice
+
+    @Test
+    fun createAccessibilityNodeInfo() {
+        if (!talkbackEnabled) return
+        benchmarkRule.measureRepeated(
+            packageName = PACKAGE,
+            metrics =
+                @OptIn(ExperimentalMetricApi::class)
+                listOf(
+                    TraceSectionMetric(
+                        sectionName = CREATE_ANI_TRACE,
+                        mode = TraceSectionMetric.Mode.Sum
+                    ),
+                    TraceSectionMetric(
+                        sectionName = ACCESSIBILITY_EVENT_TRACE,
+                        mode = TraceSectionMetric.Mode.Sum
+                    )
+                ),
+            iterations = 10,
+            setupBlock = {
+                if (iteration == 0) {
+                    startActivityAndWait(
+                        Intent()
+                            .setAction("$PACKAGE.$ACTIVITY")
+                            .putExtra(TYPE, type)
+                            .putExtra(MODE, CREATE_ANI_MODE)
+                    )
+                    device.waitForIdle()
+
+                    // Run one iteration to allow the scroll position to stabilize, and to remove
+                    // the effect of the initial frame which draws the accessibility focus box.
+                    performScrollAndWait(millis = 10_000)
+                }
+            },
+            measureBlock = {
+
+                // Scroll and pause to allow all frames to complete, for the accessibility events
+                // to be sent, for talkback to assign focus, and finally for talkback to trigger
+                // createAccessibilityNodeInfo calls which is the thing we want to measure.
+                performScrollAndWait(millis = 10_000)
+            }
+        )
+    }
+
+    @Test
+    fun frameInfo() {
+        benchmarkRule.measureRepeated(
+            packageName = PACKAGE,
+            metrics = listOf(FrameTimingMetric()),
+            iterations = 10,
+            setupBlock = {
+                if (iteration == 0) {
+                    startActivityAndWait(
+                        Intent()
+                            .setAction("$PACKAGE.$ACTIVITY")
+                            .putExtra(TYPE, type)
+                            .putExtra(MODE, FRAME_MEASUREMENT_MODE)
+                    )
+                    Thread.sleep(2_000)
+                    device.waitForIdle()
+
+                    // Run one iteration to allow the scroll position to stabilize, and to remove
+                    // the effect of the initial frame which draws the accessibility focus box.
+                    performScrollAndWait(millis = 20)
+                }
+            },
+            measureBlock = {
+                // Instead of using an animation to scroll (Where the number of frames triggered
+                // is not deterministic, we attempt to scroll 100 times with an aim to scroll once
+                // every frame deadline of 20ms.
+                repeat(100) { performScrollAndWait(millis = 20) }
+                Thread.sleep(10_000)
+            }
+        )
+    }
+
+    @Before
+    fun setUp() {
+        Configurator.getInstance().uiAutomationFlags = FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES
+        instrumentation = InstrumentationRegistry.getInstrumentation()
+        device = UiDevice.getInstance(instrumentation)
+        if (talkbackEnabled) {
+            previousTalkbackSettings = instrumentation.enableTalkback()
+            // Wait for talkback to turn on.
+            Thread.sleep(2_000)
+        }
+    }
+
+    @After
+    fun tearDown() {
+        if (talkbackEnabled) {
+            instrumentation.disableTalkback(previousTalkbackSettings)
+            // Wait for talkback to turn off.
+            Thread.sleep(2_000)
+        }
+    }
+
+    private fun performScrollAndWait(millis: Long) {
+        // We don't use UI Automator to scroll because UI Automator itself is an accessibility
+        // service, and this affects the benchmark. Instead we send an event to the activity that
+        // requests it to scroll.
+        instrumentation.context.startActivity(
+            Intent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setAction("$PACKAGE.$ACTIVITY")
+        )
+
+        // Pause to allow all frames to complete, for the accessibility events to be sent,
+        // for talkback to assign focus, and finally for talkback to trigger
+        // createAccessibilityNodeInfo calls which is the thing we want to measure.
+        Thread.sleep(millis)
+    }
+
+    companion object {
+        private const val PACKAGE = "androidx.compose.integration.macrobenchmark.target"
+        private const val ACTIVITY = "FORM_ACTIVITY"
+        private const val TYPE = "TYPE"
+        private const val COMPOSE = "Compose"
+        private const val VIEW = "View"
+        const val MODE = "MODE"
+        const val CREATE_ANI_MODE = 1
+        const val FRAME_MEASUREMENT_MODE = 2
+        const val CREATE_ANI_TRACE = "createAccessibilityNodeInfo"
+        const val ACCESSIBILITY_EVENT_TRACE = "sendAccessibilityEvent"
+
+        // Manually set up LastPass on the device and use these parameters when running locally.
+        // @Parameterized.Parameters(name = "LastPassEnabled=true, type={1}")
+        // @JvmStatic
+        // fun parameters() = mutableListOf<Array<Any>>().also {
+        //    for (type in arrayOf(COMPOSE, VIEW)) {
+        //        it.add(arrayOf(false, type))
+        //    }
+        // }
+
+        @Parameterized.Parameters(name = "TalkbackEnabled={0}, type={1}")
+        @JvmStatic
+        fun parameters() =
+            mutableListOf<Array<Any>>().also {
+                for (talkbackEnabled in arrayOf(false, true)) {
+                    for (type in arrayOf(COMPOSE, VIEW)) {
+                        it.add(arrayOf(talkbackEnabled, type))
+                    }
+                }
+            }
+    }
+}
+
+private fun Instrumentation.enableTalkback(): String? {
+    val talkback =
+        "com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService"
+    val previousTalkbackSettings =
+        Secure.getString(context.contentResolver, Secure.ENABLED_ACCESSIBILITY_SERVICES)
+    UiDevice.getInstance(this)
+        .executeShellCommand("settings put secure enabled_accessibility_services $talkback")
+    return previousTalkbackSettings
+}
+
+private fun Instrumentation.disableTalkback(previousTalkbackSettings: String? = null): String {
+    return UiDevice.getInstance(this)
+        .executeShellCommand(
+            if (previousTalkbackSettings == null || previousTalkbackSettings == "") {
+                "settings delete secure enabled_accessibility_services"
+            } else {
+                "settings put secure enabled_accessibility_services $previousTalkbackSettings"
+            }
+        )
+}
diff --git a/compose/integration-tests/material-catalog/build.gradle b/compose/integration-tests/material-catalog/build.gradle
index 973f875..6d7990a 100644
--- a/compose/integration-tests/material-catalog/build.gradle
+++ b/compose/integration-tests/material-catalog/build.gradle
@@ -32,6 +32,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         applicationId "androidx.compose.material.catalog"
         versionCode 2400
@@ -53,6 +54,7 @@
     implementation project(":compose:foundation:foundation-layout")
     implementation project(":compose:ui:ui")
     implementation project(":compose:material:material")
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation project(":compose:material3:material3")
     implementation project(":compose:material:material:integration-tests:material-catalog")
     implementation project(":compose:material3:material3:integration-tests:material3-catalog")
diff --git a/compose/material/material-navigation/build.gradle b/compose/material/material-navigation/build.gradle
index 90440bf..26c903c 100644
--- a/compose/material/material-navigation/build.gradle
+++ b/compose/material/material-navigation/build.gradle
@@ -48,5 +48,8 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material.navigation"
+    // TODO(b/349411310)?
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/material/material-navigation/samples/build.gradle b/compose/material/material-navigation/samples/build.gradle
index b0a9deb..20b3d14d 100644
--- a/compose/material/material-navigation/samples/build.gradle
+++ b/compose/material/material-navigation/samples/build.gradle
@@ -48,5 +48,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material.navigation.samples"
 }
diff --git a/compose/material/material-ripple/benchmark/build.gradle b/compose/material/material-ripple/benchmark/build.gradle
index 6cc659b..72173e4 100644
--- a/compose/material/material-ripple/benchmark/build.gradle
+++ b/compose/material/material-ripple/benchmark/build.gradle
@@ -46,6 +46,8 @@
 }
 
 android {
+    compileSdk 35
+
     namespace "androidx.compose.material.ripple.benchmark"
 }
 
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index 7189ea2..417619c 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -33,19 +33,20 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
     sourceSets {
         commonMain {
             dependencies {
-                implementation(libs.kotlinStdlibCommon)
-                api("androidx.compose.foundation:foundation:1.6.0")
+                implementation(libs.kotlinStdlib)
+                api(project(":compose:foundation:foundation"))
                 api(project(":compose:runtime:runtime"))
 
-                implementation("androidx.collection:collection:1.4.0")
-                implementation("androidx.compose.animation:animation:1.6.0")
-                implementation("androidx.compose.ui:ui-util:1.6.0")
+                implementation(project(":collection:collection"))
+                implementation(project(":compose:animation:animation"))
+                implementation(project(":compose:ui:ui-util"))
             }
         }
 
@@ -66,11 +67,16 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-                implementation(libs.kotlinStdlib)
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -104,8 +110,10 @@
     inceptionYear = "2020"
     description = "Material ripple used to build interactive components"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material.ripple"
 }
diff --git a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/RippleHostView.android.kt b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/RippleHostView.android.kt
index 27114af..24815c3a 100644
--- a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/RippleHostView.android.kt
+++ b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/RippleHostView.android.kt
@@ -25,7 +25,6 @@
 import android.os.Build
 import android.view.View
 import android.view.animation.AnimationUtils
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.ui.geometry.Size
@@ -366,7 +365,6 @@
     @RequiresApi(Build.VERSION_CODES.M)
     private object MRadiusHelper {
         /** Sets the [radius] for the given [ripple]. */
-        @DoNotInline
         fun setRadius(ripple: RippleDrawable, radius: Int) {
             ripple.radius = radius
         }
diff --git a/compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.commonStubs.kt b/compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.commonStubs.kt
new file mode 100644
index 0000000..75e04663
--- /dev/null
+++ b/compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.commonStubs.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material.ripple
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun implementedInJetBrainsFork(): Nothing =
+    throw NotImplementedError(
+        """
+        Implemented only in JetBrains fork.
+        Please use `org.jetbrains.compose.material:material-ripple` package instead.
+        """
+            .trimIndent()
+    )
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/Ripple.jvmStubs.kt b/compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/Ripple.commonStubs.kt
similarity index 100%
rename from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/Ripple.jvmStubs.kt
rename to compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/Ripple.commonStubs.kt
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
deleted file mode 100644
index a5cf43e..0000000
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material.ripple
-
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
-    throw NotImplementedError(
-        """
-        Implemented only in JetBrains fork.
-        Please use `org.jetbrains.compose.material:material` package instead.
-        """
-            .trimIndent()
-    )
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 70c4afc..db7832e 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -595,6 +595,7 @@
   }
 
   public final class OutlinedTextFieldKt {
+    method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @Deprecated @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @Deprecated @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
@@ -646,6 +647,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class RippleConfiguration {
+    ctor public RippleConfiguration();
     ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
     method public long getColor();
     method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
@@ -693,6 +695,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
@@ -930,6 +933,7 @@
   }
 
   public final class TextFieldKt {
+    method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @Deprecated @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @Deprecated @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 70c4afc..db7832e 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -595,6 +595,7 @@
   }
 
   public final class OutlinedTextFieldKt {
+    method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @Deprecated @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @Deprecated @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
@@ -646,6 +647,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class RippleConfiguration {
+    ctor public RippleConfiguration();
     ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
     method public long getColor();
     method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
@@ -693,6 +695,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
@@ -930,6 +933,7 @@
   }
 
   public final class TextFieldKt {
+    method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @Deprecated @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
     method @Deprecated @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors);
diff --git a/compose/material/material/benchmark/build.gradle b/compose/material/material/benchmark/build.gradle
index 8b39308..d727fef 100644
--- a/compose/material/material/benchmark/build.gradle
+++ b/compose/material/material/benchmark/build.gradle
@@ -49,6 +49,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material.benchmark"
 }
 
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index a550d824..b76e4f3 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -33,25 +33,25 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
     sourceSets {
         commonMain {
             dependencies {
-                implementation(libs.kotlinStdlibCommon)
-                api("androidx.compose.animation:animation-core:1.6.0")
+                implementation(libs.kotlinStdlib)
+                api(project(":compose:animation:animation-core"))
                 api(project(":compose:foundation:foundation"))
                 api(project(":compose:ui:ui-text"))
-                api("androidx.compose.material:material-icons-core:1.6.7")
                 api(project(":compose:material:material-ripple"))
                 api(project(":compose:runtime:runtime"))
-                api("androidx.compose.ui:ui:1.6.0")
+                api(project(":compose:ui:ui"))
 
                 implementation(project(":compose:animation:animation-core"))
-                implementation("androidx.compose.animation:animation:1.6.0")
-                implementation("androidx.compose.foundation:foundation-layout:1.6.0")
-                implementation("androidx.compose.ui:ui-util:1.6.0")
+                implementation(project(":compose:animation:animation"))
+                implementation(project(":compose:foundation:foundation-layout"))
+                implementation(project(":compose:ui:ui-util"))
             }
         }
 
@@ -69,7 +69,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
 
                 // TODO: remove next 3 dependencies when b/202810604 is fixed
@@ -79,11 +79,16 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-                implementation(libs.kotlinStdlib)
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -130,11 +135,13 @@
     inceptionYear = "2018"
     description = "Compose Material Design Components library"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:material:material:material-samples"))
 }
 
 // Screenshot tests related setup
 android {
+    compileSdk 35
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/compose/material/material"
     namespace "androidx.compose.material"
diff --git a/compose/material/material/integration-tests/material-catalog/build.gradle b/compose/material/material/integration-tests/material-catalog/build.gradle
index ba6ac0a..31c9cef 100644
--- a/compose/material/material/integration-tests/material-catalog/build.gradle
+++ b/compose/material/material/integration-tests/material-catalog/build.gradle
@@ -49,5 +49,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material.catalog.library"
 }
diff --git a/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt b/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt
index 0bfc7bc..1a2892f 100644
--- a/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt
+++ b/compose/material/material/integration-tests/material-catalog/src/main/java/androidx/compose/material/catalog/library/model/Examples.kt
@@ -58,7 +58,7 @@
 import androidx.compose.material.samples.OneLineRtlLtrListItems
 import androidx.compose.material.samples.OutlinedButtonSample
 import androidx.compose.material.samples.OutlinedChipWithIconSample
-import androidx.compose.material.samples.OutlinedTextFieldSample
+import androidx.compose.material.samples.OutlinedTextFieldWithInitialValueAndSelection
 import androidx.compose.material.samples.PasswordTextField
 import androidx.compose.material.samples.RadioButtonSample
 import androidx.compose.material.samples.RadioGroupSample
@@ -82,11 +82,11 @@
 import androidx.compose.material.samples.TextAndIconTabs
 import androidx.compose.material.samples.TextArea
 import androidx.compose.material.samples.TextButtonSample
-import androidx.compose.material.samples.TextFieldSample
 import androidx.compose.material.samples.TextFieldWithErrorState
 import androidx.compose.material.samples.TextFieldWithHelperMessage
 import androidx.compose.material.samples.TextFieldWithHideKeyboardOnImeAction
 import androidx.compose.material.samples.TextFieldWithIcons
+import androidx.compose.material.samples.TextFieldWithInitialValueAndSelection
 import androidx.compose.material.samples.TextFieldWithPlaceholder
 import androidx.compose.material.samples.TextTabs
 import androidx.compose.material.samples.ThreeLineListItems
@@ -664,11 +664,11 @@
                 SimpleTextFieldSample()
             },
             Example(
-                name = "TextFieldSample",
+                name = "TextFieldWithInitialValueAndSelection",
                 description = TextFieldsExampleDescription,
                 sourceUrl = TextFieldsExampleSourceUrl
             ) {
-                TextFieldSample()
+                TextFieldWithInitialValueAndSelection()
             },
             Example(
                 name = "SimpleOutlinedTextFieldSample",
@@ -678,11 +678,11 @@
                 SimpleOutlinedTextFieldSample()
             },
             Example(
-                name = "OutlinedTextFieldSample",
+                name = "OutlinedTextFieldWithInitialValueAndSelection",
                 description = TextFieldsExampleDescription,
                 sourceUrl = TextFieldsExampleSourceUrl
             ) {
-                OutlinedTextFieldSample()
+                OutlinedTextFieldWithInitialValueAndSelection()
             },
             Example(
                 name = "TextFieldWithIcons",
diff --git a/compose/material/material/integration-tests/material-demos/build.gradle b/compose/material/material/integration-tests/material-demos/build.gradle
index c51c55c..a9bb660 100644
--- a/compose/material/material/integration-tests/material-demos/build.gradle
+++ b/compose/material/material/integration-tests/material-demos/build.gradle
@@ -37,5 +37,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material.demos"
 }
diff --git a/compose/material/material/integration-tests/material-demos/lint-baseline.xml b/compose/material/material/integration-tests/material-demos/lint-baseline.xml
index c9e4f70..40e3012 100644
--- a/compose/material/material/integration-tests/material-demos/lint-baseline.xml
+++ b/compose/material/material/integration-tests/material-demos/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha04" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha04)" variant="all" version="8.3.0-alpha04">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
         message="field elevations with type List&lt;Dp>: replace with FloatList"
-        errorLine1="private val elevations = listOf("
-        errorLine2="^">
+        errorLine1="private val elevations = listOf(0.dp, 1.dp, 2.dp, 3.dp, 4.dp, 6.dp, 8.dp, 12.dp, 16.dp, 24.dp)"
+        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/material/demos/ElevationDemo.kt"/>
     </issue>
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
index 04dac12..69dc9d9 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/DynamicThemeActivity.kt
@@ -64,7 +64,9 @@
         setContent {
             val palette = interpolateTheme(scrollFraction.floatValue)
             val darkenedPrimary = palette.darkenedPrimary
+            @Suppress("DEPRECATION")
             window.statusBarColor = darkenedPrimary
+            @Suppress("DEPRECATION")
             window.navigationBarColor = darkenedPrimary
 
             DynamicThemeApp(scrollFraction, palette)
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
index 4fbb9e4..ea887e1 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/MaterialTextField.kt
@@ -51,7 +51,6 @@
 import androidx.compose.material.samples.PasswordTextField
 import androidx.compose.material.samples.SimpleOutlinedTextFieldSample
 import androidx.compose.material.samples.TextArea
-import androidx.compose.material.samples.TextFieldSample
 import androidx.compose.material.samples.TextFieldWithErrorState
 import androidx.compose.material.samples.TextFieldWithHelperMessage
 import androidx.compose.material.samples.TextFieldWithHideKeyboardOnImeAction
@@ -104,10 +103,6 @@
             TextFieldWithHideKeyboardOnImeAction()
         }
         item {
-            Text("TextFieldValue overload")
-            TextFieldSample()
-        }
-        item {
             Text("Outlined text field with custom shape")
             CustomShapeOutlinedTextFieldSample()
         }
diff --git a/compose/material/material/lint-baseline.xml b/compose/material/material/lint-baseline.xml
index d11a145..b2ceca1 100644
--- a/compose/material/material/lint-baseline.xml
+++ b/compose/material/material/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -119,24 +119,6 @@
     </issue>
 
     <issue
-        id="ComposableNaming"
-        message="Composable functions with a return type should start with a lowercase letter"
-        errorLine1="    fun Url("
-        errorLine2="        ~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material/Text.kt"/>
-    </issue>
-
-    <issue
-        id="ComposableNaming"
-        message="Composable functions with a return type should start with a lowercase letter"
-        errorLine1="    fun Clickable("
-        errorLine2="        ~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material/Text.kt"/>
-    </issue>
-
-    <issue
         id="PrimitiveInCollection"
         message="variable crossAxisSizes with type List&lt;Integer>: replace with IntList"
         errorLine1="        val crossAxisSizes = mutableListOf&lt;Int>()"
@@ -165,7 +147,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="return type Map&lt;T, Float> of getAnchors$lint_module: replace with ObjectFloatMap"
+        message="return type Map&lt;T, Float> of getAnchors$material: replace with ObjectFloatMap"
         errorLine1="    internal val anchors = mutableMapOf&lt;T, Float>()"
         errorLine2="                 ~~~~~~~">
         <location
@@ -202,8 +184,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable tickFractions with type List&lt;? extends Float>: replace with FloatList"
-        errorLine1="    val tickFractions = remember(steps) {"
-        errorLine2="    ^">
+        errorLine1="    val tickFractions = remember(steps) { stepsToTickFractions(steps) }"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/Slider.kt"/>
     </issue>
@@ -211,8 +193,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable tickFractions with type List&lt;? extends Float>: replace with FloatList"
-        errorLine1="    val tickFractions = remember(steps) {"
-        errorLine2="    ^">
+        errorLine1="    val tickFractions = remember(steps) { stepsToTickFractions(steps) }"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/Slider.kt"/>
     </issue>
@@ -265,15 +247,15 @@
     <issue
         id="PrimitiveInCollection"
         message="variable anchors with type Map&lt;Float, DismissValue>: replace with FloatObjectMap"
-        errorLine1="    val anchors = mutableMapOf(0f to Default)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="        val anchors = mutableMapOf(0f to Default)"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt"/>
     </issue>
 
     <issue
         id="PrimitiveInCollection"
-        message="method setAnchors$lint_module has parameter &lt;set-?> with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        message="method setAnchors$material has parameter anchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;Float, T>())"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -282,7 +264,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="return type Map&lt;Float, T> of getAnchors$lint_module: replace with FloatObjectMap"
+        message="return type Map&lt;Float, T> of getAnchors$material: replace with FloatObjectMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;Float, T>())"
         errorLine2="                 ~~~~~~~">
         <location
@@ -291,7 +273,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method ensureInit$lint_module has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        message="method ensureInit$material has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
         errorLine1="    internal fun ensureInit(newAnchors: Map&lt;Float, T>) {"
         errorLine2="                                        ~~~~~~~~~~~~~">
         <location
@@ -300,18 +282,18 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method processNewAnchors$lint_module has parameter oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        oldAnchors: Map&lt;Float, T>,"
-        errorLine2="                    ~~~~~~~~~~~~~">
+        message="method processNewAnchors$material has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        errorLine1="    internal suspend fun processNewAnchors(oldAnchors: Map&lt;Float, T>, newAnchors: Map&lt;Float, T>) {"
+        errorLine2="                                                                                  ~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/Swipeable.kt"/>
     </issue>
 
     <issue
         id="PrimitiveInCollection"
-        message="method processNewAnchors$lint_module has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        newAnchors: Map&lt;Float, T>"
-        errorLine2="                    ~~~~~~~~~~~~~">
+        message="method processNewAnchors$material has parameter oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        errorLine1="    internal suspend fun processNewAnchors(oldAnchors: Map&lt;Float, T>, newAnchors: Map&lt;Float, T>) {"
+        errorLine2="                                                       ~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/Swipeable.kt"/>
     </issue>
@@ -337,8 +319,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        val oldAnchors = state.anchors"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            val oldAnchors = state.anchors"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/Swipeable.kt"/>
     </issue>
@@ -346,8 +328,8 @@
     <issue
         id="PrimitiveInCollection"
         message="method findBounds has parameter anchors with type Set&lt;Float>: replace with FloatSet"
-        errorLine1="    anchors: Set&lt;Float>"
-        errorLine2="             ~~~~~~~~~~">
+        errorLine1="private fun findBounds(offset: Float, anchors: Set&lt;Float>): List&lt;Float> {"
+        errorLine2="                                               ~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/Swipeable.kt"/>
     </issue>
@@ -355,8 +337,8 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Float> of findBounds: replace with FloatList"
-        errorLine1="): List&lt;Float> {"
-        errorLine2="   ~~~~~~~~~~~">
+        errorLine1="private fun findBounds(offset: Float, anchors: Set&lt;Float>): List&lt;Float> {"
+        errorLine2="                                                            ~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/material/Swipeable.kt"/>
     </issue>
diff --git a/compose/material/material/samples/build.gradle b/compose/material/material/samples/build.gradle
index daf6d1e..cc8ffef 100644
--- a/compose/material/material/samples/build.gradle
+++ b/compose/material/material/samples/build.gradle
@@ -40,6 +40,7 @@
     implementation("androidx.compose.foundation:foundation:1.2.1")
     implementation("androidx.compose.foundation:foundation-layout:1.4.0")
     implementation(project(":compose:material:material"))
+    api("androidx.compose.material:material-icons-core:1.6.7")
     implementation("androidx.compose.runtime:runtime:1.2.1")
     implementation("androidx.compose.ui:ui:1.2.1")
     implementation("androidx.compose.ui:ui-text:1.2.1")
@@ -54,5 +55,8 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material.samples"
+    // TODO(b/328001575)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ExposedDropdownMenuSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ExposedDropdownMenuSamples.kt
index 02c7680..b7f8a6d 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ExposedDropdownMenuSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ExposedDropdownMenuSamples.kt
@@ -17,6 +17,8 @@
 package androidx.compose.material.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
 import androidx.compose.material.DropdownMenuItem
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.ExposedDropdownMenuBox
@@ -35,7 +37,7 @@
 fun ExposedDropdownMenuSample() {
     val options = listOf("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")
     var expanded by remember { mutableStateOf(false) }
-    var selectedOptionText by remember { mutableStateOf(options[0]) }
+    val textFieldState = rememberTextFieldState(options[0])
     // We want to react on tap/press on TextField to show menu
     ExposedDropdownMenuBox(
         expanded = expanded,
@@ -43,8 +45,7 @@
     ) {
         TextField(
             readOnly = true,
-            value = selectedOptionText,
-            onValueChange = {},
+            state = textFieldState,
             label = { Text("Label") },
             trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
             colors = ExposedDropdownMenuDefaults.textFieldColors()
@@ -53,7 +54,7 @@
             options.forEach { selectionOption ->
                 DropdownMenuItem(
                     onClick = {
-                        selectedOptionText = selectionOption
+                        textFieldState.setTextAndPlaceCursorAtEnd(selectionOption)
                         expanded = false
                     }
                 ) {
@@ -70,26 +71,26 @@
 fun EditableExposedDropdownMenuSample() {
     val options = listOf("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")
     var expanded by remember { mutableStateOf(false) }
-    var selectedOptionText by remember { mutableStateOf("") }
+    val textFieldState = rememberTextFieldState()
     ExposedDropdownMenuBox(
         expanded = expanded,
         onExpandedChange = { expanded = it },
     ) {
         TextField(
-            value = selectedOptionText,
-            onValueChange = { selectedOptionText = it },
+            state = textFieldState,
             label = { Text("Label") },
             trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
             colors = ExposedDropdownMenuDefaults.textFieldColors()
         )
         // filter options based on text field value
-        val filteringOptions = options.filter { it.contains(selectedOptionText, ignoreCase = true) }
+        val filteringOptions =
+            options.filter { it.contains(textFieldState.text, ignoreCase = true) }
         if (filteringOptions.isNotEmpty()) {
             ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
                 filteringOptions.forEach { selectionOption ->
                     DropdownMenuItem(
                         onClick = {
-                            selectedOptionText = selectionOption
+                            textFieldState.setTextAndPlaceCursorAtEnd(selectionOption)
                             expanded = false
                         }
                     ) {
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
index 8f787c6..8b5dca9 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
@@ -23,8 +23,10 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.clearText
+import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.material.ContentAlpha
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.Icon
@@ -36,16 +38,18 @@
 import androidx.compose.material.TextFieldDefaults
 import androidx.compose.material.TextFieldDefaults.indicatorLine
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Clear
 import androidx.compose.material.icons.filled.Favorite
-import androidx.compose.material.icons.filled.Info
 import androidx.compose.material.icons.materialIcon
 import androidx.compose.material.icons.materialPath
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
@@ -57,53 +61,52 @@
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.dp
 
 @Sampled
 @Composable
 fun SimpleTextFieldSample() {
-    var text by rememberSaveable { mutableStateOf("") }
-
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = rememberTextFieldState(),
         label = { Text("Label") },
-        singleLine = true
+        lineLimits = TextFieldLineLimits.SingleLine,
     )
 }
 
 @Sampled
 @Composable
 fun SimpleOutlinedTextFieldSample() {
-    var text by rememberSaveable { mutableStateOf("") }
-
-    OutlinedTextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+    OutlinedTextField(
+        state = rememberTextFieldState(),
+        label = { Text("Label") },
+        lineLimits = TextFieldLineLimits.SingleLine,
+    )
 }
 
 @Sampled
 @Composable
 fun TextFieldWithIcons() {
-    var text by rememberSaveable { mutableStateOf("") }
-
+    val state = rememberTextFieldState()
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = state,
+        lineLimits = TextFieldLineLimits.SingleLine,
         placeholder = { Text("placeholder") },
-        leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = "Localized description") },
-        trailingIcon = { Icon(Icons.Filled.Info, contentDescription = "Localized description") }
+        leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
+        trailingIcon = {
+            IconButton(onClick = { state.clearText() }) {
+                Icon(Icons.Filled.Clear, contentDescription = "Clear text")
+            }
+        }
     )
 }
 
 @Sampled
 @Composable
 fun TextFieldWithPlaceholder() {
-    var text by rememberSaveable { mutableStateOf("") }
-
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = rememberTextFieldState(),
+        lineLimits = TextFieldLineLimits.SingleLine,
         label = { Text("Email") },
         placeholder = { Text("[email protected]") }
     )
@@ -112,23 +115,28 @@
 @Sampled
 @Composable
 fun TextFieldWithErrorState() {
-    var text by rememberSaveable { mutableStateOf("") }
+    val state = rememberTextFieldState()
     var isError by rememberSaveable { mutableStateOf(false) }
 
-    fun validate(text: String) {
-        isError = text.count() < 5
+    fun validate(text: CharSequence) {
+        val atIndex = text.indexOf('@')
+        isError = atIndex < 0 || text.indexOf('.', startIndex = atIndex) < 0
     }
 
+    LaunchedEffect(Unit) {
+        snapshotFlow { state.text }
+            .collect {
+                // Do something whenever text field value changes
+                isError = false
+            }
+    }
     TextField(
-        value = text,
-        onValueChange = {
-            text = it
-            isError = false
-        },
-        singleLine = true,
+        state = state,
+        lineLimits = TextFieldLineLimits.SingleLine,
         label = { Text(if (isError) "Email*" else "Email") },
         isError = isError,
-        keyboardActions = KeyboardActions { validate(text) },
+        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
+        onKeyboardAction = { validate(state.text) },
         modifier =
             Modifier.semantics {
                 // Provide localized description of the error
@@ -140,10 +148,12 @@
 @Sampled
 @Composable
 fun TextFieldWithHelperMessage() {
-    var text by rememberSaveable { mutableStateOf("") }
-
     Column {
-        TextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+        TextField(
+            state = rememberTextFieldState(),
+            label = { Text("Label") },
+            lineLimits = TextFieldLineLimits.SingleLine,
+        )
         Text(
             text = "Helper message",
             color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
@@ -153,7 +163,7 @@
     }
 }
 
-@Sampled
+// TODO: update sample with TextFieldState once we have wrappers for BasicSecureTextField
 @Composable
 fun PasswordTextField() {
     var password by rememberSaveable { mutableStateOf("") }
@@ -269,59 +279,45 @@
 
 @Sampled
 @Composable
-fun TextFieldSample() {
-    var text by
-        rememberSaveable(stateSaver = TextFieldValue.Saver) {
-            mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
-        }
-
-    TextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+fun TextFieldWithInitialValueAndSelection() {
+    val state = rememberTextFieldState("Initial text", TextRange(0, 12))
+    TextField(
+        state = state,
+        label = { Text("Label") },
+        lineLimits = TextFieldLineLimits.SingleLine,
+    )
 }
 
 @Sampled
 @Composable
-fun OutlinedTextFieldSample() {
-    var text by
-        rememberSaveable(stateSaver = TextFieldValue.Saver) {
-            mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
-        }
-
-    OutlinedTextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+fun OutlinedTextFieldWithInitialValueAndSelection() {
+    val state = rememberTextFieldState("Initial text", TextRange(0, 12))
+    OutlinedTextField(
+        state = state,
+        label = { Text("Label") },
+        lineLimits = TextFieldLineLimits.SingleLine,
+    )
 }
 
 @Sampled
 @Composable
 fun TextFieldWithHideKeyboardOnImeAction() {
     val keyboardController = LocalSoftwareKeyboardController.current
-    var text by rememberSaveable { mutableStateOf("") }
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = rememberTextFieldState(),
         label = { Text("Label") },
         keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
-        keyboardActions =
-            KeyboardActions(
-                onDone = {
-                    keyboardController?.hide()
-                    // do something here
-                }
-            )
+        onKeyboardAction = { keyboardController?.hide() },
     )
 }
 
 @Composable
 fun TextArea() {
-    var text by rememberSaveable {
-        mutableStateOf(
-            "This is a very long input that extends beyond " + "the height of the text area."
+    val state =
+        rememberTextFieldState(
+            "This is a very long input that extends beyond the height of the text area."
         )
-    }
-    TextField(
-        value = text,
-        onValueChange = { text = it },
-        modifier = Modifier.height(100.dp),
-        label = { Text("Label") }
-    )
+    TextField(state = state, modifier = Modifier.height(100.dp), label = { Text("Label") })
 }
 
 @Sampled
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
index 020a84e..717e55e 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
@@ -40,6 +40,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -76,6 +77,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -123,6 +125,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_surfaceColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -170,6 +173,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -220,6 +224,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_surfaceColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -272,6 +277,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_primaryColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt
index d241dd6..97207df 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt
@@ -41,6 +41,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -82,6 +83,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun ripple() {
         rule.setMaterialContent {
             Box(Modifier.requiredSize(200.dp, 100.dp).wrapContentSize()) { Button(onClick = {}) {} }
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt
index a7f583a..3f8fc60 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.kt
@@ -42,6 +42,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -87,6 +88,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun checkBoxTest_pressed() {
         rule.setMaterialContent {
             Box(wrap.testTag(wrapperTestTag)) {
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt
index fec062f..3914770 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/ExposedDropdownMenuTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import android.view.KeyEvent
 import android.widget.FrameLayout
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -23,6 +24,8 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
@@ -109,6 +112,49 @@
     }
 
     @Test
+    fun expandedBehaviour_dismissesOnBackPress() {
+        rule.setMaterialContent {
+            var expanded by remember { mutableStateOf(true) }
+            ExposedDropdownMenuForTest(
+                expanded = expanded,
+                onExpandChange = { expanded = it },
+            )
+        }
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+
+        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack()
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertDoesNotExist()
+        rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
+    }
+
+    @Test
+    fun expandedBehaviour_dismissesOnEscapePress() {
+        rule.setMaterialContent {
+            var expanded by remember { mutableStateOf(true) }
+            ExposedDropdownMenuForTest(
+                expanded = expanded,
+                onExpandChange = { expanded = it },
+            )
+        }
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+
+        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+            .pressKeyCode(KeyEvent.KEYCODE_ESCAPE)
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertDoesNotExist()
+        rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
+    }
+
+    @Test
     fun expandedBehaviour_collapseOnTextFieldClick() {
         rule.setMaterialContent {
             var expanded by remember { mutableStateOf(true) }
@@ -199,16 +245,6 @@
             )
         }
         rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
-
-        // A swipe that ends within the bounds of the anchor should expand the menu.
-        rule.onNodeWithTag(TFTag).performTouchInput {
-            swipe(
-                start = this.center,
-                end = Offset(this.centerX, this.centerY + (textFieldBounds.height / 2) - 1),
-                durationMillis = 100
-            )
-        }
-        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
     }
 
     @Test
@@ -222,7 +258,7 @@
             ) {
                 items(50) { index ->
                     var expanded by remember { mutableStateOf(false) }
-                    var selectedOptionText by remember { mutableStateOf("") }
+                    val textFieldState = rememberTextFieldState()
 
                     ExposedDropdownMenuBox(
                         expanded = expanded,
@@ -238,8 +274,7 @@
                                         Modifier
                                     }
                                 ),
-                            value = selectedOptionText,
-                            onValueChange = { selectedOptionText = it },
+                            state = textFieldState,
                             label = { Text("Label") },
                             trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) },
                             colors = ExposedDropdownMenuDefaults.textFieldColors()
@@ -256,7 +291,7 @@
                         ) {
                             DropdownMenuItem(
                                 onClick = {
-                                    selectedOptionText = OptionName
+                                    textFieldState.setTextAndPlaceCursorAtEnd(OptionName)
                                     expanded = false
                                 },
                                 modifier =
@@ -287,16 +322,6 @@
             )
         }
         rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
-
-        // But a swipe that does not cause a scroll should expand the menu.
-        rule.onNodeWithTag(TFTag).performTouchInput {
-            swipe(
-                start = this.center,
-                end = Offset(this.centerX + (textFieldSize.width / 2) - 1, this.centerY),
-                durationMillis = 100
-            )
-        }
-        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
     }
 
     @Test
@@ -353,10 +378,7 @@
                                             expanded = true,
                                             onExpandedChange = {}
                                         ) {
-                                            TextField(
-                                                value = "Text",
-                                                onValueChange = {},
-                                            )
+                                            TextField(state = rememberTextFieldState("Text"))
                                             ExposedDropdownMenu(
                                                 expanded = true,
                                                 onDismissRequest = {},
@@ -394,8 +416,7 @@
                 ) {
                     val scrollState = rememberScrollState()
                     TextField(
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         label = { Text("Label") },
                     )
                     ExposedDropdownMenu(
@@ -428,7 +449,7 @@
         onTextFieldBoundsChanged: ((Rect) -> Unit)? = null,
         onMenuBoundsChanged: ((Rect) -> Unit)? = null
     ) {
-        var selectedOptionText by remember { mutableStateOf("") }
+        val textFieldState = rememberTextFieldState()
         Box(Modifier.fillMaxSize()) {
             ExposedDropdownMenuBox(
                 modifier = Modifier.testTag(EDMBoxTag).align(Alignment.Center),
@@ -440,8 +461,7 @@
                         Modifier.testTag(TFTag).onGloballyPositioned {
                             onTextFieldBoundsChanged?.invoke(it.boundsInRoot())
                         },
-                    value = selectedOptionText,
-                    onValueChange = { selectedOptionText = it },
+                    state = textFieldState,
                     label = { Text("Label") },
                     trailingIcon = {
                         Box(modifier = Modifier.testTag(TrailingIconTag)) {
@@ -460,7 +480,7 @@
                 ) {
                     DropdownMenuItem(
                         onClick = {
-                            selectedOptionText = OptionName
+                            textFieldState.setTextAndPlaceCursorAtEnd(OptionName)
                             onExpandChange(false)
                         },
                         modifier = Modifier.testTag(MenuItemTag)
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/NavigationRailScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/NavigationRailScreenshotTest.kt
index 7e5221a..3fe0d72 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/NavigationRailScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/NavigationRailScreenshotTest.kt
@@ -42,6 +42,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -76,6 +77,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -118,6 +120,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -160,6 +163,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_withHeaderFab_pressed() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt
index 69f6fc6..d932c01 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt
@@ -44,6 +44,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -87,6 +88,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun radioButtonTest_pressed() {
         rule.setMaterialContent {
             Box(wrap.testTag(wrapperTestTag)) { RadioButton(selected = false, onClick = {}) }
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SnackbarHostTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
index ffbb3ba..e4b9bb3 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SnackbarHostTest.kt
@@ -167,10 +167,12 @@
     @Test
     fun snackbarHost_semantics() {
         val hostState = SnackbarHostState()
+        lateinit var paneTitle: String
         lateinit var scope: CoroutineScope
         rule.setContent {
             scope = rememberCoroutineScope()
             SnackbarHost(hostState) { data -> Snackbar(data) }
+            paneTitle = getString(Strings.SnackbarPaneTitle)
         }
         val job1 =
             scope.launch {
@@ -184,6 +186,7 @@
             .assert(
                 SemanticsMatcher.expectValue(SemanticsProperties.LiveRegion, LiveRegionMode.Polite)
             )
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.PaneTitle, paneTitle))
             .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.Dismiss))
             .performSemanticsAction(SemanticsActions.Dismiss)
 
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt
index da526bd6..1dfd461c 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt
@@ -50,6 +50,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -142,6 +143,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun switchTest_pressed() {
         rule.setMaterialContent {
             Box(wrapperModifier) { Switch(checked = false, enabled = true, onCheckedChange = {}) }
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/TabScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
index 7f1e27f..67a40a3 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
@@ -40,6 +40,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -73,6 +74,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -118,6 +120,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_surfaceColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -163,6 +166,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -211,6 +215,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_surfaceColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -263,6 +268,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_primaryColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
index d16fa1b..17b1500 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldScreenshotTest.kt
@@ -22,6 +22,8 @@
 import androidx.compose.foundation.layout.requiredWidth
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.material.AnimationDuration
 import androidx.compose.material.GOLDEN_MATERIAL
 import androidx.compose.material.Icon
@@ -38,9 +40,12 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.InterceptPlatformTextInput
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.PlatformTextInputInterceptor
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.captureToImage
@@ -49,7 +54,6 @@
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipeLeft
 import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -57,6 +61,7 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
+import kotlinx.coroutines.awaitCancellation
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -68,13 +73,11 @@
     private val TextFieldTag = "OutlinedTextField"
 
     private val longText =
-        TextFieldValue(
-            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
-                "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
-                " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
-                "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
-                "fugiat nulla pariatur."
-        )
+        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
+            "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
+            " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+            "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+            "fugiat nulla pariatur."
 
     @get:Rule val rule = createComposeRule()
 
@@ -85,8 +88,7 @@
         rule.setMaterialContent {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -99,8 +101,7 @@
     fun outlinedTextField_notFocused() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -113,8 +114,7 @@
     fun outlinedTextField_focused() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -130,8 +130,7 @@
         rule.setMaterialContent {
             CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("Label") },
                     modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
                 )
@@ -143,19 +142,24 @@
         assertAgainstGolden("outlined_textField_focused_rtl")
     }
 
+    @OptIn(ExperimentalComposeUiApi::class)
     @Test
     fun outlinedTextField_error_focused() {
+        // No-op interceptor to prevent interference from actual IME
+        val inputInterceptor = PlatformTextInputInterceptor { _, _ -> awaitCancellation() }
+
         // stop animation of blinking cursor
         rule.mainClock.autoAdvance = false
         rule.setMaterialContent {
             val text = "Input"
-            OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                label = { Text("Label") },
-                isError = true,
-                modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
-            )
+            InterceptPlatformTextInput(inputInterceptor) {
+                OutlinedTextField(
+                    state = rememberTextFieldState(text, TextRange(text.length)),
+                    label = { Text("Label") },
+                    isError = true,
+                    modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
+                )
+            }
         }
 
         rule.onNodeWithTag(TextFieldTag).focus()
@@ -168,8 +172,7 @@
     fun outlinedTextField_error_notFocused() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
@@ -185,8 +188,7 @@
             CompositionLocalProvider(LocalContentColor provides Color.Magenta) {
                 val text = "Hello, world!"
                 OutlinedTextField(
-                    value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                    onValueChange = {},
+                    state = rememberTextFieldState(text, TextRange(text.length)),
                     modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
                 )
             }
@@ -200,8 +202,7 @@
         rule.setMaterialContent {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -216,8 +217,7 @@
         rule.setMaterialContent {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
             )
@@ -230,8 +230,7 @@
     fun outlinedTextField_multiLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 placeholder = { Text("placeholder") },
                 modifier =
@@ -248,8 +247,7 @@
     fun outlinedTextField_multiLine_withoutLabel_placeholderAlignedToTop() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -265,8 +263,7 @@
     fun outlinedTextField_multiLine_labelAlignedToTop() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -281,9 +278,8 @@
         rule.setMaterialContent {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text, TextRange(text.length)),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -297,9 +293,8 @@
         rule.setMaterialContent {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text, TextRange(text.length)),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
         }
@@ -311,11 +306,10 @@
     fun outlinedTextField_singleLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 label = { Text("Label") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
         }
@@ -329,10 +323,9 @@
     fun outlinedTextField_singleLine_withoutLabel_placeholderCenteredVertically() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
         }
@@ -345,12 +338,12 @@
     }
 
     @Test
-    fun outlinedTextField_singleLine_labelCenteredVetically() {
+    fun outlinedTextField_singleLine_labelCenteredVertically() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
         }
@@ -362,9 +355,8 @@
     fun outlinedTextField_disabled() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState("Text"),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -377,9 +369,8 @@
     fun outlinedTextField_disabled_notFocusable() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState("Text"),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -394,9 +385,8 @@
     fun outlinedTextField_disabled_notScrolled() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = longText,
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(longText),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
                 enabled = false
             )
@@ -417,8 +407,7 @@
     fun outlinedTextField_readOnly() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
                 enabled = true,
                 readOnly = true
@@ -432,8 +421,7 @@
     fun outlinedTextField_readOnly_focused() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
                 enabled = true,
                 readOnly = true
@@ -449,10 +437,9 @@
     fun outlinedTextField_readOnly_scrolled() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = longText,
-                onValueChange = {},
+                state = rememberTextFieldState(longText),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = true,
                 readOnly = true
             )
@@ -474,11 +461,10 @@
         rule.setMaterialContent {
             val text = "Hello world"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -490,11 +476,10 @@
         rule.setMaterialContent {
             val text = "Hello world"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 modifier = Modifier.fillMaxWidth().testTag(TextFieldTag),
                 textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.End),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -514,11 +499,10 @@
     fun outlinedTextField_customShape() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 shape = CutCornerShape(10.dp)
             )
         }
@@ -532,8 +516,7 @@
         rule.setMaterialContent {
             makeLabelNull = remember { mutableStateOf(false) }
             OutlinedTextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 label =
                     if (makeLabelNull.value) {
@@ -554,8 +537,7 @@
     fun outlinedTextField_leadingTrailingIcons() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -570,8 +552,7 @@
     fun outlinedTextField_leadingTrailingIcons_error() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
index bdd1fc4..6bde177 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
@@ -36,6 +36,11 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.delete
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.placeCursorAtEnd
+import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.material.Divider
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
@@ -98,7 +103,6 @@
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
@@ -131,8 +135,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 OutlinedTextField(
-                    value = "input",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input"),
                     modifier = Modifier.requiredWidth(40.dp)
                 )
             }
@@ -143,7 +146,7 @@
     fun testOutlinedTextField_defaultWidth() {
         rule
             .setMaterialContentForSizeAssertions {
-                OutlinedTextField(value = "input", onValueChange = {})
+                OutlinedTextField(state = rememberTextFieldState("input"))
             }
             .assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
     }
@@ -163,16 +166,14 @@
                         Modifier.testTag(textField1Tag).onFocusChanged {
                             textField1Focused = it.isFocused
                         },
-                    value = "input1",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input1"),
                 )
                 OutlinedTextField(
                     modifier =
                         Modifier.testTag(textField2Tag).onFocusChanged {
                             textField2Focused = it.isFocused
                         },
-                    value = "input2",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input2"),
                 )
             }
         }
@@ -200,8 +201,7 @@
                 OutlinedTextField(
                     modifier =
                         Modifier.testTag(TextfieldTag).onFocusChanged { focused = it.isFocused },
-                    value = "input",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input"),
                 )
             }
         }
@@ -220,8 +220,7 @@
             CompositionLocalProvider(LocalDensity provides density) {
                 Box(Modifier.testTag("box").background(Color.Red)) {
                     OutlinedTextField(
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         colors =
                             TextFieldDefaults.outlinedTextFieldColors(
                                 textColor = Color.White,
@@ -253,9 +252,8 @@
         rule.setMaterialContent {
             Box {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
-                    singleLine = true,
+                    state = rememberTextFieldState(),
+                    lineLimits = TextFieldLineLimits.SingleLine,
                     label = {
                         Text(
                             text = "label",
@@ -293,8 +291,7 @@
         rule.setMaterialContent {
             Box {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     modifier = Modifier.requiredWidth(textFieldWidth),
                     label = {
                         Text(
@@ -322,8 +319,7 @@
         rule.setMaterialContent {
             Box {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         Text(
                             text = "label",
@@ -358,8 +354,7 @@
         rule.setMaterialContent {
             OutlinedTextField(
                 modifier = Modifier.testTag(TextfieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Text(
                         text = "label",
@@ -395,8 +390,7 @@
         rule.setMaterialContent {
             Box {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     modifier = Modifier.testTag(TextfieldTag).requiredWidth(textFieldWidth),
                     label = {
                         Text(
@@ -426,8 +420,7 @@
         val labelHeight = 200.dp
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier =
                     Modifier.testTag(TextfieldTag).onGloballyPositioned { tfSize.value = it.size },
                 label = { Box(Modifier.size(width = 50.dp, height = labelHeight)) },
@@ -447,8 +440,7 @@
         val trailingSize = Ref<IntSize>()
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.testTag(TextfieldTag).requiredWidth(textFieldWidth),
                 label = {
                     Text(
@@ -493,8 +485,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 label = {
                     Text(
                         text = "label",
@@ -525,8 +516,7 @@
         // Regression test for b/251162419
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text(text = "Label") },
                 placeholder = {
                     Text(text = "Placeholder", modifier = Modifier.testTag("Placeholder"))
@@ -545,8 +535,7 @@
             Box {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextfieldTag),
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("label") },
                     placeholder = {
                         Text(
@@ -587,8 +576,7 @@
             Box {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextfieldTag),
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     placeholder = {
                         Text(
                             text = "placeholder",
@@ -626,8 +614,7 @@
             Column {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextfieldTag),
-                    value = "input",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input"),
                     placeholder = {
                         Text(
                             text = "placeholder",
@@ -656,8 +643,7 @@
         rule.setMaterialContent {
             OutlinedTextField(
                 modifier = Modifier.testTag(TextfieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = {
                     Text("placeholder")
                     assertThat(LocalContentColor.current.copy(alpha = LocalContentAlpha.current))
@@ -682,8 +668,7 @@
         rule.setMaterialContent {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth),
                     label = { Text("label") },
                     leadingIcon = {
@@ -755,8 +740,7 @@
         rule.setMaterialContent {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth).height(textFieldHeight),
                     leadingIcon = {
                         IconButton(
@@ -824,8 +808,7 @@
         rule.setMaterialContent {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth).height(textFieldHeight),
                     leadingIcon = {
                         Box(
@@ -874,8 +857,7 @@
         rule.setMaterialContent {
             Box {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         Text(
                             text = "label",
@@ -907,8 +889,7 @@
         rule.setMaterialContent {
             Box {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         Text(
                             text = "label",
@@ -933,8 +914,7 @@
     fun testOutlinedTextField_colorInLeadingTrailing_whenValidInput() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = false,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -952,8 +932,7 @@
     fun testOutlinedTextField_colorInLeadingTrailing_whenInvalidInput() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -978,12 +957,10 @@
             awaitCancellation()
         }
         rule.setContent {
-            val text = remember { mutableStateOf("") }
             InterceptPlatformTextInput(interceptor) {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextfieldTag),
-                    value = text.value,
-                    onValueChange = { text.value = it },
+                    state = rememberTextFieldState(),
                     keyboardOptions =
                         KeyboardOptions(imeAction = ImeAction.Go, keyboardType = KeyboardType.Email)
                 )
@@ -1010,9 +987,14 @@
             Box(Modifier.background(color = Color.White)) {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextfieldTag),
-                    value = "qwerty",
-                    onValueChange = {},
-                    visualTransformation = PasswordVisualTransformation('\u0020')
+                    state = rememberTextFieldState("qwerty"),
+                    outputTransformation = {
+                        // transform all chars to blank spaces
+                        val size = length
+                        delete(0, length)
+                        insert(0, " ".repeat(size))
+                        placeCursorAtEnd()
+                    },
                 )
             }
         }
@@ -1034,7 +1016,7 @@
     fun testErrorSemantics_defaultMessage() {
         lateinit var errorMessage: String
         rule.setMaterialContent {
-            OutlinedTextField(value = "test", onValueChange = {}, isError = true)
+            OutlinedTextField(state = rememberTextFieldState("test"), isError = true)
             errorMessage = getString(Strings.DefaultErrorMessage)
         }
 
@@ -1051,8 +1033,7 @@
         rule.setMaterialContent {
             var isError = remember { mutableStateOf(true) }
             OutlinedTextField(
-                value = "test",
-                onValueChange = {},
+                state = rememberTextFieldState("test"),
                 modifier =
                     Modifier.testTag(TextfieldTag).semantics {
                         if (isError.value) error(errorMessage)
@@ -1083,7 +1064,6 @@
         var size: IntSize? = null
         var dividerSize: IntSize? = null
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { size = it.size }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
                     Divider(
@@ -1093,9 +1073,8 @@
                             }
                     )
                     OutlinedTextField(
-                        value = text.value,
+                        state = rememberTextFieldState(),
                         label = { Text(text = "Label") },
-                        onValueChange = { text.value = it }
                     )
                 }
             }
@@ -1113,8 +1092,7 @@
     fun testOutlinedTextField_appliesBackgroundColor() {
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.testTag(TextfieldTag),
                 colors =
                     TextFieldDefaults.outlinedTextFieldColors(
@@ -1133,7 +1111,6 @@
         var textFieldSize: IntSize? = null
         var dividerSize: IntSize? = null
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Box {
                 Column(Modifier.width(IntrinsicSize.Min)) {
                     Divider(
@@ -1143,9 +1120,8 @@
                             }
                     )
                     OutlinedTextField(
-                        value = text.value,
+                        state = rememberTextFieldState(),
                         label = { Text(text = "Label") },
-                        onValueChange = { text.value = it },
                         modifier = Modifier.onGloballyPositioned { textFieldSize = it.size }
                     )
                 }
@@ -1165,8 +1141,7 @@
         rule.setMaterialContent {
             Box(Modifier.background(Color.White).padding(10.dp)) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     modifier = Modifier.testTag(TextfieldTag),
                     label = { Text("Label") },
                     isError = true,
@@ -1194,8 +1169,7 @@
 
         rule.setMaterialContent {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     textStyle = LocalTextStyle.current
                     contentColor = LocalContentColor.current
@@ -1236,8 +1210,7 @@
             val caption = MaterialTheme.typography.caption.copy(color = captionColor)
             MaterialTheme(typography = Typography(caption = caption)) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1278,8 +1251,7 @@
             val caption = MaterialTheme.typography.caption.copy(color = expectedLabelColor)
             MaterialTheme(typography = Typography(caption = caption)) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1323,8 +1295,7 @@
             val subtitle1 = MaterialTheme.typography.subtitle1.copy(color = subtitleColor)
             MaterialTheme(typography = Typography(caption = caption, subtitle1 = subtitle1)) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1355,13 +1326,8 @@
     @Test
     fun testOutlinedTextField_withIntrinsicsMeasurement_getsIdle() {
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Row(Modifier.height(IntrinsicSize.Min)) {
-                OutlinedTextField(
-                    value = text.value,
-                    onValueChange = { text.value = it },
-                    label = { Text("Label") }
-                )
+                OutlinedTextField(state = rememberTextFieldState(), label = { Text("Label") })
                 Divider(Modifier.fillMaxHeight())
             }
         }
@@ -1375,12 +1341,10 @@
     fun testOutlinedTextField_intrinsicHeight_withOnlyEmptyInput() {
         var height = 0
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
                     OutlinedTextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
+                        state = rememberTextFieldState(),
                     )
                     Divider(Modifier.fillMaxHeight())
                 }
@@ -1396,12 +1360,10 @@
     fun testOutlinedTextField_intrinsicHeight_withEmptyInput_andDecorations() {
         var height = 0
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
                     OutlinedTextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
+                        state = rememberTextFieldState(),
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
                         trailingIcon = { Icon(Icons.Default.Favorite, null) },
                     )
@@ -1424,8 +1386,7 @@
             Row {
                 Box(Modifier.width(150.dp).height(IntrinsicSize.Min)) {
                     OutlinedTextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1434,8 +1395,7 @@
                 }
                 Box(Modifier.width(150.dp)) {
                     OutlinedTextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightNoIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1487,7 +1447,10 @@
                 modifier =
                     Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
             ) {
-                OutlinedTextField(value = "Cat", onValueChange = {}, leadingIcon = { Text("Icon") })
+                OutlinedTextField(
+                    state = rememberTextFieldState("Cat"),
+                    leadingIcon = { Text("Icon") }
+                )
             }
         }
     }
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
index dcd4562..c3eeb27 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldScreenshotTest.kt
@@ -22,6 +22,8 @@
 import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.layout.requiredWidth
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.material.AnimationDuration
 import androidx.compose.material.GOLDEN_MATERIAL
 import androidx.compose.material.Icon
@@ -48,7 +50,6 @@
 import androidx.compose.ui.test.swipeLeft
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -66,13 +67,11 @@
 class TextFieldScreenshotTest {
     private val TextFieldTag = "TextField"
     private val longText =
-        TextFieldValue(
-            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
-                "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
-                " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
-                "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
-                "fugiat nulla pariatur."
-        )
+        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
+            "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
+            " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+            "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+            "fugiat nulla pariatur."
 
     @get:Rule val rule = createComposeRule()
 
@@ -86,8 +85,7 @@
             Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
                 val text = "Text"
                 TextField(
-                    value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                    onValueChange = {},
+                    state = rememberTextFieldState(text, TextRange(text.length)),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -102,8 +100,7 @@
         rule.setMaterialContent {
             Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -118,8 +115,7 @@
         rule.setMaterialContent {
             Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -137,8 +133,7 @@
             CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
                 Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
                     TextField(
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         label = { Text("Label") },
                         modifier = Modifier.requiredWidth(280.dp)
                     )
@@ -158,8 +153,7 @@
         rule.setMaterialContent {
             val text = "Input"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -176,8 +170,7 @@
     fun textField_error_notFocused() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -193,8 +186,7 @@
             CompositionLocalProvider(LocalContentColor provides Color.Green) {
                 val text = "Hello, world!"
                 TextField(
-                    value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                    onValueChange = {},
+                    state = rememberTextFieldState(text, TextRange(text.length)),
                     modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
                 )
             }
@@ -208,8 +200,7 @@
         rule.setMaterialContent {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -224,8 +215,7 @@
         rule.setMaterialContent {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
             )
@@ -238,8 +228,7 @@
     fun textField_multiLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 placeholder = { Text("placeholder") },
                 modifier =
@@ -256,8 +245,7 @@
     fun textField_multiLine_withoutLabel_placeholderAlignedToTop() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -273,8 +261,7 @@
     fun textField_multiLine_labelAlignedToTop() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -289,9 +276,8 @@
         rule.setMaterialContent {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text, TextRange(text.length)),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 label = { Text("Label") },
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
@@ -305,9 +291,8 @@
         rule.setMaterialContent {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text, TextRange(text.length)),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
         }
@@ -319,11 +304,10 @@
     fun textField_singleLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 label = { Text("Label") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
         }
@@ -337,10 +321,9 @@
     fun textField_singleLine_withoutLabel_placeholderCenteredVertically() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
         }
@@ -353,12 +336,12 @@
     }
 
     @Test
-    fun textField_singleLine_labelCenteredVetically() {
+    fun textField_singleLine_labelCenteredVertically() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
         }
@@ -370,10 +353,9 @@
     fun textField_disabled() {
         rule.setMaterialContent {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false
             )
         }
@@ -385,9 +367,8 @@
     fun textField_disabled_notFocusable() {
         rule.setMaterialContent {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState("Text"),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 enabled = false
             )
@@ -402,9 +383,8 @@
     fun textField_disabled_notScrolled() {
         rule.setMaterialContent {
             TextField(
-                value = longText,
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(longText),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
                 enabled = false
             )
@@ -425,8 +405,7 @@
     fun textField_readOnly() {
         rule.setMaterialContent {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 enabled = true,
                 readOnly = true
@@ -440,8 +419,7 @@
     fun textField_readOnly_focused() {
         rule.setMaterialContent {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 enabled = true,
                 readOnly = true
@@ -457,10 +435,9 @@
     fun textField_readOnly_scrolled() {
         rule.setMaterialContent {
             TextField(
-                value = longText,
-                onValueChange = {},
+                state = rememberTextFieldState(longText),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = true,
                 readOnly = true
             )
@@ -481,12 +458,11 @@
         rule.setMaterialContent {
             val text = "Hello world"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 textStyle =
                     TextStyle(textAlign = TextAlign.Center, platformStyle = platformTextStyle),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -498,11 +474,10 @@
         rule.setMaterialContent {
             val text = "Hello world"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text, TextRange(text.length)),
                 modifier = Modifier.fillMaxWidth().testTag(TextFieldTag),
                 textStyle = TextStyle(textAlign = TextAlign.End, platformStyle = platformTextStyle),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -513,8 +488,7 @@
     fun textField_leadingTrailingIcons() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -529,8 +503,7 @@
     fun textField_leadingTrailingIcons_error() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
index 1a145ae..a562508 100644
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
@@ -43,6 +43,11 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.delete
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.placeCursorAtEnd
+import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.material.Divider
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
@@ -107,13 +112,8 @@
 import androidx.compose.ui.test.performTextClearance
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.OffsetMapping
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.TransformedText
-import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
@@ -150,7 +150,10 @@
     fun testTextField_minimumHeight() {
         rule
             .setMaterialContentForSizeAssertions {
-                TextField(value = "input", onValueChange = {}, modifier = Modifier.height(20.dp))
+                TextField(
+                    state = rememberTextFieldState("input"),
+                    modifier = Modifier.height(20.dp)
+                )
             }
             .assertHeightIsEqualTo(20.dp)
     }
@@ -160,8 +163,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 TextField(
-                    value = "input",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input"),
                     modifier = Modifier.requiredWidth(40.dp)
                 )
             }
@@ -171,7 +173,7 @@
     @Test
     fun testTextField_defaultWidth() {
         rule
-            .setMaterialContentForSizeAssertions { TextField(value = "input", onValueChange = {}) }
+            .setMaterialContentForSizeAssertions { TextField(rememberTextFieldState("input")) }
             .assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
     }
 
@@ -189,14 +191,12 @@
             Column {
                 TextField(
                     modifier = Modifier.testTag(textField1Tag),
-                    value = "input1",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input1"),
                     interactionSource = interactionSource1
                 )
                 TextField(
                     modifier = Modifier.testTag(textField2Tag),
-                    value = "input2",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input2"),
                     interactionSource = interactionSource2
                 )
             }
@@ -241,8 +241,7 @@
             scope = rememberCoroutineScope()
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 interactionSource = interactionSource
             )
         }
@@ -275,8 +274,7 @@
                             .focusTarget()
                             .focusRequester(focusRequester)
                             .testTag(TextFieldTag),
-                    value = "input",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input"),
                 )
             }
         }
@@ -305,8 +303,7 @@
                             .focusTarget()
                             .focusRequester(focusRequester)
                             .testTag(TextFieldTag),
-                    value = "input",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input"),
                 )
             }
         }
@@ -330,9 +327,8 @@
         rule.setMaterialContent {
             Box {
                 TextField(
-                    value = "",
-                    onValueChange = {},
-                    singleLine = true,
+                    state = rememberTextFieldState(),
+                    lineLimits = TextFieldLineLimits.SingleLine,
                     label = {
                         Text(
                             text = "label",
@@ -369,8 +365,7 @@
         rule.setMaterialContent {
             Box {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         Text(
                             text = "label",
@@ -406,8 +401,7 @@
         rule.setMaterialContent {
             Box {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     modifier = Modifier.height(height),
                     label = {
                         Text(
@@ -442,8 +436,7 @@
             Box {
                 TextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         Text(
                             text = "label",
@@ -482,8 +475,7 @@
         rule.setMaterialContent {
             Box {
                 TextField(
-                    value = "input",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input"),
                     label = {
                         Text(
                             text = "label",
@@ -519,8 +511,7 @@
             Box {
                 TextField(
                     modifier = Modifier.height(60.dp).testTag(TextFieldTag),
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("label") },
                     placeholder = {
                         Text(
@@ -563,8 +554,7 @@
             Box {
                 TextField(
                     modifier = Modifier.height(height).testTag(TextFieldTag),
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     placeholder = {
                         Text(
                             text = "placeholder",
@@ -601,8 +591,7 @@
             Column {
                 TextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = "input",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input"),
                     placeholder = {
                         Text(
                             text = "placeholder",
@@ -631,8 +620,7 @@
         rule.setMaterialContent {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = {
                     Text("placeholder")
                     assertThat(LocalContentColor.current.copy(alpha = LocalContentAlpha.current))
@@ -659,8 +647,7 @@
         rule.setMaterialContent {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.size(textFieldWidth, textFieldHeight),
                     leadingIcon = {
                         Icon(
@@ -732,8 +719,7 @@
         rule.setMaterialContent {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.size(textFieldWidth, textFieldHeight),
                     leadingIcon = {
                         IconButton(
@@ -805,8 +791,7 @@
         rule.setMaterialContent {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.size(textFieldWidth, textFieldHeight),
                     leadingIcon = {
                         Box(
@@ -860,8 +845,7 @@
         rule.setMaterialContent {
             Box {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     modifier = Modifier.height(height),
                     label = {
                         Text(
@@ -895,8 +879,7 @@
         rule.setMaterialContent {
             Box {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     modifier = Modifier.height(height),
                     label = {
                         Text(
@@ -922,8 +905,7 @@
     fun testTextField_colorInLeadingTrailing_whenValidInput() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = false,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -941,8 +923,7 @@
     fun testTextField_colorInLeadingTrailing_whenInvalidInput() {
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -967,12 +948,11 @@
             awaitCancellation()
         }
         rule.setContent {
-            val text = remember { mutableStateOf("") }
+            val state = rememberTextFieldState()
             InterceptPlatformTextInput(interceptor) {
                 TextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = text.value,
-                    onValueChange = { text.value = it },
+                    state = state,
                     keyboardOptions =
                         KeyboardOptions(imeAction = ImeAction.Go, keyboardType = KeyboardType.Email)
                 )
@@ -994,13 +974,18 @@
     @Test
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun testTextField_visualTransformationPropagated() {
+    fun testTextField_outputTransformationPropagated() {
         rule.setMaterialContent {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "qwerty",
-                onValueChange = {},
-                visualTransformation = PasswordVisualTransformation('\u0020'),
+                state = rememberTextFieldState("qwerty"),
+                outputTransformation = {
+                    // transform all chars to blank spaces
+                    val size = length
+                    delete(0, length)
+                    insert(0, " ".repeat(size))
+                    placeCursorAtEnd()
+                },
                 shape = RectangleShape,
                 colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
             )
@@ -1028,8 +1013,7 @@
             Box(Modifier.background(color = Color.White)) {
                 TextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = "test",
-                    onValueChange = {},
+                    state = rememberTextFieldState("test"),
                     label = { Text("label") },
                     shape = RectangleShape,
                     leadingIcon = { Icon(Icons.Default.Favorite, null, tint = Color.Transparent) },
@@ -1079,28 +1063,12 @@
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testTransformedTextIsUsed_toDefineLabelPosition() {
-        // if non-transformed value were used to check if the text input is empty, the label
-        // wouldn't be aligned to the top, as a result it would be obscured by text
-        val prefixTransformation = VisualTransformation { text ->
-            val prefix = "prefix"
-            val transformed = buildAnnotatedString {
-                append(prefix)
-                append(text)
-            }
-            val mapping =
-                object : OffsetMapping {
-                    override fun originalToTransformed(offset: Int) = offset + prefix.length
-
-                    override fun transformedToOriginal(offset: Int) =
-                        (offset - prefix.length).coerceAtLeast(0)
-                }
-            TransformedText(transformed, mapping)
-        }
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
-                visualTransformation = prefixTransformation,
+                state = rememberTextFieldState(),
+                // if non-transformed value were used to check if the text input is empty, the label
+                // wouldn't be aligned to the top, as a result it would be obscured by text
+                outputTransformation = { insert(0, "prefix") },
                 label = {
                     Text(
                         text = "label",
@@ -1122,29 +1090,13 @@
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testTransformedTextIsUsed_toDefineIfPlaceholderNeeded() {
-        // if original value were used to check if the text input is empty, the placeholder would be
-        // displayed on top of the text
-        val prefixTransformation = VisualTransformation { text ->
-            val prefix = "prefix"
-            val transformed = buildAnnotatedString {
-                append(prefix)
-                append(text)
-            }
-            val mapping =
-                object : OffsetMapping {
-                    override fun originalToTransformed(offset: Int) = offset + prefix.length
-
-                    override fun transformedToOriginal(offset: Int) =
-                        (offset - prefix.length).coerceAtLeast(0)
-                }
-            TransformedText(transformed, mapping)
-        }
         rule.setMaterialContent {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
-                visualTransformation = prefixTransformation,
+                state = rememberTextFieldState(),
+                // if original value were used to check if the text input is empty, the placeholder
+                // would be displayed on top of the text
+                outputTransformation = { insert(0, "prefix") },
                 placeholder = {
                     Text(
                         text = "placeholder",
@@ -1167,7 +1119,7 @@
     fun testErrorSemantics_defaultMessage() {
         lateinit var errorMessage: String
         rule.setMaterialContent {
-            TextField(value = "test", onValueChange = {}, isError = true)
+            TextField(state = rememberTextFieldState("test"), isError = true)
             errorMessage = getString(DefaultErrorMessage)
         }
 
@@ -1184,8 +1136,7 @@
         rule.setMaterialContent {
             val isError = remember { mutableStateOf(true) }
             TextField(
-                value = "test",
-                onValueChange = {},
+                state = rememberTextFieldState("test"),
                 modifier =
                     Modifier.testTag(TextFieldTag).semantics {
                         if (isError.value) error(errorMessage)
@@ -1224,7 +1175,7 @@
                                 dividerSize = it.size
                             }
                     )
-                    TextField(value = "", label = { Text(text = "Label") }, onValueChange = {})
+                    TextField(state = rememberTextFieldState(), label = { Text(text = "Label") })
                 }
             }
         }
@@ -1241,7 +1192,6 @@
         var textFieldSize: IntSize? = null
         var dividerSize: IntSize? = null
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Box {
                 Column(Modifier.width(IntrinsicSize.Min)) {
                     Divider(
@@ -1251,9 +1201,8 @@
                             }
                     )
                     TextField(
-                        value = text.value,
+                        state = rememberTextFieldState(),
                         label = { Text(text = "Label") },
-                        onValueChange = { text.value = it },
                         modifier = Modifier.onGloballyPositioned { textFieldSize = it.size }
                     )
                 }
@@ -1273,8 +1222,7 @@
         rule.setMaterialContent {
             Box(Modifier.background(Color.White).padding(10.dp)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     modifier = Modifier.testTag(TextFieldTag),
                     label = { Text("Label") },
                     isError = true,
@@ -1303,8 +1251,7 @@
 
         rule.setMaterialContent {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     textStyle = LocalTextStyle.current
                     contentColor = LocalContentColor.current
@@ -1345,8 +1292,7 @@
             val caption = MaterialTheme.typography.caption.copy(color = captionColor)
             MaterialTheme(typography = Typography(caption = caption)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1387,8 +1333,7 @@
             val caption = MaterialTheme.typography.caption.copy(color = expectedLabelColor)
             MaterialTheme(typography = Typography(caption = caption)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1432,8 +1377,7 @@
             val subtitle1 = MaterialTheme.typography.subtitle1.copy(color = subtitleColor)
             MaterialTheme(typography = Typography(caption = caption, subtitle1 = subtitle1)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1465,13 +1409,9 @@
     fun testTextField_intrinsicHeight_withOnlyEmptyInput() {
         var height = 0
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
-                    TextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
-                    )
+                    TextField(rememberTextFieldState())
                     Divider(Modifier.fillMaxHeight())
                 }
             }
@@ -1486,12 +1426,10 @@
     fun testTextField_intrinsicHeight_withEmptyInput_andDecorations() {
         var height = 0
         rule.setMaterialContent {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
                     TextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
+                        state = rememberTextFieldState(),
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
                         trailingIcon = { Icon(Icons.Default.Favorite, null) },
                     )
@@ -1514,8 +1452,7 @@
             Row {
                 Box(Modifier.width(150.dp).height(IntrinsicSize.Min)) {
                     TextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1524,8 +1461,7 @@
                 }
                 Box(Modifier.width(150.dp)) {
                     TextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightNoIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1578,7 +1514,7 @@
                 modifier =
                     Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
             ) {
-                TextField(value = "Cat", onValueChange = {}, leadingIcon = { Text("Icon") })
+                TextField(state = rememberTextFieldState("Cat"), leadingIcon = { Text("Icon") })
             }
         }
     }
diff --git a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.android.kt b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.android.kt
index cf017e2..28d9e7b 100644
--- a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.android.kt
+++ b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.android.kt
@@ -32,9 +32,8 @@
 import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowDropDown
 import androidx.compose.material.internal.ExposedDropdownMenuPopup
+import androidx.compose.material.internal.Icons
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.Immutable
diff --git a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/Strings.android.kt b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/Strings.android.kt
index fcae854..4ce4a0e 100644
--- a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/Strings.android.kt
+++ b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/Strings.android.kt
@@ -33,6 +33,7 @@
         Strings.ExposedDropdownMenu -> resources.getString(R.string.dropdown_menu)
         Strings.SliderRangeStart -> resources.getString(R.string.range_start)
         Strings.SliderRangeEnd -> resources.getString(R.string.range_end)
+        Strings.SnackbarPaneTitle -> resources.getString(R.string.snackbar_pane_title)
         else -> ""
     }
 }
diff --git a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/internal/ExposedDropdownMenuPopup.android.kt b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/internal/ExposedDropdownMenuPopup.android.kt
index 0f94d78..ecbd45c 100644
--- a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/internal/ExposedDropdownMenuPopup.android.kt
+++ b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/internal/ExposedDropdownMenuPopup.android.kt
@@ -310,17 +310,13 @@
 
     /** Taken from PopupWindow */
     override fun dispatchKeyEvent(event: KeyEvent): Boolean {
-        if (event.keyCode == KeyEvent.KEYCODE_BACK) {
-            if (keyDispatcherState == null) {
-                return super.dispatchKeyEvent(event)
-            }
+        if (event.keyCode == KeyEvent.KEYCODE_BACK || event.keyCode == KeyEvent.KEYCODE_ESCAPE) {
+            val state = keyDispatcherState ?: return super.dispatchKeyEvent(event)
             if (event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0) {
-                val state = keyDispatcherState
-                state?.startTracking(event, this)
+                state.startTracking(event, this)
                 return true
             } else if (event.action == KeyEvent.ACTION_UP) {
-                val state = keyDispatcherState
-                if (state != null && state.isTracking(event) && !event.isCanceled) {
+                if (state.isTracking(event) && !event.isCanceled) {
                     onDismissRequest?.invoke()
                     return true
                 }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
index 1764b73..e444fbf 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
@@ -713,12 +713,7 @@
     val AnimationSpec = SpringSpec<Float>()
 }
 
-private class AnchoredDragFinishedSignal : CancellationException() {
-    override fun fillInStackTrace(): Throwable {
-        stackTrace = emptyArray()
-        return this
-    }
-}
+internal expect class AnchoredDragFinishedSignal() : CancellationException
 
 private suspend fun <I> restartable(inputs: () -> I, block: suspend (I) -> Unit) {
     try {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
index ae9557c..8e9bc84 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
@@ -66,6 +66,7 @@
 import androidx.compose.ui.util.fastCoerceIn
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMap
+import kotlin.jvm.JvmName
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
index f9134e1..76fa17e 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
@@ -53,6 +53,7 @@
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMap
 import androidx.compose.ui.util.fastMaxBy
+import kotlin.jvm.JvmName
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt
index f92ff6f..0fa7967 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt
@@ -32,7 +32,6 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.material.icons.Icons
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
@@ -174,8 +173,8 @@
  *   color for this chip in different states. See [ChipDefaults.filterChipColors].
  * @param leadingIcon Optional icon at the start of the chip, preceding the content text.
  * @param selectedIcon Icon used to indicate a chip's selected state, it is commonly a
- *   [Icons.Filled.Done]. By default, if a leading icon is also provided, the leading icon will be
- *   obscured by a circle overlay and then the selected icon.
+ *   [androidx.compose.material.icons.Icons.Filled.Done]. By default, if a leading icon is also
+ *   provided, the leading icon will be obscured by a circle overlay and then the selected icon.
  * @param trailingIcon Optional icon at the end of the chip, following the content text. Filter
  *   chips commonly do not display any trailing icon.
  * @param content the content of this chip
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
index ffb693f..97b3d16 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
@@ -66,6 +66,7 @@
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastCoerceIn
+import kotlin.jvm.JvmName
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt
index d2497c9..adbc015 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.currentValueOf
 import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
@@ -41,11 +42,14 @@
  *
  * This uses the Material recommended minimum size of 48.dp x 48.dp, which may not the same as the
  * system enforced minimum size. The minimum clickable / touch target size (48.dp by default) is
- * controlled by the system via ViewConfiguration` and automatically expanded at the touch input
+ * controlled by the system via [ViewConfiguration] and automatically expanded at the touch input
  * layer.
  *
  * This modifier is not needed for touch target expansion to happen. It only affects layout, to make
  * sure there is adequate space for touch target expansion.
+ *
+ * Because layout constraints are affected by modifier order, for this modifier to take effect, it
+ * must come before any size modifiers on the element that might limit its constraints.
  */
 fun Modifier.minimumInteractiveComponentSize(): Modifier = this then MinimumInteractiveModifier
 
@@ -64,11 +68,13 @@
                 "interactions if the element would measure smaller"
     }
 
-    override fun hashCode(): Int = System.identityHashCode(this)
+    override fun hashCode(): Int = identityHashCode(this)
 
     override fun equals(other: Any?) = (other === this)
 }
 
+internal expect inline fun identityHashCode(value: Any): Int
+
 internal class MinimumInteractiveModifierNode :
     Modifier.Node(), CompositionLocalConsumerModifierNode, LayoutModifierNode {
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
index e421ecf..64341fe 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -63,6 +63,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
+import kotlin.jvm.JvmName
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index 09d14b8..076cf77 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
@@ -24,9 +25,18 @@
 import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.KeyboardActionHandler
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldBuffer
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -79,9 +89,174 @@
  * ![Outlined text field
  * image](https://developer.android.com/images/reference/androidx/compose/material/outlined-text-field.png)
  *
+ * This overload of [OutlinedTextField] uses [TextFieldState] to keep track of its text content and
+ * position of the cursor or selection.
+ *
  * See example usage:
  *
  * @sample androidx.compose.material.samples.SimpleOutlinedTextFieldSample
+ * @sample androidx.compose.material.samples.OutlinedTextFieldWithInitialValueAndSelection
+ * @param state [TextFieldState] object that holds the internal editing state of this text field.
+ * @param modifier a [Modifier] for this text field
+ * @param enabled controls the enabled state of the [OutlinedTextField]. When `false`, the text
+ *   field will be neither editable nor focusable, the input of the text field will not be
+ *   selectable, visually text field will appear in the disabled UI state
+ * @param readOnly controls the editable state of the [OutlinedTextField]. When `true`, the text
+ *   field can not be modified, however, a user can focus it and copy text from it. Read-only text
+ *   fields are usually used to display pre-filled forms that user can not edit
+ * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
+ *   [LocalTextStyle] defined by the theme
+ * @param label the optional label to be displayed inside the text field container. The default text
+ *   style for internal [Text] is [Typography.caption] when the text field is in focus and
+ *   [Typography.subtitle1] when the text field is not in focus
+ * @param placeholder the optional placeholder to be displayed when the text field is in focus and
+ *   the input text is empty. The default text style for internal [Text] is [Typography.subtitle1]
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+ *   container
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ *   container
+ * @param isError indicates if the text field's current value is in error. If set to true, the
+ *   label, bottom indicator and trailing icon by default will be displayed in error color
+ * @param inputTransformation Optional [InputTransformation] that will be used to transform changes
+ *   to the [TextFieldState] made by the user. The transformation will be applied to changes made by
+ *   hardware and software keyboard events, pasting or dropping text, accessibility services, and
+ *   tests. The transformation will _not_ be applied when changing the [state] programmatically, or
+ *   when the transformation is changed. If the transformation is changed on an existing text field,
+ *   it will be applied to the next user edit. the transformation will not immediately affect the
+ *   current [state].
+ * @param outputTransformation An [OutputTransformation] that transforms how the contents of the
+ *   text field are presented.
+ * @param keyboardOptions software keyboard options that contains configuration such as
+ *   [KeyboardType] and [ImeAction]
+ * @param onKeyboardAction Called when the user presses the action button in the input method editor
+ *   (IME), or by pressing the enter key on a hardware keyboard. By default this parameter is null,
+ *   and would execute the default behavior for a received IME Action e.g., [ImeAction.Done] would
+ *   close the keyboard, [ImeAction.Next] would switch the focus to the next focusable item on the
+ *   screen.
+ * @param lineLimits Whether the text field should be [SingleLine], scroll horizontally, and ignore
+ *   newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all newline
+ *   characters ('\n') within the text will be replaced with regular whitespace (' '), ensuring that
+ *   the contents of the text field are presented in a single line.
+ * @param scrollState Scroll state that manages either horizontal or vertical scroll of the text
+ *   field. If [lineLimits] is [SingleLine], this text field is treated as single line with
+ *   horizontal scroll behavior. In other cases the text field becomes vertically scrollable.
+ * @param shape the shape of the text field's border
+ * @param colors [TextFieldColors] that will be used to resolve color of the text and content
+ *   (including label, placeholder, leading and trailing icons, border) for this text field in
+ *   different states. See [TextFieldDefaults.outlinedTextFieldColors]
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this text field. You can use this to change the text field's
+ *   appearance or preview the text field in different states. Note that if `null` is provided,
+ *   interactions will still happen internally.
+ */
+@Composable
+fun OutlinedTextField(
+    state: TextFieldState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    textStyle: TextStyle = LocalTextStyle.current,
+    label: @Composable (() -> Unit)? = null,
+    placeholder: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    isError: Boolean = false,
+    inputTransformation: InputTransformation? = null,
+    outputTransformation: OutputTransformation? = null,
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+    onKeyboardAction: KeyboardActionHandler? = null,
+    lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+    scrollState: ScrollState = rememberScrollState(),
+    shape: Shape = TextFieldDefaults.OutlinedTextFieldShape,
+    colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors(),
+    interactionSource: MutableInteractionSource? = null,
+) {
+    @Suppress("NAME_SHADOWING")
+    val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+    // If color is not provided via the text style, use content color as a default
+    val textColor = textStyle.color.takeOrElse { colors.textColor(enabled).value }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+    val density = LocalDensity.current
+
+    @OptIn(ExperimentalMaterialApi::class)
+    BasicTextField(
+        state = state,
+        modifier =
+            modifier
+                .then(
+                    if (label != null) {
+                        Modifier
+                            // Merge semantics at the beginning of the modifier chain to ensure
+                            // padding is considered part of the text field.
+                            .semantics(mergeDescendants = true) {}
+                            .padding(top = with(density) { OutlinedTextFieldTopPadding.toDp() })
+                    } else {
+                        Modifier
+                    }
+                )
+                .defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
+                .defaultMinSize(
+                    minWidth = TextFieldDefaults.MinWidth,
+                    minHeight = TextFieldDefaults.MinHeight
+                ),
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = mergedTextStyle,
+        cursorBrush = SolidColor(colors.cursorColor(isError).value),
+        inputTransformation = inputTransformation,
+        outputTransformation = outputTransformation,
+        keyboardOptions = keyboardOptions,
+        onKeyboardAction = onKeyboardAction,
+        interactionSource = interactionSource,
+        scrollState = scrollState,
+        lineLimits = lineLimits,
+        decorator = { innerTextField ->
+            val textPostTransformation =
+                if (outputTransformation == null) {
+                    state.text.toString()
+                } else {
+                    // TODO: use constructor to create TextFieldBuffer from TextFieldState when
+                    // available
+                    lateinit var buffer: TextFieldBuffer
+                    state.edit { buffer = this }
+                    // after edit completes, mutations on buffer are ineffective
+                    with(outputTransformation) { buffer.transformOutput() }
+                    buffer.asCharSequence().toString()
+                }
+
+            TextFieldDefaults.OutlinedTextFieldDecorationBox(
+                value = textPostTransformation,
+                visualTransformation = VisualTransformation.None,
+                innerTextField = innerTextField,
+                placeholder = placeholder,
+                label = label,
+                leadingIcon = leadingIcon,
+                trailingIcon = trailingIcon,
+                singleLine = lineLimits == SingleLine,
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                shape = shape,
+                colors = colors,
+                border = {
+                    TextFieldDefaults.BorderBox(enabled, isError, interactionSource, colors, shape)
+                }
+            )
+        }
+    )
+}
+
+/**
+ * <a href="https://material.io/components/text-fields#outlined-text-field" class="external"
+ * target="_blank">Material Design outlined text field</a>.
+ *
+ * Outlined text fields have less visual emphasis than filled text fields. When they appear in
+ * places like forms, where many text fields are placed together, their reduced emphasis helps
+ * simplify the layout.
+ *
+ * ![Outlined text field
+ * image](https://developer.android.com/images/reference/androidx/compose/material/outlined-text-field.png)
  *
  * If apart from input text change you also want to observe the cursor location, selection range, or
  * IME composition use the OutlinedTextField overload with the [TextFieldValue] parameter instead.
@@ -290,10 +465,6 @@
  * ![Outlined text field
  * image](https://developer.android.com/images/reference/androidx/compose/material/outlined-text-field.png)
  *
- * See example usage:
- *
- * @sample androidx.compose.material.samples.OutlinedTextFieldSample
- *
  * This overload provides access to the input text, cursor position and selection range and IME
  * composition. If you only want to observe an input text change, use the OutlinedTextField overload
  * with the [String] parameter instead.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index 3db57d2..b7b30dc 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -43,6 +43,7 @@
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMap
 import androidx.compose.ui.util.fastMaxBy
+import kotlin.jvm.JvmInline
 
 /**
  * State for [Scaffold] composable component.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt
index 2cd1bdf..36afe64 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SnackbarHost.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.semantics.LiveRegionMode
 import androidx.compose.ui.semantics.dismiss
 import androidx.compose.ui.semantics.liveRegion
+import androidx.compose.ui.semantics.paneTitle
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.util.fastFilterNotNull
 import androidx.compose.ui.util.fastForEach
@@ -244,6 +245,7 @@
     content: @Composable (SnackbarData) -> Unit
 ) {
     val state = remember { FadeInFadeOutState<SnackbarData?>() }
+    val a11yPaneTitle = getString(Strings.SnackbarPaneTitle)
     if (current != state.current) {
         state.current = current
         val keys = state.items.fastMap { it.key }.toMutableList()
@@ -297,6 +299,7 @@
                         )
                         .semantics {
                             liveRegion = LiveRegionMode.Polite
+                            paneTitle = a11yPaneTitle
                             dismiss {
                                 key.dismiss()
                                 true
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Strings.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Strings.kt
index 5294b4e..39c2fbd 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Strings.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Strings.kt
@@ -30,6 +30,7 @@
         val ExposedDropdownMenu = Strings(4)
         val SliderRangeStart = Strings(5)
         val SliderRangeEnd = Strings(6)
+        val SnackbarPaneTitle = Strings(7)
     }
 }
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
index 3b8c5a1..c038442 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
@@ -153,7 +153,7 @@
  */
 @Composable
 @ExperimentalMaterialApi
-@SuppressWarnings("ReferencesDeprecated")
+@Suppress("ReferencesDeprecated")
 fun SwipeToDismiss(
     state: DismissState,
     modifier: Modifier = Modifier,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index c641643..20fa7fe 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -17,6 +17,7 @@
 package androidx.compose.material
 
 import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
@@ -25,10 +26,19 @@
 import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.ZeroCornerSize
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.KeyboardActionHandler
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldBuffer
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
 import androidx.compose.material.TextFieldDefaults.indicatorLine
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
@@ -78,10 +88,17 @@
  *
  * If you are looking for an outlined version, see [OutlinedTextField].
  *
+ * This overload of [TextField] uses [TextFieldState] to keep track of its text content and position
+ * of the cursor or selection.
+ *
  * A simple single line text field looks like:
  *
  * @sample androidx.compose.material.samples.SimpleTextFieldSample
  *
+ * You can control the initial text input and selection:
+ *
+ * @sample androidx.compose.material.samples.TextFieldWithInitialValueAndSelection
+ *
  * You may provide a placeholder:
  *
  * @sample androidx.compose.material.samples.TextFieldWithPlaceholder
@@ -98,13 +115,156 @@
  *
  * @sample androidx.compose.material.samples.TextFieldWithHelperMessage
  *
- * Password text field example:
- *
- * @sample androidx.compose.material.samples.PasswordTextField
- *
  * Hiding a software keyboard on IME action performed:
  *
  * @sample androidx.compose.material.samples.TextFieldWithHideKeyboardOnImeAction
+ * @param state [TextFieldState] object that holds the internal editing state of this text field.
+ * @param modifier a [Modifier] for this text field
+ * @param enabled controls the enabled state of the [TextField]. When `false`, the text field will
+ *   be neither editable nor focusable, the input of the text field will not be selectable, visually
+ *   text field will appear in the disabled UI state
+ * @param readOnly controls the editable state of the [TextField]. When `true`, the text field can
+ *   not be modified, however, a user can focus it and copy text from it. Read-only text fields are
+ *   usually used to display pre-filled forms that user can not edit
+ * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
+ *   [LocalTextStyle] defined by the theme
+ * @param label the optional label to be displayed inside the text field container. The default text
+ *   style for internal [Text] is [Typography.caption] when the text field is in focus and
+ *   [Typography.subtitle1] when the text field is not in focus
+ * @param placeholder the optional placeholder to be displayed when the text field is in focus and
+ *   the input text is empty. The default text style for internal [Text] is [Typography.subtitle1]
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+ *   container
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ *   container
+ * @param isError indicates if the text field's current value is in error. If set to true, the
+ *   label, bottom indicator and trailing icon by default will be displayed in error color
+ * @param inputTransformation Optional [InputTransformation] that will be used to transform changes
+ *   to the [TextFieldState] made by the user. The transformation will be applied to changes made by
+ *   hardware and software keyboard events, pasting or dropping text, accessibility services, and
+ *   tests. The transformation will _not_ be applied when changing the [state] programmatically, or
+ *   when the transformation is changed. If the transformation is changed on an existing text field,
+ *   it will be applied to the next user edit. the transformation will not immediately affect the
+ *   current [state].
+ * @param outputTransformation An [OutputTransformation] that transforms how the contents of the
+ *   text field are presented.
+ * @param keyboardOptions software keyboard options that contains configuration such as
+ *   [KeyboardType] and [ImeAction].
+ * @param onKeyboardAction Called when the user presses the action button in the input method editor
+ *   (IME), or by pressing the enter key on a hardware keyboard. By default this parameter is null,
+ *   and would execute the default behavior for a received IME Action e.g., [ImeAction.Done] would
+ *   close the keyboard, [ImeAction.Next] would switch the focus to the next focusable item on the
+ *   screen.
+ * @param lineLimits Whether the text field should be [SingleLine], scroll horizontally, and ignore
+ *   newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all newline
+ *   characters ('\n') within the text will be replaced with regular whitespace (' '), ensuring that
+ *   the contents of the text field are presented in a single line.
+ * @param scrollState Scroll state that manages either horizontal or vertical scroll of the text
+ *   field. If [lineLimits] is [SingleLine], this text field is treated as single line with
+ *   horizontal scroll behavior. In other cases the text field becomes vertically scrollable.
+ * @param shape the shape of the text field's container
+ * @param colors [TextFieldColors] that will be used to resolve color of the text, content
+ *   (including label, placeholder, leading and trailing icons, indicator line) and background for
+ *   this text field in different states. See [TextFieldDefaults.textFieldColors]
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this text field. You can use this to change the text field's
+ *   appearance or preview the text field in different states. Note that if `null` is provided,
+ *   interactions will still happen internally.
+ */
+@Composable
+fun TextField(
+    state: TextFieldState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    textStyle: TextStyle = LocalTextStyle.current,
+    label: @Composable (() -> Unit)? = null,
+    placeholder: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    isError: Boolean = false,
+    inputTransformation: InputTransformation? = null,
+    outputTransformation: OutputTransformation? = null,
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+    onKeyboardAction: KeyboardActionHandler? = null,
+    lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+    scrollState: ScrollState = rememberScrollState(),
+    shape: Shape = TextFieldDefaults.TextFieldShape,
+    colors: TextFieldColors = TextFieldDefaults.textFieldColors(),
+    interactionSource: MutableInteractionSource? = null,
+) {
+    @Suppress("NAME_SHADOWING")
+    val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+    // If color is not provided via the text style, use content color as a default
+    val textColor = textStyle.color.takeOrElse { colors.textColor(enabled).value }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+    @OptIn(ExperimentalMaterialApi::class)
+    BasicTextField(
+        state = state,
+        modifier =
+            modifier
+                .indicatorLine(enabled, isError, interactionSource, colors)
+                .defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
+                .defaultMinSize(
+                    minWidth = TextFieldDefaults.MinWidth,
+                    minHeight = TextFieldDefaults.MinHeight
+                ),
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = mergedTextStyle,
+        cursorBrush = SolidColor(colors.cursorColor(isError).value),
+        inputTransformation = inputTransformation,
+        outputTransformation = outputTransformation,
+        keyboardOptions = keyboardOptions,
+        onKeyboardAction = onKeyboardAction,
+        interactionSource = interactionSource,
+        scrollState = scrollState,
+        lineLimits = lineLimits,
+        decorator = { innerTextField ->
+            val textPostTransformation =
+                if (outputTransformation == null) {
+                    state.text.toString()
+                } else {
+                    // TODO: use constructor to create TextFieldBuffer from TextFieldState when
+                    // available
+                    lateinit var buffer: TextFieldBuffer
+                    state.edit { buffer = this }
+                    // after edit completes, mutations on buffer are ineffective
+                    with(outputTransformation) { buffer.transformOutput() }
+                    buffer.asCharSequence().toString()
+                }
+
+            TextFieldDefaults.TextFieldDecorationBox(
+                value = textPostTransformation,
+                visualTransformation = VisualTransformation.None,
+                innerTextField = innerTextField,
+                placeholder = placeholder,
+                label = label,
+                leadingIcon = leadingIcon,
+                trailingIcon = trailingIcon,
+                singleLine = lineLimits == SingleLine,
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                shape = shape,
+                colors = colors,
+            )
+        }
+    )
+}
+
+/**
+ * <a href="https://material.io/components/text-fields#filled-text-field" class="external"
+ * target="_blank">Material Design filled text field</a>.
+ *
+ * Filled text fields have more visual emphasis than outlined text fields, making them stand out
+ * when surrounded by other content and components.
+ *
+ * ![Filled text field
+ * image](https://developer.android.com/images/reference/androidx/compose/material/filled-text-field.png)
+ *
+ * If you are looking for an outlined version, see [OutlinedTextField].
  *
  * If apart from input text change you also want to observe the cursor location, selection range, or
  * IME composition use the TextField overload with the [TextFieldValue] parameter instead.
@@ -295,10 +455,6 @@
  *
  * If you are looking for an outlined version, see [OutlinedTextField].
  *
- * See example usage:
- *
- * @sample androidx.compose.material.samples.TextFieldSample
- *
  * This overload provides access to the input text, cursor position, selection range and IME
  * composition. If you only want to observe an input text change, use the TextField overload with
  * the [String] parameter instead.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/internal/Icons.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/internal/Icons.kt
new file mode 100644
index 0000000..59811672
--- /dev/null
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/internal/Icons.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material.internal
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.vector.DefaultFillType
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.PathBuilder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.unit.dp
+
+internal object Icons {
+    internal object Filled {
+        internal val ArrowDropDown: ImageVector
+            get() {
+                if (_arrowDropDown != null) {
+                    return _arrowDropDown!!
+                }
+                _arrowDropDown =
+                    materialIcon(name = "Filled.ArrowDropDown") {
+                        materialPath {
+                            moveTo(7.0f, 10.0f)
+                            lineToRelative(5.0f, 5.0f)
+                            lineToRelative(5.0f, -5.0f)
+                            close()
+                        }
+                    }
+                return _arrowDropDown!!
+            }
+
+        private var _arrowDropDown: ImageVector? = null
+    }
+}
+
+private inline fun materialIcon(
+    name: String,
+    block: ImageVector.Builder.() -> ImageVector.Builder
+): ImageVector =
+    ImageVector.Builder(
+            name = name,
+            defaultWidth = MaterialIconDimension.dp,
+            defaultHeight = MaterialIconDimension.dp,
+            viewportWidth = MaterialIconDimension,
+            viewportHeight = MaterialIconDimension
+        )
+        .block()
+        .build()
+
+private inline fun ImageVector.Builder.materialPath(
+    fillAlpha: Float = 1f,
+    strokeAlpha: Float = 1f,
+    pathFillType: PathFillType = DefaultFillType,
+    pathBuilder: PathBuilder.() -> Unit
+) =
+    path(
+        fill = SolidColor(Color.Black),
+        fillAlpha = fillAlpha,
+        stroke = null,
+        strokeAlpha = strokeAlpha,
+        strokeLineWidth = 1f,
+        strokeLineCap = StrokeCap.Butt,
+        strokeLineJoin = StrokeJoin.Bevel,
+        strokeLineMiter = 1f,
+        pathFillType = pathFillType,
+        pathBuilder = pathBuilder
+    )
+
+private const val MaterialIconDimension = 24f
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt
diff --git a/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AnchoredDraggable.commonStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AnchoredDraggable.commonStubs.kt
new file mode 100644
index 0000000..f96d54d
--- /dev/null
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AnchoredDraggable.commonStubs.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal : CancellationException("Anchored drag finished") {
+    init {
+        implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt
diff --git a/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InteractiveComponentSize.commonStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InteractiveComponentSize.commonStubs.kt
new file mode 100644
index 0000000..360c617
--- /dev/null
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InteractiveComponentSize.commonStubs.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun identityHashCode(value: Any): Int = implementedInJetBrainsFork()
diff --git a/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InternalMutatorMutex.commonStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InternalMutatorMutex.commonStubs.kt
new file mode 100644
index 0000000..9d385de
--- /dev/null
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InternalMutatorMutex.commonStubs.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+internal actual class InternalAtomicReference<V> actual constructor(value: V) {
+    actual fun get(): V = implementedInJetBrainsFork()
+
+    actual fun set(value: V) {
+        implementedInJetBrainsFork()
+    }
+
+    actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+    actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt
diff --git a/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Menu.commonStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Menu.commonStubs.kt
new file mode 100644
index 0000000..191389b
--- /dev/null
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Menu.commonStubs.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.window.PopupProperties
+
+@Composable
+actual fun DropdownMenuItem(
+    onClick: () -> Unit,
+    modifier: Modifier,
+    enabled: Boolean,
+    contentPadding: PaddingValues,
+    interactionSource: MutableInteractionSource?,
+    content: @Composable RowScope.() -> Unit
+): Unit = implementedInJetBrainsFork()
+
+@Composable
+actual fun DropdownMenu(
+    expanded: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier,
+    offset: DpOffset,
+    scrollState: ScrollState,
+    properties: PopupProperties,
+    content: @Composable ColumnScope.() -> Unit
+): Unit = implementedInJetBrainsFork()
+
+internal actual val DefaultMenuProperties: PopupProperties = implementedInJetBrainsFork()
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt
diff --git a/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/AnchoredDraggable.jvm.kt b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/AnchoredDraggable.jvm.kt
new file mode 100644
index 0000000..7f07ef3
--- /dev/null
+++ b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/AnchoredDraggable.jvm.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal : CancellationException("Anchored drag finished") {
+    override fun fillInStackTrace(): Throwable {
+        stackTrace = emptyArray()
+        return this
+    }
+}
diff --git a/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/InteractiveComponentSize.jvm.kt b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/InteractiveComponentSize.jvm.kt
new file mode 100644
index 0000000..7597077
--- /dev/null
+++ b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/InteractiveComponentSize.jvm.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun identityHashCode(value: Any): Int = System.identityHashCode(value)
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DesktopMenu.jvmStubs.kt b/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DesktopMenu.jvmStubs.kt
deleted file mode 100644
index 2e0c188..0000000
--- a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DesktopMenu.jvmStubs.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material
-
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-
-@Composable
-actual fun DropdownMenuItem(
-    onClick: () -> Unit,
-    modifier: Modifier,
-    enabled: Boolean,
-    contentPadding: PaddingValues,
-    interactionSource: MutableInteractionSource?,
-    content: @Composable RowScope.() -> Unit
-): Unit = implementedInJetBrainsFork()
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Menu.jvmStubs.kt b/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Menu.jvmStubs.kt
deleted file mode 100644
index 4164d13..0000000
--- a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Menu.jvmStubs.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material
-
-import androidx.compose.foundation.ScrollState
-import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.DpOffset
-import androidx.compose.ui.window.PopupProperties
-
-@Composable
-actual fun DropdownMenu(
-    expanded: Boolean,
-    onDismissRequest: () -> Unit,
-    modifier: Modifier,
-    offset: DpOffset,
-    scrollState: ScrollState,
-    properties: PopupProperties,
-    content: @Composable ColumnScope.() -> Unit
-): Unit = implementedInJetBrainsFork()
-
-internal actual val DefaultMenuProperties: PopupProperties = implementedInJetBrainsFork()
diff --git a/compose/material3/adaptive/adaptive-layout/build.gradle b/compose/material3/adaptive/adaptive-layout/build.gradle
index bc2a2d1..cf597c7 100644
--- a/compose/material3/adaptive/adaptive-layout/build.gradle
+++ b/compose/material3/adaptive/adaptive-layout/build.gradle
@@ -42,9 +42,9 @@
             dependencies {
                 implementation(libs.kotlinStdlib)
                 api(project(":compose:material3:adaptive:adaptive"))
-                api("androidx.compose.animation:animation-core:1.7.0-beta04")
-                api("androidx.compose.ui:ui:1.7.0-beta04")
-                implementation("androidx.compose.animation:animation:1.7.0-beta04")
+                api("androidx.compose.animation:animation-core:1.7.0-beta06")
+                api("androidx.compose.ui:ui:1.7.0-beta06")
+                implementation("androidx.compose.animation:animation:1.7.0-beta06")
                 implementation("androidx.compose.foundation:foundation:1.6.5")
                 implementation("androidx.compose.foundation:foundation-layout:1.6.5")
                 implementation("androidx.compose.ui:ui-geometry:1.6.5")
@@ -67,7 +67,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
             }
         }
@@ -101,16 +101,13 @@
     }
 }
 
-android {
-    namespace "androidx.compose.material3.adaptive.layout"
-}
-
 androidx {
     name = "Material Adaptive"
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2023"
     description = "Compose Material Design Adaptive Library"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:material3:adaptive:adaptive-samples"))
 }
 
@@ -120,6 +117,7 @@
 
 // Screenshot tests related setup
 android {
+    compileSdk 35
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/compose/material3/adaptive"
     namespace "androidx.compose.material3.adaptive.layout"
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
index 3a88901..0ed8d91 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
@@ -438,7 +438,7 @@
                         layoutPhysicalPartitions.add(
                             Rect(actualLeft, actualTop, hingeBound.left, actualBottom)
                         )
-                        actualLeft += max(hingeBound.right, hingeBound.left + verticalSpacerSize)
+                        actualLeft = max(hingeBound.right, hingeBound.left + verticalSpacerSize)
                     }
                 }
                 if (actualLeft < actualRight) {
diff --git a/compose/material3/adaptive/adaptive-navigation/build.gradle b/compose/material3/adaptive/adaptive-navigation/build.gradle
index 9c75325..67fef66 100644
--- a/compose/material3/adaptive/adaptive-navigation/build.gradle
+++ b/compose/material3/adaptive/adaptive-navigation/build.gradle
@@ -61,7 +61,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
                 implementation("androidx.activity:activity-compose:1.8.2")
             }
@@ -106,6 +106,7 @@
     inceptionYear = "2023"
     description = "Compose Material Design Adaptive Library"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 tasks.withType(KotlinCompile).configureEach {
@@ -114,6 +115,7 @@
 
 // Screenshot tests related setup
 android {
+    compileSdk 35
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/compose/material3/adaptive"
     namespace "androidx.compose.material3.adaptive.navigation"
diff --git a/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt b/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt
index 10a5006..3feed63 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt
@@ -42,6 +42,7 @@
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.util.fastMap
+import kotlin.collections.removeLast as removeLastKt
 
 /**
  * The common interface of the default navigation implementations for different three-pane
@@ -344,7 +345,7 @@
         }
         val targetSize = previousDestinationIndex + 1
         while (destinationHistory.size > targetSize) {
-            destinationHistory.removeLast()
+            destinationHistory.removeLastKt()
         }
         return true
     }
diff --git a/compose/material3/adaptive/adaptive/api/current.txt b/compose/material3/adaptive/adaptive/api/current.txt
index 4532ffe..4758b19 100644
--- a/compose/material3/adaptive/adaptive/api/current.txt
+++ b/compose/material3/adaptive/adaptive/api/current.txt
@@ -29,6 +29,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Posture {
+    ctor public Posture();
     ctor public Posture(optional boolean isTabletop, optional java.util.List<androidx.compose.material3.adaptive.HingeInfo> hingeList);
     method public java.util.List<androidx.compose.material3.adaptive.HingeInfo> getHingeList();
     method public boolean isTabletop();
diff --git a/compose/material3/adaptive/adaptive/api/restricted_current.txt b/compose/material3/adaptive/adaptive/api/restricted_current.txt
index 4532ffe..4758b19 100644
--- a/compose/material3/adaptive/adaptive/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive/api/restricted_current.txt
@@ -29,6 +29,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Posture {
+    ctor public Posture();
     ctor public Posture(optional boolean isTabletop, optional java.util.List<androidx.compose.material3.adaptive.HingeInfo> hingeList);
     method public java.util.List<androidx.compose.material3.adaptive.HingeInfo> getHingeList();
     method public boolean isTabletop();
diff --git a/compose/material3/adaptive/adaptive/build.gradle b/compose/material3/adaptive/adaptive/build.gradle
index 2289202..7ab026e 100644
--- a/compose/material3/adaptive/adaptive/build.gradle
+++ b/compose/material3/adaptive/adaptive/build.gradle
@@ -61,7 +61,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
                 api("androidx.window:window:1.3.0-rc01")
             }
@@ -106,6 +106,7 @@
     inceptionYear = "2023"
     description = "Compose Material Design Adaptive Library"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 tasks.withType(KotlinCompile).configureEach {
@@ -114,6 +115,7 @@
 
 // Screenshot tests related setup
 android {
+    compileSdk 35
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/compose/material3/adaptive"
     namespace "androidx.compose.material3.adaptive"
diff --git a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
index 18b0996..bf9047b 100644
--- a/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
+++ b/compose/material3/adaptive/adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/CollectWindowSizeAsStateTest.kt
@@ -83,7 +83,10 @@
 internal class MockWindowMetricsCalculator(private val mockWindowSize: State<IntSize>) :
     WindowMetricsCalculator {
     override fun computeCurrentWindowMetrics(activity: Activity): WindowMetrics {
-        return WindowMetrics(Rect(0, 0, mockWindowSize.value.width, mockWindowSize.value.height))
+        return WindowMetrics(
+            Rect(0, 0, mockWindowSize.value.width, mockWindowSize.value.height),
+            density = 1f
+        )
     }
 
     override fun computeMaximumWindowMetrics(activity: Activity): WindowMetrics {
@@ -91,6 +94,9 @@
     }
 
     override fun computeCurrentWindowMetrics(@UiContext context: Context): WindowMetrics {
-        return WindowMetrics(Rect(0, 0, mockWindowSize.value.width, mockWindowSize.value.height))
+        return WindowMetrics(
+            Rect(0, 0, mockWindowSize.value.width, mockWindowSize.value.height),
+            density = 1f
+        )
     }
 }
diff --git a/compose/material3/adaptive/benchmark/build.gradle b/compose/material3/adaptive/benchmark/build.gradle
index e63b29e..95ff74a 100644
--- a/compose/material3/adaptive/benchmark/build.gradle
+++ b/compose/material3/adaptive/benchmark/build.gradle
@@ -34,5 +34,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.adaptive.benchmark"
 }
diff --git a/compose/material3/adaptive/samples/build.gradle b/compose/material3/adaptive/samples/build.gradle
index f641666..08e4e4e 100644
--- a/compose/material3/adaptive/samples/build.gradle
+++ b/compose/material3/adaptive/samples/build.gradle
@@ -38,6 +38,7 @@
 
     implementation("androidx.compose.foundation:foundation:1.6.0-rc01")
     implementation("androidx.compose.foundation:foundation-layout:1.6.0-rc01")
+    implementation("androidx.compose.material:material-icons-core:1.6.8")
     implementation(project(":compose:material3:adaptive:adaptive-layout"))
     implementation(project(":compose:material3:adaptive:adaptive-navigation"))
     implementation(project(":compose:material3:material3"))
@@ -57,5 +58,7 @@
 }
 
 android {
+    compileSdk 35
+
     namespace "androidx.compose.material3.adaptive.samples"
 }
diff --git a/compose/material3/benchmark/build.gradle b/compose/material3/benchmark/build.gradle
index 6a019df..3018b05 100644
--- a/compose/material3/benchmark/build.gradle
+++ b/compose/material3/benchmark/build.gradle
@@ -38,5 +38,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.benchmark"
 }
\ No newline at end of file
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/BottomAppBarBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/BottomAppBarBenchmark.kt
new file mode 100644
index 0000000..96f3a4c
--- /dev/null
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/BottomAppBarBenchmark.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.benchmark
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.ArrowForward
+import androidx.compose.material3.BottomAppBar
+import androidx.compose.material3.BottomAppBarDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.benchmark.benchmarkFirstDraw
+import androidx.compose.testutils.benchmark.benchmarkFirstLayout
+import androidx.compose.testutils.benchmark.benchmarkFirstMeasure
+import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
+import androidx.compose.ui.Modifier
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+class BottomAppBarBenchmark {
+    @get:Rule val benchmarkRule = ComposeBenchmarkRule()
+
+    private val bottomAppBarTestCaseFactory = { BottomAppBarTestCase() }
+
+    @Ignore
+    @Test
+    fun first_compose() {
+        benchmarkRule.benchmarkFirstCompose(bottomAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun first_measure() {
+        benchmarkRule.benchmarkFirstMeasure(bottomAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun first_layout() {
+        benchmarkRule.benchmarkFirstLayout(bottomAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun first_draw() {
+        benchmarkRule.benchmarkFirstDraw(bottomAppBarTestCaseFactory)
+    }
+
+    @Test
+    fun bottomAppBar_firstPixel() {
+        benchmarkRule.benchmarkToFirstPixel(bottomAppBarTestCaseFactory)
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+internal class BottomAppBarTestCase : LayeredComposeTestCase() {
+    @Composable
+    override fun MeasuredContent() {
+        BottomAppBar(
+            horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+            modifier = Modifier.fillMaxWidth(),
+        ) {
+            IconButton(onClick = { /* doSomething() */ }) {
+                Icon(
+                    Icons.AutoMirrored.Filled.ArrowBack,
+                    contentDescription = "Localized description",
+                )
+            }
+            IconButton(onClick = { /* doSomething() */ }) {
+                Icon(
+                    Icons.AutoMirrored.Filled.ArrowForward,
+                    contentDescription = "Localized description",
+                )
+            }
+        }
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialTheme { content() }
+    }
+}
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/FloatingAppBarBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/FloatingAppBarBenchmark.kt
new file mode 100644
index 0000000..9efb2d5
--- /dev/null
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/FloatingAppBarBenchmark.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.benchmark
+
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.HorizontalFloatingAppBar
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.VerticalFloatingAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.benchmark.benchmarkFirstDraw
+import androidx.compose.testutils.benchmark.benchmarkFirstLayout
+import androidx.compose.testutils.benchmark.benchmarkFirstMeasure
+import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
+import androidx.compose.ui.Modifier
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+class FloatingAppBarBenchmark {
+    @get:Rule val benchmarkRule = ComposeBenchmarkRule()
+
+    private val horizontalFloatingAppBarTestCaseFactory = { HorizontalFloatingAppBarTestCase() }
+    private val verticalFloatingAppBarTestCaseFactory = { VerticalFloatingAppBarTestCase() }
+
+    @Ignore
+    @Test
+    fun horizontalFloatingAppBar_first_compose() {
+        benchmarkRule.benchmarkFirstCompose(horizontalFloatingAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun verticalFloatingAppBar_first_compose() {
+        benchmarkRule.benchmarkFirstCompose(verticalFloatingAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun horizontalFloatingAppBar_first_measure() {
+        benchmarkRule.benchmarkFirstMeasure(horizontalFloatingAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun verticalFloatingAppBar_first_measure() {
+        benchmarkRule.benchmarkFirstMeasure(verticalFloatingAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun horizontalFloatingAppBar_first_layout() {
+        benchmarkRule.benchmarkFirstLayout(horizontalFloatingAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun verticalFloatingAppBar_first_layout() {
+        benchmarkRule.benchmarkFirstLayout(verticalFloatingAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun horizontalFloatingAppBar_first_draw() {
+        benchmarkRule.benchmarkFirstDraw(horizontalFloatingAppBarTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun verticalFloatingAppBar_first_draw() {
+        benchmarkRule.benchmarkFirstDraw(verticalFloatingAppBarTestCaseFactory)
+    }
+
+    @Test
+    fun horizontalFloatingAppBar_firstPixel() {
+        benchmarkRule.benchmarkToFirstPixel(horizontalFloatingAppBarTestCaseFactory)
+    }
+
+    @Test
+    fun verticalFloatingAppBar_firstPixel() {
+        benchmarkRule.benchmarkToFirstPixel(verticalFloatingAppBarTestCaseFactory)
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+internal class HorizontalFloatingAppBarTestCase : LayeredComposeTestCase() {
+    @Composable
+    override fun MeasuredContent() {
+        HorizontalFloatingAppBar(
+            expanded = true,
+            modifier = Modifier.fillMaxWidth(),
+            leadingContent = { leadingContent() },
+            trailingContent = { trailingContent() },
+            content = { mainContent() },
+        )
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialTheme { content() }
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+internal class VerticalFloatingAppBarTestCase : LayeredComposeTestCase() {
+    @Composable
+    override fun MeasuredContent() {
+        VerticalFloatingAppBar(
+            expanded = true,
+            modifier = Modifier.fillMaxHeight(),
+            leadingContent = { leadingContent() },
+            trailingContent = { trailingContent() },
+            content = { mainContent() },
+        )
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialTheme { content() }
+    }
+}
+
+@Composable
+private fun leadingContent() {
+    IconButton(onClick = { /* doSomething() */ }) {
+        Icon(Icons.Filled.Add, contentDescription = "Localized description")
+    }
+}
+
+@Composable
+private fun trailingContent() {
+    IconButton(onClick = { /* doSomething() */ }) {
+        Icon(Icons.Filled.Check, contentDescription = "Localized description")
+    }
+}
+
+@Composable
+private fun mainContent() {
+    IconButton(onClick = { /* doSomething() */ }) {
+        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+    }
+}
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationBarBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationBarBenchmark.kt
index e5428ed..3772c80 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationBarBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationBarBenchmark.kt
@@ -18,9 +18,14 @@
 
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialExpressiveTheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.NavigationBar
 import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.NavigationItemIconPosition
+import androidx.compose.material3.ShortNavigationBar
+import androidx.compose.material3.ShortNavigationBarItem
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableIntState
 import androidx.compose.runtime.mutableIntStateOf
@@ -28,21 +33,27 @@
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
 import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
+import androidx.test.filters.LargeTest
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 class NavigationBarBenchmark {
     @get:Rule val benchmarkRule = ComposeBenchmarkRule()
 
     private val testCaseFactory = { NavigationBarTestCase() }
+    private val shortNavBarTopIconTestCaseFactory = { NavigationBarTestCase(true) }
+    private val shortNavBarStartIconTestCaseFactory = {
+        NavigationBarTestCase(true, NavigationItemIconPosition.Start)
+    }
 
     @Test
     fun firstPixel() {
@@ -56,32 +67,86 @@
             assertOneRecomposition = false,
         )
     }
+
+    @Test
+    fun shortNavigationBar_topIcon_firstPixel() {
+        benchmarkRule.benchmarkFirstRenderUntilStable(shortNavBarTopIconTestCaseFactory)
+    }
+
+    @Test
+    fun shortNavigationBar_topIcon_changeSelection() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            shortNavBarTopIconTestCaseFactory,
+            assertOneRecomposition = false,
+        )
+    }
+
+    @Test
+    fun shortNavigationBar_startIcon_firstPixel() {
+        benchmarkRule.benchmarkToFirstPixel(shortNavBarStartIconTestCaseFactory)
+    }
+
+    @Test
+    fun shortNavigationBar_startIcon_changeSelection() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            shortNavBarStartIconTestCaseFactory,
+            assertOneRecomposition = false,
+        )
+    }
 }
 
-internal class NavigationBarTestCase : LayeredComposeTestCase(), ToggleableTestCase {
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+internal class NavigationBarTestCase(
+    private val isShortNavBar: Boolean = false,
+    private val shortNavBarIconPosition: NavigationItemIconPosition =
+        NavigationItemIconPosition.Top,
+) : LayeredComposeTestCase(), ToggleableTestCase {
     private lateinit var selectedIndexState: MutableIntState
 
     @Composable
     override fun MeasuredContent() {
         selectedIndexState = remember { mutableIntStateOf(0) }
 
-        NavigationBar {
-            NavigationBarItem(
-                selected = selectedIndexState.value == 0,
-                onClick = {},
-                icon = { Spacer(Modifier.size(24.dp)) },
-            )
-            NavigationBarItem(
-                selected = selectedIndexState.value == 1,
-                onClick = {},
-                icon = { Spacer(Modifier.size(24.dp)) },
-            )
+        if (isShortNavBar) {
+            ShortNavigationBar {
+                ShortNavigationBarItem(
+                    selected = selectedIndexState.value == 0,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                    iconPosition = shortNavBarIconPosition,
+                    label = { Spacer(Modifier.size(24.dp)) }
+                )
+                ShortNavigationBarItem(
+                    selected = selectedIndexState.value == 1,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                    iconPosition = shortNavBarIconPosition,
+                    label = { Spacer(Modifier.size(24.dp)) }
+                )
+            }
+        } else {
+            NavigationBar {
+                NavigationBarItem(
+                    selected = selectedIndexState.value == 0,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                )
+                NavigationBarItem(
+                    selected = selectedIndexState.value == 1,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                )
+            }
         }
     }
 
     @Composable
     override fun ContentWrappers(content: @Composable () -> Unit) {
-        MaterialTheme { content() }
+        if (isShortNavBar) {
+            MaterialExpressiveTheme { content() }
+        } else {
+            MaterialTheme { content() }
+        }
     }
 
     override fun toggleState() {
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt
index 955274f..86a552e 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt
@@ -18,12 +18,18 @@
 
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialExpressiveTheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.NavigationRail
 import androidx.compose.material3.NavigationRailItem
+import androidx.compose.material3.WideNavigationRail
+import androidx.compose.material3.WideNavigationRailItem
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableIntState
+import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
@@ -44,6 +50,8 @@
     @get:Rule val benchmarkRule = ComposeBenchmarkRule()
 
     private val testCaseFactory = { NavigationRailTestCase() }
+    private val collapsedWideRailTestCaseFactory = { NavigationRailTestCase(true) }
+    private val expandedWideRailTestCaseFactory = { NavigationRailTestCase(true, true) }
 
     @Test
     fun firstPixel() {
@@ -57,35 +65,119 @@
             assertOneRecomposition = false,
         )
     }
+
+    @Test
+    fun wideNavigationRail_collapsed_firstPixel() {
+        benchmarkRule.benchmarkFirstRenderUntilStable(collapsedWideRailTestCaseFactory)
+    }
+
+    @Test
+    fun wideNavigationRail_collapsed_changeSelection() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            collapsedWideRailTestCaseFactory,
+            assertOneRecomposition = false,
+        )
+    }
+
+    @Test
+    fun wideNavigationRail_collapsed_expands() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            { NavigationRailTestCase(isWideNavRail = true, changeSelectionToggleTestCase = false) },
+            assertOneRecomposition = false,
+        )
+    }
+
+    @Test
+    fun wideNavigationRail_expanded_firstPixel() {
+        benchmarkRule.benchmarkToFirstPixel(expandedWideRailTestCaseFactory)
+    }
+
+    @Test
+    fun wideNavigationRail_expanded_changeSelection() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            expandedWideRailTestCaseFactory,
+            assertOneRecomposition = false,
+        )
+    }
+
+    @Test
+    fun wideNavigationRail_expanded_collapses() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            {
+                NavigationRailTestCase(
+                    isWideNavRail = true,
+                    expanded = true,
+                    changeSelectionToggleTestCase = false
+                )
+            },
+            assertOneRecomposition = false,
+        )
+    }
 }
 
-internal class NavigationRailTestCase : LayeredComposeTestCase(), ToggleableTestCase {
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+internal class NavigationRailTestCase(
+    private val isWideNavRail: Boolean = false,
+    private var expanded: Boolean = false,
+    private val changeSelectionToggleTestCase: Boolean = true,
+) : LayeredComposeTestCase(), ToggleableTestCase {
     private lateinit var selectedIndexState: MutableIntState
+    private lateinit var actualExpanded: MutableState<Boolean>
 
     @Composable
     override fun MeasuredContent() {
         selectedIndexState = remember { mutableIntStateOf(0) }
+        actualExpanded = remember { mutableStateOf(expanded) }
 
-        NavigationRail {
-            NavigationRailItem(
-                selected = selectedIndexState.value == 0,
-                onClick = {},
-                icon = { Spacer(Modifier.size(24.dp)) },
-            )
-            NavigationRailItem(
-                selected = selectedIndexState.value == 1,
-                onClick = {},
-                icon = { Spacer(Modifier.size(24.dp)) },
-            )
+        if (isWideNavRail) {
+            WideNavigationRail(expanded = actualExpanded.value) {
+                WideNavigationRailItem(
+                    selected = selectedIndexState.value == 0,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                    railExpanded = actualExpanded.value,
+                    label = { Spacer(Modifier.size(24.dp)) }
+                )
+                WideNavigationRailItem(
+                    selected = selectedIndexState.value == 1,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                    railExpanded = actualExpanded.value,
+                    label = { Spacer(Modifier.size(24.dp)) }
+                )
+            }
+        } else {
+            NavigationRail {
+                NavigationRailItem(
+                    selected = selectedIndexState.value == 0,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                )
+                NavigationRailItem(
+                    selected = selectedIndexState.value == 1,
+                    onClick = {},
+                    icon = { Spacer(Modifier.size(24.dp)) },
+                )
+            }
         }
     }
 
     @Composable
     override fun ContentWrappers(content: @Composable () -> Unit) {
-        MaterialTheme { content() }
+        if (isWideNavRail) {
+            MaterialExpressiveTheme { content() }
+        } else {
+            MaterialTheme { content() }
+        }
     }
 
     override fun toggleState() {
-        selectedIndexState.value = if (selectedIndexState.value == 0) 1 else 0
+        if (changeSelectionToggleTestCase) {
+            // Case where item selection changes.
+            selectedIndexState.value = if (selectedIndexState.value == 0) 1 else 0
+        } else {
+            // Case where rail expands if it's collapsed, or collapses if it's expanded.
+            actualExpanded.value = !actualExpanded.value
+        }
     }
 }
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
new file mode 100644
index 0000000..424bd9e
--- /dev/null
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.benchmark
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Edit
+import androidx.compose.material.icons.outlined.KeyboardArrowDown
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ElevatedSplitButton
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.FilledSplitButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedSplitButton
+import androidx.compose.material3.SplitButton
+import androidx.compose.material3.Text
+import androidx.compose.material3.TonalSplitButton
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.benchmark.benchmarkFirstDraw
+import androidx.compose.testutils.benchmark.benchmarkFirstLayout
+import androidx.compose.testutils.benchmark.benchmarkFirstMeasure
+import androidx.compose.testutils.benchmark.benchmarkToFirstPixel
+import androidx.compose.ui.Modifier
+import androidx.test.filters.LargeTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class SplitButtonBenchmark(private val type: SplitButtonType) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun parameters() = SplitButtonType.values()
+    }
+
+    @get:Rule val benchmarkRule = ComposeBenchmarkRule()
+
+    private val splitButtonTestCaseFactory = { SplitButtonTestCase(type) }
+
+    @Ignore
+    @Test
+    fun splitButton_first_compose() {
+        benchmarkRule.benchmarkFirstCompose(splitButtonTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun splitButton_measure() {
+        benchmarkRule.benchmarkFirstMeasure(splitButtonTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun splitButton_layout() {
+        benchmarkRule.benchmarkFirstLayout(splitButtonTestCaseFactory)
+    }
+
+    @Ignore
+    @Test
+    fun splitButton_draw() {
+        benchmarkRule.benchmarkFirstDraw(splitButtonTestCaseFactory)
+    }
+
+    @Test
+    fun splitButton_firstPixel() {
+        benchmarkRule.benchmarkToFirstPixel(splitButtonTestCaseFactory)
+    }
+}
+
+internal class SplitButtonTestCase(private val type: SplitButtonType) : LayeredComposeTestCase() {
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Composable
+    override fun MeasuredContent() {
+        when (type) {
+            SplitButtonType.SplitButton ->
+                SplitButton(
+                    leadingButton = {
+                        Button(onClick = { /* Do something! */ }) { Text("Button") }
+                    },
+                    trailingButton = {
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.Outlined.Edit,
+                                contentDescription = "Localized description",
+                            )
+                        }
+                    }
+                )
+            SplitButtonType.Filled ->
+                FilledSplitButton(
+                    onLeadingButtonClick = {},
+                    expanded = false,
+                    onTrailingButtonClick = { /* Do Nothing */ },
+                    leadingContent = { leadingContent() },
+                    trailingContent = { trailingContent() },
+                )
+            SplitButtonType.Tonal ->
+                TonalSplitButton(
+                    onLeadingButtonClick = {},
+                    expanded = false,
+                    onTrailingButtonClick = { /* Do Nothing */ },
+                    leadingContent = { leadingContent() },
+                    trailingContent = { trailingContent() },
+                )
+            SplitButtonType.Elevated ->
+                ElevatedSplitButton(
+                    onLeadingButtonClick = {},
+                    expanded = false,
+                    onTrailingButtonClick = { /* Do Nothing */ },
+                    leadingContent = { leadingContent() },
+                    trailingContent = { trailingContent() },
+                )
+            SplitButtonType.Outlined ->
+                OutlinedSplitButton(
+                    onLeadingButtonClick = {},
+                    expanded = false,
+                    onTrailingButtonClick = { /* Do Nothing */ },
+                    leadingContent = { leadingContent() },
+                    trailingContent = { trailingContent() },
+                )
+        }
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialTheme { content() }
+    }
+}
+
+@Composable
+private fun leadingContent() {
+    Icon(
+        Icons.Outlined.Edit,
+        contentDescription = "Localized description",
+    )
+    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+    Text("My Button")
+}
+
+@Composable
+private fun trailingContent() {
+    Icon(Icons.Outlined.KeyboardArrowDown, contentDescription = "Localized description")
+}
+
+enum class SplitButtonType {
+    SplitButton,
+    Filled,
+    Tonal,
+    Elevated,
+    Outlined,
+}
diff --git a/compose/material3/integration-tests/macrobenchmark-target/build.gradle b/compose/material3/integration-tests/macrobenchmark-target/build.gradle
index 087ceb2..c3ee0c6 100644
--- a/compose/material3/integration-tests/macrobenchmark-target/build.gradle
+++ b/compose/material3/integration-tests/macrobenchmark-target/build.gradle
@@ -14,9 +14,11 @@
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
         }
     }
+    compileSdkVersion 35
 }
 
 dependencies {
     implementation(project(":activity:activity-compose"))
     implementation(project(":compose:material3:material3"))
-}
\ No newline at end of file
+    implementation("androidx.compose.material:material-icons-core:1.6.8")
+}
diff --git a/compose/material3/material3-adaptive-navigation-suite/api/current.txt b/compose/material3/material3-adaptive-navigation-suite/api/current.txt
index 83d23c0..62a3e0d 100644
--- a/compose/material3/material3-adaptive-navigation-suite/api/current.txt
+++ b/compose/material3/material3-adaptive-navigation-suite/api/current.txt
@@ -59,10 +59,12 @@
   }
 
   public static final class NavigationSuiteType.Companion {
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public String getCustom();
     method public String getNavigationBar();
     method public String getNavigationDrawer();
     method public String getNavigationRail();
     method public String getNone();
+    property @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final String Custom;
     property public final String NavigationBar;
     property public final String NavigationDrawer;
     property public final String NavigationRail;
diff --git a/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt b/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
index 83d23c0..62a3e0d 100644
--- a/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
+++ b/compose/material3/material3-adaptive-navigation-suite/api/restricted_current.txt
@@ -59,10 +59,12 @@
   }
 
   public static final class NavigationSuiteType.Companion {
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public String getCustom();
     method public String getNavigationBar();
     method public String getNavigationDrawer();
     method public String getNavigationRail();
     method public String getNone();
+    property @SuppressCompatibility @androidx.compose.material3.adaptive.navigationsuite.ExperimentalMaterial3AdaptiveNavigationSuiteApi public final String Custom;
     property public final String NavigationBar;
     property public final String NavigationDrawer;
     property public final String NavigationRail;
diff --git a/compose/material3/material3-adaptive-navigation-suite/build.gradle b/compose/material3/material3-adaptive-navigation-suite/build.gradle
index 83a6e86..1c8f9ff 100644
--- a/compose/material3/material3-adaptive-navigation-suite/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/build.gradle
@@ -62,7 +62,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
             }
         }
@@ -96,6 +96,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.adaptive.navigationsuite"
 }
 
@@ -105,5 +106,6 @@
     inceptionYear = "2023"
     description = "Compose Material Design Adaptive Navigation Suite Library"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:material3:material3-adaptive-navigation-suite:material3-adaptive-navigation-suite-samples"))
 }
diff --git a/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle b/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle
index 135d478..d65e6a8 100644
--- a/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle
+++ b/compose/material3/material3-adaptive-navigation-suite/samples/build.gradle
@@ -38,6 +38,7 @@
 
     implementation("androidx.compose.foundation:foundation:1.6.0-rc01")
     implementation("androidx.compose.foundation:foundation-layout:1.6.0-rc01")
+    implementation("androidx.compose.material:material-icons-core:1.6.8")
     implementation(project(":compose:material3:adaptive:adaptive"))
     implementation(project(":compose:material3:material3"))
     implementation(project(":compose:material3:material3-adaptive-navigation-suite"))
@@ -57,5 +58,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.adaptive.navigationsuite.samples"
 }
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
index de80399..5e96cd5 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
@@ -381,6 +381,16 @@
         val NavigationDrawer = NavigationSuiteType(description = "NavigationDrawer")
 
         /**
+         * A navigation suite type that instructs the [NavigationSuite] to perform specialized
+         * custom behavior. Different `NavigationSuite` implementations will exhibit different
+         * behaviors when using this type.
+         */
+        @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+        @get:ExperimentalMaterial3AdaptiveNavigationSuiteApi
+        @ExperimentalMaterial3AdaptiveNavigationSuiteApi
+        val Custom = NavigationSuiteType(description = "Custom")
+
+        /**
          * A navigation suite type that instructs the [NavigationSuite] to not display any
          * navigation components on the screen.
          */
diff --git a/compose/material3/material3-common/build.gradle b/compose/material3/material3-common/build.gradle
index 286e981..cff69e2 100644
--- a/compose/material3/material3-common/build.gradle
+++ b/compose/material3/material3-common/build.gradle
@@ -35,6 +35,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -43,11 +44,11 @@
             dependencies {
                 implementation(libs.kotlinStdlib)
                 implementation(project(":compose:ui:ui-util"))
-                api("androidx.compose.foundation:foundation:1.6.0")
-                api("androidx.compose.foundation:foundation-layout:1.6.0")
+                api(project(":compose:foundation:foundation"))
+                api(project(":compose:foundation:foundation-layout"))
                 api(project(":compose:runtime:runtime"))
-                api("androidx.compose.ui:ui-graphics:1.6.0")
-                api("androidx.compose.ui:ui-text:1.6.0")
+                api(project(":compose:ui:ui-graphics"))
+                api(project(":compose:ui:ui-text"))
             }
         }
 
@@ -65,14 +66,20 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -80,6 +87,7 @@
             dependencies {
                 implementation(project(":compose:material3:material3"))
                 implementation(project(":compose:material3:material3-common"))
+                implementation("androidx.compose.material:material-icons-core:1.6.8")
                 implementation(project(":compose:test-utils"))
                 implementation(libs.testRules)
                 implementation(libs.junit)
@@ -90,6 +98,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.common"
 }
 
@@ -102,5 +111,6 @@
             "components that can be shared between different Material libraries or used by app" +
             " developers. It builds upon the Jetpack Compose libraries."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:material3:material3-common:material3-common-samples"))
 }
diff --git a/compose/material3/material3-common/samples/build.gradle b/compose/material3/material3-common/samples/build.gradle
index 057acd9..54f689b 100644
--- a/compose/material3/material3-common/samples/build.gradle
+++ b/compose/material3/material3-common/samples/build.gradle
@@ -53,5 +53,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.common.samples"
 }
diff --git a/compose/material3/material3-common/src/commonMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.kt b/compose/material3/material3-common/src/commonMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.kt
index 89e7dfb5..f2bcce7 100644
--- a/compose/material3/material3-common/src/commonMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.kt
+++ b/compose/material3/material3-common/src/commonMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.currentValueOf
 import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.coerceAtLeast
@@ -39,15 +40,20 @@
  * Reserves at least 48.dp in size to disambiguate touch interactions if the element would measure
  * smaller.
  *
- * https://m3.material.io/foundations/accessible-design/accessibility-basics
+ * https://m3.material.io/foundations/designing/structure#dab862b1-e042-4c40-b680-b484b9f077f6
  *
  * This uses the Material recommended minimum size of 48.dp x 48.dp, which may not the same as the
  * system enforced minimum size. The minimum clickable / touch target size (48.dp by default) is
- * controlled by the system via ViewConfiguration and automatically expanded at the touch input
+ * controlled by the system via [ViewConfiguration] and automatically expanded at the touch input
  * layer.
  *
  * This modifier is not needed for touch target expansion to happen. It only affects layout, to make
  * sure there is adequate space for touch target expansion.
+ *
+ * Because layout constraints are affected by modifier order, for this modifier to take effect, it
+ * must come before any size modifiers on the element that might limit its constraints.
+ *
+ * @see LocalMinimumInteractiveComponentSize
  */
 @Stable
 fun Modifier.minimumInteractiveComponentSize(): Modifier = this then MinimumInteractiveModifier
@@ -67,11 +73,13 @@
                 "interactions if the element would measure smaller"
     }
 
-    override fun hashCode(): Int = System.identityHashCode(this)
+    override fun hashCode(): Int = identifyHashCode(this)
 
     override fun equals(other: Any?) = (other === this)
 }
 
+internal expect inline fun identifyHashCode(value: Any): Int
+
 internal class MinimumInteractiveModifierNode :
     Modifier.Node(), CompositionLocalConsumerModifierNode, LayoutModifierNode {
     override fun MeasureScope.measure(
diff --git a/compose/material3/material3-common/src/commonStubsMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.commonStubs.kt b/compose/material3/material3-common/src/commonStubsMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.commonStubs.kt
new file mode 100644
index 0000000..0332b5c
--- /dev/null
+++ b/compose/material3/material3-common/src/commonStubsMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.commonStubs.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.common
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun identifyHashCode(value: Any): Int = implementedInJetBrainsFork()
diff --git a/compose/material3/material3-common/src/commonStubsMain/kotlin/androidx/compose/material3/common/NotImplemented.commonStubs.kt b/compose/material3/material3-common/src/commonStubsMain/kotlin/androidx/compose/material3/common/NotImplemented.commonStubs.kt
new file mode 100644
index 0000000..dfb855a
--- /dev/null
+++ b/compose/material3/material3-common/src/commonStubsMain/kotlin/androidx/compose/material3/common/NotImplemented.commonStubs.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.common
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun implementedInJetBrainsFork(): Nothing =
+    throw NotImplementedError(
+        """
+        Implemented only in JetBrains fork.
+        Please use `org.jetbrains.compose.material3:material3-common` package instead.
+        """
+            .trimIndent()
+    )
diff --git a/compose/material3/material3-common/src/jvmMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.jvm.kt b/compose/material3/material3-common/src/jvmMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.jvm.kt
new file mode 100644
index 0000000..280a27c
--- /dev/null
+++ b/compose/material3/material3-common/src/jvmMain/kotlin/androidx/compose/material3/common/InteractiveComponentSize.jvm.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.common
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun identifyHashCode(value: Any): Int = System.identityHashCode(value)
diff --git a/compose/material3/material3-window-size-class/build.gradle b/compose/material3/material3-window-size-class/build.gradle
index e415e38..92988f3 100644
--- a/compose/material3/material3-window-size-class/build.gradle
+++ b/compose/material3/material3-window-size-class/build.gradle
@@ -33,6 +33,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -40,10 +41,10 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlib)
-                implementation("androidx.compose.ui:ui-util:1.6.0")
-                api("androidx.compose.runtime:runtime:1.7.0-beta02")
-                api("androidx.compose.ui:ui:1.6.0")
-                api("androidx.compose.ui:ui-unit:1.6.0")
+                implementation(project(":compose:ui:ui-util"))
+                api(project(":compose:runtime:runtime"))
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-unit"))
             }
         }
 
@@ -66,10 +67,16 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -100,9 +107,11 @@
     inceptionYear = "2022"
     description = "Provides window size classes for building responsive UIs"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:material3:material3-window-size-class:material3-window-size-class-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.windowsizeclass"
 }
diff --git a/compose/material3/material3-window-size-class/lint-baseline.xml b/compose/material3/material3-window-size-class/lint-baseline.xml
index 4f32419..4911d23 100644
--- a/compose/material3/material3-window-size-class/lint-baseline.xml
+++ b/compose/material3/material3-window-size-class/lint-baseline.xml
@@ -21,7 +21,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method fromWidth$lint_module has parameter supportedSizeClasses with type Set&lt;WindowWidthSizeClass>: replace with IntSet"
+        message="method fromWidth has parameter supportedSizeClasses with type Set&lt;WindowWidthSizeClass>: replace with IntSet"
         errorLine1="            supportedSizeClasses: Set&lt;WindowWidthSizeClass>"
         errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -30,7 +30,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method fromHeight$lint_module has parameter supportedSizeClasses with type Set&lt;WindowHeightSizeClass>: replace with IntSet"
+        message="method fromHeight has parameter supportedSizeClasses with type Set&lt;WindowHeightSizeClass>: replace with IntSet"
         errorLine1="            supportedSizeClasses: Set&lt;WindowHeightSizeClass>"
         errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
diff --git a/compose/material3/material3-window-size-class/samples/build.gradle b/compose/material3/material3-window-size-class/samples/build.gradle
index 9315c48..1f9a9da 100644
--- a/compose/material3/material3-window-size-class/samples/build.gradle
+++ b/compose/material3/material3-window-size-class/samples/build.gradle
@@ -49,5 +49,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.windowsizeclass.samples"
 }
diff --git a/compose/material3/material3-window-size-class/src/commonStubsMain/kotlin/androidx/compose/material3/windowsizeclass/TestOnly.stubsCommon.kt b/compose/material3/material3-window-size-class/src/commonStubsMain/kotlin/androidx/compose/material3/windowsizeclass/TestOnly.stubsCommon.kt
new file mode 100644
index 0000000..a4e3587
--- /dev/null
+++ b/compose/material3/material3-window-size-class/src/commonStubsMain/kotlin/androidx/compose/material3/windowsizeclass/TestOnly.stubsCommon.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.windowsizeclass
+
+@MustBeDocumented actual annotation class TestOnly()
diff --git a/compose/material3/material3/api/api_lint.ignore b/compose/material3/material3/api/api_lint.ignore
index 3ec7be4..ffc33ab 100644
--- a/compose/material3/material3/api/api_lint.ignore
+++ b/compose/material3/material3/api/api_lint.ignore
@@ -3,14 +3,8 @@
     Must avoid boxed primitives (`java.lang.Long`)
 
 
-ExecutorRegistration: androidx.compose.material3.TextDefaults#Clickable(String, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.LinkInteractionListener):
-    Registration methods should have overload that accepts delivery Executor: `Clickable`
-ExecutorRegistration: androidx.compose.material3.TextDefaults#Url(String, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.LinkInteractionListener):
-    Registration methods should have overload that accepts delivery Executor: `Url`
-ExecutorRegistration: androidx.compose.material3.TextDefaults#fromHtml(String, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.SpanStyle, androidx.compose.ui.text.LinkInteractionListener):
-    Registration methods should have overload that accepts delivery Executor: `fromHtml`
-
-
+GetterSetterNames: androidx.compose.material3.ModalBottomSheetProperties#getShouldDismissOnBackPress():
+    Getter for boolean property `shouldDismissOnBackPress` is named `getShouldDismissOnBackPress` but should match the property name. Use `@get:JvmName` to rename.
 GetterSetterNames: androidx.compose.material3.SheetState#getHasExpandedState():
     Getter for boolean property `hasExpandedState` is named `getHasExpandedState` but should match the property name. Use `@get:JvmName` to rename.
 GetterSetterNames: androidx.compose.material3.SheetState#getHasPartiallyExpandedState():
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index deda2bc..6323753 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -34,6 +34,7 @@
   }
 
   public final class AppBarKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void BottomAppBar(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void BottomAppBar(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets);
@@ -89,9 +90,11 @@
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method public float getContainerElevation();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public androidx.compose.foundation.layout.Arrangement.Horizontal getHorizontalArrangement();
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
     property public final float ContainerElevation;
     property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final androidx.compose.foundation.layout.Arrangement.Horizontal HorizontalArrangement;
     property @androidx.compose.runtime.Composable public final long bottomAppBarFabColor;
     property @androidx.compose.runtime.Composable public final long containerColor;
     property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
@@ -459,6 +462,7 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
     method @Deprecated public static androidx.compose.material3.ColorScheme darkColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim);
     method public static androidx.compose.material3.ColorScheme darkColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim, optional long surfaceBright, optional long surfaceContainer, optional long surfaceContainerHigh, optional long surfaceContainerHighest, optional long surfaceContainerLow, optional long surfaceContainerLowest, optional long surfaceDim);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.ColorScheme expressiveLightColorScheme();
     method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalTonalElevationEnabled();
     method @Deprecated public static androidx.compose.material3.ColorScheme lightColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim);
     method public static androidx.compose.material3.ColorScheme lightColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim, optional long surfaceBright, optional long surfaceContainer, optional long surfaceContainerHigh, optional long surfaceContainerHighest, optional long surfaceContainerLow, optional long surfaceContainerLowest, optional long surfaceDim);
@@ -776,16 +780,26 @@
     method @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getExtendedFabShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeExtendedFabShape();
     method public float getLargeIconSize();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getMediumExtendedFabShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public float getMediumIconSize();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getMediumShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSmallExtendedFabShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSmallShape();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingActionButtonElevation loweredElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
     property public final float LargeIconSize;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final float MediumIconSize;
     property @androidx.compose.runtime.Composable public final long containerColor;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape extendedFabShape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeExtendedFabShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeShape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape mediumExtendedFabShape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape mediumShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallExtendedFabShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallShape;
     field public static final androidx.compose.material3.FloatingActionButtonDefaults INSTANCE;
   }
@@ -797,10 +811,32 @@
     method @androidx.compose.runtime.Composable public static void ExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void ExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void FloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LargeExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LargeExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void LargeFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SmallExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SmallExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void SmallFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class FloatingActionButtonMenuKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FloatingActionButtonMenu(boolean expanded, int itemsCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatingActionButtonMenuScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FloatingActionButtonMenuItem(androidx.compose.material3.FloatingActionButtonMenuScope, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, int itemIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ToggleableFloatingActionButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.graphics.Color> containerColor, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.unit.Dp> containerSize, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadius, kotlin.jvm.functions.Function1<? super androidx.compose.material3.ToggleableFloatingActionButtonScope,kotlin.Unit> content);
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public interface FloatingActionButtonMenuScope extends androidx.compose.foundation.layout.ColumnScope {
+    method public androidx.compose.ui.Alignment.Horizontal getHorizontalAlignment();
+    method public int getItemsCount();
+    method public float getStaggerProgress();
+    property public abstract androidx.compose.ui.Alignment.Horizontal horizontalAlignment;
+    property public abstract int itemsCount;
+    property public abstract float staggerProgress;
+  }
+
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class FloatingAppBarDefaults {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingAppBarScrollBehavior exitAlwaysScrollBehavior(int position, optional float screenOffset, optional androidx.compose.material3.FloatingAppBarState state, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> flingAnimationSpec);
     method @androidx.compose.runtime.Composable public long getContainerColor();
@@ -809,10 +845,10 @@
     method public float getContainerSize();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
     method public float getScreenOffset();
-    method public androidx.compose.animation.EnterTransition horizontalEnterTransition(androidx.compose.ui.Alignment.Horizontal expandFrom);
-    method public androidx.compose.animation.ExitTransition horizontalExitTransition(androidx.compose.ui.Alignment.Horizontal shrinkTowards);
-    method public androidx.compose.animation.EnterTransition verticalEnterTransition(androidx.compose.ui.Alignment.Vertical expandFrom);
-    method public androidx.compose.animation.ExitTransition verticalExitTransition(androidx.compose.ui.Alignment.Vertical shrinkTowards);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.EnterTransition horizontalEnterTransition(androidx.compose.ui.Alignment.Horizontal expandFrom);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ExitTransition horizontalExitTransition(androidx.compose.ui.Alignment.Horizontal shrinkTowards);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.EnterTransition verticalEnterTransition(androidx.compose.ui.Alignment.Vertical expandFrom);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ExitTransition verticalExitTransition(androidx.compose.ui.Alignment.Vertical shrinkTowards);
     property @androidx.compose.runtime.Composable public final long ContainerColor;
     property public final float ContainerElevation;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape ContainerShape;
@@ -824,8 +860,8 @@
 
   public final class FloatingAppBarKt {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.FloatingAppBarState FloatingAppBarState(float initialOffsetLimit, float initialOffset, float initialContentOffset);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void HorizontalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? trailingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void VerticalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? trailingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void HorizontalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? trailingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void VerticalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? trailingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.FloatingAppBarState rememberFloatingAppBarState(optional float initialOffsetLimit, optional float initialOffset, optional float initialContentOffset);
   }
 
@@ -994,6 +1030,7 @@
 
   @androidx.compose.runtime.Immutable public final class ListItemColors {
     ctor public ListItemColors(long containerColor, long headlineColor, long leadingIconColor, long overlineColor, long supportingTextColor, long trailingIconColor, long disabledHeadlineColor, long disabledLeadingIconColor, long disabledTrailingIconColor);
+    method public androidx.compose.material3.ListItemColors copy(optional long containerColor, optional long headlineColor, optional long leadingIconColor, optional long overlineColor, optional long supportingTextColor, optional long trailingIconColor, optional long disabledHeadlineColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
     method public long getContainerColor();
     method public long getDisabledHeadlineColor();
     method public long getDisabledLeadingIconColor();
@@ -1015,6 +1052,7 @@
   }
 
   public final class ListItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ListItemColors colors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ListItemColors colors(optional long containerColor, optional long headlineColor, optional long leadingIconColor, optional long overlineColor, optional long supportingColor, optional long trailingIconColor, optional long disabledHeadlineColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public long getContainerColor();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public long getContentColor();
@@ -1032,7 +1070,8 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class LoadingIndicatorDefaults {
-    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getContainedContainerColor();
+    method @androidx.compose.runtime.Composable public long getContainedIndicatorColor();
     method public float getContainerHeight();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getContainerShape();
     method public float getContainerWidth();
@@ -1040,7 +1079,8 @@
     method public java.util.List<androidx.graphics.shapes.RoundedPolygon> getIndeterminateIndicatorPolygons();
     method @androidx.compose.runtime.Composable public long getIndicatorColor();
     method public float getIndicatorSize();
-    property @androidx.compose.runtime.Composable public final long ContainerColor;
+    property @androidx.compose.runtime.Composable public final long ContainedContainerColor;
+    property @androidx.compose.runtime.Composable public final long ContainedIndicatorColor;
     property public final float ContainerHeight;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape ContainerShape;
     property public final float ContainerWidth;
@@ -1052,8 +1092,10 @@
   }
 
   public final class LoadingIndicatorKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> indicatorPolygons);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> indicatorPolygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ContainedLoadingIndicator(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ContainedLoadingIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public abstract sealed class MaterialShapes {
@@ -1140,16 +1182,19 @@
 
   public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.ColorScheme getColorScheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.MotionScheme getMotionScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.Shapes getShapes();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.Typography getTypography();
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.ColorScheme colorScheme;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.MotionScheme motionScheme;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.Shapes shapes;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.Typography typography;
     field public static final androidx.compose.material3.MaterialTheme INSTANCE;
   }
 
   public final class MaterialThemeKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MaterialExpressiveTheme(optional androidx.compose.material3.ColorScheme? colorScheme, optional androidx.compose.material3.Shapes? shapes, optional androidx.compose.material3.Typography? typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MaterialExpressiveTheme(optional androidx.compose.material3.ColorScheme? colorScheme, optional androidx.compose.material3.MotionScheme? motionScheme, optional androidx.compose.material3.Shapes? shapes, optional androidx.compose.material3.Typography? typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material3.ColorScheme colorScheme, optional androidx.compose.material3.MotionScheme motionScheme, optional androidx.compose.material3.Shapes shapes, optional androidx.compose.material3.Typography typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material3.ColorScheme colorScheme, optional androidx.compose.material3.Shapes shapes, optional androidx.compose.material3.Typography typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
@@ -1199,6 +1244,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class ModalBottomSheetProperties {
+    ctor public ModalBottomSheetProperties();
     ctor public ModalBottomSheetProperties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean shouldDismissOnBackPress);
     ctor @Deprecated public ModalBottomSheetProperties(androidx.compose.ui.window.SecureFlagPolicy securePolicy, boolean isFocusable, boolean shouldDismissOnBackPress);
     ctor public ModalBottomSheetProperties(optional boolean shouldDismissOnBackPress);
@@ -1212,6 +1258,26 @@
     method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Immutable public interface MotionScheme {
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> defaultEffectsSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> defaultSpatialSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> fastEffectsSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> fastSpatialSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowEffectsSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowSpatialSpec();
+  }
+
+  public final class MotionSchemeKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.MotionScheme expressiveMotionScheme();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultEffectsSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultSpatialSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastEffectsSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastSpatialSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowEffectsSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowSpatialSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.MotionScheme standardMotionScheme();
+  }
+
   public interface MultiChoiceSegmentedButtonRowScope extends androidx.compose.foundation.layout.RowScope {
   }
 
@@ -1326,6 +1392,28 @@
     property public final int Top;
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @kotlin.jvm.JvmInline public final value class NavigationRailArrangement {
+    field public static final androidx.compose.material3.NavigationRailArrangement.Companion Companion;
+  }
+
+  public static final class NavigationRailArrangement.Companion {
+    method public int getBottom();
+    method public int getCenter();
+    method public int getTop();
+    property public final int Bottom;
+    property public final int Center;
+    property public final int Top;
+  }
+
+  @androidx.compose.runtime.Immutable public final class NavigationRailColors {
+    ctor public NavigationRailColors(long containerColor, long contentColor);
+    method public androidx.compose.material3.NavigationRailColors copy(optional long containerColor, optional long contentColor);
+    method public long getContainerColor();
+    method public long getContentColor();
+    property public final long containerColor;
+    property public final long contentColor;
+  }
+
   public final class NavigationRailDefaults {
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
@@ -1365,6 +1453,10 @@
     method @androidx.compose.runtime.Composable public static void NavigationRailItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional androidx.compose.material3.NavigationRailItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
   }
 
+  public final class OpticalCenteringKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.ui.Modifier opticalCentering(androidx.compose.ui.Modifier, androidx.compose.foundation.shape.CornerBasedShape shape, androidx.compose.foundation.layout.PaddingValues basePadding);
+  }
+
   @androidx.compose.runtime.Immutable public final class OutlinedTextFieldDefaults {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Container(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
     method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void ContainerBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
@@ -1372,6 +1464,7 @@
     method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
     method public androidx.compose.foundation.layout.PaddingValues contentPadding(optional float start, optional float top, optional float end, optional float bottom);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
     method public float getFocusedBorderThickness();
     method public float getMinHeight();
     method public float getMinWidth();
@@ -1386,6 +1479,7 @@
   }
 
   public final class OutlinedTextFieldKt {
+    method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
   }
@@ -1425,7 +1519,8 @@
 
   public final class ProgressIndicatorKt {
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
-    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
+    method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap, optional float gapSize);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
@@ -1463,6 +1558,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class RangeSliderState {
+    ctor public RangeSliderState();
     ctor public RangeSliderState(optional float activeRangeStart, optional float activeRangeEnd, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
     method public float getActiveRangeEnd();
     method public float getActiveRangeStart();
@@ -1493,6 +1589,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class RippleConfiguration {
+    ctor public RippleConfiguration();
     ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
     method public long getColor();
     method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
@@ -1665,6 +1762,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     ctor @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge, optional androidx.compose.foundation.shape.CornerBasedShape largeIncreased, optional androidx.compose.foundation.shape.CornerBasedShape extraLargeIncreased, optional androidx.compose.foundation.shape.CornerBasedShape extraExtraLarge);
     method public androidx.compose.material3.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
@@ -1793,6 +1891,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class SliderPositions {
+    ctor @Deprecated public SliderPositions();
     ctor @Deprecated public SliderPositions(optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> initialActiveRange, optional float[] initialTickFractions);
     method @Deprecated public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getActiveRange();
     method @Deprecated public float[] getTickFractions();
@@ -1801,6 +1900,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class SliderState implements androidx.compose.foundation.gestures.DraggableState {
+    ctor public SliderState();
     ctor public SliderState(optional float value, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
     method public void dispatchRawDelta(float delta);
     method public suspend Object? drag(androidx.compose.foundation.MutatePriority dragPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.DragScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -2167,6 +2267,7 @@
     method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
     method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithLabel(optional float start, optional float end, optional float top, optional float bottom);
     method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithoutLabel(optional float start, optional float top, optional float end, optional float bottom);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
     method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
     method @Deprecated public float getFocusedBorderThickness();
     method public float getFocusedIndicatorThickness();
@@ -2193,10 +2294,34 @@
   }
 
   public final class TextFieldKt {
+    method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
   }
 
+  public abstract class TextFieldLabelPosition {
+    method public abstract boolean getAlwaysMinimize();
+    property public abstract boolean alwaysMinimize;
+  }
+
+  public static final class TextFieldLabelPosition.Above extends androidx.compose.material3.TextFieldLabelPosition {
+    method public boolean getAlwaysMinimize();
+    property public boolean alwaysMinimize;
+    field public static final androidx.compose.material3.TextFieldLabelPosition.Above INSTANCE;
+  }
+
+  public static final class TextFieldLabelPosition.Default extends androidx.compose.material3.TextFieldLabelPosition {
+    ctor public TextFieldLabelPosition.Default();
+    ctor public TextFieldLabelPosition.Default(optional boolean alwaysMinimize);
+    method public boolean getAlwaysMinimize();
+    property public boolean alwaysMinimize;
+  }
+
+  @androidx.compose.runtime.Stable public interface TextFieldLabelScope {
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
+    property @FloatRange(from=0.0, to=1.0) public abstract float progress;
+  }
+
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
@@ -2251,6 +2376,7 @@
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void TimeInput(androidx.compose.material3.TimePickerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.TimePickerColors colors);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void TimePicker(androidx.compose.material3.TimePickerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.TimePickerColors colors, optional int layoutType);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.TimePickerState TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
+    method public static boolean isPm(androidx.compose.material3.TimePickerState);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.TimePickerState rememberTimePickerState(optional int initialHour, optional int initialMinute, optional boolean is24Hour);
   }
 
@@ -2283,19 +2409,40 @@
     method @IntRange(from=0L, to=59L) public int getMinute();
     method public int getSelection();
     method public boolean is24hour();
-    method public boolean isAfternoon();
     method public void set24hour(boolean);
-    method public void setAfternoon(boolean);
     method public void setHour(@IntRange(from=0L, to=23L) int);
     method public void setMinute(@IntRange(from=0L, to=59L) int);
     method public void setSelection(int);
     property @IntRange(from=0L, to=23L) public abstract int hour;
     property public abstract boolean is24hour;
-    property public abstract boolean isAfternoon;
     property @IntRange(from=0L, to=59L) public abstract int minute;
     property public abstract int selection;
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class ToggleableFloatingActionButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.Modifier animateIcon(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<java.lang.Float> checkedProgress, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.graphics.Color> color, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.unit.Dp> size);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.graphics.Color> containerColor(optional long initialColor, optional long finalColor);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadius();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadius(float initialSize, optional float finalSize);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadiusLarge();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadiusMedium();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSize();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSize(float initialSize, optional float finalSize);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSizeLarge();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSizeMedium();
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.graphics.Color> iconColor(optional long initialColor, optional long finalColor);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSize();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSize(float initialSize, optional float finalSize);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSizeLarge();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSizeMedium();
+    field public static final androidx.compose.material3.ToggleableFloatingActionButtonDefaults INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public interface ToggleableFloatingActionButtonScope {
+    method public float getCheckedProgress();
+    property public abstract float checkedProgress;
+  }
+
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class TooltipDefaults {
     method public long getCaretSize();
     method @androidx.compose.runtime.Composable public long getPlainTooltipContainerColor();
@@ -2441,6 +2588,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography();
     ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.ui.text.TextStyle getBodyLarge();
@@ -2520,6 +2668,28 @@
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LinearWavyProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional androidx.compose.ui.graphics.drawscope.Stroke stroke, optional androidx.compose.ui.graphics.drawscope.Stroke trackStroke, optional float gapSize, optional float stopSize, optional kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> amplitude, optional float wavelength, optional float waveSpeed);
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class WideNavigationRailDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailColors colors();
+    method public int getArrangement();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getContainerShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final int arrangement;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape containerShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.WideNavigationRailDefaults INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class WideNavigationRailItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationItemColors colors();
+    method public int iconPositionFor(boolean railExpanded);
+    field public static final androidx.compose.material3.WideNavigationRailItemDefaults INSTANCE;
+  }
+
+  public final class WideNavigationRailKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void WideNavigationRail(optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.NavigationRailColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? header, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional int arrangement, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void WideNavigationRailItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional boolean railExpanded, optional int iconPosition, optional androidx.compose.material3.NavigationItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+  }
+
 }
 
 package androidx.compose.material3.carousel {
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index deda2bc..6323753 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -34,6 +34,7 @@
   }
 
   public final class AppBarKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void BottomAppBar(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void BottomAppBar(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets);
@@ -89,9 +90,11 @@
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method public float getContainerElevation();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public androidx.compose.foundation.layout.Arrangement.Horizontal getHorizontalArrangement();
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
     property public final float ContainerElevation;
     property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final androidx.compose.foundation.layout.Arrangement.Horizontal HorizontalArrangement;
     property @androidx.compose.runtime.Composable public final long bottomAppBarFabColor;
     property @androidx.compose.runtime.Composable public final long containerColor;
     property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
@@ -459,6 +462,7 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
     method @Deprecated public static androidx.compose.material3.ColorScheme darkColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim);
     method public static androidx.compose.material3.ColorScheme darkColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim, optional long surfaceBright, optional long surfaceContainer, optional long surfaceContainerHigh, optional long surfaceContainerHighest, optional long surfaceContainerLow, optional long surfaceContainerLowest, optional long surfaceDim);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.ColorScheme expressiveLightColorScheme();
     method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalTonalElevationEnabled();
     method @Deprecated public static androidx.compose.material3.ColorScheme lightColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim);
     method public static androidx.compose.material3.ColorScheme lightColorScheme(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline, optional long outlineVariant, optional long scrim, optional long surfaceBright, optional long surfaceContainer, optional long surfaceContainerHigh, optional long surfaceContainerHighest, optional long surfaceContainerLow, optional long surfaceContainerLowest, optional long surfaceDim);
@@ -776,16 +780,26 @@
     method @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getExtendedFabShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeExtendedFabShape();
     method public float getLargeIconSize();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getMediumExtendedFabShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public float getMediumIconSize();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getMediumShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSmallExtendedFabShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSmallShape();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingActionButtonElevation loweredElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
     property public final float LargeIconSize;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final float MediumIconSize;
     property @androidx.compose.runtime.Composable public final long containerColor;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape extendedFabShape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeExtendedFabShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeShape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape mediumExtendedFabShape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape mediumShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallExtendedFabShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallShape;
     field public static final androidx.compose.material3.FloatingActionButtonDefaults INSTANCE;
   }
@@ -797,10 +811,32 @@
     method @androidx.compose.runtime.Composable public static void ExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void ExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void FloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LargeExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LargeExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void LargeFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SmallExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SmallExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void SmallFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  public final class FloatingActionButtonMenuKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FloatingActionButtonMenu(boolean expanded, int itemsCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatingActionButtonMenuScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FloatingActionButtonMenuItem(androidx.compose.material3.FloatingActionButtonMenuScope, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, int itemIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ToggleableFloatingActionButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.graphics.Color> containerColor, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.unit.Dp> containerSize, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadius, kotlin.jvm.functions.Function1<? super androidx.compose.material3.ToggleableFloatingActionButtonScope,kotlin.Unit> content);
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public interface FloatingActionButtonMenuScope extends androidx.compose.foundation.layout.ColumnScope {
+    method public androidx.compose.ui.Alignment.Horizontal getHorizontalAlignment();
+    method public int getItemsCount();
+    method public float getStaggerProgress();
+    property public abstract androidx.compose.ui.Alignment.Horizontal horizontalAlignment;
+    property public abstract int itemsCount;
+    property public abstract float staggerProgress;
+  }
+
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class FloatingAppBarDefaults {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingAppBarScrollBehavior exitAlwaysScrollBehavior(int position, optional float screenOffset, optional androidx.compose.material3.FloatingAppBarState state, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> flingAnimationSpec);
     method @androidx.compose.runtime.Composable public long getContainerColor();
@@ -809,10 +845,10 @@
     method public float getContainerSize();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
     method public float getScreenOffset();
-    method public androidx.compose.animation.EnterTransition horizontalEnterTransition(androidx.compose.ui.Alignment.Horizontal expandFrom);
-    method public androidx.compose.animation.ExitTransition horizontalExitTransition(androidx.compose.ui.Alignment.Horizontal shrinkTowards);
-    method public androidx.compose.animation.EnterTransition verticalEnterTransition(androidx.compose.ui.Alignment.Vertical expandFrom);
-    method public androidx.compose.animation.ExitTransition verticalExitTransition(androidx.compose.ui.Alignment.Vertical shrinkTowards);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.EnterTransition horizontalEnterTransition(androidx.compose.ui.Alignment.Horizontal expandFrom);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ExitTransition horizontalExitTransition(androidx.compose.ui.Alignment.Horizontal shrinkTowards);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.EnterTransition verticalEnterTransition(androidx.compose.ui.Alignment.Vertical expandFrom);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ExitTransition verticalExitTransition(androidx.compose.ui.Alignment.Vertical shrinkTowards);
     property @androidx.compose.runtime.Composable public final long ContainerColor;
     property public final float ContainerElevation;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape ContainerShape;
@@ -824,8 +860,8 @@
 
   public final class FloatingAppBarKt {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.FloatingAppBarState FloatingAppBarState(float initialOffsetLimit, float initialOffset, float initialContentOffset);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void HorizontalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? trailingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void VerticalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? trailingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void HorizontalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? trailingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void VerticalFloatingAppBar(boolean expanded, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.material3.FloatingAppBarScrollBehavior? scrollBehavior, optional androidx.compose.ui.graphics.Shape shape, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? trailingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.FloatingAppBarState rememberFloatingAppBarState(optional float initialOffsetLimit, optional float initialOffset, optional float initialContentOffset);
   }
 
@@ -994,6 +1030,7 @@
 
   @androidx.compose.runtime.Immutable public final class ListItemColors {
     ctor public ListItemColors(long containerColor, long headlineColor, long leadingIconColor, long overlineColor, long supportingTextColor, long trailingIconColor, long disabledHeadlineColor, long disabledLeadingIconColor, long disabledTrailingIconColor);
+    method public androidx.compose.material3.ListItemColors copy(optional long containerColor, optional long headlineColor, optional long leadingIconColor, optional long overlineColor, optional long supportingTextColor, optional long trailingIconColor, optional long disabledHeadlineColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
     method public long getContainerColor();
     method public long getDisabledHeadlineColor();
     method public long getDisabledLeadingIconColor();
@@ -1015,6 +1052,7 @@
   }
 
   public final class ListItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ListItemColors colors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ListItemColors colors(optional long containerColor, optional long headlineColor, optional long leadingIconColor, optional long overlineColor, optional long supportingColor, optional long trailingIconColor, optional long disabledHeadlineColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public long getContainerColor();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public long getContentColor();
@@ -1032,7 +1070,8 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class LoadingIndicatorDefaults {
-    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getContainedContainerColor();
+    method @androidx.compose.runtime.Composable public long getContainedIndicatorColor();
     method public float getContainerHeight();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getContainerShape();
     method public float getContainerWidth();
@@ -1040,7 +1079,8 @@
     method public java.util.List<androidx.graphics.shapes.RoundedPolygon> getIndeterminateIndicatorPolygons();
     method @androidx.compose.runtime.Composable public long getIndicatorColor();
     method public float getIndicatorSize();
-    property @androidx.compose.runtime.Composable public final long ContainerColor;
+    property @androidx.compose.runtime.Composable public final long ContainedContainerColor;
+    property @androidx.compose.runtime.Composable public final long ContainedIndicatorColor;
     property public final float ContainerHeight;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape ContainerShape;
     property public final float ContainerWidth;
@@ -1052,8 +1092,10 @@
   }
 
   public final class LoadingIndicatorKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> indicatorPolygons);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> indicatorPolygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ContainedLoadingIndicator(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ContainedLoadingIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long indicatorColor, optional androidx.compose.ui.graphics.Shape containerShape, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LoadingIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional java.util.List<androidx.graphics.shapes.RoundedPolygon> polygons);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public abstract sealed class MaterialShapes {
@@ -1140,16 +1182,19 @@
 
   public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.ColorScheme getColorScheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.MotionScheme getMotionScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.Shapes getShapes();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.Typography getTypography();
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.ColorScheme colorScheme;
+    property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.MotionScheme motionScheme;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.Shapes shapes;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.material3.Typography typography;
     field public static final androidx.compose.material3.MaterialTheme INSTANCE;
   }
 
   public final class MaterialThemeKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MaterialExpressiveTheme(optional androidx.compose.material3.ColorScheme? colorScheme, optional androidx.compose.material3.Shapes? shapes, optional androidx.compose.material3.Typography? typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MaterialExpressiveTheme(optional androidx.compose.material3.ColorScheme? colorScheme, optional androidx.compose.material3.MotionScheme? motionScheme, optional androidx.compose.material3.Shapes? shapes, optional androidx.compose.material3.Typography? typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material3.ColorScheme colorScheme, optional androidx.compose.material3.MotionScheme motionScheme, optional androidx.compose.material3.Shapes shapes, optional androidx.compose.material3.Typography typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.compose.material3.ColorScheme colorScheme, optional androidx.compose.material3.Shapes shapes, optional androidx.compose.material3.Typography typography, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
@@ -1199,6 +1244,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class ModalBottomSheetProperties {
+    ctor public ModalBottomSheetProperties();
     ctor public ModalBottomSheetProperties(optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean shouldDismissOnBackPress);
     ctor @Deprecated public ModalBottomSheetProperties(androidx.compose.ui.window.SecureFlagPolicy securePolicy, boolean isFocusable, boolean shouldDismissOnBackPress);
     ctor public ModalBottomSheetProperties(optional boolean shouldDismissOnBackPress);
@@ -1212,6 +1258,26 @@
     method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ModalBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState, optional float sheetMaxWidth, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional float tonalElevation, optional long scrimColor, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dragHandle, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.ModalBottomSheetProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Immutable public interface MotionScheme {
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> defaultEffectsSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> defaultSpatialSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> fastEffectsSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> fastSpatialSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowEffectsSpec();
+    method public <T> androidx.compose.animation.core.FiniteAnimationSpec<T> slowSpatialSpec();
+  }
+
+  public final class MotionSchemeKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.MotionScheme expressiveMotionScheme();
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultEffectsSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberDefaultSpatialSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastEffectsSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberFastSpatialSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowEffectsSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static inline <reified T> androidx.compose.animation.core.FiniteAnimationSpec<T> rememberSlowSpatialSpec(androidx.compose.material3.MotionScheme);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.material3.MotionScheme standardMotionScheme();
+  }
+
   public interface MultiChoiceSegmentedButtonRowScope extends androidx.compose.foundation.layout.RowScope {
   }
 
@@ -1326,6 +1392,28 @@
     property public final int Top;
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @kotlin.jvm.JvmInline public final value class NavigationRailArrangement {
+    field public static final androidx.compose.material3.NavigationRailArrangement.Companion Companion;
+  }
+
+  public static final class NavigationRailArrangement.Companion {
+    method public int getBottom();
+    method public int getCenter();
+    method public int getTop();
+    property public final int Bottom;
+    property public final int Center;
+    property public final int Top;
+  }
+
+  @androidx.compose.runtime.Immutable public final class NavigationRailColors {
+    ctor public NavigationRailColors(long containerColor, long contentColor);
+    method public androidx.compose.material3.NavigationRailColors copy(optional long containerColor, optional long contentColor);
+    method public long getContainerColor();
+    method public long getContentColor();
+    property public final long containerColor;
+    property public final long contentColor;
+  }
+
   public final class NavigationRailDefaults {
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
@@ -1365,6 +1453,10 @@
     method @androidx.compose.runtime.Composable public static void NavigationRailItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional androidx.compose.material3.NavigationRailItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
   }
 
+  public final class OpticalCenteringKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public static androidx.compose.ui.Modifier opticalCentering(androidx.compose.ui.Modifier, androidx.compose.foundation.shape.CornerBasedShape shape, androidx.compose.foundation.layout.PaddingValues basePadding);
+  }
+
   @androidx.compose.runtime.Immutable public final class OutlinedTextFieldDefaults {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Container(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
     method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void ContainerBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
@@ -1372,6 +1464,7 @@
     method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
     method public androidx.compose.foundation.layout.PaddingValues contentPadding(optional float start, optional float top, optional float end, optional float bottom);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
     method public float getFocusedBorderThickness();
     method public float getMinHeight();
     method public float getMinWidth();
@@ -1386,6 +1479,7 @@
   }
 
   public final class OutlinedTextFieldKt {
+    method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
   }
@@ -1425,7 +1519,8 @@
 
   public final class ProgressIndicatorKt {
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
-    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
+    method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap, optional float gapSize);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long trackColor, optional int strokeCap);
@@ -1463,6 +1558,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class RangeSliderState {
+    ctor public RangeSliderState();
     ctor public RangeSliderState(optional float activeRangeStart, optional float activeRangeEnd, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
     method public float getActiveRangeEnd();
     method public float getActiveRangeStart();
@@ -1493,6 +1589,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class RippleConfiguration {
+    ctor public RippleConfiguration();
     ctor public RippleConfiguration(optional long color, optional androidx.compose.material.ripple.RippleAlpha? rippleAlpha);
     method public long getColor();
     method public androidx.compose.material.ripple.RippleAlpha? getRippleAlpha();
@@ -1665,6 +1762,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     ctor @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge, optional androidx.compose.foundation.shape.CornerBasedShape largeIncreased, optional androidx.compose.foundation.shape.CornerBasedShape extraLargeIncreased, optional androidx.compose.foundation.shape.CornerBasedShape extraExtraLarge);
     method public androidx.compose.material3.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
@@ -1793,6 +1891,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class SliderPositions {
+    ctor @Deprecated public SliderPositions();
     ctor @Deprecated public SliderPositions(optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> initialActiveRange, optional float[] initialTickFractions);
     method @Deprecated public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getActiveRange();
     method @Deprecated public float[] getTickFractions();
@@ -1801,6 +1900,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class SliderState implements androidx.compose.foundation.gestures.DraggableState {
+    ctor public SliderState();
     ctor public SliderState(optional float value, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
     method public void dispatchRawDelta(float delta);
     method public suspend Object? drag(androidx.compose.foundation.MutatePriority dragPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.DragScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -2167,6 +2267,7 @@
     method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
     method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithLabel(optional float start, optional float end, optional float top, optional float bottom);
     method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithoutLabel(optional float start, optional float top, optional float end, optional float bottom);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
     method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
     method @Deprecated public float getFocusedBorderThickness();
     method public float getFocusedIndicatorThickness();
@@ -2193,10 +2294,34 @@
   }
 
   public final class TextFieldKt {
+    method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.material3.TextFieldLabelPosition labelPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.TextFieldLabelScope,kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
     method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
     method @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
   }
 
+  public abstract class TextFieldLabelPosition {
+    method public abstract boolean getAlwaysMinimize();
+    property public abstract boolean alwaysMinimize;
+  }
+
+  public static final class TextFieldLabelPosition.Above extends androidx.compose.material3.TextFieldLabelPosition {
+    method public boolean getAlwaysMinimize();
+    property public boolean alwaysMinimize;
+    field public static final androidx.compose.material3.TextFieldLabelPosition.Above INSTANCE;
+  }
+
+  public static final class TextFieldLabelPosition.Default extends androidx.compose.material3.TextFieldLabelPosition {
+    ctor public TextFieldLabelPosition.Default();
+    ctor public TextFieldLabelPosition.Default(optional boolean alwaysMinimize);
+    method public boolean getAlwaysMinimize();
+    property public boolean alwaysMinimize;
+  }
+
+  @androidx.compose.runtime.Stable public interface TextFieldLabelScope {
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
+    property @FloatRange(from=0.0, to=1.0) public abstract float progress;
+  }
+
   public final class TextKt {
     method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
@@ -2251,6 +2376,7 @@
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void TimeInput(androidx.compose.material3.TimePickerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.TimePickerColors colors);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void TimePicker(androidx.compose.material3.TimePickerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.TimePickerColors colors, optional int layoutType);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.TimePickerState TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
+    method public static boolean isPm(androidx.compose.material3.TimePickerState);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.TimePickerState rememberTimePickerState(optional int initialHour, optional int initialMinute, optional boolean is24Hour);
   }
 
@@ -2283,19 +2409,40 @@
     method @IntRange(from=0L, to=59L) public int getMinute();
     method public int getSelection();
     method public boolean is24hour();
-    method public boolean isAfternoon();
     method public void set24hour(boolean);
-    method public void setAfternoon(boolean);
     method public void setHour(@IntRange(from=0L, to=23L) int);
     method public void setMinute(@IntRange(from=0L, to=59L) int);
     method public void setSelection(int);
     property @IntRange(from=0L, to=23L) public abstract int hour;
     property public abstract boolean is24hour;
-    property public abstract boolean isAfternoon;
     property @IntRange(from=0L, to=59L) public abstract int minute;
     property public abstract int selection;
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class ToggleableFloatingActionButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.Modifier animateIcon(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<java.lang.Float> checkedProgress, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.graphics.Color> color, optional kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.compose.ui.unit.Dp> size);
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.graphics.Color> containerColor(optional long initialColor, optional long finalColor);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadius();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadius(float initialSize, optional float finalSize);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadiusLarge();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerCornerRadiusMedium();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSize();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSize(float initialSize, optional float finalSize);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSizeLarge();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> containerSizeMedium();
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.graphics.Color> iconColor(optional long initialColor, optional long finalColor);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSize();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSize(float initialSize, optional float finalSize);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSizeLarge();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.compose.ui.unit.Dp> iconSizeMedium();
+    field public static final androidx.compose.material3.ToggleableFloatingActionButtonDefaults INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public interface ToggleableFloatingActionButtonScope {
+    method public float getCheckedProgress();
+    property public abstract float checkedProgress;
+  }
+
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class TooltipDefaults {
     method public long getCaretSize();
     method @androidx.compose.runtime.Composable public long getPlainTooltipContainerColor();
@@ -2441,6 +2588,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography();
     ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.ui.text.TextStyle getBodyLarge();
@@ -2520,6 +2668,28 @@
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LinearWavyProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional androidx.compose.ui.graphics.drawscope.Stroke stroke, optional androidx.compose.ui.graphics.drawscope.Stroke trackStroke, optional float gapSize, optional float stopSize, optional kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> amplitude, optional float wavelength, optional float waveSpeed);
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class WideNavigationRailDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailColors colors();
+    method public int getArrangement();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getContainerShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final int arrangement;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape containerShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.WideNavigationRailDefaults INSTANCE;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class WideNavigationRailItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationItemColors colors();
+    method public int iconPositionFor(boolean railExpanded);
+    field public static final androidx.compose.material3.WideNavigationRailItemDefaults INSTANCE;
+  }
+
+  public final class WideNavigationRailKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void WideNavigationRail(optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.NavigationRailColors colors, optional kotlin.jvm.functions.Function0<kotlin.Unit>? header, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional int arrangement, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void WideNavigationRailItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional boolean railExpanded, optional int iconPosition, optional androidx.compose.material3.NavigationItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+  }
+
 }
 
 package androidx.compose.material3.carousel {
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index 4b4ba3e..d809956 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -33,6 +33,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -41,17 +42,16 @@
             dependencies {
                 implementation(libs.kotlinStdlib)
                 // Keep pinned unless there is a need for tip of tree behavior
-                implementation("androidx.collection:collection:1.4.0")
-                implementation("androidx.compose.animation:animation-core:1.6.0")
-                implementation("androidx.compose.ui:ui-util:1.6.0")
-                api("androidx.compose.foundation:foundation:1.7.0-beta02")
-                api("androidx.compose.foundation:foundation-layout:1.7.0-beta02")
-                api("androidx.compose.material:material-icons-core:1.6.0")
-                api("androidx.compose.material:material-ripple:1.7.0-beta02")
-                api("androidx.compose.runtime:runtime:1.7.0-beta02")
-                api("androidx.compose.ui:ui:1.6.0")
-                api("androidx.compose.ui:ui-text:1.6.0")
-                api("androidx.graphics:graphics-shapes:1.0.0-beta01")
+                implementation("androidx.collection:collection:1.4.1")
+                implementation(project(":compose:animation:animation-core"))
+                implementation(project(":compose:ui:ui-util"))
+                api(project(":compose:foundation:foundation"))
+                api(project(":compose:foundation:foundation-layout"))
+                api(project(":compose:material:material-ripple"))
+                api(project(":compose:runtime:runtime"))
+                api(project(":compose:ui:ui"))
+                api(project(":compose:ui:ui-text"))
+                api(project(":graphics:graphics-shapes"))
             }
         }
 
@@ -69,18 +69,23 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
                 implementation("androidx.activity:activity-compose:1.8.2")
-
                 implementation("androidx.lifecycle:lifecycle-common-java8:2.6.1")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidUnitTest {
@@ -99,6 +104,8 @@
                 implementation(project(":compose:material3:material3:material3-samples"))
                 implementation(project(":compose:test-utils"))
                 implementation(project(':compose:foundation:foundation-layout'))
+                implementation(project(':compose:foundation:foundation'))
+                implementation("androidx.compose.material:material-icons-core:1.6.8")
                 implementation(project(":test:screenshot:screenshot"))
                 implementation(projectOrArtifact(":core:core"))
                 implementation(libs.testRules)
@@ -124,12 +131,16 @@
     inceptionYear = "2021"
     description = "Compose Material You Design Components library"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:material3:material3:material3-samples"))
 }
 
 // Screenshot tests related setup
 android {
+    compileSdk 35
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/compose/material3/material3"
     namespace "androidx.compose.material3"
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/material3/material3/integration-tests/material3-catalog/build.gradle b/compose/material3/material3/integration-tests/material3-catalog/build.gradle
index 7fb4a83..23f8592 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/build.gradle
+++ b/compose/material3/material3/integration-tests/material3-catalog/build.gradle
@@ -55,5 +55,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.catalog.library"
 }
diff --git a/compose/material3/material3/integration-tests/material3-catalog/lint-baseline.xml b/compose/material3/material3/integration-tests/material3-catalog/lint-baseline.xml
index 2693060..40985cd 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/lint-baseline.xml
+++ b/compose/material3/material3/integration-tests/material3-catalog/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
         message="constructor Theme has parameter map with type Map&lt;String, Float>: replace with ObjectFloatMap"
-        errorLine1="    constructor(map: Map&lt;String, Float>) : this("
-        errorLine2="                     ~~~~~~~~~~~~~~~~~~">
+        errorLine1="        map: Map&lt;String, Float>"
+        errorLine2="             ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt"/>
     </issue>
@@ -13,7 +13,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type Map&lt;String, Float> of toMap: replace with ObjectFloatMap"
-        errorLine1="    fun toMap() = mapOf("
+        errorLine1="    fun toMap() ="
         errorLine2="        ~~~~~">
         <location
             file="src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt"/>
@@ -22,7 +22,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable themeMap with type Map&lt;String, ? extends Float>: replace with ObjectFloatMap"
-        errorLine1="        val themeMap = themeString.substring(1, themeString.length - 1)"
+        errorLine1="        val themeMap ="
         errorLine2="        ^">
         <location
             file="src/main/java/androidx/compose/material3/catalog/library/data/UserPreferencesRepository.kt"/>
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
index 37555f9..d77126b 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
@@ -225,6 +225,18 @@
         examples = FloatingActionButtonsExamples,
     )
 
+private val FloatingActionButtonMenu =
+    Component(
+        id = nextId(),
+        name = "FAB Menu",
+        description = "The FAB Menu displays additional key actions on click of a FAB.",
+        // No FAB Menu icon
+        guidelinesUrl = "$ComponentGuidelinesUrl/fab-menu",
+        docsUrl = "$PackageSummaryUrl#floatingactionbuttonmenu",
+        sourceUrl = "$Material3SourceUrl/FloatingActionButtonMenu.kt",
+        examples = FloatingActionButtonMenuExamples,
+    )
+
 private val FloatingAppBars =
     Component(
         id = nextId(),
@@ -541,6 +553,7 @@
         Dialogs,
         ExtendedFloatingActionButton,
         FloatingActionButtons,
+        FloatingActionButtonMenu,
         FloatingAppBars,
         IconButtons,
         Lists,
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index 3d68f05..dcd328f 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -47,11 +47,14 @@
 import androidx.compose.material3.samples.ClickableCardSample
 import androidx.compose.material3.samples.ClickableElevatedCardSample
 import androidx.compose.material3.samples.ClickableOutlinedCardSample
+import androidx.compose.material3.samples.ContainedLoadingIndicatorSample
 import androidx.compose.material3.samples.DateInputSample
 import androidx.compose.material3.samples.DatePickerDialogSample
 import androidx.compose.material3.samples.DatePickerSample
 import androidx.compose.material3.samples.DatePickerWithDateSelectableDatesSample
 import androidx.compose.material3.samples.DateRangePickerSample
+import androidx.compose.material3.samples.DenseTextFieldContentPadding
+import androidx.compose.material3.samples.DeterminateContainedLoadingIndicatorSample
 import androidx.compose.material3.samples.DeterminateLoadingIndicatorSample
 import androidx.compose.material3.samples.DismissibleNavigationDrawerSample
 import androidx.compose.material3.samples.DockedSearchBarSample
@@ -64,6 +67,10 @@
 import androidx.compose.material3.samples.ElevatedSuggestionChipSample
 import androidx.compose.material3.samples.EnterAlwaysTopAppBar
 import androidx.compose.material3.samples.ExitAlwaysBottomAppBar
+import androidx.compose.material3.samples.ExitAlwaysBottomAppBarFixed
+import androidx.compose.material3.samples.ExitAlwaysBottomAppBarSpacedAround
+import androidx.compose.material3.samples.ExitAlwaysBottomAppBarSpacedBetween
+import androidx.compose.material3.samples.ExitAlwaysBottomAppBarSpacedEvenly
 import androidx.compose.material3.samples.ExitUntilCollapsedCenterAlignedLargeTopAppBarWithSubtitle
 import androidx.compose.material3.samples.ExitUntilCollapsedCenterAlignedMediumTopAppBarWithSubtitle
 import androidx.compose.material3.samples.ExitUntilCollapsedLargeTopAppBar
@@ -83,6 +90,7 @@
 import androidx.compose.material3.samples.FilledTonalIconToggleButtonSample
 import androidx.compose.material3.samples.FilterChipSample
 import androidx.compose.material3.samples.FilterChipWithLeadingIconSample
+import androidx.compose.material3.samples.FloatingActionButtonMenuSample
 import androidx.compose.material3.samples.FloatingActionButtonSample
 import androidx.compose.material3.samples.HorizontalFloatingAppBar
 import androidx.compose.material3.samples.HorizontalMultiBrowseCarouselSample
@@ -95,16 +103,24 @@
 import androidx.compose.material3.samples.IndeterminateLinearWavyProgressIndicatorSample
 import androidx.compose.material3.samples.InputChipSample
 import androidx.compose.material3.samples.InputChipWithAvatarSample
+import androidx.compose.material3.samples.LargeAnimatedExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.LargeExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.LargeExtendedFloatingActionButtonTextSample
 import androidx.compose.material3.samples.LargeFloatingActionButtonSample
 import androidx.compose.material3.samples.LeadingIconTabs
 import androidx.compose.material3.samples.LinearProgressIndicatorSample
 import androidx.compose.material3.samples.LinearWavyProgressIndicatorSample
 import androidx.compose.material3.samples.LoadingIndicatorPullToRefreshSample
 import androidx.compose.material3.samples.LoadingIndicatorSample
+import androidx.compose.material3.samples.MediumAnimatedExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.MediumExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.MediumExtendedFloatingActionButtonTextSample
+import androidx.compose.material3.samples.MediumFloatingActionButtonSample
 import androidx.compose.material3.samples.MenuSample
 import androidx.compose.material3.samples.MenuWithScrollStateSample
 import androidx.compose.material3.samples.ModalBottomSheetSample
 import androidx.compose.material3.samples.ModalNavigationDrawerSample
+import androidx.compose.material3.samples.MultiAutocompleteExposedDropdownMenuSample
 import androidx.compose.material3.samples.NavigationBarItemWithBadge
 import androidx.compose.material3.samples.NavigationBarSample
 import androidx.compose.material3.samples.NavigationBarWithOnlySelectedLabelsSample
@@ -117,7 +133,7 @@
 import androidx.compose.material3.samples.OutlinedIconButtonSample
 import androidx.compose.material3.samples.OutlinedIconToggleButtonSample
 import androidx.compose.material3.samples.OutlinedSplitButtonSample
-import androidx.compose.material3.samples.OutlinedTextFieldSample
+import androidx.compose.material3.samples.OutlinedTextFieldWithInitialValueAndSelection
 import androidx.compose.material3.samples.PasswordTextField
 import androidx.compose.material3.samples.PermanentNavigationDrawerSample
 import androidx.compose.material3.samples.PinnedTopAppBar
@@ -167,6 +183,9 @@
 import androidx.compose.material3.samples.SliderSample
 import androidx.compose.material3.samples.SliderWithCustomThumbSample
 import androidx.compose.material3.samples.SliderWithCustomTrackAndThumb
+import androidx.compose.material3.samples.SmallAnimatedExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.SmallExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.SmallExtendedFloatingActionButtonTextSample
 import androidx.compose.material3.samples.SmallFloatingActionButtonSample
 import androidx.compose.material3.samples.SplitButtonSample
 import androidx.compose.material3.samples.SplitButtonWithIconSample
@@ -179,13 +198,14 @@
 import androidx.compose.material3.samples.TextAndIconTabs
 import androidx.compose.material3.samples.TextArea
 import androidx.compose.material3.samples.TextButtonSample
-import androidx.compose.material3.samples.TextFieldSample
 import androidx.compose.material3.samples.TextFieldWithErrorState
 import androidx.compose.material3.samples.TextFieldWithHideKeyboardOnImeAction
 import androidx.compose.material3.samples.TextFieldWithIcons
+import androidx.compose.material3.samples.TextFieldWithInitialValueAndSelection
 import androidx.compose.material3.samples.TextFieldWithPlaceholder
 import androidx.compose.material3.samples.TextFieldWithPrefixAndSuffix
 import androidx.compose.material3.samples.TextFieldWithSupportingText
+import androidx.compose.material3.samples.TextFieldWithTransformations
 import androidx.compose.material3.samples.ThreeLineListItemWithExtendedSupporting
 import androidx.compose.material3.samples.ThreeLineListItemWithOverlineAndSupporting
 import androidx.compose.material3.samples.TimeInputSample
@@ -196,6 +216,10 @@
 import androidx.compose.material3.samples.TriStateCheckboxSample
 import androidx.compose.material3.samples.TwoLineListItem
 import androidx.compose.material3.samples.VerticalFloatingAppBar
+import androidx.compose.material3.samples.WideNavigationRailArrangementsSample
+import androidx.compose.material3.samples.WideNavigationRailCollapsedSample
+import androidx.compose.material3.samples.WideNavigationRailExpandedSample
+import androidx.compose.material3.samples.WideNavigationRailResponsiveSample
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
@@ -599,6 +623,34 @@
             sourceUrl = BottomAppBarsExampleSourceUrl,
         ) {
             ExitAlwaysBottomAppBar()
+        },
+        Example(
+            name = "ExitAlwaysBottomAppBarSpacedAround",
+            description = BottomAppBarsExampleDescription,
+            sourceUrl = BottomAppBarsExampleSourceUrl,
+        ) {
+            ExitAlwaysBottomAppBarSpacedAround()
+        },
+        Example(
+            name = "ExitAlwaysBottomAppBarSpacedBetween",
+            description = BottomAppBarsExampleDescription,
+            sourceUrl = BottomAppBarsExampleSourceUrl,
+        ) {
+            ExitAlwaysBottomAppBarSpacedBetween()
+        },
+        Example(
+            name = "ExitAlwaysBottomAppBarSpacedEvenly",
+            description = BottomAppBarsExampleDescription,
+            sourceUrl = BottomAppBarsExampleSourceUrl,
+        ) {
+            ExitAlwaysBottomAppBarSpacedEvenly()
+        },
+        Example(
+            name = "ExitAlwaysBottomAppBarFixed",
+            description = BottomAppBarsExampleDescription,
+            sourceUrl = BottomAppBarsExampleSourceUrl,
+        ) {
+            ExitAlwaysBottomAppBarFixed()
         }
     )
 
@@ -711,6 +763,27 @@
             ExtendedFloatingActionButtonSample()
         },
         Example(
+            name = "SmallExtendedFloatingActionButtonSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            SmallExtendedFloatingActionButtonSample()
+        },
+        Example(
+            name = "MediumExtendedFloatingActionButtonSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            MediumExtendedFloatingActionButtonSample()
+        },
+        Example(
+            name = "LargeExtendedFloatingActionButtonSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            LargeExtendedFloatingActionButtonSample()
+        },
+        Example(
             name = "ExtendedFloatingActionButtonTextSample",
             description = ExtendedFABExampleDescription,
             sourceUrl = ExtendedFABExampleSourceUrl,
@@ -718,12 +791,54 @@
             ExtendedFloatingActionButtonTextSample()
         },
         Example(
+            name = "SmallExtendedFloatingActionButtonTextSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            SmallExtendedFloatingActionButtonTextSample()
+        },
+        Example(
+            name = "MediumExtendedFloatingActionButtonTextSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            MediumExtendedFloatingActionButtonTextSample()
+        },
+        Example(
+            name = "LargeExtendedFloatingActionButtonTextSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            LargeExtendedFloatingActionButtonTextSample()
+        },
+        Example(
             name = "AnimatedExtendedFloatingActionButtonSample",
             description = ExtendedFABExampleDescription,
             sourceUrl = ExtendedFABExampleSourceUrl,
         ) {
             AnimatedExtendedFloatingActionButtonSample()
         },
+        Example(
+            name = "SmallAnimatedExtendedFloatingActionButtonSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            SmallAnimatedExtendedFloatingActionButtonSample()
+        },
+        Example(
+            name = "MediumAnimatedExtendedFloatingActionButtonSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            MediumAnimatedExtendedFloatingActionButtonSample()
+        },
+        Example(
+            name = "LargeAnimatedExtendedFloatingActionButtonSample",
+            description = ExtendedFABExampleDescription,
+            sourceUrl = ExtendedFABExampleSourceUrl,
+        ) {
+            LargeAnimatedExtendedFloatingActionButtonSample()
+        },
     )
 
 private const val FloatingActionButtonsExampleDescription = "Floating action button examples"
@@ -746,6 +861,13 @@
             LargeFloatingActionButtonSample()
         },
         Example(
+            name = "MediumFloatingActionButtonSample",
+            description = FloatingActionButtonsExampleDescription,
+            sourceUrl = FloatingActionButtonsExampleSourceUrl,
+        ) {
+            MediumFloatingActionButtonSample()
+        },
+        Example(
             name = "SmallFloatingActionButtonSample",
             description = FloatingActionButtonsExampleDescription,
             sourceUrl = FloatingActionButtonsExampleSourceUrl,
@@ -754,6 +876,20 @@
         }
     )
 
+private const val FloatingActionButtonMenuExampleDescription = "FAB Menu examples"
+private const val FloatingActionButtonMenuExampleSourceUrl =
+    "$SampleSourceUrl/FloatingActionButtonMenuSamples.kt"
+val FloatingActionButtonMenuExamples =
+    listOf(
+        Example(
+            name = "FloatingActionButtonMenuSample",
+            description = FloatingActionButtonMenuExampleDescription,
+            sourceUrl = FloatingActionButtonMenuExampleSourceUrl,
+        ) {
+            FloatingActionButtonMenuSample()
+        },
+    )
+
 private const val ListsExampleDescription = "List examples"
 private const val ListsExampleSourceUrl = "$SampleSourceUrl/ListSamples.kt"
 val ListsExamples =
@@ -870,6 +1006,13 @@
             LoadingIndicatorSample()
         },
         Example(
+            name = "ContainedLoadingIndicatorSample",
+            description = LoadingIndicatorsExampleDescription,
+            sourceUrl = LoadingIndicatorsExampleSourceUrl
+        ) {
+            ContainedLoadingIndicatorSample()
+        },
+        Example(
             name = "DeterminateLoadingIndicatorSample",
             description = LoadingIndicatorsExampleDescription,
             sourceUrl = LoadingIndicatorsExampleSourceUrl
@@ -877,6 +1020,13 @@
             DeterminateLoadingIndicatorSample()
         },
         Example(
+            name = "DeterminateContainedLoadingIndicatorSample",
+            description = LoadingIndicatorsExampleDescription,
+            sourceUrl = LoadingIndicatorsExampleSourceUrl
+        ) {
+            DeterminateContainedLoadingIndicatorSample()
+        },
+        Example(
             name = "LoadingIndicatorPullToRefreshSample",
             description = LoadingIndicatorsExampleDescription,
             sourceUrl = LoadingIndicatorsExampleSourceUrl
@@ -917,6 +1067,13 @@
         ) {
             EditableExposedDropdownMenuSample()
         },
+        Example(
+            name = "MultiAutocompleteExposedDropdownMenuSample",
+            description = MenusExampleDescription,
+            sourceUrl = MenusExampleSourceUrl
+        ) {
+            MultiAutocompleteExposedDropdownMenuSample()
+        },
     )
 
 private const val NavigationBarExampleDescription = "Navigation bar examples"
@@ -958,6 +1115,34 @@
 val NavigationRailExamples =
     listOf(
         Example(
+            name = "WideNavigationRailResponsiveSample",
+            description = NavigationRailExampleDescription,
+            sourceUrl = NavigationRailExampleSourceUrl,
+        ) {
+            WideNavigationRailResponsiveSample()
+        },
+        Example(
+            name = "WideNavigationRailCollapsedSample",
+            description = NavigationRailExampleDescription,
+            sourceUrl = NavigationRailExampleSourceUrl,
+        ) {
+            WideNavigationRailCollapsedSample()
+        },
+        Example(
+            name = "WideNavigationRailExpandedSample",
+            description = NavigationRailExampleDescription,
+            sourceUrl = NavigationRailExampleSourceUrl,
+        ) {
+            WideNavigationRailExpandedSample()
+        },
+        Example(
+            name = "WideNavigationRailArrangementsSample",
+            description = NavigationRailExampleDescription,
+            sourceUrl = NavigationRailExampleSourceUrl,
+        ) {
+            WideNavigationRailArrangementsSample()
+        },
+        Example(
             name = "NavigationRailSample",
             description = NavigationRailExampleDescription,
             sourceUrl = NavigationRailExampleSourceUrl,
@@ -1506,11 +1691,11 @@
                 SimpleTextFieldSample()
             },
             Example(
-                name = "TextFieldSample",
+                name = "TextFieldWithInitialValueAndSelection",
                 description = TextFieldsExampleDescription,
                 sourceUrl = TextFieldsExampleSourceUrl
             ) {
-                TextFieldSample()
+                TextFieldWithInitialValueAndSelection()
             },
             Example(
                 name = "SimpleOutlinedTextFieldSample",
@@ -1520,11 +1705,18 @@
                 SimpleOutlinedTextFieldSample()
             },
             Example(
-                name = "OutlinedTextFieldSample",
+                name = "OutlinedTextFieldWithInitialValueAndSelection",
                 description = TextFieldsExampleDescription,
                 sourceUrl = TextFieldsExampleSourceUrl
             ) {
-                OutlinedTextFieldSample()
+                OutlinedTextFieldWithInitialValueAndSelection()
+            },
+            Example(
+                name = "TextFieldWithTransformations",
+                description = TextFieldsExampleDescription,
+                sourceUrl = TextFieldsExampleSourceUrl
+            ) {
+                TextFieldWithTransformations()
             },
             Example(
                 name = "TextFieldWithIcons",
@@ -1562,6 +1754,13 @@
                 TextFieldWithSupportingText()
             },
             Example(
+                name = "DenseTextFieldContentPadding",
+                description = TextFieldsExampleDescription,
+                sourceUrl = TextFieldsExampleSourceUrl
+            ) {
+                DenseTextFieldContentPadding()
+            },
+            Example(
                 name = "PasswordTextField",
                 description = TextFieldsExampleDescription,
                 sourceUrl = TextFieldsExampleSourceUrl
@@ -1585,10 +1784,9 @@
         )
         .map {
             // By default text field samples are minimal and don't have a `width` modifier to
-            // restrict the
-            // width. As a result, they grow horizontally if enough text is typed. To prevent this
-            // behavior
-            // in Catalog app the code below restricts the width of every text field sample
+            // restrict the width. As a result, they grow horizontally if enough text is typed. To
+            // prevent this behavior in Catalog app, the code below restricts the width of every
+            // text field sample
             it.copy(content = { Box(Modifier.wrapContentWidth().width(280.dp)) { it.content() } })
         }
 
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt
index d497651..4d2970d 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt
@@ -16,9 +16,13 @@
 
 package androidx.compose.material3.catalog.library.model
 
+import androidx.compose.material3.MaterialExpressiveTheme
+import androidx.compose.material3.MaterialTheme
+
 data class Theme(
-    val themeMode: ThemeMode = ThemeMode.System,
+    val themeColorMode: ThemeColorMode = ThemeColorMode.System,
     val colorMode: ColorMode = ColorMode.Baseline,
+    val expressiveThemeMode: ExpressiveThemeMode = ExpressiveThemeMode.NonExpressive,
     val fontScale: Float = 1.0f,
     val fontScaleMode: FontScaleMode = FontScaleMode.System,
     val textDirection: TextDirection = TextDirection.System,
@@ -26,8 +30,10 @@
     constructor(
         map: Map<String, Float>
     ) : this(
-        themeMode = ThemeMode.values()[map.getValue(ThemeModeKey).toInt()],
+        themeColorMode = ThemeColorMode.values()[map.getValue(ThemeModeKey).toInt()],
         colorMode = ColorMode.values()[map.getValue(ColorModeKey).toInt()],
+        expressiveThemeMode =
+            ExpressiveThemeMode.values()[map.getValue(ExpressiveThemeModeKey).toInt()],
         fontScale = map.getValue(FontScaleKey).toFloat(),
         fontScaleMode = FontScaleMode.values()[map.getValue(FontScaleModeKey).toInt()],
         textDirection = TextDirection.values()[map.getValue(TextDirectionKey).toInt()],
@@ -35,8 +41,9 @@
 
     fun toMap() =
         mapOf(
-            ThemeModeKey to themeMode.ordinal.toFloat(),
+            ThemeModeKey to themeColorMode.ordinal.toFloat(),
             ColorModeKey to colorMode.ordinal.toFloat(),
+            ExpressiveThemeModeKey to expressiveThemeMode.ordinal.toFloat(),
             FontScaleKey to fontScale,
             FontScaleModeKey to fontScaleMode.ordinal.toFloat(),
             TextDirectionKey to textDirection.ordinal.toFloat(),
@@ -95,17 +102,32 @@
     override fun toString(): String = label
 }
 
-enum class ThemeMode {
+/**
+ * Determines whether the current [ColorMode] should be in light theme, dark theme, or determined by
+ * the system.
+ */
+enum class ThemeColorMode {
     System,
     Light,
     Dark,
 }
 
+/**
+ * A class for identifying legacy and expressive Material3 themes.
+ *
+ * See [MaterialTheme] and [MaterialExpressiveTheme] for more information.
+ */
+enum class ExpressiveThemeMode {
+    Expressive,
+    NonExpressive,
+}
+
 const val MinFontScale = 0.4f
 const val MaxFontScale = 2f
 
 private const val ThemeModeKey = "themeMode"
 private const val ColorModeKey = "colorMode"
+private const val ExpressiveThemeModeKey = "expressiveThemeMode"
 private const val FontScaleKey = "fontScale"
 private const val FontScaleModeKey = "fontScaleMode"
 private const val TextDirectionKey = "textDirection"
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogTopAppBar.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogTopAppBar.kt
index 6c5ff118..60e0806 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogTopAppBar.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/common/CatalogTopAppBar.kt
@@ -40,8 +40,12 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.isTraversalGroup
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.traversalIndex
 import androidx.compose.ui.text.style.TextOverflow
 
 @OptIn(ExperimentalMaterial3Api::class)
@@ -64,6 +68,11 @@
 ) {
     var moreMenuExpanded by remember { mutableStateOf(false) }
     TopAppBar(
+        modifier =
+            Modifier.semantics {
+                traversalIndex = -2f
+                isTraversalGroup = true
+            },
         title = {
             Text(
                 text = title,
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt
index 4aec483..e1c92f3 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt
@@ -22,15 +22,19 @@
 import android.content.ContextWrapper
 import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialExpressiveTheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.catalog.library.model.ColorMode
+import androidx.compose.material3.catalog.library.model.ExpressiveThemeMode
 import androidx.compose.material3.catalog.library.model.FontScaleMode
 import androidx.compose.material3.catalog.library.model.TextDirection
 import androidx.compose.material3.catalog.library.model.Theme
-import androidx.compose.material3.catalog.library.model.ThemeMode
+import androidx.compose.material3.catalog.library.model.ThemeColorMode
 import androidx.compose.material3.darkColorScheme
 import androidx.compose.material3.dynamicDarkColorScheme
 import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.expressiveLightColorScheme
 import androidx.compose.material3.lightColorScheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -45,6 +49,7 @@
 import androidx.core.view.WindowCompat
 
 @SuppressLint("NewApi")
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun CatalogTheme(theme: Theme, content: @Composable () -> Unit) {
     val context = LocalContext.current
@@ -52,7 +57,11 @@
         when (theme.colorMode) {
             ColorMode.Dynamic -> dynamicLightColorScheme(context)
             ColorMode.Custom -> LightCustomColorScheme
-            ColorMode.Baseline -> lightColorScheme()
+            ColorMode.Baseline -> {
+                if (theme.expressiveThemeMode == ExpressiveThemeMode.Expressive) {
+                    expressiveLightColorScheme()
+                } else lightColorScheme()
+            }
         }
     val darkColorScheme =
         when (theme.colorMode) {
@@ -62,7 +71,7 @@
         }
     val colorScheme =
         colorSchemeFromThemeMode(
-            themeMode = theme.themeMode,
+            themeColorMode = theme.themeColorMode,
             lightColorScheme = lightColorScheme,
             darkColorScheme = darkColorScheme
         )
@@ -94,23 +103,30 @@
                     }
             )
     ) {
-        MaterialTheme(
-            colorScheme = colorScheme,
-            content = content,
-        )
+        if (theme.expressiveThemeMode == ExpressiveThemeMode.Expressive) {
+            MaterialExpressiveTheme(
+                colorScheme = colorScheme,
+                content = content,
+            )
+        } else {
+            MaterialTheme(
+                colorScheme = colorScheme,
+                content = content,
+            )
+        }
     }
 }
 
 @Composable
 fun colorSchemeFromThemeMode(
-    themeMode: ThemeMode,
+    themeColorMode: ThemeColorMode,
     lightColorScheme: ColorScheme,
     darkColorScheme: ColorScheme
 ): ColorScheme {
-    return when (themeMode) {
-        ThemeMode.Light -> lightColorScheme
-        ThemeMode.Dark -> darkColorScheme
-        ThemeMode.System ->
+    return when (themeColorMode) {
+        ThemeColorMode.Light -> lightColorScheme
+        ThemeColorMode.Dark -> darkColorScheme
+        ThemeColorMode.System ->
             if (!isSystemInDarkTheme()) {
                 lightColorScheme
             } else {
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
index cb21b3d..c5a5228 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
@@ -30,24 +30,31 @@
 import androidx.compose.foundation.layout.safeDrawing
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.selection.selectable
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Warning
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Badge
 import androidx.compose.material3.Button
 import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.RadioButton
 import androidx.compose.material3.Slider
 import androidx.compose.material3.Text
 import androidx.compose.material3.catalog.library.R
 import androidx.compose.material3.catalog.library.model.ColorMode
+import androidx.compose.material3.catalog.library.model.ExpressiveThemeMode
 import androidx.compose.material3.catalog.library.model.FontScaleMode
 import androidx.compose.material3.catalog.library.model.MaxFontScale
 import androidx.compose.material3.catalog.library.model.MinFontScale
 import androidx.compose.material3.catalog.library.model.TextDirection
 import androidx.compose.material3.catalog.library.model.Theme
-import androidx.compose.material3.catalog.library.model.ThemeMode
+import androidx.compose.material3.catalog.library.model.ThemeColorMode
 import androidx.compose.material3.minimumInteractiveComponentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -58,6 +65,9 @@
 
 @Composable
 fun ThemePicker(theme: Theme, onThemeChange: (theme: Theme) -> Unit) {
+    val openExpressiveDialog = remember { mutableStateOf(false) }
+    val expressiveThemeValue = remember { mutableStateOf(ExpressiveThemeMode.NonExpressive) }
+
     LazyColumn(
         contentPadding =
             WindowInsets.safeDrawing
@@ -73,30 +83,30 @@
                 modifier = Modifier.padding(horizontal = ThemePickerPadding)
             )
             // LazyVerticalGrid can't be used within LazyColumn due to nested scrolling
-            val themeModes = ThemeMode.values()
+            val themeColorModes = ThemeColorMode.values()
             Column(
                 modifier = Modifier.padding(ThemePickerPadding),
             ) {
                 Row(horizontalArrangement = Arrangement.spacedBy(ThemePickerPadding)) {
                     RadioButtonOption(
                         modifier = Modifier.weight(1f),
-                        option = themeModes[0],
-                        selected = themeModes[0] == theme.themeMode,
-                        onClick = { onThemeChange(theme.copy(themeMode = it)) }
+                        option = themeColorModes[0],
+                        selected = themeColorModes[0] == theme.themeColorMode,
+                        onClick = { onThemeChange(theme.copy(themeColorMode = it)) }
                     )
                     RadioButtonOption(
                         modifier = Modifier.weight(1f),
-                        option = themeModes[1],
-                        selected = themeModes[1] == theme.themeMode,
-                        onClick = { onThemeChange(theme.copy(themeMode = it)) }
+                        option = themeColorModes[1],
+                        selected = themeColorModes[1] == theme.themeColorMode,
+                        onClick = { onThemeChange(theme.copy(themeColorMode = it)) }
                     )
                 }
                 Row {
                     RadioButtonOption(
                         modifier = Modifier.weight(1f),
-                        option = themeModes[2],
-                        selected = themeModes[2] == theme.themeMode,
-                        onClick = { onThemeChange(theme.copy(themeMode = it)) }
+                        option = themeColorModes[2],
+                        selected = themeColorModes[2] == theme.themeColorMode,
+                        onClick = { onThemeChange(theme.copy(themeColorMode = it)) }
                     )
                 }
             }
@@ -204,6 +214,53 @@
                     onValueChangeFinished = { onThemeChange(theme.copy(fontScale = fontScale)) }
                 )
             }
+            HorizontalDivider(Modifier.padding(horizontal = ThemePickerPadding))
+        }
+        item {
+            Row {
+                Text(
+                    text = stringResource(id = R.string.expressive_theme_mode),
+                    style = MaterialTheme.typography.bodyMedium,
+                    modifier =
+                        Modifier.padding(
+                            start = ThemePickerPadding,
+                            // Align Badge closer to text
+                            end = ThemePickerPadding / 2
+                        )
+                )
+                Badge { Text(stringResource(R.string.experimental)) }
+            }
+            // LazyVerticalGrid can't be used within LazyColumn due to nested scrolling
+            val expressiveThemeModes = ExpressiveThemeMode.values()
+            Column(
+                modifier = Modifier.padding(ThemePickerPadding),
+            ) {
+                Row(horizontalArrangement = Arrangement.spacedBy(ThemePickerPadding)) {
+                    RadioButtonOption(
+                        modifier = Modifier.weight(1f),
+                        option = expressiveThemeModes[0],
+                        selected = expressiveThemeModes[0] == theme.expressiveThemeMode,
+                        onClick = {
+                            if (theme.expressiveThemeMode != it) {
+                                expressiveThemeValue.value = it
+                                openExpressiveDialog.value = true
+                            }
+                        }
+                    )
+                    RadioButtonOption(
+                        modifier = Modifier.weight(1f),
+                        option = expressiveThemeModes[1],
+                        selected = expressiveThemeModes[1] == theme.expressiveThemeMode,
+                        onClick = {
+                            if (theme.expressiveThemeMode != it) {
+                                expressiveThemeValue.value = it
+                                openExpressiveDialog.value = true
+                            }
+                        }
+                    )
+                }
+            }
+            HorizontalDivider(Modifier.padding(horizontal = ThemePickerPadding))
         }
         item {
             Column(
@@ -216,6 +273,31 @@
             }
         }
     }
+    if (openExpressiveDialog.value) {
+        ExpressiveAlertDialog(
+            onDismissRequest = {
+                // Return to previous data type.
+                if (expressiveThemeValue.value == ExpressiveThemeMode.NonExpressive) {
+                    expressiveThemeValue.value = ExpressiveThemeMode.Expressive
+                } else {
+                    expressiveThemeValue.value = ExpressiveThemeMode.NonExpressive
+                }
+                openExpressiveDialog.value = false
+            },
+            onDismissButtonClick = {
+                // Return to previous data type.
+                if (expressiveThemeValue.value == ExpressiveThemeMode.NonExpressive) {
+                    expressiveThemeValue.value = ExpressiveThemeMode.Expressive
+                } else {
+                    expressiveThemeValue.value = ExpressiveThemeMode.NonExpressive
+                }
+                openExpressiveDialog.value = false
+            },
+            onConfirmButtonClick = {
+                onThemeChange(theme.copy(expressiveThemeMode = expressiveThemeValue.value))
+            }
+        )
+    }
 }
 
 @Composable
@@ -273,4 +355,25 @@
     }
 }
 
+@Composable
+private fun ExpressiveAlertDialog(
+    onDismissRequest: () -> Unit,
+    onConfirmButtonClick: () -> Unit,
+    onDismissButtonClick: () -> Unit,
+) {
+    AlertDialog(
+        icon = { Icon(imageVector = Icons.Filled.Warning, contentDescription = null) },
+        title = { Text("Warning") },
+        text = {
+            Text(
+                "Setting a new Material theme will reset the catalog and progress will be " +
+                    "lost. Please confirm before proceeding."
+            )
+        },
+        onDismissRequest = onDismissRequest,
+        confirmButton = { Button(onClick = onConfirmButtonClick) { Text("Confirm") } },
+        dismissButton = { Button(onClick = onDismissButtonClick) { Text("Cancel") } },
+    )
+}
+
 private val ThemePickerPadding = 16.dp
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml b/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml
index efb89e9..89c8d8e 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml
@@ -25,6 +25,7 @@
     <string name="more_menu_button">External links</string>
 
     <string name="description">Description</string>
+    <string name="experimental">Experimental</string>
     <string name="examples">Examples</string>
     <string name="no_examples">No examples</string>
 
@@ -37,6 +38,7 @@
     <string name="open_source_licenses">Open source licenses</string>
 
     <string name="theme" description="Theme. [CHAR_LIMIT=NONE]">Theme</string>
+    <string name="expressive_theme_mode" description="Theme. [CHAR_LIMIT=NONE]">Expressive theme mode</string>
     <string name="color_mode" description=" Material 3 Color Mode. [CHAR_LIMIT=NONE]">Color mode</string>
     <string name="font_scale" description="Font scale. [CHAR_LIMIT=NONE]">Font scale</string>
     <string name="text_direction" description="Text direction. [CHAR_LIMIT=NONE]">Text direction</string>
diff --git a/compose/material3/material3/integration-tests/material3-demos/build.gradle b/compose/material3/material3/integration-tests/material3-demos/build.gradle
index cca0272..539b8cb 100644
--- a/compose/material3/material3/integration-tests/material3-demos/build.gradle
+++ b/compose/material3/material3/integration-tests/material3-demos/build.gradle
@@ -34,6 +34,7 @@
 
     implementation(libs.kotlinStdlib)
 
+    implementation(project(":activity:activity-compose"))
     implementation(project(":compose:animation:animation"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:foundation:foundation-layout"))
@@ -42,6 +43,8 @@
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:ui:ui-text"))
+
+    implementation("androidx.compose.material:material-icons-extended:1.6.0")
 }
 
 androidx {
@@ -52,5 +55,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.material3.demos"
 }
diff --git a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/FloatingActionButtonMenuDemos.kt b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/FloatingActionButtonMenuDemos.kt
new file mode 100644
index 0000000..683d927
--- /dev/null
+++ b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/FloatingActionButtonMenuDemos.kt
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.demos
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.Label
+import androidx.compose.material.icons.automirrored.filled.Message
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.AlignHorizontalCenter
+import androidx.compose.material.icons.filled.Archive
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Contacts
+import androidx.compose.material.icons.filled.FormatSize
+import androidx.compose.material.icons.filled.Palette
+import androidx.compose.material.icons.filled.People
+import androidx.compose.material.icons.filled.Snooze
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.FloatingActionButtonMenu
+import androidx.compose.material3.FloatingActionButtonMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.Text
+import androidx.compose.material3.ToggleableFloatingActionButton
+import androidx.compose.material3.ToggleableFloatingActionButtonDefaults
+import androidx.compose.material3.ToggleableFloatingActionButtonDefaults.animateIcon
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.customActions
+import androidx.compose.ui.semantics.isTraversalGroup
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.semantics.traversalIndex
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+fun FloatingActionButtonMenuDemo() {
+    Box(Modifier.fillMaxSize()) {
+        val colorOptions =
+            listOf(
+                ColorOption(
+                    label = "Primary Container",
+                    initialContainerColor = MaterialTheme.colorScheme.primaryContainer,
+                    finalContainerColor = MaterialTheme.colorScheme.primary,
+                    initialIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
+                    finalIconColor = MaterialTheme.colorScheme.onPrimary,
+                    itemContainerColor = MaterialTheme.colorScheme.primaryContainer,
+                ),
+                ColorOption(
+                    label = "Secondary Container",
+                    initialContainerColor = MaterialTheme.colorScheme.secondaryContainer,
+                    finalContainerColor = MaterialTheme.colorScheme.secondary,
+                    initialIconColor = MaterialTheme.colorScheme.onSecondaryContainer,
+                    finalIconColor = MaterialTheme.colorScheme.onSecondary,
+                    itemContainerColor = MaterialTheme.colorScheme.secondaryContainer,
+                ),
+                ColorOption(
+                    label = "Tertiary Container",
+                    initialContainerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                    finalContainerColor = MaterialTheme.colorScheme.tertiary,
+                    initialIconColor = MaterialTheme.colorScheme.onTertiaryContainer,
+                    finalIconColor = MaterialTheme.colorScheme.onTertiary,
+                    itemContainerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                ),
+                ColorOption(
+                    label = "Primary",
+                    initialContainerColor = MaterialTheme.colorScheme.primary,
+                    finalContainerColor = MaterialTheme.colorScheme.primary,
+                    initialIconColor = MaterialTheme.colorScheme.onPrimary,
+                    finalIconColor = MaterialTheme.colorScheme.onPrimary,
+                    itemContainerColor = MaterialTheme.colorScheme.primaryContainer,
+                ),
+                ColorOption(
+                    label = "Secondary",
+                    initialContainerColor = MaterialTheme.colorScheme.secondary,
+                    finalContainerColor = MaterialTheme.colorScheme.secondary,
+                    initialIconColor = MaterialTheme.colorScheme.onSecondary,
+                    finalIconColor = MaterialTheme.colorScheme.onSecondary,
+                    itemContainerColor = MaterialTheme.colorScheme.secondaryContainer,
+                ),
+                ColorOption(
+                    label = "Tertiary",
+                    initialContainerColor = MaterialTheme.colorScheme.tertiary,
+                    finalContainerColor = MaterialTheme.colorScheme.tertiary,
+                    initialIconColor = MaterialTheme.colorScheme.onTertiary,
+                    finalIconColor = MaterialTheme.colorScheme.onTertiary,
+                    itemContainerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                )
+            )
+        val (selectedColor, onColorSelected) = remember { mutableStateOf(colorOptions[0]) }
+
+        val sizeOptions =
+            listOf(
+                SizeOption(
+                    label = "Default",
+                    initialContainerSize = ToggleableFloatingActionButtonDefaults.containerSize(),
+                    initialContainerCornerRadius =
+                        ToggleableFloatingActionButtonDefaults.containerCornerRadius(),
+                    initialIconSize = ToggleableFloatingActionButtonDefaults.iconSize(),
+                ),
+                SizeOption(
+                    label = "Medium",
+                    initialContainerSize =
+                        ToggleableFloatingActionButtonDefaults.containerSizeMedium(),
+                    initialContainerCornerRadius =
+                        ToggleableFloatingActionButtonDefaults.containerCornerRadiusMedium(),
+                    initialIconSize = ToggleableFloatingActionButtonDefaults.iconSizeMedium(),
+                ),
+                SizeOption(
+                    label = "Large",
+                    initialContainerSize =
+                        ToggleableFloatingActionButtonDefaults.containerSizeLarge(),
+                    initialContainerCornerRadius =
+                        ToggleableFloatingActionButtonDefaults.containerCornerRadiusLarge(),
+                    initialIconSize = ToggleableFloatingActionButtonDefaults.iconSizeLarge(),
+                ),
+            )
+        val (selectedSize, onSizeSelected) = remember { mutableStateOf(sizeOptions[0]) }
+
+        val alignmentOptions =
+            listOf(
+                AlignmentOption(
+                    label = "End",
+                    alignment = Alignment.BottomEnd,
+                    fabCheckedAlignment = Alignment.TopEnd,
+                    menuAlignment = Alignment.End,
+                ),
+                AlignmentOption(
+                    label = "Center",
+                    alignment = Alignment.BottomCenter,
+                    fabCheckedAlignment = Alignment.TopCenter,
+                    menuAlignment = Alignment.CenterHorizontally,
+                ),
+                AlignmentOption(
+                    label = "Start",
+                    alignment = Alignment.BottomStart,
+                    fabCheckedAlignment = Alignment.TopStart,
+                    menuAlignment = Alignment.Start,
+                ),
+            )
+        val (selectedAlignment, onAlignmentSelected) =
+            remember { mutableStateOf(alignmentOptions[0]) }
+
+        Column(modifier = Modifier.verticalScroll(rememberScrollState()).padding(16.dp)) {
+            Row(Modifier.padding(bottom = 8.dp).semantics { isTraversalGroup = true }) {
+                Icon(
+                    imageVector = Icons.Filled.Palette,
+                    contentDescription = null,
+                    tint = MaterialTheme.colorScheme.secondary
+                )
+                Text(
+                    text = "Color Options",
+                    style = MaterialTheme.typography.titleLarge,
+                    modifier = Modifier.padding(start = 8.dp)
+                )
+            }
+            Column(Modifier.selectableGroup()) {
+                colorOptions.forEach { color ->
+                    Row(
+                        Modifier.fillMaxWidth()
+                            .height(48.dp)
+                            .selectable(
+                                selected = (color.label == selectedColor.label),
+                                onClick = { onColorSelected(color) },
+                                role = Role.RadioButton
+                            )
+                            .padding(horizontal = 16.dp),
+                        verticalAlignment = Alignment.CenterVertically
+                    ) {
+                        RadioButton(selected = (color.label == selectedColor.label), onClick = null)
+                        Text(
+                            text = color.label,
+                            style = MaterialTheme.typography.bodyLarge,
+                            modifier = Modifier.padding(start = 16.dp)
+                        )
+                    }
+                }
+            }
+
+            Row(
+                Modifier.padding(top = 16.dp, bottom = 8.dp).semantics { isTraversalGroup = true }
+            ) {
+                Icon(
+                    imageVector = Icons.Filled.FormatSize,
+                    contentDescription = null,
+                    tint = MaterialTheme.colorScheme.secondary
+                )
+                Text(
+                    text = "Size Options",
+                    style = MaterialTheme.typography.titleLarge,
+                    modifier = Modifier.padding(start = 8.dp)
+                )
+            }
+            Column(Modifier.selectableGroup()) {
+                sizeOptions.forEach { size ->
+                    Row(
+                        Modifier.fillMaxWidth()
+                            .height(48.dp)
+                            .selectable(
+                                selected = (size.label == selectedSize.label),
+                                onClick = { onSizeSelected(size) },
+                                role = Role.RadioButton
+                            )
+                            .padding(horizontal = 16.dp),
+                        verticalAlignment = Alignment.CenterVertically
+                    ) {
+                        RadioButton(selected = (size.label == selectedSize.label), onClick = null)
+                        Text(
+                            text = size.label,
+                            style = MaterialTheme.typography.bodyLarge,
+                            modifier = Modifier.padding(start = 16.dp)
+                        )
+                    }
+                }
+            }
+
+            Row(
+                Modifier.padding(top = 16.dp, bottom = 8.dp).semantics { isTraversalGroup = true }
+            ) {
+                Icon(
+                    imageVector = Icons.Filled.AlignHorizontalCenter,
+                    contentDescription = null,
+                    tint = MaterialTheme.colorScheme.secondary
+                )
+                Text(
+                    text = "Alignment Options",
+                    style = MaterialTheme.typography.titleLarge,
+                    modifier = Modifier.padding(start = 8.dp)
+                )
+            }
+            Column(Modifier.selectableGroup()) {
+                alignmentOptions.forEach { alignment ->
+                    Row(
+                        Modifier.fillMaxWidth()
+                            .height(48.dp)
+                            .selectable(
+                                selected = (alignment.label == selectedAlignment.label),
+                                onClick = { onAlignmentSelected(alignment) },
+                                role = Role.RadioButton
+                            )
+                            .padding(horizontal = 16.dp),
+                        verticalAlignment = Alignment.CenterVertically
+                    ) {
+                        RadioButton(
+                            selected = (alignment.label == selectedAlignment.label),
+                            onClick = null
+                        )
+                        Text(
+                            text = alignment.label,
+                            style = MaterialTheme.typography.bodyLarge,
+                            modifier = Modifier.padding(start = 16.dp)
+                        )
+                    }
+                }
+            }
+        }
+
+        val items =
+            listOf(
+                Icons.AutoMirrored.Filled.Message to "Reply",
+                Icons.Filled.People to "Reply all",
+                Icons.Filled.Contacts to "Forward",
+                Icons.Filled.Snooze to "Snooze",
+                Icons.Filled.Archive to "Archive",
+                Icons.AutoMirrored.Filled.Label to "Label",
+            )
+
+        var fabMenuExpanded by rememberSaveable { mutableStateOf(false) }
+
+        BackHandler(fabMenuExpanded) { fabMenuExpanded = false }
+
+        Column(
+            modifier =
+                Modifier.align(selectedAlignment.alignment)
+                    .padding(bottom = 16.dp, start = 16.dp, end = 16.dp)
+                    .semantics {
+                        isTraversalGroup = true
+                        traversalIndex = -1f
+                    },
+            horizontalAlignment = selectedAlignment.menuAlignment
+        ) {
+            FloatingActionButtonMenu(
+                modifier = Modifier.weight(weight = 1f, fill = false),
+                expanded = fabMenuExpanded,
+                itemsCount = items.size,
+                horizontalAlignment = selectedAlignment.menuAlignment,
+            ) {
+                items.forEachIndexed { i, item ->
+                    FloatingActionButtonMenuItem(
+                        modifier =
+                            Modifier.semantics {
+                                isTraversalGroup = true
+                                // Add a custom a11y action to allow closing the menu when focusing
+                                // the last menu item, since the close button comes before the first
+                                // menu item in the traversal order.
+                                if (i == itemsCount - 1) {
+                                    customActions =
+                                        listOf(
+                                            CustomAccessibilityAction(
+                                                label = "Close menu",
+                                                action = {
+                                                    fabMenuExpanded = false
+                                                    true
+                                                }
+                                            )
+                                        )
+                                }
+                            },
+                        onClick = { fabMenuExpanded = false },
+                        icon = { Icon(item.first, contentDescription = null) },
+                        text = { Text(text = item.second) },
+                        itemIndex = i,
+                        containerColor = selectedColor.itemContainerColor,
+                    )
+                }
+            }
+
+            ToggleableFloatingActionButton(
+                modifier =
+                    Modifier.semantics {
+                        isTraversalGroup = true
+                        traversalIndex = -1f
+                        stateDescription = if (fabMenuExpanded) "Expanded" else "Collapsed"
+                        contentDescription = "Toggle menu"
+                    },
+                checked = fabMenuExpanded,
+                onCheckedChange = { fabMenuExpanded = !fabMenuExpanded },
+                contentAlignment = selectedAlignment.fabCheckedAlignment,
+                containerColor =
+                    ToggleableFloatingActionButtonDefaults.containerColor(
+                        selectedColor.initialContainerColor,
+                        selectedColor.finalContainerColor
+                    ),
+                containerSize = selectedSize.initialContainerSize,
+                containerCornerRadius = selectedSize.initialContainerCornerRadius,
+            ) {
+                val imageVector by remember {
+                    derivedStateOf {
+                        if (checkedProgress > 0.5f) Icons.Filled.Close else Icons.Filled.Add
+                    }
+                }
+                Icon(
+                    painter = rememberVectorPainter(imageVector),
+                    contentDescription = null,
+                    modifier =
+                        Modifier.animateIcon(
+                            checkedProgress = { checkedProgress },
+                            color =
+                                ToggleableFloatingActionButtonDefaults.iconColor(
+                                    selectedColor.initialIconColor,
+                                    selectedColor.finalIconColor
+                                ),
+                            size = selectedSize.initialIconSize
+                        )
+                )
+            }
+        }
+    }
+}
+
+private data class ColorOption(
+    val label: String,
+    val initialContainerColor: Color,
+    val finalContainerColor: Color,
+    val initialIconColor: Color,
+    val finalIconColor: Color,
+    val itemContainerColor: Color,
+)
+
+private data class SizeOption(
+    val label: String,
+    val initialContainerSize: (Float) -> Dp,
+    val initialContainerCornerRadius: (Float) -> Dp,
+    val initialIconSize: (Float) -> Dp,
+)
+
+private data class AlignmentOption(
+    val label: String,
+    val alignment: Alignment,
+    val fabCheckedAlignment: Alignment,
+    val menuAlignment: Alignment.Horizontal,
+)
diff --git a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt
index c2953f7..22b439f 100644
--- a/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt
+++ b/compose/material3/material3/integration-tests/material3-demos/src/main/java/androidx/compose/material3/demos/Material3Demos.kt
@@ -24,6 +24,7 @@
         "Material 3",
         listOf(
             ComposableDemo("Color Scheme") { ColorSchemeDemo() },
+            ComposableDemo("FAB Menu") { FloatingActionButtonMenuDemo() },
             ComposableDemo("Pull To Refresh") { PullToRefreshDemo() },
             ComposableDemo("Shape") { ShapeDemo() },
             ComposableDemo("Swipe To Dismiss") { SwipeToDismissDemo() },
diff --git a/compose/material3/material3/lint-baseline.xml b/compose/material3/material3/lint-baseline.xml
index 6e32e2f..7efb2b1 100644
--- a/compose/material3/material3/lint-baseline.xml
+++ b/compose/material3/material3/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -155,73 +155,6 @@
     </issue>
 
     <issue
-        id="ComposableNaming"
-        message="Composable functions with a return type should start with a lowercase letter"
-        errorLine1="    fun Url("
-        errorLine2="        ~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/Text.kt"/>
-    </issue>
-
-    <issue
-        id="ComposableNaming"
-        message="Composable functions with a return type should start with a lowercase letter"
-        errorLine1="    fun Clickable("
-        errorLine2="        ~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/Text.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.compose.material3.ExperimentalMaterial3Api` or `@OptIn(markerClass = androidx.compose.material3.ExperimentalMaterial3Api.class)`">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt"/>
-    </issue>
-
-    <issue
         id="PrimitiveInCollection"
         message="variable crossAxisSizes with type List&lt;Integer>: replace with IntList"
         errorLine1="        val crossAxisSizes = mutableListOf&lt;Int>()"
@@ -248,40 +181,4 @@
             file="src/commonMain/kotlin/androidx/compose/material3/TabRow.kt"/>
     </issue>
 
-    <issue
-        id="PrimitiveInCollection"
-        message="return type List&lt;Integer> of getValues$lint_module: replace with IntList"
-        errorLine1="    internal val values get() = if (selection == Selection.Minute) Minutes else Hours"
-        errorLine2="                 ~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="field Minutes with type List&lt;Integer>: replace with IntList"
-        errorLine1="private val Minutes = listOf(0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="field Hours with type List&lt;Integer>: replace with IntList"
-        errorLine1="private val Hours = listOf(12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="field ExtraHours with type List&lt;Integer>: replace with IntList"
-        errorLine1="private val ExtraHours = Hours.fastMap { (it % 12 + 12) }"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt"/>
-    </issue>
-
 </issues>
diff --git a/compose/material3/material3/samples/build.gradle b/compose/material3/material3/samples/build.gradle
index c0b2f3b..c514173 100644
--- a/compose/material3/material3/samples/build.gradle
+++ b/compose/material3/material3/samples/build.gradle
@@ -61,5 +61,9 @@
 }
 
 android {
+    compileSdk 35
+
     namespace "androidx.compose.material3.samples"
+    // TODO(b/328001575)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt
index 5a92111..a513171 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt
@@ -16,13 +16,19 @@
 
 package androidx.compose.material3.samples
 
+import android.content.Context
+import android.view.accessibility.AccessibilityManager
 import androidx.annotation.Sampled
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.ArrowForward
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Check
 import androidx.compose.material.icons.filled.Edit
@@ -34,6 +40,7 @@
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.FabPosition
+import androidx.compose.material3.FilledIconButton
 import androidx.compose.material3.FloatingActionButton
 import androidx.compose.material3.FloatingActionButtonDefaults
 import androidx.compose.material3.Icon
@@ -47,8 +54,10 @@
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.material3.TopAppBarTitleAlignment
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
@@ -644,6 +653,11 @@
 @Sampled
 @Composable
 fun ExitAlwaysBottomAppBar() {
+    val context = LocalContext.current
+    val isTouchExplorationEnabled = remember {
+        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        am.isEnabled && am.isTouchExplorationEnabled
+    }
     val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
     Scaffold(
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
@@ -657,7 +671,7 @@
                         Icon(Icons.Filled.Edit, contentDescription = "Localized description")
                     }
                 },
-                scrollBehavior = scrollBehavior
+                scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
             )
         },
         floatingActionButton = {
@@ -688,3 +702,273 @@
         }
     )
 }
+
+/**
+ * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
+ * the content scrolled down. The content is spaced around.
+ */
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun ExitAlwaysBottomAppBarSpacedAround() {
+    val context = LocalContext.current
+    val isTouchExplorationEnabled = remember {
+        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        am.isEnabled && am.isTouchExplorationEnabled
+    }
+    val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
+    Scaffold(
+        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+        bottomBar = {
+            BottomAppBar(
+                horizontalArrangement = Arrangement.SpaceAround,
+                contentPadding = PaddingValues(horizontal = 0.dp),
+                scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
+                content = {
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowBack,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowForward,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    FilledIconButton(
+                        modifier = Modifier.width(56.dp),
+                        onClick = { /* doSomething() */ }
+                    ) {
+                        Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                    }
+                }
+            )
+        },
+        content = { innerPadding ->
+            LazyColumn(
+                contentPadding = innerPadding,
+                verticalArrangement = Arrangement.spacedBy(8.dp)
+            ) {
+                val list = (0..75).map { it.toString() }
+                items(count = list.size) {
+                    Text(
+                        text = list[it],
+                        style = MaterialTheme.typography.bodyLarge,
+                        modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
+                    )
+                }
+            }
+        }
+    )
+}
+
+/**
+ * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
+ * the content scrolled down. The content is spaced between.
+ */
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun ExitAlwaysBottomAppBarSpacedBetween() {
+    val context = LocalContext.current
+    val isTouchExplorationEnabled = remember {
+        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        am.isEnabled && am.isTouchExplorationEnabled
+    }
+    val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
+    Scaffold(
+        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+        bottomBar = {
+            BottomAppBar(
+                horizontalArrangement = Arrangement.SpaceBetween,
+                scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
+                content = {
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowBack,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowForward,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    FilledIconButton(
+                        modifier = Modifier.width(56.dp),
+                        onClick = { /* doSomething() */ }
+                    ) {
+                        Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                    }
+                }
+            )
+        },
+        content = { innerPadding ->
+            LazyColumn(
+                contentPadding = innerPadding,
+                verticalArrangement = Arrangement.spacedBy(8.dp)
+            ) {
+                val list = (0..75).map { it.toString() }
+                items(count = list.size) {
+                    Text(
+                        text = list[it],
+                        style = MaterialTheme.typography.bodyLarge,
+                        modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
+                    )
+                }
+            }
+        }
+    )
+}
+
+/**
+ * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
+ * the content scrolled down. The content is spaced evenly.
+ */
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun ExitAlwaysBottomAppBarSpacedEvenly() {
+    val context = LocalContext.current
+    val isTouchExplorationEnabled = remember {
+        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        am.isEnabled && am.isTouchExplorationEnabled
+    }
+    val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
+    Scaffold(
+        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+        bottomBar = {
+            BottomAppBar(
+                horizontalArrangement = Arrangement.SpaceEvenly,
+                contentPadding = PaddingValues(horizontal = 0.dp),
+                scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
+                content = {
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowBack,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowForward,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    FilledIconButton(
+                        modifier = Modifier.width(56.dp),
+                        onClick = { /* doSomething() */ }
+                    ) {
+                        Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                    }
+                }
+            )
+        },
+        content = { innerPadding ->
+            LazyColumn(
+                contentPadding = innerPadding,
+                verticalArrangement = Arrangement.spacedBy(8.dp)
+            ) {
+                val list = (0..75).map { it.toString() }
+                items(count = list.size) {
+                    Text(
+                        text = list[it],
+                        style = MaterialTheme.typography.bodyLarge,
+                        modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
+                    )
+                }
+            }
+        }
+    )
+}
+
+/**
+ * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
+ * the content scrolled down. The content arrangement is fixed.
+ */
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun ExitAlwaysBottomAppBarFixed() {
+    val context = LocalContext.current
+    val isTouchExplorationEnabled = remember {
+        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        am.isEnabled && am.isTouchExplorationEnabled
+    }
+    val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
+    Scaffold(
+        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+        bottomBar = {
+            BottomAppBar(
+                horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+                scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
+                content = {
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowBack,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowForward,
+                            contentDescription = "Localized description"
+                        )
+                    }
+                    FilledIconButton(
+                        modifier = Modifier.width(56.dp),
+                        onClick = { /* doSomething() */ }
+                    ) {
+                        Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                    }
+                    IconButton(onClick = { /* doSomething() */ }) {
+                        Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                    }
+                }
+            )
+        },
+        content = { innerPadding ->
+            LazyColumn(
+                contentPadding = innerPadding,
+                verticalArrangement = Arrangement.spacedBy(8.dp)
+            ) {
+                val list = (0..75).map { it.toString() }
+                items(count = list.size) {
+                    Text(
+                        text = list[it],
+                        style = MaterialTheme.typography.bodyLarge,
+                        modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
+                    )
+                }
+            }
+        }
+    )
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ColorSchemeSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ColorSchemeSamples.kt
index a8edad8..b162298 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ColorSchemeSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ColorSchemeSamples.kt
@@ -18,8 +18,11 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialExpressiveTheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.expressiveLightColorScheme
 import androidx.compose.material3.lightColorScheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -71,3 +74,18 @@
         }
     }
 }
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+@Sampled
+fun MaterialExpressiveThemeColorSchemeSample() {
+    @Composable
+    fun MyMaterialTheme(content: @Composable () -> Unit) {
+        MaterialExpressiveTheme(
+            colorScheme =
+                if (isSystemInDarkTheme()) darkColorScheme() else expressiveLightColorScheme()
+        ) {
+            content()
+        }
+    }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ExposedDropdownMenuSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ExposedDropdownMenuSamples.kt
index b63b53d..bf4d912 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ExposedDropdownMenuSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ExposedDropdownMenuSamples.kt
@@ -14,9 +14,18 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalMaterial3Api::class)
+
 package androidx.compose.material3.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
 import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExposedDropdownMenuAnchorType
@@ -35,19 +44,19 @@
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.buildAnnotatedString
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.substring
 import androidx.compose.ui.text.withStyle
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Preview
 @Sampled
 @Composable
 fun ExposedDropdownMenuSample() {
-    val options = listOf("Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread")
+    val options: List<String> = SampleData.take(5)
     var expanded by remember { mutableStateOf(false) }
-    var text by remember { mutableStateOf(options[0]) }
+    val textFieldState = rememberTextFieldState(options[0])
 
     ExposedDropdownMenuBox(
         expanded = expanded,
@@ -58,10 +67,9 @@
             // expanding/collapsing the menu on click. A read-only text field has
             // the anchor type `PrimaryNotEditable`.
             modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
-            value = text,
-            onValueChange = {},
+            state = textFieldState,
             readOnly = true,
-            singleLine = true,
+            lineLimits = TextFieldLineLimits.SingleLine,
             label = { Text("Label") },
             trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
             colors = ExposedDropdownMenuDefaults.textFieldColors(),
@@ -74,7 +82,7 @@
                 DropdownMenuItem(
                     text = { Text(option, style = MaterialTheme.typography.bodyLarge) },
                     onClick = {
-                        text = option
+                        textFieldState.setTextAndPlaceCursorAtEnd(option)
                         expanded = false
                     },
                     contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
@@ -84,17 +92,16 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Preview
 @Sampled
 @Composable
 fun EditableExposedDropdownMenuSample() {
-    val options = listOf("Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread")
-    var text by remember { mutableStateOf(TextFieldValue()) }
+    val options: List<String> = SampleData
+    val textFieldState = rememberTextFieldState()
 
     // The text that the user inputs into the text field can be used to filter the options.
     // This sample uses string subsequence matching.
-    val filteredOptions = options.filteredBy(text.text)
+    val filteredOptions = options.filteredBy(textFieldState.text)
 
     val (allowExpanded, setExpanded) = remember { mutableStateOf(false) }
     val expanded = allowExpanded && filteredOptions.isNotEmpty()
@@ -107,10 +114,10 @@
             // The `menuAnchor` modifier must be passed to the text field to handle
             // expanding/collapsing the menu on click. An editable text field has
             // the anchor type `PrimaryEditable`.
-            modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
-            value = text,
-            onValueChange = { text = it },
-            singleLine = true,
+            modifier =
+                Modifier.width(280.dp).menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
+            state = textFieldState,
+            lineLimits = TextFieldLineLimits.SingleLine,
             label = { Text("Label") },
             trailingIcon = {
                 ExposedDropdownMenuDefaults.TrailingIcon(
@@ -125,6 +132,7 @@
             colors = ExposedDropdownMenuDefaults.textFieldColors(),
         )
         ExposedDropdownMenu(
+            modifier = Modifier.heightIn(max = 280.dp),
             expanded = expanded,
             onDismissRequest = { setExpanded(false) },
         ) {
@@ -132,11 +140,7 @@
                 DropdownMenuItem(
                     text = { Text(option, style = MaterialTheme.typography.bodyLarge) },
                     onClick = {
-                        text =
-                            TextFieldValue(
-                                text = option.text,
-                                selection = TextRange(option.text.length),
-                            )
+                        textFieldState.setTextAndPlaceCursorAtEnd(option.text)
                         setExpanded(false)
                     },
                     contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
@@ -146,12 +150,103 @@
     }
 }
 
+@Preview
+@Sampled
+@Composable
+fun MultiAutocompleteExposedDropdownMenuSample() {
+    /**
+     * Returns the TextRange of the current token around the cursor, where commas define token
+     * boundaries.
+     */
+    fun TextFieldState.currentTokenRange(): TextRange? {
+        if (!selection.collapsed) return null
+
+        val cursor = selection.start
+        var start = cursor
+        while (start > 0 && text[start - 1] != ',') {
+            start--
+        }
+        while (start < cursor && text[start] == ' ') {
+            start++
+        }
+
+        var end = cursor
+        while (end < text.length && text[end] != ',') {
+            end++
+        }
+        return TextRange(start, end)
+    }
+
+    fun TextFieldState.replaceThenAddComma(start: Int, end: Int, text: CharSequence) = edit {
+        replace(start, end, text)
+        val afterText = start + text.length
+        if (afterText == this.length || this.charAt(afterText) != ',') {
+            insert(afterText, ", ")
+            placeCursorBeforeCharAt(afterText + 2)
+        } else {
+            placeCursorAfterCharAt(afterText)
+        }
+    }
+
+    val allOptions: List<String> = SampleData
+    val textFieldState = rememberTextFieldState()
+    val tokenSelection = textFieldState.currentTokenRange()
+    val tokenAtCursor =
+        if (tokenSelection != null) textFieldState.text.substring(tokenSelection) else ""
+    val filteredOptions =
+        if (tokenAtCursor.isBlank()) emptyList() else allOptions.filteredBy(tokenAtCursor)
+
+    val (allowExpanded, setExpanded) = remember { mutableStateOf(false) }
+    val expanded = allowExpanded && filteredOptions.isNotEmpty()
+
+    ExposedDropdownMenuBox(
+        expanded = expanded,
+        onExpandedChange = setExpanded,
+    ) {
+        TextField(
+            modifier =
+                Modifier.width(280.dp).menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
+            state = textFieldState,
+            lineLimits = TextFieldLineLimits.SingleLine,
+            label = { Text("Label") },
+            trailingIcon = {
+                ExposedDropdownMenuDefaults.TrailingIcon(
+                    expanded = expanded,
+                    modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.SecondaryEditable),
+                )
+            },
+            colors = ExposedDropdownMenuDefaults.textFieldColors(),
+        )
+        ExposedDropdownMenu(
+            modifier = Modifier.heightIn(max = 280.dp),
+            expanded = expanded,
+            onDismissRequest = { setExpanded(false) },
+        ) {
+            filteredOptions.forEach { option ->
+                DropdownMenuItem(
+                    text = { Text(option, style = MaterialTheme.typography.bodyLarge) },
+                    onClick = {
+                        if (tokenSelection != null) {
+                            textFieldState.replaceThenAddComma(
+                                tokenSelection.start,
+                                tokenSelection.end,
+                                option
+                            )
+                        }
+                    },
+                    contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
+                )
+            }
+        }
+    }
+}
+
 /**
- * Returns the element of [this] list that contain [text] as a subsequence, with the subsequence
+ * Returns the elements of [this] list that contain [text] as a subsequence, with the subsequence
  * underlined as an [AnnotatedString].
  */
-private fun List<String>.filteredBy(text: String): List<AnnotatedString> {
-    fun underlineSubsequence(needle: String, haystack: String): AnnotatedString? {
+private fun List<String>.filteredBy(text: CharSequence): List<AnnotatedString> {
+    fun underlineSubsequence(needle: CharSequence, haystack: String): AnnotatedString? {
         return buildAnnotatedString {
             var i = 0
             for (char in needle) {
@@ -170,3 +265,33 @@
     }
     return this.mapNotNull { option -> underlineSubsequence(text, option) }
 }
+
+private val SampleData =
+    listOf(
+        "Android",
+        "Base",
+        "Cupcake",
+        "Donut",
+        "Eclair",
+        "Froyo",
+        "Gingerbread",
+        "Honeycomb",
+        "Ice Cream Sandwich",
+        "Jelly Bean",
+        "KitKat",
+        "Lollipop",
+        "Marshmallow",
+        "Nougat",
+        "Oreo",
+        "Pie",
+        "Quince Tart",
+        "Red Velvet Cake",
+        "Snow Cone",
+        "Tiramisu",
+        "Upside Down Cake",
+        "Vanilla Ice Cream",
+        "W123",
+        "X456",
+        "Y789",
+        "Z000"
+    )
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonMenuSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonMenuSamples.kt
new file mode 100644
index 0000000..b4aa96d
--- /dev/null
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonMenuSamples.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.samples
+
+import androidx.activity.compose.BackHandler
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.Label
+import androidx.compose.material.icons.automirrored.filled.Message
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Archive
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Contacts
+import androidx.compose.material.icons.filled.People
+import androidx.compose.material.icons.filled.Snooze
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.FloatingActionButtonMenu
+import androidx.compose.material3.FloatingActionButtonMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.material3.ToggleableFloatingActionButton
+import androidx.compose.material3.ToggleableFloatingActionButtonDefaults.animateIcon
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.customActions
+import androidx.compose.ui.semantics.isTraversalGroup
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.semantics.traversalIndex
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun FloatingActionButtonMenuSample() {
+    Box {
+        LazyColumn(state = rememberLazyListState(), modifier = Modifier.fillMaxSize()) {
+            for (index in 0 until 100) {
+                item { Text(text = "List item - $index", modifier = Modifier.padding(24.dp)) }
+            }
+        }
+
+        val items =
+            listOf(
+                Icons.AutoMirrored.Filled.Message to "Reply",
+                Icons.Filled.People to "Reply all",
+                Icons.Filled.Contacts to "Forward",
+                Icons.Filled.Snooze to "Snooze",
+                Icons.Filled.Archive to "Archive",
+                Icons.AutoMirrored.Filled.Label to "Label",
+            )
+
+        var fabMenuExpanded by rememberSaveable { mutableStateOf(false) }
+
+        BackHandler(fabMenuExpanded) { fabMenuExpanded = false }
+
+        Column(
+            modifier =
+                Modifier.align(Alignment.BottomEnd).padding(bottom = 16.dp, end = 16.dp).semantics {
+                    isTraversalGroup = true
+                    traversalIndex = -1f
+                },
+            horizontalAlignment = Alignment.End
+        ) {
+            FloatingActionButtonMenu(
+                modifier = Modifier.weight(weight = 1f, fill = false),
+                expanded = fabMenuExpanded,
+                itemsCount = items.size,
+            ) {
+                items.forEachIndexed { i, item ->
+                    FloatingActionButtonMenuItem(
+                        modifier =
+                            Modifier.semantics {
+                                isTraversalGroup = true
+                                // Add a custom a11y action to allow closing the menu when focusing
+                                // the last menu item, since the close button comes before the first
+                                // menu item in the traversal order.
+                                if (i == itemsCount - 1) {
+                                    customActions =
+                                        listOf(
+                                            CustomAccessibilityAction(
+                                                label = "Close menu",
+                                                action = {
+                                                    fabMenuExpanded = false
+                                                    true
+                                                }
+                                            )
+                                        )
+                                }
+                            },
+                        onClick = { fabMenuExpanded = false },
+                        icon = { Icon(item.first, contentDescription = null) },
+                        text = { Text(text = item.second) },
+                        itemIndex = i,
+                    )
+                }
+            }
+
+            ToggleableFloatingActionButton(
+                modifier =
+                    Modifier.semantics {
+                        isTraversalGroup = true
+                        traversalIndex = -1f
+                        stateDescription = if (fabMenuExpanded) "Expanded" else "Collapsed"
+                        contentDescription = "Toggle menu"
+                    },
+                checked = fabMenuExpanded,
+                onCheckedChange = { fabMenuExpanded = !fabMenuExpanded }
+            ) {
+                val imageVector by remember {
+                    derivedStateOf {
+                        if (checkedProgress > 0.5f) Icons.Filled.Close else Icons.Filled.Add
+                    }
+                }
+                Icon(
+                    painter = rememberVectorPainter(imageVector),
+                    contentDescription = null,
+                    modifier = Modifier.animateIcon({ checkedProgress })
+                )
+            }
+        }
+    }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt
index 2850b62..6fb74ca 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt
@@ -24,13 +24,18 @@
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.ExtendedFloatingActionButton
 import androidx.compose.material3.FabPosition
 import androidx.compose.material3.FloatingActionButton
 import androidx.compose.material3.FloatingActionButtonDefaults
 import androidx.compose.material3.Icon
+import androidx.compose.material3.LargeExtendedFloatingActionButton
 import androidx.compose.material3.LargeFloatingActionButton
+import androidx.compose.material3.MediumExtendedFloatingActionButton
+import androidx.compose.material3.MediumFloatingActionButton
 import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SmallExtendedFloatingActionButton
 import androidx.compose.material3.SmallFloatingActionButton
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -63,6 +68,22 @@
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun MediumFloatingActionButtonSample() {
+    MediumFloatingActionButton(
+        onClick = { /* do something */ },
+    ) {
+        Icon(
+            Icons.Filled.Add,
+            contentDescription = "Localized description",
+            modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize),
+        )
+    }
+}
+
 @Preview
 @Sampled
 @Composable
@@ -85,6 +106,36 @@
     ExtendedFloatingActionButton(onClick = { /* do something */ }) { Text(text = "Extended FAB") }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun SmallExtendedFloatingActionButtonTextSample() {
+    SmallExtendedFloatingActionButton(onClick = { /* do something */ }) {
+        Text(text = "Small Extended FAB")
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun MediumExtendedFloatingActionButtonTextSample() {
+    MediumExtendedFloatingActionButton(onClick = { /* do something */ }) {
+        Text(text = "Medium Extended FAB")
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun LargeExtendedFloatingActionButtonTextSample() {
+    LargeExtendedFloatingActionButton(onClick = { /* do something */ }) {
+        Text(text = "Large Extended FAB")
+    }
+}
+
 @Preview
 @Sampled
 @Composable
@@ -96,6 +147,54 @@
     )
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun SmallExtendedFloatingActionButtonSample() {
+    SmallExtendedFloatingActionButton(
+        onClick = { /* do something */ },
+        icon = { Icon(Icons.Filled.Add, "Localized description") },
+        text = { Text(text = "Small Extended FAB") },
+    )
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun MediumExtendedFloatingActionButtonSample() {
+    MediumExtendedFloatingActionButton(
+        onClick = { /* do something */ },
+        icon = {
+            Icon(
+                Icons.Filled.Add,
+                "Localized description",
+                modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize)
+            )
+        },
+        text = { Text(text = "Medium Extended FAB") },
+    )
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun LargeExtendedFloatingActionButtonSample() {
+    LargeExtendedFloatingActionButton(
+        onClick = { /* do something */ },
+        icon = {
+            Icon(
+                Icons.Filled.Add,
+                "Localized description",
+                modifier = Modifier.size(FloatingActionButtonDefaults.LargeIconSize)
+            )
+        },
+        text = { Text(text = "Large Extended FAB") },
+    )
+}
+
 @Preview
 @Sampled
 @Composable
@@ -122,3 +221,99 @@
         }
     }
 }
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun SmallAnimatedExtendedFloatingActionButtonSample() {
+    val listState = rememberLazyListState()
+    // The FAB is initially expanded. Once the first visible item is past the first item we
+    // collapse the FAB. We use a remembered derived state to minimize unnecessary compositions.
+    val expandedFab by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
+    Scaffold(
+        floatingActionButton = {
+            SmallExtendedFloatingActionButton(
+                onClick = { /* do something */ },
+                expanded = expandedFab,
+                icon = { Icon(Icons.Filled.Add, "Localized Description") },
+                text = { Text(text = "Small Extended FAB") },
+            )
+        },
+        floatingActionButtonPosition = FabPosition.End,
+    ) {
+        LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) {
+            for (index in 0 until 100) {
+                item { Text(text = "List item - $index", modifier = Modifier.padding(24.dp)) }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun MediumAnimatedExtendedFloatingActionButtonSample() {
+    val listState = rememberLazyListState()
+    // The FAB is initially expanded. Once the first visible item is past the first item we
+    // collapse the FAB. We use a remembered derived state to minimize unnecessary compositions.
+    val expandedFab by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
+    Scaffold(
+        floatingActionButton = {
+            MediumExtendedFloatingActionButton(
+                onClick = { /* do something */ },
+                expanded = expandedFab,
+                icon = {
+                    Icon(
+                        Icons.Filled.Add,
+                        "Localized Description",
+                        modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize)
+                    )
+                },
+                text = { Text(text = "Medium Extended FAB") },
+            )
+        },
+        floatingActionButtonPosition = FabPosition.End,
+    ) {
+        LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) {
+            for (index in 0 until 100) {
+                item { Text(text = "List item - $index", modifier = Modifier.padding(24.dp)) }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun LargeAnimatedExtendedFloatingActionButtonSample() {
+    val listState = rememberLazyListState()
+    // The FAB is initially expanded. Once the first visible item is past the first item we
+    // collapse the FAB. We use a remembered derived state to minimize unnecessary compositions.
+    val expandedFab by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
+    Scaffold(
+        floatingActionButton = {
+            LargeExtendedFloatingActionButton(
+                onClick = { /* do something */ },
+                expanded = expandedFab,
+                icon = {
+                    Icon(
+                        Icons.Filled.Add,
+                        "Localized Description",
+                        modifier = Modifier.size(FloatingActionButtonDefaults.LargeIconSize)
+                    )
+                },
+                text = { Text(text = "Large Extended FAB") },
+            )
+        },
+        floatingActionButtonPosition = FabPosition.End,
+    ) {
+        LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) {
+            for (index in 0 until 100) {
+                item { Text(text = "List item - $index", modifier = Modifier.padding(24.dp)) }
+            }
+        }
+    }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingAppBarSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingAppBarSamples.kt
index c84b3b5..99bb66c 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingAppBarSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingAppBarSamples.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.material3.samples
 
+import android.content.Context
+import android.view.accessibility.AccessibilityManager
 import androidx.annotation.Sampled
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -57,15 +59,21 @@
 import androidx.compose.ui.Alignment.Companion.CenterEnd
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.unit.dp
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Sampled
 @Composable
 fun HorizontalFloatingAppBar() {
+    val context = LocalContext.current
+    val isTouchExplorationEnabled = remember {
+        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        am.isEnabled && am.isTouchExplorationEnabled
+    }
     val listState = rememberLazyListState()
     var currentItem = 0
-    val expanded = remember {
+    val expanded by remember {
         derivedStateOf {
             val temp = currentItem
             currentItem = listState.firstVisibleItemIndex
@@ -95,10 +103,10 @@
                 }
                 HorizontalFloatingAppBar(
                     modifier = Modifier.align(BottomCenter).offset(y = -ScreenOffset),
-                    expanded = expanded.value,
-                    trailingContent = { trailingContent() },
-                    leadingContent = {
-                        leadingContent()
+                    expanded = expanded || isTouchExplorationEnabled,
+                    leadingContent = { leadingContent() },
+                    trailingContent = {
+                        trailingContent()
                         FilledIconToggleButton(
                             checked = anchored,
                             onCheckedChange = { anchored = it }
@@ -117,7 +125,9 @@
                             )
                         }
                     },
-                    scrollBehavior = if (!anchored) exitAlwaysScrollBehavior else null
+                    scrollBehavior =
+                        if (!anchored && !isTouchExplorationEnabled) exitAlwaysScrollBehavior
+                        else null,
                 )
             }
         }
@@ -128,9 +138,14 @@
 @Sampled
 @Composable
 fun VerticalFloatingAppBar() {
+    val context = LocalContext.current
+    val isTouchExplorationEnabled = remember {
+        val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        am.isEnabled && am.isTouchExplorationEnabled
+    }
     val listState = rememberLazyListState()
     var currentItem = 0
-    val expanded = remember {
+    val expanded by remember {
         derivedStateOf {
             val temp = currentItem
             currentItem = listState.firstVisibleItemIndex
@@ -159,10 +174,10 @@
                 }
                 VerticalFloatingAppBar(
                     modifier = Modifier.align(CenterEnd).offset(x = -ScreenOffset),
-                    expanded = expanded.value,
-                    trailingContent = { trailingContent() },
-                    leadingContent = {
-                        leadingContent()
+                    expanded = expanded || isTouchExplorationEnabled,
+                    leadingContent = { leadingContent() },
+                    trailingContent = {
+                        trailingContent()
                         FilledIconToggleButton(
                             checked = anchored,
                             onCheckedChange = { anchored = it }
@@ -181,7 +196,9 @@
                             )
                         }
                     },
-                    scrollBehavior = if (!anchored) exitAlwaysScrollBehavior else null
+                    scrollBehavior =
+                        if (!anchored && !isTouchExplorationEnabled) exitAlwaysScrollBehavior
+                        else null,
                 )
             }
         }
@@ -189,7 +206,7 @@
 }
 
 @Composable
-private fun trailingContent() {
+private fun leadingContent() {
     IconButton(onClick = { /* doSomething() */ }) {
         Icon(Icons.Filled.Check, contentDescription = "Localized description")
     }
@@ -202,7 +219,7 @@
 }
 
 @Composable
-private fun leadingContent() {
+private fun trailingContent() {
     IconButton(onClick = { /* doSomething() */ }) {
         Icon(Icons.Filled.Add, contentDescription = "Localized description")
     }
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/IconButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/IconButtonSamples.kt
index 7613a08..fac8536 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/IconButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/IconButtonSamples.kt
@@ -97,7 +97,7 @@
         Icon(
             rememberVectorPainter(image = Icons.Outlined.Lock),
             contentDescription = "Localized description",
-            tint = { Color.Red }
+            tint = Color.Red
         )
     }
 }
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/InteractiveComponentSizeSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/InteractiveComponentSizeSamples.kt
new file mode 100644
index 0000000..55fea9b
--- /dev/null
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/InteractiveComponentSizeSamples.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.Text
+import androidx.compose.material3.minimumInteractiveComponentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+@Preview
+@Sampled
+@Composable
+fun MinimumInteractiveComponentSizeSample() {
+    @Composable
+    fun Widget(color: Color, modifier: Modifier = Modifier) {
+        // Default size is 24.dp, which is smaller than the recommended touch target
+        Box(modifier.size(24.dp).background(color))
+    }
+
+    Column(Modifier.border(1.dp, Color.Black)) {
+        // Not interactable, no need for touch target enforcement
+        Widget(Color.Red)
+
+        Widget(
+            color = Color.Green,
+            modifier =
+                Modifier.clickable { /* do something */ }
+                    // Component is now interactable, so it should enforce a sufficient touch target
+                    .minimumInteractiveComponentSize()
+        )
+
+        Widget(
+            color = Color.Blue,
+            modifier =
+                Modifier.clickable { /* do something */ }
+                    // Component is now interactable, so it should enforce a sufficient touch target
+                    .minimumInteractiveComponentSize()
+                    // Any size modifiers should come after `minimumInteractiveComponentSize`
+                    // so as not to interfere with layout expansion
+                    .size(36.dp)
+        )
+    }
+}
+
+@Preview
+@Sampled
+@Composable
+fun MinimumInteractiveComponentSizeCheckboxRowSample() {
+    var checked by remember { mutableStateOf(false) }
+
+    // The entire row accepts interactions to toggle the checkbox,
+    // so we apply `minimumInteractiveComponentSize`
+    Row(
+        verticalAlignment = Alignment.CenterVertically,
+        modifier =
+            Modifier.toggleable(
+                    value = checked,
+                    onValueChange = { checked = it },
+                    role = Role.Checkbox,
+                )
+                .minimumInteractiveComponentSize()
+    ) {
+        // Cannot rely on Checkbox for touch target expansion because it only enforces
+        // `minimumInteractiveComponentSize` if onCheckedChange is non-null
+        Checkbox(checked = checked, onCheckedChange = null)
+        Spacer(Modifier.width(8.dp))
+        Text("Label for checkbox")
+    }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/LoadingIndicatorSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/LoadingIndicatorSamples.kt
index 93040ab..69e63e6 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/LoadingIndicatorSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/LoadingIndicatorSamples.kt
@@ -31,6 +31,7 @@
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Refresh
+import androidx.compose.material3.ContainedLoadingIndicator
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
@@ -71,6 +72,14 @@
 @Preview
 @Sampled
 @Composable
+fun ContainedLoadingIndicatorSample() {
+    Column(horizontalAlignment = Alignment.CenterHorizontally) { ContainedLoadingIndicator() }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
 fun DeterminateLoadingIndicatorSample() {
     var progress by remember { mutableFloatStateOf(0f) }
     val animatedProgress by
@@ -97,6 +106,36 @@
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun DeterminateContainedLoadingIndicatorSample() {
+    var progress by remember { mutableFloatStateOf(0f) }
+    val animatedProgress by
+        animateFloatAsState(
+            targetValue = progress,
+            animationSpec =
+                spring(
+                    dampingRatio = Spring.DampingRatioNoBouncy,
+                    stiffness = Spring.StiffnessVeryLow,
+                    visibilityThreshold = 1 / 1000f
+                )
+        )
+
+    Column(horizontalAlignment = Alignment.CenterHorizontally) {
+        ContainedLoadingIndicator(progress = { animatedProgress })
+        Spacer(Modifier.requiredHeight(30.dp))
+        Text("Set loading progress:")
+        Slider(
+            modifier = Modifier.width(300.dp),
+            value = progress,
+            valueRange = 0f..1f,
+            onValueChange = { progress = it },
+        )
+    }
+}
+
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Sampled
 @Composable
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt
index 67f02f2..64043cc 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationBarSamples.kt
@@ -63,7 +63,7 @@
                 icon = {
                     Icon(
                         if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
-                        contentDescription = item
+                        contentDescription = null
                     )
                 },
                 label = { Text(item) },
@@ -101,7 +101,7 @@
                         Icon(
                             if (selectedItem == index) selectedIcons[index]
                             else unselectedIcons[index],
-                            contentDescription = item
+                            contentDescription = null
                         )
                     },
                     label = { Text(item) },
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationRailSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationRailSamples.kt
index f0f9b03..c717d82 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationRailSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/NavigationRailSamples.kt
@@ -17,22 +17,202 @@
 package androidx.compose.material3.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.material.icons.filled.Home
-import androidx.compose.material.icons.filled.Search
-import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material.icons.filled.Menu
+import androidx.compose.material.icons.filled.Star
+import androidx.compose.material.icons.outlined.FavoriteBorder
+import androidx.compose.material.icons.outlined.Home
+import androidx.compose.material.icons.outlined.StarBorder
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
 import androidx.compose.material3.NavigationRail
+import androidx.compose.material3.NavigationRailArrangement
 import androidx.compose.material3.NavigationRailItem
 import androidx.compose.material3.Text
+import androidx.compose.material3.WideNavigationRail
+import androidx.compose.material3.WideNavigationRailItem
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun WideNavigationRailResponsiveSample() {
+    var selectedItem by remember { mutableIntStateOf(0) }
+    val items = listOf("Home", "Search", "Settings")
+    val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
+    val unselectedIcons =
+        listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
+    var expanded by remember { mutableStateOf(false) }
+
+    Row(Modifier.fillMaxWidth()) {
+        WideNavigationRail(
+            expanded = expanded,
+            header = {
+                IconButton(
+                    modifier = Modifier.padding(start = 24.dp),
+                    onClick = { expanded = !expanded }
+                ) {
+                    Icon(Icons.Filled.Menu, "Header button")
+                }
+            }
+        ) {
+            items.forEachIndexed { index, item ->
+                WideNavigationRailItem(
+                    railExpanded = expanded,
+                    icon = {
+                        Icon(
+                            if (selectedItem == index) selectedIcons[index]
+                            else unselectedIcons[index],
+                            contentDescription = null
+                        )
+                    },
+                    label = { Text(item) },
+                    selected = selectedItem == index,
+                    onClick = { selectedItem = index }
+                )
+            }
+        }
+
+        val textString = if (expanded) "expanded" else "collapsed"
+        Text(modifier = Modifier.padding(16.dp), text = "The rail is $textString.")
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun WideNavigationRailCollapsedSample() {
+    var selectedItem by remember { mutableIntStateOf(0) }
+    val items = listOf("Home", "Search", "Settings")
+    val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
+    val unselectedIcons =
+        listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
+    WideNavigationRail {
+        items.forEachIndexed { index, item ->
+            WideNavigationRailItem(
+                icon = {
+                    Icon(
+                        if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
+                        contentDescription = null
+                    )
+                },
+                label = { Text(item) },
+                selected = selectedItem == index,
+                onClick = { selectedItem = index }
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun WideNavigationRailExpandedSample() {
+    var selectedItem by remember { mutableIntStateOf(0) }
+    val items = listOf("Home", "Search", "Settings")
+    val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
+    val unselectedIcons =
+        listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
+    WideNavigationRail(expanded = true) {
+        items.forEachIndexed { index, item ->
+            WideNavigationRailItem(
+                railExpanded = true,
+                icon = {
+                    Icon(
+                        if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
+                        contentDescription = null
+                    )
+                },
+                label = { Text(item) },
+                selected = selectedItem == index,
+                onClick = { selectedItem = index }
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Composable
+fun WideNavigationRailArrangementsSample() {
+    var selectedItem by remember { mutableIntStateOf(0) }
+    val items = listOf("Home", "Search", "Settings")
+    val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
+    val unselectedIcons =
+        listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
+    var expanded by remember { mutableStateOf(false) }
+    var arrangement by remember { mutableStateOf(NavigationRailArrangement.Center) }
+
+    Row(Modifier.fillMaxWidth()) {
+        WideNavigationRail(
+            expanded = expanded,
+            arrangement = arrangement,
+            header = {
+                IconButton(
+                    modifier = Modifier.padding(start = 24.dp),
+                    onClick = { expanded = !expanded }
+                ) {
+                    Icon(Icons.Filled.Menu, "Header button")
+                }
+            }
+        ) {
+            items.forEachIndexed { index, item ->
+                WideNavigationRailItem(
+                    railExpanded = expanded,
+                    icon = {
+                        Icon(
+                            if (selectedItem == index) selectedIcons[index]
+                            else unselectedIcons[index],
+                            contentDescription = null
+                        )
+                    },
+                    label = { Text(item) },
+                    selected = selectedItem == index,
+                    onClick = { selectedItem = index }
+                )
+            }
+        }
+
+        val isArrangementCenter = arrangement == NavigationRailArrangement.Center
+        val changeToString = if (isArrangementCenter) "Bottom" else "Center"
+        Column(modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally) {
+            Text(modifier = Modifier.padding(16.dp), text = "Change arrangement to:")
+            Button(
+                modifier = Modifier.padding(4.dp),
+                onClick = {
+                    if (isArrangementCenter) {
+                        arrangement = NavigationRailArrangement.Bottom
+                    } else {
+                        arrangement = NavigationRailArrangement.Center
+                    }
+                }
+            ) {
+                Text(changeToString)
+            }
+        }
+    }
+}
 
 @Preview
 @Sampled
@@ -40,11 +220,18 @@
 fun NavigationRailSample() {
     var selectedItem by remember { mutableIntStateOf(0) }
     val items = listOf("Home", "Search", "Settings")
-    val icons = listOf(Icons.Filled.Home, Icons.Filled.Search, Icons.Filled.Settings)
+    val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
+    val unselectedIcons =
+        listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
     NavigationRail {
         items.forEachIndexed { index, item ->
             NavigationRailItem(
-                icon = { Icon(icons[index], contentDescription = item) },
+                icon = {
+                    Icon(
+                        if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
+                        contentDescription = item
+                    )
+                },
                 label = { Text(item) },
                 selected = selectedItem == index,
                 onClick = { selectedItem = index }
@@ -57,11 +244,18 @@
 fun NavigationRailWithOnlySelectedLabelsSample() {
     var selectedItem by remember { mutableIntStateOf(0) }
     val items = listOf("Home", "Search", "Settings")
-    val icons = listOf(Icons.Filled.Home, Icons.Filled.Search, Icons.Filled.Settings)
+    val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
+    val unselectedIcons =
+        listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
     NavigationRail {
         items.forEachIndexed { index, item ->
             NavigationRailItem(
-                icon = { Icon(icons[index], contentDescription = item) },
+                icon = {
+                    Icon(
+                        if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
+                        contentDescription = item
+                    )
+                },
                 label = { Text(item) },
                 selected = selectedItem == index,
                 onClick = { selectedItem = index },
@@ -75,14 +269,21 @@
 fun NavigationRailBottomAlignSample() {
     var selectedItem by remember { mutableIntStateOf(0) }
     val items = listOf("Home", "Search", "Settings")
-    val icons = listOf(Icons.Filled.Home, Icons.Filled.Search, Icons.Filled.Settings)
+    val selectedIcons = listOf(Icons.Filled.Home, Icons.Filled.Favorite, Icons.Filled.Star)
+    val unselectedIcons =
+        listOf(Icons.Outlined.Home, Icons.Outlined.FavoriteBorder, Icons.Outlined.StarBorder)
 
     NavigationRail {
         // A Spacer that pushes the NavigationRail items to the bottom of the NavigationRail.
         Spacer(Modifier.weight(1f))
         items.forEachIndexed { index, item ->
             NavigationRailItem(
-                icon = { Icon(icons[index], contentDescription = item) },
+                icon = {
+                    Icon(
+                        if (selectedItem == index) selectedIcons[index] else unselectedIcons[index],
+                        contentDescription = item
+                    )
+                },
                 label = { Text(item) },
                 selected = selectedItem == index,
                 onClick = { selectedItem = index },
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
index d175861..6680a0f 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
@@ -20,9 +20,7 @@
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Edit
@@ -47,7 +45,6 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Sampled
@@ -59,11 +56,11 @@
     SplitButton(
         leadingButton = {
             SplitButtonDefaults.LeadingButton(
-                modifier = Modifier.height(48.dp),
                 onClick = { /* Do Nothing */ },
             ) {
                 Icon(
                     Icons.Outlined.Edit,
+                    modifier = Modifier.size(20.dp),
                     contentDescription = "Localized description",
                 )
                 Spacer(Modifier.size(ButtonDefaults.IconSpacing))
@@ -72,7 +69,7 @@
         },
         trailingButton = {
             SplitButtonDefaults.AnimatedTrailingButton(
-                modifier = Modifier.size(48.dp),
+                modifier = Modifier.fillMaxHeight(),
                 onClick = { expanded = !expanded },
                 expanded = expanded,
             ) {
@@ -83,7 +80,7 @@
                     )
                 Icon(
                     Icons.Outlined.KeyboardArrowDown,
-                    modifier = Modifier.graphicsLayer { this.rotationZ = rotation },
+                    modifier = Modifier.size(20.dp).graphicsLayer { this.rotationZ = rotation },
                     contentDescription = "Localized description"
                 )
             }
@@ -105,11 +102,11 @@
         leadingContent = {
             Icon(
                 Icons.Outlined.Edit,
-                contentDescription = "Localized description",
-                modifier = Modifier.size(28.dp)
+                modifier = Modifier.size(20.dp),
+                contentDescription = "Localized description"
             )
             Spacer(Modifier.size(ButtonDefaults.IconSpacing))
-            Text("My Button", fontSize = 18.sp)
+            Text("My Button")
         },
         trailingContent = {
             val rotation: Float by
@@ -117,13 +114,10 @@
                     targetValue = if (expanded) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
-            Box(
-                modifier = Modifier.fillMaxHeight().aspectRatio(1f),
-                contentAlignment = Alignment.Center
-            ) {
+            Box(modifier = Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
                 Icon(
                     Icons.Outlined.KeyboardArrowDown,
-                    modifier = Modifier.size(38.dp).graphicsLayer { this.rotationZ = rotation },
+                    modifier = Modifier.size(20.dp).graphicsLayer { this.rotationZ = rotation },
                     contentDescription = "Localized description"
                 )
             }
@@ -143,7 +137,11 @@
         expanded = expanded,
         onTrailingButtonClick = { expanded = !expanded },
         leadingContent = {
-            Icon(Icons.Outlined.Edit, contentDescription = "Localized description")
+            Icon(
+                Icons.Outlined.Edit,
+                modifier = Modifier.size(20.dp),
+                contentDescription = "Localized description"
+            )
             Spacer(Modifier.size(ButtonDefaults.IconSpacing))
             Text("My Button")
         },
@@ -153,10 +151,10 @@
                     targetValue = if (expanded) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
-            Box(modifier = Modifier.size(44.dp), contentAlignment = Alignment.Center) {
+            Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
                 Icon(
                     Icons.Outlined.KeyboardArrowDown,
-                    modifier = Modifier.graphicsLayer { this.rotationZ = rotation },
+                    modifier = Modifier.size(20.dp).graphicsLayer { this.rotationZ = rotation },
                     contentDescription = "Localized description"
                 )
             }
@@ -176,7 +174,11 @@
         expanded = expanded,
         onTrailingButtonClick = { expanded = !expanded },
         leadingContent = {
-            Icon(Icons.Outlined.Edit, contentDescription = "Localized description")
+            Icon(
+                Icons.Outlined.Edit,
+                modifier = Modifier.size(20.dp),
+                contentDescription = "Localized description"
+            )
             Spacer(Modifier.size(ButtonDefaults.IconSpacing))
             Text("My Button")
         },
@@ -186,10 +188,10 @@
                     targetValue = if (expanded) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
-            Box(modifier = Modifier.size(44.dp), contentAlignment = Alignment.Center) {
+            Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
                 Icon(
                     Icons.Outlined.KeyboardArrowDown,
-                    modifier = Modifier.graphicsLayer { this.rotationZ = rotation },
+                    modifier = Modifier.size(20.dp).graphicsLayer { this.rotationZ = rotation },
                     contentDescription = "Localized description"
                 )
             }
@@ -209,7 +211,11 @@
         expanded = expanded,
         onTrailingButtonClick = { expanded = !expanded },
         leadingContent = {
-            Icon(Icons.Outlined.Edit, contentDescription = "Localized description")
+            Icon(
+                Icons.Outlined.Edit,
+                modifier = Modifier.size(20.dp),
+                contentDescription = "Localized description"
+            )
             Spacer(Modifier.size(ButtonDefaults.IconSpacing))
             Text("My Button")
         },
@@ -219,10 +225,10 @@
                     targetValue = if (expanded) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
-            Box(modifier = Modifier.size(44.dp), contentAlignment = Alignment.Center) {
+            Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
                 Icon(
                     Icons.Outlined.KeyboardArrowDown,
-                    modifier = Modifier.graphicsLayer { this.rotationZ = rotation },
+                    modifier = Modifier.size(20.dp).graphicsLayer { this.rotationZ = rotation },
                     contentDescription = "Localized description"
                 )
             }
@@ -249,7 +255,7 @@
             SplitButtonDefaults.AnimatedTrailingButton(
                 onClick = { expanded = !expanded },
                 expanded = expanded,
-                modifier = Modifier.size(40.dp)
+                modifier = Modifier.fillMaxHeight()
             ) {
                 val rotation: Float by
                     animateFloatAsState(
@@ -258,7 +264,7 @@
                     )
                 Icon(
                     Icons.Outlined.KeyboardArrowDown,
-                    modifier = Modifier.graphicsLayer { this.rotationZ = rotation },
+                    modifier = Modifier.size(20.dp).graphicsLayer { this.rotationZ = rotation },
                     contentDescription = "Localized description"
                 )
             }
@@ -281,6 +287,7 @@
                 Icon(
                     Icons.Outlined.Edit,
                     contentDescription = "Localized description",
+                    Modifier.size(20.dp)
                 )
             }
         },
@@ -288,7 +295,7 @@
             SplitButtonDefaults.AnimatedTrailingButton(
                 onClick = { expanded = !expanded },
                 expanded = expanded,
-                modifier = Modifier.size(44.dp)
+                modifier = Modifier.fillMaxHeight()
             ) {
                 val rotation: Float by
                     animateFloatAsState(
@@ -297,7 +304,7 @@
                     )
                 Icon(
                     Icons.Outlined.KeyboardArrowDown,
-                    modifier = Modifier.graphicsLayer { this.rotationZ = rotation },
+                    modifier = Modifier.size(20.dp).graphicsLayer { this.rotationZ = rotation },
                     contentDescription = "Localized description"
                 )
             }
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
index b78589e..1b4dca7 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
@@ -20,59 +20,69 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.clearText
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.maxLength
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.input.then
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Clear
 import androidx.compose.material.icons.filled.Favorite
-import androidx.compose.material.icons.filled.Info
-import androidx.compose.material.icons.materialIcon
-import androidx.compose.material.icons.materialPath
+import androidx.compose.material.icons.filled.Visibility
+import androidx.compose.material.icons.filled.VisibilityOff
+import androidx.compose.material3.Checkbox
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
+import androidx.compose.material3.LocalTextStyle
 import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.OutlinedTextFieldDefaults
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextField
 import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.material3.TextFieldLabelPosition
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.LocalSoftwareKeyboardController
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.error
+import androidx.compose.ui.semantics.maxTextLength
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
+import androidx.core.text.isDigitsOnly
 
 @Preview
 @Sampled
 @Composable
 fun SimpleTextFieldSample() {
-    var text by rememberSaveable { mutableStateOf("") }
-
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = rememberTextFieldState(),
+        lineLimits = TextFieldLineLimits.SingleLine,
         label = { Text("Label") },
-        singleLine = true
     )
 }
 
@@ -80,23 +90,54 @@
 @Sampled
 @Composable
 fun SimpleOutlinedTextFieldSample() {
-    var text by rememberSaveable { mutableStateOf("") }
+    OutlinedTextField(
+        state = rememberTextFieldState(),
+        lineLimits = TextFieldLineLimits.SingleLine,
+        label = { Text("Label") },
+    )
+}
 
-    OutlinedTextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+@Preview
+@Sampled
+@Composable
+fun TextFieldWithTransformations() {
+    TextField(
+        state = rememberTextFieldState(),
+        lineLimits = TextFieldLineLimits.SingleLine,
+        label = { Text("Phone number") },
+        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
+        // Input transformation to limit user input to 10 digits
+        inputTransformation =
+            InputTransformation.maxLength(10).then {
+                if (!this.asCharSequence().isDigitsOnly()) {
+                    revertAllChanges()
+                }
+            },
+        outputTransformation = {
+            // Output transformation to format as a phone number: (XXX) XXX-XXXX
+            if (length > 0) insert(0, "(")
+            if (length > 4) insert(4, ") ")
+            if (length > 9) insert(9, "-")
+        },
+    )
 }
 
 @Preview
 @Sampled
 @Composable
 fun TextFieldWithIcons() {
-    var text by rememberSaveable { mutableStateOf("") }
+    val state = rememberTextFieldState()
 
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = state,
+        lineLimits = TextFieldLineLimits.SingleLine,
         label = { Text("Label") },
-        leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = "Localized description") },
-        trailingIcon = { Icon(Icons.Filled.Info, contentDescription = "Localized description") }
+        leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
+        trailingIcon = {
+            IconButton(onClick = { state.clearText() }) {
+                Icon(Icons.Filled.Clear, contentDescription = "Clear text")
+            }
+        }
     )
 }
 
@@ -104,31 +145,44 @@
 @Sampled
 @Composable
 fun TextFieldWithPlaceholder() {
-    var text by rememberSaveable { mutableStateOf("") }
-
-    TextField(
-        value = text,
-        onValueChange = { text = it },
-        label = { Text("Email") },
-        placeholder = { Text("[email protected]") }
-    )
+    var alwaysMinimizeLabel by remember { mutableStateOf(false) }
+    Column {
+        Row {
+            Checkbox(checked = alwaysMinimizeLabel, onCheckedChange = { alwaysMinimizeLabel = it })
+            Text("Show placeholder even when unfocused")
+        }
+        Spacer(Modifier.height(16.dp))
+        TextField(
+            state = rememberTextFieldState(),
+            lineLimits = TextFieldLineLimits.SingleLine,
+            label = { Text("Email") },
+            labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = alwaysMinimizeLabel),
+            placeholder = { Text("[email protected]") }
+        )
+    }
 }
 
 @Preview
 @Sampled
 @Composable
 fun TextFieldWithPrefixAndSuffix() {
-    var text by rememberSaveable { mutableStateOf("") }
-
-    TextField(
-        value = text,
-        onValueChange = { text = it },
-        singleLine = true,
-        label = { Text("Label") },
-        prefix = { Text("www.") },
-        suffix = { Text(".com") },
-        placeholder = { Text("google") },
-    )
+    var alwaysMinimizeLabel by remember { mutableStateOf(false) }
+    Column {
+        Row {
+            Checkbox(checked = alwaysMinimizeLabel, onCheckedChange = { alwaysMinimizeLabel = it })
+            Text("Show placeholder even when unfocused")
+        }
+        Spacer(Modifier.height(16.dp))
+        TextField(
+            state = rememberTextFieldState(),
+            lineLimits = TextFieldLineLimits.SingleLine,
+            label = { Text("Label") },
+            labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = alwaysMinimizeLabel),
+            prefix = { Text("www.") },
+            suffix = { Text(".com") },
+            placeholder = { Text("google") },
+        )
+    }
 }
 
 @Preview
@@ -136,33 +190,34 @@
 @Composable
 fun TextFieldWithErrorState() {
     val errorMessage = "Text input too long"
-    var text by rememberSaveable { mutableStateOf("") }
+    val state = rememberTextFieldState()
     var isError by rememberSaveable { mutableStateOf(false) }
     val charLimit = 10
 
-    fun validate(text: String) {
+    fun validate(text: CharSequence) {
         isError = text.length > charLimit
     }
 
+    LaunchedEffect(Unit) {
+        // Run validation whenever text value changes
+        snapshotFlow { state.text }.collect { validate(it) }
+    }
     TextField(
-        value = text,
-        onValueChange = {
-            text = it
-            validate(text)
-        },
-        singleLine = true,
+        state = state,
+        lineLimits = TextFieldLineLimits.SingleLine,
         label = { Text(if (isError) "Username*" else "Username") },
         supportingText = {
             Row {
                 Text(if (isError) errorMessage else "", Modifier.clearAndSetSemantics {})
                 Spacer(Modifier.weight(1f))
-                Text("Limit: ${text.length}/$charLimit")
+                Text("Limit: ${state.text.length}/$charLimit")
             }
         },
         isError = isError,
-        keyboardActions = KeyboardActions { validate(text) },
+        onKeyboardAction = { validate(state.text) },
         modifier =
             Modifier.semantics {
+                maxTextLength = charLimit
                 // Provide localized description of the error
                 if (isError) error(errorMessage)
             }
@@ -173,11 +228,9 @@
 @Sampled
 @Composable
 fun TextFieldWithSupportingText() {
-    var text by rememberSaveable { mutableStateOf("") }
-
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = rememberTextFieldState(),
+        lineLimits = TextFieldLineLimits.SingleLine,
         label = { Text("Label") },
         supportingText = {
             Text("Supporting text that is long and perhaps goes onto another line.")
@@ -185,8 +238,8 @@
     )
 }
 
+// TODO: update sample with TextFieldState once we have wrappers for BasicSecureTextField
 @Preview
-@Sampled
 @Composable
 fun PasswordTextField() {
     var password by rememberSaveable { mutableStateOf("") }
@@ -211,117 +264,42 @@
     )
 }
 
-/**
- * We copy the implementation of Visibility and VisibilityOff icons to showcase them in the password
- * text field sample but to avoid adding material-icons-extended library as a dependency to the
- * samples not to increase the build time
- */
-private val Icons.Filled.Visibility: ImageVector
-    get() {
-        if (_visibility != null) {
-            return _visibility!!
-        }
-        _visibility =
-            materialIcon(name = "Filled.Visibility") {
-                materialPath {
-                    moveTo(12.0f, 4.5f)
-                    curveTo(7.0f, 4.5f, 2.73f, 7.61f, 1.0f, 12.0f)
-                    curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f)
-                    reflectiveCurveToRelative(9.27f, -3.11f, 11.0f, -7.5f)
-                    curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f)
-                    close()
-                    moveTo(12.0f, 17.0f)
-                    curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f)
-                    reflectiveCurveToRelative(2.24f, -5.0f, 5.0f, -5.0f)
-                    reflectiveCurveToRelative(5.0f, 2.24f, 5.0f, 5.0f)
-                    reflectiveCurveToRelative(-2.24f, 5.0f, -5.0f, 5.0f)
-                    close()
-                    moveTo(12.0f, 9.0f)
-                    curveToRelative(-1.66f, 0.0f, -3.0f, 1.34f, -3.0f, 3.0f)
-                    reflectiveCurveToRelative(1.34f, 3.0f, 3.0f, 3.0f)
-                    reflectiveCurveToRelative(3.0f, -1.34f, 3.0f, -3.0f)
-                    reflectiveCurveToRelative(-1.34f, -3.0f, -3.0f, -3.0f)
-                    close()
-                }
-            }
-        return _visibility!!
-    }
-private var _visibility: ImageVector? = null
-
-private val Icons.Filled.VisibilityOff: ImageVector
-    get() {
-        if (_visibilityOff != null) {
-            return _visibilityOff!!
-        }
-        _visibilityOff =
-            materialIcon(name = "Filled.VisibilityOff") {
-                materialPath {
-                    moveTo(12.0f, 7.0f)
-                    curveToRelative(2.76f, 0.0f, 5.0f, 2.24f, 5.0f, 5.0f)
-                    curveToRelative(0.0f, 0.65f, -0.13f, 1.26f, -0.36f, 1.83f)
-                    lineToRelative(2.92f, 2.92f)
-                    curveToRelative(1.51f, -1.26f, 2.7f, -2.89f, 3.43f, -4.75f)
-                    curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f)
-                    curveToRelative(-1.4f, 0.0f, -2.74f, 0.25f, -3.98f, 0.7f)
-                    lineToRelative(2.16f, 2.16f)
-                    curveTo(10.74f, 7.13f, 11.35f, 7.0f, 12.0f, 7.0f)
-                    close()
-                    moveTo(2.0f, 4.27f)
-                    lineToRelative(2.28f, 2.28f)
-                    lineToRelative(0.46f, 0.46f)
-                    curveTo(3.08f, 8.3f, 1.78f, 10.02f, 1.0f, 12.0f)
-                    curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f)
-                    curveToRelative(1.55f, 0.0f, 3.03f, -0.3f, 4.38f, -0.84f)
-                    lineToRelative(0.42f, 0.42f)
-                    lineTo(19.73f, 22.0f)
-                    lineTo(21.0f, 20.73f)
-                    lineTo(3.27f, 3.0f)
-                    lineTo(2.0f, 4.27f)
-                    close()
-                    moveTo(7.53f, 9.8f)
-                    lineToRelative(1.55f, 1.55f)
-                    curveToRelative(-0.05f, 0.21f, -0.08f, 0.43f, -0.08f, 0.65f)
-                    curveToRelative(0.0f, 1.66f, 1.34f, 3.0f, 3.0f, 3.0f)
-                    curveToRelative(0.22f, 0.0f, 0.44f, -0.03f, 0.65f, -0.08f)
-                    lineToRelative(1.55f, 1.55f)
-                    curveToRelative(-0.67f, 0.33f, -1.41f, 0.53f, -2.2f, 0.53f)
-                    curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f)
-                    curveToRelative(0.0f, -0.79f, 0.2f, -1.53f, 0.53f, -2.2f)
-                    close()
-                    moveTo(11.84f, 9.02f)
-                    lineToRelative(3.15f, 3.15f)
-                    lineToRelative(0.02f, -0.16f)
-                    curveToRelative(0.0f, -1.66f, -1.34f, -3.0f, -3.0f, -3.0f)
-                    lineToRelative(-0.17f, 0.01f)
-                    close()
-                }
-            }
-        return _visibilityOff!!
-    }
-private var _visibilityOff: ImageVector? = null
-
 @Preview
 @Sampled
 @Composable
-fun TextFieldSample() {
-    var text by
-        rememberSaveable(stateSaver = TextFieldValue.Saver) {
-            mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
-        }
-
-    TextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+fun TextFieldWithInitialValueAndSelection() {
+    val state = rememberTextFieldState("Initial text", TextRange(0, 12))
+    TextField(
+        state = state,
+        lineLimits = TextFieldLineLimits.SingleLine,
+        label = { Text("Label") },
+    )
 }
 
 @Preview
 @Sampled
 @Composable
-fun OutlinedTextFieldSample() {
-    var text by
-        rememberSaveable(stateSaver = TextFieldValue.Saver) {
-            mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
-        }
+fun OutlinedTextFieldWithInitialValueAndSelection() {
+    val state = rememberTextFieldState("Initial text", TextRange(0, 12))
+    OutlinedTextField(
+        state = state,
+        lineLimits = TextFieldLineLimits.SingleLine,
+        label = { Text("Label") },
+    )
+}
 
-    OutlinedTextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+@Preview
+@Sampled
+@Composable
+fun DenseTextFieldContentPadding() {
+    TextField(
+        state = rememberTextFieldState(),
+        lineLimits = TextFieldLineLimits.SingleLine,
+        label = { Text("Label") },
+        // Need to set a min height using `heightIn` to override the default
+        modifier = Modifier.heightIn(min = 48.dp),
+        contentPadding = PaddingValues(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp),
+    )
 }
 
 @Preview
@@ -329,34 +307,104 @@
 @Composable
 fun TextFieldWithHideKeyboardOnImeAction() {
     val keyboardController = LocalSoftwareKeyboardController.current
-    var text by rememberSaveable { mutableStateOf("") }
     TextField(
-        value = text,
-        onValueChange = { text = it },
+        state = rememberTextFieldState(),
         label = { Text("Label") },
         keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
-        keyboardActions =
-            KeyboardActions(
-                onDone = {
-                    keyboardController?.hide()
-                    // do something here
-                }
-            )
+        onKeyboardAction = { keyboardController?.hide() }
     )
 }
 
 @Composable
 fun TextArea() {
-    var text by rememberSaveable {
-        mutableStateOf(
-            "This is a very long input that extends beyond " + "the height of the text area."
+    val state =
+        rememberTextFieldState(
+            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " +
+                "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quisque " +
+                "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+                "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+                "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non  proident, sunt in " +
+                "culpa qui officia deserunt mollit anim id est laborum."
         )
-    }
-    TextField(
-        value = text,
-        onValueChange = { text = it },
-        modifier = Modifier.height(100.dp),
-        label = { Text("Label") }
+    TextField(state = state, modifier = Modifier.height(120.dp), label = { Text("Label") })
+}
+
+@Preview
+@Sampled
+@Composable
+fun CustomTextFieldUsingDecorator() {
+    val state = rememberTextFieldState()
+    val interactionSource = remember { MutableInteractionSource() }
+    val enabled = true
+    val isError = false
+    val lineLimits = TextFieldLineLimits.SingleLine
+
+    BasicTextField(
+        state = state,
+        modifier = Modifier,
+        interactionSource = interactionSource,
+        enabled = enabled,
+        lineLimits = lineLimits,
+        textStyle = LocalTextStyle.current,
+        decorator =
+            TextFieldDefaults.decorator(
+                state = state,
+                outputTransformation = null,
+                lineLimits = lineLimits,
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                container = {
+                    TextFieldDefaults.Container(
+                        enabled = enabled,
+                        isError = isError,
+                        interactionSource = interactionSource,
+                        // Update indicator line thickness
+                        unfocusedIndicatorLineThickness = 2.dp,
+                        focusedIndicatorLineThickness = 4.dp,
+                    )
+                }
+            )
+    )
+}
+
+@Preview
+@Sampled
+@Composable
+fun CustomOutlinedTextFieldUsingDecorator() {
+    val state = rememberTextFieldState()
+    val interactionSource = remember { MutableInteractionSource() }
+    val enabled = true
+    val isError = false
+    val lineLimits = TextFieldLineLimits.SingleLine
+
+    BasicTextField(
+        state = state,
+        modifier = Modifier,
+        interactionSource = interactionSource,
+        enabled = enabled,
+        lineLimits = lineLimits,
+        textStyle = LocalTextStyle.current,
+        decorator =
+            OutlinedTextFieldDefaults.decorator(
+                state = state,
+                outputTransformation = null,
+                lineLimits = lineLimits,
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                container = {
+                    OutlinedTextFieldDefaults.Container(
+                        enabled = enabled,
+                        isError = isError,
+                        interactionSource = interactionSource,
+                        // Update border thickness and shape
+                        shape = RectangleShape,
+                        unfocusedBorderThickness = 2.dp,
+                        focusedBorderThickness = 4.dp
+                    )
+                },
+            )
     )
 }
 
@@ -364,101 +412,77 @@
 @Sampled
 @Composable
 fun CustomTextFieldBasedOnDecorationBox() {
-    @OptIn(ExperimentalMaterial3Api::class)
-    @Composable
-    fun CustomTextField(
-        value: String,
-        onValueChange: (String) -> Unit,
-        modifier: Modifier = Modifier
-    ) {
-        val interactionSource = remember { MutableInteractionSource() }
-        // parameters below will be passed to BasicTextField for correct behavior of the text field,
-        // and to the decoration box for proper styling and sizing
-        val enabled = true
-        val singleLine = true
-        val passwordTransformation = PasswordVisualTransformation()
+    var text by remember { mutableStateOf("") }
+    val interactionSource = remember { MutableInteractionSource() }
+    val enabled = true
+    val isError = false
+    val singleLine = true
 
-        BasicTextField(
-            value = value,
-            onValueChange = onValueChange,
-            modifier = modifier,
-            visualTransformation = passwordTransformation,
-            // internal implementation of the BasicTextField will dispatch focus events
-            interactionSource = interactionSource,
-            enabled = enabled,
-            singleLine = singleLine
-        ) {
+    BasicTextField(
+        value = text,
+        onValueChange = { text = it },
+        modifier = Modifier,
+        interactionSource = interactionSource,
+        enabled = enabled,
+        singleLine = singleLine,
+        textStyle = LocalTextStyle.current,
+        decorationBox = { innerTextField ->
             TextFieldDefaults.DecorationBox(
-                value = value,
-                visualTransformation = passwordTransformation,
-                innerTextField = it,
+                value = text,
+                innerTextField = innerTextField,
+                visualTransformation = VisualTransformation.None,
                 singleLine = singleLine,
                 enabled = enabled,
-                // same interaction source as the one passed to BasicTextField to read focus state
-                // for text field styling
+                isError = isError,
                 interactionSource = interactionSource,
-                supportingText = { Text("Supporting text") },
-                // keep horizontal paddings but change the vertical
-                contentPadding =
-                    TextFieldDefaults.contentPaddingWithoutLabel(top = 8.dp, bottom = 8.dp),
+                container = {
+                    TextFieldDefaults.Container(
+                        enabled = enabled,
+                        isError = isError,
+                        interactionSource = interactionSource,
+                        // Update indicator line thickness
+                        unfocusedIndicatorLineThickness = 2.dp,
+                        focusedIndicatorLineThickness = 4.dp,
+                    )
+                }
             )
         }
-    }
+    )
 }
 
 @Preview
 @Sampled
 @Composable
 fun CustomOutlinedTextFieldBasedOnDecorationBox() {
-    @OptIn(ExperimentalMaterial3Api::class)
-    @Composable
-    fun CustomTextField(
-        value: String,
-        onValueChange: (String) -> Unit,
-        modifier: Modifier = Modifier
-    ) {
-        val interactionSource = remember { MutableInteractionSource() }
-        // parameters below will be passed to BasicTextField for correct behavior of the text field,
-        // and to the decoration box for proper styling and sizing
-        val enabled = true
-        val singleLine = true
+    var text by remember { mutableStateOf("") }
+    val interactionSource = remember { MutableInteractionSource() }
+    val enabled = true
+    val isError = false
+    val singleLine = true
 
-        val colors =
-            OutlinedTextFieldDefaults.colors(
-                unfocusedBorderColor = Color.LightGray,
-                focusedBorderColor = Color.DarkGray
-            )
-        BasicTextField(
-            value = value,
-            onValueChange = onValueChange,
-            modifier = modifier,
-            // internal implementation of the BasicTextField will dispatch focus events
-            interactionSource = interactionSource,
-            enabled = enabled,
-            singleLine = singleLine
-        ) {
+    BasicTextField(
+        value = text,
+        onValueChange = { text = it },
+        modifier = Modifier,
+        interactionSource = interactionSource,
+        enabled = enabled,
+        singleLine = singleLine,
+        textStyle = LocalTextStyle.current,
+        decorationBox = { innerTextField ->
             OutlinedTextFieldDefaults.DecorationBox(
-                value = value,
+                value = text,
+                innerTextField = innerTextField,
                 visualTransformation = VisualTransformation.None,
-                innerTextField = it,
                 singleLine = singleLine,
                 enabled = enabled,
-                // same interaction source as the one passed to BasicTextField to read focus state
-                // for text field styling
+                isError = isError,
                 interactionSource = interactionSource,
-                supportingText = { Text("Supporting text") },
-                // keep horizontal paddings but change the vertical
-                contentPadding =
-                    OutlinedTextFieldDefaults.contentPadding(top = 8.dp, bottom = 8.dp),
-                // update border colors
-                colors = colors,
-                // update border thickness and shape
                 container = {
                     OutlinedTextFieldDefaults.Container(
                         enabled = enabled,
-                        isError = false,
-                        colors = colors,
+                        isError = isError,
                         interactionSource = interactionSource,
+                        // Update border thickness and shape
                         shape = RectangleShape,
                         unfocusedBorderThickness = 2.dp,
                         focusedBorderThickness = 4.dp
@@ -466,5 +490,5 @@
                 },
             )
         }
-    }
+    )
 }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt
index 40dce46..1ddb902 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt
@@ -17,11 +17,18 @@
 package androidx.compose.material3
 
 import android.os.Build
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.width
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.ArrowForward
 import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Edit
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.material.icons.filled.Menu
 import androidx.compose.material3.BottomAppBarDefaults.bottomAppBarFabColor
@@ -32,7 +39,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.captureToImage
-import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.unit.dp
@@ -50,7 +57,7 @@
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 class AppBarScreenshotTest {
 
-    @get:Rule val composeTestRule = createComposeRule()
+    @get:Rule val composeTestRule = createAndroidComposeRule<ComponentActivity>()
 
     @get:Rule val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
 
@@ -647,6 +654,218 @@
         )
     }
 
+    @Test
+    fun bottomAppBarSpacedAround_lightTheme() {
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.testTag(BottomAppBarTestTag)) {
+                BottomAppBar(
+                    horizontalArrangement = Arrangement.SpaceAround,
+                    contentPadding = PaddingValues(horizontal = 0.dp),
+                    content = {
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowBack,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowForward,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        FilledIconButton(
+                            modifier = Modifier.width(56.dp),
+                            onClick = { /* doSomething() */ }
+                        ) {
+                            Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                        }
+                    }
+                )
+            }
+        }
+
+        assertAppBarAgainstGolden(
+            goldenIdentifier = "bottomAppBarSpacedAround_lightTheme",
+            testTag = BottomAppBarTestTag
+        )
+    }
+
+    @Test
+    fun bottomAppBarSpacedBetween_lightTheme() {
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.testTag(BottomAppBarTestTag)) {
+                BottomAppBar(
+                    horizontalArrangement = Arrangement.SpaceBetween,
+                    content = {
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowBack,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowForward,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        FilledIconButton(
+                            modifier = Modifier.width(56.dp),
+                            onClick = { /* doSomething() */ }
+                        ) {
+                            Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                        }
+                    }
+                )
+            }
+        }
+
+        assertAppBarAgainstGolden(
+            goldenIdentifier = "bottomAppBarSpacedBetween_lightTheme",
+            testTag = BottomAppBarTestTag
+        )
+    }
+
+    @Test
+    fun bottomAppBarSpacedEvenly_lightTheme() {
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.testTag(BottomAppBarTestTag)) {
+                BottomAppBar(
+                    horizontalArrangement = Arrangement.SpaceEvenly,
+                    contentPadding = PaddingValues(horizontal = 0.dp),
+                    content = {
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowBack,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowForward,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        FilledIconButton(
+                            modifier = Modifier.width(56.dp),
+                            onClick = { /* doSomething() */ }
+                        ) {
+                            Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                        }
+                    }
+                )
+            }
+        }
+
+        assertAppBarAgainstGolden(
+            goldenIdentifier = "bottomAppBarSpacedEvenly_lightTheme",
+            testTag = BottomAppBarTestTag
+        )
+    }
+
+    @Test
+    fun bottomAppBarFixed_lightTheme() {
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.testTag(BottomAppBarTestTag)) {
+                BottomAppBar(
+                    horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+                    content = {
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowBack,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowForward,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        FilledIconButton(
+                            modifier = Modifier.width(56.dp),
+                            onClick = { /* doSomething() */ }
+                        ) {
+                            Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                        }
+                    }
+                )
+            }
+        }
+
+        assertAppBarAgainstGolden(
+            goldenIdentifier = "bottomAppBarFixed_lightTheme",
+            testTag = BottomAppBarTestTag
+        )
+    }
+
+    @Test
+    fun bottomAppBarFixed_darkTheme() {
+        composeTestRule.setMaterialContent(darkColorScheme()) {
+            Box(Modifier.testTag(BottomAppBarTestTag)) {
+                BottomAppBar(
+                    horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+                    content = {
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowBack,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(
+                                Icons.AutoMirrored.Filled.ArrowForward,
+                                contentDescription = "Localized description"
+                            )
+                        }
+                        FilledIconButton(
+                            modifier = Modifier.width(56.dp),
+                            onClick = { /* doSomething() */ }
+                        ) {
+                            Icon(Icons.Filled.Add, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Check, contentDescription = "Localized description")
+                        }
+                        IconButton(onClick = { /* doSomething() */ }) {
+                            Icon(Icons.Filled.Edit, contentDescription = "Localized description")
+                        }
+                    }
+                )
+            }
+        }
+
+        assertAppBarAgainstGolden(
+            goldenIdentifier = "bottomAppBarFixed_darkTheme",
+            testTag = BottomAppBarTestTag
+        )
+    }
+
     private fun assertAppBarAgainstGolden(
         goldenIdentifier: String,
         testTag: String = TopAppBarTestTag
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt
index d3e034f..23a08d5 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt
@@ -1317,6 +1317,19 @@
     }
 
     @Test
+    fun bottomAppBarWithCustomArrangement_heightIsFromSpec() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                BottomAppBar(
+                    horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+                    content = {}
+                )
+            }
+            .assertHeightIsEqualTo(64.dp) // TODO tokens
+            .assertWidthIsEqualTo(rule.rootWidth())
+    }
+
+    @Test
     fun bottomAppBarWithFAB_respectsWindowInsets() {
         rule
             .setMaterialContentForSizeAssertions {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
index 624415c..61654d2 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
@@ -567,6 +567,7 @@
                     latch.countDown()
                 }
 
+                @Deprecated("deprecated")
                 override fun onLowMemory() {
                     // NO-OP
                 }
@@ -622,6 +623,7 @@
                     latch.countDown()
                 }
 
+                @Deprecated("deprecated")
                 override fun onLowMemory() {
                     // NO-OP
                 }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CardScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CardScreenshotTest.kt
index 497663d..b97ec2f 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CardScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CardScreenshotTest.kt
@@ -43,6 +43,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -259,6 +260,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun filledCard_pressed() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag), contentAlignment = Alignment.Center) {
@@ -274,6 +276,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun elevatedCard_pressed() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag), contentAlignment = Alignment.Center) {
@@ -289,6 +292,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun outlinedCard_pressed() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag), contentAlignment = Alignment.Center) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
index 637ec4b..7a2851b 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
@@ -46,6 +46,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -92,6 +93,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun checkBox_pressed() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ColorSchemeTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ColorSchemeTest.kt
index bea890f..c0ab85b 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ColorSchemeTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ColorSchemeTest.kt
@@ -28,6 +28,7 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 class ColorSchemeTest {
 
     @get:Rule val rule = createComposeRule()
@@ -77,6 +78,83 @@
         }
     }
 
+    @Test
+    fun baselineContentContrast() {
+        val expectedContrastValue = 3 // Minimum 3:1 contrast ratio
+        val baselineSchemes =
+            listOf(lightColorScheme(), darkColorScheme(), expressiveLightColorScheme())
+
+        for (colorScheme in baselineSchemes) {
+            assertThat(calculateContrastRatio(colorScheme.onPrimary, colorScheme.primary))
+                .isAtLeast(expectedContrastValue)
+            assertThat(calculateContrastRatio(colorScheme.onSecondary, colorScheme.secondary))
+                .isAtLeast(expectedContrastValue)
+            assertThat(calculateContrastRatio(colorScheme.onTertiary, colorScheme.tertiary))
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(
+                        colorScheme.onPrimaryContainer,
+                        colorScheme.primaryContainer
+                    )
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(
+                        colorScheme.onSecondaryContainer,
+                        colorScheme.secondaryContainer
+                    )
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(
+                        colorScheme.onTertiaryContainer,
+                        colorScheme.tertiaryContainer
+                    )
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(calculateContrastRatio(colorScheme.onError, colorScheme.error))
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(colorScheme.onErrorContainer, colorScheme.errorContainer)
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(calculateContrastRatio(colorScheme.onSurface, colorScheme.surface))
+                .isAtLeast(expectedContrastValue)
+            assertThat(calculateContrastRatio(colorScheme.onSurface, colorScheme.surfaceContainer))
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(colorScheme.onSurface, colorScheme.surfaceContainerHigh)
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(
+                        colorScheme.onSurface,
+                        colorScheme.surfaceContainerHighest
+                    )
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(colorScheme.onSurface, colorScheme.surfaceContainerLow)
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(
+                        colorScheme.onSurface,
+                        colorScheme.surfaceContainerLowest
+                    )
+                )
+                .isAtLeast(expectedContrastValue)
+            assertThat(calculateContrastRatio(colorScheme.onSurface, colorScheme.surfaceDim))
+                .isAtLeast(expectedContrastValue)
+            assertThat(calculateContrastRatio(colorScheme.onSurface, colorScheme.surfaceBright))
+                .isAtLeast(expectedContrastValue)
+            assertThat(
+                    calculateContrastRatio(colorScheme.inverseOnSurface, colorScheme.inverseSurface)
+                )
+                .isAtLeast(expectedContrastValue)
+        }
+    }
+
     @Composable
     private fun Button(onReadColorScheme: (ColorScheme) -> Unit) {
         val colorScheme = MaterialTheme.colorScheme
@@ -118,5 +196,14 @@
     if (errorContainer != other.errorContainer) return false
     if (onErrorContainer != other.onErrorContainer) return false
     if (outline != other.outline) return false
+    if (outlineVariant != other.outlineVariant) return false
+    if (scrim != other.scrim) return false
+    if (surfaceBright != other.surfaceBright) return false
+    if (surfaceContainer != other.surfaceContainer) return false
+    if (surfaceContainerHigh != other.surfaceContainerHigh) return false
+    if (surfaceContainerHighest != other.surfaceContainerHighest) return false
+    if (surfaceContainerLow != other.surfaceContainerLow) return false
+    if (surfaceContainerLowest != other.surfaceContainerLowest) return false
+    if (surfaceDim != other.surfaceDim) return false
     return true
 }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
index 7b1c830..6453fe4 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material3
 
+import android.view.KeyEvent
 import android.widget.FrameLayout
 import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
@@ -31,6 +32,9 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.SideEffect
@@ -147,6 +151,50 @@
 
     @Test
     fun edm_notEditable_collapsesOnBackPress() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var expanded by remember { mutableStateOf(true) }
+            ExposedDropdownMenuForTest(
+                expanded = expanded,
+                onExpandChange = { expanded = it },
+                editable = false,
+            )
+        }
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+
+        rule.waitForIdle()
+        val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+        device.pressBack()
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
+    }
+
+    @Test
+    fun edm_editable_collapsesOnBackPress() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var expanded by remember { mutableStateOf(true) }
+            ExposedDropdownMenuForTest(
+                expanded = expanded,
+                onExpandChange = { expanded = it },
+                editable = true,
+            )
+        }
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+
+        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack()
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
+    }
+
+    @Test
+    fun edm_notEditable_collapsesOnBackDispatch() {
         lateinit var backDispatcher: OnBackPressedDispatcher
         rule.setMaterialContent(lightColorScheme()) {
             backDispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
@@ -169,7 +217,7 @@
     }
 
     @Test
-    fun edm_editable_collapsesOnBackPress() {
+    fun edm_editable_collapsesOnBackDispatch() {
         lateinit var backDispatcher: OnBackPressedDispatcher
         rule.setMaterialContent(lightColorScheme()) {
             backDispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
@@ -192,6 +240,50 @@
     }
 
     @Test
+    fun edm_notEditable_collapsesOnEscapePress() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var expanded by remember { mutableStateOf(true) }
+            ExposedDropdownMenuForTest(
+                expanded = expanded,
+                onExpandChange = { expanded = it },
+                editable = false,
+            )
+        }
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+
+        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+            .pressKeyCode(KeyEvent.KEYCODE_ESCAPE)
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
+    }
+
+    @Test
+    fun edm_editable_collapsesOnEscapePress() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var expanded by remember { mutableStateOf(true) }
+            ExposedDropdownMenuForTest(
+                expanded = expanded,
+                onExpandChange = { expanded = it },
+                editable = true,
+            )
+        }
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(EDMTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
+
+        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+            .pressKeyCode(KeyEvent.KEYCODE_ESCAPE)
+
+        rule.onNodeWithTag(TFTag).assertIsDisplayed()
+        rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
+    }
+
+    @Test
     fun edm_notEditable_doesNotExpand_whenDisabled() {
         rule.setMaterialContent(lightColorScheme()) {
             var expanded by remember { mutableStateOf(false) }
@@ -314,16 +406,6 @@
             )
         }
         rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
-
-        // A swipe that ends within the bounds of the anchor should expand the menu.
-        rule.onNodeWithTag(TFTag).performTouchInput {
-            swipe(
-                start = this.center,
-                end = Offset(this.centerX, this.centerY + (textFieldBounds.height / 2) - 1),
-                durationMillis = 100
-            )
-        }
-        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
     }
 
     @Test
@@ -337,7 +419,7 @@
             ) {
                 items(50) { index ->
                     var expanded by remember { mutableStateOf(false) }
-                    var selectedOptionText by remember { mutableStateOf("") }
+                    val textFieldState = rememberTextFieldState()
 
                     ExposedDropdownMenuBox(
                         expanded = expanded,
@@ -356,8 +438,7 @@
                                             Modifier
                                         }
                                     ),
-                            value = selectedOptionText,
-                            onValueChange = { selectedOptionText = it },
+                            state = textFieldState,
                             label = { Text("Label") },
                             trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) },
                             colors = ExposedDropdownMenuDefaults.textFieldColors()
@@ -375,7 +456,7 @@
                             DropdownMenuItem(
                                 text = { Text(OptionName) },
                                 onClick = {
-                                    selectedOptionText = OptionName
+                                    textFieldState.setTextAndPlaceCursorAtEnd(OptionName)
                                     expanded = false
                                 },
                                 modifier =
@@ -404,16 +485,6 @@
             )
         }
         rule.onNodeWithTag(MenuItemTag).assertDoesNotExist()
-
-        // But a swipe that does not cause a scroll should expand the menu.
-        rule.onNodeWithTag(TFTag).performTouchInput {
-            swipe(
-                start = this.center,
-                end = Offset(this.centerX + (textFieldSize.width / 2) - 1, this.centerY),
-                durationMillis = 100
-            )
-        }
-        rule.onNodeWithTag(MenuItemTag).assertIsDisplayed()
     }
 
     @Test
@@ -436,8 +507,7 @@
                         modifier =
                             Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
                         readOnly = true,
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         label = { Text("Label") },
                     )
                     ExposedDropdownMenu(
@@ -520,8 +590,7 @@
                     TextField(
                         modifier =
                             Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         label = { Text("Label") },
                     )
                     ExposedDropdownMenu(
@@ -558,8 +627,7 @@
                                             onExpandedChange = {}
                                         ) {
                                             TextField(
-                                                value = "Text",
-                                                onValueChange = {},
+                                                state = rememberTextFieldState("Text"),
                                                 modifier =
                                                     Modifier.menuAnchor(
                                                         ExposedDropdownMenuAnchorType
@@ -607,8 +675,7 @@
                     TextField(
                         modifier =
                             Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable),
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         label = { Text("Label") },
                     )
                     ExposedDropdownMenu(
@@ -742,7 +809,7 @@
         textFieldModifier: Modifier = Modifier,
         menuModifier: Modifier = Modifier,
     ) {
-        var selectedOptionText by remember { mutableStateOf("") }
+        val textFieldState = rememberTextFieldState()
         Box(Modifier.fillMaxSize()) {
             ExposedDropdownMenuBox(
                 modifier = Modifier.align(Alignment.Center),
@@ -762,8 +829,8 @@
                                 enabled = enabled,
                             )
                             .testTag(TFTag),
-                    value = selectedOptionText,
-                    onValueChange = { selectedOptionText = it },
+                    state = textFieldState,
+                    lineLimits = TextFieldLineLimits.SingleLine,
                     readOnly = !editable,
                     label = { Text("Label") },
                     trailingIcon = {
@@ -781,7 +848,7 @@
                     DropdownMenuItem(
                         text = { Text(OptionName) },
                         onClick = {
-                            selectedOptionText = OptionName
+                            textFieldState.setTextAndPlaceCursorAtEnd(OptionName)
                             onExpandChange(false)
                         },
                         modifier = Modifier.testTag(MenuItemTag)
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonMenuScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonMenuScreenshotTest.kt
new file mode 100644
index 0000000..0860ba1
--- /dev/null
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonMenuScreenshotTest.kt
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Build
+import androidx.compose.material.icons.filled.Call
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material.icons.filled.Email
+import androidx.compose.material.icons.filled.Face
+import androidx.compose.material3.ToggleableFloatingActionButtonDefaults.animateIcon
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+class FloatingActionButtonMenuScreenshotTest {
+
+    @get:Rule val rule = createComposeRule()
+
+    @get:Rule val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
+
+    @Test
+    fun fabMenu_collapsed_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) { testContent() }
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenu_collapsed_lightTheme")
+    }
+
+    @Test
+    fun fabMenu_collapsed_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) { testContent() }
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenu_collapsed_darkTheme")
+    }
+
+    @Test
+    fun fabMenu_expanded_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) { testContent() }
+
+        rule.onNodeWithTag(ToggleableFabTestTag).performClick()
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenu_expanded_lightTheme")
+    }
+
+    @Test
+    fun fabMenu_expanded_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) { testContent() }
+
+        rule.onNodeWithTag(ToggleableFabTestTag).performClick()
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenu_expanded_darkTheme")
+    }
+
+    @Test
+    fun fabMenuMediumSecondaryContainer_collapsed_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            testContent(
+                containerColor =
+                    ToggleableFloatingActionButtonDefaults.containerColor(
+                        MaterialTheme.colorScheme.secondaryContainer,
+                        MaterialTheme.colorScheme.secondary
+                    ),
+                containerSize = ToggleableFloatingActionButtonDefaults.containerSizeMedium(),
+                containerCornerRadius =
+                    ToggleableFloatingActionButtonDefaults.containerCornerRadiusMedium(),
+                iconColor =
+                    ToggleableFloatingActionButtonDefaults.iconColor(
+                        MaterialTheme.colorScheme.onSecondaryContainer,
+                        MaterialTheme.colorScheme.onSecondary
+                    ),
+                iconSize = ToggleableFloatingActionButtonDefaults.iconSizeMedium(),
+                itemContainerColor = MaterialTheme.colorScheme.secondaryContainer
+            )
+        }
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(
+                screenshotRule,
+                "fabMenuMediumSecondaryContainer_collapsed_lightTheme"
+            )
+    }
+
+    @Test
+    fun fabMenuMediumSecondaryContainer_expanded_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            testContent(
+                containerColor =
+                    ToggleableFloatingActionButtonDefaults.containerColor(
+                        MaterialTheme.colorScheme.secondaryContainer,
+                        MaterialTheme.colorScheme.secondary
+                    ),
+                containerSize = ToggleableFloatingActionButtonDefaults.containerSizeMedium(),
+                containerCornerRadius =
+                    ToggleableFloatingActionButtonDefaults.containerCornerRadiusMedium(),
+                iconColor =
+                    ToggleableFloatingActionButtonDefaults.iconColor(
+                        MaterialTheme.colorScheme.onSecondaryContainer,
+                        MaterialTheme.colorScheme.onSecondary
+                    ),
+                iconSize = ToggleableFloatingActionButtonDefaults.iconSizeMedium(),
+                itemContainerColor = MaterialTheme.colorScheme.secondaryContainer
+            )
+        }
+
+        rule.onNodeWithTag(ToggleableFabTestTag).performClick()
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(
+                screenshotRule,
+                "fabMenuMediumSecondaryContainer_expanded_lightTheme"
+            )
+    }
+
+    @Test
+    fun fabMenuLargeTertiary_collapsed_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            testContent(
+                containerColor =
+                    ToggleableFloatingActionButtonDefaults.containerColor(
+                        MaterialTheme.colorScheme.tertiary,
+                        MaterialTheme.colorScheme.tertiary
+                    ),
+                containerSize = ToggleableFloatingActionButtonDefaults.containerSizeLarge(),
+                containerCornerRadius =
+                    ToggleableFloatingActionButtonDefaults.containerCornerRadiusLarge(),
+                iconColor =
+                    ToggleableFloatingActionButtonDefaults.iconColor(
+                        MaterialTheme.colorScheme.onTertiary,
+                        MaterialTheme.colorScheme.onTertiary
+                    ),
+                iconSize = ToggleableFloatingActionButtonDefaults.iconSizeLarge(),
+                itemContainerColor = MaterialTheme.colorScheme.tertiaryContainer
+            )
+        }
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenuLargeTertiary_collapsed_lightTheme")
+    }
+
+    @Test
+    fun fabMenuLargeTertiary_expanded_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            testContent(
+                containerColor =
+                    ToggleableFloatingActionButtonDefaults.containerColor(
+                        MaterialTheme.colorScheme.tertiary,
+                        MaterialTheme.colorScheme.tertiary
+                    ),
+                containerSize = ToggleableFloatingActionButtonDefaults.containerSizeLarge(),
+                containerCornerRadius =
+                    ToggleableFloatingActionButtonDefaults.containerCornerRadiusLarge(),
+                iconColor =
+                    ToggleableFloatingActionButtonDefaults.iconColor(
+                        MaterialTheme.colorScheme.onTertiary,
+                        MaterialTheme.colorScheme.onTertiary
+                    ),
+                iconSize = ToggleableFloatingActionButtonDefaults.iconSizeLarge(),
+                itemContainerColor = MaterialTheme.colorScheme.tertiaryContainer
+            )
+        }
+
+        rule.onNodeWithTag(ToggleableFabTestTag).performClick()
+
+        rule
+            .onNodeWithTag(FabMenuTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenuLargeTertiary_expanded_lightTheme")
+    }
+
+    @Test
+    fun fabMenuItem_iconOnly_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FloatingActionButtonMenu(
+                expanded = true,
+                itemsCount = 1,
+            ) {
+                FloatingActionButtonMenuItem(
+                    modifier = Modifier.testTag(FabMenuItemTestTag),
+                    onClick = {},
+                    icon = { Icon(Icons.Filled.Add, contentDescription = null) },
+                    text = {},
+                    itemIndex = 0,
+                )
+            }
+        }
+
+        rule
+            .onNodeWithTag(FabMenuItemTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenuItem_iconOnly_lightTheme")
+    }
+
+    @Test
+    fun fabMenuItem_textOnly_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FloatingActionButtonMenu(
+                expanded = true,
+                itemsCount = 1,
+            ) {
+                FloatingActionButtonMenuItem(
+                    modifier = Modifier.testTag(FabMenuItemTestTag),
+                    onClick = {},
+                    icon = {},
+                    text = { Text(text = "Text") },
+                    itemIndex = 0,
+                )
+            }
+        }
+
+        rule
+            .onNodeWithTag(FabMenuItemTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenuItem_textOnly_lightTheme")
+    }
+
+    @Test
+    fun fabMenuItem_minimumTextOnly_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FloatingActionButtonMenu(
+                expanded = true,
+                itemsCount = 1,
+            ) {
+                FloatingActionButtonMenuItem(
+                    modifier = Modifier.testTag(FabMenuItemTestTag),
+                    onClick = {},
+                    icon = {},
+                    text = { Text(text = ".") },
+                    itemIndex = 0,
+                )
+            }
+        }
+
+        rule
+            .onNodeWithTag(FabMenuItemTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "fabMenuItem_minimumTextOnly_lightTheme")
+    }
+
+    private val FabMenuTestTag = "fabMenu"
+    private val FabMenuItemTestTag = "fabMenuItem"
+    private val ToggleableFabTestTag = "toggleableFab"
+
+    @Composable
+    private fun testContent(
+        containerColor: (Float) -> Color = ToggleableFloatingActionButtonDefaults.containerColor(),
+        containerSize: (Float) -> Dp = ToggleableFloatingActionButtonDefaults.containerSize(),
+        containerCornerRadius: (Float) -> Dp =
+            ToggleableFloatingActionButtonDefaults.containerCornerRadius(),
+        iconColor: (Float) -> Color = ToggleableFloatingActionButtonDefaults.iconColor(),
+        iconSize: (Float) -> Dp = ToggleableFloatingActionButtonDefaults.iconSize(),
+        itemContainerColor: Color = MaterialTheme.colorScheme.primaryContainer
+    ) {
+        Box {
+            val items =
+                listOf(
+                    Icons.Filled.Add to "Add",
+                    Icons.Filled.Build to "Build",
+                    Icons.Filled.Call to "Call",
+                    Icons.Filled.Delete to "Delete",
+                    Icons.Filled.Email to "Email",
+                    Icons.Filled.Face to "Face",
+                )
+
+            var fabMenuExpanded by rememberSaveable { mutableStateOf(false) }
+
+            Column(
+                modifier =
+                    Modifier.testTag(FabMenuTestTag)
+                        .align(Alignment.BottomEnd)
+                        .padding(bottom = 16.dp, end = 16.dp),
+                horizontalAlignment = Alignment.End
+            ) {
+                FloatingActionButtonMenu(
+                    modifier = Modifier.weight(weight = 1f, fill = false),
+                    expanded = fabMenuExpanded,
+                    itemsCount = items.size,
+                ) {
+                    items.forEachIndexed { i, item ->
+                        FloatingActionButtonMenuItem(
+                            onClick = { fabMenuExpanded = !fabMenuExpanded },
+                            icon = { Icon(item.first, contentDescription = null) },
+                            text = { Text(text = item.second) },
+                            itemIndex = i,
+                            containerColor = itemContainerColor
+                        )
+                    }
+                }
+
+                ToggleableFloatingActionButton(
+                    modifier = Modifier.testTag(ToggleableFabTestTag),
+                    checked = fabMenuExpanded,
+                    onCheckedChange = { fabMenuExpanded = !fabMenuExpanded },
+                    containerColor = containerColor,
+                    containerSize = containerSize,
+                    containerCornerRadius = containerCornerRadius,
+                ) {
+                    val imageVector by remember {
+                        derivedStateOf {
+                            if (checkedProgress > 0.5f) Icons.Filled.Close else Icons.Filled.Add
+                        }
+                    }
+                    Icon(
+                        painter = rememberVectorPainter(imageVector),
+                        contentDescription = null,
+                        modifier = Modifier.animateIcon({ checkedProgress }, iconColor, iconSize)
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt
index a5fa9ea..1b63ae9 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt
@@ -17,12 +17,17 @@
 
 import android.os.Build
 import android.os.Build.VERSION.SDK_INT
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.VectorConverter
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -224,6 +229,48 @@
     }
 
     @Test
+    fun smallFab() {
+        rule.setMaterialContent(lightColorScheme()) {
+            SmallFloatingActionButton(onClick = {}) {
+                Icon(Icons.Filled.Favorite, contentDescription = null)
+            }
+        }
+
+        assertClickableAgainstGolden("fab_small_size")
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun mediumFab() {
+        rule.setMaterialContent(lightColorScheme()) {
+            MediumFloatingActionButton(onClick = {}) {
+                Icon(
+                    Icons.Filled.Add,
+                    contentDescription = "Localized description",
+                    modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize),
+                )
+            }
+        }
+
+        assertClickableAgainstGolden("fab_medium_size")
+    }
+
+    @Test
+    fun largeFab() {
+        rule.setMaterialContent(lightColorScheme()) {
+            LargeFloatingActionButton(onClick = {}) {
+                Icon(
+                    Icons.Filled.Add,
+                    contentDescription = "Localized description",
+                    modifier = Modifier.size(FloatingActionButtonDefaults.LargeIconSize),
+                )
+            }
+        }
+
+        assertClickableAgainstGolden("fab_large_size")
+    }
+
+    @Test
     fun text() {
         rule.setMaterialContent(lightColorScheme()) {
             ExtendedFloatingActionButton(
@@ -248,6 +295,99 @@
         assertClickableAgainstGolden("fab_extended_text_and_icon")
     }
 
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun smallExtendedFabTextOnly() {
+        rule.setMaterialContent(lightColorScheme()) {
+            SmallExtendedFloatingActionButton(
+                onClick = {},
+                content = { Text("EXTENDED") },
+            )
+        }
+
+        assertClickableAgainstGolden("fab_small_extended_text")
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun smallExtendedFabTextAndIcon() {
+        rule.setMaterialContent(lightColorScheme()) {
+            SmallExtendedFloatingActionButton(
+                text = { Text("EXTENDED") },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
+                onClick = {}
+            )
+        }
+
+        assertClickableAgainstGolden("fab_small_extended_text_and_icon")
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun mediumExtendedFabTextOnly() {
+        rule.setMaterialContent(lightColorScheme()) {
+            MediumExtendedFloatingActionButton(
+                onClick = {},
+                content = { Text("EXTENDED") },
+            )
+        }
+
+        assertClickableAgainstGolden("fab_medium_extended_text")
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun mediumExtendedFabTextAndIcon() {
+        rule.setMaterialContent(lightColorScheme()) {
+            MediumExtendedFloatingActionButton(
+                text = { Text("EXTENDED") },
+                icon = {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        contentDescription = null,
+                        modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize)
+                    )
+                },
+                onClick = {}
+            )
+        }
+
+        assertClickableAgainstGolden("fab_medium_extended_text_and_icon")
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun largeExtendedFabTextOnly() {
+        rule.setMaterialContent(lightColorScheme()) {
+            LargeExtendedFloatingActionButton(
+                onClick = {},
+                content = { Text("EXTENDED") },
+            )
+        }
+
+        assertClickableAgainstGolden("fab_large_extended_text")
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun largeExtendedFabTextAndIcon() {
+        rule.setMaterialContent(lightColorScheme()) {
+            LargeExtendedFloatingActionButton(
+                text = { Text("EXTENDED") },
+                icon = {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        contentDescription = null,
+                        modifier = Modifier.size(FloatingActionButtonDefaults.LargeIconSize)
+                    )
+                },
+                onClick = {}
+            )
+        }
+
+        assertClickableAgainstGolden("fab_large_extended_text_and_icon")
+    }
+
     @Test
     fun ripple() {
         rule.setMaterialContent(lightColorScheme()) {
@@ -314,12 +454,16 @@
         assertRootAgainstGolden("fab_focus")
     }
 
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @Test
     fun extended_fab_half_way_animation() {
         rule.mainClock.autoAdvance = false
 
         var expanded by mutableStateOf(true)
+        lateinit var motionSpec: FiniteAnimationSpec<Float>
         rule.setMaterialContent(lightColorScheme()) {
+            // Loads the same FiniteAnimationSpec that is used by the ExtendedFloatingActionButton
+            motionSpec = MotionSchemeKeyTokens.FastSpatial.value()
             ExtendedFloatingActionButton(
                 expanded = expanded,
                 onClick = {},
@@ -332,7 +476,18 @@
 
         rule.runOnIdle { expanded = false }
 
-        rule.mainClock.advanceTimeBy(127)
+        // Calculate the time it should take the current motion to run and advance the clock to
+        // 50% of it.
+        val duration =
+            motionSpec
+                .vectorize(Float.VectorConverter)
+                .getDurationNanos(
+                    initialValue = AnimationVector1D(0f),
+                    targetValue = AnimationVector1D(1f),
+                    initialVelocity = AnimationVector1D(0f)
+                ) / 1_000_000.0
+
+        rule.mainClock.advanceTimeBy(duration.toLong() / 2)
 
         assertRootAgainstGolden("fab_extended_animation")
     }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonTest.kt
index fbcef79..1f4370d 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonTest.kt
@@ -498,6 +498,106 @@
             .assertWidthIsEqualTo(FabPrimaryTokens.ContainerWidth)
     }
 
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun expandedLargeExtendedFabTextAndIconHaveSizeFromSpecAndVisible() {
+        rule.setMaterialContent(lightColorScheme()) {
+            LargeExtendedFloatingActionButton(
+                expanded = true,
+                onClick = {},
+                icon = {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        "Add",
+                        modifier =
+                            Modifier.size(FloatingActionButtonDefaults.LargeIconSize)
+                                .testTag("icon"),
+                    )
+                },
+                text = { Text(text = "FAB", modifier = Modifier.testTag("text")) },
+                modifier = Modifier.testTag("FAB"),
+            )
+        }
+
+        rule
+            .onNodeWithTag("icon", useUnmergedTree = true)
+            .assertHeightIsEqualTo(FloatingActionButtonDefaults.LargeIconSize)
+            .assertWidthIsEqualTo(FloatingActionButtonDefaults.LargeIconSize)
+
+        rule.onNodeWithTag("FAB").assertHeightIsEqualTo(96.dp).assertWidthIsAtLeast(112.dp)
+
+        rule.onNodeWithTag("text", useUnmergedTree = true).assertIsDisplayed()
+        rule.onNodeWithTag("icon", useUnmergedTree = true).assertIsDisplayed()
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun collapsedLargeExtendedFabTextAndIconHaveSizeFromSpecAndTextNotVisible() {
+        rule.setMaterialContent(lightColorScheme()) {
+            LargeExtendedFloatingActionButton(
+                expanded = false,
+                onClick = {},
+                icon = {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        "Add",
+                        modifier =
+                            Modifier.size(FloatingActionButtonDefaults.LargeIconSize)
+                                .testTag("icon")
+                    )
+                },
+                text = { Text(text = "FAB", modifier = Modifier.testTag("text")) },
+                modifier = Modifier.testTag("FAB"),
+            )
+        }
+
+        rule.onNodeWithTag("FAB").assertIsSquareWithSize(96.dp)
+
+        rule
+            .onNodeWithTag("icon", useUnmergedTree = true)
+            .assertHeightIsEqualTo(FloatingActionButtonDefaults.LargeIconSize)
+            .assertWidthIsEqualTo(FloatingActionButtonDefaults.LargeIconSize)
+
+        rule.onNodeWithTag("text", useUnmergedTree = true).assertDoesNotExist()
+        rule.onNodeWithTag("icon", useUnmergedTree = true).assertIsDisplayed()
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun largeExtendedFabAnimates() {
+        rule.mainClock.autoAdvance = false
+
+        var expanded by mutableStateOf(true)
+        rule.setMaterialContent(lightColorScheme()) {
+            LargeExtendedFloatingActionButton(
+                expanded = expanded,
+                onClick = {},
+                icon = {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        "Add",
+                        modifier =
+                            Modifier.size(FloatingActionButtonDefaults.LargeIconSize)
+                                .testTag("icon")
+                    )
+                },
+                text = { Text(text = "FAB", modifier = Modifier.testTag("text")) },
+                modifier = Modifier.testTag("FAB"),
+            )
+        }
+
+        rule.onNodeWithTag("FAB").assertHeightIsEqualTo(96.dp).assertWidthIsAtLeast(112.dp)
+
+        rule.runOnIdle { expanded = false }
+        rule.mainClock.advanceTimeBy(400)
+
+        rule
+            .onNodeWithTag("FAB")
+            .assertIsSquareWithSize(96.dp)
+            .assertHeightIsEqualTo(96.dp)
+            .assertWidthIsEqualTo(96.dp)
+    }
+
     @Test
     fun floatingActionButtonElevation_newInteraction() {
         val interactionSource = MutableInteractionSource()
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarScreenshotTest.kt
index 069576e..9eed84f 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarScreenshotTest.kt
@@ -24,12 +24,15 @@
 import androidx.compose.material.icons.filled.Edit
 import androidx.compose.material.icons.outlined.Favorite
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -105,22 +108,6 @@
     }
 
     @Test
-    fun horizontalFloatingAppBar_trailing_lightTheme() {
-        rule.setMaterialContent(lightColorScheme()) {
-            Box(Modifier.testTag(FloatingAppBarTestTag)) {
-                HorizontalFloatingAppBar(expanded = true, trailingContent = { Text("trailing") }) {
-                    content()
-                }
-            }
-        }
-
-        rule
-            .onNodeWithTag(FloatingAppBarTestTag)
-            .captureToImage()
-            .assertAgainstGolden(screenshotRule, "horizontalFloatingAppBar_trailing_lightTheme")
-    }
-
-    @Test
     fun horizontalFloatingAppBar_leading_lightTheme() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(Modifier.testTag(FloatingAppBarTestTag)) {
@@ -137,13 +124,29 @@
     }
 
     @Test
-    fun horizontalFloatingAppBar_trailing_leading_lightTheme() {
+    fun horizontalFloatingAppBar_trailing_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.testTag(FloatingAppBarTestTag)) {
+                HorizontalFloatingAppBar(expanded = true, trailingContent = { Text("trailing") }) {
+                    content()
+                }
+            }
+        }
+
+        rule
+            .onNodeWithTag(FloatingAppBarTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "horizontalFloatingAppBar_trailing_lightTheme")
+    }
+
+    @Test
+    fun horizontalFloatingAppBar_leading_trailing_lightTheme() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(Modifier.testTag(FloatingAppBarTestTag)) {
                 HorizontalFloatingAppBar(
                     expanded = true,
-                    trailingContent = { Text("trailing") },
-                    leadingContent = { Text("leading") }
+                    leadingContent = { Text("leading") },
+                    trailingContent = { Text("trailing") }
                 ) {
                     content()
                 }
@@ -155,18 +158,18 @@
             .captureToImage()
             .assertAgainstGolden(
                 screenshotRule,
-                "horizontalFloatingAppBar_trailing_leading_lightTheme"
+                "horizontalFloatingAppBar_leading_trailing_lightTheme"
             )
     }
 
     @Test
-    fun horizontalFloatingAppBar_trailing_leading_darkTheme() {
+    fun horizontalFloatingAppBar_leading_trailing_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             Box(Modifier.testTag(FloatingAppBarTestTag)) {
                 HorizontalFloatingAppBar(
                     expanded = true,
-                    trailingContent = { Text("trailing") },
-                    leadingContent = { Text("leading") }
+                    leadingContent = { Text("leading") },
+                    trailingContent = { Text("trailing") }
                 ) {
                     content()
                 }
@@ -178,18 +181,18 @@
             .captureToImage()
             .assertAgainstGolden(
                 screenshotRule,
-                "horizontalFloatingAppBar_trailing_leading_darkTheme"
+                "horizontalFloatingAppBar_leading_trailing_darkTheme"
             )
     }
 
     @Test
-    fun horizontalFloatingAppBar_trailing_leading_collapsed_lightTheme() {
+    fun horizontalFloatingAppBar_leading_trailing_collapsed_lightTheme() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(Modifier.testTag(FloatingAppBarTestTag)) {
                 HorizontalFloatingAppBar(
                     expanded = false,
-                    trailingContent = { Text("trailing") },
-                    leadingContent = { Text("leading") }
+                    leadingContent = { Text("leading") },
+                    trailingContent = { Text("trailing") }
                 ) {
                     content()
                 }
@@ -201,19 +204,22 @@
             .captureToImage()
             .assertAgainstGolden(
                 screenshotRule,
-                "horizontalFloatingAppBar_trailing_leading_collapsed_lightTheme"
+                "horizontalFloatingAppBar_leading_trailing_collapsed_lightTheme"
             )
     }
 
     @Test
-    fun verticalFloatingAppBar_trailing_lightTheme() {
+    fun horizontalFloatingAppBar_leading_trailing_rtl_lightTheme() {
         rule.setMaterialContent(lightColorScheme()) {
-            Box(Modifier.testTag(FloatingAppBarTestTag)) {
-                VerticalFloatingAppBar(
-                    expanded = true,
-                    trailingContent = { Text(text = "trailing") }
-                ) {
-                    content()
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Box(Modifier.testTag(FloatingAppBarTestTag)) {
+                    HorizontalFloatingAppBar(
+                        expanded = true,
+                        leadingContent = { Text("leading") },
+                        trailingContent = { Text("trailing") }
+                    ) {
+                        content()
+                    }
                 }
             }
         }
@@ -221,7 +227,10 @@
         rule
             .onNodeWithTag(FloatingAppBarTestTag)
             .captureToImage()
-            .assertAgainstGolden(screenshotRule, "verticalFloatingAppBar_trailing_lightTheme")
+            .assertAgainstGolden(
+                screenshotRule,
+                "horizontalFloatingAppBar_leading_trailing_rtl_lightTheme"
+            )
     }
 
     @Test
@@ -244,13 +253,32 @@
     }
 
     @Test
-    fun verticalFloatingAppBar_trailing_leading_lightTheme() {
+    fun verticalFloatingAppBar_trailing_lightTheme() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(Modifier.testTag(FloatingAppBarTestTag)) {
                 VerticalFloatingAppBar(
                     expanded = true,
-                    trailingContent = { Text(text = "trailing") },
-                    leadingContent = { Text(text = "leading") }
+                    trailingContent = { Text(text = "trailing") }
+                ) {
+                    content()
+                }
+            }
+        }
+
+        rule
+            .onNodeWithTag(FloatingAppBarTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "verticalFloatingAppBar_trailing_lightTheme")
+    }
+
+    @Test
+    fun verticalFloatingAppBar_leading_trailing_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.testTag(FloatingAppBarTestTag)) {
+                VerticalFloatingAppBar(
+                    expanded = true,
+                    leadingContent = { Text(text = "leading") },
+                    trailingContent = { Text(text = "trailing") }
                 ) {
                     content()
                 }
@@ -262,18 +290,18 @@
             .captureToImage()
             .assertAgainstGolden(
                 screenshotRule,
-                "verticalFloatingAppBar_trailing_leading_lightTheme"
+                "verticalFloatingAppBar_leading_trailing_lightTheme"
             )
     }
 
     @Test
-    fun verticalFloatingAppBar_trailing_leading_darkTheme() {
+    fun verticalFloatingAppBar_leading_trailing_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             Box(Modifier.testTag(FloatingAppBarTestTag)) {
                 VerticalFloatingAppBar(
                     expanded = true,
-                    trailingContent = { Text(text = "trailing") },
-                    leadingContent = { Text(text = "leading") }
+                    leadingContent = { Text(text = "leading") },
+                    trailingContent = { Text(text = "trailing") }
                 ) {
                     content()
                 }
@@ -285,18 +313,18 @@
             .captureToImage()
             .assertAgainstGolden(
                 screenshotRule,
-                "verticalFloatingAppBar_trailing_leading_darkTheme"
+                "verticalFloatingAppBar_leading_trailing_darkTheme"
             )
     }
 
     @Test
-    fun verticalFloatingAppBar_trailing_leading_collapsed_lightTheme() {
+    fun verticalFloatingAppBar_leading_trailing_collapsed_lightTheme() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(Modifier.testTag(FloatingAppBarTestTag)) {
                 VerticalFloatingAppBar(
                     expanded = false,
-                    trailingContent = { Text(text = "trailing") },
-                    leadingContent = { Text(text = "leading") }
+                    leadingContent = { Text(text = "leading") },
+                    trailingContent = { Text(text = "trailing") }
                 ) {
                     content()
                 }
@@ -308,7 +336,7 @@
             .captureToImage()
             .assertAgainstGolden(
                 screenshotRule,
-                "verticalFloatingAppBar_trailing_leading_collapsed_lightTheme"
+                "verticalFloatingAppBar_leading_trailing_collapsed_lightTheme"
             )
     }
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarTest.kt
index cc1e857..ad4ec7b9 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingAppBarTest.kt
@@ -313,17 +313,17 @@
     }
 
     @Test
-    fun horizontalFloatingAppBar_trailingAndLeadingContent_expanded() {
+    fun horizontalFloatingAppBar_leadingAndTrailingContent_expanded() {
         rule.setMaterialContent(lightColorScheme()) {
             HorizontalFloatingAppBar(
                 modifier = Modifier.testTag(FloatingAppBarTestTag),
                 expanded = true,
-                trailingContent = {
+                leadingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
                 },
-                leadingContent = {
+                trailingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
@@ -338,17 +338,17 @@
     }
 
     @Test
-    fun horizontalFloatingAppBar_trailingAndLeadingContent_notExpanded() {
+    fun horizontalFloatingAppBar_leadingAndTrailingContent_notExpanded() {
         rule.setMaterialContent(lightColorScheme()) {
             HorizontalFloatingAppBar(
                 modifier = Modifier.testTag(FloatingAppBarTestTag),
                 expanded = false,
-                trailingContent = {
+                leadingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
                 },
-                leadingContent = {
+                trailingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
@@ -438,17 +438,17 @@
     }
 
     @Test
-    fun verticalFloatingAppBar_trailingAndLeadingContent_expanded() {
+    fun verticalFloatingAppBar_leadingAndTrailingContent_expanded() {
         rule.setMaterialContent(lightColorScheme()) {
             VerticalFloatingAppBar(
                 modifier = Modifier.testTag(FloatingAppBarTestTag),
                 expanded = true,
-                trailingContent = {
+                leadingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
                 },
-                leadingContent = {
+                trailingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
@@ -463,17 +463,17 @@
     }
 
     @Test
-    fun verticalFloatingAppBar_trailingAndLeadingContent_notExpanded() {
+    fun verticalFloatingAppBar_leadingAndTrailingContent_notExpanded() {
         rule.setMaterialContent(lightColorScheme()) {
             VerticalFloatingAppBar(
                 modifier = Modifier.testTag(FloatingAppBarTestTag),
                 expanded = false,
-                trailingContent = {
+                leadingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
                 },
-                leadingContent = {
+                trailingContent = {
                     IconButton(onClick = { /* doSomething() */ }) {
                         Icon(Icons.Filled.Check, contentDescription = "Localized description")
                     }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/IconButtonScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/IconButtonScreenshotTest.kt
index aaa4a49..d57c4a2 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/IconButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/IconButtonScreenshotTest.kt
@@ -48,6 +48,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -109,6 +110,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun iconButton_lightTheme_pressed() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag)) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorScreenshotTest.kt
index 61febfd..7ffe31c 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorScreenshotTest.kt
@@ -47,27 +47,42 @@
     private val wrapperTestTag = "loadingIndicatorWrapper"
 
     @Test
-    fun loadingIndicator_determinate_start_progress() {
+    fun containedLoadingIndicator_determinate_start_progress() {
         rule.setMaterialContent(scheme.colorScheme) {
-            Box(wrap.testTag(wrapperTestTag)) { LoadingIndicator(progress = { 0f }) }
+            Box(wrap.testTag(wrapperTestTag)) { ContainedLoadingIndicator(progress = { 0f }) }
         }
-        assertIndicatorAgainstGolden("loadingIndicator_determinate_start_progress_${scheme.name}")
+        assertIndicatorAgainstGolden(
+            "containedLoadingIndicator_determinate_start_progress_${scheme.name}"
+        )
     }
 
     @Test
-    fun loadingIndicator_determinate_mid_progress() {
+    fun containedLoadingIndicator_determinate_mid_progress() {
         rule.setMaterialContent(scheme.colorScheme) {
-            Box(wrap.testTag(wrapperTestTag)) { LoadingIndicator(progress = { 0.5f }) }
+            Box(wrap.testTag(wrapperTestTag)) { ContainedLoadingIndicator(progress = { 0.5f }) }
         }
-        assertIndicatorAgainstGolden("loadingIndicator_determinate_mid_progress_${scheme.name}")
+        assertIndicatorAgainstGolden(
+            "containedLoadingIndicator_determinate_mid_progress_${scheme.name}"
+        )
     }
 
     @Test
-    fun loadingIndicator_determinate_end_progress() {
+    fun containedLoadingIndicator_determinate_end_progress() {
         rule.setMaterialContent(scheme.colorScheme) {
-            Box(wrap.testTag(wrapperTestTag)) { LoadingIndicator(progress = { 1f }) }
+            Box(wrap.testTag(wrapperTestTag)) { ContainedLoadingIndicator(progress = { 1f }) }
         }
-        assertIndicatorAgainstGolden("loadingIndicator_determinate_end_progress_${scheme.name}")
+        assertIndicatorAgainstGolden(
+            "containedLoadingIndicator_determinate_end_progress_${scheme.name}"
+        )
+    }
+
+    @Test
+    fun loadingIndicator_determinate() {
+        rule.mainClock.autoAdvance = false
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(wrap.testTag(wrapperTestTag)) { LoadingIndicator() }
+        }
+        assertIndicatorAgainstGolden("loadingIndicator_determinate_${scheme.name}")
     }
 
     @Test
@@ -80,16 +95,12 @@
     }
 
     @Test
-    fun loadingIndicator_indeterminate_withContainerColor() {
+    fun containedLoadingIndicator_indeterminate() {
         rule.mainClock.autoAdvance = false
         rule.setMaterialContent(scheme.colorScheme) {
-            Box(wrap.testTag(wrapperTestTag)) {
-                LoadingIndicator(containerColor = LoadingIndicatorDefaults.ContainerColor)
-            }
+            Box(wrap.testTag(wrapperTestTag)) { ContainedLoadingIndicator() }
         }
-        assertIndicatorAgainstGolden(
-            "loadingIndicator_indeterminate_withContainerColor_${scheme.name}"
-        )
+        assertIndicatorAgainstGolden("containedLoadingIndicator_indeterminate_${scheme.name}")
     }
 
     private fun assertIndicatorAgainstGolden(goldenName: String) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorTest.kt
index 636281c..7e964a3 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/LoadingIndicatorTest.kt
@@ -40,60 +40,95 @@
     @get:Rule val rule = createComposeRule()
 
     @Test
-    fun nonMaterialSetContent() {
-        val tag = "indicator"
+    fun nonMaterialSetContent_loadingIndicator() {
         val progress = mutableFloatStateOf(0f)
 
         rule.setContent {
             LoadingIndicator(
-                modifier = Modifier.testTag(tag),
+                modifier = Modifier.testTag(TestTag),
                 progress = { progress.value },
             )
         }
 
-        rule.onNodeWithTag(tag).assertIsDisplayed()
+        rule.onNodeWithTag(TestTag).assertIsDisplayed()
+    }
+
+    @Test
+    fun nonMaterialSetContent_containedLoadingIndicator() {
+        val progress = mutableFloatStateOf(0f)
+
+        rule.setContent {
+            ContainedLoadingIndicator(
+                modifier = Modifier.testTag(TestTag),
+                progress = { progress.value },
+            )
+        }
+
+        rule.onNodeWithTag(TestTag).assertIsDisplayed()
     }
 
     @Test
     fun determinateLoadingIndicator_Progress() {
-        val tag = "indicator"
         val progress = mutableFloatStateOf(0f)
 
         rule.setMaterialContent(lightColorScheme()) {
-            LoadingIndicator(modifier = Modifier.testTag(tag), progress = { progress.value })
+            LoadingIndicator(modifier = Modifier.testTag(TestTag), progress = { progress.value })
         }
 
         rule
-            .onNodeWithTag(tag)
+            .onNodeWithTag(TestTag)
             .assertIsDisplayed()
             .assertRangeInfoEquals(ProgressBarRangeInfo(0f, 0f..1f))
 
         rule.runOnUiThread { progress.value = 0.5f }
 
         rule
-            .onNodeWithTag(tag)
+            .onNodeWithTag(TestTag)
+            .assertIsDisplayed()
+            .assertRangeInfoEquals(ProgressBarRangeInfo(0.5f, 0f..1f))
+    }
+
+    @Test
+    fun determinateContainedLoadingIndicator_Progress() {
+        val progress = mutableFloatStateOf(0f)
+
+        rule.setMaterialContent(lightColorScheme()) {
+            ContainedLoadingIndicator(
+                modifier = Modifier.testTag(TestTag),
+                progress = { progress.value }
+            )
+        }
+
+        rule
+            .onNodeWithTag(TestTag)
+            .assertIsDisplayed()
+            .assertRangeInfoEquals(ProgressBarRangeInfo(0f, 0f..1f))
+
+        rule.runOnUiThread { progress.value = 0.5f }
+
+        rule
+            .onNodeWithTag(TestTag)
             .assertIsDisplayed()
             .assertRangeInfoEquals(ProgressBarRangeInfo(0.5f, 0f..1f))
     }
 
     @Test
     fun determinateLoadingIndicator_ProgressIsCoercedInBounds() {
-        val tag = "indicator"
         val progress = mutableStateOf(-1f)
 
         rule.setMaterialContent(lightColorScheme()) {
-            LoadingIndicator(modifier = Modifier.testTag(tag), progress = { progress.value })
+            LoadingIndicator(modifier = Modifier.testTag(TestTag), progress = { progress.value })
         }
 
         rule
-            .onNodeWithTag(tag)
+            .onNodeWithTag(TestTag)
             .assertIsDisplayed()
             .assertRangeInfoEquals(ProgressBarRangeInfo(0f, 0f..1f))
 
         rule.runOnUiThread { progress.value = 1.5f }
 
         rule
-            .onNodeWithTag(tag)
+            .onNodeWithTag(TestTag)
             .assertIsDisplayed()
             .assertRangeInfoEquals(ProgressBarRangeInfo(1f, 0f..1f))
     }
@@ -106,28 +141,41 @@
             .assertHeightIsEqualTo(LoadingIndicatorDefaults.ContainerHeight)
     }
 
+    @Test
+    fun determinateContainedLoadingIndicator_Size() {
+        rule
+            .setMaterialContentForSizeAssertions { ContainedLoadingIndicator(progress = { 0f }) }
+            .assertWidthIsEqualTo(LoadingIndicatorDefaults.ContainerWidth)
+            .assertHeightIsEqualTo(LoadingIndicatorDefaults.ContainerHeight)
+    }
+
     @Test(expected = IllegalArgumentException::class)
     fun determinateLoadingIndicator_MinPolygons() {
         rule.setMaterialContent(lightColorScheme()) {
-            LoadingIndicator(
-                progress = { 0f },
-                indicatorPolygons = listOf(MaterialShapes.PuffyDiamond)
-            )
+            LoadingIndicator(progress = { 0f }, polygons = listOf(MaterialShapes.PuffyDiamond))
         }
     }
 
     @Test
     fun indeterminateLoadingIndicator_Progress() {
-        val tag = "indicator"
-
         rule.mainClock.autoAdvance = false
         rule.setMaterialContent(lightColorScheme()) {
-            LoadingIndicator(modifier = Modifier.testTag(tag))
+            LoadingIndicator(modifier = Modifier.testTag(TestTag))
         }
 
         rule.mainClock.advanceTimeByFrame() // Kick off the animation
+        rule.onNodeWithTag(TestTag).assertRangeInfoEquals(ProgressBarRangeInfo.Indeterminate)
+    }
 
-        rule.onNodeWithTag(tag).assertRangeInfoEquals(ProgressBarRangeInfo.Indeterminate)
+    @Test
+    fun indeterminateContainedLoadingIndicator_Progress() {
+        rule.mainClock.autoAdvance = false
+        rule.setMaterialContent(lightColorScheme()) {
+            ContainedLoadingIndicator(modifier = Modifier.testTag(TestTag))
+        }
+
+        rule.mainClock.advanceTimeByFrame() // Kick off the animation
+        rule.onNodeWithTag(TestTag).assertRangeInfoEquals(ProgressBarRangeInfo.Indeterminate)
     }
 
     @Test
@@ -142,10 +190,24 @@
             .assertHeightIsEqualTo(LoadingIndicatorDefaults.ContainerHeight)
     }
 
+    @Test
+    fun indeterminateContainedLoadingIndicator_Size() {
+        rule.mainClock.autoAdvance = false
+        val contentToTest = rule.setMaterialContentForSizeAssertions { ContainedLoadingIndicator() }
+
+        rule.mainClock.advanceTimeByFrame() // Kick off the animation
+
+        contentToTest
+            .assertWidthIsEqualTo(LoadingIndicatorDefaults.ContainerWidth)
+            .assertHeightIsEqualTo(LoadingIndicatorDefaults.ContainerHeight)
+    }
+
     @Test(expected = IllegalArgumentException::class)
     fun indeterminateLoadingIndicator_MinPolygons() {
         rule.setMaterialContent(lightColorScheme()) {
-            LoadingIndicator(indicatorPolygons = listOf(MaterialShapes.PuffyDiamond))
+            LoadingIndicator(polygons = listOf(MaterialShapes.PuffyDiamond))
         }
     }
+
+    private val TestTag = "indicator"
 }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MaterialTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MaterialTest.kt
index e3954ae..dfd8be6 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MaterialTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MaterialTest.kt
@@ -21,6 +21,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.luminance
 import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.ui.layout.LastBaseline
 import androidx.compose.ui.platform.LocalWindowInfo
@@ -40,6 +42,8 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.height
 import androidx.compose.ui.unit.width
+import kotlin.math.max
+import kotlin.math.min
 
 /**
  * Wraps Compose content in a [MaterialTheme] and a [Surface].
@@ -109,3 +113,25 @@
 
     return onNodeWithTag("containerForSizeAssertion")
 }
+
+/**
+ * Logic forked from
+ * compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTextSelectionColors.kt
+ *
+ * Calculates the contrast ratio of [foreground] against [background], returning a value between 1
+ * and 21. (1:1 and 21:1 ratios).
+ *
+ * Formula taken from
+ * [WCAG 2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef)
+ *
+ * Note: [foreground] and [background] *must* be opaque. See [Color.compositeOver] to pre-composite
+ * a translucent foreground over the background.
+ *
+ * @return the contrast ratio as a value between 1 and 21. See [calculateContrastRatio]
+ */
+fun calculateContrastRatio(foreground: Color, background: Color): Float {
+    val foregroundLuminance = foreground.luminance() + 0.05f
+    val backgroundLuminance = background.luminance() + 0.05f
+    return max(foregroundLuminance, backgroundLuminance) /
+        min(foregroundLuminance, backgroundLuminance)
+}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MenuTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MenuTest.kt
index 03c7756..edecebd 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MenuTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MenuTest.kt
@@ -76,13 +76,13 @@
         rule.mainClock.advanceTimeByFrame() // Trigger the popup
         rule.waitForIdle()
         rule.mainClock.advanceTimeByFrame() // Kick off the animation
-        rule.mainClock.advanceTimeBy(InTransitionDuration.toLong())
+        rule.mainClock.advanceTimeBy(300)
         rule.onNodeWithTag("MenuContent").assertExists()
 
         rule.runOnUiThread { expanded = false }
         rule.mainClock.advanceTimeByFrame() // Trigger the popup
         rule.mainClock.advanceTimeByFrame() // Kick off the animation
-        rule.mainClock.advanceTimeBy(OutTransitionDuration.toLong())
+        rule.mainClock.advanceTimeBy(300)
         rule.mainClock.advanceTimeByFrame()
         rule.onNodeWithTag("MenuContent").assertDoesNotExist()
 
@@ -90,7 +90,7 @@
         rule.mainClock.advanceTimeByFrame() // Trigger the popup
         rule.waitForIdle()
         rule.mainClock.advanceTimeByFrame() // Kick off the animation
-        rule.mainClock.advanceTimeBy(InTransitionDuration.toLong())
+        rule.mainClock.advanceTimeBy(300)
         rule.onNodeWithTag("MenuContent").assertExists()
     }
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
index 5a06bfd..eadf4b8 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
@@ -209,6 +209,7 @@
                     latch.countDown()
                 }
 
+                @Deprecated("deprecated")
                 override fun onLowMemory() {
                     // NO-OP
                 }
@@ -265,6 +266,7 @@
                     latch.countDown()
                 }
 
+                @Deprecated("deprecated")
                 override fun onLowMemory() {
                     // NO-OP
                 }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MotionSchemeTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MotionSchemeTest.kt
new file mode 100644
index 0000000..dbf5957
--- /dev/null
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MotionSchemeTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+class MotionSchemeTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun tokenValue() {
+        lateinit var motionScheme: MotionScheme
+        lateinit var defaultSpatialSpec: FiniteAnimationSpec<Float>
+        lateinit var fastSpatialSpec: FiniteAnimationSpec<Float>
+        lateinit var slowSpatialSpec: FiniteAnimationSpec<Float>
+        lateinit var defaultEffectsSpec: FiniteAnimationSpec<Float>
+        lateinit var fastEffectsSpec: FiniteAnimationSpec<Float>
+        lateinit var slowEffectsSpec: FiniteAnimationSpec<Float>
+        rule.setMaterialContent(lightColorScheme()) {
+            motionScheme = LocalMotionScheme.current
+            defaultSpatialSpec = MotionSchemeKeyTokens.DefaultSpatial.value()
+            fastSpatialSpec = MotionSchemeKeyTokens.FastSpatial.value()
+            slowSpatialSpec = MotionSchemeKeyTokens.SlowSpatial.value()
+            defaultEffectsSpec = MotionSchemeKeyTokens.DefaultEffects.value()
+            fastEffectsSpec = MotionSchemeKeyTokens.FastEffects.value()
+            slowEffectsSpec = MotionSchemeKeyTokens.SlowEffects.value()
+        }
+
+        rule.runOnIdle {
+            assertThat(motionScheme.defaultSpatialSpec<Float>()).isEqualTo(defaultSpatialSpec)
+            assertThat(motionScheme.fastSpatialSpec<Float>()).isEqualTo(fastSpatialSpec)
+            assertThat(motionScheme.slowSpatialSpec<Float>()).isEqualTo(slowSpatialSpec)
+            assertThat(motionScheme.defaultEffectsSpec<Float>()).isEqualTo(defaultEffectsSpec)
+            assertThat(motionScheme.fastEffectsSpec<Float>()).isEqualTo(fastEffectsSpec)
+            assertThat(motionScheme.slowEffectsSpec<Float>()).isEqualTo(slowEffectsSpec)
+        }
+    }
+}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
index 13db85c..ca5305b 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationBarScreenshotTest.kt
@@ -43,6 +43,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -76,6 +77,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -152,6 +154,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationDrawerItemScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationDrawerItemScreenshotTest.kt
index 81c7be8..0ce4637 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationDrawerItemScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationDrawerItemScreenshotTest.kt
@@ -40,6 +40,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -73,6 +74,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -111,6 +113,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationRailScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationRailScreenshotTest.kt
index 64abfde..603b8f6 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationRailScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/NavigationRailScreenshotTest.kt
@@ -43,6 +43,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -75,6 +76,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -132,6 +134,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_defaultColors_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -189,6 +192,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_defaultColors_withHeaderFab_pressed() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OpticalCenteringTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OpticalCenteringTest.kt
new file mode 100644
index 0000000..54b95a6
--- /dev/null
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OpticalCenteringTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.testutils.assertIsEqualTo
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OpticalCenteringTest {
+    private val TextTag = "text"
+    private val ContainerTag = "container"
+    @get:Rule val rule = createComposeRule()
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun opticalCentering_contentPadding_asymmetricShape() {
+        val shape =
+            RoundedCornerShape(
+                topStart = 20.dp,
+                bottomStart = 20.dp,
+                topEnd = 0.dp,
+                bottomEnd = 0.dp
+            )
+        val baseContentPadding = PaddingValues(horizontal = 20.dp)
+        val expectedStartPadding = 20.dp + (0.11f * 20f).dp
+        val expectedEndPadding = 20.dp - (0.11f * 20f).dp
+        rule.setContent {
+            Box(modifier = Modifier.clip(shape).testTag(ContainerTag)) {
+                Row(modifier = Modifier.opticalCentering(shape, baseContentPadding)) {
+                    Text(text = "Test", modifier = Modifier.testTag(TextTag))
+                }
+            }
+        }
+
+        val containerBounds = rule.onNodeWithTag(ContainerTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(TextTag).getUnclippedBoundsInRoot()
+
+        (textBounds.left - containerBounds.left).assertIsEqualTo(expectedStartPadding)
+        (containerBounds.right - textBounds.right).assertIsEqualTo(expectedEndPadding)
+    }
+
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Test
+    fun opticalCentering_contentPadding_symmetricShape() {
+        val shape = RoundedCornerShape(0.dp)
+        val baseContentPadding = PaddingValues(horizontal = 20.dp)
+        val expectedPadding = 20.dp
+        rule.setContent {
+            Box(modifier = Modifier.clip(shape).testTag(ContainerTag)) {
+                Row(modifier = Modifier.opticalCentering(shape, baseContentPadding)) {
+                    Text(text = "Test", modifier = Modifier.testTag(TextTag))
+                }
+            }
+        }
+
+        val containerBounds = rule.onNodeWithTag(ContainerTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(TextTag).getUnclippedBoundsInRoot()
+
+        (textBounds.left - containerBounds.left).assertIsEqualTo(expectedPadding)
+        (containerBounds.right - textBounds.right).assertIsEqualTo(expectedPadding)
+    }
+}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt
index 734f398..b71f1d3 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt
@@ -22,6 +22,8 @@
 import androidx.compose.foundation.layout.requiredWidth
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.foundation.text.selection.TextSelectionColors
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Call
@@ -44,7 +46,6 @@
 import androidx.compose.ui.test.swipeLeft
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -56,7 +57,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(ExperimentalMaterial3Api::class)
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@@ -64,13 +64,11 @@
     private val TextFieldTag = "OutlinedTextField"
 
     private val longText =
-        TextFieldValue(
-            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
-                "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
-                " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
-                "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
-                "fugiat nulla pariatur."
-        )
+        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
+            "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
+            " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+            "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+            "fugiat nulla pariatur."
 
     private val platformTextStyle = defaultPlatformTextStyle()
 
@@ -83,8 +81,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -97,8 +94,7 @@
     fun outlinedTextField_notFocused() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -111,8 +107,7 @@
     fun outlinedTextField_focused() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -128,8 +123,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("Label") },
                     modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
                 )
@@ -146,8 +140,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Input"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
@@ -163,8 +156,7 @@
     fun outlinedTextField_error_notFocused() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
@@ -179,8 +171,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Hello, world!"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
                 colors = OutlinedTextFieldDefaults.colors(unfocusedTextColor = Color.Magenta),
             )
@@ -194,8 +185,11 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Hello, world!"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(0, text.length)),
-                onValueChange = {},
+                state =
+                    rememberTextFieldState(
+                        initialText = text,
+                        initialSelection = TextRange(0, text.length),
+                    ),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 colors =
                     OutlinedTextFieldDefaults.colors(
@@ -218,8 +212,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -234,8 +227,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
             )
@@ -248,8 +240,7 @@
     fun outlinedTextField_multiLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 placeholder = { Text("placeholder") },
                 modifier =
@@ -266,8 +257,7 @@
     fun outlinedTextField_multiLine_withoutLabel_placeholderAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -283,8 +273,7 @@
     fun outlinedTextField_multiLine_labelAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -299,9 +288,8 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -315,9 +303,8 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
         }
@@ -329,11 +316,10 @@
     fun outlinedTextField_singleLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 label = { Text("Label") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
         }
@@ -347,10 +333,9 @@
     fun outlinedTextField_singleLine_withoutLabel_placeholderCenteredVertically() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
         }
@@ -366,8 +351,7 @@
     fun outlinedTextField_singleLine_labelCenteredVertically() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -380,9 +364,8 @@
     fun outlinedTextField_disabled() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState("Text"),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -395,9 +378,8 @@
     fun outlinedTextField_disabled_notFocusable() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState("Text"),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -412,9 +394,8 @@
     fun outlinedTextField_disabled_notScrolled() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = longText,
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(longText),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
                 enabled = false
             )
@@ -435,8 +416,7 @@
     fun outlinedTextField_readOnly() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
                 enabled = true,
                 readOnly = true
@@ -450,8 +430,7 @@
     fun outlinedTextField_readOnly_focused() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
                 enabled = true,
                 readOnly = true
@@ -467,10 +446,9 @@
     fun outlinedTextField_readOnly_scrolled() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = longText,
-                onValueChange = {},
+                state = rememberTextFieldState(longText),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = true,
                 readOnly = true
             )
@@ -490,14 +468,12 @@
     @Test
     fun outlinedTextField_textCenterAligned() {
         rule.setMaterialContent(lightColorScheme()) {
-            val text = "Hello world"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState("Hello world"),
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 textStyle =
                     TextStyle(textAlign = TextAlign.Center, platformStyle = platformTextStyle),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -507,13 +483,11 @@
     @Test
     fun outlinedTextField_textAlignedToEnd() {
         rule.setMaterialContent(lightColorScheme()) {
-            val text = "Hello world"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState("Hello world"),
                 modifier = Modifier.fillMaxWidth().testTag(TextFieldTag),
                 textStyle = TextStyle(textAlign = TextAlign.End, platformStyle = platformTextStyle),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -524,11 +498,10 @@
     fun outlinedTextField_customShape() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 shape = CutCornerShape(10.dp)
             )
         }
@@ -540,10 +513,9 @@
     fun outlinedTextField_supportingText() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 supportingText = { Text("Supporting text") }
             )
         }
@@ -555,11 +527,10 @@
     fun outlinedTextField_errorSupportingText() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 supportingText = { Text("Error supporting text") }
             )
         }
@@ -573,8 +544,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             makeLabelNull = remember { mutableStateOf(false) }
             OutlinedTextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 label =
                     if (makeLabelNull.value) {
@@ -595,8 +565,7 @@
     fun outlinedTextField_leadingTrailingIcons() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -611,8 +580,7 @@
     fun outlinedTextField_leadingTrailingIcons_error() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -625,11 +593,59 @@
     }
 
     @Test
+    fun outlinedTextField_labelPositionAbove_withIcons_andPlaceholder_andSupporting() {
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = { Text("Label") },
+                labelPosition = TextFieldLabelPosition.Above,
+                leadingIcon = { Icon(Icons.Default.Call, null) },
+                trailingIcon = { Icon(Icons.Default.Clear, null) },
+                placeholder = { Text("Placeholder") },
+                supportingText = { Text("Supporting") },
+            )
+        }
+
+        assertAgainstGolden(
+            "outlinedTextField_labelPositionAbove_withIcons_andPlaceholder_andSupporting"
+        )
+    }
+
+    @Test
+    fun outlinedTextField_alwaysMinimizeLabel_noPlaceholder() {
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = { Text("Label") },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = true),
+            )
+        }
+
+        assertAgainstGolden("outlinedTextField_alwaysMinimizeLabel_noPlaceholder")
+    }
+
+    @Test
+    fun outlinedTextField_alwaysMinimizeLabel_withPlaceholder() {
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = { Text("Label") },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = true),
+                placeholder = { Text("Placeholder") },
+            )
+        }
+
+        assertAgainstGolden("outlinedTextField_alwaysMinimizeLabel_withPlaceholder")
+    }
+
+    @Test
     fun outlinedTextField_prefixSuffix_withLabelAndInput() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -644,8 +660,7 @@
     fun outlinedTextField_prefixSuffix_withLabelAndInput_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             OutlinedTextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -660,8 +675,7 @@
     fun outlinedTextField_prefixSuffix_withLabelAndInput_focused() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -678,8 +692,7 @@
     fun outlinedTextField_prefixSuffix_withPlaceholder() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("Placeholder") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -694,8 +707,7 @@
     fun outlinedTextField_prefixSuffix_withLeadingTrailingIcons() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -713,8 +725,7 @@
         rule.setMaterialContent(darkColorScheme()) {
             val text = "Text"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -727,8 +738,7 @@
     fun outlinedTextField_focused_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -744,8 +754,7 @@
         rule.setMaterialContent(darkColorScheme()) {
             val text = "Input"
             OutlinedTextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
@@ -761,9 +770,8 @@
     fun outlinedTextField_disabled_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             OutlinedTextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState("Text"),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
             )
@@ -776,8 +784,7 @@
     fun outlinedTextField_leadingTrailingIcons_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
index 8d66554..a8d58ef 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
@@ -33,18 +33,27 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.delete
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.placeCursorAtEnd
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.TextSelectionColors
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material3.internal.AboveLabelBottomPadding
+import androidx.compose.material3.internal.AboveLabelHorizontalPadding
 import androidx.compose.material3.internal.HorizontalIconPadding
 import androidx.compose.material3.internal.MinFocusedLabelLineHeight
 import androidx.compose.material3.internal.MinSupportingTextLineHeight
 import androidx.compose.material3.internal.MinTextLineHeight
 import androidx.compose.material3.internal.Strings
 import androidx.compose.material3.internal.SupportingTopPadding
-import androidx.compose.material3.internal.TextFieldAnimationDuration
 import androidx.compose.material3.internal.TextFieldPadding
 import androidx.compose.material3.internal.getString
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.testutils.assertPixels
@@ -57,6 +66,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.node.Ref
@@ -71,7 +81,9 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.click
@@ -86,7 +98,6 @@
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
@@ -118,8 +129,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 OutlinedTextField(
-                    value = "input",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input"),
                     modifier = Modifier.requiredWidth(40.dp)
                 )
             }
@@ -130,7 +140,7 @@
     fun testOutlinedTextField_defaultWidth() {
         rule
             .setMaterialContentForSizeAssertions {
-                OutlinedTextField(value = "input", onValueChange = {})
+                OutlinedTextField(rememberTextFieldState("input"))
             }
             .assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
     }
@@ -150,16 +160,14 @@
                         Modifier.testTag(textField1Tag).onFocusChanged {
                             textField1Focused = it.isFocused
                         },
-                    value = "input1",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input1"),
                 )
                 OutlinedTextField(
                     modifier =
                         Modifier.testTag(textField2Tag).onFocusChanged {
                             textField2Focused = it.isFocused
                         },
-                    value = "input2",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input2"),
                 )
             }
         }
@@ -187,8 +195,7 @@
                 OutlinedTextField(
                     modifier =
                         Modifier.testTag(TextFieldTag).onFocusChanged { focused = it.isFocused },
-                    value = "input",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input"),
                 )
             }
         }
@@ -207,8 +214,7 @@
             CompositionLocalProvider(LocalDensity provides density) {
                 Box(Modifier.testTag("box").background(Color.Red)) {
                     OutlinedTextField(
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         colors =
                             OutlinedTextFieldDefaults.colors(
                                 unfocusedTextColor = Color.White,
@@ -238,9 +244,8 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 label = {
                     Box(
                         Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -270,8 +275,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Box(
                         Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -299,8 +303,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.requiredWidth(textFieldWidth),
                 label = {
                     Text(
@@ -339,8 +342,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Box(
                         Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
@@ -369,8 +371,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(textFieldWidth),
                 label = {
                     Text(
@@ -405,13 +406,41 @@
     }
 
     @Test
+    fun testOutlinedTextField_labelPosition_whenPositionedAbove() {
+        val labelPosition = Ref<Offset>()
+        rule
+            .setMaterialContentForSizeAssertions {
+                OutlinedTextField(
+                    state = rememberTextFieldState(),
+                    label = {
+                        Box(
+                            Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
+                                labelPosition.value = it.positionInRoot()
+                            }
+                        )
+                    },
+                    labelPosition = TextFieldLabelPosition.Above,
+                )
+            }
+            .assertHeightIsEqualTo(
+                MinFocusedLabelLineHeight + AboveLabelBottomPadding + ExpectedMinimumTextFieldHeight
+            )
+
+        rule.runOnIdleWithDensity {
+            // x position is padding
+            assertThat(labelPosition.value?.x).isWithin(1f).of(AboveLabelHorizontalPadding.toPx())
+            // y position is 0
+            assertThat(labelPosition.value?.y).isEqualTo(0f)
+        }
+    }
+
+    @Test
     fun testOutlinedTextField_labelHeight_contributesToTextFieldMeasurements_whenUnfocused() {
         val tfSize = Ref<IntSize>()
         val labelHeight = 200.dp
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier =
                     Modifier.testTag(TextFieldTag).onGloballyPositioned { tfSize.value = it.size },
                 label = { Box(Modifier.size(width = 50.dp, height = labelHeight)) },
@@ -431,8 +460,7 @@
         val trailingSize = Ref<IntSize>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(textFieldWidth),
                 label = {
                     Text(
@@ -477,8 +505,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 label = {
                     Box(
                         Modifier.size(labelSize).onGloballyPositioned {
@@ -499,12 +526,39 @@
     }
 
     @Test
+    fun testOutlinedTextField_labelScope_progressAndRecomposition() {
+        val progressValue = Ref<Float>()
+        var compositionCount = 0
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = {
+                    SideEffect { compositionCount++ }
+
+                    // lambda reads `progress` in the draw phase
+                    Box(Modifier.graphicsLayer { progressValue.value = progress })
+                }
+            )
+        }
+
+        assertThat(progressValue.value).isEqualTo(0f)
+        assertThat(compositionCount).isEqualTo(1)
+
+        // click to focus
+        rule.onNodeWithTag(TextFieldTag).performClick()
+        rule.waitForIdle()
+
+        assertThat(progressValue.value).isEqualTo(1f)
+        assertThat(compositionCount).isEqualTo(1)
+    }
+
+    @Test
     fun testOutlinedTextField_transparentComponents_doNotAppearInComposition() {
         // Regression test for b/251162419
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text(text = "Label") },
                 placeholder = {
                     Text(text = "Placeholder", modifier = Modifier.testTag("Placeholder"))
@@ -527,8 +581,7 @@
             Box {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
                     placeholder = {
                         Box(
@@ -558,8 +611,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = {
                     Box(
                         Modifier.size(placeholderSize).onGloballyPositioned {
@@ -585,8 +637,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 placeholder = {
                     Text(
                         text = "placeholder",
@@ -614,8 +665,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = {
                     Text("placeholder")
                     assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodyLarge)
@@ -631,11 +681,9 @@
     fun testOutlinedTextField_placeholderColor_whenInputEmptyAndFocused() {
         var focused = false
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             OutlinedTextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = text.value,
-                onValueChange = { text.value = it },
+                state = rememberTextFieldState(),
                 colors =
                     OutlinedTextFieldDefaults.colors(
                         focusedPlaceholderColor = Color.Red,
@@ -671,8 +719,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Box(
                         Modifier.size(labelSize).onGloballyPositioned {
@@ -730,8 +777,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth),
                     label = { Text("label") },
                     leadingIcon = {
@@ -803,8 +849,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth).height(textFieldHeight),
                     leadingIcon = {
                         IconButton(
@@ -872,8 +917,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth).height(textFieldHeight),
                     leadingIcon = {
                         Box(
@@ -917,6 +961,76 @@
     }
 
     @Test
+    fun testOutlinedTextField_prefixAndSuffixAndPlaceholder_areNotDisplayed_withLabel_ifLabelCanExpand() {
+        val labelText = "Label"
+        val prefixText = "Prefix"
+        val suffixText = "Suffix"
+        val placeholderText = "Placeholder"
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                state = rememberTextFieldState(),
+                label = { Text(labelText) },
+                prefix = { Text(prefixText) },
+                suffix = { Text(suffixText) },
+                placeholder = { Text(placeholderText) },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = false),
+            )
+        }
+
+        rule.onNodeWithText(labelText).assertIsDisplayed()
+
+        rule.onNodeWithText(prefixText).assertIsNotDisplayed()
+        rule.onNodeWithText(suffixText).assertIsNotDisplayed()
+        rule.onNodeWithText(placeholderText).assertIsNotDisplayed()
+    }
+
+    @Test
+    fun testOutlinedTextField_prefixAndSuffixAndPlaceholder_areDisplayed_withLabel_ifLabelCannotExpand() {
+        val labelText = "Label"
+        val prefixText = "Prefix"
+        val suffixText = "Suffix"
+        val placeholderText = "Placeholder"
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                state = rememberTextFieldState(),
+                label = { Text(labelText) },
+                prefix = { Text(prefixText) },
+                suffix = { Text(suffixText) },
+                placeholder = { Text(placeholderText) },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = true),
+            )
+        }
+
+        rule.onNodeWithText(labelText).assertIsDisplayed()
+        rule.onNodeWithText(prefixText).assertIsDisplayed()
+        rule.onNodeWithText(suffixText).assertIsDisplayed()
+        rule.onNodeWithText(placeholderText).assertIsDisplayed()
+    }
+
+    @Test
+    fun testOutlinedTextField_prefixAndSuffixAndPlaceholder_areDisplayed_withLabel_ifLabelIsAbove() {
+        val labelText = "Label"
+        val prefixText = "Prefix"
+        val suffixText = "Suffix"
+        val placeholderText = "Placeholder"
+        rule.setMaterialContent(lightColorScheme()) {
+            OutlinedTextField(
+                state = rememberTextFieldState(),
+                label = { Text(labelText) },
+                prefix = { Text(prefixText) },
+                suffix = { Text(suffixText) },
+                placeholder = { Text(placeholderText) },
+                labelPosition = TextFieldLabelPosition.Above,
+            )
+        }
+
+        rule.onNodeWithText(labelText).assertIsDisplayed()
+        rule.onNodeWithText(prefixText).assertIsDisplayed()
+        rule.onNodeWithText(suffixText).assertIsDisplayed()
+        rule.onNodeWithText(placeholderText).assertIsDisplayed()
+    }
+
+    @Test
     fun testOutlinedTextField_prefixAndSuffixPosition_withLabel() {
         val textFieldWidth = 300.dp
         val prefixPosition = Ref<Offset>()
@@ -928,8 +1042,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth),
                     label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
                     prefix = {
@@ -981,8 +1094,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth),
                     prefix = {
                         Box(
@@ -1029,8 +1141,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 OutlinedTextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(textFieldWidth),
                     prefix = {
                         Box(
@@ -1079,8 +1190,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Text(
                         text = "label",
@@ -1108,8 +1218,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Text(
                         text = "label",
@@ -1133,8 +1242,7 @@
     fun testOutlinedTextField_colorInLeadingTrailing_whenValidInput() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = false,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -1152,8 +1260,7 @@
     fun testOutlinedTextField_colorInLeadingTrailing_whenInvalidInput() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -1171,8 +1278,7 @@
         val supportingPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 textStyle = TextStyle(fontSize = 1.sp), // ensure text size is minimum
                 supportingText = {
                     Box(
@@ -1198,8 +1304,7 @@
         val supportingSize = Ref<IntSize>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
                 supportingText = {
                     Text(
@@ -1222,8 +1327,7 @@
         val tfSize = Ref<IntSize>()
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
                 supportingText = { Text("Supporting") }
             )
@@ -1239,8 +1343,8 @@
     fun testOutlinedTextField_supportingText_remainsVisibleWithTallInput() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = buildString { repeat(200) { append("line $it\n") } },
-                onValueChange = {},
+                state =
+                    rememberTextFieldState(buildString { repeat(200) { append("line $it\n") } }),
                 modifier = Modifier.size(width = ExpectedDefaultTextFieldWidth, height = 150.dp),
                 supportingText = { Text("Supporting", modifier = Modifier.testTag("Supporting")) }
             )
@@ -1255,8 +1359,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
                 modifier = Modifier.onFocusChanged { focused = it.isFocused },
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 supportingText = { Text("Supporting") }
             )
         }
@@ -1269,8 +1372,7 @@
     fun testOutlinedTextField_supportingText_colorAndStyle() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 supportingText = {
                     assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
                     assertThat(LocalContentColor.current)
@@ -1284,8 +1386,7 @@
     fun testOutlinedTextField_supportingText_error_colorAndStyle() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 supportingText = {
                     assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
@@ -1308,12 +1409,10 @@
             awaitCancellation()
         }
         rule.setContent {
-            val text = remember { mutableStateOf("") }
             InterceptPlatformTextInput(interceptor) {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = text.value,
-                    onValueChange = { text.value = it },
+                    state = rememberTextFieldState(),
                     keyboardOptions =
                         KeyboardOptions(imeAction = ImeAction.Go, keyboardType = KeyboardType.Email)
                 )
@@ -1340,9 +1439,14 @@
             Box(Modifier.background(color = Color.White)) {
                 OutlinedTextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = "qwerty",
-                    onValueChange = {},
-                    visualTransformation = PasswordVisualTransformation('\u0020')
+                    state = rememberTextFieldState("qwerty"),
+                    outputTransformation = {
+                        // transform all chars to blank spaces
+                        val size = length
+                        delete(0, length)
+                        insert(0, " ".repeat(size))
+                        placeCursorAtEnd()
+                    }
                 )
             }
         }
@@ -1364,7 +1468,7 @@
     fun testOutlinedTextField_errorSemantics_defaultMessage() {
         lateinit var errorMessage: String
         rule.setMaterialContent(lightColorScheme()) {
-            OutlinedTextField(value = "test", onValueChange = {}, isError = true)
+            OutlinedTextField(state = rememberTextFieldState("test"), isError = true)
             errorMessage = getString(Strings.DefaultErrorMessage)
         }
 
@@ -1381,8 +1485,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val isError = remember { mutableStateOf(true) }
             OutlinedTextField(
-                value = "test",
-                onValueChange = {},
+                state = rememberTextFieldState("test"),
                 modifier =
                     Modifier.testTag(TextFieldTag).semantics {
                         if (isError.value) error(errorMessage)
@@ -1413,7 +1516,6 @@
         var size: IntSize? = null
         var dividerSize: IntSize? = null
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { size = it.size }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
                     VerticalDivider(
@@ -1421,9 +1523,8 @@
                         modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
                     )
                     OutlinedTextField(
-                        value = text.value,
+                        state = rememberTextFieldState(),
                         label = { Text(text = "Label") },
-                        onValueChange = { text.value = it }
                     )
                 }
             }
@@ -1441,8 +1542,7 @@
     fun testOutlinedTextField_appliesContainerColor() {
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.testTag(TextFieldTag),
                 colors =
                     OutlinedTextFieldDefaults.colors(
@@ -1461,7 +1561,6 @@
         var textFieldSize: IntSize? = null
         var dividerSize: IntSize? = null
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Box {
                 Column(Modifier.width(IntrinsicSize.Min)) {
                     HorizontalDivider(
@@ -1469,9 +1568,8 @@
                         modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
                     )
                     OutlinedTextField(
-                        value = text.value,
+                        state = rememberTextFieldState(),
                         label = { Text(text = "Label") },
-                        onValueChange = { text.value = it },
                         modifier = Modifier.onGloballyPositioned { textFieldSize = it.size }
                     )
                 }
@@ -1496,8 +1594,7 @@
 
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedTextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     textStyle = LocalTextStyle.current
                     contentColor = LocalContentColor.current
@@ -1538,8 +1635,7 @@
             val bodySmall = MaterialTheme.typography.bodySmall.copy(color = bodySmallColor)
             MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1580,8 +1676,7 @@
             val bodySmall = MaterialTheme.typography.bodySmall.copy(color = expectedLabelColor)
             MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1599,7 +1694,7 @@
         rule.runOnUiThread { focusRequester.requestFocus() }
 
         // advance to middle of animation
-        rule.mainClock.advanceTimeBy(TextFieldAnimationDuration.toLong() / 2)
+        rule.mainClock.advanceTimeBy(TextFieldAnimationDuration)
 
         rule.runOnIdle {
             assertThat(textStyle.color).isEqualTo(expectedLabelColor)
@@ -1625,8 +1720,7 @@
             val bodyLarge = MaterialTheme.typography.bodyLarge.copy(color = bodyLargeColor)
             MaterialTheme(typography = Typography(bodySmall = bodySmall, bodyLarge = bodyLarge)) {
                 OutlinedTextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1655,15 +1749,69 @@
     }
 
     @Test
+    fun testOutlinedTextField_selectionColors_areCustomizable() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Column {
+                // default colors
+                OutlinedTextField(
+                    state = rememberTextFieldState(),
+                    label = {
+                        val textSelectionColors = LocalTextSelectionColors.current
+                        assertThat(textSelectionColors.handleColor)
+                            .isEqualTo(MaterialTheme.colorScheme.primary)
+                        assertThat(textSelectionColors.backgroundColor)
+                            .isEqualTo(
+                                MaterialTheme.colorScheme.primary.copy(
+                                    alpha = TextSelectionBackgroundOpacity
+                                )
+                            )
+                    }
+                )
+
+                // set via `colors()`
+                OutlinedTextField(
+                    state = rememberTextFieldState(),
+                    colors =
+                        OutlinedTextFieldDefaults.colors(
+                            selectionColors =
+                                TextSelectionColors(
+                                    handleColor = Color.Red,
+                                    backgroundColor = Color.Green
+                                )
+                        ),
+                    label = {
+                        val textSelectionColors = LocalTextSelectionColors.current
+                        assertThat(textSelectionColors.handleColor).isEqualTo(Color.Red)
+                        assertThat(textSelectionColors.backgroundColor).isEqualTo(Color.Green)
+                    }
+                )
+
+                // set via `CompositionLocal`
+                CompositionLocalProvider(
+                    LocalTextSelectionColors provides
+                        TextSelectionColors(
+                            handleColor = Color.Magenta,
+                            backgroundColor = Color.Yellow
+                        )
+                ) {
+                    OutlinedTextField(
+                        state = rememberTextFieldState(),
+                        label = {
+                            val textSelectionColors = LocalTextSelectionColors.current
+                            assertThat(textSelectionColors.handleColor).isEqualTo(Color.Magenta)
+                            assertThat(textSelectionColors.backgroundColor).isEqualTo(Color.Yellow)
+                        }
+                    )
+                }
+            }
+        }
+    }
+
+    @Test
     fun testOutlinedTextField_withIntrinsicsMeasurement_getsIdle() {
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Row(Modifier.height(IntrinsicSize.Min)) {
-                OutlinedTextField(
-                    value = text.value,
-                    onValueChange = { text.value = it },
-                    label = { Text("Label") }
-                )
+                OutlinedTextField(state = rememberTextFieldState(), label = { Text("Label") })
                 VerticalDivider()
             }
         }
@@ -1677,13 +1825,9 @@
     fun testOutlinedTextField_intrinsicHeight_withOnlyEmptyInput() {
         var height = 0
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
-                    OutlinedTextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
-                    )
+                    OutlinedTextField(rememberTextFieldState())
                     VerticalDivider()
                 }
             }
@@ -1698,12 +1842,10 @@
     fun testOutlinedTextField_intrinsicHeight_withEmptyInput_andDecorations() {
         var height = 0
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
                     OutlinedTextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
+                        state = rememberTextFieldState(),
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
                         trailingIcon = { Icon(Icons.Default.Favorite, null) },
                         prefix = { Text("P") },
@@ -1728,8 +1870,7 @@
             Row {
                 Box(Modifier.width(150.dp).height(IntrinsicSize.Min)) {
                     OutlinedTextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1741,8 +1882,7 @@
 
                 Box(Modifier.width(150.dp)) {
                     OutlinedTextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightNoIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1797,7 +1937,7 @@
                 modifier =
                     Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
             ) {
-                OutlinedTextField(value = "Cat", onValueChange = {}, leadingIcon = { Text("Icon") })
+                OutlinedTextField(state = rememberTextFieldState(), leadingIcon = { Text("Icon") })
             }
         }
     }
@@ -1810,3 +1950,6 @@
         return (paddingTop - labelHalfHeight).roundToInt()
     }
 }
+
+// We use springs to animate, so picking an arbitrary duration that work.
+private const val TextFieldAnimationDuration = 75L
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt
index f62ae18..cc0ca89 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt
@@ -96,7 +96,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag)) { LinearProgressIndicator() }
         }
-        rule.mainClock.advanceTimeBy(500)
+        rule.mainClock.advanceTimeBy(LinearAnimationDuration / 2L)
         assertIndicatorAgainstGolden("linearProgressIndicator_lightTheme_indeterminate")
     }
 
@@ -116,7 +116,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag)) { LinearProgressIndicator(gapSize = 0.dp) }
         }
-        rule.mainClock.advanceTimeBy(500)
+        rule.mainClock.advanceTimeBy(LinearAnimationDuration / 2L)
         assertIndicatorAgainstGolden("linearProgressIndicator_lightTheme_indeterminate_no_gap")
     }
 
@@ -172,17 +172,45 @@
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag)) { CircularProgressIndicator() }
         }
-        rule.mainClock.advanceTimeBy(500)
+        rule.mainClock.advanceTimeBy(CircularAnimationProgressDuration / 3L * 4L)
         assertIndicatorAgainstGolden("circularProgressIndicator_lightTheme_indeterminate")
     }
 
     @Test
+    fun circularProgressIndicator_lightTheme_indeterminate_with_track() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box(wrap.testTag(wrapperTestTag)) {
+                CircularProgressIndicator(trackColor = MaterialTheme.colorScheme.secondaryContainer)
+            }
+        }
+        assertIndicatorAgainstGolden(
+            "circularProgressIndicator_lightTheme_indeterminate_with_track"
+        )
+    }
+
+    @Test
+    fun circularProgressIndicator_lightTheme_indeterminate_with_track_no_gap() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box(wrap.testTag(wrapperTestTag)) {
+                CircularProgressIndicator(
+                    trackColor = MaterialTheme.colorScheme.secondaryContainer,
+                    gapSize = 0.dp
+                )
+            }
+        }
+        assertIndicatorAgainstGolden(
+            "circularProgressIndicator_lightTheme_indeterminate_with_track_no_gap"
+        )
+    }
+
+    @Test
     fun circularProgressIndicator_lightTheme_indeterminate_start() {
         rule.mainClock.autoAdvance = false
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag)) { CircularProgressIndicator() }
         }
         rule.mainClock.advanceTimeBy(0)
+        // Expecting about 10% of a circle, as defined by the CircularIndeterminateMinProgress.
         assertIndicatorAgainstGolden("circularProgressIndicator_lightTheme_indeterminate_start")
     }
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RadioButtonScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RadioButtonScreenshotTest.kt
index cb2d574..a967d07 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RadioButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/RadioButtonScreenshotTest.kt
@@ -43,6 +43,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -99,6 +100,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun radioButton_lightTheme_pressed() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrap.testTag(wrapperTestTag)) { RadioButton(selected = false, onClick = {}) }
@@ -119,6 +121,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun radioButton_darkTheme_pressed() {
         rule.setMaterialContent(darkColorScheme()) {
             Box(wrap.testTag(wrapperTestTag)) { RadioButton(selected = false, onClick = {}) }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt
index 9e8e3be..7bc3613 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt
@@ -19,6 +19,13 @@
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.layout.width
 import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens
+import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.DisabledLabelTextColor
+import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.DisabledLabelTextOpacity
+import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.DisabledOutlineOpacity
+import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.OutlineColor
+import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.SelectedContainerColor
+import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.SelectedLabelTextColor
+import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.UnselectedLabelTextColor
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -89,6 +96,31 @@
     }
 
     @Test
+    fun selectableSegmentedButton_defaultColors() {
+        rule.setMaterialContent(lightColorScheme()) {
+            assertThat(SegmentedButtonDefaults.colors())
+                .isEqualTo(
+                    SegmentedButtonColors(
+                        activeContainerColor = SelectedContainerColor.value,
+                        activeContentColor = SelectedLabelTextColor.value,
+                        activeBorderColor = OutlineColor.value,
+                        inactiveContainerColor = Color.Transparent,
+                        inactiveContentColor = UnselectedLabelTextColor.value,
+                        inactiveBorderColor = OutlineColor.value,
+                        disabledActiveContainerColor = SelectedContainerColor.value,
+                        disabledActiveContentColor =
+                            DisabledLabelTextColor.value.copy(alpha = DisabledLabelTextOpacity),
+                        disabledActiveBorderColor =
+                            OutlineColor.value.copy(alpha = DisabledOutlineOpacity),
+                        disabledInactiveContainerColor = Color.Transparent,
+                        disabledInactiveContentColor = DisabledLabelTextColor.value,
+                        disabledInactiveBorderColor = OutlineColor.value,
+                    )
+                )
+        }
+    }
+
+    @Test
     fun segmentedButton_itemsChecked() {
         var checked by mutableStateOf(true)
         rule.setMaterialContent(lightColorScheme()) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarScreenshotTest.kt
index bf33073..cdd96d5 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarScreenshotTest.kt
@@ -49,6 +49,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -82,6 +83,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun equalWeightArrangement_lightTheme_pressed() {
         val interactionSource = MutableInteractionSource()
         lateinit var scope: CoroutineScope
@@ -175,6 +177,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun equalWeightArrangement_darkTheme_pressed() {
         val interactionSource = MutableInteractionSource()
         lateinit var scope: CoroutineScope
@@ -233,6 +236,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun centeredArrangement_lightTheme_pressed() {
         val interactionSource = MutableInteractionSource()
         lateinit var scope: CoroutineScope
@@ -342,6 +346,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun centeredArrangement_darkTheme_pressed() {
         val interactionSource = MutableInteractionSource()
         lateinit var scope: CoroutineScope
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarTest.kt
index e02d719..ddf0279 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ShortNavigationBarTest.kt
@@ -475,23 +475,6 @@
     }
 
     @Test
-    fun item_iconSemanticsIsNull_whenLabelIsPresent() {
-        rule.setMaterialContent(lightColorScheme()) {
-            ShortNavigationBarItem(
-                modifier = Modifier.testTag("item"),
-                icon = { Icon(Icons.Filled.Favorite, "Favorite") },
-                label = { Text("Favorite") },
-                selected = true,
-                onClick = {}
-            )
-        }
-
-        val node = rule.onNodeWithTag("item").fetchSemanticsNode()
-
-        Truth.assertThat(node.config.getOrNull(SemanticsProperties.ContentDescription)).isNull()
-    }
-
-    @Test
     fun item_unselectedItem_hasIconSemantics_whenLabelNotPresent() {
         rule.setMaterialContent(lightColorScheme()) {
             ShortNavigationBarItem(
@@ -510,7 +493,9 @@
 
     @Test
     fun itemContent_topIconPosition_sizeAndPosition() {
+        var minSize: Dp? = null
         rule.setMaterialContent(lightColorScheme()) {
+            minSize = LocalMinimumInteractiveComponentSize.current
             ShortNavigationBarItem(
                 modifier = Modifier.testTag("item"),
                 icon = { Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon")) },
@@ -525,8 +510,8 @@
             rule.onNodeWithTag("icon", useUnmergedTree = true).getUnclippedBoundsInRoot()
 
         // Assert the item has its minimal width and height values.
-        Truth.assertThat(itemBounds.width).isAtLeast(NavigationItemMinWidth)
-        Truth.assertThat(itemBounds.height).isAtLeast(NavigationItemMinHeight)
+        Truth.assertThat(itemBounds.width).isAtLeast(minSize)
+        Truth.assertThat(itemBounds.height).isAtLeast(minSize)
 
         rule
             .onNodeWithTag("icon", useUnmergedTree = true)
@@ -552,7 +537,9 @@
 
     @Test
     fun itemContent_startIconPosition_sizeAndPosition() {
+        var minSize: Dp? = null
         rule.setMaterialContent(lightColorScheme()) {
+            minSize = LocalMinimumInteractiveComponentSize.current
             ShortNavigationBarItem(
                 modifier = Modifier.testTag("item"),
                 icon = { Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon")) },
@@ -570,8 +557,8 @@
             rule.onNodeWithTag("label", useUnmergedTree = true).getUnclippedBoundsInRoot()
 
         // Assert the item has its minimal width and height values.
-        Truth.assertThat(itemBounds.width).isAtLeast(NavigationItemMinWidth)
-        Truth.assertThat(itemBounds.height).isAtLeast(NavigationItemMinHeight)
+        Truth.assertThat(itemBounds.width).isAtLeast(minSize)
+        Truth.assertThat(itemBounds.height).isAtLeast(minSize)
 
         // Assert width.
         val expectedWidth =
@@ -582,8 +569,8 @@
                 StartIconIndicatorHorizontalPadding
         Truth.assertThat(itemBounds.width.value).isWithin(1f).of(expectedWidth.value)
         // Assert height. Note: The item's content height is less than its minimum touch target
-        // height, so its actual height is the same as NavigationItemMinHeight.
-        Truth.assertThat(itemBounds.height).isEqualTo(NavigationItemMinHeight)
+        // height, so its actual height is the same as LocalMinimumInteractiveComponentSize.
+        Truth.assertThat(itemBounds.height).isEqualTo(minSize)
 
         rule
             .onNodeWithTag("icon", useUnmergedTree = true)
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SliderTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SliderTest.kt
index db9331f..42a65554 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SliderTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SliderTest.kt
@@ -30,6 +30,7 @@
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.requiredWidth
 import androidx.compose.foundation.layout.width
+import androidx.compose.material3.internal.HorizontalSemanticsBoundsPadding
 import androidx.compose.material3.tokens.SliderTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -55,16 +56,21 @@
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsEqualTo
 import androidx.compose.ui.test.assertRangeInfoEquals
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.click
+import androidx.compose.ui.test.getBoundsInRoot
 import androidx.compose.ui.test.isFocusable
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onParent
 import androidx.compose.ui.test.performSemanticsAction
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.height
+import androidx.compose.ui.unit.width
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth
@@ -398,7 +404,7 @@
 
         rule
             .onNodeWithTag(tag)
-            .assertWidthIsEqualTo(rowWidth - spacerWidth.times(2))
+            .assertWidthIsEqualTo(rowWidth - spacerWidth * 2 + HorizontalSemanticsBoundsPadding * 2)
             .assertHeightIsEqualTo(SliderTokens.HandleHeight)
     }
 
@@ -413,7 +419,7 @@
 
         rule
             .onNodeWithTag(tag)
-            .assertWidthIsEqualTo(SliderTokens.HandleWidth)
+            .assertWidthIsEqualTo(SliderTokens.HandleWidth + HorizontalSemanticsBoundsPadding * 2)
             .assertHeightIsEqualTo(SliderTokens.InactiveTrackHeight)
     }
 
@@ -1093,6 +1099,85 @@
 
     @OptIn(ExperimentalMaterial3Api::class)
     @Test
+    fun rangeSlider_thumbs_semanticsNodeBounds() {
+        val startThumbTag = "startThumb"
+        val endThumbTag = "endThumb"
+        val padding = 10.dp
+
+        val expectedWidth =
+            with(rule.density) { SliderTokens.HandleWidth.roundToPx() + padding.roundToPx() }
+        val expectedHeight = with(rule.density) { SliderTokens.HandleHeight.roundToPx() }
+
+        val state = RangeSliderState(0f, 1f)
+        rule.setMaterialContent(lightColorScheme()) {
+            RangeSlider(
+                state = state,
+                startThumb = {
+                    SliderDefaults.Thumb(
+                        interactionSource = MutableInteractionSource(),
+                        modifier = Modifier.testTag(startThumbTag)
+                    )
+                },
+                endThumb = {
+                    SliderDefaults.Thumb(
+                        interactionSource = MutableInteractionSource(),
+                        modifier = Modifier.testTag(endThumbTag)
+                    )
+                }
+            )
+        }
+
+        listOf(startThumbTag, endThumbTag).forEach {
+            val thumbNode =
+                rule
+                    .onNodeWithTag(it, true)
+                    .onParent()
+                    .fetchSemanticsNode("couldn't find node with tag $it")
+            val thumbNodeBounds = thumbNode.boundsInRoot
+
+            // Check that the SemanticsNode bounds include the padding. This means that the
+            // SemanticsNode bounds are big enough to trigger TalkBack's green focus indicator.
+            Truth.assertThat(thumbNodeBounds.width).isEqualTo(expectedWidth)
+            Truth.assertThat(thumbNodeBounds.height).isEqualTo(expectedHeight)
+        }
+    }
+
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Test
+    fun rangeSlider_thumbs_visualBounds() {
+        val startThumbTag = "startThumb"
+        val endThumbTag = "endThumb"
+
+        val state = RangeSliderState(0f, 1f)
+        rule.setMaterialContent(lightColorScheme()) {
+            RangeSlider(
+                state = state,
+                startThumb = {
+                    SliderDefaults.Thumb(
+                        interactionSource = MutableInteractionSource(),
+                        modifier = Modifier.testTag(startThumbTag)
+                    )
+                },
+                endThumb = {
+                    SliderDefaults.Thumb(
+                        interactionSource = MutableInteractionSource(),
+                        modifier = Modifier.testTag(endThumbTag)
+                    )
+                }
+            )
+        }
+
+        listOf(startThumbTag, endThumbTag).forEach {
+            val thumbNodeBounds = rule.onNodeWithTag(it, true).getBoundsInRoot()
+
+            // Check that the visual bounds are the expected visual size.
+            thumbNodeBounds.width.assertIsEqualTo(SliderTokens.HandleWidth, it)
+            thumbNodeBounds.height.assertIsEqualTo(SliderTokens.HandleHeight, it)
+        }
+    }
+
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Test
     fun slider_dragOutsideTouchArea_doesntJump() {
         val state = SliderState(.5f)
         var slop = 0f
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt
index 4ad80d5..c9a26942 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt
@@ -17,6 +17,8 @@
 package androidx.compose.material3
 
 import android.os.Build
+import androidx.compose.material3.internal.Strings
+import androidx.compose.material3.internal.getString
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.rememberCoroutineScope
@@ -169,10 +171,12 @@
     @Test
     fun snackbarHost_semantics() {
         val hostState = SnackbarHostState()
+        lateinit var paneTitle: String
         lateinit var scope: CoroutineScope
         rule.setContent {
             scope = rememberCoroutineScope()
             SnackbarHost(hostState) { data -> Snackbar(data) }
+            paneTitle = getString(Strings.SnackbarPaneTitle)
         }
         val job1 =
             scope.launch {
@@ -186,6 +190,7 @@
             .assert(
                 SemanticsMatcher.expectValue(SemanticsProperties.LiveRegion, LiveRegionMode.Polite)
             )
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.PaneTitle, paneTitle))
             .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.Dismiss))
             .performSemanticsAction(SemanticsActions.Dismiss)
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
index d0ae210..1c4fd93 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
@@ -40,6 +40,7 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,6 +59,7 @@
     private val wrapperTestTag = "splitButtonWrapper"
 
     @Test
+    @Ignore("b/355548641")
     fun splitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
@@ -95,6 +97,7 @@
     }
 
     @Test
+    @Ignore("b/355548641")
     fun filledSplitButton_large() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
@@ -131,6 +134,7 @@
     }
 
     @Test
+    @Ignore("b/355548641")
     fun filledSplitButtonExpanded() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
@@ -168,6 +172,7 @@
     }
 
     @Test
+    @Ignore("b/355548641")
     fun tonalSplitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
@@ -199,6 +204,7 @@
     }
 
     @Test
+    @Ignore("b/355548641")
     fun elevatedSplitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
@@ -230,6 +236,7 @@
     }
 
     @Test
+    @Ignore("b/355548641")
     fun outlinedSplitButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
@@ -261,6 +268,7 @@
     }
 
     @Test
+    @Ignore("b/355548641")
     fun splitButton_iconLeadingButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
@@ -295,6 +303,7 @@
     }
 
     @Test
+    @Ignore("b/355548641")
     fun splitButton_textLeadingButton() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt
index 5ac39b3..65abfc0 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt
@@ -52,6 +52,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.After
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -134,6 +135,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun switchTest_pressed() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(wrapperModifier) { Switch(checked = false, enabled = true, onCheckedChange = {}) }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
index fa58ee1..2e523a3 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
@@ -40,6 +40,7 @@
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -92,6 +93,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_primary_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -111,6 +113,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun lightTheme_secondary_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -168,6 +171,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_primary_pressed() {
         val interactionSource = MutableInteractionSource()
 
@@ -187,6 +191,7 @@
     }
 
     @Test
+    @Ignore("b/355413615")
     fun darkTheme_secondary_pressed() {
         val interactionSource = MutableInteractionSource()
 
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt
deleted file mode 100644
index a130983..0000000
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt
+++ /dev/null
@@ -1,718 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material3
-
-import android.os.Build
-import androidx.compose.foundation.horizontalScroll
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.IntrinsicSize
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.material3.TextFieldDefaults.indicatorLine
-import androidx.compose.material3.internal.TextFieldPadding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.remember
-import androidx.compose.testutils.assertPixels
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.layout.positionInRoot
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.test.captureToImage
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.text.input.VisualTransformation
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import androidx.test.filters.SdkSuppress
-import com.google.common.truth.Truth.assertThat
-import kotlin.math.roundToInt
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalMaterial3Api::class)
-class TextFieldDecorationBoxTest {
-    @get:Rule val rule = createComposeRule()
-
-    private val Density = Density(1f)
-    private val LabelHeight = 40.dp
-    private val InnerTextFieldHeight = 50.dp
-    private val InnerTextFieldWidth = 100.dp
-
-    @Test
-    fun outlinedTextFieldBox_overrideTopPadding_multiLine() {
-        assertVerticalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(top = 10.dp),
-            singleLine = false,
-            expectedHeight = 10.dp + InnerTextFieldHeight + TextFieldPadding,
-            expectedPosition = 10.dp
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_overrideTopPadding_singleLine() {
-        assertVerticalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(top = 10.dp),
-            singleLine = true,
-            expectedHeight = 10.dp + InnerTextFieldHeight + TextFieldPadding,
-            expectedPosition = (10.dp + TextFieldPadding) / 2
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_overrideBottomPadding_multiLine() {
-        assertVerticalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(bottom = 10.dp),
-            singleLine = false,
-            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 10.dp,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_overrideBottomPadding_singleLine() {
-        assertVerticalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(bottom = 10.dp),
-            singleLine = true,
-            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 10.dp,
-            expectedPosition = (10.dp + TextFieldPadding) / 2
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_overrideStartPadding() {
-        assertHorizontalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(start = 10.dp),
-            rtl = false,
-            expectedWidth = 10.dp + InnerTextFieldWidth + TextFieldPadding,
-            expectedPosition = 10.dp
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_overrideStartPadding_rtl() {
-        assertHorizontalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(start = 10.dp),
-            rtl = true,
-            expectedWidth = 10.dp + InnerTextFieldWidth + TextFieldPadding,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_overrideEndPadding() {
-        assertHorizontalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(end = 20.dp),
-            rtl = false,
-            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 20.dp,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_overrideEndPadding_rtl() {
-        assertHorizontalSizeAndPosition_outlinedTextField(
-            padding = OutlinedTextFieldDefaults.contentPadding(end = 20.dp),
-            rtl = true,
-            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 20.dp,
-            expectedPosition = 20.dp
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideTopPadding_singleLine_withoutLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(top = 40.dp),
-            singleLine = true,
-            hasLabel = false,
-            expectedHeight = 40.dp + InnerTextFieldHeight + TextFieldPadding,
-            expectedPosition = (40.dp + TextFieldPadding) / 2
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideTopPadding_singleLine_withLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(top = 40.dp),
-            singleLine = true,
-            hasLabel = true,
-            expectedHeight =
-                40.dp + LabelHeight + InnerTextFieldHeight + TextFieldWithLabelVerticalPadding,
-            expectedPosition = 40.dp + LabelHeight
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideBottomPadding_singleLine_withoutLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(bottom = 40.dp),
-            singleLine = true,
-            hasLabel = false,
-            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 40.dp,
-            expectedPosition = (TextFieldPadding + 40.dp) / 2
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideBottomPadding_singleLine_withLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(bottom = 40.dp),
-            singleLine = true,
-            hasLabel = true,
-            expectedHeight =
-                TextFieldWithLabelVerticalPadding + LabelHeight + InnerTextFieldHeight + 40.dp,
-            expectedPosition = TextFieldWithLabelVerticalPadding + LabelHeight
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideTopPadding_multiLine_withoutLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(top = 40.dp),
-            singleLine = false,
-            hasLabel = false,
-            expectedHeight = 40.dp + InnerTextFieldHeight + TextFieldPadding,
-            expectedPosition = 40.dp
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideTopPadding_multiLine_withLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(top = 40.dp),
-            singleLine = false,
-            hasLabel = true,
-            expectedHeight =
-                40.dp + LabelHeight + InnerTextFieldHeight + TextFieldWithLabelVerticalPadding,
-            expectedPosition = 40.dp + LabelHeight
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideBottomPadding_multiLine_withoutLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(bottom = 40.dp),
-            singleLine = false,
-            hasLabel = false,
-            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 40.dp,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideBottomPadding_multiLine_withLabel() {
-        assertVerticalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(bottom = 40.dp),
-            singleLine = false,
-            hasLabel = true,
-            expectedHeight =
-                TextFieldWithLabelVerticalPadding + LabelHeight + InnerTextFieldHeight + 40.dp,
-            expectedPosition = TextFieldWithLabelVerticalPadding + LabelHeight
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideStartPadding_withLabel() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(start = 40.dp),
-            rtl = false,
-            hasLabel = true,
-            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
-            expectedPosition = 40.dp
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideStartPadding_withLabel_rtl() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(start = 40.dp),
-            rtl = true,
-            hasLabel = true,
-            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideStartPadding_withoutLabel() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(start = 40.dp),
-            rtl = false,
-            hasLabel = false,
-            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
-            expectedPosition = 40.dp
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideStartPadding_withoutLabel_rtl() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(start = 40.dp),
-            rtl = true,
-            hasLabel = false,
-            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideEndPadding_withLabel() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(end = 40.dp),
-            rtl = false,
-            hasLabel = true,
-            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideEndPadding_withLabel_rtl() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithLabel(end = 40.dp),
-            rtl = true,
-            hasLabel = true,
-            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
-            expectedPosition = 40.dp
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideEndPadding_withoutLabel() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(end = 40.dp),
-            rtl = false,
-            hasLabel = false,
-            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
-            expectedPosition = TextFieldPadding
-        )
-    }
-
-    @Test
-    fun textFieldBox_overrideEndPadding_withoutLabel_rtl() {
-        assertHorizontalSizeAndPosition_textField(
-            padding = TextFieldDefaults.contentPaddingWithoutLabel(end = 40.dp),
-            rtl = true,
-            hasLabel = false,
-            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
-            expectedPosition = 40.dp
-        )
-    }
-
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    @Test
-    fun outlinedTextFieldBox_defaultBorderColor_comesFromColors() {
-        val textFieldWidth = 300
-        val textFieldHeight = 150
-        val borderWidth = 40
-        val value = "Text"
-
-        rule.setMaterialContent(lightColorScheme()) {
-            CompositionLocalProvider(LocalDensity provides Density) {
-                val interactionSource = remember { MutableInteractionSource() }
-                val singleLine = true
-                val colors = OutlinedTextFieldDefaults.colors(unfocusedBorderColor = Color.Red)
-                BasicTextField(
-                    value = value,
-                    onValueChange = {},
-                    modifier =
-                        Modifier.size(
-                            with(Density) { textFieldWidth.toDp() },
-                            with(Density) { textFieldHeight.toDp() }
-                        ),
-                    singleLine = singleLine,
-                    interactionSource = interactionSource
-                ) {
-                    OutlinedTextFieldDefaults.DecorationBox(
-                        value = value,
-                        innerTextField = it,
-                        enabled = true,
-                        visualTransformation = VisualTransformation.None,
-                        interactionSource = interactionSource,
-                        singleLine = singleLine,
-                        container = {
-                            OutlinedTextFieldDefaults.Container(
-                                enabled = true,
-                                isError = false,
-                                colors = colors,
-                                interactionSource = interactionSource,
-                                shape = RectangleShape,
-                                unfocusedBorderThickness = with(Density) { borderWidth.toDp() }
-                            )
-                        },
-                        colors = colors,
-                        contentPadding = PaddingValues(0.dp)
-                    )
-                }
-            }
-        }
-
-        rule.onNodeWithText(value).captureToImage().assertPixels(
-            IntSize(textFieldWidth, textFieldHeight)
-        ) {
-            // to account for edge pixels
-            if (it.x in 2..(textFieldWidth - 2) && it.y in 2..(borderWidth - 2)) {
-                Color.Red
-            } else {
-                null
-            }
-        }
-    }
-
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    @Test
-    fun textFieldBox_defaultIndicatorLineColor_comesFromColors() {
-        val textFieldWidth = 300
-        val textFieldHeight = 150
-        val borderWidth = 40
-        val value = "Text"
-
-        rule.setMaterialContent(lightColorScheme()) {
-            CompositionLocalProvider(LocalDensity provides Density) {
-                val interactionSource = remember { MutableInteractionSource() }
-                val singleLine = true
-                val colors = TextFieldDefaults.colors(unfocusedIndicatorColor = Color.Red)
-                BasicTextField(
-                    value = value,
-                    onValueChange = {},
-                    modifier =
-                        Modifier.indicatorLine(
-                                enabled = true,
-                                isError = false,
-                                colors = colors,
-                                interactionSource = interactionSource,
-                                unfocusedIndicatorLineThickness =
-                                    with(Density) { borderWidth.toDp() }
-                            )
-                            .size(
-                                with(Density) { textFieldWidth.toDp() },
-                                with(Density) { textFieldHeight.toDp() }
-                            ),
-                    singleLine = singleLine,
-                    interactionSource = interactionSource
-                ) {
-                    TextFieldDefaults.DecorationBox(
-                        value = value,
-                        innerTextField = it,
-                        enabled = true,
-                        visualTransformation = VisualTransformation.None,
-                        interactionSource = interactionSource,
-                        singleLine = singleLine,
-                        colors = colors,
-                        contentPadding = PaddingValues(0.dp)
-                    )
-                }
-            }
-        }
-
-        rule.onNodeWithText(value).captureToImage().assertPixels(
-            IntSize(textFieldWidth, textFieldHeight)
-        ) {
-            // to account for edge pixels
-            if (
-                it.x in 2..(textFieldWidth - 2) &&
-                    it.y in (textFieldHeight - borderWidth + 2)..(textFieldHeight - 2)
-            ) {
-                Color.Red
-            } else {
-                null
-            }
-        }
-    }
-
-    @Test
-    fun textFieldBox_overridePadding_unfocusedState_withoutLabel_withPlaceholder() {
-        val placeholderDimension = 50.dp
-        val verticalPadding = 10.dp
-        val value = ""
-        var size: IntSize? = null
-
-        rule.setMaterialContent(lightColorScheme()) {
-            CompositionLocalProvider(LocalDensity provides Density) {
-                val interactionSource = remember { MutableInteractionSource() }
-                val singleLine = false
-                BasicTextField(
-                    value = value,
-                    onValueChange = {},
-                    modifier = Modifier.onSizeChanged { size = it },
-                    singleLine = singleLine,
-                    interactionSource = interactionSource
-                ) {
-                    TextFieldDefaults.DecorationBox(
-                        value = value,
-                        innerTextField = it,
-                        enabled = true,
-                        visualTransformation = VisualTransformation.None,
-                        interactionSource = interactionSource,
-                        singleLine = singleLine,
-                        placeholder = { Spacer(Modifier.size(placeholderDimension)) },
-                        contentPadding = PaddingValues(vertical = verticalPadding)
-                    )
-                }
-            }
-        }
-
-        rule.runOnIdle {
-            with(Density) {
-                assertThat(size).isNotNull()
-                assertThat(size!!.height)
-                    .isEqualTo((placeholderDimension + verticalPadding * 2).roundToPx())
-            }
-        }
-    }
-
-    @Test
-    fun outlinedTextFieldBox_innerTextLocation_withMultilineLabel() {
-        assertSizeAndPosition(
-            padding = OutlinedTextFieldDefaults.contentPadding(),
-            singleLine = false,
-            expectedSize = LabelHeight / 2 + InnerTextFieldHeight + TextFieldPadding,
-            expectedPosition = LabelHeight / 2,
-            isVertical = true,
-            isOutlined = true,
-            label = {
-                // imitates the multiline label
-                Box(Modifier.size(10.dp, LabelHeight))
-            }
-        )
-    }
-
-    @Test
-    fun outlinedTextFieldBox_singleLine_innerTextLocation_withMultilineLabel() {
-        assertSizeAndPosition(
-            padding = OutlinedTextFieldDefaults.contentPadding(),
-            singleLine = true,
-            expectedSize = LabelHeight / 2 + InnerTextFieldHeight + TextFieldPadding,
-            expectedPosition = LabelHeight / 2,
-            isVertical = true,
-            isOutlined = true,
-            label = {
-                // imitates the multiline label
-                Box(Modifier.size(10.dp, LabelHeight))
-            }
-        )
-    }
-
-    private fun assertVerticalSizeAndPosition_outlinedTextField(
-        padding: PaddingValues,
-        singleLine: Boolean,
-        expectedHeight: Dp,
-        expectedPosition: Dp
-    ) {
-        assertSizeAndPosition(
-            padding = padding,
-            singleLine = singleLine,
-            expectedSize = expectedHeight,
-            expectedPosition = expectedPosition,
-            isVertical = true,
-            isOutlined = true,
-        )
-    }
-
-    private fun assertHorizontalSizeAndPosition_outlinedTextField(
-        padding: PaddingValues,
-        rtl: Boolean,
-        expectedWidth: Dp,
-        expectedPosition: Dp
-    ) {
-        assertSizeAndPosition(
-            padding = padding,
-            singleLine = true,
-            expectedSize = expectedWidth,
-            expectedPosition = expectedPosition,
-            isVertical = false,
-            isOutlined = true,
-            layoutDirection = if (rtl) LayoutDirection.Rtl else LayoutDirection.Ltr
-        )
-    }
-
-    private fun assertVerticalSizeAndPosition_textField(
-        padding: PaddingValues,
-        singleLine: Boolean,
-        hasLabel: Boolean,
-        expectedHeight: Dp,
-        expectedPosition: Dp
-    ) {
-        assertSizeAndPosition(
-            padding = padding,
-            singleLine = singleLine,
-            expectedSize = expectedHeight,
-            expectedPosition = expectedPosition,
-            isVertical = true,
-            isOutlined = false,
-            label =
-                if (hasLabel) {
-                    { Text("Label", modifier = Modifier.height(LabelHeight)) }
-                } else {
-                    null
-                },
-        )
-    }
-
-    private fun assertHorizontalSizeAndPosition_textField(
-        padding: PaddingValues,
-        rtl: Boolean,
-        hasLabel: Boolean,
-        expectedWidth: Dp,
-        expectedPosition: Dp
-    ) {
-        assertSizeAndPosition(
-            padding = padding,
-            singleLine = true,
-            expectedSize = expectedWidth,
-            expectedPosition = expectedPosition,
-            isVertical = false,
-            isOutlined = false,
-            layoutDirection = if (rtl) LayoutDirection.Rtl else LayoutDirection.Ltr,
-            label =
-                if (hasLabel) {
-                    { Text("Label", modifier = Modifier.height(LabelHeight)) }
-                } else {
-                    null
-                },
-        )
-    }
-
-    private fun assertSizeAndPosition(
-        padding: PaddingValues,
-        singleLine: Boolean,
-        expectedSize: Dp,
-        expectedPosition: Dp,
-        isVertical: Boolean,
-        isOutlined: Boolean,
-        layoutDirection: LayoutDirection = LayoutDirection.Ltr,
-        label: @Composable (() -> Unit)? = null,
-    ) {
-        var size: IntSize? = null
-        var position: Offset? = null
-        val focusRequester = FocusRequester()
-        rule.setMaterialContent(lightColorScheme()) {
-            CompositionLocalProvider(
-                LocalLayoutDirection provides layoutDirection,
-                LocalDensity provides Density
-            ) {
-                Box(Modifier.onSizeChanged { size = it }) {
-                    val value = "Text"
-                    val interactionSource = remember { MutableInteractionSource() }
-                    BasicTextField(
-                        value = value,
-                        onValueChange = {},
-                        modifier = Modifier.focusRequester(focusRequester),
-                        singleLine = singleLine,
-                        interactionSource = interactionSource
-                    ) {
-                        val innerTextField: @Composable () -> Unit = {
-                            Box(
-                                Modifier.size(InnerTextFieldWidth, InnerTextFieldHeight)
-                                    .onGloballyPositioned { position = it.positionInRoot() }
-                            ) {
-                                it()
-                            }
-                        }
-                        if (isOutlined) {
-                            OutlinedTextFieldDefaults.DecorationBox(
-                                value = value,
-                                innerTextField = innerTextField,
-                                enabled = true,
-                                singleLine = singleLine,
-                                visualTransformation = VisualTransformation.None,
-                                interactionSource = interactionSource,
-                                contentPadding = padding,
-                                label = label
-                            )
-                        } else {
-                            TextFieldDefaults.DecorationBox(
-                                value = value,
-                                innerTextField = innerTextField,
-                                enabled = true,
-                                singleLine = singleLine,
-                                visualTransformation = VisualTransformation.None,
-                                interactionSource = interactionSource,
-                                contentPadding = padding,
-                                label = label
-                            )
-                        }
-                    }
-                }
-            }
-        }
-
-        rule.runOnUiThread { focusRequester.requestFocus() }
-
-        rule.runOnIdle {
-            with(Density) {
-                assertThat(size).isNotNull()
-                if (isVertical) {
-                    assertThat(size!!.height).isEqualTo(expectedSize.roundToPx())
-                } else {
-                    assertThat(size!!.width).isEqualTo(expectedSize.roundToPx())
-                }
-                assertThat(position).isNotNull()
-                if (isVertical) {
-                    assertThat(position!!.y.roundToInt()).isEqualTo(expectedPosition.roundToPx())
-                } else {
-                    assertThat(position!!.x.roundToInt()).isEqualTo(expectedPosition.roundToPx())
-                }
-            }
-        }
-    }
-
-    @Test
-    fun testTextFields_TextDecoration_noCrashConstraintsInfinity() {
-
-        rule.setMaterialContent(lightColorScheme()) {
-            Column(
-                modifier =
-                    Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
-            ) {
-                TextFieldDefaults.DecorationBox(
-                    value = "Hats",
-                    innerTextField = { Text("Cats") },
-                    enabled = true,
-                    singleLine = true,
-                    visualTransformation = VisualTransformation.None,
-                    interactionSource = remember { MutableInteractionSource() },
-                    suffix = { Text("Rats") },
-                    colors = TextFieldDefaults.colors(),
-                )
-            }
-        }
-
-        rule.runOnIdle {}
-    }
-}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecoratorTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecoratorTest.kt
new file mode 100644
index 0000000..812e06a
--- /dev/null
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecoratorTest.kt
@@ -0,0 +1,719 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.material3.TextFieldDefaults.indicatorLine
+import androidx.compose.material3.internal.TextFieldPadding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertPixels
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalMaterial3Api::class)
+class TextFieldDecoratorTest {
+    @get:Rule val rule = createComposeRule()
+
+    private val Density = Density(1f)
+    private val LabelHeight = 40.dp
+    private val InnerTextFieldHeight = 50.dp
+    private val InnerTextFieldWidth = 100.dp
+
+    @Test
+    fun outlinedTextFieldBox_overrideTopPadding_multiLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(top = 10.dp),
+            singleLine = false,
+            expectedHeight = 10.dp + InnerTextFieldHeight + TextFieldPadding,
+            expectedPosition = 10.dp
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideTopPadding_singleLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(top = 10.dp),
+            singleLine = true,
+            expectedHeight = 10.dp + InnerTextFieldHeight + TextFieldPadding,
+            expectedPosition = (10.dp + TextFieldPadding) / 2
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideBottomPadding_multiLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(bottom = 10.dp),
+            singleLine = false,
+            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 10.dp,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideBottomPadding_singleLine() {
+        assertVerticalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(bottom = 10.dp),
+            singleLine = true,
+            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 10.dp,
+            expectedPosition = (10.dp + TextFieldPadding) / 2
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideStartPadding() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(start = 10.dp),
+            rtl = false,
+            expectedWidth = 10.dp + InnerTextFieldWidth + TextFieldPadding,
+            expectedPosition = 10.dp
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideStartPadding_rtl() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(start = 10.dp),
+            rtl = true,
+            expectedWidth = 10.dp + InnerTextFieldWidth + TextFieldPadding,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideEndPadding() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(end = 20.dp),
+            rtl = false,
+            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 20.dp,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_overrideEndPadding_rtl() {
+        assertHorizontalSizeAndPosition_outlinedTextField(
+            padding = OutlinedTextFieldDefaults.contentPadding(end = 20.dp),
+            rtl = true,
+            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 20.dp,
+            expectedPosition = 20.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_singleLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(top = 40.dp),
+            singleLine = true,
+            hasLabel = false,
+            expectedHeight = 40.dp + InnerTextFieldHeight + TextFieldPadding,
+            expectedPosition = (40.dp + TextFieldPadding) / 2
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_singleLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(top = 40.dp),
+            singleLine = true,
+            hasLabel = true,
+            expectedHeight =
+                40.dp + LabelHeight + InnerTextFieldHeight + TextFieldWithLabelVerticalPadding,
+            expectedPosition = 40.dp + LabelHeight
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_singleLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(bottom = 40.dp),
+            singleLine = true,
+            hasLabel = false,
+            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 40.dp,
+            expectedPosition = (TextFieldPadding + 40.dp) / 2
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_singleLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(bottom = 40.dp),
+            singleLine = true,
+            hasLabel = true,
+            expectedHeight =
+                TextFieldWithLabelVerticalPadding + LabelHeight + InnerTextFieldHeight + 40.dp,
+            expectedPosition = TextFieldWithLabelVerticalPadding + LabelHeight
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_multiLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(top = 40.dp),
+            singleLine = false,
+            hasLabel = false,
+            expectedHeight = 40.dp + InnerTextFieldHeight + TextFieldPadding,
+            expectedPosition = 40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideTopPadding_multiLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(top = 40.dp),
+            singleLine = false,
+            hasLabel = true,
+            expectedHeight =
+                40.dp + LabelHeight + InnerTextFieldHeight + TextFieldWithLabelVerticalPadding,
+            expectedPosition = 40.dp + LabelHeight
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_multiLine_withoutLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(bottom = 40.dp),
+            singleLine = false,
+            hasLabel = false,
+            expectedHeight = TextFieldPadding + InnerTextFieldHeight + 40.dp,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideBottomPadding_multiLine_withLabel() {
+        assertVerticalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(bottom = 40.dp),
+            singleLine = false,
+            hasLabel = true,
+            expectedHeight =
+                TextFieldWithLabelVerticalPadding + LabelHeight + InnerTextFieldHeight + 40.dp,
+            expectedPosition = TextFieldWithLabelVerticalPadding + LabelHeight
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(start = 40.dp),
+            rtl = false,
+            hasLabel = true,
+            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
+            expectedPosition = 40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(start = 40.dp),
+            rtl = true,
+            hasLabel = true,
+            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withoutLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(start = 40.dp),
+            rtl = false,
+            hasLabel = false,
+            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
+            expectedPosition = 40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideStartPadding_withoutLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(start = 40.dp),
+            rtl = true,
+            hasLabel = false,
+            expectedWidth = 40.dp + InnerTextFieldWidth + TextFieldPadding,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(end = 40.dp),
+            rtl = false,
+            hasLabel = true,
+            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithLabel(end = 40.dp),
+            rtl = true,
+            hasLabel = true,
+            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            expectedPosition = 40.dp
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withoutLabel() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(end = 40.dp),
+            rtl = false,
+            hasLabel = false,
+            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            expectedPosition = TextFieldPadding
+        )
+    }
+
+    @Test
+    fun textFieldBox_overrideEndPadding_withoutLabel_rtl() {
+        assertHorizontalSizeAndPosition_textField(
+            padding = TextFieldDefaults.contentPaddingWithoutLabel(end = 40.dp),
+            rtl = true,
+            hasLabel = false,
+            expectedWidth = TextFieldPadding + InnerTextFieldWidth + 40.dp,
+            expectedPosition = 40.dp
+        )
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun outlinedTextFieldBox_defaultBorderColor_comesFromColors() {
+        val textFieldWidth = 300
+        val textFieldHeight = 150
+        val borderWidth = 40
+        val value = "Text"
+
+        rule.setMaterialContent(lightColorScheme()) {
+            CompositionLocalProvider(LocalDensity provides Density) {
+                val interactionSource = remember { MutableInteractionSource() }
+                val lineLimits = TextFieldLineLimits.SingleLine
+                val colors = OutlinedTextFieldDefaults.colors(unfocusedBorderColor = Color.Red)
+                val state = rememberTextFieldState(value)
+                BasicTextField(
+                    state = state,
+                    modifier =
+                        Modifier.size(
+                            with(Density) { textFieldWidth.toDp() },
+                            with(Density) { textFieldHeight.toDp() }
+                        ),
+                    lineLimits = lineLimits,
+                    interactionSource = interactionSource,
+                    decorator =
+                        OutlinedTextFieldDefaults.decorator(
+                            state = state,
+                            enabled = true,
+                            outputTransformation = null,
+                            interactionSource = interactionSource,
+                            lineLimits = lineLimits,
+                            container = {
+                                OutlinedTextFieldDefaults.Container(
+                                    enabled = true,
+                                    isError = false,
+                                    colors = colors,
+                                    interactionSource = interactionSource,
+                                    shape = RectangleShape,
+                                    unfocusedBorderThickness = with(Density) { borderWidth.toDp() }
+                                )
+                            },
+                            colors = colors,
+                            contentPadding = PaddingValues(0.dp),
+                        )
+                )
+            }
+        }
+
+        rule.onNodeWithText(value).captureToImage().assertPixels(
+            IntSize(textFieldWidth, textFieldHeight)
+        ) {
+            // to account for edge pixels
+            if (it.x in 2..(textFieldWidth - 2) && it.y in 2..(borderWidth - 2)) {
+                Color.Red
+            } else {
+                null
+            }
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun textFieldBox_defaultIndicatorLineColor_comesFromColors() {
+        val textFieldWidth = 300
+        val textFieldHeight = 150
+        val borderWidth = 40
+        val value = "Text"
+
+        rule.setMaterialContent(lightColorScheme()) {
+            CompositionLocalProvider(LocalDensity provides Density) {
+                val interactionSource = remember { MutableInteractionSource() }
+                val lineLimits = TextFieldLineLimits.SingleLine
+                val colors = TextFieldDefaults.colors(unfocusedIndicatorColor = Color.Red)
+                val state = rememberTextFieldState(value)
+                BasicTextField(
+                    state = state,
+                    modifier =
+                        Modifier.indicatorLine(
+                                enabled = true,
+                                isError = false,
+                                colors = colors,
+                                interactionSource = interactionSource,
+                                unfocusedIndicatorLineThickness =
+                                    with(Density) { borderWidth.toDp() }
+                            )
+                            .size(
+                                with(Density) { textFieldWidth.toDp() },
+                                with(Density) { textFieldHeight.toDp() }
+                            ),
+                    lineLimits = lineLimits,
+                    interactionSource = interactionSource,
+                    decorator =
+                        TextFieldDefaults.decorator(
+                            state = state,
+                            enabled = true,
+                            outputTransformation = null,
+                            interactionSource = interactionSource,
+                            lineLimits = lineLimits,
+                            colors = colors,
+                            contentPadding = PaddingValues(0.dp)
+                        )
+                )
+            }
+        }
+
+        rule.onNodeWithText(value).captureToImage().assertPixels(
+            IntSize(textFieldWidth, textFieldHeight)
+        ) {
+            // to account for edge pixels
+            if (
+                it.x in 2..(textFieldWidth - 2) &&
+                    it.y in (textFieldHeight - borderWidth + 2)..(textFieldHeight - 2)
+            ) {
+                Color.Red
+            } else {
+                null
+            }
+        }
+    }
+
+    @Test
+    fun textFieldBox_overridePadding_unfocusedState_withoutLabel_withPlaceholder() {
+        val placeholderDimension = 50.dp
+        val verticalPadding = 10.dp
+        val value = ""
+        var size: IntSize? = null
+
+        rule.setMaterialContent(lightColorScheme()) {
+            CompositionLocalProvider(LocalDensity provides Density) {
+                val interactionSource = remember { MutableInteractionSource() }
+                val lineLimits = TextFieldLineLimits.Default
+                val state = rememberTextFieldState(value)
+                BasicTextField(
+                    state = state,
+                    modifier = Modifier.onSizeChanged { size = it },
+                    lineLimits = lineLimits,
+                    interactionSource = interactionSource,
+                    decorator =
+                        TextFieldDefaults.decorator(
+                            state = state,
+                            enabled = true,
+                            outputTransformation = null,
+                            interactionSource = interactionSource,
+                            lineLimits = lineLimits,
+                            placeholder = { Spacer(Modifier.size(placeholderDimension)) },
+                            contentPadding = PaddingValues(vertical = verticalPadding)
+                        )
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            with(Density) {
+                assertThat(size).isNotNull()
+                assertThat(size!!.height)
+                    .isEqualTo((placeholderDimension + verticalPadding * 2).roundToPx())
+            }
+        }
+    }
+
+    @Test
+    fun outlinedTextFieldBox_innerTextLocation_withMultilineLabel() {
+        assertSizeAndPosition(
+            padding = OutlinedTextFieldDefaults.contentPadding(),
+            singleLine = false,
+            expectedSize = LabelHeight / 2 + InnerTextFieldHeight + TextFieldPadding,
+            expectedPosition = LabelHeight / 2,
+            isVertical = true,
+            isOutlined = true,
+            label = {
+                // imitates the multiline label
+                Box(Modifier.size(10.dp, LabelHeight))
+            }
+        )
+    }
+
+    @Test
+    fun outlinedTextFieldBox_singleLine_innerTextLocation_withMultilineLabel() {
+        assertSizeAndPosition(
+            padding = OutlinedTextFieldDefaults.contentPadding(),
+            singleLine = true,
+            expectedSize = LabelHeight / 2 + InnerTextFieldHeight + TextFieldPadding,
+            expectedPosition = LabelHeight / 2,
+            isVertical = true,
+            isOutlined = true,
+            label = {
+                // imitates the multiline label
+                Box(Modifier.size(10.dp, LabelHeight))
+            }
+        )
+    }
+
+    private fun assertVerticalSizeAndPosition_outlinedTextField(
+        padding: PaddingValues,
+        singleLine: Boolean,
+        expectedHeight: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition(
+            padding = padding,
+            singleLine = singleLine,
+            expectedSize = expectedHeight,
+            expectedPosition = expectedPosition,
+            isVertical = true,
+            isOutlined = true,
+        )
+    }
+
+    private fun assertHorizontalSizeAndPosition_outlinedTextField(
+        padding: PaddingValues,
+        rtl: Boolean,
+        expectedWidth: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition(
+            padding = padding,
+            singleLine = true,
+            expectedSize = expectedWidth,
+            expectedPosition = expectedPosition,
+            isVertical = false,
+            isOutlined = true,
+            layoutDirection = if (rtl) LayoutDirection.Rtl else LayoutDirection.Ltr
+        )
+    }
+
+    private fun assertVerticalSizeAndPosition_textField(
+        padding: PaddingValues,
+        singleLine: Boolean,
+        hasLabel: Boolean,
+        expectedHeight: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition(
+            padding = padding,
+            singleLine = singleLine,
+            expectedSize = expectedHeight,
+            expectedPosition = expectedPosition,
+            isVertical = true,
+            isOutlined = false,
+            label =
+                if (hasLabel) {
+                    { Text("Label", modifier = Modifier.height(LabelHeight)) }
+                } else {
+                    null
+                },
+        )
+    }
+
+    private fun assertHorizontalSizeAndPosition_textField(
+        padding: PaddingValues,
+        rtl: Boolean,
+        hasLabel: Boolean,
+        expectedWidth: Dp,
+        expectedPosition: Dp
+    ) {
+        assertSizeAndPosition(
+            padding = padding,
+            singleLine = true,
+            expectedSize = expectedWidth,
+            expectedPosition = expectedPosition,
+            isVertical = false,
+            isOutlined = false,
+            layoutDirection = if (rtl) LayoutDirection.Rtl else LayoutDirection.Ltr,
+            label =
+                if (hasLabel) {
+                    { Text("Label", modifier = Modifier.height(LabelHeight)) }
+                } else {
+                    null
+                },
+        )
+    }
+
+    private fun assertSizeAndPosition(
+        padding: PaddingValues,
+        singleLine: Boolean,
+        expectedSize: Dp,
+        expectedPosition: Dp,
+        isVertical: Boolean,
+        isOutlined: Boolean,
+        layoutDirection: LayoutDirection = LayoutDirection.Ltr,
+        label: @Composable (TextFieldLabelScope.() -> Unit)? = null,
+    ) {
+        var size: IntSize? = null
+        var position: Offset? = null
+        val focusRequester = FocusRequester()
+        rule.setMaterialContent(lightColorScheme()) {
+            CompositionLocalProvider(
+                LocalLayoutDirection provides layoutDirection,
+                LocalDensity provides Density
+            ) {
+                Box(Modifier.onSizeChanged { size = it }) {
+                    val value = "Text"
+                    val interactionSource = remember { MutableInteractionSource() }
+                    val state = rememberTextFieldState(value)
+                    val lineLimits =
+                        if (singleLine) TextFieldLineLimits.SingleLine
+                        else TextFieldLineLimits.Default
+                    BasicTextField(
+                        state = state,
+                        modifier = Modifier.focusRequester(focusRequester),
+                        lineLimits = lineLimits,
+                        interactionSource = interactionSource,
+                        decorator = {
+                            val innerTextField: @Composable () -> Unit = {
+                                Box(
+                                    Modifier.size(InnerTextFieldWidth, InnerTextFieldHeight)
+                                        .onGloballyPositioned { position = it.positionInRoot() }
+                                ) {
+                                    it()
+                                }
+                            }
+                            if (isOutlined) {
+                                OutlinedTextFieldDefaults.decorator(
+                                        state = state,
+                                        enabled = true,
+                                        lineLimits = lineLimits,
+                                        outputTransformation = null,
+                                        interactionSource = interactionSource,
+                                        contentPadding = padding,
+                                        label = label
+                                    )
+                                    .Decoration(innerTextField)
+                            } else {
+                                TextFieldDefaults.decorator(
+                                        state = state,
+                                        enabled = true,
+                                        lineLimits = lineLimits,
+                                        outputTransformation = null,
+                                        interactionSource = interactionSource,
+                                        contentPadding = padding,
+                                        label = label
+                                    )
+                                    .Decoration(innerTextField)
+                            }
+                        }
+                    )
+                }
+            }
+        }
+
+        rule.runOnUiThread { focusRequester.requestFocus() }
+
+        rule.runOnIdle {
+            with(Density) {
+                assertThat(size).isNotNull()
+                if (isVertical) {
+                    assertThat(size!!.height).isEqualTo(expectedSize.roundToPx())
+                } else {
+                    assertThat(size!!.width).isEqualTo(expectedSize.roundToPx())
+                }
+                assertThat(position).isNotNull()
+                if (isVertical) {
+                    assertThat(position!!.y.roundToInt()).isEqualTo(expectedPosition.roundToPx())
+                } else {
+                    assertThat(position!!.x.roundToInt()).isEqualTo(expectedPosition.roundToPx())
+                }
+            }
+        }
+    }
+
+    @Test
+    fun testTextFields_TextDecoration_noCrashConstraintsInfinity() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Column(
+                modifier =
+                    Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
+            ) {
+                TextFieldDefaults.decorator(
+                        state = rememberTextFieldState("Hats"),
+                        enabled = true,
+                        lineLimits = TextFieldLineLimits.SingleLine,
+                        outputTransformation = null,
+                        interactionSource = remember { MutableInteractionSource() },
+                        suffix = { Text("Rats") },
+                        colors = TextFieldDefaults.colors(),
+                    )
+                    .Decoration { Text("Cats") }
+            }
+        }
+
+        rule.runOnIdle {}
+    }
+}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt
index 50beab3..e953af3 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt
@@ -22,6 +22,8 @@
 import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.layout.requiredWidth
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.foundation.text.selection.TextSelectionColors
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Call
@@ -42,7 +44,6 @@
 import androidx.compose.ui.test.swipeLeft
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -54,20 +55,17 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(ExperimentalMaterial3Api::class)
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
 class TextFieldScreenshotTest {
     private val TextFieldTag = "TextField"
     private val longText =
-        TextFieldValue(
-            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
-                "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
-                " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
-                "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
-                "fugiat nulla pariatur."
-        )
+        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
+            "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
+            " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+            "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+            "fugiat nulla pariatur."
 
     private val platformTextStyle = defaultPlatformTextStyle()
 
@@ -78,11 +76,9 @@
     @Test
     fun textField_withInput() {
         rule.setMaterialContent(lightColorScheme()) {
-            Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
-                val text = "Text"
+            Box(Modifier.testTag(TextFieldTag)) {
                 TextField(
-                    value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                    onValueChange = {},
+                    state = rememberTextFieldState("Text"),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -95,10 +91,9 @@
     @Test
     fun textField_notFocused() {
         rule.setMaterialContent(lightColorScheme()) {
-            Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
+            Box(Modifier.testTag(TextFieldTag)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -111,10 +106,9 @@
     @Test
     fun textField_focused() {
         rule.setMaterialContent(lightColorScheme()) {
-            Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
+            Box(Modifier.testTag(TextFieldTag)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -130,10 +124,9 @@
     fun textField_focused_rtl() {
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
-                Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
+                Box(Modifier.testTag(TextFieldTag)) {
                     TextField(
-                        value = "",
-                        onValueChange = {},
+                        state = rememberTextFieldState(),
                         label = { Text("Label") },
                         modifier = Modifier.requiredWidth(280.dp)
                     )
@@ -149,10 +142,8 @@
     @Test
     fun textField_error_focused() {
         rule.setMaterialContent(lightColorScheme()) {
-            val text = "Input"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState("Input"),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -168,8 +159,7 @@
     fun textField_error_notFocused() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -184,8 +174,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Hello, world!"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 colors = TextFieldDefaults.colors(unfocusedTextColor = Color.Green)
             )
@@ -199,8 +188,11 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Hello, world!"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(0, text.length)),
-                onValueChange = {},
+                state =
+                    rememberTextFieldState(
+                        initialText = text,
+                        initialSelection = TextRange(0, text.length),
+                    ),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 colors =
                     TextFieldDefaults.colors(
@@ -223,8 +215,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -239,8 +230,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
             )
@@ -253,8 +243,7 @@
     fun textField_multiLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 placeholder = { Text("placeholder") },
                 modifier =
@@ -271,8 +260,7 @@
     fun textField_multiLine_withoutLabel_placeholderAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -288,8 +276,7 @@
     fun textField_multiLine_labelAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier =
                     Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -304,9 +291,8 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 label = { Text("Label") },
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
@@ -320,9 +306,8 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Text"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(text),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
         }
@@ -334,11 +319,10 @@
     fun textField_singleLine_withLabel_placeholderAlignedToTop() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
                 label = { Text("Label") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
         }
@@ -352,10 +336,9 @@
     fun textField_singleLine_withoutLabel_placeholderCenteredVertically() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("placeholder") },
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
         }
@@ -371,8 +354,7 @@
     fun textField_singleLine_labelCenteredVertically() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
             )
@@ -385,10 +367,9 @@
     fun textField_disabled() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false
             )
         }
@@ -400,9 +381,8 @@
     fun textField_disabled_notFocusable() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState("Text"),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 enabled = false
             )
@@ -417,9 +397,8 @@
     fun textField_disabled_notScrolled() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = longText,
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(longText),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
                 enabled = false
             )
@@ -440,8 +419,7 @@
     fun textField_readOnly() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 enabled = true,
                 readOnly = true
@@ -455,8 +433,7 @@
     fun textField_readOnly_focused() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
                 enabled = true,
                 readOnly = true
@@ -472,10 +449,9 @@
     fun textField_readOnly_scrolled() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = longText,
-                onValueChange = {},
+                state = rememberTextFieldState(longText),
                 modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = true,
                 readOnly = true
             )
@@ -496,12 +472,11 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Hello world"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 textStyle =
                     TextStyle(textAlign = TextAlign.Center, platformStyle = platformTextStyle),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -513,11 +488,10 @@
         rule.setMaterialContent(lightColorScheme()) {
             val text = "Hello world"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 modifier = Modifier.fillMaxWidth().testTag(TextFieldTag),
                 textStyle = TextStyle(textAlign = TextAlign.End, platformStyle = platformTextStyle),
-                singleLine = true
+                lineLimits = TextFieldLineLimits.SingleLine
             )
         }
 
@@ -528,10 +502,9 @@
     fun textField_supportingText() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 supportingText = { Text("Supporting text") }
             )
         }
@@ -543,11 +516,10 @@
     fun textField_errorSupportingText() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 supportingText = { Text("Error supporting text") }
             )
         }
@@ -559,8 +531,7 @@
     fun textField_leadingTrailingIcons() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -575,8 +546,7 @@
     fun textField_leadingTrailingIcons_error() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -589,11 +559,57 @@
     }
 
     @Test
+    fun textField_labelPositionAbove_withIcons_andPlaceholder_andSupporting() {
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = { Text("Label") },
+                labelPosition = TextFieldLabelPosition.Above,
+                leadingIcon = { Icon(Icons.Default.Call, null) },
+                trailingIcon = { Icon(Icons.Default.Clear, null) },
+                placeholder = { Text("Placeholder") },
+                supportingText = { Text("Supporting") },
+            )
+        }
+
+        assertAgainstGolden("textField_labelPositionAbove_withIcons_andPlaceholder_andSupporting")
+    }
+
+    @Test
+    fun textField_alwaysMinimizeLabel_noPlaceholder() {
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = { Text("Label") },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = true),
+            )
+        }
+
+        assertAgainstGolden("textField_alwaysMinimizeLabel_noPlaceholder")
+    }
+
+    @Test
+    fun textField_alwaysMinimizeLabel_withPlaceholder() {
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = { Text("Label") },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = true),
+                placeholder = { Text("Placeholder") },
+            )
+        }
+
+        assertAgainstGolden("textField_alwaysMinimizeLabel_withPlaceholder")
+    }
+
+    @Test
     fun textField_prefixSuffix_withLabelAndInput() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -608,8 +624,7 @@
     fun textField_prefixSuffix_withLabelAndInput_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             TextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -624,8 +639,7 @@
     fun textField_prefixSuffix_withLabelAndInput_focused() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -642,8 +656,7 @@
     fun textField_prefixSuffix_withPlaceholder() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = { Text("Placeholder") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -658,8 +671,7 @@
     fun textField_prefixSuffix_withLeadingTrailingIcons() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "Text",
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 prefix = { Text("P:") },
@@ -678,8 +690,7 @@
             Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
                 val text = "Text"
                 TextField(
-                    value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                    onValueChange = {},
+                    state = rememberTextFieldState(text),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -694,8 +705,7 @@
         rule.setMaterialContent(darkColorScheme()) {
             Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = { Text("Label") },
                     modifier = Modifier.requiredWidth(280.dp)
                 )
@@ -712,8 +722,7 @@
         rule.setMaterialContent(darkColorScheme()) {
             val text = "Input"
             TextField(
-                value = TextFieldValue(text = text, selection = TextRange(text.length)),
-                onValueChange = {},
+                state = rememberTextFieldState(text),
                 label = { Text("Label") },
                 isError = true,
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -729,10 +738,9 @@
     fun textField_disabled_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             TextField(
-                value = TextFieldValue("Text"),
-                onValueChange = {},
+                state = rememberTextFieldState("Text"),
                 modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
-                singleLine = true,
+                lineLimits = TextFieldLineLimits.SingleLine,
                 enabled = false
             )
         }
@@ -744,8 +752,7 @@
     fun textField_leadingTrailingIcons_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
                 modifier = Modifier.width(300.dp).testTag(TextFieldTag),
                 leadingIcon = { Icon(Icons.Default.Call, null) },
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
index c934995..8c4e554 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:Suppress("DEPRECATION")
-
 package androidx.compose.material3
 
 import android.content.Context
@@ -38,18 +36,27 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.delete
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.placeCursorAtEnd
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.TextSelectionColors
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material3.internal.AboveLabelBottomPadding
+import androidx.compose.material3.internal.AboveLabelHorizontalPadding
 import androidx.compose.material3.internal.HorizontalIconPadding
 import androidx.compose.material3.internal.MinFocusedLabelLineHeight
 import androidx.compose.material3.internal.MinSupportingTextLineHeight
 import androidx.compose.material3.internal.MinTextLineHeight
 import androidx.compose.material3.internal.Strings.Companion.DefaultErrorMessage
 import androidx.compose.material3.internal.SupportingTopPadding
-import androidx.compose.material3.internal.TextFieldAnimationDuration
 import androidx.compose.material3.internal.TextFieldPadding
 import androidx.compose.material3.internal.getString
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -65,6 +72,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.node.Ref
@@ -84,6 +92,7 @@
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.click
@@ -97,14 +106,10 @@
 import androidx.compose.ui.test.performTextInput
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.OffsetMapping
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.TransformedText
-import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.height
@@ -138,7 +143,10 @@
     fun testTextField_setSmallHeight() {
         rule
             .setMaterialContentForSizeAssertions {
-                TextField(value = "input", onValueChange = {}, modifier = Modifier.height(20.dp))
+                TextField(
+                    state = rememberTextFieldState("input"),
+                    modifier = Modifier.height(20.dp)
+                )
             }
             .assertHeightIsEqualTo(20.dp)
     }
@@ -148,8 +156,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 TextField(
-                    value = "input",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input"),
                     modifier = Modifier.requiredWidth(40.dp)
                 )
             }
@@ -161,8 +168,7 @@
         rule
             .setMaterialContentForSizeAssertions {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     textStyle = TextStyle(fontSize = 1.sp), // ensure text size is minimum
                 )
             }
@@ -172,7 +178,7 @@
     @Test
     fun testTextField_defaultWidth() {
         rule
-            .setMaterialContentForSizeAssertions { TextField(value = "input", onValueChange = {}) }
+            .setMaterialContentForSizeAssertions { TextField(rememberTextFieldState("input")) }
             .assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
     }
 
@@ -185,8 +191,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Text("Label") },
             )
         }
@@ -206,6 +211,81 @@
     }
 
     @Test
+    fun testTextField_heightDoesNotChange_duringFocusAnimation_withLargeLabelText() {
+        val numTicks = 5
+        val tick = TextFieldAnimationDuration / numTicks
+        val tfHeight = Ref<Dp>()
+        rule.mainClock.autoAdvance = false
+
+        rule.setMaterialContent(lightColorScheme()) {
+            MaterialTheme(
+                typography =
+                    MaterialTheme.typography.copy(bodyLarge = MaterialTheme.typography.displayLarge)
+            ) {
+                val density = LocalDensity.current
+                TextField(
+                    modifier =
+                        Modifier.testTag(TextFieldTag).onGloballyPositioned {
+                            if (tfHeight.value == null) {
+                                tfHeight.value = with(density) { it.size.height.toDp() }
+                            }
+                        },
+                    state = rememberTextFieldState(),
+                    label = { Text("Label") },
+                )
+            }
+        }
+
+        // click to focus
+        rule.onNodeWithTag(TextFieldTag).performClick()
+
+        repeat(numTicks + 1) {
+            if (tfHeight.value != null) {
+                rule
+                    .onNodeWithTag(TextFieldTag)
+                    .getBoundsInRoot()
+                    .height
+                    .assertIsEqualTo(tfHeight.value!!)
+            }
+
+            rule.mainClock.advanceTimeBy(tick.toLong())
+        }
+    }
+
+    @Test
+    fun testTextField_withSupportingText_heightDoesNotChange_duringFocusAnimation() {
+        val numTicks = 5
+        val tick = TextFieldAnimationDuration / numTicks
+        rule.mainClock.autoAdvance = false
+
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                modifier = Modifier.testTag(TextFieldTag),
+                state = rememberTextFieldState(),
+                label = { Text("Label") },
+                supportingText = { Text("Supporting") }
+            )
+        }
+
+        // click to focus
+        rule.onNodeWithTag(TextFieldTag).performClick()
+
+        repeat(numTicks + 1) {
+            rule
+                .onNodeWithTag(TextFieldTag)
+                .getBoundsInRoot()
+                .height
+                .assertIsEqualTo(
+                    ExpectedDefaultTextFieldHeight +
+                        SupportingTopPadding +
+                        MinSupportingTextLineHeight
+                )
+
+            rule.mainClock.advanceTimeBy(tick.toLong())
+        }
+    }
+
+    @Test
     fun testTextFields_singleFocus() {
         val textField1Tag = "TextField1"
         val textField2Tag = "TextField2"
@@ -219,14 +299,12 @@
             Column {
                 TextField(
                     modifier = Modifier.testTag(textField1Tag),
-                    value = "input1",
-                    onValueChange = {},
-                    interactionSource = interactionSource1
+                    state = rememberTextFieldState("input1"),
+                    interactionSource = interactionSource1,
                 )
                 TextField(
                     modifier = Modifier.testTag(textField2Tag),
-                    value = "input2",
-                    onValueChange = {},
+                    state = rememberTextFieldState("input2"),
                     interactionSource = interactionSource2
                 )
             }
@@ -271,8 +349,7 @@
             scope = rememberCoroutineScope()
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 interactionSource = interactionSource
             )
         }
@@ -305,8 +382,7 @@
                             .focusTarget()
                             .focusRequester(focusRequester)
                             .testTag(TextFieldTag),
-                    value = "input",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input"),
                 )
             }
         }
@@ -336,8 +412,7 @@
                             .focusTarget()
                             .focusRequester(focusRequester)
                             .testTag(TextFieldTag),
-                    value = "input",
-                    onValueChange = {}
+                    state = rememberTextFieldState("input"),
                 )
             }
         }
@@ -359,9 +434,8 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
-                singleLine = true,
+                state = rememberTextFieldState(),
+                lineLimits = TextFieldLineLimits.SingleLine,
                 label = {
                     Box(
                         Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -387,8 +461,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Box(
                         Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -413,8 +486,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.height(height),
                 label = {
                     Box(
@@ -440,8 +512,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Box(
                         Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
@@ -470,8 +541,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 label = {
                     Box(
                         Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
@@ -493,13 +563,69 @@
     }
 
     @Test
+    fun testTextField_labelPosition_whenPositionedAbove() {
+        val labelPosition = Ref<Offset>()
+        rule
+            .setMaterialContentForSizeAssertions {
+                TextField(
+                    state = rememberTextFieldState(),
+                    label = {
+                        Box(
+                            Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
+                                labelPosition.value = it.positionInRoot()
+                            }
+                        )
+                    },
+                    labelPosition = TextFieldLabelPosition.Above,
+                )
+            }
+            .assertHeightIsEqualTo(
+                MinFocusedLabelLineHeight + AboveLabelBottomPadding + ExpectedDefaultTextFieldHeight
+            )
+
+        rule.runOnIdleWithDensity {
+            // x position is padding
+            assertThat(labelPosition.value?.x).isWithin(1f).of(AboveLabelHorizontalPadding.toPx())
+            // y position is 0
+            assertThat(labelPosition.value?.y).isEqualTo(0f)
+        }
+    }
+
+    @Test
+    fun testTextField_labelScope_progressAndRecomposition() {
+        val progressValue = Ref<Float>()
+        var compositionCount = 0
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                state = rememberTextFieldState(),
+                modifier = Modifier.testTag(TextFieldTag),
+                label = {
+                    SideEffect { compositionCount++ }
+
+                    // lambda reads `progress` in the draw phase
+                    Box(Modifier.graphicsLayer { progressValue.value = progress })
+                }
+            )
+        }
+
+        assertThat(progressValue.value).isEqualTo(0f)
+        assertThat(compositionCount).isEqualTo(1)
+
+        // click to focus
+        rule.onNodeWithTag(TextFieldTag).performClick()
+        rule.waitForIdle()
+
+        assertThat(progressValue.value).isEqualTo(1f)
+        assertThat(compositionCount).isEqualTo(1)
+    }
+
+    @Test
     fun testTextField_placeholderPosition_withLabel() {
         val placeholderPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
                 placeholder = {
                     Box(
@@ -530,8 +656,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = {
                     Box(
                         Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -560,8 +685,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 placeholder = {
                     Text(
                         text = "placeholder",
@@ -589,8 +713,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 placeholder = {
                     Text("placeholder")
                     assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodyLarge)
@@ -606,11 +729,9 @@
     fun testTextField_placeholderColor_whenInputEmptyAndFocused() {
         var focused = false
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = text.value,
-                onValueChange = { text.value = it },
+                state = rememberTextFieldState(),
                 colors =
                     TextFieldDefaults.colors(
                         focusedPlaceholderColor = Color.Red,
@@ -646,8 +767,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Box(
                         Modifier.size(labelSize).onGloballyPositioned {
@@ -711,8 +831,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.size(TextFieldWidth, textFieldHeight),
                     leadingIcon = {
                         Icon(
@@ -783,8 +902,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.size(TextFieldWidth, textFieldHeight),
                     leadingIcon = {
                         IconButton(
@@ -855,8 +973,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.size(TextFieldWidth, textFieldHeight),
                     leadingIcon = {
                         Box(
@@ -904,6 +1021,76 @@
     }
 
     @Test
+    fun testTextField_prefixAndSuffixAndPlaceholder_areNotDisplayed_withLabel_ifLabelCanExpand() {
+        val labelText = "Label"
+        val prefixText = "Prefix"
+        val suffixText = "Suffix"
+        val placeholderText = "Placeholder"
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                state = rememberTextFieldState(),
+                label = { Text(labelText) },
+                prefix = { Text(prefixText) },
+                suffix = { Text(suffixText) },
+                placeholder = { Text(placeholderText) },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = false),
+            )
+        }
+
+        rule.onNodeWithText(labelText).assertIsDisplayed()
+
+        rule.onNodeWithText(prefixText).assertIsNotDisplayed()
+        rule.onNodeWithText(suffixText).assertIsNotDisplayed()
+        rule.onNodeWithText(placeholderText).assertIsNotDisplayed()
+    }
+
+    @Test
+    fun testTextField_prefixAndSuffixAndPlaceholder_areDisplayed_withLabel_ifLabelCannotExpand() {
+        val labelText = "Label"
+        val prefixText = "Prefix"
+        val suffixText = "Suffix"
+        val placeholderText = "Placeholder"
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                state = rememberTextFieldState(),
+                label = { Text(labelText) },
+                prefix = { Text(prefixText) },
+                suffix = { Text(suffixText) },
+                placeholder = { Text(placeholderText) },
+                labelPosition = TextFieldLabelPosition.Default(alwaysMinimize = true),
+            )
+        }
+
+        rule.onNodeWithText(labelText).assertIsDisplayed()
+        rule.onNodeWithText(prefixText).assertIsDisplayed()
+        rule.onNodeWithText(suffixText).assertIsDisplayed()
+        rule.onNodeWithText(placeholderText).assertIsDisplayed()
+    }
+
+    @Test
+    fun testTextField_prefixAndSuffixAndPlaceholder_areDisplayed_withLabel_ifLabelIsAbove() {
+        val labelText = "Label"
+        val prefixText = "Prefix"
+        val suffixText = "Suffix"
+        val placeholderText = "Placeholder"
+        rule.setMaterialContent(lightColorScheme()) {
+            TextField(
+                state = rememberTextFieldState(),
+                label = { Text(labelText) },
+                prefix = { Text(prefixText) },
+                suffix = { Text(suffixText) },
+                placeholder = { Text(placeholderText) },
+                labelPosition = TextFieldLabelPosition.Above,
+            )
+        }
+
+        rule.onNodeWithText(labelText).assertIsDisplayed()
+        rule.onNodeWithText(prefixText).assertIsDisplayed()
+        rule.onNodeWithText(suffixText).assertIsDisplayed()
+        rule.onNodeWithText(placeholderText).assertIsDisplayed()
+    }
+
+    @Test
     fun testTextField_prefixAndSuffixPosition_withLabel() {
         val prefixPosition = Ref<Offset>()
         val prefixSize = MinTextLineHeight
@@ -914,8 +1101,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(TextFieldWidth),
                     label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
                     prefix = {
@@ -966,8 +1152,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(TextFieldWidth),
                     prefix = {
                         Box(
@@ -1013,8 +1198,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             CompositionLocalProvider(LocalDensity provides density) {
                 TextField(
-                    value = "text",
-                    onValueChange = {},
+                    state = rememberTextFieldState("text"),
                     modifier = Modifier.width(TextFieldWidth),
                     prefix = {
                         Box(
@@ -1063,8 +1247,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Text(
                         text = "label",
@@ -1092,8 +1275,7 @@
         val labelPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     Text(
                         text = "label",
@@ -1117,8 +1299,7 @@
     fun testTextField_colorInLeadingTrailing_whenValidInput() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = false,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -1136,8 +1317,7 @@
     fun testTextField_colorInLeadingTrailing_whenInvalidInput() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 leadingIcon = {
                     assertThat(LocalContentColor.current)
@@ -1155,8 +1335,7 @@
         val supportingPosition = Ref<Offset>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 textStyle = TextStyle(fontSize = 1.sp), // ensure text size is minimum
                 supportingText = {
                     Box(
@@ -1182,8 +1361,7 @@
         val supportingSize = Ref<IntSize>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
                 supportingText = {
                     Text(
@@ -1206,8 +1384,7 @@
         val tfSize = Ref<IntSize>()
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
                 supportingText = { Text("Supporting") }
             )
@@ -1223,8 +1400,8 @@
     fun testTextField_supportingText_remainsVisibleWithTallInput() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = buildString { repeat(200) { append("line $it\n") } },
-                onValueChange = {},
+                state =
+                    rememberTextFieldState(buildString { repeat(200) { append("line $it\n") } }),
                 modifier = Modifier.size(width = ExpectedDefaultTextFieldWidth, height = 150.dp),
                 supportingText = { Text("Supporting", modifier = Modifier.testTag("Supporting")) }
             )
@@ -1239,8 +1416,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.onFocusChanged { focused = it.isFocused },
-                value = "input",
-                onValueChange = {},
+                state = rememberTextFieldState("input"),
                 supportingText = { Text("Supporting") }
             )
         }
@@ -1253,8 +1429,7 @@
     fun testTextField_supportingText_colorAndStyle() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 supportingText = {
                     assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
                     assertThat(LocalContentColor.current)
@@ -1268,8 +1443,7 @@
     fun testTextField_supportingText_error_colorAndStyle() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 isError = true,
                 supportingText = {
                     assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
@@ -1292,12 +1466,10 @@
             awaitCancellation()
         }
         rule.setContent {
-            val text = remember { mutableStateOf("") }
             InterceptPlatformTextInput(interceptor) {
-                OutlinedTextField(
+                TextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = text.value,
-                    onValueChange = { text.value = it },
+                    state = rememberTextFieldState(),
                     keyboardOptions =
                         KeyboardOptions(imeAction = ImeAction.Go, keyboardType = KeyboardType.Email)
                 )
@@ -1320,13 +1492,18 @@
     @Test
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun testTextField_visualTransformationPropagated() {
+    fun testTextField_outputTransformationPropagated() {
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "qwerty",
-                onValueChange = {},
-                visualTransformation = PasswordVisualTransformation('\u0020'),
+                state = rememberTextFieldState("qwerty"),
+                outputTransformation = {
+                    // transform all chars to blank spaces
+                    val size = length
+                    delete(0, length)
+                    insert(0, " ".repeat(size))
+                    placeCursorAtEnd()
+                },
                 shape = RectangleShape,
                 colors = TextFieldDefaults.colors(unfocusedContainerColor = Color.White)
             )
@@ -1354,8 +1531,7 @@
             Box(Modifier.background(color = Color.White)) {
                 TextField(
                     modifier = Modifier.testTag(TextFieldTag),
-                    value = "test",
-                    onValueChange = {},
+                    state = rememberTextFieldState("test"),
                     label = { Text("label") },
                     shape = RectangleShape,
                     leadingIcon = { Icon(Icons.Default.Favorite, null, tint = Color.Transparent) },
@@ -1407,28 +1583,12 @@
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testTextField_transformedTextIsUsed_toDefineLabelPosition() {
-        // if non-transformed value were used to check if the text input is empty, the label
-        // wouldn't be aligned to the top, as a result it would be obscured by text
-        val prefixTransformation = VisualTransformation { text ->
-            val prefix = "prefix"
-            val transformed = buildAnnotatedString {
-                append(prefix)
-                append(text)
-            }
-            val mapping =
-                object : OffsetMapping {
-                    override fun originalToTransformed(offset: Int) = offset + prefix.length
-
-                    override fun transformedToOriginal(offset: Int) =
-                        (offset - prefix.length).coerceAtLeast(0)
-                }
-            TransformedText(transformed, mapping)
-        }
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
-                visualTransformation = prefixTransformation,
+                state = rememberTextFieldState(),
+                // if non-transformed value were used to check if the text input is empty, the label
+                // wouldn't be aligned to the top, as a result it would be obscured by text
+                outputTransformation = { insert(0, "prefix") },
                 label = {
                     Text("label", color = Color.Red, modifier = Modifier.background(Color.Red))
                 },
@@ -1446,31 +1606,15 @@
     @LargeTest
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun testTextField_transformedTextIsUsed_toDefineIfPlaceholderNeeded() {
-        // if original value were used to check if the text input is empty, the placeholder would be
-        // displayed on top of the text
-        val prefixTransformation = VisualTransformation { text ->
-            val prefix = "prefix"
-            val transformed = buildAnnotatedString {
-                append(prefix)
-                append(text)
-            }
-            val mapping =
-                object : OffsetMapping {
-                    override fun originalToTransformed(offset: Int) = offset + prefix.length
-
-                    override fun transformedToOriginal(offset: Int) =
-                        (offset - prefix.length).coerceAtLeast(0)
-                }
-            TransformedText(transformed, mapping)
-        }
         // While surface is not used in TextField, setMaterialContent wraps content in a Surface
         // component which is checked during assertPixels.
         rule.setMaterialContent(lightColorScheme(surface = Color.White)) {
             TextField(
                 modifier = Modifier.testTag(TextFieldTag),
-                value = "",
-                onValueChange = {},
-                visualTransformation = prefixTransformation,
+                state = rememberTextFieldState(),
+                // if original value were used to check if the text input is empty, the placeholder
+                // would be displayed on top of the text
+                outputTransformation = { insert(0, "prefix") },
                 placeholder = {
                     Text(
                         text = "placeholder",
@@ -1493,7 +1637,7 @@
     fun testTextField_errorSemantics_defaultMessage() {
         lateinit var errorMessage: String
         rule.setMaterialContent(lightColorScheme()) {
-            TextField(value = "test", onValueChange = {}, isError = true)
+            TextField(state = rememberTextFieldState("test"), isError = true)
             errorMessage = getString(DefaultErrorMessage)
         }
 
@@ -1510,8 +1654,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             val isError = remember { mutableStateOf(true) }
             TextField(
-                value = "test",
-                onValueChange = {},
+                state = rememberTextFieldState("test"),
                 modifier =
                     Modifier.testTag(TextFieldTag).semantics {
                         if (isError.value) error(errorMessage)
@@ -1548,7 +1691,10 @@
                         thickness = 10.dp,
                         modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
                     )
-                    TextField(value = "", label = { Text(text = "Label") }, onValueChange = {})
+                    TextField(
+                        state = rememberTextFieldState(),
+                        label = { Text(text = "Label") },
+                    )
                 }
             }
         }
@@ -1565,7 +1711,6 @@
         var textFieldSize: IntSize? = null
         var dividerSize: IntSize? = null
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Box {
                 Column(Modifier.width(IntrinsicSize.Min)) {
                     HorizontalDivider(
@@ -1573,9 +1718,8 @@
                         modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
                     )
                     TextField(
-                        value = text.value,
+                        state = rememberTextFieldState(),
                         label = { Text(text = "Label") },
-                        onValueChange = { text.value = it },
                         modifier = Modifier.onGloballyPositioned { textFieldSize = it.size }
                     )
                 }
@@ -1600,8 +1744,7 @@
 
         rule.setMaterialContent(lightColorScheme()) {
             TextField(
-                value = "",
-                onValueChange = {},
+                state = rememberTextFieldState(),
                 label = {
                     textStyle = LocalTextStyle.current
                     contentColor = LocalContentColor.current
@@ -1642,8 +1785,7 @@
             val bodySmall = MaterialTheme.typography.bodySmall.copy(color = bodySmallColor)
             MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1684,8 +1826,7 @@
             val bodySmall = MaterialTheme.typography.bodySmall.copy(color = expectedLabelColor)
             MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1703,7 +1844,7 @@
         rule.runOnUiThread { focusRequester.requestFocus() }
 
         // advance to middle of animation
-        rule.mainClock.advanceTimeBy(TextFieldAnimationDuration.toLong() / 2)
+        rule.mainClock.advanceTimeBy(TextFieldAnimationDuration)
 
         rule.runOnIdle {
             assertThat(textStyle.color).isEqualTo(expectedLabelColor)
@@ -1729,8 +1870,7 @@
             val bodyLarge = MaterialTheme.typography.bodyLarge.copy(color = bodyLargeColor)
             MaterialTheme(typography = Typography(bodySmall = bodySmall, bodyLarge = bodyLarge)) {
                 TextField(
-                    value = "",
-                    onValueChange = {},
+                    state = rememberTextFieldState(),
                     label = {
                         textStyle = LocalTextStyle.current
                         contentColor = LocalContentColor.current
@@ -1759,16 +1899,71 @@
     }
 
     @Test
+    fun testTextField_selectionColors_areCustomizable() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Column {
+                // default colors
+                TextField(
+                    state = rememberTextFieldState(),
+                    label = {
+                        val textSelectionColors = LocalTextSelectionColors.current
+                        assertThat(textSelectionColors.handleColor)
+                            .isEqualTo(MaterialTheme.colorScheme.primary)
+                        assertThat(textSelectionColors.backgroundColor)
+                            .isEqualTo(
+                                MaterialTheme.colorScheme.primary.copy(
+                                    alpha = TextSelectionBackgroundOpacity
+                                )
+                            )
+                    }
+                )
+
+                // set via `colors()`
+                TextField(
+                    state = rememberTextFieldState(),
+                    colors =
+                        TextFieldDefaults.colors(
+                            selectionColors =
+                                TextSelectionColors(
+                                    handleColor = Color.Red,
+                                    backgroundColor = Color.Green
+                                )
+                        ),
+                    label = {
+                        val textSelectionColors = LocalTextSelectionColors.current
+                        assertThat(textSelectionColors.handleColor).isEqualTo(Color.Red)
+                        assertThat(textSelectionColors.backgroundColor).isEqualTo(Color.Green)
+                    }
+                )
+
+                // set via `LocalTextSelectionColors`
+                CompositionLocalProvider(
+                    LocalTextSelectionColors provides
+                        TextSelectionColors(
+                            handleColor = Color.Magenta,
+                            backgroundColor = Color.Yellow
+                        )
+                ) {
+                    TextField(
+                        state = rememberTextFieldState(),
+                        label = {
+                            val textSelectionColors = LocalTextSelectionColors.current
+                            assertThat(textSelectionColors.handleColor).isEqualTo(Color.Magenta)
+                            assertThat(textSelectionColors.backgroundColor).isEqualTo(Color.Yellow)
+                        }
+                    )
+                }
+            }
+        }
+    }
+
+    @Test
     fun testTextField_intrinsicHeight_withOnlyEmptyInput() {
         var height = 0
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
-                    TextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
-                    )
+                    TextField(rememberTextFieldState())
                     VerticalDivider()
                 }
             }
@@ -1783,12 +1978,10 @@
     fun testTextField_intrinsicHeight_withEmptyInput_andDecorations() {
         var height = 0
         rule.setMaterialContent(lightColorScheme()) {
-            val text = remember { mutableStateOf("") }
             Box(Modifier.onGloballyPositioned { height = it.size.height }) {
                 Row(Modifier.height(IntrinsicSize.Min)) {
                     TextField(
-                        value = text.value,
-                        onValueChange = { text.value = it },
+                        state = rememberTextFieldState(),
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
                         trailingIcon = { Icon(Icons.Default.Favorite, null) },
                         prefix = { Text("P") },
@@ -1813,8 +2006,7 @@
             Row {
                 Box(Modifier.width(150.dp).height(IntrinsicSize.Min)) {
                     TextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1826,8 +2018,7 @@
 
                 Box(Modifier.width(150.dp)) {
                     TextField(
-                        value = text,
-                        onValueChange = {},
+                        state = rememberTextFieldState(text),
                         modifier =
                             Modifier.onGloballyPositioned { tfHeightNoIntrinsic = it.size.height },
                         leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1875,14 +2066,13 @@
     }
 
     @Test
-    fun testTextFields_noCrashConstraintsInfinity() {
-
+    fun testTextField_noCrashConstraintsInfinity() {
         rule.setMaterialContent(lightColorScheme()) {
             Column(
                 modifier =
                     Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
             ) {
-                TextField(value = "Cat", onValueChange = {}, leadingIcon = { Text("Icon") })
+                TextField(state = rememberTextFieldState(), leadingIcon = { Text("Icon") })
             }
         }
     }
@@ -1896,3 +2086,6 @@
         //  way to check if the software keyboard is shown.
         return inputMethodManager.isAcceptingText()
     }
+
+// We use springs to animate, so picking an arbitrary duration that work.
+private const val TextFieldAnimationDuration = 75L
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TimePickerTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TimePickerTest.kt
index 0dbe53e3..d08e5fb 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TimePickerTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TimePickerTest.kt
@@ -458,11 +458,11 @@
 
         rule.setMaterialContent(lightColorScheme()) { TimeInput(state) }
 
-        assertThat(state.isAfternoon).isTrue()
+        assertThat(state.isPm).isTrue()
 
         rule.onNodeWithText("11").performKeyInput { pressKey(Key.Four) }
 
-        assertThat(state.isAfternoon).isTrue()
+        assertThat(state.isPm).isTrue()
     }
 
     @OptIn(ExperimentalTestApi::class)
@@ -472,14 +472,14 @@
 
         rule.setMaterialContent(lightColorScheme()) { TimeInput(state) }
 
-        assertThat(state.isAfternoon).isTrue()
+        assertThat(state.isPm).isTrue()
 
         rule.onNodeWithText("11").performKeyInput {
             pressKey(Key.Delete)
             pressKey(Key.Delete)
         }
 
-        assertThat(state.isAfternoon).isTrue()
+        assertThat(state.isPm).isTrue()
     }
 
     @Test
@@ -495,7 +495,7 @@
 
     @Test
     @OptIn(ExperimentalTestApi::class)
-    fun timeInput_24Hour_writeAfternoonHour() {
+    fun timeInput_24Hour_writePmHour() {
         val state = TimePickerState(initialHour = 10, initialMinute = 23, is24Hour = true)
 
         rule.setMaterialContent(lightColorScheme()) { TimeInput(state) }
@@ -510,7 +510,7 @@
 
     @Test
     @OptIn(ExperimentalTestApi::class)
-    fun timeInput_24HourStartingAfternoon_writeAfternoonHour() {
+    fun timeInput_24HourStartingPm_writePmHour() {
         val state = TimePickerState(initialHour = 20, initialMinute = 23, is24Hour = true)
 
         rule.setMaterialContent(lightColorScheme()) { TimeInput(state) }
@@ -596,22 +596,22 @@
     }
 
     @Test
-    fun state_setHour_updatesIsAfternoon() {
+    fun state_setHour_updatesIsPm() {
         val state = TimePickerState(initialHour = 8, initialMinute = 0, is24Hour = false)
         state.hour = 20
 
-        assertThat(state.isAfternoon).isTrue()
+        assertThat(state.isPm).isTrue()
     }
 
     @Test
-    fun analogState_setHour_updatesIsAfternoon() {
+    fun analogState_setHour_updatesIsPm() {
         val state =
             AnalogTimePickerState(
                 TimePickerState(initialHour = 8, initialMinute = 0, is24Hour = false)
             )
         state.hour = 20
 
-        assertThat(state.isAfternoon).isTrue()
+        assertThat(state.isPm).isTrue()
     }
 
     @Test
@@ -665,15 +665,10 @@
             ClockFace(state, TimePickerDefaults.colors(), autoSwitchToMinute = true)
         }
 
-        repeat(24) { number ->
-            if (number >= 12) {
-                state.isAfternoon = true
-            }
-
+        repeat(12) { number ->
             val hour =
                 when {
                     number == 0 -> 12
-                    number > 12 -> number - 12
                     else -> number
                 }
 
@@ -689,7 +684,7 @@
     fun clockFace_12Hour_initAtNoon() {
         val state = TimePickerState(initialHour = 12, initialMinute = 0, is24Hour = false)
 
-        assertThat(state.isAfternoon).isTrue()
+        assertThat(state.isPm).isTrue()
 
         assertThat(state.hour).isEqualTo(12)
     }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
index 531128f..feb118a 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipScreenshotTest.kt
@@ -55,7 +55,7 @@
         rule.onNodeWithTag(AnchorTestTag).performTouchInput { longClick() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         assertAgainstGolden("plainTooltip_lightTheme")
@@ -71,7 +71,7 @@
         rule.onNodeWithTag(AnchorTestTag).performTouchInput { longClick() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         assertAgainstGolden("plainTooltip_darkTheme")
@@ -87,7 +87,7 @@
         rule.onNodeWithTag(AnchorTestTag).performTouchInput { longClick() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         assertAgainstGolden("richTooltip_lightTheme")
@@ -103,7 +103,7 @@
         rule.onNodeWithTag(AnchorTestTag).performTouchInput { longClick() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         assertAgainstGolden("richTooltip_darkTheme")
@@ -160,3 +160,5 @@
 
 private const val AnchorTestTag = "Anchor"
 private const val TooltipTestTag = "tooltip"
+// We use springs to animate, so picking an arbitrary duration that works.
+private const val TooltipFadeInDuration = 300L
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt
index 7d228d9..0cfc573 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TooltipTest.kt
@@ -84,7 +84,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         rule
@@ -110,7 +110,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         rule
@@ -141,7 +141,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         rule
@@ -172,7 +172,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         rule
@@ -201,7 +201,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         rule
@@ -232,7 +232,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         rule.waitForIdle()
         val subhead = rule.onNodeWithTag(SubheadTestTag)
@@ -283,7 +283,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         // Check that the tooltip is now showing
         rule.waitForIdle()
@@ -319,7 +319,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         // Check that the tooltip is now showing
         rule.waitForIdle()
@@ -363,7 +363,7 @@
         scope.launch { state.show() }
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         // Check that the tooltip is now showing
         rule.waitForIdle()
@@ -380,7 +380,7 @@
 
         // Advance by the fade out duration
         // plus some additional time to make sure that the tooltip is full faded out.
-        rule.mainClock.advanceTimeBy(TooltipFadeOutDuration.toLong() + 100L)
+        rule.mainClock.advanceTimeBy(TooltipFadeOutDuration)
         rule.waitForIdle()
         assertThat(state.isVisible).isFalse()
     }
@@ -661,7 +661,7 @@
         rule.mainClock.autoAdvance = false
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         // Check that only the tooltip associated with bottomState is visible
         rule.waitForIdle()
@@ -726,7 +726,7 @@
         rule.mainClock.autoAdvance = false
 
         // Advance by the fade in time
-        rule.mainClock.advanceTimeBy(TooltipFadeInDuration.toLong())
+        rule.mainClock.advanceTimeBy(TooltipFadeInDuration)
 
         // Check that both tooltips are now showing
         rule.waitForIdle()
@@ -809,3 +809,6 @@
 private const val ContainerTestTag = "Container"
 private const val SubheadTestTag = "Subhead"
 private const val TextTestTag = "Text"
+// We use springs to animate, so picking an arbitrary durations that work.
+private const val TooltipFadeInDuration = 300L
+private const val TooltipFadeOutDuration = 300L
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt
index d33f34b..73586b6 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WavyProgressIndicatorTest.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.internal.VerticalSemanticsBoundsPadding
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.assertContainsColor
 import androidx.compose.testutils.assertPixelColor
@@ -220,7 +221,7 @@
 
     @Test
     fun indeterminateLinearWavyProgressIndicator_semanticsNodeBounds() {
-        val paddingPx = with(rule.density) { SemanticsBoundsPadding.roundToPx() }
+        val paddingPx = with(rule.density) { VerticalSemanticsBoundsPadding.roundToPx() }
 
         val expectedWidth =
             with(rule.density) { WavyProgressIndicatorDefaults.LinearContainerWidth.roundToPx() }
@@ -269,7 +270,7 @@
 
     @Test
     fun determinateLinearWavyProgressIndicator_semanticsNodeBounds() {
-        val paddingPx = with(rule.density) { SemanticsBoundsPadding.roundToPx() }
+        val paddingPx = with(rule.density) { VerticalSemanticsBoundsPadding.roundToPx() }
 
         val expectedWidth =
             with(rule.density) { WavyProgressIndicatorDefaults.LinearContainerWidth.roundToPx() }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WideNavigationRailScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WideNavigationRailScreenshotTest.kt
new file mode 100644
index 0000000..442146f
--- /dev/null
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WideNavigationRailScreenshotTest.kt
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.PressInteraction
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.Menu
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class WideNavigationRailScreenshotTest(private val scheme: TestWrapper) {
+
+    @get:Rule val composeTestRule = createComposeRule()
+
+    @get:Rule val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
+
+    @Test
+    fun wideNavigationRail() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setMaterialContent(scheme.colorScheme) {
+            scope = rememberCoroutineScope()
+            DefaultWideNavigationRail(interactionSource, expanded = scheme.expanded)
+        }
+
+        assertWideNavigationRailMatches(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "wideNavigationRail_${scheme.name}"
+        )
+    }
+
+    @Test
+    @Ignore("b/355413615")
+    fun wideNavigationRail_pressed() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setMaterialContent(scheme.colorScheme) {
+            scope = rememberCoroutineScope()
+            DefaultWideNavigationRail(interactionSource, expanded = scheme.expanded)
+        }
+
+        assertWideNavigationRailMatches(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = PressInteraction.Press(Offset(10f, 10f)),
+            goldenIdentifier = "wideNavigationRail_${scheme.name}_pressed"
+        )
+    }
+
+    @Test
+    fun wideNavigationRail_disabled() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setMaterialContent(scheme.colorScheme) {
+            scope = rememberCoroutineScope()
+            DefaultWideNavigationRail(
+                interactionSource = interactionSource,
+                expanded = scheme.expanded,
+                setUnselectedItemsAsDisabled = true
+            )
+        }
+
+        assertWideNavigationRailMatches(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "wideNavigationRail_${scheme.name}_disabled"
+        )
+    }
+
+    @Test
+    fun wideNavigationRail_withHeader() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            scope = rememberCoroutineScope()
+            DefaultWideNavigationRail(
+                interactionSource,
+                expanded = scheme.expanded,
+                withHeader = true,
+            )
+        }
+
+        val expanded = if (scheme.expanded) "expanded" else "collapsed"
+        assertWideNavigationRailMatches(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "wideNavigationRail_${expanded}_lightTheme_defaultColors_withHeader"
+        )
+    }
+
+    @Test
+    fun wideNavigationRail_centeredArrangement() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            scope = rememberCoroutineScope()
+            DefaultWideNavigationRail(
+                interactionSource,
+                expanded = scheme.expanded,
+                arrangement = NavigationRailArrangement.Center
+            )
+        }
+
+        val expanded = if (scheme.expanded) "expanded" else "collapsed"
+        assertWideNavigationRailMatches(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier =
+                "wideNavigationRail_${expanded}_lightTheme_defaultColors_centeredArrangement"
+        )
+    }
+
+    @Test
+    fun wideNavigationRail_bottomArrangement() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setMaterialContent(lightColorScheme()) {
+            scope = rememberCoroutineScope()
+            DefaultWideNavigationRail(
+                interactionSource,
+                expanded = scheme.expanded,
+                arrangement = NavigationRailArrangement.Bottom
+            )
+        }
+
+        val expanded = if (scheme.expanded) "expanded" else "collapsed"
+        assertWideNavigationRailMatches(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier =
+                "wideNavigationRail_${expanded}_lightTheme_defaultColors_bottomArrangement"
+        )
+    }
+
+    /**
+     * Asserts that the WideNavigationRail matches the screenshot with identifier
+     * [goldenIdentifier].
+     *
+     * @param scope [CoroutineScope] used to interact with [MutableInteractionSource]
+     * @param interactionSource the [MutableInteractionSource] used for the first NavigationRailItem
+     * @param interaction the [Interaction] to assert for, or `null` if no [Interaction].
+     * @param goldenIdentifier the identifier for the corresponding screenshot
+     */
+    private fun assertWideNavigationRailMatches(
+        scope: CoroutineScope,
+        interactionSource: MutableInteractionSource,
+        interaction: Interaction? = null,
+        goldenIdentifier: String
+    ) {
+        if (interaction != null) {
+            composeTestRule.runOnIdle {
+                // Start ripple.
+                scope.launch { interactionSource.emit(interaction) }
+            }
+
+            composeTestRule.waitForIdle()
+            // Ripples are drawn on the RenderThread, not the main (UI) thread, so we can't properly
+            // wait for synchronization. Instead just advance until after the ripples are finished
+            // animating.
+            composeTestRule.mainClock.autoAdvance = false
+            composeTestRule.mainClock.advanceTimeBy(300)
+        }
+
+        // Capture and compare screenshots.
+        composeTestRule
+            .onNodeWithTag(Tag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenIdentifier)
+    }
+
+    // Provide the ColorScheme, expanded value and their name parameter in a TestWrapper.
+    // This makes sure that the default method name and the initial Scuba image generated
+    // name is as expected.
+    companion object {
+        @Parameterized.Parameters(name = "expanded={0} name={1}")
+        @JvmStatic
+        fun parameters() =
+            arrayOf(
+                TestWrapper(
+                    expanded = false,
+                    "collapsed_lightTheme_defaultColors",
+                    lightColorScheme()
+                ),
+                TestWrapper(
+                    expanded = false,
+                    "collapsed_darkTheme_defaultColors",
+                    darkColorScheme()
+                ),
+                TestWrapper(
+                    expanded = true,
+                    "expanded_lightTheme_defaultColors",
+                    lightColorScheme()
+                ),
+                TestWrapper(expanded = true, "expanded_darkTheme_defaultColors", darkColorScheme()),
+            )
+    }
+
+    class TestWrapper(val expanded: Boolean, val name: String, val colorScheme: ColorScheme) {
+        override fun toString(): String {
+            return name
+        }
+    }
+}
+
+/**
+ * Default colored [WideNavigationRail] with three [WideNavigationRailItem]s. The first is selected,
+ * and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [WideNavigationRailItem],
+ *   to control its visual state
+ * @param expanded whether the rail is expanded
+ * @param arrangement the [NavigationRailArrangement] of the rail
+ * @param withHeader when true, shows a [FloatingActionButton] as the header
+ * @param setUnselectedItemsAsDisabled when true, marks unselected items as disabled
+ */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun DefaultWideNavigationRail(
+    interactionSource: MutableInteractionSource,
+    expanded: Boolean = false,
+    arrangement: NavigationRailArrangement = NavigationRailArrangement.Top,
+    withHeader: Boolean = false,
+    setUnselectedItemsAsDisabled: Boolean = false,
+) {
+    Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+        WideNavigationRail(
+            expanded = expanded,
+            arrangement = arrangement,
+            header =
+                if (withHeader) {
+                    { Header() }
+                } else {
+                    null
+                }
+        ) {
+            WideNavigationRailItem(
+                railExpanded = expanded,
+                icon = { Icon(Icons.Filled.Favorite, null) },
+                label = { Text("Favorites") },
+                selected = true,
+                onClick = {},
+                interactionSource = interactionSource
+            )
+            WideNavigationRailItem(
+                railExpanded = expanded,
+                icon = { Icon(Icons.Filled.Home, null) },
+                label = { Text("Home") },
+                selected = false,
+                enabled = !setUnselectedItemsAsDisabled,
+                onClick = {}
+            )
+            WideNavigationRailItem(
+                railExpanded = expanded,
+                icon = { Icon(Icons.Filled.Search, null) },
+                label = { Text("Search") },
+                selected = false,
+                enabled = !setUnselectedItemsAsDisabled,
+                onClick = {}
+            )
+        }
+    }
+}
+
+/**
+ * Default Menu header button to be used along with the [DefaultWideNavigationRail] when the
+ * withHeader flag is true.
+ */
+@Composable
+private fun Header() {
+    Column {
+        IconButton(modifier = Modifier.padding(start = 24.dp), onClick = {}) {
+            Icon(Icons.Filled.Menu, "Menu")
+        }
+    }
+}
+
+private const val Tag = "WideNavigationRail"
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WideNavigationRailTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WideNavigationRailTest.kt
new file mode 100644
index 0000000..58e4555
--- /dev/null
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/WideNavigationRailTest.kt
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertCountEquals
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsNotSelected
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.isSelectable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onParent
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.height
+import androidx.compose.ui.unit.width
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class WideNavigationRailTest {
+    @get:Rule val rule = createComposeRule()
+
+    private val collapsedWidth = 96.dp // TODO: Replace with token.
+    private val expandedMinWidth = 220.dp // TODO: Replace with token.
+    private val expandedMaxWidth = 360.dp // TODO: Replace with token.
+
+    @Test
+    fun rail_defaultSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRail {
+                WideNavigationRailItem(
+                    modifier = Modifier.testTag("item"),
+                    icon = { Icon(Icons.Filled.Favorite, null) },
+                    label = { Text("ItemText") },
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        rule
+            .onNodeWithTag("item")
+            .onParent()
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsProperties.SelectableGroup))
+    }
+
+    @Test
+    fun rail_collapsed_size() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                WideNavigationRail {
+                    repeat(3) { index ->
+                        WideNavigationRailItem(
+                            icon = { Icon(Icons.Filled.Favorite, null) },
+                            label = { Text("Item $index") },
+                            selected = index == 0,
+                            onClick = {}
+                        )
+                    }
+                }
+            }
+            .assertHeightIsEqualTo(rule.rootHeight())
+            .assertWidthIsEqualTo(collapsedWidth)
+    }
+
+    @Test
+    fun rail_expanded_size() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                WideNavigationRail(expanded = true) {
+                    repeat(3) { index ->
+                        WideNavigationRailItem(
+                            railExpanded = true,
+                            icon = { Icon(Icons.Filled.Favorite, null) },
+                            label = { Text("Item $index") },
+                            selected = index == 0,
+                            onClick = {}
+                        )
+                    }
+                }
+            }
+            .assertHeightIsEqualTo(rule.rootHeight())
+            .assertWidthIsEqualTo(expandedMinWidth)
+    }
+
+    @Test
+    fun rail_expanded_maxSize() {
+        rule
+            .setMaterialContentForSizeAssertions {
+                WideNavigationRail(expanded = true, header = { Spacer(Modifier.width(400.dp)) }) {
+                    repeat(3) { index ->
+                        WideNavigationRailItem(
+                            railExpanded = true,
+                            icon = { Icon(Icons.Filled.Favorite, null) },
+                            label = { Text("Item $index") },
+                            selected = index == 0,
+                            onClick = {}
+                        )
+                    }
+                }
+            }
+            .assertHeightIsEqualTo(rule.rootHeight())
+            .assertWidthIsEqualTo(expandedMaxWidth)
+    }
+
+    @Test
+    fun rail_collapsed_expands() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var expanded by remember { mutableStateOf(false) }
+            WideNavigationRail(
+                modifier = Modifier.testTag("rail"),
+                expanded = expanded,
+                header = {
+                    Button(
+                        modifier = Modifier.testTag("header"),
+                        onClick = { expanded = !expanded }
+                    ) {}
+                }
+            ) {}
+        }
+
+        // Assert width is collapsed width.
+        rule.onNodeWithTag("rail").assertWidthIsEqualTo(collapsedWidth)
+        // Click on header to expand.
+        rule.onNodeWithTag("header").performClick()
+        // Assert width changed to expanded width.
+        rule.onNodeWithTag("rail").assertWidthIsEqualTo(expandedMinWidth)
+    }
+
+    @Test
+    fun rail_expanded_collapses() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var expanded by remember { mutableStateOf(true) }
+            WideNavigationRail(
+                modifier = Modifier.testTag("rail"),
+                expanded = expanded,
+                header = {
+                    Button(
+                        modifier = Modifier.testTag("header"),
+                        onClick = { expanded = !expanded }
+                    ) {}
+                }
+            ) {}
+        }
+
+        // Assert width is expanded width.
+        rule.onNodeWithTag("rail").assertWidthIsEqualTo(expandedMinWidth)
+        // Click on header to collapse.
+        rule.onNodeWithTag("header").performClick()
+        // Assert width changed to collapse width.
+        rule.onNodeWithTag("rail").assertWidthIsEqualTo(collapsedWidth)
+    }
+
+    @Test
+    fun rail_respectsWindowInsets() {
+        rule.setMaterialContentForSizeAssertions {
+            WideNavigationRail(windowInsets = WindowInsets(13.dp, 13.dp, 13.dp, 13.dp)) {
+                Box(Modifier.fillMaxSize().testTag("content"))
+            }
+        }
+        rule
+            .onNodeWithTag("content")
+            .assertTopPositionInRootIsEqualTo(13.dp + WNRVerticalPadding)
+            .assertLeftPositionInRootIsEqualTo(13.dp)
+    }
+
+    @Test
+    fun rail_itemsWithCustomColors() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val customItemColors =
+                WideNavigationRailItemDefaults.colors()
+                    .copy(
+                        selectedIconColor = Color.Red,
+                        selectedTextColor = Color.Blue,
+                        unselectedIconColor = Color.Green,
+                        unselectedTextColor = Color.White,
+                        disabledIconColor = Color.Gray,
+                        disabledTextColor = Color.Black,
+                    )
+
+            WideNavigationRail {
+                WideNavigationRailItem(
+                    selected = true,
+                    colors = customItemColors,
+                    icon = { Truth.assertThat(LocalContentColor.current).isEqualTo(Color.Red) },
+                    label = { Truth.assertThat(LocalContentColor.current).isEqualTo(Color.Blue) },
+                    onClick = {}
+                )
+                WideNavigationRailItem(
+                    selected = false,
+                    colors = customItemColors,
+                    icon = { Truth.assertThat(LocalContentColor.current).isEqualTo(Color.Green) },
+                    label = { Truth.assertThat(LocalContentColor.current).isEqualTo(Color.White) },
+                    onClick = {}
+                )
+                WideNavigationRailItem(
+                    enabled = false,
+                    selected = false,
+                    colors = customItemColors,
+                    icon = { Truth.assertThat(LocalContentColor.current).isEqualTo(Color.Gray) },
+                    label = { Truth.assertThat(LocalContentColor.current).isEqualTo(Color.Black) },
+                    onClick = {}
+                )
+            }
+        }
+    }
+
+    @Test
+    fun rail_selectNewItem() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var selectedItem by remember { mutableStateOf(0) }
+            WideNavigationRail {
+                repeat(3) { index ->
+                    WideNavigationRailItem(
+                        icon = { Icon(Icons.Filled.Favorite, null) },
+                        label = { Text("Item $index") },
+                        selected = selectedItem == index,
+                        onClick = { selectedItem = index }
+                    )
+                }
+            }
+        }
+
+        // Find all items and ensure there are 3
+        rule
+            .onAllNodes(isSelectable())
+            .assertCountEquals(3)
+            // Ensure semantics match for selected state of the items.
+            .apply {
+                get(0).assertIsSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsNotSelected()
+            }
+            // Click the last item.
+            .apply { get(2).performClick() }
+            .apply {
+                get(0).assertIsNotSelected()
+                get(1).assertIsNotSelected()
+                get(2).assertIsSelected()
+            }
+    }
+
+    @Test
+    fun item_defaultSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRailItem(
+                modifier = Modifier.testTag("item"),
+                icon = { Icon(Icons.Filled.Favorite, null) },
+                label = { Text("ItemText") },
+                selected = true,
+                onClick = {}
+            )
+        }
+
+        rule
+            .onNodeWithTag("item")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Tab))
+            .assertIsSelected()
+            .assertIsEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun item_disabledSemantics() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRailItem(
+                enabled = false,
+                modifier = Modifier.testTag("item"),
+                icon = { Icon(Icons.Filled.Favorite, null) },
+                label = { Text("ItemText") },
+                selected = true,
+                onClick = {}
+            )
+        }
+
+        rule
+            .onNodeWithTag("item")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Tab))
+            .assertIsSelected()
+            .assertIsNotEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun item_unselectedItem_hasIconSemantics_whenLabelNotPresent() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRailItem(
+                modifier = Modifier.testTag("item"),
+                icon = { Icon(Icons.Filled.Favorite, "Favorite") },
+                selected = false,
+                onClick = {}
+            )
+        }
+
+        val node = rule.onNodeWithTag("item").fetchSemanticsNode()
+
+        Truth.assertThat(node.config.getOrNull(SemanticsProperties.ContentDescription))
+            .isEqualTo(listOf("Favorite"))
+    }
+
+    @Test
+    fun item_disabled_noClicks() {
+        var clicks = 0
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRailItem(
+                enabled = false,
+                modifier = Modifier.testTag("item"),
+                icon = { Icon(Icons.Filled.Favorite, null) },
+                label = { Text("ItemText") },
+                selected = true,
+                onClick = { clicks++ }
+            )
+        }
+
+        rule.onNodeWithTag("item").performClick()
+
+        rule.runOnIdle { Truth.assertThat(clicks).isEqualTo(0) }
+    }
+
+    @Test
+    fun item_topIconPosition_withLongLabel_automaticallyResizesHeight() {
+        val defaultHeight = WNRTopItemMinHeight
+
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRailItem(
+                icon = { Icon(Icons.Filled.Favorite, null) },
+                label = { Text("Long\nLabel\nMultiple\nLines") },
+                selected = true,
+                onClick = {},
+                modifier = Modifier.testTag("TAG"),
+            )
+        }
+
+        Truth.assertThat(
+                rule.onNodeWithTag("TAG", useUnmergedTree = true).getUnclippedBoundsInRoot().height
+            )
+            .isGreaterThan(defaultHeight)
+    }
+
+    @Test
+    fun item_startIconPosition_withLongLabel_automaticallyResizesHeight() {
+        var defaultHeight: Dp? = null
+
+        rule.setMaterialContent(lightColorScheme()) {
+            defaultHeight = LocalMinimumInteractiveComponentSize.current
+            WideNavigationRailItem(
+                iconPosition = NavigationItemIconPosition.Start,
+                icon = { Icon(Icons.Filled.Favorite, null) },
+                label = { Text("Long\nLabel\nMultiple\nLines") },
+                selected = true,
+                onClick = {},
+                modifier = Modifier.testTag("TAG"),
+            )
+        }
+
+        Truth.assertThat(
+                rule.onNodeWithTag("TAG", useUnmergedTree = true).getUnclippedBoundsInRoot().height
+            )
+            .isGreaterThan(defaultHeight)
+    }
+
+    @Test
+    fun itemContent_withoutLabel_sizeAndPosition() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRailItem(
+                modifier = Modifier.testTag("item"),
+                icon = { Icon(Icons.Filled.Favorite, null, Modifier.testTag("icon")) },
+                selected = true,
+                onClick = {}
+            )
+        }
+
+        val itemBounds = rule.onNodeWithTag("item").getUnclippedBoundsInRoot()
+        val iconBounds =
+            rule.onNodeWithTag("icon", useUnmergedTree = true).getUnclippedBoundsInRoot()
+
+        val itemSize =
+            WNRItemNoLabelIndicatorPadding + iconBounds.height + WNRItemNoLabelIndicatorPadding
+
+        // Assert the item has its minimal width and height values.
+        Truth.assertThat(itemBounds.width).isEqualTo(itemSize)
+        Truth.assertThat(itemBounds.height).isEqualTo(itemSize)
+
+        // The icon should be centered in the item, as there is no text placeable provided
+        rule
+            .onNodeWithTag("icon", useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo((itemBounds.width - iconBounds.width) / 2)
+            .assertTopPositionInRootIsEqualTo((itemBounds.height - iconBounds.height) / 2)
+    }
+
+    @Test
+    fun header_position() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRail(header = { Box(Modifier.testTag("header").size(10.dp)) }) {
+                WideNavigationRailItem(
+                    modifier = Modifier.testTag("item"),
+                    icon = { Icon(Icons.Filled.Favorite, null) },
+                    label = { Text("ItemText") },
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        val headerBounds = rule.onNodeWithTag("header").getUnclippedBoundsInRoot()
+
+        // Header should always be at the top.
+        rule
+            .onNodeWithTag("header", useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo(WNRVerticalPadding)
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+
+        // Item should be `HeaderPadding` below the header in top arrangement.
+        rule
+            .onNodeWithTag("item", useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo(
+                WNRVerticalPadding + headerBounds.height + WNRHeaderPadding
+            )
+    }
+
+    @Test
+    fun header_position_centeredArrangement() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRail(
+                modifier = Modifier.testTag("rail"),
+                arrangement = NavigationRailArrangement.Center,
+                header = { Box(Modifier.testTag("header").size(10.dp)) }
+            ) {
+                WideNavigationRailItem(
+                    modifier = Modifier.testTag("item"),
+                    icon = { Icon(Icons.Filled.Favorite, null) },
+                    label = { Text("ItemText") },
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        val railBounds = rule.onNodeWithTag("rail").getUnclippedBoundsInRoot()
+        val itemBounds = rule.onNodeWithTag("item").getUnclippedBoundsInRoot()
+
+        // Header should always be at the top.
+        rule
+            .onNodeWithTag("header", useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo(WNRVerticalPadding)
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+        // Assert item is centered.
+        rule
+            .onNodeWithTag("item", useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo((railBounds.height - itemBounds.height) / 2)
+    }
+
+    @Test
+    fun header_position_bottomArrangement() {
+        rule.setMaterialContent(lightColorScheme()) {
+            WideNavigationRail(
+                modifier = Modifier.testTag("rail"),
+                arrangement = NavigationRailArrangement.Bottom,
+                header = { Box(Modifier.testTag("header").size(10.dp)) }
+            ) {
+                WideNavigationRailItem(
+                    modifier = Modifier.testTag("item"),
+                    icon = { Icon(Icons.Filled.Favorite, null) },
+                    label = { Text("ItemText") },
+                    selected = true,
+                    onClick = {}
+                )
+            }
+        }
+
+        val railBounds = rule.onNodeWithTag("rail").getUnclippedBoundsInRoot()
+        val itemBounds = rule.onNodeWithTag("item").getUnclippedBoundsInRoot()
+
+        // Header should always be at the top.
+        rule
+            .onNodeWithTag("header", useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo(WNRVerticalPadding)
+            .assertLeftPositionInRootIsEqualTo(0.dp)
+        // Assert item is at the bottom.
+        rule
+            .onNodeWithTag("item", useUnmergedTree = true)
+            .assertTopPositionInRootIsEqualTo(
+                (railBounds.height - WNRVerticalPadding - itemBounds.height)
+            )
+    }
+}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/anchoredDraggable/AnchoredDraggableStateTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/anchoredDraggable/AnchoredDraggableStateTest.kt
index b16b455..bf7443d 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/anchoredDraggable/AnchoredDraggableStateTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/anchoredDraggable/AnchoredDraggableStateTest.kt
@@ -200,7 +200,7 @@
         val state =
             AnchoredDraggableState(
                 initialValue = A,
-                animationSpec = tween(animationDuration, easing = LinearEasing),
+                animationSpec = { tween(animationDuration, easing = LinearEasing) },
                 positionalThreshold = { distance -> distance * 0.5f },
                 velocityThreshold = defaultVelocityThreshold
             )
@@ -334,7 +334,7 @@
         val restorationTester = StateRestorationTester(rule)
 
         val initialState = C
-        val animationSpec = tween<Float>(durationMillis = 1000)
+        val animationSpec = { tween<Float>(durationMillis = 1000) }
         val state =
             AnchoredDraggableState(
                 initialValue = initialState,
@@ -529,7 +529,7 @@
                 initialValue = A,
                 positionalThreshold = defaultPositionalThreshold,
                 velocityThreshold = defaultVelocityThreshold,
-                animationSpec = animationSpec
+                animationSpec = { animationSpec }
             )
         lateinit var scope: CoroutineScope
 
@@ -1005,5 +1005,5 @@
 
     private val defaultVelocityThreshold: () -> Float = { with(rule.density) { 125.dp.toPx() } }
 
-    private val defaultAnimationSpec = tween<Float>()
+    private val defaultAnimationSpec = { tween<Float>() }
 }
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/pulltorefresh/PullToRefreshIndicatorScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/pulltorefresh/PullToRefreshIndicatorScreenshotTest.kt
index 619cc1a..6acbb51 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/pulltorefresh/PullToRefreshIndicatorScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/pulltorefresh/PullToRefreshIndicatorScreenshotTest.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material3.CircularAnimationProgressDuration
 import androidx.compose.material3.CircularIndicatorDiameter
 import androidx.compose.material3.ColorScheme
 import androidx.compose.material3.ExperimentalMaterial3Api
@@ -65,7 +66,7 @@
                 )
             }
         }
-        rule.mainClock.advanceTimeBy(500)
+        rule.mainClock.advanceTimeBy(CircularAnimationProgressDuration / 3L * 4L)
 
         assertAgainstGolden("pullRefreshIndicator_${scheme.name}_refreshing")
     }
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DateInput.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DateInput.android.kt
new file mode 100644
index 0000000..4f33650
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DateInput.android.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.material3.internal.CalendarDate
+import androidx.compose.material3.internal.DateInputFormat
+import androidx.compose.runtime.Stable
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Stable
+internal actual class DateInputValidator
+actual constructor(
+    private val yearRange: IntRange,
+    private val selectableDates: SelectableDates,
+    private val dateInputFormat: DateInputFormat,
+    private val dateFormatter: DatePickerFormatter,
+    private val errorDatePattern: String,
+    private val errorDateOutOfYearRange: String,
+    private val errorInvalidNotAllowed: String,
+    private val errorInvalidRangeInput: String
+) {
+    /**
+     * the currently selected start date in milliseconds. Only checked against when the
+     * [InputIdentifier] is [InputIdentifier.EndDateInput].
+     */
+    actual var currentStartDateMillis: Long? = null
+
+    /**
+     * the currently selected end date in milliseconds. Only checked against when the
+     * [InputIdentifier] is [InputIdentifier.StartDateInput].
+     */
+    actual var currentEndDateMillis: Long? = null
+
+    actual fun validate(
+        dateToValidate: CalendarDate?,
+        inputIdentifier: InputIdentifier,
+        locale: CalendarLocale
+    ): String {
+        if (dateToValidate == null) {
+            return errorDatePattern.format(dateInputFormat.patternWithDelimiters.uppercase())
+        }
+        // Check that the date is within the valid range of years.
+        if (!yearRange.contains(dateToValidate.year)) {
+            return errorDateOutOfYearRange.format(
+                yearRange.first.toLocalString(),
+                yearRange.last.toLocalString()
+            )
+        }
+        // Check that the provided SelectableDates allows this date to be selected.
+        with(selectableDates) {
+            if (
+                !isSelectableYear(dateToValidate.year) ||
+                    !isSelectableDate(dateToValidate.utcTimeMillis)
+            ) {
+                return errorInvalidNotAllowed.format(
+                    dateFormatter.formatDate(
+                        dateMillis = dateToValidate.utcTimeMillis,
+                        locale = locale
+                    )
+                )
+            }
+        }
+
+        // Additional validation when the InputIdentifier is for start of end dates in a range input
+        if (
+            (inputIdentifier == InputIdentifier.StartDateInput &&
+                dateToValidate.utcTimeMillis >= (currentEndDateMillis ?: Long.MAX_VALUE)) ||
+                (inputIdentifier == InputIdentifier.EndDateInput &&
+                    dateToValidate.utcTimeMillis < (currentStartDateMillis ?: Long.MIN_VALUE))
+        ) {
+            // The input start date is after the end date, or the end date is before the start date.
+            return errorInvalidRangeInput
+        }
+
+        return ""
+    }
+}
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DatePicker.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DatePicker.android.kt
new file mode 100644
index 0000000..d5e94bf
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DatePicker.android.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun formatDatePickerNavigateToYearString(
+    template: String,
+    localizedYear: String
+): String = template.format(localizedYear)
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun formatHeadlineDescription(
+    template: String,
+    verboseDateDescription: String
+): String = template.format(verboseDateDescription)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.android.kt
index 4a5ded7..88731df 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.android.kt
@@ -21,7 +21,6 @@
 import android.content.Context
 import android.os.Build
 import androidx.annotation.ColorRes
-import androidx.annotation.DoNotInline
 import androidx.annotation.FloatRange
 import androidx.annotation.RequiresApi
 import androidx.compose.material3.internal.colorUtil.Cam
@@ -235,7 +234,6 @@
 
 @RequiresApi(23)
 private object ColorResourceHelper {
-    @DoNotInline
     fun getColor(context: Context, @ColorRes id: Int): Color {
         return Color(context.resources.getColor(id, context.theme))
     }
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt
index 146b300..abed5ef 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.android.kt
@@ -35,8 +35,7 @@
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.selection.LocalTextSelectionColors
 import androidx.compose.foundation.text.selection.TextSelectionColors
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowDropDown
+import androidx.compose.material3.internal.Icons
 import androidx.compose.material3.internal.MenuPosition
 import androidx.compose.material3.internal.Strings
 import androidx.compose.material3.internal.getString
@@ -122,6 +121,10 @@
  * An example of an editable Exposed Dropdown Menu:
  *
  * @sample androidx.compose.material3.samples.EditableExposedDropdownMenuSample
+ *
+ * An example of an editable Exposed Dropdown Menu used like a MultiAutoCompleteTextView:
+ *
+ * @sample androidx.compose.material3.samples.MultiAutocompleteExposedDropdownMenuSample
  * @param expanded whether the menu is expanded or not
  * @param onExpandedChange called when the exposed dropdown menu is clicked and the expansion state
  *   changes.
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/InteractiveComponentSize.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/InteractiveComponentSize.android.kt
new file mode 100644
index 0000000..a4ebf46
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/InteractiveComponentSize.android.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun identityHashCode(value: Any): Int = System.identityHashCode(value)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
index bc590c2..0717e97 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
@@ -31,7 +31,6 @@
 import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentDialog
 import androidx.activity.addCallback
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationVector1D
@@ -391,7 +390,6 @@
     @RequiresApi(34)
     private object Api34Impl {
         @JvmStatic
-        @DoNotInline
         fun createBackCallback(
             onDismissRequest: () -> Unit,
             predictiveBackProgress: Animatable<Float, AnimationVector1D>,
@@ -423,12 +421,10 @@
     @RequiresApi(33)
     private object Api33Impl {
         @JvmStatic
-        @DoNotInline
         fun createBackCallback(onDismissRequest: () -> Unit) =
             OnBackInvokedCallback(onDismissRequest)
 
         @JvmStatic
-        @DoNotInline
         fun maybeRegisterBackCallback(view: View, backCallback: Any?) {
             if (backCallback is OnBackInvokedCallback) {
                 view
@@ -441,7 +437,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun maybeUnregisterBackCallback(view: View, backCallback: Any?) {
             if (backCallback is OnBackInvokedCallback) {
                 view.findOnBackInvokedDispatcher()?.unregisterOnBackInvokedCallback(backCallback)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt
index bc98cbc..12c544b 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt
@@ -23,7 +23,6 @@
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
@@ -160,7 +159,6 @@
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     private object Api33Impl {
         @JvmStatic
-        @DoNotInline
         fun addAccessibilityServicesStateChangeListener(
             am: AccessibilityManager,
             listener: AccessibilityServicesStateChangeListener
@@ -169,7 +167,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun removeAccessibilityServicesStateChangeListener(
             am: AccessibilityManager,
             listener: AccessibilityServicesStateChangeListener
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.android.kt
new file mode 100644
index 0000000..95a2223
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.android.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal actual constructor() :
+    CancellationException("Anchored drag finished") {
+    override fun fillInStackTrace(): Throwable {
+        stackTrace = emptyArray()
+        return this
+    }
+}
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/CalendarModel.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/CalendarModel.android.kt
index dad7b59..59ca59a 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/CalendarModel.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/CalendarModel.android.kt
@@ -64,3 +64,38 @@
         LegacyCalendarModelImpl.formatWithPattern(utcTimeMillis, pattern, locale, cache)
     }
 }
+
+/**
+ * Receives a given local date format string and returns a string that can be displayed to the user
+ * and parsed by the date parser.
+ *
+ * This function:
+ * - Removes all characters that don't match `d`, `M` and `y`, or any of the date format delimiters
+ *   `.`, `/` and `-`.
+ * - Ensures that the format is for two digits day and month, and four digits year.
+ *
+ * The output of this cleanup is always a 10 characters string in one of the following variations:
+ * - yyyy/MM/dd
+ * - yyyy-MM-dd
+ * - yyyy.MM.dd
+ * - dd/MM/yyyy
+ * - dd-MM-yyyy
+ * - dd.MM.yyyy
+ * - MM/dd/yyyy
+ */
+internal actual fun datePatternAsInputFormat(localeFormat: String): DateInputFormat {
+    val patternWithDelimiters =
+        localeFormat
+            .replace(Regex("[^dMy/\\-.]"), "")
+            .replace(Regex("d{1,2}"), "dd")
+            .replace(Regex("M{1,2}"), "MM")
+            .replace(Regex("y{1,4}"), "yyyy")
+            .replace("My", "M/y") // Edge case for the Kako locale
+            .removeSuffix(".") // Removes a dot suffix that appears in some formats
+
+    val delimiterRegex = Regex("[/\\-.]")
+    val delimiterMatchResult = delimiterRegex.find(patternWithDelimiters)
+    val delimiterIndex = delimiterMatchResult!!.groups[0]!!.range.first
+    val delimiter = patternWithDelimiters.substring(delimiterIndex, delimiterIndex + 1)
+    return DateInputFormat(patternWithDelimiters = patternWithDelimiters, delimiter = delimiter[0])
+}
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/Strings.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/Strings.android.kt
index b442670..aa629c2 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/Strings.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/Strings.android.kt
@@ -74,6 +74,9 @@
         actual inline val SnackbarDismiss
             get() = Strings(MaterialR.string.m3c_snackbar_dismiss)
 
+        actual inline val SnackbarPaneTitle
+            get() = Strings(MaterialR.string.m3c_snackbar_pane_title)
+
         actual inline val SearchBarSearch
             get() = Strings(MaterialR.string.m3c_search_bar_search)
 
diff --git a/compose/material3/material3/src/androidMain/res/values/strings.xml b/compose/material3/material3/src/androidMain/res/values/strings.xml
index 6546d3f..371e90f 100644
--- a/compose/material3/material3/src/androidMain/res/values/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values/strings.xml
@@ -26,6 +26,8 @@
     <string name="m3c_dropdown_menu_toggle">Toggle dropdown menu</string>
     <!-- Spoken description of a snackbar dismiss action -->
     <string name="m3c_snackbar_dismiss">Dismiss</string>
+    <!-- Accessibility pane title for a snackbar -->
+    <string name="m3c_snackbar_pane_title">Alert</string>
     <!-- Spoken description of a search bar -->
     <string name="m3c_search_bar_search">Search</string>
     <!-- Spoken description when search suggestions are available -->
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
index 1018ffb..e15c050 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
@@ -22,10 +22,8 @@
 import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.DecayAnimationSpec
 import androidx.compose.animation.core.FastOutLinearInEasing
-import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.animateDecay
 import androidx.compose.animation.core.animateTo
-import androidx.compose.animation.core.spring
 import androidx.compose.animation.rememberSplineBasedDecay
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.draggable
@@ -49,6 +47,7 @@
 import androidx.compose.material3.internal.systemBarsForVisualComponents
 import androidx.compose.material3.tokens.BottomAppBarTokens
 import androidx.compose.material3.tokens.FabSecondaryTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.TopAppBarLargeTokens
 import androidx.compose.material3.tokens.TopAppBarMediumTokens
 import androidx.compose.material3.tokens.TopAppBarSmallCenteredTokens
@@ -94,6 +93,7 @@
 import androidx.compose.ui.unit.isFinite
 import androidx.compose.ui.unit.isSpecified
 import androidx.compose.ui.util.fastFirst
+import kotlin.jvm.JvmInline
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.roundToInt
@@ -209,7 +209,7 @@
         modifier = modifier,
         title = title,
         titleTextStyle = TopAppBarSmallTokens.HeadlineFont.value,
-        subtitle = {},
+        subtitle = null,
         subtitleTextStyle = TextStyle.Default,
         titleHorizontalAlignment = TopAppBarTitleAlignment.Start,
         navigationIcon = navigationIcon,
@@ -334,7 +334,7 @@
         modifier = modifier,
         title = title,
         titleTextStyle = TopAppBarSmallTokens.HeadlineFont.value,
-        subtitle = {},
+        subtitle = null,
         subtitleTextStyle = TextStyle.Default,
         titleHorizontalAlignment = TopAppBarTitleAlignment.Center,
         navigationIcon = navigationIcon,
@@ -546,9 +546,9 @@
         smallTitleTextStyle = TopAppBarSmallTokens.HeadlineFont.value,
         titleBottomPadding = MediumTitleBottomPadding,
         smallTitle = title,
-        subtitle = {},
+        subtitle = null,
         subtitleTextStyle = TextStyle.Default,
-        smallSubtitle = {},
+        smallSubtitle = null,
         smallSubtitleTextStyle = TextStyle.Default,
         titleHorizontalAlignment = TopAppBarTitleAlignment.Start,
         navigationIcon = navigationIcon,
@@ -798,9 +798,9 @@
         titleBottomPadding = LargeTitleBottomPadding,
         smallTitle = title,
         modifier = modifier,
-        subtitle = {},
+        subtitle = null,
         subtitleTextStyle = TextStyle.Default,
-        smallSubtitle = {},
+        smallSubtitle = null,
         smallSubtitleTextStyle = TextStyle.Default,
         titleHorizontalAlignment = TopAppBarTitleAlignment.Start,
         navigationIcon = navigationIcon,
@@ -1153,6 +1153,99 @@
     scrollBehavior: BottomAppBarScrollBehavior? = null,
     content: @Composable RowScope.() -> Unit
 ) {
+    BottomAppBarLayout(
+        containerHeight = BottomAppBarTokens.ContainerHeight,
+        horizontalArrangement = Arrangement.Start,
+        modifier = modifier,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        tonalElevation = tonalElevation,
+        contentPadding = contentPadding,
+        windowInsets = windowInsets,
+        scrollBehavior = scrollBehavior,
+        content = content
+    )
+}
+
+/**
+ * <a href="https://m3.material.io/components/bottom-app-bar/overview" class="external"
+ * target="_blank">Material Design bottom app bar</a>.
+ *
+ * A bottom app bar displays navigation and key actions at the bottom of mobile screens.
+ *
+ * ![Bottom app bar
+ * image](https://developer.android.com/images/reference/androidx/compose/material3/bottom-app-bar.png)
+ *
+ * If you are interested in displaying a [FloatingActionButton], consider using another overload
+ * that takes a [FloatingActionButton] parameter.
+ *
+ * Also see [NavigationBar].
+ *
+ * A bottom app bar that specifies an [horizontalArrangement] and uses a [scrollBehavior] to
+ * customize its nested scrolling behavior when working in conjunction with a scrolling content
+ * looks like:
+ *
+ * @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBarSpacedAround
+ * @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBarSpacedBetween
+ * @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBarSpacedEvenly
+ * @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBarFixed
+ * @param horizontalArrangement the horizontal arrangement of the content.
+ * @param modifier the [Modifier] to be applied to this BottomAppBar
+ * @param containerColor the color used for the background of this BottomAppBar. Use
+ *   [Color.Transparent] to have no color.
+ * @param contentColor the preferred color for content inside this BottomAppBar. Defaults to either
+ *   the matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param contentPadding the padding applied to the content of this BottomAppBar
+ * @param windowInsets a window insets that app bar will respect.
+ * @param scrollBehavior a [BottomAppBarScrollBehavior] which holds various offset values that will
+ *   be applied by this bottom app bar to set up its height. A scroll behavior is designed to work
+ *   in conjunction with a scrolled content to change the bottom app bar appearance as the content
+ *   scrolls. See [BottomAppBarScrollBehavior.nestedScrollConnection].
+ * @param content the content of this BottomAppBar. The default layout here is a [Row], so content
+ *   inside will be placed horizontally.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun BottomAppBar(
+    horizontalArrangement: Arrangement.Horizontal,
+    modifier: Modifier = Modifier,
+    containerColor: Color = BottomAppBarDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp), // TODO tokens
+    windowInsets: WindowInsets = BottomAppBarDefaults.windowInsets,
+    scrollBehavior: BottomAppBarScrollBehavior? = null,
+    content: @Composable RowScope.() -> Unit
+) {
+    BottomAppBarLayout(
+        containerHeight = 64.dp, // TODO tokens
+        horizontalArrangement = horizontalArrangement,
+        modifier = modifier,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        tonalElevation = BottomAppBarDefaults.ContainerElevation,
+        contentPadding = contentPadding,
+        windowInsets = windowInsets,
+        scrollBehavior = scrollBehavior,
+        content = content
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun BottomAppBarLayout(
+    containerHeight: Dp,
+    horizontalArrangement: Arrangement.Horizontal,
+    modifier: Modifier = Modifier,
+    containerColor: Color,
+    contentColor: Color,
+    tonalElevation: Dp,
+    contentPadding: PaddingValues,
+    windowInsets: WindowInsets,
+    scrollBehavior: BottomAppBarScrollBehavior?,
+    content: @Composable RowScope.() -> Unit
+) {
     // Set up support for resizing the bottom app bar when vertically dragging the bar itself.
     val appBarDragModifier =
         if (scrollBehavior != null && !scrollBehavior.isPinned) {
@@ -1186,10 +1279,8 @@
             modifier
                 .layout { measurable, constraints ->
                     // Sets the app bar's height offset to collapse the entire bar's height when
-                    // content
-                    // is scrolled.
-                    scrollBehavior?.state?.heightOffsetLimit =
-                        -BottomAppBarTokens.ContainerHeight.toPx()
+                    // content is scrolled.
+                    scrollBehavior?.state?.heightOffsetLimit = -containerHeight.toPx()
 
                     val placeable = measurable.measure(constraints)
                     val height = placeable.height + (scrollBehavior?.state?.heightOffset ?: 0f)
@@ -1200,9 +1291,9 @@
         Row(
             Modifier.fillMaxWidth()
                 .windowInsetsPadding(windowInsets)
-                .height(BottomAppBarTokens.ContainerHeight)
+                .height(containerHeight)
                 .padding(contentPadding),
-            horizontalArrangement = Arrangement.Start,
+            horizontalArrangement = horizontalArrangement,
             verticalAlignment = Alignment.CenterVertically,
             content = content
         )
@@ -1496,12 +1587,14 @@
      * @param flingAnimationSpec an optional [DecayAnimationSpec] that defined how to fling the top
      *   app bar when the user flings the app bar itself, or the content below it
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @ExperimentalMaterial3Api
     @Composable
     fun enterAlwaysScrollBehavior(
         state: TopAppBarState = rememberTopAppBarState(),
         canScroll: () -> Boolean = { true },
-        snapAnimationSpec: AnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow),
+        // TODO Load the motionScheme tokens from the component tokens file
+        snapAnimationSpec: AnimationSpec<Float>? = MotionSchemeKeyTokens.DefaultEffects.value(),
         flingAnimationSpec: DecayAnimationSpec<Float>? = rememberSplineBasedDecay()
     ): TopAppBarScrollBehavior =
         EnterAlwaysScrollBehavior(
@@ -1529,12 +1622,14 @@
      * @param flingAnimationSpec an optional [DecayAnimationSpec] that defined how to fling the top
      *   app bar when the user flings the app bar itself, or the content below it
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @ExperimentalMaterial3Api
     @Composable
     fun exitUntilCollapsedScrollBehavior(
         state: TopAppBarState = rememberTopAppBarState(),
         canScroll: () -> Boolean = { true },
-        snapAnimationSpec: AnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow),
+        // TODO Load the motionScheme tokens from the component tokens file
+        snapAnimationSpec: AnimationSpec<Float>? = MotionSchemeKeyTokens.DefaultEffects.value(),
         flingAnimationSpec: DecayAnimationSpec<Float>? = rememberSplineBasedDecay()
     ): TopAppBarScrollBehavior =
         remember(state, canScroll, snapAnimationSpec, flingAnimationSpec) {
@@ -1878,6 +1973,12 @@
     val bottomAppBarFabColor: Color
         @Composable get() = FabSecondaryTokens.ContainerColor.value
 
+    val HorizontalArrangement =
+        Arrangement.spacedBy(32.dp, Alignment.CenterHorizontally) // TODO tokens
+
+    // TODO: note that this scroll behavior may impact assistive technologies making the component
+    //  inaccessible. See @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBar on how
+    //  to disable scrolling when touch exploration is enabled.
     /**
      * Returns a [BottomAppBarScrollBehavior]. A bottom app bar that is set up with this
      * [BottomAppBarScrollBehavior] will immediately collapse when the content is pulled up, and
@@ -1893,12 +1994,14 @@
      * @param flingAnimationSpec an optional [DecayAnimationSpec] that defined how to fling the
      *   bottom app bar when the user flings the app bar itself, or the content below it
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @ExperimentalMaterial3Api
     @Composable
     fun exitAlwaysScrollBehavior(
         state: BottomAppBarState = rememberBottomAppBarState(),
         canScroll: () -> Boolean = { true },
-        snapAnimationSpec: AnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow),
+        // TODO Load the motionScheme tokens from the component tokens file
+        snapAnimationSpec: AnimationSpec<Float>? = MotionSchemeKeyTokens.FastSpatial.value(),
         flingAnimationSpec: DecayAnimationSpec<Float>? = rememberSplineBasedDecay()
     ): BottomAppBarScrollBehavior =
         ExitAlwaysScrollBehavior(
@@ -2167,7 +2270,7 @@
     modifier: Modifier = Modifier,
     title: @Composable () -> Unit,
     titleTextStyle: TextStyle,
-    subtitle: @Composable () -> Unit,
+    subtitle: (@Composable () -> Unit)?,
     subtitleTextStyle: TextStyle,
     titleHorizontalAlignment: TopAppBarTitleAlignment,
     navigationIcon: @Composable () -> Unit,
@@ -2200,7 +2303,8 @@
     val appBarContainerColor =
         animateColorAsState(
             targetColor,
-            animationSpec = spring(stiffness = Spring.StiffnessMediumLow)
+            // TODO Load the motionScheme tokens from the component tokens file
+            animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
         )
 
     // Wrap the given actions in a Row.
@@ -2288,9 +2392,9 @@
     titleBottomPadding: Dp,
     smallTitle: @Composable () -> Unit,
     smallTitleTextStyle: TextStyle,
-    subtitle: @Composable () -> Unit,
+    subtitle: (@Composable () -> Unit)?,
     subtitleTextStyle: TextStyle,
-    smallSubtitle: @Composable () -> Unit,
+    smallSubtitle: (@Composable () -> Unit)?,
     smallSubtitleTextStyle: TextStyle,
     titleHorizontalAlignment: TopAppBarTitleAlignment,
     navigationIcon: @Composable () -> Unit,
@@ -2465,7 +2569,7 @@
     actionIconContentColor: Color,
     title: @Composable () -> Unit,
     titleTextStyle: TextStyle,
-    subtitle: @Composable () -> Unit,
+    subtitle: (@Composable () -> Unit)?,
     subtitleTextStyle: TextStyle,
     titleAlpha: () -> Float,
     titleVerticalArrangement: Arrangement.Vertical,
@@ -2483,31 +2587,48 @@
                     content = navigationIcon
                 )
             }
-            Column(
-                modifier =
-                    Modifier.layoutId("title")
-                        .padding(horizontal = TopAppBarHorizontalPadding)
-                        .then(
-                            if (hideTitleSemantics) Modifier.clearAndSetSemantics {} else Modifier
-                        )
-                        .graphicsLayer { alpha = titleAlpha() },
-                horizontalAlignment =
-                    when (titleHorizontalAlignment) {
-                        TopAppBarTitleAlignment.Start -> Alignment.Start
-                        TopAppBarTitleAlignment.Center -> Alignment.CenterHorizontally
-                        else -> Alignment.End
+            if (subtitle != null) {
+                Column(
+                    modifier =
+                        Modifier.layoutId("title")
+                            .padding(horizontal = TopAppBarHorizontalPadding)
+                            .then(
+                                if (hideTitleSemantics) Modifier.clearAndSetSemantics {}
+                                else Modifier
+                            )
+                            .graphicsLayer { alpha = titleAlpha() },
+                    horizontalAlignment =
+                        when (titleHorizontalAlignment) {
+                            TopAppBarTitleAlignment.Start -> Alignment.Start
+                            TopAppBarTitleAlignment.Center -> Alignment.CenterHorizontally
+                            else -> Alignment.End
+                        }
+                ) {
+                    ProvideContentColorTextStyle(
+                        contentColor = titleContentColor,
+                        textStyle = titleTextStyle
+                    ) {
+                        title()
+                        ProvideTextStyle(value = subtitleTextStyle, content = subtitle)
                     }
-            ) {
-                ProvideContentColorTextStyle(
-                    contentColor = titleContentColor,
-                    textStyle = titleTextStyle,
-                    content = title
-                )
-                ProvideContentColorTextStyle(
-                    contentColor = titleContentColor,
-                    textStyle = subtitleTextStyle,
-                    content = subtitle
-                )
+                }
+            } else { // TODO(b/352770398): Workaround to maintain compatibility
+                Box(
+                    modifier =
+                        Modifier.layoutId("title")
+                            .padding(horizontal = TopAppBarHorizontalPadding)
+                            .then(
+                                if (hideTitleSemantics) Modifier.clearAndSetSemantics {}
+                                else Modifier
+                            )
+                            .graphicsLayer { alpha = titleAlpha() }
+                ) {
+                    ProvideContentColorTextStyle(
+                        contentColor = titleContentColor,
+                        textStyle = titleTextStyle,
+                        content = title
+                    )
+                }
             }
             Box(Modifier.layoutId("actionIcons").padding(end = TopAppBarHorizontalPadding)) {
                 CompositionLocalProvider(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt
index a8c9b1e..29c9337 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material3
 
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -32,7 +33,9 @@
 import androidx.compose.material3.internal.anchoredDraggable
 import androidx.compose.material3.internal.draggableAnchors
 import androidx.compose.material3.internal.getString
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -40,6 +43,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.LocalDensity
@@ -52,6 +57,7 @@
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMap
 import androidx.compose.ui.util.fastMaxOfOrNull
+import kotlin.math.max
 import kotlin.math.roundToInt
 import kotlinx.coroutines.launch
 
@@ -91,7 +97,7 @@
  * @param sheetDragHandle optional visual marker to pull the scaffold's bottom sheet
  * @param sheetSwipeEnabled whether the sheet swiping is enabled and should react to the user's
  *   input
- * @param topBar top app bar of the screen, typically a [SmallTopAppBar]
+ * @param topBar top app bar of the screen, typically a [TopAppBar]
  * @param snackbarHost component to host [Snackbar]s that are pushed to be shown via
  *   [SnackbarHostState.showSnackbar], typically a [SnackbarHost]
  * @param containerColor the color used for the background of this scaffold. Use [Color.Transparent]
@@ -207,7 +213,7 @@
         skipHiddenState = skipHiddenState,
     )
 
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun StandardBottomSheet(
     state: SheetState,
@@ -222,6 +228,18 @@
     dragHandle: @Composable (() -> Unit)?,
     content: @Composable ColumnScope.() -> Unit
 ) {
+    // TODO Load the motionScheme tokens from the component tokens file
+    val anchoredDraggableMotion: FiniteAnimationSpec<Float> =
+        MotionSchemeKeyTokens.DefaultSpatial.value()
+    val showMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.DefaultSpatial.value()
+    val hideMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.FastEffects.value()
+
+    SideEffect {
+        state.showMotionSpec = showMotion
+        state.hideMotionSpec = hideMotion
+        state.anchoredDraggableMotionSpec = anchoredDraggableMotion
+    }
+
     val scope = rememberCoroutineScope()
     val orientation = Orientation.Vertical
     val peekHeightPx = with(LocalDensity.current) { peekHeight.toPx() }
@@ -285,6 +303,14 @@
                     state = state.anchoredDraggableState,
                     orientation = orientation,
                     enabled = sheetSwipeEnabled
+                )
+                // Scale up the Surface vertically in case the sheet's offset overflows below the
+                // min anchor. This is done to avoid showing a gap when the sheet opens and bounces
+                // when it's applied with a bouncy motion. Note that the content inside the Surface
+                // is scaled back down to maintain its aspect ratio (see below).
+                .verticalScaleUp(
+                    { state.anchoredDraggableState.offset },
+                    { state.anchoredDraggableState.anchors.minAnchor() }
                 ),
         shape = shape,
         color = containerColor,
@@ -292,7 +318,16 @@
         tonalElevation = tonalElevation,
         shadowElevation = shadowElevation,
     ) {
-        Column(Modifier.fillMaxWidth()) {
+        Column(
+            Modifier.fillMaxWidth()
+                // Scale the content down in case the sheet offset overflows below the min anchor.
+                // The wrapping Surface is scaled up, so this is done to maintain the content's
+                // aspect ratio.
+                .verticalScaleDown(
+                    { state.anchoredDraggableState.offset },
+                    { state.anchoredDraggableState.anchors.minAnchor() }
+                )
+        ) {
             if (dragHandle != null) {
                 val partialExpandActionLabel =
                     getString(Strings.BottomSheetPartialExpandDescription)
@@ -386,7 +421,7 @@
 
         layout(layoutWidth, layoutHeight) {
             val sheetWidth = sheetPlaceables.fastMaxOfOrNull { it.width } ?: 0
-            val sheetOffsetX = Integer.max(0, (layoutWidth - sheetWidth) / 2)
+            val sheetOffsetX = max(0, (layoutWidth - sheetWidth) / 2)
 
             val snackbarWidth = snackbarPlaceables.fastMaxOfOrNull { it.width } ?: 0
             val snackbarHeight = snackbarPlaceables.fastMaxOfOrNull { it.height } ?: 0
@@ -406,3 +441,44 @@
         }
     }
 }
+
+/**
+ * A [Modifier] that scales up the drawing layer on the Y axis in case the [sheetOffset] overflows
+ * below the min anchor coordinates. The scaling will ensure that there is no visible gap between
+ * the sheet and the edge of the screen in case the sheet bounces when it opens due to a more
+ * expressive motion setting.
+ *
+ * A [verticalScaleDown] should be applied to the content of the sheet to maintain the content
+ * aspect ratio as the container scales up.
+ *
+ * @param sheetOffset a lambda that provides the current sheet's offset
+ * @param minAnchor a lambda that provides the sheet's min anchor coordinate
+ * @see verticalScaleDown
+ */
+internal fun Modifier.verticalScaleUp(sheetOffset: () -> Float, minAnchor: () -> Float) =
+    graphicsLayer {
+        val offset = sheetOffset()
+        val anchor = minAnchor()
+        val overflow = if (offset < anchor) anchor - offset else 0f
+        scaleY = if (overflow > 0f) (size.height + overflow) / size.height else 1f
+        transformOrigin = TransformOrigin(pivotFractionX = 0.5f, pivotFractionY = 0f)
+    }
+
+/**
+ * A [Modifier] that scales down the drawing layer on the Y axis in case the [sheetOffset] overflows
+ * below the min anchor coordinates. This modifier should be applied to the content inside a
+ * component that was scaled up with a [verticalScaleUp] modifier. It will ensure that the content
+ * maintains its aspect ratio as the container scales up.
+ *
+ * @param sheetOffset a lambda that provides the current sheet's offset
+ * @param minAnchor a lambda that provides the sheet's min anchor coordinate
+ * @see verticalScaleUp
+ */
+internal fun Modifier.verticalScaleDown(sheetOffset: () -> Float, minAnchor: () -> Float) =
+    graphicsLayer {
+        val offset = sheetOffset()
+        val anchor = minAnchor()
+        val overflow = if (offset < anchor) anchor - offset else 0f
+        scaleY = if (overflow > 0f) 1 / ((size.height + overflow) / size.height) else 1f
+        transformOrigin = TransformOrigin(pivotFractionX = 0.5f, pivotFractionY = 0f)
+    }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
index 544d421..84c8d9c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
@@ -17,10 +17,9 @@
 package androidx.compose.material3
 
 import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.animateFloat
 import androidx.compose.animation.core.snap
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.interaction.Interaction
@@ -30,6 +29,7 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.selection.triStateToggleable
 import androidx.compose.material3.tokens.CheckboxTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.State
@@ -257,6 +257,7 @@
         }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun CheckboxImpl(
     enabled: Boolean,
@@ -269,9 +270,11 @@
         transition.animateFloat(
             transitionSpec = {
                 when {
-                    initialState == ToggleableState.Off -> tween(CheckAnimationDuration)
-                    targetState == ToggleableState.Off -> snap(BoxOutDuration)
-                    else -> spring()
+                    // TODO Load the motionScheme tokens from the component tokens file
+                    initialState == ToggleableState.Off ->
+                        MotionSchemeKeyTokens.DefaultSpatial.value()
+                    targetState == ToggleableState.Off -> snap(delayMillis = SnapAnimationDelay)
+                    else -> MotionSchemeKeyTokens.DefaultSpatial.value()
                 }
             }
         ) {
@@ -286,9 +289,10 @@
         transition.animateFloat(
             transitionSpec = {
                 when {
+                    // TODO Load the motionScheme tokens from the component tokens file
                     initialState == ToggleableState.Off -> snap()
-                    targetState == ToggleableState.Off -> snap(BoxOutDuration)
-                    else -> tween(durationMillis = CheckAnimationDuration)
+                    targetState == ToggleableState.Off -> snap(delayMillis = SnapAnimationDelay)
+                    else -> MotionSchemeKeyTokens.DefaultSpatial.value()
                 }
             }
         ) {
@@ -481,8 +485,7 @@
                 checkedCheckmarkColor
             }
 
-        val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
-        return animateColorAsState(target, tween(durationMillis = duration))
+        return animateColorAsState(target, colorAnimationSpecForState(state))
     }
 
     /**
@@ -512,8 +515,7 @@
         // If not enabled 'snap' to the disabled state, as there should be no animations between
         // enabled / disabled.
         return if (enabled) {
-            val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
-            animateColorAsState(target, tween(durationMillis = duration))
+            animateColorAsState(target, colorAnimationSpecForState(state))
         } else {
             rememberUpdatedState(target)
         }
@@ -545,13 +547,26 @@
         // If not enabled 'snap' to the disabled state, as there should be no animations between
         // enabled / disabled.
         return if (enabled) {
-            val duration = if (state == ToggleableState.Off) BoxOutDuration else BoxInDuration
-            animateColorAsState(target, tween(durationMillis = duration))
+            animateColorAsState(target, colorAnimationSpecForState(state))
         } else {
             rememberUpdatedState(target)
         }
     }
 
+    /** Returns the color [AnimationSpec] for the given state. */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+    @Composable
+    private fun colorAnimationSpecForState(state: ToggleableState): AnimationSpec<Color> {
+        // TODO Load the motionScheme tokens from the component tokens file
+        return if (state == ToggleableState.Off) {
+            // Box out
+            MotionSchemeKeyTokens.FastEffects.value()
+        } else {
+            // Box in
+            MotionSchemeKeyTokens.DefaultEffects.value()
+        }
+    }
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other == null || other !is CheckboxColors) return false
@@ -589,9 +604,7 @@
     }
 }
 
-private const val BoxInDuration = 50
-private const val BoxOutDuration = 100
-private const val CheckAnimationDuration = 100
+private const val SnapAnimationDelay = 100
 
 // TODO(b/188529841): Update the padding and size when the Checkbox spec is finalized.
 private val CheckboxDefaultPadding = 2.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
index 2476954..b426d89 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
@@ -2087,15 +2087,15 @@
                 measurables
                     .fastFirstOrNull { it.layoutId == LeadingIconLayoutId }
                     ?.measure(constraints.copy(minWidth = 0, minHeight = 0))
-            val leadingIconWidth = widthOrZero(leadingIconPlaceable)
-            val leadingIconHeight = heightOrZero(leadingIconPlaceable)
+            val leadingIconWidth = leadingIconPlaceable.widthOrZero
+            val leadingIconHeight = leadingIconPlaceable.heightOrZero
 
             val trailingIconPlaceable: Placeable? =
                 measurables
                     .fastFirstOrNull { it.layoutId == TrailingIconLayoutId }
                     ?.measure(constraints.copy(minWidth = 0, minHeight = 0))
-            val trailingIconWidth = widthOrZero(trailingIconPlaceable)
-            val trailingIconHeight = heightOrZero(trailingIconPlaceable)
+            val trailingIconWidth = trailingIconPlaceable.widthOrZero
+            val trailingIconHeight = trailingIconPlaceable.heightOrZero
 
             val labelPlaceable =
                 measurables
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt
index e03736e..dc85616 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt
@@ -498,13 +498,16 @@
     internal var defaultOutlinedIconButtonColorsCached: IconButtonColors? = null
     internal var defaultOutlinedIconToggleButtonColorsCached: IconToggleButtonColors? = null
 
+    internal var defaultListItemColorsCached: ListItemColors? = null
+
     internal var defaultMenuItemColorsCached: MenuItemColors? = null
 
     internal var defaultNavigationBarItemColorsCached: NavigationBarItemColors? = null
+    internal var defaultShortNavigationBarItemColorsCached: NavigationItemColors? = null
 
     internal var defaultNavigationRailItemColorsCached: NavigationRailItemColors? = null
-
-    internal var defaultShortNavigationBarItemColorsCached: NavigationItemColors? = null
+    internal var defaultWideNavigationRailColorsCached: NavigationRailColors? = null
+    internal var defaultWideNavigationRailItemColorsCached: NavigationItemColors? = null
 
     internal var defaultRadioButtonColorsCached: RadioButtonColors? = null
 
@@ -922,10 +925,13 @@
  * Returns a light Material color scheme.
  *
  * The default color scheme for [MaterialExpressiveTheme]. For dark mode, use [darkColorScheme].
+ *
+ * Example of MaterialExpressiveTheme toggling expressiveLightColorScheme and darkTheme.
+ *
+ * @sample androidx.compose.material3.samples.MaterialExpressiveThemeColorSchemeSample
  */
-// TODO: Mark as experimental if scope is changed to public
-// TODO: Add samples for MaterialExpressiveTheme toggling expressiveLightColorScheme and darkTheme.
-internal fun expressiveLightColorScheme() =
+@ExperimentalMaterial3ExpressiveApi
+fun expressiveLightColorScheme() =
     lightColorScheme(
         // TODO: Replace palette references with color token references when available.
         onPrimaryContainer = PaletteTokens.Primary30,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateInput.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateInput.kt
index d915eb2..9065f0c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateInput.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateInput.kt
@@ -47,6 +47,7 @@
 import androidx.compose.ui.text.input.TransformedText
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.dp
+import kotlin.jvm.JvmInline
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
@@ -222,25 +223,29 @@
  * @param errorInvalidRangeInput a string for displaying an error message when in a range input mode
  *   and one of the input dates is out of order (i.e. the user inputs a start date that is after the
  *   end date, or an end date that is before the start date)
- * @param currentStartDateMillis the currently selected start date in milliseconds. Only checked
- *   against when the [InputIdentifier] is [InputIdentifier.EndDateInput].
- * @param currentEndDateMillis the currently selected end date in milliseconds. Only checked against
- *   when the [InputIdentifier] is [InputIdentifier.StartDateInput].
  */
 @OptIn(ExperimentalMaterial3Api::class)
 @Stable
-internal class DateInputValidator(
-    private val yearRange: IntRange,
-    private val selectableDates: SelectableDates,
-    private val dateInputFormat: DateInputFormat,
-    private val dateFormatter: DatePickerFormatter,
-    private val errorDatePattern: String,
-    private val errorDateOutOfYearRange: String,
-    private val errorInvalidNotAllowed: String,
-    private val errorInvalidRangeInput: String,
-    internal var currentStartDateMillis: Long? = null,
-    internal var currentEndDateMillis: Long? = null,
+internal expect class DateInputValidator(
+    yearRange: IntRange,
+    selectableDates: SelectableDates,
+    dateInputFormat: DateInputFormat,
+    dateFormatter: DatePickerFormatter,
+    errorDatePattern: String,
+    errorDateOutOfYearRange: String,
+    errorInvalidNotAllowed: String,
+    errorInvalidRangeInput: String,
 ) {
+    /**
+     * the currently selected start date in milliseconds. Only checked against when the
+     * [InputIdentifier] is [InputIdentifier.EndDateInput].
+     */
+    var currentStartDateMillis: Long?
+    /**
+     * the currently selected end date in milliseconds. Only checked against when the
+     * [InputIdentifier] is [InputIdentifier.StartDateInput].
+     */
+    var currentEndDateMillis: Long?
 
     /**
      * Validates a [CalendarDate] input and returns an error string in case an issue with the given
@@ -255,45 +260,7 @@
         dateToValidate: CalendarDate?,
         inputIdentifier: InputIdentifier,
         locale: CalendarLocale
-    ): String {
-        if (dateToValidate == null) {
-            return errorDatePattern.format(dateInputFormat.patternWithDelimiters.uppercase())
-        }
-        // Check that the date is within the valid range of years.
-        if (!yearRange.contains(dateToValidate.year)) {
-            return errorDateOutOfYearRange.format(
-                yearRange.first.toLocalString(),
-                yearRange.last.toLocalString()
-            )
-        }
-        // Check that the provided SelectableDates allows this date to be selected.
-        with(selectableDates) {
-            if (
-                !isSelectableYear(dateToValidate.year) ||
-                    !isSelectableDate(dateToValidate.utcTimeMillis)
-            ) {
-                return errorInvalidNotAllowed.format(
-                    dateFormatter.formatDate(
-                        dateMillis = dateToValidate.utcTimeMillis,
-                        locale = locale
-                    )
-                )
-            }
-        }
-
-        // Additional validation when the InputIdentifier is for start of end dates in a range input
-        if (
-            (inputIdentifier == InputIdentifier.StartDateInput &&
-                dateToValidate.utcTimeMillis >= (currentEndDateMillis ?: Long.MAX_VALUE)) ||
-                (inputIdentifier == InputIdentifier.EndDateInput &&
-                    dateToValidate.utcTimeMillis < (currentStartDateMillis ?: Long.MIN_VALUE))
-        ) {
-            // The input start date is after the end date, or the end date is before the start date.
-            return errorInvalidRangeInput
-        }
-
-        return ""
-    }
+    ): String
 }
 
 /**
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
index bf91ec5..fc3c261 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
@@ -20,10 +20,8 @@
 import androidx.compose.animation.SizeTransform
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.exponentialDecay
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.expandVertically
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
@@ -53,21 +51,15 @@
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.lazy.LazyRow
 import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
 import androidx.compose.foundation.lazy.grid.rememberLazyGridState
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
-import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
-import androidx.compose.material.icons.filled.ArrowDropDown
-import androidx.compose.material.icons.filled.DateRange
-import androidx.compose.material.icons.filled.Edit
 import androidx.compose.material3.OutlinedTextFieldDefaults.defaultOutlinedTextFieldColors
 import androidx.compose.material3.internal.CalendarModel
 import androidx.compose.material3.internal.CalendarMonth
 import androidx.compose.material3.internal.DaysInWeek
+import androidx.compose.material3.internal.Icons
 import androidx.compose.material3.internal.MillisecondsIn24Hours
 import androidx.compose.material3.internal.ProvideContentColorTextStyle
 import androidx.compose.material3.internal.Strings
@@ -77,7 +69,7 @@
 import androidx.compose.material3.tokens.DatePickerModalTokens
 import androidx.compose.material3.tokens.DividerTokens
 import androidx.compose.material3.tokens.ElevationTokens
-import androidx.compose.material3.tokens.MotionTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
@@ -103,13 +95,11 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.semantics.CustomAccessibilityAction
 import androidx.compose.ui.semantics.LiveRegionMode
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.ScrollAxisRange
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.customActions
 import androidx.compose.ui.semantics.horizontalScrollAxisRange
 import androidx.compose.ui.semantics.isContainer
 import androidx.compose.ui.semantics.liveRegion
@@ -117,15 +107,16 @@
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.text
-import androidx.compose.ui.semantics.verticalScrollAxisRange
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastForEach
+import kotlin.jvm.JvmInline
 import kotlin.math.max
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
 /**
@@ -672,11 +663,14 @@
                 }
 
         val headlineDescription =
-            when (displayMode) {
-                DisplayMode.Picker -> getString(Strings.DatePickerHeadlineDescription)
-                DisplayMode.Input -> getString(Strings.DateInputHeadlineDescription)
-                else -> ""
-            }.format(verboseDateDescription)
+            formatHeadlineDescription(
+                when (displayMode) {
+                    DisplayMode.Picker -> getString(Strings.DatePickerHeadlineDescription)
+                    DisplayMode.Input -> getString(Strings.DateInputHeadlineDescription)
+                    else -> ""
+                },
+                verboseDateDescription
+            )
 
         Text(
             text = headlineText,
@@ -696,11 +690,14 @@
      * @param lazyListState a [LazyListState]
      * @param decayAnimationSpec the decay to use
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @Composable
     internal fun rememberSnapFlingBehavior(
         lazyListState: LazyListState,
         decayAnimationSpec: DecayAnimationSpec<Float> = exponentialDecay()
     ): FlingBehavior {
+        // TODO Load the motionScheme tokens from the component tokens file
+        val animationSpec: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.DefaultEffects.value()
         return remember(decayAnimationSpec, lazyListState) {
             val original = SnapLayoutInfoProvider(lazyListState)
             val snapLayoutInfoProvider =
@@ -714,7 +711,7 @@
             snapFlingBehavior(
                 snapLayoutInfoProvider = snapLayoutInfoProvider,
                 decayAnimationSpec = decayAnimationSpec,
-                snapAnimationSpec = spring(stiffness = Spring.StiffnessMediumLow)
+                snapAnimationSpec = animationSpec,
             )
         }
     }
@@ -748,6 +745,11 @@
     const val YearMonthWeekdayDaySkeleton: String = "yMMMMEEEEd"
 }
 
+internal expect inline fun formatHeadlineDescription(
+    template: String,
+    verboseDateDescription: String
+): String
+
 /**
  * Represents the colors used by the date picker.
  *
@@ -785,6 +787,7 @@
  * @constructor create an instance with arbitrary colors, see [DatePickerDefaults.colors] for the
  *   default implementation that follows Material specifications.
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @ExperimentalMaterial3Api
 @Immutable
 class DatePickerColors
@@ -910,7 +913,11 @@
             rememberUpdatedState(target)
         } else {
             // Animate the content color only when the day is not in a range.
-            animateColorAsState(target, tween(durationMillis = MotionTokens.DurationShort2.toInt()))
+            animateColorAsState(
+                target,
+                // TODO Load the motionScheme tokens from the component tokens file
+                MotionSchemeKeyTokens.DefaultEffects.value()
+            )
         }
     }
 
@@ -934,7 +941,11 @@
                 Color.Transparent
             }
         return if (animate) {
-            animateColorAsState(target, tween(durationMillis = MotionTokens.DurationShort2.toInt()))
+            animateColorAsState(
+                target,
+                // TODO Load the motionScheme tokens from the component tokens file
+                MotionSchemeKeyTokens.DefaultEffects.value()
+            )
         } else {
             rememberUpdatedState(target)
         }
@@ -964,7 +975,8 @@
 
         return animateColorAsState(
             target,
-            tween(durationMillis = MotionTokens.DurationShort2.toInt())
+            // TODO Load the motionScheme tokens from the component tokens file
+            MotionSchemeKeyTokens.DefaultEffects.value()
         )
     }
 
@@ -984,7 +996,8 @@
             }
         return animateColorAsState(
             target,
-            tween(durationMillis = MotionTokens.DurationShort2.toInt())
+            // TODO Load the motionScheme tokens from the component tokens file
+            MotionSchemeKeyTokens.DefaultEffects.value()
         )
     }
 
@@ -1376,7 +1389,7 @@
  * Date entry content that displays a [DatePickerContent] or a [DateInputContent] according to the
  * state's display mode.
  */
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun SwitchableDateEntryContent(
     selectedDateMillis: Long?,
@@ -1393,6 +1406,15 @@
     // Parallax effect offset that will slightly scroll in and out the navigation part of the picker
     // when the display mode changes.
     val parallaxTarget = with(LocalDensity.current) { -48.dp.roundToPx() }
+    // TODO Load the motionScheme tokens from the component tokens file
+    val effectsInAnimationSpec: FiniteAnimationSpec<Float> =
+        MotionSchemeKeyTokens.DefaultEffects.value()
+    val effectsOutAnimationSpec: FiniteAnimationSpec<Float> =
+        MotionSchemeKeyTokens.FastEffects.value()
+    val spatialInOutAnimationSpec: FiniteAnimationSpec<IntOffset> =
+        MotionSchemeKeyTokens.DefaultSpatial.value()
+    val spatialSizeAnimationSpec: FiniteAnimationSpec<IntSize> =
+        MotionSchemeKeyTokens.DefaultSpatial.value()
     AnimatedContent(
         targetState = displayMode,
         modifier =
@@ -1406,42 +1428,30 @@
             // When animating the input mode, fade out the calendar picker and slide in the text
             // field from the bottom with a delay to show up after the picker is hidden.
             if (targetState == DisplayMode.Input) {
-                    slideInVertically { height -> height } +
-                        fadeIn(
-                            animationSpec =
-                                tween(
-                                    durationMillis = MotionTokens.DurationShort2.toInt(),
-                                    delayMillis = MotionTokens.DurationShort2.toInt()
-                                )
-                        ) togetherWith
-                        fadeOut(tween(durationMillis = MotionTokens.DurationShort2.toInt())) +
-                            slideOutVertically(targetOffsetY = { _ -> parallaxTarget })
+                    slideInVertically(animationSpec = spatialInOutAnimationSpec) { height ->
+                        height
+                    } + fadeIn(animationSpec = effectsInAnimationSpec) togetherWith
+                        fadeOut(effectsOutAnimationSpec) +
+                            slideOutVertically(
+                                animationSpec = spatialInOutAnimationSpec,
+                                targetOffsetY = { _ -> parallaxTarget }
+                            )
                 } else {
                     // When animating the picker mode, slide out text field and fade in calendar
                     // picker with a delay to show up after the text field is hidden.
                     slideInVertically(
-                        animationSpec = tween(delayMillis = MotionTokens.DurationShort1.toInt()),
+                        animationSpec = spatialInOutAnimationSpec,
                         initialOffsetY = { _ -> parallaxTarget }
-                    ) +
-                        fadeIn(
-                            animationSpec =
-                                tween(
-                                    durationMillis = MotionTokens.DurationShort2.toInt(),
-                                    delayMillis = MotionTokens.DurationShort2.toInt()
-                                )
-                        ) togetherWith
-                        slideOutVertically(targetOffsetY = { fullHeight -> fullHeight }) +
-                            fadeOut(animationSpec = tween(MotionTokens.DurationShort2.toInt()))
+                    ) + fadeIn(animationSpec = effectsInAnimationSpec) togetherWith
+                        slideOutVertically(
+                            animationSpec = spatialInOutAnimationSpec,
+                            targetOffsetY = { fullHeight -> fullHeight }
+                        ) + fadeOut(animationSpec = effectsOutAnimationSpec)
                 }
                 .using(
                     SizeTransform(
                         clip = true,
-                        sizeAnimationSpec = { _, _ ->
-                            tween(
-                                MotionTokens.DurationLong2.toInt(),
-                                easing = MotionTokens.EasingEmphasizedDecelerateCubicBezier
-                            )
-                        }
+                        sizeAnimationSpec = { _, _ -> spatialSizeAnimationSpec }
                     )
                 )
         },
@@ -1474,7 +1484,7 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun DatePickerContent(
     selectedDateMillis: Long?,
@@ -1547,11 +1557,22 @@
                     colors = colors
                 )
             }
+            // TODO Load the motionScheme tokens from the component tokens file
+            val fadeInAnimationSpec: FiniteAnimationSpec<Float> =
+                MotionSchemeKeyTokens.DefaultEffects.value()
+            val fadeOutAnimationSpec: FiniteAnimationSpec<Float> =
+                MotionSchemeKeyTokens.FastEffects.value()
+            val shrinkExpandAnimationSpec: FiniteAnimationSpec<IntSize> =
+                MotionSchemeKeyTokens.DefaultEffects.value()
             androidx.compose.animation.AnimatedVisibility(
                 visible = yearPickerVisible,
                 modifier = Modifier.clipToBounds(),
-                enter = expandVertically() + fadeIn(initialAlpha = 0.6f),
-                exit = shrinkVertically() + fadeOut()
+                enter =
+                    expandVertically(animationSpec = shrinkExpandAnimationSpec) +
+                        fadeIn(animationSpec = fadeInAnimationSpec, initialAlpha = 0.6f),
+                exit =
+                    shrinkVertically(animationSpec = shrinkExpandAnimationSpec) +
+                        fadeOut(animationSpec = fadeOutAnimationSpec)
             ) {
                 // Apply a paneTitle to make the screen reader focus on a relevant node after this
                 // column is hidden and disposed.
@@ -1994,22 +2015,11 @@
                 // selection of previous years.
                 initialFirstVisibleItemIndex = max(0, displayedYear - yearRange.first - YearsInRow)
             )
-        // Match the years container color to any elevated surface color that is composed under it.
-        val containerColor = colors.containerColor
-        val coroutineScope = rememberCoroutineScope()
-        val scrollToEarlierYearsLabel = getString(Strings.DatePickerScrollToShowEarlierYears)
-        val scrollToLaterYearsLabel = getString(Strings.DatePickerScrollToShowLaterYears)
         LazyVerticalGrid(
             columns = GridCells.Fixed(YearsInRow),
-            modifier =
-                modifier
-                    .background(containerColor)
-                    // Apply this to have the screen reader traverse outside the visible list of
-                    // years
-                    // and not scroll them by default.
-                    .semantics {
-                        verticalScrollAxisRange = ScrollAxisRange(value = { 0f }, maxValue = { 0f })
-                    },
+            // Match the years container color to any elevated surface color that is composed under
+            // it.
+            modifier = modifier.background(colors.containerColor),
             state = lazyGridState,
             horizontalArrangement = Arrangement.SpaceEvenly,
             verticalArrangement = Arrangement.spacedBy(YearsVerticalPadding)
@@ -2020,39 +2030,18 @@
                 Year(
                     modifier =
                         Modifier.requiredSize(
-                                width = DatePickerModalTokens.SelectionYearContainerWidth,
-                                height = DatePickerModalTokens.SelectionYearContainerHeight
-                            )
-                            .semantics {
-                                // Apply a11y custom actions to the first and last items in the
-                                // years
-                                // grid. The actions will suggest to scroll to earlier or later
-                                // years in
-                                // the grid.
-                                customActions =
-                                    if (
-                                        lazyGridState.firstVisibleItemIndex == it ||
-                                            lazyGridState.layoutInfo.visibleItemsInfo
-                                                .lastOrNull()
-                                                ?.index == it
-                                    ) {
-                                        customScrollActions(
-                                            state = lazyGridState,
-                                            coroutineScope = coroutineScope,
-                                            scrollUpLabel = scrollToEarlierYearsLabel,
-                                            scrollDownLabel = scrollToLaterYearsLabel
-                                        )
-                                    } else {
-                                        emptyList()
-                                    }
-                            },
+                            width = DatePickerModalTokens.SelectionYearContainerWidth,
+                            height = DatePickerModalTokens.SelectionYearContainerHeight
+                        ),
                     selected = selectedYear == displayedYear,
                     currentYear = selectedYear == currentYear,
                     onClick = { onYearSelected(selectedYear) },
                     enabled = selectableDates.isSelectableYear(selectedYear),
                     description =
-                        getString(Strings.DatePickerNavigateToYearDescription)
-                            .format(localizedYear),
+                        formatDatePickerNavigateToYearString(
+                            getString(Strings.DatePickerNavigateToYearDescription),
+                            localizedYear
+                        ),
                     colors = colors
                 ) {
                     Text(
@@ -2067,6 +2056,11 @@
     }
 }
 
+internal expect inline fun formatDatePickerNavigateToYearString(
+    template: String,
+    localizedYear: String
+): String
+
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun Year(
@@ -2153,8 +2147,8 @@
                     modifier =
                         Modifier.semantics {
                             // Make the screen reader read out updates to the menu button text as
-                            // the
-                            // user navigates the arrows or scrolls to change the displayed month.
+                            // the user navigates the arrows or scrolls to change the displayed
+                            // month.
                             liveRegion = LiveRegionMode.Polite
                             contentDescription = yearPickerText
                         }
@@ -2212,34 +2206,6 @@
     }
 }
 
-private fun customScrollActions(
-    state: LazyGridState,
-    coroutineScope: CoroutineScope,
-    scrollUpLabel: String,
-    scrollDownLabel: String
-): List<CustomAccessibilityAction> {
-    val scrollUpAction = {
-        if (!state.canScrollBackward) {
-            false
-        } else {
-            coroutineScope.launch { state.scrollToItem(state.firstVisibleItemIndex - YearsInRow) }
-            true
-        }
-    }
-    val scrollDownAction = {
-        if (!state.canScrollForward) {
-            false
-        } else {
-            coroutineScope.launch { state.scrollToItem(state.firstVisibleItemIndex + YearsInRow) }
-            true
-        }
-    }
-    return listOf(
-        CustomAccessibilityAction(label = scrollUpLabel, action = scrollUpAction),
-        CustomAccessibilityAction(label = scrollDownLabel, action = scrollDownAction)
-    )
-}
-
 internal val RecommendedSizeForAccessibility = 48.dp
 internal val MonthYearHeight = 56.dp
 internal val DatePickerHorizontalPadding = 12.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateRangePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateRangePicker.kt
index 01bc36c..59a8f4b 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateRangePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DateRangePicker.kt
@@ -17,7 +17,6 @@
 package androidx.compose.material3
 
 import androidx.compose.animation.Crossfade
-import androidx.compose.animation.core.spring
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
@@ -34,6 +33,7 @@
 import androidx.compose.material3.internal.createCalendarModel
 import androidx.compose.material3.internal.getString
 import androidx.compose.material3.tokens.DatePickerModalTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
@@ -644,7 +644,7 @@
  * Date entry content that displays a [DateRangePickerContent] or a [DateRangeInputContent]
  * according to the state's display mode.
  */
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun SwitchableDateEntryContent(
     selectedStartDateMillis: Long?,
@@ -661,9 +661,10 @@
 ) {
     // TODO(b/266480386): Apply the motion spec for this once we have it. Consider replacing this
     //  with AnimatedContent when it's out of experimental.
+    // TODO Load the motionScheme tokens from the component tokens file
     Crossfade(
         targetState = displayMode,
-        animationSpec = spring(),
+        animationSpec = MotionSchemeKeyTokens.FastEffects.value(),
         modifier =
             Modifier.semantics {
                 @Suppress("DEPRECATION")
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
index 9fc92ad..d0d23f8 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
@@ -19,7 +19,8 @@
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.VectorConverter
-import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.updateTransition
 import androidx.compose.animation.expandHorizontally
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
@@ -45,22 +46,28 @@
 import androidx.compose.material3.tokens.FabPrimaryLargeTokens
 import androidx.compose.material3.tokens.FabPrimarySmallTokens
 import androidx.compose.material3.tokens.FabPrimaryTokens
-import androidx.compose.material3.tokens.MotionTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
+import androidx.compose.material3.tokens.TypographyKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.lerp
 import kotlinx.coroutines.launch
 
 /**
@@ -103,6 +110,34 @@
     elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
     interactionSource: MutableInteractionSource? = null,
     content: @Composable () -> Unit,
+) =
+    FloatingActionButton(
+        onClick,
+        ExtendedFabPrimaryTokens.LabelTextFont.value,
+        FabPrimaryTokens.ContainerWidth,
+        FabPrimaryTokens.ContainerHeight,
+        modifier,
+        shape,
+        containerColor,
+        contentColor,
+        elevation,
+        interactionSource,
+        content
+    )
+
+@Composable
+private fun FloatingActionButton(
+    onClick: () -> Unit,
+    textStyle: TextStyle,
+    minWidth: Dp,
+    minHeight: Dp,
+    modifier: Modifier = Modifier,
+    shape: Shape = FloatingActionButtonDefaults.shape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+    content: @Composable () -> Unit,
 ) {
     @Suppress("NAME_SHADOWING")
     val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
@@ -116,15 +151,12 @@
         shadowElevation = elevation.shadowElevation(interactionSource = interactionSource).value,
         interactionSource = interactionSource
     ) {
-        ProvideContentColorTextStyle(
-            contentColor = contentColor,
-            textStyle = ExtendedFabPrimaryTokens.LabelTextFont.value
-        ) {
+        ProvideContentColorTextStyle(contentColor = contentColor, textStyle = textStyle) {
             Box(
                 modifier =
                     Modifier.defaultMinSize(
-                        minWidth = FabPrimaryTokens.ContainerWidth,
-                        minHeight = FabPrimaryTokens.ContainerHeight,
+                        minWidth = minWidth,
+                        minHeight = minHeight,
                     ),
                 contentAlignment = Alignment.Center,
             ) {
@@ -191,6 +223,60 @@
 
 /**
  * <a href="https://m3.material.io/components/floating-action-button/overview" class="external"
+ * target="_blank">Material Design medium floating action button</a>.
+ *
+ * The FAB represents the most important action on a screen. It puts key actions within reach.
+ *
+ * @sample androidx.compose.material3.samples.MediumFloatingActionButtonSample
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ *   have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ *   matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ *   different states. This controls the size of the shadow below the FAB. Additionally, when the
+ *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ *   an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ *   preview the FAB in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ * @param content the content of this FAB, typically an [Icon]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun MediumFloatingActionButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    shape: Shape = FloatingActionButtonDefaults.mediumShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+    content: @Composable () -> Unit,
+) {
+    FloatingActionButton(
+        onClick = onClick,
+        modifier =
+            // TODO: update sizes to use tokens
+            modifier.sizeIn(
+                minWidth = 80.dp,
+                minHeight = 80.dp,
+            ),
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+        content = content,
+    )
+}
+
+/**
+ * <a href="https://m3.material.io/components/floating-action-button/overview" class="external"
  * target="_blank">Material Design large floating action button</a>.
  *
  * The FAB represents the most important action on a screen. It puts key actions within reach.
@@ -244,6 +330,192 @@
     )
 }
 
+// TODO link to image
+/**
+ * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
+ * target="_blank">Material Design small extended floating action button</a>.
+ *
+ * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
+ * label and larger target area.
+ *
+ * The other small extended floating action button overload supports a text label and icon.
+ *
+ * @sample androidx.compose.material3.samples.SmallExtendedFloatingActionButtonTextSample
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ *   have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ *   matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ *   different states. This controls the size of the shadow below the FAB. Additionally, when the
+ *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ *   an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ *   preview the FAB in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ * @param content the content of this FAB, typically a [Text] label
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun SmallExtendedFloatingActionButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    shape: Shape = FloatingActionButtonDefaults.smallExtendedFabShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+    content: @Composable RowScope.() -> Unit,
+) {
+    FloatingActionButton(
+        onClick = onClick,
+        textStyle = SmallExtendedFabTextStyle.value,
+        minWidth = SmallExtendedFabMinimumWidth,
+        minHeight = SmallExtendedFabMinimumHeight,
+        modifier = modifier,
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+    ) {
+        Row(
+            modifier = Modifier.padding(horizontal = SmallExtendedFabHorizontalPadding),
+            horizontalArrangement = Arrangement.Center,
+            verticalAlignment = Alignment.CenterVertically,
+            content = content,
+        )
+    }
+}
+
+// TODO link to image
+/**
+ * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
+ * target="_blank">Material Design medium extended floating action button</a>.
+ *
+ * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
+ * label and larger target area.
+ *
+ * The other medium extended floating action button overload supports a text label and icon.
+ *
+ * @sample androidx.compose.material3.samples.MediumExtendedFloatingActionButtonTextSample
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ *   have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ *   matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ *   different states. This controls the size of the shadow below the FAB. Additionally, when the
+ *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ *   an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ *   preview the FAB in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ * @param content the content of this FAB, typically a [Text] label
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun MediumExtendedFloatingActionButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    shape: Shape = FloatingActionButtonDefaults.mediumExtendedFabShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+    content: @Composable RowScope.() -> Unit,
+) {
+    FloatingActionButton(
+        onClick = onClick,
+        textStyle = MediumExtendedFabTextStyle.value,
+        minWidth = MediumExtendedFabMinimumWidth,
+        minHeight = MediumExtendedFabMinimumHeight,
+        modifier = modifier,
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+    ) {
+        Row(
+            modifier = Modifier.padding(horizontal = MediumExtendedFabHorizontalPadding),
+            horizontalArrangement = Arrangement.Center,
+            verticalAlignment = Alignment.CenterVertically,
+            content = content,
+        )
+    }
+}
+
+// TODO link to image
+/**
+ * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
+ * target="_blank">Material Design large extended floating action button</a>.
+ *
+ * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
+ * label and larger target area.
+ *
+ * The other large extended floating action button overload supports a text label and icon.
+ *
+ * @sample androidx.compose.material3.samples.LargeExtendedFloatingActionButtonTextSample
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ *   have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ *   matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ *   different states. This controls the size of the shadow below the FAB. Additionally, when the
+ *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ *   an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ *   preview the FAB in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ * @param content the content of this FAB, typically a [Text] label
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun LargeExtendedFloatingActionButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    shape: Shape = FloatingActionButtonDefaults.largeExtendedFabShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+    content: @Composable RowScope.() -> Unit,
+) {
+    FloatingActionButton(
+        onClick = onClick,
+        textStyle = LargeExtendedFabTextStyle.value,
+        minWidth = LargeExtendedFabMinimumWidth,
+        minHeight = LargeExtendedFabMinimumHeight,
+        modifier = modifier,
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+    ) {
+        Row(
+            modifier = Modifier.padding(horizontal = LargeExtendedFabHorizontalPadding),
+            horizontalArrangement = Arrangement.Center,
+            verticalAlignment = Alignment.CenterVertically,
+            content = content,
+        )
+    }
+}
+
 /**
  * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
  * target="_blank">Material Design extended floating action button</a>.
@@ -308,6 +580,207 @@
 
 /**
  * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
+ * target="_blank">Material Design small extended floating action button</a>.
+ *
+ * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
+ * label and larger target area.
+ *
+ * The other small extended floating action button overload is for FABs without an icon.
+ *
+ * Default content description for accessibility is extended from the extended fabs icon. For custom
+ * behavior, you can provide your own via [Modifier.semantics].
+ *
+ * @sample androidx.compose.material3.samples.SmallExtendedFloatingActionButtonSample
+ * @sample androidx.compose.material3.samples.SmallAnimatedExtendedFloatingActionButtonSample
+ * @param text label displayed inside this FAB
+ * @param icon icon for this FAB, typically an [Icon]
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
+ *   both the [icon] and [text]. In a collapsed state, the FAB will show only the [icon].
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ *   have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ *   matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ *   different states. This controls the size of the shadow below the FAB. Additionally, when the
+ *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ *   an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ *   preview the FAB in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun SmallExtendedFloatingActionButton(
+    text: @Composable () -> Unit,
+    icon: @Composable () -> Unit,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    expanded: Boolean = true,
+    shape: Shape = FloatingActionButtonDefaults.smallExtendedFabShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+) =
+    ExtendedFloatingActionButton(
+        text = text,
+        icon = icon,
+        onClick = onClick,
+        textStyle = SmallExtendedFabTextStyle.value,
+        minWidth = SmallExtendedFabMinimumWidth,
+        minHeight = SmallExtendedFabMinimumHeight,
+        horizontalPadding = SmallExtendedFabHorizontalPadding,
+        iconPadding = SmallExtendedFabIconPadding,
+        modifier = modifier,
+        expanded = expanded,
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+    )
+
+/**
+ * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
+ * target="_blank">Material Design medium extended floating action button</a>.
+ *
+ * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
+ * label and larger target area.
+ *
+ * The other medium extended floating action button overload is for FABs without an icon.
+ *
+ * Default content description for accessibility is extended from the extended fabs icon. For custom
+ * behavior, you can provide your own via [Modifier.semantics].
+ *
+ * @sample androidx.compose.material3.samples.MediumExtendedFloatingActionButtonSample
+ * @sample androidx.compose.material3.samples.MediumAnimatedExtendedFloatingActionButtonSample
+ * @param text label displayed inside this FAB
+ * @param icon icon for this FAB, typically an [Icon]
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
+ *   both the [icon] and [text]. In a collapsed state, the FAB will show only the [icon].
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ *   have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ *   matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ *   different states. This controls the size of the shadow below the FAB. Additionally, when the
+ *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ *   an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ *   preview the FAB in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun MediumExtendedFloatingActionButton(
+    text: @Composable () -> Unit,
+    icon: @Composable () -> Unit,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    expanded: Boolean = true,
+    shape: Shape = FloatingActionButtonDefaults.mediumExtendedFabShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+) =
+    ExtendedFloatingActionButton(
+        text = text,
+        icon = icon,
+        onClick = onClick,
+        textStyle = MediumExtendedFabTextStyle.value,
+        minWidth = MediumExtendedFabMinimumWidth,
+        minHeight = MediumExtendedFabMinimumHeight,
+        horizontalPadding = MediumExtendedFabHorizontalPadding,
+        iconPadding = MediumExtendedFabIconPadding,
+        modifier = modifier,
+        expanded = expanded,
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+    )
+
+/**
+ * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
+ * target="_blank">Material Design large extended floating action button</a>.
+ *
+ * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
+ * label and larger target area.
+ *
+ * The other large extended floating action button overload is for FABs without an icon.
+ *
+ * Default content description for accessibility is extended from the extended fabs icon. For custom
+ * behavior, you can provide your own via [Modifier.semantics].
+ *
+ * @sample androidx.compose.material3.samples.LargeExtendedFloatingActionButtonSample
+ * @sample androidx.compose.material3.samples.LargeAnimatedExtendedFloatingActionButtonSample
+ * @param text label displayed inside this FAB
+ * @param icon icon for this FAB, typically an [Icon]
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
+ *   both the [icon] and [text]. In a collapsed state, the FAB will show only the [icon].
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ *   have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ *   matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ *   different states. This controls the size of the shadow below the FAB. Additionally, when the
+ *   container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ *   an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ *   preview the FAB in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun LargeExtendedFloatingActionButton(
+    text: @Composable () -> Unit,
+    icon: @Composable () -> Unit,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    expanded: Boolean = true,
+    shape: Shape = FloatingActionButtonDefaults.largeExtendedFabShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+) =
+    ExtendedFloatingActionButton(
+        text = text,
+        icon = icon,
+        onClick = onClick,
+        textStyle = LargeExtendedFabTextStyle.value,
+        minWidth = LargeExtendedFabMinimumWidth,
+        minHeight = LargeExtendedFabMinimumHeight,
+        horizontalPadding = LargeExtendedFabHorizontalPadding,
+        iconPadding = LargeExtendedFabIconPadding,
+        modifier = modifier,
+        expanded = expanded,
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+    )
+
+/**
+ * <a href="https://m3.material.io/components/extended-fab/overview" class="external"
  * target="_blank">Material Design extended floating action button</a>.
  *
  * Extended FABs help people take primary actions. They're wider than FABs to accommodate a text
@@ -324,7 +797,7 @@
  * @sample androidx.compose.material3.samples.ExtendedFloatingActionButtonSample
  * @sample androidx.compose.material3.samples.AnimatedExtendedFloatingActionButtonSample
  * @param text label displayed inside this FAB
- * @param icon optional icon for this FAB, typically an [Icon]
+ * @param icon icon for this FAB, typically an [Icon]
  * @param onClick called when this FAB is clicked
  * @param modifier the [Modifier] to be applied to this FAB
  * @param expanded controls the expansion state of this FAB. In an expanded state, the FAB will show
@@ -373,8 +846,11 @@
             modifier =
                 Modifier.sizeIn(
                         minWidth =
-                            if (expanded) ExtendedFabMinimumWidth
-                            else FabPrimaryTokens.ContainerWidth
+                            if (expanded) {
+                                ExtendedFabMinimumWidth
+                            } else {
+                                FabPrimaryTokens.ContainerWidth
+                            }
                     )
                     .padding(start = startPadding, end = endPadding),
             verticalAlignment = Alignment.CenterVertically,
@@ -383,8 +859,8 @@
             icon()
             AnimatedVisibility(
                 visible = expanded,
-                enter = ExtendedFabExpandAnimation,
-                exit = ExtendedFabCollapseAnimation,
+                enter = extendedFabExpandAnimation(),
+                exit = extendedFabCollapseAnimation(),
             ) {
                 Row(Modifier.clearAndSetSemantics {}) {
                     Spacer(Modifier.width(ExtendedFabEndIconPadding))
@@ -395,8 +871,91 @@
     }
 }
 
+@Composable
+private fun ExtendedFloatingActionButton(
+    text: @Composable () -> Unit,
+    icon: @Composable () -> Unit,
+    onClick: () -> Unit,
+    textStyle: TextStyle,
+    minWidth: Dp,
+    minHeight: Dp,
+    horizontalPadding: Dp,
+    iconPadding: Dp,
+    modifier: Modifier = Modifier,
+    expanded: Boolean = true,
+    shape: Shape = FloatingActionButtonDefaults.extendedFabShape,
+    containerColor: Color = FloatingActionButtonDefaults.containerColor,
+    contentColor: Color = contentColorFor(containerColor),
+    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+    interactionSource: MutableInteractionSource? = null,
+) {
+    FloatingActionButton(
+        onClick = onClick,
+        textStyle = textStyle,
+        minWidth = Dp.Unspecified,
+        minHeight = Dp.Unspecified,
+        modifier = modifier,
+        shape = shape,
+        containerColor = containerColor,
+        contentColor = contentColor,
+        elevation = elevation,
+        interactionSource = interactionSource,
+    ) {
+        val expandTransition = updateTransition(if (expanded) 1f else 0f, label = "expanded state")
+        // TODO Load the motionScheme tokens from the component tokens file
+        val expandedWidthProgress =
+            expandTransition.animateFloat(
+                transitionSpec = { MotionSchemeKeyTokens.FastSpatial.value() }
+            ) {
+                it
+            }
+        val expandedAlphaProgress =
+            expandTransition.animateFloat(
+                transitionSpec = { MotionSchemeKeyTokens.FastEffects.value() }
+            ) {
+                it
+            }
+        Row(
+            modifier =
+                Modifier.layout { measurable, constraints ->
+                        val expandedWidth = measurable.maxIntrinsicWidth(constraints.maxHeight)
+                        val width =
+                            lerp(minWidth.roundToPx(), expandedWidth, expandedWidthProgress.value)
+                        val placeable = measurable.measure(constraints)
+                        layout(width, placeable.height) { placeable.place(0, 0) }
+                    }
+                    .sizeIn(minWidth = minWidth, minHeight = minHeight)
+                    .padding(horizontal = horizontalPadding),
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            icon()
+            val fullyCollapsed =
+                remember(expandTransition) {
+                    derivedStateOf {
+                        expandTransition.currentState == 0f && !expandTransition.isRunning
+                    }
+                }
+            if (!fullyCollapsed.value) {
+                Row(
+                    Modifier.clearAndSetSemantics {}
+                        .graphicsLayer { alpha = expandedAlphaProgress.value }
+                ) {
+                    Spacer(Modifier.width(iconPadding))
+                    text()
+                }
+            }
+        }
+    }
+}
+
 /** Contains the default values used by [FloatingActionButton] */
 object FloatingActionButtonDefaults {
+    /** The recommended size of the icon inside a [MediumFloatingActionButton]. */
+    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+    @get:ExperimentalMaterial3ExpressiveApi
+    @ExperimentalMaterial3ExpressiveApi
+    val MediumIconSize = 28.dp // TODO: update to use token
+
     /** The recommended size of the icon inside a [LargeFloatingActionButton]. */
     val LargeIconSize = FabPrimaryLargeTokens.IconSize
 
@@ -408,6 +967,13 @@
     val smallShape: Shape
         @Composable get() = FabPrimarySmallTokens.ContainerShape.value
 
+    /** Default shape for a medium floating action button. */
+    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+    @get:ExperimentalMaterial3ExpressiveApi
+    @ExperimentalMaterial3ExpressiveApi
+    val mediumShape: Shape
+        @Composable get() = ShapeDefaults.LargeIncreased // TODO: update to use token
+
     /** Default shape for a large floating action button. */
     val largeShape: Shape
         @Composable get() = FabPrimaryLargeTokens.ContainerShape.value
@@ -416,6 +982,27 @@
     val extendedFabShape: Shape
         @Composable get() = ExtendedFabPrimaryTokens.ContainerShape.value
 
+    /** Default shape for a small extended floating action button. */
+    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+    @get:ExperimentalMaterial3ExpressiveApi
+    @ExperimentalMaterial3ExpressiveApi
+    val smallExtendedFabShape: Shape
+        @Composable get() = ShapeDefaults.Large // TODO: update to use token
+
+    /** Default shape for a medium extended floating action button. */
+    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+    @get:ExperimentalMaterial3ExpressiveApi
+    @ExperimentalMaterial3ExpressiveApi
+    val mediumExtendedFabShape: Shape
+        @Composable get() = ShapeDefaults.LargeIncreased // TODO: update to use token
+
+    /** Default shape for a large extended floating action button. */
+    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+    @get:ExperimentalMaterial3ExpressiveApi
+    @ExperimentalMaterial3ExpressiveApi
+    val largeExtendedFabShape: Shape
+        @Composable get() = ShapeDefaults.ExtraLarge // TODO: update to use token
+
     /** Default container color for a floating action button. */
     val containerColor: Color
         @Composable get() = FabPrimaryTokens.ContainerColor.value
@@ -651,6 +1238,24 @@
     fun asState(): State<Dp> = animatable.asState()
 }
 
+private val SmallExtendedFabMinimumWidth = 56.dp
+private val SmallExtendedFabMinimumHeight = 56.dp
+private val SmallExtendedFabHorizontalPadding = 16.dp
+private val SmallExtendedFabIconPadding = 8.dp
+private val SmallExtendedFabTextStyle = TypographyKeyTokens.TitleMedium
+
+private val MediumExtendedFabMinimumWidth = 80.dp
+private val MediumExtendedFabMinimumHeight = 80.dp
+private val MediumExtendedFabHorizontalPadding = 26.dp
+private val MediumExtendedFabIconPadding = 16.dp
+private val MediumExtendedFabTextStyle = TypographyKeyTokens.TitleLarge
+
+private val LargeExtendedFabMinimumWidth = 96.dp
+private val LargeExtendedFabMinimumHeight = 96.dp
+private val LargeExtendedFabHorizontalPadding = 28.dp
+private val LargeExtendedFabIconPadding = 20.dp
+private val LargeExtendedFabTextStyle = TypographyKeyTokens.HeadlineSmall
+
 private val ExtendedFabStartIconPadding = 16.dp
 
 private val ExtendedFabEndIconPadding = 12.dp
@@ -659,37 +1264,26 @@
 
 private val ExtendedFabMinimumWidth = 80.dp
 
-private val ExtendedFabCollapseAnimation =
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun extendedFabCollapseAnimation() =
     fadeOut(
-        animationSpec =
-            tween(
-                durationMillis = MotionTokens.DurationShort2.toInt(),
-                easing = MotionTokens.EasingLinearCubicBezier,
-            )
+        // TODO Load the motionScheme tokens from the component tokens file
+        animationSpec = MotionSchemeKeyTokens.FastEffects.value()
     ) +
         shrinkHorizontally(
-            animationSpec =
-                tween(
-                    durationMillis = MotionTokens.DurationLong2.toInt(),
-                    easing = MotionTokens.EasingEmphasizedCubicBezier,
-                ),
+            animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value(),
             shrinkTowards = Alignment.Start,
         )
 
-private val ExtendedFabExpandAnimation =
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun extendedFabExpandAnimation() =
     fadeIn(
-        animationSpec =
-            tween(
-                durationMillis = MotionTokens.DurationShort4.toInt(),
-                delayMillis = MotionTokens.DurationShort2.toInt(),
-                easing = MotionTokens.EasingLinearCubicBezier,
-            ),
+        // TODO Load the motionScheme tokens from the component tokens file
+        animationSpec = MotionSchemeKeyTokens.DefaultEffects.value(),
     ) +
         expandHorizontally(
-            animationSpec =
-                tween(
-                    durationMillis = MotionTokens.DurationLong2.toInt(),
-                    easing = MotionTokens.EasingEmphasizedCubicBezier,
-                ),
+            animationSpec = MotionSchemeKeyTokens.FastSpatial.value(),
             expandFrom = Alignment.Start,
         )
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButtonMenu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButtonMenu.kt
new file mode 100644
index 0000000..75d4df4
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButtonMenu.kt
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.expandHorizontally
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.shrinkHorizontally
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.GenericShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.geometry.toRect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.layer.drawLayer
+import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.lerp
+import kotlin.math.hypot
+
+// TODO: link to spec and image
+/**
+ * FAB Menus should be used in conjunction with a [ToggleableFloatingActionButton] to provide
+ * additional choices to the user after clicking a FAB.
+ *
+ * @sample androidx.compose.material3.samples.FloatingActionButtonMenuSample
+ * @param expanded whether the FAB Menu is expanded, which will trigger a staggered animation of the
+ *   FAB Menu Items
+ * @param itemsCount the total number of FAB Menu Items in this FAB Menu
+ * @param modifier the [Modifier] to be applied to this FAB Menu
+ * @param horizontalAlignment the horizontal alignment of the FAB Menu Items
+ * @param content the content of this FAB Menu, typically a list of [FloatingActionButtonMenuItem]s
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun FloatingActionButtonMenu(
+    expanded: Boolean,
+    itemsCount: Int,
+    modifier: Modifier = Modifier,
+    horizontalAlignment: Alignment.Horizontal = Alignment.End,
+    content: @Composable FloatingActionButtonMenuScope.() -> Unit
+) =
+    Column(
+        modifier =
+            modifier
+                .verticalScroll(state = rememberScrollState())
+                .padding(bottom = FabMenuPaddingBottom),
+        horizontalAlignment = horizontalAlignment,
+        verticalArrangement = Arrangement.spacedBy(FabMenuItemSpacingVertical)
+    ) {
+        val staggerDelayMillis = if (expanded) StaggerEnterDelayMillis else StaggerExitDelayMillis
+        val staggerProgress =
+            animateFloatAsState(
+                targetValue = if (expanded) 1f else 0f,
+                animationSpec =
+                    tween(durationMillis = itemsCount * staggerDelayMillis, easing = LinearEasing)
+            )
+        val scope =
+            remember(horizontalAlignment) {
+                object : FloatingActionButtonMenuScopeWrapper(this) {
+                    override val itemsCount: Int
+                        get() = itemsCount
+
+                    override val staggerProgress: Float
+                        get() = staggerProgress.value
+
+                    override val horizontalAlignment: Alignment.Horizontal
+                        get() = horizontalAlignment
+                }
+            }
+        content(scope)
+    }
+
+/** Scope for the children of [FloatingActionButtonMenu] */
+@ExperimentalMaterial3ExpressiveApi
+interface FloatingActionButtonMenuScope : ColumnScope {
+    val itemsCount: Int
+
+    val staggerProgress: Float
+
+    val horizontalAlignment: Alignment.Horizontal
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+private abstract class FloatingActionButtonMenuScopeWrapper(scope: ColumnScope) :
+    FloatingActionButtonMenuScope, ColumnScope by scope
+
+// TODO: link to spec and image
+/**
+ * FAB Menu Items should be used within a [FloatingActionButtonMenu] to provide additional choices
+ * to the user after clicking a FAB.
+ *
+ * @sample androidx.compose.material3.samples.FloatingActionButtonMenuSample
+ * @param onClick called when this FAB Menu Item is clicked
+ * @param text label displayed inside this FAB Menu Item
+ * @param icon optional icon for this FAB Menu Item, typically an [Icon]
+ * @param itemIndex the index of this FAB Menu Item within the list of all items
+ * @param modifier the [Modifier] to be applied to this FAB Menu Item
+ * @param containerColor the color used for the background of this FAB Menu Item
+ * @param contentColor the preferred color for content inside this FAB Menu Item. Defaults to either
+ *   the matching content color for [containerColor], or to the current [LocalContentColor] if
+ *   [containerColor] is not a color from the theme.
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun FloatingActionButtonMenuScope.FloatingActionButtonMenuItem(
+    onClick: () -> Unit,
+    text: @Composable () -> Unit,
+    icon: @Composable () -> Unit,
+    itemIndex: Int,
+    modifier: Modifier = Modifier,
+    containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
+    contentColor: Color = contentColorFor(containerColor)
+) {
+    val stagger = 1f / itemsCount
+    val visible by remember {
+        derivedStateOf { (itemsCount - 1 - itemIndex) * stagger < staggerProgress }
+    }
+    AnimatedVisibility(
+        visible = visible,
+        // TODO Load the motionScheme tokens from the component tokens file
+        enter = fadeIn(animationSpec = MotionSchemeKeyTokens.FastEffects.value()),
+        exit = fadeOut(animationSpec = MotionSchemeKeyTokens.FastEffects.value())
+    ) {
+        // Disable min interactive component size because it interferes with the item expand
+        // animation and we know we are meeting the size requirements below.
+        CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
+            Surface(
+                modifier = modifier,
+                shape = CircleShape,
+                color = containerColor,
+                contentColor = contentColor,
+                onClick = onClick
+            ) {
+                Row(
+                    // TODO Load the motionScheme tokens from the component tokens file
+                    Modifier.animateEnterExit(
+                            enter =
+                                expandHorizontally(
+                                    expandFrom = getItemAnimationAlignment(horizontalAlignment),
+                                    animationSpec = MotionSchemeKeyTokens.FastSpatial.value(),
+                                    clip = false
+                                ),
+                            exit =
+                                shrinkHorizontally(
+                                    shrinkTowards = getItemAnimationAlignment(horizontalAlignment),
+                                    animationSpec = MotionSchemeKeyTokens.FastSpatial.value(),
+                                    clip = false
+                                )
+                        )
+                        .sizeIn(minWidth = FabMenuItemMinWidth, minHeight = FabMenuItemHeight)
+                        .padding(horizontal = FabMenuItemContentPaddingHorizontal),
+                    verticalAlignment = Alignment.CenterVertically,
+                    horizontalArrangement =
+                        Arrangement.spacedBy(
+                            FabMenuItemContentSpacingHorizontal,
+                            Alignment.CenterHorizontally
+                        )
+                ) {
+                    icon()
+                    text()
+                }
+            }
+        }
+    }
+}
+
+/* Workaround for issue where the content itself (icon/text) slides in RTL. */
+@Composable
+private fun getItemAnimationAlignment(
+    horizontalAlignment: Alignment.Horizontal
+): Alignment.Horizontal {
+    if (LocalLayoutDirection.current == LayoutDirection.Rtl) {
+        if (horizontalAlignment == Alignment.End) return Alignment.Start
+        if (horizontalAlignment == Alignment.Start) return Alignment.End
+    }
+    return horizontalAlignment
+}
+
+// TODO: link to spec and image
+/**
+ * Toggleable FAB supports animating its container size, corner radius, and color when it is
+ * toggled, and should be used in conjunction with a [FloatingActionButtonMenu] to provide
+ * additional choices to the user after clicking the FAB.
+ *
+ * Use [ToggleableFloatingActionButtonDefaults.animateIcon] to animate the color and size of the
+ * icon while the [ToggleableFloatingActionButton] is being toggled.
+ *
+ * @sample androidx.compose.material3.samples.FloatingActionButtonMenuSample
+ * @param checked whether this Toggleable FAB is checked
+ * @param onCheckedChange callback to be invoked when this Toggleable FAB is clicked, therefore the
+ *   change of the state in requested
+ * @param modifier the [Modifier] to be applied to this Toggleable FAB
+ * @param containerColor the color used for the background of this Toggleable FAB, based on the
+ *   checked progress value from 0-1
+ * @param contentAlignment the alignment of this Toggleable FAB when checked
+ * @param containerSize the size of this Toggleable FAB, based on the checked progress value from
+ *   0-1
+ * @param containerCornerRadius the corner radius of this Toggleable FAB, based on the checked
+ *   progress value from 0-1
+ * @param content the content of this Toggleable FAB, typically an [Icon] that switches from an Add
+ *   to a Close sign at 50% checked progress
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun ToggleableFloatingActionButton(
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    modifier: Modifier = Modifier,
+    containerColor: (Float) -> Color = ToggleableFloatingActionButtonDefaults.containerColor(),
+    contentAlignment: Alignment = Alignment.TopEnd,
+    containerSize: (Float) -> Dp = ToggleableFloatingActionButtonDefaults.containerSize(),
+    containerCornerRadius: (Float) -> Dp =
+        ToggleableFloatingActionButtonDefaults.containerCornerRadius(),
+    content: @Composable ToggleableFloatingActionButtonScope.() -> Unit,
+) {
+    val checkedProgress =
+        animateFloatAsState(
+            targetValue = if (checked) 1f else 0f,
+            // TODO Load the motionScheme tokens from the component tokens file
+            animationSpec = MotionSchemeKeyTokens.FastSpatial.value()
+        )
+    ToggleableFloatingActionButton(
+        checked,
+        onCheckedChange,
+        { checkedProgress.value },
+        modifier,
+        containerColor,
+        contentAlignment,
+        containerSize,
+        containerCornerRadius,
+        content
+    )
+}
+
+// TODO: link to spec and image
+/**
+ * Toggleable FAB supports animating its container size, corner radius, and color when it is
+ * toggled, and should be used in conjunction with a [FloatingActionButtonMenu] to provide
+ * additional choices to the user after clicking the FAB.
+ *
+ * Use [ToggleableFloatingActionButtonDefaults.animateIcon] to animate the color and size of the
+ * icon while the [ToggleableFloatingActionButton] is being toggled.
+ *
+ * This overload of Toggleable FAB also supports a [checkedProgress] param which is used to drive
+ * the toggle animation.
+ *
+ * @sample androidx.compose.material3.samples.FloatingActionButtonMenuSample
+ * @param checked whether this Toggleable FAB is checked
+ * @param onCheckedChange callback to be invoked when this Toggleable FAB is clicked, therefore the
+ *   change of the state in requested
+ * @param checkedProgress callback that provides the progress value for the checked and unchecked
+ *   animations
+ * @param modifier the [Modifier] to be applied to this Toggleable FAB
+ * @param containerColor the color used for the background of this Toggleable FAB, based on the
+ *   checked progress value from 0-1
+ * @param contentAlignment the alignment of this Toggleable FAB when checked
+ * @param containerSize the size of this Toggleable FAB, based on the checked progress value from
+ *   0-1
+ * @param containerCornerRadius the corner radius of this Toggleable FAB, based on the checked
+ *   progress value from 0-1
+ * @param content the content of this Toggleable FAB, typically an [Icon] that switches from an Add
+ *   to a Close sign at 50% checked progress
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+private fun ToggleableFloatingActionButton(
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    checkedProgress: () -> Float,
+    modifier: Modifier = Modifier,
+    containerColor: (Float) -> Color = ToggleableFloatingActionButtonDefaults.containerColor(),
+    contentAlignment: Alignment = Alignment.TopEnd,
+    containerSize: (Float) -> Dp = ToggleableFloatingActionButtonDefaults.containerSize(),
+    containerCornerRadius: (Float) -> Dp =
+        ToggleableFloatingActionButtonDefaults.containerCornerRadius(),
+    content: @Composable ToggleableFloatingActionButtonScope.() -> Unit,
+) {
+    val initialSize = remember(containerSize) { containerSize(0f) }
+    Box(Modifier.size(initialSize), contentAlignment = contentAlignment) {
+        val density = LocalDensity.current
+        val fabRippleRadius =
+            remember(initialSize) {
+                with(density) {
+                    val fabSizeHalf = initialSize.toPx() / 2
+                    hypot(fabSizeHalf, fabSizeHalf).toDp()
+                }
+            }
+        val shape =
+            remember(density, checkedProgress, containerCornerRadius) {
+                GenericShape { size, _ ->
+                    val radius = with(density) { containerCornerRadius(checkedProgress()).toPx() }
+                    addRoundRect(RoundRect(size.toRect(), CornerRadius(radius)))
+                }
+            }
+        Box(
+            modifier
+                .graphicsLayer {
+                    this.shadowElevation = FabShadowElevation.toPx()
+                    this.shape = shape
+                    this.clip = true
+                }
+                .drawBehind {
+                    val radius = with(density) { containerCornerRadius(checkedProgress()).toPx() }
+                    drawRoundRect(
+                        color = containerColor(checkedProgress()),
+                        cornerRadius = CornerRadius(radius)
+                    )
+                }
+                .toggleable(
+                    value = checked,
+                    onValueChange = onCheckedChange,
+                    interactionSource = null,
+                    indication = ripple(radius = fabRippleRadius)
+                )
+                .layout { measurable, constraints ->
+                    val placeable = measurable.measure(constraints)
+                    val sizePx = containerSize(checkedProgress()).roundToPx()
+                    layout(sizePx, sizePx) {
+                        placeable.place(
+                            (sizePx - placeable.width) / 2,
+                            (sizePx - placeable.height) / 2
+                        )
+                    }
+                }
+        ) {
+            val scope =
+                remember(checkedProgress) {
+                    object : ToggleableFloatingActionButtonScope {
+                        override val checkedProgress: Float
+                            get() = checkedProgress()
+                    }
+                }
+            content(scope)
+        }
+    }
+}
+
+/** Contains the default values used by [ToggleableFloatingActionButton] */
+@ExperimentalMaterial3ExpressiveApi
+object ToggleableFloatingActionButtonDefaults {
+
+    @Composable
+    fun containerColor(
+        initialColor: Color = MaterialTheme.colorScheme.primaryContainer,
+        finalColor: Color = MaterialTheme.colorScheme.primary
+    ): (Float) -> Color = { progress -> lerp(initialColor, finalColor, progress) }
+
+    fun containerSize(initialSize: Dp, finalSize: Dp = FabFinalSize): (Float) -> Dp = { progress ->
+        lerp(initialSize, finalSize, progress)
+    }
+
+    fun containerSize() = containerSize(FabInitialSize)
+
+    fun containerSizeMedium() = containerSize(FabMediumInitialSize)
+
+    fun containerSizeLarge() = containerSize(FabLargeInitialSize)
+
+    fun containerCornerRadius(
+        initialSize: Dp,
+        finalSize: Dp = FabFinalCornerRadius
+    ): (Float) -> Dp = { progress -> lerp(initialSize, finalSize, progress) }
+
+    fun containerCornerRadius() = containerCornerRadius(FabInitialCornerRadius)
+
+    fun containerCornerRadiusMedium() = containerCornerRadius(FabMediumInitialCornerRadius)
+
+    fun containerCornerRadiusLarge() = containerCornerRadius(FabLargeInitialCornerRadius)
+
+    @Composable
+    fun iconColor(
+        initialColor: Color = MaterialTheme.colorScheme.onPrimaryContainer,
+        finalColor: Color = MaterialTheme.colorScheme.onPrimary
+    ): (Float) -> Color = { progress -> lerp(initialColor, finalColor, progress) }
+
+    fun iconSize(initialSize: Dp, finalSize: Dp = FabFinalIconSize): (Float) -> Dp = { progress ->
+        lerp(initialSize, finalSize, progress)
+    }
+
+    fun iconSize() = iconSize(FabInitialIconSize)
+
+    fun iconSizeMedium() = iconSize(FabMediumInitialIconSize)
+
+    fun iconSizeLarge() = iconSize(FabLargeInitialIconSize)
+
+    /**
+     * Modifier for animating the color and size of an icon within [ToggleableFloatingActionButton]
+     * based on a progress value.
+     *
+     * @param checkedProgress callback that provides the progress value for the icon animation
+     * @param color the color of the icon, based on the checked progress value from 0-1
+     * @param size the size of the icon, based on the checked progress value from 0-1
+     */
+    @Composable
+    fun Modifier.animateIcon(
+        checkedProgress: () -> Float,
+        color: (Float) -> Color = iconColor(),
+        size: (Float) -> Dp = iconSize(),
+    ) =
+        this.layout { measurable, _ ->
+                val sizePx = size(checkedProgress()).roundToPx()
+                val placeable = measurable.measure(Constraints.fixed(sizePx, sizePx))
+                layout(sizePx, sizePx) { placeable.place(0, 0) }
+            }
+            .drawWithCache {
+                val layer = obtainGraphicsLayer()
+                layer.apply {
+                    record { drawContent() }
+                    this.colorFilter = ColorFilter.tint(color(checkedProgress()))
+                }
+
+                onDrawWithContent { drawLayer(graphicsLayer = layer) }
+            }
+}
+
+/** Scope for the children of [ToggleableFloatingActionButton] */
+@ExperimentalMaterial3ExpressiveApi
+interface ToggleableFloatingActionButtonScope {
+
+    val checkedProgress: Float
+}
+
+private val FabInitialSize = 56.dp
+private val FabInitialCornerRadius = 16.dp
+private val FabInitialIconSize = 24.dp
+private val FabMediumInitialSize = 80.dp
+private val FabMediumInitialCornerRadius = 24.dp
+private val FabMediumInitialIconSize = 28.dp
+private val FabLargeInitialSize = 96.dp
+private val FabLargeInitialCornerRadius = 28.dp
+private val FabLargeInitialIconSize = 36.dp
+private val FabFinalSize = 56.dp
+private val FabFinalCornerRadius = FabFinalSize.div(2)
+private val FabFinalIconSize = 20.dp
+private val FabShadowElevation = 6.dp
+private val FabMenuPaddingBottom = 8.dp
+private val FabMenuItemMinWidth = 56.dp
+private val FabMenuItemHeight = 56.dp
+private val FabMenuItemSpacingVertical = 4.dp
+private val FabMenuItemContentPaddingHorizontal = 16.dp
+private val FabMenuItemContentSpacingHorizontal = 8.dp
+private const val StaggerEnterDelayMillis = 35
+private const val StaggerExitDelayMillis = 25
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingAppBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingAppBar.kt
index a7260f7..1f15609 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingAppBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingAppBar.kt
@@ -20,10 +20,8 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationState
 import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.animateDecay
 import androidx.compose.animation.core.animateTo
-import androidx.compose.animation.core.spring
 import androidx.compose.animation.expandHorizontally
 import androidx.compose.animation.expandVertically
 import androidx.compose.animation.rememberSplineBasedDecay
@@ -52,6 +50,7 @@
 import androidx.compose.material3.FloatingAppBarPosition.Companion.Top
 import androidx.compose.material3.tokens.ColorSchemeKeyTokens
 import androidx.compose.material3.tokens.ElevationTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.ShapeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
@@ -80,18 +79,18 @@
 
 /**
  * @sample androidx.compose.material3.samples.HorizontalFloatingAppBar
- * @param expanded whether the FloatingAppBar is in expanded mode, i.e. showing [trailingContent]
- *   and [leadingContent].
+ * @param expanded whether the FloatingAppBar is in expanded mode, i.e. showing [leadingContent] and
+ *   [trailingContent].
  * @param modifier the [Modifier] to be applied to this FloatingAppBar.
  * @param containerColor the color used for the background of this FloatingAppBar. Use
  *   [Color.Transparent] to have no color.
  * @param contentPadding the padding applied to the content of this FloatingAppBar.
  * @param scrollBehavior a [FloatingAppBarScrollBehavior].
  * @param shape the shape used for this FloatingAppBar.
- * @param trailingContent the trailing content of this FloatingAppBar. The default layout here is a
- *   [Row], so content inside will be placed horizontally. Only showing if [expanded] is true.
  * @param leadingContent the leading content of this FloatingAppBar. The default layout here is a
  *   [Row], so content inside will be placed horizontally. Only showing if [expanded] is true.
+ * @param trailingContent the trailing content of this FloatingAppBar. The default layout here is a
+ *   [Row], so content inside will be placed horizontally. Only showing if [expanded] is true.
  * @param content the main content of this FloatingAppBar. The default layout here is a [Row], so
  *   content inside will be placed horizontally.
  */
@@ -104,8 +103,8 @@
     contentPadding: PaddingValues = FloatingAppBarDefaults.ContentPadding,
     scrollBehavior: FloatingAppBarScrollBehavior? = null,
     shape: Shape = FloatingAppBarDefaults.ContainerShape,
-    trailingContent: @Composable (RowScope.() -> Unit)? = null,
     leadingContent: @Composable (RowScope.() -> Unit)? = null,
+    trailingContent: @Composable (RowScope.() -> Unit)? = null,
     content: @Composable RowScope.() -> Unit
 ) {
     val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
@@ -122,7 +121,7 @@
         horizontalArrangement = Arrangement.Center,
         verticalAlignment = Alignment.CenterVertically
     ) {
-        trailingContent?.let {
+        leadingContent?.let {
             val alignment = if (isRtl) Alignment.Start else Alignment.End
             AnimatedVisibility(
                 visible = expanded,
@@ -133,7 +132,7 @@
             }
         }
         content()
-        leadingContent?.let {
+        trailingContent?.let {
             val alignment = if (isRtl) Alignment.End else Alignment.Start
             AnimatedVisibility(
                 visible = expanded,
@@ -148,18 +147,18 @@
 
 /**
  * @sample androidx.compose.material3.samples.VerticalFloatingAppBar
- * @param expanded whether the FloatingAppBar is in expanded mode, i.e. showing [trailingContent]
- *   and [leadingContent].
+ * @param expanded whether the FloatingAppBar is in expanded mode, i.e. showing [leadingContent] and
+ *   [trailingContent].
  * @param modifier the [Modifier] to be applied to this FloatingAppBar.
  * @param containerColor the color used for the background of this FloatingAppBar. Use
  *   Color.Transparent] to have no color.
  * @param contentPadding the padding applied to the content of this FloatingAppBar.
  * @param scrollBehavior a [FloatingAppBarScrollBehavior].
  * @param shape the shape used for this FloatingAppBar.
- * @param trailingContent the trailing content of this FloatingAppBar. The default layout here is a
- *   [Column], so content inside will be placed vertically. Only showing if [expanded] is true.
  * @param leadingContent the leading content of this FloatingAppBar. The default layout here is a
  *   [Column], so content inside will be placed vertically. Only showing if [expanded] is true.
+ * @param trailingContent the trailing content of this FloatingAppBar. The default layout here is a
+ *   [Column], so content inside will be placed vertically. Only showing if [expanded] is true.
  * @param content the main content of this FloatingAppBar. The default layout here is a [Column], so
  *   content inside will be placed vertically.
  */
@@ -172,8 +171,8 @@
     contentPadding: PaddingValues = FloatingAppBarDefaults.ContentPadding,
     scrollBehavior: FloatingAppBarScrollBehavior? = null,
     shape: Shape = FloatingAppBarDefaults.ContainerShape,
-    trailingContent: @Composable (ColumnScope.() -> Unit)? = null,
     leadingContent: @Composable (ColumnScope.() -> Unit)? = null,
+    trailingContent: @Composable (ColumnScope.() -> Unit)? = null,
     content: @Composable ColumnScope.() -> Unit
 ) {
     Column(
@@ -189,7 +188,7 @@
         verticalArrangement = Arrangement.Center,
         horizontalAlignment = Alignment.CenterHorizontally
     ) {
-        trailingContent?.let {
+        leadingContent?.let {
             AnimatedVisibility(
                 visible = expanded,
                 enter = verticalEnterTransition(expandFrom = Alignment.Bottom),
@@ -199,7 +198,7 @@
             }
         }
         content()
-        leadingContent?.let {
+        trailingContent?.let {
             AnimatedVisibility(
                 visible = expanded,
                 enter = verticalEnterTransition(expandFrom = Alignment.Top),
@@ -380,6 +379,9 @@
      */
     val ScreenOffset = 16.dp
 
+    // TODO: note that this scroll behavior may impact assistive technologies making the component
+    //  inaccessible. See @sample androidx.compose.material3.samples.HorizontalFloatingAppBar on how
+    //  to disable scrolling when touch exploration is enabled.
     /**
      * Returns a [FloatingAppBarScrollBehavior]. A floating app bar that is set up with this
      * [FloatingAppBarScrollBehavior] will immediately collapse when the content is pulled up, and
@@ -402,7 +404,7 @@
         position: FloatingAppBarPosition,
         screenOffset: Dp = ScreenOffset,
         state: FloatingAppBarState = rememberFloatingAppBarState(),
-        snapAnimationSpec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
+        snapAnimationSpec: AnimationSpec<Float> = MotionSchemeKeyTokens.DefaultEffects.value(),
         flingAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay()
     ): FloatingAppBarScrollBehavior =
         remember(position, screenOffset, state, snapAnimationSpec, flingAnimationSpec) {
@@ -416,46 +418,34 @@
         }
 
     /** Default enter transition used for [HorizontalFloatingAppBar] when expanding */
+    @Composable
     fun horizontalEnterTransition(expandFrom: Alignment.Horizontal) =
         expandHorizontally(
-            animationSpec =
-                spring(
-                    dampingRatio = .8f,
-                    stiffness = 380f,
-                ),
+            animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value(),
             expandFrom = expandFrom,
         )
 
     /** Default enter transition used for [VerticalFloatingAppBar] when expanding */
+    @Composable
     fun verticalEnterTransition(expandFrom: Alignment.Vertical) =
         expandVertically(
-            animationSpec =
-                spring(
-                    dampingRatio = .8f,
-                    stiffness = 380f,
-                ),
+            animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value(),
             expandFrom = expandFrom,
         )
 
     /** Default exit transition used for [HorizontalFloatingAppBar] when shrinking */
+    @Composable
     fun horizontalExitTransition(shrinkTowards: Alignment.Horizontal) =
         shrinkHorizontally(
-            animationSpec =
-                spring(
-                    dampingRatio = .8f,
-                    stiffness = 380f,
-                ),
+            animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value(),
             shrinkTowards = shrinkTowards,
         )
 
     /** Default exit transition used for [VerticalFloatingAppBar] when shrinking */
+    @Composable
     fun verticalExitTransition(shrinkTowards: Alignment.Vertical) =
         shrinkVertically(
-            animationSpec =
-                spring(
-                    dampingRatio = .8f,
-                    stiffness = 380f,
-                ),
+            animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value(),
             shrinkTowards = shrinkTowards,
         )
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/InteractiveComponentSize.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/InteractiveComponentSize.kt
index 6917960..db9690e90 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/InteractiveComponentSize.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/InteractiveComponentSize.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.currentValueOf
 import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.coerceAtLeast
@@ -39,15 +40,22 @@
  * Reserves at least 48.dp in size to disambiguate touch interactions if the element would measure
  * smaller.
  *
- * https://m3.material.io/foundations/accessible-design/accessibility-basics#28032e45-c598-450c-b355-f9fe737b1cd8
+ * https://m3.material.io/foundations/designing/structure#dab862b1-e042-4c40-b680-b484b9f077f6
  *
  * This uses the Material recommended minimum size of 48.dp x 48.dp, which may not the same as the
  * system enforced minimum size. The minimum clickable / touch target size (48.dp by default) is
- * controlled by the system via ViewConfiguration` and automatically expanded at the touch input
+ * controlled by the system via [ViewConfiguration] and automatically expanded at the touch input
  * layer.
  *
  * This modifier is not needed for touch target expansion to happen. It only affects layout, to make
  * sure there is adequate space for touch target expansion.
+ *
+ * Because layout constraints are affected by modifier order, for this modifier to take effect, it
+ * must come before any size modifiers on the element that might limit its constraints.
+ *
+ * @sample androidx.compose.material3.samples.MinimumInteractiveComponentSizeSample
+ * @sample androidx.compose.material3.samples.MinimumInteractiveComponentSizeCheckboxRowSample
+ * @see LocalMinimumInteractiveComponentSize
  */
 @Stable
 fun Modifier.minimumInteractiveComponentSize(): Modifier = this then MinimumInteractiveModifier
@@ -67,11 +75,13 @@
                 "interactions if the element would measure smaller"
     }
 
-    override fun hashCode(): Int = System.identityHashCode(this)
+    override fun hashCode(): Int = identityHashCode(this)
 
     override fun equals(other: Any?) = (other === this)
 }
 
+internal expect inline fun identityHashCode(value: Any): Int
+
 internal class MinimumInteractiveModifierNode :
     Modifier.Node(), CompositionLocalConsumerModifierNode, LayoutModifierNode {
     override fun MeasureScope.measure(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
index a5f8ac9..717f48b 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.internal.ProvideContentColorTextStyle
 import androidx.compose.material3.internal.heightOrZero
+import androidx.compose.material3.internal.subtractConstraintSafely
 import androidx.compose.material3.internal.widthOrZero
 import androidx.compose.material3.tokens.ListTokens
 import androidx.compose.material3.tokens.TypographyKeyTokens
@@ -34,6 +35,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
@@ -51,6 +53,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.offset
 import androidx.compose.ui.unit.sp
+import kotlin.jvm.JvmInline
 import kotlin.math.max
 
 /**
@@ -248,13 +251,13 @@
             )
 
         val leadingPlaceable = leadingMeasurable.firstOrNull()?.measure(paddedLooseConstraints)
-        currentTotalWidth += widthOrZero(leadingPlaceable)
+        currentTotalWidth += leadingPlaceable.widthOrZero
 
         val trailingPlaceable =
             trailingMeasurable
                 .firstOrNull()
                 ?.measure(paddedLooseConstraints.offset(horizontal = -currentTotalWidth))
-        currentTotalWidth += widthOrZero(trailingPlaceable)
+        currentTotalWidth += trailingPlaceable.widthOrZero
 
         var currentTotalHeight = 0
 
@@ -262,7 +265,7 @@
             headlineMeasurable
                 .firstOrNull()
                 ?.measure(paddedLooseConstraints.offset(horizontal = -currentTotalWidth))
-        currentTotalHeight += heightOrZero(headlinePlaceable)
+        currentTotalHeight += headlinePlaceable.heightOrZero
 
         val supportingPlaceable =
             supportingMeasurable
@@ -273,7 +276,7 @@
                         vertical = -currentTotalHeight
                     )
                 )
-        currentTotalHeight += heightOrZero(supportingPlaceable)
+        currentTotalHeight += supportingPlaceable.heightOrZero
         val isSupportingMultiline =
             supportingPlaceable != null &&
                 (supportingPlaceable[FirstBaseline] != supportingPlaceable[LastBaseline])
@@ -299,21 +302,21 @@
 
         val width =
             calculateWidth(
-                leadingWidth = widthOrZero(leadingPlaceable),
-                trailingWidth = widthOrZero(trailingPlaceable),
-                headlineWidth = widthOrZero(headlinePlaceable),
-                overlineWidth = widthOrZero(overlinePlaceable),
-                supportingWidth = widthOrZero(supportingPlaceable),
+                leadingWidth = leadingPlaceable.widthOrZero,
+                trailingWidth = trailingPlaceable.widthOrZero,
+                headlineWidth = headlinePlaceable.widthOrZero,
+                overlineWidth = overlinePlaceable.widthOrZero,
+                supportingWidth = supportingPlaceable.widthOrZero,
                 horizontalPadding = horizontalPadding,
                 constraints = constraints,
             )
         val height =
             calculateHeight(
-                leadingHeight = heightOrZero(leadingPlaceable),
-                trailingHeight = heightOrZero(trailingPlaceable),
-                headlineHeight = heightOrZero(headlinePlaceable),
-                overlineHeight = heightOrZero(overlinePlaceable),
-                supportingHeight = heightOrZero(supportingPlaceable),
+                leadingHeight = leadingPlaceable.heightOrZero,
+                trailingHeight = trailingPlaceable.heightOrZero,
+                headlineHeight = headlinePlaceable.heightOrZero,
+                overlineHeight = overlinePlaceable.heightOrZero,
+                supportingHeight = supportingPlaceable.heightOrZero,
                 listItemType = listItemType,
                 verticalPadding = verticalPadding.roundToPx(),
                 constraints = constraints,
@@ -502,24 +505,24 @@
             )
         }
 
-        val mainContentX = startPadding + widthOrZero(leadingPlaceable)
+        val mainContentX = startPadding + leadingPlaceable.widthOrZero
         val mainContentY =
             if (isThreeLine) {
                 topPadding
             } else {
                 val totalHeight =
-                    heightOrZero(headlinePlaceable) +
-                        heightOrZero(overlinePlaceable) +
-                        heightOrZero(supportingPlaceable)
+                    headlinePlaceable.heightOrZero +
+                        overlinePlaceable.heightOrZero +
+                        supportingPlaceable.heightOrZero
                 CenterVertically.align(totalHeight, height)
             }
         var currentY = mainContentY
 
         overlinePlaceable?.placeRelative(mainContentX, currentY)
-        currentY += heightOrZero(overlinePlaceable)
+        currentY += overlinePlaceable.heightOrZero
 
         headlinePlaceable?.placeRelative(mainContentX, currentY)
-        currentY += heightOrZero(headlinePlaceable)
+        currentY += headlinePlaceable.heightOrZero
 
         supportingPlaceable?.placeRelative(mainContentX, currentY)
     }
@@ -545,6 +548,12 @@
     /**
      * Creates a [ListItemColors] that represents the default container and content colors used in a
      * [ListItem].
+     */
+    @Composable fun colors() = MaterialTheme.colorScheme.defaultListItemColors
+
+    /**
+     * Creates a [ListItemColors] that represents the default container and content colors used in a
+     * [ListItem].
      *
      * @param containerColor the container color of this list item when enabled.
      * @param headlineColor the headline text content color of this list item when enabled.
@@ -560,26 +569,17 @@
      */
     @Composable
     fun colors(
-        containerColor: Color = ListTokens.ListItemContainerColor.value,
-        headlineColor: Color = ListTokens.ListItemLabelTextColor.value,
-        leadingIconColor: Color = ListTokens.ListItemLeadingIconColor.value,
-        overlineColor: Color = ListTokens.ListItemOverlineColor.value,
-        supportingColor: Color = ListTokens.ListItemSupportingTextColor.value,
-        trailingIconColor: Color = ListTokens.ListItemTrailingIconColor.value,
-        disabledHeadlineColor: Color =
-            ListTokens.ListItemDisabledLabelTextColor.value.copy(
-                alpha = ListTokens.ListItemDisabledLabelTextOpacity
-            ),
-        disabledLeadingIconColor: Color =
-            ListTokens.ListItemDisabledLeadingIconColor.value.copy(
-                alpha = ListTokens.ListItemDisabledLeadingIconOpacity
-            ),
-        disabledTrailingIconColor: Color =
-            ListTokens.ListItemDisabledTrailingIconColor.value.copy(
-                alpha = ListTokens.ListItemDisabledTrailingIconOpacity
-            )
+        containerColor: Color = Color.Unspecified,
+        headlineColor: Color = Color.Unspecified,
+        leadingIconColor: Color = Color.Unspecified,
+        overlineColor: Color = Color.Unspecified,
+        supportingColor: Color = Color.Unspecified,
+        trailingIconColor: Color = Color.Unspecified,
+        disabledHeadlineColor: Color = Color.Unspecified,
+        disabledLeadingIconColor: Color = Color.Unspecified,
+        disabledTrailingIconColor: Color = Color.Unspecified,
     ): ListItemColors =
-        ListItemColors(
+        MaterialTheme.colorScheme.defaultListItemColors.copy(
             containerColor = containerColor,
             headlineColor = headlineColor,
             leadingIconColor = leadingIconColor,
@@ -590,6 +590,29 @@
             disabledLeadingIconColor = disabledLeadingIconColor,
             disabledTrailingIconColor = disabledTrailingIconColor,
         )
+
+    internal val ColorScheme.defaultListItemColors: ListItemColors
+        get() {
+            return defaultListItemColorsCached
+                ?: ListItemColors(
+                        containerColor = fromToken(ListTokens.ListItemContainerColor),
+                        headlineColor = fromToken(ListTokens.ListItemLabelTextColor),
+                        leadingIconColor = fromToken(ListTokens.ListItemLeadingIconColor),
+                        overlineColor = fromToken(ListTokens.ListItemOverlineColor),
+                        supportingTextColor = fromToken(ListTokens.ListItemSupportingTextColor),
+                        trailingIconColor = fromToken(ListTokens.ListItemTrailingIconColor),
+                        disabledHeadlineColor =
+                            fromToken(ListTokens.ListItemDisabledLabelTextColor)
+                                .copy(alpha = ListTokens.ListItemDisabledLabelTextOpacity),
+                        disabledLeadingIconColor =
+                            fromToken(ListTokens.ListItemDisabledLeadingIconColor)
+                                .copy(alpha = ListTokens.ListItemDisabledLeadingIconOpacity),
+                        disabledTrailingIconColor =
+                            fromToken(ListTokens.ListItemDisabledTrailingIconColor)
+                                .copy(alpha = ListTokens.ListItemDisabledTrailingIconOpacity),
+                    )
+                    .also { defaultListItemColorsCached = it }
+        }
 }
 
 /**
@@ -620,6 +643,35 @@
     val disabledLeadingIconColor: Color,
     val disabledTrailingIconColor: Color,
 ) {
+    /**
+     * Returns a copy of this ListItemColors, optionally overriding some of the values. This uses
+     * the Color.Unspecified to mean “use the value from the source”
+     */
+    fun copy(
+        containerColor: Color = this.containerColor,
+        headlineColor: Color = this.headlineColor,
+        leadingIconColor: Color = this.leadingIconColor,
+        overlineColor: Color = this.overlineColor,
+        supportingTextColor: Color = this.supportingTextColor,
+        trailingIconColor: Color = this.trailingIconColor,
+        disabledHeadlineColor: Color = this.disabledHeadlineColor,
+        disabledLeadingIconColor: Color = this.disabledLeadingIconColor,
+        disabledTrailingIconColor: Color = this.disabledTrailingIconColor,
+    ) =
+        ListItemColors(
+            containerColor = containerColor.takeOrElse { this.containerColor },
+            headlineColor = headlineColor.takeOrElse { this.headlineColor },
+            leadingIconColor = leadingIconColor.takeOrElse { this.leadingIconColor },
+            overlineColor = overlineColor.takeOrElse { this.overlineColor },
+            supportingTextColor = supportingTextColor.takeOrElse { this.supportingTextColor },
+            trailingIconColor = trailingIconColor.takeOrElse { this.trailingIconColor },
+            disabledHeadlineColor = disabledHeadlineColor.takeOrElse { this.disabledHeadlineColor },
+            disabledLeadingIconColor =
+                disabledLeadingIconColor.takeOrElse { this.disabledLeadingIconColor },
+            disabledTrailingIconColor =
+                disabledTrailingIconColor.takeOrElse { this.disabledTrailingIconColor },
+        )
+
     /** The container color of this [ListItem] based on enabled state */
     internal fun containerColor(): Color {
         return containerColor
@@ -719,10 +771,3 @@
         ListItemType.ThreeLine -> ListItemThreeLineVerticalPadding
         else -> ListItemVerticalPadding
     }
-
-private fun Int.subtractConstraintSafely(n: Int): Int {
-    if (this == Constraints.Infinity) {
-        return this
-    }
-    return this - n
-}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/LoadingIndicator.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/LoadingIndicator.kt
index 9138238..139d2e3 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/LoadingIndicator.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/LoadingIndicator.kt
@@ -68,39 +68,178 @@
 /**
  * A Material Design loading indicator.
  *
- * This version of the loading indicator morphs between its [indicatorPolygons] shapes by the value
- * of its [progress].
+ * This version of the loading indicator morphs between its [polygons] shapes by the value of its
+ * [progress].
  *
  * It can be created like this:
  *
  * @sample androidx.compose.material3.samples.DeterminateLoadingIndicatorSample
+ * @param progress the progress of this loading indicator, where 0.0 represents no progress and 1.0
+ *   represents full progress. Values outside of this range are coerced into the range. The
+ *   indicator will morph its shapes between the provided [polygons] according to the value of the
+ *   progress.
+ * @param modifier the [Modifier] to be applied to this loading indicator
+ * @param color the loading indicator's color
+ * @param polygons a list of [RoundedPolygon]s for the sequence of shapes this loading indicator
+ *   will morph between as it progresses from 0.0 to 1.0. The loading indicator expects at least two
+ *   items in that list.
+ * @throws IllegalArgumentException if the [polygons] list holds less than two items
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun LoadingIndicator(
+    progress: () -> Float,
+    modifier: Modifier = Modifier,
+    color: Color = LoadingIndicatorDefaults.IndicatorColor,
+    polygons: List<RoundedPolygon> = LoadingIndicatorDefaults.DeterminateIndicatorPolygons
+) =
+    LoadingIndicatorImpl(
+        progress = progress,
+        modifier = modifier,
+        containerColor = Color.Unspecified,
+        indicatorColor = color,
+        containerShape = LoadingIndicatorDefaults.ContainerShape,
+        indicatorPolygons = polygons,
+    )
+
+// TODO Update the docs images to point to the loading indicator.
+/**
+ * A Material Design loading indicator.
+ *
+ * This version of the loading indicator animates and morphs between various shapes as long as the
+ * loading indicator is visible.
+ *
+ * It can be created like this:
+ *
+ * @sample androidx.compose.material3.samples.LoadingIndicatorSample
+ * @param modifier the [Modifier] to be applied to this loading indicator
+ * @param color the loading indicator's color
+ * @param polygons a list of [RoundedPolygon]s for the sequence of shapes this loading indicator
+ *   will morph between. The loading indicator expects at least two items in that list.
+ * @throws IllegalArgumentException if the [polygons] list holds less than two items
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun LoadingIndicator(
+    modifier: Modifier = Modifier,
+    color: Color = LoadingIndicatorDefaults.IndicatorColor,
+    polygons: List<RoundedPolygon> = LoadingIndicatorDefaults.IndeterminateIndicatorPolygons,
+) =
+    LoadingIndicatorImpl(
+        modifier = modifier,
+        containerColor = Color.Unspecified,
+        indicatorColor = color,
+        containerShape = LoadingIndicatorDefaults.ContainerShape,
+        indicatorPolygons = polygons,
+    )
+
+// TODO Update the docs images to point to the loading indicator
+/**
+ * A Material Design contained loading indicator.
+ *
+ * This version of the loading indicator morphs between its [polygons] shapes by the value of its
+ * [progress]. The shapes in this variation are contained within a colored [containerShape].
+ *
+ * It can be created like this:
+ *
+ * @sample androidx.compose.material3.samples.DeterminateContainedLoadingIndicatorSample
  *
  * It can also be used as an indicator for a [PullToRefreshBox] like this:
  *
  * @sample androidx.compose.material3.samples.LoadingIndicatorPullToRefreshSample
  * @param progress the progress of this loading indicator, where 0.0 represents no progress and 1.0
  *   represents full progress. Values outside of this range are coerced into the range. The
+ *   indicator will morph its shapes between the provided [polygons] according to the value of the
+ *   progress.
+ * @param modifier the [Modifier] to be applied to this loading indicator
+ * @param containerColor the loading indicator's container color
+ * @param indicatorColor the loading indicator's color
+ * @param containerShape the loading indicator's container shape
+ * @param polygons a list of [RoundedPolygon]s for the sequence of shapes this loading indicator
+ *   will morph between as it progresses from 0.0 to 1.0. The loading indicator expects at least two
+ *   items in that list.
+ * @throws IllegalArgumentException if the [polygons] list holds less than two items
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun ContainedLoadingIndicator(
+    progress: () -> Float,
+    modifier: Modifier = Modifier,
+    containerColor: Color = LoadingIndicatorDefaults.ContainedContainerColor,
+    indicatorColor: Color = LoadingIndicatorDefaults.ContainedIndicatorColor,
+    containerShape: Shape = LoadingIndicatorDefaults.ContainerShape,
+    polygons: List<RoundedPolygon> = LoadingIndicatorDefaults.DeterminateIndicatorPolygons
+) =
+    LoadingIndicatorImpl(
+        progress = progress,
+        modifier = modifier,
+        containerColor = containerColor,
+        indicatorColor = indicatorColor,
+        containerShape = containerShape,
+        indicatorPolygons = polygons,
+    )
+
+// TODO Update the docs images to point to the loading indicator.
+/**
+ * A Material Design contained loading indicator.
+ *
+ * This version of the loading indicator animates and morphs between various shapes as long as the
+ * loading indicator is visible. The shapes in this variation are contained within a colored
+ * [containerShape].
+ *
+ * It can be created like this:
+ *
+ * @sample androidx.compose.material3.samples.ContainedLoadingIndicatorSample
+ * @param modifier the [Modifier] to be applied to this loading indicator
+ * @param containerColor the loading indicator's container color
+ * @param indicatorColor the loading indicator's color
+ * @param containerShape the loading indicator's container shape
+ * @param polygons a list of [RoundedPolygon]s for the sequence of shapes this loading indicator
+ *   will morph between. The loading indicator expects at least two items in that list.
+ * @throws IllegalArgumentException if the [polygons] list holds less than two items
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun ContainedLoadingIndicator(
+    modifier: Modifier = Modifier,
+    containerColor: Color = LoadingIndicatorDefaults.ContainedContainerColor,
+    indicatorColor: Color = LoadingIndicatorDefaults.ContainedIndicatorColor,
+    containerShape: Shape = LoadingIndicatorDefaults.ContainerShape,
+    polygons: List<RoundedPolygon> = LoadingIndicatorDefaults.IndeterminateIndicatorPolygons,
+) =
+    LoadingIndicatorImpl(
+        modifier = modifier,
+        containerColor = containerColor,
+        indicatorColor = indicatorColor,
+        containerShape = containerShape,
+        indicatorPolygons = polygons,
+    )
+
+/**
+ * A determinate loading indicator implementation.
+ *
+ * @param progress the progress of this loading indicator, where 0.0 represents no progress and 1.0
+ *   represents full progress. Values outside of this range are coerced into the range. The
  *   indicator will morph its shapes between the provided [indicatorPolygons] according to the value
  *   of the progress.
  * @param modifier the [Modifier] to be applied to this loading indicator
  * @param containerColor the loading indicator's container color
  * @param indicatorColor the loading indicator's color
- * @param containerShape the loading indicator's container shape. Note that the indicator will be
- *   clipped to this shape only when the [containerColor] is defined and non-transparent.
- * @param indicatorPolygons a list of [RoundedPolygon]s that defines the sequence of shapes this
- *   loading indicator will morph between as it progresses from 0.0 to 1.0. The loading indicator
- *   expects at least two items in that list.
+ * @param containerShape the loading indicator's container shape
+ * @param indicatorPolygons a list of [RoundedPolygon]s for the sequence of shapes this loading
+ *   indicator will morph between as it progresses from 0.0 to 1.0. The loading indicator expects at
+ *   least two items in that list.
  * @throws IllegalArgumentException if the [indicatorPolygons] list holds less than two items
  */
-@ExperimentalMaterial3ExpressiveApi
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
-fun LoadingIndicator(
+private fun LoadingIndicatorImpl(
     progress: () -> Float,
-    modifier: Modifier = Modifier,
-    containerColor: Color = LoadingIndicatorDefaults.ContainerColor,
-    indicatorColor: Color = LoadingIndicatorDefaults.IndicatorColor,
-    containerShape: Shape = LoadingIndicatorDefaults.ContainerShape,
-    indicatorPolygons: List<RoundedPolygon> = LoadingIndicatorDefaults.DeterminateIndicatorPolygons
+    modifier: Modifier,
+    containerColor: Color,
+    indicatorColor: Color,
+    containerShape: Shape,
+    indicatorPolygons: List<RoundedPolygon>
 ) {
     require(indicatorPolygons.size > 1) {
         "indicatorPolygons should have, at least, two RoundedPolygons"
@@ -184,35 +323,25 @@
     }
 }
 
-// TODO Update the docs images to point to the loading indicator.
 /**
- * A Material Design loading indicator.
+ * An indeterminate loading indicator implementation.
  *
- * This version of the loading indicator animates and morphs between various shapes as long as the
- * loading indicator is visible.
- *
- * It can be created like this:
- *
- * @sample androidx.compose.material3.samples.LoadingIndicatorSample
  * @param modifier the [Modifier] to be applied to this loading indicator
  * @param containerColor the loading indicator's container color
  * @param indicatorColor the loading indicator's color
- * @param containerShape the loading indicator's container shape. Note that the indicator will be
- *   clipped to this shape only when the [containerColor] is defined and non-transparent.
- * @param indicatorPolygons a list of [RoundedPolygon]s that defines the sequence of shapes this
- *   loading indicator will morph between. The loading indicator expects at least two items in that
- *   list.
+ * @param containerShape the loading indicator's container shape
+ * @param indicatorPolygons a list of [RoundedPolygon]s for the sequence of shapes this loading
+ *   indicator will morph between. The loading indicator expects at least two items in that list.
  * @throws IllegalArgumentException if the [indicatorPolygons] list holds less than two items
  */
 @ExperimentalMaterial3ExpressiveApi
 @Composable
-fun LoadingIndicator(
-    modifier: Modifier = Modifier,
-    containerColor: Color = Color.Unspecified,
-    indicatorColor: Color = LoadingIndicatorDefaults.IndicatorColor,
-    containerShape: Shape = LoadingIndicatorDefaults.ContainerShape,
-    indicatorPolygons: List<RoundedPolygon> =
-        LoadingIndicatorDefaults.IndeterminateIndicatorPolygons,
+private fun LoadingIndicatorImpl(
+    modifier: Modifier,
+    containerColor: Color,
+    indicatorColor: Color,
+    containerShape: Shape,
+    indicatorPolygons: List<RoundedPolygon>
 ) {
     require(indicatorPolygons.size > 1) {
         "indicatorPolygons should have, at least, two RoundedPolygons"
@@ -334,13 +463,23 @@
     val ContainerShape: Shape
         @Composable get() = LoadingIndicatorTokens.ContainerShape.value
 
-    /** A [LoadingIndicator] default active indicator [Color]. */
+    /**
+     * A [LoadingIndicator] default active indicator [Color] when using an uncontained
+     * [LoadingIndicator].
+     */
     val IndicatorColor: Color
         @Composable get() = LoadingIndicatorTokens.ActiveIndicatorColor.value
 
-    /** A [LoadingIndicator] default container [Color]. */
-    val ContainerColor: Color
-        @Composable get() = LoadingIndicatorTokens.ContainerColor.value
+    /**
+     * A [LoadingIndicator] default active indicator [Color] when using a
+     * [ContainedLoadingIndicator].
+     */
+    val ContainedIndicatorColor: Color
+        @Composable get() = LoadingIndicatorTokens.ContainedActiveColor.value
+
+    /** A [LoadingIndicator] default container [Color] when using a [ContainedLoadingIndicator]. */
+    val ContainedContainerColor: Color
+        @Composable get() = LoadingIndicatorTokens.ContainedContainerColor.value
 
     /**
      * The sequence of [RoundedPolygon]s that the indeterminate [LoadingIndicator] will morph
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt
index c3e23ea..c0393ae 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt
@@ -47,17 +47,57 @@
  * @param shapes A set of corner shapes to be used as this hierarchy's shape system
  * @param content The content inheriting this theme
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun MaterialTheme(
     colorScheme: ColorScheme = MaterialTheme.colorScheme,
     shapes: Shapes = MaterialTheme.shapes,
     typography: Typography = MaterialTheme.typography,
     content: @Composable () -> Unit
+) =
+    MaterialTheme(
+        colorScheme = colorScheme,
+        motionScheme = MaterialTheme.motionScheme,
+        shapes = shapes,
+        typography = typography,
+        content = content
+    )
+
+/**
+ * Material Theming refers to the customization of your Material Design app to better reflect your
+ * product’s brand.
+ *
+ * Material components such as [Button] and [Checkbox] use values provided here when retrieving
+ * default values.
+ *
+ * All values may be set by providing this component with the [colorScheme][ColorScheme],
+ * [typography][Typography] attributes. Use this to configure the overall theme of elements within
+ * this MaterialTheme.
+ *
+ * Any values that are not set will inherit the current value from the theme, falling back to the
+ * defaults if there is no parent MaterialTheme. This allows using a MaterialTheme at the top of
+ * your application, and then separate MaterialTheme(s) for different screens / parts of your UI,
+ * overriding only the parts of the theme definition that need to change.
+ *
+ * @param colorScheme A complete definition of the Material Color theme for this hierarchy
+ * @param motionScheme A complete definition of the Material Motion scheme for this hierarchy
+ * @param typography A set of text styles to be used as this hierarchy's typography system
+ * @param shapes A set of corner shapes to be used as this hierarchy's shape system
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun MaterialTheme(
+    colorScheme: ColorScheme = MaterialTheme.colorScheme,
+    motionScheme: MotionScheme = MaterialTheme.motionScheme,
+    shapes: Shapes = MaterialTheme.shapes,
+    typography: Typography = MaterialTheme.typography,
+    content: @Composable () -> Unit
 ) {
     val rippleIndication = ripple()
     val selectionColors = rememberTextSelectionColors(colorScheme)
     CompositionLocalProvider(
         LocalColorScheme provides colorScheme,
+        LocalMotionScheme provides motionScheme,
         LocalIndication provides rippleIndication,
         LocalShapes provides shapes,
         LocalTextSelectionColors provides selectionColors,
@@ -95,6 +135,11 @@
      */
     val shapes: Shapes
         @Composable @ReadOnlyComposable get() = LocalShapes.current
+
+    /** Retrieves the current [MotionScheme] at the call site's position in the hierarchy. */
+    @ExperimentalMaterial3ExpressiveApi
+    val motionScheme: MotionScheme
+        @Composable @ReadOnlyComposable get() = LocalMotionScheme.current
 }
 
 // TODO: Create a sample androidx.compose.material3.samples.MaterialExpressiveThemeSample
@@ -118,6 +163,7 @@
  * overriding only the parts of the theme definition that need to change.
  *
  * @param colorScheme A complete definition of the Material Color theme for this hierarchy
+ * @param motionScheme A complete definition of the Material motion theme for this hierarchy
  * @param typography A set of text styles to be used as this hierarchy's typography system
  * @param shapes A set of corner shapes to be used as this hierarchy's shape system
  * @param content The content inheriting this theme
@@ -126,6 +172,7 @@
 @Composable
 fun MaterialExpressiveTheme(
     colorScheme: ColorScheme? = null,
+    motionScheme: MotionScheme? = null,
     shapes: Shapes? = null,
     typography: Typography? = null,
     content: @Composable () -> Unit
@@ -133,6 +180,7 @@
     if (LocalUsingExpressiveTheme.current) {
         MaterialTheme(
             colorScheme = colorScheme ?: MaterialTheme.colorScheme,
+            motionScheme = motionScheme ?: MaterialTheme.motionScheme,
             typography = typography ?: MaterialTheme.typography,
             shapes = shapes ?: MaterialTheme.shapes,
             content = content
@@ -141,6 +189,7 @@
         CompositionLocalProvider(LocalUsingExpressiveTheme provides true) {
             MaterialTheme(
                 colorScheme = colorScheme ?: expressiveLightColorScheme(),
+                motionScheme = motionScheme ?: expressiveMotionScheme(),
                 // TODO: replace with calls to Expressive shape default
                 shapes = shapes ?: Shapes(),
                 // TODO: replace with calls to Expressive typography default
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
index 14f8150..310d6c6 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -16,10 +16,8 @@
 
 package androidx.compose.material3
 
-import androidx.compose.animation.core.LinearOutSlowInEasing
 import androidx.compose.animation.core.MutableTransitionState
 import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.ScrollState
@@ -42,6 +40,7 @@
 import androidx.compose.material3.tokens.ElevationTokens
 import androidx.compose.material3.tokens.ListTokens
 import androidx.compose.material3.tokens.MenuTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
@@ -358,6 +357,7 @@
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 internal fun DropdownMenuContent(
     modifier: Modifier,
@@ -376,31 +376,15 @@
 
     val scale by
         transition.animateFloat(
-            transitionSpec = {
-                if (false isTransitioningTo true) {
-                    // Dismissed to expanded
-                    tween(durationMillis = InTransitionDuration, easing = LinearOutSlowInEasing)
-                } else {
-                    // Expanded to dismissed.
-                    tween(durationMillis = 1, delayMillis = OutTransitionDuration - 1)
-                }
-            }
+            // TODO Load the motionScheme tokens from the component tokens file
+            transitionSpec = { MotionSchemeKeyTokens.FastSpatial.value() }
         ) { expanded ->
             if (expanded) ExpandedScaleTarget else ClosedScaleTarget
         }
 
     val alpha by
-        transition.animateFloat(
-            transitionSpec = {
-                if (false isTransitioningTo true) {
-                    // Dismissed to expanded
-                    tween(durationMillis = 30)
-                } else {
-                    // Expanded to dismissed.
-                    tween(durationMillis = OutTransitionDuration)
-                }
-            }
-        ) { expanded ->
+        transition.animateFloat(transitionSpec = { MotionSchemeKeyTokens.FastEffects.value() }) {
+            expanded ->
             if (expanded) ExpandedAlphaTarget else ClosedAlphaTarget
         }
 
@@ -549,8 +533,6 @@
 private val DropdownMenuItemDefaultMaxWidth = 280.dp
 
 // Menu open/close animation.
-internal const val InTransitionDuration = 120
-internal const val OutTransitionDuration = 75
 internal const val ExpandedScaleTarget = 1f
 internal const val ClosedScaleTarget = 0.8f
 internal const val ExpandedAlphaTarget = 1f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ModalBottomSheet.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ModalBottomSheet.kt
index 79c2f0d..91cb8fa 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ModalBottomSheet.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ModalBottomSheet.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.gestures.Orientation
@@ -42,9 +42,11 @@
 import androidx.compose.material3.internal.Strings
 import androidx.compose.material3.internal.draggableAnchors
 import androidx.compose.material3.internal.getString
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -111,6 +113,7 @@
  *   sheet's window behavior.
  * @param content The content to be displayed inside the bottom sheet.
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 @ExperimentalMaterial3Api
 fun ModalBottomSheet(
@@ -128,6 +131,17 @@
     properties: ModalBottomSheetProperties = ModalBottomSheetDefaults.properties,
     content: @Composable ColumnScope.() -> Unit,
 ) {
+    // TODO Load the motionScheme tokens from the component tokens file
+    val anchoredDraggableMotion: FiniteAnimationSpec<Float> =
+        MotionSchemeKeyTokens.DefaultSpatial.value()
+    val showMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.DefaultSpatial.value()
+    val hideMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.FastEffects.value()
+
+    SideEffect {
+        sheetState.showMotionSpec = showMotion
+        sheetState.hideMotionSpec = hideMotion
+        sheetState.anchoredDraggableMotionSpec = anchoredDraggableMotion
+    }
     val scope = rememberCoroutineScope()
     val animateToDismiss: () -> Unit = {
         if (sheetState.anchoredDraggableState.confirmValueChange(Hidden)) {
@@ -218,17 +232,6 @@
                 .align(Alignment.TopCenter)
                 .widthIn(max = sheetMaxWidth)
                 .fillMaxWidth()
-                .graphicsLayer {
-                    val sheetOffset = sheetState.anchoredDraggableState.offset
-                    val sheetHeight = size.height
-                    if (!sheetOffset.isNaN() && !sheetHeight.isNaN() && sheetHeight != 0f) {
-                        val progress = predictiveBackProgress.value
-                        scaleX = calculatePredictiveBackScaleX(progress)
-                        scaleY = calculatePredictiveBackScaleY(progress)
-                        transformOrigin =
-                            TransformOrigin(0.5f, (sheetOffset + sheetHeight) / sheetHeight)
-                    }
-                }
                 .nestedScroll(
                     remember(sheetState) {
                         ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
@@ -275,24 +278,52 @@
                     startDragImmediately = sheetState.anchoredDraggableState.isAnimationRunning,
                     onDragStopped = { settleToDismiss(it) }
                 )
-                .semantics { paneTitle = bottomSheetPaneTitle },
+                .semantics { paneTitle = bottomSheetPaneTitle }
+                .graphicsLayer {
+                    val sheetOffset = sheetState.anchoredDraggableState.offset
+                    val sheetHeight = size.height
+                    if (!sheetOffset.isNaN() && !sheetHeight.isNaN() && sheetHeight != 0f) {
+                        val progress = predictiveBackProgress.value
+                        scaleX = calculatePredictiveBackScaleX(progress)
+                        scaleY = calculatePredictiveBackScaleY(progress)
+                        transformOrigin =
+                            TransformOrigin(0.5f, (sheetOffset + sheetHeight) / sheetHeight)
+                    }
+                }
+                // Scale up the Surface vertically in case the sheet's offset overflows below the
+                // min anchor. This is done to avoid showing a gap when the sheet opens and bounces
+                // when it's applied with a bouncy motion. Note that the content inside the Surface
+                // is scaled back down to maintain its aspect ratio (see below).
+                .verticalScaleUp(
+                    { sheetState.anchoredDraggableState.offset },
+                    { sheetState.anchoredDraggableState.anchors.minAnchor() }
+                ),
         shape = shape,
         color = containerColor,
         contentColor = contentColor,
         tonalElevation = tonalElevation,
     ) {
         Column(
-            Modifier.fillMaxWidth().windowInsetsPadding(contentWindowInsets()).graphicsLayer {
-                val progress = predictiveBackProgress.value
-                val predictiveBackScaleX = calculatePredictiveBackScaleX(progress)
-                val predictiveBackScaleY = calculatePredictiveBackScaleY(progress)
+            Modifier.fillMaxWidth()
+                .windowInsetsPadding(contentWindowInsets())
+                .graphicsLayer {
+                    val progress = predictiveBackProgress.value
+                    val predictiveBackScaleX = calculatePredictiveBackScaleX(progress)
+                    val predictiveBackScaleY = calculatePredictiveBackScaleY(progress)
 
-                // Preserve the original aspect ratio and alignment of the child content.
-                scaleY =
-                    if (predictiveBackScaleY != 0f) predictiveBackScaleX / predictiveBackScaleY
-                    else 1f
-                transformOrigin = PredictiveBackChildTransformOrigin
-            }
+                    // Preserve the original aspect ratio and alignment of the child content.
+                    scaleY =
+                        if (predictiveBackScaleY != 0f) predictiveBackScaleX / predictiveBackScaleY
+                        else 1f
+                    transformOrigin = PredictiveBackChildTransformOrigin
+                }
+                // Scale the content down in case the sheet offset overflows below the min anchor.
+                // The wrapping Surface is scaled up, so this is done to maintain the content's
+                // aspect ratio.
+                .verticalScaleDown(
+                    { sheetState.anchoredDraggableState.offset },
+                    { sheetState.anchoredDraggableState.anchors.minAnchor() }
+                )
         ) {
             if (dragHandle != null) {
                 val collapseActionLabel = getString(Strings.BottomSheetPartialExpandDescription)
@@ -398,11 +429,16 @@
         initialValue = Hidden,
     )
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun Scrim(color: Color, onDismissRequest: () -> Unit, visible: Boolean) {
+    // TODO Load the motionScheme tokens from the component tokens file
     if (color.isSpecified) {
         val alpha by
-            animateFloatAsState(targetValue = if (visible) 1f else 0f, animationSpec = TweenSpec())
+            animateFloatAsState(
+                targetValue = if (visible) 1f else 0f,
+                animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
+            )
         val closeSheet = getString(Strings.CloseSheet)
         val dismissSheet =
             if (visible) {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MotionScheme.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MotionScheme.kt
new file mode 100644
index 0000000..52dd6f2
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MotionScheme.kt
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.animation.core.AnimationVector
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.TwoWayConverter
+import androidx.compose.animation.core.spring
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.staticCompositionLocalOf
+
+/**
+ * A motion scheme provides all the [FiniteAnimationSpec]s for a [MaterialTheme].
+ *
+ * Motion schemes are designed to create a harmonious motion for components in the app.
+ *
+ * There are two built-in schemes, a [standardMotionScheme] and an [expressiveMotionScheme], that
+ * can be used as-is or customized.
+ *
+ * You can customize the motion scheme for all components in the [MaterialTheme], or you can do it
+ * on a per component basis by providing a motion scheme using a [CompositionLocalProvider] and
+ * [LocalMotionScheme].
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Immutable
+interface MotionScheme {
+    /**
+     * A default spatial motion [FiniteAnimationSpec].
+     *
+     * This motion spec is designed to be applied to animations that may change the shape or bounds
+     * of the component. For color or alpha animations use the `effects` equivalent which ensures a
+     * "non-spatial" motion.
+     *
+     * [T] is the generic data type that will be animated by the system, as long as the appropriate
+     * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
+     *
+     * When called from a Composable, use [rememberDefaultSpatialSpec] extension to ensure that the
+     * returned animation spec is remembered across compositions.
+     */
+    fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T>
+
+    /**
+     * A fast spatial motion [FiniteAnimationSpec].
+     *
+     * This motion spec is designed to be applied to animations that may change the shape or bounds
+     * of the component. For color or alpha animations use the `effects` equivalent which ensures a
+     * "non-spatial" motion.
+     *
+     * [T] is the generic data type that will be animated by the system, as long as the appropriate
+     * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
+     *
+     * When called from a Composable, use [rememberFastSpatialSpec] extension to ensure that the
+     * returned animation spec is remembered across compositions.
+     */
+    fun <T> fastSpatialSpec(): FiniteAnimationSpec<T>
+
+    /**
+     * A slow spatial motion [FiniteAnimationSpec].
+     *
+     * This motion spec is designed to be applied to animations that may change the shape or bounds
+     * of the component. For color or alpha animations use the `effects` equivalent which ensures a
+     * "non-spatial" motion.
+     *
+     * [T] is the generic data type that will be animated by the system, as long as the appropriate
+     * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
+     *
+     * When called from a Composable, use [rememberSlowSpatialSpec] extension to ensure that the
+     * returned animation spec is remembered across compositions.
+     */
+    fun <T> slowSpatialSpec(): FiniteAnimationSpec<T>
+
+    /**
+     * A default effects motion [FiniteAnimationSpec].
+     *
+     * This motion spec is designed to be applied to animations that do not change the shape or
+     * bounds of the component. For example, color animation.
+     *
+     * [T] is the generic data type that will be animated by the system, as long as the appropriate
+     * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
+     *
+     * When called from a Composable, use [rememberDefaultEffectsSpec] extension to ensure that the
+     * returned animation spec is remembered across compositions.
+     */
+    fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T>
+
+    /**
+     * A fast effects motion [FiniteAnimationSpec].
+     *
+     * This motion spec is designed to be applied to animations that do not change the shape or
+     * bounds of the component. For example, color animation.
+     *
+     * [T] is the generic data type that will be animated by the system, as long as the appropriate
+     * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
+     *
+     * When called from a Composable, use [rememberFastEffectsSpec] extension to ensure that the
+     * returned animation spec is remembered across compositions.
+     */
+    fun <T> fastEffectsSpec(): FiniteAnimationSpec<T>
+
+    /**
+     * A slow effects motion [FiniteAnimationSpec].
+     *
+     * This motion spec is designed to be applied to animations that do not change the shape or
+     * bounds of the component. For example, color animation.
+     *
+     * [T] is the generic data type that will be animated by the system, as long as the appropriate
+     * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
+     *
+     * When called from a Composable, use [rememberSlowEffectsSpec] extension to ensure that the
+     * returned animation spec is remembered across compositions.
+     */
+    fun <T> slowEffectsSpec(): FiniteAnimationSpec<T>
+}
+
+/**
+ * A default spatial motion [FiniteAnimationSpec] that is remembered across compositions.
+ *
+ * [T] is the generic data type that will be animated by the system, as long as the appropriate
+ * [TwoWayConverter] for converting the data to and from an [AnimationVector] is supplied.
+ *
+ * @see [MotionScheme.defaultSpatialSpec]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+inline fun <reified T> MotionScheme.rememberDefaultSpatialSpec() =
+    remember(this, T::class) {
+        val spec: FiniteAnimationSpec<T> = defaultSpatialSpec()
+        spec
+    }
+
+/**
+ * A fast spatial motion [FiniteAnimationSpec] that is remembered across compositions.
+ *
+ * [T] is the generic data type that will be animated by the system.
+ *
+ * @see [MotionScheme.fastSpatialSpec]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+inline fun <reified T> MotionScheme.rememberFastSpatialSpec() =
+    remember(this, T::class) {
+        val spec: FiniteAnimationSpec<T> = fastSpatialSpec()
+        spec
+    }
+
+/**
+ * A slow spatial motion [FiniteAnimationSpec] that is remembered across compositions.
+ *
+ * [T] is the generic data type that will be animated by the system.
+ *
+ * @see [MotionScheme.slowSpatialSpec]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+inline fun <reified T> MotionScheme.rememberSlowSpatialSpec() =
+    remember(this, T::class) {
+        val spec: FiniteAnimationSpec<T> = slowSpatialSpec()
+        spec
+    }
+
+/**
+ * A default effects motion [FiniteAnimationSpec] that is remembered across compositions.
+ *
+ * [T] is the generic data type that will be animated by the system.
+ *
+ * @see [MotionScheme.defaultEffectsSpec]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+inline fun <reified T> MotionScheme.rememberDefaultEffectsSpec() =
+    remember(this, T::class) {
+        val spec: FiniteAnimationSpec<T> = defaultEffectsSpec()
+        spec
+    }
+
+/**
+ * A fast effects motion [FiniteAnimationSpec] that is remembered across compositions.
+ *
+ * [T] is the generic data type that will be animated by the system.
+ *
+ * @see [MotionScheme.fastEffectsSpec]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+inline fun <reified T> MotionScheme.rememberFastEffectsSpec() =
+    remember(this, T::class) {
+        val spec: FiniteAnimationSpec<T> = fastEffectsSpec()
+        spec
+    }
+
+/**
+ * A slow effects motion [FiniteAnimationSpec] that is remembered across compositions.
+ *
+ * [T] is the generic data type that will be animated by the system.
+ *
+ * @see [MotionScheme.slowEffectsSpec]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+inline fun <reified T> MotionScheme.rememberSlowEffectsSpec() =
+    remember(this, T::class) {
+        val spec: FiniteAnimationSpec<T> = slowEffectsSpec()
+        spec
+    }
+
+/** Returns a standard Material motion scheme. */
+@ExperimentalMaterial3ExpressiveApi
+fun standardMotionScheme(): MotionScheme =
+    object : MotionScheme {
+        override fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = StandardSpatialDampingRatio, stiffness = 700f)
+        }
+
+        override fun <T> fastSpatialSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = StandardSpatialDampingRatio, stiffness = 1400f)
+        }
+
+        override fun <T> slowSpatialSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = StandardSpatialDampingRatio, stiffness = 300f)
+        }
+
+        override fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsDefaultStiffness)
+        }
+
+        override fun <T> fastEffectsSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsFastStiffness)
+        }
+
+        override fun <T> slowEffectsSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsSlowStiffness)
+        }
+    }
+
+/** Returns an expressive Material motion scheme. */
+@ExperimentalMaterial3ExpressiveApi
+fun expressiveMotionScheme(): MotionScheme =
+    object : MotionScheme {
+        override fun <T> defaultSpatialSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = 0.8f, stiffness = 380f)
+        }
+
+        override fun <T> fastSpatialSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = 0.6f, stiffness = 800f)
+        }
+
+        override fun <T> slowSpatialSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = 0.8f, stiffness = 200f)
+        }
+
+        override fun <T> defaultEffectsSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsDefaultStiffness)
+        }
+
+        override fun <T> fastEffectsSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsFastStiffness)
+        }
+
+        override fun <T> slowEffectsSpec(): FiniteAnimationSpec<T> {
+            return spring(dampingRatio = EffectsDampingRatio, stiffness = EffectsSlowStiffness)
+        }
+    }
+
+/**
+ * CompositionLocal used to pass [MotionScheme] down the tree.
+ *
+ * Setting the value here is typically done as part of [MaterialTheme]. To retrieve the current
+ * value of this CompositionLocal, use [MaterialTheme.motionScheme].
+ */
+@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+@get:ExperimentalMaterial3ExpressiveApi
+@ExperimentalMaterial3ExpressiveApi
+internal val LocalMotionScheme = staticCompositionLocalOf { standardMotionScheme() }
+
+/**
+ * Helper function for component motion tokens.
+ *
+ * Here is an example on how to use component motion tokens:
+ * ``MaterialTheme.motionScheme.fromToken(ExtendedFabBranded.ExpandMotion)``
+ *
+ * The returned [FiniteAnimationSpec] is remembered across compositions.
+ *
+ * @param value the token's value
+ */
+@Composable
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Stable
+internal inline fun <reified T> MotionScheme.fromToken(
+    value: MotionSchemeKeyTokens
+): FiniteAnimationSpec<T> {
+    return when (value) {
+        MotionSchemeKeyTokens.DefaultSpatial -> rememberDefaultSpatialSpec()
+        MotionSchemeKeyTokens.FastSpatial -> rememberFastSpatialSpec()
+        MotionSchemeKeyTokens.SlowSpatial -> rememberSlowSpatialSpec()
+        MotionSchemeKeyTokens.DefaultEffects -> rememberDefaultEffectsSpec()
+        MotionSchemeKeyTokens.FastEffects -> rememberFastEffectsSpec()
+        MotionSchemeKeyTokens.SlowEffects -> rememberSlowEffectsSpec()
+    }
+}
+
+/**
+ * Converts a [MotionSchemeKeyTokens] key to the [FiniteAnimationSpec] provided by the
+ * [MotionScheme].
+ */
+@Composable
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+internal inline fun <reified T> MotionSchemeKeyTokens.value(): FiniteAnimationSpec<T> =
+    MaterialTheme.motionScheme.fromToken(this)
+
+// Common effects damping and stiffness values for both Standard and Expressive
+private const val EffectsDampingRatio = Spring.DampingRatioNoBouncy
+private const val EffectsDefaultStiffness = 1600f
+private const val EffectsFastStiffness = 3800f
+private const val EffectsSlowStiffness = 800f
+
+// Common damping for Standard spatial specs
+private const val StandardSpatialDampingRatio = 0.9f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
index 39c18a0..f172805 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.indication
 import androidx.compose.foundation.interaction.Interaction
@@ -40,6 +39,7 @@
 import androidx.compose.material3.internal.ProvideContentColorTextStyle
 import androidx.compose.material3.internal.systemBarsForVisualComponents
 import androidx.compose.material3.tokens.ElevationTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.NavigationBarTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -168,6 +168,7 @@
  *   preview the item in different states. Note that if `null` is provided, interactions will still
  *   happen internally.
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun RowScope.NavigationBarItem(
     selected: Boolean,
@@ -187,7 +188,8 @@
             val iconColor by
                 animateColorAsState(
                     targetValue = colors.iconColor(selected = selected, enabled = enabled),
-                    animationSpec = tween(ItemAnimationDurationMillis)
+                    // TODO Load the motionScheme tokens from the component tokens file
+                    animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
                 )
             // If there's a label, don't have a11y services repeat the icon description.
             val clearSemantics = label != null && (alwaysShowLabel || selected)
@@ -203,7 +205,8 @@
                 val textColor by
                     animateColorAsState(
                         targetValue = colors.textColor(selected = selected, enabled = enabled),
-                        animationSpec = tween(ItemAnimationDurationMillis)
+                        // TODO Load the motionScheme tokens from the component tokens file
+                        animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
                     )
                 ProvideContentColorTextStyle(
                     contentColor = textColor,
@@ -231,12 +234,18 @@
         contentAlignment = Alignment.Center,
         propagateMinConstraints = true,
     ) {
-        val animationProgress: State<Float> =
+        val alphaAnimationProgress: State<Float> =
             animateFloatAsState(
                 targetValue = if (selected) 1f else 0f,
-                animationSpec = tween(ItemAnimationDurationMillis)
+                // TODO Load the motionScheme tokens from the component tokens file
+                animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
             )
-
+        val sizeAnimationProgress: State<Float> =
+            animateFloatAsState(
+                targetValue = if (selected) 1f else 0f,
+                // TODO Load the motionScheme tokens from the component tokens file
+                animationSpec = MotionSchemeKeyTokens.FastSpatial.value()
+            )
         // The entire item is selectable, but only the indicator pill shows the ripple. To achieve
         // this, we re-map the coordinates of the item's InteractionSource into the coordinates of
         // the indicator.
@@ -265,7 +274,7 @@
             @Composable {
                 Box(
                     Modifier.layoutId(IndicatorLayoutIdTag)
-                        .graphicsLayer { alpha = animationProgress.value }
+                        .graphicsLayer { alpha = alphaAnimationProgress.value }
                         .background(
                             color = colors.indicatorColor,
                             shape = NavigationBarTokens.ActiveIndicatorShape.value,
@@ -279,7 +288,8 @@
             icon = styledIcon,
             label = styledLabel,
             alwaysShowLabel = alwaysShowLabel,
-            animationProgress = { animationProgress.value },
+            alphaAnimationProgress = { alphaAnimationProgress.value },
+            sizeAnimationProgress = { sizeAnimationProgress.value }
         )
     }
 }
@@ -502,8 +512,10 @@
  * @param label text label for this item
  * @param alwaysShowLabel whether to always show the label for this item. If false, the label will
  *   only be shown when this item is selected.
- * @param animationProgress progress of the animation, where 0 represents the unselected state of
- *   this item and 1 represents the selected state. This value controls other values such as
+ * @param alphaAnimationProgress progress of the animation, where 0 represents the unselected state
+ *   of this item and 1 represents the selected state. This value controls the indicator's opacity.
+ * @param sizeAnimationProgress progress of the animation, where 0 represents the unselected state
+ *   of this item and 1 represents the selected state. This value controls other values such as
  *   indicator size, icon and label positions, etc.
  */
 @Composable
@@ -513,7 +525,8 @@
     icon: @Composable () -> Unit,
     label: @Composable (() -> Unit)?,
     alwaysShowLabel: Boolean,
-    animationProgress: () -> Float,
+    alphaAnimationProgress: () -> Float,
+    sizeAnimationProgress: () -> Float,
 ) {
     Layout(
         modifier = Modifier.badgeBounds(),
@@ -526,7 +539,9 @@
             if (label != null) {
                 Box(
                     Modifier.layoutId(LabelLayoutIdTag)
-                        .graphicsLayer { alpha = if (alwaysShowLabel) 1f else animationProgress() }
+                        .graphicsLayer {
+                            alpha = if (alwaysShowLabel) 1f else alphaAnimationProgress()
+                        }
                         .padding(horizontal = NavigationBarItemHorizontalPadding / 2)
                 ) {
                     label()
@@ -534,7 +549,9 @@
             }
         }
     ) { measurables, constraints ->
-        @Suppress("NAME_SHADOWING") val animationProgress = animationProgress()
+        @Suppress("NAME_SHADOWING")
+        // Ensure that the progress is >= 0. It may be negative on bouncy springs, for example.
+        val animationProgress = sizeAnimationProgress().coerceAtLeast(0f)
         val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
         val iconPlaceable =
             measurables.fastFirst { it.layoutId == IconLayoutIdTag }.measure(looseConstraints)
@@ -702,8 +719,6 @@
 
 private val NavigationBarHeight: Dp = NavigationBarTokens.ContainerHeight
 
-private const val ItemAnimationDurationMillis: Int = 100
-
 /*@VisibleForTesting*/
 internal val NavigationBarItemHorizontalPadding: Dp = 8.dp
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
index cbb0fad..d1e5dba 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
@@ -17,8 +17,10 @@
 package androidx.compose.material3
 
 import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.animate
+import androidx.compose.animation.core.snap
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.detectTapGestures
@@ -50,6 +52,7 @@
 import androidx.compose.material3.internal.snapTo
 import androidx.compose.material3.internal.systemBarsForVisualComponents
 import androidx.compose.material3.tokens.ElevationTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.NavigationDrawerTokens
 import androidx.compose.material3.tokens.ScrimTokens
 import androidx.compose.runtime.Composable
@@ -120,10 +123,13 @@
     confirmStateChange: (DrawerValue) -> Boolean = { true }
 ) {
 
+    internal var anchoredDraggableMotionSpec: FiniteAnimationSpec<Float> =
+        AnchoredDraggableDefaultAnimationSpec
+
     internal val anchoredDraggableState =
         AnchoredDraggableState(
             initialValue = initialValue,
-            animationSpec = AnimationSpec,
+            animationSpec = { anchoredDraggableMotionSpec },
             confirmValueChange = confirmStateChange,
             positionalThreshold = { distance -> distance * DrawerPositionalThreshold },
             velocityThreshold = { with(requireDensity()) { DrawerVelocityThreshold.toPx() } }
@@ -161,7 +167,8 @@
      *
      * @return the reason the open animation ended
      */
-    suspend fun open() = animateTo(DrawerValue.Open)
+    suspend fun open() =
+        animateTo(targetValue = DrawerValue.Open, animationSpec = openDrawerMotionSpec)
 
     /**
      * Close the drawer with animation and suspend until it if fully closed or animation has been
@@ -169,7 +176,8 @@
      *
      * @return the reason the close animation ended
      */
-    suspend fun close() = animateTo(DrawerValue.Closed)
+    suspend fun close() =
+        animateTo(targetValue = DrawerValue.Closed, animationSpec = closeDrawerMotionSpec)
 
     /**
      * Set the state of the drawer with specific animation
@@ -234,6 +242,10 @@
 
     internal var density: Density? by mutableStateOf(null)
 
+    internal var openDrawerMotionSpec: FiniteAnimationSpec<Float> = snap()
+
+    internal var closeDrawerMotionSpec: FiniteAnimationSpec<Float> = snap()
+
     private fun requireDensity() =
         requireNotNull(density) {
             "The density on DrawerState ($this) was not set. Did you use DrawerState" +
@@ -244,7 +256,7 @@
 
     private suspend fun animateTo(
         targetValue: DrawerValue,
-        animationSpec: AnimationSpec<Float> = AnimationSpec,
+        animationSpec: AnimationSpec<Float>,
         velocity: Float = anchoredDraggableState.lastVelocity
     ) {
         anchoredDraggableState.anchoredDrag(targetValue = targetValue) { anchors, latestTarget ->
@@ -309,6 +321,7 @@
  * @param scrimColor color of the scrim that obscures content when the drawer is open
  * @param content content of the rest of the UI
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun ModalNavigationDrawer(
     drawerContent: @Composable () -> Unit,
@@ -325,7 +338,18 @@
     var minValue by remember(density) { mutableFloatStateOf(0f) }
     val maxValue = 0f
 
-    SideEffect { drawerState.density = density }
+    // TODO Load the motionScheme tokens from the component tokens file
+    val anchoredDraggableMotion: FiniteAnimationSpec<Float> =
+        MotionSchemeKeyTokens.DefaultSpatial.value()
+    val openMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.DefaultSpatial.value()
+    val closeMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.FastEffects.value()
+
+    SideEffect {
+        drawerState.density = density
+        drawerState.openDrawerMotionSpec = openMotion
+        drawerState.closeDrawerMotionSpec = closeMotion
+        drawerState.anchoredDraggableMotionSpec = anchoredDraggableMotion
+    }
 
     val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
     Box(
@@ -426,6 +450,7 @@
  * @param gesturesEnabled whether or not the drawer can be interacted by gestures
  * @param content content of the rest of the UI
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun DismissibleNavigationDrawer(
     drawerContent: @Composable () -> Unit,
@@ -436,7 +461,16 @@
 ) {
     var anchorsInitialized by remember { mutableStateOf(false) }
     val density = LocalDensity.current
-    SideEffect { drawerState.density = density }
+
+    // TODO Load the motionScheme tokens from the component tokens file
+    val openMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.DefaultSpatial.value()
+    val closeMotion: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.FastEffects.value()
+
+    SideEffect {
+        drawerState.density = density
+        drawerState.openDrawerMotionSpec = openMotion
+        drawerState.closeDrawerMotionSpec = closeMotion
+    }
 
     val scope = rememberCoroutineScope()
     val navigationMenu = getString(Strings.NavigationMenu)
@@ -565,13 +599,13 @@
 ) {
     DrawerSheet(
         drawerPredictiveBackState = null,
-        windowInsets,
-        modifier,
-        drawerShape,
-        drawerContainerColor,
-        drawerContentColor,
-        drawerTonalElevation,
-        content
+        windowInsets = windowInsets,
+        modifier = modifier,
+        drawerShape = drawerShape,
+        drawerContainerColor = drawerContainerColor,
+        drawerContentColor = drawerContentColor,
+        drawerTonalElevation = drawerTonalElevation,
+        content = content
     )
 }
 
@@ -609,14 +643,15 @@
 ) {
     DrawerPredictiveBackHandler(drawerState) { drawerPredictiveBackState ->
         DrawerSheet(
-            drawerPredictiveBackState,
-            windowInsets,
-            modifier,
-            drawerShape,
-            drawerContainerColor,
-            drawerContentColor,
-            drawerTonalElevation,
-            content
+            drawerPredictiveBackState = drawerPredictiveBackState,
+            windowInsets = windowInsets,
+            modifier = modifier,
+            drawerShape = drawerShape,
+            drawerContainerColor = drawerContainerColor,
+            drawerContentColor = drawerContentColor,
+            drawerTonalElevation = drawerTonalElevation,
+            drawerOffset = { drawerState.anchoredDraggableState.offset },
+            content = content
         )
     }
 }
@@ -653,13 +688,13 @@
 ) {
     DrawerSheet(
         drawerPredictiveBackState = null,
-        windowInsets,
-        modifier,
-        drawerShape,
-        drawerContainerColor,
-        drawerContentColor,
-        drawerTonalElevation,
-        content
+        windowInsets = windowInsets,
+        modifier = modifier,
+        drawerShape = drawerShape,
+        drawerContainerColor = drawerContainerColor,
+        drawerContentColor = drawerContentColor,
+        drawerTonalElevation = drawerTonalElevation,
+        content = content
     )
 }
 
@@ -697,14 +732,15 @@
 ) {
     DrawerPredictiveBackHandler(drawerState) { drawerPredictiveBackState ->
         DrawerSheet(
-            drawerPredictiveBackState,
-            windowInsets,
-            modifier,
-            drawerShape,
-            drawerContainerColor,
-            drawerContentColor,
-            drawerTonalElevation,
-            content
+            drawerPredictiveBackState = drawerPredictiveBackState,
+            windowInsets = windowInsets,
+            modifier = modifier,
+            drawerShape = drawerShape,
+            drawerContainerColor = drawerContainerColor,
+            drawerContentColor = drawerContentColor,
+            drawerTonalElevation = drawerTonalElevation,
+            drawerOffset = { drawerState.anchoredDraggableState.offset },
+            content = content
         )
     }
 }
@@ -738,13 +774,13 @@
     val navigationMenu = getString(Strings.NavigationMenu)
     DrawerSheet(
         drawerPredictiveBackState = null,
-        windowInsets,
-        modifier.semantics { paneTitle = navigationMenu },
-        drawerShape,
-        drawerContainerColor,
-        drawerContentColor,
-        drawerTonalElevation,
-        content
+        windowInsets = windowInsets,
+        modifier = modifier.semantics { paneTitle = navigationMenu },
+        drawerShape = drawerShape,
+        drawerContainerColor = drawerContainerColor,
+        drawerContentColor = drawerContentColor,
+        drawerTonalElevation = drawerTonalElevation,
+        content = content
     )
 }
 
@@ -757,17 +793,32 @@
     drawerContainerColor: Color = DrawerDefaults.standardContainerColor,
     drawerContentColor: Color = contentColorFor(drawerContainerColor),
     drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
+    drawerOffset: () -> Float = { 0F },
     content: @Composable ColumnScope.() -> Unit
 ) {
+    val density = LocalDensity.current
+    val maxWidth = NavigationDrawerTokens.ContainerWidth
+    val maxWidthPx = with(density) { maxWidth.toPx() }
     val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
     val predictiveBackDrawerContainerModifier =
-        if (drawerPredictiveBackState != null)
+        if (drawerPredictiveBackState != null) {
             Modifier.predictiveBackDrawerContainer(drawerPredictiveBackState, isRtl)
-        else Modifier
+        } else {
+            Modifier
+        }
     Surface(
         modifier =
             modifier
-                .sizeIn(minWidth = MinimumDrawerWidth, maxWidth = DrawerDefaults.MaximumDrawerWidth)
+                .sizeIn(minWidth = MinimumDrawerWidth, maxWidth = maxWidth)
+                // Scale up the Surface horizontally in case the drawer offset it greater than zero.
+                // This is done to avoid showing a gap when the drawer opens and bounces when it's
+                // applied with a bouncy motion. Note that the content inside the Surface is scaled
+                // back down to maintain its aspect ratio (see below).
+                .horizontalScaleUp(
+                    drawerOffset = drawerOffset,
+                    drawerWidth = maxWidthPx,
+                    isRtl = isRtl
+                )
                 .then(predictiveBackDrawerContainerModifier)
                 .fillMaxHeight(),
         shape = drawerShape,
@@ -780,9 +831,14 @@
                 Modifier.predictiveBackDrawerChild(drawerPredictiveBackState, isRtl)
             else Modifier
         Column(
-            Modifier.sizeIn(
-                    minWidth = MinimumDrawerWidth,
-                    maxWidth = DrawerDefaults.MaximumDrawerWidth
+            Modifier.sizeIn(minWidth = MinimumDrawerWidth, maxWidth = maxWidth)
+                // Scale the content down in case the drawer offset is greater than one. The
+                // wrapping Surface is scaled up, so this is done to maintain the content's aspect
+                // ratio.
+                .horizontalScaleDown(
+                    drawerOffset = drawerOffset,
+                    drawerWidth = maxWidthPx,
+                    isRtl = isRtl
                 )
                 .then(predictiveBackDrawerChildModifier)
                 .windowInsetsPadding(windowInsets),
@@ -791,6 +847,44 @@
     }
 }
 
+/**
+ * A [Modifier] that scales up the drawing layer on the X axis in case the [drawerOffset] is greater
+ * than zero. The scaling will ensure that there is no visible gap between the drawer and the edge
+ * of the screen in case the drawer bounces when it opens due to a more expressive motion setting.
+ *
+ * A [horizontalScaleDown] should be applied to the content of the drawer to maintain the content
+ * aspect ratio as the container scales up.
+ *
+ * @see horizontalScaleDown
+ */
+private fun Modifier.horizontalScaleUp(
+    drawerOffset: () -> Float,
+    drawerWidth: Float,
+    isRtl: Boolean
+) = graphicsLayer {
+    val offset = drawerOffset()
+    scaleX = if (offset > 0f) 1f + offset / drawerWidth else 1f
+    transformOrigin = TransformOrigin(if (isRtl) 0f else 1f, 0.5f)
+}
+
+/**
+ * A [Modifier] that scales down the drawing layer on the X axis in case the [drawerOffset] is
+ * greater than zero. This modifier should be applied to the content inside a component that was
+ * scaled up with a [horizontalScaleUp] modifier. It will ensure that the content maintains its
+ * aspect ratio as the container scales up.
+ *
+ * @see horizontalScaleUp
+ */
+private fun Modifier.horizontalScaleDown(
+    drawerOffset: () -> Float,
+    drawerWidth: Float,
+    isRtl: Boolean
+) = graphicsLayer {
+    val offset = drawerOffset()
+    scaleX = if (offset > 0f) 1 / (1f + offset / drawerWidth) else 1f
+    transformOrigin = TransformOrigin(if (isRtl) 0f else 1f, 0f)
+}
+
 private fun Modifier.predictiveBackDrawerContainer(
     drawerPredictiveBackState: DrawerPredictiveBackState,
     isRtl: Boolean
@@ -1152,6 +1246,7 @@
 private val DrawerPositionalThreshold = 0.5f
 private val DrawerVelocityThreshold = 400.dp
 private val MinimumDrawerWidth = 240.dp
+
 // TODO: b/177571613 this should be a proper decay settling
 // this is taken from the DrawerLayout's DragViewHelper as a min duration.
-private val AnimationSpec = TweenSpec<Float>(durationMillis = 256)
+private val AnchoredDraggableDefaultAnimationSpec = TweenSpec<Float>(durationMillis = 256)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationItem.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationItem.kt
index 8933e20..93dab1a 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationItem.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationItem.kt
@@ -16,8 +16,8 @@
 
 package androidx.compose.material3
 
+import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.indication
 import androidx.compose.foundation.interaction.Interaction
@@ -29,11 +29,11 @@
 import androidx.compose.material3.internal.MappedInteractionSource
 import androidx.compose.material3.internal.ProvideContentColorTextStyle
 import androidx.compose.material3.internal.layoutId
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
@@ -58,7 +58,6 @@
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.Role
-import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
@@ -69,6 +68,8 @@
 import androidx.compose.ui.unit.offset
 import androidx.compose.ui.util.fastFirst
 import androidx.compose.ui.util.fastFirstOrNull
+import androidx.compose.ui.util.lerp
+import kotlin.jvm.JvmInline
 import kotlin.math.max
 import kotlin.math.roundToInt
 
@@ -197,8 +198,7 @@
 }
 
 /**
- * Internal function to make a navigation item to be used with the Navigation Bar item or the
- * Navigation Rail item, depending on the passed in param values.
+ * Internal function to make a navigation suite component, such as the [ShortNavigationBarItem].
  *
  * @param selected whether this item is selected
  * @param onClick called when this item is clicked
@@ -248,32 +248,14 @@
     iconPosition: NavigationItemIconPosition,
     interactionSource: MutableInteractionSource
 ) {
-    val styledIcon =
-        @Composable {
-            val iconColor = colors.iconColor(selected = selected, enabled = enabled)
-            // If there's a label, don't have a11y services repeat the icon description.
-            val clearSemantics = label != null
-            Box(modifier = if (clearSemantics) Modifier.clearAndSetSemantics {} else Modifier) {
-                CompositionLocalProvider(LocalContentColor provides iconColor, content = icon)
-            }
-        }
-    val iconWithBadge =
-        if (badge != null) {
-            { BadgedBox(badge = { badge() }) { styledIcon() } }
-        } else {
-            styledIcon
-        }
-
+    val iconWithBadge: @Composable () -> Unit = {
+        StyledIcon(selected, icon, colors, enabled, badge)
+    }
     val styledLabel: @Composable (() -> Unit)? =
-        label?.let {
-            @Composable {
-                val textColor = colors.textColor(selected = selected, enabled = enabled)
-                ProvideContentColorTextStyle(
-                    contentColor = textColor,
-                    textStyle = labelTextStyle,
-                    content = label
-                )
-            }
+        if (label == null) {
+            null
+        } else {
+            { StyledLabel(selected, labelTextStyle, colors, enabled, label) }
         }
 
     var itemWidth by remember { mutableIntStateOf(0) }
@@ -288,17 +270,15 @@
                 interactionSource = interactionSource,
                 indication = null,
             )
-            .defaultMinSize(minWidth = NavigationItemMinWidth, minHeight = NavigationItemMinHeight)
+            .defaultMinSize(
+                minWidth = LocalMinimumInteractiveComponentSize.current,
+                minHeight = LocalMinimumInteractiveComponentSize.current
+            )
             .onSizeChanged { itemWidth = it.width },
         contentAlignment = Alignment.Center,
         propagateMinConstraints = true,
     ) {
-        val animationProgress: State<Float> =
-            animateFloatAsState(
-                targetValue = if (selected) 1f else 0f,
-                animationSpec = tween(ItemAnimationDurationMillis)
-            )
-
+        val indicatorAnimationProgress = animateIndicatorProgressAsState(selected)
         var offsetInteractionSource: MappedInteractionSource? = null
         if (iconPosition == NavigationItemIconPosition.Top) {
             // The entire item is selectable, but only the indicator pill shows the ripple. To
@@ -325,7 +305,7 @@
             icon = iconWithBadge,
             iconPosition = iconPosition,
             label = styledLabel,
-            animationProgress = { animationProgress.value },
+            indicatorAnimationProgress = { indicatorAnimationProgress.value },
             indicatorHorizontalPadding = indicatorHorizontalPadding,
             indicatorVerticalPadding = indicatorVerticalPadding,
             indicatorToLabelVerticalPadding = indicatorToLabelVerticalPadding,
@@ -335,6 +315,129 @@
     }
 }
 
+/**
+ * Internal function to make an animated navigation item to be used with a navigation suite
+ * component, such as the [WideNavigationRailItem].
+ *
+ * This item will animate its elements when the value of [iconPosition] changes.
+ */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+internal fun AnimatedNavigationItem(
+    selected: Boolean,
+    onClick: () -> Unit,
+    icon: @Composable () -> Unit,
+    indicatorShape: Shape,
+    topIconIndicatorWidth: Dp,
+    topIconLabelTextStyle: TextStyle,
+    startIconLabelTextStyle: TextStyle,
+    topIconIndicatorHorizontalPadding: Dp,
+    topIconIndicatorVerticalPadding: Dp,
+    topIconIndicatorToLabelVerticalPadding: Dp,
+    startIconIndicatorHorizontalPadding: Dp,
+    startIconIndicatorVerticalPadding: Dp,
+    startIconToLabelHorizontalPadding: Dp,
+    startIconItemPadding: Dp,
+    colors: NavigationItemColors,
+    modifier: Modifier,
+    enabled: Boolean,
+    label: @Composable (() -> Unit),
+    badge: (@Composable () -> Unit)?,
+    iconPosition: NavigationItemIconPosition,
+    interactionSource: MutableInteractionSource
+) {
+    val iconWithBadge: @Composable () -> Unit = {
+        StyledIcon(selected, icon, colors, enabled, badge)
+    }
+
+    var itemWidth by remember { mutableIntStateOf(0) }
+
+    Box(
+        modifier
+            .selectable(
+                selected = selected,
+                onClick = onClick,
+                enabled = enabled,
+                role = Role.Tab,
+                interactionSource = interactionSource,
+                indication = null,
+            )
+            .defaultMinSize(
+                minWidth = LocalMinimumInteractiveComponentSize.current,
+                minHeight = LocalMinimumInteractiveComponentSize.current
+            )
+            .onSizeChanged { itemWidth = it.width },
+        contentAlignment = Alignment.Center,
+        propagateMinConstraints = true,
+    ) {
+        val indicatorAnimationProgress = animateIndicatorProgressAsState(selected)
+        val iconPositionProgress =
+            animateFloatAsState(
+                targetValue = if (iconPosition == NavigationItemIconPosition.Top) 0f else 1f,
+                // TODO Load the motionScheme tokens from the component tokens file
+                animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value()
+            )
+
+        // We'll always display only one label, but for the animation to be correct we need two
+        // separate composables that will fade in/out appropriately.
+        val labelTopIconAlphaProgress =
+            animateLabelAlphaProgressAsState(iconPosition == NavigationItemIconPosition.Top)
+        val labelStartIconAlphaProgress =
+            animateLabelAlphaProgressAsState(iconPosition == NavigationItemIconPosition.Start)
+        val labelTopIcon: @Composable (() -> Unit) = {
+            Box(modifier = Modifier.graphicsLayer { alpha = labelTopIconAlphaProgress.value }) {
+                StyledLabel(selected, topIconLabelTextStyle, colors, enabled, label)
+            }
+        }
+        val labelStartIcon =
+            @Composable {
+                Box(
+                    modifier = Modifier.graphicsLayer { alpha = labelStartIconAlphaProgress.value }
+                ) {
+                    StyledLabel(selected, startIconLabelTextStyle, colors, enabled, label)
+                }
+            }
+
+        var offsetInteractionSource: MappedInteractionSource? = null
+        if (iconPosition == NavigationItemIconPosition.Top) {
+            // The entire item is selectable, but only the indicator pill shows the ripple. To
+            // achieve this, we re-map the coordinates of the item's InteractionSource into the
+            // coordinates of the indicator.
+            val deltaOffset: Offset
+            with(LocalDensity.current) {
+                deltaOffset =
+                    Offset(
+                        (itemWidth - topIconIndicatorWidth.roundToPx()).toFloat() / 2,
+                        IndicatorVerticalOffset.toPx()
+                    )
+            }
+            offsetInteractionSource =
+                remember(interactionSource, deltaOffset) {
+                    MappedInteractionSource(interactionSource, deltaOffset)
+                }
+        }
+
+        AnimatedNavigationItemLayout(
+            interactionSource = offsetInteractionSource ?: interactionSource,
+            indicatorColor = colors.selectedIndicatorColor,
+            indicatorShape = indicatorShape,
+            indicatorAnimationProgress = { indicatorAnimationProgress.value.coerceAtLeast(0f) },
+            icon = iconWithBadge,
+            iconPosition = iconPosition,
+            iconPositionProgress = { iconPositionProgress.value.coerceAtLeast(0f) },
+            labelTopIcon = labelTopIcon,
+            labelStartIcon = labelStartIcon,
+            topIconIndicatorHorizontalPadding = topIconIndicatorHorizontalPadding,
+            topIconIndicatorVerticalPadding = topIconIndicatorVerticalPadding,
+            topIconIndicatorToLabelVerticalPadding = topIconIndicatorToLabelVerticalPadding,
+            startIconIndicatorHorizontalPadding = startIconIndicatorHorizontalPadding,
+            startIconIndicatorVerticalPadding = startIconIndicatorVerticalPadding,
+            startIconToLabelHorizontalPadding = startIconToLabelHorizontalPadding,
+            startIconItemPadding = startIconItemPadding
+        )
+    }
+}
+
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun NavigationItemLayout(
@@ -344,7 +447,7 @@
     icon: @Composable () -> Unit,
     iconPosition: NavigationItemIconPosition,
     label: @Composable (() -> Unit)?,
-    animationProgress: () -> Float,
+    indicatorAnimationProgress: () -> Float,
     indicatorHorizontalPadding: Dp,
     indicatorVerticalPadding: Dp,
     indicatorToLabelVerticalPadding: Dp,
@@ -355,23 +458,13 @@
         modifier = Modifier.badgeBounds(),
         content = {
             // Create the indicator ripple.
-            Box(
-                Modifier.layoutId(IndicatorRippleLayoutIdTag)
-                    .clip(indicatorShape)
-                    .indication(interactionSource, ripple())
-            )
+            IndicatorRipple(interactionSource, indicatorShape)
             // Create the indicator. The indicator has a width-expansion animation which interferes
-            // with
-            // the timing of the ripple, which is why they are separate composables.
-            Box(
-                Modifier.layoutId(IndicatorLayoutIdTag)
-                    .graphicsLayer { alpha = animationProgress() }
-                    .background(
-                        color = indicatorColor,
-                        shape = indicatorShape,
-                    )
-            )
+            // with the timing of the ripple, which is why they are separate composables.
+            Indicator(indicatorColor, indicatorShape, indicatorAnimationProgress)
+
             Box(Modifier.layoutId(IconLayoutIdTag)) { icon() }
+
             if (label != null) {
                 Box(Modifier.layoutId(LabelLayoutIdTag)) { label() }
             }
@@ -380,7 +473,7 @@
             if (label == null || iconPosition == NavigationItemIconPosition.Top) {
                 TopIconOrIconOnlyMeasurePolicy(
                     label != null,
-                    animationProgress,
+                    indicatorAnimationProgress,
                     indicatorHorizontalPadding,
                     indicatorVerticalPadding,
                     indicatorToLabelVerticalPadding,
@@ -388,7 +481,7 @@
                 )
             } else {
                 StartIconMeasurePolicy(
-                    animationProgress,
+                    indicatorAnimationProgress,
                     indicatorHorizontalPadding,
                     indicatorVerticalPadding,
                     startIconToLabelHorizontalPadding,
@@ -397,9 +490,58 @@
     )
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun AnimatedNavigationItemLayout(
+    interactionSource: InteractionSource,
+    indicatorColor: Color,
+    indicatorShape: Shape,
+    indicatorAnimationProgress: () -> Float,
+    icon: @Composable () -> Unit,
+    iconPosition: NavigationItemIconPosition,
+    iconPositionProgress: () -> Float,
+    labelTopIcon: @Composable (() -> Unit),
+    labelStartIcon: @Composable (() -> Unit),
+    topIconIndicatorHorizontalPadding: Dp,
+    topIconIndicatorVerticalPadding: Dp,
+    topIconIndicatorToLabelVerticalPadding: Dp,
+    startIconIndicatorHorizontalPadding: Dp,
+    startIconIndicatorVerticalPadding: Dp,
+    startIconToLabelHorizontalPadding: Dp,
+    startIconItemPadding: Dp,
+) {
+    Layout(
+        content = {
+            // Create the indicator ripple.
+            IndicatorRipple(interactionSource, indicatorShape)
+            // Create the indicator. The indicator has a width-expansion animation which interferes
+            // with the timing of the ripple, which is why they are separate composables.
+            Indicator(indicatorColor, indicatorShape, indicatorAnimationProgress)
+
+            Box(Modifier.layoutId(IconLayoutIdTag)) { icon() }
+
+            Box(Modifier.layoutId(AnimatedLabelTopIconLayoutIdTag)) { labelTopIcon() }
+            Box(Modifier.layoutId(AnimatedLabelStartIconLayoutIdTag)) { labelStartIcon() }
+        },
+        measurePolicy =
+            AnimatedMeasurePolicy(
+                iconPosition = iconPosition,
+                iconPositionProgress = iconPositionProgress,
+                indicatorAnimationProgress = indicatorAnimationProgress,
+                topIconIndicatorHorizontalPadding = topIconIndicatorHorizontalPadding,
+                topIconIndicatorVerticalPadding = topIconIndicatorVerticalPadding,
+                topIconIndicatorToLabelVerticalPadding = topIconIndicatorToLabelVerticalPadding,
+                startIconIndicatorHorizontalPadding = startIconIndicatorHorizontalPadding,
+                startIconIndicatorVerticalPadding = startIconIndicatorVerticalPadding,
+                startIconToLabelHorizontalPadding = startIconToLabelHorizontalPadding,
+                startIconItemPadding = startIconItemPadding
+            )
+    )
+}
+
 private class TopIconOrIconOnlyMeasurePolicy(
     val hasLabel: Boolean,
-    val animationProgress: () -> Float,
+    val indicatorAnimationProgress: () -> Float,
     val indicatorHorizontalPadding: Dp,
     val indicatorVerticalPadding: Dp,
     val indicatorToLabelVerticalPadding: Dp,
@@ -409,7 +551,7 @@
         measurables: List<Measurable>,
         constraints: Constraints
     ): MeasureResult {
-        @Suppress("NAME_SHADOWING") val animationProgress = animationProgress()
+        @Suppress("NAME_SHADOWING") val indicatorAnimationProgress = indicatorAnimationProgress()
         val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
         // When measuring icon, account for the indicator in its constraints.
         val iconPlaceable =
@@ -424,7 +566,7 @@
         // Next, when measuring the indicator and ripple, still need to obey looseConstraints.
         val totalIndicatorWidth = iconPlaceable.width + (indicatorHorizontalPadding * 2).roundToPx()
         val indicatorHeight = iconPlaceable.height + (indicatorVerticalPadding * 2).roundToPx()
-        val animatedIndicatorWidth = (totalIndicatorWidth * animationProgress).roundToInt()
+        val animatedIndicatorWidth = (totalIndicatorWidth * indicatorAnimationProgress).roundToInt()
         val indicatorRipplePlaceable =
             measurables
                 .fastFirst { it.layoutId == IndicatorRippleLayoutIdTag }
@@ -492,7 +634,7 @@
 }
 
 private class StartIconMeasurePolicy(
-    val animationProgress: () -> Float,
+    val indicatorAnimationProgress: () -> Float,
     val indicatorHorizontalPadding: Dp,
     val indicatorVerticalPadding: Dp,
     val startIconToLabelHorizontalPadding: Dp,
@@ -501,7 +643,7 @@
         measurables: List<Measurable>,
         constraints: Constraints
     ): MeasureResult {
-        @Suppress("NAME_SHADOWING") val animationProgress = animationProgress()
+        @Suppress("NAME_SHADOWING") val indicatorAnimationProgress = indicatorAnimationProgress()
         val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
         // When measuring icon, account for the indicator in its constraints.
         val iconConstraints =
@@ -530,7 +672,7 @@
         val indicatorHeight =
             max(iconPlaceable.height, labelPlaceable.height) +
                 (indicatorVerticalPadding * 2).roundToPx()
-        val animatedIndicatorWidth = (totalIndicatorWidth * animationProgress).roundToInt()
+        val animatedIndicatorWidth = (totalIndicatorWidth * indicatorAnimationProgress).roundToInt()
         // When measuring the indicator and ripple, still need to obey looseConstraints.
         val indicatorRipplePlaceable =
             measurables
@@ -587,6 +729,130 @@
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+private class AnimatedMeasurePolicy(
+    val iconPosition: NavigationItemIconPosition,
+    val iconPositionProgress: () -> Float,
+    val indicatorAnimationProgress: () -> Float,
+    val topIconIndicatorHorizontalPadding: Dp,
+    val topIconIndicatorVerticalPadding: Dp,
+    val topIconIndicatorToLabelVerticalPadding: Dp,
+    val startIconIndicatorHorizontalPadding: Dp,
+    val startIconIndicatorVerticalPadding: Dp,
+    val startIconToLabelHorizontalPadding: Dp,
+    val startIconItemPadding: Dp,
+) : MeasurePolicy {
+    override fun MeasureScope.measure(
+        measurables: List<Measurable>,
+        constraints: Constraints
+    ): MeasureResult {
+        @Suppress("NAME_SHADOWING") val indicatorAnimationProgress = indicatorAnimationProgress()
+        val iconPositionProgressValue = iconPositionProgress()
+        val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
+
+        val iconPlaceable =
+            measurables.fastFirst { it.layoutId == IconLayoutIdTag }.measure(looseConstraints)
+
+        val labelPlaceableTopIcon =
+            measurables
+                .fastFirst { it.layoutId == AnimatedLabelTopIconLayoutIdTag }
+                .measure(looseConstraints)
+        val labelPlaceableStartIcon =
+            measurables
+                .fastFirst { it.layoutId == AnimatedLabelStartIconLayoutIdTag }
+                .measure(looseConstraints)
+
+        val topIconIndicatorWidth =
+            iconPlaceable.width + (topIconIndicatorHorizontalPadding * 2).roundToPx()
+        val topIconIndicatorHeight =
+            iconPlaceable.height + (topIconIndicatorVerticalPadding * 2).roundToPx()
+
+        val startIconIndicatorWidth =
+            iconPlaceable.width +
+                labelPlaceableStartIcon.width +
+                (startIconToLabelHorizontalPadding + startIconIndicatorHorizontalPadding * 2)
+                    .roundToPx()
+        val startIconIndicatorHeight =
+            max(iconPlaceable.height, labelPlaceableStartIcon.height) +
+                (startIconIndicatorVerticalPadding * 2).roundToPx()
+
+        val indicatorWidthProgress =
+            lerp(topIconIndicatorWidth, startIconIndicatorWidth, iconPositionProgressValue)
+        val animatedIndicatorWidth =
+            (indicatorWidthProgress * indicatorAnimationProgress).roundToInt()
+        val indicatorHeightProgress =
+            lerp(topIconIndicatorHeight, startIconIndicatorHeight, iconPositionProgressValue)
+
+        val indicatorRipplePlaceable =
+            measurables
+                .fastFirst { it.layoutId == IndicatorRippleLayoutIdTag }
+                .measure(
+                    looseConstraints.constrain(
+                        Constraints.fixed(
+                            width = indicatorWidthProgress,
+                            height = indicatorHeightProgress
+                        )
+                    )
+                )
+        val indicatorPlaceable =
+            measurables
+                .fastFirst { it.layoutId == IndicatorLayoutIdTag }
+                .measure(
+                    looseConstraints.constrain(
+                        Constraints.fixed(
+                            width = animatedIndicatorWidth,
+                            height = indicatorHeightProgress
+                        )
+                    )
+                )
+
+        return placeAnimatedLabelAndIcon(
+            iconPosition = iconPosition,
+            iconPositionProgress = iconPositionProgress,
+            labelPlaceableTopIcon = labelPlaceableTopIcon,
+            labelPlaceableStartIcon = labelPlaceableStartIcon,
+            iconPlaceable = iconPlaceable,
+            indicatorRipplePlaceable = indicatorRipplePlaceable,
+            indicatorPlaceable = indicatorPlaceable,
+            topIconIndicatorWidth = topIconIndicatorWidth,
+            constraints = looseConstraints,
+            topIconIndicatorToLabelVerticalPadding = topIconIndicatorToLabelVerticalPadding,
+            topIconIndicatorVerticalPadding = topIconIndicatorVerticalPadding,
+            startIconIndicatorHorizontalPadding = startIconIndicatorHorizontalPadding,
+            startIconIndicatorVerticalPadding = startIconIndicatorVerticalPadding,
+            startIconToLabelHorizontalPadding = startIconToLabelHorizontalPadding,
+            startIconItemPadding = startIconItemPadding
+        )
+    }
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurables: List<IntrinsicMeasurable>,
+        height: Int
+    ): Int {
+        val iconWidth =
+            measurables.fastFirst { it.layoutId == IconLayoutIdTag }.maxIntrinsicWidth(height)
+        if (iconPosition == NavigationItemIconPosition.Top) {
+            val labelWidth =
+                measurables
+                    .fastFirst { it.layoutId == AnimatedLabelTopIconLayoutIdTag }
+                    .maxIntrinsicWidth(height)
+            val paddings = (topIconIndicatorHorizontalPadding * 2).roundToPx()
+
+            return maxOf(labelWidth, (iconWidth + paddings))
+        } else {
+            val labelWidth =
+                measurables
+                    .fastFirst { it.layoutId == AnimatedLabelStartIconLayoutIdTag }
+                    .maxIntrinsicWidth(height)
+            val paddings =
+                (startIconIndicatorHorizontalPadding * 2 + startIconToLabelHorizontalPadding)
+                    .roundToPx()
+
+            return iconWidth + labelWidth + paddings
+        }
+    }
+}
+
 /**
  * Places the provided [Placeable]s in the correct position.
  *
@@ -714,15 +980,176 @@
     }
 }
 
-/*@VisibleForTesting*/
-internal val NavigationItemMinWidth = NavigationRailItemWidth
-/*@VisibleForTesting*/
-internal val NavigationItemMinHeight = NavigationRailItemHeight
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+private fun MeasureScope.placeAnimatedLabelAndIcon(
+    iconPosition: NavigationItemIconPosition,
+    iconPositionProgress: () -> Float,
+    labelPlaceableTopIcon: Placeable,
+    labelPlaceableStartIcon: Placeable,
+    iconPlaceable: Placeable,
+    indicatorRipplePlaceable: Placeable,
+    indicatorPlaceable: Placeable,
+    topIconIndicatorWidth: Int,
+    constraints: Constraints,
+    topIconIndicatorToLabelVerticalPadding: Dp,
+    topIconIndicatorVerticalPadding: Dp,
+    startIconIndicatorHorizontalPadding: Dp,
+    startIconIndicatorVerticalPadding: Dp,
+    startIconToLabelHorizontalPadding: Dp,
+    startIconItemPadding: Dp,
+): MeasureResult {
+    @Suppress("NAME_SHADOWING") val iconPositionProgress = iconPositionProgress()
+    val isIconPositionTop = iconPosition == NavigationItemIconPosition.Top
+    val widthTopIcon =
+        constraints.constrainWidth(maxOf(labelPlaceableTopIcon.width, topIconIndicatorWidth))
+    val widthStartIcon = constraints.constrainWidth(indicatorRipplePlaceable.width)
+    val width = widthTopIcon + (widthStartIcon - widthTopIcon) * iconPositionProgress
+
+    val heightTopIcon =
+        constraints.constrainHeight(
+            (indicatorRipplePlaceable.height +
+                    topIconIndicatorToLabelVerticalPadding.toPx() +
+                    labelPlaceableTopIcon.height)
+                .roundToInt()
+        )
+    val heightStartIcon = constraints.constrainHeight(indicatorRipplePlaceable.height)
+    val height = lerp(heightTopIcon, heightStartIcon, iconPositionProgress)
+
+    val topIconHorizontalOffset = startIconItemPadding.roundToPx() * iconPositionProgress
+
+    val indicatorXTopIcon =
+        ((widthTopIcon - indicatorPlaceable.width) / 2 + topIconHorizontalOffset).roundToInt()
+    val indicatorXStartIcon = (widthStartIcon - indicatorPlaceable.width) / 2
+    val indicatorX = lerp(indicatorXTopIcon, indicatorXStartIcon, iconPositionProgress)
+
+    val rippleXTopIcon = (width - indicatorRipplePlaceable.width) / 2 + topIconHorizontalOffset
+    val rippleXStartIcon = (width - indicatorRipplePlaceable.width) / 2
+    val rippleX = lerp(rippleXTopIcon, rippleXStartIcon, iconPositionProgress)
+
+    val iconXTopIcon = (widthTopIcon - iconPlaceable.width) / 2
+    val iconXStartIcon = startIconIndicatorHorizontalPadding.roundToPx()
+
+    val iconYTopIcon = topIconIndicatorVerticalPadding.roundToPx()
+    val iconYStartIcon = startIconIndicatorVerticalPadding.roundToPx()
+
+    val iconX = lerp(iconXTopIcon, iconXStartIcon, iconPositionProgress)
+    val iconY = lerp(iconYTopIcon, iconYStartIcon, iconPositionProgress)
+
+    val labelXTopIcon = (widthTopIcon - labelPlaceableTopIcon.width) / 2
+    val labelYTopIcon =
+        iconY +
+            iconPlaceable.height +
+            (topIconIndicatorToLabelVerticalPadding + topIconIndicatorToLabelVerticalPadding)
+                .roundToPx()
+
+    val labelXStartIconHorizontalOffset =
+        if (isIconPositionTop && iconPositionProgress > 0f) {
+            0f
+        } else {
+            startIconItemPadding.roundToPx() * (1f - iconPositionProgress)
+        }
+    val labelXStartIcon =
+        iconX + iconPlaceable.width + startIconToLabelHorizontalPadding.roundToPx() -
+            labelXStartIconHorizontalOffset
+    val labelYStartIcon = (height - labelPlaceableStartIcon.height) / 2
+
+    return layout(width.roundToInt(), height) {
+        indicatorPlaceable.placeRelative(indicatorX, 0)
+        iconPlaceable.placeRelative(iconX, iconY)
+        labelPlaceableTopIcon.placeRelative(labelXTopIcon, labelYTopIcon)
+        labelPlaceableStartIcon.placeRelative(labelXStartIcon.roundToInt(), labelYStartIcon)
+        indicatorRipplePlaceable.placeRelative(rippleX.roundToInt(), 0)
+    }
+}
+
+@Composable
+private fun StyledIcon(
+    selected: Boolean,
+    icon: @Composable () -> Unit,
+    colors: NavigationItemColors,
+    enabled: Boolean,
+    badge: (@Composable () -> Unit)?,
+) {
+    val iconColor = colors.iconColor(selected = selected, enabled = enabled)
+    val styledIcon: @Composable () -> Unit = {
+        CompositionLocalProvider(LocalContentColor provides iconColor, content = icon)
+    }
+
+    if (badge != null) {
+        BadgedBox(badge = { badge() }) { styledIcon() }
+    } else {
+        styledIcon()
+    }
+}
+
+@Composable
+private fun StyledLabel(
+    selected: Boolean,
+    labelTextStyle: TextStyle,
+    colors: NavigationItemColors,
+    enabled: Boolean,
+    content: @Composable () -> Unit,
+) {
+    val textColor = colors.textColor(selected = selected, enabled = enabled)
+    ProvideContentColorTextStyle(
+        contentColor = textColor,
+        textStyle = labelTextStyle,
+        content = content
+    )
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun animateIndicatorProgressAsState(selected: Boolean) =
+    animateFloatAsState(
+        targetValue = if (selected) 1f else 0f,
+        // TODO Load the motionScheme tokens from the component tokens file
+        animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value()
+    )
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun animateLabelAlphaProgressAsState(isTargetValue: Boolean) =
+    animateFloatAsState(
+        targetValue = if (isTargetValue) 1f else 0f,
+        // TODO Load the motionScheme tokens from the component tokens file
+        animationSpec = MotionSchemeKeyTokens.DefaultEffects.value(),
+        visibilityThreshold =
+            if (isTargetValue) Spring.DefaultDisplacementThreshold
+            else LabelAnimationVisibilityThreshold
+    )
+
+@Composable
+private fun IndicatorRipple(interactionSource: InteractionSource, indicatorShape: Shape) {
+    Box(
+        Modifier.layoutId(IndicatorRippleLayoutIdTag)
+            .clip(indicatorShape)
+            .indication(interactionSource, ripple())
+    )
+}
+
+@Composable
+private fun Indicator(
+    indicatorColor: Color,
+    indicatorShape: Shape,
+    indicatorAnimationProgress: () -> Float,
+) {
+    Box(
+        Modifier.layoutId(IndicatorLayoutIdTag)
+            .graphicsLayer { alpha = indicatorAnimationProgress() }
+            .background(
+                color = indicatorColor,
+                shape = indicatorShape,
+            )
+    )
+}
 
 private const val IndicatorRippleLayoutIdTag: String = "indicatorRipple"
 private const val IndicatorLayoutIdTag: String = "indicator"
 private const val IconLayoutIdTag: String = "icon"
 private const val LabelLayoutIdTag: String = "label"
-private const val ItemAnimationDurationMillis: Int = 100
+private const val AnimatedLabelTopIconLayoutIdTag: String = "animatedLabelTopIcon"
+private const val AnimatedLabelStartIconLayoutIdTag: String = "animatedLabelStartIcon"
+private const val LabelAnimationVisibilityThreshold: Float = 0.35f
 
 private val IndicatorVerticalOffset: Dp = 12.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
index 10e79f7..b518c36 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.indication
 import androidx.compose.foundation.interaction.Interaction
@@ -42,6 +41,7 @@
 import androidx.compose.material3.internal.MappedInteractionSource
 import androidx.compose.material3.internal.ProvideContentColorTextStyle
 import androidx.compose.material3.internal.systemBarsForVisualComponents
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.NavigationRailTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -168,6 +168,7 @@
  *   preview the item in different states. Note that if `null` is provided, interactions will still
  *   happen internally.
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun NavigationRailItem(
     selected: Boolean,
@@ -187,7 +188,8 @@
             val iconColor by
                 animateColorAsState(
                     targetValue = colors.iconColor(selected = selected, enabled = enabled),
-                    animationSpec = tween(ItemAnimationDurationMillis)
+                    // TODO Load the motionScheme tokens from the component tokens file
+                    animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
                 )
             // If there's a label, don't have a11y services repeat the icon description.
             val clearSemantics = label != null && (alwaysShowLabel || selected)
@@ -203,7 +205,8 @@
                 val textColor by
                     animateColorAsState(
                         targetValue = colors.textColor(selected = selected, enabled = enabled),
-                        animationSpec = tween(ItemAnimationDurationMillis)
+                        // TODO Load the motionScheme tokens from the component tokens file
+                        animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
                     )
                 ProvideContentColorTextStyle(
                     contentColor = textColor,
@@ -228,10 +231,17 @@
         contentAlignment = Alignment.Center,
         propagateMinConstraints = true,
     ) {
-        val animationProgress: State<Float> =
+        val alphaAnimationProgress: State<Float> =
             animateFloatAsState(
                 targetValue = if (selected) 1f else 0f,
-                animationSpec = tween(ItemAnimationDurationMillis)
+                // TODO Load the motionScheme tokens from the component tokens file
+                animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
+            )
+        val sizeAnimationProgress: State<Float> =
+            animateFloatAsState(
+                targetValue = if (selected) 1f else 0f,
+                // TODO Load the motionScheme tokens from the component tokens file
+                animationSpec = MotionSchemeKeyTokens.FastSpatial.value()
             )
 
         // The entire item is selectable, but only the indicator pill shows the ripple. To achieve
@@ -269,7 +279,7 @@
             @Composable {
                 Box(
                     Modifier.layoutId(IndicatorLayoutIdTag)
-                        .graphicsLayer { alpha = animationProgress.value }
+                        .graphicsLayer { alpha = alphaAnimationProgress.value }
                         .background(color = colors.indicatorColor, shape = indicatorShape)
                 )
             }
@@ -280,7 +290,8 @@
             icon = styledIcon,
             label = styledLabel,
             alwaysShowLabel = alwaysShowLabel,
-            animationProgress = { animationProgress.value },
+            alphaAnimationProgress = { alphaAnimationProgress.value },
+            sizeAnimationProgress = { sizeAnimationProgress.value },
         )
     }
 }
@@ -500,8 +511,11 @@
  * @param label text label for this item
  * @param alwaysShowLabel whether to always show the label for this item. If false, the label will
  *   only be shown when this item is selected.
- * @param animationProgress progress of the animation, where 0 represents the unselected state of
- *   this item and 1 represents the selected state. This value controls other values such as
+ * @param alphaAnimationProgress progress of the animation, where 0 represents the unselected state
+ *   of this item and 1 represents the selected state. This value controls the indicator's color
+ *   alpha.
+ * @param sizeAnimationProgress progress of the animation, where 0 represents the unselected state
+ *   of this item and 1 represents the selected state. This value controls other values such as
  *   indicator size, icon and label positions, etc.
  */
 @Composable
@@ -511,7 +525,8 @@
     icon: @Composable () -> Unit,
     label: @Composable (() -> Unit)?,
     alwaysShowLabel: Boolean,
-    animationProgress: () -> Float,
+    alphaAnimationProgress: () -> Float,
+    sizeAnimationProgress: () -> Float,
 ) {
     Layout(
         modifier = Modifier.badgeBounds(),
@@ -524,7 +539,7 @@
             if (label != null) {
                 Box(
                     Modifier.layoutId(LabelLayoutIdTag).graphicsLayer {
-                        alpha = if (alwaysShowLabel) 1f else animationProgress()
+                        alpha = if (alwaysShowLabel) 1f else alphaAnimationProgress()
                     }
                 ) {
                     label()
@@ -532,7 +547,9 @@
             }
         }
     ) { measurables, constraints ->
-        @Suppress("NAME_SHADOWING") val animationProgress = animationProgress()
+        @Suppress("NAME_SHADOWING")
+        // Ensure that the progress is >= 0. It may be negative on bouncy springs, for example.
+        val animationProgress = sizeAnimationProgress().coerceAtLeast(0f)
         val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
         val iconPlaceable =
             measurables.fastFirst { it.layoutId == IconLayoutIdTag }.measure(looseConstraints)
@@ -727,8 +744,6 @@
  */
 private val NavigationRailHeaderPadding: Dp = 8.dp
 
-private const val ItemAnimationDurationMillis: Int = 150
-
 /*@VisibleForTesting*/
 /** Width of an individual [NavigationRailItem]. */
 internal val NavigationRailItemWidth: Dp = NavigationRailTokens.ContainerWidth
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OpticalCentering.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OpticalCentering.kt
new file mode 100644
index 0000000..a38ff4d
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OpticalCentering.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.unit.offset
+
+/**
+ * [Modifier] that centers the content depending on the shape provided. It will increase or decrease
+ * the start padding to better center the content depending on the corner radii of the provided
+ * shape. This is meant to be used with asymmetric shapes, the modifier will not do anything to the
+ * content if the shape provided is symmetric.
+ *
+ * @param shape the [CornerBasedShape] that the content should be adjusted to so that the content is
+ *   more centered within the shape.
+ * @param basePadding the initial content padding that would have been applied to the content before
+ *   correcting for the corner radii of the shape.
+ */
+@ExperimentalMaterial3ExpressiveApi
+fun Modifier.opticalCentering(shape: CornerBasedShape, basePadding: PaddingValues) =
+    this.layout { measurable, constraints ->
+        val start = basePadding.calculateStartPadding(layoutDirection)
+        val end = basePadding.calculateEndPadding(layoutDirection)
+        val top = basePadding.calculateTopPadding()
+        val bottom = basePadding.calculateBottomPadding()
+
+        val horizontalPadding = start.roundToPx() + end.roundToPx()
+        val verticalPadding = top.roundToPx() + bottom.roundToPx()
+
+        val placeable = measurable.measure(constraints.offset(-horizontalPadding, -verticalPadding))
+
+        val width = constraints.constrainWidth(placeable.width + horizontalPadding)
+        val height = constraints.constrainHeight(placeable.height + verticalPadding)
+
+        val size = Size(width = width.toFloat(), height = height.toFloat())
+        val density = this@layout
+        layout(width, height) {
+            val topStart = shape.topStart.toPx(shapeSize = size, density = density)
+            val topEnd = shape.topEnd.toPx(shapeSize = size, density = density)
+            val bottomStart = shape.bottomStart.toPx(shapeSize = size, density = density)
+            val bottomEnd = shape.bottomEnd.toPx(shapeSize = size, density = density)
+            val avgStart = (topStart + bottomStart) / 2
+            val avgEnd = (topEnd + bottomEnd) / 2
+            val startPadding = start.roundToPx() + OpticalCenteringCoefficient * (avgStart - avgEnd)
+
+            placeable.place(startPadding.toInt(), top.roundToPx())
+        }
+    }
+
+private const val OpticalCenteringCoefficient = 0.11f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
index 12ce5d5..cb30f48 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material3
 
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.collectIsFocusedAsState
@@ -27,10 +28,20 @@
 import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.KeyboardActionHandler
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
 import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.material3.internal.AboveLabelBottomPadding
+import androidx.compose.material3.internal.AboveLabelHorizontalPadding
 import androidx.compose.material3.internal.ContainerId
 import androidx.compose.material3.internal.HorizontalIconPadding
 import androidx.compose.material3.internal.IconDefaultSizeModifier
@@ -47,11 +58,11 @@
 import androidx.compose.material3.internal.SupportingId
 import androidx.compose.material3.internal.TextFieldId
 import androidx.compose.material3.internal.TrailingId
-import androidx.compose.material3.internal.ZeroConstraints
 import androidx.compose.material3.internal.defaultErrorSemantics
 import androidx.compose.material3.internal.getString
 import androidx.compose.material3.internal.heightOrZero
 import androidx.compose.material3.internal.layoutId
+import androidx.compose.material3.internal.subtractConstraintSafely
 import androidx.compose.material3.internal.widthOrZero
 import androidx.compose.material3.tokens.TypeScaleTokens
 import androidx.compose.runtime.Composable
@@ -78,13 +89,14 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.coerceAtLeast
 import androidx.compose.ui.unit.dp
@@ -108,9 +120,194 @@
  * ![Outlined text field
  * image](https://developer.android.com/images/reference/androidx/compose/material3/outlined-text-field.png)
  *
+ * This overload of [OutlinedTextField] uses [TextFieldState] to keep track of its text content and
+ * position of the cursor or selection.
+ *
  * See example usage:
  *
  * @sample androidx.compose.material3.samples.SimpleOutlinedTextFieldSample
+ * @sample androidx.compose.material3.samples.OutlinedTextFieldWithInitialValueAndSelection
+ * @param state [TextFieldState] object that holds the internal editing state of the text field.
+ * @param modifier the [Modifier] to be applied to this text field.
+ * @param enabled controls the enabled state of this text field. When `false`, this component will
+ *   not respond to user input, and it will appear visually disabled and disabled to accessibility
+ *   services.
+ * @param readOnly controls the editable state of the text field. When `true`, the text field cannot
+ *   be modified. However, a user can focus it and copy text from it. Read-only text fields are
+ *   usually used to display pre-filled forms that a user cannot edit.
+ * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
+ * @param labelPosition the position of the label. See [TextFieldLabelPosition].
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ *   [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
+ * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+ *   default text style uses [Typography.bodyLarge].
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+ *   container.
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ *   container.
+ * @param prefix the optional prefix to be displayed before the input text in the text field.
+ * @param suffix the optional suffix to be displayed after the input text in the text field.
+ * @param supportingText the optional supporting text to be displayed below the text field.
+ * @param isError indicates if the text field's current value is in error. When `true`, the
+ *   components of the text field will be displayed in an error color, and an error will be
+ *   announced to accessibility services.
+ * @param inputTransformation optional [InputTransformation] that will be used to transform changes
+ *   to the [TextFieldState] made by the user. The transformation will be applied to changes made by
+ *   hardware and software keyboard events, pasting or dropping text, accessibility services, and
+ *   tests. The transformation will _not_ be applied when changing the [state] programmatically, or
+ *   when the transformation is changed. If the transformation is changed on an existing text field,
+ *   it will be applied to the next user edit. The transformation will not immediately affect the
+ *   current [state].
+ * @param outputTransformation optional [OutputTransformation] that transforms how the contents of
+ *   the text field are presented.
+ * @param keyboardOptions software keyboard options that contains configuration such as
+ *   [KeyboardType] and [ImeAction].
+ * @param onKeyboardAction called when the user presses the action button in the input method editor
+ *   (IME), or by pressing the enter key on a hardware keyboard. By default this parameter is null,
+ *   and would execute the default behavior for a received IME Action e.g., [ImeAction.Done] would
+ *   close the keyboard, [ImeAction.Next] would switch the focus to the next focusable item on the
+ *   screen.
+ * @param lineLimits whether the text field should be [SingleLine], scroll horizontally, and ignore
+ *   newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all newline
+ *   characters ('\n') within the text will be replaced with regular whitespace (' ').
+ * @param onTextLayout Callback that is executed when the text layout becomes queryable. The
+ *   callback receives a function that returns a [TextLayoutResult] if the layout can be calculated,
+ *   or null if it cannot. The function reads the layout result from a snapshot state object, and
+ *   will invalidate its caller when the layout result changes. A [TextLayoutResult] object contains
+ *   paragraph information, size of the text, baselines and other details. [Density] scope is the
+ *   one that was used while creating the given text layout.
+ * @param scrollState scroll state that manages either horizontal or vertical scroll of the text
+ *   field. If [lineLimits] is [SingleLine], this text field is treated as single line with
+ *   horizontal scroll behavior. Otherwise, the text field becomes vertically scrollable.
+ * @param shape defines the shape of this text field's border.
+ * @param colors [TextFieldColors] that will be used to resolve the colors used for this text field
+ *   in different states. See [OutlinedTextFieldDefaults.colors].
+ * @param contentPadding the padding applied to the inner text field that separates it from the
+ *   surrounding elements of the text field. Note that the padding values may not be respected if
+ *   they are incompatible with the text field's size constraints or layout. See
+ *   [OutlinedTextFieldDefaults.contentPadding].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this text field. You can use this to change the text field's
+ *   appearance or preview the text field in different states. Note that if `null` is provided,
+ *   interactions will still happen internally.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun OutlinedTextField(
+    state: TextFieldState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    textStyle: TextStyle = LocalTextStyle.current,
+    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Default(),
+    label: @Composable (TextFieldLabelScope.() -> Unit)? = null,
+    placeholder: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    prefix: @Composable (() -> Unit)? = null,
+    suffix: @Composable (() -> Unit)? = null,
+    supportingText: @Composable (() -> Unit)? = null,
+    isError: Boolean = false,
+    inputTransformation: InputTransformation? = null,
+    outputTransformation: OutputTransformation? = null,
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+    onKeyboardAction: KeyboardActionHandler? = null,
+    lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+    onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
+    scrollState: ScrollState = rememberScrollState(),
+    shape: Shape = OutlinedTextFieldDefaults.shape,
+    colors: TextFieldColors = OutlinedTextFieldDefaults.colors(),
+    contentPadding: PaddingValues = OutlinedTextFieldDefaults.contentPadding(),
+    interactionSource: MutableInteractionSource? = null,
+) {
+    @Suppress("NAME_SHADOWING")
+    val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+    // If color is not provided via the text style, use content color as a default
+    val textColor =
+        textStyle.color.takeOrElse {
+            val focused = interactionSource.collectIsFocusedAsState().value
+            colors.textColor(enabled, isError, focused)
+        }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+    val density = LocalDensity.current
+
+    CompositionLocalProvider(LocalTextSelectionColors provides colors.textSelectionColors) {
+        BasicTextField(
+            state = state,
+            modifier =
+                modifier
+                    .then(
+                        if (label != null && labelPosition != TextFieldLabelPosition.Above) {
+                            Modifier
+                                // Merge semantics at the beginning of the modifier chain to ensure
+                                // padding is considered part of the text field.
+                                .semantics(mergeDescendants = true) {}
+                                .padding(top = with(density) { OutlinedTextFieldTopPadding.toDp() })
+                        } else {
+                            Modifier
+                        }
+                    )
+                    .defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
+                    .defaultMinSize(
+                        minWidth = OutlinedTextFieldDefaults.MinWidth,
+                        minHeight = OutlinedTextFieldDefaults.MinHeight
+                    ),
+            enabled = enabled,
+            readOnly = readOnly,
+            textStyle = mergedTextStyle,
+            cursorBrush = SolidColor(colors.cursorColor(isError)),
+            keyboardOptions = keyboardOptions,
+            onKeyboardAction = onKeyboardAction,
+            lineLimits = lineLimits,
+            onTextLayout = onTextLayout,
+            interactionSource = interactionSource,
+            inputTransformation = inputTransformation,
+            outputTransformation = outputTransformation,
+            scrollState = scrollState,
+            decorator =
+                OutlinedTextFieldDefaults.decorator(
+                    state = state,
+                    enabled = enabled,
+                    lineLimits = lineLimits,
+                    outputTransformation = outputTransformation,
+                    interactionSource = interactionSource,
+                    labelPosition = labelPosition,
+                    label = label,
+                    placeholder = placeholder,
+                    leadingIcon = leadingIcon,
+                    trailingIcon = trailingIcon,
+                    prefix = prefix,
+                    suffix = suffix,
+                    supportingText = supportingText,
+                    isError = isError,
+                    colors = colors,
+                    contentPadding = contentPadding,
+                    container = {
+                        OutlinedTextFieldDefaults.Container(
+                            enabled = enabled,
+                            isError = isError,
+                            interactionSource = interactionSource,
+                            colors = colors,
+                            shape = shape,
+                        )
+                    }
+                )
+        )
+    }
+}
+
+/**
+ * <a href="https://m3.material.io/components/text-fields/overview" class="external"
+ * target="_blank">Material Design outlined text field</a>.
+ *
+ * Text fields allow users to enter text into a UI. They typically appear in forms and dialogs.
+ * Outlined text fields have less visual emphasis than filled text fields. When they appear in
+ * places like forms, where many text fields are placed together, their reduced emphasis helps
+ * simplify the layout.
+ *
+ * ![Outlined text field
+ * image](https://developer.android.com/images/reference/androidx/compose/material3/outlined-text-field.png)
  *
  * If apart from input text change you also want to observe the cursor location, selection range, or
  * IME composition use the OutlinedTextField overload with the [TextFieldValue] parameter instead.
@@ -126,9 +323,8 @@
  *   be modified. However, a user can focus it and copy text from it. Read-only text fields are
  *   usually used to display pre-filled forms that a user cannot edit.
  * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
- * @param label the optional label to be displayed inside the text field container. The default text
- *   style for internal [Text] is [Typography.bodySmall] when the text field is in focus and
- *   [Typography.bodyLarge] when the text field is not in focus
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ *   [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
  * @param placeholder the optional placeholder to be displayed when the text field is in focus and
  *   the input text is empty. The default text style for internal [Text] is [Typography.bodyLarge]
  * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
@@ -282,10 +478,6 @@
  * ![Outlined text field
  * image](https://developer.android.com/images/reference/androidx/compose/material3/outlined-text-field.png)
  *
- * See example usage:
- *
- * @sample androidx.compose.material3.samples.OutlinedTextFieldSample
- *
  * This overload provides access to the input text, cursor position and selection range and IME
  * composition. If you only want to observe an input text change, use the OutlinedTextField overload
  * with the [String] parameter instead.
@@ -301,9 +493,8 @@
  *   be modified. However, a user can focus it and copy text from it. Read-only text fields are
  *   usually used to display pre-filled forms that a user cannot edit.
  * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
- * @param label the optional label to be displayed inside the text field container. The default text
- *   style for internal [Text] is [Typography.bodySmall] when the text field is in focus and
- *   [Typography.bodyLarge] when the text field is not in focus
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ *   [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
  * @param placeholder the optional placeholder to be displayed when the text field is in focus and
  *   the input text is empty. The default text style for internal [Text] is [Typography.bodyLarge]
  * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
@@ -461,18 +652,20 @@
     prefix: @Composable (() -> Unit)?,
     suffix: @Composable (() -> Unit)?,
     singleLine: Boolean,
-    animationProgress: Float,
+    labelPosition: TextFieldLabelPosition,
+    labelProgress: Float,
     onLabelMeasured: (Size) -> Unit,
     container: @Composable () -> Unit,
     supporting: @Composable (() -> Unit)?,
     paddingValues: PaddingValues
 ) {
     val measurePolicy =
-        remember(onLabelMeasured, singleLine, animationProgress, paddingValues) {
+        remember(onLabelMeasured, singleLine, labelPosition, labelProgress, paddingValues) {
             OutlinedTextFieldMeasurePolicy(
                 onLabelMeasured,
                 singleLine,
-                animationProgress,
+                labelPosition,
+                labelProgress,
                 paddingValues
             )
         }
@@ -555,18 +748,25 @@
                 textField()
             }
 
+            val labelPadding =
+                if (labelPosition == TextFieldLabelPosition.Above) {
+                    Modifier.padding(
+                        start = AboveLabelHorizontalPadding,
+                        end = AboveLabelHorizontalPadding,
+                        bottom = AboveLabelBottomPadding,
+                    )
+                } else {
+                    Modifier
+                }
+
             if (label != null) {
                 Box(
                     Modifier.heightIn(
-                            min =
-                                lerp(
-                                    MinTextLineHeight,
-                                    MinFocusedLabelLineHeight,
-                                    animationProgress
-                                )
+                            min = lerp(MinTextLineHeight, MinFocusedLabelLineHeight, labelProgress)
                         )
                         .wrapContentHeight()
                         .layoutId(LabelId)
+                        .then(labelPadding)
                 ) {
                     label()
                 }
@@ -590,7 +790,8 @@
 private class OutlinedTextFieldMeasurePolicy(
     private val onLabelMeasured: (Size) -> Unit,
     private val singleLine: Boolean,
-    private val animationProgress: Float,
+    private val labelPosition: TextFieldLabelPosition,
+    private val labelProgress: Float,
     private val paddingValues: PaddingValues
 ) : MeasurePolicy {
     override fun MeasureScope.measure(
@@ -606,53 +807,64 @@
         // measure leading icon
         val leadingPlaceable =
             measurables.fastFirstOrNull { it.layoutId == LeadingId }?.measure(relaxedConstraints)
-        occupiedSpaceHorizontally += widthOrZero(leadingPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(leadingPlaceable))
+        occupiedSpaceHorizontally += leadingPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, leadingPlaceable.heightOrZero)
 
         // measure trailing icon
         val trailingPlaceable =
             measurables
                 .fastFirstOrNull { it.layoutId == TrailingId }
                 ?.measure(relaxedConstraints.offset(horizontal = -occupiedSpaceHorizontally))
-        occupiedSpaceHorizontally += widthOrZero(trailingPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(trailingPlaceable))
+        occupiedSpaceHorizontally += trailingPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, trailingPlaceable.heightOrZero)
 
         // measure prefix
         val prefixPlaceable =
             measurables
                 .fastFirstOrNull { it.layoutId == PrefixId }
                 ?.measure(relaxedConstraints.offset(horizontal = -occupiedSpaceHorizontally))
-        occupiedSpaceHorizontally += widthOrZero(prefixPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(prefixPlaceable))
+        occupiedSpaceHorizontally += prefixPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, prefixPlaceable.heightOrZero)
 
         // measure suffix
         val suffixPlaceable =
             measurables
                 .fastFirstOrNull { it.layoutId == SuffixId }
                 ?.measure(relaxedConstraints.offset(horizontal = -occupiedSpaceHorizontally))
-        occupiedSpaceHorizontally += widthOrZero(suffixPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(suffixPlaceable))
+        occupiedSpaceHorizontally += suffixPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, suffixPlaceable.heightOrZero)
 
         // measure label
-        val labelHorizontalPaddingOffset =
-            paddingValues.calculateLeftPadding(layoutDirection).roundToPx() +
-                paddingValues.calculateRightPadding(layoutDirection).roundToPx()
-        val labelConstraints =
-            relaxedConstraints.offset(
-                horizontal =
-                    lerp(
-                        -occupiedSpaceHorizontally -
-                            labelHorizontalPaddingOffset, // label in middle
-                        -labelHorizontalPaddingOffset, // label at top
-                        animationProgress,
-                    ),
-                vertical = -bottomPadding
-            )
-        val labelPlaceable =
-            measurables.fastFirstOrNull { it.layoutId == LabelId }?.measure(labelConstraints)
-        val labelSize =
-            labelPlaceable?.let { Size(it.width.toFloat(), it.height.toFloat()) } ?: Size.Zero
-        onLabelMeasured(labelSize)
+        val isLabelAbove = labelPosition == TextFieldLabelPosition.Above
+        val labelMeasurable = measurables.fastFirstOrNull { it.layoutId == LabelId }
+        var labelPlaceable: Placeable? = null
+        val labelIntrinsicHeight: Int
+        if (!isLabelAbove) {
+            // if label is not Above, we can measure it like normal
+            val totalHorizontalPadding =
+                paddingValues.calculateLeftPadding(layoutDirection).roundToPx() +
+                    paddingValues.calculateRightPadding(layoutDirection).roundToPx()
+            val labelHorizontalConstraintOffset =
+                lerp(
+                    occupiedSpaceHorizontally + totalHorizontalPadding, // label in middle
+                    totalHorizontalPadding, // label in outline
+                    labelProgress,
+                )
+            val labelConstraints =
+                relaxedConstraints.offset(
+                    horizontal = -labelHorizontalConstraintOffset,
+                    vertical = -bottomPadding
+                )
+            labelPlaceable = labelMeasurable?.measure(labelConstraints)
+            val labelSize =
+                labelPlaceable?.let { Size(it.width.toFloat(), it.height.toFloat()) } ?: Size.Zero
+            onLabelMeasured(labelSize)
+            labelIntrinsicHeight = 0
+        } else {
+            // if label is Above, it must be measured after other elements, but we
+            // reserve space for it using its intrinsic height as a heuristic
+            labelIntrinsicHeight = labelMeasurable?.minIntrinsicHeight(constraints.minWidth) ?: 0
+        }
 
         // supporting text must be measured after other elements, but we
         // reserve space for it using its intrinsic height as a heuristic
@@ -662,12 +874,23 @@
 
         // measure text field
         val topPadding =
-            max(heightOrZero(labelPlaceable) / 2, paddingValues.calculateTopPadding().roundToPx())
+            if (isLabelAbove) {
+                paddingValues.calculateTopPadding().roundToPx()
+            } else {
+                max(
+                    labelPlaceable.heightOrZero / 2,
+                    paddingValues.calculateTopPadding().roundToPx()
+                )
+            }
         val textConstraints =
             constraints
                 .offset(
                     horizontal = -occupiedSpaceHorizontally,
-                    vertical = -bottomPadding - topPadding - supportingIntrinsicHeight
+                    vertical =
+                        -bottomPadding -
+                            topPadding -
+                            labelIntrinsicHeight -
+                            supportingIntrinsicHeight
                 )
                 .copy(minHeight = 0)
         val textFieldPlaceable =
@@ -683,50 +906,56 @@
         occupiedSpaceVertically =
             max(
                 occupiedSpaceVertically,
-                max(heightOrZero(textFieldPlaceable), heightOrZero(placeholderPlaceable)) +
+                max(textFieldPlaceable.heightOrZero, placeholderPlaceable.heightOrZero) +
                     topPadding +
                     bottomPadding
             )
 
         val width =
             calculateWidth(
-                leadingPlaceableWidth = widthOrZero(leadingPlaceable),
-                trailingPlaceableWidth = widthOrZero(trailingPlaceable),
-                prefixPlaceableWidth = widthOrZero(prefixPlaceable),
-                suffixPlaceableWidth = widthOrZero(suffixPlaceable),
+                leadingPlaceableWidth = leadingPlaceable.widthOrZero,
+                trailingPlaceableWidth = trailingPlaceable.widthOrZero,
+                prefixPlaceableWidth = prefixPlaceable.widthOrZero,
+                suffixPlaceableWidth = suffixPlaceable.widthOrZero,
                 textFieldPlaceableWidth = textFieldPlaceable.width,
-                labelPlaceableWidth = widthOrZero(labelPlaceable),
-                placeholderPlaceableWidth = widthOrZero(placeholderPlaceable),
-                animationProgress = animationProgress,
+                labelPlaceableWidth = labelPlaceable.widthOrZero,
+                placeholderPlaceableWidth = placeholderPlaceable.widthOrZero,
                 constraints = constraints,
-                density = density,
-                paddingValues = paddingValues,
             )
 
+        if (isLabelAbove) {
+            // now that we know the width, measure label
+            val labelConstraints =
+                relaxedConstraints.copy(maxHeight = labelIntrinsicHeight, maxWidth = width)
+            labelPlaceable = labelMeasurable?.measure(labelConstraints)
+            val labelSize =
+                labelPlaceable?.let { Size(it.width.toFloat(), it.height.toFloat()) } ?: Size.Zero
+            onLabelMeasured(labelSize)
+        }
+
         // measure supporting text
         val supportingConstraints =
             relaxedConstraints
                 .offset(vertical = -occupiedSpaceVertically)
                 .copy(minHeight = 0, maxWidth = width)
         val supportingPlaceable = supportingMeasurable?.measure(supportingConstraints)
-        val supportingHeight = heightOrZero(supportingPlaceable)
+        val supportingHeight = supportingPlaceable.heightOrZero
 
         val totalHeight =
             calculateHeight(
-                leadingHeight = heightOrZero(leadingPlaceable),
-                trailingHeight = heightOrZero(trailingPlaceable),
-                prefixHeight = heightOrZero(prefixPlaceable),
-                suffixHeight = heightOrZero(suffixPlaceable),
+                leadingHeight = leadingPlaceable.heightOrZero,
+                trailingHeight = trailingPlaceable.heightOrZero,
+                prefixHeight = prefixPlaceable.heightOrZero,
+                suffixHeight = suffixPlaceable.heightOrZero,
                 textFieldHeight = textFieldPlaceable.height,
-                labelHeight = heightOrZero(labelPlaceable),
-                placeholderHeight = heightOrZero(placeholderPlaceable),
-                supportingHeight = heightOrZero(supportingPlaceable),
-                animationProgress = animationProgress,
+                labelHeight = labelPlaceable.heightOrZero,
+                placeholderHeight = placeholderPlaceable.heightOrZero,
+                supportingHeight = supportingPlaceable.heightOrZero,
                 constraints = constraints,
-                density = density,
-                paddingValues = paddingValues,
+                isLabelAbove = isLabelAbove,
             )
-        val height = totalHeight - supportingHeight
+        val height =
+            totalHeight - supportingHeight - (if (isLabelAbove) labelPlaceable.heightOrZero else 0)
 
         val containerPlaceable =
             measurables
@@ -752,11 +981,9 @@
                 placeholderPlaceable = placeholderPlaceable,
                 containerPlaceable = containerPlaceable,
                 supportingPlaceable = supportingPlaceable,
-                animationProgress = animationProgress,
-                singleLine = singleLine,
                 density = density,
                 layoutDirection = layoutDirection,
-                paddingValues = paddingValues,
+                isLabelAbove = isLabelAbove,
             )
         }
     }
@@ -836,10 +1063,7 @@
             textFieldPlaceableWidth = textFieldWidth,
             labelPlaceableWidth = labelWidth,
             placeholderPlaceableWidth = placeholderWidth,
-            animationProgress = animationProgress,
-            constraints = ZeroConstraints,
-            density = density,
-            paddingValues = paddingValues,
+            constraints = Constraints(),
         )
     }
 
@@ -854,7 +1078,7 @@
                 .fastFirstOrNull { it.layoutId == LeadingId }
                 ?.let {
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     intrinsicMeasurer(it, width)
@@ -864,7 +1088,7 @@
                 .fastFirstOrNull { it.layoutId == TrailingId }
                 ?.let {
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     intrinsicMeasurer(it, width)
@@ -873,7 +1097,7 @@
         val labelHeight =
             measurables
                 .fastFirstOrNull { it.layoutId == LabelId }
-                ?.let { intrinsicMeasurer(it, lerp(remainingWidth, width, animationProgress)) } ?: 0
+                ?.let { intrinsicMeasurer(it, lerp(remainingWidth, width, labelProgress)) } ?: 0
 
         val prefixHeight =
             measurables
@@ -881,7 +1105,7 @@
                 ?.let {
                     val height = intrinsicMeasurer(it, remainingWidth)
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     height
@@ -892,7 +1116,7 @@
                 ?.let {
                     val height = intrinsicMeasurer(it, remainingWidth)
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     height
@@ -920,198 +1144,214 @@
             labelHeight = labelHeight,
             placeholderHeight = placeholderHeight,
             supportingHeight = supportingHeight,
-            animationProgress = animationProgress,
-            constraints = ZeroConstraints,
-            density = density,
-            paddingValues = paddingValues
+            constraints = Constraints(),
+            isLabelAbove = labelPosition == TextFieldLabelPosition.Above,
         )
     }
-}
 
-private fun Int.substractConstraintSafely(from: Int): Int {
-    if (this == Constraints.Infinity) {
-        return this
+    /**
+     * Calculate the width of the [OutlinedTextField] given all elements that should be placed
+     * inside.
+     */
+    private fun Density.calculateWidth(
+        leadingPlaceableWidth: Int,
+        trailingPlaceableWidth: Int,
+        prefixPlaceableWidth: Int,
+        suffixPlaceableWidth: Int,
+        textFieldPlaceableWidth: Int,
+        labelPlaceableWidth: Int,
+        placeholderPlaceableWidth: Int,
+        constraints: Constraints,
+    ): Int {
+        val affixTotalWidth = prefixPlaceableWidth + suffixPlaceableWidth
+        val middleSection =
+            maxOf(
+                textFieldPlaceableWidth + affixTotalWidth,
+                placeholderPlaceableWidth + affixTotalWidth,
+                // Prefix/suffix does not get applied to label
+                lerp(labelPlaceableWidth, 0, labelProgress),
+            )
+        val wrappedWidth = leadingPlaceableWidth + middleSection + trailingPlaceableWidth
+
+        // Actual LayoutDirection doesn't matter; we only need the sum
+        val labelHorizontalPadding =
+            (paddingValues.calculateLeftPadding(LayoutDirection.Ltr) +
+                    paddingValues.calculateRightPadding(LayoutDirection.Ltr))
+                .toPx()
+        val focusedLabelWidth =
+            ((labelPlaceableWidth + labelHorizontalPadding) * labelProgress).roundToInt()
+        return maxOf(wrappedWidth, focusedLabelWidth, constraints.minWidth)
     }
-    return this - from
-}
 
-/**
- * Calculate the width of the [OutlinedTextField] given all elements that should be placed inside.
- */
-private fun calculateWidth(
-    leadingPlaceableWidth: Int,
-    trailingPlaceableWidth: Int,
-    prefixPlaceableWidth: Int,
-    suffixPlaceableWidth: Int,
-    textFieldPlaceableWidth: Int,
-    labelPlaceableWidth: Int,
-    placeholderPlaceableWidth: Int,
-    animationProgress: Float,
-    constraints: Constraints,
-    density: Float,
-    paddingValues: PaddingValues,
-): Int {
-    val affixTotalWidth = prefixPlaceableWidth + suffixPlaceableWidth
-    val middleSection =
-        maxOf(
-            textFieldPlaceableWidth + affixTotalWidth,
-            placeholderPlaceableWidth + affixTotalWidth,
-            // Prefix/suffix does not get applied to label
-            lerp(labelPlaceableWidth, 0, animationProgress),
-        )
-    val wrappedWidth = leadingPlaceableWidth + middleSection + trailingPlaceableWidth
-
-    // Actual LayoutDirection doesn't matter; we only need the sum
-    val labelHorizontalPadding =
-        (paddingValues.calculateLeftPadding(LayoutDirection.Ltr) +
-                paddingValues.calculateRightPadding(LayoutDirection.Ltr))
-            .value * density
-    val focusedLabelWidth =
-        ((labelPlaceableWidth + labelHorizontalPadding) * animationProgress).roundToInt()
-    return maxOf(wrappedWidth, focusedLabelWidth, constraints.minWidth)
-}
-
-/**
- * Calculate the height of the [OutlinedTextField] given all elements that should be placed inside.
- * This includes the supporting text, if it exists, even though this element is not "visually"
- * inside the text field.
- */
-private fun calculateHeight(
-    leadingHeight: Int,
-    trailingHeight: Int,
-    prefixHeight: Int,
-    suffixHeight: Int,
-    textFieldHeight: Int,
-    labelHeight: Int,
-    placeholderHeight: Int,
-    supportingHeight: Int,
-    animationProgress: Float,
-    constraints: Constraints,
-    density: Float,
-    paddingValues: PaddingValues
-): Int {
-    val inputFieldHeight =
-        maxOf(
-            textFieldHeight,
-            placeholderHeight,
-            prefixHeight,
-            suffixHeight,
-            lerp(labelHeight, 0, animationProgress)
-        )
-    val topPadding = paddingValues.calculateTopPadding().value * density
-    val actualTopPadding = lerp(topPadding, max(topPadding, labelHeight / 2f), animationProgress)
-    val bottomPadding = paddingValues.calculateBottomPadding().value * density
-    val middleSectionHeight = actualTopPadding + inputFieldHeight + bottomPadding
-
-    return max(
-        constraints.minHeight,
-        maxOf(leadingHeight, trailingHeight, middleSectionHeight.roundToInt()) + supportingHeight
-    )
-}
-
-/**
- * Places the provided text field, placeholder, label, optional leading and trailing icons inside
- * the [OutlinedTextField]
- */
-private fun Placeable.PlacementScope.place(
-    totalHeight: Int,
-    width: Int,
-    leadingPlaceable: Placeable?,
-    trailingPlaceable: Placeable?,
-    prefixPlaceable: Placeable?,
-    suffixPlaceable: Placeable?,
-    textFieldPlaceable: Placeable,
-    labelPlaceable: Placeable?,
-    placeholderPlaceable: Placeable?,
-    containerPlaceable: Placeable,
-    supportingPlaceable: Placeable?,
-    animationProgress: Float,
-    singleLine: Boolean,
-    density: Float,
-    layoutDirection: LayoutDirection,
-    paddingValues: PaddingValues
-) {
-    // place container
-    containerPlaceable.place(IntOffset.Zero)
-
-    // Most elements should be positioned w.r.t the text field's "visual" height, i.e., excluding
-    // the supporting text on bottom
-    val height = totalHeight - heightOrZero(supportingPlaceable)
-    val topPadding = (paddingValues.calculateTopPadding().value * density).roundToInt()
-    val startPadding =
-        (paddingValues.calculateStartPadding(layoutDirection).value * density).roundToInt()
-
-    val iconPadding = HorizontalIconPadding.value * density
-
-    // placed center vertically and to the start edge horizontally
-    leadingPlaceable?.placeRelative(
-        0,
-        Alignment.CenterVertically.align(leadingPlaceable.height, height)
-    )
-
-    // label position is animated
-    // in single line text field, label is centered vertically before animation starts
-    labelPlaceable?.let {
-        val startPositionY =
-            if (singleLine) {
-                Alignment.CenterVertically.align(it.height, height)
-            } else {
+    /**
+     * Calculate the height of the [OutlinedTextField] given all elements that should be placed
+     * inside. This includes the supporting text, if it exists, even though this element is not
+     * "visually" inside the text field.
+     */
+    private fun Density.calculateHeight(
+        leadingHeight: Int,
+        trailingHeight: Int,
+        prefixHeight: Int,
+        suffixHeight: Int,
+        textFieldHeight: Int,
+        labelHeight: Int,
+        placeholderHeight: Int,
+        supportingHeight: Int,
+        constraints: Constraints,
+        isLabelAbove: Boolean,
+    ): Int {
+        val inputFieldHeight =
+            maxOf(
+                textFieldHeight,
+                placeholderHeight,
+                prefixHeight,
+                suffixHeight,
+                if (isLabelAbove) 0 else lerp(labelHeight, 0, labelProgress),
+            )
+        val topPadding = paddingValues.calculateTopPadding().toPx()
+        val actualTopPadding =
+            if (isLabelAbove) {
                 topPadding
+            } else {
+                lerp(topPadding, max(topPadding, labelHeight / 2f), labelProgress)
             }
-        val positionY = lerp(startPositionY, -(it.height / 2), animationProgress)
-        val positionX =
-            (if (leadingPlaceable == null) {
-                    0f
-                } else {
-                    (widthOrZero(leadingPlaceable) - iconPadding) * (1 - animationProgress)
-                })
-                .roundToInt() + startPadding
-        it.placeRelative(positionX, positionY)
+        val bottomPadding = paddingValues.calculateBottomPadding().toPx()
+        val middleSectionHeight = actualTopPadding + inputFieldHeight + bottomPadding
+
+        return max(
+            constraints.minHeight,
+            (if (isLabelAbove) labelHeight else 0) +
+                maxOf(leadingHeight, trailingHeight, middleSectionHeight.roundToInt()) +
+                supportingHeight
+        )
     }
 
-    // Single line text fields have text components centered vertically.
-    // Multiline text fields have text components aligned to top with padding.
-    fun calculateVerticalPosition(placeable: Placeable): Int =
-        max(
-            if (singleLine) {
-                Alignment.CenterVertically.align(placeable.height, height)
-            } else {
-                topPadding
-            },
-            heightOrZero(labelPlaceable) / 2
+    /**
+     * Places the provided text field, placeholder, label, optional leading and trailing icons
+     * inside the [OutlinedTextField]
+     */
+    private fun Placeable.PlacementScope.place(
+        totalHeight: Int,
+        width: Int,
+        leadingPlaceable: Placeable?,
+        trailingPlaceable: Placeable?,
+        prefixPlaceable: Placeable?,
+        suffixPlaceable: Placeable?,
+        textFieldPlaceable: Placeable,
+        labelPlaceable: Placeable?,
+        placeholderPlaceable: Placeable?,
+        containerPlaceable: Placeable,
+        supportingPlaceable: Placeable?,
+        density: Float,
+        layoutDirection: LayoutDirection,
+        isLabelAbove: Boolean,
+    ) {
+        val yOffset = if (isLabelAbove) labelPlaceable.heightOrZero else 0
+
+        // place container
+        containerPlaceable.place(0, yOffset)
+
+        // Most elements should be positioned w.r.t the text field's "visual" height, i.e.,
+        // excluding the label (if it's Above) and the supporting text on bottom
+        val height =
+            totalHeight -
+                supportingPlaceable.heightOrZero -
+                (if (isLabelAbove) labelPlaceable.heightOrZero else 0)
+
+        val topPadding = (paddingValues.calculateTopPadding().value * density).roundToInt()
+        val startPadding = paddingValues.calculateStartPadding(layoutDirection).value * density
+
+        val iconPadding = HorizontalIconPadding.value * density
+
+        // placed center vertically and to the start edge horizontally
+        leadingPlaceable?.placeRelative(
+            0,
+            yOffset + Alignment.CenterVertically.align(leadingPlaceable.height, height)
         )
 
-    prefixPlaceable?.placeRelative(
-        widthOrZero(leadingPlaceable),
-        calculateVerticalPosition(prefixPlaceable)
-    )
+        // label position is animated
+        // in single line text field, label is centered vertically before animation starts
+        labelPlaceable?.let {
+            val startY =
+                when {
+                    isLabelAbove -> 0
+                    singleLine -> Alignment.CenterVertically.align(it.height, height)
+                    else -> topPadding
+                }
+            val endY =
+                when {
+                    isLabelAbove -> 0
+                    else -> -(it.height / 2)
+                }
+            val positionY = lerp(startY, endY, labelProgress)
 
-    val textHorizontalPosition = widthOrZero(leadingPlaceable) + widthOrZero(prefixPlaceable)
+            val startX =
+                when {
+                    isLabelAbove -> 0f
+                    leadingPlaceable == null -> startPadding
+                    else ->
+                        startPadding +
+                            (leadingPlaceable.widthOrZero - iconPadding).coerceAtLeast(0f)
+                }
+            val endX =
+                when {
+                    isLabelAbove -> 0f
+                    else -> startPadding
+                }
+            val positionX = lerp(startX, endX, labelProgress).roundToInt()
+            it.placeRelative(positionX, positionY)
+        }
 
-    textFieldPlaceable.placeRelative(
-        textHorizontalPosition,
-        calculateVerticalPosition(textFieldPlaceable)
-    )
+        fun calculateVerticalPosition(placeable: Placeable): Int {
+            val defaultPosition =
+                yOffset +
+                    if (singleLine) {
+                        // Single line text fields have text components centered vertically.
+                        Alignment.CenterVertically.align(placeable.height, height)
+                    } else {
+                        // Multiline text fields have text components aligned to top with padding.
+                        topPadding
+                    }
+            return if (labelPosition is TextFieldLabelPosition.Default) {
+                // Ensure components are placed below label when it's in the border
+                max(defaultPosition, labelPlaceable.heightOrZero / 2)
+            } else {
+                defaultPosition
+            }
+        }
 
-    // placed similar to the input text above
-    placeholderPlaceable?.placeRelative(
-        textHorizontalPosition,
-        calculateVerticalPosition(placeholderPlaceable)
-    )
+        prefixPlaceable?.placeRelative(
+            leadingPlaceable.widthOrZero,
+            calculateVerticalPosition(prefixPlaceable)
+        )
 
-    suffixPlaceable?.placeRelative(
-        width - widthOrZero(trailingPlaceable) - suffixPlaceable.width,
-        calculateVerticalPosition(suffixPlaceable)
-    )
+        val textHorizontalPosition = leadingPlaceable.widthOrZero + prefixPlaceable.widthOrZero
 
-    // placed center vertically and to the end edge horizontally
-    trailingPlaceable?.placeRelative(
-        width - trailingPlaceable.width,
-        Alignment.CenterVertically.align(trailingPlaceable.height, height)
-    )
+        textFieldPlaceable.placeRelative(
+            textHorizontalPosition,
+            calculateVerticalPosition(textFieldPlaceable)
+        )
 
-    // place supporting text
-    supportingPlaceable?.placeRelative(0, height)
+        // placed similar to the input text above
+        placeholderPlaceable?.placeRelative(
+            textHorizontalPosition,
+            calculateVerticalPosition(placeholderPlaceable)
+        )
+
+        suffixPlaceable?.placeRelative(
+            width - trailingPlaceable.widthOrZero - suffixPlaceable.width,
+            calculateVerticalPosition(suffixPlaceable)
+        )
+
+        // placed center vertically and to the end edge horizontally
+        trailingPlaceable?.placeRelative(
+            width - trailingPlaceable.width,
+            yOffset + Alignment.CenterVertically.align(trailingPlaceable.height, height)
+        )
+
+        // place supporting text
+        supportingPlaceable?.placeRelative(0, yOffset + height)
+    }
 }
 
 internal fun Modifier.outlineCutout(labelSize: () -> Size, paddingValues: PaddingValues) =
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
index 7fead98..5f79700 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
@@ -16,25 +16,22 @@
 
 package androidx.compose.material3
 
-import androidx.annotation.VisibleForTesting
-import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.SpringSpec
-import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.animateValue
 import androidx.compose.animation.core.infiniteRepeatable
 import androidx.compose.animation.core.keyframes
 import androidx.compose.animation.core.rememberInfiniteTransition
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.progressSemantics
 import androidx.compose.material3.ProgressIndicatorDefaults.drawStopIndicator
+import androidx.compose.material3.internal.IncreaseVerticalSemanticsBounds
 import androidx.compose.material3.tokens.CircularProgressIndicatorTokens
 import androidx.compose.material3.tokens.LinearProgressIndicatorTokens
+import androidx.compose.material3.tokens.MotionTokens
 import androidx.compose.material3.tokens.ProgressIndicatorTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
@@ -44,7 +41,7 @@
 import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.layout.layout
+import androidx.compose.ui.graphics.drawscope.rotate
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.ProgressBarRangeInfo
 import androidx.compose.ui.semantics.progressBarRangeInfo
@@ -52,7 +49,6 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.offset
 import kotlin.math.PI
 import kotlin.math.abs
 import kotlin.math.max
@@ -155,7 +151,7 @@
     val coercedProgress = { progress().coerceIn(0f, 1f) }
     Canvas(
         modifier
-            .then(IncreaseSemanticsBounds)
+            .then(IncreaseVerticalSemanticsBounds)
             .semantics(mergeDescendants = true) {
                 progressBarRangeInfo = ProgressBarRangeInfo(coercedProgress(), 0f..1f)
             }
@@ -252,64 +248,33 @@
     gapSize: Dp = ProgressIndicatorDefaults.LinearIndicatorTrackGapSize,
 ) {
     val infiniteTransition = rememberInfiniteTransition()
-    // Fractional position of the 'head' and 'tail' of the two lines drawn, i.e. if the head is 0.8
-    // and the tail is 0.2, there is a line drawn from between 20% along to 80% along the total
-    // width.
     val firstLineHead =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at FirstLineHeadDelay using FirstLineHeadEasing
-                        1f at FirstLineHeadDuration + FirstLineHeadDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateFirstLineHeadAnimationSpec
         )
     val firstLineTail =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at FirstLineTailDelay using FirstLineTailEasing
-                        1f at FirstLineTailDuration + FirstLineTailDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateFirstLineTailAnimationSpec
         )
     val secondLineHead =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at SecondLineHeadDelay using SecondLineHeadEasing
-                        1f at SecondLineHeadDuration + SecondLineHeadDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateSecondLineHeadAnimationSpec
         )
     val secondLineTail =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at SecondLineTailDelay using SecondLineTailEasing
-                        1f at SecondLineTailDuration + SecondLineTailDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateSecondLineTailAnimationSpec
         )
     Canvas(
         modifier
-            .then(IncreaseSemanticsBounds)
+            .then(IncreaseVerticalSemanticsBounds)
             .progressSemantics()
             .size(LinearIndicatorWidth, LinearIndicatorHeight)
     ) {
@@ -465,27 +430,6 @@
     }
 }
 
-@VisibleForTesting internal val SemanticsBoundsPadding: Dp = 10.dp
-internal val IncreaseSemanticsBounds: Modifier =
-    Modifier.layout { measurable, constraints ->
-            val paddingPx = SemanticsBoundsPadding.roundToPx()
-            // We need to add vertical padding to the semantics bounds in order to meet
-            // screenreader green box minimum size, but we also want to
-            // preserve a visual appearance and layout size below that minimum
-            // in order to maintain backwards compatibility. This custom
-            // layout effectively implements "negative padding".
-            val newConstraint = constraints.offset(0, paddingPx * 2)
-            val placeable = measurable.measure(newConstraint)
-
-            // But when actually placing the placeable, create the layout without additional
-            // space. Place the placeable where it would've been without any extra padding.
-            val height = placeable.height - paddingPx * 2
-            val width = placeable.width
-            layout(width, height) { placeable.place(0, -paddingPx) }
-        }
-        .semantics(mergeDescendants = true) {}
-        .padding(vertical = SemanticsBoundsPadding)
-
 /**
  * <a href="https://m3.material.io/components/progress-indicators/overview" class="external"
  * target="_blank">Determinate Material Design circular progress indicator</a>.
@@ -594,8 +538,7 @@
             } else {
                 gapSize + strokeWidth
             }
-        val gapSizeSweep =
-            (adjustedGapSize.value / (Math.PI * size.width.toDp().value).toFloat()) * 360f
+        val gapSizeSweep = (adjustedGapSize.value / (PI * size.width.toDp().value).toFloat()) * 360f
 
         drawCircularIndicator(
             startAngle + sweep + min(sweep, gapSizeSweep),
@@ -624,6 +567,16 @@
  *   reached the area of the overall indicator yet
  * @param strokeCap stroke cap to use for the ends of this progress indicator
  */
+@Deprecated(
+    message = "Use the overload that takes `gapSize`",
+    replaceWith =
+        ReplaceWith(
+            "CircularProgressIndicator(modifier, color, strokeWidth, trackColor, strokeCap, " +
+                "gapSize)"
+        ),
+    level = DeprecationLevel.HIDDEN
+)
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun CircularProgressIndicator(
     modifier: Modifier = Modifier,
@@ -631,77 +584,91 @@
     strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth,
     trackColor: Color = ProgressIndicatorDefaults.circularIndeterminateTrackColor,
     strokeCap: StrokeCap = ProgressIndicatorDefaults.CircularIndeterminateStrokeCap,
+) =
+    CircularProgressIndicator(
+        modifier = modifier,
+        color = color,
+        strokeWidth = strokeWidth,
+        trackColor = trackColor,
+        strokeCap = strokeCap,
+        gapSize = ProgressIndicatorDefaults.CircularIndicatorTrackGapSize
+    )
+
+/**
+ * <a href="https://m3.material.io/components/progress-indicators/overview" class="external"
+ * target="_blank">Indeterminate Material Design circular progress indicator</a>.
+ *
+ * Progress indicators express an unspecified wait time or display the duration of a process.
+ *
+ * ![Circular progress indicator
+ * image](https://firebasestorage.googleapis.com/v0/b/design-spec/o/projects%2Fgoogle-material-3%2Fimages%2Flqdiyyvh-1P-progress-indicator-configurations.png?alt=media)
+ *
+ * @sample androidx.compose.material3.samples.IndeterminateCircularProgressIndicatorSample
+ * @param modifier the [Modifier] to be applied to this progress indicator
+ * @param color color of this progress indicator
+ * @param strokeWidth stroke width of this progress indicator
+ * @param trackColor color of the track behind the indicator, visible when the progress has not
+ *   reached the area of the overall indicator yet
+ * @param strokeCap stroke cap to use for the ends of this progress indicator
+ * @param gapSize size of the gap between the progress indicator and the track
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun CircularProgressIndicator(
+    modifier: Modifier = Modifier,
+    color: Color = ProgressIndicatorDefaults.circularColor,
+    strokeWidth: Dp = ProgressIndicatorDefaults.CircularStrokeWidth,
+    trackColor: Color = ProgressIndicatorDefaults.circularIndeterminateTrackColor,
+    strokeCap: StrokeCap = ProgressIndicatorDefaults.CircularIndeterminateStrokeCap,
+    gapSize: Dp = ProgressIndicatorDefaults.CircularIndicatorTrackGapSize
 ) {
     val stroke = with(LocalDensity.current) { Stroke(width = strokeWidth.toPx(), cap = strokeCap) }
 
-    val transition = rememberInfiniteTransition()
-    // The current rotation around the circle, so we know where to start the rotation from
-    val currentRotation =
-        transition.animateValue(
-            0,
-            RotationsPerCycle,
-            Int.VectorConverter,
-            infiniteRepeatable(
-                animation =
-                    tween(
-                        durationMillis = RotationDuration * RotationsPerCycle,
-                        easing = LinearEasing
-                    )
-            )
+    val infiniteTransition = rememberInfiniteTransition()
+    // A global rotation that does a 360 degrees rotation in 6 seconds.
+    val globalRotation =
+        infiniteTransition.animateFloat(
+            initialValue = 0f,
+            targetValue = CircularGlobalRotationDegreesTarget,
+            animationSpec = circularIndeterminateGlobalRotationAnimationSpec
         )
-    // How far forward (degrees) the base point should be from the start point
-    val baseRotation =
-        transition.animateFloat(
-            0f,
-            BaseRotationAngle,
-            infiniteRepeatable(
-                animation = tween(durationMillis = RotationDuration, easing = LinearEasing)
-            )
+
+    // An additional rotation that moves by 90 degrees in 500ms and then rest for 1 second.
+    val additionalRotation =
+        infiniteTransition.animateFloat(
+            initialValue = 0f,
+            targetValue = CircularAdditionalRotationDegreesTarget,
+            animationSpec = circularIndeterminateRotationAnimationSpec
         )
-    // How far forward (degrees) both the head and tail should be from the base point
-    val endAngle =
-        transition.animateFloat(
-            0f,
-            JumpRotationAngle,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
-                        0f at 0 using CircularEasing
-                        JumpRotationAngle at HeadAndTailAnimationDuration
-                    }
-            )
+
+    // Indicator progress animation that will be changing the progress up and down as the indicator
+    // rotates.
+    val progressAnimation =
+        infiniteTransition.animateFloat(
+            initialValue = CircularIndeterminateMinProgress,
+            targetValue = CircularIndeterminateMaxProgress,
+            animationSpec = circularIndeterminateProgressAnimationSpec
         )
-    val startAngle =
-        transition.animateFloat(
-            0f,
-            JumpRotationAngle,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
-                        0f at HeadAndTailDelayDuration using CircularEasing
-                        JumpRotationAngle at durationMillis
-                    }
-            )
-        )
+
     Canvas(modifier.progressSemantics().size(CircularIndicatorDiameter)) {
-        drawCircularIndicatorTrack(trackColor, stroke)
+        val sweep = progressAnimation.value * 360f
+        val adjustedGapSize =
+            if (strokeCap == StrokeCap.Butt || size.height > size.width) {
+                gapSize
+            } else {
+                gapSize + strokeWidth
+            }
+        val gapSizeSweep = (adjustedGapSize.value / (PI * size.width.toDp().value).toFloat()) * 360f
 
-        val currentRotationAngleOffset = (currentRotation.value * RotationAngleOffset) % 360f
-
-        // How long a line to draw using the start angle as a reference point
-        val sweep = abs(endAngle.value - startAngle.value)
-
-        // Offset by the constant offset and the per rotation offset
-        val offset = StartAngleOffset + currentRotationAngleOffset + baseRotation.value
-        drawIndeterminateCircularIndicator(
-            startAngle.value + offset,
-            strokeWidth,
-            sweep,
-            color,
-            stroke
-        )
+        rotate(globalRotation.value + additionalRotation.value) {
+            drawCircularIndicator(
+                sweep + min(sweep, gapSizeSweep),
+                360f - sweep - min(sweep, gapSizeSweep) * 2,
+                trackColor,
+                stroke
+            )
+            drawDeterminateCircularIndicator(startAngle = 0f, sweep, color, stroke)
+        }
     }
 }
 
@@ -950,6 +917,103 @@
     }
 }
 
+/** A global animation spec for indeterminate circular progress indicator. */
+internal val circularIndeterminateGlobalRotationAnimationSpec
+    get() =
+        infiniteRepeatable<Float>(
+            animation = tween(CircularAnimationProgressDuration, easing = LinearEasing)
+        )
+
+/**
+ * An animation spec for indeterminate circular progress indicators that infinitely rotates a 360
+ * degrees.
+ */
+internal val circularIndeterminateRotationAnimationSpec
+    get() =
+        infiniteRepeatable(
+            animation =
+                keyframes {
+                    durationMillis = CircularAnimationProgressDuration // 6000ms
+                    90f at
+                        CircularAnimationAdditionalRotationDuration using
+                        MotionTokens.EasingEmphasizedDecelerateCubicBezier // 300ms
+                    90f at CircularAnimationAdditionalRotationDelay // hold till 1500ms
+                    180f at
+                        CircularAnimationAdditionalRotationDuration +
+                            CircularAnimationAdditionalRotationDelay // 1800ms
+                    180f at CircularAnimationAdditionalRotationDelay * 2 // hold till 3000ms
+                    270f at
+                        CircularAnimationAdditionalRotationDuration +
+                            CircularAnimationAdditionalRotationDelay * 2 // 3300ms
+                    270f at CircularAnimationAdditionalRotationDelay * 3 // hold till 4500ms
+                    360f at
+                        CircularAnimationAdditionalRotationDuration +
+                            CircularAnimationAdditionalRotationDelay * 3 // 4800ms
+                    360f at CircularAnimationProgressDuration // hold till 6000ms
+                }
+        )
+
+/** An animation spec for indeterminate circular progress indicators progress motion. */
+internal val circularIndeterminateProgressAnimationSpec
+    get() =
+        infiniteRepeatable(
+            animation =
+                keyframes {
+                    durationMillis = CircularAnimationProgressDuration // 6000ms
+                    CircularIndeterminateMaxProgress at
+                        CircularAnimationProgressDuration / 2 using
+                        CircularProgressEasing // 3000ms
+                    CircularIndeterminateMinProgress at CircularAnimationProgressDuration
+                }
+        )
+
+/** An animation spec for indeterminate linear progress indicator first line head position. */
+internal val linearIndeterminateFirstLineHeadAnimationSpec
+    get() =
+        infiniteRepeatable(
+            animation =
+                keyframes {
+                    durationMillis = LinearAnimationDuration
+                    0f at FirstLineHeadDelay using LinearIndeterminateProgressEasing
+                    1f at FirstLineHeadDuration + FirstLineHeadDelay
+                }
+        )
+
+/** An animation spec for indeterminate linear progress indicator first line tail position. */
+internal val linearIndeterminateFirstLineTailAnimationSpec
+    get() =
+        infiniteRepeatable(
+            animation =
+                keyframes {
+                    durationMillis = LinearAnimationDuration
+                    0f at FirstLineTailDelay using LinearIndeterminateProgressEasing
+                    1f at FirstLineTailDuration + FirstLineTailDelay
+                }
+        )
+
+/** An animation spec for indeterminate linear progress indicator second line head position. */
+internal val linearIndeterminateSecondLineHeadAnimationSpec
+    get() =
+        infiniteRepeatable(
+            animation =
+                keyframes {
+                    durationMillis = LinearAnimationDuration
+                    0f at SecondLineHeadDelay using LinearIndeterminateProgressEasing
+                    1f at SecondLineHeadDuration + SecondLineHeadDelay
+                }
+        )
+
+/** An animation spec for indeterminate linear progress indicator second line tail position. */
+internal val linearIndeterminateSecondLineTailAnimationSpec
+    get() =
+        infiniteRepeatable(
+            animation =
+                keyframes {
+                    durationMillis = LinearAnimationDuration
+                    0f at SecondLineTailDelay using LinearIndeterminateProgressEasing
+                    1f at SecondLineTailDuration + SecondLineTailDelay
+                }
+        )
 // LinearProgressIndicator Material specs
 
 // Width is given in the spec but not defined as a token.
@@ -966,53 +1030,32 @@
 
 // Indeterminate linear indicator transition specs
 
-// Total duration for one cycle
-private const val LinearAnimationDuration = 1800
+// Total duration for one linear cycle
+internal const val LinearAnimationDuration = 1750
 
 // Duration of the head and tail animations for both lines
-private const val FirstLineHeadDuration = 750
-private const val FirstLineTailDuration = 850
-private const val SecondLineHeadDuration = 567
-private const val SecondLineTailDuration = 533
+internal const val FirstLineHeadDuration = 1000
+internal const val FirstLineTailDuration = 1000
+internal const val SecondLineHeadDuration = 850
+internal const val SecondLineTailDuration = 850
 
 // Delay before the start of the head and tail animations for both lines
-private const val FirstLineHeadDelay = 0
-private const val FirstLineTailDelay = 333
-private const val SecondLineHeadDelay = 1000
-private const val SecondLineTailDelay = 1267
+internal const val FirstLineHeadDelay = 0
+internal const val FirstLineTailDelay = 250
+internal const val SecondLineHeadDelay = 650
+internal const val SecondLineTailDelay = 900
 
-private val FirstLineHeadEasing = CubicBezierEasing(0.2f, 0f, 0.8f, 1f)
-private val FirstLineTailEasing = CubicBezierEasing(0.4f, 0f, 1f, 1f)
-private val SecondLineHeadEasing = CubicBezierEasing(0f, 0f, 0.65f, 1f)
-private val SecondLineTailEasing = CubicBezierEasing(0.1f, 0f, 0.45f, 1f)
+internal val LinearIndeterminateProgressEasing = MotionTokens.EasingEmphasizedAccelerateCubicBezier
 
 // Indeterminate circular indicator transition specs
 
-// The animation comprises of 5 rotations around the circle forming a 5 pointed star.
-// After the 5th rotation, we are back at the beginning of the circle.
-private const val RotationsPerCycle = 5
+// The indeterminate circular indicator easing constants for its motion
+internal val CircularProgressEasing = MotionTokens.EasingStandardCubicBezier
+internal const val CircularIndeterminateMinProgress = 0.1f
+internal const val CircularIndeterminateMaxProgress = 0.87f
 
-// Each rotation is 1 and 1/3 seconds, but 1332ms divides more evenly
-private const val RotationDuration = 1332
-
-// When the rotation is at its beginning (0 or 360 degrees) we want it to be drawn at 12 o clock,
-// which means 270 degrees when drawing.
-private const val StartAngleOffset = -90f
-
-// How far the base point moves around the circle
-private const val BaseRotationAngle = 286f
-
-// How far the head and tail should jump forward during one rotation past the base point
-private const val JumpRotationAngle = 290f
-
-// Each rotation we want to offset the start position by this much, so we continue where
-// the previous rotation ended. This is the maximum angle covered during one rotation.
-private const val RotationAngleOffset = (BaseRotationAngle + JumpRotationAngle) % 360f
-
-// The head animates for the first half of a rotation, then is static for the second half
-// The tail is static for the first half and then animates for the second half
-private const val HeadAndTailAnimationDuration = (RotationDuration * 0.5).toInt()
-private const val HeadAndTailDelayDuration = HeadAndTailAnimationDuration
-
-// The easing for the head and tail jump
-private val CircularEasing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f)
+internal const val CircularAnimationProgressDuration = 6000
+internal const val CircularAnimationAdditionalRotationDelay = 1500
+internal const val CircularAnimationAdditionalRotationDuration = 300
+internal const val CircularAdditionalRotationDegreesTarget = 360f
+internal const val CircularGlobalRotationDegreesTarget = 1080f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt
index 623866a..9241096 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.animateDpAsState
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -28,6 +27,7 @@
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.selection.selectable
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.RadioButtonTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
@@ -71,6 +71,7 @@
  *   appearance or preview the radio button in different states. Note that if `null` is provided,
  *   interactions will still happen internally.
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun RadioButton(
     selected: Boolean,
@@ -83,7 +84,8 @@
     val dotRadius =
         animateDpAsState(
             targetValue = if (selected) RadioButtonDotSize / 2 else 0.dp,
-            animationSpec = tween(durationMillis = RadioAnimationDuration)
+            // TODO Load the motionScheme tokens from the component tokens file
+            animationSpec = MotionSchemeKeyTokens.FastSpatial.value()
         )
     val radioColor = colors.radioColor(enabled, selected)
     val selectableModifier =
@@ -220,6 +222,7 @@
      * @param enabled whether the [RadioButton] is enabled
      * @param selected whether the [RadioButton] is selected
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @Composable
     internal fun radioColor(enabled: Boolean, selected: Boolean): State<Color> {
         val target =
@@ -233,7 +236,8 @@
         // If not enabled 'snap' to the disabled state, as there should be no animations between
         // enabled / disabled.
         return if (enabled) {
-            animateColorAsState(target, tween(durationMillis = RadioAnimationDuration))
+            // TODO Load the motionScheme tokens from the component tokens file
+            animateColorAsState(target, MotionSchemeKeyTokens.DefaultEffects.value())
         } else {
             rememberUpdatedState(target)
         }
@@ -260,8 +264,6 @@
     }
 }
 
-private const val RadioAnimationDuration = 100
-
 private val RadioButtonPadding = 2.dp
 private val RadioButtonDotSize = 12.dp
 private val RadioStrokeWidth = 2.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt
index 178a5b7..476c2bf 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt
@@ -20,9 +20,10 @@
 import androidx.compose.animation.Crossfade
 import androidx.compose.animation.ExitTransition
 import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.VectorConverter
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.scaleIn
 import androidx.compose.foundation.BorderStroke
@@ -43,9 +44,8 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.foundation.shape.CornerBasedShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Check
-import androidx.compose.material3.tokens.MotionTokens
+import androidx.compose.material3.internal.Icons
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens
 import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.DisabledLabelTextColor
 import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.DisabledLabelTextOpacity
@@ -314,6 +314,7 @@
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun SegmentedButtonContent(
     icon: @Composable () -> Unit,
@@ -324,9 +325,13 @@
         modifier = Modifier.padding(ButtonDefaults.TextButtonContentPadding)
     ) {
         val typography = OutlinedSegmentedButtonTokens.LabelTextFont.value
+        // TODO Load the motionScheme tokens from the component tokens file
+        val animationSpec: FiniteAnimationSpec<Int> = MotionSchemeKeyTokens.FastSpatial.value()
         ProvideTextStyle(typography) {
             val scope = rememberCoroutineScope()
-            val measurePolicy = remember { SegmentedButtonContentMeasurePolicy(scope) }
+            val measurePolicy = remember {
+                SegmentedButtonContentMeasurePolicy(scope, animationSpec)
+            }
 
             Layout(
                 modifier = Modifier.height(IntrinsicSize.Min),
@@ -337,8 +342,10 @@
     }
 }
 
-internal class SegmentedButtonContentMeasurePolicy(val scope: CoroutineScope) :
-    MultiContentMeasurePolicy {
+internal class SegmentedButtonContentMeasurePolicy(
+    val scope: CoroutineScope,
+    val animationSpec: AnimationSpec<Int>
+) : MultiContentMeasurePolicy {
     var animatable: Animatable<Int, AnimationVector1D>? = null
     private var initialOffset: Int? = null
 
@@ -370,9 +377,7 @@
                 animatable
                     ?: Animatable(initialOffset!!, Int.VectorConverter).also { animatable = it }
             if (anim.targetValue != offsetX) {
-                scope.launch {
-                    anim.animateTo(offsetX, tween(MotionTokens.DurationMedium3.toInt()))
-                }
+                scope.launch { anim.animateTo(offsetX, animationSpec) }
             }
         }
 
@@ -483,7 +488,7 @@
                         activeContainerColor = fromToken(SelectedContainerColor),
                         activeContentColor = fromToken(SelectedLabelTextColor),
                         activeBorderColor = fromToken(OutlineColor),
-                        inactiveContainerColor = surface,
+                        inactiveContainerColor = Color.Transparent,
                         inactiveContentColor = fromToken(UnselectedLabelTextColor),
                         inactiveBorderColor = fromToken(OutlineColor),
                         disabledActiveContainerColor = fromToken(SelectedContainerColor),
@@ -492,7 +497,7 @@
                                 .copy(alpha = DisabledLabelTextOpacity),
                         disabledActiveBorderColor =
                             fromToken(OutlineColor).copy(alpha = DisabledOutlineOpacity),
-                        disabledInactiveContainerColor = surface,
+                        disabledInactiveContainerColor = Color.Transparent,
                         disabledInactiveContentColor = fromToken(DisabledLabelTextColor),
                         disabledInactiveBorderColor = fromToken(OutlineColor),
                     )
@@ -555,6 +560,7 @@
      * @param inactiveContent typically an icon of [IconSize]. It shows only when the button is not
      *   checked.
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @Composable
     fun Icon(
         active: Boolean,
@@ -562,21 +568,28 @@
         inactiveContent: (@Composable () -> Unit)? = null
     ) {
         if (inactiveContent == null) {
+            // TODO Load the motionScheme tokens from the component tokens file
             AnimatedVisibility(
                 visible = active,
                 exit = ExitTransition.None,
                 enter =
-                    fadeIn(tween(MotionTokens.DurationMedium3.toInt())) +
+                    fadeIn(MotionSchemeKeyTokens.DefaultEffects.value()) +
                         scaleIn(
                             initialScale = 0f,
                             transformOrigin = TransformOrigin(0f, 1f),
-                            animationSpec = tween(MotionTokens.DurationMedium3.toInt()),
+                            animationSpec = MotionSchemeKeyTokens.FastSpatial.value()
                         ),
             ) {
                 activeContent()
             }
         } else {
-            Crossfade(targetState = active) { if (it) activeContent() else inactiveContent() }
+            Crossfade(
+                targetState = active,
+                // TODO Load the motionScheme tokens from the component tokens file
+                animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
+            ) {
+                if (it) activeContent() else inactiveContent()
+            }
         }
     }
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
index ed14759..7619c29 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
@@ -276,7 +276,7 @@
     @get:ExperimentalMaterial3ExpressiveApi
     @ExperimentalMaterial3ExpressiveApi
     /** Large sized corner shape, slightly larger than [Large] */
-    val LargeIncreased: CornerBasedShape = RoundedCornerShape(16.dp)
+    val LargeIncreased: CornerBasedShape = RoundedCornerShape(20.dp)
 
     /** Extra large sized corner shape */
     val ExtraLarge: CornerBasedShape = ShapeTokens.CornerExtraLarge
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
index a97f7c0..5ca702d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
@@ -18,6 +18,9 @@
 
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.animate
+import androidx.compose.animation.core.snap
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Box
@@ -54,6 +57,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
+import kotlin.jvm.JvmName
 import kotlinx.coroutines.CancellationException
 
 /**
@@ -168,7 +172,7 @@
             "Attempted to animate to partial expanded when skipPartiallyExpanded was enabled. Set" +
                 " skipPartiallyExpanded to false to use this function."
         }
-        animateTo(PartiallyExpanded)
+        animateTo(PartiallyExpanded, showMotionSpec)
     }
 
     /**
@@ -183,7 +187,7 @@
                 hasPartiallyExpandedState -> PartiallyExpanded
                 else -> Expanded
             }
-        animateTo(targetValue)
+        animateTo(targetValue, showMotionSpec)
     }
 
     /**
@@ -197,7 +201,7 @@
             "Attempted to animate to hidden when skipHiddenState was enabled. Set skipHiddenState" +
                 " to false to use this function."
         }
-        animateTo(Hidden)
+        animateTo(Hidden, hideMotionSpec)
     }
 
     /**
@@ -205,15 +209,31 @@
      * [currentValue] will be updated to the [targetValue] without updating the offset.
      *
      * @param targetValue The target value of the animation
+     * @param animationSpec an [AnimationSpec]
+     * @param velocity an initial velocity for the animation
      * @throws CancellationException if the interaction interrupted by another interaction like a
      *   gesture interaction or another programmatic interaction like a [animateTo] or [snapTo]
      *   call.
      */
     internal suspend fun animateTo(
         targetValue: SheetValue,
+        animationSpec: FiniteAnimationSpec<Float>,
         velocity: Float = anchoredDraggableState.lastVelocity
     ) {
-        anchoredDraggableState.animateTo(targetValue, velocity)
+        anchoredDraggableState.anchoredDrag(targetValue = targetValue) { anchors, latestTarget ->
+            val targetOffset = anchors.positionOf(latestTarget)
+            if (!targetOffset.isNaN()) {
+                var prev = if (offset.isNaN()) 0f else offset
+                animate(prev, targetOffset, velocity, animationSpec) { value, velocity ->
+                    // Our onDrag coerces the value within the bounds, but an animation may
+                    // overshoot, for example a spring animation or an overshooting interpolator
+                    // We respect the user's intention and allow the overshoot, but still use
+                    // DraggableState's drag for its mutex.
+                    dragTo(value, velocity)
+                    prev = value
+                }
+            }
+        }
     }
 
     /**
@@ -235,18 +255,24 @@
         anchoredDraggableState.settle(velocity)
     }
 
+    internal var anchoredDraggableMotionSpec: AnimationSpec<Float> = BottomSheetAnimationSpec
+
     internal var anchoredDraggableState =
         AnchoredDraggableState(
             initialValue = initialValue,
-            animationSpec = BottomSheetAnimationSpec,
+            animationSpec = { anchoredDraggableMotionSpec },
             confirmValueChange = confirmValueChange,
             positionalThreshold = { with(density) { 56.dp.toPx() } },
             velocityThreshold = { with(density) { 125.dp.toPx() } },
         )
 
-    internal val offset: Float?
+    internal val offset: Float
         get() = anchoredDraggableState.offset
 
+    internal var showMotionSpec: FiniteAnimationSpec<Float> = snap()
+
+    internal var hideMotionSpec: FiniteAnimationSpec<Float> = snap()
+
     companion object {
         /** The default [Saver] implementation for [SheetState]. */
         fun Saver(
@@ -430,6 +456,7 @@
 }
 
 private val DragHandleVerticalPadding = 22.dp
-/** The default animation spec used by [SheetState]. */
+
+/** A function that provides the default animation spec used by [SheetState]. */
 private val BottomSheetAnimationSpec: AnimationSpec<Float> =
     tween(durationMillis = 300, easing = FastOutSlowInEasing)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ShortNavigationBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ShortNavigationBar.kt
index e0fd8eb..553c9ad 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ShortNavigationBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ShortNavigationBar.kt
@@ -44,6 +44,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMap
+import kotlin.jvm.JvmInline
 import kotlin.math.roundToInt
 
 /**
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index 02fe5f1..d7cb737 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -43,6 +43,7 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentWidth
 import androidx.compose.foundation.progressSemantics
+import androidx.compose.material3.internal.IncreaseHorizontalSemanticsBounds
 import androidx.compose.material3.internal.Strings
 import androidx.compose.material3.internal.awaitHorizontalPointerSlopOrCancellation
 import androidx.compose.material3.internal.getString
@@ -99,6 +100,7 @@
 import androidx.compose.ui.util.packFloats
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackFloat2
+import kotlin.jvm.JvmInline
 import kotlin.math.abs
 import kotlin.math.floor
 import kotlin.math.max
@@ -737,9 +739,6 @@
             enabled
         )
 
-    val startThumbSemantics = Modifier.rangeSliderStartThumbSemantics(state, enabled)
-    val endThumbSemantics = Modifier.rangeSliderEndThumbSemantics(state, enabled)
-
     val startContentDescription = getString(Strings.SliderRangeStart)
     val endContentDescription = getString(Strings.SliderRangeEnd)
 
@@ -750,11 +749,11 @@
                     Modifier.layoutId(RangeSliderComponents.STARTTHUMB)
                         .wrapContentWidth()
                         .onSizeChanged { state.startThumbWidth = it.width.toFloat() }
+                        .rangeSliderStartThumbSemantics(state, enabled)
                         .semantics(mergeDescendants = true) {
                             contentDescription = startContentDescription
                         }
                         .focusable(enabled, startInteractionSource)
-                        .then(startThumbSemantics)
             ) {
                 startThumb(state)
             }
@@ -763,11 +762,11 @@
                     Modifier.layoutId(RangeSliderComponents.ENDTHUMB)
                         .wrapContentWidth()
                         .onSizeChanged { state.endThumbWidth = it.width.toFloat() }
+                        .rangeSliderEndThumbSemantics(state, enabled)
                         .semantics(mergeDescendants = true) {
                             contentDescription = endContentDescription
                         }
                         .focusable(enabled, endInteractionSource)
-                        .then(endThumbSemantics)
             ) {
                 endThumb(state)
             }
@@ -1486,6 +1485,7 @@
                 }
             )
         }
+        .then(IncreaseHorizontalSemanticsBounds)
         .progressSemantics(
             state.value,
             state.valueRange.start..state.valueRange.endInclusive,
@@ -1547,6 +1547,7 @@
                 }
             )
         }
+        .then(IncreaseHorizontalSemanticsBounds)
         .progressSemantics(state.activeRangeStart, valueRange, state.startSteps)
 }
 
@@ -1605,6 +1606,7 @@
                 }
             )
         }
+        .then(IncreaseHorizontalSemanticsBounds)
         .progressSemantics(state.activeRangeEnd, valueRange, state.endSteps)
 }
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt
index 06b06b7..2142d27 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt
@@ -23,8 +23,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.paddingFromBaseline
 import androidx.compose.foundation.layout.widthIn
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.internal.Icons
 import androidx.compose.material3.internal.Strings
 import androidx.compose.material3.internal.getString
 import androidx.compose.material3.tokens.SnackbarTokens
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SnackbarHost.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SnackbarHost.kt
index 2025643..01b9fdf 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SnackbarHost.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SnackbarHost.kt
@@ -18,10 +18,10 @@
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.FastOutSlowInEasing
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.internal.Strings
+import androidx.compose.material3.internal.getString
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.RecomposeScope
@@ -40,6 +40,7 @@
 import androidx.compose.ui.semantics.LiveRegionMode
 import androidx.compose.ui.semantics.dismiss
 import androidx.compose.ui.semantics.liveRegion
+import androidx.compose.ui.semantics.paneTitle
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.util.fastFilterNotNull
 import androidx.compose.ui.util.fastForEach
@@ -318,12 +319,14 @@
 
 // TODO: to be replaced with the public customizable implementation
 // it's basically tweaked nullable version of Crossfade
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun FadeInFadeOutWithScale(
     current: SnackbarData?,
     modifier: Modifier = Modifier,
     content: @Composable (SnackbarData) -> Unit
 ) {
+    val a11yPaneTitle = getString(Strings.SnackbarPaneTitle)
     val state = remember { FadeInFadeOutState<SnackbarData?>() }
     if (current != state.current) {
         state.current = current
@@ -335,22 +338,10 @@
         keys.fastFilterNotNull().fastMapTo(state.items) { key ->
             FadeInFadeOutAnimationItem(key) { children ->
                 val isVisible = key == current
-                val duration = if (isVisible) SnackbarFadeInMillis else SnackbarFadeOutMillis
-                val delay = SnackbarFadeOutMillis + SnackbarInBetweenDelayMillis
-                val animationDelay =
-                    if (isVisible && keys.fastFilterNotNull().size != 1) {
-                        delay
-                    } else {
-                        0
-                    }
+                // TODO Load the motionScheme tokens from the component tokens file
                 val opacity =
                     animatedOpacity(
-                        animation =
-                            tween(
-                                easing = LinearEasing,
-                                delayMillis = animationDelay,
-                                durationMillis = duration
-                            ),
+                        animation = MotionSchemeKeyTokens.FastEffects.value(),
                         visible = isVisible,
                         onAnimationFinish = {
                             if (key != state.current) {
@@ -362,12 +353,8 @@
                     )
                 val scale =
                     animatedScale(
-                        animation =
-                            tween(
-                                easing = FastOutSlowInEasing,
-                                delayMillis = animationDelay,
-                                durationMillis = duration
-                            ),
+                        // TODO Load the motionScheme tokens from the component tokens file
+                        animation = MotionSchemeKeyTokens.FastSpatial.value(),
                         visible = isVisible
                     )
                 Box(
@@ -382,6 +369,7 @@
                                 key.dismiss()
                                 true
                             }
+                            paneTitle = a11yPaneTitle
                         }
                 ) {
                     children()
@@ -431,7 +419,3 @@
     }
     return scale.asState()
 }
-
-private const val SnackbarFadeInMillis = 150
-private const val SnackbarFadeOutMillis = 75
-private const val SnackbarInBetweenDelayMillis = 0
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
index 1eb3f62..3dbcda3 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
@@ -21,13 +21,12 @@
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CornerBasedShape
 import androidx.compose.foundation.shape.GenericShape
 import androidx.compose.foundation.shape.RoundedCornerShape
@@ -48,6 +47,7 @@
 import androidx.compose.ui.geometry.lerp
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.role
@@ -55,11 +55,9 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMap
+import androidx.compose.ui.util.fastFirst
 import androidx.compose.ui.util.fastMaxOfOrNull
 import androidx.compose.ui.util.fastSumBy
-import kotlin.math.roundToInt
 
 /**
  * A [SplitButton] let user define a button group consisting of 2 buttons. The leading button
@@ -93,10 +91,10 @@
     spacing: Dp = SplitButtonDefaults.Spacing,
 ) {
     SplitButtonLayout(
-        { LeadingButtonLayout(content = leadingButton) },
-        { Spacer(Modifier.size(spacing)) },
-        { TrailingButtonLayout(content = trailingButton) },
-        modifier
+        leadingButton,
+        trailingButton,
+        spacing,
+        modifier.minimumInteractiveComponentSize()
     )
 }
 
@@ -373,37 +371,47 @@
 @Composable
 private fun SplitButtonLayout(
     leadingButton: @Composable () -> Unit,
-    spacer: @Composable () -> Unit,
     trailingButton: @Composable () -> Unit,
+    spacing: Dp,
     modifier: Modifier = Modifier,
 ) {
     Layout(
         {
-            leadingButton()
-            spacer()
-            trailingButton()
+            // Override min component size enforcement to avoid create extra padding internally
+            // Enforce it on the parent instead
+            CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides Dp.Unspecified) {
+                Box(
+                    modifier = Modifier.layoutId(LeadingButtonLayoutId),
+                    contentAlignment = Alignment.Center,
+                    content = { leadingButton() }
+                )
+                Box(
+                    modifier = Modifier.layoutId(TrailingButtonLayoutId),
+                    contentAlignment = Alignment.Center,
+                    content = { trailingButton() }
+                )
+            }
         },
         modifier,
         measurePolicy = { measurables, constraints ->
-            val leadingButtonPlaceable = measurables[0].measure(constraints)
-
-            val spacerPlaceable =
-                measurables[1].measure(constraints.copy(maxHeight = leadingButtonPlaceable.height))
+            // TODO(b/355553502) Handle split button modifier constraints
+            val leadingButtonPlaceable =
+                measurables.fastFirst { it.layoutId == LeadingButtonLayoutId }.measure(constraints)
 
             val trailingButtonPlaceable =
-                measurables[2].measure(constraints.copy(maxHeight = leadingButtonPlaceable.height))
+                measurables
+                    .fastFirst { it.layoutId == TrailingButtonLayoutId }
+                    .measure(constraints.copy(maxHeight = leadingButtonPlaceable.height))
 
-            val placeables =
-                listOf(leadingButtonPlaceable, spacerPlaceable, trailingButtonPlaceable)
+            val placeables = listOf(leadingButtonPlaceable, trailingButtonPlaceable)
 
-            val width = placeables.fastSumBy { it.width }
+            val width = placeables.fastSumBy { it.width } + spacing.roundToPx()
             val height = placeables.fastMaxOfOrNull { it.height } ?: 0
 
             layout(width, height) {
                 leadingButtonPlaceable.placeRelative(0, 0)
-                spacerPlaceable.placeRelative(leadingButtonPlaceable.width, 0)
                 trailingButtonPlaceable.placeRelative(
-                    x = leadingButtonPlaceable.width + spacerPlaceable.width,
+                    x = leadingButtonPlaceable.width + spacing.roundToPx(),
                     y = 0
                 )
             }
@@ -411,68 +419,6 @@
     )
 }
 
-@Composable
-private fun TrailingButtonLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
-    Layout(
-        content,
-        modifier,
-        measurePolicy = { measurables, constraints ->
-            val placeables = measurables.fastMap { measurable -> measurable.measure(constraints) }
-
-            val measuredWidth = placeables.fastSumBy { it.width }
-            val measuredHeight = placeables.fastMaxOfOrNull { it.height } ?: 0
-
-            // TODO Handle minimum tap target when element is less than 48.dp
-            val width = measuredWidth.coerceAtLeast(48.dp.roundToPx())
-            val height = measuredHeight.coerceAtLeast(48.dp.roundToPx())
-
-            layout(width, height) {
-                var x = 0
-                var y: Int
-                placeables.fastForEach { placeable ->
-                    y = ((height - placeable.height) / 2f).roundToInt()
-                    placeable.placeRelative(x, y)
-
-                    x += placeable.width
-                }
-            }
-        }
-    )
-}
-
-@Composable
-private fun LeadingButtonLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
-    Layout(
-        content,
-        modifier,
-        measurePolicy = { measurables, constraints ->
-            val placeables = measurables.fastMap { measurable -> measurable.measure(constraints) }
-
-            val measuredWidth = placeables.fastSumBy { it.width }
-            val measuredHeight = placeables.fastMaxOfOrNull { it.height } ?: 0
-
-            // TODO Handle minimum tap target when element is less than 48.dp
-            val width = measuredWidth.coerceAtLeast(48.dp.roundToPx())
-            val height = measuredHeight.coerceAtLeast(48.dp.roundToPx())
-
-            layout(width, height) {
-                // Aligning children from end to start, in case visual bound is less than coerced
-                // layout size
-                var i = placeables.lastIndex
-                var x = width
-
-                while (i >= 0) {
-                    val placeable = placeables[i]
-                    x -= placeable.width
-                    val y = ((height - placeable.height) / 2f).roundToInt()
-                    placeable.placeRelative(x, y)
-                    i--
-                }
-            }
-        }
-    )
-}
-
 // TODO Replace default value with tokens
 /** Contains default values used by [SplitButton] and its style variants. */
 @ExperimentalMaterial3ExpressiveApi
@@ -555,34 +501,28 @@
     ) {
         @Suppress("NAME_SHADOWING")
         val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
-        CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides Dp.Unspecified) {
-            Surface(
-                onClick = onClick,
-                modifier = modifier.semantics { role = Role.Button },
-                enabled = enabled,
-                shape = shape,
-                color = colors.containerColor,
+        Surface(
+            onClick = onClick,
+            modifier = modifier.semantics { role = Role.Button },
+            enabled = enabled,
+            shape = shape,
+            color = colors.containerColor,
+            contentColor = colors.contentColor,
+            shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+            border = border,
+            interactionSource = interactionSource
+        ) {
+            ProvideContentColorTextStyle(
                 contentColor = colors.contentColor,
-                shadowElevation =
-                    elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
-                border = border,
-                interactionSource = interactionSource
+                textStyle = MaterialTheme.typography.labelLarge
             ) {
-                ProvideContentColorTextStyle(
-                    contentColor = colors.contentColor,
-                    textStyle = MaterialTheme.typography.labelLarge
-                ) {
-                    Row(
-                        Modifier.defaultMinSize(
-                                minWidth = LeadingButtonMinWidth,
-                                minHeight = MinHeight
-                            )
-                            .padding(contentPadding),
-                        horizontalArrangement = Arrangement.Center,
-                        verticalAlignment = Alignment.CenterVertically,
-                        content = content
-                    )
-                }
+                Row(
+                    Modifier.defaultMinSize(minWidth = LeadingButtonMinWidth, minHeight = MinHeight)
+                        .padding(contentPadding),
+                    horizontalArrangement = Arrangement.Center,
+                    verticalAlignment = Alignment.CenterVertically,
+                    content = content
+                )
             }
         }
     }
@@ -631,33 +571,30 @@
         @Suppress("NAME_SHADOWING")
         val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
 
-        CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides Dp.Unspecified) {
-            Surface(
-                onClick = onClick,
-                modifier = modifier.semantics { role = Role.Button },
-                enabled = enabled,
-                shape = shape,
-                color = colors.containerColor,
+        Surface(
+            onClick = onClick,
+            modifier = modifier.semantics { role = Role.Button },
+            enabled = enabled,
+            shape = shape,
+            color = colors.containerColor,
+            contentColor = colors.contentColor,
+            shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+            border = border,
+            interactionSource = interactionSource
+        ) {
+            ProvideContentColorTextStyle(
                 contentColor = colors.contentColor,
-                shadowElevation =
-                    elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
-                border = border,
-                interactionSource = interactionSource
+                textStyle = MaterialTheme.typography.labelLarge
             ) {
-                ProvideContentColorTextStyle(
-                    contentColor = colors.contentColor,
-                    textStyle = MaterialTheme.typography.labelLarge
-                ) {
-                    Row(
-                        Modifier.defaultMinSize(
-                            minWidth = TrailingButtonMinWidth,
-                            minHeight = MinHeight
-                        ),
-                        horizontalArrangement = Arrangement.Center,
-                        verticalAlignment = Alignment.CenterVertically,
-                        content = content
-                    )
-                }
+                Row(
+                    Modifier.defaultMinSize(
+                        minWidth = TrailingButtonMinWidth,
+                        minHeight = MinHeight
+                    ),
+                    horizontalArrangement = Arrangement.Center,
+                    verticalAlignment = Alignment.CenterVertically,
+                    content = content
+                )
             }
         }
     }
@@ -878,3 +815,6 @@
         content = content
     )
 }
+
+private const val LeadingButtonLayoutId = "LeadingButton"
+private const val TrailingButtonLayoutId = "TrailingButton"
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismissBox.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismissBox.kt
index 87406b4..64040d8 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismissBox.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismissBox.kt
@@ -73,7 +73,7 @@
     internal val anchoredDraggableState =
         AnchoredDraggableState(
             initialValue = initialValue,
-            animationSpec = AnchoredDraggableDefaults.AnimationSpec,
+            animationSpec = { AnchoredDraggableDefaults.AnimationSpec },
             confirmValueChange = confirmValueChange,
             positionalThreshold = positionalThreshold,
             velocityThreshold = { with(density) { DismissVelocityThreshold.toPx() } }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
index 2bcce38..a65c503 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
@@ -18,8 +18,8 @@
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.SnapSpec
-import androidx.compose.animation.core.TweenSpec
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.indication
@@ -31,6 +31,7 @@
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.selection.toggleable
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.SwitchTokens
 import androidx.compose.material3.tokens.SwitchTokens.TrackOutlineWidth
 import androidx.compose.runtime.Composable
@@ -131,6 +132,7 @@
     )
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 @Suppress("ComposableLambdaParameterNaming", "ComposableLambdaParameterPosition")
 private fun SwitchImpl(
@@ -154,7 +156,14 @@
         Box(
             modifier =
                 Modifier.align(Alignment.CenterStart)
-                    .then(ThumbElement(interactionSource, checked))
+                    .then(
+                        ThumbElement(
+                            interactionSource = interactionSource,
+                            checked = checked,
+                            // TODO Load the motionScheme tokens from the component tokens file
+                            animationSpec = MotionSchemeKeyTokens.FastSpatial.value()
+                        )
+                    )
                     .indication(
                         interactionSource = interactionSource,
                         indication =
@@ -177,8 +186,9 @@
 private data class ThumbElement(
     val interactionSource: InteractionSource,
     val checked: Boolean,
+    val animationSpec: FiniteAnimationSpec<Float>,
 ) : ModifierNodeElement<ThumbNode>() {
-    override fun create() = ThumbNode(interactionSource, checked)
+    override fun create() = ThumbNode(interactionSource, checked, animationSpec)
 
     override fun update(node: ThumbNode) {
         node.interactionSource = interactionSource
@@ -186,6 +196,7 @@
             node.invalidateMeasurement()
         }
         node.checked = checked
+        node.animationSpec = animationSpec
         node.update()
     }
 
@@ -193,12 +204,14 @@
         name = "switchThumb"
         properties["interactionSource"] = interactionSource
         properties["checked"] = checked
+        properties["animationSpec"] = animationSpec
     }
 }
 
 private class ThumbNode(
     var interactionSource: InteractionSource,
     var checked: Boolean,
+    var animationSpec: FiniteAnimationSpec<Float>,
 ) : Modifier.Node(), LayoutModifierNode {
 
     override val shouldAutoInvalidate: Boolean
@@ -258,13 +271,13 @@
 
         if (sizeAnim?.targetValue != size) {
             coroutineScope.launch {
-                sizeAnim?.animateTo(size, if (isPressed) SnapSpec else AnimationSpec)
+                sizeAnim?.animateTo(size, if (isPressed) SnapSpec else animationSpec)
             }
         }
 
         if (offsetAnim?.targetValue != offset) {
             coroutineScope.launch {
-                offsetAnim?.animateTo(offset, if (isPressed) SnapSpec else AnimationSpec)
+                offsetAnim?.animateTo(offset, if (isPressed) SnapSpec else animationSpec)
             }
         }
 
@@ -617,4 +630,3 @@
 private val SwitchHeight = SwitchTokens.TrackHeight
 private val ThumbPadding = (SwitchHeight - ThumbDiameter) / 2
 private val SnapSpec = SnapSpec<Float>()
-private val AnimationSpec = TweenSpec<Float>(durationMillis = 100)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
index c5e3fa1..a6263eb 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
@@ -17,8 +17,6 @@
 package androidx.compose.material3
 
 import androidx.compose.animation.animateColor
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -33,6 +31,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredWidth
 import androidx.compose.foundation.selection.selectable
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.PrimaryNavigationTabTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -270,6 +269,7 @@
  * component uses [LocalContentColor] to provide an interpolated value between [activeColor] and
  * [inactiveColor] depending on the animation status.
  */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun TabTransition(
     activeColor: Color,
@@ -278,17 +278,16 @@
     content: @Composable () -> Unit
 ) {
     val transition = updateTransition(selected)
+    // TODO Load the motionScheme tokens from the component tokens file
     val color by
         transition.animateColor(
             transitionSpec = {
                 if (false isTransitioningTo true) {
-                    tween(
-                        durationMillis = TabFadeInAnimationDuration,
-                        delayMillis = TabFadeInAnimationDelay,
-                        easing = LinearEasing
-                    )
+                    // Fade-in
+                    MotionSchemeKeyTokens.DefaultEffects.value()
                 } else {
-                    tween(durationMillis = TabFadeOutAnimationDuration, easing = LinearEasing)
+                    // Fade-out
+                    MotionSchemeKeyTokens.FastEffects.value()
                 }
             }
         ) {
@@ -425,11 +424,6 @@
 private val SmallTabHeight = PrimaryNavigationTabTokens.ContainerHeight
 private val LargeTabHeight = 72.dp
 
-// Tab transition specifications
-private const val TabFadeInAnimationDuration = 150
-private const val TabFadeInAnimationDelay = 100
-private const val TabFadeOutAnimationDuration = 100
-
 // The horizontal padding on the left and right of text
 internal val HorizontalTextPadding = 16.dp
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
index f6b5f03..b1ebe95 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
@@ -17,12 +17,10 @@
 package androidx.compose.material3
 
 import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.animateDpAsState
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.background
 import androidx.compose.foundation.horizontalScroll
@@ -38,6 +36,7 @@
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.PrimaryNavigationTabTokens
 import androidx.compose.material3.tokens.SecondaryNavigationTabTokens
 import androidx.compose.runtime.Composable
@@ -556,7 +555,7 @@
     fun setTabPositions(positions: List<TabPosition>)
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun TabRowImpl(
     modifier: Modifier,
@@ -571,6 +570,9 @@
         color = containerColor,
         contentColor = contentColor
     ) {
+        // TODO Load the motionScheme tokens from the component tokens file
+        val tabIndicatorAnimationSpec: FiniteAnimationSpec<Dp> =
+            MotionSchemeKeyTokens.DefaultSpatial.value()
         val scope = remember {
             object : TabIndicatorScope, TabPositionsHolder {
 
@@ -597,7 +599,12 @@
                     matchContentSize: Boolean
                 ): Modifier =
                     this.then(
-                        TabIndicatorModifier(tabPositions, selectedTabIndex, matchContentSize)
+                        TabIndicatorModifier(
+                            tabPositions,
+                            selectedTabIndex,
+                            matchContentSize,
+                            tabIndicatorAnimationSpec
+                        )
                     )
 
                 override fun setTabPositions(positions: List<TabPosition>) {
@@ -681,7 +688,7 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun ScrollableTabRowImpl(
     selectedTabIndex: Int,
@@ -706,9 +713,18 @@
         contentColor = contentColor
     ) {
         val coroutineScope = rememberCoroutineScope()
+        // TODO Load the motionScheme tokens from the component tokens file
+        val scrollAnimationSpec: FiniteAnimationSpec<Float> =
+            MotionSchemeKeyTokens.DefaultSpatial.value()
+        val tabIndicatorAnimationSpec: FiniteAnimationSpec<Dp> =
+            MotionSchemeKeyTokens.DefaultSpatial.value()
         val scrollableTabData =
             remember(scrollState, coroutineScope) {
-                ScrollableTabData(scrollState = scrollState, coroutineScope = coroutineScope)
+                ScrollableTabData(
+                    scrollState = scrollState,
+                    coroutineScope = coroutineScope,
+                    animationSpec = scrollAnimationSpec
+                )
             }
 
         val scope = remember {
@@ -737,7 +753,12 @@
                     matchContentSize: Boolean
                 ): Modifier =
                     this.then(
-                        TabIndicatorModifier(tabPositions, selectedTabIndex, matchContentSize)
+                        TabIndicatorModifier(
+                            tabPositions,
+                            selectedTabIndex,
+                            matchContentSize,
+                            tabIndicatorAnimationSpec
+                        )
                     )
 
                 override fun setTabPositions(positions: List<TabPosition>) {
@@ -841,6 +862,7 @@
     val tabPositionsState: State<List<TabPosition>>,
     val selectedTabIndex: Int,
     val followContentSize: Boolean,
+    val animationSpec: FiniteAnimationSpec<Dp>
 ) : ModifierNodeElement<TabIndicatorOffsetNode>() {
 
     override fun create(): TabIndicatorOffsetNode {
@@ -848,6 +870,7 @@
             tabPositionsState = tabPositionsState,
             selectedTabIndex = selectedTabIndex,
             followContentSize = followContentSize,
+            animationSpec = animationSpec
         )
     }
 
@@ -855,6 +878,7 @@
         node.tabPositionsState = tabPositionsState
         node.selectedTabIndex = selectedTabIndex
         node.followContentSize = followContentSize
+        node.animationSpec = animationSpec
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -865,7 +889,8 @@
 internal class TabIndicatorOffsetNode(
     var tabPositionsState: State<List<TabPosition>>,
     var selectedTabIndex: Int,
-    var followContentSize: Boolean
+    var followContentSize: Boolean,
+    var animationSpec: FiniteAnimationSpec<Dp>
 ) : Modifier.Node(), LayoutModifierNode {
 
     private var offsetAnimatable: Animatable<Dp, AnimationVector1D>? = null
@@ -894,7 +919,7 @@
                     ?: Animatable(initialWidth!!, Dp.VectorConverter).also { widthAnimatable = it }
 
             if (currentTabWidth != widthAnim.targetValue) {
-                coroutineScope.launch { widthAnim.animateTo(currentTabWidth, TabRowIndicatorSpec) }
+                coroutineScope.launch { widthAnim.animateTo(currentTabWidth, animationSpec) }
             }
         } else {
             initialWidth = currentTabWidth
@@ -910,7 +935,7 @@
                     }
 
             if (indicatorOffset != offsetAnim.targetValue) {
-                coroutineScope.launch { offsetAnim.animateTo(indicatorOffset, TabRowIndicatorSpec) }
+                coroutineScope.launch { offsetAnim.animateTo(indicatorOffset, animationSpec) }
             }
         } else {
             initialOffset = indicatorOffset
@@ -998,6 +1023,7 @@
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun ScrollableTabRowWithSubcomposeImpl(
     selectedTabIndex: Int,
@@ -1012,9 +1038,16 @@
 ) {
     Surface(modifier = modifier, color = containerColor, contentColor = contentColor) {
         val coroutineScope = rememberCoroutineScope()
+        // TODO Load the motionScheme tokens from the component tokens file
+        val scrollAnimationSpec: FiniteAnimationSpec<Float> =
+            MotionSchemeKeyTokens.DefaultSpatial.value()
         val scrollableTabData =
             remember(scrollState, coroutineScope) {
-                ScrollableTabData(scrollState = scrollState, coroutineScope = coroutineScope)
+                ScrollableTabData(
+                    scrollState = scrollState,
+                    coroutineScope = coroutineScope,
+                    animationSpec = scrollAnimationSpec
+                )
             }
         SubcomposeLayout(
             Modifier.fillMaxWidth()
@@ -1252,6 +1285,7 @@
      * @param currentTabPosition [TabPosition] of the currently selected tab. This is used to
      *   calculate the offset of the indicator this modifier is applied to, as well as its width.
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     fun Modifier.tabIndicatorOffset(currentTabPosition: TabPosition): Modifier =
         composed(
             inspectorInfo =
@@ -1260,15 +1294,16 @@
                     value = currentTabPosition
                 }
         ) {
+            // TODO Load the motionScheme tokens from the component tokens file
             val currentTabWidth by
                 animateDpAsState(
                     targetValue = currentTabPosition.width,
-                    animationSpec = TabRowIndicatorSpec
+                    animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value()
                 )
             val indicatorOffset by
                 animateDpAsState(
                     targetValue = currentTabPosition.left,
-                    animationSpec = TabRowIndicatorSpec
+                    animationSpec = MotionSchemeKeyTokens.DefaultSpatial.value()
                 )
             fillMaxWidth()
                 .wrapContentSize(Alignment.BottomStart)
@@ -1286,7 +1321,8 @@
 /** Class holding onto state needed for [ScrollableTabRow] */
 private class ScrollableTabData(
     private val scrollState: ScrollState,
-    private val coroutineScope: CoroutineScope
+    private val coroutineScope: CoroutineScope,
+    private val animationSpec: FiniteAnimationSpec<Float>
 ) {
     private var selectedTab: Int? = null
 
@@ -1306,10 +1342,7 @@
                 val calculatedOffset = it.calculateTabOffset(density, edgeOffset, tabPositions)
                 if (scrollState.value != calculatedOffset) {
                     coroutineScope.launch {
-                        scrollState.animateScrollTo(
-                            calculatedOffset,
-                            animationSpec = ScrollableTabRowScrollSpec
-                        )
+                        scrollState.animateScrollTo(calculatedOffset, animationSpec = animationSpec)
                     }
                 }
             }
@@ -1341,11 +1374,3 @@
 }
 
 private val ScrollableTabRowMinimumTabWidth = 90.dp
-
-/** [AnimationSpec] used when scrolling to a tab that is not fully visible. */
-private val ScrollableTabRowScrollSpec: AnimationSpec<Float> =
-    tween(durationMillis = 250, easing = FastOutSlowInEasing)
-
-/** [AnimationSpec] used when an indicator is updating width and/or offset. */
-private val TabRowIndicatorSpec: AnimationSpec<Dp> =
-    tween(durationMillis = 250, easing = FastOutSlowInEasing)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
index 1f41226..aa4c7db 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
@@ -17,6 +17,7 @@
 package androidx.compose.material3
 
 import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.collectIsFocusedAsState
@@ -28,10 +29,20 @@
 import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.KeyboardActionHandler
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
 import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.material3.internal.AboveLabelBottomPadding
+import androidx.compose.material3.internal.AboveLabelHorizontalPadding
 import androidx.compose.material3.internal.ContainerId
 import androidx.compose.material3.internal.HorizontalIconPadding
 import androidx.compose.material3.internal.IconDefaultSizeModifier
@@ -49,12 +60,13 @@
 import androidx.compose.material3.internal.TextFieldId
 import androidx.compose.material3.internal.TextFieldLabelExtraPadding
 import androidx.compose.material3.internal.TrailingId
-import androidx.compose.material3.internal.ZeroConstraints
 import androidx.compose.material3.internal.defaultErrorSemantics
 import androidx.compose.material3.internal.getString
 import androidx.compose.material3.internal.heightOrZero
 import androidx.compose.material3.internal.layoutId
+import androidx.compose.material3.internal.subtractConstraintSafely
 import androidx.compose.material3.internal.widthOrZero
+import androidx.compose.material3.tokens.MotionTokens.EasingEmphasizedAccelerateCubicBezier
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.State
@@ -76,6 +88,7 @@
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
@@ -107,10 +120,21 @@
  *
  * If you are looking for an outlined version, see [OutlinedTextField].
  *
+ * This overload of [TextField] uses [TextFieldState] to keep track of its text content and position
+ * of the cursor or selection.
+ *
  * A simple single line text field looks like:
  *
  * @sample androidx.compose.material3.samples.SimpleTextFieldSample
  *
+ * You can control the initial text input and selection:
+ *
+ * @sample androidx.compose.material3.samples.TextFieldWithInitialValueAndSelection
+ *
+ * Use input and output transformations to control user input and the displayed text:
+ *
+ * @sample androidx.compose.material3.samples.TextFieldWithTransformations
+ *
  * You may provide a placeholder:
  *
  * @sample androidx.compose.material3.samples.TextFieldWithPlaceholder
@@ -131,13 +155,187 @@
  *
  * @sample androidx.compose.material3.samples.TextFieldWithSupportingText
  *
- * Password text field example:
+ * You can change the content padding to create a dense text field:
  *
- * @sample androidx.compose.material3.samples.PasswordTextField
+ * @sample androidx.compose.material3.samples.DenseTextFieldContentPadding
  *
  * Hiding a software keyboard on IME action performed:
  *
  * @sample androidx.compose.material3.samples.TextFieldWithHideKeyboardOnImeAction
+ * @param state [TextFieldState] object that holds the internal editing state of the text field.
+ * @param modifier the [Modifier] to be applied to this text field.
+ * @param enabled controls the enabled state of this text field. When `false`, this component will
+ *   not respond to user input, and it will appear visually disabled and disabled to accessibility
+ *   services.
+ * @param readOnly controls the editable state of the text field. When `true`, the text field cannot
+ *   be modified. However, a user can focus it and copy text from it. Read-only text fields are
+ *   usually used to display pre-filled forms that a user cannot edit.
+ * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
+ * @param labelPosition the position of the label. See [TextFieldLabelPosition].
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ *   [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
+ * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+ *   default text style uses [Typography.bodyLarge].
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+ *   container.
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ *   container.
+ * @param prefix the optional prefix to be displayed before the input text in the text field.
+ * @param suffix the optional suffix to be displayed after the input text in the text field.
+ * @param supportingText the optional supporting text to be displayed below the text field.
+ * @param isError indicates if the text field's current value is in error. When `true`, the
+ *   components of the text field will be displayed in an error color, and an error will be
+ *   announced to accessibility services.
+ * @param inputTransformation optional [InputTransformation] that will be used to transform changes
+ *   to the [TextFieldState] made by the user. The transformation will be applied to changes made by
+ *   hardware and software keyboard events, pasting or dropping text, accessibility services, and
+ *   tests. The transformation will _not_ be applied when changing the [state] programmatically, or
+ *   when the transformation is changed. If the transformation is changed on an existing text field,
+ *   it will be applied to the next user edit. The transformation will not immediately affect the
+ *   current [state].
+ * @param outputTransformation optional [OutputTransformation] that transforms how the contents of
+ *   the text field are presented.
+ * @param keyboardOptions software keyboard options that contains configuration such as
+ *   [KeyboardType] and [ImeAction].
+ * @param onKeyboardAction called when the user presses the action button in the input method editor
+ *   (IME), or by pressing the enter key on a hardware keyboard. By default this parameter is null,
+ *   and would execute the default behavior for a received IME Action e.g., [ImeAction.Done] would
+ *   close the keyboard, [ImeAction.Next] would switch the focus to the next focusable item on the
+ *   screen.
+ * @param lineLimits whether the text field should be [SingleLine], scroll horizontally, and ignore
+ *   newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all newline
+ *   characters ('\n') within the text will be replaced with regular whitespace (' ').
+ * @param onTextLayout Callback that is executed when the text layout becomes queryable. The
+ *   callback receives a function that returns a [TextLayoutResult] if the layout can be calculated,
+ *   or null if it cannot. The function reads the layout result from a snapshot state object, and
+ *   will invalidate its caller when the layout result changes. A [TextLayoutResult] object contains
+ *   paragraph information, size of the text, baselines and other details. [Density] scope is the
+ *   one that was used while creating the given text layout.
+ * @param scrollState scroll state that manages either horizontal or vertical scroll of the text
+ *   field. If [lineLimits] is [SingleLine], this text field is treated as single line with
+ *   horizontal scroll behavior. Otherwise, the text field becomes vertically scrollable.
+ * @param shape defines the shape of this text field's container.
+ * @param colors [TextFieldColors] that will be used to resolve the colors used for this text field
+ *   in different states. See [TextFieldDefaults.colors].
+ * @param contentPadding the padding applied to the inner text field that separates it from the
+ *   surrounding elements of the text field. Note that the padding values may not be respected if
+ *   they are incompatible with the text field's size constraints or layout. See
+ *   [TextFieldDefaults.contentPaddingWithLabel] and [TextFieldDefaults.contentPaddingWithoutLabel].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this text field. You can use this to change the text field's
+ *   appearance or preview the text field in different states. Note that if `null` is provided,
+ *   interactions will still happen internally.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun TextField(
+    state: TextFieldState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    textStyle: TextStyle = LocalTextStyle.current,
+    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Default(),
+    label: @Composable (TextFieldLabelScope.() -> Unit)? = null,
+    placeholder: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    prefix: @Composable (() -> Unit)? = null,
+    suffix: @Composable (() -> Unit)? = null,
+    supportingText: @Composable (() -> Unit)? = null,
+    isError: Boolean = false,
+    inputTransformation: InputTransformation? = null,
+    outputTransformation: OutputTransformation? = null,
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+    onKeyboardAction: KeyboardActionHandler? = null,
+    lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+    onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
+    scrollState: ScrollState = rememberScrollState(),
+    shape: Shape = TextFieldDefaults.shape,
+    colors: TextFieldColors = TextFieldDefaults.colors(),
+    contentPadding: PaddingValues =
+        if (label == null || labelPosition == TextFieldLabelPosition.Above) {
+            TextFieldDefaults.contentPaddingWithoutLabel()
+        } else {
+            TextFieldDefaults.contentPaddingWithLabel()
+        },
+    interactionSource: MutableInteractionSource? = null,
+) {
+    @Suppress("NAME_SHADOWING")
+    val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+    // If color is not provided via the text style, use content color as a default
+    val textColor =
+        textStyle.color.takeOrElse {
+            val focused = interactionSource.collectIsFocusedAsState().value
+            colors.textColor(enabled, isError, focused)
+        }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+    CompositionLocalProvider(LocalTextSelectionColors provides colors.textSelectionColors) {
+        BasicTextField(
+            state = state,
+            modifier =
+                modifier
+                    .defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
+                    .defaultMinSize(
+                        minWidth = TextFieldDefaults.MinWidth,
+                        minHeight = TextFieldDefaults.MinHeight
+                    ),
+            enabled = enabled,
+            readOnly = readOnly,
+            textStyle = mergedTextStyle,
+            cursorBrush = SolidColor(colors.cursorColor(isError)),
+            keyboardOptions = keyboardOptions,
+            onKeyboardAction = onKeyboardAction,
+            lineLimits = lineLimits,
+            onTextLayout = onTextLayout,
+            interactionSource = interactionSource,
+            inputTransformation = inputTransformation,
+            outputTransformation = outputTransformation,
+            scrollState = scrollState,
+            decorator =
+                TextFieldDefaults.decorator(
+                    state = state,
+                    enabled = enabled,
+                    lineLimits = lineLimits,
+                    outputTransformation = outputTransformation,
+                    interactionSource = interactionSource,
+                    labelPosition = labelPosition,
+                    label = label,
+                    placeholder = placeholder,
+                    leadingIcon = leadingIcon,
+                    trailingIcon = trailingIcon,
+                    prefix = prefix,
+                    suffix = suffix,
+                    supportingText = supportingText,
+                    isError = isError,
+                    colors = colors,
+                    contentPadding = contentPadding,
+                    container = {
+                        TextFieldDefaults.Container(
+                            enabled = enabled,
+                            isError = isError,
+                            interactionSource = interactionSource,
+                            colors = colors,
+                            shape = shape,
+                        )
+                    }
+                )
+        )
+    }
+}
+
+/**
+ * <a href="https://m3.material.io/components/text-fields/overview" class="external"
+ * target="_blank">Material Design filled text field</a>.
+ *
+ * Text fields allow users to enter text into a UI. They typically appear in forms and dialogs.
+ * Filled text fields have more visual emphasis than outlined text fields, making them stand out
+ * when surrounded by other content and components.
+ *
+ * ![Filled text field
+ * image](https://developer.android.com/images/reference/androidx/compose/material3/filled-text-field.png)
+ *
+ * If you are looking for an outlined version, see [OutlinedTextField].
  *
  * If apart from input text change you also want to observe the cursor location, selection range, or
  * IME composition use the TextField overload with the [TextFieldValue] parameter instead.
@@ -153,9 +351,8 @@
  *   be modified. However, a user can focus it and copy text from it. Read-only text fields are
  *   usually used to display pre-filled forms that a user cannot edit.
  * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
- * @param label the optional label to be displayed inside the text field container. The default text
- *   style for internal [Text] is [Typography.bodySmall] when the text field is in focus and
- *   [Typography.bodyLarge] when the text field is not in focus
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ *   [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
  * @param placeholder the optional placeholder to be displayed when the text field is in focus and
  *   the input text is empty. The default text style for internal [Text] is [Typography.bodyLarge]
  * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
@@ -290,10 +487,6 @@
  *
  * If you are looking for an outlined version, see [OutlinedTextField].
  *
- * See example usage:
- *
- * @sample androidx.compose.material3.samples.TextFieldSample
- *
  * This overload provides access to the input text, cursor position, selection range and IME
  * composition. If you only want to observe an input text change, use the TextField overload with
  * the [String] parameter instead.
@@ -309,9 +502,8 @@
  *   be modified. However, a user can focus it and copy text from it. Read-only text fields are
  *   usually used to display pre-filled forms that a user cannot edit.
  * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
- * @param label the optional label to be displayed inside the text field container. The default text
- *   style for internal [Text] is [Typography.bodySmall] when the text field is in focus and
- *   [Typography.bodyLarge] when the text field is not in focus
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ *   [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
  * @param placeholder the optional placeholder to be displayed when the text field is in focus and
  *   the input text is empty. The default text style for internal [Text] is [Typography.bodyLarge]
  * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
@@ -448,14 +640,15 @@
     prefix: @Composable (() -> Unit)?,
     suffix: @Composable (() -> Unit)?,
     singleLine: Boolean,
-    animationProgress: Float,
+    labelPosition: TextFieldLabelPosition,
+    labelProgress: Float,
     container: @Composable () -> Unit,
     supporting: @Composable (() -> Unit)?,
     paddingValues: PaddingValues
 ) {
     val measurePolicy =
-        remember(singleLine, animationProgress, paddingValues) {
-            TextFieldMeasurePolicy(singleLine, animationProgress, paddingValues)
+        remember(singleLine, labelPosition, labelProgress, paddingValues) {
+            TextFieldMeasurePolicy(singleLine, labelPosition, labelProgress, paddingValues)
         }
     val layoutDirection = LocalLayoutDirection.current
     Layout(
@@ -520,19 +713,24 @@
                 }
             }
 
+            val labelPadding =
+                if (labelPosition == TextFieldLabelPosition.Above) {
+                    Modifier.padding(
+                        start = AboveLabelHorizontalPadding,
+                        end = AboveLabelHorizontalPadding,
+                        bottom = AboveLabelBottomPadding,
+                    )
+                } else {
+                    Modifier.padding(start = startPadding, end = endPadding)
+                }
             if (label != null) {
                 Box(
                     Modifier.layoutId(LabelId)
                         .heightIn(
-                            min =
-                                lerp(
-                                    MinTextLineHeight,
-                                    MinFocusedLabelLineHeight,
-                                    animationProgress
-                                )
+                            min = lerp(MinTextLineHeight, MinFocusedLabelLineHeight, labelProgress)
                         )
                         .wrapContentHeight()
-                        .padding(start = startPadding, end = endPadding)
+                        .then(labelPadding)
                 ) {
                     label()
                 }
@@ -574,7 +772,8 @@
 
 private class TextFieldMeasurePolicy(
     private val singleLine: Boolean,
-    private val animationProgress: Float,
+    private val labelPosition: TextFieldLabelPosition,
+    private val labelProgress: Float,
     private val paddingValues: PaddingValues
 ) : MeasurePolicy {
     override fun MeasureScope.measure(
@@ -592,41 +791,51 @@
         // measure leading icon
         val leadingPlaceable =
             measurables.fastFirstOrNull { it.layoutId == LeadingId }?.measure(looseConstraints)
-        occupiedSpaceHorizontally += widthOrZero(leadingPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(leadingPlaceable))
+        occupiedSpaceHorizontally += leadingPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, leadingPlaceable.heightOrZero)
 
         // measure trailing icon
         val trailingPlaceable =
             measurables
                 .fastFirstOrNull { it.layoutId == TrailingId }
                 ?.measure(looseConstraints.offset(horizontal = -occupiedSpaceHorizontally))
-        occupiedSpaceHorizontally += widthOrZero(trailingPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(trailingPlaceable))
+        occupiedSpaceHorizontally += trailingPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, trailingPlaceable.heightOrZero)
 
         // measure prefix
         val prefixPlaceable =
             measurables
                 .fastFirstOrNull { it.layoutId == PrefixId }
                 ?.measure(looseConstraints.offset(horizontal = -occupiedSpaceHorizontally))
-        occupiedSpaceHorizontally += widthOrZero(prefixPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(prefixPlaceable))
+        occupiedSpaceHorizontally += prefixPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, prefixPlaceable.heightOrZero)
 
         // measure suffix
         val suffixPlaceable =
             measurables
                 .fastFirstOrNull { it.layoutId == SuffixId }
                 ?.measure(looseConstraints.offset(horizontal = -occupiedSpaceHorizontally))
-        occupiedSpaceHorizontally += widthOrZero(suffixPlaceable)
-        occupiedSpaceVertically = max(occupiedSpaceVertically, heightOrZero(suffixPlaceable))
+        occupiedSpaceHorizontally += suffixPlaceable.widthOrZero
+        occupiedSpaceVertically = max(occupiedSpaceVertically, suffixPlaceable.heightOrZero)
 
-        // measure label
-        val labelConstraints =
-            looseConstraints.offset(
-                vertical = -bottomPaddingValue,
-                horizontal = -occupiedSpaceHorizontally
-            )
-        val labelPlaceable =
-            measurables.fastFirstOrNull { it.layoutId == LabelId }?.measure(labelConstraints)
+        val isLabelAbove = labelPosition == TextFieldLabelPosition.Above
+        val labelMeasurable = measurables.fastFirstOrNull { it.layoutId == LabelId }
+        var labelPlaceable: Placeable? = null
+        val labelIntrinsicHeight: Int
+        if (!isLabelAbove) {
+            // if label is not Above, we can measure it like normal
+            val labelConstraints =
+                looseConstraints.offset(
+                    vertical = -bottomPaddingValue,
+                    horizontal = -occupiedSpaceHorizontally
+                )
+            labelPlaceable = labelMeasurable?.measure(labelConstraints)
+            labelIntrinsicHeight = 0
+        } else {
+            // if label is Above, it must be measured after other elements, but we
+            // reserve space for it using its intrinsic height as a heuristic
+            labelIntrinsicHeight = labelMeasurable?.minIntrinsicHeight(constraints.minWidth) ?: 0
+        }
 
         // supporting text must be measured after other elements, but we
         // reserve space for it using its intrinsic height as a heuristic
@@ -634,8 +843,11 @@
         val supportingIntrinsicHeight =
             supportingMeasurable?.minIntrinsicHeight(constraints.minWidth) ?: 0
 
+        // at most one of these is non-zero
+        val labelHeightOrIntrinsic = labelPlaceable.heightOrZero + labelIntrinsicHeight
+
         // measure input field
-        val effectiveTopOffset = topPaddingValue + heightOrZero(labelPlaceable)
+        val effectiveTopOffset = topPaddingValue + labelHeightOrIntrinsic
         val textFieldConstraints =
             constraints
                 .copy(minHeight = 0)
@@ -656,45 +868,52 @@
         occupiedSpaceVertically =
             max(
                 occupiedSpaceVertically,
-                max(heightOrZero(textFieldPlaceable), heightOrZero(placeholderPlaceable)) +
+                max(textFieldPlaceable.heightOrZero, placeholderPlaceable.heightOrZero) +
                     effectiveTopOffset +
                     bottomPaddingValue
             )
         val width =
             calculateWidth(
-                leadingWidth = widthOrZero(leadingPlaceable),
-                trailingWidth = widthOrZero(trailingPlaceable),
-                prefixWidth = widthOrZero(prefixPlaceable),
-                suffixWidth = widthOrZero(suffixPlaceable),
+                leadingWidth = leadingPlaceable.widthOrZero,
+                trailingWidth = trailingPlaceable.widthOrZero,
+                prefixWidth = prefixPlaceable.widthOrZero,
+                suffixWidth = suffixPlaceable.widthOrZero,
                 textFieldWidth = textFieldPlaceable.width,
-                labelWidth = widthOrZero(labelPlaceable),
-                placeholderWidth = widthOrZero(placeholderPlaceable),
+                labelWidth = labelPlaceable.widthOrZero,
+                placeholderWidth = placeholderPlaceable.widthOrZero,
                 constraints = constraints,
             )
 
+        if (isLabelAbove) {
+            // now that we know the width, measure label
+            val labelConstraints =
+                looseConstraints.copy(maxHeight = labelIntrinsicHeight, maxWidth = width)
+            labelPlaceable = labelMeasurable?.measure(labelConstraints)
+        }
+
         // measure supporting text
         val supportingConstraints =
             looseConstraints
                 .offset(vertical = -occupiedSpaceVertically)
                 .copy(minHeight = 0, maxWidth = width)
         val supportingPlaceable = supportingMeasurable?.measure(supportingConstraints)
-        val supportingHeight = heightOrZero(supportingPlaceable)
+        val supportingHeight = supportingPlaceable.heightOrZero
 
         val totalHeight =
             calculateHeight(
                 textFieldHeight = textFieldPlaceable.height,
-                labelHeight = heightOrZero(labelPlaceable),
-                leadingHeight = heightOrZero(leadingPlaceable),
-                trailingHeight = heightOrZero(trailingPlaceable),
-                prefixHeight = heightOrZero(prefixPlaceable),
-                suffixHeight = heightOrZero(suffixPlaceable),
-                placeholderHeight = heightOrZero(placeholderPlaceable),
-                supportingHeight = heightOrZero(supportingPlaceable),
-                animationProgress = animationProgress,
+                labelHeight = labelPlaceable.heightOrZero,
+                leadingHeight = leadingPlaceable.heightOrZero,
+                trailingHeight = trailingPlaceable.heightOrZero,
+                prefixHeight = prefixPlaceable.heightOrZero,
+                suffixHeight = suffixPlaceable.heightOrZero,
+                placeholderHeight = placeholderPlaceable.heightOrZero,
+                supportingHeight = supportingPlaceable.heightOrZero,
                 constraints = constraints,
-                paddingValues = paddingValues,
+                isLabelAbove = isLabelAbove,
             )
-        val height = totalHeight - supportingHeight
+        val height =
+            totalHeight - supportingHeight - (if (isLabelAbove) labelPlaceable.heightOrZero else 0)
 
         val containerPlaceable =
             measurables
@@ -710,9 +929,24 @@
 
         return layout(width, totalHeight) {
             if (labelPlaceable != null) {
-                // The padding defined by the user only applies to the text field when the label
-                // is focused. More padding needs to be added when the text field is unfocused.
-                val labelStartPosition = topPaddingValue + TextFieldLabelExtraPadding.roundToPx()
+                val labelStartY =
+                    when {
+                        isLabelAbove -> 0
+                        singleLine ->
+                            Alignment.CenterVertically.align(labelPlaceable.height, height)
+                        else ->
+                            // The padding defined by the user only applies to the text field when
+                            // the
+                            // label is focused. More padding needs to be added when the text field
+                            // is
+                            // unfocused.
+                            topPaddingValue + TextFieldLabelExtraPadding.roundToPx()
+                    }
+                val labelEndY =
+                    when {
+                        isLabelAbove -> 0
+                        else -> topPaddingValue
+                    }
                 placeWithLabel(
                     width = width,
                     totalHeight = totalHeight,
@@ -725,11 +959,11 @@
                     suffixPlaceable = suffixPlaceable,
                     containerPlaceable = containerPlaceable,
                     supportingPlaceable = supportingPlaceable,
-                    singleLine = singleLine,
-                    labelStartPosition = labelStartPosition,
-                    labelEndPosition = topPaddingValue,
-                    textPosition = topPaddingValue + labelPlaceable.height,
-                    animationProgress = animationProgress,
+                    labelStartY = labelStartY,
+                    labelEndY = labelEndY,
+                    isLabelAbove = isLabelAbove,
+                    textPosition =
+                        topPaddingValue + (if (isLabelAbove) 0 else labelPlaceable.height),
                 )
             } else {
                 placeWithoutLabel(
@@ -743,9 +977,7 @@
                     suffixPlaceable = suffixPlaceable,
                     containerPlaceable = containerPlaceable,
                     supportingPlaceable = supportingPlaceable,
-                    singleLine = singleLine,
                     density = density,
-                    paddingValues = paddingValues
                 )
             }
         }
@@ -826,7 +1058,7 @@
             textFieldWidth = textFieldWidth,
             labelWidth = labelWidth,
             placeholderWidth = placeholderWidth,
-            constraints = ZeroConstraints
+            constraints = Constraints(),
         )
     }
 
@@ -841,7 +1073,7 @@
                 .fastFirstOrNull { it.layoutId == LeadingId }
                 ?.let {
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     intrinsicMeasurer(it, width)
@@ -851,7 +1083,7 @@
                 .fastFirstOrNull { it.layoutId == TrailingId }
                 ?.let {
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     intrinsicMeasurer(it, width)
@@ -867,7 +1099,7 @@
                 ?.let {
                     val height = intrinsicMeasurer(it, remainingWidth)
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     height
@@ -878,7 +1110,7 @@
                 ?.let {
                     val height = intrinsicMeasurer(it, remainingWidth)
                     remainingWidth =
-                        remainingWidth.substractConstraintSafely(
+                        remainingWidth.subtractConstraintSafely(
                             it.maxIntrinsicWidth(Constraints.Infinity)
                         )
                     height
@@ -905,219 +1137,220 @@
             suffixHeight = suffixHeight,
             placeholderHeight = placeholderHeight,
             supportingHeight = supportingHeight,
-            animationProgress = animationProgress,
-            constraints = ZeroConstraints,
-            paddingValues = paddingValues
+            constraints = Constraints(),
+            isLabelAbove = labelPosition == TextFieldLabelPosition.Above,
         )
     }
-}
 
-private fun Int.substractConstraintSafely(from: Int): Int {
-    if (this == Constraints.Infinity) {
-        return this
+    private fun calculateWidth(
+        leadingWidth: Int,
+        trailingWidth: Int,
+        prefixWidth: Int,
+        suffixWidth: Int,
+        textFieldWidth: Int,
+        labelWidth: Int,
+        placeholderWidth: Int,
+        constraints: Constraints
+    ): Int {
+        val affixTotalWidth = prefixWidth + suffixWidth
+        val middleSection =
+            maxOf(
+                textFieldWidth + affixTotalWidth,
+                placeholderWidth + affixTotalWidth,
+                // Prefix/suffix does not get applied to label
+                labelWidth,
+            )
+        val wrappedWidth = leadingWidth + middleSection + trailingWidth
+        return max(wrappedWidth, constraints.minWidth)
     }
-    return this - from
-}
 
-private fun calculateWidth(
-    leadingWidth: Int,
-    trailingWidth: Int,
-    prefixWidth: Int,
-    suffixWidth: Int,
-    textFieldWidth: Int,
-    labelWidth: Int,
-    placeholderWidth: Int,
-    constraints: Constraints
-): Int {
-    val affixTotalWidth = prefixWidth + suffixWidth
-    val middleSection =
-        maxOf(
-            textFieldWidth + affixTotalWidth,
-            placeholderWidth + affixTotalWidth,
-            // Prefix/suffix does not get applied to label
-            labelWidth,
+    private fun Density.calculateHeight(
+        textFieldHeight: Int,
+        labelHeight: Int,
+        leadingHeight: Int,
+        trailingHeight: Int,
+        prefixHeight: Int,
+        suffixHeight: Int,
+        placeholderHeight: Int,
+        supportingHeight: Int,
+        constraints: Constraints,
+        isLabelAbove: Boolean,
+    ): Int {
+        val verticalPadding =
+            (paddingValues.calculateTopPadding() + paddingValues.calculateBottomPadding())
+                .roundToPx()
+
+        val inputFieldHeight =
+            maxOf(
+                textFieldHeight,
+                placeholderHeight,
+                prefixHeight,
+                suffixHeight,
+                if (isLabelAbove) 0 else lerp(labelHeight, 0, labelProgress)
+            )
+
+        val hasLabel = labelHeight > 0
+        val nonOverlappedLabelHeight =
+            if (hasLabel && !isLabelAbove) {
+                // The label animates from overlapping the input field to floating above it,
+                // so its contribution to the height calculation changes over time. Extra padding
+                // is added in the unfocused state to keep the height consistent.
+                max(
+                    (TextFieldLabelExtraPadding * 2).roundToPx(),
+                    lerp(
+                        0,
+                        labelHeight,
+                        EasingEmphasizedAccelerateCubicBezier.transform(labelProgress)
+                    )
+                )
+            } else {
+                0
+            }
+
+        val middleSectionHeight = verticalPadding + nonOverlappedLabelHeight + inputFieldHeight
+
+        return max(
+            constraints.minHeight,
+            (if (isLabelAbove) labelHeight else 0) +
+                maxOf(leadingHeight, trailingHeight, middleSectionHeight) +
+                supportingHeight
         )
-    val wrappedWidth = leadingWidth + middleSection + trailingWidth
-    return max(wrappedWidth, constraints.minWidth)
-}
+    }
 
-private fun Density.calculateHeight(
-    textFieldHeight: Int,
-    labelHeight: Int,
-    leadingHeight: Int,
-    trailingHeight: Int,
-    prefixHeight: Int,
-    suffixHeight: Int,
-    placeholderHeight: Int,
-    supportingHeight: Int,
-    animationProgress: Float,
-    constraints: Constraints,
-    paddingValues: PaddingValues
-): Int {
-    val hasLabel = labelHeight > 0
+    /**
+     * Places the provided text field, placeholder, and label in the TextField given the
+     * PaddingValues when there is a label. When there is no label, [placeWithoutLabel] is used
+     * instead.
+     */
+    private fun Placeable.PlacementScope.placeWithLabel(
+        width: Int,
+        totalHeight: Int,
+        textfieldPlaceable: Placeable,
+        labelPlaceable: Placeable,
+        placeholderPlaceable: Placeable?,
+        leadingPlaceable: Placeable?,
+        trailingPlaceable: Placeable?,
+        prefixPlaceable: Placeable?,
+        suffixPlaceable: Placeable?,
+        containerPlaceable: Placeable,
+        supportingPlaceable: Placeable?,
+        labelStartY: Int,
+        labelEndY: Int,
+        isLabelAbove: Boolean,
+        textPosition: Int,
+    ) {
+        val yOffset = if (isLabelAbove) labelPlaceable.height else 0
 
-    // The padding defined by the user only applies to the text field when the label
-    // is focused. More padding needs to be added when the text field is unfocused.
-    val baseVerticalPadding =
-        (paddingValues.calculateTopPadding() + paddingValues.calculateBottomPadding()).toPx()
-    val labelVerticalPadding =
-        if (hasLabel) {
-            lerp((TextFieldLabelExtraPadding * 2).toPx(), 0f, animationProgress)
+        // place container
+        containerPlaceable.place(0, yOffset)
+
+        // Most elements should be positioned w.r.t the text field's "visual" height, i.e.,
+        // excluding the label (if it's Above) and the supporting text on bottom
+        val height =
+            totalHeight -
+                supportingPlaceable.heightOrZero -
+                (if (isLabelAbove) labelPlaceable.height else 0)
+
+        leadingPlaceable?.placeRelative(
+            0,
+            yOffset + Alignment.CenterVertically.align(leadingPlaceable.height, height)
+        )
+
+        val labelY = lerp(labelStartY, labelEndY, labelProgress)
+        if (isLabelAbove) {
+            labelPlaceable.placeRelative(0, labelY)
         } else {
-            0f
+            labelPlaceable.placeRelative(leadingPlaceable.widthOrZero, labelY)
         }
-    val verticalPadding = (baseVerticalPadding + labelVerticalPadding).roundToInt()
 
-    val inputFieldHeight =
-        maxOf(
-            textFieldHeight,
-            placeholderHeight,
-            prefixHeight,
-            suffixHeight,
-            lerp(labelHeight, 0, animationProgress)
+        prefixPlaceable?.placeRelative(leadingPlaceable.widthOrZero, yOffset + textPosition)
+
+        val textHorizontalPosition = leadingPlaceable.widthOrZero + prefixPlaceable.widthOrZero
+        textfieldPlaceable.placeRelative(textHorizontalPosition, yOffset + textPosition)
+        placeholderPlaceable?.placeRelative(textHorizontalPosition, yOffset + textPosition)
+
+        suffixPlaceable?.placeRelative(
+            width - trailingPlaceable.widthOrZero - suffixPlaceable.width,
+            yOffset + textPosition,
         )
 
-    val middleSectionHeight =
-        verticalPadding + (if (animationProgress == 1f) labelHeight else 0) + inputFieldHeight
+        trailingPlaceable?.placeRelative(
+            width - trailingPlaceable.width,
+            yOffset + Alignment.CenterVertically.align(trailingPlaceable.height, height)
+        )
 
-    return max(
-        constraints.minHeight,
-        maxOf(leadingHeight, trailingHeight, middleSectionHeight) + supportingHeight
-    )
-}
-
-/**
- * Places the provided text field, placeholder, and label in the TextField given the PaddingValues
- * when there is a label. When there is no label, [placeWithoutLabel] is used instead.
- */
-private fun Placeable.PlacementScope.placeWithLabel(
-    width: Int,
-    totalHeight: Int,
-    textfieldPlaceable: Placeable,
-    labelPlaceable: Placeable,
-    placeholderPlaceable: Placeable?,
-    leadingPlaceable: Placeable?,
-    trailingPlaceable: Placeable?,
-    prefixPlaceable: Placeable?,
-    suffixPlaceable: Placeable?,
-    containerPlaceable: Placeable,
-    supportingPlaceable: Placeable?,
-    singleLine: Boolean,
-    labelStartPosition: Int,
-    labelEndPosition: Int,
-    textPosition: Int,
-    animationProgress: Float,
-) {
-    // place container
-    containerPlaceable.place(IntOffset.Zero)
-
-    // Most elements should be positioned w.r.t the text field's "visual" height, i.e., excluding
-    // the supporting text on bottom
-    val height = totalHeight - heightOrZero(supportingPlaceable)
-
-    leadingPlaceable?.placeRelative(
-        0,
-        Alignment.CenterVertically.align(leadingPlaceable.height, height)
-    )
-
-    val labelY =
-        labelPlaceable.let {
-            val startPosition =
-                if (singleLine) {
-                    Alignment.CenterVertically.align(it.height, height)
-                } else {
-                    labelStartPosition
-                }
-            lerp(startPosition, labelEndPosition, animationProgress)
-        }
-    labelPlaceable.placeRelative(widthOrZero(leadingPlaceable), labelY)
-
-    prefixPlaceable?.placeRelative(widthOrZero(leadingPlaceable), textPosition)
-
-    val textHorizontalPosition = widthOrZero(leadingPlaceable) + widthOrZero(prefixPlaceable)
-    textfieldPlaceable.placeRelative(textHorizontalPosition, textPosition)
-    placeholderPlaceable?.placeRelative(textHorizontalPosition, textPosition)
-
-    suffixPlaceable?.placeRelative(
-        width - widthOrZero(trailingPlaceable) - suffixPlaceable.width,
-        textPosition,
-    )
-
-    trailingPlaceable?.placeRelative(
-        width - trailingPlaceable.width,
-        Alignment.CenterVertically.align(trailingPlaceable.height, height)
-    )
-
-    supportingPlaceable?.placeRelative(0, height)
-}
-
-/**
- * Places the provided text field and placeholder in [TextField] when there is no label. When there
- * is a label, [placeWithLabel] is used
- */
-private fun Placeable.PlacementScope.placeWithoutLabel(
-    width: Int,
-    totalHeight: Int,
-    textPlaceable: Placeable,
-    placeholderPlaceable: Placeable?,
-    leadingPlaceable: Placeable?,
-    trailingPlaceable: Placeable?,
-    prefixPlaceable: Placeable?,
-    suffixPlaceable: Placeable?,
-    containerPlaceable: Placeable,
-    supportingPlaceable: Placeable?,
-    singleLine: Boolean,
-    density: Float,
-    paddingValues: PaddingValues
-) {
-    // place container
-    containerPlaceable.place(IntOffset.Zero)
-
-    // Most elements should be positioned w.r.t the text field's "visual" height, i.e., excluding
-    // the supporting text on bottom
-    val height = totalHeight - heightOrZero(supportingPlaceable)
-    val topPadding = (paddingValues.calculateTopPadding().value * density).roundToInt()
-
-    leadingPlaceable?.placeRelative(
-        0,
-        Alignment.CenterVertically.align(leadingPlaceable.height, height)
-    )
-
-    // Single line text field without label places its text components centered vertically.
-    // Multiline text field without label places its text components at the top with padding.
-    fun calculateVerticalPosition(placeable: Placeable): Int {
-        return if (singleLine) {
-            Alignment.CenterVertically.align(placeable.height, height)
-        } else {
-            topPadding
-        }
+        supportingPlaceable?.placeRelative(0, yOffset + height)
     }
 
-    prefixPlaceable?.placeRelative(
-        widthOrZero(leadingPlaceable),
-        calculateVerticalPosition(prefixPlaceable)
-    )
+    /**
+     * Places the provided text field and placeholder in [TextField] when there is no label. When
+     * there is a label, [placeWithLabel] is used
+     */
+    private fun Placeable.PlacementScope.placeWithoutLabel(
+        width: Int,
+        totalHeight: Int,
+        textPlaceable: Placeable,
+        placeholderPlaceable: Placeable?,
+        leadingPlaceable: Placeable?,
+        trailingPlaceable: Placeable?,
+        prefixPlaceable: Placeable?,
+        suffixPlaceable: Placeable?,
+        containerPlaceable: Placeable,
+        supportingPlaceable: Placeable?,
+        density: Float,
+    ) {
+        // place container
+        containerPlaceable.place(IntOffset.Zero)
 
-    val textHorizontalPosition = widthOrZero(leadingPlaceable) + widthOrZero(prefixPlaceable)
+        // Most elements should be positioned w.r.t the text field's "visual" height, i.e.,
+        // excluding the supporting text on bottom
+        val height = totalHeight - supportingPlaceable.heightOrZero
+        val topPadding = (paddingValues.calculateTopPadding().value * density).roundToInt()
 
-    textPlaceable.placeRelative(textHorizontalPosition, calculateVerticalPosition(textPlaceable))
+        leadingPlaceable?.placeRelative(
+            0,
+            Alignment.CenterVertically.align(leadingPlaceable.height, height)
+        )
 
-    placeholderPlaceable?.placeRelative(
-        textHorizontalPosition,
-        calculateVerticalPosition(placeholderPlaceable)
-    )
+        // Single line text field without label places its text components centered vertically.
+        // Multiline text field without label places its text components at the top with padding.
+        fun calculateVerticalPosition(placeable: Placeable): Int {
+            return if (singleLine) {
+                Alignment.CenterVertically.align(placeable.height, height)
+            } else {
+                topPadding
+            }
+        }
 
-    suffixPlaceable?.placeRelative(
-        width - widthOrZero(trailingPlaceable) - suffixPlaceable.width,
-        calculateVerticalPosition(suffixPlaceable),
-    )
+        prefixPlaceable?.placeRelative(
+            leadingPlaceable.widthOrZero,
+            calculateVerticalPosition(prefixPlaceable)
+        )
 
-    trailingPlaceable?.placeRelative(
-        width - trailingPlaceable.width,
-        Alignment.CenterVertically.align(trailingPlaceable.height, height)
-    )
+        val textHorizontalPosition = leadingPlaceable.widthOrZero + prefixPlaceable.widthOrZero
 
-    supportingPlaceable?.placeRelative(0, height)
+        textPlaceable.placeRelative(
+            textHorizontalPosition,
+            calculateVerticalPosition(textPlaceable)
+        )
+
+        placeholderPlaceable?.placeRelative(
+            textHorizontalPosition,
+            calculateVerticalPosition(placeholderPlaceable)
+        )
+
+        suffixPlaceable?.placeRelative(
+            width - trailingPlaceable.widthOrZero - suffixPlaceable.width,
+            calculateVerticalPosition(suffixPlaceable),
+        )
+
+        trailingPlaceable?.placeRelative(
+            width - trailingPlaceable.width,
+            Alignment.CenterVertically.align(trailingPlaceable.height, height)
+        )
+
+        supportingPlaceable?.placeRelative(0, height)
+    }
 }
 
 /** A draw modifier that draws a bottom indicator line in [TextField] */
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
index af7ea36..5d5f983 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
@@ -16,8 +16,8 @@
 
 package androidx.compose.material3
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.border
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.InteractionSource
@@ -26,26 +26,35 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldBuffer
+import androidx.compose.foundation.text.input.TextFieldDecorator
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
 import androidx.compose.foundation.text.selection.LocalTextSelectionColors
 import androidx.compose.foundation.text.selection.TextSelectionColors
 import androidx.compose.material3.internal.CommonDecorationBox
 import androidx.compose.material3.internal.SupportingTopPadding
-import androidx.compose.material3.internal.TextFieldAnimationDuration
 import androidx.compose.material3.internal.TextFieldPadding
 import androidx.compose.material3.internal.TextFieldType
 import androidx.compose.material3.internal.animateBorderStrokeAsState
 import androidx.compose.material3.internal.textFieldBackground
 import androidx.compose.material3.tokens.FilledTextFieldTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.OutlinedTextFieldTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -79,6 +88,130 @@
     val FocusedIndicatorThickness = 2.dp
 
     /**
+     * A decorator used to create custom text fields based on <a
+     * href="https://m3.material.io/components/text-fields/overview" class="external"
+     * target="_blank">Material Design filled text field</a>.
+     *
+     * If your text field requires customising elements that aren't exposed by [TextField], such as
+     * the indicator line thickness, consider using this decorator to achieve the desired design.
+     *
+     * For example, if you wish to customise the bottom indicator line, you can pass a custom
+     * [Container] to this decorator's [container].
+     *
+     * This decorator is meant to be used in conjunction with the overload of [BasicTextField] that
+     * accepts a [TextFieldDecorator] parameter. For other overloads of [BasicTextField] that use a
+     * `decorationBox`, see [DecorationBox].
+     *
+     * An example of building a custom text field using [decorator]:
+     *
+     * @sample androidx.compose.material3.samples.CustomTextFieldUsingDecorator
+     * @param state [TextFieldState] object that holds the internal editing state of the text field.
+     * @param enabled the enabled state of the text field. When `false`, this decorator will appear
+     *   visually disabled. This must be the same value that is passed to [BasicTextField].
+     * @param lineLimits whether the text field is [SingleLine] or [MultiLine]. This must be the
+     *   same value that is passed to [BasicTextField].
+     * @param outputTransformation [OutputTransformation] that transforms how the contents of the
+     *   text field are presented. This must be the same value that is passed to [BasicTextField].
+     * @param interactionSource the read-only [InteractionSource] representing the stream of
+     *   [Interaction]s for this text field. You must first create and pass in your own `remember`ed
+     *   [MutableInteractionSource] instance to the [BasicTextField] for it to dispatch events. And
+     *   then pass the same instance to this decorator to observe [Interaction]s and customize the
+     *   appearance/behavior of the text field in different states.
+     * @param labelPosition the position of the label. See [TextFieldLabelPosition].
+     * @param label the optional label to be displayed with this text field. The default text style
+     *   uses [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
+     * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+     *   default text style uses [Typography.bodyLarge].
+     * @param leadingIcon the optional leading icon to be displayed at the beginning of the text
+     *   field container.
+     * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+     *   container.
+     * @param prefix the optional prefix to be displayed before the input text in the text field.
+     * @param suffix the optional suffix to be displayed after the input text in the text field.
+     * @param supportingText the optional supporting text to be displayed below the text field.
+     * @param isError indicates if the text field's current value is in an error state. When `true`,
+     *   this decorator will display its contents in an error color.
+     * @param colors [TextFieldColors] that will be used to resolve the colors used for this text
+     *   field decorator in different states. See [TextFieldDefaults.colors].
+     * @param contentPadding the padding between the input field and the surrounding elements of the
+     *   decorator. Note that the padding values may not be respected if they are incompatible with
+     *   the text field's size constraints or layout. See
+     *   [TextFieldDefaults.contentPaddingWithLabel] and
+     *   [TextFieldDefaults.contentPaddingWithoutLabel].
+     * @param container the container to be drawn behind the text field. By default, this uses
+     *   [Container]. Default colors for the container come from the [colors].
+     */
+    @Composable
+    @ExperimentalMaterial3Api
+    fun decorator(
+        state: TextFieldState,
+        enabled: Boolean,
+        lineLimits: TextFieldLineLimits,
+        outputTransformation: OutputTransformation?,
+        interactionSource: InteractionSource,
+        labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Default(),
+        label: @Composable (TextFieldLabelScope.() -> Unit)? = null,
+        placeholder: @Composable (() -> Unit)? = null,
+        leadingIcon: @Composable (() -> Unit)? = null,
+        trailingIcon: @Composable (() -> Unit)? = null,
+        prefix: @Composable (() -> Unit)? = null,
+        suffix: @Composable (() -> Unit)? = null,
+        supportingText: @Composable (() -> Unit)? = null,
+        isError: Boolean = false,
+        colors: TextFieldColors = colors(),
+        contentPadding: PaddingValues =
+            if (label == null || labelPosition == TextFieldLabelPosition.Above) {
+                contentPaddingWithoutLabel()
+            } else {
+                contentPaddingWithLabel()
+            },
+        container: @Composable () -> Unit = {
+            Container(
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                colors = colors,
+                shape = shape,
+                focusedIndicatorLineThickness = FocusedIndicatorThickness,
+                unfocusedIndicatorLineThickness = UnfocusedIndicatorThickness,
+            )
+        }
+    ): TextFieldDecorator = TextFieldDecorator { innerTextField ->
+        val visualText =
+            if (outputTransformation == null) state.text
+            else {
+                // TODO: use constructor to create TextFieldBuffer from TextFieldState when
+                // available
+                lateinit var buffer: TextFieldBuffer
+                state.edit { buffer = this }
+                // after edit completes, mutations on buffer are ineffective
+                with(outputTransformation) { buffer.transformOutput() }
+                buffer.asCharSequence()
+            }
+
+        CommonDecorationBox(
+            type = TextFieldType.Filled,
+            visualText = visualText,
+            innerTextField = innerTextField,
+            placeholder = placeholder,
+            labelPosition = labelPosition,
+            label = label,
+            leadingIcon = leadingIcon,
+            trailingIcon = trailingIcon,
+            prefix = prefix,
+            suffix = suffix,
+            supportingText = supportingText,
+            singleLine = lineLimits == SingleLine,
+            enabled = enabled,
+            isError = isError,
+            interactionSource = interactionSource,
+            colors = colors,
+            contentPadding = contentPadding,
+            container = container,
+        )
+    }
+
+    /**
      * Composable that draws a default container for a [TextField] with an indicator line at the
      * bottom. You can apply it to a [BasicTextField] using [DecorationBox] to create a custom text
      * field based on the styling of a Material filled text field. The [TextField] component applies
@@ -96,6 +229,7 @@
      * @param unfocusedIndicatorLineThickness thickness of the indicator line when the text field is
      *   not focused
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @ExperimentalMaterial3Api
     @Composable
     fun Container(
@@ -109,10 +243,11 @@
         unfocusedIndicatorLineThickness: Dp = UnfocusedIndicatorThickness,
     ) {
         val focused = interactionSource.collectIsFocusedAsState().value
+        // TODO Load the motionScheme tokens from the component tokens file
         val containerColor =
             animateColorAsState(
                 targetValue = colors.containerColor(enabled, isError, focused),
-                animationSpec = tween(durationMillis = TextFieldAnimationDuration),
+                animationSpec = MotionSchemeKeyTokens.FastEffects.value(),
             )
         Box(
             modifier
@@ -191,6 +326,10 @@
      * For example, if you wish to customise the bottom indicator line, you can pass a custom
      * [Container] to this decoration box's [container].
      *
+     * This decoration box is meant to be used in conjunction with overloads of [BasicTextField]
+     * that accept a `decorationBox` parameter. For other overloads of [BasicTextField] that use a
+     * [TextFieldDecorator], see [decorator].
+     *
      * An example of building a custom text field using [DecorationBox]:
      *
      * @sample androidx.compose.material3.samples.CustomTextFieldBasedOnDecorationBox
@@ -211,9 +350,8 @@
      *   the appearance / behavior of this text field in different states.
      * @param isError indicates if the text field's current value is in an error state. When `true`,
      *   this decoration box will display its contents in an error color.
-     * @param label the optional label to be displayed inside the text field container. The default
-     *   text style for internal [Text] is [Typography.bodySmall] when the text field is in focus
-     *   and [Typography.bodyLarge] when the text field is not in focus.
+     * @param label the optional label to be displayed with this text field. The default text style
+     *   uses [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
      * @param placeholder the optional placeholder to be displayed when the text field is in focus
      *   and the input text is empty. The default text style for internal [Text] is
      *   [Typography.bodyLarge].
@@ -273,13 +411,20 @@
             )
         }
     ) {
+        val visualText =
+            remember(value, visualTransformation) {
+                    visualTransformation.filter(AnnotatedString(value))
+                }
+                .text
+                .text
+
         CommonDecorationBox(
             type = TextFieldType.Filled,
-            value = value,
+            visualText = visualText,
             innerTextField = innerTextField,
-            visualTransformation = visualTransformation,
             placeholder = placeholder,
-            label = label,
+            labelPosition = TextFieldLabelPosition.Default(),
+            label = label?.let { { it.invoke() } },
             leadingIcon = leadingIcon,
             trailingIcon = trailingIcon,
             prefix = prefix,
@@ -291,14 +436,14 @@
             interactionSource = interactionSource,
             colors = colors,
             contentPadding = contentPadding,
-            container = container
+            container = container,
         )
     }
 
     /**
-     * Default content padding of the input field within the [TextField] when there is a label,
-     * except for the top padding, which instead represents the padding of the label in the focused
-     * state. The input field is placed directly beneath the label.
+     * Default content padding of the input field within the [TextField] when there is an inside
+     * label. Note that the top padding represents the padding above the label in the focused state.
+     * The input field is placed directly beneath the label.
      *
      * Horizontal padding represents the distance between the input field and the leading/trailing
      * icons (if present) or the horizontal edges of the container if there are no icons.
@@ -311,7 +456,8 @@
     ): PaddingValues = PaddingValues(start, top, end, bottom)
 
     /**
-     * Default content padding of the input field within the [TextField] when the label is null.
+     * Default content padding of the input field within the [TextField] when the label is null or
+     * positioned [TextFieldLabelPosition.Above].
      *
      * Horizontal padding represents the distance between the input field and the leading/trailing
      * icons (if present) or the horizontal edges of the container if there are no icons.
@@ -490,7 +636,16 @@
     internal val ColorScheme.defaultTextFieldColors: TextFieldColors
         @Composable
         get() {
-            return defaultTextFieldColorsCached
+            return defaultTextFieldColorsCached?.let { cachedColors ->
+                val localTextSelectionColors = LocalTextSelectionColors.current
+                if (cachedColors.textSelectionColors == localTextSelectionColors) {
+                    cachedColors
+                } else {
+                    cachedColors.copy(textSelectionColors = localTextSelectionColors).also {
+                        defaultTextFieldColorsCached = it
+                    }
+                }
+            }
                 ?: TextFieldColors(
                         focusedTextColor = fromToken(FilledTextFieldTokens.FocusInputColor),
                         unfocusedTextColor = fromToken(FilledTextFieldTokens.InputColor),
@@ -753,6 +908,126 @@
     val FocusedBorderThickness = 2.dp
 
     /**
+     * A decorator used to create custom text fields based on <a
+     * href="https://m3.material.io/components/text-fields/overview" class="external"
+     * target="_blank">Material Design outlined text field</a>.
+     *
+     * If your text field requires customising elements that aren't exposed by [OutlinedTextField],
+     * such as the border thickness, consider using this decorator to achieve the desired design.
+     *
+     * For example, if you wish to customize the thickness of the border, you can pass a custom
+     * [Container] to this decoration box's [container].
+     *
+     * This decorator is meant to be used in conjunction with the overload of [BasicTextField] that
+     * accepts a [TextFieldDecorator] parameter. For other overloads of [BasicTextField] that use a
+     * `decorationBox`, see [DecorationBox].
+     *
+     * An example of building a custom text field using [decorator]:
+     *
+     * @sample androidx.compose.material3.samples.CustomOutlinedTextFieldUsingDecorator
+     * @param state [TextFieldState] object that holds the internal editing state of the text field.
+     * @param enabled the enabled state of the text field. When `false`, this decorator will appear
+     *   visually disabled. This must be the same value that is passed to [BasicTextField].
+     * @param lineLimits whether the text field is [SingleLine] or [MultiLine]. This must be the
+     *   same value that is passed to [BasicTextField].
+     * @param outputTransformation [OutputTransformation] that transforms how the contents of the
+     *   text field are presented. This must be the same value that is passed to [BasicTextField].
+     * @param interactionSource the read-only [InteractionSource] representing the stream of
+     *   [Interaction]s for this text field. You must first create and pass in your own `remember`ed
+     *   [MutableInteractionSource] instance to the [BasicTextField] for it to dispatch events. And
+     *   then pass the same instance to this decorator to observe [Interaction]s and customize the
+     *   appearance/behavior of the text field in different states.
+     * @param labelPosition the position of the label. See [TextFieldLabelPosition].
+     * @param label the optional label to be displayed with this text field. The default text style
+     *   uses [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
+     * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+     *   default text style uses [Typography.bodyLarge].
+     * @param leadingIcon the optional leading icon to be displayed at the beginning of the text
+     *   field container.
+     * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+     *   container.
+     * @param prefix the optional prefix to be displayed before the input text in the text field.
+     * @param suffix the optional suffix to be displayed after the input text in the text field.
+     * @param supportingText the optional supporting text to be displayed below the text field.
+     * @param isError indicates if the text field's current value is in an error state. When `true`,
+     *   this decorator will display its contents in an error color.
+     * @param colors [TextFieldColors] that will be used to resolve the colors used for this text
+     *   field decorator in different states. See [OutlinedTextFieldDefaults.colors].
+     * @param contentPadding the padding between the input field and the surrounding elements of the
+     *   decorator. Note that the padding values may not be respected if they are incompatible with
+     *   the text field's size constraints or layout. See
+     *   [OutlinedTextFieldDefaults.contentPadding].
+     * @param container the container to be drawn behind the text field. By default, this is
+     *   transparent and only includes a border. The cutout in the border to fit the [label] will be
+     *   automatically added by the framework. Default colors for the container come from the
+     *   [colors].
+     */
+    @Composable
+    @ExperimentalMaterial3Api
+    fun decorator(
+        state: TextFieldState,
+        enabled: Boolean,
+        lineLimits: TextFieldLineLimits,
+        outputTransformation: OutputTransformation?,
+        interactionSource: InteractionSource,
+        labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Default(),
+        label: @Composable (TextFieldLabelScope.() -> Unit)? = null,
+        placeholder: @Composable (() -> Unit)? = null,
+        leadingIcon: @Composable (() -> Unit)? = null,
+        trailingIcon: @Composable (() -> Unit)? = null,
+        prefix: @Composable (() -> Unit)? = null,
+        suffix: @Composable (() -> Unit)? = null,
+        supportingText: @Composable (() -> Unit)? = null,
+        isError: Boolean = false,
+        colors: TextFieldColors = colors(),
+        contentPadding: PaddingValues = contentPadding(),
+        container: @Composable () -> Unit = {
+            Container(
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                colors = colors,
+                shape = shape,
+                focusedBorderThickness = FocusedBorderThickness,
+                unfocusedBorderThickness = UnfocusedBorderThickness,
+            )
+        }
+    ): TextFieldDecorator = TextFieldDecorator { innerTextField ->
+        val visualText =
+            if (outputTransformation == null) state.text
+            else {
+                // TODO: use constructor to create TextFieldBuffer from TextFieldState when
+                // available
+                lateinit var buffer: TextFieldBuffer
+                state.edit { buffer = this }
+                // after edit completes, mutations on buffer are ineffective
+                with(outputTransformation) { buffer.transformOutput() }
+                buffer.asCharSequence()
+            }
+
+        CommonDecorationBox(
+            type = TextFieldType.Outlined,
+            visualText = visualText,
+            innerTextField = innerTextField,
+            placeholder = placeholder,
+            labelPosition = labelPosition,
+            label = label,
+            leadingIcon = leadingIcon,
+            trailingIcon = trailingIcon,
+            prefix = prefix,
+            suffix = suffix,
+            supportingText = supportingText,
+            singleLine = lineLimits == SingleLine,
+            enabled = enabled,
+            isError = isError,
+            interactionSource = interactionSource,
+            colors = colors,
+            contentPadding = contentPadding,
+            container = container,
+        )
+    }
+
+    /**
      * Composable that draws a default container for an [OutlinedTextField] with a border stroke.
      * You can apply it to a [BasicTextField] using [DecorationBox] to create a custom text field
      * based on the styling of a Material outlined text field. The [OutlinedTextField] component
@@ -768,6 +1043,7 @@
      * @param focusedBorderThickness thickness of the border when the text field is focused
      * @param unfocusedBorderThickness thickness of the border when the text field is not focused
      */
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @ExperimentalMaterial3Api
     @Composable
     fun Container(
@@ -790,10 +1066,11 @@
                 focusedBorderThickness,
                 unfocusedBorderThickness,
             )
+        // TODO Load the motionScheme tokens from the component tokens file
         val containerColor =
             animateColorAsState(
                 targetValue = colors.containerColor(enabled, isError, focused),
-                animationSpec = tween(durationMillis = TextFieldAnimationDuration),
+                animationSpec = MotionSchemeKeyTokens.FastEffects.value(),
             )
         Box(
             modifier
@@ -813,6 +1090,10 @@
      * For example, if you wish to customize the thickness of the border, you can pass a custom
      * [Container] to this decoration box's [container].
      *
+     * This decoration box is meant to be used in conjunction with overloads of [BasicTextField]
+     * that accept a `decorationBox` parameter. For other overloads of [BasicTextField] that use a
+     * [TextFieldDecorator], see [decorator].
+     *
      * An example of building a custom text field using [DecorationBox]:
      *
      * @sample androidx.compose.material3.samples.CustomOutlinedTextFieldBasedOnDecorationBox
@@ -833,9 +1114,8 @@
      *   the appearance / behavior of this text field in different states.
      * @param isError indicates if the text field's current value is in an error state. When `true`,
      *   this decoration box will display its contents in an error color.
-     * @param label the optional label to be displayed inside the text field container. The default
-     *   text style for internal [Text] is [Typography.bodySmall] when the text field is in focus
-     *   and [Typography.bodyLarge] when the text field is not in focus.
+     * @param label the optional label to be displayed with this text field. The default text style
+     *   uses [Typography.bodySmall] when minimized and [Typography.bodyLarge] when expanded.
      * @param placeholder the optional placeholder to be displayed when the text field is in focus
      *   and the input text is empty. The default text style for internal [Text] is
      *   [Typography.bodyLarge].
@@ -889,13 +1169,20 @@
             )
         }
     ) {
+        val visualText =
+            remember(value, visualTransformation) {
+                    visualTransformation.filter(AnnotatedString(value))
+                }
+                .text
+                .text
+
         CommonDecorationBox(
             type = TextFieldType.Outlined,
-            value = value,
-            visualTransformation = visualTransformation,
+            visualText = visualText,
             innerTextField = innerTextField,
             placeholder = placeholder,
-            label = label,
+            labelPosition = TextFieldLabelPosition.Default(),
+            label = label?.let { { it.invoke() } },
             leadingIcon = leadingIcon,
             trailingIcon = trailingIcon,
             prefix = prefix,
@@ -907,7 +1194,7 @@
             interactionSource = interactionSource,
             colors = colors,
             contentPadding = contentPadding,
-            container = container
+            container = container,
         )
     }
 
@@ -1079,7 +1366,16 @@
     internal val ColorScheme.defaultOutlinedTextFieldColors: TextFieldColors
         @Composable
         get() {
-            return defaultOutlinedTextFieldColorsCached
+            return defaultOutlinedTextFieldColorsCached?.let { cachedColors ->
+                val localTextSelectionColors = LocalTextSelectionColors.current
+                if (cachedColors.textSelectionColors == localTextSelectionColors) {
+                    cachedColors
+                } else {
+                    cachedColors.copy(textSelectionColors = localTextSelectionColors).also {
+                        defaultOutlinedTextFieldColorsCached = it
+                    }
+                }
+            }
                 ?: TextFieldColors(
                         focusedTextColor = fromToken(OutlinedTextFieldTokens.FocusInputColor),
                         unfocusedTextColor = fromToken(OutlinedTextFieldTokens.InputColor),
@@ -1704,3 +2000,64 @@
         return result
     }
 }
+
+/** The position of the label with respect to the text field. */
+abstract class TextFieldLabelPosition private constructor() {
+    /**
+     * The default label position.
+     *
+     * For [TextField], the label is positioned inside the text field container. For
+     * [OutlinedTextField], the label is positioned inside the text field container when expanded
+     * and cuts into the border when minimized.
+     */
+    class Default(@get:Suppress("GetterSetterNames") override val alwaysMinimize: Boolean = false) :
+        TextFieldLabelPosition() {
+        override fun toString(): String = "Default(alwaysMinimize=$alwaysMinimize)"
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Default) return false
+
+            return alwaysMinimize == other.alwaysMinimize
+        }
+
+        override fun hashCode(): Int {
+            return alwaysMinimize.hashCode()
+        }
+    }
+
+    /**
+     * The label is positioned above and outside the text field container. This results in the label
+     * always being minimized.
+     */
+    object Above : TextFieldLabelPosition() {
+        @get:Suppress("GetterSetterNames")
+        override val alwaysMinimize: Boolean
+            get() = true
+
+        override fun toString(): String = "Above"
+    }
+
+    /**
+     * Whether to always keep the label of the text field minimized.
+     *
+     * If `false`, the label will expand to occupy the input area when the text field is unfocused
+     * and empty. If `true`, this allows displaying the placeholder, prefix, and suffix alongside
+     * the label when the text field is unfocused and empty.
+     */
+    @get:Suppress("GetterSetterNames") abstract val alwaysMinimize: Boolean
+}
+
+/** Scope for the label of a [TextField] or [OutlinedTextField]. */
+@Stable
+interface TextFieldLabelScope {
+    /**
+     * The animation progress of a label between its expanded and minimized sizes, where 0
+     * represents an expanded label and 1 represents a minimized label.
+     *
+     * Label animation is handled by the framework when using a component that reads from
+     * [LocalTextStyle], such as the default [Text]. This [progress] value can be used to coordinate
+     * other animations in conjunction with the default animation.
+     */
+    @get:FloatRange(from = 0.0, to = 1.0) val progress: Float
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
index aa0c01f..f341edbc 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
@@ -23,8 +23,8 @@
 import androidx.collection.intListOf
 import androidx.compose.animation.Crossfade
 import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.MutatePriority
 import androidx.compose.foundation.MutatePriority.PreventUserInput
@@ -57,6 +57,7 @@
 import androidx.compose.material3.internal.Strings
 import androidx.compose.material3.internal.getString
 import androidx.compose.material3.internal.rememberAccessibilityServiceState
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.TimeInputTokens
 import androidx.compose.material3.tokens.TimeInputTokens.PeriodSelectorContainerHeight
 import androidx.compose.material3.tokens.TimeInputTokens.PeriodSelectorContainerWidth
@@ -174,6 +175,7 @@
 import androidx.compose.ui.util.fastForEachIndexed
 import androidx.compose.ui.util.fastMap
 import androidx.compose.ui.zIndex
+import kotlin.jvm.JvmInline
 import kotlin.math.PI
 import kotlin.math.atan2
 import kotlin.math.cos
@@ -627,12 +629,16 @@
 
     /** Specifies whether the hour or minute component is being actively selected by the user. */
     var selection: TimePickerSelectionMode
-
-    /** Indicates whether the selected time falls within the afternoon period (12 PM - 12 AM). */
-    var isAfternoon: Boolean
 }
 
 /**
+ * Indicates whether the selected time falls within the period from 12 PM inclusive to 12 AM non
+ * inclusive.
+ */
+val TimePickerState.isPm
+    get() = hour >= 12
+
+/**
  * Factory function for the default implementation of [TimePickerState] [rememberTimePickerState]
  * should be used in most cases.
  *
@@ -678,9 +684,7 @@
 
     override var selection by mutableStateOf(TimePickerSelectionMode.Hour)
 
-    override var isAfternoon by mutableStateOf(initialHour >= 12)
-
-    val hourState = mutableIntStateOf(initialHour % 12)
+    val hourState = mutableIntStateOf(initialHour)
 
     val minuteState = mutableIntStateOf(initialMinute)
 
@@ -691,10 +695,9 @@
         }
 
     override var hour: Int
-        get() = hourState.intValue + if (isAfternoon) 12 else 0
+        get() = hourState.intValue
         set(value) {
-            isAfternoon = value >= 12
-            hourState.intValue = value % 12
+            hourState.intValue = value
         }
 
     companion object {
@@ -721,7 +724,7 @@
     private var hourAngle = RadiansPerHour * (state.hour % 12) - FullCircle / 4
     private var minuteAngle = RadiansPerMinute * state.minute - FullCircle / 4
 
-    suspend fun animateToCurrent() {
+    suspend fun animateToCurrent(animationSpec: AnimationSpec<Float>) {
         if (!isUpdated()) {
             return
         }
@@ -732,9 +735,7 @@
             } else {
                 endValueForAnimation(minuteAngle)
             }
-        mutex.mutate(priority = PreventUserInput) {
-            anim.animateTo(end, spring(dampingRatio = 1f, stiffness = 700f))
-        }
+        mutex.mutate(priority = PreventUserInput) { anim.animateTo(end, animationSpec) }
     }
 
     private fun isUpdated(): Boolean {
@@ -774,7 +775,7 @@
 
     private var anim = Animatable(hourAngle)
 
-    suspend fun onGestureEnd() {
+    suspend fun onGestureEnd(animationSpec: AnimationSpec<Float>) {
         val end =
             endValueForAnimation(
                 if (selection == TimePickerSelectionMode.Hour) {
@@ -784,14 +785,18 @@
                 }
             )
 
-        mutex.mutate(priority = PreventUserInput) { anim.animateTo(end, spring()) }
+        mutex.mutate(priority = PreventUserInput) { anim.animateTo(end, animationSpec) }
     }
 
-    suspend fun rotateTo(angle: Float, animate: Boolean = false) {
+    suspend fun rotateTo(
+        angle: Float,
+        animationSpec: AnimationSpec<Float>,
+        animate: Boolean = false
+    ) {
         mutex.mutate(MutatePriority.UserInput) {
             if (selection == TimePickerSelectionMode.Hour) {
                 hourAngle = angle.toHour() % 12 * RadiansPerHour
-                state.hour = hourAngle.toHour() % 12 + if (isAfternoon) 12 else 0
+                state.hour = hourAngle.toHour() % 12 + if (isPm) 12 else 0
             } else {
                 minuteAngle = angle.toMinute() * RadiansPerMinute
                 state.minute = minuteAngle.toMinute()
@@ -801,7 +806,7 @@
                 anim.snapTo(offsetAngle(angle))
             } else {
                 val endAngle = endValueForAnimation(offsetAngle(angle))
-                anim.animateTo(endAngle, spring(dampingRatio = 1f, stiffness = 700f))
+                anim.animateTo(endAngle, animationSpec)
             }
         }
     }
@@ -864,13 +869,18 @@
         when {
             is24hour -> hour % 24
             hour % 12 == 0 -> 12
-            isAfternoon -> hour - 12
+            isPm -> hour - 12
             else -> hour
         }
 
 private fun TimePickerState.moveSelector(x: Float, y: Float, maxDist: Float, center: IntOffset) {
     if (selection == TimePickerSelectionMode.Hour && is24hour) {
-        isAfternoon = dist(x, y, center.x, center.y) < maxDist
+        val currentDist = dist(x, y, center.x, center.y)
+        if (isPm) {
+            hour -= if (currentDist >= maxDist) 12 else 0
+        } else {
+            hour += if (currentDist < maxDist) 12 else 0
+        }
     }
 }
 
@@ -880,6 +890,7 @@
     maxDist: Float,
     autoSwitchToMinute: Boolean,
     center: IntOffset,
+    animationSpec: AnimationSpec<Float>,
 ) {
     var angle = atan(y - center.y, x - center.x)
     if (selection == TimePickerSelectionMode.Minute) {
@@ -889,7 +900,7 @@
     }
 
     moveSelector(x, y, maxDist, center)
-    rotateTo(angle, animate = true)
+    rotateTo(angle, animationSpec = animationSpec, animate = true)
 
     if (selection == TimePickerSelectionMode.Hour && autoSwitchToMinute) {
         delay(100)
@@ -904,7 +915,7 @@
     get() {
         val handleRadiusPx = ClockDialSelectorHandleContainerSize / 2
         val selectorLength =
-            if (is24hour && this.isAfternoon && selection == TimePickerSelectionMode.Hour) {
+            if (is24hour && this.isPm && selection == TimePickerSelectionMode.Hour) {
                     InnerCircleRadius
                 } else {
                     OuterCircleSizeRadius
@@ -1268,9 +1279,13 @@
         measurePolicy = measurePolicy,
         content = {
             ToggleItem(
-                checked = !state.isAfternoon,
+                checked = !state.isPm,
                 shape = startShape,
-                onClick = { state.isAfternoon = false },
+                onClick = {
+                    if (state.isPm) {
+                        state.hour -= 12
+                    }
+                },
                 colors = colors,
             ) {
                 Text(text = getString(string = Strings.TimePickerAM))
@@ -1282,9 +1297,13 @@
                     .background(color = colors.periodSelectorBorderColor)
             )
             ToggleItem(
-                checked = state.isAfternoon,
+                checked = state.isPm,
                 shape = endShape,
-                onClick = { state.isAfternoon = true },
+                onClick = {
+                    if (!state.isPm) {
+                        state.hour += 12
+                    }
+                },
                 colors = colors,
             ) {
                 Text(getString(string = Strings.TimePickerPM))
@@ -1393,6 +1412,7 @@
     private val state: AnalogTimePickerState,
     private val autoSwitchToMinute: Boolean,
     private val selection: TimePickerSelectionMode,
+    private val animationSpec: AnimationSpec<Float>,
 ) : ModifierNodeElement<ClockDialNode>() {
 
     override fun create(): ClockDialNode =
@@ -1400,6 +1420,7 @@
             state = state,
             autoSwitchToMinute = autoSwitchToMinute,
             selection = selection,
+            animationSpec = animationSpec,
         )
 
     override fun update(node: ClockDialNode) {
@@ -1407,6 +1428,7 @@
             state = state,
             autoSwitchToMinute = autoSwitchToMinute,
             selection = selection,
+            animationSpec = animationSpec,
         )
     }
 
@@ -1419,6 +1441,7 @@
     private var state: AnalogTimePickerState,
     private var autoSwitchToMinute: Boolean,
     private var selection: TimePickerSelectionMode,
+    private var animationSpec: AnimationSpec<Float>,
 ) :
     DelegatingNode(),
     PointerInputModifierNode,
@@ -1441,7 +1464,14 @@
                     },
                     onTap = {
                         coroutineScope.launch {
-                            state.onTap(it.x, it.y, maxDist, autoSwitchToMinute, center)
+                            state.onTap(
+                                it.x,
+                                it.y,
+                                maxDist,
+                                autoSwitchToMinute,
+                                center,
+                                animationSpec
+                            )
                         }
                     },
                 )
@@ -1457,14 +1487,14 @@
                             if (autoSwitchToMinute) {
                                 state.selection = TimePickerSelectionMode.Minute
                             }
-                            state.onGestureEnd()
+                            state.onGestureEnd(animationSpec)
                         }
                     }
                 ) { _, dragAmount ->
                     coroutineScope.launch {
                         offsetX += dragAmount.x
                         offsetY += dragAmount.y
-                        state.rotateTo(atan(offsetY - center.y, offsetX - center.x))
+                        state.rotateTo(atan(offsetY - center.y, offsetX - center.x), animationSpec)
                     }
                     state.moveSelector(offsetX, offsetY, maxDist, center)
                 }
@@ -1492,31 +1522,42 @@
     fun updateNode(
         state: AnalogTimePickerState,
         autoSwitchToMinute: Boolean,
-        selection: TimePickerSelectionMode
+        selection: TimePickerSelectionMode,
+        animationSpec: AnimationSpec<Float>
     ) {
         this.state = state
         this.autoSwitchToMinute = autoSwitchToMinute
+        this.animationSpec = animationSpec
         if (this.selection != selection) {
             this.selection = selection
-            coroutineScope.launch { state.animateToCurrent() }
+            coroutineScope.launch { state.animateToCurrent(animationSpec) }
         }
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 internal fun ClockFace(
     state: AnalogTimePickerState,
     colors: TimePickerColors,
     autoSwitchToMinute: Boolean
 ) {
+    // TODO Load the motionScheme tokens from the component tokens file
     Crossfade(
         modifier =
             Modifier.background(shape = CircleShape, color = colors.clockDialColor)
-                .then(ClockDialModifier(state, autoSwitchToMinute, state.selection))
+                .then(
+                    ClockDialModifier(
+                        state,
+                        autoSwitchToMinute,
+                        state.selection,
+                        MotionSchemeKeyTokens.DefaultSpatial.value()
+                    )
+                )
                 .size(ClockDialContainerSize)
                 .drawSelector(state, colors),
         targetState = state.clockFaceValues,
-        animationSpec = tween(durationMillis = 200)
+        animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
     ) { screen ->
         CircularLayout(
             modifier = Modifier.size(ClockDialContainerSize).semantics { selectableGroup() },
@@ -1628,6 +1669,7 @@
         )
     }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun ClockText(
     modifier: Modifier,
@@ -1655,6 +1697,8 @@
             state.hour.toLocalString() == text
         }
 
+    // TODO Load the motionScheme tokens from the component tokens file
+    val animationSpec: FiniteAnimationSpec<Float> = MotionSchemeKeyTokens.DefaultSpatial.value()
     Box(
         contentAlignment = Alignment.Center,
         modifier =
@@ -1674,7 +1718,8 @@
                                 center.y,
                                 maxDist,
                                 autoSwitchToMinute,
-                                parentCenter
+                                parentCenter,
+                                animationSpec
                             )
                         }
                         true
@@ -1707,7 +1752,7 @@
 
     if (value.text.isEmpty()) {
         if (selection == TimePickerSelectionMode.Hour) {
-            state.hour = if (state.isAfternoon && !state.is24hour) 12 else 0
+            state.hour = if (state.isPm && !state.is24hour) 12 else 0
         } else {
             state.minute = 0
         }
@@ -1725,7 +1770,7 @@
 
         if (newValue <= max) {
             if (selection == TimePickerSelectionMode.Hour) {
-                state.hour = newValue + if (state.isAfternoon && !state.is24hour) 12 else 0
+                state.hour = newValue + if (state.isPm && !state.is24hour) 12 else 0
                 if (newValue > 1 && !state.is24hour) {
                     state.selection = TimePickerSelectionMode.Minute
                 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
index 9ae181a..e176caf 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
@@ -16,12 +16,10 @@
 
 package androidx.compose.material3
 
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.LinearOutSlowInEasing
+import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.MutableTransitionState
 import androidx.compose.animation.core.Transition
 import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.MutatePriority
 import androidx.compose.foundation.MutatorMutex
@@ -32,6 +30,7 @@
 import androidx.compose.material3.internal.BasicTooltipBox
 import androidx.compose.material3.internal.BasicTooltipDefaults
 import androidx.compose.material3.tokens.ElevationTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.tokens.PlainTooltipTokens
 import androidx.compose.material3.tokens.RichTooltipTokens
 import androidx.compose.runtime.Composable
@@ -41,7 +40,6 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.draw.CacheDrawScope
@@ -573,6 +571,7 @@
     }
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 internal fun Modifier.animateTooltip(transition: Transition<Boolean>): Modifier =
     composed(
         inspectorInfo =
@@ -581,23 +580,14 @@
                 properties["transition"] = transition
             }
     ) {
+        // TODO Load the motionScheme tokens from the component tokens file
+        val inOutScaleAnimationSpec: FiniteAnimationSpec<Float> =
+            MotionSchemeKeyTokens.FastSpatial.value()
+        val inOutAlphaAnimationSpec: FiniteAnimationSpec<Float> =
+            MotionSchemeKeyTokens.FastEffects.value()
         val scale by
             transition.animateFloat(
-                transitionSpec = {
-                    if (false isTransitioningTo true) {
-                        // show tooltip
-                        tween(
-                            durationMillis = TooltipFadeInDuration,
-                            easing = LinearOutSlowInEasing
-                        )
-                    } else {
-                        // dismiss tooltip
-                        tween(
-                            durationMillis = TooltipFadeOutDuration,
-                            easing = LinearOutSlowInEasing
-                        )
-                    }
-                },
+                transitionSpec = { inOutScaleAnimationSpec },
                 label = "tooltip transition: scaling"
             ) {
                 if (it) 1f else 0.8f
@@ -605,15 +595,7 @@
 
         val alpha by
             transition.animateFloat(
-                transitionSpec = {
-                    if (false isTransitioningTo true) {
-                        // show tooltip
-                        tween(durationMillis = TooltipFadeInDuration, easing = LinearEasing)
-                    } else {
-                        // dismiss tooltip
-                        tween(durationMillis = TooltipFadeOutDuration, easing = LinearEasing)
-                    }
-                },
+                transitionSpec = { inOutAlphaAnimationSpec },
                 label = "tooltip transition: alpha"
             ) {
                 if (it) 1f else 0f
@@ -637,7 +619,3 @@
 private val TextBottomPadding = 16.dp
 internal val ActionLabelMinHeight = 36.dp
 internal val ActionLabelBottomPadding = 8.dp
-// No specification for fade in and fade out duration, so aligning it with the behavior for snack
-// bar
-internal const val TooltipFadeInDuration = 150
-internal const val TooltipFadeOutDuration = 75
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt
index 477aa04..67617e6 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WavyProgressIndicator.kt
@@ -34,6 +34,7 @@
 import androidx.compose.foundation.layout.requiredSizeIn
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.progressSemantics
+import androidx.compose.material3.internal.IncreaseVerticalSemanticsBounds
 import androidx.compose.material3.internal.toPath
 import androidx.compose.material3.tokens.CircularProgressIndicatorTokens
 import androidx.compose.material3.tokens.LinearProgressIndicatorTokens
@@ -65,7 +66,6 @@
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.semantics.ProgressBarRangeInfo
 import androidx.compose.ui.semantics.progressBarRangeInfo
 import androidx.compose.ui.semantics.semantics
@@ -182,7 +182,7 @@
     val progressDrawingCache = remember { LinearProgressDrawingCache() }
     Spacer(
         modifier
-            .then(IncreaseSemanticsBounds)
+            .then(IncreaseVerticalSemanticsBounds)
             .semantics(mergeDescendants = true) {
                 progressBarRangeInfo = ProgressBarRangeInfo(coercedProgress(), 0f..1f)
             }
@@ -301,55 +301,27 @@
     val infiniteTransition = rememberInfiniteTransition()
     val firstLineHead =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at FirstLineHeadDelay using IndeterminateLinearProgressEasing
-                        1f at FirstLineHeadDuration + FirstLineHeadDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateFirstLineHeadAnimationSpec
         )
     val firstLineTail =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at FirstLineTailDelay using IndeterminateLinearProgressEasing
-                        1f at FirstLineTailDuration + FirstLineTailDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateFirstLineTailAnimationSpec
         )
     val secondLineHead =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at SecondLineHeadDelay using IndeterminateLinearProgressEasing
-                        1f at SecondLineHeadDuration + SecondLineHeadDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateSecondLineHeadAnimationSpec
         )
     val secondLineTail =
         infiniteTransition.animateFloat(
-            0f,
-            1f,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = LinearAnimationDuration
-                        0f at SecondLineTailDelay using IndeterminateLinearProgressEasing
-                        1f at SecondLineTailDuration + SecondLineTailDelay
-                    }
-            )
+            initialValue = 0f,
+            targetValue = 1f,
+            animationSpec = linearIndeterminateSecondLineTailAnimationSpec
         )
 
     val waveOffset =
@@ -360,7 +332,7 @@
                 animation =
                     keyframes {
                         durationMillis = LinearAnimationDuration
-                        1f at LinearAnimationDuration using IndeterminateLinearProgressEasing
+                        1f at LinearAnimationDuration using LinearIndeterminateProgressEasing
                     }
             )
         )
@@ -373,7 +345,7 @@
     val progressDrawingCache = remember { LinearProgressDrawingCache() }
     Spacer(
         modifier
-            .then(IncreaseSemanticsBounds)
+            .then(IncreaseVerticalSemanticsBounds)
             .progressSemantics()
             .requiredSizeIn(minWidth = LinearContainerMinWidth)
             .size(
@@ -691,6 +663,16 @@
 
                 if (animatedAmplitude.targetValue != amplitudeForProgress) {
                     coroutineScope.launch {
+                        if (animatedAmplitude.isRunning) {
+                            // In case the amplitude animation is running, update the upperBound to
+                            // match the new amplitudeForProgress. This will help when that
+                            // amplitudeForProgress is changing before the previous change animation
+                            // is done.
+                            animatedAmplitude.updateBounds(
+                                lowerBound = 0f,
+                                upperBound = amplitudeForProgress
+                            )
+                        }
                         animatedAmplitude.animateTo(
                             targetValue = amplitudeForProgress,
                             animationSpec =
@@ -801,58 +783,39 @@
         "Expecting a progress end that is greater than the progress start"
     }
     val infiniteTransition = rememberInfiniteTransition()
-
-    // Rotation animation of 360 degrees, where the initial 90 degrees are moving in an accelerated
-    // speed.
+    // A global rotation that does a 1080 degrees rotation in 6 seconds.
     val globalRotation =
         infiniteTransition.animateFloat(
             initialValue = 0f,
-            targetValue = 360f,
-            animationSpec =
-                infiniteRepeatable(
-                    animation =
-                        keyframes {
-                            durationMillis = CircularAnimationGlobalRotationDuration // 1500ms
-                            90f at
-                                CircularAnimationGlobalRotationAcceleratedDuration using
-                                CircularGlobalRotationEasing // 200ms
-                            360f at CircularAnimationGlobalRotationDuration // 1500ms
-                        }
-                )
+            targetValue = CircularGlobalRotationDegreesTarget,
+            animationSpec = circularIndeterminateGlobalRotationAnimationSpec
+        )
+
+    // An additional rotation that moves by 90 degrees in 500ms and then rest for 1 second.
+    val additionalRotation =
+        infiniteTransition.animateFloat(
+            initialValue = 0f,
+            targetValue = CircularAdditionalRotationDegreesTarget,
+            animationSpec = circularIndeterminateRotationAnimationSpec
         )
 
     // Indicator progress animation that will be changing the progress up and down as the indicator
     // rotates.
     val progressAnimation =
         infiniteTransition.animateFloat(
-            initialValue = progressStart,
-            targetValue = progressStart,
-            infiniteRepeatable(
-                animation =
-                    keyframes {
-                        durationMillis = CircularAnimationProgressDuration // 6000ms
-                        progressStart at
-                            CircularAnimationProgressDelay using
-                            CircularProgressEasing // 1000ms
-                        progressEnd at
-                            CircularAnimationProgressDelay +
-                                CircularAnimationProgressMovementDuration // 3000ms
-                        progressEnd at
-                            CircularAnimationProgressDelay * 2 +
-                                CircularAnimationProgressMovementDuration // 4000ms
-                        progressStart at CircularAnimationProgressDuration // 6000ms
-                    }
-            )
+            initialValue = CircularIndeterminateMinProgress,
+            targetValue = CircularIndeterminateMaxProgress,
+            animationSpec = circularIndeterminateProgressAnimationSpec
         )
 
     // Holds the start and end progress fractions.
     val progressDrawingCache = remember { CircularProgressDrawingCache() }
-    val direction = if (LocalLayoutDirection.current == LayoutDirection.Ltr) 1f else -1f
     Box(modifier = modifier) {
         Spacer(
             // Apply the rotation from the animation.
             Modifier.fillMaxSize()
-                .graphicsLayer { rotationZ = globalRotation.value * direction }
+                // Adding 90 degrees to align the Wavy indicator motion to the NTC indicator motion.
+                .graphicsLayer { rotationZ = globalRotation.value + additionalRotation.value + 90 }
                 .drawWithCache {
                     val trackGapSize = gapSize.toPx()
                     with(progressDrawingCache) {
@@ -878,26 +841,13 @@
                         )
                     }
                     onDrawWithContent {
-                        if (layoutDirection == LayoutDirection.Rtl) {
-                            // Scaling on the X will flip the drawing for RTL
-                            scale(scaleX = -1f, scaleY = 1f) {
-                                drawCircularIndicator(
-                                    color = color,
-                                    trackColor = trackColor,
-                                    stroke = stroke,
-                                    trackStroke = trackStroke,
-                                    drawingCache = progressDrawingCache
-                                )
-                            }
-                        } else {
-                            drawCircularIndicator(
-                                color = color,
-                                trackColor = trackColor,
-                                stroke = stroke,
-                                trackStroke = trackStroke,
-                                drawingCache = progressDrawingCache
-                            )
-                        }
+                        drawCircularIndicator(
+                            color = color,
+                            trackColor = trackColor,
+                            stroke = stroke,
+                            trackStroke = trackStroke,
+                            drawingCache = progressDrawingCache
+                        )
                     }
                 }
         )
@@ -1000,7 +950,8 @@
     val LinearDeterminateWavelength: Dp = LinearProgressIndicatorTokens.ActiveWaveWavelength
 
     /** A default wavelength of a linear progress indicator when it's in a wavy form. */
-    val LinearIndeterminateWavelength: Dp = 20.dp // TODO Read from tokens when available
+    val LinearIndeterminateWavelength: Dp =
+        LinearProgressIndicatorTokens.IndeterminateActiveWaveWavelength
 
     /** A default linear progress indicator container height. */
     val LinearContainerHeight: Dp = LinearProgressIndicatorTokens.WaveHeight
@@ -1905,23 +1856,6 @@
 // linear indicators should be substituted with circular ones.
 private val LinearContainerMinWidth = CircularProgressIndicatorTokens.Size
 
-// Total duration for one linear cycle
-private const val LinearAnimationDuration = 1750
-
-// Duration of the head and tail animations for both lines
-private const val FirstLineHeadDuration = 1000
-private const val FirstLineTailDuration = 1000
-private const val SecondLineHeadDuration = 850
-private const val SecondLineTailDuration = 850
-
-// Delay before the start of the head and tail animations for both lines
-private const val FirstLineHeadDelay = 0
-private const val FirstLineTailDelay = 250
-private const val SecondLineHeadDelay = 650
-private const val SecondLineTailDelay = 900
-
-private val IndeterminateLinearProgressEasing = MotionTokens.EasingEmphasizedAccelerateCubicBezier
-
 // Animation spec for increasing the amplitude drawing when its changing.
 private val IncreasingAmplitudeAnimationSpec: AnimationSpec<Float> =
     tween(
@@ -1936,17 +1870,4 @@
         easing = MotionTokens.EasingEmphasizedAccelerateCubicBezier
     )
 
-// The indeterminate circular indicator easing constants for its motion
-private val CircularProgressEasing = MotionTokens.EasingStandardCubicBezier
-private val CircularGlobalRotationEasing = MotionTokens.EasingLinearCubicBezier
-private const val CircularIndeterminateMinProgress = 0.1f
-private const val CircularIndeterminateMaxProgress = 0.87f
-
-private const val CircularAnimationGlobalRotationDuration = 1500
-private const val CircularAnimationGlobalRotationAcceleratedDuration = 200
-
-private const val CircularAnimationProgressDuration = 6000
-private const val CircularAnimationProgressDelay = 1000
-private const val CircularAnimationProgressMovementDuration = 2000
-
 private const val MinCircularVertexCount = 5
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WideNavigationRail.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WideNavigationRail.kt
new file mode 100644
index 0000000..6c46e89
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/WideNavigationRail.kt
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.WindowInsetsSides
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.only
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material3.internal.systemBarsForVisualComponents
+import androidx.compose.material3.tokens.ColorSchemeKeyTokens
+import androidx.compose.material3.tokens.ShapeKeyTokens
+import androidx.compose.material3.tokens.TypographyKeyTokens
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.constrain
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.offset
+import androidx.compose.ui.util.fastFirst
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMap
+import androidx.compose.ui.util.fastSumBy
+import kotlin.jvm.JvmInline
+import kotlin.math.min
+
+/**
+ * Material design wide navigation rail.
+ *
+ * Wide navigation rails provide access to primary destinations in apps when using tablet and
+ * desktop screens.
+ *
+ * The wide navigation rail should be used to display multiple [WideNavigationRailItem]s, each
+ * representing a singular app destination, and, optionally, a header containing a menu button, a
+ * [FloatingActionButton], and/or a logo. Each destination is typically represented by an icon and a
+ * text label.
+ *
+ * The [WideNavigationRail] is collapsed by default, but it also supports being expanded via the
+ * value of [expanded]. When collapsed, the rail should display three to seven navigation items. A
+ * simple example looks like:
+ *
+ * @sample androidx.compose.material3.samples.WideNavigationRailCollapsedSample
+ *
+ * When expanded, the rail should display at least three navigation items. A simple example looks
+ * like:
+ *
+ * @sample androidx.compose.material3.samples.WideNavigationRailExpandedSample
+ *
+ * Finally, the [WideNavigationRail] also supports automatically animating between the collapsed and
+ * expanded values. That can be done like so:
+ *
+ * @sample androidx.compose.material3.samples.WideNavigationRailResponsiveSample
+ *
+ * The [WideNavigationRail] supports setting an [NavigationRailArrangement] for the items, so that
+ * the items can be grouped at the top (the default), at the middle, or at the bottom of the rail.
+ * The header will always be at the top.
+ *
+ * See [WideNavigationRailItem] for configuration specific to each item, and not the overall
+ * [WideNavigationRail] component.
+ *
+ * @param modifier the [Modifier] to be applied to this wide navigation rail
+ * @param expanded whether this wide navigation rail is expanded or collapsed (default).
+ * @param shape defines the shape of this wide navigation rail's container.
+ * @param colors [NavigationRailColors] that will be used to resolve the colors used for this wide
+ *   navigation rail. See [WideNavigationRailDefaults.colors]
+ * @param header optional header that may hold a [FloatingActionButton] or a logo
+ * @param windowInsets a window insets of the wide navigation rail
+ * @param arrangement the [NavigationRailArrangement] of this wide navigation rail
+ * @param content the content of this wide navigation rail, typically [WideNavigationRailItem]s
+ *
+ * TODO: Implement modal expanded option and add relevant params.
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun WideNavigationRail(
+    modifier: Modifier = Modifier,
+    expanded: Boolean = false,
+    shape: Shape = WideNavigationRailDefaults.containerShape,
+    colors: NavigationRailColors = WideNavigationRailDefaults.colors(),
+    header: @Composable (() -> Unit)? = null,
+    windowInsets: WindowInsets = WideNavigationRailDefaults.windowInsets,
+    arrangement: NavigationRailArrangement = WideNavigationRailDefaults.arrangement,
+    content: @Composable () -> Unit
+) {
+    WideNavigationRailLayout(
+        modifier = modifier,
+        expanded = expanded,
+        colors = colors,
+        shape = shape,
+        header = header,
+        windowInsets = windowInsets,
+        arrangement = arrangement,
+        content = content
+    )
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun WideNavigationRailLayout(
+    modifier: Modifier = Modifier,
+    expanded: Boolean = false,
+    colors: NavigationRailColors,
+    shape: Shape,
+    header: @Composable (() -> Unit)?,
+    windowInsets: WindowInsets,
+    arrangement: NavigationRailArrangement,
+    content: @Composable () -> Unit
+) {
+    var currentWidth by remember { mutableIntStateOf(0) }
+    var actualMaxExpandedWidth by remember { mutableIntStateOf(0) }
+    val minimumA11ySize =
+        if (LocalMinimumInteractiveComponentSize.current == Dp.Unspecified) {
+            0.dp
+        } else {
+            LocalMinimumInteractiveComponentSize.current
+        }
+
+    val minWidth by
+        animateDpAsState(
+            targetValue = if (!expanded) CollapsedRailWidth else ExpandedRailMinWidth,
+            animationSpec = AnimationSpec
+        )
+    val widthFullRange by
+        animateDpAsState(
+            targetValue = if (!expanded) CollapsedRailWidth else ExpandedRailMaxWidth,
+            animationSpec = AnimationSpec
+        )
+    val itemVerticalSpacedBy by
+        animateDpAsState(
+            targetValue = if (!expanded) VerticalPaddingBetweenTopIconItems else 0.dp,
+            animationSpec = AnimationSpec
+        )
+    val itemMarginStart by
+        animateDpAsState(
+            targetValue = if (!expanded) 0.dp else ExpandedRailHorizontalItemPadding,
+            animationSpec = AnimationSpec
+        )
+
+    Surface(
+        color = colors.containerColor,
+        contentColor = colors.contentColor,
+        shape = shape,
+        modifier = modifier,
+    ) {
+        Layout(
+            modifier =
+                Modifier.fillMaxHeight()
+                    .windowInsetsPadding(windowInsets)
+                    .widthIn(max = ExpandedRailMaxWidth)
+                    .padding(top = WNRVerticalPadding)
+                    .selectableGroup(),
+            content = {
+                if (header != null) {
+                    Box(Modifier.layoutId(HeaderLayoutIdTag)) { header() }
+                }
+                content()
+            },
+            measurePolicy =
+                object : MeasurePolicy {
+                    override fun MeasureScope.measure(
+                        measurables: List<Measurable>,
+                        constraints: Constraints
+                    ): MeasureResult {
+                        val height = constraints.maxHeight
+                        var itemsCount = measurables.size
+                        var actualExpandedMinWidth = constraints.minWidth
+                        val actualMinWidth =
+                            if (constraints.minWidth == 0) {
+                                actualExpandedMinWidth =
+                                    ExpandedRailMinWidth.roundToPx()
+                                        .coerceAtMost(constraints.maxWidth)
+                                minWidth.roundToPx().coerceAtMost(constraints.maxWidth)
+                            } else {
+                                constraints.minWidth
+                            }
+                        // If there are no items, rail will be empty.
+                        if (itemsCount < 1) {
+                            return layout(actualMinWidth, height) {}
+                        }
+                        val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
+                        var itemsMeasurables = measurables
+
+                        var constraintsOffset = 0
+                        var headerPlaceable: Placeable? = null
+                        if (header != null) {
+                            headerPlaceable =
+                                measurables
+                                    .fastFirst { it.layoutId == HeaderLayoutIdTag }
+                                    .measure(looseConstraints)
+                            // Header is always first element in measurables list.
+                            if (itemsCount > 1)
+                                itemsMeasurables = measurables.subList(1, itemsCount)
+                            // Real item count doesn't include the header.
+                            itemsCount--
+                            constraintsOffset = headerPlaceable.height
+                        }
+
+                        val itemsPlaceables =
+                            if (itemsCount > 0) mutableListOf<Placeable>() else null
+                        val itemMaxWidthConstraint =
+                            if (expanded) looseConstraints.maxWidth else actualMinWidth
+                        var expandedItemMaxWidth = 0
+                        if (itemsPlaceables != null) {
+                            itemsMeasurables.fastMap {
+                                val measuredItem =
+                                    it.measure(
+                                        looseConstraints
+                                            .offset(vertical = -constraintsOffset)
+                                            .constrain(
+                                                Constraints.fitPrioritizingWidth(
+                                                    minWidth =
+                                                        min(
+                                                            ItemMinWidth.roundToPx(),
+                                                            itemMaxWidthConstraint
+                                                        ),
+                                                    minHeight =
+                                                        if (!expanded)
+                                                            WNRTopItemMinHeight.roundToPx()
+                                                        else minimumA11ySize.roundToPx(),
+                                                    maxWidth = itemMaxWidthConstraint,
+                                                    maxHeight = looseConstraints.maxHeight,
+                                                )
+                                            )
+                                    )
+                                val maxIntrinsicWidth = it.maxIntrinsicWidth(constraintsOffset)
+                                if (expanded && expandedItemMaxWidth < maxIntrinsicWidth) {
+                                    expandedItemMaxWidth =
+                                        maxIntrinsicWidth +
+                                            (ExpandedRailHorizontalItemPadding * 2).roundToPx()
+                                }
+                                constraintsOffset = measuredItem.height
+                                itemsPlaceables.add(measuredItem)
+                            }
+                        }
+
+                        var width = actualMinWidth
+                        // Limit collapsed rail to fixed width, but expanded rail can be as wide as
+                        // constraints.maxWidth
+                        if (expanded) {
+                            val widestElementWidth =
+                                maxOf(expandedItemMaxWidth, headerPlaceable?.width ?: 0)
+
+                            if (
+                                widestElementWidth > actualMinWidth &&
+                                    widestElementWidth > actualExpandedMinWidth
+                            ) {
+                                val widthConstrain =
+                                    maxOf(widestElementWidth, actualExpandedMinWidth)
+                                        .coerceAtMost(constraints.maxWidth)
+                                // Use widthFullRange so there's no jump in animation for when the
+                                // expanded width has to be wider than actualExpandedMinWidth.
+                                width = widthFullRange.roundToPx().coerceAtMost(widthConstrain)
+                                actualMaxExpandedWidth = width
+                            }
+                        } else {
+                            if (actualMaxExpandedWidth > 0) {
+                                // Use widthFullRange so there's no jump in animation for the case
+                                // when the expanded width was wider than actualExpandedMinWidth.
+                                width =
+                                    widthFullRange
+                                        .roundToPx()
+                                        .coerceIn(
+                                            minimumValue = actualMinWidth,
+                                            maximumValue = currentWidth
+                                        )
+                            }
+                        }
+                        currentWidth = width
+
+                        return layout(width, height) {
+                            var y = 0
+                            var headerHeight = 0
+                            if (headerPlaceable != null && headerPlaceable.height > 0) {
+                                headerPlaceable.placeRelative(0, y)
+                                headerHeight = headerPlaceable.height
+                                if (arrangement == NavigationRailArrangement.Top) {
+                                    y += headerHeight + WNRHeaderPadding.roundToPx()
+                                }
+                            }
+
+                            val itemsHeight = itemsPlaceables?.fastSumBy { it.height } ?: 0
+                            val verticalPadding = itemVerticalSpacedBy.roundToPx()
+                            if (arrangement == NavigationRailArrangement.Center) {
+                                y =
+                                    (height -
+                                        WNRVerticalPadding.roundToPx() -
+                                        (itemsHeight + (itemsCount - 1) * verticalPadding)) / 2
+                                y = y.coerceAtLeast(headerHeight)
+                            } else if (arrangement == NavigationRailArrangement.Bottom) {
+                                y =
+                                    height -
+                                        WNRVerticalPadding.roundToPx() -
+                                        (itemsHeight + (itemsCount - 1) * verticalPadding)
+                                y = y.coerceAtLeast(headerHeight)
+                            }
+                            itemsPlaceables?.fastForEach { item ->
+                                val x = itemMarginStart.roundToPx()
+                                item.placeRelative(x, y)
+                                y += item.height + verticalPadding
+                            }
+                        }
+                    }
+                }
+        )
+    }
+}
+
+/** Class that describes the different supported item arrangements of the [WideNavigationRail]. */
+@ExperimentalMaterial3ExpressiveApi
+@JvmInline
+value class NavigationRailArrangement private constructor(private val value: Int) {
+    companion object {
+        /* The items are grouped at the top on the wide navigation Rail. */
+        val Top = NavigationRailArrangement(0)
+
+        /* The items are centered on the wide navigation Rail. */
+        val Center = NavigationRailArrangement(1)
+
+        /* The items are grouped at the bottom on the wide navigation Rail. */
+        val Bottom = NavigationRailArrangement(2)
+    }
+
+    override fun toString() =
+        when (this) {
+            Top -> "Top"
+            Center -> "Center"
+            Bottom -> "Bottom"
+            else -> "Unknown"
+        }
+}
+
+/**
+ * Represents the colors of the various elements of a wide navigation rail.
+ *
+ * @param containerColor the color used for the background of a wide navigation rail. Use
+ *   [Color.Transparent] to have no color
+ * @param contentColor the preferred color for content inside a wide navigation rail. Defaults to
+ *   either the matching content color for [containerColor], or to the current [LocalContentColor]
+ *   if [containerColor] is not a color from the theme
+ */
+@Immutable
+class NavigationRailColors(
+    val containerColor: Color,
+    val contentColor: Color,
+    /* TODO: Add color params related to the Modal option. */
+) {
+    /**
+     * Returns a copy of this NavigationRailColors, optionally overriding some of the values. This
+     * uses the Color.Unspecified to mean “use the value from the source”.
+     */
+    fun copy(
+        containerColor: Color = this.containerColor,
+        contentColor: Color = this.contentColor,
+    ) =
+        NavigationRailColors(
+            containerColor = containerColor.takeOrElse { this.containerColor },
+            contentColor = contentColor.takeOrElse { this.contentColor },
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is NavigationRailColors) return false
+
+        if (containerColor != other.containerColor) return false
+        if (contentColor != other.contentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerColor.hashCode()
+        result = 31 * result + contentColor.hashCode()
+
+        return result
+    }
+}
+
+/**
+ * Material Design wide navigation rail item.
+ *
+ * It's recommend for navigation items to always have a text label. A [WideNavigationRailItem]
+ * always displays labels (if they exist) when selected and unselected.
+ *
+ * The [WideNavigationRailItem] supports two different icon positions, top and start, which is
+ * controlled by the [iconPosition] param:
+ * - If the icon position is [NavigationItemIconPosition.Top] the icon will be displayed above the
+ *   label. This configuration should be used with collapsed wide navigation rails.
+ * - If the icon position is [NavigationItemIconPosition.Start] the icon will be displayed to the
+ *   start of the label. This configuration should be used with expanded wide navigation rails.
+ *
+ * However, if an animated item is desired, the [iconPosition] can be controlled via the expanded
+ * value of the associated [WideNavigationRail]. By default, it'll use the [railExpanded] to follow
+ * the configuration described above.
+ *
+ * @param selected whether this item is selected
+ * @param onClick called when this item is clicked
+ * @param icon icon for this item, typically an [Icon]
+ * @param modifier the [Modifier] to be applied to this item
+ * @param enabled controls the enabled state of this item. When `false`, this component will not
+ *   respond to user input, and it will appear visually disabled and disabled to accessibility
+ *   services.
+ * @param label text label for this item
+ * @param badge optional badge to show on this item, typically a [Badge]
+ * @param railExpanded whether the associated [WideNavigationRail] is expanded or collapsed
+ * @param iconPosition the [NavigationItemIconPosition] for the icon
+ * @param colors [NavigationItemColors] that will be used to resolve the colors used for this item
+ *   in different states. See [WideNavigationRailItemDefaults.colors]
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ *   emitting [Interaction]s for this item. You can use this to change the item's appearance or
+ *   preview the item in different states. Note that if `null` is provided, interactions will still
+ *   happen internally.
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun WideNavigationRailItem(
+    selected: Boolean,
+    onClick: () -> Unit,
+    icon: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    label: @Composable (() -> Unit)? = null,
+    badge: (@Composable () -> Unit)? = null,
+    railExpanded: Boolean = false,
+    iconPosition: NavigationItemIconPosition =
+        WideNavigationRailItemDefaults.iconPositionFor(railExpanded),
+    colors: NavigationItemColors = WideNavigationRailItemDefaults.colors(),
+    interactionSource: MutableInteractionSource? = null,
+) {
+    @Suppress("NAME_SHADOWING")
+    val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+
+    if (label != null) {
+        AnimatedNavigationItem(
+            selected = selected,
+            onClick = onClick,
+            icon = icon,
+            indicatorShape = ActiveIndicatorShape.value,
+            topIconIndicatorWidth = TopIconItemActiveIndicatorWidth,
+            topIconLabelTextStyle = TopIconLabelTextFont.value,
+            startIconLabelTextStyle = StartIconLabelTextFont.value,
+            topIconIndicatorHorizontalPadding = ItemTopIconIndicatorHorizontalPadding,
+            topIconIndicatorVerticalPadding = ItemTopIconIndicatorVerticalPadding,
+            topIconIndicatorToLabelVerticalPadding = ItemTopIconIndicatorToLabelPadding,
+            startIconIndicatorHorizontalPadding = ItemStartIconIndicatorHorizontalPadding,
+            startIconIndicatorVerticalPadding = ItemStartIconIndicatorVerticalPadding,
+            startIconToLabelHorizontalPadding = ItemStartIconToLabelPadding,
+            startIconItemPadding = ExpandedRailHorizontalItemPadding,
+            colors = colors,
+            modifier = modifier,
+            enabled = enabled,
+            label = label,
+            badge = badge,
+            iconPosition = iconPosition,
+            interactionSource = interactionSource,
+        )
+    } else {
+        // If no label, default to circular indicator for the item.
+        NavigationItem(
+            selected = selected,
+            onClick = onClick,
+            icon = icon,
+            labelTextStyle = TopIconLabelTextFont.value,
+            indicatorShape = ActiveIndicatorShape.value,
+            indicatorWidth = TopIconItemActiveIndicatorWidth,
+            indicatorHorizontalPadding = WNRItemNoLabelIndicatorPadding,
+            indicatorVerticalPadding = WNRItemNoLabelIndicatorPadding,
+            indicatorToLabelVerticalPadding = 0.dp,
+            startIconToLabelHorizontalPadding = 0.dp,
+            topIconItemVerticalPadding = 0.dp,
+            colors = colors,
+            modifier = modifier,
+            enabled = enabled,
+            label = label,
+            badge = badge,
+            iconPosition = iconPosition,
+            interactionSource = interactionSource,
+        )
+    }
+}
+
+/** Defaults used in [WideNavigationRail]. */
+@ExperimentalMaterial3ExpressiveApi
+object WideNavigationRailDefaults {
+    /** Default container shape of a wide navigation rail. */
+    // TODO: Replace with token.
+    val containerShape: Shape
+        @Composable get() = ShapeKeyTokens.CornerNone.value
+
+    /** Default arrangement for a wide navigation rail. */
+    val arrangement: NavigationRailArrangement
+        get() = NavigationRailArrangement.Top
+
+    /** Default window insets for a wide navigation rail. */
+    val windowInsets: WindowInsets
+        @Composable
+        get() =
+            WindowInsets.systemBarsForVisualComponents.only(
+                WindowInsetsSides.Vertical + WindowInsetsSides.Start
+            )
+
+    /**
+     * Creates a [NavigationRailColors] with the provided colors according to the Material
+     * specification.
+     */
+    @Composable fun colors() = MaterialTheme.colorScheme.defaultWideNavigationRailColors
+
+    private val ColorScheme.defaultWideNavigationRailColors: NavigationRailColors
+        get() {
+            return defaultWideNavigationRailColorsCached
+                ?: NavigationRailColors(
+                        // TODO: Replace with tokens.
+                        containerColor = fromToken(ColorSchemeKeyTokens.Surface),
+                        contentColor = fromToken(ColorSchemeKeyTokens.OnSurfaceVariant),
+                    )
+                    .also { defaultWideNavigationRailColorsCached = it }
+        }
+}
+
+/** Defaults used in [WideNavigationRailItem]. */
+@ExperimentalMaterial3ExpressiveApi
+object WideNavigationRailItemDefaults {
+    /**
+     * The default icon position of a [WideNavigationRailItem] given whether the associated
+     * [WideNavigationRail] is collapsed or expanded.
+     */
+    fun iconPositionFor(railExpanded: Boolean) =
+        if (railExpanded) NavigationItemIconPosition.Start else NavigationItemIconPosition.Top
+
+    /**
+     * Creates a [NavigationItemColors] with the provided colors according to the Material
+     * specification.
+     */
+    @Composable fun colors() = MaterialTheme.colorScheme.defaultWideNavigationRailItemColors
+
+    private val ColorScheme.defaultWideNavigationRailItemColors: NavigationItemColors
+        get() {
+            return defaultWideNavigationRailItemColorsCached
+                ?: NavigationItemColors(
+                        selectedIconColor = fromToken(ActiveIconColor),
+                        selectedTextColor = fromToken(ActiveLabelTextColor),
+                        selectedIndicatorColor = fromToken(ActiveIndicatorColor),
+                        unselectedIconColor = fromToken(InactiveIconColor),
+                        unselectedTextColor = fromToken(InactiveLabelTextColor),
+                        disabledIconColor =
+                            fromToken(InactiveIconColor).copy(alpha = DisabledAlpha),
+                        disabledTextColor =
+                            fromToken(InactiveLabelTextColor).copy(alpha = DisabledAlpha),
+                    )
+                    .also { defaultWideNavigationRailItemColorsCached = it }
+        }
+}
+
+private const val HeaderLayoutIdTag: String = "header"
+
+/* TODO: Replace below values with tokens. */
+private val AnimationSpec: AnimationSpec<Dp> = spring(dampingRatio = 0.8f, stiffness = 380f)
+private val IconSize = 24.0.dp
+private val TopIconItemActiveIndicatorWidth = 56.dp
+private val TopIconItemActiveIndicatorHeight = 32.dp
+private val StartIconItemActiveIndicatorHeight = 56.dp
+private val NoLabelItemActiveIndicatorHeight = 56.dp
+private val TopIconLabelTextFont = TypographyKeyTokens.LabelMedium
+private val StartIconLabelTextFont = TypographyKeyTokens.LabelLarge
+private val ActiveIndicatorShape = ShapeKeyTokens.CornerFull
+// TODO: Update to OnSecondaryContainer once value matches Secondary.
+private val ActiveIconColor = ColorSchemeKeyTokens.Secondary
+private val ActiveLabelTextColor = ColorSchemeKeyTokens.Secondary
+private val ActiveIndicatorColor = ColorSchemeKeyTokens.SecondaryContainer
+private val InactiveIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+private val InactiveLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+private val CollapsedRailWidth = 96.dp
+private val ExpandedRailMinWidth = 220.dp
+private val ExpandedRailMaxWidth = 360.dp
+private val ExpandedRailHorizontalItemPadding = 20.dp
+private val ItemStartIconIndicatorHorizontalPadding = 16.dp
+private val ItemStartIconToLabelPadding = 8.dp
+/*@VisibleForTesting*/
+internal val WNRTopItemMinHeight = 64.dp
+
+/*@VisibleForTesting*/
+// Vertical padding between the contents of the wide navigation rail and its top/bottom.
+internal val WNRVerticalPadding = 44.dp
+/*@VisibleForTesting*/
+// Padding at the bottom of the rail's header. This padding will only be added when the header is
+// not null and the rail arrangement is Top.
+internal val WNRHeaderPadding: Dp = 40.dp
+/*@VisibleForTesting*/
+internal val WNRItemNoLabelIndicatorPadding = (NoLabelItemActiveIndicatorHeight - IconSize) / 2
+
+private val VerticalPaddingBetweenTopIconItems = 4.dp
+private val ItemMinWidth = CollapsedRailWidth
+private val ItemTopIconIndicatorVerticalPadding = (TopIconItemActiveIndicatorHeight - IconSize) / 2
+private val ItemTopIconIndicatorHorizontalPadding = (TopIconItemActiveIndicatorWidth - IconSize) / 2
+private val ItemStartIconIndicatorVerticalPadding =
+    (StartIconItemActiveIndicatorHeight - IconSize) / 2
+private val ItemTopIconIndicatorToLabelPadding: Dp = 4.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
index 46534c4..67883b1 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/carousel/Carousel.kt
@@ -53,6 +53,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import kotlin.jvm.JvmInline
 import kotlin.math.roundToInt
 
 /**
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AccessibilityUtil.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AccessibilityUtil.kt
new file mode 100644
index 0000000..f84d315
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AccessibilityUtil.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.offset
+
+@VisibleForTesting internal val HorizontalSemanticsBoundsPadding: Dp = 10.dp
+@VisibleForTesting internal val VerticalSemanticsBoundsPadding: Dp = 10.dp
+
+/**
+ * Increases the semantics bounds horizontally by [HorizontalSemanticsBoundsPadding] in order to
+ * meet the TalkBack box minimum size while preserving the visual appearance.
+ */
+internal val IncreaseHorizontalSemanticsBounds: Modifier =
+    Modifier.layout { measurable, constraints ->
+            val paddingPx = HorizontalSemanticsBoundsPadding.roundToPx()
+            // We need to add horizontal padding to the semantics bounds in order to meet
+            // screenreader green box minimum size, but we also want to
+            // preserve a visual appearance and layout size below that minimum
+            // in order to maintain backwards compatibility. This custom
+            // layout effectively implements "negative padding".
+            val newConstraint = constraints.offset(paddingPx * 2, 0)
+            val placeable = measurable.measure(newConstraint)
+
+            // But when actually placing the placeable, create the layout without additional
+            // space. Place the placeable where it would've been without any extra padding.
+            val height = placeable.height
+            val width = placeable.width - paddingPx * 2
+            layout(width, height) { placeable.place(-paddingPx, 0) }
+        }
+        .semantics(mergeDescendants = true) {}
+        .padding(horizontal = HorizontalSemanticsBoundsPadding)
+
+/**
+ * Increases the semantics bounds vertically by [VerticalSemanticsBoundsPadding] in order to meet
+ * the TalkBack box minimum size while preserving the visual appearance.
+ */
+internal val IncreaseVerticalSemanticsBounds: Modifier =
+    Modifier.layout { measurable, constraints ->
+            val paddingPx = VerticalSemanticsBoundsPadding.roundToPx()
+            // We need to add vertical padding to the semantics bounds in order to meet
+            // screenreader green box minimum size, but we also want to
+            // preserve a visual appearance and layout size below that minimum
+            // in order to maintain backwards compatibility. This custom
+            // layout effectively implements "negative padding".
+            val newConstraint = constraints.offset(0, paddingPx * 2)
+            val placeable = measurable.measure(newConstraint)
+
+            // But when actually placing the placeable, create the layout without additional
+            // space. Place the placeable where it would've been without any extra padding.
+            val height = placeable.height - paddingPx * 2
+            val width = placeable.width
+            layout(width, height) { placeable.place(0, -paddingPx) }
+        }
+        .semantics(mergeDescendants = true) {}
+        .padding(vertical = VerticalSemanticsBoundsPadding)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.kt
index 0a4a8fc..fa71348 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.kt
@@ -218,7 +218,7 @@
     initialValue: T,
     internal val positionalThreshold: (totalDistance: Float) -> Float,
     internal val velocityThreshold: () -> Float,
-    val animationSpec: AnimationSpec<Float>,
+    val animationSpec: () -> AnimationSpec<Float>,
     internal val confirmValueChange: (newValue: T) -> Boolean = { true }
 ) {
 
@@ -244,7 +244,7 @@
         anchors: DraggableAnchors<T>,
         positionalThreshold: (totalDistance: Float) -> Float,
         velocityThreshold: () -> Float,
-        animationSpec: AnimationSpec<Float>,
+        animationSpec: () -> AnimationSpec<Float>,
         confirmValueChange: (newValue: T) -> Boolean = { true }
     ) : this(
         initialValue,
@@ -628,7 +628,7 @@
     companion object {
         /** The default [Saver] implementation for [AnchoredDraggableState]. */
         fun <T : Any> Saver(
-            animationSpec: AnimationSpec<Float>,
+            animationSpec: () -> AnimationSpec<Float>,
             confirmValueChange: (T) -> Boolean,
             positionalThreshold: (distance: Float) -> Float,
             velocityThreshold: () -> Float,
@@ -682,7 +682,7 @@
         val targetOffset = anchors.positionOf(latestTarget)
         if (!targetOffset.isNaN()) {
             var prev = if (offset.isNaN()) 0f else offset
-            animate(prev, targetOffset, velocity, animationSpec) { value, velocity ->
+            animate(prev, targetOffset, velocity, animationSpec.invoke()) { value, velocity ->
                 // Our onDrag coerces the value within the bounds, but an animation may
                 // overshoot, for example a spring animation or an overshooting interpolator
                 // We respect the user's intention and allow the overshoot, but still use
@@ -701,12 +701,7 @@
     val AnimationSpec = SpringSpec<Float>()
 }
 
-private class AnchoredDragFinishedSignal : CancellationException() {
-    override fun fillInStackTrace(): Throwable {
-        stackTrace = emptyArray()
-        return this
-    }
-}
+internal expect class AnchoredDragFinishedSignal() : CancellationException
 
 private suspend fun <I> restartable(inputs: () -> I, block: suspend (I) -> Unit) {
     try {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/CalendarModel.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/CalendarModel.kt
index 3d8c3c2..7ece81f 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/CalendarModel.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/CalendarModel.kt
@@ -297,22 +297,7 @@
  * - dd.MM.yyyy
  * - MM/dd/yyyy
  */
-internal fun datePatternAsInputFormat(localeFormat: String): DateInputFormat {
-    val patternWithDelimiters =
-        localeFormat
-            .replace(Regex("[^dMy/\\-.]"), "")
-            .replace(Regex("d{1,2}"), "dd")
-            .replace(Regex("M{1,2}"), "MM")
-            .replace(Regex("y{1,4}"), "yyyy")
-            .replace("My", "M/y") // Edge case for the Kako locale
-            .removeSuffix(".") // Removes a dot suffix that appears in some formats
-
-    val delimiterRegex = Regex("[/\\-.]")
-    val delimiterMatchResult = delimiterRegex.find(patternWithDelimiters)
-    val delimiterIndex = delimiterMatchResult!!.groups[0]!!.range.first
-    val delimiter = patternWithDelimiters.substring(delimiterIndex, delimiterIndex + 1)
-    return DateInputFormat(patternWithDelimiters = patternWithDelimiters, delimiter = delimiter[0])
-}
+internal expect fun datePatternAsInputFormat(localeFormat: String): DateInputFormat
 
 internal const val DaysInWeek: Int = 7
 internal const val MillisecondsIn24Hours = 86400000L
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/ChildParentSemantics.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/ChildParentSemantics.kt
index dce3887..02ece13 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/ChildParentSemantics.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/ChildParentSemantics.kt
@@ -43,7 +43,8 @@
 
     override fun InspectorInfo.inspectableProperties() {
         name = "childSemantics"
-        properties["properties"] = properties
+        [email protected]["properties"] =
+            [email protected]
     }
 }
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/Icons.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/Icons.kt
new file mode 100644
index 0000000..e2334af
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/Icons.kt
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.vector.DefaultFillType
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.PathBuilder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.unit.dp
+
+internal object Icons {
+    internal object AutoMirrored {
+        internal object Filled {
+            internal val KeyboardArrowLeft: ImageVector
+                get() {
+                    if (_keyboardArrowLeft != null) {
+                        return _keyboardArrowLeft!!
+                    }
+                    _keyboardArrowLeft =
+                        materialIcon(
+                            name = "AutoMirrored.Filled.KeyboardArrowLeft",
+                            autoMirror = true
+                        ) {
+                            materialPath {
+                                moveTo(15.41f, 16.59f)
+                                lineTo(10.83f, 12.0f)
+                                lineToRelative(4.58f, -4.59f)
+                                lineTo(14.0f, 6.0f)
+                                lineToRelative(-6.0f, 6.0f)
+                                lineToRelative(6.0f, 6.0f)
+                                lineToRelative(1.41f, -1.41f)
+                                close()
+                            }
+                        }
+                    return _keyboardArrowLeft!!
+                }
+
+            private var _keyboardArrowLeft: ImageVector? = null
+
+            internal val KeyboardArrowRight: ImageVector
+                get() {
+                    if (_keyboardArrowRight != null) {
+                        return _keyboardArrowRight!!
+                    }
+                    _keyboardArrowRight =
+                        materialIcon(
+                            name = "AutoMirrored.Filled.KeyboardArrowRight",
+                            autoMirror = true
+                        ) {
+                            materialPath {
+                                moveTo(8.59f, 16.59f)
+                                lineTo(13.17f, 12.0f)
+                                lineTo(8.59f, 7.41f)
+                                lineTo(10.0f, 6.0f)
+                                lineToRelative(6.0f, 6.0f)
+                                lineToRelative(-6.0f, 6.0f)
+                                lineToRelative(-1.41f, -1.41f)
+                                close()
+                            }
+                        }
+                    return _keyboardArrowRight!!
+                }
+
+            private var _keyboardArrowRight: ImageVector? = null
+        }
+    }
+
+    internal object Filled {
+        internal val Close: ImageVector
+            get() {
+                if (_close != null) {
+                    return _close!!
+                }
+                _close =
+                    materialIcon(name = "Filled.Close") {
+                        materialPath {
+                            moveTo(19.0f, 6.41f)
+                            lineTo(17.59f, 5.0f)
+                            lineTo(12.0f, 10.59f)
+                            lineTo(6.41f, 5.0f)
+                            lineTo(5.0f, 6.41f)
+                            lineTo(10.59f, 12.0f)
+                            lineTo(5.0f, 17.59f)
+                            lineTo(6.41f, 19.0f)
+                            lineTo(12.0f, 13.41f)
+                            lineTo(17.59f, 19.0f)
+                            lineTo(19.0f, 17.59f)
+                            lineTo(13.41f, 12.0f)
+                            close()
+                        }
+                    }
+                return _close!!
+            }
+
+        private var _close: ImageVector? = null
+
+        internal val Check: ImageVector
+            get() {
+                if (_check != null) {
+                    return _check!!
+                }
+                _check =
+                    materialIcon(name = "Filled.Check") {
+                        materialPath {
+                            moveTo(9.0f, 16.17f)
+                            lineTo(4.83f, 12.0f)
+                            lineToRelative(-1.42f, 1.41f)
+                            lineTo(9.0f, 19.0f)
+                            lineTo(21.0f, 7.0f)
+                            lineToRelative(-1.41f, -1.41f)
+                            close()
+                        }
+                    }
+                return _check!!
+            }
+
+        private var _check: ImageVector? = null
+
+        internal val Edit: ImageVector
+            get() {
+                if (_edit != null) {
+                    return _edit!!
+                }
+                _edit =
+                    materialIcon(name = "Filled.Edit") {
+                        materialPath {
+                            moveTo(3.0f, 17.25f)
+                            verticalLineTo(21.0f)
+                            horizontalLineToRelative(3.75f)
+                            lineTo(17.81f, 9.94f)
+                            lineToRelative(-3.75f, -3.75f)
+                            lineTo(3.0f, 17.25f)
+                            close()
+                            moveTo(20.71f, 7.04f)
+                            curveToRelative(0.39f, -0.39f, 0.39f, -1.02f, 0.0f, -1.41f)
+                            lineToRelative(-2.34f, -2.34f)
+                            curveToRelative(-0.39f, -0.39f, -1.02f, -0.39f, -1.41f, 0.0f)
+                            lineToRelative(-1.83f, 1.83f)
+                            lineToRelative(3.75f, 3.75f)
+                            lineToRelative(1.83f, -1.83f)
+                            close()
+                        }
+                    }
+                return _edit!!
+            }
+
+        private var _edit: ImageVector? = null
+
+        internal val DateRange: ImageVector
+            get() {
+                if (_dateRange != null) {
+                    return _dateRange!!
+                }
+                _dateRange =
+                    materialIcon(name = "Filled.DateRange") {
+                        materialPath {
+                            moveTo(9.0f, 11.0f)
+                            lineTo(7.0f, 11.0f)
+                            verticalLineToRelative(2.0f)
+                            horizontalLineToRelative(2.0f)
+                            verticalLineToRelative(-2.0f)
+                            close()
+                            moveTo(13.0f, 11.0f)
+                            horizontalLineToRelative(-2.0f)
+                            verticalLineToRelative(2.0f)
+                            horizontalLineToRelative(2.0f)
+                            verticalLineToRelative(-2.0f)
+                            close()
+                            moveTo(17.0f, 11.0f)
+                            horizontalLineToRelative(-2.0f)
+                            verticalLineToRelative(2.0f)
+                            horizontalLineToRelative(2.0f)
+                            verticalLineToRelative(-2.0f)
+                            close()
+                            moveTo(19.0f, 4.0f)
+                            horizontalLineToRelative(-1.0f)
+                            lineTo(18.0f, 2.0f)
+                            horizontalLineToRelative(-2.0f)
+                            verticalLineToRelative(2.0f)
+                            lineTo(8.0f, 4.0f)
+                            lineTo(8.0f, 2.0f)
+                            lineTo(6.0f, 2.0f)
+                            verticalLineToRelative(2.0f)
+                            lineTo(5.0f, 4.0f)
+                            curveToRelative(-1.11f, 0.0f, -1.99f, 0.9f, -1.99f, 2.0f)
+                            lineTo(3.0f, 20.0f)
+                            curveToRelative(0.0f, 1.1f, 0.89f, 2.0f, 2.0f, 2.0f)
+                            horizontalLineToRelative(14.0f)
+                            curveToRelative(1.1f, 0.0f, 2.0f, -0.9f, 2.0f, -2.0f)
+                            lineTo(21.0f, 6.0f)
+                            curveToRelative(0.0f, -1.1f, -0.9f, -2.0f, -2.0f, -2.0f)
+                            close()
+                            moveTo(19.0f, 20.0f)
+                            lineTo(5.0f, 20.0f)
+                            lineTo(5.0f, 9.0f)
+                            horizontalLineToRelative(14.0f)
+                            verticalLineToRelative(11.0f)
+                            close()
+                        }
+                    }
+                return _dateRange!!
+            }
+
+        private var _dateRange: ImageVector? = null
+
+        internal val ArrowDropDown: ImageVector
+            get() {
+                if (_arrowDropDown != null) {
+                    return _arrowDropDown!!
+                }
+                _arrowDropDown =
+                    materialIcon(name = "Filled.ArrowDropDown") {
+                        materialPath {
+                            moveTo(7.0f, 10.0f)
+                            lineToRelative(5.0f, 5.0f)
+                            lineToRelative(5.0f, -5.0f)
+                            close()
+                        }
+                    }
+                return _arrowDropDown!!
+            }
+
+        private var _arrowDropDown: ImageVector? = null
+    }
+}
+
+private inline fun materialIcon(
+    name: String,
+    block: ImageVector.Builder.() -> ImageVector.Builder
+): ImageVector =
+    ImageVector.Builder(
+            name = name,
+            defaultWidth = MaterialIconDimension.dp,
+            defaultHeight = MaterialIconDimension.dp,
+            viewportWidth = MaterialIconDimension,
+            viewportHeight = MaterialIconDimension
+        )
+        .block()
+        .build()
+
+private inline fun materialIcon(
+    name: String,
+    autoMirror: Boolean = false,
+    block: ImageVector.Builder.() -> ImageVector.Builder
+): ImageVector =
+    ImageVector.Builder(
+            name = name,
+            defaultWidth = MaterialIconDimension.dp,
+            defaultHeight = MaterialIconDimension.dp,
+            viewportWidth = MaterialIconDimension,
+            viewportHeight = MaterialIconDimension,
+            autoMirror = autoMirror
+        )
+        .block()
+        .build()
+
+private inline fun ImageVector.Builder.materialPath(
+    fillAlpha: Float = 1f,
+    strokeAlpha: Float = 1f,
+    pathFillType: PathFillType = DefaultFillType,
+    pathBuilder: PathBuilder.() -> Unit
+) =
+    path(
+        fill = SolidColor(Color.Black),
+        fillAlpha = fillAlpha,
+        stroke = null,
+        strokeAlpha = strokeAlpha,
+        strokeLineWidth = 1f,
+        strokeLineCap = StrokeCap.Butt,
+        strokeLineJoin = StrokeJoin.Bevel,
+        strokeLineMiter = 1f,
+        pathFillType = pathFillType,
+        pathBuilder = pathBuilder
+    )
+
+// All Material icons (currently) are 24dp by 24dp, with a viewport size of 24 by 24.
+private const val MaterialIconDimension = 24f
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/LayoutUtil.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/LayoutUtil.kt
new file mode 100644
index 0000000..96f0b5d
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/LayoutUtil.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.LayoutIdParentData
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.unit.Constraints
+
+internal val IntrinsicMeasurable.layoutId: Any?
+    get() = (parentData as? LayoutIdParentData)?.layoutId
+
+internal val Placeable?.widthOrZero: Int
+    get() = this?.width ?: 0
+
+internal val Placeable?.heightOrZero: Int
+    get() = this?.height ?: 0
+
+internal fun Int.subtractConstraintSafely(other: Int): Int {
+    if (this == Constraints.Infinity) {
+        return this
+    }
+    return this - other
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/Strings.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/Strings.kt
index f9b7962..d0e4575 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/Strings.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/Strings.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.ReadOnlyComposable
+import kotlin.jvm.JvmInline
 
 @Immutable
 @JvmInline
@@ -36,6 +37,7 @@
         val MenuCollapsed: Strings
         val ToggleDropdownMenu: Strings
         val SnackbarDismiss: Strings
+        val SnackbarPaneTitle: Strings
         val SearchBarSearch: Strings
         val SuggestionsAvailable: Strings
         val DatePickerTitle: Strings
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt
index 34a9bfa..0b58bed 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt
@@ -18,11 +18,8 @@
 
 import androidx.compose.animation.animateColor
 import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.animateFloat
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.interaction.InteractionSource
@@ -30,12 +27,17 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedTextFieldLayout
 import androidx.compose.material3.TextFieldColors
+import androidx.compose.material3.TextFieldLabelPosition
+import androidx.compose.material3.TextFieldLabelScope
 import androidx.compose.material3.TextFieldLayout
 import androidx.compose.material3.outlineCutout
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
+import androidx.compose.material3.value
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.State
@@ -54,17 +56,11 @@
 import androidx.compose.ui.graphics.drawOutline
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.graphics.takeOrElse
-import androidx.compose.ui.layout.IntrinsicMeasurable
-import androidx.compose.ui.layout.LayoutIdParentData
-import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.semantics.error
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.text.lerp
-import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
@@ -74,40 +70,32 @@
     Outlined
 }
 
-/** Implementation of the [TextField] and [OutlinedTextField] */
 @Composable
 internal fun CommonDecorationBox(
     type: TextFieldType,
-    value: String,
+    visualText: CharSequence,
     innerTextField: @Composable () -> Unit,
-    visualTransformation: VisualTransformation,
-    label: @Composable (() -> Unit)?,
-    placeholder: @Composable (() -> Unit)? = null,
-    leadingIcon: @Composable (() -> Unit)? = null,
-    trailingIcon: @Composable (() -> Unit)? = null,
-    prefix: @Composable (() -> Unit)? = null,
-    suffix: @Composable (() -> Unit)? = null,
-    supportingText: @Composable (() -> Unit)? = null,
-    singleLine: Boolean = false,
-    enabled: Boolean = true,
-    isError: Boolean = false,
+    labelPosition: TextFieldLabelPosition,
+    label: @Composable (TextFieldLabelScope.() -> Unit)?,
+    placeholder: @Composable (() -> Unit)?,
+    leadingIcon: @Composable (() -> Unit)?,
+    trailingIcon: @Composable (() -> Unit)?,
+    prefix: @Composable (() -> Unit)?,
+    suffix: @Composable (() -> Unit)?,
+    supportingText: @Composable (() -> Unit)?,
+    singleLine: Boolean,
+    enabled: Boolean,
+    isError: Boolean,
     interactionSource: InteractionSource,
     contentPadding: PaddingValues,
     colors: TextFieldColors,
     container: @Composable () -> Unit,
 ) {
-    val transformedText =
-        remember(value, visualTransformation) {
-                visualTransformation.filter(AnnotatedString(value))
-            }
-            .text
-            .text
-
     val isFocused = interactionSource.collectIsFocusedAsState().value
     val inputState =
         when {
             isFocused -> InputPhase.Focused
-            transformedText.isEmpty() -> InputPhase.UnfocusedEmpty
+            visualText.isEmpty() -> InputPhase.UnfocusedEmpty
             else -> InputPhase.UnfocusedNotEmpty
         }
 
@@ -131,22 +119,27 @@
                 if (overrideLabelTextStyleColor) this.takeOrElse { labelColor } else this
             },
         labelColor = labelColor,
-        showLabel = label != null,
+        showExpandedLabel = label != null && !labelPosition.alwaysMinimize,
     ) { labelProgress, labelTextStyleColor, labelContentColor, placeholderAlpha, prefixSuffixAlpha
         ->
-        val labelProgressValue = labelProgress.value
+        val labelScope = remember {
+            object : TextFieldLabelScope {
+                override val progress: Float
+                    get() = labelProgress.value
+            }
+        }
         val decoratedLabel: @Composable (() -> Unit)? =
-            label?.let {
+            label?.let { label ->
                 @Composable {
                     val labelTextStyle =
-                        lerp(bodyLarge, bodySmall, labelProgressValue).let { textStyle ->
+                        lerp(bodyLarge, bodySmall, labelProgress.value).let { textStyle ->
                             if (overrideLabelTextStyleColor) {
                                 textStyle.copy(color = labelTextStyleColor.value)
                             } else {
                                 textStyle
                             }
                         }
-                    Decoration(labelContentColor.value, labelTextStyle, it)
+                    Decoration(labelContentColor.value, labelTextStyle) { labelScope.label() }
                 }
             }
 
@@ -158,7 +151,7 @@
             derivedStateOf(structuralEqualityPolicy()) { placeholderAlpha.value > 0f }
         }
         val decoratedPlaceholder: @Composable ((Modifier) -> Unit)? =
-            if (placeholder != null && transformedText.isEmpty() && showPlaceholder) {
+            if (placeholder != null && visualText.isEmpty() && showPlaceholder) {
                 @Composable { modifier ->
                     Box(modifier.graphicsLayer { alpha = placeholderAlpha.value }) {
                         Decoration(
@@ -245,18 +238,19 @@
                     container = containerWithId,
                     supporting = decoratedSupporting,
                     singleLine = singleLine,
+                    labelPosition = labelPosition,
                     // TODO(b/271000818): progress state read should be deferred to layout phase
-                    animationProgress = labelProgressValue,
+                    labelProgress = labelProgress.value,
                     paddingValues = contentPadding
                 )
             }
             TextFieldType.Outlined -> {
                 // Outlined cutout
-                val labelSize = remember { mutableStateOf(Size.Zero) }
+                val cutoutSize = remember { mutableStateOf(Size.Zero) }
                 val borderContainerWithId: @Composable () -> Unit = {
                     Box(
                         Modifier.layoutId(ContainerId)
-                            .outlineCutout(labelSize::value, contentPadding),
+                            .outlineCutout(cutoutSize::value, contentPadding),
                         propagateMinConstraints = true
                     ) {
                         container()
@@ -275,17 +269,22 @@
                     supporting = decoratedSupporting,
                     singleLine = singleLine,
                     onLabelMeasured = {
-                        val labelWidth = it.width * labelProgressValue
-                        val labelHeight = it.height * labelProgressValue
+                        if (labelPosition !is TextFieldLabelPosition.Default) {
+                            return@OutlinedTextFieldLayout
+                        }
+                        val progress = labelProgress.value
+                        val labelWidth = it.width * progress
+                        val labelHeight = it.height * progress
                         if (
-                            labelSize.value.width != labelWidth ||
-                                labelSize.value.height != labelHeight
+                            cutoutSize.value.width != labelWidth ||
+                                cutoutSize.value.height != labelHeight
                         ) {
-                            labelSize.value = Size(labelWidth, labelHeight)
+                            cutoutSize.value = Size(labelWidth, labelHeight)
                         }
                     },
+                    labelPosition = labelPosition,
                     // TODO(b/271000818): progress state read should be deferred to layout phase
-                    animationProgress = labelProgressValue,
+                    labelProgress = labelProgress.value,
                     container = borderContainerWithId,
                     paddingValues = contentPadding
                 )
@@ -324,17 +323,15 @@
         onDrawBehind { drawOutline(outline, color = color()) }
     }
 
-internal fun widthOrZero(placeable: Placeable?) = placeable?.width ?: 0
-
-internal fun heightOrZero(placeable: Placeable?) = placeable?.height ?: 0
-
+@Suppress("BanInlineOptIn")
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private inline fun TextFieldTransitionScope(
     inputState: InputPhase,
     focusedLabelTextStyleColor: Color,
     unfocusedLabelTextStyleColor: Color,
     labelColor: Color,
-    showLabel: Boolean,
+    showExpandedLabel: Boolean,
     content:
         @Composable
         (
@@ -350,14 +347,15 @@
     // multiple text fields.
     val transition = updateTransition(inputState, label = "TextFieldInputState")
 
+    // TODO Load the motionScheme tokens from the component tokens file
     val labelProgress =
         transition.animateFloat(
             label = "LabelProgress",
-            transitionSpec = { tween(durationMillis = TextFieldAnimationDuration) }
+            transitionSpec = { MotionSchemeKeyTokens.FastSpatial.value() }
         ) {
             when (it) {
                 InputPhase.Focused -> 1f
-                InputPhase.UnfocusedEmpty -> 0f
+                InputPhase.UnfocusedEmpty -> if (showExpandedLabel) 0f else 1f
                 InputPhase.UnfocusedNotEmpty -> 1f
             }
         }
@@ -367,27 +365,20 @@
             label = "PlaceholderOpacity",
             transitionSpec = {
                 if (InputPhase.Focused isTransitioningTo InputPhase.UnfocusedEmpty) {
-                    tween(
-                        durationMillis = PlaceholderAnimationDelayOrDuration,
-                        easing = LinearEasing
-                    )
+                    MotionSchemeKeyTokens.FastEffects.value()
                 } else if (
                     InputPhase.UnfocusedEmpty isTransitioningTo InputPhase.Focused ||
                         InputPhase.UnfocusedNotEmpty isTransitioningTo InputPhase.UnfocusedEmpty
                 ) {
-                    tween(
-                        durationMillis = PlaceholderAnimationDuration,
-                        delayMillis = PlaceholderAnimationDelayOrDuration,
-                        easing = LinearEasing
-                    )
+                    MotionSchemeKeyTokens.SlowEffects.value()
                 } else {
-                    spring()
+                    MotionSchemeKeyTokens.FastEffects.value()
                 }
             }
         ) {
             when (it) {
                 InputPhase.Focused -> 1f
-                InputPhase.UnfocusedEmpty -> if (showLabel) 0f else 1f
+                InputPhase.UnfocusedEmpty -> if (showExpandedLabel) 0f else 1f
                 InputPhase.UnfocusedNotEmpty -> 0f
             }
         }
@@ -395,18 +386,18 @@
     val prefixSuffixOpacity =
         transition.animateFloat(
             label = "PrefixSuffixOpacity",
-            transitionSpec = { tween(durationMillis = TextFieldAnimationDuration) }
+            transitionSpec = { MotionSchemeKeyTokens.FastEffects.value() }
         ) {
             when (it) {
                 InputPhase.Focused -> 1f
-                InputPhase.UnfocusedEmpty -> if (showLabel) 0f else 1f
+                InputPhase.UnfocusedEmpty -> if (showExpandedLabel) 0f else 1f
                 InputPhase.UnfocusedNotEmpty -> 1f
             }
         }
 
     val labelTextStyleColor =
         transition.animateColor(
-            transitionSpec = { tween(durationMillis = TextFieldAnimationDuration) },
+            transitionSpec = { MotionSchemeKeyTokens.FastEffects.value() },
             label = "LabelTextStyleColor"
         ) {
             when (it) {
@@ -418,7 +409,7 @@
     @Suppress("UnusedTransitionTargetStateParameter")
     val labelContentColor =
         transition.animateColor(
-            transitionSpec = { tween(durationMillis = TextFieldAnimationDuration) },
+            transitionSpec = { MotionSchemeKeyTokens.FastEffects.value() },
             label = "LabelContentColor",
             targetValueByState = { labelColor }
         )
@@ -432,6 +423,7 @@
     )
 }
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 internal fun animateBorderStrokeAsState(
     enabled: Boolean,
@@ -441,10 +433,11 @@
     focusedBorderThickness: Dp,
     unfocusedBorderThickness: Dp
 ): State<BorderStroke> {
+    // TODO Load the motionScheme tokens from the component tokens file
     val targetColor = colors.indicatorColor(enabled, isError, focused)
     val indicatorColor =
         if (enabled) {
-            animateColorAsState(targetColor, tween(durationMillis = TextFieldAnimationDuration))
+            animateColorAsState(targetColor, MotionSchemeKeyTokens.FastEffects.value())
         } else {
             rememberUpdatedState(targetColor)
         }
@@ -452,7 +445,7 @@
     val thickness =
         if (enabled) {
             val targetThickness = if (focused) focusedBorderThickness else unfocusedBorderThickness
-            animateDpAsState(targetThickness, tween(durationMillis = TextFieldAnimationDuration))
+            animateDpAsState(targetThickness, MotionSchemeKeyTokens.FastSpatial.value())
         } else {
             rememberUpdatedState(unfocusedBorderThickness)
         }
@@ -472,9 +465,6 @@
     UnfocusedNotEmpty
 }
 
-internal val IntrinsicMeasurable.layoutId: Any?
-    get() = (parentData as? LayoutIdParentData)?.layoutId
-
 internal const val TextFieldId = "TextField"
 internal const val PlaceholderId = "Hint"
 internal const val LabelId = "Label"
@@ -484,16 +474,13 @@
 internal const val SuffixId = "Suffix"
 internal const val SupportingId = "Supporting"
 internal const val ContainerId = "Container"
-internal val ZeroConstraints = Constraints(0, 0, 0, 0)
-
-internal const val TextFieldAnimationDuration = 150
-private const val PlaceholderAnimationDuration = 83
-private const val PlaceholderAnimationDelayOrDuration = 67
 
 internal val TextFieldPadding = 16.dp
 // SP not DP because it should scale with font size. Value equal to bodySmall line height / 2.
 internal val TextFieldLabelExtraPadding = 8.sp
 internal val HorizontalIconPadding = 12.dp
+internal val AboveLabelHorizontalPadding = 4.dp
+internal val AboveLabelBottomPadding = 4.dp
 internal val SupportingTopPadding = 4.dp
 internal val PrefixSuffixTextPadding = 2.dp
 internal val MinTextLineHeight = 24.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/pulltorefresh/PullToRefresh.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/pulltorefresh/PullToRefresh.kt
index b1e681f..9e697a1 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/pulltorefresh/PullToRefresh.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/pulltorefresh/PullToRefresh.kt
@@ -20,10 +20,8 @@
 import androidx.compose.animation.Crossfade
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
@@ -33,15 +31,14 @@
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ContainedLoadingIndicator
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.LoadingIndicatorDefaults
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
-import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.loadingIndicatorColor
-import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.loadingIndicatorContainerColor
 import androidx.compose.material3.tokens.ElevationTokens
-import androidx.compose.material3.tokens.MotionTokens
+import androidx.compose.material3.tokens.MotionSchemeKeyTokens
 import androidx.compose.material3.value
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
@@ -394,7 +391,7 @@
      */
     @ExperimentalMaterial3ExpressiveApi
     val loadingIndicatorContainerColor: Color
-        @Composable get() = LoadingIndicatorDefaults.ContainerColor
+        @Composable get() = LoadingIndicatorDefaults.ContainedContainerColor
 
     /** The default indicator color for [Indicator] */
     @Deprecated("Use loadingIndicatorColor instead", ReplaceWith("loadingIndicatorColor"))
@@ -407,7 +404,7 @@
      */
     @ExperimentalMaterial3ExpressiveApi
     val loadingIndicatorColor: Color
-        @Composable get() = LoadingIndicatorDefaults.IndicatorColor
+        @Composable get() = LoadingIndicatorDefaults.ContainedIndicatorColor
 
     /** The default refresh threshold for [rememberPullToRefreshState] */
     val PositionalThreshold = 80.dp
@@ -482,6 +479,7 @@
      *   release
      */
     @Suppress("DEPRECATION")
+    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
     @Composable
     fun Indicator(
         state: PullToRefreshState,
@@ -498,9 +496,10 @@
             containerColor = containerColor,
             threshold = threshold,
         ) {
+            // TODO Load the motionScheme tokens from the component tokens file
             Crossfade(
                 targetState = isRefreshing,
-                animationSpec = tween(durationMillis = CrossfadeDurationMs)
+                animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
             ) { refreshing ->
                 if (refreshing) {
                     CircularProgressIndicator(
@@ -538,12 +537,13 @@
             elevation = elevation,
             threshold = threshold,
         ) {
+            // TODO Load the motionScheme tokens from the component tokens file
             Crossfade(
                 targetState = isRefreshing,
-                animationSpec = tween(durationMillis = CrossfadeDurationMs)
+                animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
             ) { refreshing ->
                 if (refreshing) {
-                    androidx.compose.material3.LoadingIndicator(
+                    ContainedLoadingIndicator(
                         // TODO Set the LoadingIndicator colors
                         modifier =
                             Modifier.requiredSize(
@@ -557,7 +557,7 @@
                     // The LoadingIndicator will rotate and morph for a coerced progress value of 0
                     // to 1. When the state's distanceFraction is above one, we rotate the entire
                     // component we have a continuous rotation until the refreshing flag is true.
-                    androidx.compose.material3.LoadingIndicator(
+                    ContainedLoadingIndicator(
                         // TODO Set the LoadingIndicator colors
                         progress = { state.distanceFraction },
                         modifier =
@@ -672,6 +672,7 @@
 }
 
 /** The default pull indicator for [PullToRefreshBox] */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 private fun CircularArrowProgressIndicator(
     progress: () -> Float,
@@ -680,7 +681,12 @@
     val path = remember { Path().apply { fillType = PathFillType.EvenOdd } }
     // TODO: Consider refactoring this sub-component utilizing Modifier.Node
     val targetAlpha by remember { derivedStateOf { if (progress() >= 1f) MaxAlpha else MinAlpha } }
-    val alphaState = animateFloatAsState(targetValue = targetAlpha, animationSpec = AlphaTween)
+    // TODO Load the motionScheme tokens from the component tokens file
+    val alphaState =
+        animateFloatAsState(
+            targetValue = targetAlpha,
+            animationSpec = MotionSchemeKeyTokens.DefaultEffects.value()
+        )
     Canvas(
         Modifier.semantics(mergeDescendants = true) {
                 progressBarRangeInfo = ProgressBarRangeInfo(progress(), 0f..1f, 0)
@@ -770,7 +776,6 @@
 }
 
 private const val MaxProgressArc = 0.8f
-private const val CrossfadeDurationMs = MotionTokens.DurationShort2.toInt()
 
 /** The default stroke width for [Indicator] */
 private val StrokeWidth = 2.5.dp
@@ -788,7 +793,6 @@
 // Values taken from SwipeRefreshLayout
 private const val MinAlpha = 0.3f
 private const val MaxAlpha = 1f
-private val AlphaTween = tween<Float>(MotionTokens.DurationMedium2.toInt(), easing = LinearEasing)
 
 /**
  * The distance pulled is multiplied by this value to give us the adjusted distance pulled, which is
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
index 9ed1149..1e3dd93 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// VERSION: v0_4_0
+// VERSION: v0_7_0
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -22,7 +22,7 @@
 
 internal object CircularProgressIndicatorTokens {
     val ActiveThickness = 4.0.dp
-    val ActiveWaveAmplitude = 2.0.dp
+    val ActiveWaveAmplitude = 1.6.dp
     val ActiveWaveWavelength = 15.0.dp
     val Size = 40.0.dp
     val TrackActiveSpace = 4.0.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
index d0838d3..8bbd505 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// VERSION: v0_4_0
+// VERSION: v0_7_0
 // GENERATED CODE - DO NOT MODIFY BY HAND
+
 package androidx.compose.material3.tokens
 
 import androidx.compose.ui.unit.dp
@@ -24,6 +25,7 @@
     val ActiveWaveAmplitude = 3.0.dp
     val ActiveWaveWavelength = 40.0.dp
     val Height = 4.0.dp
+    val IndeterminateActiveWaveWavelength = 20.0.dp
     val StopSize = 4.0.dp
     val StopTrailingSpace = 0.0.dp
     val TrackActiveSpace = 4.0.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LoadingIndicatorTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LoadingIndicatorTokens.kt
index 1ef18af..ee8a50d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LoadingIndicatorTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LoadingIndicatorTokens.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// VERSION: v0_4_0
+// VERSION: v0_7_0
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -23,7 +23,8 @@
 internal object LoadingIndicatorTokens {
     val ActiveIndicatorColor = ColorSchemeKeyTokens.Primary
     val ActiveSize = 38.0.dp
-    val ContainerColor = ColorSchemeKeyTokens.SecondaryContainer
+    val ContainedActiveColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val ContainedContainerColor = ColorSchemeKeyTokens.PrimaryContainer
     val ContainerHeight = 48.0.dp
     val ContainerShape = ShapeKeyTokens.CornerFull
     val ContainerWidth = 48.0.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/MotionSchemeKeyTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/MotionSchemeKeyTokens.kt
new file mode 100644
index 0000000..3719c9f
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/MotionSchemeKeyTokens.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.tokens
+
+// TODO - These key-tokens should be generated (similar to the TypographyKeyTokens)
+internal enum class MotionSchemeKeyTokens {
+    DefaultSpatial,
+    FastSpatial,
+    SlowSpatial,
+    DefaultEffects,
+    FastEffects,
+    SlowEffects
+}
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/AlertDialog.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/AlertDialog.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/AlertDialog.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/AlertDialog.commonStubs.kt
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/CalendarLocale.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/CalendarLocale.commonStubs.kt
new file mode 100644
index 0000000..4639efb
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/CalendarLocale.commonStubs.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+
+@ExperimentalMaterial3Api
+actual class CalendarLocale {
+    init {
+        implementedInJetBrainsFork()
+    }
+}
+
+@Composable
+@ReadOnlyComposable
+@OptIn(ExperimentalMaterial3Api::class)
+internal actual fun defaultLocale(): CalendarLocale = implementedInJetBrainsFork()
+
+internal actual fun Int.toLocalString(
+    minDigits: Int,
+    maxDigits: Int,
+    isGroupingUsed: Boolean
+): String = implementedInJetBrainsFork()
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/DateInput.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/DateInput.commonStubs.kt
new file mode 100644
index 0000000..83856230
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/DateInput.commonStubs.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.material3.internal.CalendarDate
+import androidx.compose.material3.internal.DateInputFormat
+import androidx.compose.runtime.Stable
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Stable
+internal actual class DateInputValidator
+actual constructor(
+    yearRange: IntRange,
+    selectableDates: SelectableDates,
+    dateInputFormat: DateInputFormat,
+    dateFormatter: DatePickerFormatter,
+    errorDatePattern: String,
+    errorDateOutOfYearRange: String,
+    errorInvalidNotAllowed: String,
+    errorInvalidRangeInput: String
+) {
+    actual var currentStartDateMillis: Long? = implementedInJetBrainsFork()
+    actual var currentEndDateMillis: Long? = implementedInJetBrainsFork()
+
+    actual fun validate(
+        dateToValidate: CalendarDate?,
+        inputIdentifier: InputIdentifier,
+        locale: CalendarLocale
+    ): String = implementedInJetBrainsFork()
+}
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/DatePicker.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/DatePicker.commonStubs.kt
new file mode 100644
index 0000000..f692c5e1
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/DatePicker.commonStubs.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun formatDatePickerNavigateToYearString(
+    template: String,
+    localizedYear: String
+): String = implementedInJetBrainsFork()
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun formatHeadlineDescription(
+    template: String,
+    verboseDateDescription: String
+): String = implementedInJetBrainsFork()
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/InteractiveComponentSize.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/InteractiveComponentSize.commonStubs.kt
new file mode 100644
index 0000000..66e32fd
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/InteractiveComponentSize.commonStubs.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun identityHashCode(value: Any): Int = implementedInJetBrainsFork()
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/ModalBottomSheet.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/ModalBottomSheet.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/ModalBottomSheet.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/ModalBottomSheet.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/NavigationDrawer.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/NavigationDrawer.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/NavigationDrawer.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/NavigationDrawer.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/NotImplemented.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/NotImplemented.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/NotImplemented.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/SkikoMenu.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/SkikoMenu.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/SkikoMenu.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/SkikoMenu.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/TimeFormat.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/TimeFormat.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/TimeFormat.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/TimeFormat.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/TimePicker.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/TimePicker.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/TimePicker.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/TimePicker.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/Tooltip.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/Tooltip.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/Tooltip.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/Tooltip.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.commonStubs.kt
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.commonStubs.kt
new file mode 100644
index 0000000..8b75665
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AnchoredDraggable.commonStubs.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import androidx.compose.material3.implementedInJetBrainsFork
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal actual constructor() :
+    CancellationException("Anchored drag finished") {
+    init {
+        implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/BasicTooltip.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/BasicTooltip.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/BasicTooltip.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/BasicTooltip.commonStubs.kt
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/CalendarModel.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/CalendarModel.commonStubs.kt
new file mode 100644
index 0000000..30bc924
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/CalendarModel.commonStubs.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import androidx.compose.material3.CalendarLocale
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.implementedInJetBrainsFork
+
+@OptIn(ExperimentalMaterial3Api::class)
+internal actual fun createCalendarModel(locale: CalendarLocale): CalendarModel =
+    implementedInJetBrainsFork()
+
+@OptIn(ExperimentalMaterial3Api::class)
+internal actual fun formatWithSkeleton(
+    utcTimeMillis: Long,
+    skeleton: String,
+    locale: CalendarLocale,
+    cache: MutableMap<String, Any>
+): String = implementedInJetBrainsFork()
+
+internal actual fun datePatternAsInputFormat(localeFormat: String): DateInputFormat =
+    implementedInJetBrainsFork()
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/DefaultPlatformTextStyle.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/DefaultPlatformTextStyle.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/DefaultPlatformTextStyle.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/DefaultPlatformTextStyle.commonStubs.kt
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/InternalMutatorMutex.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/InternalMutatorMutex.commonStubs.kt
new file mode 100644
index 0000000..0d47281
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/InternalMutatorMutex.commonStubs.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import androidx.compose.material3.implementedInJetBrainsFork
+
+internal actual class InternalAtomicReference<V> actual constructor(value: V) {
+    actual fun get(): V = implementedInJetBrainsFork()
+
+    actual fun set(value: V) {
+        implementedInJetBrainsFork()
+    }
+
+    actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+    actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/Strings.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/Strings.commonStubs.kt
new file mode 100644
index 0000000..a2c5526
--- /dev/null
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/Strings.commonStubs.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.internal
+
+import androidx.compose.material3.implementedInJetBrainsFork
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ReadOnlyComposable
+import kotlin.jvm.JvmInline
+
+@Composable
+@ReadOnlyComposable
+internal actual fun getString(string: Strings): String = implementedInJetBrainsFork()
+
+@JvmInline
+@Immutable
+internal actual value class Strings constructor(val value: Int) {
+    actual companion object {
+        actual val NavigationMenu: Strings = implementedInJetBrainsFork()
+        actual val CloseDrawer: Strings = implementedInJetBrainsFork()
+        actual val CloseSheet: Strings = implementedInJetBrainsFork()
+        actual val DefaultErrorMessage: Strings = implementedInJetBrainsFork()
+        actual val SliderRangeStart: Strings = implementedInJetBrainsFork()
+        actual val SliderRangeEnd: Strings = implementedInJetBrainsFork()
+        actual val Dialog: Strings = implementedInJetBrainsFork()
+        actual val MenuExpanded: Strings = implementedInJetBrainsFork()
+        actual val MenuCollapsed: Strings = implementedInJetBrainsFork()
+        actual val SnackbarDismiss: Strings = implementedInJetBrainsFork()
+        actual val SnackbarPaneTitle: Strings = implementedInJetBrainsFork()
+        actual val SearchBarSearch: Strings = implementedInJetBrainsFork()
+        actual val SuggestionsAvailable: Strings = implementedInJetBrainsFork()
+        actual val DatePickerTitle: Strings = implementedInJetBrainsFork()
+        actual val DatePickerHeadline: Strings = implementedInJetBrainsFork()
+        actual val DatePickerYearPickerPaneTitle: Strings = implementedInJetBrainsFork()
+        actual val DatePickerSwitchToYearSelection: Strings = implementedInJetBrainsFork()
+        actual val DatePickerSwitchToDaySelection: Strings = implementedInJetBrainsFork()
+        actual val DatePickerSwitchToNextMonth: Strings = implementedInJetBrainsFork()
+        actual val DatePickerSwitchToPreviousMonth: Strings = implementedInJetBrainsFork()
+        actual val DatePickerNavigateToYearDescription: Strings = implementedInJetBrainsFork()
+        actual val DatePickerHeadlineDescription: Strings = implementedInJetBrainsFork()
+        actual val DatePickerNoSelectionDescription: Strings = implementedInJetBrainsFork()
+        actual val DatePickerTodayDescription: Strings = implementedInJetBrainsFork()
+        actual val DatePickerScrollToShowLaterYears: Strings = implementedInJetBrainsFork()
+        actual val DatePickerScrollToShowEarlierYears: Strings = implementedInJetBrainsFork()
+        actual val DateInputTitle: Strings = implementedInJetBrainsFork()
+        actual val DateInputHeadline: Strings = implementedInJetBrainsFork()
+        actual val DateInputLabel: Strings = implementedInJetBrainsFork()
+        actual val DateInputHeadlineDescription: Strings = implementedInJetBrainsFork()
+        actual val DateInputNoInputDescription: Strings = implementedInJetBrainsFork()
+        actual val DateInputInvalidNotAllowed: Strings = implementedInJetBrainsFork()
+        actual val DateInputInvalidForPattern: Strings = implementedInJetBrainsFork()
+        actual val DateInputInvalidYearRange: Strings = implementedInJetBrainsFork()
+        actual val DatePickerSwitchToCalendarMode: Strings = implementedInJetBrainsFork()
+        actual val DatePickerSwitchToInputMode: Strings = implementedInJetBrainsFork()
+        actual val DateRangePickerTitle: Strings = implementedInJetBrainsFork()
+        actual val DateRangePickerStartHeadline: Strings = implementedInJetBrainsFork()
+        actual val DateRangePickerEndHeadline: Strings = implementedInJetBrainsFork()
+        actual val DateRangePickerScrollToShowNextMonth: Strings = implementedInJetBrainsFork()
+        actual val DateRangePickerScrollToShowPreviousMonth: Strings = implementedInJetBrainsFork()
+        actual val DateRangePickerDayInRange: Strings = implementedInJetBrainsFork()
+        actual val DateRangeInputTitle: Strings = implementedInJetBrainsFork()
+        actual val DateRangeInputInvalidRangeInput: Strings = implementedInJetBrainsFork()
+        actual val BottomSheetPaneTitle: Strings = implementedInJetBrainsFork()
+        actual val BottomSheetDragHandleDescription: Strings = implementedInJetBrainsFork()
+        actual val BottomSheetPartialExpandDescription: Strings = implementedInJetBrainsFork()
+        actual val BottomSheetDismissDescription: Strings = implementedInJetBrainsFork()
+        actual val BottomSheetExpandDescription: Strings = implementedInJetBrainsFork()
+        actual val TooltipLongPressLabel: Strings = implementedInJetBrainsFork()
+        actual val TimePickerAM: Strings = implementedInJetBrainsFork()
+        actual val TimePickerPM: Strings = implementedInJetBrainsFork()
+        actual val TimePickerPeriodToggle: Strings = implementedInJetBrainsFork()
+        actual val TimePickerHourSelection: Strings = implementedInJetBrainsFork()
+        actual val TimePickerMinuteSelection: Strings = implementedInJetBrainsFork()
+        actual val TimePickerHourSuffix: Strings = implementedInJetBrainsFork()
+        actual val TimePicker24HourSuffix: Strings = implementedInJetBrainsFork()
+        actual val TimePickerMinuteSuffix: Strings = implementedInJetBrainsFork()
+        actual val TimePickerHour: Strings = implementedInJetBrainsFork()
+        actual val TimePickerMinute: Strings = implementedInJetBrainsFork()
+        actual val TimePickerHourTextField: Strings = implementedInJetBrainsFork()
+        actual val TimePickerMinuteTextField: Strings = implementedInJetBrainsFork()
+        actual val TooltipPaneDescription: Strings = implementedInJetBrainsFork()
+        actual val ExposedDropdownMenu: Strings = implementedInJetBrainsFork()
+        actual val ToggleDropdownMenu: Strings = implementedInJetBrainsFork()
+    }
+}
+
+@Composable
+@ReadOnlyComposable
+internal actual fun getString(string: Strings, vararg formatArgs: Any): String =
+    implementedInJetBrainsFork()
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/SystemBarsDefaultInsets.jvmStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/SystemBarsDefaultInsets.commonStubs.kt
similarity index 100%
rename from compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/SystemBarsDefaultInsets.jvmStubs.kt
rename to compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/SystemBarsDefaultInsets.commonStubs.kt
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/CalendarLocale.jvmStubs.kt b/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/CalendarLocale.jvmStubs.kt
deleted file mode 100644
index 02ed679..0000000
--- a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/CalendarLocale.jvmStubs.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material3
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
-
-/** Returns the default [CalendarLocale]. */
-@Composable
-@ReadOnlyComposable
-@OptIn(ExperimentalMaterial3Api::class)
-internal actual fun defaultLocale(): CalendarLocale = implementedInJetBrainsFork()
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/CalendarModel.jvmStubs.kt b/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/CalendarModel.jvmStubs.kt
deleted file mode 100644
index ab14c60..0000000
--- a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/CalendarModel.jvmStubs.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material3.internal
-
-import androidx.compose.material3.CalendarLocale
-import androidx.compose.material3.implementedInJetBrainsFork
-
-internal actual fun createCalendarModel(locale: CalendarLocale): CalendarModel =
-    implementedInJetBrainsFork()
-
-internal actual fun formatWithSkeleton(
-    utcTimeMillis: Long,
-    skeleton: String,
-    locale: CalendarLocale,
-    cache: MutableMap<String, Any>
-): String = implementedInJetBrainsFork()
diff --git a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/Strings.jvmStubs.kt b/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/Strings.jvmStubs.kt
deleted file mode 100644
index 8b30e91..0000000
--- a/compose/material3/material3/src/jvmStubsMain/kotlin/androidx/compose/material3/internal/Strings.jvmStubs.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.material3.internal
-
-import androidx.compose.material3.implementedInJetBrainsFork
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.ReadOnlyComposable
-
-@Composable
-@ReadOnlyComposable
-internal actual fun getString(string: Strings): String = implementedInJetBrainsFork()
-
-@JvmInline
-@Immutable
-internal actual value class Strings constructor(val value: Int) {
-    actual companion object {
-        actual val NavigationMenu: Strings = implementedInJetBrainsFork()
-        actual val CloseDrawer: Strings = implementedInJetBrainsFork()
-        actual val CloseSheet: Strings = implementedInJetBrainsFork()
-        actual val DefaultErrorMessage: Strings = implementedInJetBrainsFork()
-        actual val SliderRangeStart: Strings = implementedInJetBrainsFork()
-        actual val SliderRangeEnd: Strings = implementedInJetBrainsFork()
-        actual val Dialog: Strings = implementedInJetBrainsFork()
-        actual val MenuExpanded: Strings = implementedInJetBrainsFork()
-        actual val MenuCollapsed: Strings = implementedInJetBrainsFork()
-        actual val SnackbarDismiss: Strings = implementedInJetBrainsFork()
-        actual val SearchBarSearch: Strings = implementedInJetBrainsFork()
-        actual val SuggestionsAvailable: Strings = implementedInJetBrainsFork()
-        actual val DatePickerTitle: Strings = implementedInJetBrainsFork()
-        actual val DatePickerHeadline: Strings = implementedInJetBrainsFork()
-        actual val DatePickerYearPickerPaneTitle: Strings = implementedInJetBrainsFork()
-        actual val DatePickerSwitchToYearSelection: Strings = implementedInJetBrainsFork()
-        actual val DatePickerSwitchToDaySelection: Strings = implementedInJetBrainsFork()
-        actual val DatePickerSwitchToNextMonth: Strings = implementedInJetBrainsFork()
-        actual val DatePickerSwitchToPreviousMonth: Strings = implementedInJetBrainsFork()
-        actual val DatePickerNavigateToYearDescription: Strings = implementedInJetBrainsFork()
-        actual val DatePickerHeadlineDescription: Strings = implementedInJetBrainsFork()
-        actual val DatePickerNoSelectionDescription: Strings = implementedInJetBrainsFork()
-        actual val DatePickerTodayDescription: Strings = implementedInJetBrainsFork()
-        actual val DatePickerScrollToShowLaterYears: Strings = implementedInJetBrainsFork()
-        actual val DatePickerScrollToShowEarlierYears: Strings = implementedInJetBrainsFork()
-        actual val DateInputTitle: Strings = implementedInJetBrainsFork()
-        actual val DateInputHeadline: Strings = implementedInJetBrainsFork()
-        actual val DateInputLabel: Strings = implementedInJetBrainsFork()
-        actual val DateInputHeadlineDescription: Strings = implementedInJetBrainsFork()
-        actual val DateInputNoInputDescription: Strings = implementedInJetBrainsFork()
-        actual val DateInputInvalidNotAllowed: Strings = implementedInJetBrainsFork()
-        actual val DateInputInvalidForPattern: Strings = implementedInJetBrainsFork()
-        actual val DateInputInvalidYearRange: Strings = implementedInJetBrainsFork()
-        actual val DatePickerSwitchToCalendarMode: Strings = implementedInJetBrainsFork()
-        actual val DatePickerSwitchToInputMode: Strings = implementedInJetBrainsFork()
-        actual val DateRangePickerTitle: Strings = implementedInJetBrainsFork()
-        actual val DateRangePickerStartHeadline: Strings = implementedInJetBrainsFork()
-        actual val DateRangePickerEndHeadline: Strings = implementedInJetBrainsFork()
-        actual val DateRangePickerScrollToShowNextMonth: Strings = implementedInJetBrainsFork()
-        actual val DateRangePickerScrollToShowPreviousMonth: Strings = implementedInJetBrainsFork()
-        actual val DateRangePickerDayInRange: Strings = implementedInJetBrainsFork()
-        actual val DateRangeInputTitle: Strings = implementedInJetBrainsFork()
-        actual val DateRangeInputInvalidRangeInput: Strings = implementedInJetBrainsFork()
-        actual val BottomSheetPaneTitle: Strings = implementedInJetBrainsFork()
-        actual val BottomSheetDragHandleDescription: Strings = implementedInJetBrainsFork()
-        actual val BottomSheetPartialExpandDescription: Strings = implementedInJetBrainsFork()
-        actual val BottomSheetDismissDescription: Strings = implementedInJetBrainsFork()
-        actual val BottomSheetExpandDescription: Strings = implementedInJetBrainsFork()
-        actual val TooltipLongPressLabel: Strings = implementedInJetBrainsFork()
-        actual val TimePickerAM: Strings = implementedInJetBrainsFork()
-        actual val TimePickerPM: Strings = implementedInJetBrainsFork()
-        actual val TimePickerPeriodToggle: Strings = implementedInJetBrainsFork()
-        actual val TimePickerHourSelection: Strings = implementedInJetBrainsFork()
-        actual val TimePickerMinuteSelection: Strings = implementedInJetBrainsFork()
-        actual val TimePickerHourSuffix: Strings = implementedInJetBrainsFork()
-        actual val TimePicker24HourSuffix: Strings = implementedInJetBrainsFork()
-        actual val TimePickerMinuteSuffix: Strings = implementedInJetBrainsFork()
-        actual val TimePickerHour: Strings = implementedInJetBrainsFork()
-        actual val TimePickerMinute: Strings = implementedInJetBrainsFork()
-        actual val TimePickerHourTextField: Strings = implementedInJetBrainsFork()
-        actual val TimePickerMinuteTextField: Strings = implementedInJetBrainsFork()
-        actual val TooltipPaneDescription: Strings = implementedInJetBrainsFork()
-        actual val ExposedDropdownMenu: Strings = implementedInJetBrainsFork()
-        actual val ToggleDropdownMenu: Strings = implementedInJetBrainsFork()
-    }
-}
-
-@Composable
-@ReadOnlyComposable
-internal actual fun getString(string: Strings, vararg formatArgs: Any): String =
-    implementedInJetBrainsFork()
diff --git a/compose/runtime/runtime-livedata/build.gradle b/compose/runtime/runtime-livedata/build.gradle
index 3efb105..d82bd53 100644
--- a/compose/runtime/runtime-livedata/build.gradle
+++ b/compose/runtime/runtime-livedata/build.gradle
@@ -53,10 +53,10 @@
     inceptionYear = "2020"
     description = "Compose integration with LiveData"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     samples(projectOrArtifact(":compose:runtime:runtime-livedata:runtime-livedata-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.livedata"
 }
diff --git a/compose/runtime/runtime-livedata/samples/build.gradle b/compose/runtime/runtime-livedata/samples/build.gradle
index 4f5ab4b..80ae202 100644
--- a/compose/runtime/runtime-livedata/samples/build.gradle
+++ b/compose/runtime/runtime-livedata/samples/build.gradle
@@ -47,5 +47,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.livedata.samples"
 }
diff --git a/compose/runtime/runtime-rxjava2/build.gradle b/compose/runtime/runtime-rxjava2/build.gradle
index 682c0a4..5750ab4 100644
--- a/compose/runtime/runtime-rxjava2/build.gradle
+++ b/compose/runtime/runtime-rxjava2/build.gradle
@@ -51,10 +51,10 @@
     inceptionYear = "2020"
     description = "Compose integration with RxJava 2"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     samples(projectOrArtifact(":compose:runtime:runtime-rxjava2:runtime-rxjava2-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.rxjava2"
 }
diff --git a/compose/runtime/runtime-rxjava2/samples/build.gradle b/compose/runtime/runtime-rxjava2/samples/build.gradle
index af352ad..7360519 100644
--- a/compose/runtime/runtime-rxjava2/samples/build.gradle
+++ b/compose/runtime/runtime-rxjava2/samples/build.gradle
@@ -47,5 +47,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.rxjava2.samples"
 }
diff --git a/compose/runtime/runtime-rxjava3/build.gradle b/compose/runtime/runtime-rxjava3/build.gradle
index 490e4e0..7aac897 100644
--- a/compose/runtime/runtime-rxjava3/build.gradle
+++ b/compose/runtime/runtime-rxjava3/build.gradle
@@ -51,10 +51,10 @@
     inceptionYear = "2020"
     description = "Compose integration with RxJava 3"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     samples(projectOrArtifact(":compose:runtime:runtime-rxjava3:runtime-rxjava3-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.rxjava3"
 }
diff --git a/compose/runtime/runtime-rxjava3/samples/build.gradle b/compose/runtime/runtime-rxjava3/samples/build.gradle
index b0e37bc..61a0015 100644
--- a/compose/runtime/runtime-rxjava3/samples/build.gradle
+++ b/compose/runtime/runtime-rxjava3/samples/build.gradle
@@ -47,5 +47,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.rxjava3.samples"
 }
diff --git a/compose/runtime/runtime-saveable/build.gradle b/compose/runtime/runtime-saveable/build.gradle
index 45c128e..382f86e 100644
--- a/compose/runtime/runtime-saveable/build.gradle
+++ b/compose/runtime/runtime-saveable/build.gradle
@@ -40,7 +40,7 @@
     sourceSets {
         commonMain {
             dependencies {
-                implementation(libs.kotlinStdlibCommon)
+                implementation(libs.kotlinStdlib)
                 api(project(":compose:runtime:runtime"))
             }
         }
@@ -60,7 +60,7 @@
             dependsOn(jvmMain)
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
@@ -110,9 +110,11 @@
     inceptionYear = "2020"
     description = "Compose components that allow saving and restoring the local ui state"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:runtime:runtime-saveable:runtime-saveable-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.saveable"
 }
diff --git a/compose/runtime/runtime-saveable/samples/build.gradle b/compose/runtime/runtime-saveable/samples/build.gradle
index d994399..dec1229a 100644
--- a/compose/runtime/runtime-saveable/samples/build.gradle
+++ b/compose/runtime/runtime-saveable/samples/build.gradle
@@ -50,5 +50,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.saveable.samples"
 }
diff --git a/compose/runtime/runtime-test-utils/build.gradle b/compose/runtime/runtime-test-utils/build.gradle
index 141b480..82a06ac 100644
--- a/compose/runtime/runtime-test-utils/build.gradle
+++ b/compose/runtime/runtime-test-utils/build.gradle
@@ -53,10 +53,14 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
     }
 }
diff --git a/compose/runtime/runtime-test-utils/lint-baseline.xml b/compose/runtime/runtime-test-utils/lint-baseline.xml
index 6d10e42..8868abf 100644
--- a/compose/runtime/runtime-test-utils/lint-baseline.xml
+++ b/compose/runtime/runtime-test-utils/lint-baseline.xml
@@ -1,104 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="    val filtered get() = contacts.filter { it.name.contains(filter) }"
-        errorLine2="                                  ~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/ContactModel.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                    val toRun = awaiters.toList()"
-        errorLine2="                                         ~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                    toRun.map { it.runFrame(frameTime) }.forEach { it() }"
-        errorLine2="                          ~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                    toRun.map { it.runFrame(frameTime) }.forEach { it() }"
-        errorLine2="                                                         ~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            children.forEach { it.render(indent + 2, builder) }"
-        errorLine2="                     ~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                removedChildren.forEach { child -> child.parent = null }"
-        errorLine2="                                ~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            val copyOfItems = itemsToMove.map { it }"
-        errorLine2="                                          ~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        children.forEach { child -> child.parent = null }"
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        else attributes.map { &quot; ${it.key}=&apos;${it.value}&apos;&quot; }.joinToString()"
-        errorLine2="                                                           ~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        children.map { it.toString() }.joinToString(&quot; &quot;)"
-        errorLine2="                 ~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        children.map { it.toString() }.joinToString(&quot; &quot;)"
-        errorLine2="                                       ~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ListIterator"
@@ -109,13 +10,4 @@
             file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
     </issue>
 
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="fun View.flatten(): List&lt;View> = listOf(this) + children.flatMap { it.flatten() }"
-        errorLine2="                                                         ~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/runtime/mock/View.kt"/>
-    </issue>
-
 </issues>
diff --git a/compose/runtime/runtime-test-utils/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/mock/SynchronizedObject.linuxx64Stubs.kt b/compose/runtime/runtime-test-utils/src/commonStubsMain/kotlin/androidx/compose/runtime/mock/SynchronizedObject.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime-test-utils/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/mock/SynchronizedObject.linuxx64Stubs.kt
rename to compose/runtime/runtime-test-utils/src/commonStubsMain/kotlin/androidx/compose/runtime/mock/SynchronizedObject.commonStubs.kt
diff --git a/compose/runtime/runtime-tracing/build.gradle b/compose/runtime/runtime-tracing/build.gradle
index 658367c..d6c37d5 100644
--- a/compose/runtime/runtime-tracing/build.gradle
+++ b/compose/runtime/runtime-tracing/build.gradle
@@ -34,7 +34,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.3.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(libs.kotlinStdlib)
     implementation("androidx.compose.runtime:runtime:1.3.3")
     implementation("androidx.tracing:tracing-perfetto:1.0.0")
@@ -49,6 +49,5 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2022"
     description = "Additional tracing in Compose"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index ee38e7d..03b44f4 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -36,6 +36,7 @@
   }
 
   public final class BroadcastFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public BroadcastFrameClock();
     ctor public BroadcastFrameClock(optional kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
     method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
     method public boolean getHasAwaiters();
@@ -150,6 +151,7 @@
     method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public void recordSideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
     method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public void recordUsed(androidx.compose.runtime.RecomposeScope scope);
     method @androidx.compose.runtime.ComposeCompilerApi public Object? rememberedValue();
+    method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public boolean shouldExecute(boolean parametersChanged);
     method @androidx.compose.runtime.ComposeCompilerApi public void skipCurrentGroup();
     method @androidx.compose.runtime.ComposeCompilerApi public void skipToGroupEnd();
     method public void sourceInformation(String sourceInformation);
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 00784cf..565de13 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -40,6 +40,7 @@
   }
 
   public final class BroadcastFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public BroadcastFrameClock();
     ctor public BroadcastFrameClock(optional kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
     method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
     method public boolean getHasAwaiters();
@@ -155,6 +156,7 @@
     method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public void recordSideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
     method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public void recordUsed(androidx.compose.runtime.RecomposeScope scope);
     method @androidx.compose.runtime.ComposeCompilerApi public Object? rememberedValue();
+    method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public boolean shouldExecute(boolean parametersChanged);
     method @androidx.compose.runtime.ComposeCompilerApi public void skipCurrentGroup();
     method @androidx.compose.runtime.ComposeCompilerApi public void skipToGroupEnd();
     method public void sourceInformation(String sourceInformation);
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index 9618c50..a82f5ac 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -111,17 +111,20 @@
             dependsOn(commonMain)
         }
 
-        jvmStubsMain {
-            dependsOn(jvmMain)
+        commonStubsMain {
             dependsOn(nonAndroidMain)
         }
 
+        jvmStubsMain {
+            dependsOn(commonStubsMain)
+        }
+
         jvmStubsTest {
             dependsOn(jvmTest)
         }
 
         linuxx64StubsMain {
-            dependsOn(nonAndroidMain)
+            dependsOn(commonStubsMain)
         }
     }
 }
@@ -144,5 +147,6 @@
     inceptionYear = "2019"
     description = "Tree composition support for code generated by the Compose compiler plugin and corresponding public API"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:runtime:runtime:runtime-samples"))
 }
diff --git a/compose/runtime/runtime/compose-runtime-benchmark/build.gradle b/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
index 56d4d26..2e1c4e4 100644
--- a/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
@@ -22,7 +22,14 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.benchmark"
+
+    buildTypes {
+        release {
+            minifyEnabled true
+        }
+    }
 }
 
 dependencies {
diff --git a/compose/runtime/runtime/integration-tests/build.gradle b/compose/runtime/runtime/integration-tests/build.gradle
index 588e594..f4785bb 100644
--- a/compose/runtime/runtime/integration-tests/build.gradle
+++ b/compose/runtime/runtime/integration-tests/build.gradle
@@ -59,7 +59,7 @@
             dependsOn(jvmMain)
             dependencies {
                 api(libs.kotlinCoroutinesAndroid)
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
 
                 implementation("androidx.core:core-ktx:1.1.0")
             }
@@ -93,6 +93,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.integrationtests"
 }
 
@@ -177,4 +178,4 @@
         task.source = findFile()
         task.sizes = project.findProperty("compose.newExpectedSizes")
     }
-}
\ No newline at end of file
+}
diff --git a/compose/runtime/runtime/integration-tests/lint-baseline.xml b/compose/runtime/runtime/integration-tests/lint-baseline.xml
index 80c0abd..ddb52c8 100644
--- a/compose/runtime/runtime/integration-tests/lint-baseline.xml
+++ b/compose/runtime/runtime/integration-tests/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                    sleep(1)"
-        errorLine2="                    ~~~~~">
+        errorLine1="                        sleep(1)"
+        errorLine2="                        ~~~~~">
         <location
             file="src/androidInstrumentedTest/kotlin/androidx/compose/runtime/AndroidSnapshotTests.kt"/>
     </issue>
diff --git a/compose/runtime/runtime/lint-baseline.xml b/compose/runtime/runtime/lint-baseline.xml
index 457d006..db0da51 100644
--- a/compose/runtime/runtime/lint-baseline.xml
+++ b/compose/runtime/runtime/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ExperimentalPropertyAnnotation"
@@ -22,8 +22,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable useDefault with type List&lt;? extends Integer>: replace with IntList"
-        errorLine1="            val useDefault ="
-        errorLine2="            ^">
+        errorLine1="                val useDefault ="
+        errorLine2="                ^">
         <location
             file="src/jvmMain/kotlin/androidx/compose/runtime/reflect/ComposableMethod.jvm.kt"/>
     </issue>
@@ -67,8 +67,8 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of keys: replace with IntList"
-        errorLine1="    private fun keys() = groups.keys(groupsSize * Group_Fields_Size)"
-        errorLine2="                ~~~~">
+        errorLine1="    @Suppress(&quot;unused&quot;) private fun keys() = groups.keys(groupsSize * Group_Fields_Size)"
+        errorLine2="                                    ~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt"/>
     </issue>
@@ -76,8 +76,8 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of nodes: replace with IntList"
-        errorLine1="    private fun nodes() = groups.nodeCounts(groupsSize * Group_Fields_Size)"
-        errorLine2="                ~~~~~">
+        errorLine1="    @Suppress(&quot;unused&quot;) private fun nodes() = groups.nodeCounts(groupsSize * Group_Fields_Size)"
+        errorLine2="                                    ~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt"/>
     </issue>
@@ -103,8 +103,8 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of groupSizes: replace with IntList"
-        errorLine1="    private fun groupSizes() = groups.groupSizes(groupsSize * Group_Fields_Size)"
-        errorLine2="                ~~~~~~~~~~">
+        errorLine1="    @Suppress(&quot;unused&quot;) private fun groupSizes() = groups.groupSizes(groupsSize * Group_Fields_Size)"
+        errorLine2="                                    ~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt"/>
     </issue>
@@ -112,7 +112,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of dataIndexes: replace with IntList"
-        errorLine1="    private fun IntArray.dataIndexes() = groups.dataAnchors().let {"
+        errorLine1="    private fun IntArray.dataIndexes() ="
         errorLine2="                         ~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt"/>
@@ -121,7 +121,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of keys: replace with IntList"
-        errorLine1="    private fun keys() = groups.keys().fastFilterIndexed { index, _ ->"
+        errorLine1="    private fun keys() ="
         errorLine2="                ~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt"/>
@@ -130,7 +130,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Integer> of keys: replace with IntList"
-        errorLine1="private fun IntArray.keys(len: Int = size) ="
+        errorLine1="private fun IntArray.keys(len: Int = size) = slice(Key_Offset until len step Group_Fields_Size)"
         errorLine2="                     ~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt"/>
diff --git a/compose/runtime/runtime/samples/build.gradle b/compose/runtime/runtime/samples/build.gradle
index 1fab6a6..11be910 100644
--- a/compose/runtime/runtime/samples/build.gradle
+++ b/compose/runtime/runtime/samples/build.gradle
@@ -49,5 +49,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.runtime.samples"
 }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index 96121f1..0e48726 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -977,6 +977,16 @@
      */
     @InternalComposeApi fun recordUsed(scope: RecomposeScope)
 
+    /**
+     * A Compose compiler plugin API. DO NOT call directly.
+     *
+     * Generated by the compile to determine if the composable function should be executed. It may
+     * not execute if parameter has not changed and the nothing else is forcing the function to
+     * execute (such as its scope was invalidated or a static composition local it was changed) or
+     * the composition is pausable and the composition is pausing.
+     */
+    @InternalComposeApi fun shouldExecute(parametersChanged: Boolean): Boolean
+
     // Internal API
 
     /**
@@ -3027,17 +3037,28 @@
         reader.skipToGroupEnd()
     }
 
+    @ComposeCompilerApi
+    override fun shouldExecute(parametersChanged: Boolean): Boolean {
+        return parametersChanged || !skipping
+    }
+
     /** Skip to the end of the group opened by [startGroup]. */
     @ComposeCompilerApi
     override fun skipToGroupEnd() {
         runtimeCheck(groupNodeCount == 0) {
             "No nodes can be emitted before calling skipAndEndGroup"
         }
-        currentRecomposeScope?.scopeSkipped()
-        if (invalidations.isEmpty()) {
-            skipReaderToGroupEnd()
-        } else {
-            recomposeToGroupEnd()
+
+        // This can be called when inserting is true and `shouldExecute` returns false.
+        // When `inserting` the writer is already at the end of the group so we don't need to
+        // move the writer.
+        if (!inserting) {
+            currentRecomposeScope?.scopeSkipped()
+            if (invalidations.isEmpty()) {
+                skipReaderToGroupEnd()
+            } else {
+                recomposeToGroupEnd()
+            }
         }
     }
 
diff --git a/compose/runtime/runtime/src/jvmStubsMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.jvmStubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmStubsMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.jvmStubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.commonStubs.kt
diff --git a/compose/runtime/runtime/src/jvmStubsMain/kotlin/androidx/compose/runtime/NotImplemented.jvmStubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/jvmStubsMain/kotlin/androidx/compose/runtime/NotImplemented.jvmStubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/NotImplemented.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/SynchronizedObject.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/SynchronizedObject.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/SynchronizedObject.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/SynchronizedObject.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/TestOnly.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/TestOnly.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/TestOnly.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/TestOnly.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/Atomic.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/Atomic.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/Atomic.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/Atomic.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/JvmDefaultWithCompatibility.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/JvmDefaultWithCompatibility.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/JvmDefaultWithCompatibility.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/JvmDefaultWithCompatibility.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/Thread.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/Thread.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/Thread.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/Thread.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/Utils.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/Utils.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/Utils.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/Utils.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/WeakReference.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/WeakReference.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/internal/WeakReference.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/internal/WeakReference.commonStubs.kt
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElement.linuxx64Stubs.kt b/compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElement.commonStubs.kt
similarity index 100%
rename from compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElement.linuxx64Stubs.kt
rename to compose/runtime/runtime/src/commonStubsMain/kotlin/androidx/compose/runtime/snapshots/SnapshotContextElement.commonStubs.kt
diff --git a/compose/runtime/runtime/src/jvmStubsMain/kotlin/androidx/compose/runtime/internal/Utils.jvmStubs.kt b/compose/runtime/runtime/src/jvmStubsMain/kotlin/androidx/compose/runtime/internal/Utils.jvmStubs.kt
deleted file mode 100644
index 74c5291..0000000
--- a/compose/runtime/runtime/src/jvmStubsMain/kotlin/androidx/compose/runtime/internal/Utils.jvmStubs.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.runtime.internal
-
-import androidx.compose.runtime.implementedInJetBrainsFork
-
-internal actual fun logError(message: String, e: Throwable): Unit = implementedInJetBrainsFork()
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.linuxx64Stubs.kt b/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.linuxx64Stubs.kt
deleted file mode 100644
index 2cf85ce..0000000
--- a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.linuxx64Stubs.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.runtime
-
-actual val DefaultMonotonicFrameClock: MonotonicFrameClock
-    get() = implementedInJetBrainsFork()
diff --git a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/NotImplemented.linuxx64Stubs.kt b/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/NotImplemented.linuxx64Stubs.kt
deleted file mode 100644
index c1b2c07..0000000
--- a/compose/runtime/runtime/src/linuxx64StubsMain/kotlin/androidx/compose/runtime/NotImplemented.linuxx64Stubs.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.runtime
-
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
-    throw NotImplementedError(
-        """
-        Implemented only in JetBrains fork.
-        Please use `org.jetbrains.compose.runtime:runtime` package instead.
-        """
-            .trimIndent()
-    )
diff --git a/compose/test-utils/build.gradle b/compose/test-utils/build.gradle
index 7745525..1fe35c8 100644
--- a/compose/test-utils/build.gradle
+++ b/compose/test-utils/build.gradle
@@ -107,5 +107,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.testutils"
 }
diff --git a/compose/test-utils/src/androidInstrumentedTest/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunnerTest.kt b/compose/test-utils/src/androidInstrumentedTest/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunnerTest.kt
index d23b45c..3793dc6 100644
--- a/compose/test-utils/src/androidInstrumentedTest/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunnerTest.kt
+++ b/compose/test-utils/src/androidInstrumentedTest/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunnerTest.kt
@@ -19,6 +19,7 @@
 import androidx.activity.ComponentActivity
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -27,12 +28,18 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.focusTarget
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.test.junit4.AndroidComposeTestRule
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
 import kotlin.coroutines.suspendCoroutine
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
@@ -198,6 +205,26 @@
     }
 
     @Test
+    fun layout_preservesActiveFocus() {
+        lateinit var focusState: FocusState
+        composeTestRule
+            .forGivenContent {
+                val focusRequester = FocusRequester()
+                Box(
+                    Modifier.fillMaxSize()
+                        .onFocusChanged { focusState = it }
+                        .focusRequester(focusRequester)
+                        .focusTarget()
+                )
+                LaunchedEffect(Unit) { focusRequester.requestFocus() }
+            }
+            .performTestWithEventsControl {
+                doFrame()
+                assertThat(focusState.isFocused).isTrue()
+            }
+    }
+
+    @Test
     fun countLaunchedCoroutines_noContentLaunches() {
         composeTestRule
             .forGivenContent { Box { Text("Hello") } }
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
index 25992b7..b7fa4ac 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
@@ -29,7 +29,6 @@
 import android.widget.ImageView
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.snapshots.Snapshot
@@ -227,7 +226,12 @@
             "Layout can be only executed after measure, current state is '$simulationState'"
         }
         val view = getView()
-        view.layout(view.left, view.top, view.right, view.bottom)
+        view.layout(
+            /* l= */ 0,
+            /* t= */ 0,
+            /* r= */ view.measuredWidth,
+            /* b= */ view.measuredHeight
+        )
         simulationState = SimulationState.LayoutDone
     }
 
@@ -415,7 +419,6 @@
 
 @RequiresApi(28)
 private object BitmapHelper {
-    @DoNotInline
     fun createBitmap(picture: Picture): Bitmap {
         return Bitmap.createBitmap(picture)
     }
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ViewCapture.android.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ViewCapture.android.kt
index aa2e1ee..7c2d95c 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ViewCapture.android.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/ViewCapture.android.kt
@@ -29,7 +29,6 @@
 import android.view.View
 import android.view.ViewTreeObserver
 import android.view.Window
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.asImageBitmap
@@ -120,7 +119,6 @@
 
 @RequiresApi(29)
 private object FrameCommitCallbackHelper {
-    @DoNotInline
     fun registerFrameCommitCallback(viewTreeObserver: ViewTreeObserver, runnable: Runnable) {
         viewTreeObserver.registerFrameCommitCallback(runnable)
     }
@@ -128,7 +126,6 @@
 
 @RequiresApi(Build.VERSION_CODES.O)
 private object PixelCopyHelper {
-    @DoNotInline
     fun request(
         source: Window,
         srcRect: Rect?,
diff --git a/compose/ui/ui-android-stubs/build.gradle b/compose/ui/ui-android-stubs/build.gradle
index 7b4498e..da858f2 100644
--- a/compose/ui/ui-android-stubs/build.gradle
+++ b/compose/ui/ui-android-stubs/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
@@ -37,7 +37,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2020"
     description = "Stubs for classes in older Android APIs"
-    metalavaK2UastEnabled = true
     doNotDocumentReason = "Not published to maven"
 }
 
diff --git a/compose/ui/ui-geometry/build.gradle b/compose/ui/ui-geometry/build.gradle
index e988544..9a0e69b 100644
--- a/compose/ui/ui-geometry/build.gradle
+++ b/compose/ui/ui-geometry/build.gradle
@@ -34,6 +34,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -61,14 +62,21 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
             dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -89,6 +97,7 @@
     inceptionYear = "2020"
     description = "Compose classes related to dimensions without units"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 android {
diff --git a/compose/ui/ui-graphics/api/current.txt b/compose/ui/ui-graphics/api/current.txt
index 28f5720..a3306fe 100644
--- a/compose/ui/ui-graphics/api/current.txt
+++ b/compose/ui/ui-graphics/api/current.txt
@@ -85,6 +85,7 @@
   }
 
   public final class AndroidPath implements androidx.compose.ui.graphics.Path {
+    ctor public AndroidPath();
     ctor public AndroidPath(optional android.graphics.Path internalPath);
     method public void addArc(androidx.compose.ui.geometry.Rect oval, float startAngleDegrees, float sweepAngleDegrees);
     method public void addArcRad(androidx.compose.ui.geometry.Rect oval, float startAngleRadians, float sweepAngleRadians);
@@ -402,6 +403,7 @@
   }
 
   @kotlin.jvm.JvmInline public final value class ColorMatrix {
+    ctor public ColorMatrix();
     ctor public ColorMatrix(optional float[] values);
     method public void convertRgbToYuv();
     method public void convertYuvToRgb();
@@ -510,6 +512,7 @@
   }
 
   @kotlin.jvm.JvmInline public final value class Matrix {
+    ctor public Matrix();
     ctor public Matrix(optional float[] values);
     method public inline operator float get(int row, int column);
     method public float[] getValues();
@@ -899,6 +902,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shadow {
+    ctor public Shadow();
     ctor public Shadow(optional @androidx.compose.runtime.Stable long color, optional @androidx.compose.runtime.Stable long offset, optional @androidx.compose.runtime.Stable float blurRadius);
     method public androidx.compose.ui.graphics.Shadow copy(optional long color, optional long offset, optional float blurRadius);
     method public float getBlurRadius();
@@ -1385,6 +1389,7 @@
   }
 
   public final class Stroke extends androidx.compose.ui.graphics.drawscope.DrawStyle {
+    ctor public Stroke();
     ctor public Stroke(optional float width, optional float miter, optional int cap, optional int join, optional androidx.compose.ui.graphics.PathEffect? pathEffect);
     method public int getCap();
     method public int getJoin();
diff --git a/compose/ui/ui-graphics/api/restricted_current.txt b/compose/ui/ui-graphics/api/restricted_current.txt
index 6fdf5ca..423e117 100644
--- a/compose/ui/ui-graphics/api/restricted_current.txt
+++ b/compose/ui/ui-graphics/api/restricted_current.txt
@@ -115,6 +115,7 @@
   }
 
   public final class AndroidPath implements androidx.compose.ui.graphics.Path {
+    ctor public AndroidPath();
     ctor public AndroidPath(optional android.graphics.Path internalPath);
     method public void addArc(androidx.compose.ui.geometry.Rect oval, float startAngleDegrees, float sweepAngleDegrees);
     method public void addArcRad(androidx.compose.ui.geometry.Rect oval, float startAngleRadians, float sweepAngleRadians);
@@ -443,6 +444,7 @@
   }
 
   @kotlin.jvm.JvmInline public final value class ColorMatrix {
+    ctor public ColorMatrix();
     ctor public ColorMatrix(optional float[] values);
     method public void convertRgbToYuv();
     method public void convertYuvToRgb();
@@ -582,6 +584,7 @@
   }
 
   @kotlin.jvm.JvmInline public final value class Matrix {
+    ctor public Matrix();
     ctor public Matrix(optional float[] values);
     method public inline operator float get(int row, int column);
     method public float[] getValues();
@@ -971,6 +974,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shadow {
+    ctor public Shadow();
     ctor public Shadow(optional @androidx.compose.runtime.Stable long color, optional @androidx.compose.runtime.Stable long offset, optional @androidx.compose.runtime.Stable float blurRadius);
     method public androidx.compose.ui.graphics.Shadow copy(optional long color, optional long offset, optional float blurRadius);
     method public float getBlurRadius();
@@ -1347,6 +1351,7 @@
   }
 
   @kotlin.PublishedApi internal static final class CanvasDrawScope.DrawParams {
+    ctor public CanvasDrawScope.DrawParams();
     ctor public CanvasDrawScope.DrawParams(optional androidx.compose.ui.unit.Density density, optional androidx.compose.ui.unit.LayoutDirection layoutDirection, optional androidx.compose.ui.graphics.Canvas canvas, optional long size);
     method public androidx.compose.ui.unit.Density component1();
     method public androidx.compose.ui.unit.LayoutDirection component2();
@@ -1481,6 +1486,7 @@
   }
 
   public final class Stroke extends androidx.compose.ui.graphics.drawscope.DrawStyle {
+    ctor public Stroke();
     ctor public Stroke(optional float width, optional float miter, optional int cap, optional int join, optional androidx.compose.ui.graphics.PathEffect? pathEffect);
     method public int getCap();
     method public int getJoin();
diff --git a/compose/ui/ui-graphics/benchmark/build.gradle b/compose/ui/ui-graphics/benchmark/build.gradle
index f96bd8a..680479b 100644
--- a/compose/ui/ui-graphics/benchmark/build.gradle
+++ b/compose/ui/ui-graphics/benchmark/build.gradle
@@ -34,5 +34,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.graphics.benchmark"
 }
diff --git a/compose/ui/ui-graphics/benchmark/test/build.gradle b/compose/ui/ui-graphics/benchmark/test/build.gradle
index 550e37c..6823007 100644
--- a/compose/ui/ui-graphics/benchmark/test/build.gradle
+++ b/compose/ui/ui-graphics/benchmark/test/build.gradle
@@ -35,5 +35,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.graphics.benchmark.test"
 }
diff --git a/compose/ui/ui-graphics/build.gradle b/compose/ui/ui-graphics/build.gradle
index f31ca3a..146aa0a 100644
--- a/compose/ui/ui-graphics/build.gradle
+++ b/compose/ui/ui-graphics/build.gradle
@@ -34,6 +34,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -56,14 +57,8 @@
             }
         }
 
-        jvmMain {
-            dependsOn(commonMain)
-            dependencies {
-            }
-        }
-
         androidMain {
-            dependsOn(jvmMain)
+            dependsOn(commonMain)
             dependencies {
                 // This has stub APIs for access to legacy Android APIs, so we don't want
                 // any dependency on this module.
@@ -74,10 +69,14 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -116,6 +115,7 @@
     inceptionYear = "2020"
     description = "Compose graphics"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:ui:ui-graphics:ui-graphics-samples"))
 }
 
@@ -124,5 +124,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.graphics"
 }
diff --git a/compose/ui/ui-graphics/lint-baseline.xml b/compose/ui/ui-graphics/lint-baseline.xml
index d92d59c..9b84f2e 100644
--- a/compose/ui/ui-graphics/lint-baseline.xml
+++ b/compose/ui/ui-graphics/lint-baseline.xml
@@ -1,5 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha07)" variant="all" version="8.6.0-alpha07">
+
+    <issue
+        id="PrimitiveInCollection"
+        message="method drawPoints has parameter points with type List&lt;Offset>: replace with LongList"
+        errorLine1="    override fun drawPoints(pointMode: PointMode, points: List&lt;Offset>, paint: Paint) {"
+        errorLine2="                                                          ~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.android.kt"/>
+    </issue>
 
     <issue
         id="PrimitiveInCollection"
@@ -85,8 +94,8 @@
     <issue
         id="PrimitiveInCollection"
         message="method makeTransparentColors has parameter colors with type List&lt;Color>: replace with LongList"
-        errorLine1="    colors: List&lt;Color>,"
-        errorLine2="            ~~~~~~~~~~~">
+        errorLine1="internal fun makeTransparentColors(colors: List&lt;Color>, numTransparentColors: Int): IntArray {"
+        errorLine2="                                           ~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidShader.android.kt"/>
     </issue>
@@ -166,8 +175,8 @@
     <issue
         id="PrimitiveInCollection"
         message="method sweepGradient has parameter colors with type List&lt;Color>: replace with LongList"
-        errorLine1="            colors: List&lt;Color>,"
-        errorLine2="                    ~~~~~~~~~~~">
+        errorLine1="        fun sweepGradient(colors: List&lt;Color>, center: Offset = Offset.Unspecified): Brush ="
+        errorLine2="                                  ~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt"/>
     </issue>
@@ -295,6 +304,24 @@
         errorLine1="        points: List&lt;Offset>,"
         errorLine2="                ~~~~~~~~~~~~">
         <location
+            file="src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInCollection"
+        message="method drawPoints has parameter points with type List&lt;Offset>: replace with LongList"
+        errorLine1="        points: List&lt;Offset>,"
+        errorLine2="                ~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInCollection"
+        message="method drawPoints has parameter points with type List&lt;Offset>: replace with LongList"
+        errorLine1="        points: List&lt;Offset>,"
+        errorLine2="                ~~~~~~~~~~~~">
+        <location
             file="src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt"/>
     </issue>
 
@@ -309,6 +336,15 @@
 
     <issue
         id="PrimitiveInCollection"
+        message="method drawPoints has parameter points with type List&lt;Offset>: replace with LongList"
+        errorLine1="    override fun drawPoints(pointMode: PointMode, points: List&lt;Offset>, paint: Paint) {"
+        errorLine2="                                                          ~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInCollection"
         message="method LinearGradientShader has parameter colors with type List&lt;Color>: replace with LongList"
         errorLine1="    colors: List&lt;Color>,"
         errorLine2="            ~~~~~~~~~~~">
diff --git a/compose/ui/ui-graphics/samples/build.gradle b/compose/ui/ui-graphics/samples/build.gradle
index 2540cfd..927431d 100644
--- a/compose/ui/ui-graphics/samples/build.gradle
+++ b/compose/ui/ui-graphics/samples/build.gradle
@@ -50,5 +50,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.graphics.samples"
 }
diff --git a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
index c2e994f..f664667 100644
--- a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
+++ b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
@@ -1667,6 +1667,49 @@
         }
     }
 
+    @Test
+    fun testSwitchingFromConvexPathToRect() {
+        val bgColor = Color.Black
+        val targetColor = Color.Red
+        graphicsLayerTest(
+            block = { graphicsContext ->
+                val halfSize = size.toIntSize() / 2
+                val center = size.toIntSize().center
+                val layer =
+                    graphicsContext.createGraphicsLayer().apply {
+                        clip = true
+                        setPathOutline(
+                            // random not convex shape
+                            Path().apply {
+                                addRect(Rect(0f, 0f, 1f, 1f))
+                                addRect(Rect(2f, 2f, 3f, 3f))
+                            }
+                        )
+                        setRectOutline()
+                        record(size = halfSize) { drawRect(targetColor) }
+                        topLeft = center
+                    }
+                drawRect(bgColor)
+                drawLayer(layer)
+            },
+            verify = { pixmap ->
+                with(pixmap) {
+                    for (x in 0 until width) {
+                        for (y in 0 until height) {
+                            val expected =
+                                if (x < width / 2 || y < height / 2) {
+                                    bgColor
+                                } else {
+                                    targetColor
+                                }
+                            assertEquals(this[x, y], expected)
+                        }
+                    }
+                }
+            }
+        )
+    }
+
     private class GraphicsContextHostDrawable(
         val graphicsContext: GraphicsContext,
         val block: DrawScope.(GraphicsContext) -> Unit
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorFilter.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorFilter.android.kt
index d054f4e..b26613c 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorFilter.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorFilter.android.kt
@@ -22,7 +22,6 @@
 import android.graphics.LightingColorFilter as AndroidLightingColorFilter
 import android.graphics.PorterDuffColorFilter as AndroidPorterDuffColorFilter
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import java.lang.IllegalArgumentException
 
@@ -72,12 +71,10 @@
 
 @RequiresApi(Build.VERSION_CODES.Q)
 private object BlendModeColorFilterHelper {
-    @DoNotInline
     fun BlendModeColorFilter(color: Color, blendMode: BlendMode): AndroidBlendModeColorFilter {
         return AndroidBlendModeColorFilter(color.toArgb(), blendMode.toAndroidBlendMode())
     }
 
-    @DoNotInline
     fun createBlendModeColorFilter(
         androidBlendModeColorFilter: AndroidBlendModeColorFilter
     ): BlendModeColorFilter {
@@ -117,7 +114,6 @@
 @RequiresApi(Build.VERSION_CODES.O)
 private object ColorMatrixFilterHelper {
 
-    @DoNotInline
     fun getColorMatrix(colorFilter: AndroidColorMatrixColorFilter): ColorMatrix {
         val androidColorMatrix = AndroidColorMatrix()
         colorFilter.getColorMatrix(androidColorMatrix)
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
index 95358a3..c58ba2d2 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.ColorSpace.get
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.ColorSpaceVerificationHelper.composeColorSpace
 import androidx.compose.ui.graphics.colorspace.ColorSpace
@@ -40,7 +39,6 @@
 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 private object ColorSpaceVerificationHelperV34 {
 
-    @DoNotInline
     @JvmStatic
     fun obtainAndroidColorSpace(colorSpace: ColorSpace): android.graphics.ColorSpace? =
         when (colorSpace) {
@@ -49,7 +47,6 @@
             else -> null
         }
 
-    @DoNotInline
     @JvmStatic
     fun obtainComposeColorSpaceFromId(id: Int): ColorSpace =
         when (id) {
@@ -62,7 +59,6 @@
 @RequiresApi(Build.VERSION_CODES.O)
 private object ColorSpaceVerificationHelper {
 
-    @DoNotInline
     @JvmStatic
     @RequiresApi(Build.VERSION_CODES.O)
     fun ColorSpace.androidColorSpace(): android.graphics.ColorSpace {
@@ -134,7 +130,6 @@
         }
     }
 
-    @DoNotInline
     @JvmStatic
     @RequiresApi(Build.VERSION_CODES.O)
     fun android.graphics.ColorSpace.composeColorSpace(): ColorSpace {
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
index 4b1ac8b..ed2e9d0 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
@@ -62,6 +62,7 @@
                         // NO-OP
                     }
 
+                    @Suppress("OVERRIDE_DEPRECATION")
                     override fun onLowMemory() {
                         // NO-OP
                     }
@@ -207,9 +208,7 @@
 
     @RequiresApi(29)
     private object UniqueDrawingIdApi29 {
-        @JvmStatic
-        @androidx.annotation.DoNotInline
-        fun getUniqueDrawingId(view: View) = view.uniqueDrawingId
+        @JvmStatic fun getUniqueDrawingId(view: View) = view.uniqueDrawingId
     }
 }
 
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
index 1291e9c..93f415a 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
@@ -19,7 +19,6 @@
 import android.graphics.Bitmap
 import android.os.Build
 import android.util.DisplayMetrics
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.colorspace.ColorSpace
 import androidx.compose.ui.graphics.colorspace.ColorSpaces
@@ -67,7 +66,7 @@
         get() = bitmap.height
 
     override val config: ImageBitmapConfig
-        get() = bitmap.config.toImageConfig()
+        get() = bitmap.config!!.toImageConfig()
 
     override val colorSpace: ColorSpace
         get() =
@@ -170,7 +169,6 @@
  */
 @RequiresApi(Build.VERSION_CODES.O)
 internal object Api26Bitmap {
-    @DoNotInline
     @JvmStatic
     internal fun createBitmap(
         width: Int,
@@ -190,7 +188,6 @@
         )
     }
 
-    @DoNotInline
     @JvmStatic
     internal fun Bitmap.composeColorSpace() = colorSpace?.toComposeColorSpace() ?: ColorSpaces.Srgb
 }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.android.kt
index 7b376a6..6070726 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.android.kt
@@ -262,7 +262,6 @@
  */
 @RequiresApi(Build.VERSION_CODES.Q)
 internal object WrapperVerificationHelperMethods {
-    @androidx.annotation.DoNotInline
     fun setBlendMode(paint: NativePaint, mode: BlendMode) {
         paint.blendMode = mode.toAndroidBlendMode()
     }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
index 33019de..fbd989f 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
@@ -214,7 +214,7 @@
     override fun getBounds(): Rect {
         if (rectF == null) rectF = PlatformRectF()
         with(rectF!!) {
-            internalPath.computeBounds(this, true)
+            @Suppress("DEPRECATION") internalPath.computeBounds(this, true)
             return Rect(this.left, this.top, this.right, this.bottom)
         }
     }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt
index 256e0d0..e6c7790 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt
@@ -123,7 +123,6 @@
 @RequiresApi(Build.VERSION_CODES.S)
 private object RenderEffectVerificationHelper {
 
-    @androidx.annotation.DoNotInline
     fun createBlurEffect(
         inputRenderEffect: RenderEffect?,
         radiusX: Float,
@@ -145,7 +144,6 @@
             )
         }
 
-    @androidx.annotation.DoNotInline
     fun createOffsetEffect(
         inputRenderEffect: RenderEffect?,
         offset: Offset
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidTileMode.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidTileMode.android.kt
index 6ee59bf..5ef33fa 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidTileMode.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidTileMode.android.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Shader
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 /**
@@ -62,7 +61,7 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object TileModeVerificationHelper {
-    @DoNotInline fun getFrameworkTileModeDecal() = Shader.TileMode.DECAL
+    fun getFrameworkTileModeDecal() = Shader.TileMode.DECAL
 
-    @DoNotInline fun getComposeTileModeDecal() = TileMode.Decal
+    fun getComposeTileModeDecal() = TileMode.Decal
 }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt
index d583741..d59b401 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt
@@ -19,7 +19,6 @@
 import android.annotation.SuppressLint
 import android.graphics.Canvas
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import java.lang.reflect.InvocationTargetException
 import java.lang.reflect.Method
@@ -93,7 +92,6 @@
 
 @RequiresApi(29)
 private object CanvasZHelper {
-    @DoNotInline
     fun enableZ(canvas: Canvas, enable: Boolean) {
         if (enable) {
             canvas.enableZ()
diff --git a/compose/ui/ui-graphics/src/jvmMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.jvm.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.android.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.jvm.kt
rename to compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.android.kt
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
index ddcd1a2..ac0523c 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
@@ -717,6 +717,7 @@
         roundRectOutlineTopLeft = Offset.Zero
         roundRectCornerRadius = 0f
         outlineDirty = true
+        usePathForClip = false
     }
 
     /**
@@ -748,7 +749,8 @@
         if (
             this.roundRectOutlineTopLeft != topLeft ||
                 this.roundRectOutlineSize != size ||
-                this.roundRectCornerRadius != cornerRadius
+                this.roundRectCornerRadius != cornerRadius ||
+                this.outlinePath != null
         ) {
             resetOutlineParams()
             this.roundRectOutlineTopLeft = topLeft
@@ -950,7 +952,6 @@
 @RequiresApi(Build.VERSION_CODES.R)
 internal object OutlineVerificationHelper {
 
-    @androidx.annotation.DoNotInline
     fun setPath(outline: AndroidOutline, path: Path) {
         outline.setPath(path.asAndroidPath())
     }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt
index 59cd922..1d967f5 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt
@@ -385,22 +385,18 @@
 @RequiresApi(Build.VERSION_CODES.P)
 private object RenderNodeVerificationHelper28 {
 
-    @androidx.annotation.DoNotInline
     fun getAmbientShadowColor(renderNode: RenderNode): Int {
         return renderNode.ambientShadowColor
     }
 
-    @androidx.annotation.DoNotInline
     fun setAmbientShadowColor(renderNode: RenderNode, target: Int) {
         renderNode.ambientShadowColor = target
     }
 
-    @androidx.annotation.DoNotInline
     fun getSpotShadowColor(renderNode: RenderNode): Int {
         return renderNode.spotShadowColor
     }
 
-    @androidx.annotation.DoNotInline
     fun setSpotShadowColor(renderNode: RenderNode, target: Int) {
         renderNode.spotShadowColor = target
     }
@@ -409,7 +405,6 @@
 @RequiresApi(Build.VERSION_CODES.N)
 private object RenderNodeVerificationHelper24 {
 
-    @androidx.annotation.DoNotInline
     fun discardDisplayList(renderNode: RenderNode) {
         renderNode.discardDisplayList()
     }
@@ -418,7 +413,6 @@
 @RequiresApi(Build.VERSION_CODES.M)
 private object RenderNodeVerificationHelper23 {
 
-    @androidx.annotation.DoNotInline
     fun destroyDisplayListData(renderNode: RenderNode) {
         renderNode.destroyDisplayListData()
     }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt
index 7330a3d..d5e7a8a 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt
@@ -290,7 +290,6 @@
 @RequiresApi(Build.VERSION_CODES.S)
 internal object RenderNodeVerificationHelper {
 
-    @androidx.annotation.DoNotInline
     fun setRenderEffect(renderNode: RenderNode, target: RenderEffect?) {
         renderNode.setRenderEffect(target?.asAndroidRenderEffect())
     }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsViewLayer.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsViewLayer.android.kt
index 6ae9971..b926db0 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsViewLayer.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsViewLayer.android.kt
@@ -511,7 +511,6 @@
 @RequiresApi(Build.VERSION_CODES.S)
 private object ViewLayerVerificationHelper31 {
 
-    @androidx.annotation.DoNotInline
     fun setRenderEffect(view: View, target: RenderEffect?) {
         view.setRenderEffect(target?.asAndroidRenderEffect())
     }
@@ -520,17 +519,14 @@
 @RequiresApi(Build.VERSION_CODES.P)
 private object ViewLayerVerificationHelper28 {
 
-    @androidx.annotation.DoNotInline
     fun setOutlineAmbientShadowColor(view: View, target: Int) {
         view.outlineAmbientShadowColor = target
     }
 
-    @androidx.annotation.DoNotInline
     fun setOutlineSpotShadowColor(view: View, target: Int) {
         view.outlineSpotShadowColor = target
     }
 
-    @androidx.annotation.DoNotInline
     fun resetPivot(view: View) {
         view.resetPivot()
     }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt
index 5b59be4..22cef94 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerManager.android.kt
@@ -162,6 +162,5 @@
 @RequiresApi(Build.VERSION_CODES.M)
 private object LockHardwareCanvasHelper {
 
-    @androidx.annotation.DoNotInline
     fun lockHardwareCanvas(surface: Surface): android.graphics.Canvas = surface.lockHardwareCanvas()
 }
diff --git a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerSnapshot.android.kt b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerSnapshot.android.kt
index 91fbf1a..7171dc9 100644
--- a/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerSnapshot.android.kt
+++ b/compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerSnapshot.android.kt
@@ -121,7 +121,6 @@
 @RequiresApi(Build.VERSION_CODES.M)
 private object SurfaceVerificationHelper {
 
-    @androidx.annotation.DoNotInline
     fun lockHardwareCanvas(surface: Surface): Canvas = surface.lockHardwareCanvas()
 }
 
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
index 0904ef1..4bd040c 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.util.fastMaxOf
 import androidx.compose.ui.util.fastMinOf
 import androidx.compose.ui.util.lerp
+import kotlin.math.PI
 import kotlin.math.abs
 import kotlin.math.acos
 import kotlin.math.cos
@@ -31,12 +32,13 @@
 import kotlin.math.sign
 import kotlin.math.sqrt
 
-private const val Tau = Math.PI * 2.0
+private const val Tau = PI * 2.0
 private const val Epsilon = 1e-7
 // We use a fairly high epsilon here because it's post double->float conversion
 // and because we use a fast approximation of cbrt(). The epsilon we use here is
-// the max error of fastCbrt() in the -1f..1f range.
-private const val FloatEpsilon = 8.3446500e-7f
+// slightly larger than the max error of fastCbrt() in the -1f..1f range
+// (8.3446500e-7f) but smaller than 1.0f.ulp * 10.
+private const val FloatEpsilon = 1e-6f
 
 /**
  * Evaluate the specified [segment] at position [t] and returns the X coordinate of the segment's
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt
index 1c87aef..24e2318 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt
@@ -16,6 +16,7 @@
 package androidx.compose.ui.graphics
 
 import androidx.compose.ui.util.floatFromBits
+import kotlin.jvm.JvmInline
 
 /**
  * The `Float16` class is a wrapper and a utility class to manipulate half-precision 16-bit
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
index 4c3e7ca..f0758b9 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.graphics
 
 import androidx.annotation.RestrictTo
+import kotlin.collections.removeLast as removeLastKt
 import kotlin.math.max
 import kotlin.math.min
 
@@ -152,7 +153,11 @@
             val s = stack
             s.add(root)
             while (s.size > 0) {
-                val node = s.removeLast()
+
+                // MutableCollections.removeLast() is shadowed by java.util.list.removeAt()
+                // which was added in sdk 35 making this call unsafe
+                // val node = s.removeLast()
+                val node = s.removeLastKt()
                 if (node.overlaps(start, end)) block(node)
                 if (node.left !== terminator && node.left.max >= start) {
                     s.add(node.left)
diff --git a/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/BlendMode.commonStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/BlendMode.commonStubs.kt
new file mode 100644
index 0000000..2cba823
--- /dev/null
+++ b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/BlendMode.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics
+
+actual fun BlendMode.isSupported(): Boolean = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Canvas.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Canvas.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Canvas.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Canvas.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/ColorFilter.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/ColorFilter.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/ColorFilter.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/ColorFilter.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/NotImplemented.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/NotImplemented.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/NotImplemented.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Paint.commonStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Paint.commonStubs.kt
new file mode 100644
index 0000000..92ef826
--- /dev/null
+++ b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Paint.commonStubs.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics
+
+actual class NativePaint
+
+actual fun Paint(): Paint = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Path.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Path.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Path.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Path.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/PathEffect.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/PathEffect.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/PathEffect.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/PathEffect.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/PathIterator.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/PathIterator.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/PathIterator.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/PathIterator.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/PathMeasure.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/PathMeasure.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/PathMeasure.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/PathMeasure.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/RenderEffect.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/RenderEffect.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/RenderEffect.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/RenderEffect.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Shader.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Shader.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Shader.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/Shader.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/TileMode.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/TileMode.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/TileMode.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/TileMode.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..4ea4274
--- /dev/null
+++ b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.graphics.internal
+
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.jvmStubs.kt b/compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.jvmStubs.kt
rename to compose/ui/ui-graphics/src/commonStubsMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.commonStubs.kt
diff --git a/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/FastFloatParserTest.kt b/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/FastFloatParserTest.kt
index f5a0efe..11d18a0 100644
--- a/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/FastFloatParserTest.kt
+++ b/compose/ui/ui-graphics/src/commonTest/kotlin/androidx/compose/ui/graphics/vector/FastFloatParserTest.kt
@@ -18,6 +18,7 @@
 
 import kotlin.math.abs
 import kotlin.test.Test
+import kotlin.test.assertTrue
 
 class FastFloatParserTest {
     @Test
@@ -30,15 +31,16 @@
 
             val nodes = parser.parsePathString("H$number").toNodes()
 
-            assert(nodes.isNotEmpty()) { line }
+            assertTrue(nodes.isNotEmpty(), line)
 
             val x = (nodes[0] as PathNode.HorizontalTo).x
 
-            assert(abs(x.toBits() - bits) < 2) {
+            assertTrue(
+                abs(x.toBits() - bits) < 2,
                 "Expected: 0x$bits\n" +
                     "Actual:   0x${x.toBits()}\n" +
                     "    in $line (toFloat() = ${number.toFloat()})"
-            }
+            )
         }
     }
 }
diff --git a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Paint.jvmStubs.kt b/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Paint.jvmStubs.kt
deleted file mode 100644
index 9df4b05..0000000
--- a/compose/ui/ui-graphics/src/jvmStubsMain/kotlin/androidx/compose/ui/graphics/Paint.jvmStubs.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.graphics
-
-actual class NativePaint
-
-actual fun Paint(): Paint = implementedInJetBrainsFork()
-
-actual fun BlendMode.isSupported(): Boolean = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-inspection/build.gradle b/compose/ui/ui-inspection/build.gradle
index 20b1101..e40811d 100644
--- a/compose/ui/ui-inspection/build.gradle
+++ b/compose/ui/ui-inspection/build.gradle
@@ -33,7 +33,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.1.0", {
+    implementation("androidx.annotation:annotation:1.8.1", {
         exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib"
     })
     // Following dependencies will be provided by inspected app itself
@@ -64,6 +64,7 @@
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
     androidTestImplementation(project(":internal-testutils-fonts"))
     androidTestImplementation(project(":compose:material:material"))
+    androidTestImplementation("androidx.compose.material:material-icons-core:1.6.7")
     androidTestImplementation(project(":inspection:inspection-testing"))
     androidTestImplementation("androidx.activity:activity-compose:1.3.1")
     androidTestImplementation(libs.testRunner)
@@ -80,6 +81,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         // layout inspection supported starting on Android Q
         minSdkVersion 29
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
index b38bed3..e5a9a65 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
@@ -52,6 +52,7 @@
 import androidx.inspection.InspectorFactory
 import com.google.protobuf.ByteString
 import com.google.protobuf.InvalidProtocolBufferException
+import kotlin.collections.removeLast as removeLastKt
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.Command
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.GetAllParametersCommand
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.GetAllParametersResponse
@@ -97,7 +98,7 @@
             val stack = mutableListOf<InspectorNode>()
             trees.forEach { stack.addAll(it.nodes) }
             while (stack.isNotEmpty()) {
-                val node = stack.removeLast()
+                val node = stack.removeLastKt()
                 stack.addAll(node.children)
                 result.put(node.id, node)
             }
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/compose/AndroidComposeViewWrapper.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/compose/AndroidComposeViewWrapper.kt
index b80b7a8..f6c9c3a 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/compose/AndroidComposeViewWrapper.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/compose/AndroidComposeViewWrapper.kt
@@ -120,7 +120,13 @@
         return result
     }
 
-    private fun View.hasHideFromInspectionTag(): Boolean =
-        getTag(R.id.hide_in_inspector_tag) != null ||
-            getTag(androidx.compose.ui.graphics.R.id.hide_graphics_layer_in_inspector_tag) != null
+    private fun View.hasHideFromInspectionTag(): Boolean {
+        return runCatching {
+                getTag(R.id.hide_in_inspector_tag) != null ||
+                    getTag(
+                        androidx.compose.ui.graphics.R.id.hide_graphics_layer_in_inspector_tag
+                    ) != null
+            }
+            .getOrNull() ?: false
+    }
 }
diff --git a/compose/ui/ui-test-junit4/build.gradle b/compose/ui/ui-test-junit4/build.gradle
index 7bb5e9b..7634a56 100644
--- a/compose/ui/ui-test-junit4/build.gradle
+++ b/compose/ui/ui-test-junit4/build.gradle
@@ -40,6 +40,7 @@
         commonMain {
             dependencies {
                 api(project(":compose:ui:ui-test"))
+                implementation("androidx.annotation:annotation:1.8.1")
                 implementation(libs.kotlinStdlib)
                 implementation(libs.kotlinCoroutinesCore)
                 implementation(libs.kotlinCoroutinesTest)
@@ -55,7 +56,6 @@
             dependsOn(commonMain)
             dependencies {
                 api(libs.junit)
-                compileOnly("androidx.annotation:annotation:1.1.0")
             }
         }
 
@@ -65,7 +65,6 @@
                 api("androidx.activity:activity:1.2.1")
                 api("androidx.test.ext:junit:1.1.5")
                 implementation("androidx.activity:activity-compose:1.3.0")
-                implementation("androidx.annotation:annotation:1.1.0")
                 implementation("androidx.compose.runtime:runtime-saveable:1.6.0")
                 implementation("androidx.lifecycle:lifecycle-common:2.5.1")
                 implementation("androidx.lifecycle:lifecycle-runtime:2.5.1")
@@ -112,6 +111,7 @@
 
 
 android {
+    compileSdk 35
     lintOptions {
         disable("InvalidPackage")
     }
@@ -124,4 +124,5 @@
     inceptionYear = "2020"
     description = "Compose testing integration with JUnit4"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/compose/ui/ui-test-manifest/build.gradle b/compose/ui/ui-test-manifest/build.gradle
index c06cf1f..d79ee4b 100644
--- a/compose/ui/ui-test-manifest/build.gradle
+++ b/compose/ui/ui-test-manifest/build.gradle
@@ -40,7 +40,6 @@
     type = LibraryType.PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY
     inceptionYear = "2021"
     description = "Compose testing library that should be added as a debugImplementation dependency to add properties to the debug manifest necessary for testing an application"
-    metalavaK2UastEnabled = true
     doNotDocumentReason = "No public API"
 }
 
diff --git a/compose/ui/ui-test-manifest/integration-tests/testapp/build.gradle b/compose/ui/ui-test-manifest/integration-tests/testapp/build.gradle
index 4fd6efb..387b078 100644
--- a/compose/ui/ui-test-manifest/integration-tests/testapp/build.gradle
+++ b/compose/ui/ui-test-manifest/integration-tests/testapp/build.gradle
@@ -33,5 +33,6 @@
 }
 
 android {
+    compileSdkVersion 35
     namespace "androidx.compose.ui.test.manifest.integration.testapp"
 }
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 4ca2049..a5b3fc9 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -28,6 +28,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
+    ctor public AndroidComposeUiTestEnvironment();
     ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
     method public final void cancelAndRecreateRecomposer();
     method protected abstract A? getActivity();
@@ -482,6 +483,7 @@
     method public androidx.compose.ui.test.SemanticsNodeInteraction assertExists(optional String? errorMessageOnFail);
     method public void assertIsDeactivated(optional String? errorMessageOnFail);
     method public androidx.compose.ui.semantics.SemanticsNode fetchSemanticsNode(optional String? errorMessageOnFail);
+    method public int semanticsId();
   }
 
   public final class SemanticsNodeInteractionCollection {
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index e0e9e2d..4f052f1e 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -28,6 +28,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.ui.test.ExperimentalTestApi public abstract class AndroidComposeUiTestEnvironment<A extends androidx.activity.ComponentActivity> {
+    ctor public AndroidComposeUiTestEnvironment();
     ctor public AndroidComposeUiTestEnvironment(optional kotlin.coroutines.CoroutineContext effectContext);
     method public final void cancelAndRecreateRecomposer();
     method protected abstract A? getActivity();
@@ -483,6 +484,7 @@
     method public androidx.compose.ui.test.SemanticsNodeInteraction assertExists(optional String? errorMessageOnFail);
     method public void assertIsDeactivated(optional String? errorMessageOnFail);
     method public androidx.compose.ui.semantics.SemanticsNode fetchSemanticsNode(optional String? errorMessageOnFail);
+    method public int semanticsId();
   }
 
   public final class SemanticsNodeInteractionCollection {
diff --git a/compose/ui/ui-test/build.gradle b/compose/ui/ui-test/build.gradle
index 7e6712d..5df6d49 100644
--- a/compose/ui/ui-test/build.gradle
+++ b/compose/ui/ui-test/build.gradle
@@ -33,6 +33,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -44,7 +45,8 @@
                 api(project(":compose:ui:ui-unit"))
                 api(project(":compose:runtime:runtime"))
                 api(libs.kotlinStdlib)
-
+                api(libs.kotlinCoroutinesCore)
+                api(libs.kotlinCoroutinesTest)
                 implementation(project(":compose:ui:ui-util"))
             }
         }
@@ -57,8 +59,6 @@
         jvmMain {
             dependsOn(commonMain)
             dependencies {
-                api(libs.kotlinCoroutinesCore)
-                api(libs.kotlinCoroutinesTest)
             }
         }
 
@@ -67,7 +67,7 @@
             dependencies {
                 api(project(":compose:ui:ui-graphics"))
                 implementation("androidx.activity:activity-compose:1.3.0")
-                implementation("androidx.annotation:annotation:1.1.0")
+                implementation("androidx.annotation:annotation:1.8.1")
                 implementation("androidx.core:core-ktx:1.12.0")
                 implementation("androidx.test.espresso:espresso-core:3.5.0")
                 implementation("androidx.test.espresso:espresso-idling-resource:3.5.0")
@@ -75,12 +75,16 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-                implementation(libs.junit)
-                implementation(libs.truth)
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidCommonTest {
@@ -131,6 +135,7 @@
 }
 
 android {
+    compileSdk 35
     sourceSets {
         test.java.srcDirs += "src/androidCommonTest/kotlin"
         androidTest.java.srcDirs += "src/androidCommonTest/kotlin"
@@ -140,6 +145,8 @@
         disable("InvalidPackage")
     }
     namespace "androidx.compose.ui.test"
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
@@ -148,5 +155,6 @@
     inceptionYear = "2019"
     description = "Compose testing library"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:ui:ui-test:ui-test-samples"))
 }
diff --git a/compose/ui/ui-test/samples/build.gradle b/compose/ui/ui-test/samples/build.gradle
index 2f926bc..7567c76 100644
--- a/compose/ui/ui-test/samples/build.gradle
+++ b/compose/ui/ui-test/samples/build.gradle
@@ -51,5 +51,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.test.samples"
 }
diff --git a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt
index 06bc995..9c6800f 100644
--- a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt
@@ -23,6 +23,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.testutils.expectError
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
@@ -104,6 +105,7 @@
                 Modifier.semantics {
                     isEditable = true
                     insertTextAtCursor { true }
+                    requestFocus { true }
                 }
             )
         }
@@ -184,7 +186,7 @@
         var lastSeenText = ""
         rule.setContent { TextFieldUi(readOnly = true) }
 
-        rule.onNodeWithTag(fieldTag).performTextInput("hi")
+        expectError<AssertionError> { rule.onNodeWithTag(fieldTag).performTextInput("hi") }
         rule.runOnIdle { assertThat(lastSeenText).isEqualTo("") }
     }
 
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/android/WindowCapture.android.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/android/WindowCapture.android.kt
index b7d1a3e..72ac899 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/android/WindowCapture.android.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/android/WindowCapture.android.kt
@@ -25,7 +25,6 @@
 import android.view.View
 import android.view.ViewTreeObserver
 import android.view.Window
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.compose.ui.graphics.ImageBitmap
@@ -185,7 +184,6 @@
 
 @RequiresApi(Build.VERSION_CODES.Q)
 private object FrameCommitCallbackHelper {
-    @DoNotInline
     fun registerFrameCommitCallback(viewTreeObserver: ViewTreeObserver, runnable: Runnable) {
         viewTreeObserver.registerFrameCommitCallback(runnable)
     }
@@ -193,7 +191,6 @@
 
 @RequiresApi(Build.VERSION_CODES.O)
 private object PixelCopyHelper {
-    @DoNotInline
     fun request(
         source: Window,
         srcRect: Rect?,
diff --git a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt
index 5670ba3..de21d68 100644
--- a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt
+++ b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/KeyAndMouseEventsTest.kt
@@ -31,6 +31,7 @@
 import androidx.compose.ui.test.util.verifyMouseEvent
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth
+import kotlin.collections.removeFirst as removeFirstKt
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -447,7 +448,7 @@
     }
 
     private fun <E> MutableList<E>.removeFirst(n: Int): List<E> {
-        return mutableListOf<E>().also { result -> repeat(n) { result.add(removeFirst()) } }
+        return mutableListOf<E>().also { result -> repeat(n) { result.add(removeFirstKt()) } }
     }
 
     private fun enqueueKeyPress(key: Key) {
diff --git a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/MouseEventsTest.kt b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/MouseEventsTest.kt
index c8bac18..829d278 100644
--- a/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/MouseEventsTest.kt
+++ b/compose/ui/ui-test/src/androidUnitTest/kotlin/androidx/compose/ui/test/inputdispatcher/MouseEventsTest.kt
@@ -45,6 +45,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.collections.removeFirst as removeFirstKt
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.annotation.Config
@@ -815,6 +816,6 @@
     }
 
     private fun <E> MutableList<E>.removeFirst(n: Int): List<E> {
-        return mutableListOf<E>().also { result -> repeat(n) { result.add(removeFirst()) } }
+        return mutableListOf<E>().also { result -> repeat(n) { result.add(removeFirstKt()) } }
     }
 }
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
index 507c604..8a25f19 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
@@ -36,6 +36,7 @@
 import androidx.compose.ui.semantics.getOrNull
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.toSize
+import kotlin.jvm.JvmName
 import kotlin.math.abs
 import kotlin.math.sign
 
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt
index 55f08fb..4c00359 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt
@@ -74,7 +74,7 @@
 infix fun DeviceConfigurationOverride.then(
     other: DeviceConfigurationOverride
 ): DeviceConfigurationOverride = DeviceConfigurationOverride { contentUnderTest ->
-    this.Override { other.Override { contentUnderTest() } }
+    this.Override { other.Override(contentUnderTest) }
 }
 
 /**
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GlobalAssertions.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GlobalAssertions.kt
index be74a60..4e79ab1 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GlobalAssertions.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/GlobalAssertions.kt
@@ -17,6 +17,8 @@
 
 package androidx.compose.ui.test
 
+import kotlin.jvm.JvmName
+
 /**
  * Adds a named assertion to the collection of assertions to be executed before test actions.
  *
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
index dc3e83c..92b89bd 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
@@ -149,6 +149,11 @@
         }
     }
 
+    /** Fetch the semantics ID. */
+    fun semanticsId(): Int {
+        return fetchSemanticsNode().id
+    }
+
     private fun fetchOneOrThrow(
         errorMessageOnFail: String? = null,
         skipDeactivatedNodes: Boolean = true
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TextActions.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TextActions.kt
index 29afc16..2ebac43 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TextActions.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TextActions.kt
@@ -48,7 +48,7 @@
  */
 @ExperimentalTestApi
 fun SemanticsNodeInteraction.performTextInputSelection(selection: TextRange) {
-    getNodeAndFocus()
+    getNodeAndFocus(requireEditable = false)
     performSemanticsAction(SemanticsActions.SetSelection) {
         // Pass true as the last parameter since this range is relative to the text before any
         // VisualTransformation is applied.
@@ -83,11 +83,11 @@
     assert(hasPerformImeAction()) { errorOnFail }
     assert(!hasImeAction(ImeAction.Default)) { errorOnFail }
     @OptIn(ExperimentalTestApi::class) invokeGlobalAssertions()
-    val node = getNodeAndFocus(errorOnFail)
+    val node = getNodeAndFocus(errorOnFail, requireEditable = false)
 
     wrapAssertionErrorsWithNodeInfo(selector, node) {
         performSemanticsAction(OnImeAction) {
-            assert(it()) {
+            assertOnJvm(it()) {
                 buildGeneralErrorMessage(
                     "Failed to perform IME action, handler returned false.",
                     selector,
@@ -99,14 +99,17 @@
 }
 
 private fun SemanticsNodeInteraction.getNodeAndFocus(
-    errorOnFail: String = "Failed to perform text input."
+    errorOnFail: String = "Failed to perform text input.",
+    requireEditable: Boolean = true
 ): SemanticsNode {
     @OptIn(ExperimentalTestApi::class) invokeGlobalAssertions()
     val node = fetchSemanticsNode(errorOnFail)
     assert(isEnabled()) { errorOnFail }
-    assert(hasSetTextAction()) { errorOnFail }
     assert(hasRequestFocusAction()) { errorOnFail }
-    assert(hasInsertTextAtCursorAction()) { errorOnFail }
+    if (requireEditable) {
+        assert(hasSetTextAction()) { errorOnFail }
+        assert(hasInsertTextAtCursorAction()) { errorOnFail }
+    }
 
     if (!isFocused().matches(node)) {
         // Get focus
@@ -116,26 +119,10 @@
     return node
 }
 
-private inline fun <R> wrapAssertionErrorsWithNodeInfo(
+internal expect inline fun <R> wrapAssertionErrorsWithNodeInfo(
     selector: SemanticsSelector,
     node: SemanticsNode,
     block: () -> R
-): R {
-    try {
-        return block()
-    } catch (e: AssertionError) {
-        throw ProxyAssertionError(e.message.orEmpty(), selector, node, e)
-    }
-}
+): R
 
-private class ProxyAssertionError(
-    message: String,
-    selector: SemanticsSelector,
-    node: SemanticsNode,
-    cause: Throwable
-) : AssertionError(buildGeneralErrorMessage(message, selector, node), cause) {
-    init {
-        // Duplicate the stack trace to make troubleshooting easier.
-        stackTrace = cause.stackTrace
-    }
-}
+internal expect inline fun assertOnJvm(value: Boolean, lazyMessage: () -> Any)
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/Actions.jvmStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Actions.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/Actions.jvmStubs.kt
rename to compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Actions.commonStubs.kt
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopAssertions.jvmStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Assertions.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopAssertions.jvmStubs.kt
rename to compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Assertions.commonStubs.kt
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/ComposeUiTest.jvmStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/ComposeUiTest.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/ComposeUiTest.jvmStubs.kt
rename to compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/ComposeUiTest.commonStubs.kt
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DensityForcedSize.jvmStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/DensityForcedSize.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DensityForcedSize.jvmStubs.kt
rename to compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/DensityForcedSize.commonStubs.kt
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.jvmStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.jvmStubs.kt
rename to compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.commonStubs.kt
diff --git a/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Expect.commonStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Expect.commonStubs.kt
new file mode 100644
index 0000000..5844d79
--- /dev/null
+++ b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Expect.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test
+
+internal actual fun identityHashCode(instance: Any?): Int = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/InputDispatcher.commonStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/InputDispatcher.commonStubs.kt
new file mode 100644
index 0000000..02373a8
--- /dev/null
+++ b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/InputDispatcher.commonStubs.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test
+
+import androidx.compose.ui.node.RootForTest
+
+internal actual fun createInputDispatcher(
+    testContext: TestContext,
+    root: RootForTest
+): InputDispatcher = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Mouse.commonStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Mouse.commonStubs.kt
new file mode 100644
index 0000000..6300078
--- /dev/null
+++ b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Mouse.commonStubs.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test
+
+import kotlin.jvm.JvmInline
+
+@JvmInline
+actual value class MouseButton(val buttonId: Int) {
+    actual companion object {
+        actual val Primary: MouseButton = implementedInJetBrainsFork()
+        actual val Secondary: MouseButton = implementedInJetBrainsFork()
+        actual val Tertiary: MouseButton = implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/NotImplemented.jvmStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/NotImplemented.jvmStubs.kt
rename to compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/NotImplemented.commonStubs.kt
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopOutput.jvmStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Output.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopOutput.jvmStubs.kt
rename to compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/Output.commonStubs.kt
diff --git a/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/TextActions.commonStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/TextActions.commonStubs.kt
new file mode 100644
index 0000000..86ba6a1
--- /dev/null
+++ b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/TextActions.commonStubs.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test
+
+import androidx.compose.ui.semantics.SemanticsNode
+
+internal actual inline fun <R> wrapAssertionErrorsWithNodeInfo(
+    selector: SemanticsSelector,
+    node: SemanticsNode,
+    block: () -> R
+): R = implementedInJetBrainsFork()
+
+internal actual inline fun assertOnJvm(value: Boolean, lazyMessage: () -> Any): Unit =
+    implementedInJetBrainsFork()
diff --git a/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..a56eb13
--- /dev/null
+++ b/compose/ui/ui-test/src/commonStubsMain/kotlin/androidx/compose/ui/test/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test.internal
+
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TextActions.jvm.kt b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TextActions.jvm.kt
new file mode 100644
index 0000000..4b95f82
--- /dev/null
+++ b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TextActions.jvm.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.test
+
+import androidx.compose.ui.semantics.SemanticsNode
+
+internal actual inline fun <R> wrapAssertionErrorsWithNodeInfo(
+    selector: SemanticsSelector,
+    node: SemanticsNode,
+    block: () -> R
+): R {
+    try {
+        return block()
+    } catch (e: AssertionError) {
+        throw ProxyAssertionError(e.message.orEmpty(), selector, node, e)
+    }
+}
+
+internal class ProxyAssertionError(
+    message: String,
+    selector: SemanticsSelector,
+    node: SemanticsNode,
+    cause: Throwable
+) : AssertionError(buildGeneralErrorMessage(message, selector, node), cause) {
+    init {
+        // Duplicate the stack trace to make troubleshooting easier.
+        stackTrace = cause.stackTrace
+    }
+}
+
+internal actual inline fun assertOnJvm(value: Boolean, lazyMessage: () -> Any) =
+    assert(value, lazyMessage)
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.jvmStubs.kt b/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.jvmStubs.kt
deleted file mode 100644
index ef8e57a..0000000
--- a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopInputDispatcher.jvmStubs.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(InternalComposeUiApi::class)
-
-package androidx.compose.ui.test
-
-import androidx.compose.ui.InternalComposeUiApi
-import androidx.compose.ui.node.RootForTest
-
-internal actual fun createInputDispatcher(
-    testContext: TestContext,
-    root: RootForTest
-): InputDispatcher = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopSynchronization.jvmStubs.kt b/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopSynchronization.jvmStubs.kt
deleted file mode 100644
index 2739a21a..0000000
--- a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/DesktopSynchronization.jvmStubs.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.test
-
-internal fun <T> runOnUiThread(action: () -> T): T = action()
diff --git a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/Mouse.jvmStubs.kt b/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/Mouse.jvmStubs.kt
deleted file mode 100644
index 2a01d5a..0000000
--- a/compose/ui/ui-test/src/jvmStubsMain/kotlin/androidx/compose/ui/test/Mouse.jvmStubs.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.test
-
-@JvmInline
-actual value class MouseButton(val buttonId: Int) {
-    actual companion object {
-        actual val Primary: MouseButton = implementedInJetBrainsFork()
-        actual val Secondary: MouseButton = implementedInJetBrainsFork()
-        actual val Tertiary: MouseButton = implementedInJetBrainsFork()
-    }
-}
diff --git a/compose/ui/ui-text-google-fonts/build.gradle b/compose/ui/ui-text-google-fonts/build.gradle
index 06afb53..5e65e32 100644
--- a/compose/ui/ui-text-google-fonts/build.gradle
+++ b/compose/ui/ui-text-google-fonts/build.gradle
@@ -51,10 +51,10 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2022"
     description = "Compose Downloadable Fonts integration for Google Fonts"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.text.googlefonts"
 }
diff --git a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/FontProviderHelper.kt b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/FontProviderHelper.kt
index 9789e31..8859e04 100644
--- a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/FontProviderHelper.kt
+++ b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/FontProviderHelper.kt
@@ -81,7 +81,7 @@
     @Suppress("DEPRECATION")
     @SuppressLint("PackageManagerGetSignatures")
     val packageInfo: PackageInfo = getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
-    @Suppress("DEPRECATION") return convertToByteArrayList(packageInfo.signatures)
+    @Suppress("DEPRECATION") return convertToByteArrayList(packageInfo.signatures!!)
 }
 
 private val ByteArrayComparator = Comparator { l: ByteArray, r: ByteArray ->
diff --git a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/HandlerHelper.kt b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/HandlerHelper.kt
index cf9e811..ed746da 100644
--- a/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/HandlerHelper.kt
+++ b/compose/ui/ui-text-google-fonts/src/main/java/androidx/compose/ui/text/googlefonts/HandlerHelper.kt
@@ -19,7 +19,6 @@
 import android.os.Build
 import android.os.Handler
 import android.os.Looper
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 /**
@@ -40,7 +39,6 @@
 
     @RequiresApi(28)
     internal object Handler28Impl {
-        @DoNotInline
         fun createAsync(looper: Looper): Handler {
             return Handler.createAsync(looper)
         }
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index f93f646..7ccf854 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -38,6 +38,7 @@
   }
 
   public static final class AnnotatedString.Builder implements java.lang.Appendable {
+    ctor public AnnotatedString.Builder();
     ctor public AnnotatedString.Builder(androidx.compose.ui.text.AnnotatedString text);
     ctor public AnnotatedString.Builder(optional int capacity);
     ctor public AnnotatedString.Builder(String text);
@@ -301,6 +302,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class ParagraphStyle implements androidx.compose.ui.text.AnnotatedString.Annotation {
+    ctor public ParagraphStyle();
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
@@ -378,6 +380,7 @@
   }
 
   public final class PlatformParagraphStyle {
+    ctor public PlatformParagraphStyle();
     ctor public PlatformParagraphStyle(optional boolean includeFontPadding);
     ctor public PlatformParagraphStyle(optional int emojiSupportMatch);
     ctor public PlatformParagraphStyle(optional int emojiSupportMatch, optional boolean includeFontPadding);
@@ -582,6 +585,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextLinkStyles {
+    ctor public TextLinkStyles();
     ctor public TextLinkStyles(optional androidx.compose.ui.text.SpanStyle? style, optional androidx.compose.ui.text.SpanStyle? focusedStyle, optional androidx.compose.ui.text.SpanStyle? hoveredStyle, optional androidx.compose.ui.text.SpanStyle? pressedStyle);
     method public androidx.compose.ui.text.SpanStyle? getFocusedStyle();
     method public androidx.compose.ui.text.SpanStyle? getHoveredStyle();
@@ -1132,6 +1136,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class ImeOptions {
+    ctor public ImeOptions();
     ctor @Deprecated public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
     ctor @Deprecated public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
     ctor public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions, optional androidx.compose.ui.text.intl.LocaleList hintLocales);
@@ -1228,6 +1233,7 @@
   }
 
   public final class PasswordVisualTransformation implements androidx.compose.ui.text.input.VisualTransformation {
+    ctor public PasswordVisualTransformation();
     ctor public PasswordVisualTransformation(optional char mask);
     method public androidx.compose.ui.text.input.TransformedText filter(androidx.compose.ui.text.AnnotatedString text);
     method public char getMask();
@@ -1235,6 +1241,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class PlatformImeOptions {
+    ctor public PlatformImeOptions();
     ctor public PlatformImeOptions(optional String? privateImeOptions);
     method public String? getPrivateImeOptions();
     property public final String? privateImeOptions;
@@ -1631,6 +1638,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextGeometricTransform {
+    ctor public TextGeometricTransform();
     ctor public TextGeometricTransform(optional float scaleX, optional float skewX);
     method public androidx.compose.ui.text.style.TextGeometricTransform copy(optional float scaleX, optional float skewX);
     method public float getScaleX();
@@ -1648,6 +1656,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextIndent {
+    ctor public TextIndent();
     ctor public TextIndent(optional long firstLine, optional long restLine);
     method public androidx.compose.ui.text.style.TextIndent copy(optional long firstLine, optional long restLine);
     method public long getFirstLine();
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index 1375e6c..89caf66 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -38,6 +38,7 @@
   }
 
   public static final class AnnotatedString.Builder implements java.lang.Appendable {
+    ctor public AnnotatedString.Builder();
     ctor public AnnotatedString.Builder(androidx.compose.ui.text.AnnotatedString text);
     ctor public AnnotatedString.Builder(optional int capacity);
     ctor public AnnotatedString.Builder(String text);
@@ -301,6 +302,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class ParagraphStyle implements androidx.compose.ui.text.AnnotatedString.Annotation {
+    ctor public ParagraphStyle();
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle);
     ctor @Deprecated public ParagraphStyle(optional androidx.compose.ui.text.style.TextAlign? textAlign, optional androidx.compose.ui.text.style.TextDirection? textDirection, optional long lineHeight, optional androidx.compose.ui.text.style.TextIndent? textIndent, optional androidx.compose.ui.text.PlatformParagraphStyle? platformStyle, optional androidx.compose.ui.text.style.LineHeightStyle? lineHeightStyle, optional androidx.compose.ui.text.style.LineBreak? lineBreak, optional androidx.compose.ui.text.style.Hyphens? hyphens);
@@ -378,6 +380,7 @@
   }
 
   public final class PlatformParagraphStyle {
+    ctor public PlatformParagraphStyle();
     ctor public PlatformParagraphStyle(optional boolean includeFontPadding);
     ctor public PlatformParagraphStyle(optional int emojiSupportMatch);
     ctor public PlatformParagraphStyle(optional int emojiSupportMatch, optional boolean includeFontPadding);
@@ -582,6 +585,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextLinkStyles {
+    ctor public TextLinkStyles();
     ctor public TextLinkStyles(optional androidx.compose.ui.text.SpanStyle? style, optional androidx.compose.ui.text.SpanStyle? focusedStyle, optional androidx.compose.ui.text.SpanStyle? hoveredStyle, optional androidx.compose.ui.text.SpanStyle? pressedStyle);
     method public androidx.compose.ui.text.SpanStyle? getFocusedStyle();
     method public androidx.compose.ui.text.SpanStyle? getHoveredStyle();
@@ -1132,6 +1136,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class ImeOptions {
+    ctor public ImeOptions();
     ctor @Deprecated public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
     ctor @Deprecated public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
     ctor public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions, optional androidx.compose.ui.text.intl.LocaleList hintLocales);
@@ -1228,6 +1233,7 @@
   }
 
   public final class PasswordVisualTransformation implements androidx.compose.ui.text.input.VisualTransformation {
+    ctor public PasswordVisualTransformation();
     ctor public PasswordVisualTransformation(optional char mask);
     method public androidx.compose.ui.text.input.TransformedText filter(androidx.compose.ui.text.AnnotatedString text);
     method public char getMask();
@@ -1235,6 +1241,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class PlatformImeOptions {
+    ctor public PlatformImeOptions();
     ctor public PlatformImeOptions(optional String? privateImeOptions);
     method public String? getPrivateImeOptions();
     property public final String? privateImeOptions;
@@ -1642,6 +1649,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextGeometricTransform {
+    ctor public TextGeometricTransform();
     ctor public TextGeometricTransform(optional float scaleX, optional float skewX);
     method public androidx.compose.ui.text.style.TextGeometricTransform copy(optional float scaleX, optional float skewX);
     method public float getScaleX();
@@ -1659,6 +1667,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextIndent {
+    ctor public TextIndent();
     ctor public TextIndent(optional long firstLine, optional long restLine);
     method public androidx.compose.ui.text.style.TextIndent copy(optional long firstLine, optional long restLine);
     method public long getFirstLine();
diff --git a/compose/ui/ui-text/benchmark/build.gradle b/compose/ui/ui-text/benchmark/build.gradle
index bf6e10a..8bfa343 100644
--- a/compose/ui/ui-text/benchmark/build.gradle
+++ b/compose/ui/ui-text/benchmark/build.gradle
@@ -39,5 +39,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.text.benchmark"
 }
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index 3f468d2..f100ef9 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -34,6 +34,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -48,16 +49,18 @@
 
                 // when updating the runtime version please also update the runtime-saveable version
                 implementation(project(":compose:runtime:runtime"))
-                implementation("androidx.compose.runtime:runtime-saveable:1.6.0")
+                implementation(project(":compose:runtime:runtime-saveable"))
 
                 implementation(project(":compose:ui:ui-util"))
+
+                // TODO: Pin androidx.collection when SieveCache is available
+                // implementation("androidx.collection:collection:1.x.0")
+                implementation(project(":collection:collection"))
             }
         }
 
         commonTest {
             dependencies {
-                implementation(libs.junit)
-                implementation(libs.truth)
             }
         }
 
@@ -71,21 +74,29 @@
             dependsOn(commonMain)
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
                 implementation("androidx.core:core:1.7.0")
                 implementation("androidx.emoji2:emoji2:1.4.0")
-                implementation("androidx.collection:collection:1.4.0")
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
             dependsOn(commonTest)
             dependencies {
+                implementation("androidx.emoji2:emoji2-bundled:1.4.0")
                 implementation(project(":compose:ui:ui-test-junit4"))
                 implementation(project(":internal-testutils-fonts"))
                 implementation(project(":compose:foundation:foundation"))
@@ -116,7 +127,6 @@
                 implementation(libs.byteBuddy)
             }
         }
-        androidMain.kotlin.srcDirs("${supportRootFolder}/text/text/src/main/java")
     }
 }
 
@@ -131,9 +141,13 @@
     inceptionYear = "2019"
     description = "Compose Text primitives and utilities"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:ui:ui-text:ui-text-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.text"
+    // TODO(b/328001575)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/ui/ui-text/lint-baseline.xml b/compose/ui/ui-text/lint-baseline.xml
index d8802c0..b56dacd 100644
--- a/compose/ui/ui-text/lint-baseline.xml
+++ b/compose/ui/ui-text/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha03" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha03)" variant="all" version="8.5.0-alpha03">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="NewApi"
@@ -85,8 +85,8 @@
     <issue
         id="ListIterator"
         message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                        &quot;&apos;$key&apos; must be unique. Actual [ [${value.joinToString()}]&quot;"
-        errorLine2="                                                                  ~~~~~~~~~~~~">
+        errorLine1="                                &quot;&apos;$key&apos; must be unique. Actual [ [${value.joinToString()}]&quot;"
+        errorLine2="                                                                          ~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/ui/text/font/FontVariation.kt"/>
     </issue>
@@ -121,8 +121,8 @@
     <issue
         id="PrimitiveInCollection"
         message="method collectRangeTransitions has parameter target with type SortedSet&lt;Integer>: replace with IntSet"
-        errorLine1="    target: SortedSet&lt;Int>"
-        errorLine2="            ~~~~~~~~~~~~~~">
+        errorLine1="private fun collectRangeTransitions(ranges: List&lt;Range&lt;*>>?, target: SortedSet&lt;Int>) {"
+        errorLine2="                                                                     ~~~~~~~~~~~~~~">
         <location
             file="src/jvmMain/kotlin/androidx/compose/ui/text/JvmAnnotatedString.jvm.kt"/>
     </issue>
@@ -148,8 +148,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable list with type List&lt;? extends Float>: replace with FloatList"
-        errorLine1="        @Suppress(&quot;UNCHECKED_CAST&quot;)"
-        errorLine2="        ^">
+        errorLine1="            @Suppress(&quot;UNCHECKED_CAST&quot;) val list = it as List&lt;Float>"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/ui/text/Savers.kt"/>
     </issue>
@@ -175,8 +175,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable set with type TreeSet&lt;Integer>: replace with IntSet"
-        errorLine1="        val set = TreeSet&lt;Int>().apply {"
-        errorLine2="        ^">
+        errorLine1="        val set = TreeSet&lt;Int>().apply { words.fastForEach { add(it) } }"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="${:compose:ui:ui-text*debug*MAIN*sourceProvider*0*javaDir*4}/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt"/>
     </issue>
diff --git a/compose/ui/ui-text/samples/build.gradle b/compose/ui/ui-text/samples/build.gradle
index ea2947f..f19e8a0 100644
--- a/compose/ui/ui-text/samples/build.gradle
+++ b/compose/ui/ui-text/samples/build.gradle
@@ -51,5 +51,8 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.text.samples"
+    // TODO(b/328001575)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
index 35ecfc4..fb03165 100644
--- a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
@@ -49,6 +49,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.em
 import androidx.compose.ui.unit.sp
+import androidx.emoji2.bundled.BundledEmojiCompatConfig
 import androidx.emoji2.text.EmojiCompat
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -85,7 +86,9 @@
         @JvmStatic
         fun setup() {
             EmojiCompat.reset(null)
-            EmojiCompat.init(appContext)
+            // we want a temporary thread, we don't need to control the font loading thread
+            // for this test, hence the deprecation suppression
+            @Suppress("DEPRECATION") EmojiCompat.init(BundledEmojiCompatConfig(appContext))
         }
 
         @AfterClass
@@ -4406,6 +4409,18 @@
     }
 
     @Test
+    fun getWordBoundary_multichar() {
+        // "ab 𐐔𐐯𐑅𐐨𐑉𐐯𐐻 cd" - example of multi-char code units
+        //             | (offset=3)      | (offset=6)
+        val text =
+            "ab \uD801\uDC14\uD801\uDC2F\uD801\uDC45\uD801\uDC28\uD801\uDC49\uD801\uDC2F\uD801\uDC3B cd"
+        val paragraph = simpleParagraph(text, TextStyle())
+        val result = paragraph.getWordBoundary(6)
+        assertThat(result.start).isEqualTo(3)
+        assertThat(result.end).isEqualTo(17)
+    }
+
+    @Test
     fun test_finalFontSizeChangesWithDensity() {
         val text = "a"
         val fontSize = 20.sp
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/BoringLayoutFactoryTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/CharSequenceCharacterIteratorTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/CharSequenceCharacterIteratorTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/CharSequenceCharacterIteratorTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/CharSequenceCharacterIteratorTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/FontPaddingTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/FontPaddingTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingWithCustomFallbackTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/FontPaddingWithCustomFallbackTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingWithCustomFallbackTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/FontPaddingWithCustomFallbackTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutCompatTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutCompatTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutCompatTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutCompatTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalMultiLineTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutGetHorizontalMultiLineTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalMultiLineTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutGetHorizontalMultiLineTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutGetHorizontalTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutHelperParagraphTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/LayoutIntrinsicsTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/PaintBoundsTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/PaintBoundsTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/PaintBoundsTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/PaintBoundsTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/SegmentBreakerTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/SegmentBreakerTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/SegmentBreakerTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/SegmentBreakerTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextAndroidCanvasTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextAndroidCanvasTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/TextAndroidCanvasTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextAndroidCanvasTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutFillBoundingBoxesTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutFillBoundingBoxesTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutFillBoundingBoxesTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutFillBoundingBoxesTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIsLineEllipsizedTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutIsLineEllipsizedTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIsLineEllipsizedTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutIsLineEllipsizedTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutLineVisibleEndTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutLineVisibleEndTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutLineVisibleEndTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutLineVisibleEndTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutSpanTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/TextLayoutTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/animation/SegmentBreakerBreakSegmentTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/selection/WordBoundaryTest.kt
diff --git a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/selection/WordIteratorTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/selection/WordIteratorTest.kt
new file mode 100644
index 0000000..95354e1
--- /dev/null
+++ b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/selection/WordIteratorTest.kt
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.android.selection
+
+import androidx.emoji2.bundled.BundledEmojiCompatConfig
+import androidx.emoji2.text.EmojiCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import java.text.BreakIterator
+import java.util.Locale
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class WordIteratorTest {
+    companion object {
+        private val context = InstrumentationRegistry.getInstrumentation().targetContext
+
+        @BeforeClass
+        @JvmStatic
+        fun setup() {
+            EmojiCompat.reset(null)
+            // we want a temporary thread, we don't need to control the font loading thread
+            // for this test, hence the deprecation suppression
+            @Suppress("DEPRECATION") EmojiCompat.init(BundledEmojiCompatConfig(context))
+        }
+
+        @AfterClass
+        @JvmStatic
+        fun clean() {
+            EmojiCompat.reset(null)
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testConstructor_IndexOutOfBounds_too_big() {
+        WordIterator("text", 100, 100, Locale.ENGLISH)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testConstructor_IndexOutOfBounds_too_small() {
+        WordIterator("text", -100, -100, Locale.ENGLISH)
+    }
+
+    @Test
+    fun testConstructor_valid_full_text() {
+        val text = "text"
+        WordIterator(text, 0, text.length, Locale.ENGLISH)
+    }
+
+    @Test
+    fun testConstructor_valid_beginning() {
+        val text = "text"
+        WordIterator(text, 0, 0, Locale.ENGLISH)
+    }
+
+    @Test
+    fun testConstructor_valid_end() {
+        val text = "text"
+        WordIterator(text, 0, text.length, Locale.ENGLISH)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testNextBoundary_out_of_boundary_too_small() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.nextBoundary(-1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testNextBoundary_out_of_boundary_too_big() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.nextBoundary(text.length + 1)
+    }
+
+    @Test
+    fun testNextBoundary_iterate_through() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        // Start from the beginning.
+        var currentOffset = 0
+        // The word is "abc".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('c') + 1)
+        // The word is space.
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('d'))
+        // The word is "def".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('f') + 1)
+        // The word is "-".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('g'))
+        // The word is "ghi".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('i') + 1)
+        // The word is ".".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
+        // The word is space.
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('j'))
+        // The word is "jkl".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.length)
+        // WordIterator reaches the end.
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
+    }
+
+    @Test
+    fun testNextBoundary_iterate_through_RTL() { // Hebrew -- "אבג דה-וז. חט"
+        val text = "\u05d0\u05d1\u05d2 \u05d3\u05d4-\u05d5\u05d6. \u05d7\u05d8"
+        val wordIterator = WordIterator(text, 0, text.length, Locale("he", "IL"))
+        // Start from the beginning.
+        var currentOffset = 0
+        // The word is "\u05d0\u05d1\u05d2"("אבג")
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d2') + 1)
+        // The word is space.
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d3'))
+        // The word is "\u05d3\u05d4"("דה")
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('-'))
+        // The word is "-".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d5'))
+        // The word is "\u05d5\u05d6("וז")
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('.'))
+        // The word is ".".
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
+        // The word is space.
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d7'))
+        // The word is "\u05d7\u05d8"("חט")
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.length)
+        // WordIterator reaches the end.
+        currentOffset = wordIterator.nextBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
+    }
+
+    @Test(timeout = 5000)
+    fun testNextBoundary_emoji() {
+        assertThat(EmojiCompat.isConfigured()).isTrue()
+        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
+        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
+
+        val text = "ab, c\uD83D\uDC4D\uD83C\uDFFEd!" // ab, c👍🏾d!
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.nextBoundary(4)).isEqualTo(10)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testPrevBoundary_out_of_boundary_too_small() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.prevBoundary(-1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testPrevBoundary_out_of_boundary_too_big() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.prevBoundary(text.length + 1)
+    }
+
+    @Test
+    fun testPrevBoundary_iterate_through() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        // Start from the end.
+        var currentOffset = text.length
+        // The word is "jkl".
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('j'))
+        // The word is space.
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
+        // The word is ".".
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('i') + 1)
+        // The word is "ghi".
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('g'))
+        // The word is "-".
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('f') + 1)
+        // The word is "def".
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('d'))
+        // The word is space.
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('c') + 1)
+        // The word is "abc".
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('a'))
+        // WordIterator reaches the beginning.
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
+    }
+
+    @Test
+    fun testPrevBoundary_iterate_through_RTL() { // Hebrew -- "אבג דה-וז. חט"
+        val text = "\u05d0\u05d1\u05d2 \u05d3\u05d4-\u05d5\u05d6. \u05d7\u05d8"
+        val wordIterator = WordIterator(text, 0, text.length, Locale("he", "IL"))
+        // Start from the end.
+        var currentOffset = text.length
+        // The word is "\u05d7\u05d8"("חט")
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d7'))
+        // The word is space.
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
+        // The word is '.'.
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('.'))
+        // The word is "\u05d5\u05d6("וז")
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d5'))
+        // The word is "-".
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('-'))
+        // The word is "\u05d3\u05d4"("דה")
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d3'))
+        // The word is space.
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf(' '))
+        // The word is "\u05d0\u05d1\u05d2"("אבג")
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d0'))
+        // WordIterator reaches the beginning.
+        currentOffset = wordIterator.prevBoundary(currentOffset)
+        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
+    }
+
+    @Test(timeout = 5000)
+    fun testPrevBoundary_emoji() {
+        assertThat(EmojiCompat.isConfigured()).isTrue()
+        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
+        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
+
+        val text = "ab, c\uD83D\uDC4D\uD83C\uDFFE!" // ab, c👍🏾!
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.prevBoundary(9)).isEqualTo(4)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPrevWordBeginningOnTwoWordsBoundary_out_of_boundary_too_small() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPrevWordBeginningOnTwoWordsBoundary(-1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPrevWordBeginningOnTwoWordsBoundary_out_of_boundary_too_big() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length + 1)
+    }
+
+    @Test
+    fun testGetPrevWordBeginningOnTwoWordsBoundary_Empty_String() {
+        val text = ""
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(0))
+            .isEqualTo(BreakIterator.DONE)
+    }
+
+    @Test
+    fun testGetPrevWordBeginningOnTwoWordsBoundary() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('a')))
+            .isEqualTo(text.indexOf('a'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('c')))
+            .isEqualTo(text.indexOf('a'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('d')))
+            .isEqualTo(text.indexOf('d'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('f')))
+            .isEqualTo(text.indexOf('d'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('-')))
+            .isEqualTo(text.indexOf('d'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('g')))
+            .isEqualTo(text.indexOf('g'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('.')))
+            .isEqualTo(text.indexOf('g'))
+    }
+
+    @Test
+    fun testGetPrevWordBeginningOnTwoWordsBoundary_CJK() {
+        // Japanese HIRAGANA letter + KATAKANA letters -- "あアィイ"
+        val text = "\u3042\u30A2\u30A3\u30A4"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.JAPANESE)
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u3042')))
+            .isEqualTo(text.indexOf('\u3042'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A2')))
+            .isEqualTo(text.indexOf('\u3042'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A4')))
+            .isEqualTo(text.indexOf('\u30A2'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length))
+            .isEqualTo(text.indexOf('\u30A2'))
+    }
+
+    @Test
+    fun testGetPrevWordBeginningOnTwoWordsBoundary_apostropheMiddleOfWord() {
+        // These tests confirm that the word "isn't" is treated like one word.
+        val text = "isn't he"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('i')))
+            .isEqualTo(text.indexOf('i'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('n')))
+            .isEqualTo(text.indexOf('i'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\'')))
+            .isEqualTo(text.indexOf('i'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('t')))
+            .isEqualTo(text.indexOf('i'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('t') + 1))
+            .isEqualTo(text.indexOf('i'))
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('h')))
+            .isEqualTo(text.indexOf('h'))
+    }
+
+    @Test(timeout = 5000)
+    fun testGetPrevWordBeginningOnTwoWordsBoundary_emoji() {
+        assertThat(EmojiCompat.isConfigured()).isTrue()
+        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
+        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
+
+        val text = "a b\uD83E\uDDD1\uD83C\uDFFF\u200D\uD83E\uDDB0c\uD83D\uDC4D\uD83C\uDFFE d"
+        // a b🧑🏿‍🦰c👍🏾 d
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('d') - 2))
+            .isEqualTo(text.indexOf('b'))
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetNextWordEndOnTwoWordBoundary_out_of_boundary_too_small() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getNextWordEndOnTwoWordBoundary(-1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetNextWordEndOnTwoWordBoundary_out_of_boundary_too_big() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getNextWordEndOnTwoWordBoundary(text.length + 1)
+    }
+
+    @Test
+    fun testGetNextWordEndOnTwoWordBoundary_Empty_String() {
+        val text = ""
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(0)).isEqualTo(BreakIterator.DONE)
+    }
+
+    @Test
+    fun testGetNextWordEndOnTwoWordBoundary() {
+        val text = "abc def-ghi. jkl"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('a')))
+            .isEqualTo(text.indexOf(' '))
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('c')))
+            .isEqualTo(text.indexOf(' '))
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('d')))
+            .isEqualTo(text.indexOf('-'))
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('f')))
+            .isEqualTo(text.indexOf('-'))
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('-')))
+            .isEqualTo(text.indexOf('-'))
+    }
+
+    @Test
+    fun testGetNextWordEndOnTwoWordBoundary_CJK() {
+        // Japanese HIRAGANA letter + KATAKANA letters -- "あアィイ"
+        val text = "\u3042\u30A2\u30A3\u30A4"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.JAPANESE)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u3042')))
+            .isEqualTo(text.indexOf('\u3042') + 1)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A2')))
+            .isEqualTo(text.indexOf('\u30A4') + 1)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4')))
+            .isEqualTo(text.indexOf('\u30A4') + 1)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4') + 1))
+            .isEqualTo(text.indexOf('\u30A4') + 1)
+    }
+
+    @Test
+    fun testGetNextWordEndOnTwoWordBoundary_apostropheMiddleOfWord() {
+        // These tests confirm that the word "isn't" is treated like one word.
+        val text = "isn't he"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('i')))
+            .isEqualTo(text.indexOf('t') + 1)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('n')))
+            .isEqualTo(text.indexOf('t') + 1)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\'')))
+            .isEqualTo(text.indexOf('t') + 1)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('t')))
+            .isEqualTo(text.indexOf('t') + 1)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('h')))
+            .isEqualTo(text.indexOf('e') + 1)
+    }
+
+    @Test(timeout = 5000)
+    fun testGetNextWordEndOnTwoWordBoundary_emoji() {
+        assertThat(EmojiCompat.isConfigured()).isTrue()
+        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
+        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
+
+        val text = "a b\uD83E\uDDD1\uD83C\uDFFF\u200D\uD83E\uDDB0c\uD83D\uDC4D\uD83C\uDFFE d"
+        // a b🧑🏿‍🦰c👍🏾 d
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(2)).isEqualTo(text.indexOf('d') - 1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPunctuationBeginning_out_of_boundary_too_small() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPunctuationBeginning(-2)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPunctuationBeginning_out_of_boundary_too_big() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPunctuationBeginning(text.length + 1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPunctuationBeginning_DONE() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPunctuationBeginning(BreakIterator.DONE)
+    }
+
+    @Test
+    fun testGetPunctuationBeginning() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('a')))
+            .isEqualTo(BreakIterator.DONE)
+        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('c')))
+            .isEqualTo(BreakIterator.DONE)
+        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('!')))
+            .isEqualTo(text.indexOf('!'))
+        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('?') + 1))
+            .isEqualTo(text.indexOf('!'))
+        assertThat(wordIterator.getPunctuationBeginning(text.indexOf(';')))
+            .isEqualTo(text.indexOf(';'))
+        assertThat(wordIterator.getPunctuationBeginning(text.indexOf(')')))
+            .isEqualTo(text.indexOf(';'))
+        assertThat(wordIterator.getPunctuationBeginning(text.length)).isEqualTo(text.indexOf(';'))
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPunctuationEnd_out_of_boundary_too_small() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPunctuationEnd(-2)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPunctuationEnd_out_of_boundary_too_big() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPunctuationEnd(text.length + 1)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testGetPunctuationEnd_DONE() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        wordIterator.getPunctuationEnd(BreakIterator.DONE)
+    }
+
+    @Test
+    fun testGetPunctuationEnd() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.getPunctuationEnd(text.indexOf('a')))
+            .isEqualTo(text.indexOf('?') + 1)
+        assertThat(wordIterator.getPunctuationEnd(text.indexOf('?') + 1))
+            .isEqualTo(text.indexOf('?') + 1)
+        assertThat(wordIterator.getPunctuationEnd(text.indexOf('(')))
+            .isEqualTo(text.indexOf('(') + 1)
+        assertThat(wordIterator.getPunctuationEnd(text.indexOf('(') + 2))
+            .isEqualTo(text.indexOf(')') + 1)
+        assertThat(wordIterator.getPunctuationEnd(text.indexOf(')') + 1))
+            .isEqualTo(text.indexOf(')') + 1)
+        assertThat(wordIterator.getPunctuationEnd(text.indexOf('d'))).isEqualTo(BreakIterator.DONE)
+        assertThat(wordIterator.getPunctuationEnd(text.length)).isEqualTo(BreakIterator.DONE)
+    }
+
+    @Test
+    fun testIsAfterPunctuation() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.isAfterPunctuation(text.indexOf('a'))).isFalse()
+        assertThat(wordIterator.isAfterPunctuation(text.indexOf('!'))).isFalse()
+        assertThat(wordIterator.isAfterPunctuation(text.indexOf('?'))).isTrue()
+        assertThat(wordIterator.isAfterPunctuation(text.indexOf('?') + 1)).isTrue()
+        assertThat(wordIterator.isAfterPunctuation(text.indexOf('d'))).isFalse()
+        assertThat(wordIterator.isAfterPunctuation(BreakIterator.DONE)).isFalse()
+        assertThat(wordIterator.isAfterPunctuation(text.length + 1)).isFalse()
+    }
+
+    @Test
+    fun testIsOnPunctuation() {
+        val text = "abc!? (^^;) def"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        assertThat(wordIterator.isOnPunctuation(text.indexOf('a'))).isFalse()
+        assertThat(wordIterator.isOnPunctuation(text.indexOf('!'))).isTrue()
+        assertThat(wordIterator.isOnPunctuation(text.indexOf('?'))).isTrue()
+        assertThat(wordIterator.isOnPunctuation(text.indexOf('?') + 1)).isFalse()
+        assertThat(wordIterator.isOnPunctuation(text.indexOf(')'))).isTrue()
+        assertThat(wordIterator.isOnPunctuation(text.indexOf(')') + 1)).isFalse()
+        assertThat(wordIterator.isOnPunctuation(text.indexOf('d'))).isFalse()
+        assertThat(wordIterator.isOnPunctuation(BreakIterator.DONE)).isFalse()
+        assertThat(wordIterator.isOnPunctuation(text.length)).isFalse()
+        assertThat(wordIterator.isOnPunctuation(text.length + 1)).isFalse()
+    }
+
+    @Test
+    fun testOneWord() {
+        val text = "zen"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsWord(wordIterator, 0, 3)
+    }
+
+    @Test
+    fun testSpacesOnly() {
+        val text = " "
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsNotWord(wordIterator, 0, 1)
+    }
+
+    @Test
+    fun testCommaWithSpace() {
+        val text = ", "
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsNotWord(wordIterator, 0, 2)
+    }
+
+    @Test
+    fun testSymbols() {
+        val text = ":-)"
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsNotWord(wordIterator, 0, 3)
+    }
+
+    @Test
+    fun testBeginningEnd1() {
+        val text = "Well hello,   there! "
+        //         0123456789012345678901
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsWord(wordIterator, 0, 4)
+        verifyIsWord(wordIterator, 5, 10)
+        verifyIsNotWord(wordIterator, 11, 13)
+        verifyIsWord(wordIterator, 14, 19)
+        verifyIsNotWord(wordIterator, 20, 21)
+    }
+
+    @Test
+    fun testBeginningEnd2() {
+        val text = "  Another - sentence"
+        //         012345678901234567890
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsNotWord(wordIterator, 0, 1)
+        verifyIsWord(wordIterator, 2, 9)
+        verifyIsNotWord(wordIterator, 10, 11)
+        verifyIsWord(wordIterator, 12, 20)
+    }
+
+    @Test
+    fun testBeginningEnd3() {
+        val text = "This is \u0644\u0627 tested" // Lama-aleph
+        //         012345678     9     01234567
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsWord(wordIterator, 0, 4)
+        verifyIsWord(wordIterator, 5, 7)
+        verifyIsWord(wordIterator, 8, 10)
+        verifyIsWord(wordIterator, 11, 17)
+    }
+
+    @Test
+    fun testSurrogate() {
+        val gothicBairkan = "\uD800\uDF31"
+        val text = "one " + gothicBairkan + "xxx word"
+        //         0123    45         678901234
+        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
+        verifyIsWord(wordIterator, 0, 3)
+        verifyIsWordWithSurrogate(wordIterator, 4, 9, 5)
+        verifyIsWord(wordIterator, 10, 14)
+    }
+
+    private fun verifyIsWordWithSurrogate(
+        wordIterator: WordIterator,
+        beginning: Int,
+        end: Int,
+        surrogateIndex: Int
+    ) {
+        for (i in beginning..end) {
+            if (i == surrogateIndex) continue
+            assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(i)).isEqualTo(beginning)
+            assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(i)).isEqualTo(end)
+        }
+    }
+
+    private fun verifyIsWord(wordIterator: WordIterator, beginning: Int, end: Int) {
+        verifyIsWordWithSurrogate(wordIterator, beginning, end, -1)
+    }
+
+    private fun verifyIsNotWord(wordIterator: WordIterator, beginning: Int, end: Int) {
+        for (i in beginning..end) {
+            assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(i))
+                .isEqualTo(BreakIterator.DONE)
+            assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(i))
+                .isEqualTo(BreakIterator.DONE)
+        }
+    }
+}
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/BaselineShiftSpanTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/FontFeatureSpanTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanEmTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanPxTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LineHeightStyleSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/LineHeightStyleSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/LineHeightStyleSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/LineHeightStyleSpanTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/PlaceholderSpanTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/ShadowSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/ShadowSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/ShadowSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/ShadowSpanTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/SkewXSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/SkewXSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/SkewXSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/SkewXSpanTest.kt
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt
similarity index 100%
rename from text/text/src/androidTest/java/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt
rename to compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/android/style/TypefaceSpanTest.kt
diff --git a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt
index f66bee2..3d78fa6 100644
--- a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt
+++ b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt
@@ -17,8 +17,10 @@
 package androidx.compose.ui.text.font
 
 import android.content.Context
+import android.content.res.Configuration
 import android.graphics.Typeface
 import android.os.Build
+import android.view.ContextThemeWrapper
 import androidx.compose.ui.text.FontTestData
 import androidx.compose.ui.text.UncachedFontFamilyResolver
 import androidx.compose.ui.text.font.testutils.AsyncFauxFont
@@ -752,4 +754,32 @@
         val typeface = resolveAsTypeface(fontFamily, FontWeight.W400)
         assertThat(typeface).isSameInstanceAs(Typeface.SERIF)
     }
+
+    @SdkSuppress(maxSdkVersion = 30)
+    @Test
+    fun androidResolveInterceptor_noFontWeightApplied_beforeApi31() {
+        initializeSubject(AndroidFontResolveInterceptor(context))
+        val typeface = resolveAsTypeface()
+        assertThat(typeface).hasWeightAndStyle(FontWeight.Normal, FontStyle.Normal)
+    }
+
+    @SdkSuppress(minSdkVersion = 31)
+    @Test
+    fun androidResolveInterceptor_fontWeightAdjustment_appliesPastApi31() {
+        val newContext =
+            ContextThemeWrapper(context, 0).apply {
+                applyOverrideConfiguration(
+                    Configuration().apply {
+                        updateFrom(context.resources.configuration)
+                        this.fontWeightAdjustment = 100
+                    }
+                )
+            }
+
+        initializeSubject(AndroidFontResolveInterceptor(newContext))
+        val typeface = resolveAsTypeface()
+
+        // FontWeight.Normal + 100 = FontWeight.Medium
+        assertThat(typeface).hasWeightAndStyle(FontWeight.Medium, FontStyle.Normal)
+    }
 }
diff --git a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/matchers/BitmapSubject.kt b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/matchers/BitmapSubject.kt
index 9f676b8..617f75c 100644
--- a/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/matchers/BitmapSubject.kt
+++ b/compose/ui/ui-text/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/matchers/BitmapSubject.kt
@@ -56,7 +56,7 @@
 
     override fun actualCustomStringRepresentation(): String {
         return if (subject != null) {
-            "($subject ${subject.width}x${subject.height} ${subject.config.name})"
+            "($subject ${subject.width}x${subject.height} ${subject.config!!.name})"
         } else {
             super.actualCustomStringRepresentation()
         }
diff --git a/compose/ui/ui-text/src/androidMain/baseline-prof.txt b/compose/ui/ui-text/src/androidMain/baseline-prof.txt
index dab1232..2f037b4 100644
--- a/compose/ui/ui-text/src/androidMain/baseline-prof.txt
+++ b/compose/ui/ui-text/src/androidMain/baseline-prof.txt
@@ -31,8 +31,6 @@
 HSPLandroidx/compose/ui/text/android/style/LetterSpacingSpanPx;->**(**)**
 HSPLandroidx/compose/ui/text/android/style/LineHeightSpan;->**(**)**
 HSPLandroidx/compose/ui/text/android/style/TypefaceSpan;->**(**)**
-HSPLandroidx/compose/ui/text/caches/LruCache;->**(**)**
-HSPLandroidx/compose/ui/text/caches/SimpleArrayMap;->**(**)**
 HSPLandroidx/compose/ui/text/font/AndroidFontLoader;->**(**)**
 HSPLandroidx/compose/ui/text/font/AndroidFontResolveInterceptor**->**(**)**
 HSPLandroidx/compose/ui/text/font/AsyncTypefaceCache;->**(**)**
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/BoringLayoutFactory.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/BoringLayoutFactory.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/CharSequenceCharacterIterator.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/CharSequenceCharacterIterator.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/CharSequenceCharacterIterator.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/CharSequenceCharacterIterator.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/InlineClassUtils.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/InlineClassUtils.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/InlineClassUtils.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/InlineClassUtils.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/InternalPlatformTextApi.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/InternalPlatformTextApi.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/InternalPlatformTextApi.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/InternalPlatformTextApi.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutCompat.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutCompat.android.kt
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutHelper.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutHelper.android.kt
new file mode 100644
index 0000000..599853d
--- /dev/null
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutHelper.android.kt
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("PrimitiveInCollection")
+
+package androidx.compose.ui.text.android
+
+import android.text.Layout
+import android.text.TextUtils
+import androidx.annotation.IntRange
+import java.text.Bidi
+
+private const val LINE_FEED = '\n'
+
+/**
+ * Provide utilities for Layout class
+ *
+ * This class is not thread-safe. Do not share an instance with multiple threads.
+ */
+internal class LayoutHelper(val layout: Layout) {
+
+    private val paragraphEnds: List<Int>
+
+    // Stores the list of Bidi object for each paragraph. This could be null if Bidi is not
+    // necessary, i.e. single direction text. Do not use this directly. Use analyzeBidi function
+    // instead.
+    private val paragraphBidi: MutableList<Bidi?>
+
+    // Stores true if the each paragraph already has bidi analyze result. Do not use this
+    // directly. Use analyzeBidi function instead.
+    private val bidiProcessedParagraphs: BooleanArray
+
+    // Temporary buffer for bidi processing.
+    private var tmpBuffer: CharArray? = null
+
+    init {
+        var paragraphEnd = 0
+        val lineFeeds = mutableListOf<Int>()
+        do {
+            paragraphEnd = layout.text.indexOf(char = LINE_FEED, startIndex = paragraphEnd)
+            if (paragraphEnd < 0) {
+                // No more LINE_FEED char found. Use the end of the text as the paragraph end.
+                paragraphEnd = layout.text.length
+            } else {
+                // increment since end offset is exclusive.
+                paragraphEnd++
+            }
+            lineFeeds.add(paragraphEnd)
+        } while (paragraphEnd < layout.text.length)
+        paragraphEnds = lineFeeds
+        paragraphBidi = MutableList(paragraphEnds.size) { null }
+        bidiProcessedParagraphs = BooleanArray(paragraphEnds.size)
+    }
+
+    /**
+     * Analyze the BiDi runs for the paragraphs and returns result object.
+     *
+     * Layout#isRtlCharAt or Layout#getLineDirection is not useful for determining preceding or
+     * following run in visual order. We need to analyze by ourselves.
+     *
+     * This may return null if the Bidi process is not necessary, i.e. there is only single bidi
+     * run.
+     *
+     * @param paragraphIndex a paragraph index
+     */
+    fun analyzeBidi(paragraphIndex: Int): Bidi? {
+        // If we already analyzed target paragraph, just return the result.
+        if (bidiProcessedParagraphs[paragraphIndex]) {
+            return paragraphBidi[paragraphIndex]
+        }
+
+        val paragraphStart = if (paragraphIndex == 0) 0 else paragraphEnds[paragraphIndex - 1]
+        val paragraphEnd = paragraphEnds[paragraphIndex]
+        val paragraphLength = paragraphEnd - paragraphStart
+
+        // We allocate the character buffer for saving memories. The internal implementation
+        // anyway allocate character buffer even if we pass text through
+        // AttributedCharacterIterator. Also there is no way of passing
+        // Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT via AttributedCharacterIterator.
+        //
+        // We also cannot always reuse this buffer since the internal Bidi object keeps this
+        // reference and use it for creating lineBidi. We may be able to share buffer by avoiding
+        // using lineBidi but this is internal implementation details, so share memory as
+        // much as possible and allocate new buffer if we need Bidi object.
+        var buffer = tmpBuffer
+        buffer =
+            if (buffer == null || buffer.size < paragraphLength) {
+                CharArray(paragraphLength)
+            } else {
+                buffer
+            }
+        TextUtils.getChars(layout.text, paragraphStart, paragraphEnd, buffer, 0)
+
+        val result =
+            if (Bidi.requiresBidi(buffer, 0, paragraphLength)) {
+                val flag =
+                    if (isRtlParagraph(paragraphIndex)) {
+                        Bidi.DIRECTION_RIGHT_TO_LEFT
+                    } else {
+                        Bidi.DIRECTION_LEFT_TO_RIGHT
+                    }
+                val bidi = Bidi(buffer, 0, null, 0, paragraphLength, flag)
+
+                if (bidi.runCount == 1) {
+                    // This corresponds to the all text is Right-to-Left case. We don't need to keep
+                    // Bidi object
+                    null
+                } else {
+                    bidi
+                }
+            } else {
+                null
+            }
+
+        paragraphBidi[paragraphIndex] = result
+        bidiProcessedParagraphs[paragraphIndex] = true
+
+        tmpBuffer =
+            if (result != null) {
+                // The ownership of buffer is now passed to Bidi object.
+                // Release tmpBuffer if we didn't allocated in this time.
+                if (buffer === tmpBuffer) null else tmpBuffer
+            } else {
+                // We might allocate larger buffer in this time. Update tmpBuffer with latest one.
+                // (the latest buffer may be same as tmpBuffer)
+                buffer
+            }
+        return result
+    }
+
+    /** Retrieve the number of the paragraph in this layout. */
+    val paragraphCount = paragraphEnds.size
+
+    /**
+     * Returns the zero based paragraph number at the offset.
+     *
+     * The paragraphs are divided by line feed character (U+000A) and line feed character is
+     * included in the preceding paragraph, i.e. if the offset points the line feed character, this
+     * function returns preceding paragraph index.
+     *
+     * @param offset a character offset in the text
+     * @return the paragraph number
+     */
+    fun getParagraphForOffset(@IntRange(from = 0) offset: Int, upstream: Boolean = false): Int {
+        val paragraphIndex =
+            paragraphEnds.binarySearch(offset).let { if (it < 0) -(it + 1) else it + 1 }
+
+        if (upstream && paragraphIndex > 0 && offset == paragraphEnds[paragraphIndex - 1]) {
+            return paragraphIndex - 1
+        }
+
+        return paragraphIndex
+    }
+
+    /**
+     * Returns the inclusive paragraph starting offset of the given paragraph index.
+     *
+     * @param paragraphIndex a paragraph index.
+     * @return an inclusive start character offset of the given paragraph.
+     */
+    fun getParagraphStart(@IntRange(from = 0) paragraphIndex: Int) =
+        if (paragraphIndex == 0) 0 else paragraphEnds[paragraphIndex - 1]
+
+    /**
+     * Returns the exclusive paragraph end offset of the given paragraph index.
+     *
+     * @param paragraphIndex a paragraph index.
+     * @return an exclusive end character offset of the given paragraph.
+     */
+    fun getParagraphEnd(@IntRange(from = 0) paragraphIndex: Int) = paragraphEnds[paragraphIndex]
+
+    /**
+     * Returns true if the resolved paragraph direction is RTL, otherwise return false.
+     *
+     * @param paragraphIndex a paragraph index
+     * @return true if the paragraph is RTL, otherwise false
+     */
+    fun isRtlParagraph(@IntRange(from = 0) paragraphIndex: Int): Boolean {
+        val lineNumber = layout.getLineForOffset(getParagraphStart(paragraphIndex))
+        return layout.getParagraphDirection(lineNumber) == Layout.DIR_RIGHT_TO_LEFT
+    }
+
+    /**
+     * Returns horizontal offset from the drawing origin
+     *
+     * This is the location where a new character would be inserted. If offset points the line
+     * broken offset, this return the insertion offset of preceding line if upstream is true.
+     * Otherwise returns the following line's insertion offset.
+     *
+     * In case of Bi-Directional text, the offset may points graphically different location. Here
+     * primary means that the inserting character's direction will be resolved to the same direction
+     * to the paragraph direction. For example, set usePrimaryHorizontal to true if you want to get
+     * LTR character insertion position for the LTR paragraph, or if you want to get RTL character
+     * insertion position for the RTL paragraph. Set usePrimaryDirection to false if you want to get
+     * RTL character insertion position for the LTR paragraph, or if you want to get LTR character
+     * insertion position for the RTL paragraph.
+     *
+     * @param offset an offset to be insert a character
+     * @param usePrimaryDirection no effect if the given offset does not point the directionally
+     *   transition point. If offset points the directional transition point and this argument is
+     *   true, treat the given offset as the offset of the Bidi run that has the same direction to
+     *   the paragraph direction. Otherwise treat the given offset as the offset of the Bidi run
+     *   that has the different direction to the paragraph direction.
+     * @param upstream if offset points the line broken offset, use upstream offset if true,
+     *   otherwise false.
+     * @return the horizontal offset from the drawing origin.
+     */
+    fun getHorizontalPosition(offset: Int, usePrimaryDirection: Boolean, upstream: Boolean): Float {
+        // Android already calculates downstream
+        if (!upstream) {
+            return getDownstreamHorizontal(offset, usePrimaryDirection)
+        }
+
+        val lineNo = layout.getLineForOffset(offset, upstream)
+        val lineStart = layout.getLineStart(lineNo)
+        val lineEnd = layout.getLineEnd(lineNo)
+
+        // Early exit if the offset points not an edge of line. There is no difference between
+        // downstream and upstream horizontals. This includes out-of-range request
+        if (offset != lineStart && offset != lineEnd) {
+            return getDownstreamHorizontal(offset, usePrimaryDirection)
+        }
+
+        // Similarly, even if the offset points the edge of the line start and line end, we can
+        // use downstream result.
+        if (offset == 0 || offset == layout.text.length) {
+            return getDownstreamHorizontal(offset, usePrimaryDirection)
+        }
+
+        val paraNo = getParagraphForOffset(offset, upstream)
+        val isParaRtl = isRtlParagraph(paraNo)
+
+        // Use line visible end for creating bidi object since invisible whitespaces should not be
+        // considered for location retrieval.
+        val lineVisibleEnd = lineEndToVisibleEnd(lineEnd, lineStart)
+        val paragraphStart = getParagraphStart(paraNo)
+        val bidiStart = lineStart - paragraphStart
+        val bidiEnd = lineVisibleEnd - paragraphStart
+        val lineBidi = analyzeBidi(paraNo)?.createLineBidi(bidiStart, bidiEnd)
+        if (lineBidi == null || lineBidi.runCount == 1) { // easy case. All directions are the same
+            val runDirection = layout.isRtlCharAt(lineStart)
+            val isStartLeft =
+                if (usePrimaryDirection || isParaRtl == runDirection) {
+                    !isParaRtl
+                } else {
+                    isParaRtl
+                }
+            val isOffsetLeft = if (offset == lineStart) isStartLeft else !isStartLeft
+            return if (isOffsetLeft) layout.getLineLeft(lineNo) else layout.getLineRight(lineNo)
+        }
+
+        // Somehow need to find the character's position without using getPrimaryHorizontal.
+        val runs =
+            Array(lineBidi.runCount) {
+                // We may be able to reduce this Bidi Run allocation by using run indices
+                // but unfortunately, Bidi#reorderVisually only accepts array of Object. So auto
+                // boxing happens anyway. Also, looks like Bidi#getRunStart and Bidi#getRunLimit
+                // does non-trivial amount of work. So we save the result into BidiRun.
+                BidiRun(
+                    start = lineStart + lineBidi.getRunStart(it),
+                    end = lineStart + lineBidi.getRunLimit(it),
+                    isRtl = lineBidi.getRunLevel(it) % 2 == 1
+                )
+            }
+        val levels = ByteArray(lineBidi.runCount) { lineBidi.getRunLevel(it).toByte() }
+        Bidi.reorderVisually(levels, 0, runs, 0, runs.size)
+
+        if (offset == lineStart) {
+            // find the visual position of the last character
+            val index = runs.indexOfFirst { it.start == offset }
+            val run = runs[index]
+            // True if the requesting end offset is left edge of the run.
+            val isLeftRequested =
+                if (usePrimaryDirection || isParaRtl == run.isRtl) {
+                    !isParaRtl
+                } else {
+                    isParaRtl
+                }
+
+            if (index == 0 && isLeftRequested) {
+                // Requesting most left run's left offset, just use line left.
+                return layout.getLineLeft(lineNo)
+            } else if (index == runs.lastIndex && !isLeftRequested) {
+                // Requesting most right run's right offset, just use line right.
+                return layout.getLineRight(lineNo)
+            } else if (isLeftRequested) {
+                // Reaching here means the run is LTR, since RTL run cannot be start from the
+                // middle of the text in RTL context.
+                // This is LTR run, so left position of this run is the same to left
+                // RTL run's right (i.e. start) position.
+                return layout.getPrimaryHorizontal(runs[index - 1].start)
+            } else {
+                // Reaching here means the run is RTL, since LTR run cannot be start from the
+                // middle of the text in LTR context.
+                // This is RTL run, so right position of this run is the same to right
+                // LTR run's left (i.e. start) position.
+                return layout.getPrimaryHorizontal(runs[index + 1].start)
+            }
+        } else {
+            // Bidi runs are created between lineStart and lineVisibleEnd
+            // If the requested offset is a white space at the end of the line, it would be
+            // out of bounds for the runs in this Bidi. We are adjusting the requested offset
+            // to the visible end of line.
+            val lineEndAdjustedOffset =
+                if (offset > lineVisibleEnd) {
+                    lineEndToVisibleEnd(offset, lineStart)
+                } else {
+                    offset
+                }
+            // find the visual position of the last character
+            val index = runs.indexOfFirst { it.end == lineEndAdjustedOffset }
+            val run = runs[index]
+            // True if the requesting end offset is left edge of the run.
+            val isLeftRequested =
+                if (usePrimaryDirection || isParaRtl == run.isRtl) {
+                    isParaRtl
+                } else {
+                    !isParaRtl
+                }
+            if (index == 0 && isLeftRequested) {
+                // Requesting most left run's left offset, just use line left.
+                return layout.getLineLeft(lineNo)
+            } else if (index == runs.lastIndex && !isLeftRequested) {
+                // Requesting most right run's right offset, just use line right.
+                return layout.getLineRight(lineNo)
+            } else if (isLeftRequested) {
+                // Reaching here means the run is RTL, since LTR run cannot be broken from the
+                // middle of the text in LTR context.
+                // This is RTL run, so left position of this run is the same to left
+                // LTR run's right (i.e. end) position.
+                return layout.getPrimaryHorizontal(runs[index - 1].end)
+            } else { // !isEndLeft
+                // Reaching here means the run is LTR, since RTL run cannot be broken from the
+                // middle of the text in RTL context.
+                // This is LTR run, so right position of this run is the same to right
+                // RTL run's left (i.e. end) position.
+                return layout.getPrimaryHorizontal(runs[index + 1].end)
+            }
+        }
+    }
+
+    /**
+     * Return the text offset after the last visible character on the specified line. For example
+     * whitespaces are not counted as visible characters.
+     */
+    fun getLineVisibleEnd(lineIndex: Int): Int {
+        return lineEndToVisibleEnd(layout.getLineEnd(lineIndex), layout.getLineStart(lineIndex))
+    }
+
+    private fun getDownstreamHorizontal(offset: Int, primary: Boolean): Float {
+        val lineNo = layout.getLineForOffset(offset)
+        val lineEnd = layout.getLineEnd(lineNo)
+
+        // [android.text.Layout#getHorizontal] has a bug that causes a crash if requested offset
+        // is in an ellipsized region and comes after a line feed character. We coerce at most to
+        // lineEnd of the line this offset belongs to. getLineEnd respects line feed characters.
+        // Any ellipsized character should already return the visible end value, which they do until
+        // a line feed character. We can safely assume rest of the characters can also return the
+        // same result as the reported line end.
+        val targetOffset = offset.coerceAtMost(lineEnd)
+
+        return if (primary) {
+            layout.getPrimaryHorizontal(targetOffset)
+        } else {
+            layout.getSecondaryHorizontal(targetOffset)
+        }
+    }
+
+    internal data class BidiRun(val start: Int, val end: Int, val isRtl: Boolean)
+
+    /**
+     * Convert line end offset to the offset that is the last visible character. Last visible
+     * character on this line cannot be before line start.
+     */
+    private fun lineEndToVisibleEnd(lineEnd: Int, lineStart: Int): Int {
+        var visibleEnd = lineEnd
+        while (visibleEnd > lineStart) {
+            if (isLineEndSpace(layout.text[visibleEnd - 1 /* visibleEnd is exclusive */])) {
+                visibleEnd--
+            } else {
+                break
+            }
+        }
+        return visibleEnd
+    }
+
+    internal fun getLineBidiRuns(lineIndex: Int): Array<BidiRun> {
+        val lineStart = layout.getLineStart(lineIndex)
+        val lineEnd = layout.getLineEnd(lineIndex)
+
+        val paragraphIndex = getParagraphForOffset(lineStart)
+        val paragraphStart = getParagraphStart(paragraphIndex)
+
+        val bidiStart = lineStart - paragraphStart
+        val bidiEnd = lineEnd - paragraphStart
+        val lineBidi =
+            analyzeBidi(paragraphIndex)?.createLineBidi(bidiStart, bidiEnd)
+                ?: return arrayOf(BidiRun(lineStart, lineEnd, layout.isRtlCharAt(lineStart)))
+
+        return Array(lineBidi.runCount) {
+            BidiRun(
+                start = lineStart + lineBidi.getRunStart(it),
+                end = lineStart + lineBidi.getRunLimit(it),
+                isRtl = lineBidi.getRunLevel(it) % 2 == 1
+            )
+        }
+    }
+
+    // The spaces that will not be rendered if they are placed at the line end. In most case, it is
+    // whitespace or line feed character, hence checking linearly should be enough.
+    @Suppress("ConvertTwoComparisonsToRangeCheck")
+    fun isLineEndSpace(c: Char) =
+        c == ' ' ||
+            c == '\n' ||
+            c == '\u1680' ||
+            (c >= '\u2000' && c <= '\u200A' && c != '\u2007') ||
+            c == '\u205F' ||
+            c == '\u3000'
+}
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt
new file mode 100644
index 0000000..80c11b3
--- /dev/null
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.android
+
+import android.text.BoringLayout
+import android.text.Layout
+import android.text.SpannableString
+import android.text.Spanned
+import android.text.TextPaint
+import android.text.style.CharacterStyle
+import android.text.style.MetricAffectingSpan
+import androidx.compose.ui.text.android.style.LetterSpacingSpanEm
+import androidx.compose.ui.text.android.style.LetterSpacingSpanPx
+import java.text.BreakIterator
+import java.util.PriorityQueue
+import kotlin.math.ceil
+
+/**
+ * Flag for applying the fix for [b/346918500](https://issuetracker.google.com/346918500).
+ *
+ * If true, this will allocate a new [SpannableString] if there are spans that must be removed
+ * before measuring any intrinsic width.
+ */
+@Suppress("MayBeConstant") // Don't inline so folks can R8 assumevalues it
+private val stripNonMetricAffectingCharSpans: Boolean = true
+
+/** Computes and caches the text layout intrinsic values such as min/max width. */
+internal class LayoutIntrinsics(
+    private val charSequence: CharSequence,
+    private val textPaint: TextPaint,
+    @LayoutCompat.TextDirection private val textDirectionHeuristic: Int
+) {
+
+    private var _maxIntrinsicWidth: Float = Float.NaN
+    private var _minIntrinsicWidth: Float = Float.NaN
+    private var _boringMetrics: BoringLayout.Metrics? = null
+    private var boringMetricsIsInit: Boolean = false
+
+    private var _charSequenceForIntrinsicWidth: CharSequence? = null
+    private val charSequenceForIntrinsicWidth: CharSequence
+        get() =
+            if (_charSequenceForIntrinsicWidth == null) {
+                if (stripNonMetricAffectingCharSpans) {
+                    stripNonMetricAffectingCharacterStyleSpans(charSequence).also {
+                        _charSequenceForIntrinsicWidth = it
+                    }
+                } else {
+                    charSequence
+                }
+            } else {
+                _charSequenceForIntrinsicWidth!!
+            }
+
+    /**
+     * Compute Android platform BoringLayout metrics. A null value means the provided CharSequence
+     * cannot be laid out using a BoringLayout.
+     */
+    val boringMetrics: BoringLayout.Metrics?
+        get() {
+            if (!boringMetricsIsInit) {
+                val frameworkTextDir = getTextDirectionHeuristic(textDirectionHeuristic)
+                _boringMetrics =
+                    BoringLayoutFactory.measure(charSequence, textPaint, frameworkTextDir)
+                boringMetricsIsInit = true
+            }
+            return _boringMetrics
+        }
+
+    /**
+     * Calculate minimum intrinsic width of the CharSequence.
+     *
+     * @see androidx.compose.ui.text.android.minIntrinsicWidth
+     */
+    val minIntrinsicWidth: Float
+        get() =
+            if (!_minIntrinsicWidth.isNaN()) {
+                _minIntrinsicWidth
+            } else {
+                _minIntrinsicWidth = computeMinIntrinsicWidth()
+                _minIntrinsicWidth
+            }
+
+    /**
+     * Returns the word with the longest length. To calculate it in a performant way, it applies a
+     * heuristics where
+     * - it first finds a set of words with the longest length
+     * - finds the word with maximum width in that set
+     */
+    private fun computeMinIntrinsicWidth(): Float {
+        val iterator = BreakIterator.getLineInstance(textPaint.textLocale)
+        iterator.text = CharSequenceCharacterIterator(charSequence, 0, charSequence.length)
+
+        // 10 is just a random number that limits the size of the candidate list
+        val heapSize = 10
+        // min heap that will hold [heapSize] many words with max length
+        val longestWordCandidates =
+            PriorityQueue(
+                heapSize,
+                Comparator<Pair<Int, Int>> { left, right ->
+                    (left.second - left.first) - (right.second - right.first)
+                }
+            )
+
+        var start = 0
+        var end = iterator.next()
+        while (end != BreakIterator.DONE) {
+            if (longestWordCandidates.size < heapSize) {
+                longestWordCandidates.add(Pair(start, end))
+            } else {
+                longestWordCandidates.peek()?.let { minPair ->
+                    if ((minPair.second - minPair.first) < (end - start)) {
+                        longestWordCandidates.poll()
+                        longestWordCandidates.add(Pair(start, end))
+                    }
+                }
+            }
+
+            start = end
+            end = iterator.next()
+        }
+
+        return if (longestWordCandidates.isEmpty()) 0f
+        else longestWordCandidates.maxOf { (start, end) -> getDesiredWidth(start, end) }
+    }
+
+    /**
+     * Calculate maximum intrinsic width for the CharSequence. Maximum intrinsic width is the width
+     * of text where no soft line breaks are applied.
+     */
+    val maxIntrinsicWidth: Float
+        get() =
+            if (!_maxIntrinsicWidth.isNaN()) {
+                _maxIntrinsicWidth
+            } else {
+                _maxIntrinsicWidth = computeMaxIntrinsicWidth()
+                _maxIntrinsicWidth
+            }
+
+    private fun computeMaxIntrinsicWidth(): Float {
+        var desiredWidth = (boringMetrics?.width ?: -1).toFloat()
+
+        // boring metrics doesn't cover RTL text so we fallback to different calculation
+        // when boring metrics can't be calculated
+        if (desiredWidth < 0) {
+            // b/233856978, apply `ceil` function here to be consistent with the boring
+            // metrics width calculation that does it under the hood, too
+            desiredWidth = ceil(getDesiredWidth())
+        }
+
+        if (shouldIncreaseMaxIntrinsic(desiredWidth, charSequence, textPaint)) {
+            // b/173574230, increase maxIntrinsicWidth, so that StaticLayout won't form 2
+            // lines for the given maxIntrinsicWidth
+            desiredWidth += 0.5f
+        }
+        return desiredWidth
+    }
+
+    private fun getDesiredWidth(
+        start: Int = 0,
+        end: Int = charSequenceForIntrinsicWidth.length
+    ): Float = Layout.getDesiredWidth(charSequenceForIntrinsicWidth, start, end, textPaint)
+}
+
+/**
+ * See [b/346918500#comment7](https://issuetracker.google.com/346918500#comment7).
+ *
+ * Remove all character styling spans for measuring intrinsic width. [CharacterStyle] spans may
+ * affect the intrinsic width, even though they aren't supposed to, resulting in a width that
+ * doesn't actually fit the text. This can cause the line to unexpectedly wrap, even if `maxLines`
+ * was set to `1` or `softWrap` was `false`.
+ *
+ * [MetricAffectingSpan] extends [CharacterStyle], but [MetricAffectingSpan]s are allowed to affect
+ * the width, so only remove spans that **do** extend [CharacterStyle] but **don't** extend
+ * [MetricAffectingSpan].
+ */
+private fun stripNonMetricAffectingCharacterStyleSpans(charSequence: CharSequence): CharSequence {
+    if (charSequence !is Spanned || !charSequence.hasSpan(CharacterStyle::class.java)) {
+        return charSequence
+    }
+
+    val spans = charSequence.getSpans(0, charSequence.length, CharacterStyle::class.java)
+    if (spans.isNullOrEmpty()) return charSequence
+
+    // Don't allocate a new SpannableString unless we are certain we will be modifying it.
+    var spannableString: SpannableString? = null
+    for (span in spans) {
+        if (span is MetricAffectingSpan) continue
+        if (spannableString == null) spannableString = SpannableString(charSequence)
+        spannableString.removeSpan(span)
+    }
+    return spannableString ?: charSequence
+}
+
+/**
+ * b/173574230 on Android 11 and above, creating a StaticLayout when
+ * - desiredWidth is an Integer,
+ * - letterSpacing is set
+ * - lineHeight is set StaticLayout forms 2 lines for the given desiredWidth.
+ *
+ * This function checks if those conditions are met.
+ */
+private fun shouldIncreaseMaxIntrinsic(
+    desiredWidth: Float,
+    charSequence: CharSequence,
+    textPaint: TextPaint
+): Boolean {
+    return desiredWidth != 0f &&
+        (charSequence is Spanned &&
+            (charSequence.hasSpan(LetterSpacingSpanPx::class.java) ||
+                charSequence.hasSpan(LetterSpacingSpanEm::class.java)) ||
+            textPaint.letterSpacing != 0f)
+}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/ListUtils.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/ListUtils.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/ListUtils.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/ListUtils.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/PaintExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/PaintExtensions.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/PaintExtensions.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/PaintExtensions.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/SpannedExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/SpannedExtensions.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/SpannedExtensions.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/SpannedExtensions.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/StaticLayoutFactory.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/StaticLayoutFactory.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/TextAndroidCanvas.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/TextAndroidCanvas.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/TextAndroidCanvas.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/TextAndroidCanvas.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/TextLayout.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/TextLayout.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/TextLayoutGetRangeForRectExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/TextLayoutGetRangeForRectExtensions.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/TextLayoutGetRangeForRectExtensions.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/TextLayoutGetRangeForRectExtensions.android.kt
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt
new file mode 100644
index 0000000..b32ca07
--- /dev/null
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("PrimitiveInCollection")
+
+package androidx.compose.ui.text.android.animation
+
+import android.text.Layout
+import androidx.compose.ui.text.android.CharSequenceCharacterIterator
+import androidx.compose.ui.text.android.LayoutHelper
+import androidx.compose.ui.text.android.fastForEach
+import androidx.compose.ui.text.android.fastZipWithNext
+import androidx.compose.ui.text.android.getLineForOffset
+import java.text.BreakIterator
+import java.util.Locale
+import java.util.TreeSet
+import kotlin.math.ceil
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * A class represents animation segment.
+ *
+ * @param startOffset an inclusive start character offset of this segment.
+ * @param endOffset an exclusive end character offset of this segment.
+ * @param left a graphical left position from the layout origin.
+ * @param top a graphical top position from the layout origin.
+ * @param right a graphical right position from the layout origin.
+ * @param bottom a graphical bottom position from the layout origin.
+ */
+internal data class Segment(
+    val startOffset: Int,
+    val endOffset: Int,
+    val left: Int,
+    val top: Int,
+    val right: Int,
+    val bottom: Int
+)
+
+/** Provide a segmentation breaker for the text animation. */
+internal object SegmentBreaker {
+    private fun breakInWords(layoutHelper: LayoutHelper): List<Int> {
+        val text = layoutHelper.layout.text
+        val words = breakWithBreakIterator(text, BreakIterator.getLineInstance(Locale.getDefault()))
+
+        val set = TreeSet<Int>().apply { words.fastForEach { add(it) } }
+
+        for (paraIndex in 0 until layoutHelper.paragraphCount) {
+            val bidi = layoutHelper.analyzeBidi(paraIndex) ?: continue
+            val paragraphStart = layoutHelper.getParagraphStart(paraIndex)
+            for (i in 0 until bidi.runCount) {
+                set.add(bidi.getRunStart(i) + paragraphStart)
+            }
+        }
+        return set.toList()
+    }
+
+    private fun breakWithBreakIterator(text: CharSequence, breaker: BreakIterator): List<Int> {
+        val iter = CharSequenceCharacterIterator(text, 0, text.length)
+
+        val res = mutableListOf(0)
+        breaker.text = iter
+        while (breaker.next() != BreakIterator.DONE) {
+            res.add(breaker.current())
+        }
+        return res
+    }
+
+    /**
+     * Gets all offsets of the given segment type for animation.
+     *
+     * @param layoutHelper a layout helper
+     * @param segmentType a segmentation type
+     * @return all break offsets of the given segmentation type including 0 and text length.
+     */
+    fun breakOffsets(layoutHelper: LayoutHelper, segmentType: SegmentType): List<Int> {
+        val layout = layoutHelper.layout
+        val text = layout.text
+
+        return when (segmentType) {
+            SegmentType.Document -> listOf(0, text.length)
+            SegmentType.Paragraph -> {
+                mutableListOf(0).also {
+                    for (i in 0 until layoutHelper.paragraphCount) {
+                        it.add(layoutHelper.getParagraphEnd(i))
+                    }
+                }
+            }
+            SegmentType.Line -> {
+                mutableListOf(0).also {
+                    for (i in 0 until layout.lineCount) {
+                        it.add(layout.getLineEnd(i))
+                    }
+                }
+            }
+            SegmentType.Word -> breakInWords(layoutHelper)
+            SegmentType.Character ->
+                breakWithBreakIterator(
+                    text,
+                    BreakIterator.getCharacterInstance(Locale.getDefault())
+                )
+        }
+    }
+
+    /**
+     * Break Layout into list of segments.
+     *
+     * A segment represents a unit of text animation. For example, if you specify, SegmentType
+     * .Line, this function will give you a list of Line segments which have line start offset and
+     * line end offset, and also line bounding box.
+     *
+     * The dropSpaces argument is ignored if segmentType is Document or Paragraph.
+     *
+     * If segmentType is Line and dropSpaces is true, this removes trailing spaces. If segmentType
+     * is Line and dropSpace is false, this use layout width as the right position of the line.
+     *
+     * If segmentType is Word and dropSpaces is true, this removes trailing spaces if there. If
+     * segmentType is Word and dropSpace is false, this includes the trailing whitespace into
+     * segment.
+     *
+     * If segmentType is Character and dropSpace is true, this drops whitespace only segment. If
+     * segmentType is Character and dropSpace is true, this include whitespace only segment.
+     *
+     * @param layoutHelper a layout helper
+     * @param segmentType a segmentation type
+     * @param dropSpaces whether dropping spacing. See function comment for more details.
+     * @return list of segment object
+     */
+    fun breakSegments(
+        layoutHelper: LayoutHelper,
+        segmentType: SegmentType,
+        dropSpaces: Boolean
+    ): List<Segment> {
+        return when (segmentType) {
+            SegmentType.Document -> breakSegmentWithDocument(layoutHelper)
+            SegmentType.Paragraph -> breakSegmentWithParagraph(layoutHelper)
+            SegmentType.Line -> breakSegmentWithLine(layoutHelper, dropSpaces)
+            SegmentType.Word -> breakSegmentWithWord(layoutHelper, dropSpaces)
+            SegmentType.Character -> breakSegmentWithChar(layoutHelper, dropSpaces)
+        }
+    }
+
+    private fun breakSegmentWithDocument(layoutHelper: LayoutHelper): List<Segment> {
+        return listOf(
+            Segment(
+                startOffset = 0,
+                endOffset = layoutHelper.layout.text.length,
+                left = 0,
+                top = 0,
+                right = layoutHelper.layout.width,
+                bottom = layoutHelper.layout.height
+            )
+        )
+    }
+
+    private fun breakSegmentWithParagraph(layoutHelper: LayoutHelper): List<Segment> {
+        val result = mutableListOf<Segment>()
+        val layout = layoutHelper.layout
+        for (i in 0 until layoutHelper.paragraphCount) {
+            val paraStart = layoutHelper.getParagraphStart(i)
+            val paraEnd = layoutHelper.getParagraphEnd(i)
+            val paraFirstLine = layout.getLineForOffset(paraStart, false /* downstream */)
+            val paraLastLine = layout.getLineForOffset(paraEnd, true /* upstream */)
+            result.add(
+                Segment(
+                    startOffset = paraStart,
+                    endOffset = paraEnd,
+                    left = 0,
+                    top = layout.getLineTop(paraFirstLine),
+                    right = layout.width,
+                    bottom = layout.getLineBottom(paraLastLine)
+                )
+            )
+        }
+        return result
+    }
+
+    private fun breakSegmentWithLine(
+        layoutHelper: LayoutHelper,
+        dropSpaces: Boolean
+    ): List<Segment> {
+        val result = mutableListOf<Segment>()
+        val layout = layoutHelper.layout
+        for (i in 0 until layoutHelper.layout.lineCount) {
+            result.add(
+                Segment(
+                    startOffset = layout.getLineStart(i),
+                    endOffset = layout.getLineEnd(i),
+                    left = if (dropSpaces) ceil(layout.getLineLeft(i)).toInt() else 0,
+                    top = layout.getLineTop(i),
+                    right = if (dropSpaces) ceil(layout.getLineRight(i)).toInt() else layout.width,
+                    bottom = layout.getLineBottom(i)
+                )
+            )
+        }
+        return result
+    }
+
+    private fun breakSegmentWithWord(
+        layoutHelper: LayoutHelper,
+        dropSpaces: Boolean
+    ): List<Segment> {
+        val layout = layoutHelper.layout
+        val wsWidth = ceil(layout.paint.measureText(" ")).toInt()
+        return breakOffsets(layoutHelper, SegmentType.Word).fastZipWithNext { start, end ->
+            val lineNo = layout.getLineForOffset(start, false /* downstream */)
+            val paraRTL = layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT
+            val runRtl = layout.isRtlCharAt(start) // no bidi transition inside segment
+            val startPos =
+                ceil(
+                        layoutHelper.getHorizontalPosition(
+                            offset = start,
+                            usePrimaryDirection = runRtl == paraRTL,
+                            upstream = false
+                        )
+                    )
+                    .toInt()
+            val endPos =
+                ceil(
+                        layoutHelper.getHorizontalPosition(
+                            offset = end,
+                            usePrimaryDirection = runRtl == paraRTL,
+                            upstream = true
+                        )
+                    )
+                    .toInt()
+
+            // Drop trailing space is the line does not end with this word.
+            var left = min(startPos, endPos)
+            var right = max(startPos, endPos)
+            if (dropSpaces && end != 0 && layout.text[end - 1] == ' ') {
+                val lineEnd = layout.getLineEnd(lineNo)
+                if (lineEnd != end) {
+                    if (runRtl) {
+                        left += wsWidth
+                    } else {
+                        right -= wsWidth
+                    }
+                }
+            }
+
+            Segment(
+                startOffset = start,
+                endOffset = end,
+                left = left,
+                top = layout.getLineTop(lineNo),
+                right = right,
+                bottom = layout.getLineBottom(lineNo)
+            )
+        }
+    }
+
+    private fun breakSegmentWithChar(
+        layoutHelper: LayoutHelper,
+        dropSpaces: Boolean
+    ): List<Segment> {
+        val res = mutableListOf<Segment>()
+        breakOffsets(layoutHelper, SegmentType.Character).fastZipWithNext lambda@{ start, end ->
+            val layout = layoutHelper.layout
+
+            if (dropSpaces && end == start + 1 && layoutHelper.isLineEndSpace(layout.text[start]))
+                return@lambda
+            val lineNo = layout.getLineForOffset(start, false /* downstream */)
+            val paraRTL = layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT
+            val runRtl = layout.isRtlCharAt(start) // no bidi transition inside segment
+            val startPos =
+                ceil(
+                        layoutHelper.getHorizontalPosition(
+                            offset = start,
+                            usePrimaryDirection = runRtl == paraRTL,
+                            upstream = false
+                        )
+                    )
+                    .toInt()
+            val endPos =
+                ceil(
+                        layoutHelper.getHorizontalPosition(
+                            offset = end,
+                            usePrimaryDirection = runRtl == paraRTL,
+                            upstream = true
+                        )
+                    )
+                    .toInt()
+            res.add(
+                Segment(
+                    startOffset = start,
+                    endOffset = end,
+                    left = min(startPos, endPos),
+                    top = layout.getLineTop(lineNo),
+                    right = max(startPos, endPos),
+                    bottom = layout.getLineBottom(lineNo)
+                )
+            )
+        }
+        return res
+    }
+}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentType.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/animation/SegmentType.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentType.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/animation/SegmentType.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/selection/SegmentFinder.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/selection/SegmentFinder.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/selection/SegmentFinder.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/selection/SegmentFinder.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/selection/WordBoundary.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/selection/WordBoundary.android.kt
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/selection/WordIterator.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/selection/WordIterator.android.kt
new file mode 100644
index 0000000..0d43401
--- /dev/null
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/selection/WordIterator.android.kt
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.ui.text.android.selection
+
+import androidx.compose.ui.text.android.CharSequenceCharacterIterator
+import androidx.emoji2.text.EmojiCompat
+import java.text.BreakIterator
+import java.util.Locale
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Walks through cursor positions at word boundaries.
+ *
+ * Also provides methods to determine word boundaries.
+ *
+ * Note: This file is copied from
+ * [WordIterator.java](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/method/WordIterator.java)
+ *
+ * @param locale The locale to be used for analyzing the text. Caches [CharSequence] for performance
+ *   reasons.
+ * @constructor Constructs a new WordIterator for the specified locale.
+ */
+internal class WordIterator(val charSequence: CharSequence, start: Int, end: Int, locale: Locale?) {
+    private val start: Int
+    private val end: Int
+    private val iterator: BreakIterator
+
+    init {
+        require(start in 0..charSequence.length) { "input start index is outside the CharSequence" }
+        require(end in 0..charSequence.length) { "input end index is outside the CharSequence" }
+        iterator = BreakIterator.getWordInstance(locale)
+        this.start = max(0, start - WINDOW_WIDTH)
+        this.end = min(charSequence.length, end + WINDOW_WIDTH)
+        iterator.text = CharSequenceCharacterIterator(charSequence, start, end)
+    }
+
+    /**
+     * Returns the position of next boundary after the given offset. Returns `BreakIterator.DONE` if
+     * there is no boundary after the given offset.
+     *
+     * @param offset the given start position to search from.
+     * @return the position of the last boundary preceding the given offset.
+     */
+    fun nextBoundary(offset: Int): Int {
+        checkOffsetIsValid(offset)
+        val following = iterator.following(offset)
+        // We should iterate through if the boundary is between a letter/digit and emoji
+        // These should not be considered boundaries
+        if (
+            isOnLetterOrDigitOrEmoji(following - 1) &&
+                isOnLetterOrDigitOrEmoji(following) &&
+                // Extra logic for Japanese to detect boundary between Hiragana and Katakana
+                // characters
+                !isHiraganaKatakanaBoundary(following)
+        ) {
+            return nextBoundary(following)
+        }
+        return following
+    }
+
+    /**
+     * Returns the position of boundary preceding the given offset or `BreakIterator.DONE` if the
+     * given offset specifies the starting position.
+     *
+     * @param offset the given start position to search from.
+     * @return the position of the last boundary preceding the given offset.
+     */
+    fun prevBoundary(offset: Int): Int {
+        checkOffsetIsValid(offset)
+        val preceding = iterator.preceding(offset)
+        // We should iterate through if the boundary is between a letter/digit and emoji
+        // These should not be considered boundaries
+        return if (
+            isOnLetterOrDigitOrEmoji(preceding) &&
+                isAfterLetterOrDigitOrEmoji(preceding) &&
+                // Extra logic for Japanese to detect boundary between Hiragana and Katakana
+                // characters
+                !isHiraganaKatakanaBoundary(preceding)
+        ) {
+            prevBoundary(preceding)
+        } else preceding
+    }
+
+    /**
+     * If the `offset` is within a word or on a word boundary that can only be considered the start
+     * of a word (e.g. _word where "_" is any character that would not be considered part of the
+     * word) then this returns the index of the first character of that word.
+     *
+     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
+     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, this
+     * would return the start of the previous word, AA.
+     *
+     * Returns BreakIterator.DONE if there is no previous boundary.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    fun getPrevWordBeginningOnTwoWordsBoundary(offset: Int): Int {
+        return getBeginning(offset, true)
+    }
+
+    /**
+     * If the `offset` is within a word or on a word boundary that can only be considered the end of
+     * a word (e.g. word_ where "_" is any character that would not be considered part of the word)
+     * then this returns the index of the last character plus one of that word.
+     *
+     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
+     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, this
+     * would return the end of the next word, BB.
+     *
+     * Returns BreakIterator.DONE if there is no next boundary.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    fun getNextWordEndOnTwoWordBoundary(offset: Int): Int {
+        return getEnd(offset, true)
+    }
+
+    /**
+     * If `offset` is within a group of punctuation as defined by [isPunctuation], returns the index
+     * of the first character of that group, otherwise returns BreakIterator.DONE.
+     *
+     * @param offset the offset to search from.
+     */
+    fun getPunctuationBeginning(offset: Int): Int {
+        checkOffsetIsValid(offset)
+        var result = offset
+        while (result != BreakIterator.DONE && !isPunctuationStartBoundary(result)) {
+            result = prevBoundary(result)
+        }
+        // No need to shift offset, prevBoundary handles that.
+        return result
+    }
+
+    /**
+     * If `offset` is within a group of punctuation as defined by [isPunctuation], returns the index
+     * of the last character of that group plus one, otherwise returns BreakIterator.DONE.
+     *
+     * @param offset the offset to search from.
+     */
+    fun getPunctuationEnd(offset: Int): Int {
+        checkOffsetIsValid(offset)
+        var result = offset
+        while (result != BreakIterator.DONE && !isPunctuationEndBoundary(result)) {
+            result = nextBoundary(result)
+        }
+        // No need to shift offset, nextBoundary handles that.
+        return result
+    }
+
+    /**
+     * Indicates if the provided offset is after a punctuation character as defined by
+     * [isPunctuation].
+     *
+     * @param offset the offset to check from.
+     * @return Whether the offset is after a punctuation character.
+     */
+    fun isAfterPunctuation(offset: Int): Boolean {
+        if (offset in (start + 1)..end) {
+            val codePoint = Character.codePointBefore(charSequence, offset)
+            return isPunctuation(codePoint)
+        }
+        return false
+    }
+
+    /**
+     * Indicates if the provided offset is at a punctuation character as defined by [isPunctuation].
+     *
+     * @param offset the offset to check from.
+     * @return Whether the offset is at a punctuation character.
+     */
+    fun isOnPunctuation(offset: Int): Boolean {
+        if (offset in start until end) {
+            val codePoint = Character.codePointAt(charSequence, offset)
+            return isPunctuation(codePoint)
+        }
+        return false
+    }
+
+    /**
+     * If the `offset` is within a word or on a word boundary that can only be considered the start
+     * of a word (e.g. _word where "_" is any character that would not be considered part of the
+     * word) then this returns the index of the first character of that word.
+     *
+     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
+     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, and
+     * getPrevWordBeginningOnTwoWordsBoundary is true then this would return the start of the
+     * previous word, AA. Otherwise it would return the current offset, the start of BB.
+     *
+     * Returns BreakIterator.DONE if there is no previous boundary.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    private fun getBeginning(offset: Int, getPrevWordBeginningOnTwoWordsBoundary: Boolean): Int {
+        checkOffsetIsValid(offset)
+        if (isOnLetterOrDigitOrEmoji(offset)) {
+            return if (
+                isBoundary(offset) &&
+                    (!isAfterLetterOrDigitOrEmoji(offset) ||
+                        !getPrevWordBeginningOnTwoWordsBoundary)
+            ) {
+                offset
+            } else {
+                prevBoundary(offset)
+            }
+        } else {
+            if (isAfterLetterOrDigitOrEmoji(offset)) {
+                return prevBoundary(offset)
+            }
+        }
+        return BreakIterator.DONE
+    }
+
+    /**
+     * If the `offset` is within a word or on a word boundary that can only be considered the end of
+     * a word (e.g. word_ where "_" is any character that would not be considered part of the word)
+     * then this returns the index of the last character plus one of that word.
+     *
+     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
+     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, and
+     * getNextWordEndOnTwoWordBoundary is true then this would return the end of the next word, BB.
+     * Otherwise it would return the current offset, the end of AA.
+     *
+     * Returns BreakIterator.DONE if there is no next boundary.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    private fun getEnd(offset: Int, getNextWordEndOnTwoWordBoundary: Boolean): Int {
+        checkOffsetIsValid(offset)
+        if (isAfterLetterOrDigitOrEmoji(offset)) {
+            return if (
+                isBoundary(offset) &&
+                    (!isOnLetterOrDigitOrEmoji(offset) || !getNextWordEndOnTwoWordBoundary)
+            ) {
+                offset
+            } else {
+                nextBoundary(offset)
+            }
+        } else {
+            if (isOnLetterOrDigitOrEmoji(offset)) {
+                return nextBoundary(offset)
+            }
+        }
+        return BreakIterator.DONE
+    }
+
+    private fun isPunctuationStartBoundary(offset: Int): Boolean {
+        return isOnPunctuation(offset) && !isAfterPunctuation(offset)
+    }
+
+    private fun isPunctuationEndBoundary(offset: Int): Boolean {
+        return !isOnPunctuation(offset) && isAfterPunctuation(offset)
+    }
+
+    /**
+     * Check if offset before current offset points to letter or digit. Emoji and surrogate
+     * codepoints are treated as letters or digits.
+     *
+     * Note: if EmojiCompat is not initialized, emojis are not treated like letters/digits
+     */
+    private fun isAfterLetterOrDigitOrEmoji(offset: Int): Boolean {
+        if (offset in (start + 1)..end) {
+            val codePoint = Character.codePointBefore(charSequence, offset)
+            if (Character.isLetterOrDigit(codePoint)) return true
+            if (Character.isSurrogate(charSequence[offset - 1])) return true
+
+            if (EmojiCompat.isConfigured()) {
+                val emojiCompat = EmojiCompat.get()
+                if (emojiCompat.loadState == EmojiCompat.LOAD_STATE_SUCCEEDED) {
+                    val emojiStart = emojiCompat.getEmojiStart(charSequence, offset - 1)
+                    // If given offset points to emoji return true
+                    if (emojiStart != -1) return true
+                }
+            }
+        }
+        return false
+    }
+
+    /**
+     * Check if current offset points to letter or digit. Emoji and surrogate codepoints are treated
+     * as letters or digits.
+     *
+     * Note: if EmojiCompat is not initialized, emojis are not treated like letters/digits
+     */
+    private fun isOnLetterOrDigitOrEmoji(offset: Int): Boolean {
+        if (offset in start until end) {
+            val codePoint = Character.codePointAt(charSequence, offset)
+            if (Character.isLetterOrDigit(codePoint)) return true
+            if (Character.isSurrogate(charSequence[offset])) return true
+
+            if (EmojiCompat.isConfigured()) {
+                val emojiCompat = EmojiCompat.get()
+                if (emojiCompat.loadState == EmojiCompat.LOAD_STATE_SUCCEEDED) {
+                    val emojiStart = emojiCompat.getEmojiStart(charSequence, offset)
+                    // If given offset points to emoji return true
+                    if (emojiStart != -1) return true
+                }
+            }
+        }
+        return false
+    }
+
+    /** Check if the given offset is in the given range. */
+    private fun checkOffsetIsValid(offset: Int) {
+        require(offset in start..end) {
+            ("Invalid offset: $offset. Valid range is [$start , $end]")
+        }
+    }
+
+    /**
+     * Modified implementation of `iterator.isBoundary` that additionally checks boundary between
+     * letters/digits and emojis as these should not be treated as boundaries.
+     */
+    private fun isBoundary(offset: Int): Boolean {
+        checkOffsetIsValid(offset)
+        return iterator.isBoundary(offset) &&
+            // check offset and two characters before and after to see if all three characters
+            // are letters/digits/emojis
+            !(isOnLetterOrDigitOrEmoji(offset) &&
+                isOnLetterOrDigitOrEmoji(offset - 1) &&
+                isOnLetterOrDigitOrEmoji(offset + 1)) &&
+            // check if there is boundary between hiragana and katakana characters
+            // indexes 0 and charSequence.length - 1 should always be considered boundaries
+            !(offset > 0 &&
+                offset < charSequence.length - 1 &&
+                (isHiraganaKatakanaBoundary(offset) || isHiraganaKatakanaBoundary(offset + 1)))
+    }
+
+    /** Checks if characters before and at `offset` are either hiragana or katakana */
+    private fun isHiraganaKatakanaBoundary(offset: Int): Boolean {
+        return ((Character.UnicodeBlock.of(charSequence[offset - 1]) ==
+            Character.UnicodeBlock.HIRAGANA &&
+            Character.UnicodeBlock.of(charSequence[offset]) == Character.UnicodeBlock.KATAKANA) ||
+            (Character.UnicodeBlock.of(charSequence[offset]) == Character.UnicodeBlock.HIRAGANA &&
+                Character.UnicodeBlock.of(charSequence[offset - 1]) ==
+                    Character.UnicodeBlock.KATAKANA))
+    }
+
+    companion object {
+        // The size of the WINDOW_WIDTH is currently 50, as in Android.
+        // According to Wikipedia https://en.wikipedia.org/wiki/Longest_word_in_English , the
+        // longest English word in English contains 45 letters. Then 50 is a good number for
+        // WINDOW_WIDTH. Size of the window for the word iterator, should be greater than the
+        // longest word's length.
+        private const val WINDOW_WIDTH = 50
+
+        internal fun isPunctuation(cp: Int): Boolean {
+            val type = Character.getType(cp)
+            return type == Character.CONNECTOR_PUNCTUATION.toInt() ||
+                type == Character.DASH_PUNCTUATION.toInt() ||
+                type == Character.END_PUNCTUATION.toInt() ||
+                type == Character.FINAL_QUOTE_PUNCTUATION.toInt() ||
+                type == Character.INITIAL_QUOTE_PUNCTUATION.toInt() ||
+                type == Character.OTHER_PUNCTUATION.toInt() ||
+                type == Character.START_PUNCTUATION.toInt()
+        }
+    }
+}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/BaselineShiftSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/BaselineShiftSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/FontFeatureSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/FontFeatureSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/IndentationFixSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/IndentationFixSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/IndentationFixSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/IndentationFixSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LineHeightSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LineHeightSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightStyleSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LineHeightStyleSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightStyleSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/LineHeightStyleSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/PlaceholderSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/PlaceholderSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/ShadowSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/ShadowSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/SkewXSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/SkewXSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/TextDecorationSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/TextDecorationSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/TextDecorationSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/TextDecorationSpan.android.kt
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/TypefaceSpan.android.kt
similarity index 100%
rename from text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.android.kt
rename to compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/android/style/TypefaceSpan.android.kt
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontResolveInterceptor.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontResolveInterceptor.android.kt
index 4dfb192..db71d6d 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontResolveInterceptor.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontResolveInterceptor.android.kt
@@ -20,6 +20,8 @@
 import android.content.res.Configuration
 import android.graphics.fonts.FontStyle
 import android.os.Build
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
 import androidx.compose.ui.unit.Density
 
 /**
@@ -52,11 +54,23 @@
 
 /** A helper function to create an interceptor using a context. */
 internal fun AndroidFontResolveInterceptor(context: Context): AndroidFontResolveInterceptor {
-    val fontWeightAdjustment =
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
-            context.resources.configuration.fontWeightAdjustment
-        } else {
-            0
-        }
+    val fontWeightAdjustment = FontWeightAdjustmentHelper.getFontWeightAdjustment(context)
     return AndroidFontResolveInterceptor(fontWeightAdjustment)
 }
+
+internal object FontWeightAdjustmentHelper {
+    fun getFontWeightAdjustment(context: Context): Int =
+        when {
+            Build.VERSION.SDK_INT >= 31 ->
+                FontWeightAdjustmentHelperApi31.fontWeightAdjustment(context)
+            else -> 0
+        }
+}
+
+@RequiresApi(31)
+internal object FontWeightAdjustmentHelperApi31 {
+    @DoNotInline
+    @RequiresApi(31)
+    fun fontWeightAdjustment(context: Context): Int =
+        context.resources.configuration.fontWeightAdjustment
+}
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontUtils.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontUtils.android.kt
index bd5ad88..7713ec8 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontUtils.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontUtils.android.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 internal val FontWeight.Companion.AndroidBold
@@ -67,7 +66,6 @@
 @RequiresApi(28)
 internal object TypefaceHelperMethodsApi28 {
     @RequiresApi(28)
-    @DoNotInline
     fun create(typeface: Typeface, finalFontWeight: Int, finalFontStyle: Boolean) =
         Typeface.create(typeface, finalFontWeight, finalFontStyle)
 }
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.android.kt
index 44118eb..8d50ab4 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.android.kt
@@ -22,7 +22,6 @@
 import android.graphics.fonts.FontVariationAxis
 import android.os.Build
 import android.os.ParcelFileDescriptor
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.unit.Density
@@ -175,7 +174,6 @@
 @RequiresApi(api = 26)
 private object TypefaceBuilderCompat {
     @ExperimentalTextApi
-    @DoNotInline
     fun createFromAssets(
         assetManager: AssetManager,
         path: String,
@@ -191,7 +189,6 @@
     }
 
     @ExperimentalTextApi
-    @DoNotInline
     fun createFromFile(
         file: File,
         context: Context?,
@@ -206,7 +203,6 @@
     }
 
     @ExperimentalTextApi
-    @DoNotInline
     fun createFromFileDescriptor(
         fileDescriptor: ParcelFileDescriptor,
         context: Context?,
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableString.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableString.android.kt
index f89514a..c3783b0 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableString.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableString.android.kt
@@ -25,7 +25,6 @@
 import android.text.style.StyleSpan
 import android.text.style.TypefaceSpan
 import android.text.style.UnderlineSpan
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.compose.ui.text.AnnotatedString
@@ -189,7 +188,7 @@
 
 @RequiresApi(28)
 private object Api28Impl {
-    @DoNotInline fun createTypefaceSpan(typeface: Typeface): TypefaceSpan = TypefaceSpan(typeface)
+    fun createTypefaceSpan(typeface: Typeface): TypefaceSpan = TypefaceSpan(typeface)
 }
 
 private fun AnnotatedString.Range<LinkAnnotation>.toUrlLink() =
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt
index 78e1d7b1..aaa9578 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt
@@ -20,11 +20,8 @@
 import android.graphics.Typeface
 import android.os.Build
 import android.util.TypedValue
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
-import androidx.collection.LruCache
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.android.InternalPlatformTextApi
+import androidx.collection.SieveCache
 import androidx.compose.ui.text.font.AndroidFont
 import androidx.compose.ui.text.font.AndroidPreloadedFont
 import androidx.compose.ui.text.font.Font
@@ -111,18 +108,17 @@
 internal object AndroidTypefaceCache {
 
     // TODO multiple TypefaceCache's, would be good to unify
-    private val cache = LruCache<String, Typeface>(16)
+    private val cache = SieveCache<String, Typeface>(16, 16)
 
     /**
      * Returns NativeTypeface for [font] if it is in cache. Otherwise create new NativeTypeface and
      * put it into internal cache.
      */
-    @OptIn(InternalPlatformTextApi::class, ExperimentalTextApi::class)
     fun getOrCreate(context: Context, font: Font): Typeface {
         val key = getKey(context, font)
 
         key?.let {
-            cache.get(key)?.let {
+            cache[key]?.let {
                 return it
             }
         }
@@ -140,13 +136,13 @@
                 else -> throw IllegalArgumentException("Unknown font type: $font")
             } ?: throw IllegalArgumentException("Unable to load font $font")
 
-        key?.let { cache.put(key, typeface) }
+        key?.let { cache[key] = typeface }
 
         return typeface
     }
 
     /** Utility method to generate a key for caching purposes. */
-    fun getKey(context: Context, font: Font): String? {
+    private fun getKey(context: Context, font: Font): String? {
         return when (font) {
             is ResourceFont -> {
                 val value = TypedValue()
@@ -168,7 +164,6 @@
 @Deprecated("Only used by deprecated APIs in this file, remove with them.")
 private object AndroidResourceFontLoaderHelper {
     @RequiresApi(26)
-    @DoNotInline
     fun create(context: Context, resourceId: Int): Typeface {
         return context.resources.getFont(resourceId)
     }
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/LocaleExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/LocaleExtensions.android.kt
index fe326c3..c3acecd 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/LocaleExtensions.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/LocaleExtensions.android.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.platform.extensions
 
 import android.text.style.LocaleSpan
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.text.intl.LocaleList
 import androidx.compose.ui.text.platform.AndroidTextPaint
@@ -30,12 +29,10 @@
 @RequiresApi(24)
 internal object LocaleListHelperMethods {
     @RequiresApi(24)
-    @DoNotInline
     fun localeSpan(localeList: LocaleList): Any =
         LocaleSpan(android.os.LocaleList(*localeList.map { it.platformLocale }.toTypedArray()))
 
     @RequiresApi(24)
-    @DoNotInline
     fun setTextLocales(textPaint: AndroidTextPaint, localeList: LocaleList) {
         textPaint.textLocales =
             android.os.LocaleList(*localeList.map { it.platformLocale }.toTypedArray())
diff --git a/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/AndroidSaversTest.kt b/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/AndroidSaversTest.kt
deleted file mode 100644
index b05d927..0000000
--- a/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/AndroidSaversTest.kt
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text
-
-import androidx.compose.runtime.saveable.SaverScope
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shadow
-import androidx.compose.ui.text.font.FontStyle
-import androidx.compose.ui.text.font.FontSynthesis
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.intl.Locale
-import androidx.compose.ui.text.intl.LocaleList
-import androidx.compose.ui.text.style.BaselineShift
-import androidx.compose.ui.text.style.LineHeightStyle
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.style.TextDecoration
-import androidx.compose.ui.text.style.TextDirection
-import androidx.compose.ui.text.style.TextGeometricTransform
-import androidx.compose.ui.text.style.TextIndent
-import androidx.compose.ui.unit.TextUnit
-import androidx.compose.ui.unit.em
-import androidx.compose.ui.unit.sp
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-@Suppress("Deprecation")
-class AndroidSaversTest {
-    private val defaultSaverScope = SaverScope { true }
-
-    @Test
-    fun test_TextUnit() {
-        val original = 2.sp
-        val saved = save(original, TextUnit.Saver, defaultSaverScope)
-        val restored: TextUnit? = restore(saved, TextUnit.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextUnit_unspecified() {
-        val original = TextUnit.Unspecified
-        val saved = save(original, TextUnit.Saver, defaultSaverScope)
-        val restored: TextUnit? = restore(saved, TextUnit.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Offset() {
-        val original = Offset(10f, 10f)
-        val saved = save(original, Offset.Saver, defaultSaverScope)
-        val restored: Offset? = restore(saved, Offset.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Offset_Unspecified() {
-        val original = Offset.Unspecified
-        val saved = save(original, Offset.Saver, defaultSaverScope)
-        val restored: Offset? = restore(saved, Offset.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Offset_Infinite() {
-        val original = Offset.Infinite
-        val saved = save(original, Offset.Saver, defaultSaverScope)
-        val restored: Offset? = restore(saved, Offset.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Color() {
-        val original = Color.Yellow
-        val saved = save(original, Color.Saver, defaultSaverScope)
-        val restored: Color? = restore(saved, Color.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Color_Unspecified() {
-        val original = Color.Unspecified
-        val saved = save(original, Color.Saver, defaultSaverScope)
-        val restored: Color? = restore(saved, Color.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Shadow() {
-        val original = Shadow(color = Color.Blue, offset = Offset(5f, 5f), blurRadius = 2f)
-        val saved = save(original, Shadow.Saver, defaultSaverScope)
-        val restored: Shadow? = restore(saved, Shadow.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Shadow_None() {
-        val original = Shadow.None
-        val saved = save(original, Shadow.Saver, defaultSaverScope)
-        val restored: Shadow? = restore(saved, Shadow.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_ParagraphStyle() {
-        val original = ParagraphStyle()
-        val saved = save(original, ParagraphStyleSaver, defaultSaverScope)
-        val restored: ParagraphStyle? = restore(saved, ParagraphStyleSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_ParagraphStyle_with_a_nonnull_value() {
-        val original = ParagraphStyle(textDirection = TextDirection.Rtl)
-        val saved = save(original, ParagraphStyleSaver, defaultSaverScope)
-        val restored: ParagraphStyle? = restore(saved, ParagraphStyleSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_SpanStyle() {
-        val original = SpanStyle()
-        val saved = save(original, SpanStyleSaver, defaultSaverScope)
-        val restored: SpanStyle? = restore(saved, SpanStyleSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_SpanStyle_with_a_nonnull_value() {
-        val original = SpanStyle(baselineShift = BaselineShift.Subscript)
-        val saved = save(original, SpanStyleSaver, defaultSaverScope)
-        val restored: SpanStyle? = restore(saved, SpanStyleSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_SpanStyle_with_no_null_value() {
-        val original =
-            SpanStyle(
-                color = Color.Red,
-                fontSize = 10.sp,
-                fontWeight = FontWeight.Bold,
-                fontStyle = FontStyle.Italic,
-                fontSynthesis = FontSynthesis.All,
-                // fontFamily =
-                fontFeatureSettings = "feature settings",
-                letterSpacing = 2.em,
-                baselineShift = BaselineShift.Superscript,
-                textGeometricTransform = TextGeometricTransform(2f, 3f),
-                localeList = LocaleList(Locale("sr-Latn-SR"), Locale("sr-Cyrl-SR"), Locale.current),
-                background = Color.Blue,
-                textDecoration = TextDecoration.LineThrough,
-                shadow = Shadow(color = Color.Red, offset = Offset(2f, 2f), blurRadius = 4f)
-            )
-        val saved = save(original, SpanStyleSaver, defaultSaverScope)
-        val restored: SpanStyle? = restore(saved, SpanStyleSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextLinkStyles() {
-        val original = TextLinkStyles(null)
-        val saved = save(original, TextLinkStylesSaver, defaultSaverScope)
-        val restored: TextLinkStyles? = restore(saved, TextLinkStylesSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextLinkStyles_withNonNullValues() {
-        val original =
-            TextLinkStyles(
-                SpanStyle(color = Color.Red),
-                SpanStyle(color = Color.Green),
-                SpanStyle(color = Color.Blue),
-                SpanStyle(color = Color.Gray)
-            )
-        val saved = save(original, TextLinkStylesSaver, defaultSaverScope)
-        val restored: TextLinkStyles? = restore(saved, TextLinkStylesSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_FontWeight() {
-        val original = FontWeight(123)
-        val saved = save(original, FontWeight.Saver, defaultSaverScope)
-        val restored: FontWeight? = restore(saved, FontWeight.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_FontWeight_w100() {
-        val original = FontWeight.W100
-        val saved = save(original, FontWeight.Saver, defaultSaverScope)
-        val restored: FontWeight? = restore(saved, FontWeight.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_BaselineShift() {
-        val original = BaselineShift(2f)
-        val saved = save(original, BaselineShift.Saver, defaultSaverScope)
-        val restored: BaselineShift? = restore(saved, BaselineShift.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_BaselineShift_None() {
-        val original = BaselineShift.None
-        val saved = save(original, BaselineShift.Saver, defaultSaverScope)
-        val restored: BaselineShift? = restore(saved, BaselineShift.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextDecoration() {
-        val original =
-            TextDecoration.combine(listOf(TextDecoration.LineThrough, TextDecoration.Underline))
-        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
-        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextDecoration_None() {
-        val original = TextDecoration.None
-        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
-        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun testSaveRestore_lineThrough() {
-        val original = TextDecoration.LineThrough
-        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
-        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun testSaveRestore_underline() {
-        val original = TextDecoration.Underline
-        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
-        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextGeometricTransform() {
-        val original = TextGeometricTransform(1f, 2f)
-        val saved = save(original, TextGeometricTransform.Saver, defaultSaverScope)
-        val restored: TextGeometricTransform? = restore(saved, TextGeometricTransform.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextGeometricTransform_None() {
-        val original = TextGeometricTransform.None
-        val saved = save(original, TextGeometricTransform.Saver, defaultSaverScope)
-        val restored: TextGeometricTransform? = restore(saved, TextGeometricTransform.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextIndent() {
-        val original = TextIndent(1.sp, 2.sp)
-        val saved = save(original, TextIndent.Saver, defaultSaverScope)
-        val restored: TextIndent? = restore(saved, TextIndent.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextIndent_None() {
-        val original = TextIndent.None
-        val saved = save(original, TextIndent.Saver, defaultSaverScope)
-        val restored: TextIndent? = restore(saved, TextIndent.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_AnnotatedString() {
-        val original = AnnotatedString("abc")
-        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
-
-        assertThat(AnnotatedStringSaver.restore(saved!!)).isEqualTo(original)
-    }
-
-    @Test
-    fun test_AnnotatedString_withSpanStyles() {
-        val original = buildAnnotatedString {
-            withStyle(SpanStyle(color = Color.Red)) { append("1") }
-            withStyle(SpanStyle(fontStyle = FontStyle.Italic)) { append("2") }
-        }
-
-        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
-
-        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_AnnotatedString_withParagraphStyles() {
-        val original = buildAnnotatedString {
-            withStyle(ParagraphStyle(textAlign = TextAlign.Justify)) { append("1") }
-            withStyle(ParagraphStyle(textDirection = TextDirection.Rtl)) { append("2") }
-        }
-
-        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
-
-        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    fun test_AnnotatedString_withAnnotations() {
-        val original = buildAnnotatedString {
-            withAnnotation(tag = "Tag1", annotation = "Annotation1") { append("1") }
-            withAnnotation(VerbatimTtsAnnotation("verbatim1")) { append("2") }
-            withAnnotation(tag = "Tag2", annotation = "Annotation2") { append("3") }
-            withAnnotation(VerbatimTtsAnnotation("verbatim2")) { append("4") }
-            withAnnotation(UrlAnnotation("url1")) { append("5") }
-            withAnnotation(UrlAnnotation("url2")) { append("6") }
-            withLink(
-                LinkAnnotation.Url(
-                    "url3",
-                    TextLinkStyles(
-                        SpanStyle(color = Color.Red),
-                        SpanStyle(color = Color.Green),
-                        SpanStyle(color = Color.Blue),
-                        SpanStyle(color = Color.White)
-                    )
-                )
-            ) {
-                append("7")
-            }
-            withLink(
-                LinkAnnotation.Clickable(
-                    "tag3",
-                    TextLinkStyles(
-                        SpanStyle(color = Color.Red),
-                        SpanStyle(color = Color.Green),
-                        SpanStyle(color = Color.Blue),
-                        SpanStyle(background = Color.Gray)
-                    ),
-                    null
-                )
-            ) {
-                append("8")
-            }
-        }
-
-        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
-
-        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    fun test_AnnotatedString_withSpanAndParagraphStylesAndAnnotations() {
-        val original = buildAnnotatedString {
-            withStyle(ParagraphStyle(textAlign = TextAlign.Justify)) { append("1") }
-            withStyle(ParagraphStyle(textDirection = TextDirection.Rtl)) { append("2") }
-            withStyle(SpanStyle(color = Color.Red)) { append("3") }
-            withStyle(SpanStyle(fontStyle = FontStyle.Italic)) { append("4") }
-            withAnnotation(tag = "Tag1", annotation = "Annotation1") { append("5") }
-            withAnnotation(VerbatimTtsAnnotation("verbatim1")) { append("6") }
-            withAnnotation(tag = "Tag2", annotation = "Annotation2") { append("7") }
-            withAnnotation(VerbatimTtsAnnotation("verbatim2")) { append("8") }
-            withAnnotation(UrlAnnotation("url1")) { append("9") }
-            withAnnotation(UrlAnnotation("url2")) { append("10") }
-            withLink(
-                LinkAnnotation.Url(
-                    "url3",
-                    TextLinkStyles(
-                        SpanStyle(color = Color.Red),
-                        SpanStyle(color = Color.Green),
-                        SpanStyle(color = Color.Blue),
-                        SpanStyle(color = Color.Yellow)
-                    )
-                )
-            ) {
-                append("11")
-            }
-            withLink(
-                LinkAnnotation.Clickable(
-                    "tag3",
-                    TextLinkStyles(
-                        SpanStyle(color = Color.Red),
-                        SpanStyle(color = Color.Green),
-                        SpanStyle(color = Color.Blue),
-                        SpanStyle(color = Color.Gray)
-                    ),
-                    null
-                )
-            ) {
-                append("12")
-            }
-        }
-
-        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
-
-        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_Locale() {
-        val original = Locale("sr-Latn-SR")
-        val saved = with(Locale.Saver) { defaultSaverScope.save(original) }
-
-        assertThat(Locale.Saver.restore(saved!!)).isEqualTo(original)
-    }
-
-    @Test
-    fun test_LocaleList() {
-        val original = LocaleList(Locale("sr-Latn-SR"), Locale("sr-Cyrl-SR"), Locale.current)
-        val saved = with(LocaleList.Saver) { defaultSaverScope.save(original) }
-
-        assertThat(LocaleList.Saver.restore(saved!!)).isEqualTo(original)
-    }
-
-    @Test
-    fun test_LineHeightStyle() {
-        val original =
-            LineHeightStyle(
-                LineHeightStyle.Alignment.Proportional,
-                LineHeightStyle.Trim.Both,
-                LineHeightStyle.Mode.Minimum
-            )
-        val saved = save(original, LineHeightStyle.Saver, defaultSaverScope)
-        val restored: LineHeightStyle? = restore(saved, LineHeightStyle.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_PlatformParagraphStyle_with_no_null_args() {
-        val original = PlatformParagraphStyle(EmojiSupportMatch.All, true)
-        val saved = save(original, PlatformParagraphStyle.Saver, defaultSaverScope)
-        val restored: PlatformParagraphStyle? = restore(saved, PlatformParagraphStyle.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-}
diff --git a/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/SaversTest.kt b/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/SaversTest.kt
new file mode 100644
index 0000000..3465275
--- /dev/null
+++ b/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/SaversTest.kt
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text
+
+import androidx.compose.runtime.saveable.SaverScope
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shadow
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontSynthesis
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.intl.Locale
+import androidx.compose.ui.text.intl.LocaleList
+import androidx.compose.ui.text.style.BaselineShift
+import androidx.compose.ui.text.style.Hyphens
+import androidx.compose.ui.text.style.LineBreak
+import androidx.compose.ui.text.style.LineHeightStyle
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextDirection
+import androidx.compose.ui.text.style.TextGeometricTransform
+import androidx.compose.ui.text.style.TextIndent
+import androidx.compose.ui.text.style.TextMotion
+import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.sp
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+@Suppress("Deprecation")
+class SaversTest {
+    private val defaultSaverScope = SaverScope { true }
+
+    @Test
+    fun test_TextUnit() {
+        val original = 2.sp
+        val saved = save(original, TextUnit.Saver, defaultSaverScope)
+        val restored: TextUnit? = restore(saved, TextUnit.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextUnit_unspecified() {
+        val original = TextUnit.Unspecified
+        val saved = save(original, TextUnit.Saver, defaultSaverScope)
+        val restored: TextUnit? = restore(saved, TextUnit.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Offset() {
+        val original = Offset(10f, 10f)
+        val saved = save(original, Offset.Saver, defaultSaverScope)
+        val restored: Offset? = restore(saved, Offset.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Offset_Unspecified() {
+        val original = Offset.Unspecified
+        val saved = save(original, Offset.Saver, defaultSaverScope)
+        val restored: Offset? = restore(saved, Offset.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Offset_Infinite() {
+        val original = Offset.Infinite
+        val saved = save(original, Offset.Saver, defaultSaverScope)
+        val restored: Offset? = restore(saved, Offset.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Color() {
+        val original = Color.Yellow
+        val saved = save(original, Color.Saver, defaultSaverScope)
+        val restored: Color? = restore(saved, Color.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Color_Unspecified() {
+        val original = Color.Unspecified
+        val saved = save(original, Color.Saver, defaultSaverScope)
+        val restored: Color? = restore(saved, Color.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Shadow() {
+        val original = Shadow(color = Color.Blue, offset = Offset(5f, 5f), blurRadius = 2f)
+        val saved = save(original, Shadow.Saver, defaultSaverScope)
+        val restored: Shadow? = restore(saved, Shadow.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Shadow_None() {
+        val original = Shadow.None
+        val saved = save(original, Shadow.Saver, defaultSaverScope)
+        val restored: Shadow? = restore(saved, Shadow.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_ParagraphStyle() {
+        val original = ParagraphStyle()
+        val saved = save(original, ParagraphStyleSaver, defaultSaverScope)
+        val restored: ParagraphStyle? = restore(saved, ParagraphStyleSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_ParagraphStyle_with_a_nonnull_value() {
+        val original = ParagraphStyle(textDirection = TextDirection.Rtl)
+        val saved = save(original, ParagraphStyleSaver, defaultSaverScope)
+        val restored: ParagraphStyle? = restore(saved, ParagraphStyleSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_ParagraphStyle_with_no_null_value() {
+        val original =
+            ParagraphStyle(
+                textAlign = TextAlign.Justify,
+                textDirection = TextDirection.Rtl,
+                lineHeight = 10.sp,
+                textIndent = TextIndent(firstLine = 2.sp, restLine = 3.sp),
+                platformStyle = PlatformParagraphStyle.Default,
+                lineHeightStyle = LineHeightStyle.Default,
+                lineBreak = LineBreak.Paragraph,
+                hyphens = Hyphens.Auto,
+                textMotion = TextMotion.Animated
+            )
+        val saved = save(original, ParagraphStyleSaver, defaultSaverScope)
+        val restored: ParagraphStyle? = restore(saved, ParagraphStyleSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_SpanStyle() {
+        val original = SpanStyle()
+        val saved = save(original, SpanStyleSaver, defaultSaverScope)
+        val restored: SpanStyle? = restore(saved, SpanStyleSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_SpanStyle_with_a_nonnull_value() {
+        val original = SpanStyle(baselineShift = BaselineShift.Subscript)
+        val saved = save(original, SpanStyleSaver, defaultSaverScope)
+        val restored: SpanStyle? = restore(saved, SpanStyleSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_SpanStyle_with_no_null_value() {
+        val original =
+            SpanStyle(
+                color = Color.Red,
+                fontSize = 10.sp,
+                fontWeight = FontWeight.Bold,
+                fontStyle = FontStyle.Italic,
+                fontSynthesis = FontSynthesis.All,
+                // fontFamily =
+                fontFeatureSettings = "feature settings",
+                letterSpacing = 2.em,
+                baselineShift = BaselineShift.Superscript,
+                textGeometricTransform = TextGeometricTransform(2f, 3f),
+                localeList = LocaleList(Locale("sr-Latn-SR"), Locale("sr-Cyrl-SR"), Locale.current),
+                background = Color.Blue,
+                textDecoration = TextDecoration.LineThrough,
+                shadow = Shadow(color = Color.Red, offset = Offset(2f, 2f), blurRadius = 4f)
+            )
+        val saved = save(original, SpanStyleSaver, defaultSaverScope)
+        val restored: SpanStyle? = restore(saved, SpanStyleSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextLinkStyles() {
+        val original = TextLinkStyles(null)
+        val saved = save(original, TextLinkStylesSaver, defaultSaverScope)
+        val restored: TextLinkStyles? = restore(saved, TextLinkStylesSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextLinkStyles_withNonNullValues() {
+        val original =
+            TextLinkStyles(
+                SpanStyle(color = Color.Red),
+                SpanStyle(color = Color.Green),
+                SpanStyle(color = Color.Blue),
+                SpanStyle(color = Color.Gray)
+            )
+        val saved = save(original, TextLinkStylesSaver, defaultSaverScope)
+        val restored: TextLinkStyles? = restore(saved, TextLinkStylesSaver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_FontWeight() {
+        val original = FontWeight(123)
+        val saved = save(original, FontWeight.Saver, defaultSaverScope)
+        val restored: FontWeight? = restore(saved, FontWeight.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_FontWeight_w100() {
+        val original = FontWeight.W100
+        val saved = save(original, FontWeight.Saver, defaultSaverScope)
+        val restored: FontWeight? = restore(saved, FontWeight.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_BaselineShift() {
+        val original = BaselineShift(2f)
+        val saved = save(original, BaselineShift.Saver, defaultSaverScope)
+        val restored: BaselineShift? = restore(saved, BaselineShift.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_BaselineShift_None() {
+        val original = BaselineShift.None
+        val saved = save(original, BaselineShift.Saver, defaultSaverScope)
+        val restored: BaselineShift? = restore(saved, BaselineShift.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextDecoration() {
+        val original =
+            TextDecoration.combine(listOf(TextDecoration.LineThrough, TextDecoration.Underline))
+        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
+        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextDecoration_None() {
+        val original = TextDecoration.None
+        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
+        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun testSaveRestore_lineThrough() {
+        val original = TextDecoration.LineThrough
+        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
+        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun testSaveRestore_underline() {
+        val original = TextDecoration.Underline
+        val saved = save(original, TextDecoration.Saver, defaultSaverScope)
+        val restored: TextDecoration? = restore(saved, TextDecoration.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextGeometricTransform() {
+        val original = TextGeometricTransform(1f, 2f)
+        val saved = save(original, TextGeometricTransform.Saver, defaultSaverScope)
+        val restored: TextGeometricTransform? = restore(saved, TextGeometricTransform.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextGeometricTransform_None() {
+        val original = TextGeometricTransform.None
+        val saved = save(original, TextGeometricTransform.Saver, defaultSaverScope)
+        val restored: TextGeometricTransform? = restore(saved, TextGeometricTransform.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextIndent() {
+        val original = TextIndent(1.sp, 2.sp)
+        val saved = save(original, TextIndent.Saver, defaultSaverScope)
+        val restored: TextIndent? = restore(saved, TextIndent.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextIndent_None() {
+        val original = TextIndent.None
+        val saved = save(original, TextIndent.Saver, defaultSaverScope)
+        val restored: TextIndent? = restore(saved, TextIndent.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_AnnotatedString() {
+        val original = AnnotatedString("abc")
+        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
+
+        assertThat(AnnotatedStringSaver.restore(saved!!)).isEqualTo(original)
+    }
+
+    @Test
+    fun test_AnnotatedString_withSpanStyles() {
+        val original = buildAnnotatedString {
+            withStyle(SpanStyle(color = Color.Red)) { append("1") }
+            withStyle(SpanStyle(fontStyle = FontStyle.Italic)) { append("2") }
+        }
+
+        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
+
+        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_AnnotatedString_withParagraphStyles() {
+        val original = buildAnnotatedString {
+            withStyle(ParagraphStyle(textAlign = TextAlign.Justify)) { append("1") }
+            withStyle(ParagraphStyle(textDirection = TextDirection.Rtl)) { append("2") }
+        }
+
+        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
+
+        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @OptIn(ExperimentalTextApi::class)
+    @Test
+    fun test_AnnotatedString_withAnnotations() {
+        val original = buildAnnotatedString {
+            withAnnotation(tag = "Tag1", annotation = "Annotation1") { append("1") }
+            withAnnotation(VerbatimTtsAnnotation("verbatim1")) { append("2") }
+            withAnnotation(tag = "Tag2", annotation = "Annotation2") { append("3") }
+            withAnnotation(VerbatimTtsAnnotation("verbatim2")) { append("4") }
+            withAnnotation(UrlAnnotation("url1")) { append("5") }
+            withAnnotation(UrlAnnotation("url2")) { append("6") }
+            withLink(
+                LinkAnnotation.Url(
+                    "url3",
+                    TextLinkStyles(
+                        SpanStyle(color = Color.Red),
+                        SpanStyle(color = Color.Green),
+                        SpanStyle(color = Color.Blue),
+                        SpanStyle(color = Color.White)
+                    )
+                )
+            ) {
+                append("7")
+            }
+            withLink(
+                LinkAnnotation.Clickable(
+                    "tag3",
+                    TextLinkStyles(
+                        SpanStyle(color = Color.Red),
+                        SpanStyle(color = Color.Green),
+                        SpanStyle(color = Color.Blue),
+                        SpanStyle(background = Color.Gray)
+                    ),
+                    null
+                )
+            ) {
+                append("8")
+            }
+        }
+
+        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
+
+        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @OptIn(ExperimentalTextApi::class)
+    @Test
+    fun test_AnnotatedString_withSpanAndParagraphStylesAndAnnotations() {
+        val original = buildAnnotatedString {
+            withStyle(ParagraphStyle(textAlign = TextAlign.Justify)) { append("1") }
+            withStyle(ParagraphStyle(textDirection = TextDirection.Rtl)) { append("2") }
+            withStyle(SpanStyle(color = Color.Red)) { append("3") }
+            withStyle(SpanStyle(fontStyle = FontStyle.Italic)) { append("4") }
+            withAnnotation(tag = "Tag1", annotation = "Annotation1") { append("5") }
+            withAnnotation(VerbatimTtsAnnotation("verbatim1")) { append("6") }
+            withAnnotation(tag = "Tag2", annotation = "Annotation2") { append("7") }
+            withAnnotation(VerbatimTtsAnnotation("verbatim2")) { append("8") }
+            withAnnotation(UrlAnnotation("url1")) { append("9") }
+            withAnnotation(UrlAnnotation("url2")) { append("10") }
+            withLink(
+                LinkAnnotation.Url(
+                    "url3",
+                    TextLinkStyles(
+                        SpanStyle(color = Color.Red),
+                        SpanStyle(color = Color.Green),
+                        SpanStyle(color = Color.Blue),
+                        SpanStyle(color = Color.Yellow)
+                    )
+                )
+            ) {
+                append("11")
+            }
+            withLink(
+                LinkAnnotation.Clickable(
+                    "tag3",
+                    TextLinkStyles(
+                        SpanStyle(color = Color.Red),
+                        SpanStyle(color = Color.Green),
+                        SpanStyle(color = Color.Blue),
+                        SpanStyle(color = Color.Gray)
+                    ),
+                    null
+                )
+            ) {
+                append("12")
+            }
+        }
+
+        val saved = with(AnnotatedStringSaver) { defaultSaverScope.save(original) }
+
+        val restored: AnnotatedString = AnnotatedStringSaver.restore(saved!!)!!
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_Locale() {
+        val original = Locale("sr-Latn-SR")
+        val saved = with(Locale.Saver) { defaultSaverScope.save(original) }
+
+        assertThat(Locale.Saver.restore(saved!!)).isEqualTo(original)
+    }
+
+    @Test
+    fun test_LocaleList() {
+        val original = LocaleList(Locale("sr-Latn-SR"), Locale("sr-Cyrl-SR"), Locale.current)
+        val saved = with(LocaleList.Saver) { defaultSaverScope.save(original) }
+
+        assertThat(LocaleList.Saver.restore(saved!!)).isEqualTo(original)
+    }
+
+    @Test
+    fun test_PlatformParagraphStyle() {
+        val original = PlatformParagraphStyle.Default
+        val saved = save(original, PlatformParagraphStyle.Saver, defaultSaverScope)
+        val restored: PlatformParagraphStyle? = restore(saved, PlatformParagraphStyle.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_PlatformParagraphStyle_with_no_null_args() {
+        val original = PlatformParagraphStyle(EmojiSupportMatch.All, true)
+        val saved = save(original, PlatformParagraphStyle.Saver, defaultSaverScope)
+        val restored: PlatformParagraphStyle? = restore(saved, PlatformParagraphStyle.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_LineHeightStyle() {
+        val original =
+            LineHeightStyle(
+                LineHeightStyle.Alignment.Proportional,
+                LineHeightStyle.Trim.Both,
+                LineHeightStyle.Mode.Minimum
+            )
+        val saved = save(original, LineHeightStyle.Saver, defaultSaverScope)
+        val restored: LineHeightStyle? = restore(saved, LineHeightStyle.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_LineBreak() {
+        val original = LineBreak.Paragraph
+        val saved = save(original, LineBreak.Saver, defaultSaverScope)
+        val restored: LineBreak? = restore(saved, LineBreak.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+
+    @Test
+    fun test_TextMotion() {
+        val original = TextMotion.Animated
+        val saved = save(original, TextMotion.Saver, defaultSaverScope)
+        val restored: TextMotion? = restore(saved, TextMotion.Saver)
+
+        assertThat(restored).isEqualTo(original)
+    }
+}
diff --git a/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/input/EditProcessorTest.kt b/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/input/EditProcessorTest.kt
index fb8d909..56b3d25 100644
--- a/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/input/EditProcessorTest.kt
+++ b/compose/ui/ui-text/src/androidUnitTest/kotlin/androidx/compose/ui/text/input/EditProcessorTest.kt
@@ -16,7 +16,10 @@
 
 package androidx.compose.ui.text.input
 
+import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.withStyle
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
 import org.junit.Test
@@ -92,6 +95,28 @@
     }
 
     @Test
+    fun testNewState_compositionNotLost_ifTextIsSame_butAnnotationsAreDifferent() {
+        val processor = EditProcessor()
+        val textInputSession = mock<TextInputSession>()
+
+        val textFieldValue = TextFieldValue(buildAnnotatedString { append("abc") }, TextRange.Zero)
+        processor.reset(textFieldValue, textInputSession)
+        // first reset call always removes the composing region due to text change
+        processor.apply(listOf(SetComposingRegionCommand(0, 3)))
+        val initialBuffer = processor.mBuffer
+
+        val newTextFieldValue =
+            textFieldValue.copy(
+                buildAnnotatedString { withStyle(SpanStyle()) { append("abc") } },
+                composition = TextRange(0, 3)
+            )
+        processor.reset(newTextFieldValue, textInputSession)
+
+        assertThat(processor.mBuffer).isEqualTo(initialBuffer)
+        assertThat(processor.mBufferState.composition).isEqualTo(TextRange(0, 3))
+    }
+
+    @Test
     fun testNewState_buffer_not_recreated_if_selection_is_different() {
         val processor = EditProcessor()
         val textInputSession = mock<TextInputSession>()
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
index 66e075a..380f9d4 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
@@ -30,6 +30,7 @@
 import androidx.compose.ui.util.fastMap
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
+import kotlin.jvm.JvmName
 
 /**
  * The basic data structure of text with multiple styles. To construct an [AnnotatedString] you can
@@ -464,7 +465,7 @@
          * copied and any other information held in the sequence, such as Android `Span`s, will be
          * dropped.
          */
-        @Suppress("BuilderSetStyle")
+        @Suppress("BuilderSetStyle", "PARAMETER_NAME_CHANGED_ON_OVERRIDE")
         override fun append(text: CharSequence?): Builder {
             if (text is AnnotatedString) {
                 append(text)
@@ -487,7 +488,7 @@
          * @param start The index of the first character in [text] to copy over (inclusive).
          * @param end The index after the last character in [text] to copy over (exclusive).
          */
-        @Suppress("BuilderSetStyle")
+        @Suppress("BuilderSetStyle", "PARAMETER_NAME_CHANGED_ON_OVERRIDE")
         override fun append(text: CharSequence?, start: Int, end: Int): Builder {
             if (text is AnnotatedString) {
                 append(text, start, end)
@@ -498,6 +499,7 @@
         }
 
         // Kdoc comes from interface method.
+        @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
         override fun append(char: Char): Builder {
             this.text.append(char)
             return this
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
index b8e0abc..afeeab0 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
@@ -30,6 +30,7 @@
 import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.isSpecified
 import androidx.compose.ui.unit.isUnspecified
+import kotlin.jvm.JvmName
 
 private val DefaultLineHeight = TextUnit.Unspecified
 
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/StringAnnotation.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/StringAnnotation.kt
index 4c293b1..0ba312c 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/StringAnnotation.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/StringAnnotation.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.ui.text
 
+import kotlin.jvm.JvmInline
+
 /**
  * An [AnnotatedString.Annotation] class which holds a String [value].
  *
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextGranularity.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextGranularity.kt
index 169a02e..9d0cc84 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextGranularity.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextGranularity.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.ui.text
 
+import kotlin.jvm.JvmInline
+
 /**
  * Used by [Paragraph.getRangeForRect]. It specifies the minimal unit of the text ranges that is
  * considered by the [Paragraph.getRangeForRect].
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
index 47d2651..f3fa771 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt
@@ -16,9 +16,9 @@
 
 package androidx.compose.ui.text
 
+import androidx.collection.SieveCache
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
-import androidx.compose.ui.text.caches.LruCache
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.Constraints
@@ -38,7 +38,7 @@
  * that cache does not becomes unnecessarily large and miss penalty stays low. Of course developers
  * should be aware that in a use case like that the cache should explicitly be disabled.
  */
-private val DefaultCacheSize = 8
+private const val DefaultCacheSize = 8
 
 /**
  * TextMeasurer is responsible for measuring a text in its entirety so that it's ready to be drawn.
@@ -80,8 +80,7 @@
  *   would miss the cache.
  */
 @Immutable
-class TextMeasurer
-constructor(
+class TextMeasurer(
     private val defaultFontFamilyResolver: FontFamily.Resolver,
     private val defaultDensity: Density,
     private val defaultLayoutDirection: LayoutDirection,
@@ -352,18 +351,18 @@
 }
 
 /**
- * Keeps an LRU layout cache of TextLayoutInput, TextLayoutResult pairs. Any non-layout affecting
- * change in TextLayoutInput (color, brush, shadow, TextDecoration) is ignored by this cache.
+ * Keeps a layout cache of TextLayoutInput, TextLayoutResult pairs. Any non-layout affecting change
+ * in TextLayoutInput (color, brush, shadow, TextDecoration) is ignored by this cache.
  *
- * @param capacity Maximum size of LRU cache. Size unit is the number of [CacheTextLayoutInput] and
+ * @param capacity Maximum size of the cache. Size unit is the number of [CacheTextLayoutInput] and
  *   [TextLayoutResult] pairs.
  * @throws IllegalArgumentException if capacity is not a positive integer.
  */
 internal class TextLayoutCache(capacity: Int = DefaultCacheSize) {
-    private val lruCache = LruCache<CacheTextLayoutInput, TextLayoutResult>(capacity)
+    private val cache = SieveCache<CacheTextLayoutInput, TextLayoutResult>(capacity, capacity)
 
     fun get(key: TextLayoutInput): TextLayoutResult? {
-        val resultFromCache = lruCache.get(CacheTextLayoutInput(key)) ?: return null
+        val resultFromCache = cache[CacheTextLayoutInput(key)] ?: return null
 
         if (resultFromCache.multiParagraph.intrinsics.hasStaleResolvedFonts) {
             // one of the resolved fonts has updated, and this MeasuredText is no longer valid for
@@ -375,11 +374,11 @@
     }
 
     fun put(key: TextLayoutInput, value: TextLayoutResult): TextLayoutResult? {
-        return lruCache.put(CacheTextLayoutInput(key), value)
+        return cache.put(CacheTextLayoutInput(key), value)
     }
 
     fun remove(key: TextLayoutInput): TextLayoutResult? {
-        return lruCache.remove(CacheTextLayoutInput(key))
+        return cache.remove(CacheTextLayoutInput(key))
     }
 }
 
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
index 527f81a..6461877 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.text.style.TextMotion
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.TextUnit
+import kotlin.jvm.JvmName
 
 /**
  * Styling configuration for a `Text`.
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/ContainerHelpers.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/ContainerHelpers.kt
deleted file mode 100644
index 67f9902..0000000
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/ContainerHelpers.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.caches
-
-/** Copied from ContainerHelpers.binarySearch until collection2 can be linked */
[email protected] internal val EMPTY_INTS = IntArray(0)
-
-/** Copied from ContainerHelpers.binarySearch until collection2 can be linked */
[email protected] internal val EMPTY_OBJECTS = arrayOfNulls<Any>(0)
-
-/** Copied from ContainerHelpers.binarySearch until collection2 can be linked */
-internal fun IntArray.binarySearchInternal(size: Int, value: Int): Int {
-    var lo = 0
-    var hi = size - 1
-    while (lo <= hi) {
-        val mid = lo + hi ushr 1
-        val midVal = this[mid]
-        if (midVal < value) {
-            lo = mid + 1
-        } else if (midVal > value) {
-            hi = mid - 1
-        } else {
-            return mid // value found
-        }
-    }
-    return lo.inv() // value not present
-}
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/LruCache.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/LruCache.kt
deleted file mode 100644
index 7a9544c..0000000
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/LruCache.kt
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.caches
-
-import androidx.compose.ui.text.platform.createSynchronizedObject
-import androidx.compose.ui.text.platform.synchronized
-import kotlin.jvm.JvmName
-
-/** Copy from collection2 until that library can be added as a dependency */
-internal open class LruCache<K, V> {
-
-    private val monitor = createSynchronizedObject()
-    private val map: HashMap<K, V>
-    private val keySet: LinkedHashSet<K>
-
-    /** Size of this cache in units. Not necessarily the number of elements. */
-    @get:JvmName("size")
-    public var size: Int = 0
-        /**
-         * For caches that do not override [sizeOf], this returns the number of entries in the
-         * cache. For all other caches, this returns the sum of the sizes of the entries in this
-         * cache.
-         */
-        get() = synchronizedValue {
-            return field
-        }
-        private set
-
-    private var maxSize = 0
-
-    private var putCount = 0
-    private var createCount = 0
-    private var evictionCount = 0
-    private var hitCount = 0
-    private var missCount = 0
-
-    /**
-     * @param maxSize for caches that do not override [sizeOf], this is the maximum number of
-     *   entries in the cache. For all other caches, this is the maximum sum of the sizes of the
-     *   entries in this cache.
-     */
-    constructor(maxSize: Int) {
-        require(maxSize > 0) { "maxSize <= 0" }
-        this.maxSize = maxSize
-        map = HashMap<K, V>(0, 0.75f)
-        keySet = LinkedHashSet<K>()
-    }
-
-    /**
-     * Sets the size of the cache.
-     *
-     * @param maxSize The new maximum size.
-     */
-    open fun resize(maxSize: Int) {
-        require(maxSize > 0) { "maxSize <= 0" }
-
-        synchronized(monitor) { this.maxSize = maxSize }
-        trimToSize(maxSize)
-    }
-
-    /**
-     * Returns the value for [key] if it exists in the cache or can be created by [create]. If a
-     * value was returned, it is moved to the head of the queue. This returns `null` if a value is
-     * not cached and cannot be created.
-     */
-    fun get(key: K): V? {
-        var mapValue: V? = null
-
-        synchronized(monitor) {
-            mapValue = map.get(key)
-            if (mapValue != null) {
-                // Push the key to the end of the keySet as the cached entry gets hit.
-                keySet.remove(key)
-                keySet.add(key)
-                hitCount++
-                return mapValue
-            } else {
-                missCount++
-            }
-        }
-
-        val createdValue: V? = create(key)
-        if (createdValue == null) {
-            return null
-        }
-
-        synchronized(monitor) {
-            createCount++
-            val previousValue: V? = map.put(key, createdValue)
-            // Push the key to the end of the keySet as the cached entry gets hit.
-            keySet.remove(key)
-            keySet.add(key)
-            if (previousValue != null) {
-                // There was a conflict so undo that last put
-                map.put(key, previousValue)
-                mapValue = previousValue
-            } else {
-                size += safeSizeOf(key, createdValue)
-            }
-        }
-
-        if (mapValue != null) {
-            entryRemoved(false, key, createdValue, mapValue)
-            return mapValue
-        } else {
-            trimToSize(maxSize)
-            return createdValue
-        }
-    }
-
-    /**
-     * Caches [value] for [key]. The value is moved to the head of the queue.
-     *
-     * @return the previous value mapped by [key].
-     * @throws NullPointerException if [key] or [value] is null
-     */
-    fun put(key: K, value: V): V? {
-        // Must throw NPE for JVM interop contract.
-        if (key == null || value == null) {
-            throw NullPointerException()
-        }
-
-        var previous: V? = null
-        synchronized(monitor) {
-            putCount++
-            size += safeSizeOf(key, value)
-            previous = map.put(key, value)
-            if (previous != null) {
-                size -= safeSizeOf(key, previous!!)
-            }
-            if (keySet.contains(key)) {
-                keySet.remove(key)
-            }
-            keySet.add(key)
-        }
-
-        if (previous != null) {
-            entryRemoved(false, key, previous!!, value)
-        }
-
-        trimToSize(maxSize)
-        return previous
-    }
-
-    /**
-     * Remove the eldest entries until the total of remaining entries is at or below the requested
-     * size.
-     *
-     * @param maxSize the maximum size of the cache before returning. May be -1 to evict even
-     *   0-sized elements.
-     * @throws IllegalStateException
-     */
-    open fun trimToSize(maxSize: Int) {
-        while (true) {
-            var key: K? = null
-            var value: V? = null
-
-            synchronized(monitor) {
-                if (
-                    size < 0 || (map.isEmpty() && size != 0) || (map.isEmpty() != keySet.isEmpty())
-                ) {
-                    throw IllegalStateException("map/keySet size inconsistency")
-                }
-
-                if (size > maxSize && !map.isEmpty()) {
-                    key = keySet.first()
-                    value = map.get(key) ?: throw IllegalStateException("inconsistent " + "state")
-                    map.remove(key)
-                    keySet.remove(key)
-                    size -= safeSizeOf(key!!, value!!)
-                    evictionCount++
-                }
-            }
-
-            if (key == null && value == null) {
-                break
-            } else {
-                entryRemoved(true, key!!, value!!, null)
-            }
-        }
-    }
-
-    /**
-     * Removes the entry for [key] if it exists.
-     *
-     * @return the previous value mapped by [key].
-     * @throws NullPointerException if [key] is null from a JVM caller.
-     */
-    fun remove(key: K): V? {
-        // Must throw NPE for JVM interop contract.
-        if (key == null) {
-            throw NullPointerException()
-        }
-
-        var previous: V? = null
-        synchronized(monitor) {
-            previous = map.remove(key)
-            keySet.remove(key)
-            if (previous != null) {
-                size -= safeSizeOf(key, previous!!)
-            }
-        }
-
-        if (previous != null) {
-            entryRemoved(false, key, previous!!, null)
-        }
-
-        return previous
-    }
-
-    /**
-     * Called for entries that have been evicted or removed. This method is invoked when a value is
-     * evicted to make space, removed by a call to [remove], or replaced by a call to [put]. The
-     * default implementation does nothing.
-     *
-     * The method is called without synchronization: other threads may access the cache while this
-     * method is executing.
-     *
-     * @param evicted `true` if the entry is being removed to make space, `false` if the removal was
-     *   caused by a [put] or [remove].
-     * @param key key of the entry that was evicted or removed.
-     * @param oldValue the original value of the entry that was evicted removed.
-     * @param newValue the new value for [key], if it exists. If non-null, this removal was caused
-     *   by a [put]. Otherwise it was caused by an eviction or a [remove].
-     */
-    protected open fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {}
-
-    /**
-     * Called after a cache miss to compute a value for the corresponding key. Returns the computed
-     * value or null if no value can be computed. The default implementation returns null.
-     *
-     * The method is called without synchronization: other threads may access the cache while this
-     * method is executing.
-     *
-     * If a value for [key] exists in the cache when this method returns, the created value will be
-     * released with [entryRemoved] and discarded. This can occur when multiple threads request the
-     * same key at the same time (causing multiple values to be created), or when one thread calls
-     * [put] while another is creating a value for the same key.
-     */
-    protected open fun create(key: K): V? = null
-
-    private fun safeSizeOf(key: K, value: V): Int {
-        val result = sizeOf(key, value)
-        check(result >= 0) { "Negative size: $key=$value" }
-        return result
-    }
-
-    /**
-     * Returns the size of the entry for [key] and [value] in user-defined units. The default
-     * implementation returns 1 so that size is the number of entries and max size is the maximum
-     * number of entries.
-     *
-     * An entry's size must not change while it is in the cache.
-     */
-    protected open fun sizeOf(key: K, value: V) = 1
-
-    /** Clear the cache, calling [entryRemoved] on each removed entry. */
-    fun evictAll() {
-        trimToSize(-1) // -1 will evict 0-sized elements
-    }
-
-    /**
-     * For caches that do not override [sizeOf], this returns the maximum number of entries in the
-     * cache. For all other caches, this returns the maximum sum of the sizes of the entries in this
-     * cache.
-     */
-    fun maxSize(): Int = synchronizedValue { maxSize }
-
-    /** Returns the number of times [get] returned a value that was already present in the cache. */
-    fun hitCount(): Int = synchronizedValue { hitCount }
-
-    /** Returns the number of times [get] returned null or required a new value to be created. */
-    fun missCount(): Int = synchronizedValue { missCount }
-
-    /** Returns the number of times [create] returned a value. */
-    fun createCount(): Int = synchronizedValue { createCount }
-
-    /** Returns the number of times [put] was called. */
-    fun putCount(): Int = synchronizedValue { putCount }
-
-    /** Returns the number of values that have been evicted. */
-    fun evictionCount(): Int = synchronizedValue { evictionCount }
-
-    /**
-     * Returns a copy of the current contents of the cache, ordered from least recently accessed to
-     * most recently accessed.
-     */
-    fun snapshot(): Map<K, V> {
-        synchronized(monitor) {
-            val linkedHashMap = LinkedHashMap<K, V>()
-            for (key in keySet) {
-                linkedHashMap.put(key, map.get(key)!!)
-            }
-            return linkedHashMap
-        }
-    }
-
-    override fun toString(): String {
-        synchronized(monitor) {
-            val accesses = hitCount + missCount
-            val hitPercent = if (accesses != 0) 100 * hitCount / accesses else 0
-            return "LruCache[maxSize=$maxSize,hits=$hitCount,misses=$missCount," +
-                "hitRate=$hitPercent%]"
-        }
-    }
-
-    internal inline fun <R> synchronizedValue(block: () -> R): R {
-        return synchronized(monitor, block)
-    }
-}
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/SimpleArrayMap.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/SimpleArrayMap.kt
deleted file mode 100644
index 91cda70..0000000
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/caches/SimpleArrayMap.kt
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * Copyright 2020 The Android Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.caches
-
-private const val DEBUG = false
-private const val TAG = "SimpleArrayMap"
-
-/**
- * Attempt to spot concurrent modifications to this data structure.
- *
- * It's best-effort, but any time we can throw something more diagnostic than an
- * ArrayIndexOutOfBoundsException deep in the ArrayMap internals it's going to save a lot of
- * development time.
- *
- * Good times to look for CME include after any array allocations/copyOf calls and at the end of
- * functions that change size (put/remove/clear).
- */
-private const val CONCURRENT_MODIFICATION_EXCEPTIONS = true
-
-/**
- * The minimum amount by which the capacity of a ArrayMap will increase. This is tuned to be
- * relatively space-efficient.
- */
-private const val BASE_SIZE = 4
-
-/** Copy of SimpleArrayMap from collection2 until dependency can be added correctly */
-internal class SimpleArrayMap<K, V> {
-
-    private var hashes: IntArray
-    private var keyValues: Array<Any?>
-    protected var _size = 0
-
-    // Suppression necessary, see KT-43542.
-    @Suppress("INAPPLICABLE_JVM_NAME")
-    @get:kotlin.jvm.JvmName("size")
-    val size: Int
-        get() = _size
-
-    protected fun indexOf(key: Any, hash: Int): Int {
-        val N = _size
-
-        // Important fast case: if nothing is in here, nothing to look for.
-        if (N == 0) {
-            return 0.inv()
-        }
-        val index: Int = hashes.binarySearchInternal(N, hash)
-
-        // If the hash code wasn't found, then we have no entry for this key.
-        if (index < 0) {
-            return index
-        }
-
-        // If the key at the returned index matches, that's what we want.
-        if (key == keyValues[index shl 1]) {
-            return index
-        }
-
-        // Search for a matching key after the index.
-        var end: Int
-        end = index + 1
-        while (end < N && hashes[end] == hash) {
-            if (key == keyValues[end shl 1]) return end
-            end++
-        }
-
-        // Search for a matching key before the index.
-        var i = index - 1
-        while (i >= 0 && hashes[i] == hash) {
-            if (key == keyValues[i shl 1]) return i
-            i--
-        }
-
-        // Key not found -- return negative value indicating where a
-        // new entry for this key should go.  We use the end of the
-        // hash chain to reduce the number of array entries that will
-        // need to be copied when inserting.
-        return end.inv()
-    }
-
-    protected fun indexOfNull(): Int {
-        val N = _size
-
-        // Important fast case: if nothing is in here, nothing to look for.
-        if (N == 0) {
-            return 0.inv()
-        }
-        val index: Int = hashes.binarySearchInternal(N, 0)
-
-        // If the hash code wasn't found, then we have no entry for this key.
-        if (index < 0) {
-            return index
-        }
-
-        // If the key at the returned index matches, that's what we want.
-        if (null == keyValues[index shl 1]) {
-            return index
-        }
-
-        // Search for a matching key after the index.
-        var end: Int
-        end = index + 1
-        while (end < N && hashes[end] == 0) {
-            if (null == keyValues[end shl 1]) return end
-            end++
-        }
-
-        // Search for a matching key before the index.
-        var i = index - 1
-        while (i >= 0 && hashes[i] == 0) {
-            if (null == keyValues[i shl 1]) return i
-            i--
-        }
-
-        // Key not found -- return negative value indicating where a
-        // new entry for this key should go.  We use the end of the
-        // hash chain to reduce the number of array entries that will
-        // need to be copied when inserting.
-        return end.inv()
-    }
-
-    /** Create a new ArrayMap with a given initial capacity. */
-    @kotlin.jvm.JvmOverloads
-    constructor(capacity: Int = 0) {
-        if (capacity == 0) {
-            hashes = EMPTY_INTS
-            keyValues = EMPTY_OBJECTS
-        } else {
-            hashes = IntArray(capacity)
-            keyValues = arrayOfNulls<Any?>(capacity shl 1)
-        }
-        _size = 0
-    }
-
-    /** Create a new ArrayMap with the mappings from the given ArrayMap. */
-    constructor(map: SimpleArrayMap<K, V>?) : this() {
-        if (map != null) {
-            putAll(map)
-        }
-    }
-
-    /**
-     * Make the array map empty. All storage is released.
-     *
-     * @throws ConcurrentModificationException if the map has been concurrently modified.
-     */
-    fun clear() {
-        if (_size > 0) {
-            hashes = EMPTY_INTS
-            keyValues = EMPTY_OBJECTS
-            _size = 0
-        }
-        if (CONCURRENT_MODIFICATION_EXCEPTIONS && _size > 0) {
-            throw ConcurrentModificationException()
-        }
-    }
-
-    /**
-     * Ensure the array map can hold at least <var>minimumCapacity</var> items.
-     *
-     * @throws ConcurrentModificationException if the map has been concurrently modified.
-     */
-    fun ensureCapacity(minimumCapacity: Int) {
-        val osize = _size
-        if (hashes.size < minimumCapacity) {
-            hashes = hashes.copyOf(minimumCapacity)
-            keyValues = keyValues.copyOf(minimumCapacity shl 1)
-        }
-        if (CONCURRENT_MODIFICATION_EXCEPTIONS && _size != osize) {
-            throw ConcurrentModificationException()
-        }
-    }
-
-    /**
-     * Check whether a key exists in the array.
-     *
-     * @param key The key to search for.
-     * @return Returns true if the key exists, else false.
-     */
-    fun containsKey(key: K): Boolean = indexOfKey(key) >= 0
-
-    /**
-     * Returns the index of a key in the set.
-     *
-     * @param key The key to search for.
-     * @return Returns the index of the key if it exists, else a negative integer.
-     */
-    fun indexOfKey(key: Any?): Int =
-        if (key == null) indexOfNull() else indexOf(key, key.hashCode())
-
-    internal fun indexOfValue(value: V): Int {
-        val N = _size shl 1
-        val array = keyValues
-        if (value == null) {
-            var i = 1
-            while (i < N) {
-                if (array[i] == null) {
-                    return i shr 1
-                }
-                i += 2
-            }
-        } else {
-            var i = 1
-            while (i < N) {
-                if (value == array[i]) {
-                    return i shr 1
-                }
-                i += 2
-            }
-        }
-        return -1
-    }
-
-    /**
-     * Check whether a value exists in the array. This requires a linear search through the entire
-     * array.
-     *
-     * @param value The value to search for.
-     * @return Returns true if the value exists, else false.
-     */
-    fun containsValue(value: V): Boolean = indexOfValue(value) >= 0
-
-    /**
-     * Retrieve a value from the array.
-     *
-     * @param key The key of the value to retrieve.
-     * @return Returns the value associated with the given key, or null if there is no such key.
-     */
-    @Suppress("UNCHECKED_CAST")
-    operator fun get(key: K): V? {
-        // TODO: Explain why re-impl instead of using getOrDefault()
-        val index = indexOfKey(key)
-        return if (index >= 0) keyValues[(index shl 1) + 1] as V else null
-    }
-
-    /**
-     * Retrieve a value from the array, or [defaultValue] if there is no mapping for the key.
-     *
-     * @param key The key of the value to retrieve.
-     * @param defaultValue The default mapping of the key
-     * @return Returns the value associated with the given key, or [defaultValue] if there is no
-     *   mapping for the key.
-     */
-    @Suppress("UNCHECKED_CAST")
-    fun getOrDefault(key: K, defaultValue: V): V {
-        val index = indexOfKey(key)
-        return if (index >= 0) keyValues[(index shl 1) + 1] as V else defaultValue
-    }
-
-    /**
-     * Return the key at the given index in the array.
-     *
-     * @param index The desired index, must be between 0 and [size]-1.
-     * @return Returns the key stored at the given index.
-     */
-    @Suppress("UNCHECKED_CAST") fun keyAt(index: Int): K = keyValues[index shl 1] as K
-
-    /**
-     * Return the value at the given index in the array.
-     *
-     * @param index The desired index, must be between 0 and [size]-1.
-     * @return Returns the value stored at the given index.
-     */
-    @Suppress("UNCHECKED_CAST") fun valueAt(index: Int): V = keyValues[(index shl 1) + 1] as V
-
-    /**
-     * Set the value at a given index in the array.
-     *
-     * @param index The desired index, must be between 0 and [size]-1.
-     * @param value The new value to store at this index.
-     * @return Returns the previous value at the given index.
-     */
-    @Suppress("UNCHECKED_CAST")
-    fun setValueAt(index: Int, value: V): V {
-        val actualIndex = (index shl 1) + 1
-        val old = keyValues[actualIndex] as V
-        keyValues[actualIndex] = value
-        return old
-    }
-
-    /** Return true if the array map contains no items. */
-    fun isEmpty(): Boolean = _size <= 0
-
-    /**
-     * Add a new value to the array map.
-     *
-     * @param key The key under which to store the value. <b>Must not be null.</b> If this key
-     *   already exists in the array, its value will be replaced.
-     * @param value The value to store for the given key.
-     * @return Returns the old value that was stored for the given key, or null if there was no such
-     *   key.
-     * @throws ConcurrentModificationException if the map has been concurrently modified.
-     */
-    @Suppress("UNCHECKED_CAST")
-    fun put(key: K, value: V): V? {
-        val osize = _size
-        val hash: Int
-        var index: Int
-
-        if (key == null) {
-            hash = 0
-            index = indexOfNull()
-        } else {
-            hash = key.hashCode()
-            index = indexOf(key, hash)
-        }
-        if (index >= 0) {
-            index = (index shl 1) + 1
-            val old = keyValues[index] as V
-            keyValues[index] = value
-            return old
-        }
-
-        index = index.inv()
-        if (osize >= hashes.size) {
-            val n =
-                when {
-                    osize >= BASE_SIZE * 2 -> osize + (osize shr 1)
-                    osize >= BASE_SIZE -> BASE_SIZE * 2
-                    else -> BASE_SIZE
-                }
-            if (DEBUG) {
-                println("$TAG put: grow from ${hashes.size} to $n")
-            }
-            hashes = hashes.copyOf(n)
-            keyValues = keyValues.copyOf(n shl 1)
-
-            if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != _size) {
-                throw ConcurrentModificationException()
-            }
-        }
-
-        if (index < osize) {
-            if (DEBUG) {
-                println("$TAG put: move $index-${osize - index} to ${index + 1}")
-            }
-            hashes.copyInto(hashes, index + 1, index, osize)
-            keyValues.copyInto(keyValues, (index + 1) shl 1, index shl 1, _size shl 1)
-        }
-
-        if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
-            if (osize != _size || index >= hashes.size) {
-                throw ConcurrentModificationException()
-            }
-        }
-
-        hashes[index] = hash
-        keyValues[index shl 1] = key
-        keyValues[(index shl 1) + 1] = value
-        _size++
-        return null
-    }
-
-    /**
-     * Perform a [put] of all key/value pairs in <var>array</var>
-     *
-     * @param array The array whose contents are to be retrieved.
-     */
-    fun putAll(array: SimpleArrayMap<out K, out V>) {
-        val N = array._size
-        ensureCapacity(_size + N)
-        if (_size == 0) {
-            if (N > 0) {
-                array.hashes.copyInto(hashes, 0, 0, N)
-                array.keyValues.copyInto(keyValues, 0, 0, N shl 1)
-                _size = N
-            }
-        } else {
-            for (i in 0 until N) {
-                put(array.keyAt(i), array.valueAt(i))
-            }
-        }
-    }
-
-    /**
-     * Add a new value to the array map only if the key does not already have a value or it is
-     * mapped to `null`.
-     *
-     * @param key The key under which to store the value.
-     * @param value The value to store for the given key.
-     * @return Returns the value that was stored for the given key, or null if there was no such
-     *   key.
-     */
-    fun putIfAbsent(key: K, value: V): V? {
-        var mapValue = get(key)
-        if (mapValue == null) {
-            mapValue = put(key, value)
-        }
-        return mapValue
-    }
-
-    /**
-     * Remove an existing key from the array map.
-     *
-     * @param key The key of the mapping to remove.
-     * @return Returns the value that was stored under the key, or null if there was no such key.
-     */
-    fun remove(key: K): V? {
-        val index = indexOfKey(key)
-        return if (index >= 0) removeAt(index) else null
-    }
-
-    /**
-     * Remove an existing key from the array map only if it is currently mapped to [value].
-     *
-     * @param key The key of the mapping to remove.
-     * @param value The value expected to be mapped to the key.
-     * @return Returns true if the mapping was removed.
-     */
-    fun remove(key: K, value: V): Boolean {
-        val index = indexOfKey(key)
-        if (index >= 0) {
-            val mapValue = valueAt(index)
-            if (value == mapValue) {
-                removeAt(index)
-                return true
-            }
-        }
-        return false
-    }
-
-    /**
-     * Remove the key/value mapping at the given index.
-     *
-     * @param index The desired index, must be between 0 and [size]-1.
-     * @return Returns the value that was stored at this index.
-     * @throws ConcurrentModificationException if the map has been concurrently modified.
-     */
-    @Suppress("UNCHECKED_CAST")
-    fun removeAt(index: Int): V? {
-        val old = keyValues[(index shl 1) + 1]
-        val osize = _size
-        if (osize <= 1) {
-            // Now empty.
-            if (DEBUG) {
-                println("$TAG remove: shrink from $hashes.size to 0")
-            }
-            clear()
-        } else {
-            val nsize = osize - 1
-            if (hashes.size > BASE_SIZE * 2 && osize < hashes.size / 3) {
-                // Shrunk enough to reduce size of arrays.  We don't allow it to
-                // shrink smaller than (BASE_SIZE*2) to avoid flapping between
-                // that and BASE_SIZE.
-                val n = if (osize > BASE_SIZE * 2) osize + (osize shr 1) else BASE_SIZE * 2
-                if (DEBUG) {
-                    println("$TAG remove: shrink from $hashes.size to $n")
-                }
-                val ohashes = hashes
-                val oarray: Array<Any?> = keyValues
-
-                hashes = IntArray(n)
-                keyValues = arrayOfNulls<Any?>(n shl 1)
-
-                if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != _size) {
-                    throw ConcurrentModificationException()
-                }
-                if (index > 0) {
-                    if (DEBUG) {
-                        println("$TAG remove: copy from 0-$index to 0")
-                    }
-                    ohashes.copyInto(hashes, 0, 0, index)
-                    oarray.copyInto(keyValues, 0, 0, index shl 1)
-                }
-                if (index < nsize) {
-                    if (DEBUG) {
-                        println("$TAG remove: copy from ${index + 1}-$nsize to $index")
-                    }
-                    ohashes.copyInto(hashes, index, index + 1, nsize + 1)
-                    oarray.copyInto(keyValues, index shl 1, (index + 1) shl 1, (nsize + 1) shl 1)
-                }
-            } else {
-                if (index < nsize) {
-                    if (DEBUG) println("$TAG remove: move ${index + 1}-$nsize to $index")
-                    hashes.copyInto(hashes, index, index + 1, nsize + 1)
-                    keyValues.copyInto(keyValues, index shl 1, (index + 1) shl 1, (nsize + 1) shl 1)
-                }
-                keyValues[nsize shl 1] = null
-                keyValues[(nsize shl 1) + 1] = null
-            }
-            if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != _size) {
-                throw ConcurrentModificationException()
-            }
-            _size = nsize
-        }
-        return old as V
-    }
-
-    /**
-     * Replace the mapping for [key] only if it is already mapped to a value.
-     *
-     * @param key The key of the mapping to replace.
-     * @param value The value to store for the given key.
-     * @return Returns the previous mapped value or null.
-     */
-    fun replace(key: K, value: V): V? {
-        val index = indexOfKey(key)
-        return if (index >= 0) setValueAt(index, value) else null
-    }
-
-    /**
-     * Replace the mapping for [key] only if it is already mapped to a value.
-     *
-     * @param key The key of the mapping to replace.
-     * @param oldValue The value expected to be mapped to the key.
-     * @param newValue The value to store for the given key.
-     * @return Returns true if the value was replaced.
-     */
-    fun replace(key: K, oldValue: V, newValue: V): Boolean {
-        val index = indexOfKey(key)
-        if (index >= 0) {
-            val mapValue = valueAt(index)
-            if (mapValue === oldValue) {
-                setValueAt(index, newValue)
-                return true
-            }
-        }
-        return false
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation returns false if the object is not a Map or SimpleArrayMap, or if the
-     * maps have different sizes. Otherwise, for each key in this map, values of both maps are
-     * compared. If the values for any key are not equal, the method returns false, otherwise it
-     * returns true.
-     */
-    @Suppress("UNCHECKED_CAST")
-    override fun equals(other: Any?): Boolean {
-        if (this === other) {
-            return true
-        }
-
-        try {
-            if (other is SimpleArrayMap<*, *>) {
-                val map = other as SimpleArrayMap<in Any?, in Any?>
-                if (_size != map._size) {
-                    return false
-                }
-
-                for (i in 0 until _size) {
-                    val key = keyAt(i)
-                    val mine: V? = valueAt(i)
-                    // TODO use index-based ops for this
-                    val theirs = map.get(key)
-                    if (mine == null) {
-                        if (theirs != null || !map.containsKey(key)) {
-                            return false
-                        }
-                    } else if (mine != theirs) {
-                        return false
-                    }
-                }
-                return true
-            } else if (other is Map<*, *>) {
-                val map = other
-                if (_size != map.size) {
-                    return false
-                }
-                for (i in 0 until _size) {
-                    val key = keyAt(i)
-                    val mine: V? = valueAt(i)
-                    val theirs = map[key]
-                    if (mine == null) {
-                        if (theirs != null || !map.containsKey(key)) {
-                            return false
-                        }
-                    } else if (mine != theirs) {
-                        return false
-                    }
-                }
-                return true
-            }
-        } catch (ignored: NullPointerException) {} catch (ignored: ClassCastException) {}
-        return false
-    }
-
-    /** {@inheritDoc} */
-    override fun hashCode(): Int {
-        val hashes = hashes
-        val array: Array<Any?> = keyValues
-        var result = 0
-        var i = 0
-        var v = 1
-        val s = _size
-        while (i < s) {
-            val value = array[v]
-            result += hashes[i] xor (value?.hashCode() ?: 0)
-            i++
-            v += 2
-        }
-        return result
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation composes a string by iterating over its mappings. If this map contains
-     * itself as a key or a value, the string "(this Map)" will appear in its place.
-     */
-    override fun toString(): String {
-        if (isEmpty()) {
-            return "{}"
-        }
-
-        val buffer = StringBuilder(_size * 28)
-        buffer.append('{')
-        for (i in 0 until _size) {
-            if (i > 0) {
-                buffer.append(", ")
-            }
-            val key = keyAt(i)
-            if (key !== this) {
-                buffer.append(key)
-            } else {
-                buffer.append("(this Map)")
-            }
-            buffer.append('=')
-            val value = valueAt(i)
-            if (value !== this) {
-                buffer.append(value)
-            } else {
-                buffer.append("(this Map)")
-            }
-        }
-        buffer.append('}')
-        return buffer.toString()
-    }
-}
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
index d143fae..004da07 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
@@ -16,8 +16,8 @@
 
 package androidx.compose.ui.text.font
 
+import androidx.collection.SieveCache
 import androidx.compose.runtime.State
-import androidx.compose.ui.text.caches.LruCache
 import androidx.compose.ui.text.platform.createSynchronizedObject
 import androidx.compose.ui.text.platform.synchronized
 import androidx.compose.ui.util.fastMap
@@ -167,14 +167,14 @@
 internal class TypefaceRequestCache {
     internal val lock = createSynchronizedObject()
     // @GuardedBy("lock")
-    private val resultCache = LruCache<TypefaceRequest, TypefaceResult>(16)
+    private val resultCache = SieveCache<TypefaceRequest, TypefaceResult>(16, 16)
 
     fun runCached(
         typefaceRequest: TypefaceRequest,
         resolveTypeface: ((TypefaceResult) -> Unit) -> TypefaceResult
     ): State<Any> {
         synchronized(lock) {
-            resultCache.get(typefaceRequest)?.let {
+            resultCache[typefaceRequest]?.let {
                 if (it.cacheable) {
                     return it
                 } else {
@@ -216,7 +216,7 @@
         synchronized(lock) {
             // async result may have completed prior to this block entering, do not overwrite
             // final results
-            if (resultCache.get(typefaceRequest) == null && currentTypefaceResult.cacheable) {
+            if (resultCache[typefaceRequest] == null && currentTypefaceResult.cacheable) {
                 resultCache.put(typefaceRequest, currentTypefaceResult)
             }
         }
@@ -230,7 +230,7 @@
         for (i in typefaceRequests.indices) {
             val typeRequest = typefaceRequests[i]
 
-            val prior = synchronized(lock) { resultCache.get(typeRequest) }
+            val prior = synchronized(lock) { resultCache[typeRequest] }
             if (prior != null) continue
 
             val next =
@@ -244,13 +244,13 @@
             // has async fonts in permanent cache
             if (next is TypefaceResult.Async) continue
 
-            synchronized(lock) { resultCache.put(typeRequest, next) }
+            synchronized(lock) { resultCache[typeRequest] = next }
         }
     }
 
     // @VisibleForTesting
     internal fun get(typefaceRequest: TypefaceRequest) =
-        synchronized(lock) { resultCache.get(typefaceRequest) }
+        synchronized(lock) { resultCache[typefaceRequest] }
 
     // @VisibleForTesting
     internal val size: Int
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
index 7fd0965..54c0aa1 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
@@ -16,12 +16,12 @@
 
 package androidx.compose.ui.text.font
 
+import androidx.collection.SieveCache
+import androidx.collection.mutableScatterMapOf
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.text.caches.LruCache
-import androidx.compose.ui.text.caches.SimpleArrayMap
 import androidx.compose.ui.text.platform.FontCacheManagementDispatcher
 import androidx.compose.ui.text.platform.createSynchronizedObject
 import androidx.compose.ui.text.platform.synchronized
@@ -358,14 +358,14 @@
 
     internal data class Key(val font: Font, val loaderKey: Any?)
 
-    // 16 is based on the LruCache in TypefaceCompat Android, but no firm logic for this size.
+    // 16 is based on the cache in TypefaceCompat Android, but no firm logic for this size.
     // After loading, fonts are put into the resultCache to allow reading from a kotlin function
     // context, reducing async fonts overhead cache lookup overhead only while cached
     // @GuardedBy("cacheLock")
-    private val resultCache = LruCache<Key, AsyncTypefaceResult>(16)
+    private val resultCache = SieveCache<Key, AsyncTypefaceResult>(16, 16)
     // failures and preloads are permanent, so they are stored separately
     // @GuardedBy("cacheLock")
-    private val permanentCache = SimpleArrayMap<Key, AsyncTypefaceResult>()
+    private val permanentCache = mutableScatterMapOf<Key, AsyncTypefaceResult>()
 
     private val cacheLock = createSynchronizedObject()
 
@@ -379,13 +379,13 @@
         synchronized(cacheLock) {
             when {
                 result == null -> {
-                    permanentCache.put(key, PermanentFailure)
+                    permanentCache[key] = PermanentFailure
                 }
                 forever -> {
-                    permanentCache.put(key, AsyncTypefaceResult(result))
+                    permanentCache[key] = AsyncTypefaceResult(result)
                 }
                 else -> {
-                    resultCache.put(key, AsyncTypefaceResult(result))
+                    resultCache[key] = AsyncTypefaceResult(result)
                 }
             }
         }
@@ -393,7 +393,7 @@
 
     fun get(font: Font, platformFontLoader: PlatformFontLoader): AsyncTypefaceResult? {
         val key = Key(font, platformFontLoader.cacheKey)
-        return synchronized(cacheLock) { resultCache.get(key) ?: permanentCache[key] }
+        return synchronized(cacheLock) { resultCache[key] ?: permanentCache[key] }
     }
 
     suspend fun runCached(
@@ -404,7 +404,7 @@
     ): Any? {
         val key = Key(font, platformFontLoader.cacheKey)
         synchronized(cacheLock) {
-            val priorResult = resultCache.get(key) ?: permanentCache[key]
+            val priorResult = resultCache[key] ?: permanentCache[key]
             if (priorResult != null) {
                 return priorResult.result
             }
@@ -413,13 +413,13 @@
             synchronized(cacheLock) {
                 when {
                     it == null -> {
-                        permanentCache.put(key, PermanentFailure)
+                        permanentCache[key] = PermanentFailure
                     }
                     forever -> {
-                        permanentCache.put(key, AsyncTypefaceResult(it))
+                        permanentCache[key] = AsyncTypefaceResult(it)
                     }
                     else -> {
-                        resultCache.put(key, AsyncTypefaceResult(it))
+                        resultCache[key] = AsyncTypefaceResult(it)
                     }
                 }
             }
@@ -433,7 +433,7 @@
     ): Any? {
         synchronized(cacheLock) {
             val key = Key(font, platformFontLoader.cacheKey)
-            val priorResult = resultCache.get(key) ?: permanentCache[key]
+            val priorResult = resultCache[key] ?: permanentCache[key]
             if (priorResult != null) {
                 return priorResult.result
             }
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/EditProcessor.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/EditProcessor.kt
index 133e13b..3066e06 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/EditProcessor.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/EditProcessor.kt
@@ -58,7 +58,7 @@
         var selectionChanged = false
         val compositionChanged = value.composition != mBuffer.composition
 
-        if (mBufferState.annotatedString != value.annotatedString) {
+        if (mBufferState.annotatedString.text != value.annotatedString.text) {
             mBuffer = EditingBuffer(text = value.annotatedString, selection = value.selection)
             textChanged = true
         } else if (mBufferState.selection != value.selection) {
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/GapBuffer.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/GapBuffer.kt
index 6b968f6..1275d48 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/GapBuffer.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/GapBuffer.kt
@@ -186,8 +186,8 @@
      * @param builder The output string builder
      */
     fun append(builder: StringBuilder) {
-        builder.append(buffer, 0, gapStart)
-        builder.append(buffer, gapEnd, capacity - gapEnd)
+        builder.appendRange(buffer, 0, gapStart)
+        builder.appendRange(buffer, gapEnd, capacity)
     }
 
     /**
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/Hyphens.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/Hyphens.kt
index f189eea..7ef0236 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/Hyphens.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/Hyphens.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.ui.text.style
 
+import kotlin.jvm.JvmInline
+
 /**
  * Automatic hyphenation configuration.
  *
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineBreak.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineBreak.kt
index 6bbc8bb..b0615de 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineBreak.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineBreak.kt
@@ -21,6 +21,7 @@
 import androidx.compose.ui.text.style.LineBreak.Companion.Heading
 import androidx.compose.ui.text.style.LineBreak.Companion.Paragraph
 import androidx.compose.ui.text.style.LineBreak.Companion.Simple
+import kotlin.jvm.JvmInline
 
 /**
  * When soft wrap is enabled and the width of the text exceeds the width of its container, line
@@ -40,6 +41,7 @@
  *
  * @sample androidx.compose.ui.text.samples.AndroidLineBreakSample
  */
+@JvmInline
 @Immutable
 expect value class LineBreak private constructor(internal val mask: Int) {
     companion object {
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt
index 5ad11e9..4e60d4b 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.text.style
 
 import androidx.compose.ui.text.PlatformParagraphStyle
+import kotlin.jvm.JvmInline
 
 /**
  * The configuration for line height such as alignment of the line in the provided line height,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt
index 47b3aa7..9cd6708 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.graphics.lerp as lerpColor
 import androidx.compose.ui.text.lerpDiscrete
 import androidx.compose.ui.util.lerp
+import kotlin.jvm.JvmName
 
 /**
  * An internal interface to represent possible ways to draw Text e.g. color, brush. This interface
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/ActualAtomicReferenceJvm.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/ActualAtomicReferenceJvm.commonStubs.kt
new file mode 100644
index 0000000..be2547b
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/ActualAtomicReferenceJvm.commonStubs.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text
+
+internal actual class AtomicReference<V> actual constructor(value: V) {
+    init {
+        implementedInJetBrainsFork()
+    }
+
+    actual fun get(): V = implementedInJetBrainsFork()
+
+    actual fun set(value: V): Unit = implementedInJetBrainsFork()
+
+    actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+    actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/AnnotatedString.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/AnnotatedString.commonStubs.kt
new file mode 100644
index 0000000..4bd2666
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/AnnotatedString.commonStubs.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text
+
+internal actual fun AnnotatedString.transform(
+    transform: (String, Int, Int) -> String
+): AnnotatedString = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/JvmCharHelpers.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/JvmCharHelpers.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/JvmCharHelpers.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/JvmCharHelpers.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/NotImplemented.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/NotImplemented.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/NotImplemented.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/Paragraph.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/Paragraph.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/Paragraph.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/Paragraph.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/Savers.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/Savers.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/Savers.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/Savers.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/TextStyle.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/TextStyle.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/TextStyle.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/TextStyle.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.commonStubs.kt
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.commonStubs.kt
new file mode 100644
index 0000000..abd737f
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.commonStubs.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.font
+
+import androidx.compose.ui.text.implementedInJetBrainsFork
+
+internal actual fun FontSynthesis.synthesizeTypeface(
+    typeface: Any,
+    font: Font,
+    requestedWeight: FontWeight,
+    requestedStyle: FontStyle
+): Any = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.commonStubs.kt
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/input/GapBuffer.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/input/GapBuffer.commonStubs.kt
new file mode 100644
index 0000000..36833b1
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/input/GapBuffer.commonStubs.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.input
+
+import androidx.compose.ui.text.implementedInJetBrainsFork
+
+internal actual fun String.toCharArray(
+    destination: CharArray,
+    destinationOffset: Int,
+    startIndex: Int,
+    endIndex: Int
+): Unit = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.commonStubs.kt
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..14e41e4
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.internal
+
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/intl/DesktopPlatformLocale.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/intl/DesktopPlatformLocale.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/intl/DesktopPlatformLocale.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/intl/DesktopPlatformLocale.commonStubs.kt
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.commonStubs.kt
new file mode 100644
index 0000000..aea1184
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.commonStubs.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("EXTENSION_SHADOWED_BY_MEMBER")
+
+package androidx.compose.ui.text.intl
+
+import androidx.compose.ui.text.implementedInJetBrainsFork
+
+actual class PlatformLocale
+
+internal actual val PlatformLocale.language: String
+    get() = implementedInJetBrainsFork()
+
+internal actual val PlatformLocale.script: String
+    get() = implementedInJetBrainsFork()
+
+internal actual val PlatformLocale.region: String
+    get() = implementedInJetBrainsFork()
+
+internal actual fun PlatformLocale.getLanguageTag(): String = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/DesktopStringDelegate.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/DesktopStringDelegate.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/DesktopStringDelegate.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/DesktopStringDelegate.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraph.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraph.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraph.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraph.commonStubs.kt
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraphIntrinsics.jvmStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraphIntrinsics.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraphIntrinsics.jvmStubs.kt
rename to compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraphIntrinsics.commonStubs.kt
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/Synchronization.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/Synchronization.commonStubs.kt
new file mode 100644
index 0000000..ad58e16e
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/platform/Synchronization.commonStubs.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.platform
+
+import androidx.compose.ui.text.implementedInJetBrainsFork
+
+@PublishedApi internal actual class SynchronizedObject
+
+internal actual fun createSynchronizedObject(): SynchronizedObject = implementedInJetBrainsFork()
+
+@PublishedApi
+internal actual inline fun <R> synchronized(lock: SynchronizedObject, block: () -> R): R = block()
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/style/LineBreak.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/style/LineBreak.commonStubs.kt
new file mode 100644
index 0000000..0851b71
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/style/LineBreak.commonStubs.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.style
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.text.implementedInJetBrainsFork
+import kotlin.jvm.JvmInline
+
+@JvmInline
+@Immutable
+actual value class LineBreak private constructor(internal val mask: Int) {
+    actual companion object {
+        @Stable actual val Simple: LineBreak = implementedInJetBrainsFork()
+
+        @Stable actual val Heading: LineBreak = implementedInJetBrainsFork()
+
+        @Stable actual val Paragraph: LineBreak = implementedInJetBrainsFork()
+
+        @Stable actual val Unspecified: LineBreak = implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/style/TextMotion.commonStubs.kt b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/style/TextMotion.commonStubs.kt
new file mode 100644
index 0000000..2ef3ee0
--- /dev/null
+++ b/compose/ui/ui-text/src/commonStubsMain/kotlin/androidx/compose/ui/text/style/TextMotion.commonStubs.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.style
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.text.implementedInJetBrainsFork
+
+@Immutable
+actual class TextMotion private constructor() {
+    actual companion object {
+        actual val Static: TextMotion = implementedInJetBrainsFork()
+
+        actual val Animated: TextMotion = implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/ui/ui-text/src/commonTest/kotlin/androidx/compose/ui/text/SaversTest.kt b/compose/ui/ui-text/src/commonTest/kotlin/androidx/compose/ui/text/SaversTest.kt
deleted file mode 100644
index c6db31c..0000000
--- a/compose/ui/ui-text/src/commonTest/kotlin/androidx/compose/ui/text/SaversTest.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text
-
-import androidx.compose.runtime.saveable.SaverScope
-import androidx.compose.ui.text.style.Hyphens
-import androidx.compose.ui.text.style.LineBreak
-import androidx.compose.ui.text.style.LineHeightStyle
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.style.TextDirection
-import androidx.compose.ui.text.style.TextIndent
-import androidx.compose.ui.text.style.TextMotion
-import androidx.compose.ui.unit.sp
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class SaversTest {
-    private val defaultSaverScope = SaverScope { true }
-
-    @Test
-    fun test_ParagraphStyle_with_no_null_value() {
-        val original =
-            ParagraphStyle(
-                textAlign = TextAlign.Justify,
-                textDirection = TextDirection.Rtl,
-                lineHeight = 10.sp,
-                textIndent = TextIndent(firstLine = 2.sp, restLine = 3.sp),
-                platformStyle = PlatformParagraphStyle.Default,
-                lineHeightStyle = LineHeightStyle.Default,
-                lineBreak = LineBreak.Paragraph,
-                hyphens = Hyphens.Auto,
-                textMotion = TextMotion.Animated
-            )
-        val saved = save(original, ParagraphStyleSaver, defaultSaverScope)
-        val restored: ParagraphStyle? = restore(saved, ParagraphStyleSaver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_PlatformParagraphStyle() {
-        val original = PlatformParagraphStyle.Default
-        val saved = save(original, PlatformParagraphStyle.Saver, defaultSaverScope)
-        val restored: PlatformParagraphStyle? = restore(saved, PlatformParagraphStyle.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_LineBreak() {
-        val original = LineBreak.Paragraph
-        val saved = save(original, LineBreak.Saver, defaultSaverScope)
-        val restored: LineBreak? = restore(saved, LineBreak.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-
-    @Test
-    fun test_TextMotion() {
-        val original = TextMotion.Animated
-        val saved = save(original, TextMotion.Saver, defaultSaverScope)
-        val restored: TextMotion? = restore(saved, TextMotion.Saver)
-
-        assertThat(restored).isEqualTo(original)
-    }
-}
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.jvmStubs.kt b/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.jvmStubs.kt
deleted file mode 100644
index 2aad28e..0000000
--- a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.jvmStubs.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package androidx.compose.ui.text.font
-
-import androidx.compose.ui.text.implementedInJetBrainsFork
-
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-internal actual fun FontSynthesis.synthesizeTypeface(
-    typeface: Any,
-    font: Font,
-    requestedWeight: FontWeight,
-    requestedStyle: FontStyle
-): Any = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/style/LineBreak.jvmStubs.kt b/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/style/LineBreak.jvmStubs.kt
deleted file mode 100644
index 719a93f..0000000
--- a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/style/LineBreak.jvmStubs.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.style
-
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.Stable
-
-@Immutable
-@JvmInline
-actual value class LineBreak private constructor(internal val mask: Int) {
-    actual companion object {
-        @Stable actual val Simple: LineBreak = LineBreak(1)
-
-        @Stable actual val Heading: LineBreak = LineBreak(2)
-
-        @Stable actual val Paragraph: LineBreak = LineBreak(3)
-
-        @Stable actual val Unspecified: LineBreak = LineBreak(Int.MIN_VALUE)
-    }
-}
diff --git a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/style/TextMotion.jvmStubs.kt b/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/style/TextMotion.jvmStubs.kt
deleted file mode 100644
index eb79c74..0000000
--- a/compose/ui/ui-text/src/jvmStubsMain/kotlin/androidx/compose/ui/text/style/TextMotion.jvmStubs.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.style
-
-import androidx.compose.runtime.Immutable
-
-@Immutable
-actual class TextMotion private constructor() {
-    actual companion object {
-        actual val Static: TextMotion = TextMotion()
-
-        actual val Animated: TextMotion = TextMotion()
-    }
-}
diff --git a/compose/ui/ui-tooling-data/build.gradle b/compose/ui/ui-tooling-data/build.gradle
index d7cd9ff..ee30296 100644
--- a/compose/ui/ui-tooling-data/build.gradle
+++ b/compose/ui/ui-tooling-data/build.gradle
@@ -61,7 +61,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.annotation:annotation-experimental:1.4.1")
             }
         }
@@ -109,5 +109,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.tooling.data"
 }
diff --git a/compose/ui/ui-tooling-preview/build.gradle b/compose/ui/ui-tooling-preview/build.gradle
index c525563..8293248 100644
--- a/compose/ui/ui-tooling-preview/build.gradle
+++ b/compose/ui/ui-tooling-preview/build.gradle
@@ -58,7 +58,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.2.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
@@ -90,6 +90,7 @@
     description = "Compose tooling library API. This library provides the API required to declare" +
             " @Preview composables in user apps."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 android {
diff --git a/compose/ui/ui-tooling/build.gradle b/compose/ui/ui-tooling/build.gradle
index 4123be1..560d52b 100644
--- a/compose/ui/ui-tooling/build.gradle
+++ b/compose/ui/ui-tooling/build.gradle
@@ -61,7 +61,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 implementation(project(":compose:animation:animation"))
                 implementation("androidx.savedstate:savedstate-ktx:1.2.1")
                 implementation("androidx.compose.material:material:1.0.0")
@@ -88,6 +88,7 @@
                 implementation(libs.junit)
                 implementation(libs.testRunner)
                 implementation(libs.testRules)
+                implementation("androidx.compose.material:material-icons-core:1.6.7")
                 implementation(project(":compose:foundation:foundation-layout"))
                 implementation(project(":compose:foundation:foundation"))
                 implementation(project(":compose:test-utils"))
@@ -113,10 +114,14 @@
     inceptionYear = "2019"
     description = "Compose tooling library. This library exposes information to our tools for better IDE support."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:animation:animation:animation-samples"))
     // samples(project(":compose:animation:animation-core:animation-core-samples")) TODO(b/318840087)
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.tooling"
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/LayoutlibFontResourceLoader.android.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/LayoutlibFontResourceLoader.android.kt
index 88c9972..d21247f 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/LayoutlibFontResourceLoader.android.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/LayoutlibFontResourceLoader.android.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.graphics.Typeface
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.ResourceFont
@@ -42,7 +41,6 @@
 
 @RequiresApi(Build.VERSION_CODES.O)
 private object ResourceFontHelper {
-    @DoNotInline
     fun load(context: Context, font: ResourceFont): Typeface {
         return context.resources.getFont(font.resId)
     }
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewUtils.android.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewUtils.android.kt
index 958368d..d373115 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewUtils.android.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewUtils.android.kt
@@ -19,6 +19,7 @@
 import androidx.compose.ui.tooling.data.Group
 import androidx.compose.ui.tooling.data.UiToolingDataApi
 import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import kotlin.collections.removeLast as removeLastKt
 
 /** Tries to find the [Class] of the [PreviewParameterProvider] corresponding to the given FQN. */
 internal fun String.asPreviewProviderClass(): Class<out PreviewParameterProvider<*>>? {
@@ -119,7 +120,7 @@
     val result = mutableListOf<Group>()
     val stack = mutableListOf(root)
     while (stack.isNotEmpty()) {
-        val current = stack.removeLast()
+        val current = stack.removeLastKt()
         if (predicate(current)) {
             if (findOnlyFirst) {
                 return listOf(current)
diff --git a/compose/ui/ui-unit/build.gradle b/compose/ui/ui-unit/build.gradle
index b6d3fff..9aee596 100644
--- a/compose/ui/ui-unit/build.gradle
+++ b/compose/ui/ui-unit/build.gradle
@@ -33,6 +33,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -40,7 +41,7 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api(project(":compose:ui:ui-geometry"))
                 implementation("androidx.collection:collection:1.4.0")
                 implementation(project(":compose:runtime:runtime"))
@@ -63,16 +64,21 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
                 api("androidx.annotation:annotation-experimental:1.4.1")
                 implementation('androidx.collection:collection-ktx:1.2.0')
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -103,6 +109,7 @@
     inceptionYear = "2020"
     description = "Compose classes for simple units"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:ui:ui-unit:ui-unit-samples"))
 }
 
diff --git a/compose/ui/ui-unit/samples/build.gradle b/compose/ui/ui-unit/samples/build.gradle
index ae1cb6e..42100f5 100644
--- a/compose/ui/ui-unit/samples/build.gradle
+++ b/compose/ui/ui-unit/samples/build.gradle
@@ -51,5 +51,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.unit.samples"
 }
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
index e70985e..5dc4eb6 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.unit.Constraints.Companion.Infinity
+import kotlin.jvm.JvmInline
 import kotlin.math.min
 
 /**
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
index 12868d2..4cc9af8 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.util.packFloats
 import androidx.compose.ui.util.unpackFloat1
 import androidx.compose.ui.util.unpackFloat2
+import kotlin.jvm.JvmInline
 import kotlin.math.max
 import kotlin.math.min
 
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt
index 5453fc6..9bafe44 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt
@@ -26,6 +26,7 @@
 import androidx.compose.ui.util.packInts
 import androidx.compose.ui.util.unpackInt1
 import androidx.compose.ui.util.unpackInt2
+import kotlin.jvm.JvmInline
 
 /** Constructs a [IntOffset] from [x] and [y] position [Int] values. */
 @Stable fun IntOffset(x: Int, y: Int): IntOffset = IntOffset(packInts(x, y))
diff --git a/compose/ui/ui-unit/src/jvmStubsMain/kotlin/androidx/compose/ui/unit/FontScaling.jvmStubs.kt b/compose/ui/ui-unit/src/commonStubsMain/kotlin/androidx/compose/ui/unit/FontScaling.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-unit/src/jvmStubsMain/kotlin/androidx/compose/ui/unit/FontScaling.jvmStubs.kt
rename to compose/ui/ui-unit/src/commonStubsMain/kotlin/androidx/compose/ui/unit/FontScaling.commonStubs.kt
diff --git a/compose/ui/ui-unit/src/commonStubsMain/kotlin/androidx/compose/ui/unit/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/ui/ui-unit/src/commonStubsMain/kotlin/androidx/compose/ui/unit/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..44f7772
--- /dev/null
+++ b/compose/ui/ui-unit/src/commonStubsMain/kotlin/androidx/compose/ui/unit/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.unit.internal
+
+internal actual annotation class JvmDefaultWithCompatibility actual constructor()
diff --git a/compose/ui/ui-util/build.gradle b/compose/ui/ui-util/build.gradle
index d312d6c..9ea3f32 100644
--- a/compose/ui/ui-util/build.gradle
+++ b/compose/ui/ui-util/build.gradle
@@ -34,6 +34,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -63,8 +64,16 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -88,6 +97,7 @@
     inceptionYear = "2020"
     description = "Internal Compose utilities used by other modules"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 androidxCompose {
diff --git a/compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/InlineClassHelper.commonStubs.kt b/compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/InlineClassHelper.commonStubs.kt
new file mode 100644
index 0000000..9a4c815
--- /dev/null
+++ b/compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/InlineClassHelper.commonStubs.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.util
+
+actual fun floatFromBits(bits: Int): Float = implementedInJetBrainsFork()
+
+actual fun doubleFromBits(bits: Long): Double = implementedInJetBrainsFork()
+
+actual fun Float.fastRoundToInt(): Int = implementedInJetBrainsFork()
+
+actual fun Double.fastRoundToInt(): Int = implementedInJetBrainsFork()
diff --git a/compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/NotImplemented.commonStubs.kt b/compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/NotImplemented.commonStubs.kt
new file mode 100644
index 0000000..8dff401
--- /dev/null
+++ b/compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/NotImplemented.commonStubs.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.util
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun implementedInJetBrainsFork(): Nothing =
+    throw NotImplementedError(
+        """
+        Implemented only in JetBrains fork.
+        Please use `org.jetbrains.compose.ui:ui-util` package instead.
+        """
+            .trimIndent()
+    )
diff --git a/compose/ui/ui-util/src/jvmStubsMain/kotlin/androidx/compose/ui/util/Trace.jvmStubs.kt b/compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/Trace.commonStubs.kt
similarity index 100%
rename from compose/ui/ui-util/src/jvmStubsMain/kotlin/androidx/compose/ui/util/Trace.jvmStubs.kt
rename to compose/ui/ui-util/src/commonStubsMain/kotlin/androidx/compose/ui/util/Trace.commonStubs.kt
diff --git a/compose/ui/ui-viewbinding/build.gradle b/compose/ui/ui-viewbinding/build.gradle
index 5e60265..6390f95 100644
--- a/compose/ui/ui-viewbinding/build.gradle
+++ b/compose/ui/ui-viewbinding/build.gradle
@@ -54,10 +54,10 @@
     inceptionYear = "2020"
     description = "Compose integration with ViewBinding"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     samples(project(":compose:ui:ui-viewbinding:ui-viewbinding-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.viewbinding"
 }
diff --git a/compose/ui/ui-viewbinding/samples/build.gradle b/compose/ui/ui-viewbinding/samples/build.gradle
index c2e2d2c..05dbaa8 100644
--- a/compose/ui/ui-viewbinding/samples/build.gradle
+++ b/compose/ui/ui-viewbinding/samples/build.gradle
@@ -58,6 +58,7 @@
 }
 
 android {
+    compileSdk 35
     buildFeatures {
         viewBinding true
     }
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
index 56c5e4d..ad8a969 100644
--- a/compose/ui/ui/api/current.ignore
+++ b/compose/ui/ui/api/current.ignore
@@ -5,3 +5,7 @@
     Added method androidx.compose.ui.focus.FocusTargetModifierNode.requestFocus()
 AddedAbstractMethod: androidx.compose.ui.focus.FocusTargetModifierNode#setFocusability(int):
     Added method androidx.compose.ui.focus.FocusTargetModifierNode.setFocusability(int)
+
+
+RemovedMethod: androidx.compose.ui.layout.LayoutCoordinates#transformToScreen(float[]):
+    Removed method androidx.compose.ui.layout.LayoutCoordinates.transformToScreen(float[])
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 9284277..54ecd0c 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -191,6 +191,10 @@
   public static final class MotionDurationScale.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.ui.MotionDurationScale> {
   }
 
+  public final class SensitiveContentKt {
+    method public static androidx.compose.ui.Modifier sensitiveContent(androidx.compose.ui.Modifier, optional boolean isContentSensitive);
+  }
+
   @androidx.compose.runtime.ComposableTargetMarker(description="UI Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface UiComposable {
   }
 
@@ -1758,7 +1762,8 @@
   }
 
   @Deprecated public final class ConsumedData {
-    ctor @Deprecated public ConsumedData(optional @Deprecated boolean positionChange, optional @Deprecated boolean downChange);
+    ctor @Deprecated public ConsumedData();
+    ctor @Deprecated public ConsumedData(optional boolean positionChange, optional boolean downChange);
     method @Deprecated public boolean getDownChange();
     method @Deprecated public boolean getPositionChange();
     method @Deprecated public void setDownChange(boolean);
@@ -1918,6 +1923,7 @@
     method public int getType();
     method public long getUptimeMillis();
     method public boolean isConsumed();
+    property @Deprecated public final androidx.compose.ui.input.pointer.ConsumedData consumed;
     property public final java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical;
     property public final long id;
     property public final boolean isConsumed;
@@ -2227,7 +2233,6 @@
     method public long localToWindow(long relativeToLocal);
     method public default long screenToLocal(long relativeToScreen);
     method public default void transformFrom(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, float[] matrix);
-    method public default void transformToScreen(float[] matrix);
     method public long windowToLocal(long relativeToWindow);
     property public default boolean introducesMotionFrameOfReference;
     property public abstract boolean isAttached;
@@ -2246,6 +2251,7 @@
     method public static long positionInRoot(androidx.compose.ui.layout.LayoutCoordinates);
     method public static long positionInWindow(androidx.compose.ui.layout.LayoutCoordinates);
     method public static long positionOnScreen(androidx.compose.ui.layout.LayoutCoordinates);
+    method public static void transformToScreen(androidx.compose.ui.layout.LayoutCoordinates, float[] matrix);
   }
 
   public final class LayoutIdKt {
@@ -3321,18 +3327,18 @@
     method public int getCheckbox();
     method public int getDropdownList();
     method public int getImage();
-    method public int getNumberPicker();
     method public int getRadioButton();
     method public int getSwitch();
     method public int getTab();
+    method public int getValuePicker();
     property public final int Button;
     property public final int Checkbox;
     property public final int DropdownList;
     property public final int Image;
-    property public final int NumberPicker;
     property public final int RadioButton;
     property public final int Switch;
     property public final int Tab;
+    property public final int ValuePicker;
   }
 
   public final class ScrollAxisRange {
@@ -3719,6 +3725,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class DialogProperties {
+    ctor public DialogProperties();
     ctor @Deprecated public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy);
     ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean usePlatformDefaultWidth, optional boolean decorFitsSystemWindows);
     ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional boolean usePlatformDefaultWidth);
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
index 56c5e4d..ad8a969 100644
--- a/compose/ui/ui/api/restricted_current.ignore
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -5,3 +5,7 @@
     Added method androidx.compose.ui.focus.FocusTargetModifierNode.requestFocus()
 AddedAbstractMethod: androidx.compose.ui.focus.FocusTargetModifierNode#setFocusability(int):
     Added method androidx.compose.ui.focus.FocusTargetModifierNode.setFocusability(int)
+
+
+RemovedMethod: androidx.compose.ui.layout.LayoutCoordinates#transformToScreen(float[]):
+    Removed method androidx.compose.ui.layout.LayoutCoordinates.transformToScreen(float[])
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 56a0613..b704931 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -191,6 +191,10 @@
   public static final class MotionDurationScale.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.ui.MotionDurationScale> {
   }
 
+  public final class SensitiveContentKt {
+    method public static androidx.compose.ui.Modifier sensitiveContent(androidx.compose.ui.Modifier, optional boolean isContentSensitive);
+  }
+
   @androidx.compose.runtime.ComposableTargetMarker(description="UI Composable") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FILE, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.TYPE, kotlin.annotation.AnnotationTarget.TYPE_PARAMETER}) public @interface UiComposable {
   }
 
@@ -1758,7 +1762,8 @@
   }
 
   @Deprecated public final class ConsumedData {
-    ctor @Deprecated public ConsumedData(optional @Deprecated boolean positionChange, optional @Deprecated boolean downChange);
+    ctor @Deprecated public ConsumedData();
+    ctor @Deprecated public ConsumedData(optional boolean positionChange, optional boolean downChange);
     method @Deprecated public boolean getDownChange();
     method @Deprecated public boolean getPositionChange();
     method @Deprecated public void setDownChange(boolean);
@@ -1918,6 +1923,7 @@
     method public int getType();
     method public long getUptimeMillis();
     method public boolean isConsumed();
+    property @Deprecated public final androidx.compose.ui.input.pointer.ConsumedData consumed;
     property public final java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical;
     property public final long id;
     property public final boolean isConsumed;
@@ -2227,7 +2233,6 @@
     method public long localToWindow(long relativeToLocal);
     method public default long screenToLocal(long relativeToScreen);
     method public default void transformFrom(androidx.compose.ui.layout.LayoutCoordinates sourceCoordinates, float[] matrix);
-    method public default void transformToScreen(float[] matrix);
     method public long windowToLocal(long relativeToWindow);
     property public default boolean introducesMotionFrameOfReference;
     property public abstract boolean isAttached;
@@ -2246,6 +2251,7 @@
     method public static long positionInRoot(androidx.compose.ui.layout.LayoutCoordinates);
     method public static long positionInWindow(androidx.compose.ui.layout.LayoutCoordinates);
     method public static long positionOnScreen(androidx.compose.ui.layout.LayoutCoordinates);
+    method public static void transformToScreen(androidx.compose.ui.layout.LayoutCoordinates, float[] matrix);
   }
 
   public final class LayoutIdKt {
@@ -3381,18 +3387,18 @@
     method public int getCheckbox();
     method public int getDropdownList();
     method public int getImage();
-    method public int getNumberPicker();
     method public int getRadioButton();
     method public int getSwitch();
     method public int getTab();
+    method public int getValuePicker();
     property public final int Button;
     property public final int Checkbox;
     property public final int DropdownList;
     property public final int Image;
-    property public final int NumberPicker;
     property public final int RadioButton;
     property public final int Switch;
     property public final int Tab;
+    property public final int ValuePicker;
   }
 
   public final class ScrollAxisRange {
@@ -3779,6 +3785,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class DialogProperties {
+    ctor public DialogProperties();
     ctor @Deprecated public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy);
     ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional androidx.compose.ui.window.SecureFlagPolicy securePolicy, optional boolean usePlatformDefaultWidth, optional boolean decorFitsSystemWindows);
     ctor public DialogProperties(optional boolean dismissOnBackPress, optional boolean dismissOnClickOutside, optional boolean usePlatformDefaultWidth);
diff --git a/compose/ui/ui/benchmark/build.gradle b/compose/ui/ui/benchmark/build.gradle
index 8e0821a..9cff02b 100644
--- a/compose/ui/ui/benchmark/build.gradle
+++ b/compose/ui/ui/benchmark/build.gradle
@@ -50,6 +50,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.benchmark"
 }
 
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutCoordinatesBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutCoordinatesBenchmark.kt
new file mode 100644
index 0000000..c5f313a3
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutCoordinatesBenchmark.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.benchmark
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.ComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.doFramesUntilNoChangesPending
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class LayoutCoordinatesBenchmark {
+
+    @get:Rule val benchmarkRule = ComposeBenchmarkRule()
+
+    @Test
+    fun localPositionOfWithLayer() {
+        benchmarkRule.runBenchmarkFor({ LayoutCoordinatesTestCase(true) }) {
+            benchmarkRule.runOnUiThread { doFramesUntilNoChangesPending() }
+
+            benchmarkRule.measureRepeatedOnUiThread {
+                val testCase = getTestCase()
+                testCase.coordinates1.localPositionOf(testCase.coordinates2)
+                testCase.coordinates2.localPositionOf(testCase.coordinates1)
+            }
+        }
+    }
+
+    @Test
+    fun localPositionOfNoLayer() {
+        benchmarkRule.runBenchmarkFor({ LayoutCoordinatesTestCase(false) }) {
+            benchmarkRule.runOnUiThread { doFramesUntilNoChangesPending() }
+
+            benchmarkRule.measureRepeatedOnUiThread {
+                val testCase = getTestCase()
+                testCase.coordinates1.localPositionOf(testCase.coordinates2)
+                testCase.coordinates2.localPositionOf(testCase.coordinates1)
+            }
+        }
+    }
+
+    private class LayoutCoordinatesTestCase(val useLayer: Boolean) : ComposeTestCase {
+        lateinit var coordinates1: LayoutCoordinates
+        lateinit var coordinates2: LayoutCoordinates
+
+        @Composable
+        private fun NestedContent(depth: Int, isFirst: Boolean) {
+            if (depth == 0) {
+                Box(
+                    Modifier.fillMaxSize().onPlaced {
+                        if (isFirst) coordinates1 = it else coordinates2 = it
+                    }
+                )
+            } else {
+                val modifier = if (useLayer) Modifier.graphicsLayer {} else Modifier
+                Box(modifier.padding(1.dp)) { NestedContent(depth - 1, isFirst) }
+            }
+        }
+
+        @Composable
+        override fun Content() {
+            Column(Modifier.fillMaxSize()) {
+                Box(Modifier.weight(1f).fillMaxWidth()) { NestedContent(10, true) }
+                Box(Modifier.weight(1f).fillMaxWidth()) { NestedContent(10, false) }
+            }
+        }
+    }
+}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/focus/ParameterizedFocusBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/focus/ParameterizedFocusBenchmark.kt
index 8fae5e1..4dccf6b 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/focus/ParameterizedFocusBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/focus/ParameterizedFocusBenchmark.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.ReusableContent
@@ -26,17 +26,23 @@
 import androidx.compose.runtime.movableContentOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.ComposeTestCase
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
+import androidx.compose.testutils.assertNoPendingChanges
 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
 import androidx.compose.testutils.benchmark.toggleStateBenchmarkRecompose
+import androidx.compose.testutils.doFramesUntilNoChangesPending
+import androidx.compose.testutils.recomposeAssertHadChanges
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.benchmark.repeatModifier
 import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.focusTarget
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.focus.onFocusEvent
 import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,10 +60,12 @@
 
     @Test
     fun modifyActiveHierarchy_addRemoveSubtree() {
-        composeBenchmarkRule.toggleStateBenchmarkRecompose({
-            object : LayeredComposeTestCase(), ToggleableTestCase {
+        composeBenchmarkRule.toggleAlternatingStateBenchmarkRecompose({
+            object : LayeredComposeTestCase(), ToggleableAlternatingTestCase {
+
                 private val focusRequester = FocusRequester()
                 private var shouldAddNodes by mutableStateOf(false)
+                private var rootFocusState: FocusState? = null
 
                 @Composable
                 override fun MeasuredContent() {
@@ -74,17 +82,22 @@
 
                 @Composable
                 override fun ContentWrappers(content: @Composable () -> Unit) {
-                    Box(focusTargetModifiers()) {
+                    Box(
+                        Modifier.fillMaxSize()
+                            .onFocusEvent { rootFocusState = it }
+                            .then(focusTargetModifiers())
+                    ) {
                         Column {
-                            content()
                             Box(Modifier.focusRequester(focusRequester).focusTarget())
                             LaunchedEffect(Unit) { focusRequester.requestFocus() }
+                            content()
                         }
                     }
                 }
 
-                override fun toggleState() {
-                    shouldAddNodes = !shouldAddNodes
+                override fun toggleState(isStateChangeMeasured: Boolean) {
+                    assertThat(rootFocusState?.hasFocus).isTrue()
+                    shouldAddNodes = isStateChangeMeasured
                 }
             }
         })
@@ -92,18 +105,67 @@
 
     @Test
     fun modifyActiveHierarchy_addRemoveModifiersWithExistingSubtree() {
-        composeBenchmarkRule.toggleStateBenchmarkRecompose({
-            object : LayeredComposeTestCase(), ToggleableTestCase {
+        composeBenchmarkRule.toggleAlternatingStateBenchmarkRecompose({
+            object : LayeredComposeTestCase(), ToggleableAlternatingTestCase {
+
+                private val focusRequester = FocusRequester()
+                private var shouldAddNodes by mutableStateOf(false)
+                private var rootFocusState: FocusState? = null
+
+                @Composable
+                override fun MeasuredContent() {
+                    Box(Modifier.thenIf(shouldAddNodes) { focusTargetModifiers() }) {
+                        repeat(count) {
+                            Box(Modifier.focusTarget()) {
+                                repeat(count) { Box(Modifier.focusTarget()) }
+                            }
+                        }
+                    }
+                }
+
+                @Composable
+                override fun ContentWrappers(content: @Composable () -> Unit) {
+                    Box(
+                        Modifier.fillMaxSize()
+                            .onFocusEvent { rootFocusState = it }
+                            .then(focusTargetModifiers())
+                    ) {
+                        Column {
+                            Box(Modifier.focusRequester(focusRequester).focusTarget())
+                            LaunchedEffect(Unit) { focusRequester.requestFocus() }
+                            content()
+                        }
+                    }
+                }
+
+                override fun toggleState(isStateChangeMeasured: Boolean) {
+                    assertThat(rootFocusState?.hasFocus).isTrue()
+                    shouldAddNodes = isStateChangeMeasured
+                }
+            }
+        })
+    }
+
+    @Test
+    fun modifyActiveHierarchy_addRemoveModifiersWithExistingActiveSubtree() {
+        composeBenchmarkRule.toggleAlternatingStateBenchmarkRecompose({
+            object : LayeredComposeTestCase(), ToggleableAlternatingTestCase {
+
                 private val focusRequester = FocusRequester()
                 private var shouldAddNodes by mutableStateOf(false)
 
                 @Composable
                 override fun MeasuredContent() {
                     Box(Modifier.thenIf(shouldAddNodes) { focusTargetModifiers() }) {
-                        repeat(count) {
-                            Box(Modifier.thenIf(shouldAddNodes) { Modifier.focusTarget() }) {
-                                repeat(count) {
-                                    Box(Modifier.thenIf(shouldAddNodes) { Modifier.focusTarget() })
+                        for (i in 0 until count) {
+                            Box(Modifier.focusTarget()) {
+                                for (j in 0 until count) {
+                                    if (i == count - 1 && j == count - 1) {
+                                        // Focus on the last child in a depth first traversal
+                                        Box(Modifier.focusRequester(focusRequester).focusTarget())
+                                    } else {
+                                        Box(Modifier.focusTarget())
+                                    }
                                 }
                             }
                         }
@@ -112,17 +174,14 @@
 
                 @Composable
                 override fun ContentWrappers(content: @Composable () -> Unit) {
-                    Box(focusTargetModifiers()) {
-                        Column {
-                            content()
-                            Box(Modifier.focusRequester(focusRequester).focusTarget())
-                            LaunchedEffect(Unit) { focusRequester.requestFocus() }
-                        }
+                    Box(Modifier.fillMaxSize().then(focusTargetModifiers())) {
+                        content()
+                        LaunchedEffect(Unit) { focusRequester.requestFocus() }
                     }
                 }
 
-                override fun toggleState() {
-                    shouldAddNodes = !shouldAddNodes
+                override fun toggleState(isStateChangeMeasured: Boolean) {
+                    shouldAddNodes = isStateChangeMeasured
                 }
             }
         })
@@ -140,6 +199,11 @@
                     ReusableContent(reuseKey) { Box(focusTargetModifiers()) }
                 }
 
+                @Composable
+                override fun ContentWrappers(content: @Composable () -> Unit) {
+                    Box(Modifier.fillMaxSize()) { content() }
+                }
+
                 override fun toggleState() {
                     reuseKey++
                 }
@@ -154,6 +218,7 @@
 
                 private val focusRequester = FocusRequester()
                 private var reuseKey by mutableStateOf(0)
+                private var rootFocusState: FocusState? = null
 
                 @Composable
                 override fun MeasuredContent() {
@@ -162,13 +227,17 @@
 
                 @Composable
                 override fun ContentWrappers(content: @Composable () -> Unit) {
-                    Box(Modifier.focusRequester(focusRequester).focusTarget()) {
+                    Column(
+                        Modifier.fillMaxSize().onFocusEvent { rootFocusState = it }.focusTarget()
+                    ) {
+                        Box(Modifier.focusRequester(focusRequester).focusTarget())
                         LaunchedEffect(Unit) { focusRequester.requestFocus() }
                         content()
                     }
                 }
 
                 override fun toggleState() {
+                    assertThat(rootFocusState?.hasFocus).isTrue()
                     reuseKey++
                 }
             }
@@ -186,12 +255,17 @@
                 @Composable
                 override fun MeasuredContent() {
                     if (moveContent) {
-                        Box(Modifier.size(5.dp)) { content() }
+                        Box { content() }
                     } else {
-                        Box(Modifier.size(10.dp)) { content() }
+                        Box { content() }
                     }
                 }
 
+                @Composable
+                override fun ContentWrappers(content: @Composable () -> Unit) {
+                    Box(Modifier.fillMaxSize()) { content() }
+                }
+
                 override fun toggleState() {
                     moveContent = !moveContent
                 }
@@ -207,25 +281,30 @@
                 private val focusRequester = FocusRequester()
                 private var moveContent by mutableStateOf(false)
                 private val movableContent = movableContentOf { Box(focusTargetModifiers()) }
+                private var rootFocusState: FocusState? = null
 
                 @Composable
                 override fun MeasuredContent() {
                     if (moveContent) {
-                        Box(Modifier.size(5.dp)) { movableContent() }
+                        Box { movableContent() }
                     } else {
-                        Box(Modifier.size(10.dp)) { movableContent() }
+                        Box { movableContent() }
                     }
                 }
 
                 @Composable
                 override fun ContentWrappers(content: @Composable () -> Unit) {
-                    Box(Modifier.focusRequester(focusRequester).focusTarget()) {
+                    Column(
+                        Modifier.fillMaxSize().onFocusEvent { rootFocusState = it }.focusTarget()
+                    ) {
+                        Box(Modifier.focusRequester(focusRequester).focusTarget())
                         LaunchedEffect(Unit) { focusRequester.requestFocus() }
                         content()
                     }
                 }
 
                 override fun toggleState() {
+                    assertThat(rootFocusState?.hasFocus).isTrue()
                     moveContent = !moveContent
                 }
             }
@@ -237,4 +316,66 @@
     private inline fun Modifier.thenIf(condition: Boolean, block: () -> Modifier): Modifier {
         return if (condition) then(block()) else this
     }
+
+    /**
+     * Measures the recomposition time of the hierarchy after changing a state and then changes the
+     * state again to return to the initial composition excluding that from measurement.
+     *
+     * This is useful for benchmarks that toggle state between 2 values, i.e., an initial and target
+     * values, and only the change to the target value is intended to be measured. For example, with
+     * a boolean state, nodes are added when the state changes from false to true and removed when
+     * the state changes back to false. In this example scenario, only the node addition is
+     * measured.
+     *
+     * @param assertOneRecomposition whether the benchmark will fail if there are pending
+     *   recompositions after the first recomposition. By default this is true to enforce
+     *   correctness in the benchmark, but for components that have animations after being
+     *   recomposed this can be turned off to benchmark just the first recomposition without any
+     *   pending animations.
+     * @param requireRecomposition whether the benchmark will fail if no changes were produce from a
+     *   recomposition.there are pending recompositions. By default this is true to enforce
+     *   correctness.
+     */
+    private fun <T> ComposeBenchmarkRule.toggleAlternatingStateBenchmarkRecompose(
+        caseFactory: () -> T,
+        assertOneRecomposition: Boolean = true,
+        requireRecomposition: Boolean = true,
+    ) where T : ComposeTestCase, T : ToggleableAlternatingTestCase {
+
+        runBenchmarkFor(caseFactory) {
+            fun recomposeWithAssertions() {
+                if (requireRecomposition) {
+                    recomposeAssertHadChanges()
+                } else {
+                    recompose()
+                }
+                if (assertOneRecomposition) {
+                    assertNoPendingChanges()
+                }
+            }
+
+            runOnUiThread { doFramesUntilNoChangesPending() }
+            measureRepeatedOnUiThread {
+                runWithTimingDisabled { getTestCase().toggleState(true) }
+                recomposeWithAssertions()
+                runWithTimingDisabled {
+                    getTestCase().toggleState(false)
+                    recomposeWithAssertions()
+                }
+            }
+        }
+    }
+
+    /**
+     * Test case that triggers a state change with alternating enabling/disabling measurement of the
+     * effect of the state change.
+     *
+     * This is similar to [ToggleableTestCase] with the difference that this allows measuring only
+     * one direction of state change. For example, this can be used to only measure checking the
+     * checkbox and skipping measurement of unchecking it. This is run multiple times during a
+     * benchmark run.
+     */
+    private interface ToggleableAlternatingTestCase {
+        fun toggleState(isStateChangeMeasured: Boolean)
+    }
 }
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeMultiFingerInputUIOnlyBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeMultiFingerInputUIOnlyBenchmark.kt
new file mode 100644
index 0000000..d621768
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeMultiFingerInputUIOnlyBenchmark.kt
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.benchmark.input.pointer
+
+import android.view.MotionEvent
+import android.view.View
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.ComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.doFramesUntilNoChangesPending
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Compose benchmarks for multiple finger input (down/up and down/move/up) on an item using ONLY UI
+ * module APIs (no Foundation calls which is what differentiates it from
+ * [ComposeTapIntegrationBenchmark]). The benchmark uses pointerInput (ui) + awaitPointerEventScope
+ * (ui) + awaitPointerEvent (ui) to track simple down/move/up inputs.
+ *
+ * The intent is to measure the speed of all parts necessary for a normal down, (move for some
+ * benchmarks) and up starting from [MotionEvent]s getting dispatched to a particular view. The test
+ * therefore includes hit testing and dispatch.
+ *
+ * The hierarchy is set up to look like: rootView -> Column -> Text (with click listener) -> Text
+ * (with click listener) -> Text (with click listener) -> ...
+ *
+ * MotionEvents are dispatched to rootView as ACTION_DOWN and ACTION_UP (and for some benchmarks
+ * ACTION_MOVE(s) are added). The validity of the test is verified inside awaitPointerEventScope { }
+ * with com.google.common.truth.Truth.assertThat and by counting the events and later verifying that
+ * they count is sufficiently high.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ComposeMultiFingerInputUIOnlyBenchmark {
+
+    @get:Rule val benchmarkRule = ComposeBenchmarkRule()
+
+    @Test
+    fun clickOnLateItem() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at 0 will be hit tested late.
+        clickOnItem(item = 0, expectedLabel = "0", numberOfMoves = 0, numberOfFingers = 3)
+    }
+
+    // This test requires less hit testing so changes to dispatch will be tracked more by this test.
+    @Test
+    fun clickOnEarlyItemFyi() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at NumItems - 1 will be hit tested early.
+        val lastItem = NumItems - 1
+        clickOnItem(
+            item = lastItem,
+            expectedLabel = "$lastItem",
+            numberOfMoves = 0,
+            numberOfFingers = 3
+        )
+    }
+
+    @Test
+    fun clickWithMoveOnLateItem() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at 0 will be hit tested late.
+        clickOnItem(item = 0, expectedLabel = "0", numberOfMoves = 6, numberOfFingers = 3)
+    }
+
+    // This test requires less hit testing so changes to dispatch will be tracked more by this test.
+    @Test
+    fun clickWithMoveOnEarlyItemFyi() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at NumItems - 1 will be hit tested early.
+        val lastItem = NumItems - 1
+        clickOnItem(
+            item = lastItem,
+            expectedLabel = "$lastItem",
+            numberOfMoves = 6,
+            numberOfFingers = 3
+        )
+    }
+
+    @Test
+    fun clickWithMoveAndFlingHistoryOnLateItem() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at 0 will be hit tested late.
+        clickOnItem(
+            item = 0,
+            expectedLabel = "0",
+            numberOfMoves = 6,
+            numberOfFingers = 3,
+            enableHistory = true
+        )
+    }
+
+    // This test requires less hit testing so changes to dispatch will be tracked more by this test.
+    @Test
+    fun clickWithMoveAndFlingHistoryOnEarlyItemFyi() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at NumItems - 1 will be hit tested early.
+        val lastItem = NumItems - 1
+        clickOnItem(
+            item = lastItem,
+            expectedLabel = "$lastItem",
+            numberOfMoves = 6,
+            numberOfFingers = 3,
+            enableHistory = true
+        )
+    }
+
+    private fun clickOnItem(
+        item: Int,
+        expectedLabel: String,
+        numberOfMoves: Int,
+        numberOfFingers: Int,
+        enableHistory: Boolean = false
+    ) {
+        val initialTimeForFirstEvent = 0
+        val initialXForFirstEvent = 0f
+        // half height of an item + top of the chosen item = middle of the chosen item
+        val y = (ItemHeightPx / 2) + (item * ItemHeightPx)
+
+        benchmarkRule.runBenchmarkFor({ ComposeTapTestCase() }) {
+            lateinit var case: ComposeTapTestCase
+            lateinit var rootView: View
+
+            benchmarkRule.runOnUiThread {
+                doFramesUntilNoChangesPending()
+
+                case = getTestCase()
+                case.expectedLabel = expectedLabel
+
+                rootView = getHostView()
+            }
+
+            // Create all MotionEvents
+            // 1. Create downs
+            val downs =
+                createDowns(
+                    initialX = initialXForFirstEvent,
+                    initialTime = initialTimeForFirstEvent,
+                    y = y,
+                    rootView = rootView,
+                    numberOfEvents = numberOfFingers,
+                )
+
+            assertThat(downs.size).isEqualTo(numberOfFingers)
+
+            // 2. Create moves
+            // Get start time for moves
+            val lastDown = downs.last()
+            val initialMoveTime = lastDown.eventTime.toInt() + DefaultPointerInputTimeDelta
+
+            // Get start x, y, and pointer id for moves
+            val initialMoveDataSimplified =
+                Array(lastDown.pointerCount) { index ->
+                    BenchmarkSimplifiedPointerInputPointer(
+                        id = lastDown.getPointerId(index),
+                        x = lastDown.getX(index) + DefaultPointerInputMoveAmountPx,
+                        y = lastDown.getY(index)
+                    )
+                }
+
+            val moves =
+                createMoveMotionEvents(
+                    initialTime = initialMoveTime,
+                    initialPointers = initialMoveDataSimplified,
+                    rootView = rootView,
+                    numberOfMoveEvents = numberOfMoves,
+                    enableFlingStyleHistory = enableHistory
+                )
+
+            // 2. Create ups
+            // Get start time for ups
+            val lastMove =
+                if (moves.isNotEmpty()) {
+                    moves.last()
+                } else {
+                    lastDown
+                }
+            val initialUpTime = lastMove.eventTime.toInt() + DefaultPointerInputTimeDelta
+
+            // Get start x, y, and pointer id for ups
+            val initialUpDataSimplified =
+                Array(lastMove.pointerCount) { index ->
+                    BenchmarkSimplifiedPointerInputPointer(
+                        id = lastMove.getPointerId(index),
+                        x = lastMove.getX(index),
+                        y = lastMove.getY(index)
+                    )
+                }
+
+            val ups =
+                createUps(
+                    initialTime = initialUpTime,
+                    initialPointers = initialUpDataSimplified,
+                    rootView = rootView,
+                )
+
+            assertThat(ups.size).isEqualTo(numberOfFingers)
+
+            benchmarkRule.measureRepeatedOnUiThread {
+                // Trigger and verify all up events.
+                for (down in downs) {
+                    rootView.dispatchTouchEvent(down)
+                    case.expectedPressCount++
+                    assertThat(case.actualPressCount).isEqualTo(case.expectedPressCount)
+                }
+
+                // Trigger and verify all move events.
+                for (move in moves) {
+                    rootView.dispatchTouchEvent(move)
+                    case.expectedMoveCount++
+                    assertThat(case.actualMoveCount).isEqualTo(case.expectedMoveCount)
+                }
+
+                // Double checks move count again (in case there weren't any moves).
+                assertThat(case.actualMoveCount).isEqualTo(case.expectedMoveCount)
+
+                // Trigger and verify all down events.
+                for (up in ups) {
+                    rootView.dispatchTouchEvent(up)
+                    case.expectedReleaseCount++
+                    assertThat(case.actualReleaseCount).isEqualTo(case.expectedReleaseCount)
+                }
+
+                assertThat(case.actualOtherEventCount).isEqualTo(case.expectedOtherEventCount)
+            }
+        }
+    }
+
+    private class ComposeTapTestCase : ComposeTestCase {
+        private var itemHeightDp: Dp? = null // Is set to correct value during composition.
+        var actualPressCount = 0
+        var expectedPressCount = 0
+
+        var actualMoveCount = 0
+        var expectedMoveCount = 0
+
+        var actualReleaseCount = 0
+        var expectedReleaseCount = 0
+
+        var actualOtherEventCount = 0
+        var expectedOtherEventCount = 0
+
+        lateinit var expectedLabel: String
+
+        @Composable
+        override fun Content() {
+            with(LocalDensity.current) { itemHeightDp = ItemHeightPx.toDp() }
+
+            EmailList(NumItems)
+        }
+
+        @Composable
+        fun EmailList(count: Int) {
+            Column { repeat(count) { i -> Email("$i") } }
+        }
+
+        @Composable
+        fun Email(label: String) {
+            BasicText(
+                text = label,
+                modifier =
+                    Modifier.pointerInput(label) {
+                            awaitPointerEventScope {
+                                while (true) {
+                                    assertThat(label).isEqualTo(expectedLabel)
+                                    val event = awaitPointerEvent()
+
+                                    when (event.type) {
+                                        PointerEventType.Press -> {
+                                            actualPressCount++
+                                        }
+                                        PointerEventType.Move -> {
+                                            actualMoveCount++
+                                        }
+                                        PointerEventType.Release -> {
+                                            actualReleaseCount++
+                                        }
+                                        else -> {
+                                            actualOtherEventCount++
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        .fillMaxWidth()
+                        .requiredHeight(itemHeightDp!!)
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt
index 7f03073..2bc768a 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt
@@ -94,11 +94,32 @@
         clickOnItem(lastItem, "$lastItem", 6)
     }
 
-    private fun clickOnItem(item: Int, expectedLabel: String, numberOfMoves: Int) {
+    @Test
+    fun clickWithMoveAndFlingHistoryOnLateItem() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at 0 will be hit tested late.
+        clickOnItem(0, "0", 6, true)
+    }
+
+    // This test requires less hit testing so changes to dispatch will be tracked more by this test.
+    @Test
+    fun clickWithMoveAndFlingHistoryOnEarlyItemFyi() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at NumItems - 1 will be hit tested early.
+        val lastItem = NumItems - 1
+        clickOnItem(lastItem, "$lastItem", 6, true)
+    }
+
+    private fun clickOnItem(
+        item: Int,
+        expectedLabel: String,
+        numberOfMoves: Int,
+        enableHistory: Boolean = false
+    ) {
+        val initialTimeForFirstEvent = 0
+        val initialXForFirstEvent = 0f
         // half height of an item + top of the chosen item = middle of the chosen item
         val y = (ItemHeightPx / 2) + (item * ItemHeightPx)
-        val xDown = 0f
-        val xMoveInitial = xDown + MOVE_AMOUNT_PX
 
         benchmarkRule.runBenchmarkFor({ ComposeTapTestCase() }) {
             lateinit var case: ComposeTapTestCase
@@ -113,38 +134,58 @@
                 rootView = getHostView()
             }
 
-            // Simple Events
-            val down =
-                MotionEvent(
-                    0,
-                    MotionEvent.ACTION_DOWN,
-                    1,
-                    0,
-                    arrayOf(PointerProperties(0)),
-                    arrayOf(PointerCoords(xDown, y)),
-                    rootView
-                )
-
-            val (time, x, moves) =
-                createMoves(
-                    initialX = xMoveInitial,
-                    initialTime = 100,
+            // Create Events
+            val downs =
+                createDowns(
+                    initialX = initialXForFirstEvent,
+                    initialTime = initialTimeForFirstEvent,
                     y = y,
                     rootView = rootView,
-                    numberOfMoveEvents = numberOfMoves
+                    numberOfEvents = 1,
                 )
 
-            val up =
-                MotionEvent(
-                    time,
-                    MotionEvent.ACTION_UP,
-                    1,
-                    0,
-                    arrayOf(PointerProperties(0)),
-                    arrayOf(PointerCoords(x, y)),
-                    rootView
+            assertThat(downs.size).isEqualTo(1)
+
+            val down = downs.last()
+
+            val initialMoveX = down.x + DefaultPointerInputMoveAmountPx
+            val initialMoveTime = down.eventTime.toInt() + DefaultPointerInputTimeDelta
+
+            val moves =
+                createMoveMotionEvents(
+                    initialTime = initialMoveTime,
+                    initialPointers =
+                        arrayOf(
+                            BenchmarkSimplifiedPointerInputPointer(id = 0, x = initialMoveX, y = y)
+                        ),
+                    rootView = rootView,
+                    numberOfMoveEvents = numberOfMoves,
+                    enableFlingStyleHistory = enableHistory
                 )
 
+            val lastMotionEvent =
+                if (moves.isNotEmpty()) {
+                    moves.last()
+                } else {
+                    down
+                }
+            val upEventTime = lastMotionEvent.eventTime.toInt() + DefaultPointerInputTimeDelta
+            val upEventX = lastMotionEvent.x + DefaultPointerInputMoveAmountPx
+
+            val ups =
+                createUps(
+                    initialTime = upEventTime,
+                    initialPointers =
+                        arrayOf(
+                            BenchmarkSimplifiedPointerInputPointer(id = 0, x = upEventX, y = y)
+                        ),
+                    rootView = rootView
+                )
+
+            assertThat(ups.size).isEqualTo(1)
+
+            val up = ups.last()
+
             benchmarkRule.measureRepeatedOnUiThread {
                 rootView.dispatchTouchEvent(down)
                 case.expectedPressCount++
@@ -155,6 +196,7 @@
                     case.expectedMoveCount++
                     assertThat(case.actualMoveCount).isEqualTo(case.expectedMoveCount)
                 }
+
                 // Double checks move count again (in case there weren't any moves).
                 assertThat(case.actualMoveCount).isEqualTo(case.expectedMoveCount)
 
@@ -167,37 +209,6 @@
         }
     }
 
-    private fun createMoves(
-        initialX: Float,
-        initialTime: Int,
-        y: Float, // Same Y used for all moves
-        rootView: View,
-        numberOfMoveEvents: Int,
-        timeDelta: Int = 100,
-        moveDelta: Float = MOVE_AMOUNT_PX
-    ): Triple<Int, Float, Array<MotionEvent>> {
-        var time = initialTime
-        var x = initialX
-
-        val moveMotionEvents =
-            Array(numberOfMoveEvents) {
-                val move =
-                    MotionEvent(
-                        time,
-                        MotionEvent.ACTION_MOVE,
-                        1,
-                        0,
-                        arrayOf(PointerProperties(0)),
-                        arrayOf(PointerCoords(x, y)),
-                        rootView
-                    )
-                time += timeDelta
-                x += moveDelta
-                move
-            }
-        return Triple(time, x, moveMotionEvents)
-    }
-
     private class ComposeTapTestCase : ComposeTestCase {
         private var itemHeightDp: Dp? = null // Is set to correct value during composition.
         var actualPressCount = 0
@@ -259,8 +270,4 @@
             )
         }
     }
-
-    companion object {
-        private const val MOVE_AMOUNT_PX = 10f
-    }
 }
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/UtilsTest.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/UtilsTest.kt
new file mode 100644
index 0000000..d7dfbd5
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/UtilsTest.kt
@@ -0,0 +1,665 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.benchmark.input.pointer
+
+import android.content.Context
+import android.view.MotionEvent
+import android.view.View
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.abs
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class UtilsTest {
+    // Tests for Down Motion Event Creation <---------------
+    @Test
+    fun createDownMotionEvents_noEvent() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 0
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val downs =
+            createDowns(
+                initialX = xMoveInitial,
+                initialTime = initialTime,
+                y = y,
+                rootView = view,
+                numberOfEvents = numberOfEvents,
+            )
+
+        // Should just return an empty array
+        assertThat(downs.size).isEqualTo(numberOfEvents)
+    }
+
+    @Test
+    fun createDownMotionEvents_oneEvent() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 1
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val downs =
+            createDowns(
+                initialX = xMoveInitial,
+                initialTime = initialTime,
+                y = y,
+                rootView = view,
+                numberOfEvents = numberOfEvents,
+            )
+
+        assertThat(downs.size).isEqualTo(numberOfEvents)
+
+        for ((index, down) in downs.withIndex()) {
+            if (index == 0) {
+                assertThat(down.actionMasked).isEqualTo(MotionEvent.ACTION_DOWN)
+            } else {
+                assertThat(down.actionMasked).isEqualTo(MotionEvent.ACTION_POINTER_DOWN)
+            }
+
+            val expectedTime = initialTime + (index * DefaultPointerInputTimeDelta)
+            assertThat(down.eventTime).isEqualTo(expectedTime)
+
+            val expectedX = xMoveInitial + (index * DefaultPointerInputMoveAmountPx)
+            assertThat(down.x).isEqualTo(expectedX)
+
+            assertThat(down.y).isEqualTo(y)
+            assertThat(down.historySize).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun createDownMotionEvents_sixEventsWithSixPointers() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val downs =
+            createDowns(
+                initialX = xMoveInitial,
+                initialTime = initialTime,
+                y = y,
+                rootView = view,
+                numberOfEvents = numberOfEvents,
+            )
+
+        assertThat(downs.size).isEqualTo(numberOfEvents)
+
+        for ((index, down) in downs.withIndex()) {
+            if (index == 0) {
+                assertThat(down.actionMasked).isEqualTo(MotionEvent.ACTION_DOWN)
+            } else {
+                assertThat(down.actionMasked).isEqualTo(MotionEvent.ACTION_POINTER_DOWN)
+            }
+
+            val expectedTime = initialTime + (index * DefaultPointerInputTimeDelta)
+            assertThat(down.eventTime).isEqualTo(expectedTime)
+
+            val expectedX = xMoveInitial + (index * DefaultPointerInputMoveAmountPx)
+
+            val pointerId: Int = down.getPointerId(index)
+            val localPointerCoords = MotionEvent.PointerCoords()
+            down.getPointerCoords(pointerId, localPointerCoords)
+
+            assertThat(localPointerCoords.x).isEqualTo(expectedX)
+            assertThat(localPointerCoords.y).isEqualTo(y)
+
+            assertThat(down.historySize).isEqualTo(0)
+        }
+    }
+
+    // Tests for Up Motion Event Creation <---------------
+    @Test
+    fun createUpMotionEvents_noEvent() {
+        val initialTime = 100
+
+        val simplifiedUps = arrayOf<BenchmarkSimplifiedPointerInputPointer>()
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val ups =
+            createUps(
+                initialTime = initialTime,
+                initialPointers = simplifiedUps,
+                rootView = view,
+            )
+
+        // Should just return an empty array
+        assertThat(ups.size).isEqualTo(simplifiedUps.size)
+    }
+
+    @Test
+    fun createUpMotionEvents_oneEvent() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val simplifiedUps =
+            arrayOf(BenchmarkSimplifiedPointerInputPointer(id = 0, x = xMoveInitial, y = y))
+
+        val ups =
+            createUps(
+                initialTime = initialTime,
+                initialPointers = simplifiedUps,
+                rootView = view,
+            )
+
+        assertThat(ups.size).isEqualTo(simplifiedUps.size)
+
+        for ((index, move) in ups.withIndex()) {
+            if (index == (ups.size - 1)) { // last event should be ACTION_UP
+                assertThat(move.actionMasked).isEqualTo(MotionEvent.ACTION_UP)
+            } else {
+                assertThat(move.actionMasked).isEqualTo(MotionEvent.ACTION_POINTER_UP)
+            }
+
+            val expectedTime = initialTime + (index * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+
+            val expectedX = simplifiedUps[index].x
+            assertThat(move.x).isEqualTo(expectedX)
+
+            val expectedY = simplifiedUps[index].y
+            assertThat(move.y).isEqualTo(expectedY)
+            assertThat(move.historySize).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun createUpMotionEvents_sixEventsWithSixPointers() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+
+        val simplifiedUps =
+            Array(numberOfEvents) { index ->
+                BenchmarkSimplifiedPointerInputPointer(
+                    id = index,
+                    x = xMoveInitial + (index * DefaultPointerInputMoveAmountPx),
+                    y = y
+                )
+            }
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val ups =
+            createUps(
+                initialTime = initialTime,
+                initialPointers = simplifiedUps,
+                rootView = view,
+            )
+
+        assertThat(ups.size).isEqualTo(simplifiedUps.size)
+
+        // The main x and y will always be the first pointer, but we want to verify that for all
+        // events.
+        val expectedMainX = simplifiedUps[0].x
+        val expectedMainY = simplifiedUps[0].y
+
+        for ((index, move) in ups.withIndex()) {
+            if (index == (ups.size - 1)) { // last event should be ACTION_UP
+                assertThat(move.actionMasked).isEqualTo(MotionEvent.ACTION_UP)
+            } else {
+                assertThat(move.actionMasked).isEqualTo(MotionEvent.ACTION_POINTER_UP)
+            }
+
+            val expectedTime = initialTime + (index * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+
+            assertThat(move.x).isEqualTo(expectedMainX)
+            assertThat(move.y).isEqualTo(expectedMainY)
+
+            // Check all pointers are there and valid
+            val expectedPointerCount = simplifiedUps.size - index
+            assertThat(move.pointerCount).isEqualTo(expectedPointerCount)
+
+            for (pointerIndex in 0 until move.pointerCount) {
+                val pointerId: Int = move.getPointerId(pointerIndex)
+                val localPointerCoords = MotionEvent.PointerCoords()
+                move.getPointerCoords(pointerId, localPointerCoords)
+
+                val expectedPointerCoords = simplifiedUps[pointerIndex]
+                assertThat(localPointerCoords.x).isEqualTo(expectedPointerCoords.x)
+                assertThat(localPointerCoords.y).isEqualTo(expectedPointerCoords.y)
+            }
+
+            assertThat(move.historySize).isEqualTo(0)
+        }
+    }
+
+    // Tests for Move Motion Event Creation <---------------
+    // Note: For tests with history, I am only checking the history count, not each history's x/y.
+    // One pointer/finger
+    @Test
+    fun createMoveMotionEvents_sixEventsOnePointerNegativeMoveDeltaWithoutHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val enableFlingStyleHistory = false
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers =
+                    arrayOf(
+                        BenchmarkSimplifiedPointerInputPointer(id = 0, x = xMoveInitial, y = y)
+                    ),
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory,
+                timeDelta = 100,
+                moveDelta = -DefaultPointerInputMoveAmountPx
+            )
+
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((moveIndex, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (moveIndex * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+
+            val expectedX = xMoveInitial - (abs(moveIndex * DefaultPointerInputMoveAmountPx))
+            assertThat(move.x).isEqualTo(expectedX)
+
+            assertThat(move.y).isEqualTo(y)
+            assertThat(move.historySize).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun createMoveMotionEvents_sixEventsOnePointerPositiveMoveDeltaWithoutHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val enableFlingStyleHistory = false
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers =
+                    arrayOf(
+                        BenchmarkSimplifiedPointerInputPointer(id = 0, x = xMoveInitial, y = y)
+                    ),
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory
+            )
+
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((moveIndex, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (moveIndex * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+
+            val expectedX = xMoveInitial + (moveIndex * DefaultPointerInputMoveAmountPx)
+            assertThat(move.x).isEqualTo(expectedX)
+
+            assertThat(move.y).isEqualTo(y)
+            assertThat(move.historySize).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun createMoveMotionEvents_sixEventsOnePointerPositiveMoveDeltaWithHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val enableFlingStyleHistory = true
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers =
+                    arrayOf(
+                        BenchmarkSimplifiedPointerInputPointer(id = 0, x = xMoveInitial, y = y)
+                    ),
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory
+            )
+
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((moveIndex, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (moveIndex * DefaultPointerInputTimeDelta)
+
+            val expectedX = xMoveInitial + (moveIndex * DefaultPointerInputMoveAmountPx)
+
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+            assertThat(move.x).isEqualTo(expectedX)
+            assertThat(move.y).isEqualTo(y)
+            assertThat(move.historySize)
+                .isEqualTo(numberOfHistoricalEventsBasedOnArrayLocation(moveIndex))
+        }
+    }
+
+    @Test
+    fun createMoveMotionEvents_sixEventsOnePointerNegativeMoveDeltaWithHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val enableFlingStyleHistory = true
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers =
+                    arrayOf(
+                        BenchmarkSimplifiedPointerInputPointer(id = 0, x = xMoveInitial, y = y)
+                    ),
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory,
+                timeDelta = 100,
+                moveDelta = -DefaultPointerInputMoveAmountPx
+            )
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((moveIndex, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (moveIndex * DefaultPointerInputTimeDelta)
+
+            val expectedX = xMoveInitial - (abs(moveIndex * DefaultPointerInputMoveAmountPx))
+
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+            assertThat(move.x).isEqualTo(expectedX)
+            assertThat(move.y).isEqualTo(y)
+            assertThat(move.historySize)
+                .isEqualTo(numberOfHistoricalEventsBasedOnArrayLocation(moveIndex))
+        }
+    }
+
+    // Multiple pointers/fingers
+    @Test
+    fun createMoveMotionEvents_sixEventsThreePointerNegativeMoveDeltaWithoutHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val numberOfPointers = 3 // fingers
+        val enableFlingStyleHistory = false
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val initialPointers =
+            Array(numberOfPointers) { simpleIndex ->
+                BenchmarkSimplifiedPointerInputPointer(
+                    id = simpleIndex,
+                    x = xMoveInitial + (simpleIndex * DefaultPointerInputMoveAmountPx),
+                    y = y
+                )
+            }
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers = initialPointers,
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory,
+                timeDelta = 100,
+                moveDelta = -DefaultPointerInputMoveAmountPx
+            )
+
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((index, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (index * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+            assertThat(move.historySize).isEqualTo(0)
+
+            for (pointerIndex in 0 until move.pointerCount) {
+                val pointerId: Int = move.getPointerId(pointerIndex)
+                val localPointerCoords = MotionEvent.PointerCoords()
+                move.getPointerCoords(pointerId, localPointerCoords)
+
+                val expectedX =
+                    (xMoveInitial - (abs(index * DefaultPointerInputMoveAmountPx))) +
+                        (pointerIndex * DefaultPointerInputMoveAmountPx)
+                assertThat(localPointerCoords.x).isEqualTo(expectedX)
+
+                assertThat(localPointerCoords.y).isEqualTo(y)
+            }
+        }
+    }
+
+    @Test
+    fun createMoveMotionEvents_sixEventsThreePointerPositiveMoveDeltaWithoutHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val numberOfPointers = 3 // fingers
+        val enableFlingStyleHistory = false
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val initialPointers =
+            Array(numberOfPointers) { simpleIndex ->
+                BenchmarkSimplifiedPointerInputPointer(
+                    id = simpleIndex,
+                    x = xMoveInitial + (simpleIndex * DefaultPointerInputMoveAmountPx),
+                    y = y
+                )
+            }
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers = initialPointers,
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory
+            )
+
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((index, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (index * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+            assertThat(move.historySize).isEqualTo(0)
+
+            for (pointerIndex in 0 until move.pointerCount) {
+                val pointerId: Int = move.getPointerId(pointerIndex)
+                val localPointerCoords = MotionEvent.PointerCoords()
+                move.getPointerCoords(pointerId, localPointerCoords)
+
+                val expectedX =
+                    (xMoveInitial + (index * DefaultPointerInputMoveAmountPx)) +
+                        (pointerIndex * DefaultPointerInputMoveAmountPx)
+                assertThat(localPointerCoords.x).isEqualTo(expectedX)
+
+                assertThat(localPointerCoords.y).isEqualTo(y)
+            }
+        }
+    }
+
+    @Test
+    fun createMoveMotionEvents_sixEventsThreePointerNegativeMoveDeltaWithHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val numberOfPointers = 3 // fingers
+        val enableFlingStyleHistory = true
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val initialPointers =
+            Array(numberOfPointers) { simpleIndex ->
+                BenchmarkSimplifiedPointerInputPointer(
+                    id = simpleIndex,
+                    x = xMoveInitial + (simpleIndex * DefaultPointerInputMoveAmountPx),
+                    y = y
+                )
+            }
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers = initialPointers,
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory,
+                timeDelta = 100,
+                moveDelta = -DefaultPointerInputMoveAmountPx
+            )
+
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((moveIndex, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (moveIndex * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+
+            assertThat(move.historySize)
+                .isEqualTo(numberOfHistoricalEventsBasedOnArrayLocation(moveIndex))
+
+            for (pointerIndex in 0 until move.pointerCount) {
+                val pointerId: Int = move.getPointerId(pointerIndex)
+                val localPointerCoords = MotionEvent.PointerCoords()
+                move.getPointerCoords(pointerId, localPointerCoords)
+
+                val expectedX =
+                    (xMoveInitial - (abs(moveIndex * DefaultPointerInputMoveAmountPx))) +
+                        (pointerIndex * DefaultPointerInputMoveAmountPx)
+                assertThat(localPointerCoords.x).isEqualTo(expectedX)
+                assertThat(localPointerCoords.y).isEqualTo(y)
+            }
+        }
+    }
+
+    @Test
+    fun createMoveMotionEvents_sixEventsThreePointerPositiveMoveDeltaWithHistory() {
+        val y = (ItemHeightPx / 2)
+        val xMoveInitial = 0f
+        val initialTime = 100
+        val numberOfEvents = 6
+        val numberOfPointers = 3 // fingers
+        val enableFlingStyleHistory = true
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val view = View(context)
+
+        val initialPointers =
+            Array(numberOfPointers) { simpleIndex ->
+                BenchmarkSimplifiedPointerInputPointer(
+                    id = simpleIndex,
+                    x = xMoveInitial + (simpleIndex * DefaultPointerInputMoveAmountPx),
+                    y = y
+                )
+            }
+
+        val moves =
+            createMoveMotionEvents(
+                initialTime = initialTime,
+                initialPointers = initialPointers,
+                rootView = view,
+                numberOfMoveEvents = numberOfEvents,
+                enableFlingStyleHistory = enableFlingStyleHistory
+            )
+
+        assertThat(moves.size).isEqualTo(numberOfEvents)
+
+        for ((moveIndex, move) in moves.withIndex()) {
+            val expectedTime = initialTime + (moveIndex * DefaultPointerInputTimeDelta)
+            assertThat(move.eventTime).isEqualTo(expectedTime)
+
+            assertThat(move.historySize)
+                .isEqualTo(numberOfHistoricalEventsBasedOnArrayLocation(moveIndex))
+
+            for (pointerIndex in 0 until move.pointerCount) {
+                val pointerId: Int = move.getPointerId(pointerIndex)
+                val localPointerCoords = MotionEvent.PointerCoords()
+                move.getPointerCoords(pointerId, localPointerCoords)
+
+                val expectedX =
+                    (xMoveInitial + (moveIndex * DefaultPointerInputMoveAmountPx)) +
+                        (pointerIndex * DefaultPointerInputMoveAmountPx)
+                assertThat(localPointerCoords.x).isEqualTo(expectedX)
+                assertThat(localPointerCoords.y).isEqualTo(y)
+            }
+        }
+    }
+
+    @Test
+    fun testNumberOfHistoricalEventsBasedOnArrayLocation() {
+        var numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(0)
+        assertThat(numberOfEvents).isEqualTo(12)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(1)
+        assertThat(numberOfEvents).isEqualTo(9)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(2)
+        assertThat(numberOfEvents).isEqualTo(4)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(3)
+        assertThat(numberOfEvents).isEqualTo(4)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(4)
+        assertThat(numberOfEvents).isEqualTo(2)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(5)
+        assertThat(numberOfEvents).isEqualTo(2)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(20)
+        assertThat(numberOfEvents).isEqualTo(2)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(2000)
+        assertThat(numberOfEvents).isEqualTo(2)
+
+        numberOfEvents = numberOfHistoricalEventsBasedOnArrayLocation(-1)
+        assertThat(numberOfEvents).isEqualTo(2)
+    }
+}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt
index 190917e..90a37fa 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt
@@ -18,8 +18,15 @@
 
 import android.view.InputDevice.SOURCE_TOUCHSCREEN
 import android.view.MotionEvent
+import android.view.MotionEvent.PointerCoords
+import android.view.MotionEvent.PointerProperties
 import android.view.View
 
+internal const val DefaultPointerInputTimeDelta = 100
+internal const val DefaultPointerInputMoveAmountPx = 10f
+
+internal data class BenchmarkSimplifiedPointerInputPointer(val id: Int, val x: Float, val y: Float)
+
 /**
  * Creates a simple [MotionEvent].
  *
@@ -86,3 +93,299 @@
         this.x = x
         this.y = y
     }
+
+/**
+ * Creates an array of down [MotionEvent]s meaning the first event is a [MotionEvent.ACTION_DOWN]
+ * and all following events (if there are any) are [MotionEvent.ACTION_POINTER_DOWN]s. These will
+ * usually be paired with a [MotionEvent] up events.
+ *
+ * @param initialX Starting x coordinate for the first [MotionEvent]
+ * @param initialTime Starting time for the first [MotionEvent]
+ * @param y - Y used for all [MotionEvent]s (only x is updated for each moves).
+ * @param rootView - [View] that the [MotionEvent] is dispatched to.
+ * @param numberOfEvents Number of [MotionEvent]s to create.
+ * @param timeDelta - Time between each [MotionEvent] in milliseconds.
+ * @param moveDelta - Amount to move in pixels for each [MotionEvent]
+ */
+internal fun createDowns(
+    initialX: Float,
+    initialTime: Int,
+    y: Float, // Same Y used for all moves
+    rootView: View,
+    numberOfEvents: Int = 1,
+    timeDelta: Int = DefaultPointerInputTimeDelta,
+    moveDelta: Float = DefaultPointerInputMoveAmountPx
+): Array<MotionEvent> {
+    if (numberOfEvents < 1) {
+        return emptyArray()
+    }
+
+    var time = initialTime
+    var x = initialX
+
+    val pointerProperties = mutableListOf<PointerProperties>()
+    val pointerCoords = mutableListOf<PointerCoords>()
+
+    val downMotionEvents =
+        Array(numberOfEvents) { index ->
+            // Add pointers as we create down events
+            pointerProperties.add(index, PointerProperties(index))
+            pointerCoords.add(index, PointerCoords(x, y))
+
+            val down =
+                MotionEvent(
+                    time,
+                    if (index == 0) {
+                        MotionEvent.ACTION_DOWN
+                    } else {
+                        MotionEvent.ACTION_POINTER_DOWN
+                    },
+                    index + 1,
+                    index, // Used in conjunction with ACTION_POINTER_DOWN/UP
+                    pointerProperties.toTypedArray(),
+                    pointerCoords.toTypedArray(),
+                    rootView
+                )
+
+            time += timeDelta
+            x += moveDelta
+
+            down // return down event
+        }
+    return downMotionEvents
+}
+
+/**
+ * Creates an array of up [MotionEvent]s meaning alls up events are [MotionEvent.ACTION_POINTER_UP]s
+ * minus the last one (which is a [MotionEvent.ACTION_UP]). These will usually be paired with a
+ * [MotionEvent] down events.
+ *
+ * @param initialTime Starting time for the first [MotionEvent]
+ * @param initialPointers All pointers to create set of up events
+ * @param rootView - [View] that the [MotionEvent] is dispatched to.
+ * @param timeDelta - Time between each [MotionEvent] in milliseconds.
+ */
+internal fun createUps(
+    initialTime: Int,
+    initialPointers: Array<BenchmarkSimplifiedPointerInputPointer>,
+    rootView: View,
+    timeDelta: Int = DefaultPointerInputTimeDelta
+): Array<MotionEvent> {
+    if (initialPointers.isEmpty()) {
+        return emptyArray()
+    }
+
+    var time = initialTime
+
+    val pointerProperties = mutableListOf<PointerProperties>()
+    val pointerCoords = mutableListOf<PointerCoords>()
+
+    // Convert simplified pointers to actual PointerProperties and PointerCoords.
+    for ((index, initialPointer) in initialPointers.withIndex()) {
+        pointerProperties.add(index, PointerProperties(initialPointer.id))
+        pointerCoords.add(index, PointerCoords(initialPointer.x, initialPointer.y))
+    }
+
+    val upMotionEvents =
+        Array(initialPointers.size) { index ->
+            // Only the last element should be an ACTION_UP
+            val action =
+                if (index == initialPointers.size - 1) {
+                    MotionEvent.ACTION_UP
+                } else {
+                    MotionEvent.ACTION_POINTER_UP
+                }
+
+            val numberOfPointers = initialPointers.size - index
+
+            val up =
+                MotionEvent(
+                    time,
+                    action,
+                    numberOfPointers,
+                    numberOfPointers - 1, // Used with ACTION_POINTER_DOWN/UP
+                    pointerProperties.toTypedArray(),
+                    pointerCoords.toTypedArray(),
+                    rootView
+                )
+
+            // Update time for next ACTION_UP/ACTION_POINTER_UP
+            time += timeDelta
+
+            // The next ACTION_UP/ACTION_POINTER_UP will have one less pointer, so we remove the
+            // last element from both lists.
+            if (pointerProperties.isNotEmpty()) {
+                pointerProperties.removeAt(pointerProperties.size - 1)
+            }
+
+            if (pointerCoords.isNotEmpty()) {
+                pointerCoords.removeAt(pointerCoords.size - 1)
+            }
+            up // return up event
+        }
+    return upMotionEvents
+}
+
+/**
+ * Creates an array of subsequent [MotionEvent.ACTION_MOVE]s to pair with a
+ * [MotionEvent.ACTION_DOWN] and a [MotionEvent.ACTION_UP] to recreate a user input sequence. Note:
+ * We offset pointers/events by time and x only (y stays the same).
+ *
+ * @param initialTime Starting time for the first [MotionEvent.ACTION_MOVE]
+ * @param initialPointers Starting coordinates for all [MotionEvent.ACTION_MOVE] pointers
+ * @param rootView - [View] that the [MotionEvent] is dispatched to.
+ * @param numberOfMoveEvents Number of [MotionEvent.ACTION_MOVE]s to create.
+ * @param enableFlingStyleHistory - Adds a history of [MotionEvent.ACTION_MOVE]s to each
+ *   [MotionEvent.ACTION_MOVE] to mirror a fling event (where you will get more
+ *   [MotionEvent.ACTION_MOVE]s than the refresh rate of the phone).
+ * @param timeDelta - Time between each [MotionEvent.ACTION_MOVE] in milliseconds.
+ * @param moveDelta - Amount to move in pixels for each [MotionEvent.ACTION_MOVE]
+ */
+internal fun createMoveMotionEvents(
+    initialTime: Int,
+    initialPointers: Array<BenchmarkSimplifiedPointerInputPointer>,
+    rootView: View,
+    numberOfMoveEvents: Int,
+    enableFlingStyleHistory: Boolean = false,
+    timeDelta: Int = DefaultPointerInputTimeDelta,
+    moveDelta: Float = DefaultPointerInputMoveAmountPx
+): Array<MotionEvent> {
+
+    var time = initialTime
+
+    // Creates a list of pointer properties and coordinates from initialPointers to represent
+    // all pointers/fingers in the [MotionEvent] we create.
+    val pointerProperties = mutableListOf<PointerProperties>()
+    val pointerCoords = mutableListOf<PointerCoords>()
+
+    for ((index, initialPointer) in initialPointers.withIndex()) {
+        pointerProperties.add(index, PointerProperties(initialPointer.id))
+        pointerCoords.add(index, PointerCoords(initialPointer.x, initialPointer.y))
+    }
+
+    val moveMotionEvents =
+        Array(numberOfMoveEvents) { index ->
+            val move =
+                if (enableFlingStyleHistory) {
+                    val historicalEventCount = numberOfHistoricalEventsBasedOnArrayLocation(index)
+
+                    // Set the time to the previous event time (either a down or move event) and
+                    // offset it, so it doesn't conflict with that previous event.
+                    var historicalTime: Int = time - timeDelta + 10
+
+                    var accountForMoveOffset = -1
+
+                    // Creates starting x values for all historical pointers (takes ending x of
+                    // all pointers and subtracts a delta and offset).
+                    val historicalPointerCoords = mutableListOf<PointerCoords>()
+
+                    for ((historicalIndex, historicalPointer) in pointerCoords.withIndex()) {
+                        if (moveDelta > 0) {
+                            // accountForMoveOffset stays -1 (to account for +1 offset [below])
+                            historicalPointerCoords.add(
+                                historicalIndex,
+                                PointerCoords(
+                                    historicalPointer.x - moveDelta + 1,
+                                    historicalPointer.y
+                                )
+                            )
+                        } else {
+                            // accountForMoveOffset changes to 1 (to account for -1 offset [below])
+                            accountForMoveOffset = 1
+                            historicalPointerCoords.add(
+                                historicalIndex,
+                                PointerCoords(
+                                    historicalPointer.x - moveDelta - 1,
+                                    historicalPointer.y
+                                )
+                            )
+                        }
+                    }
+
+                    val historicalTimeDelta: Int = (timeDelta - 10) / historicalEventCount
+                    val historicalXDelta: Float =
+                        (moveDelta + accountForMoveOffset) / historicalEventCount
+
+                    // Next section of code creates "historical" events by
+                    // 1. Creating [MotionEvent] with oldest historical event.
+                    // 2. Adding each subsequent historical event one at a time via `addBatch()`.
+                    // 3. Finishes by adding the main/end pointers via `addBatch()`.
+
+                    // Executes step 1 -> Creates [MotionEvent] with oldest historical event.
+                    val moveWithHistory =
+                        MotionEvent(
+                            historicalTime,
+                            MotionEvent.ACTION_MOVE,
+                            pointerProperties.size,
+                            0,
+                            pointerProperties.toTypedArray(), // ids always the same
+                            historicalPointerCoords.toTypedArray(),
+                            rootView
+                        )
+
+                    // Executes step 2 -> Adds each subsequent historical event one at a time via
+                    // `addBatch()`.
+                    // Starts with the second historical event (1), since we've already added the
+                    // first when we created the [MotionEvent] above.
+                    for (historyIndex in 1 until historicalEventCount) {
+                        // Update historical time
+                        historicalTime += historicalTimeDelta
+
+                        // Update historical x
+                        for (historicalPointerCoord in historicalPointerCoords) {
+                            historicalPointerCoord.x += historicalXDelta
+                        }
+
+                        // Add to [MotionEvent] history
+                        moveWithHistory.addBatch(
+                            historicalTime.toLong(),
+                            historicalPointerCoords.toTypedArray(),
+                            0
+                        )
+                    }
+
+                    // Executes step 3 -> Finishes by adding the main/end pointers via `addBatch()`,
+                    // so it will show up as the current event for the [MotionEvent].
+                    moveWithHistory.addBatch(time.toLong(), pointerCoords.toTypedArray(), 0)
+                    // return move event with history added
+                    moveWithHistory
+                } else {
+                    MotionEvent(
+                        time,
+                        MotionEvent.ACTION_MOVE,
+                        pointerProperties.size,
+                        0,
+                        pointerProperties.toTypedArray(),
+                        pointerCoords.toTypedArray(),
+                        rootView
+                    )
+                }
+
+            time += timeDelta
+            // Update all pointer's x by move delta for next iteration
+            for (pointerCoord in pointerCoords) {
+                pointerCoord.x += moveDelta
+            }
+            move
+        }
+    return moveMotionEvents
+}
+
+/*
+ * Based on traces of fling events, the first events in a series of "MOVES" have more
+ * historical [MotionEvent]s than the subsequent events.
+ *
+ * Remember, historical events within a [MotionEvent] represent extra [MotionEvent]s
+ * that occurred faster than the refresh rate of the phone. A fling will have many more events
+ * in the beginning (and between the refresh rate since they are happening so quick) than in
+ * the end.
+ */
+internal fun numberOfHistoricalEventsBasedOnArrayLocation(index: Int): Int {
+    return when (index) {
+        0 -> 12
+        1 -> 9
+        2,
+        3 -> 4
+        else -> 2
+    }
+}
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index ef1a521..f9ef25d 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -36,6 +36,7 @@
 androidXMultiplatform {
     android()
     jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
@@ -44,7 +45,7 @@
             dependencies {
                 implementation(libs.kotlinStdlib)
                 implementation(libs.kotlinCoroutinesCore)
-                api("androidx.annotation:annotation:1.6.0")
+                api("androidx.annotation:annotation:1.8.1")
                 implementation("androidx.collection:collection:1.4.0")
                 // when updating the runtime version please also update the runtime-saveable version
                 implementation(project(":compose:runtime:runtime"))
@@ -106,10 +107,16 @@
             }
         }
 
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
         jvmStubsMain {
-            dependsOn(jvmMain)
-            dependencies {
-            }
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
 
         androidInstrumentedTest {
@@ -134,6 +141,7 @@
                 implementation(project(":compose:foundation:foundation"))
                 implementation(project(":compose:foundation:foundation-layout"))
                 implementation(project(":compose:material:material"))
+                implementation("androidx.compose.material:material-icons-core:1.6.7")
                 implementation(project(":compose:test-utils"))
                 implementation(project(":internal-testutils-fonts"))
                 implementation(project(":compose:ui:ui-test-junit4"))
@@ -198,14 +206,18 @@
     inceptionYear = "2019"
     description = "Compose UI primitives. This library contains the primitives that form the Compose UI Toolkit, such as drawing, measurement and layout."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":compose:ui:ui:ui-samples"))
 }
 
 android {
+    compileSdk 35
     testOptions.unitTests.includeAndroidResources = true
     buildTypes.configureEach {
         consumerProguardFiles("proguard-rules.pro")
     }
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 // Screenshot tests related setup
diff --git a/compose/ui/ui/integration-tests/ui-demos/build.gradle b/compose/ui/ui/integration-tests/ui-demos/build.gradle
index 530bc96..ff5f22c 100644
--- a/compose/ui/ui/integration-tests/ui-demos/build.gradle
+++ b/compose/ui/ui/integration-tests/ui-demos/build.gradle
@@ -23,6 +23,7 @@
     implementation(project(":compose:integration-tests:demos:common"))
     implementation(project(":compose:ui:ui:ui-samples"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:runtime:runtime-livedata"))
     implementation(project(":compose:ui:ui"))
@@ -43,8 +44,11 @@
 }
 
 android {
+    compileSdk 35
     buildFeatures {
         viewBinding true
     }
     namespace "androidx.compose.ui.demos"
+    // TODO(b/349411310)?
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml b/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml
index 517882b..4d4a40d 100644
--- a/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml
+++ b/compose/ui/ui/integration-tests/ui-demos/lint-baseline.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha04" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha04)" variant="all" version="8.3.0-alpha04">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
         message="field Colors with type List&lt;Color>: replace with LongList"
-        errorLine1="internal val Colors = listOf("
+        errorLine1="internal val Colors ="
         errorLine2="^">
         <location
             file="src/main/java/androidx/compose/ui/demos/gestures/Colors.kt"/>
@@ -13,7 +13,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Color> of getColors: replace with LongList"
-        errorLine1="internal val Colors = listOf("
+        errorLine1="internal val Colors ="
         errorLine2="             ~~~~~~">
         <location
             file="src/main/java/androidx/compose/ui/demos/gestures/Colors.kt"/>
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/ScreenCoordinatesDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/ScreenCoordinatesDemo.kt
index 66186a8..c4ca3c9f 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/ScreenCoordinatesDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/ScreenCoordinatesDemo.kt
@@ -52,6 +52,7 @@
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionOnScreen
+import androidx.compose.ui.layout.transformToScreen
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt
index 63420d3a..9981a2e 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt
@@ -17,10 +17,12 @@
 package androidx.compose.ui.demos.viewinterop
 
 import android.graphics.Color
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Box
@@ -44,8 +46,14 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.demos.R
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerInputFilter
+import androidx.compose.ui.input.pointer.PointerInputModifier
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.input.pointer.pointerInteropFilter
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 
@@ -66,6 +74,9 @@
                 TwoAndroidScrollViewsInCompose()
             },
             ComposableDemo("MotionEventPointerInputFilter") { PointerInteropFilterDemo() },
+            ComposableDemo("Sharing event with sibling of parent Demo") {
+                SharingEventWithSiblingOfParent()
+            },
         )
     )
 
@@ -296,3 +307,64 @@
         }
     }
 }
+
+@Preview
+@Composable
+fun SharingEventWithSiblingOfParent() {
+    Column(modifier = Modifier.fillMaxSize()) {
+        Text(
+            "Search \"SHARING\" in Logcat to see events (Cyan box is child to parent that " +
+                "is sharing)."
+        )
+        Box {
+            ClickableSurface()
+
+            MyView(
+                0xFFFF0000.toInt(), // Manually created hex for Color.Red,
+                Modifier.align(Alignment.TopStart)
+            )
+
+            // Parent
+            Box(
+                modifier =
+                    Modifier.align(Alignment.BottomEnd)
+                        // This is the part that shares with the sibling...
+                        .then(
+                            object : PointerInputModifier {
+                                override val pointerInputFilter: PointerInputFilter =
+                                    object : PointerInputFilter() {
+                                        override fun onPointerEvent(
+                                            pointerEvent: PointerEvent,
+                                            pass: PointerEventPass,
+                                            bounds: IntSize
+                                        ) {}
+
+                                        override fun onCancel() {}
+
+                                        override val shareWithSiblings: Boolean = true
+                                    }
+                            }
+                        )
+            ) {
+                // Tapping here with code above will allow ClickableSurface (sibling of a parent)
+                // to get the event.
+                MyView(
+                    0xFF00FFFF.toInt(), // Manually created hex for Color.Cyan,
+                )
+            }
+        }
+    }
+}
+
+@Composable
+fun ClickableSurface(modifier: Modifier = Modifier) {
+    Box(modifier.fillMaxSize().clickable { Log.d("SHARING", "onClick") })
+}
+
+@Composable
+fun MyView(color: Int, modifier: Modifier = Modifier) {
+    AndroidView(
+        modifier = modifier.fillMaxSize(0.5f),
+        factory = { context -> View(context).apply { setBackgroundColor(color) } },
+    )
+}
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
index 4e79c01..70f28a8 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
@@ -26,7 +26,6 @@
 import android.view.ViewGroup
 import android.widget.LinearLayout
 import android.widget.TextView
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
@@ -196,7 +195,6 @@
 
 @RequiresApi(Build.VERSION_CODES.O)
 private object Api26Impl {
-    @DoNotInline
     @JvmStatic
     fun setAccessibilityTraversalAfter(view: View, id: Int) {
         view.setAccessibilityTraversalAfter(id)
diff --git a/compose/ui/ui/lint-baseline.xml b/compose/ui/ui/lint-baseline.xml
index 6b4b404..5ce4ebe 100644
--- a/compose/ui/ui/lint-baseline.xml
+++ b/compose/ui/ui/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -20,33 +20,6 @@
     </issue>
 
     <issue
-        id="ExperimentalPropertyAnnotation"
-        message="This property does not have all required annotations to correctly mark it as experimental."
-        errorLine1="    @ExperimentalComposeUiApi"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt"/>
-    </issue>
-
-    <issue
-        id="ExperimentalPropertyAnnotation"
-        message="This property does not have all required annotations to correctly mark it as experimental."
-        errorLine1="    @get:ExperimentalComposeUiApi"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/platform/WindowInfo.kt"/>
-    </issue>
-
-    <issue
-        id="ExperimentalPropertyAnnotation"
-        message="This property does not have all required annotations to correctly mark it as experimental."
-        errorLine1="    @get:ExperimentalComposeUiApi"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/commonMain/kotlin/androidx/compose/ui/platform/WindowInfo.kt"/>
-    </issue>
-
-    <issue
         id="PrimitiveInCollection"
         message="field children with type Map&lt;Integer, AutofillNode>: replace with IntObjectMap"
         errorLine1="    val children: MutableMap&lt;Int, AutofillNode> = mutableMapOf()"
@@ -67,7 +40,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable deltas with type List&lt;? extends Offset>: replace with LongList"
-        errorLine1="    val deltas = items.fastZipWithNext { el1, el2 ->"
+        errorLine1="    val deltas ="
         errorLine2="    ^">
         <location
             file="src/androidMain/kotlin/androidx/compose/ui/platform/accessibility/CollectionInfo.android.kt"/>
@@ -76,7 +49,7 @@
     <issue
         id="PrimitiveInCollection"
         message="return type Map&lt;LayoutNode, Integer> of getMapOfOriginalDepth: replace with ObjectIntMap"
-        errorLine1="    private val mapOfOriginalDepth by lazy(LazyThreadSafetyMode.NONE) {"
+        errorLine1="    private val mapOfOriginalDepth by"
         errorLine2="                ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt"/>
diff --git a/compose/ui/ui/samples/build.gradle b/compose/ui/ui/samples/build.gradle
index ffe2ad7..ed4179a 100644
--- a/compose/ui/ui/samples/build.gradle
+++ b/compose/ui/ui/samples/build.gradle
@@ -53,5 +53,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.compose.ui.samples"
 }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 390127d..928488a 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -216,6 +216,7 @@
 import androidx.test.espresso.assertion.ViewAssertions.matches
 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
@@ -568,7 +569,7 @@
     @Test
     fun testCreateAccessibilityNodeInfo_numberPicker_expectedClassName() {
         // Arrange.
-        setContent { Box(Modifier.semantics { role = Role.NumberPicker }.testTag(tag)) }
+        setContent { Box(Modifier.semantics { role = Role.ValuePicker }.testTag(tag)) }
         val virtualId = rule.onNodeWithTag(tag).semanticsId
 
         // Act.
@@ -2903,6 +2904,7 @@
         }
     }
 
+    @FlakyTest(bugId = 354750986)
     @Test
     fun sendTextEvents_whenSetText() {
         // Arrange.
@@ -2926,7 +2928,7 @@
             .assert(expectValue(EditableText, AnnotatedString("H")))
 
         // TODO(b/272068594): Extra TYPE_WINDOW_CONTENT_CHANGED sent 100ms after setup.
-        rule.mainClock.advanceTimeBy(100L)
+        rule.mainClock.advanceTimeBy(accessibilityEventLoopIntervalMs)
         rule.runOnIdle { clearInvocations(container) }
 
         // Act.
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 52bb5ad..b23a9ac 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -2856,12 +2856,23 @@
 
         validateSquareColors(outerColor = Color.Red, innerColor = Color.Yellow, size = 10)
 
-        translationLatch = CountDownLatch(1)
-        activityTestRule.runOnUiThread { offset.value = -5f }
+        // Wait until the translation affects the screenshot. Give it 4 frames
+        val latch = CountDownLatch(4)
+        activityTestRule.runOnUiThread {
+            activity.window.decorView.postOnAnimation(
+                object : Runnable {
+                    override fun run() {
+                        latch.countDown()
+                        activity.window.decorView.postOnAnimation(this)
+                    }
+                }
+            )
+            translationLatch = CountDownLatch(1)
+            offset.value = -5f
+        }
         // Wait for translation to complete
         assertTrue(translationLatch.await(1, TimeUnit.SECONDS))
-
-        activityTestRule.runOnUiThread {}
+        assertTrue(latch.await(1, TimeUnit.SECONDS))
 
         activityTestRule.waitAndScreenShot(forceInvalidate = false).apply {
             // just test that it is red around the Yellow
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/SensitiveContentModifierTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/SensitiveContentModifierTest.kt
new file mode 100644
index 0000000..74c3f89
--- /dev/null
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/SensitiveContentModifierTest.kt
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+import android.os.Build
+import android.view.View
+import androidx.annotation.RequiresApi
+import androidx.compose.material.Text
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.draw.Row
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@RequiresApi(35)
+class SensitiveContentModifierTest {
+    @get:Rule val rule = createComposeRule()
+    private lateinit var androidComposeView: View
+
+    @Before
+    fun before() {
+        isDebugInspectorInfoEnabled = true
+        Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    }
+
+    @After
+    fun after() {
+        isDebugInspectorInfoEnabled = false
+    }
+
+    @Test
+    fun assertModifierEquals() {
+        val modifier = Modifier.sensitiveContent()
+        Assert.assertEquals(modifier, modifier)
+        Assert.assertEquals(modifier, Modifier.sensitiveContent())
+        Assert.assertNotEquals(modifier, Modifier.sensitiveContent(false))
+    }
+
+    @Test
+    fun testSensitiveContent() {
+        rule.setContent {
+            Text("User name", modifier = Modifier.sensitiveContent())
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+
+    @Test
+    fun testNonSensitiveContent() {
+        rule.setContent {
+            Text("Hello world!")
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_AUTO,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+
+    @Test
+    fun testDynamicSensitiveModifier() {
+        var isContentSensitive by mutableStateOf(true)
+        rule.setContent {
+            Text(
+                "Dynamic sensitive Content",
+                modifier = Modifier.sensitiveContent(isContentSensitive)
+            )
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // update modifier as non sensitive and verify
+        rule.runOnIdle { isContentSensitive = false }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_AUTO,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+
+    @Test
+    fun testMultipleSensitiveComposable() {
+        var isContentSensitive by mutableStateOf(true)
+        var isContentSensitive2 by mutableStateOf(true)
+        rule.setContent {
+            Row {
+                Text(
+                    "Dynamic sensitive Content",
+                    modifier = if (isContentSensitive) Modifier.sensitiveContent() else Modifier
+                )
+                Text(
+                    "Dynamic sensitive Content",
+                    modifier = if (isContentSensitive2) Modifier.sensitiveContent() else Modifier
+                )
+            }
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // mark one composable non sensitive and verify
+        rule.runOnIdle { isContentSensitive = false }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // mark another composable non sensitive and verify
+        rule.runOnIdle { isContentSensitive2 = false }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_AUTO,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // mark one composable sensitive again and verify
+        rule.runOnIdle { isContentSensitive2 = true }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+
+    @Test
+    fun testRemoveSensitiveModifier() {
+        var isModifierIncluded by mutableStateOf(true)
+        rule.setContent {
+            Text(
+                "Dynamic sensitive Content",
+                modifier = if (isModifierIncluded) Modifier.sensitiveContent() else Modifier
+            )
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // remove sensitive modifier from composable and verify
+        rule.runOnIdle { isModifierIncluded = false }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_AUTO,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+
+    @Test
+    fun testAddSensitiveModifier() {
+        var isModifierIncluded by mutableStateOf(false)
+        rule.setContent {
+            Text(
+                "Dynamic sensitive Content",
+                modifier = if (isModifierIncluded) Modifier.sensitiveContent() else Modifier
+            )
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_AUTO,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // add sensitive modifier to the composable and verify
+        rule.runOnIdle { isModifierIncluded = true }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+
+    @Test
+    fun testRemoveSensitiveComposable() {
+        var isSensitiveComposableIncluded by mutableStateOf(true)
+        rule.setContent {
+            if (isSensitiveComposableIncluded) {
+                Text("Dynamic sensitive Content", modifier = Modifier.sensitiveContent())
+            }
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // remove sensitive composable and verify
+        rule.runOnIdle { isSensitiveComposableIncluded = false }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_AUTO,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+
+    @Test
+    fun testAddSensitiveComposable() {
+        var isSensitiveComposableIncluded by mutableStateOf(false)
+        rule.setContent {
+            if (isSensitiveComposableIncluded) {
+                Text("Dynamic sensitive Content", modifier = Modifier.sensitiveContent())
+            }
+            androidComposeView = LocalView.current
+        }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_AUTO,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+
+        // add sensitive composable and verify
+        rule.runOnIdle { isSensitiveComposableIncluded = true }
+        rule.runOnIdle {
+            Assert.assertEquals(
+                View.CONTENT_SENSITIVITY_SENSITIVE,
+                androidComposeView.getContentSensitivity()
+            )
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofillTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofillTest.kt
index 859ada4..0bcff51 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofillTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofillTest.kt
@@ -50,7 +50,6 @@
 import androidx.compose.ui.semantics.contentDataType
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.contentType
-import androidx.compose.ui.semantics.invisibleToUser
 import androidx.compose.ui.semantics.maxTextLength
 import androidx.compose.ui.semantics.onAutofillText
 import androidx.compose.ui.semantics.onLongClick
@@ -450,10 +449,8 @@
 
         rule.setContentWithAutofillEnabled {
             Box(
-                Modifier.semantics {
-                        contentType = ContentType.Username
-                        invisibleToUser()
-                    }
+                Modifier.alpha(0f)
+                    .semantics { contentType = ContentType.Username }
                     .size(width, height)
                     .testTag(contentTag)
             )
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/SemanticAutofillManagerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/SemanticAutofillManagerTest.kt
index fd14451..f6acab1 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/SemanticAutofillManagerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/SemanticAutofillManagerTest.kt
@@ -26,6 +26,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
 import androidx.compose.ui.platform.AndroidComposeView
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.testTag
@@ -33,7 +34,6 @@
 import androidx.compose.ui.semantics.contentType
 import androidx.compose.ui.semantics.editableText
 import androidx.compose.ui.semantics.focused
-import androidx.compose.ui.semantics.invisibleToUser
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.test.junit4.ComposeContentTestRule
@@ -236,10 +236,10 @@
     }
 
     private fun invisibleModifier(testTag: String): Modifier {
-        return Modifier.semantics {
+        return Modifier.alpha(0f) // this will make the component invisible
+            .semantics {
                 contentType = ContentType.Username
                 contentDataType = ContentDataType.Text
-                invisibleToUser()
             }
             .size(width, height)
             .testTag(testTag)
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
index f58c9ee..7aa5b1a 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/draw/DrawModifierTest.kt
@@ -292,8 +292,10 @@
                             modifier =
                                 Modifier.drawWithCache {
                                     resolvedDensity = Density(density, fontScale)
-                                    drawLatch.countDown()
-                                    onDrawBehind { drawDensity = Density(density, fontScale) }
+                                    onDrawBehind {
+                                        drawDensity = Density(density, fontScale)
+                                        drawLatch.countDown()
+                                    }
                                 },
                             text = "Change Layout Direction"
                         )
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt
index 3a590ad..419a0fd 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt
@@ -751,12 +751,13 @@
         val focusRequester = FocusRequester()
         var addFocusTarget by mutableStateOf(false)
         rule.setFocusableContent {
-            Box(
+            Column(
                 Modifier.thenIf(addFocusTarget) {
                     Modifier.onFocusChanged { focusState = it }
                         .then(elementFor(instance = focusTarget))
                 }
             ) {
+                Box(Modifier.focusTarget())
                 Box(Modifier.focusRequester(focusRequester).focusTarget())
             }
         }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
index 893f08a..1d0476c 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
@@ -1152,6 +1152,7 @@
                     latch?.countDown()
                 }
 
+                @Deprecated("This callback is superseded by onTrimMemory")
                 override fun onLowMemory() {
                     // NO-OP
                 }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidCursorAnchorInfoTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidCursorAnchorInfoTest.kt
index 9489f0e..b02f0af 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidCursorAnchorInfoTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidCursorAnchorInfoTest.kt
@@ -26,7 +26,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Matrix
-import androidx.compose.ui.input.pointer.PositionCalculator
+import androidx.compose.ui.input.pointer.MatrixPositionCalculator
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.MultiParagraph
 import androidx.compose.ui.text.TextLayoutInput
@@ -78,7 +78,7 @@
             .toFontFamily()
     private val rootPosition = Offset(1.2f, 3.4f)
     private val positionCalculator =
-        object : PositionCalculator {
+        object : MatrixPositionCalculator {
             override fun screenToLocal(positionOnScreen: Offset): Offset =
                 positionOnScreen - rootPosition
 
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidEmojiTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidEmojiTest.kt
index 46c06c1..059620c 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidEmojiTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidEmojiTest.kt
@@ -21,7 +21,7 @@
 import android.view.Choreographer
 import android.view.View
 import android.view.inputmethod.EditorInfo
-import androidx.compose.ui.input.pointer.PositionCalculator
+import androidx.compose.ui.input.pointer.MatrixPositionCalculator
 import androidx.compose.ui.text.input.ImeOptions
 import androidx.compose.ui.text.input.InputMethodManager
 import androidx.compose.ui.text.input.TextFieldValue
@@ -52,7 +52,7 @@
         val e2 = mock<EmojiCompat>()
         EmojiCompat.reset(e2)
         val view = View(InstrumentationRegistry.getInstrumentation().context)
-        val positionCalculator = mock<PositionCalculator>()
+        val positionCalculator = mock<MatrixPositionCalculator>()
         val inputMethodManager = mock<InputMethodManager>()
         // Choreographer must be retrieved on main thread.
         val choreographer = Espresso.onIdle { Choreographer.getInstance() }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
index 6f7d4a3..ed3de3a 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/HitPathTrackerTest.kt
@@ -3290,10 +3290,6 @@
         TODO("Not yet implemented")
     }
 
-    override fun localToScreen(localTransform: Matrix) {
-        TODO("Not yet implemented")
-    }
-
     override val pointerIconService: PointerIconService
         get() = TODO("Not yet implemented")
 
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapterTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapterTest.kt
index 105da0f..2d249dc 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapterTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapterTest.kt
@@ -33,7 +33,6 @@
 import android.view.MotionEvent.TOOL_TYPE_FINGER
 import android.view.MotionEvent.TOOL_TYPE_MOUSE
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Matrix
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -51,8 +50,6 @@
             override fun screenToLocal(positionOnScreen: Offset): Offset = positionOnScreen
 
             override fun localToScreen(localPosition: Offset): Offset = localPosition
-
-            override fun localToScreen(localTransform: Matrix) {}
         }
 
     @Before
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
index 8853994..461d659 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
@@ -32,7 +32,6 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.GraphicsContext
-import androidx.compose.ui.graphics.Matrix
 import androidx.compose.ui.graphics.layer.GraphicsLayer
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.input.InputModeManager
@@ -113,8 +112,6 @@
             override fun screenToLocal(positionOnScreen: Offset): Offset = positionOnScreen
 
             override fun localToScreen(localPosition: Offset): Offset = localPosition
-
-            override fun localToScreen(localTransform: Matrix) {}
         }
 
     @Before
@@ -2901,10 +2898,6 @@
         TODO("Not yet implemented")
     }
 
-    override fun localToScreen(localTransform: Matrix) {
-        TODO("Not yet implemented")
-    }
-
     override val pointerIconService: PointerIconService
         get() = TODO("Not yet implemented")
 
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ComposeViewLayoutTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ComposeViewLayoutTest.kt
index 4cb23b0..e1f79f2 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ComposeViewLayoutTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/ComposeViewLayoutTest.kt
@@ -36,13 +36,11 @@
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
-import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
-import java.util.Locale
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -131,27 +129,4 @@
                 rule.onNodeWithTag(tag).fetchSemanticsNode().layoutInfo.viewConfiguration
             )
     }
-
-    @Test
-    fun rootLayoutDirectionLtr() {
-        rule.runOnUiThread {
-            val resources = rule.activity.resources
-            val configuration = resources.configuration
-            configuration.setLayoutDirection(Locale.US)
-        }
-        rule.setContent { Box(Modifier.size(10.dp)) }
-        assertThat(rule.onRoot().fetchSemanticsNode().layoutInfo.layoutDirection)
-            .isEqualTo(LayoutDirection.Ltr)
-    }
-
-    @Test
-    fun rootLayoutDirectionRtl() {
-        rule.runOnUiThread {
-            val configuration = rule.activity.resources.configuration
-            configuration.setLayoutDirection(Locale("fa"))
-        }
-        rule.setContent { Box(Modifier.size(10.dp)) }
-        assertThat(rule.onRoot().fetchSemanticsNode().layoutInfo.layoutDirection)
-            .isEqualTo(LayoutDirection.Rtl)
-    }
 }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
index afc7901..be78319 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/Helpers.kt
@@ -207,10 +207,6 @@
         TODO("Not yet implemented")
     }
 
-    override fun localToScreen(localTransform: Matrix) {
-        TODO("Not yet implemented")
-    }
-
     override val pointerIconService: PointerIconService
         get() = TODO("Not yet implemented")
 
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt
index 6083ac6..edd8b302 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.absoluteOffset
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.size
@@ -635,7 +636,8 @@
         var rulerChanged = CountDownLatch(1)
         rule.setContent {
             Box(
-                Modifier.onPlaced { rootX = it.positionInWindow().x }
+                Modifier.fillMaxSize()
+                    .onPlaced { rootX = it.positionInWindow().x }
                     .offset { IntOffset(offset, 0) }
             ) {
                 AndroidView(
@@ -659,12 +661,13 @@
                                 ) {
                                     Box(
                                         Modifier.layout { measurable, constraints ->
-                                            val p = measurable.measure(constraints)
-                                            layout(p.width, p.height) {
-                                                rulerValue = verticalRuler.current(Float.NaN)
-                                                rulerChanged.countDown()
+                                                val p = measurable.measure(constraints)
+                                                layout(p.width, p.height) {
+                                                    rulerValue = verticalRuler.current(Float.NaN)
+                                                    rulerChanged.countDown()
+                                                }
                                             }
-                                        }
+                                            .fillMaxSize(0.5f)
                                     )
                                 }
                             }
@@ -678,7 +681,6 @@
             assertThat(rulerValue).isWithin(0.01f).of(-rootX)
             rulerChanged = CountDownLatch(1)
             offset = 100
-            rule.activity.window.decorView.invalidate()
         }
         assertThat(rulerChanged.await(1, TimeUnit.SECONDS)).isTrue()
         rule.runOnIdle { assertThat(rulerValue).isWithin(0.01f).of(-100f - rootX) }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index db48053..5e34e16 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -64,6 +64,7 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.TestActivity
 import androidx.compose.ui.test.assertCountEquals
@@ -77,11 +78,13 @@
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.onChildren
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.compose.ui.zIndex
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -812,10 +815,14 @@
         val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(2))
 
         composeItems(state, items)
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
 
         rule.runOnIdle { items.value = listOf(2, 3) }
 
-        assertNodes(active = listOf(2, 3), deactivated = listOf(0, 1), disposed = listOf(4))
+        assertNodes(active = listOf(2, 3), disposed = listOf(4))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
     }
 
     @Test
@@ -824,6 +831,7 @@
         val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(2))
 
         composeItems(state, items)
+        val id0 = rule.onNodeWithTag("0").semanticsId()
 
         rule.runOnIdle {
             items.value = listOf(2, 3)
@@ -835,7 +843,8 @@
             // the last reusable slot (1) will be used for composing 5
         }
 
-        assertNodes(active = listOf(2, 3, 5), deactivated = listOf(0), disposed = listOf(1, 4))
+        assertNodes(active = listOf(2, 3, 5), disposed = listOf(1, 4))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
     }
 
     @Test
@@ -844,6 +853,7 @@
         val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(2))
 
         composeItems(state, items)
+        val id0 = rule.onNodeWithTag("0").semanticsId()
 
         rule.runOnIdle {
             items.value = listOf(2, 3)
@@ -855,7 +865,8 @@
             // slot 1 should be taken back from reusable
         }
 
-        assertNodes(active = listOf(2, 3, 1), deactivated = listOf(0))
+        assertNodes(active = listOf(2, 3, 1))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
     }
 
     @Test
@@ -864,6 +875,7 @@
         val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(2))
 
         composeItems(state, items)
+        val id0 = rule.onNodeWithTag("0").semanticsId()
 
         rule.runOnIdle {
             items.value = listOf(2, 3)
@@ -875,7 +887,8 @@
             // prefetch should take slot 1 from reuse
         }
 
-        assertNodes(active = listOf(2, 3) + /*prefetch*/ listOf(5), deactivated = listOf(0))
+        assertNodes(active = listOf(2, 3) + /*prefetch*/ listOf(5))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
     }
 
     @Test
@@ -884,6 +897,8 @@
         val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(3))
 
         composeItems(state, items)
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
 
         rule.runOnIdle {
             items.value = listOf(2)
@@ -895,11 +910,9 @@
             // prefetch should take slot 3 from reuse
         }
 
-        assertNodes(
-            active = listOf(2) + /*prefetch*/ listOf(3),
-            deactivated = listOf(0, 1),
-            disposed = listOf(4)
-        )
+        assertNodes(active = listOf(2) + /*prefetch*/ listOf(3), disposed = listOf(4))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
     }
 
     @Test
@@ -920,10 +933,12 @@
         val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(1))
 
         composeItems(state, items)
+        val id2 = rule.onNodeWithTag("2").semanticsId()
 
         rule.runOnIdle { items.value = listOf(0, 1) }
 
-        assertNodes(active = listOf(0, 1), deactivated = listOf(2), disposed = listOf(3))
+        assertNodes(active = listOf(0, 1), disposed = listOf(3))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id2)
     }
 
     @SuppressLint("RememberReturnType")
@@ -1322,14 +1337,15 @@
         }
 
         rule.onNodeWithTag("child").assertExists()
+        val idChild = rule.onNodeWithTag("child").semanticsId()
 
         needChild.value = false
 
-        rule.onNodeWithTag("child").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idChild)
 
         layoutState.value = SubcomposeLayoutState(SubcomposeSlotReusePolicy(1))
 
-        rule.onNodeWithTag("child").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idChild)
     }
 
     @Test
@@ -1351,9 +1367,11 @@
             }
         }
 
+        val idChild = rule.onNodeWithTag("child").semanticsId()
+
         rule.runOnIdle { needChild.value = false }
 
-        rule.onNodeWithTag("child").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idChild)
 
         layoutState.value = SubcomposeLayoutState(SubcomposeSlotReusePolicy(0))
 
@@ -1413,11 +1431,12 @@
         }
 
         rule.onNodeWithTag("child").assertExists()
+        val idChild = rule.onNodeWithTag("child").semanticsId()
 
         assertThat(composed).isTrue()
         needChild.value = false
 
-        rule.onNodeWithTag("child").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idChild)
         assertThat(composed).isFalse()
         needChild.value = true
 
@@ -1444,10 +1463,12 @@
         val state = SubcomposeLayoutState(policy)
 
         composeItems(state, items)
+        val id2 = rule.onNodeWithTag("2").semanticsId()
 
         rule.runOnIdle { items.value = listOf(0, 3) }
 
-        assertNodes(active = listOf(0, 3), deactivated = listOf(2), disposed = listOf(1, 4))
+        assertNodes(active = listOf(0, 3), disposed = listOf(1, 4))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id2)
 
         rule.runOnIdle { items.value = listOf(0, 3, 5) }
 
@@ -1547,10 +1568,16 @@
         val state = SubcomposeLayoutState(policy)
 
         composeItems(state, items)
+        val id1 = rule.onNodeWithTag("1").semanticsId()
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+        val id5 = rule.onNodeWithTag("5").semanticsId()
 
         rule.runOnIdle { items.value = listOf() }
 
-        assertNodes(deactivated = listOf(1, 3, 5), disposed = listOf(0, 2, 4, 6))
+        assertNodes(disposed = listOf(0, 2, 4, 6))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id5)
 
         rule.runOnIdle {
             items.value = listOf(8, 9, 10)
@@ -1558,7 +1585,9 @@
             // 5 is reused for 9
         }
 
-        assertNodes(active = listOf(8, 9, 10), deactivated = listOf(1, 3), disposed = listOf(5))
+        assertNodes(active = listOf(8, 9, 10), disposed = listOf(5))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
     }
 
     @Test
@@ -1578,16 +1607,26 @@
         val state = SubcomposeLayoutState(policy)
 
         composeItems(state, items)
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
+        val id2 = rule.onNodeWithTag("2").semanticsId()
+        val id3 = rule.onNodeWithTag("3").semanticsId()
 
         rule.runOnIdle { items.value = listOf() }
 
-        assertNodes(deactivated = listOf(0, 1, 2, 3))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id2)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
 
         rule.runOnIdle {
             items.value = listOf(10) // slot 2 should be reused
         }
 
-        assertNodes(active = listOf(10), deactivated = listOf(0, 1, 3), disposed = listOf(2))
+        assertNodes(active = listOf(10), disposed = listOf(2))
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
     }
 
     @Test
@@ -2119,13 +2158,15 @@
             }
         }
 
+        val idTag = rule.onNodeWithTag("tag").semanticsId()
+
         rule.runOnIdle { flag = false }
 
         // the node will exist when after `flag` was switched to false it will first cause
         // remeasure, and because during the remeasure we will not subcompose the child
         // the node will be deactivated before its block recomposes causing the Box to be
         // removed from the hierarchy.
-        rule.onNodeWithTag("tag").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idTag)
     }
 
     // Regression test of b/271156218
@@ -2546,13 +2587,8 @@
         Box(Modifier.fillMaxSize().testTag("$index"))
     }
 
-    private fun assertNodes(
-        active: List<Int> = emptyList(),
-        deactivated: List<Int> = emptyList(),
-        disposed: List<Int> = emptyList()
-    ) {
+    private fun assertNodes(active: List<Int> = emptyList(), disposed: List<Int> = emptyList()) {
         active.forEach { rule.onNodeWithTag("$it").assertExists() }
-        deactivated.forEach { rule.onNodeWithTag("$it").assertIsDeactivated() }
         disposed.forEach { rule.onNodeWithTag("$it").assertDoesNotExist() }
     }
 
@@ -2561,6 +2597,14 @@
         // we want to verify the node is not deactivated, but such API does not exist yet
         expectAssertionError { assertIsDeactivated() }
     }
+
+    private fun SemanticsNode.assertLayoutDeactivatedById(id: Int) {
+        children.fastForEach {
+            if (it.id == id) {
+                assert(it.layoutInfo.isDeactivated)
+            }
+        }
+    }
 }
 
 fun ImageBitmap.assertCenterPixelColor(expectedColor: Color) {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt
index 49d3c03..5d84f02 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/InvalidateSubtreeTest.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.DrawModifier
+import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.LayoutModifier
@@ -32,6 +33,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,6 +47,7 @@
     @Test
     fun invalidateSubtreeNoLayers() {
         lateinit var invalidate: () -> Unit
+        var drawLatch = CountDownLatch(1)
         val counter1 = LayoutAndDrawCounter()
         val counter2 = LayoutAndDrawCounter()
         val counter3 = LayoutAndDrawCounter()
@@ -51,8 +55,11 @@
             invalidate = { node.invalidateSubtree() }
         }
         rule.setContent {
-            Box(counter1) { Box(counter2 then captureInvalidate) { Box(counter3.size(10.dp)) } }
+            Box(counter1.drawBehind { drawLatch.countDown() }) {
+                Box(counter2 then captureInvalidate) { Box(counter3.size(10.dp)) }
+            }
         }
+        assertThat(drawLatch.await(1, TimeUnit.SECONDS)).isTrue()
         rule.waitForIdle()
         assertThat(counter1.drawCount).isEqualTo(1)
         assertThat(counter1.measureCount).isEqualTo(1)
@@ -63,8 +70,10 @@
         assertThat(counter3.drawCount).isEqualTo(1)
         assertThat(counter3.measureCount).isEqualTo(1)
         assertThat(counter3.placeCount).isEqualTo(1)
+        drawLatch = CountDownLatch(1)
 
         rule.runOnUiThread { invalidate() }
+        assertThat(drawLatch.await(1, TimeUnit.SECONDS)).isTrue()
         rule.waitForIdle()
 
         // There isn't a layer that can be invalidated, so we draw this twice also
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
index 7825101..61fe1dd 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/node/NodeChainTester.kt
@@ -454,10 +454,6 @@
         TODO("Not yet implemented")
     }
 
-    override fun localToScreen(localTransform: Matrix) {
-        TODO("Not yet implemented")
-    }
-
     @Deprecated(
         "fontLoader is deprecated, use fontFamilyResolver",
         replaceWith = ReplaceWith("fontFamilyResolver")
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidComposeViewScreenCoordinatesTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidComposeViewScreenCoordinatesTest.kt
index 4708c4b..0c762df 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidComposeViewScreenCoordinatesTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/platform/AndroidComposeViewScreenCoordinatesTest.kt
@@ -36,6 +36,7 @@
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionOnScreen
+import androidx.compose.ui.layout.transformToScreen
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchInteropTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchInteropTest.kt
index d132dbb..c01d18f 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchInteropTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchInteropTest.kt
@@ -27,14 +27,17 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.key.nativeKeyCode
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.assertIsFocused
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performKeyPress
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.MediumTest
-import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
@@ -54,8 +57,7 @@
         rule.setContent { Box(Modifier.testTag(Tag).size(10.dp).focusable()) }
 
         // Act.
-        rule.waitForIdle()
-        InstrumentationRegistry.getInstrumentation().sendKeySync(keyEvent)
+        rule.onRoot().performKeyPress(keyEvent)
 
         // Assert.
         rule.onNodeWithTag(Tag).assertIsFocused()
@@ -68,8 +70,7 @@
         rule.setContent { AndroidView({ FocusableView(it).apply { embeddedView = this } }) }
 
         // Act.
-        rule.waitForIdle()
-        InstrumentationRegistry.getInstrumentation().sendKeySync(keyEvent)
+        rule.onRoot().performKeyPress(keyEvent)
 
         // Assert.
         rule.runOnIdle { assertThat(embeddedView.isFocused).isTrue() }
@@ -97,3 +98,7 @@
             )
     }
 }
+
+private fun SemanticsNodeInteraction.performKeyPress(keyEvent: AndroidKeyEvent) {
+    performKeyPress(KeyEvent(keyEvent))
+}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt
index 20b3ad9..5abbb34 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt
@@ -15,13 +15,17 @@
  */
 package androidx.compose.ui.window
 
-import android.os.Build
+import android.content.res.Configuration
+import android.view.KeyEvent
+import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.compose.BackHandler
+import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.compositionLocalOf
 import androidx.compose.runtime.getValue
@@ -29,28 +33,28 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.lerp
 import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.test.TestActivity
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.hasAnyChild
+import androidx.compose.ui.test.isDialog
 import androidx.compose.ui.test.isRoot
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onFirst
-import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performClick
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.height
-import androidx.test.espresso.Espresso
+import androidx.compose.ui.unit.round
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.MediumTest
-import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import androidx.test.uiautomator.UiDevice
-import com.google.common.truth.Truth
-import kotlin.math.roundToInt
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertEquals
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,224 +62,185 @@
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class DialogTest {
-    @get:Rule val rule = createAndroidComposeRule<TestActivity>()
+    @get:Rule val rule = createComposeRule()
 
     private val defaultText = "dialogText"
+    private val testTag = "tag"
+    private lateinit var dispatcher: OnBackPressedDispatcher
 
     @Test
     fun dialogTest_isShowingContent() {
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
-
-            if (showDialog.value) {
-                Dialog(onDismissRequest = {}) { BasicText(defaultText) }
-            }
-        }
-
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
+        setupDialogTest(closeDialogOnDismiss = false)
+        rule.onNodeWithTag(testTag).assertIsDisplayed()
     }
 
     @Test
-    @Ignore("100% failing b/179359518")
     fun dialogTest_isNotDismissed_whenClicked() {
-        val textBeforeClick = "textBeforeClick"
-        val textAfterClick = "textAfterClick"
+        var clickCount = 0
+        setupDialogTest { DefaultDialogContent(Modifier.clickable { clickCount++ }) }
 
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
-            val text = remember { mutableStateOf(textBeforeClick) }
+        assertThat(clickCount).isEqualTo(0)
+        val interaction = rule.onNodeWithTag(testTag)
+        interaction.assertIsDisplayed()
 
-            if (showDialog.value) {
-                Dialog(onDismissRequest = { showDialog.value = false }) {
-                    BasicText(
-                        text = text.value,
-                        modifier = Modifier.clickable { text.value = textAfterClick }
-                    )
-                }
-            }
-        }
+        // Click inside the dialog
+        interaction.performClick()
 
-        rule
-            .onNodeWithText(textBeforeClick)
-            .assertIsDisplayed()
-            // Click inside the dialog
-            .performClick()
-
-        // Check that the Clickable was pressed and that the Dialog is still visible, but with
-        // the new text
-        rule.onNodeWithText(textBeforeClick).assertDoesNotExist()
-        rule.onNodeWithText(textAfterClick).assertIsDisplayed()
+        // Check that the Clickable was pressed and the Dialog is still visible.
+        interaction.assertIsDisplayed()
+        assertThat(clickCount).isEqualTo(1)
     }
 
-    @FlakyTest(bugId = 179359518)
     @Test
     fun dialogTest_isDismissed_whenSpecified() {
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
+        setupDialogTest()
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
 
-            if (showDialog.value) {
-                Dialog(onDismissRequest = { showDialog.value = false }) { BasicText(defaultText) }
-            }
-        }
-
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-
-        // Click outside the dialog to dismiss it
-        val outsideX = 0
-        val outsideY =
-            with(rule.density) {
-                rule.onAllNodes(isRoot()).onFirst().getUnclippedBoundsInRoot().height.roundToPx() /
-                    2
-            }
-        UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
-
-        rule.onNodeWithText(defaultText).assertDoesNotExist()
+        clickOutsideDialog()
+        textInteraction.assertDoesNotExist()
     }
 
     @Test
     fun dialogTest_isNotDismissed_whenNotSpecified() {
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
+        setupDialogTest(closeDialogOnDismiss = false)
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
 
-            if (showDialog.value) {
-                Dialog(onDismissRequest = {}) { BasicText(defaultText) }
-            }
-        }
-
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-
-        // Click outside the dialog to try to dismiss it
-        val outsideX = 0
-        val outsideY =
-            with(rule.density) {
-                rule.onAllNodes(isRoot()).onFirst().getUnclippedBoundsInRoot().height.roundToPx() /
-                    2
-            }
-        UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
-
+        clickOutsideDialog()
         // The Dialog should still be visible
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
+        textInteraction.assertIsDisplayed()
     }
 
     @Test
     fun dialogTest_isNotDismissed_whenDismissOnClickOutsideIsFalse() {
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
+        setupDialogTest(dialogProperties = DialogProperties(dismissOnClickOutside = false))
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
 
-            if (showDialog.value) {
-                Dialog(
-                    onDismissRequest = { showDialog.value = false },
-                    properties = DialogProperties(dismissOnClickOutside = false)
-                ) {
-                    BasicText(defaultText)
-                }
-            }
-        }
-
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-
-        // Click outside the dialog to try to dismiss it
-        val outsideX = 0
-        val outsideY =
-            with(rule.density) {
-                rule.onAllNodes(isRoot()).onFirst().getUnclippedBoundsInRoot().height.roundToPx() /
-                    2
-            }
-        UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
-
+        clickOutsideDialog()
         // The Dialog should still be visible
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
+        textInteraction.assertIsDisplayed()
     }
 
-    @FlakyTest(bugId = 159364185)
-    fun dialogTest_isDismissed_whenSpecified_backButtonPressed() {
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
-
-            if (showDialog.value) {
-                Dialog(onDismissRequest = { showDialog.value = false }) { BasicText(defaultText) }
-            }
-        }
-
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-
-        // Click the back button to dismiss the Dialog
-        Espresso.pressBack()
-
-        rule.onNodeWithText(defaultText).assertDoesNotExist()
-    }
-
-    @FlakyTest(bugId = 159364185)
-    fun dialogTest_isNotDismissed_whenNotSpecified_backButtonPressed() {
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
-
-            if (showDialog.value) {
-                Dialog(onDismissRequest = {}) { BasicText(defaultText) }
-            }
-        }
-
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-
-        // Click the back button to try to dismiss the dialog
-        Espresso.pressBack()
-
-        // The Dialog should still be visible
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-    }
-
-    @FlakyTest(bugId = 159364185)
-    fun dialogTest_isNotDismissed_whenDismissOnBackPressIsFalse() {
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
-
-            if (showDialog.value) {
-                Dialog(
-                    onDismissRequest = { showDialog.value = false },
-                    properties = DialogProperties(dismissOnBackPress = false)
-                ) {
-                    BasicText(defaultText)
-                }
-            }
-        }
-
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-
-        // Click the back button to try to dismiss the dialog
-        Espresso.pressBack()
-
-        // The Dialog should still be visible
-        rule.onNodeWithText(defaultText).assertIsDisplayed()
-    }
-
-    @Ignore // b/266613263
     @Test
-    @SdkSuppress(maxSdkVersion = 33) // b/262909049: Failing on SDK 34
+    fun dialogTest_isDismissed_whenSpecified_backButtonPressed() {
+        setupDialogTest()
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        pressBackViaKey()
+        textInteraction.assertDoesNotExist()
+    }
+
+    @Test
+    fun dialogTest_isDismissed_whenSpecified_backDispatched() {
+        setupDialogTest()
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        dispatchBackButton()
+        textInteraction.assertDoesNotExist()
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenNotSpecified_backButtonPressed() {
+        setupDialogTest(closeDialogOnDismiss = false)
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        pressBackViaKey()
+        // The Dialog should still be visible
+        textInteraction.assertIsDisplayed()
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenNotSpecified_backDispatched() {
+        setupDialogTest(closeDialogOnDismiss = false)
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        dispatchBackButton()
+        // The Dialog should still be visible
+        textInteraction.assertIsDisplayed()
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenDismissOnBackPressIsFalse_backButtonPressed() {
+        setupDialogTest(dialogProperties = DialogProperties(dismissOnBackPress = false))
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        pressBackViaKey()
+        // The Dialog should still be visible
+        textInteraction.assertIsDisplayed()
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenDismissOnBackPressIsFalse_backDispatched() {
+        setupDialogTest(dialogProperties = DialogProperties(dismissOnBackPress = false))
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        dispatchBackButton()
+        // The Dialog should still be visible
+        textInteraction.assertIsDisplayed()
+    }
+
+    @Test
     fun dialogTest_backHandler_isCalled_backButtonPressed() {
-        if (Build.VERSION.SDK_INT == 33 && Build.VERSION.CODENAME != "REL") {
-            return // b/262909049: Do not run this test on pre-release Android U.
+        var clickCount = 0
+        setupDialogTest(closeDialogOnDismiss = false) {
+            BackHandler { clickCount++ }
+            DefaultDialogContent()
         }
 
-        val clickCountPrefix = "Click: "
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+        assertThat(clickCount).isEqualTo(0)
 
-        rule.setContent {
-            val showDialog = remember { mutableStateOf(true) }
+        pressBackViaKey()
+        textInteraction.assertIsDisplayed()
+        assertThat(clickCount).isEqualTo(1)
+    }
 
-            if (showDialog.value) {
-                Dialog(onDismissRequest = {}) {
-                    val clickCount = remember { mutableStateOf(0) }
-                    BasicText(clickCountPrefix + clickCount.value)
-                    BackHandler { clickCount.value++ }
-                }
-            }
+    @Test
+    fun dialogTest_backHandler_isCalled_backDispatched() {
+        var clickCount = 0
+        setupDialogTest(closeDialogOnDismiss = false) {
+            BackHandler { clickCount++ }
+            DefaultDialogContent()
         }
 
-        rule.onNodeWithText(clickCountPrefix + "0").assertIsDisplayed()
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+        assertThat(clickCount).isEqualTo(0)
 
-        // Click the back button to trigger the BackHandler
-        Espresso.pressBack()
+        dispatchBackButton()
+        textInteraction.assertIsDisplayed()
+        assertThat(clickCount).isEqualTo(1)
+    }
 
-        rule.onNodeWithText(clickCountPrefix + "1").assertIsDisplayed()
+    @Test
+    fun dialogTest_isDismissed_escapePressed() {
+        setupDialogTest()
+
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        pressEscape()
+        textInteraction.assertDoesNotExist()
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenNotSpecified_escapePressed() {
+        setupDialogTest(closeDialogOnDismiss = false)
+
+        val textInteraction = rule.onNodeWithTag(testTag)
+        textInteraction.assertIsDisplayed()
+
+        pressEscape()
+        textInteraction.assertIsDisplayed()
     }
 
     @Test
@@ -294,7 +259,9 @@
     fun canFillScreenWidth_dependingOnProperty() {
         var box1Width = 0
         var box2Width = 0
+        lateinit var configuration: Configuration
         rule.setContent {
+            configuration = LocalConfiguration.current
             Dialog(
                 onDismissRequest = {},
                 properties = DialogProperties(usePlatformDefaultWidth = false)
@@ -305,14 +272,9 @@
                 Box(Modifier.fillMaxSize().onSizeChanged { box2Width = it.width })
             }
         }
-        rule.runOnIdle {
-            Truth.assertThat(box1Width)
-                .isEqualTo(
-                    (rule.activity.resources.configuration.screenWidthDp * rule.density.density)
-                        .roundToInt()
-                )
-            Truth.assertThat(box2Width).isLessThan(box1Width)
-        }
+        val expectedWidth = with(rule.density) { configuration.screenWidthDp.dp.roundToPx() }
+        assertThat(box1Width).isEqualTo(expectedWidth)
+        assertThat(box2Width).isLessThan(box1Width)
     }
 
     @Test
@@ -329,23 +291,78 @@
                 Box(Modifier.size(width, 150.dp).onSizeChanged { actualWidth = it.width })
             }
         }
+
         rule.runOnIdle {
-            Truth.assertThat(actualWidth).isEqualTo((10 * rule.density.density).roundToInt())
+            with(rule.density) { assertThat(actualWidth).isEqualTo(10.dp.roundToPx()) }
         }
+
         width = 20.dp
         rule.runOnIdle {
-            Truth.assertThat(actualWidth).isEqualTo((20 * rule.density.density).roundToInt())
+            with(rule.density) { assertThat(actualWidth).isEqualTo(20.dp.roundToPx()) }
         }
 
         usePlatformDefaultWidth = true
-
         width = 30.dp
         rule.runOnIdle {
-            Truth.assertThat(actualWidth).isEqualTo((30 * rule.density.density).roundToInt())
+            with(rule.density) { assertThat(actualWidth).isEqualTo(30.dp.roundToPx()) }
         }
+
         width = 40.dp
         rule.runOnIdle {
-            Truth.assertThat(actualWidth).isEqualTo((40 * rule.density.density).roundToInt())
+            with(rule.density) { assertThat(actualWidth).isEqualTo(40.dp.roundToPx()) }
         }
     }
+
+    private fun setupDialogTest(
+        closeDialogOnDismiss: Boolean = true,
+        dialogProperties: DialogProperties = DialogProperties(),
+        dialogContent: @Composable () -> Unit = { DefaultDialogContent() },
+    ) {
+        rule.setContent {
+            var showDialog by remember { mutableStateOf(true) }
+            val onDismiss: () -> Unit =
+                if (closeDialogOnDismiss) {
+                    { showDialog = false }
+                } else {
+                    {}
+                }
+            if (showDialog) {
+                Dialog(onDismiss, dialogProperties, dialogContent)
+            }
+        }
+    }
+
+    @Composable
+    private fun DefaultDialogContent(modifier: Modifier = Modifier) {
+        BasicText(defaultText, modifier = modifier.testTag(testTag))
+        dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
+    }
+
+    /** Presses and releases the back button via a key press. */
+    private fun pressBackViaKey() {
+        UiDevice.getInstance(getInstrumentation()).pressBack()
+    }
+
+    /** Dispatches the back button directly, shortcutting any key presses. */
+    private fun dispatchBackButton() {
+        rule.runOnUiThread { dispatcher.onBackPressed() }
+    }
+
+    private fun pressEscape() {
+        UiDevice.getInstance(getInstrumentation()).pressKeyCode(KeyEvent.KEYCODE_ESCAPE)
+    }
+
+    /** Try to dismiss the dialog by clicking between the topLefts of the dialog and the root. */
+    private fun clickOutsideDialog() {
+        val dialogBounds = rule.onNode(isRoot().and(hasAnyChild(isDialog()))).boundsOnScreen()
+        val rootBounds = rule.onNode(isRoot().and(hasAnyChild(isDialog()).not())).boundsOnScreen()
+        val clickPosition = lerp(dialogBounds.topLeft, rootBounds.topLeft, 0.5f).round()
+        UiDevice.getInstance(getInstrumentation()).click(clickPosition.x, clickPosition.y)
+    }
+
+    private fun SemanticsNodeInteraction.boundsOnScreen(): Rect {
+        val bounds = with(rule.density) { getUnclippedBoundsInRoot().toRect() }
+        val positionOnScreen = fetchSemanticsNode().positionOnScreen
+        return bounds.translate(positionOnScreen)
+    }
 }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupTest.kt
index da69e38..84502d7 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/PopupTest.kt
@@ -15,6 +15,7 @@
  */
 package androidx.compose.ui.window
 
+import android.view.KeyEvent
 import android.view.View
 import android.view.View.MEASURED_STATE_TOO_SMALL
 import android.view.ViewGroup
@@ -282,6 +283,36 @@
     }
 
     @Test
+    fun isDismissedOnEscapePress() {
+        var showPopup by mutableStateOf(true)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                if (showPopup) {
+                    Popup(
+                        properties =
+                            PopupProperties(
+                                // Needs to be focusable to intercept key press
+                                focusable = true
+                            ),
+                        alignment = Alignment.Center,
+                        onDismissRequest = { showPopup = false }
+                    ) {
+                        Box(Modifier.size(50.dp).testTag(testTag))
+                    }
+                }
+            }
+        }
+
+        // Popup should be visible
+        rule.onNodeWithTag(testTag).assertIsDisplayed()
+
+        UiDevice.getInstance(getInstrumentation()).pressKeyCode(KeyEvent.KEYCODE_ESCAPE)
+
+        // Popup should not exist
+        rule.onNodeWithTag(testTag).assertDoesNotExist()
+    }
+
+    @Test
     fun isNotDismissedOnTapOutside_dismissOnClickOutsideFalse() {
         var showPopup by mutableStateOf(true)
         rule.setContent {
@@ -346,6 +377,37 @@
     }
 
     @Test
+    fun isNotDismissedOnEscapePress_dismissOnBackPressFalse() {
+        var showPopup by mutableStateOf(true)
+        rule.setContent {
+            Box(Modifier.fillMaxSize()) {
+                if (showPopup) {
+                    Popup(
+                        properties =
+                            PopupProperties(
+                                // Needs to be focusable to intercept key press
+                                focusable = true,
+                                dismissOnBackPress = false
+                            ),
+                        alignment = Alignment.Center,
+                        onDismissRequest = { showPopup = false }
+                    ) {
+                        Box(Modifier.size(50.dp).testTag(testTag))
+                    }
+                }
+            }
+        }
+
+        // Popup should be visible
+        rule.onNodeWithTag(testTag).assertIsDisplayed()
+
+        UiDevice.getInstance(getInstrumentation()).pressKeyCode(KeyEvent.KEYCODE_ESCAPE)
+
+        // Popup should still be visible
+        rule.onNodeWithTag(testTag).assertIsDisplayed()
+    }
+
+    @Test
     fun canFillScreenWidth_dependingOnProperty() {
         var box1Width = 0
         var box2Width = 0
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofill.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofill.android.kt
index 5690e44..f0ec7f9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofill.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidSemanticAutofill.android.kt
@@ -28,7 +28,6 @@
 import android.view.autofill.AutofillManager
 import android.view.autofill.AutofillValue
 import android.view.inputmethod.EditorInfo
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.collection.ArraySet
 import androidx.collection.IntObjectMap
@@ -55,7 +54,6 @@
 import androidx.compose.ui.semantics.SemanticsProperties.Disabled
 import androidx.compose.ui.semantics.SemanticsProperties.EditableText
 import androidx.compose.ui.semantics.SemanticsProperties.Focused
-import androidx.compose.ui.semantics.SemanticsProperties.InvisibleToUser
 import androidx.compose.ui.semantics.SemanticsProperties.MaxTextLength
 import androidx.compose.ui.semantics.SemanticsProperties.Password
 import androidx.compose.ui.semantics.SemanticsProperties.Role
@@ -187,10 +185,10 @@
 
             // Check Visibility —————————
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
-                val previousInvisible = previousNode.unmergedConfig.contains(InvisibleToUser)
-                val currInvisible = currNode.unmergedConfig.contains(InvisibleToUser)
-                if (previousInvisible != currInvisible) {
-                    notifyVisibilityChanged(id, currInvisible)
+                val prevTransparency = previousNode.isTransparent
+                val currTransparency = currNode.isTransparent
+                if (prevTransparency != currTransparency) {
+                    notifyVisibilityChanged(id, currTransparency)
                 }
             }
         }
@@ -298,13 +296,11 @@
             }
 
             /** Registers the autofill debug callback. */
-            @DoNotInline
             fun register(semanticAutofill: AndroidSemanticAutofill) {
                 semanticAutofill.autofillManager.autofillManager.registerCallback(this)
             }
 
             /** Unregisters the autofill debug callback. */
-            @DoNotInline
             fun unregister(semanticAutofill: AndroidSemanticAutofill) {
                 semanticAutofill.autofillManager.autofillManager.unregisterCallback(this)
             }
@@ -379,10 +375,11 @@
     // TODO(MNUZEN): Set setAccessibilityFocused as well
 
     // ———————— Visibility, elevation, alpha
+    // Transparency should be the only thing affecting View.VISIBLE (pruning will take care of all
+    // covered nodes).
     AutofillApi26Helper.setVisibility(
         child,
-        if (!isTransparent && !unmergedConfig.contains(InvisibleToUser)) View.VISIBLE
-        else View.INVISIBLE
+        if (!isTransparent || isRoot) View.VISIBLE else View.INVISIBLE
     )
 
     // TODO(335726351): will call the below method when b/335726351 has been fulfilled and
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillCallback.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillCallback.android.kt
index 992ab58..8dccdef3 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillCallback.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillCallback.android.kt
@@ -20,7 +20,6 @@
 import android.util.Log
 import android.view.View
 import android.view.autofill.AutofillManager
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 /**
@@ -58,13 +57,11 @@
     }
 
     /** Registers the autofill debug callback. */
-    @DoNotInline
     fun register(autofill: AndroidAutofill) {
         autofill.autofillManager.registerCallback(this)
     }
 
     /** Unregisters the autofill debug callback. */
-    @DoNotInline
     fun unregister(autofill: AndroidAutofill) {
         autofill.autofillManager.unregisterCallback(this)
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillUtils.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillUtils.android.kt
index dc2dc2f..6309c56 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillUtils.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AutofillUtils.android.kt
@@ -21,7 +21,6 @@
 import android.view.autofill.AutofillId
 import android.view.autofill.AutofillManager
 import android.view.autofill.AutofillValue
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.platform.AndroidComposeViewAccessibilityDelegateCompat.Companion.ClassName
 import androidx.compose.ui.semantics.Role
@@ -34,7 +33,6 @@
 @RequiresApi(28)
 internal object AutofillApi28Helper {
     @RequiresApi(28)
-    @DoNotInline
     fun setMaxTextLength(structure: ViewStructure, length: Int) = structure.setMaxTextLength(length)
 }
 
@@ -46,7 +44,6 @@
 @RequiresApi(27)
 internal object AutofillApi27Helper {
     @RequiresApi(27)
-    @DoNotInline
     fun notifyViewVisibilityChanged(
         view: View,
         autofillManager: AutofillManager,
@@ -65,15 +62,12 @@
 @RequiresApi(26)
 internal object AutofillApi26Helper {
     @RequiresApi(26)
-    @DoNotInline
     fun newChild(structure: ViewStructure, index: Int): ViewStructure? = structure.newChild(index)
 
     @RequiresApi(26)
-    @DoNotInline
     fun addChildCount(structure: ViewStructure, num: Int) = structure.addChildCount(num)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setId(
         structure: ViewStructure,
         id: Int,
@@ -83,7 +77,6 @@
     ) = structure.setId(id, packageName, typeName, entryName)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setDimens(
         structure: ViewStructure,
         left: Int,
@@ -94,130 +87,104 @@
         height: Int
     ) = structure.setDimens(left, top, scrollX, scrollY, width, height)
 
-    @RequiresApi(26) @DoNotInline fun getAutofillId(structure: ViewStructure) = structure.autofillId
+    @RequiresApi(26) fun getAutofillId(structure: ViewStructure) = structure.autofillId
 
-    @RequiresApi(26) @DoNotInline fun isDate(value: AutofillValue) = value.isDate
+    @RequiresApi(26) fun isDate(value: AutofillValue) = value.isDate
 
-    @RequiresApi(26) @DoNotInline fun isList(value: AutofillValue) = value.isList
+    @RequiresApi(26) fun isList(value: AutofillValue) = value.isList
 
-    @RequiresApi(26) @DoNotInline fun isText(value: AutofillValue) = value.isText
+    @RequiresApi(26) fun isText(value: AutofillValue) = value.isText
 
-    @RequiresApi(26) @DoNotInline fun isToggle(value: AutofillValue) = value.isToggle
+    @RequiresApi(26) fun isToggle(value: AutofillValue) = value.isToggle
 
     @RequiresApi(26)
-    @DoNotInline
     fun setContentDescription(structure: ViewStructure, contentDescription: CharSequence) =
         structure.setContentDescription(contentDescription)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setAutofillHints(structure: ViewStructure, hints: Array<String>) =
         structure.setAutofillHints(hints)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setAutofillId(structure: ViewStructure, parent: AutofillId, virtualId: Int) =
         structure.setAutofillId(parent, virtualId)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setAutofillType(structure: ViewStructure, type: Int) = structure.setAutofillType(type)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setAutofillValue(structure: ViewStructure, value: AutofillValue) =
         structure.setAutofillValue(value)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setCheckable(structure: ViewStructure, checkable: Boolean) =
         structure.setCheckable(checkable)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setChecked(structure: ViewStructure, checked: Boolean) = structure.setChecked(checked)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setChildCount(structure: ViewStructure, numChildren: Int) {
         structure.childCount = numChildren
     }
 
     @RequiresApi(26)
-    @DoNotInline
     fun setClassName(structure: ViewStructure, classname: String) =
         structure.setClassName(classname)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setClickable(structure: ViewStructure, clickable: Boolean) =
         structure.setClickable(clickable)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setDataIsSensitive(structure: ViewStructure, isSensitive: Boolean) =
         structure.setDataIsSensitive(isSensitive)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setEnabled(structure: ViewStructure, enabled: Boolean) = structure.setEnabled(enabled)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setFocusable(structure: ViewStructure, focusable: Boolean) =
         structure.setFocusable(focusable)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setFocused(structure: ViewStructure, focused: Boolean) = structure.setFocused(focused)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setInputType(structure: ViewStructure, type: Int) = structure.setInputType(type)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setLongClickable(structure: ViewStructure, longClickable: Boolean) =
         structure.setLongClickable(longClickable)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setOpaque(structure: ViewStructure, isOpaque: Boolean) = structure.setOpaque(isOpaque)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setSelected(structure: ViewStructure, isSelected: Boolean) =
         structure.setSelected(isSelected)
 
     @RequiresApi(26)
-    @DoNotInline
     fun setText(structure: ViewStructure, text: CharSequence) {
         structure.text = text
     }
 
     @RequiresApi(26)
-    @DoNotInline
     fun setVisibility(structure: ViewStructure, visibility: Int) =
         structure.setVisibility(visibility)
 
-    @RequiresApi(26)
-    @DoNotInline
-    fun textValue(value: AutofillValue): CharSequence = value.textValue
+    @RequiresApi(26) fun textValue(value: AutofillValue): CharSequence = value.textValue
+
+    @RequiresApi(26) fun booleanValue(value: AutofillValue): Boolean = value.toggleValue
+
+    @RequiresApi(26) fun listValue(value: AutofillValue): Int = value.listValue
 
     @RequiresApi(26)
-    @DoNotInline
-    fun booleanValue(value: AutofillValue): Boolean = value.toggleValue
-
-    @RequiresApi(26) @DoNotInline fun listValue(value: AutofillValue): Int = value.listValue
-
-    @RequiresApi(26)
-    @DoNotInline
     fun getAutofillTextValue(value: String): AutofillValue {
         return AutofillValue.forText(value)
     }
 
     @RequiresApi(26)
-    @DoNotInline
     fun setAutofillTypeForViewStruct(child: ViewStructure, dataType: ContentDataType) {
         val autofillType =
             when (dataType) {
@@ -238,6 +205,6 @@
         Role.RadioButton -> "android.widget.RadioButton"
         Role.Image -> "android.widget.ImageView"
         Role.DropdownList -> "android.widget.Spinner"
-        Role.NumberPicker -> "android.widget.NumberPicker"
+        Role.ValuePicker -> "android.widget.NumberPicker"
         else -> ClassName
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt
index be29fd6..00b18c8 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/contentcapture/AndroidContentCaptureManager.android.kt
@@ -24,7 +24,6 @@
 import android.view.translation.TranslationRequestValue
 import android.view.translation.ViewTranslationRequest
 import android.view.translation.ViewTranslationResponse
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.collection.ArraySet
@@ -555,7 +554,6 @@
 
     @RequiresApi(Build.VERSION_CODES.S)
     private object ViewTranslationHelperMethods {
-        @DoNotInline
         @Suppress("UNUSED_PARAMETER")
         @RequiresApi(Build.VERSION_CODES.S)
         fun onCreateVirtualViewTranslationRequests(
@@ -590,7 +588,6 @@
             }
         }
 
-        @DoNotInline
         @RequiresApi(Build.VERSION_CODES.S)
         fun onVirtualViewTranslationResponses(
             contentCaptureManager: AndroidContentCaptureManager,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
index 06976c5..0ee2f3f 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
@@ -35,7 +35,6 @@
 import android.view.MotionEvent.TOOL_TYPE_MOUSE
 import android.view.MotionEvent.TOOL_TYPE_STYLUS
 import android.view.MotionEvent.TOOL_TYPE_UNKNOWN
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.compose.ui.geometry.Offset
@@ -342,7 +341,6 @@
  */
 @RequiresApi(Build.VERSION_CODES.Q)
 private object MotionEventHelper {
-    @DoNotInline
     fun toRawOffset(motionEvent: MotionEvent, index: Int): Offset {
         return Offset(motionEvent.getRawX(index), motionEvent.getRawY(index))
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.android.kt
index b884769..f524439 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.android.kt
@@ -16,8 +16,8 @@
 
 package androidx.compose.ui.input.pointer
 
+import android.view.PointerIcon.TYPE_ARROW
 import android.view.PointerIcon.TYPE_CROSSHAIR
-import android.view.PointerIcon.TYPE_DEFAULT
 import android.view.PointerIcon.TYPE_HAND
 import android.view.PointerIcon.TYPE_TEXT
 
@@ -68,7 +68,7 @@
 /** Creates [PointerIcon] from pointer icon type (see [android.view.PointerIcon.getSystemIcon] */
 fun PointerIcon(pointerIconType: Int): PointerIcon = AndroidPointerIconType(pointerIconType)
 
-internal actual val pointerIconDefault: PointerIcon = AndroidPointerIconType(TYPE_DEFAULT)
+internal actual val pointerIconDefault: PointerIcon = AndroidPointerIconType(TYPE_ARROW)
 internal actual val pointerIconCrosshair: PointerIcon = AndroidPointerIconType(TYPE_CROSSHAIR)
 internal actual val pointerIconText: PointerIcon = AndroidPointerIconType(TYPE_TEXT)
 internal actual val pointerIconHand: PointerIcon = AndroidPointerIconType(TYPE_HAND)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.android.kt
new file mode 100644
index 0000000..17b1415
--- /dev/null
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.android.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("LayoutCoordinatesKt")
+@file:JvmMultifileClass
+
+package androidx.compose.ui.layout
+
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.input.pointer.MatrixPositionCalculator
+import androidx.compose.ui.node.NodeCoordinator
+import androidx.compose.ui.node.requireOwner
+
+/**
+ * Takes a [matrix] which transforms some coordinate system `C` to local coordinates, and updates
+ * the matrix to transform from `C` to screen coordinates instead.
+ */
+@Suppress("DocumentExceptions")
+fun LayoutCoordinates.transformToScreen(matrix: Matrix) {
+    val rootCoordinates = findRootCoordinates()
+
+    // transformFrom resets matrix, so apply it to temporary one.
+    val tmpMatrix = Matrix()
+    rootCoordinates.transformFrom(this, tmpMatrix)
+    matrix *= tmpMatrix
+
+    val owner = toCoordinatorOrNull()?.layoutNode?.requireOwner() as? MatrixPositionCalculator
+    if (owner != null) {
+        owner.localToScreen(matrix)
+    } else {
+        // Fallback: try to extract just position
+        val screenPosition = rootCoordinates.positionOnScreen()
+        if (screenPosition.isSpecified) {
+            matrix.translate(screenPosition.x, screenPosition.y, 0f)
+        }
+    }
+}
+
+private fun LayoutCoordinates.toCoordinatorOrNull() =
+    (this as? LookaheadLayoutCoordinates)?.coordinator ?: this as? NodeCoordinator
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAccessibilityManager.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAccessibilityManager.android.kt
index 5090ca5..2d9e7c9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAccessibilityManager.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidAccessibilityManager.android.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 /** Android implementation for [AccessibilityManager]. */
@@ -79,7 +78,6 @@
  */
 @RequiresApi(Build.VERSION_CODES.Q)
 internal object Api29Impl {
-    @DoNotInline
     fun getRecommendedTimeoutMillis(
         accessibilityManager: android.view.accessibility.AccessibilityManager,
         originalTimeout: Int,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt
index 6fdcb29..e19ccf3 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidClipboardManager.android.kt
@@ -25,7 +25,6 @@
 import android.text.SpannableString
 import android.text.Spanned
 import android.util.Base64
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
@@ -123,7 +122,6 @@
 @RequiresApi(28)
 private object Api28ClipboardManagerClipClear {
 
-    @DoNotInline
     @JvmStatic
     fun clearPrimaryClip(clipboardManager: android.content.ClipboardManager) {
         clipboardManager.clearPrimaryClip()
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index b2a4b98..da494eb 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -144,12 +144,12 @@
 import androidx.compose.ui.input.key.type
 import androidx.compose.ui.input.pointer.AndroidPointerIcon
 import androidx.compose.ui.input.pointer.AndroidPointerIconType
+import androidx.compose.ui.input.pointer.MatrixPositionCalculator
 import androidx.compose.ui.input.pointer.MotionEventAdapter
 import androidx.compose.ui.input.pointer.PointerIcon
 import androidx.compose.ui.input.pointer.PointerIconService
 import androidx.compose.ui.input.pointer.PointerInputEventProcessor
 import androidx.compose.ui.input.pointer.PointerKeyboardModifiers
-import androidx.compose.ui.input.pointer.PositionCalculator
 import androidx.compose.ui.input.pointer.ProcessResult
 import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
 import androidx.compose.ui.input.rotary.RotaryScrollEvent
@@ -224,7 +224,7 @@
 @Suppress("ViewConstructor", "VisibleForTests", "ConstPropertyName", "NullAnnotationGroup")
 @OptIn(InternalComposeUiApi::class)
 internal class AndroidComposeView(context: Context, coroutineContext: CoroutineContext) :
-    ViewGroup(context), Owner, ViewRootForTest, PositionCalculator, DefaultLifecycleObserver {
+    ViewGroup(context), Owner, ViewRootForTest, MatrixPositionCalculator, DefaultLifecycleObserver {
 
     /**
      * Remembers the position of the last pointer input event that was down. This position will be
@@ -410,19 +410,6 @@
 
     private val canvasHolder = CanvasHolder()
 
-    // Backed by mutableStateOf so that the ambient provider recomposes when it changes
-    override var layoutDirection by
-        mutableStateOf(
-            // We don't use the attached View's layout direction here since that layout direction
-            // may not
-            // be resolved since composables may be composed without attaching to the RootViewImpl.
-            // In Jetpack Compose, use the locale layout direction (i.e. layoutDirection came from
-            // configuration) as a default layout direction.
-            toLayoutDirection(context.resources.configuration.layoutDirection)
-                ?: LayoutDirection.Ltr
-        )
-        private set
-
     override val viewConfiguration: ViewConfiguration =
         AndroidViewConfiguration(android.view.ViewConfiguration.get(context))
 
@@ -430,7 +417,6 @@
         LayoutNode().also {
             it.measurePolicy = RootMeasurePolicy
             it.density = density
-            it.layoutDirection = layoutDirection
             it.viewConfiguration = viewConfiguration
             // Composed modifiers cannot be added here directly
             it.modifier =
@@ -652,6 +638,19 @@
     private val Configuration.fontWeightAdjustmentCompat: Int
         get() = if (SDK_INT >= S) fontWeightAdjustment else 0
 
+    // Backed by mutableStateOf so that the ambient provider recomposes when it changes
+    override var layoutDirection by
+        mutableStateOf(
+            // We don't use the attached View's layout direction here since that layout direction
+            // may not
+            // be resolved since composables may be composed without attaching to the RootViewImpl.
+            // In Jetpack Compose, use the locale layout direction (i.e. layoutDirection came from
+            // configuration) as a default layout direction.
+            toLayoutDirection(context.resources.configuration.layoutDirection)
+                ?: LayoutDirection.Ltr
+        )
+        private set
+
     /** Provide haptic feedback to the user. Use the Android version of haptic feedback. */
     override val hapticFeedBack: HapticFeedback = PlatformHapticFeedback(this)
 
@@ -1231,9 +1230,10 @@
     fun removeAndroidView(view: AndroidViewHolder) {
         registerOnEndApplyChangesListener {
             androidViewsHandler.removeViewInLayout(view)
-            androidViewsHandler.layoutNodeToHolder.remove(
-                androidViewsHandler.holderToLayoutNode.remove(view)
-            )
+            val layoutNode = androidViewsHandler.holderToLayoutNode.remove(view)
+            if (layoutNode != null) {
+                androidViewsHandler.layoutNodeToHolder.remove(layoutNode)
+            }
             view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
         }
     }
@@ -1297,6 +1297,7 @@
                     requestLayout()
                 }
                 measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
+                _androidViewsHandler?.layoutChildViewsIfNeeded()
                 dispatchPendingInteropLayoutCallbacks()
             }
         }
@@ -1310,6 +1311,7 @@
             // it allows us to not traverse the hierarchy twice.
             if (!measureAndLayoutDelegate.hasPendingMeasureOrLayout) {
                 measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
+                _androidViewsHandler?.layoutChildViewsIfNeeded()
                 dispatchPendingInteropLayoutCallbacks()
             }
         }
@@ -1435,6 +1437,7 @@
         // View is not yet laid out.
         updatePositionCacheAndDispatch()
         if (_androidViewsHandler != null) {
+            androidViewsHandler.layoutChildViewsIfNeeded()
             // Even if we laid out during onMeasure, we want to set the bounds of the
             // AndroidViewsHandler for accessibility and for Views making assumptions based on
             // the size of their ancestors. Usually the Views in the hierarchy will not
@@ -2435,6 +2438,27 @@
 
     override fun shouldDelayChildPressedState(): Boolean = false
 
+    // Track sensitive composable visible in this view
+    private var sensitiveComponentCount = 0
+
+    override fun incrementSensitiveComponentCount() {
+        if (SDK_INT >= 35) {
+            if (sensitiveComponentCount == 0) {
+                AndroidComposeViewSensitiveContent35.setContentSensitivity(view, true)
+            }
+            sensitiveComponentCount += 1
+        }
+    }
+
+    override fun decrementSensitiveComponentCount() {
+        if (SDK_INT >= 35) {
+            if (sensitiveComponentCount == 1) {
+                AndroidComposeViewSensitiveContent35.setContentSensitivity(view, false)
+            }
+            sensitiveComponentCount -= 1
+        }
+    }
+
     companion object {
         private var systemPropertiesClass: Class<*>? = null
         private var getBooleanMethod: Method? = null
@@ -2618,6 +2642,19 @@
     fun calculateMatrixToWindow(view: View, matrix: Matrix)
 }
 
+@RequiresApi(35)
+private object AndroidComposeViewSensitiveContent35 {
+    @DoNotInline
+    @RequiresApi(35)
+    fun setContentSensitivity(view: View, isSensitiveContent: Boolean) {
+        if (isSensitiveContent) {
+            view.setContentSensitivity(View.CONTENT_SENSITIVITY_SENSITIVE)
+        } else {
+            view.setContentSensitivity(View.CONTENT_SENSITIVITY_AUTO)
+        }
+    }
+}
+
 @RequiresApi(Q)
 private class CalculateMatrixToWindowApi29 : CalculateMatrixToWindow {
     private val tmpMatrix = android.graphics.Matrix()
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 f5e7c2c..7298bd6 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
@@ -35,7 +35,6 @@
 import android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH
 import android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX
 import android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
-import androidx.annotation.DoNotInline
 import androidx.annotation.IntRange
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
@@ -209,7 +208,7 @@
     // flaky, so we use this callback to test accessibility events.
     @VisibleForTesting
     internal var onSendAccessibilityEvent: (AccessibilityEvent) -> Boolean = {
-        view.parent.requestSendAccessibilityEvent(view, it)
+        trace("sendAccessibilityEvent") { view.parent.requestSendAccessibilityEvent(view, it) }
     }
 
     private val accessibilityManager: AccessibilityManager =
@@ -767,7 +766,7 @@
 
         // Set a classname for text nodes before setting a classname based on a role ensuring that
         // the latter if present wins (see b/343392125)
-        if (semanticsNode.unmergedConfig.contains(SemanticsActions.SetText)) {
+        if (semanticsNode.unmergedConfig.contains(SemanticsProperties.EditableText)) {
             info.className = TextFieldClassName
         }
         if (semanticsNode.unmergedConfig.contains(SemanticsProperties.Text)) {
@@ -1356,7 +1355,7 @@
             }
         }
 
-        if (node.unmergedConfig.contains(SemanticsActions.SetText)) {
+        if (node.unmergedConfig.contains(SemanticsProperties.EditableText)) {
             stateDescription = createStateDescriptionForTextField(node)
         }
 
@@ -1520,7 +1519,7 @@
             event.contentDescription = contentDescription.fastJoinToString(",")
         }
 
-        return trace("sendEvent") { sendEvent(event) }
+        return sendEvent(event)
     }
 
     /**
@@ -2240,47 +2239,45 @@
         try {
             val subtreeChangedSemanticsNodesIds = MutableIntSet()
             for (notification in boundsUpdateChannel) {
-                trace("AccessibilityLoopIteration") {
-                    if (isEnabled) {
-                        for (i in subtreeChangedLayoutNodes.indices) {
-                            val layoutNode = subtreeChangedLayoutNodes.valueAt(i)
-                            trace("sendSubtreeChangeAccessibilityEvents") {
-                                sendSubtreeChangeAccessibilityEvents(
-                                    layoutNode,
-                                    subtreeChangedSemanticsNodesIds
-                                )
-                            }
-                            trace("sendTypeViewScrolledAccessibilityEvent") {
-                                sendTypeViewScrolledAccessibilityEvent(layoutNode)
-                            }
+                if (isEnabled) {
+                    for (i in subtreeChangedLayoutNodes.indices) {
+                        val layoutNode = subtreeChangedLayoutNodes.valueAt(i)
+                        trace("sendSubtreeChangeAccessibilityEvents") {
+                            sendSubtreeChangeAccessibilityEvents(
+                                layoutNode,
+                                subtreeChangedSemanticsNodesIds
+                            )
                         }
-                        subtreeChangedSemanticsNodesIds.clear()
-                        // When the bounds of layout nodes change, we will not always get semantics
-                        // change notifications because bounds is not part of semantics. And bounds
-                        // change from a layout node without semantics will affect the global bounds
-                        // of it children which has semantics. Bounds change will affect which nodes
-                        // are covered and which nodes are not, so the currentSemanticsNodes is not
-                        // up to date anymore.
-                        // After the subtree events are sent, accessibility services will get the
-                        // current visible/invisible state. We also try to do semantics tree diffing
-                        // to send out the proper accessibility events and update our copy here so
-                        // that
-                        // our incremental changes (represented by accessibility events) are
-                        // consistent
-                        // with accessibility services. That is: change - notify - new change -
-                        // notify, if we don't do the tree diffing and update our copy here, we will
-                        // combine old change and new change, which is missing finer-grained
-                        // notification.
-                        if (!checkingForSemanticsChanges) {
-                            checkingForSemanticsChanges = true
-                            handler.post(semanticsChangeChecker)
+                        trace("sendTypeViewScrolledAccessibilityEvent") {
+                            sendTypeViewScrolledAccessibilityEvent(layoutNode)
                         }
                     }
-                    subtreeChangedLayoutNodes.clear()
-                    pendingHorizontalScrollEvents.clear()
-                    pendingVerticalScrollEvents.clear()
-                    delay(SendRecurringAccessibilityEventsIntervalMillis)
+                    subtreeChangedSemanticsNodesIds.clear()
+                    // When the bounds of layout nodes change, we will not always get semantics
+                    // change notifications because bounds is not part of semantics. And bounds
+                    // change from a layout node without semantics will affect the global bounds
+                    // of it children which has semantics. Bounds change will affect which nodes
+                    // are covered and which nodes are not, so the currentSemanticsNodes is not
+                    // up to date anymore.
+                    // After the subtree events are sent, accessibility services will get the
+                    // current visible/invisible state. We also try to do semantics tree diffing
+                    // to send out the proper accessibility events and update our copy here so
+                    // that
+                    // our incremental changes (represented by accessibility events) are
+                    // consistent
+                    // with accessibility services. That is: change - notify - new change -
+                    // notify, if we don't do the tree diffing and update our copy here, we will
+                    // combine old change and new change, which is missing finer-grained
+                    // notification.
+                    if (!checkingForSemanticsChanges) {
+                        checkingForSemanticsChanges = true
+                        handler.post(semanticsChangeChecker)
+                    }
                 }
+                subtreeChangedLayoutNodes.clear()
+                pendingHorizontalScrollEvents.clear()
+                pendingVerticalScrollEvents.clear()
+                delay(SendRecurringAccessibilityEventsIntervalMillis)
             }
         } finally {
             subtreeChangedLayoutNodes.clear()
@@ -2615,7 +2612,7 @@
                             val newNodeIsPassword =
                                 newNode.unmergedConfig.contains(SemanticsProperties.Password)
                             val oldNodeIsTextfield =
-                                oldNode.unmergedConfig.contains(SemanticsActions.SetText)
+                                oldNode.unmergedConfig.contains(SemanticsProperties.EditableText)
 
                             // (b/247891690) We won't send a text change event when we only toggle
                             // the password visibility of the node
@@ -3151,7 +3148,7 @@
             return node.unmergedConfig[SemanticsProperties.ContentDescription].fastJoinToString(",")
         }
 
-        if (node.unmergedConfig.contains(SemanticsActions.SetText)) {
+        if (node.unmergedConfig.contains(SemanticsProperties.EditableText)) {
             return node.unmergedConfig.getTextForTextField()?.text
         }
 
@@ -3193,7 +3190,6 @@
 
     @RequiresApi(Build.VERSION_CODES.N)
     private object Api24Impl {
-        @DoNotInline
         @JvmStatic
         fun addSetProgressAction(info: AccessibilityNodeInfoCompat, semanticsNode: SemanticsNode) {
             if (semanticsNode.enabled()) {
@@ -3212,7 +3208,6 @@
     @RequiresApi(Build.VERSION_CODES.Q)
     private object Api29Impl {
         @JvmStatic
-        @DoNotInline
         fun addPageActions(info: AccessibilityNodeInfoCompat, semanticsNode: SemanticsNode) {
             if (semanticsNode.enabled()) {
                 semanticsNode.unmergedConfig.getOrNull(SemanticsActions.PageUp)?.let {
@@ -3268,7 +3263,7 @@
 private fun SemanticsNode.excludeLineAndPageGranularities(): Boolean {
     // text field that is not in focus
     if (
-        unmergedConfig.contains(SemanticsActions.SetText) &&
+        unmergedConfig.contains(SemanticsProperties.EditableText) &&
             unmergedConfig.getOrNull(SemanticsProperties.Focused) != true
     )
         return true
@@ -3279,7 +3274,7 @@
             // looking for text field merging node
             val ancestorSemanticsConfiguration = it.collapsedSemantics
             ancestorSemanticsConfiguration?.isMergingSemanticsOfDescendants == true &&
-                ancestorSemanticsConfiguration.contains(SemanticsActions.SetText)
+                ancestorSemanticsConfiguration.contains(SemanticsProperties.EditableText)
         }
     return ancestor != null &&
         ancestor.collapsedSemantics?.getOrNull(SemanticsProperties.Focused) != true
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidCompositionLocals.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidCompositionLocals.android.kt
index b865c80..fd0963c 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidCompositionLocals.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidCompositionLocals.android.kt
@@ -126,6 +126,7 @@
                 resourceIdCache.clear()
             }
 
+            @Deprecated("This callback is superseded by onTrimMemory")
             override fun onLowMemory() {
                 resourceIdCache.clear()
             }
@@ -160,6 +161,7 @@
                 currentConfiguration.setTo(configuration)
             }
 
+            @Deprecated("This callback is superseded by onTrimMemory")
             override fun onLowMemory() {
                 imageVectorCache.clear()
             }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoader.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoader.android.kt
index f8db271..6c34d23 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoader.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoader.android.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.graphics.Typeface
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.ResourceFont
@@ -59,7 +58,6 @@
 @RequiresApi(26)
 private object AndroidFontResourceLoaderHelper {
     @RequiresApi(26)
-    @DoNotInline
     fun create(context: Context, resourceId: Int): Typeface {
         return context.resources.getFont(resourceId)
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidTextToolbar.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidTextToolbar.android.kt
index 9e9d4b6e..4cee3f5 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidTextToolbar.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidTextToolbar.android.kt
@@ -19,7 +19,6 @@
 import android.os.Build
 import android.view.ActionMode
 import android.view.View
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.platform.actionmodecallback.FloatingTextActionModeCallback
@@ -78,7 +77,6 @@
 @RequiresApi(23)
 internal object TextToolbarHelperMethods {
     @RequiresApi(23)
-    @DoNotInline
     fun startActionMode(
         view: View,
         actionModeCallback: ActionMode.Callback,
@@ -88,7 +86,6 @@
     }
 
     @RequiresApi(23)
-    @DoNotInline
     fun invalidateContentRect(actionMode: ActionMode) {
         actionMode.invalidateContentRect()
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewConfiguration.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewConfiguration.android.kt
index 146a0ee..3d746f3 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewConfiguration.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewConfiguration.android.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.platform
 
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.platform.AndroidViewConfigurationApi34.getScaledHandwritingGestureLineMargin
 import androidx.compose.ui.platform.AndroidViewConfigurationApi34.getScaledHandwritingSlop
@@ -62,11 +61,9 @@
 
 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 private object AndroidViewConfigurationApi34 {
-    @DoNotInline
     fun getScaledHandwritingSlop(viewConfiguration: android.view.ViewConfiguration) =
         viewConfiguration.scaledHandwritingSlop.toFloat()
 
-    @DoNotInline
     fun getScaledHandwritingGestureLineMargin(viewConfiguration: android.view.ViewConfiguration) =
         viewConfiguration.scaledHandwritingGestureLineMargin.toFloat()
 }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt
index 40eb0306..6df225f 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt
@@ -24,6 +24,7 @@
 import android.view.View.MeasureSpec.EXACTLY
 import android.view.View.MeasureSpec.getMode
 import android.view.ViewGroup
+import androidx.collection.mutableScatterMapOf
 import androidx.compose.ui.internal.requirePrecondition
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.viewinterop.AndroidViewHolder
@@ -38,8 +39,8 @@
         clipChildren = false
     }
 
-    val holderToLayoutNode = hashMapOf<AndroidViewHolder, LayoutNode>()
-    val layoutNodeToHolder = hashMapOf<LayoutNode, AndroidViewHolder>()
+    val holderToLayoutNode = mutableScatterMapOf<AndroidViewHolder, LayoutNode>()
+    val layoutNodeToHolder = mutableScatterMapOf<LayoutNode, AndroidViewHolder>()
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
         // Layout will be handled by component nodes. However, we act like proper measurement
@@ -57,14 +58,18 @@
         // Remeasure children, such that, if ViewRootImpl did forceLayout(), the holders
         // will be set PFLAG_LAYOUT_REQUIRED and they will be relaid out during the next layout.
         // This will ensure that the need relayout flags will be cleared correctly.
-        holderToLayoutNode.keys.forEach { it.remeasure() }
+        holderToLayoutNode.forEachKey { it.remeasure() }
+    }
+
+    fun layoutChildViewsIfNeeded() {
+        holderToLayoutNode.forEachKey { androidViewHolder -> androidViewHolder.layoutIfNeeded() }
     }
 
     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
         // Layout was already handled by component nodes, but replace here because
         // the View system has forced relayout on children. This method will only be called
         // when forceLayout is called on the Views hierarchy.
-        holderToLayoutNode.keys.forEach { it.layout(it.left, it.top, it.right, it.bottom) }
+        holderToLayoutNode.forEachKey { it.layout(it.left, it.top, it.right, it.bottom) }
     }
 
     // No call to super to avoid invalidating the AndroidComposeView and the handler, and rely on
@@ -91,7 +96,7 @@
         // requestLayout() was called by a child, so we have to request remeasurement for
         // their corresponding layout node.
         for (i in 0 until childCount) {
-            val child = getChildAt(i)
+            val child = getChildAt(i) as AndroidViewHolder
             val node = holderToLayoutNode[child]
             if (child.isLayoutRequested && node != null) {
                 node.requestRemeasure()
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
index 2f492d15..9a2afc9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
@@ -34,6 +34,7 @@
 import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.isIdentity
 import androidx.compose.ui.graphics.layer.CompositingStrategy
 import androidx.compose.ui.graphics.layer.GraphicsLayer
 import androidx.compose.ui.graphics.layer.drawLayer
@@ -83,6 +84,9 @@
      * somewhat transparent (i.e. alpha less than 1.0f)
      */
     private var softwareLayerPaint: Paint? = null
+    private var isMatrixDirty = false
+    private var isInverseMatrixDirty = false
+    private var isIdentity = true
 
     override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
         val maybeChangedFields = scope.mutatedFields or mutatedFields
@@ -161,6 +165,10 @@
                     else -> throw IllegalStateException("Not supported composition strategy")
                 }
         }
+        if (maybeChangedFields and Fields.MatrixAffectingFields != 0) {
+            isMatrixDirty = true
+            isInverseMatrixDirty = true
+        }
 
         var outlineChanged = false
 
@@ -314,23 +322,27 @@
     }
 
     override fun mapOffset(point: Offset, inverse: Boolean): Offset {
-        return if (inverse) {
-            getInverseMatrix()?.map(point) ?: Offset.Infinite
+        val matrix =
+            if (inverse) {
+                getInverseMatrix() ?: return Offset.Infinite
+            } else {
+                getMatrix()
+            }
+        return if (isIdentity) {
+            point
         } else {
-            getMatrix().map(point)
+            matrix.map(point)
         }
     }
 
     override fun mapBounds(rect: MutableRect, inverse: Boolean) {
-        if (inverse) {
-            val matrix = getInverseMatrix()
+        val matrix = if (inverse) getInverseMatrix() else getMatrix()
+        if (!isIdentity) {
             if (matrix == null) {
                 rect.set(0f, 0f, 0f, 0f)
             } else {
                 matrix.map(rect)
             }
-        } else {
-            getMatrix().map(rect)
         }
     }
 
@@ -383,38 +395,53 @@
     }
 
     private fun getInverseMatrix(): Matrix? {
-        val matrix = getMatrix()
         val inverseMatrix = inverseMatrixCache ?: Matrix().also { inverseMatrixCache = it }
-        return if (matrix.invertTo(inverseMatrix)) {
+        if (!isInverseMatrixDirty) {
+            if (inverseMatrix[0, 0].isNaN()) {
+                return null
+            }
+            return inverseMatrix
+        }
+        isInverseMatrixDirty = false
+        val matrix = getMatrix()
+        return if (isIdentity) {
+            matrix
+        } else if (matrix.invertTo(inverseMatrix)) {
             inverseMatrix
         } else {
+            inverseMatrix[0, 0] = Float.NaN
             null
         }
     }
 
-    private fun updateMatrix() =
-        with(graphicsLayer) {
-            val (x, y) =
-                if (pivotOffset.isUnspecified) {
-                    [email protected]().center
-                } else {
-                    pivotOffset
-                }
+    private fun updateMatrix() {
+        if (isMatrixDirty) {
+            with(graphicsLayer) {
+                val (x, y) =
+                    if (pivotOffset.isUnspecified) {
+                        [email protected]().center
+                    } else {
+                        pivotOffset
+                    }
 
-            matrixCache.resetToPivotedTransform(
-                x,
-                y,
-                translationX,
-                translationY,
-                1.0f,
-                rotationX,
-                rotationY,
-                rotationZ,
-                scaleX,
-                scaleY,
-                1.0f
-            )
+                matrixCache.resetToPivotedTransform(
+                    x,
+                    y,
+                    translationX,
+                    translationY,
+                    1.0f,
+                    rotationX,
+                    rotationY,
+                    rotationZ,
+                    scaleX,
+                    scaleY,
+                    1.0f
+                )
+            }
+            isMatrixDirty = false
+            isIdentity = matrixCache.isIdentity()
         }
+    }
 
     /**
      * Manually clips the content of the RenderNodeLayer in the provided canvas. This is used only
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt
index dd372bc..3438c74 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt
@@ -17,7 +17,10 @@
 package androidx.compose.ui.platform
 
 import android.graphics.Matrix as AndroidMatrix
+import androidx.compose.ui.geometry.MutableRect
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.graphics.isIdentity
 import androidx.compose.ui.graphics.setFrom
 
 /**
@@ -33,12 +36,13 @@
 ) {
     private var androidMatrixCache: AndroidMatrix? = null
     private var previousAndroidMatrix: AndroidMatrix? = null
-    private var matrixCache: Matrix? = null
-    private var inverseMatrixCache: Matrix? = null
+    private var matrixCache: Matrix = Matrix()
+    private var inverseMatrixCache: Matrix = Matrix()
 
-    private var isDirty = true
-    private var isInverseDirty = true
+    private var isDirty = false
+    private var isInverseDirty = false
     private var isInverseValid = true
+    private var isIdentity = true
 
     /**
      * Ensures that the internal matrix will be updated next time [calculateMatrix] or
@@ -54,7 +58,7 @@
      * Returns the cached [Matrix], updating it if required (if [invalidate] was previously called).
      */
     fun calculateMatrix(target: T): Matrix {
-        val matrix = matrixCache ?: Matrix().also { matrixCache = it }
+        val matrix = matrixCache
         if (!isDirty) {
             return matrix
         }
@@ -71,6 +75,7 @@
         }
 
         isDirty = false
+        isIdentity = matrix.isIdentity()
         return matrix
     }
 
@@ -80,7 +85,7 @@
      * when scaling is 0.
      */
     fun calculateInverseMatrix(target: T): Matrix? {
-        val matrix = inverseMatrixCache ?: Matrix().also { inverseMatrixCache = it }
+        val matrix = inverseMatrixCache
         if (isInverseDirty) {
             val normalMatrix = calculateMatrix(target)
             isInverseValid = normalMatrix.invertTo(matrix)
@@ -88,4 +93,40 @@
         }
         return if (isInverseValid) matrix else null
     }
+
+    fun map(target: T, rect: MutableRect) {
+        val matrix = calculateMatrix(target)
+        if (!isIdentity) {
+            matrix.map(rect)
+        }
+    }
+
+    fun mapInverse(target: T, rect: MutableRect) {
+        val matrix = calculateInverseMatrix(target)
+        if (matrix == null) {
+            rect.set(0f, 0f, 0f, 0f)
+        } else if (!isIdentity) {
+            matrix.map(rect)
+        }
+    }
+
+    fun map(target: T, offset: Offset): Offset {
+        val matrix = calculateMatrix(target)
+        return if (!isIdentity) {
+            matrix.map(offset)
+        } else {
+            offset
+        }
+    }
+
+    fun mapInverse(target: T, offset: Offset): Offset {
+        val matrix = calculateInverseMatrix(target)
+        return if (matrix == null) {
+            Offset.Infinite
+        } else if (!isIdentity) {
+            matrix.map(offset)
+        } else {
+            offset
+        }
+    }
 }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt
index 887c78d..4de5e03 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt
@@ -376,22 +376,18 @@
 @RequiresApi(Build.VERSION_CODES.P)
 private object RenderNodeVerificationHelper28 {
 
-    @androidx.annotation.DoNotInline
     fun getAmbientShadowColor(renderNode: RenderNode): Int {
         return renderNode.ambientShadowColor
     }
 
-    @androidx.annotation.DoNotInline
     fun setAmbientShadowColor(renderNode: RenderNode, target: Int) {
         renderNode.ambientShadowColor = target
     }
 
-    @androidx.annotation.DoNotInline
     fun getSpotShadowColor(renderNode: RenderNode): Int {
         return renderNode.spotShadowColor
     }
 
-    @androidx.annotation.DoNotInline
     fun setSpotShadowColor(renderNode: RenderNode, target: Int) {
         renderNode.spotShadowColor = target
     }
@@ -400,7 +396,6 @@
 @RequiresApi(Build.VERSION_CODES.N)
 private object RenderNodeVerificationHelper24 {
 
-    @androidx.annotation.DoNotInline
     fun discardDisplayList(renderNode: RenderNode) {
         renderNode.discardDisplayList()
     }
@@ -409,7 +404,6 @@
 @RequiresApi(Build.VERSION_CODES.M)
 private object RenderNodeVerificationHelper23 {
 
-    @androidx.annotation.DoNotInline
     fun destroyDisplayListData(renderNode: RenderNode) {
         renderNode.destroyDisplayListData()
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt
index 04cde59..bfe266f 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt
@@ -272,7 +272,6 @@
 @RequiresApi(Build.VERSION_CODES.S)
 private object RenderNodeApi29VerificationHelper {
 
-    @androidx.annotation.DoNotInline
     fun setRenderEffect(renderNode: RenderNode, target: RenderEffect?) {
         renderNode.setRenderEffect(target?.asAndroidRenderEffect())
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
index 63081a2..f862297 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
@@ -102,9 +102,7 @@
 
     @RequiresApi(29)
     private object UniqueDrawingIdApi29 {
-        @JvmStatic
-        @androidx.annotation.DoNotInline
-        fun getUniqueDrawingId(view: View) = view.uniqueDrawingId
+        @JvmStatic fun getUniqueDrawingId(view: View) = view.uniqueDrawingId
     }
 
     private var mutatedFields: Int = 0
@@ -342,22 +340,17 @@
 
     override fun mapOffset(point: Offset, inverse: Boolean): Offset {
         return if (inverse) {
-            matrixCache.calculateInverseMatrix(renderNode)?.map(point) ?: Offset.Infinite
+            matrixCache.mapInverse(renderNode, point)
         } else {
-            matrixCache.calculateMatrix(renderNode).map(point)
+            matrixCache.map(renderNode, point)
         }
     }
 
     override fun mapBounds(rect: MutableRect, inverse: Boolean) {
         if (inverse) {
-            val matrix = matrixCache.calculateInverseMatrix(renderNode)
-            if (matrix == null) {
-                rect.set(0f, 0f, 0f, 0f)
-            } else {
-                matrix.map(rect)
-            }
+            matrixCache.mapInverse(renderNode, rect)
         } else {
-            matrixCache.calculateMatrix(renderNode).map(rect)
+            matrixCache.map(renderNode, rect)
         }
     }
 
@@ -398,7 +391,6 @@
  */
 @RequiresApi(Build.VERSION_CODES.O)
 internal object WrapperRenderNodeLayerHelperMethods {
-    @androidx.annotation.DoNotInline
     fun onDescendantInvalidated(ownerView: AndroidComposeView) {
         ownerView.parent?.onDescendantInvalidated(ownerView, ownerView)
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
index 3f890eb..83a8699 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
@@ -48,6 +48,8 @@
     currentSemanticsNodes: IntObjectMap<SemanticsNodeWithAdjustedBounds>
 ) {
     val unmergedConfig = semanticsNode.unmergedConfig
+    // Root node must always be considered visible and sent to listening services
+    val isTransparent = if (semanticsNode.isRoot) false else semanticsNode.isTransparent
     val children: MutableIntSet = mutableIntSetOf()
 
     init {
@@ -121,7 +123,7 @@
         Role.RadioButton -> "android.widget.RadioButton"
         Role.Image -> "android.widget.ImageView"
         Role.DropdownList -> "android.widget.Spinner"
-        Role.NumberPicker -> "android.widget.NumberPicker"
+        Role.ValuePicker -> "android.widget.NumberPicker"
         else -> null
     }
 
@@ -144,8 +146,14 @@
 )
 
 /** This function retrieves the View corresponding to a semanticsId, if it exists. */
-internal fun AndroidViewsHandler.semanticsIdToView(id: Int): View? =
-    layoutNodeToHolder.entries.firstOrNull { it.key.semanticsId == id }?.value
+internal fun AndroidViewsHandler.semanticsIdToView(id: Int): View? {
+    layoutNodeToHolder.forEach { key, value ->
+        if (key.semanticsId == id) {
+            return value
+        }
+    }
+    return null
+}
 
 // TODO(mnuzen): refactor `currentSemanticsNodes` in the AccessibilityDelegate file to also use
 // IntObjectMap's. Then ACVADC can also call `getAllUncoveredSemanticsNodesToIntObjectMap` instead
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
index 6d9e45c..9ec051d 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
@@ -103,9 +103,7 @@
 
     @RequiresApi(29)
     private object UniqueDrawingIdApi29 {
-        @JvmStatic
-        @androidx.annotation.DoNotInline
-        fun getUniqueDrawingId(view: View) = view.uniqueDrawingId
+        @JvmStatic fun getUniqueDrawingId(view: View) = view.uniqueDrawingId
     }
 
     /**
@@ -366,22 +364,17 @@
 
     override fun mapOffset(point: Offset, inverse: Boolean): Offset {
         return if (inverse) {
-            matrixCache.calculateInverseMatrix(this)?.map(point) ?: Offset.Infinite
+            matrixCache.mapInverse(this, point)
         } else {
-            matrixCache.calculateMatrix(this).map(point)
+            matrixCache.map(this, point)
         }
     }
 
     override fun mapBounds(rect: MutableRect, inverse: Boolean) {
         if (inverse) {
-            val matrix = matrixCache.calculateInverseMatrix(this)
-            if (matrix != null) {
-                matrix.map(rect)
-            } else {
-                rect.set(0f, 0f, 0f, 0f)
-            }
+            matrixCache.mapInverse(this, rect)
         } else {
-            matrixCache.calculateMatrix(this).map(rect)
+            matrixCache.map(this, rect)
         }
     }
 
@@ -481,7 +474,6 @@
 @RequiresApi(Build.VERSION_CODES.S)
 private object ViewLayerVerificationHelper31 {
 
-    @androidx.annotation.DoNotInline
     fun setRenderEffect(view: View, target: RenderEffect?) {
         view.setRenderEffect(target?.asAndroidRenderEffect())
     }
@@ -490,12 +482,10 @@
 @RequiresApi(Build.VERSION_CODES.P)
 private object ViewLayerVerificationHelper28 {
 
-    @androidx.annotation.DoNotInline
     fun setOutlineAmbientShadowColor(view: View, target: Int) {
         view.outlineAmbientShadowColor = target
     }
 
-    @androidx.annotation.DoNotInline
     fun setOutlineSpotShadowColor(view: View, target: Int) {
         view.outlineSpotShadowColor = target
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.android.kt
index 089a705..88590f8 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.android.kt
@@ -87,10 +87,10 @@
             Collections.newSetFromMap(WeakHashMap<CompositionData, Boolean>())
         )
     }
-    val original = Composition(UiApplier(owner.root), parent)
+
     val wrapped =
         owner.view.getTag(R.id.wrapped_composition_tag) as? WrappedComposition
-            ?: WrappedComposition(owner, original).also {
+            ?: WrappedComposition(owner, Composition(UiApplier(owner.root), parent)).also {
                 owner.view.setTag(R.id.wrapped_composition_tag, it)
             }
     wrapped.setContent(content)
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ColorResources.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ColorResources.android.kt
index 1bc45f1..ea52c9a25 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ColorResources.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/res/ColorResources.android.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.os.Build
 import androidx.annotation.ColorRes
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ReadOnlyComposable
@@ -45,7 +44,6 @@
 
 @RequiresApi(23)
 private object ColorResourceHelper {
-    @DoNotInline
     fun getColor(context: Context, @ColorRes id: Int): Color {
         return Color(context.resources.getColor(id, context.theme))
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/scrollcapture/ScrollCapture.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/scrollcapture/ScrollCapture.android.kt
index 4231020..1be1cb3 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/scrollcapture/ScrollCapture.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/scrollcapture/ScrollCapture.android.kt
@@ -20,7 +20,6 @@
 import android.view.ScrollCaptureCallback
 import android.view.ScrollCaptureTarget
 import android.view.View
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.getValue
@@ -67,7 +66,6 @@
      * See go/compose-long-screenshots for more background.
      */
     // Required not to be inlined for class verification.
-    @DoNotInline
     fun onScrollCaptureSearch(
         view: View,
         semanticsOwner: SemanticsOwner,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoBuilder.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoBuilder.android.kt
index 9946391..bc82873 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoBuilder.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoBuilder.android.kt
@@ -20,7 +20,6 @@
 import android.os.Build
 import android.view.inputmethod.CursorAnchorInfo
 import android.view.inputmethod.EditorBoundsInfo
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.toAndroidRectF
@@ -190,7 +189,6 @@
 @RequiresApi(33)
 private object CursorAnchorInfoApi33Helper {
     @JvmStatic
-    @DoNotInline
     fun setEditorBoundsInfo(
         builder: CursorAnchorInfo.Builder,
         decorationBoxBounds: Rect
@@ -206,7 +204,6 @@
 @RequiresApi(34)
 private object CursorAnchorInfoApi34Helper {
     @JvmStatic
-    @DoNotInline
     fun addVisibleLineBounds(
         builder: CursorAnchorInfo.Builder,
         textLayoutResult: TextLayoutResult,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoController.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoController.android.kt
index 8a920e0..83232f9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoController.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/CursorAnchorInfoController.android.kt
@@ -20,7 +20,7 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Matrix
 import androidx.compose.ui.graphics.setFrom
-import androidx.compose.ui.input.pointer.PositionCalculator
+import androidx.compose.ui.input.pointer.MatrixPositionCalculator
 import androidx.compose.ui.text.TextLayoutResult
 
 @Deprecated(
@@ -28,7 +28,7 @@
         "code. A copy of this class in foundation is used by the legacy BasicTextField."
 )
 internal class CursorAnchorInfoController(
-    private val rootPositionCalculator: PositionCalculator,
+    private val rootPositionCalculator: MatrixPositionCalculator,
     @Suppress("DEPRECATION") private val inputMethodManager: InputMethodManager
 ) {
     private val lock = Any()
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
index 5f7bebe..edb02b4 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
@@ -30,7 +30,7 @@
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Matrix
-import androidx.compose.ui.input.pointer.PositionCalculator
+import androidx.compose.ui.input.pointer.MatrixPositionCalculator
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.TextInputServiceAndroid.TextInputCommand.HideKeyboard
@@ -57,7 +57,7 @@
 )
 internal class TextInputServiceAndroid(
     val view: View,
-    rootPositionCalculator: PositionCalculator,
+    rootPositionCalculator: MatrixPositionCalculator,
     private val inputMethodManager: InputMethodManager,
     private val inputCommandProcessorExecutor: Executor = Choreographer.getInstance().asExecutor(),
 ) : PlatformTextInputService {
@@ -118,7 +118,7 @@
 
     constructor(
         view: View,
-        positionCalculator: PositionCalculator
+        positionCalculator: MatrixPositionCalculator
     ) : this(
         view,
         positionCalculator,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
index 9dd30ec..b4b9355 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
@@ -190,6 +190,9 @@
 
     private var isDrawing = false
 
+    /** `true` when the parent has requested this View to layout, but it hasn't been laid out yet */
+    private var isLayoutNeeded = false
+
     override val isValidOwnerScope: Boolean
         get() = isAttachedToWindow
 
@@ -245,6 +248,21 @@
         measure(lastWidthMeasureSpec, lastHeightMeasureSpec)
     }
 
+    /**
+     * Layout the View if a layout has been requested or do nothing if no layout has been requested.
+     */
+    fun layoutIfNeeded() {
+        if (isLayoutNeeded) {
+            isLayoutNeeded = false
+            if (isAttachedToWindow) {
+                val position = layoutNode.coordinates.positionInRoot()
+                val x = position.x.fastRoundToInt()
+                val y = position.y.fastRoundToInt()
+                layout(x, y, x + measuredWidth, y + measuredHeight)
+            }
+        }
+    }
+
     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
         view.layout(0, 0, r - l, b - t)
     }
@@ -363,7 +381,7 @@
                 .onGloballyPositioned {
                     // The global position of this LayoutNode can change with it being replaced. For
                     // these cases, we need to inform the View.
-                    layoutAccordingTo(layoutNode)
+                    isLayoutNeeded = true
                     @OptIn(InternalComposeUiApi::class) owner.onInteropViewLayoutChange(this)
                 }
         layoutNode.compositeKeyHash = compositeKeyHash
@@ -411,7 +429,7 @@
                             layoutParams!!.height
                         )
                     )
-                    return layout(measuredWidth, measuredHeight) { layoutAccordingTo(layoutNode) }
+                    return layout(measuredWidth, measuredHeight) { isLayoutNeeded = true }
                 }
 
                 override fun IntrinsicMeasureScope.minIntrinsicWidth(
@@ -584,13 +602,6 @@
     }
 }
 
-private fun View.layoutAccordingTo(layoutNode: LayoutNode) {
-    val position = layoutNode.coordinates.positionInRoot()
-    val x = position.x.fastRoundToInt()
-    val y = position.y.fastRoundToInt()
-    layout(x, y, x + measuredWidth, y + measuredHeight)
-}
-
 private const val Unmeasured = Int.MIN_VALUE
 
 /**
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
index 0ec0516..83c30cd 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
@@ -20,6 +20,7 @@
 import android.graphics.Outline
 import android.os.Build
 import android.view.ContextThemeWrapper
+import android.view.KeyEvent
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
@@ -69,8 +70,8 @@
 /**
  * Properties used to customize the behavior of a [Dialog].
  *
- * @property dismissOnBackPress whether the dialog can be dismissed by pressing the back button. If
- *   true, pressing the back button will call onDismissRequest.
+ * @property dismissOnBackPress whether the dialog can be dismissed by pressing the back or escape
+ *   buttons. If true, pressing the back button will call onDismissRequest.
  * @property dismissOnClickOutside whether the dialog can be dismissed by clicking outside the
  *   dialog's bounds. If true, clicking outside the dialog will call onDismissRequest.
  * @property securePolicy Policy for setting [WindowManager.LayoutParams.FLAG_SECURE] on the
@@ -178,11 +179,7 @@
                         // TODO(b/159900354): draw a scrim and add margins around the Compose
                         // Dialog, and
                         //  consume clicks so they can't pass through to the underlying UI
-                        DialogLayout(
-                            Modifier.semantics { dialog() },
-                        ) {
-                            currentContent()
-                        }
+                        DialogLayout(Modifier.semantics { dialog() }, currentContent)
                     }
                 }
         }
@@ -384,6 +381,19 @@
         }
     }
 
+    override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
+        if (
+            properties.dismissOnBackPress &&
+                event.isTracking &&
+                !event.isCanceled &&
+                keyCode == KeyEvent.KEYCODE_ESCAPE
+        ) {
+            onDismissRequest()
+            return true
+        }
+        return super.onKeyUp(keyCode, event)
+    }
+
     private fun setLayoutDirection(layoutDirection: LayoutDirection) {
         dialogLayout.layoutDirection =
             when (layoutDirection) {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
index 08a4867..35431d0 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
@@ -33,7 +33,6 @@
 import android.view.WindowManager
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.Composable
@@ -99,10 +98,10 @@
  * @property inheritSecurePolicy Whether [WindowManager.LayoutParams.FLAG_SECURE] should be set
  *   according to [SecureFlagPolicy.Inherit]. Other [SecureFlagPolicy] behaviors should be set via
  *   [flags] directly.
- * @property dismissOnBackPress Whether the popup can be dismissed by pressing the back button. If
- *   true, pressing the back button will call onDismissRequest. Note that the popup must be
- *   [focusable] in order to receive key events such as the back button. If the popup is not
- *   [focusable], then this property does nothing.
+ * @property dismissOnBackPress Whether the popup can be dismissed by pressing the back or escape
+ *   buttons. If true, pressing the back or escape buttons will call onDismissRequest. Note that the
+ *   popup must be [focusable] in order to receive key events such as the back button. If the popup
+ *   is not [focusable], then this property does nothing.
  * @property dismissOnClickOutside Whether the popup can be dismissed by clicking outside the
  *   popup's bounds. If true, clicking outside the popup will call onDismissRequest.
  * @property excludeFromSystemGesture A flag to check whether to set the
@@ -158,10 +157,10 @@
      *
      * @param focusable Whether the popup is focusable. When true, the popup will receive IME events
      *   and key presses, such as when the back button is pressed.
-     * @param dismissOnBackPress Whether the popup can be dismissed by pressing the back button. If
-     *   true, pressing the back button will call onDismissRequest. Note that [focusable] must be
-     *   set to true in order to receive key events such as the back button. If the popup is not
-     *   focusable, then this property does nothing.
+     * @param dismissOnBackPress Whether the popup can be dismissed by pressing the back or escape
+     *   buttons. If true, pressing the back or escape buttons will call onDismissRequest. Note that
+     *   [focusable] must be set to true in order to receive key events such as the back button. If
+     *   the popup is not focusable, then this property does nothing.
      * @param dismissOnClickOutside Whether the popup can be dismissed by clicking outside the
      *   popup's bounds. If true, clicking outside the popup will call onDismissRequest.
      * @param securePolicy Policy for setting [WindowManager.LayoutParams.FLAG_SECURE] on the
@@ -323,10 +322,9 @@
                                 updatePosition()
                             }
                             // Hide the popup while we can't position it correctly
-                            .alpha(if (canCalculatePosition) 1f else 0f)
-                    ) {
-                        currentContent()
-                    }
+                            .alpha(if (canCalculatePosition) 1f else 0f),
+                        currentContent
+                    )
                 }
             }
     }
@@ -627,17 +625,14 @@
 
     /** Taken from PopupWindow */
     override fun dispatchKeyEvent(event: KeyEvent): Boolean {
-        if (event.keyCode == KeyEvent.KEYCODE_BACK && properties.dismissOnBackPress) {
-            if (keyDispatcherState == null) {
-                return super.dispatchKeyEvent(event)
-            }
+        if (!properties.dismissOnBackPress) return super.dispatchKeyEvent(event)
+        if (event.keyCode == KeyEvent.KEYCODE_BACK || event.keyCode == KeyEvent.KEYCODE_ESCAPE) {
+            val state = keyDispatcherState ?: return super.dispatchKeyEvent(event)
             if (event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0) {
-                val state = keyDispatcherState
-                state?.startTracking(event, this)
+                state.startTracking(event, this)
                 return true
             } else if (event.action == KeyEvent.ACTION_UP) {
-                val state = keyDispatcherState
-                if (state != null && state.isTracking(event) && !event.isCanceled) {
+                if (state.isTracking(event) && !event.isCanceled) {
                     onDismissRequest?.invoke()
                     return true
                 }
@@ -866,13 +861,11 @@
 @RequiresApi(33)
 private object Api33Impl {
     @JvmStatic
-    @DoNotInline
     fun createBackCallback(onDismissRequest: (() -> Unit)?) = OnBackInvokedCallback {
         onDismissRequest?.invoke()
     }
 
     @JvmStatic
-    @DoNotInline
     fun maybeRegisterBackCallback(view: View, backCallback: Any?) {
         if (backCallback is OnBackInvokedCallback) {
             view
@@ -885,7 +878,6 @@
     }
 
     @JvmStatic
-    @DoNotInline
     fun maybeUnregisterBackCallback(view: View, backCallback: Any?) {
         if (backCallback is OnBackInvokedCallback) {
             view.findOnBackInvokedDispatcher()?.unregisterOnBackInvokedCallback(backCallback)
diff --git a/compose/ui/ui/src/androidMain/res/values/strings.xml b/compose/ui/ui/src/androidMain/res/values/strings.xml
index ae60b26..3de8a22 100644
--- a/compose/ui/ui/src/androidMain/res/values/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values/strings.xml
@@ -55,4 +55,6 @@
     <string name="range_start">"Range start"</string>
     <!-- Spoken content description of the end seek control in a range slider. -->
     <string name="range_end">"Range end"</string>
+    <!-- Accessibility pane title for a snackbar -->
+    <string name="snackbar_pane_title">Alert</string>
 </resources>
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/input/pointer/util/VelocityTrackerTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/input/pointer/util/VelocityTrackerTest.kt
index a02d8dd..fde3300 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/input/pointer/util/VelocityTrackerTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/input/pointer/util/VelocityTrackerTest.kt
@@ -107,6 +107,15 @@
         assertThat(tracker.calculateVelocity()).isEqualTo(Velocity.Zero)
     }
 
+    @Test // b/355160589
+    fun calculateVelocity_twoPoints_sameTimeStamp_returnsZero() {
+        val tracker = VelocityTracker()
+        tracker.addPosition(1000L, Offset(100f, 0f))
+        tracker.addPosition(1000L, Offset(200f, 0f))
+        tracker.addPosition(1000L, Offset(300f, 0f))
+        assertThat(tracker.calculateVelocity()).isEqualTo(Velocity.Zero)
+    }
+
     @Test
     fun resetTracking_resetsTracking() {
         val tracker = VelocityTracker()
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt
index 7e690aa..797cbcc 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt
@@ -136,6 +136,6 @@
 
     @Test
     fun testScaleFactorToString() {
-        assertEquals("ScaleFactor(1.2, 1.3)", ScaleFactor(1.234f, 1.25f).toString())
+        assertEquals("ScaleFactor(1.234, 1.25)", ScaleFactor(1.234f, 1.25f).toString())
     }
 }
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index c53b374..48999dc 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -2459,10 +2459,6 @@
         TODO("Not yet implemented")
     }
 
-    override fun localToScreen(localTransform: Matrix) {
-        TODO("Not yet implemented")
-    }
-
     val invalidatedLayers = mutableListOf<OwnedLayer>()
 
     override fun createLayer(
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
index 268355cc..bea163c 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
@@ -33,7 +33,6 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.GraphicsContext
-import androidx.compose.ui.graphics.Matrix
 import androidx.compose.ui.graphics.layer.GraphicsLayer
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.input.InputModeManager
@@ -459,10 +458,6 @@
         override fun localToScreen(localPosition: Offset): Offset {
             TODO("Not yet implemented")
         }
-
-        override fun localToScreen(localTransform: Matrix) {
-            TODO("Not yet implemented")
-        }
     }
 }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
index cc86355..c46d84d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.platform.NoInspectorInfo
+import kotlin.jvm.JvmName
 
 /**
  * Declare a just-in-time composition of a [Modifier] that will be composed for each element it
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
index 3f80634..0bb8464 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
@@ -30,21 +30,7 @@
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancel
 
-private val EmptyStackTraceElements = emptyArray<StackTraceElement>()
-
-/**
- * Used in place of the standard Job cancellation pathway to avoid reflective javaClass.simpleName
- * lookups to build the exception message and stack trace collection. Remove if these are changed in
- * kotlinx.coroutines.
- */
-private class ModifierNodeDetachedCancellationException :
-    CancellationException("The Modifier.Node was detached") {
-    override fun fillInStackTrace(): Throwable {
-        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
-        stackTrace = EmptyStackTraceElements
-        return this
-    }
-}
+internal expect class ModifierNodeDetachedCancellationException() : CancellationException
 
 /**
  * An ordered, immutable collection of [modifier elements][Modifier.Element] that decorate or add
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/SensitiveContent.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/SensitiveContent.kt
new file mode 100644
index 0000000..df250df
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/SensitiveContent.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+import androidx.compose.ui.internal.checkPrecondition
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.requireOwner
+import androidx.compose.ui.platform.InspectorInfo
+
+/**
+ * This modifier hints that the composable renders sensitive content (i.e. username, password,
+ * credit card etc) on the screen, and the content should be protected during screen share in
+ * supported environments.
+ */
+fun Modifier.sensitiveContent(isContentSensitive: Boolean = true): Modifier =
+    this then SensitiveNodeElement(isContentSensitive)
+
+private data class SensitiveNodeElement(val isContentSensitive: Boolean) :
+    ModifierNodeElement<SensitiveContentNode>() {
+    override fun create(): SensitiveContentNode = SensitiveContentNode(isContentSensitive)
+
+    override fun update(node: SensitiveContentNode) {
+        node.isContentSensitive = isContentSensitive
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "sensitiveContent"
+        properties["isContentSensitive"] = isContentSensitive
+    }
+}
+
+private data class SensitiveContentNode(private var _isContentSensitive: Boolean) :
+    Modifier.Node() {
+    // Tracks if this node has been counted as sensitive or not.
+    private var isCountedSensitive: Boolean = false
+
+    var isContentSensitive: Boolean = _isContentSensitive
+        set(value) {
+            field = value
+            if (isContentSensitive && !isCountedSensitive) {
+                requireOwner().incrementSensitiveComponentCount()
+                isCountedSensitive = true
+            } else if (!isContentSensitive && isCountedSensitive) {
+                requireOwner().decrementSensitiveComponentCount()
+                isCountedSensitive = false
+            }
+        }
+
+    override fun onAttach() {
+        super.onAttach()
+        if (isContentSensitive) {
+            checkPrecondition(!isCountedSensitive) { "invalid sensitive content state" }
+            requireOwner().incrementSensitiveComponentCount()
+            isCountedSensitive = true
+        }
+    }
+
+    override fun onDetach() {
+        if (isCountedSensitive) {
+            requireOwner().decrementSensitiveComponentCount()
+            isCountedSensitive = false
+        }
+        super.onDetach()
+    }
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/SessionMutex.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/SessionMutex.kt
index 5083ab2..652d31b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/SessionMutex.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/SessionMutex.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui
 
 import androidx.annotation.RestrictTo
+import kotlin.jvm.JvmInline
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancelAndJoin
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusDirection.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusDirection.kt
index 14ec165..fd968bd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusDirection.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusDirection.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.ui.focus
 
+import kotlin.jvm.JvmInline
+
 /**
  * The [FocusDirection] is used to specify the direction for a [FocusManager.moveFocus] request.
  *
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
index 424e616..cf641a7 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusEventModifier.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.internal.JvmDefaultWithCompatibility
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.InspectorInfo
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
index c1444f8..ffcc726 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.node.Nodes
 import androidx.compose.ui.node.ancestors
 import androidx.compose.ui.node.dispatchForKind
+import androidx.compose.ui.node.nearestAncestor
 import androidx.compose.ui.node.visitAncestors
 import androidx.compose.ui.node.visitLocalDescendants
 import androidx.compose.ui.platform.InspectorInfo
@@ -47,6 +48,8 @@
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachReversed
 
+private const val Warning = "FocusRelatedWarning"
+
 /**
  * The focus manager is used by different [Owner][androidx.compose.ui.node.Owner] implementations to
  * control focus.
@@ -257,16 +260,18 @@
 
     /** Dispatches a key event through the compose hierarchy. */
     override fun dispatchKeyEvent(keyEvent: KeyEvent, onFocusedItem: () -> Boolean): Boolean {
-        check(!focusInvalidationManager.hasPendingInvalidation()) {
-            "Dispatching key event while focus system is invalidated."
+        if (focusInvalidationManager.hasPendingInvalidation()) {
+            // Ignoring this to unblock b/346370327.
+            println("$Warning: Dispatching key event while focus system is invalidated.")
+            return false
         }
-
         if (!validateKeyEvent(keyEvent)) return false
 
         val activeFocusTarget = rootFocusNode.findActiveFocusNode()
         val focusedKeyInputNode =
             activeFocusTarget?.lastLocalKeyInputNode()
                 ?: activeFocusTarget?.nearestAncestorIncludingSelf(Nodes.KeyInput)?.node
+                ?: rootFocusNode.nearestAncestor(Nodes.KeyInput)?.node
 
         focusedKeyInputNode?.traverseAncestorsIncludingSelf(
             type = Nodes.KeyInput,
@@ -278,8 +283,13 @@
     }
 
     override fun dispatchInterceptedSoftKeyboardEvent(keyEvent: KeyEvent): Boolean {
-        check(!focusInvalidationManager.hasPendingInvalidation()) {
-            "Dispatching intercepted soft keyboard event while focus system is invalidated."
+        if (focusInvalidationManager.hasPendingInvalidation()) {
+            // Ignoring this to unblock b/346370327.
+            println(
+                "$Warning: Dispatching intercepted soft keyboard event while the focus system" +
+                    " is invalidated."
+            )
+            return false
         }
 
         val focusedSoftKeyboardInterceptionNode =
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt
index d474394..86f471a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequesterModifier.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.focus
 
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.internal.JvmDefaultWithCompatibility
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.InspectorInfo
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
index ad0f4bb..78d77a6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
@@ -275,11 +275,11 @@
             visitSubtreeIf(Nodes.FocusTarget) {
                 if (!it.isInitialized()) return@visitSubtreeIf true
 
-                return when (it.focusState) {
+                when (it.focusState) {
                     Active,
                     ActiveParent,
-                    Captured -> true
-                    Inactive -> false
+                    Captured -> return true
+                    Inactive -> return@visitSubtreeIf false
                 }
             }
             return false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/Focusability.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/Focusability.kt
index 9837e18..1131a42 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/Focusability.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/Focusability.kt
@@ -20,6 +20,7 @@
 import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
 import androidx.compose.ui.node.currentValueOf
 import androidx.compose.ui.platform.LocalInputModeManager
+import kotlin.jvm.JvmInline
 
 /**
  * Focusability configures whether a focus target can be focused.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/OneDimensionalFocusSearch.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/OneDimensionalFocusSearch.kt
index 5517b88..a0a79ef 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/OneDimensionalFocusSearch.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/OneDimensionalFocusSearch.kt
@@ -224,19 +224,16 @@
  * the items makes the next focus search more efficient.
  */
 private object FocusableChildrenComparator : Comparator<FocusTargetNode> {
-    override fun compare(focusTarget1: FocusTargetNode?, focusTarget2: FocusTargetNode?): Int {
-        requireNotNull(focusTarget1) { "compare requires non-null focus targets" }
-        requireNotNull(focusTarget2) { "compare requires non-null focus targets" }
-
+    override fun compare(a: FocusTargetNode, b: FocusTargetNode): Int {
         // Ignore focus modifiers that won't be considered during focus search.
-        if (!focusTarget1.isEligibleForFocusSearch || !focusTarget2.isEligibleForFocusSearch) {
-            if (focusTarget1.isEligibleForFocusSearch) return -1
-            if (focusTarget2.isEligibleForFocusSearch) return 1
+        if (!a.isEligibleForFocusSearch || !b.isEligibleForFocusSearch) {
+            if (a.isEligibleForFocusSearch) return -1
+            if (b.isEligibleForFocusSearch) return 1
             return 0
         }
 
-        val layoutNode1 = focusTarget1.requireLayoutNode()
-        val layoutNode2 = focusTarget2.requireLayoutNode()
+        val layoutNode1 = a.requireLayoutNode()
+        val layoutNode2 = b.requireLayoutNode()
 
         // Use natural order for focus modifiers within the same layout node.
         if (layoutNode1 == layoutNode2) return 0
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt
index 0c90209..091a9be 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/ImageVector.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.StrokeJoin
 import androidx.compose.ui.internal.checkPrecondition
+import androidx.compose.ui.platform.synchronized
 import androidx.compose.ui.unit.Dp
 
 /**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt
index edcaecb..e3537fd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt
@@ -486,9 +486,13 @@
      * Indicates whether the change was consumed or not. Note that the change must be consumed in
      * full as there's no partial consumption system provided.
      */
-    @Suppress("DEPRECATION")
     val isConsumed: Boolean
-        get() = consumed.downChange || consumed.positionChange
+        get() = consumedDelegate?.isConsumed ?: (downChange || positionChange)
+
+    internal var downChange = isInitiallyConsumed
+    internal var positionChange = isInitiallyConsumed
+    // Used by shallow copies (see [copy]) to share the consumed state across pointer input changes
+    internal var consumedDelegate: PointerInputChange? = null
 
     /**
      * Consume change event, claiming all the corresponding change info to the caller. This is
@@ -498,17 +502,26 @@
      * "Consumption" is just an indication of the claim and each pointer input handler
      * implementation must manually check this flag to respect it.
      */
-    @Suppress("DEPRECATION")
     fun consume() {
-        consumed.downChange = true
-        consumed.positionChange = true
+        if (consumedDelegate == null) {
+            downChange = true
+            positionChange = true
+        } else {
+            consumedDelegate?.consume()
+        }
     }
 
+    @Suppress("DEPRECATION") private var _consumed: ConsumedData? = null
+
     @Deprecated("use isConsumed and consume() pair of methods instead")
     @Suppress("DEPRECATION")
-    var consumed: ConsumedData =
-        ConsumedData(downChange = isInitiallyConsumed, positionChange = isInitiallyConsumed)
-        private set
+    val consumed: ConsumedData
+        get() {
+            if (_consumed == null) {
+                _consumed = ConsumedData(this)
+            }
+            return _consumed!!
+        }
 
     @Deprecated(
         level = DeprecationLevel.HIDDEN,
@@ -546,7 +559,12 @@
                 this.scrollDelta,
                 this.originalEventPosition,
             )
-            .also { this.consumed = consumed }
+            .also {
+                // This method makes a deep copy, copy the consumed state directly without setting
+                // the consumed delegate, which is used for shallow copies.
+                it.positionChange = this.positionChange
+                it.downChange = this.downChange
+            }
 
     /**
      * Make a shallow copy of the [PointerInputChange]
@@ -556,7 +574,6 @@
      * copies will consume any other copy automatically. Therefore, copy with the new [isConsumed]
      * is not possible. Consider creating a new [PointerInputChange]
      */
-    @Suppress("DEPRECATION")
     fun copy(
         id: PointerId = this.id,
         currentTime: Long = this.uptimeMillis,
@@ -569,18 +586,24 @@
         scrollDelta: Offset = this.scrollDelta
     ): PointerInputChange =
         copy(
-            id = id,
-            currentTime = currentTime,
-            currentPosition = currentPosition,
-            currentPressed = currentPressed,
-            pressure = this.pressure,
-            previousTime = previousTime,
-            previousPosition = previousPosition,
-            previousPressed = previousPressed,
-            type = type,
-            historical = this.historical,
-            scrollDelta = scrollDelta
-        )
+                id = id,
+                currentTime = currentTime,
+                currentPosition = currentPosition,
+                currentPressed = currentPressed,
+                pressure = this.pressure,
+                previousTime = previousTime,
+                previousPosition = previousPosition,
+                previousPressed = previousPressed,
+                type = type,
+                historical = this.historical,
+                scrollDelta = scrollDelta
+            )
+            .also {
+                // This method makes a shallow copy, copy the delegate to share the consumed state
+                // across instances. The local consumed state is irrelevant since we won't look at
+                // it, meaning there's no need to copy positionChange and downChange.
+                it.consumedDelegate = this.consumedDelegate ?: this
+            }
 
     @Suppress("DEPRECATION")
     @Deprecated(
@@ -620,7 +643,12 @@
                 scrollDelta,
                 this.originalEventPosition,
             )
-            .also { this.consumed = consumed }
+            .also {
+                // This method makes a deep copy, copy the consumed state directly without setting
+                // the consumed delegate, which is used for shallow copies.
+                it.positionChange = this.positionChange
+                it.downChange = this.downChange
+            }
 
     /**
      * Make a shallow copy of the [PointerInputChange]
@@ -630,7 +658,6 @@
      * copies will consume any other copy automatically. Therefore, copy with the new [isConsumed]
      * is not possible. Consider creating a new [PointerInputChange].
      */
-    @Suppress("DEPRECATION")
     fun copy(
         id: PointerId = this.id,
         currentTime: Long = this.uptimeMillis,
@@ -652,13 +679,18 @@
                 previousTime,
                 previousPosition,
                 previousPressed,
-                isInitiallyConsumed = false, // doesn't matter, we will pass a holder anyway
+                isInitiallyConsumed = false, // doesn't matter, we will copy the consumed booleans
                 type,
                 historical = this.historical,
                 scrollDelta,
                 this.originalEventPosition,
             )
-            .also { it.consumed = this.consumed }
+            .also {
+                // This method makes a shallow copy, copy the delegate to share the consumed state
+                // across instances. The local consumed state is irrelevant since we won't look at
+                // it, meaning there's no need to copy positionChange and downChange.
+                it.consumedDelegate = this.consumedDelegate ?: this
+            }
 
     /**
      * Make a shallow copy of the [PointerInputChange]
@@ -669,7 +701,6 @@
      * is not possible. Consider creating a new [PointerInputChange].
      */
     @ExperimentalComposeUiApi
-    @Suppress("DEPRECATION")
     fun copy(
         id: PointerId = this.id,
         currentTime: Long = this.uptimeMillis,
@@ -683,18 +714,24 @@
         scrollDelta: Offset = this.scrollDelta
     ): PointerInputChange =
         copy(
-            id = id,
-            currentTime = currentTime,
-            currentPosition = currentPosition,
-            currentPressed = currentPressed,
-            pressure = this.pressure,
-            previousTime = previousTime,
-            previousPosition = previousPosition,
-            previousPressed = previousPressed,
-            type = type,
-            historical = historical,
-            scrollDelta = scrollDelta
-        )
+                id = id,
+                currentTime = currentTime,
+                currentPosition = currentPosition,
+                currentPressed = currentPressed,
+                pressure = this.pressure,
+                previousTime = previousTime,
+                previousPosition = previousPosition,
+                previousPressed = previousPressed,
+                type = type,
+                historical = historical,
+                scrollDelta = scrollDelta
+            )
+            .also {
+                // This method makes a shallow copy, copy the delegate to share the consumed state
+                // across instances. The local consumed state is irrelevant since we won't look at
+                // it, meaning there's no need to copy positionChange and downChange.
+                it.consumedDelegate = this.consumedDelegate ?: this
+            }
 
     /**
      * Make a shallow copy of the [PointerInputChange]
@@ -704,7 +741,6 @@
      * copies will consume any other copy automatically. Therefore, copy with the new [isConsumed]
      * is not possible. Consider creating a new [PointerInputChange].
      */
-    @Suppress("DEPRECATION")
     fun copy(
         id: PointerId = this.id,
         currentTime: Long = this.uptimeMillis,
@@ -727,13 +763,18 @@
                 previousTime,
                 previousPosition,
                 previousPressed,
-                isInitiallyConsumed = false, // doesn't matter, we will pass a holder anyway
+                isInitiallyConsumed = false, // doesn't matter, we will copy the consumed booleans
                 type,
                 historical,
                 scrollDelta,
                 originalEventPosition = this.originalEventPosition,
             )
-            .also { it.consumed = this.consumed }
+            .also {
+                // This method makes a shallow copy, copy the delegate to share the consumed state
+                // across instances. The local consumed state is irrelevant since we won't look at
+                // it, meaning there's no need to copy positionChange and downChange.
+                it.consumedDelegate = this.consumedDelegate ?: this
+            }
 
     override fun toString(): String {
         return "PointerInputChange(id=$id, " +
@@ -775,7 +816,7 @@
     }
 
     override fun toString(): String {
-        return "HistoricalChange(uptimeMillis=$uptimeMillis, " + "position=$position)"
+        return "HistoricalChange(uptimeMillis=$uptimeMillis, position=$position)"
     }
 }
 
@@ -793,22 +834,43 @@
  * @param downChange True if a change to down or up has been consumed.
  */
 @Deprecated("Use PointerInputChange.isConsumed and PointerInputChange.consume() instead")
-class ConsumedData(
+class ConsumedData(positionChange: Boolean = false, downChange: Boolean = false) {
+    private var change: PointerInputChange? = null
+
+    internal constructor(
+        change: PointerInputChange
+    ) : this(change.positionChange, change.downChange) {
+        this.change = change
+    }
+
     @Suppress("GetterSetterNames")
     @get:Suppress("GetterSetterNames")
     @Deprecated(
         "Partial consumption was deprecated. Use PointerEvent.isConsumed " +
             "and PointerEvent.consume() instead."
     )
-    var positionChange: Boolean = false,
+    var positionChange: Boolean = positionChange
+        get() = change?.consumedDelegate?.positionChange ?: (change?.positionChange ?: field)
+        set(value) {
+            change?.consumedDelegate?.positionChange = value
+            change?.positionChange = value
+            field = value
+        }
+
     @Suppress("GetterSetterNames")
     @get:Suppress("GetterSetterNames")
     @Deprecated(
         "Partial consumption was deprecated. Use PointerEvent.isConsumed " +
             "and PointerEvent.consume() instead."
     )
-    var downChange: Boolean = false
-)
+    var downChange: Boolean = downChange
+        get() = change?.consumedDelegate?.downChange ?: (change?.downChange ?: field)
+        set(value) {
+            change?.consumedDelegate?.downChange = value
+            change?.downChange = value
+            field = value
+        }
+}
 
 /**
  * The enumeration of passes where [PointerInputChange] traverses up and down the UI tree.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
index 46389af..7ee0aad 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessor.kt
@@ -28,6 +28,9 @@
     fun screenToLocal(positionOnScreen: Offset): Offset
 
     fun localToScreen(localPosition: Offset): Offset
+}
+
+internal interface MatrixPositionCalculator : PositionCalculator {
 
     /**
      * Takes a matrix which transforms some coordinate system to local coordinates, and updates the
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
index 81e55d6..6141e6e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.kt
@@ -888,43 +888,22 @@
     }
 }
 
-private val EmptyStackTraceElements = emptyArray<StackTraceElement>()
-
 /**
  * An exception thrown from [AwaitPointerEventScope.withTimeout] when the execution time of the
  * coroutine is too long.
  */
-class PointerEventTimeoutCancellationException(time: Long) :
-    CancellationException("Timed out waiting for $time ms") {
-    override fun fillInStackTrace(): Throwable {
-        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
-        stackTrace = EmptyStackTraceElements
-        return this
-    }
-}
+expect class PointerEventTimeoutCancellationException(time: Long) : CancellationException
 
 /**
  * Used in place of the standard Job cancellation pathway to avoid reflective javaClass.simpleName
  * lookups to build the exception message and stack trace collection. Remove if these are changed in
  * kotlinx.coroutines.
  */
-private class PointerInputResetException : CancellationException("Pointer input was reset") {
-    override fun fillInStackTrace(): Throwable {
-        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
-        stackTrace = EmptyStackTraceElements
-        return this
-    }
-}
+internal expect class PointerInputResetException() : CancellationException
 
 /**
  * Also used in place of standard Job cancellation pathway; since we control this code path we
  * shouldn't need to worry about other code calling addSuppressed on this exception so a singleton
  * instance is used
  */
-private object CancelTimeoutCancellationException : CancellationException() {
-    override fun fillInStackTrace(): Throwable {
-        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
-        stackTrace = EmptyStackTraceElements
-        return this
-    }
-}
+internal expect object CancelTimeoutCancellationException : CancellationException
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
index f06a924..2fc6e18 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
@@ -291,7 +291,7 @@
         }
         val velocity = calculateVelocity()
 
-        return if (velocity == 0.0f) {
+        return if (velocity == 0.0f || velocity.isNaN()) {
             0.0f
         } else if (velocity > 0) {
             velocity.coerceAtMost(maximumVelocity)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
index e6d5827..dc853ae 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
@@ -42,6 +42,7 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.fastForEach
+import kotlin.jvm.JvmName
 
 /**
  * [Layout] is the main core component for layout. It can be used to measure and position zero or
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt
index b9032bd..0fec80b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutCoordinates.kt
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+@file:JvmName("LayoutCoordinatesKt")
+@file:JvmMultifileClass
+
 package androidx.compose.ui.layout
 
 import androidx.compose.ui.geometry.Offset
@@ -25,6 +28,8 @@
 import androidx.compose.ui.util.fastCoerceIn
 import androidx.compose.ui.util.fastMaxOf
 import androidx.compose.ui.util.fastMinOf
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
 /** A holder of the measured bounds for the [Layout]. */
 @JvmDefaultWithCompatibility
@@ -156,17 +161,6 @@
     }
 
     /**
-     * Takes a [matrix] which transforms some coordinate system `C` to local coordinates, and
-     * updates the matrix to transform from `C` to screen coordinates instead.
-     */
-    @Suppress("DocumentExceptions")
-    fun transformToScreen(matrix: Matrix) {
-        throw UnsupportedOperationException(
-            "transformToScreen is not implemented on this LayoutCoordinates"
-        )
-    }
-
-    /**
      * Returns the position in pixels of an [alignment line][AlignmentLine], or
      * [AlignmentLine.Unspecified] if the line is not provided.
      */
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
index 37249d5..9c1faaf 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadLayoutCoordinates.kt
@@ -172,10 +172,6 @@
         coordinator.transformFrom(sourceCoordinates, matrix)
     }
 
-    override fun transformToScreen(matrix: Matrix) {
-        coordinator.transformToScreen(matrix)
-    }
-
     override fun get(alignmentLine: AlignmentLine): Int = lookaheadDelegate.get(alignmentLine)
 }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt
index e8db3e5..60704c6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt
@@ -81,7 +81,7 @@
      */
     @Stable operator fun div(operand: Float) = ScaleFactor(scaleX / operand, scaleY / operand)
 
-    override fun toString() = "ScaleFactor(${scaleX.roundToTenths()}, ${scaleY.roundToTenths()})"
+    override fun toString() = "ScaleFactor(${scaleX}, ${scaleY})"
 
     companion object {
 
@@ -94,20 +94,6 @@
     }
 }
 
-private fun Float.roundToTenths(): Float {
-    val shifted = this * 10
-    val decimal = shifted - shifted.toInt()
-    // Kotlin's round operator rounds 0.5f down to 0. Manually compare against
-    // 0.5f and round up if necessary
-    val roundedShifted =
-        if (decimal >= 0.5f) {
-            shifted.toInt() + 1
-        } else {
-            shifted.toInt()
-        }
-    return roundedShifted.toFloat() / 10
-}
-
 /** `false` when this is [ScaleFactor.Unspecified]. */
 @Stable
 inline val ScaleFactor.isSpecified: Boolean
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
index 4ebca58e..5415836 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingNode.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.internal.checkPrecondition
-import org.jetbrains.annotations.TestOnly
 
 /**
  * A [Modifier.Node] which is able to delegate work to other [Modifier.Node] instances.
@@ -47,11 +46,12 @@
 
     internal var delegate: Modifier.Node? = null
 
-    @TestOnly
+    // @TestOnly
     internal fun <T : DelegatableNode> delegateUnprotected(delegatableNode: T): T =
         delegate(delegatableNode)
 
-    @TestOnly internal fun undelegateUnprotected(instance: DelegatableNode) = undelegate(instance)
+    // @TestOnly
+    internal fun undelegateUnprotected(instance: DelegatableNode) = undelegate(instance)
 
     override fun setAsDelegateTo(owner: Modifier.Node) {
         super.setAsDelegateTo(owner)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt
index 1e49dce..a2a9f55 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DepthSortedSet.kt
@@ -16,8 +16,21 @@
 
 package androidx.compose.ui.node
 
+import androidx.collection.MutableObjectIntMap
+import androidx.collection.mutableObjectIntMapOf
 import androidx.compose.ui.internal.checkPrecondition
 
+private val DepthComparator: Comparator<LayoutNode> =
+    object : Comparator<LayoutNode> {
+        override fun compare(a: LayoutNode, b: LayoutNode): Int {
+            val depthDiff = a.depth.compareTo(b.depth)
+            if (depthDiff != 0) {
+                return depthDiff
+            }
+            return a.hashCode().compareTo(b.hashCode())
+        }
+    }
+
 /**
  * The set of [LayoutNode]s which orders items by their [LayoutNode.depth] and allows
  * modifications(additions and removals) while we iterate through it via [popEach]. While
@@ -32,24 +45,14 @@
     // changed since then. we need to enforce this as changing the depth can break the contract
     // used in comparator for building the tree in TreeSet.
     // Created and used only when extraAssertions == true
-    private val mapOfOriginalDepth by
-        lazy(LazyThreadSafetyMode.NONE) { mutableMapOf<LayoutNode, Int>() }
-    private val DepthComparator: Comparator<LayoutNode> =
-        object : Comparator<LayoutNode> {
-            override fun compare(l1: LayoutNode, l2: LayoutNode): Int {
-                val depthDiff = l1.depth.compareTo(l2.depth)
-                if (depthDiff != 0) {
-                    return depthDiff
-                }
-                return l1.hashCode().compareTo(l2.hashCode())
-            }
-        }
+    private var mapOfOriginalDepth: MutableObjectIntMap<LayoutNode>? = null
+
     private val set = TreeSet(DepthComparator)
 
     fun contains(node: LayoutNode): Boolean {
         val contains = set.contains(node)
         if (extraAssertions) {
-            checkPrecondition(contains == mapOfOriginalDepth.containsKey(node)) {
+            checkPrecondition(contains == safeMapOfOriginalDepth().containsKey(node)) {
                 "inconsistency in TreeSet"
             }
         }
@@ -59,9 +62,10 @@
     fun add(node: LayoutNode) {
         checkPrecondition(node.isAttached) { "DepthSortedSet.add called on an unattached node" }
         if (extraAssertions) {
-            val usedDepth = mapOfOriginalDepth[node]
-            if (usedDepth == null) {
-                mapOfOriginalDepth[node] = node.depth
+            val map = safeMapOfOriginalDepth()
+            val usedDepth = map.getOrDefault(node, Int.MAX_VALUE)
+            if (usedDepth == Int.MAX_VALUE) {
+                map[node] = node.depth
             } else {
                 checkPrecondition(usedDepth == node.depth) { "invalid node depth" }
             }
@@ -73,9 +77,13 @@
         checkPrecondition(node.isAttached) { "DepthSortedSet.remove called on an unattached node" }
         val contains = set.remove(node)
         if (extraAssertions) {
-            val usedDepth = mapOfOriginalDepth.remove(node)
-            checkPrecondition(usedDepth == if (contains) node.depth else null) {
-                "invalid node depth"
+            val map = safeMapOfOriginalDepth()
+            if (map.contains(node)) {
+                val usedDepth = map[node]
+                map.remove(node)
+                checkPrecondition(usedDepth == if (contains) node.depth else Int.MAX_VALUE) {
+                    "invalid node depth"
+                }
             }
         }
         return contains
@@ -98,6 +106,13 @@
 
     @Suppress("NOTHING_TO_INLINE") inline fun isNotEmpty(): Boolean = !isEmpty()
 
+    private fun safeMapOfOriginalDepth(): MutableObjectIntMap<LayoutNode> {
+        if (mapOfOriginalDepth == null) {
+            mapOfOriginalDepth = mutableObjectIntMapOf()
+        }
+        return mapOfOriginalDepth!!
+    }
+
     override fun toString(): String {
         return set.toString()
     }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
index b971ec3..9dc1651 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/HitTestResult.kt
@@ -93,7 +93,7 @@
      * Records [node] as a hit, adding it to the [HitTestResult] or replacing the existing one. Runs
      * [childHitTest] to do further hit testing for children.
      */
-    fun hit(node: Modifier.Node, isInLayer: Boolean, childHitTest: () -> Unit) {
+    inline fun hit(node: Modifier.Node, isInLayer: Boolean, childHitTest: () -> Unit) {
         hitInMinimumTouchTarget(node, -1f, isInLayer, childHitTest)
     }
 
@@ -101,7 +101,7 @@
      * Records [node] as a hit with [distanceFromEdge] distance, replacing any existing record. Runs
      * [childHitTest] to do further hit testing for children.
      */
-    fun hitInMinimumTouchTarget(
+    inline fun hitInMinimumTouchTarget(
         node: Modifier.Node,
         distanceFromEdge: Float,
         isInLayer: Boolean,
@@ -308,7 +308,7 @@
 }
 
 @kotlin.jvm.JvmInline
-private value class DistanceAndInLayer(val packedValue: Long) {
+internal value class DistanceAndInLayer(val packedValue: Long) {
     val distance: Float
         get() = unpackFloat1(packedValue)
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index 3b7749b..2403769 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -405,6 +405,10 @@
 
     internal val collapsedSemantics: SemanticsConfiguration?
         get() {
+            // TODO: investigate if there's a better way to approach "half attached" state and
+            // whether or not deactivated nodes should be considered removed or not.
+            if (!isAttached || isDeactivated) return null
+
             trace("collapseSemantics") {
                 if (!nodes.has(Nodes.Semantics) || _collapsedSemantics != null) {
                     return _collapsedSemantics
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MyersDiff.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MyersDiff.kt
index ad1b286..fc0a078 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MyersDiff.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MyersDiff.kt
@@ -18,6 +18,7 @@
 package androidx.compose.ui.node
 
 import androidx.compose.ui.internal.checkPrecondition
+import kotlin.jvm.JvmInline
 import kotlin.math.abs
 import kotlin.math.min
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
index a032794..c9839fd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeCoordinator.kt
@@ -444,13 +444,23 @@
         visitNodes(Nodes.LayoutAware) { it.onPlaced(this) }
     }
 
+    private var drawBlockParentLayer: GraphicsLayer? = null
+    private var drawBlockCanvas: Canvas? = null
+
+    private val drawBlockCallToDrawModifiers: () -> Unit = {
+        drawContainedDrawModifiers(drawBlockCanvas!!, drawBlockParentLayer)
+    }
+
     // implementation of draw block passed to the OwnedLayer
-    @Suppress("LiftReturnOrAssignment")
     private val drawBlock: (Canvas, GraphicsLayer?) -> Unit = { canvas, parentLayer ->
         if (layoutNode.isPlaced) {
-            snapshotObserver.observeReads(this, onCommitAffectingLayer) {
-                drawContainedDrawModifiers(canvas, parentLayer)
-            }
+            this.drawBlockCanvas = canvas
+            this.drawBlockParentLayer = parentLayer
+            snapshotObserver.observeReads(
+                this,
+                onCommitAffectingLayer,
+                drawBlockCallToDrawModifiers
+            )
             lastLayerDrawingWasSkipped = false
         } else {
             // The invalidation is requested even for nodes which are not placed. As we are not
@@ -883,14 +893,7 @@
         transformFromAncestor(commonAncestor, matrix)
     }
 
-    override fun transformToScreen(matrix: Matrix) {
-        val owner = layoutNode.requireOwner()
-        val rootCoordinator = findRootCoordinates().toCoordinator()
-        transformToAncestor(rootCoordinator, matrix)
-        owner.localToScreen(matrix)
-    }
-
-    private fun transformToAncestor(ancestor: NodeCoordinator, matrix: Matrix) {
+    fun transformToAncestor(ancestor: NodeCoordinator, matrix: Matrix) {
         var wrapper = this
         while (wrapper != ancestor) {
             wrapper.layer?.transform(matrix)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
index d04ca22..fd42432 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeKind.kt
@@ -43,6 +43,8 @@
 import androidx.compose.ui.modifier.ModifierLocalModifierNode
 import androidx.compose.ui.modifier.ModifierLocalProvider
 import androidx.compose.ui.semantics.SemanticsModifier
+import kotlin.jvm.JvmInline
+import kotlin.jvm.JvmStatic
 
 @Suppress("NOTHING_TO_INLINE")
 @JvmInline
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
index 1f93d1c..30d11e0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
@@ -317,6 +317,20 @@
         session: suspend PlatformTextInputSessionScope.() -> Nothing
     ): Nothing
 
+    /**
+     * Tracks sensitive content on the screen to protect user privacy. Increment sensitive component
+     * count by 1. Implementation may protect user privacy by not showing sensitive content
+     * (username, password etc) to remote viewer during screen share.
+     */
+    fun incrementSensitiveComponentCount() {}
+
+    /**
+     * Tracks sensitive content on the screen to protect user privacy. Decrement sensitive component
+     * count by 1. Implementation may protect user privacy by not showing sensitive content
+     * (username, password etc) to remote viewer during screen share.
+     */
+    fun decrementSensitiveComponentCount() {}
+
     companion object {
         /**
          * Enables additional (and expensive to do in production) assertions. Useful to be set to
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
index 92c0875..93f57f2 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
@@ -49,21 +49,6 @@
     val semanticsConfiguration: SemanticsConfiguration
 }
 
-internal class EmptySemanticsElement(private val node: EmptySemanticsModifier) :
-    ModifierNodeElement<EmptySemanticsModifier>() {
-    override fun create() = node
-
-    override fun update(node: EmptySemanticsModifier) {}
-
-    override fun InspectorInfo.inspectableProperties() {
-        // Nothing to inspect.
-    }
-
-    override fun hashCode(): Int = System.identityHashCode(this)
-
-    override fun equals(other: Any?) = (other === this)
-}
-
 internal class CoreSemanticsModifierNode(
     var mergeDescendants: Boolean,
     var isClearingSemantics: Boolean,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
index 3d505bd..02d8924 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
@@ -672,10 +672,19 @@
         val DropdownList = Role(6)
 
         /**
-         * This element is a number picker that a user can perform gesture to adjust and select the
-         * next or previous value.
+         * This element is a value picker. It should support the following accessibility actions to
+         * enable selection of the next and previous values:
+         *
+         * [android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_FORWARD]: Select the next
+         * value.
+         *
+         * [android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD]: Select the
+         * previous value.
+         *
+         * These actions allow accessibility services to interact with this node programmatically on
+         * behalf of users, facilitating navigation within sets of selectable values.
          */
-        val NumberPicker = Role(7)
+        val ValuePicker = Role(7)
     }
 
     override fun toString() =
@@ -687,7 +696,7 @@
             Tab -> "Tab"
             Image -> "Image"
             DropdownList -> "DropdownList"
-            NumberPicker -> "NumberPicker"
+            ValuePicker -> "Picker"
             else -> "Unknown"
         }
 }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Dialog.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Dialog.kt
index 37c8dc6..54c59ee 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Dialog.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Dialog.kt
@@ -22,10 +22,9 @@
 /**
  * Properties used to customize the behavior of a [Dialog].
  *
- * @property dismissOnBackPress whether the popup can be dismissed by pressing the back button
- *     * on Android or escape key on desktop. If true, pressing the back button will call
- *       onDismissRequest.
- *
+ * @property dismissOnBackPress whether the popup can be dismissed by pressing the back or escape
+ *   buttons on Android or the escape key on desktop. If true, pressing the back button will call
+ *   onDismissRequest.
  * @property dismissOnClickOutside whether the dialog can be dismissed by clicking outside the
  *   dialog's bounds. If true, clicking outside the dialog will call onDismissRequest.
  * @property usePlatformDefaultWidth Whether the width of the dialog's content should be limited to
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt
index c7ad7a4..2e0794b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt
@@ -29,10 +29,10 @@
  *
  * @property focusable Whether the popup is focusable. When true, the popup will receive IME events
  *   and key presses, such as when the back button is pressed.
- * @property dismissOnBackPress Whether the popup can be dismissed by pressing the back button on
- *   Android or escape key on desktop. If true, pressing the back button will call onDismissRequest.
- *   Note that [focusable] must be set to true in order to receive key events such as the back
- *   button - if the popup is not focusable then this property does nothing.
+ * @property dismissOnBackPress Whether the popup can be dismissed by pressing the back or escape
+ *   buttons on Android or the escape key on desktop. If true, pressing the back button will call
+ *   onDismissRequest. Note that [focusable] must be set to true in order to receive key events such
+ *   as the back button - if the popup is not focusable then this property does nothing.
  * @property dismissOnClickOutside Whether the popup can be dismissed by clicking outside the
  *   popup's bounds. If true, clicking outside the popup will call onDismissRequest.
  * @property clippingEnabled Whether to allow the popup window to extend beyond the bounds of the
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/Actual.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/Actual.commonStubs.kt
new file mode 100644
index 0000000..cd79b71
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/Actual.commonStubs.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+
+internal actual fun areObjectsOfSameType(a: Any, b: Any): Boolean = implementedInJetBrainsFork()
+
+internal actual fun classKeyForObject(a: Any): Any = implementedInJetBrainsFork()
+
+internal actual fun InspectorInfo.tryPopulateReflectively(element: ModifierNodeElement<*>): Unit =
+    implementedInJetBrainsFork()
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/AtomicReference.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/AtomicReference.commonStubs.kt
new file mode 100644
index 0000000..205e420
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/AtomicReference.commonStubs.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+internal actual class AtomicReference<V> actual constructor(value: V) {
+    init {
+        implementedInJetBrainsFork()
+    }
+
+    actual fun get(): V = implementedInJetBrainsFork()
+
+    actual fun set(value: V): Unit = implementedInJetBrainsFork()
+
+    actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+    actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/Modifier.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/Modifier.commonStubs.kt
new file mode 100644
index 0000000..3dd31ac
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/Modifier.commonStubs.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class ModifierNodeDetachedCancellationException :
+    CancellationException("The Modifier.Node was detached")
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/NotImplemented.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/NotImplemented.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/NotImplemented.commonStubs.kt
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/autofill/ContentDataType.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/autofill/ContentDataType.commonStubs.kt
new file mode 100644
index 0000000..5db3588
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/autofill/ContentDataType.commonStubs.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.autofill
+
+import androidx.compose.ui.implementedInJetBrainsFork
+import kotlin.jvm.JvmInline
+
+@JvmInline
+internal actual value class ContentDataType actual constructor(val dataType: Int) {
+    internal actual companion object {
+        actual val Text: ContentDataType = implementedInJetBrainsFork()
+        actual val List: ContentDataType = implementedInJetBrainsFork()
+        actual val Date: ContentDataType = implementedInJetBrainsFork()
+        actual val Toggle: ContentDataType = implementedInJetBrainsFork()
+        actual val None: ContentDataType = implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/autofill/ContentType.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/autofill/ContentType.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/autofill/ContentType.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/autofill/ContentType.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/draganddrop/DragAndDrop.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/draganddrop/DragAndDrop.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/draganddrop/DragAndDrop.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/draganddrop/DragAndDrop.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/hapticfeedback/PlatformHapticFeedbackType.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/hapticfeedback/PlatformHapticFeedbackType.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/hapticfeedback/PlatformHapticFeedbackType.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/hapticfeedback/PlatformHapticFeedbackType.commonStubs.kt
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/key/Key.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/key/Key.commonStubs.kt
new file mode 100644
index 0000000..68211e2
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/key/Key.commonStubs.kt
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.ui.input.key
+
+import androidx.compose.ui.implementedInJetBrainsFork
+import kotlin.jvm.JvmInline
+
+@JvmInline
+actual value class Key(val keyCode: Long) {
+    actual companion object {
+        actual val Unknown: Key = implementedInJetBrainsFork()
+        actual val Home: Key = implementedInJetBrainsFork()
+        actual val Help: Key = implementedInJetBrainsFork()
+        actual val DirectionUp: Key = implementedInJetBrainsFork()
+        actual val DirectionDown: Key = implementedInJetBrainsFork()
+        actual val DirectionLeft: Key = implementedInJetBrainsFork()
+        actual val DirectionRight: Key = implementedInJetBrainsFork()
+        actual val Zero: Key = implementedInJetBrainsFork()
+        actual val One: Key = implementedInJetBrainsFork()
+        actual val Two: Key = implementedInJetBrainsFork()
+        actual val Three: Key = implementedInJetBrainsFork()
+        actual val Four: Key = implementedInJetBrainsFork()
+        actual val Five: Key = implementedInJetBrainsFork()
+        actual val Six: Key = implementedInJetBrainsFork()
+        actual val Seven: Key = implementedInJetBrainsFork()
+        actual val Eight: Key = implementedInJetBrainsFork()
+        actual val Nine: Key = implementedInJetBrainsFork()
+        actual val Plus: Key = implementedInJetBrainsFork()
+        actual val Minus: Key = implementedInJetBrainsFork()
+        actual val Multiply: Key = implementedInJetBrainsFork()
+        actual val Equals: Key = implementedInJetBrainsFork()
+        actual val Pound: Key = implementedInJetBrainsFork()
+        actual val A: Key = implementedInJetBrainsFork()
+        actual val B: Key = implementedInJetBrainsFork()
+        actual val C: Key = implementedInJetBrainsFork()
+        actual val D: Key = implementedInJetBrainsFork()
+        actual val E: Key = implementedInJetBrainsFork()
+        actual val F: Key = implementedInJetBrainsFork()
+        actual val G: Key = implementedInJetBrainsFork()
+        actual val H: Key = implementedInJetBrainsFork()
+        actual val I: Key = implementedInJetBrainsFork()
+        actual val J: Key = implementedInJetBrainsFork()
+        actual val K: Key = implementedInJetBrainsFork()
+        actual val L: Key = implementedInJetBrainsFork()
+        actual val M: Key = implementedInJetBrainsFork()
+        actual val N: Key = implementedInJetBrainsFork()
+        actual val O: Key = implementedInJetBrainsFork()
+        actual val P: Key = implementedInJetBrainsFork()
+        actual val Q: Key = implementedInJetBrainsFork()
+        actual val R: Key = implementedInJetBrainsFork()
+        actual val S: Key = implementedInJetBrainsFork()
+        actual val T: Key = implementedInJetBrainsFork()
+        actual val U: Key = implementedInJetBrainsFork()
+        actual val V: Key = implementedInJetBrainsFork()
+        actual val W: Key = implementedInJetBrainsFork()
+        actual val X: Key = implementedInJetBrainsFork()
+        actual val Y: Key = implementedInJetBrainsFork()
+        actual val Z: Key = implementedInJetBrainsFork()
+        actual val Comma: Key = implementedInJetBrainsFork()
+        actual val Period: Key = implementedInJetBrainsFork()
+        actual val AltLeft: Key = implementedInJetBrainsFork()
+        actual val AltRight: Key = implementedInJetBrainsFork()
+        actual val ShiftLeft: Key = implementedInJetBrainsFork()
+        actual val ShiftRight: Key = implementedInJetBrainsFork()
+        actual val Tab: Key = implementedInJetBrainsFork()
+        actual val Spacebar: Key = implementedInJetBrainsFork()
+        actual val Enter: Key = implementedInJetBrainsFork()
+        actual val Backspace: Key = implementedInJetBrainsFork()
+        actual val Delete: Key = implementedInJetBrainsFork()
+        actual val Escape: Key = implementedInJetBrainsFork()
+        actual val CtrlLeft: Key = implementedInJetBrainsFork()
+        actual val CtrlRight: Key = implementedInJetBrainsFork()
+        actual val CapsLock: Key = implementedInJetBrainsFork()
+        actual val ScrollLock: Key = implementedInJetBrainsFork()
+        actual val MetaLeft: Key = implementedInJetBrainsFork()
+        actual val MetaRight: Key = implementedInJetBrainsFork()
+        actual val PrintScreen: Key = implementedInJetBrainsFork()
+        actual val Insert: Key = implementedInJetBrainsFork()
+        actual val Cut: Key = implementedInJetBrainsFork()
+        actual val Copy: Key = implementedInJetBrainsFork()
+        actual val Paste: Key = implementedInJetBrainsFork()
+        actual val Grave: Key = implementedInJetBrainsFork()
+        actual val LeftBracket: Key = implementedInJetBrainsFork()
+        actual val RightBracket: Key = implementedInJetBrainsFork()
+        actual val Slash: Key = implementedInJetBrainsFork()
+        actual val Backslash: Key = implementedInJetBrainsFork()
+        actual val Semicolon: Key = implementedInJetBrainsFork()
+        actual val Apostrophe: Key = implementedInJetBrainsFork()
+        actual val At: Key = implementedInJetBrainsFork()
+        actual val PageUp: Key = implementedInJetBrainsFork()
+        actual val PageDown: Key = implementedInJetBrainsFork()
+        actual val F1: Key = implementedInJetBrainsFork()
+        actual val F2: Key = implementedInJetBrainsFork()
+        actual val F3: Key = implementedInJetBrainsFork()
+        actual val F4: Key = implementedInJetBrainsFork()
+        actual val F5: Key = implementedInJetBrainsFork()
+        actual val F6: Key = implementedInJetBrainsFork()
+        actual val F7: Key = implementedInJetBrainsFork()
+        actual val F8: Key = implementedInJetBrainsFork()
+        actual val F9: Key = implementedInJetBrainsFork()
+        actual val F10: Key = implementedInJetBrainsFork()
+        actual val F11: Key = implementedInJetBrainsFork()
+        actual val F12: Key = implementedInJetBrainsFork()
+        actual val NumLock: Key = implementedInJetBrainsFork()
+        actual val NumPad0: Key = implementedInJetBrainsFork()
+        actual val NumPad1: Key = implementedInJetBrainsFork()
+        actual val NumPad2: Key = implementedInJetBrainsFork()
+        actual val NumPad3: Key = implementedInJetBrainsFork()
+        actual val NumPad4: Key = implementedInJetBrainsFork()
+        actual val NumPad5: Key = implementedInJetBrainsFork()
+        actual val NumPad6: Key = implementedInJetBrainsFork()
+        actual val NumPad7: Key = implementedInJetBrainsFork()
+        actual val NumPad8: Key = implementedInJetBrainsFork()
+        actual val NumPad9: Key = implementedInJetBrainsFork()
+        actual val NumPadDivide: Key = implementedInJetBrainsFork()
+        actual val NumPadMultiply: Key = implementedInJetBrainsFork()
+        actual val NumPadSubtract: Key = implementedInJetBrainsFork()
+        actual val NumPadAdd: Key = implementedInJetBrainsFork()
+        actual val NumPadDot: Key = implementedInJetBrainsFork()
+        actual val NumPadComma: Key = implementedInJetBrainsFork()
+        actual val NumPadEnter: Key = implementedInJetBrainsFork()
+        actual val NumPadEquals: Key = implementedInJetBrainsFork()
+        actual val NumPadLeftParenthesis: Key = implementedInJetBrainsFork()
+        actual val NumPadRightParenthesis: Key = implementedInJetBrainsFork()
+        actual val MoveHome: Key = implementedInJetBrainsFork()
+        actual val MoveEnd: Key = implementedInJetBrainsFork()
+        actual val SoftLeft: Key = implementedInJetBrainsFork()
+        actual val SoftRight: Key = implementedInJetBrainsFork()
+        actual val Back: Key = implementedInJetBrainsFork()
+        actual val NavigatePrevious: Key = implementedInJetBrainsFork()
+        actual val NavigateNext: Key = implementedInJetBrainsFork()
+        actual val NavigateIn: Key = implementedInJetBrainsFork()
+        actual val NavigateOut: Key = implementedInJetBrainsFork()
+        actual val SystemNavigationUp: Key = implementedInJetBrainsFork()
+        actual val SystemNavigationDown: Key = implementedInJetBrainsFork()
+        actual val SystemNavigationLeft: Key = implementedInJetBrainsFork()
+        actual val SystemNavigationRight: Key = implementedInJetBrainsFork()
+        actual val Call: Key = implementedInJetBrainsFork()
+        actual val EndCall: Key = implementedInJetBrainsFork()
+        actual val DirectionCenter: Key = implementedInJetBrainsFork()
+        actual val DirectionUpLeft: Key = implementedInJetBrainsFork()
+        actual val DirectionDownLeft: Key = implementedInJetBrainsFork()
+        actual val DirectionUpRight: Key = implementedInJetBrainsFork()
+        actual val DirectionDownRight: Key = implementedInJetBrainsFork()
+        actual val VolumeUp: Key = implementedInJetBrainsFork()
+        actual val VolumeDown: Key = implementedInJetBrainsFork()
+        actual val Power: Key = implementedInJetBrainsFork()
+        actual val Camera: Key = implementedInJetBrainsFork()
+        actual val Clear: Key = implementedInJetBrainsFork()
+        actual val Symbol: Key = implementedInJetBrainsFork()
+        actual val Browser: Key = implementedInJetBrainsFork()
+        actual val Envelope: Key = implementedInJetBrainsFork()
+        actual val Function: Key = implementedInJetBrainsFork()
+        actual val Break: Key = implementedInJetBrainsFork()
+        actual val Number: Key = implementedInJetBrainsFork()
+        actual val HeadsetHook: Key = implementedInJetBrainsFork()
+        actual val Focus: Key = implementedInJetBrainsFork()
+        actual val Menu: Key = implementedInJetBrainsFork()
+        actual val Notification: Key = implementedInJetBrainsFork()
+        actual val Search: Key = implementedInJetBrainsFork()
+        actual val PictureSymbols: Key = implementedInJetBrainsFork()
+        actual val SwitchCharset: Key = implementedInJetBrainsFork()
+        actual val ButtonA: Key = implementedInJetBrainsFork()
+        actual val ButtonB: Key = implementedInJetBrainsFork()
+        actual val ButtonC: Key = implementedInJetBrainsFork()
+        actual val ButtonX: Key = implementedInJetBrainsFork()
+        actual val ButtonY: Key = implementedInJetBrainsFork()
+        actual val ButtonZ: Key = implementedInJetBrainsFork()
+        actual val ButtonL1: Key = implementedInJetBrainsFork()
+        actual val ButtonR1: Key = implementedInJetBrainsFork()
+        actual val ButtonL2: Key = implementedInJetBrainsFork()
+        actual val ButtonR2: Key = implementedInJetBrainsFork()
+        actual val ButtonThumbLeft: Key = implementedInJetBrainsFork()
+        actual val ButtonThumbRight: Key = implementedInJetBrainsFork()
+        actual val ButtonStart: Key = implementedInJetBrainsFork()
+        actual val ButtonSelect: Key = implementedInJetBrainsFork()
+        actual val ButtonMode: Key = implementedInJetBrainsFork()
+        actual val Button1: Key = implementedInJetBrainsFork()
+        actual val Button2: Key = implementedInJetBrainsFork()
+        actual val Button3: Key = implementedInJetBrainsFork()
+        actual val Button4: Key = implementedInJetBrainsFork()
+        actual val Button5: Key = implementedInJetBrainsFork()
+        actual val Button6: Key = implementedInJetBrainsFork()
+        actual val Button7: Key = implementedInJetBrainsFork()
+        actual val Button8: Key = implementedInJetBrainsFork()
+        actual val Button9: Key = implementedInJetBrainsFork()
+        actual val Button10: Key = implementedInJetBrainsFork()
+        actual val Button11: Key = implementedInJetBrainsFork()
+        actual val Button12: Key = implementedInJetBrainsFork()
+        actual val Button13: Key = implementedInJetBrainsFork()
+        actual val Button14: Key = implementedInJetBrainsFork()
+        actual val Button15: Key = implementedInJetBrainsFork()
+        actual val Button16: Key = implementedInJetBrainsFork()
+        actual val Forward: Key = implementedInJetBrainsFork()
+        actual val MediaPlay: Key = implementedInJetBrainsFork()
+        actual val MediaPause: Key = implementedInJetBrainsFork()
+        actual val MediaPlayPause: Key = implementedInJetBrainsFork()
+        actual val MediaStop: Key = implementedInJetBrainsFork()
+        actual val MediaRecord: Key = implementedInJetBrainsFork()
+        actual val MediaNext: Key = implementedInJetBrainsFork()
+        actual val MediaPrevious: Key = implementedInJetBrainsFork()
+        actual val MediaRewind: Key = implementedInJetBrainsFork()
+        actual val MediaFastForward: Key = implementedInJetBrainsFork()
+        actual val MediaClose: Key = implementedInJetBrainsFork()
+        actual val MediaAudioTrack: Key = implementedInJetBrainsFork()
+        actual val MediaEject: Key = implementedInJetBrainsFork()
+        actual val MediaTopMenu: Key = implementedInJetBrainsFork()
+        actual val MediaSkipForward: Key = implementedInJetBrainsFork()
+        actual val MediaSkipBackward: Key = implementedInJetBrainsFork()
+        actual val MediaStepForward: Key = implementedInJetBrainsFork()
+        actual val MediaStepBackward: Key = implementedInJetBrainsFork()
+        actual val MicrophoneMute: Key = implementedInJetBrainsFork()
+        actual val VolumeMute: Key = implementedInJetBrainsFork()
+        actual val Info: Key = implementedInJetBrainsFork()
+        actual val ChannelUp: Key = implementedInJetBrainsFork()
+        actual val ChannelDown: Key = implementedInJetBrainsFork()
+        actual val ZoomIn: Key = implementedInJetBrainsFork()
+        actual val ZoomOut: Key = implementedInJetBrainsFork()
+        actual val Tv: Key = implementedInJetBrainsFork()
+        actual val Window: Key = implementedInJetBrainsFork()
+        actual val Guide: Key = implementedInJetBrainsFork()
+        actual val Dvr: Key = implementedInJetBrainsFork()
+        actual val Bookmark: Key = implementedInJetBrainsFork()
+        actual val Captions: Key = implementedInJetBrainsFork()
+        actual val Settings: Key = implementedInJetBrainsFork()
+        actual val TvPower: Key = implementedInJetBrainsFork()
+        actual val TvInput: Key = implementedInJetBrainsFork()
+        actual val SetTopBoxPower: Key = implementedInJetBrainsFork()
+        actual val SetTopBoxInput: Key = implementedInJetBrainsFork()
+        actual val AvReceiverPower: Key = implementedInJetBrainsFork()
+        actual val AvReceiverInput: Key = implementedInJetBrainsFork()
+        actual val ProgramRed: Key = implementedInJetBrainsFork()
+        actual val ProgramGreen: Key = implementedInJetBrainsFork()
+        actual val ProgramYellow: Key = implementedInJetBrainsFork()
+        actual val ProgramBlue: Key = implementedInJetBrainsFork()
+        actual val AppSwitch: Key = implementedInJetBrainsFork()
+        actual val LanguageSwitch: Key = implementedInJetBrainsFork()
+        actual val MannerMode: Key = implementedInJetBrainsFork()
+        actual val Toggle2D3D: Key = implementedInJetBrainsFork()
+        actual val Contacts: Key = implementedInJetBrainsFork()
+        actual val Calendar: Key = implementedInJetBrainsFork()
+        actual val Music: Key = implementedInJetBrainsFork()
+        actual val Calculator: Key = implementedInJetBrainsFork()
+        actual val ZenkakuHankaru: Key = implementedInJetBrainsFork()
+        actual val Eisu: Key = implementedInJetBrainsFork()
+        actual val Muhenkan: Key = implementedInJetBrainsFork()
+        actual val Henkan: Key = implementedInJetBrainsFork()
+        actual val KatakanaHiragana: Key = implementedInJetBrainsFork()
+        actual val Yen: Key = implementedInJetBrainsFork()
+        actual val Ro: Key = implementedInJetBrainsFork()
+        actual val Kana: Key = implementedInJetBrainsFork()
+        actual val Assist: Key = implementedInJetBrainsFork()
+        actual val BrightnessDown: Key = implementedInJetBrainsFork()
+        actual val BrightnessUp: Key = implementedInJetBrainsFork()
+        actual val Sleep: Key = implementedInJetBrainsFork()
+        actual val WakeUp: Key = implementedInJetBrainsFork()
+        actual val SoftSleep: Key = implementedInJetBrainsFork()
+        actual val Pairing: Key = implementedInJetBrainsFork()
+        actual val LastChannel: Key = implementedInJetBrainsFork()
+        actual val TvDataService: Key = implementedInJetBrainsFork()
+        actual val VoiceAssist: Key = implementedInJetBrainsFork()
+        actual val TvRadioService: Key = implementedInJetBrainsFork()
+        actual val TvTeletext: Key = implementedInJetBrainsFork()
+        actual val TvNumberEntry: Key = implementedInJetBrainsFork()
+        actual val TvTerrestrialAnalog: Key = implementedInJetBrainsFork()
+        actual val TvTerrestrialDigital: Key = implementedInJetBrainsFork()
+        actual val TvSatellite: Key = implementedInJetBrainsFork()
+        actual val TvSatelliteBs: Key = implementedInJetBrainsFork()
+        actual val TvSatelliteCs: Key = implementedInJetBrainsFork()
+        actual val TvSatelliteService: Key = implementedInJetBrainsFork()
+        actual val TvNetwork: Key = implementedInJetBrainsFork()
+        actual val TvAntennaCable: Key = implementedInJetBrainsFork()
+        actual val TvInputHdmi1: Key = implementedInJetBrainsFork()
+        actual val TvInputHdmi2: Key = implementedInJetBrainsFork()
+        actual val TvInputHdmi3: Key = implementedInJetBrainsFork()
+        actual val TvInputHdmi4: Key = implementedInJetBrainsFork()
+        actual val TvInputComposite1: Key = implementedInJetBrainsFork()
+        actual val TvInputComposite2: Key = implementedInJetBrainsFork()
+        actual val TvInputComponent1: Key = implementedInJetBrainsFork()
+        actual val TvInputComponent2: Key = implementedInJetBrainsFork()
+        actual val TvInputVga1: Key = implementedInJetBrainsFork()
+        actual val TvAudioDescription: Key = implementedInJetBrainsFork()
+        actual val TvAudioDescriptionMixingVolumeUp: Key = implementedInJetBrainsFork()
+        actual val TvAudioDescriptionMixingVolumeDown: Key = implementedInJetBrainsFork()
+        actual val TvZoomMode: Key = implementedInJetBrainsFork()
+        actual val TvContentsMenu: Key = implementedInJetBrainsFork()
+        actual val TvMediaContextMenu: Key = implementedInJetBrainsFork()
+        actual val TvTimerProgramming: Key = implementedInJetBrainsFork()
+        actual val StemPrimary: Key = implementedInJetBrainsFork()
+        actual val Stem1: Key = implementedInJetBrainsFork()
+        actual val Stem2: Key = implementedInJetBrainsFork()
+        actual val Stem3: Key = implementedInJetBrainsFork()
+        actual val AllApps: Key = implementedInJetBrainsFork()
+        actual val Refresh: Key = implementedInJetBrainsFork()
+        actual val ThumbsUp: Key = implementedInJetBrainsFork()
+        actual val ThumbsDown: Key = implementedInJetBrainsFork()
+        actual val ProfileSwitch: Key = implementedInJetBrainsFork()
+    }
+}
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/key/KeyEvent.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/key/KeyEvent.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/key/KeyEvent.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/key/KeyEvent.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerEvent.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerEvent.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerEvent.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerEvent.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerIcon.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.commonStubs.kt
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.commonStubs.kt
new file mode 100644
index 0000000..ec0970a
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.commonStubs.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.input.pointer
+
+import kotlinx.coroutines.CancellationException
+
+actual class PointerEventTimeoutCancellationException actual constructor(time: Long) :
+    CancellationException("Timed out waiting for $time ms")
+
+internal actual class PointerInputResetException : CancellationException("Pointer input was reset")
+
+internal actual object CancelTimeoutCancellationException : CancellationException("")
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEvent.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEvent.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEvent.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/input/rotary/RotaryScrollEvent.commonStubs.kt
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/internal/JvmDefaultWithCompatibility.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/internal/JvmDefaultWithCompatibility.commonStubs.kt
new file mode 100644
index 0000000..262b903
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.internal
+
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/node/JvmTreeSet.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/node/JvmTreeSet.commonStubs.kt
new file mode 100644
index 0000000..9e6134f
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/node/JvmTreeSet.commonStubs.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.node
+
+import androidx.compose.ui.implementedInJetBrainsFork
+
+internal actual class TreeSet<E> actual constructor(comparator: Comparator<in E>) {
+    actual fun add(element: E): Boolean = implementedInJetBrainsFork()
+
+    actual fun remove(element: E): Boolean = implementedInJetBrainsFork()
+
+    actual fun first(): E = implementedInJetBrainsFork()
+
+    actual fun contains(element: E): Boolean = implementedInJetBrainsFork()
+
+    actual fun isEmpty(): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/node/WeakReference.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/node/WeakReference.commonStubs.kt
new file mode 100644
index 0000000..44eb0f0
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/node/WeakReference.commonStubs.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.ui.node
+
+import androidx.compose.ui.implementedInJetBrainsFork
+
+actual class WeakReference<T> actual constructor(referent: T) {
+    actual fun clear(): Unit = implementedInJetBrainsFork()
+
+    actual fun get(): T? = implementedInJetBrainsFork()
+}
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/AtomicInt.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/AtomicInt.commonStubs.kt
new file mode 100644
index 0000000..a53b41b
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/AtomicInt.commonStubs.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import androidx.compose.ui.implementedInJetBrainsFork
+
+internal actual class AtomicInt actual constructor(value: Int) {
+    actual fun addAndGet(delta: Int): Int = implementedInJetBrainsFork()
+
+    actual fun compareAndSet(expected: Int, new: Int): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/ClassHelpers.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/ClassHelpers.commonStubs.kt
new file mode 100644
index 0000000..c6e7f5f
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/ClassHelpers.commonStubs.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import androidx.compose.ui.implementedInJetBrainsFork
+
+internal actual fun Any.nativeClass(): Any = implementedInJetBrainsFork()
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/DebugUtils.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/DebugUtils.commonStubs.kt
new file mode 100644
index 0000000..01d197e
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/DebugUtils.commonStubs.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import androidx.compose.ui.implementedInJetBrainsFork
+
+internal actual fun simpleIdentityToString(obj: Any, name: String?): String =
+    implementedInJetBrainsFork()
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.commonStubs.kt
new file mode 100644
index 0000000..beb813d
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.commonStubs.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+import androidx.compose.ui.implementedInJetBrainsFork
+
+actual class ClipEntry {
+    actual val clipMetadata: ClipMetadata
+        get() = implementedInJetBrainsFork()
+}
+
+actual class ClipMetadata
+
+actual class NativeClipboard
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputMethodRequest.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputSession.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputSession.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputSession.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/PlatformTextInputSession.commonStubs.kt
diff --git a/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/Synchronization.commonStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/Synchronization.commonStubs.kt
new file mode 100644
index 0000000..b070325
--- /dev/null
+++ b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/Synchronization.commonStubs.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.platform
+
+internal actual inline fun <R> synchronized(lock: Any, block: () -> R): R = block()
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/Wrapper.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/Wrapper.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/Wrapper.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/platform/Wrapper.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropView.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropView.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropView.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropView.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropViewFactoryHolder.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropViewFactoryHolder.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropViewFactoryHolder.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/viewinterop/InteropViewFactoryHolder.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/window/Dialog.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/window/Dialog.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/window/Dialog.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/window/Dialog.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/window/Popup.jvmStubs.kt b/compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/window/Popup.commonStubs.kt
similarity index 100%
rename from compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/window/Popup.jvmStubs.kt
rename to compose/ui/ui/src/commonStubsMain/kotlin/androidx/compose/ui/window/Popup.commonStubs.kt
diff --git a/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/Modifier.jvm.kt b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/Modifier.jvm.kt
new file mode 100644
index 0000000..14b57a6
--- /dev/null
+++ b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/Modifier.jvm.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui
+
+import kotlinx.coroutines.CancellationException
+
+private val EmptyStackTraceElements = emptyArray<StackTraceElement>()
+
+/**
+ * Used in place of the standard Job cancellation pathway to avoid reflective javaClass.simpleName
+ * lookups to build the exception message and stack trace collection. Remove if these are changed in
+ * kotlinx.coroutines.
+ */
+internal actual class ModifierNodeDetachedCancellationException :
+    CancellationException("The Modifier.Node was detached") {
+    override fun fillInStackTrace(): Throwable {
+        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
+        stackTrace = EmptyStackTraceElements
+        return this
+    }
+}
diff --git a/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.jvm.kt b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.jvm.kt
new file mode 100644
index 0000000..eb2d637
--- /dev/null
+++ b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilter.jvm.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.input.pointer
+
+import kotlinx.coroutines.CancellationException
+
+private val EmptyStackTraceElements = emptyArray<StackTraceElement>()
+
+actual class PointerEventTimeoutCancellationException actual constructor(time: Long) :
+    CancellationException("Timed out waiting for $time ms") {
+    override fun fillInStackTrace(): Throwable {
+        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
+        stackTrace = EmptyStackTraceElements
+        return this
+    }
+}
+
+internal actual class PointerInputResetException :
+    CancellationException("Pointer input was reset") {
+    override fun fillInStackTrace(): Throwable {
+        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
+        stackTrace = EmptyStackTraceElements
+        return this
+    }
+}
+
+internal actual object CancelTimeoutCancellationException : CancellationException() {
+    override fun fillInStackTrace(): Throwable {
+        // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
+        stackTrace = EmptyStackTraceElements
+        return this
+    }
+}
diff --git a/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.jvm.kt b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.jvm.kt
new file mode 100644
index 0000000..e1b1eb4
--- /dev/null
+++ b/compose/ui/ui/src/jvmMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.jvm.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.semantics
+
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+
+internal class EmptySemanticsElement(private val node: EmptySemanticsModifier) :
+    ModifierNodeElement<EmptySemanticsModifier>() {
+    override fun create() = node
+
+    override fun update(node: EmptySemanticsModifier) {}
+
+    override fun InspectorInfo.inspectableProperties() {
+        // Nothing to inspect.
+    }
+
+    override fun hashCode(): Int = System.identityHashCode(this)
+
+    override fun equals(other: Any?) = (other === this)
+}
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/autofill/ContentDataType.jvmStubs.kt b/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/autofill/ContentDataType.jvmStubs.kt
deleted file mode 100644
index 673ab3a..0000000
--- a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/autofill/ContentDataType.jvmStubs.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.autofill
-
-import androidx.compose.ui.implementedInJetBrainsFork
-
-@JvmInline
-internal actual value class ContentDataType actual constructor(val dataType: Int) {
-    internal actual companion object {
-        actual val Text: ContentDataType = implementedInJetBrainsFork()
-        actual val List: ContentDataType = implementedInJetBrainsFork()
-        actual val Date: ContentDataType = implementedInJetBrainsFork()
-        actual val Toggle: ContentDataType = implementedInJetBrainsFork()
-        actual val None: ContentDataType = implementedInJetBrainsFork()
-    }
-}
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/key/Key.jvmStubs.kt b/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/key/Key.jvmStubs.kt
deleted file mode 100644
index 1cf10ac..0000000
--- a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/input/key/Key.jvmStubs.kt
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.compose.ui.input.key
-
-import androidx.compose.ui.implementedInJetBrainsFork
-
-@JvmInline
-actual value class Key(val keyCode: Long) {
-    actual companion object {
-        actual val Unknown: Key = implementedInJetBrainsFork()
-        actual val Home: Key = implementedInJetBrainsFork()
-        actual val Help: Key = implementedInJetBrainsFork()
-        actual val DirectionUp: Key = implementedInJetBrainsFork()
-        actual val DirectionDown: Key = implementedInJetBrainsFork()
-        actual val DirectionLeft: Key = implementedInJetBrainsFork()
-        actual val DirectionRight: Key = implementedInJetBrainsFork()
-        actual val Zero: Key = implementedInJetBrainsFork()
-        actual val One: Key = implementedInJetBrainsFork()
-        actual val Two: Key = implementedInJetBrainsFork()
-        actual val Three: Key = implementedInJetBrainsFork()
-        actual val Four: Key = implementedInJetBrainsFork()
-        actual val Five: Key = implementedInJetBrainsFork()
-        actual val Six: Key = implementedInJetBrainsFork()
-        actual val Seven: Key = implementedInJetBrainsFork()
-        actual val Eight: Key = implementedInJetBrainsFork()
-        actual val Nine: Key = implementedInJetBrainsFork()
-        actual val Plus: Key = implementedInJetBrainsFork()
-        actual val Minus: Key = implementedInJetBrainsFork()
-        actual val Multiply: Key = implementedInJetBrainsFork()
-        actual val Equals: Key = implementedInJetBrainsFork()
-        actual val Pound: Key = implementedInJetBrainsFork()
-        actual val A: Key = implementedInJetBrainsFork()
-        actual val B: Key = implementedInJetBrainsFork()
-        actual val C: Key = implementedInJetBrainsFork()
-        actual val D: Key = implementedInJetBrainsFork()
-        actual val E: Key = implementedInJetBrainsFork()
-        actual val F: Key = implementedInJetBrainsFork()
-        actual val G: Key = implementedInJetBrainsFork()
-        actual val H: Key = implementedInJetBrainsFork()
-        actual val I: Key = implementedInJetBrainsFork()
-        actual val J: Key = implementedInJetBrainsFork()
-        actual val K: Key = implementedInJetBrainsFork()
-        actual val L: Key = implementedInJetBrainsFork()
-        actual val M: Key = implementedInJetBrainsFork()
-        actual val N: Key = implementedInJetBrainsFork()
-        actual val O: Key = implementedInJetBrainsFork()
-        actual val P: Key = implementedInJetBrainsFork()
-        actual val Q: Key = implementedInJetBrainsFork()
-        actual val R: Key = implementedInJetBrainsFork()
-        actual val S: Key = implementedInJetBrainsFork()
-        actual val T: Key = implementedInJetBrainsFork()
-        actual val U: Key = implementedInJetBrainsFork()
-        actual val V: Key = implementedInJetBrainsFork()
-        actual val W: Key = implementedInJetBrainsFork()
-        actual val X: Key = implementedInJetBrainsFork()
-        actual val Y: Key = implementedInJetBrainsFork()
-        actual val Z: Key = implementedInJetBrainsFork()
-        actual val Comma: Key = implementedInJetBrainsFork()
-        actual val Period: Key = implementedInJetBrainsFork()
-        actual val AltLeft: Key = implementedInJetBrainsFork()
-        actual val AltRight: Key = implementedInJetBrainsFork()
-        actual val ShiftLeft: Key = implementedInJetBrainsFork()
-        actual val ShiftRight: Key = implementedInJetBrainsFork()
-        actual val Tab: Key = implementedInJetBrainsFork()
-        actual val Spacebar: Key = implementedInJetBrainsFork()
-        actual val Enter: Key = implementedInJetBrainsFork()
-        actual val Backspace: Key = implementedInJetBrainsFork()
-        actual val Delete: Key = implementedInJetBrainsFork()
-        actual val Escape: Key = implementedInJetBrainsFork()
-        actual val CtrlLeft: Key = implementedInJetBrainsFork()
-        actual val CtrlRight: Key = implementedInJetBrainsFork()
-        actual val CapsLock: Key = implementedInJetBrainsFork()
-        actual val ScrollLock: Key = implementedInJetBrainsFork()
-        actual val MetaLeft: Key = implementedInJetBrainsFork()
-        actual val MetaRight: Key = implementedInJetBrainsFork()
-        actual val PrintScreen: Key = implementedInJetBrainsFork()
-        actual val Insert: Key = implementedInJetBrainsFork()
-        actual val Cut: Key = implementedInJetBrainsFork()
-        actual val Copy: Key = implementedInJetBrainsFork()
-        actual val Paste: Key = implementedInJetBrainsFork()
-        actual val Grave: Key = implementedInJetBrainsFork()
-        actual val LeftBracket: Key = implementedInJetBrainsFork()
-        actual val RightBracket: Key = implementedInJetBrainsFork()
-        actual val Slash: Key = implementedInJetBrainsFork()
-        actual val Backslash: Key = implementedInJetBrainsFork()
-        actual val Semicolon: Key = implementedInJetBrainsFork()
-        actual val Apostrophe: Key = implementedInJetBrainsFork()
-        actual val At: Key = implementedInJetBrainsFork()
-        actual val PageUp: Key = implementedInJetBrainsFork()
-        actual val PageDown: Key = implementedInJetBrainsFork()
-        actual val F1: Key = implementedInJetBrainsFork()
-        actual val F2: Key = implementedInJetBrainsFork()
-        actual val F3: Key = implementedInJetBrainsFork()
-        actual val F4: Key = implementedInJetBrainsFork()
-        actual val F5: Key = implementedInJetBrainsFork()
-        actual val F6: Key = implementedInJetBrainsFork()
-        actual val F7: Key = implementedInJetBrainsFork()
-        actual val F8: Key = implementedInJetBrainsFork()
-        actual val F9: Key = implementedInJetBrainsFork()
-        actual val F10: Key = implementedInJetBrainsFork()
-        actual val F11: Key = implementedInJetBrainsFork()
-        actual val F12: Key = implementedInJetBrainsFork()
-        actual val NumLock: Key = implementedInJetBrainsFork()
-        actual val NumPad0: Key = implementedInJetBrainsFork()
-        actual val NumPad1: Key = implementedInJetBrainsFork()
-        actual val NumPad2: Key = implementedInJetBrainsFork()
-        actual val NumPad3: Key = implementedInJetBrainsFork()
-        actual val NumPad4: Key = implementedInJetBrainsFork()
-        actual val NumPad5: Key = implementedInJetBrainsFork()
-        actual val NumPad6: Key = implementedInJetBrainsFork()
-        actual val NumPad7: Key = implementedInJetBrainsFork()
-        actual val NumPad8: Key = implementedInJetBrainsFork()
-        actual val NumPad9: Key = implementedInJetBrainsFork()
-        actual val NumPadDivide: Key = implementedInJetBrainsFork()
-        actual val NumPadMultiply: Key = implementedInJetBrainsFork()
-        actual val NumPadSubtract: Key = implementedInJetBrainsFork()
-        actual val NumPadAdd: Key = implementedInJetBrainsFork()
-        actual val NumPadDot: Key = implementedInJetBrainsFork()
-        actual val NumPadComma: Key = implementedInJetBrainsFork()
-        actual val NumPadEnter: Key = implementedInJetBrainsFork()
-        actual val NumPadEquals: Key = implementedInJetBrainsFork()
-        actual val NumPadLeftParenthesis: Key = implementedInJetBrainsFork()
-        actual val NumPadRightParenthesis: Key = implementedInJetBrainsFork()
-        actual val MoveHome: Key = implementedInJetBrainsFork()
-        actual val MoveEnd: Key = implementedInJetBrainsFork()
-        actual val SoftLeft: Key = implementedInJetBrainsFork()
-        actual val SoftRight: Key = implementedInJetBrainsFork()
-        actual val Back: Key = implementedInJetBrainsFork()
-        actual val NavigatePrevious: Key = implementedInJetBrainsFork()
-        actual val NavigateNext: Key = implementedInJetBrainsFork()
-        actual val NavigateIn: Key = implementedInJetBrainsFork()
-        actual val NavigateOut: Key = implementedInJetBrainsFork()
-        actual val SystemNavigationUp: Key = implementedInJetBrainsFork()
-        actual val SystemNavigationDown: Key = implementedInJetBrainsFork()
-        actual val SystemNavigationLeft: Key = implementedInJetBrainsFork()
-        actual val SystemNavigationRight: Key = implementedInJetBrainsFork()
-        actual val Call: Key = implementedInJetBrainsFork()
-        actual val EndCall: Key = implementedInJetBrainsFork()
-        actual val DirectionCenter: Key = implementedInJetBrainsFork()
-        actual val DirectionUpLeft: Key = implementedInJetBrainsFork()
-        actual val DirectionDownLeft: Key = implementedInJetBrainsFork()
-        actual val DirectionUpRight: Key = implementedInJetBrainsFork()
-        actual val DirectionDownRight: Key = implementedInJetBrainsFork()
-        actual val VolumeUp: Key = implementedInJetBrainsFork()
-        actual val VolumeDown: Key = implementedInJetBrainsFork()
-        actual val Power: Key = implementedInJetBrainsFork()
-        actual val Camera: Key = implementedInJetBrainsFork()
-        actual val Clear: Key = implementedInJetBrainsFork()
-        actual val Symbol: Key = implementedInJetBrainsFork()
-        actual val Browser: Key = implementedInJetBrainsFork()
-        actual val Envelope: Key = implementedInJetBrainsFork()
-        actual val Function: Key = implementedInJetBrainsFork()
-        actual val Break: Key = implementedInJetBrainsFork()
-        actual val Number: Key = implementedInJetBrainsFork()
-        actual val HeadsetHook: Key = implementedInJetBrainsFork()
-        actual val Focus: Key = implementedInJetBrainsFork()
-        actual val Menu: Key = implementedInJetBrainsFork()
-        actual val Notification: Key = implementedInJetBrainsFork()
-        actual val Search: Key = implementedInJetBrainsFork()
-        actual val PictureSymbols: Key = implementedInJetBrainsFork()
-        actual val SwitchCharset: Key = implementedInJetBrainsFork()
-        actual val ButtonA: Key = implementedInJetBrainsFork()
-        actual val ButtonB: Key = implementedInJetBrainsFork()
-        actual val ButtonC: Key = implementedInJetBrainsFork()
-        actual val ButtonX: Key = implementedInJetBrainsFork()
-        actual val ButtonY: Key = implementedInJetBrainsFork()
-        actual val ButtonZ: Key = implementedInJetBrainsFork()
-        actual val ButtonL1: Key = implementedInJetBrainsFork()
-        actual val ButtonR1: Key = implementedInJetBrainsFork()
-        actual val ButtonL2: Key = implementedInJetBrainsFork()
-        actual val ButtonR2: Key = implementedInJetBrainsFork()
-        actual val ButtonThumbLeft: Key = implementedInJetBrainsFork()
-        actual val ButtonThumbRight: Key = implementedInJetBrainsFork()
-        actual val ButtonStart: Key = implementedInJetBrainsFork()
-        actual val ButtonSelect: Key = implementedInJetBrainsFork()
-        actual val ButtonMode: Key = implementedInJetBrainsFork()
-        actual val Button1: Key = implementedInJetBrainsFork()
-        actual val Button2: Key = implementedInJetBrainsFork()
-        actual val Button3: Key = implementedInJetBrainsFork()
-        actual val Button4: Key = implementedInJetBrainsFork()
-        actual val Button5: Key = implementedInJetBrainsFork()
-        actual val Button6: Key = implementedInJetBrainsFork()
-        actual val Button7: Key = implementedInJetBrainsFork()
-        actual val Button8: Key = implementedInJetBrainsFork()
-        actual val Button9: Key = implementedInJetBrainsFork()
-        actual val Button10: Key = implementedInJetBrainsFork()
-        actual val Button11: Key = implementedInJetBrainsFork()
-        actual val Button12: Key = implementedInJetBrainsFork()
-        actual val Button13: Key = implementedInJetBrainsFork()
-        actual val Button14: Key = implementedInJetBrainsFork()
-        actual val Button15: Key = implementedInJetBrainsFork()
-        actual val Button16: Key = implementedInJetBrainsFork()
-        actual val Forward: Key = implementedInJetBrainsFork()
-        actual val MediaPlay: Key = implementedInJetBrainsFork()
-        actual val MediaPause: Key = implementedInJetBrainsFork()
-        actual val MediaPlayPause: Key = implementedInJetBrainsFork()
-        actual val MediaStop: Key = implementedInJetBrainsFork()
-        actual val MediaRecord: Key = implementedInJetBrainsFork()
-        actual val MediaNext: Key = implementedInJetBrainsFork()
-        actual val MediaPrevious: Key = implementedInJetBrainsFork()
-        actual val MediaRewind: Key = implementedInJetBrainsFork()
-        actual val MediaFastForward: Key = implementedInJetBrainsFork()
-        actual val MediaClose: Key = implementedInJetBrainsFork()
-        actual val MediaAudioTrack: Key = implementedInJetBrainsFork()
-        actual val MediaEject: Key = implementedInJetBrainsFork()
-        actual val MediaTopMenu: Key = implementedInJetBrainsFork()
-        actual val MediaSkipForward: Key = implementedInJetBrainsFork()
-        actual val MediaSkipBackward: Key = implementedInJetBrainsFork()
-        actual val MediaStepForward: Key = implementedInJetBrainsFork()
-        actual val MediaStepBackward: Key = implementedInJetBrainsFork()
-        actual val MicrophoneMute: Key = implementedInJetBrainsFork()
-        actual val VolumeMute: Key = implementedInJetBrainsFork()
-        actual val Info: Key = implementedInJetBrainsFork()
-        actual val ChannelUp: Key = implementedInJetBrainsFork()
-        actual val ChannelDown: Key = implementedInJetBrainsFork()
-        actual val ZoomIn: Key = implementedInJetBrainsFork()
-        actual val ZoomOut: Key = implementedInJetBrainsFork()
-        actual val Tv: Key = implementedInJetBrainsFork()
-        actual val Window: Key = implementedInJetBrainsFork()
-        actual val Guide: Key = implementedInJetBrainsFork()
-        actual val Dvr: Key = implementedInJetBrainsFork()
-        actual val Bookmark: Key = implementedInJetBrainsFork()
-        actual val Captions: Key = implementedInJetBrainsFork()
-        actual val Settings: Key = implementedInJetBrainsFork()
-        actual val TvPower: Key = implementedInJetBrainsFork()
-        actual val TvInput: Key = implementedInJetBrainsFork()
-        actual val SetTopBoxPower: Key = implementedInJetBrainsFork()
-        actual val SetTopBoxInput: Key = implementedInJetBrainsFork()
-        actual val AvReceiverPower: Key = implementedInJetBrainsFork()
-        actual val AvReceiverInput: Key = implementedInJetBrainsFork()
-        actual val ProgramRed: Key = implementedInJetBrainsFork()
-        actual val ProgramGreen: Key = implementedInJetBrainsFork()
-        actual val ProgramYellow: Key = implementedInJetBrainsFork()
-        actual val ProgramBlue: Key = implementedInJetBrainsFork()
-        actual val AppSwitch: Key = implementedInJetBrainsFork()
-        actual val LanguageSwitch: Key = implementedInJetBrainsFork()
-        actual val MannerMode: Key = implementedInJetBrainsFork()
-        actual val Toggle2D3D: Key = implementedInJetBrainsFork()
-        actual val Contacts: Key = implementedInJetBrainsFork()
-        actual val Calendar: Key = implementedInJetBrainsFork()
-        actual val Music: Key = implementedInJetBrainsFork()
-        actual val Calculator: Key = implementedInJetBrainsFork()
-        actual val ZenkakuHankaru: Key = implementedInJetBrainsFork()
-        actual val Eisu: Key = implementedInJetBrainsFork()
-        actual val Muhenkan: Key = implementedInJetBrainsFork()
-        actual val Henkan: Key = implementedInJetBrainsFork()
-        actual val KatakanaHiragana: Key = implementedInJetBrainsFork()
-        actual val Yen: Key = implementedInJetBrainsFork()
-        actual val Ro: Key = implementedInJetBrainsFork()
-        actual val Kana: Key = implementedInJetBrainsFork()
-        actual val Assist: Key = implementedInJetBrainsFork()
-        actual val BrightnessDown: Key = implementedInJetBrainsFork()
-        actual val BrightnessUp: Key = implementedInJetBrainsFork()
-        actual val Sleep: Key = implementedInJetBrainsFork()
-        actual val WakeUp: Key = implementedInJetBrainsFork()
-        actual val SoftSleep: Key = implementedInJetBrainsFork()
-        actual val Pairing: Key = implementedInJetBrainsFork()
-        actual val LastChannel: Key = implementedInJetBrainsFork()
-        actual val TvDataService: Key = implementedInJetBrainsFork()
-        actual val VoiceAssist: Key = implementedInJetBrainsFork()
-        actual val TvRadioService: Key = implementedInJetBrainsFork()
-        actual val TvTeletext: Key = implementedInJetBrainsFork()
-        actual val TvNumberEntry: Key = implementedInJetBrainsFork()
-        actual val TvTerrestrialAnalog: Key = implementedInJetBrainsFork()
-        actual val TvTerrestrialDigital: Key = implementedInJetBrainsFork()
-        actual val TvSatellite: Key = implementedInJetBrainsFork()
-        actual val TvSatelliteBs: Key = implementedInJetBrainsFork()
-        actual val TvSatelliteCs: Key = implementedInJetBrainsFork()
-        actual val TvSatelliteService: Key = implementedInJetBrainsFork()
-        actual val TvNetwork: Key = implementedInJetBrainsFork()
-        actual val TvAntennaCable: Key = implementedInJetBrainsFork()
-        actual val TvInputHdmi1: Key = implementedInJetBrainsFork()
-        actual val TvInputHdmi2: Key = implementedInJetBrainsFork()
-        actual val TvInputHdmi3: Key = implementedInJetBrainsFork()
-        actual val TvInputHdmi4: Key = implementedInJetBrainsFork()
-        actual val TvInputComposite1: Key = implementedInJetBrainsFork()
-        actual val TvInputComposite2: Key = implementedInJetBrainsFork()
-        actual val TvInputComponent1: Key = implementedInJetBrainsFork()
-        actual val TvInputComponent2: Key = implementedInJetBrainsFork()
-        actual val TvInputVga1: Key = implementedInJetBrainsFork()
-        actual val TvAudioDescription: Key = implementedInJetBrainsFork()
-        actual val TvAudioDescriptionMixingVolumeUp: Key = implementedInJetBrainsFork()
-        actual val TvAudioDescriptionMixingVolumeDown: Key = implementedInJetBrainsFork()
-        actual val TvZoomMode: Key = implementedInJetBrainsFork()
-        actual val TvContentsMenu: Key = implementedInJetBrainsFork()
-        actual val TvMediaContextMenu: Key = implementedInJetBrainsFork()
-        actual val TvTimerProgramming: Key = implementedInJetBrainsFork()
-        actual val StemPrimary: Key = implementedInJetBrainsFork()
-        actual val Stem1: Key = implementedInJetBrainsFork()
-        actual val Stem2: Key = implementedInJetBrainsFork()
-        actual val Stem3: Key = implementedInJetBrainsFork()
-        actual val AllApps: Key = implementedInJetBrainsFork()
-        actual val Refresh: Key = implementedInJetBrainsFork()
-        actual val ThumbsUp: Key = implementedInJetBrainsFork()
-        actual val ThumbsDown: Key = implementedInJetBrainsFork()
-        actual val ProfileSwitch: Key = implementedInJetBrainsFork()
-    }
-}
diff --git a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.jvmStubs.kt b/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.jvmStubs.kt
deleted file mode 100644
index 80af976..0000000
--- a/compose/ui/ui/src/jvmStubsMain/kotlin/androidx/compose/ui/platform/PlatformClipboardManager.jvmStubs.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.platform
-
-import androidx.compose.ui.implementedInJetBrainsFork
-import java.awt.datatransfer.Clipboard
-
-actual class ClipEntry {
-    actual val clipMetadata: ClipMetadata
-        get() = implementedInJetBrainsFork()
-}
-
-actual class ClipMetadata
-
-actual typealias NativeClipboard = Clipboard
diff --git a/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ContentCaptureSessionCompat.java b/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ContentCaptureSessionCompat.java
index 788ae0b..a62dd2a 100644
--- a/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ContentCaptureSessionCompat.java
+++ b/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ContentCaptureSessionCompat.java
@@ -24,7 +24,6 @@
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ContentCaptureSession;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -243,7 +242,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void notifyViewsAppeared(
                 ContentCaptureSession contentCaptureSession, List<ViewStructure> appearedNodes) {
             // new API in U
@@ -256,37 +254,31 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void notifyViewsDisappeared(
                 ContentCaptureSession contentCaptureSession, AutofillId hostId, long[] virtualIds) {
             contentCaptureSession.notifyViewsDisappeared(hostId, virtualIds);
         }
 
-        @DoNotInline
         static void notifyViewAppeared(
                 ContentCaptureSession contentCaptureSession, ViewStructure node) {
             contentCaptureSession.notifyViewAppeared(node);
         }
-        @DoNotInline
         static ViewStructure newViewStructure(
                 ContentCaptureSession contentCaptureSession, View view) {
             return contentCaptureSession.newViewStructure(view);
         }
 
-        @DoNotInline
         static ViewStructure newVirtualViewStructure(ContentCaptureSession contentCaptureSession,
                 AutofillId parentId, long virtualId) {
             return contentCaptureSession.newVirtualViewStructure(parentId, virtualId);
         }
 
 
-        @DoNotInline
         static AutofillId newAutofillId(ContentCaptureSession contentCaptureSession,
                 AutofillId hostId, long virtualChildId) {
             return contentCaptureSession.newAutofillId(hostId, virtualChildId);
         }
 
-        @DoNotInline
         public static void notifyViewTextChanged(ContentCaptureSession contentCaptureSession,
                 AutofillId id, CharSequence charSequence) {
             contentCaptureSession.notifyViewTextChanged(id, charSequence);
@@ -299,7 +291,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Bundle getExtras(ViewStructure viewStructure) {
             return viewStructure.getExtras();
         }
diff --git a/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewCompatShims.java b/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewCompatShims.java
index f94c668..e6cc549 100644
--- a/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewCompatShims.java
+++ b/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewCompatShims.java
@@ -21,7 +21,6 @@
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ContentCaptureSession;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -160,7 +159,6 @@
         private Api26Impl() {
             // This class is not instantiable.
         }
-        @DoNotInline
         public static AutofillId getAutofillId(View view) {
             return view.getAutofillId();
         }
@@ -172,7 +170,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ContentCaptureSession getContentCaptureSession(View view) {
             return view.getContentCaptureSession();
         }
@@ -183,7 +180,6 @@
         private Api30Impl() {
             // This class is not instantiable.
         }
-        @DoNotInline
         static void setImportantForContentCapture(View view, int mode) {
             view.setImportantForContentCapture(mode);
         }
diff --git a/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewStructureCompat.java b/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewStructureCompat.java
index 1052324..8bc0994 100644
--- a/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewStructureCompat.java
+++ b/compose/ui/ui/src/main/java/androidx/compose/ui/platform/coreshims/ViewStructureCompat.java
@@ -21,7 +21,6 @@
 import android.os.Bundle;
 import android.view.ViewStructure;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -220,34 +219,28 @@
             viewStructure.setId(id, packageName, typeName, entryName);
         }
 
-        @DoNotInline
         static void setDimens(ViewStructure viewStructure, int left, int top, int scrollX,
                 int scrollY, int width, int height) {
             viewStructure.setDimens(left, top, scrollX, scrollY, width, height);
         }
 
-        @DoNotInline
         static void setText(ViewStructure viewStructure, CharSequence charSequence) {
             viewStructure.setText(charSequence);
         }
 
-        @DoNotInline
         static void setClassName(ViewStructure viewStructure, String string) {
             viewStructure.setClassName(string);
         }
 
-        @DoNotInline
         static void setContentDescription(ViewStructure viewStructure, CharSequence charSequence) {
             viewStructure.setContentDescription(charSequence);
         }
 
-        @DoNotInline
         static void setTextStyle(
                 ViewStructure viewStructure, float size, int fgColor, int bgColor, int style) {
             viewStructure.setTextStyle(size, fgColor, bgColor, style);
         }
 
-        @DoNotInline
         static Bundle getExtras(ViewStructure viewStructure) {
             return viewStructure.getExtras();
         }
diff --git a/concurrent/concurrent-futures-ktx/build.gradle b/concurrent/concurrent-futures-ktx/build.gradle
index a491d54..782869d 100644
--- a/concurrent/concurrent-futures-ktx/build.gradle
+++ b/concurrent/concurrent-futures-ktx/build.gradle
@@ -44,5 +44,4 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2019"
     description = "Kotlin Extensions for Androidx implementation of Guava's ListenableFuture"
-    metalavaK2UastEnabled = true
 }
diff --git a/concurrent/concurrent-futures/build.gradle b/concurrent/concurrent-futures/build.gradle
index 3dacf4e..de0a227 100644
--- a/concurrent/concurrent-futures/build.gradle
+++ b/concurrent/concurrent-futures/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.guavaListenableFuture)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
@@ -40,5 +40,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Androidx implementation of Guava's ListenableFuture"
-    metalavaK2UastEnabled = true
 }
diff --git a/constraintlayout/constraintlayout-compose/api/1.1.0-beta01.txt b/constraintlayout/constraintlayout-compose/api/1.1.0-beta01.txt
deleted file mode 100644
index 0375873..0000000
--- a/constraintlayout/constraintlayout-compose/api/1.1.0-beta01.txt
+++ /dev/null
@@ -1,1013 +0,0 @@
-// Signature format: 4.0
-package androidx.constraintlayout.compose {
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class Arc {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.Arc.Companion Companion;
-  }
-
-  public static final class Arc.Companion {
-    method public androidx.constraintlayout.compose.Arc getAbove();
-    method public androidx.constraintlayout.compose.Arc getBelow();
-    method public androidx.constraintlayout.compose.Arc getFlip();
-    method public androidx.constraintlayout.compose.Arc getNone();
-    method public androidx.constraintlayout.compose.Arc getStartHorizontal();
-    method public androidx.constraintlayout.compose.Arc getStartVertical();
-    property public final androidx.constraintlayout.compose.Arc Above;
-    property public final androidx.constraintlayout.compose.Arc Below;
-    property public final androidx.constraintlayout.compose.Arc Flip;
-    property public final androidx.constraintlayout.compose.Arc None;
-    property public final androidx.constraintlayout.compose.Arc StartHorizontal;
-    property public final androidx.constraintlayout.compose.Arc StartVertical;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public abstract sealed class BaseKeyFrameScope {
-    method protected final <E extends androidx.constraintlayout.compose.NamedPropertyOrValue> kotlin.properties.ObservableProperty<E> addNameOnPropertyChange(E initialValue, optional String? nameOverride);
-    method protected final <T> kotlin.properties.ObservableProperty<T> addOnPropertyChange(T initialValue, optional String? nameOverride);
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public abstract sealed class BaseKeyFramesScope {
-    method public final androidx.constraintlayout.compose.Easing getEasing();
-    method public final void setEasing(androidx.constraintlayout.compose.Easing);
-    property public final androidx.constraintlayout.compose.Easing easing;
-  }
-
-  @kotlin.jvm.JvmDefaultWithCompatibility public interface BaselineAnchorable {
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor anchor, optional float margin, optional float goneMargin);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor anchor, optional float margin, optional float goneMargin);
-  }
-
-  @androidx.compose.runtime.Immutable public final class ChainStyle {
-    field public static final androidx.constraintlayout.compose.ChainStyle.Companion Companion;
-  }
-
-  public static final class ChainStyle.Companion {
-    method @androidx.compose.runtime.Stable public androidx.constraintlayout.compose.ChainStyle Packed(float bias);
-    method public androidx.constraintlayout.compose.ChainStyle getPacked();
-    method public androidx.constraintlayout.compose.ChainStyle getSpread();
-    method public androidx.constraintlayout.compose.ChainStyle getSpreadInside();
-    property public final androidx.constraintlayout.compose.ChainStyle Packed;
-    property public final androidx.constraintlayout.compose.ChainStyle Spread;
-    property public final androidx.constraintlayout.compose.ChainStyle SpreadInside;
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public final class ConstrainScope {
-    method public androidx.constraintlayout.compose.Dimension asDimension(float);
-    method public void centerAround(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor anchor);
-    method public void centerAround(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor anchor);
-    method public void centerHorizontallyTo(androidx.constraintlayout.compose.ConstrainedLayoutReference other, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void centerTo(androidx.constraintlayout.compose.ConstrainedLayoutReference other);
-    method public void centerVerticallyTo(androidx.constraintlayout.compose.ConstrainedLayoutReference other, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void circular(androidx.constraintlayout.compose.ConstrainedLayoutReference other, float angle, float distance);
-    method public void clearConstraints();
-    method public void clearHorizontal();
-    method public void clearVertical();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteRight();
-    method public float getAlpha();
-    method public androidx.constraintlayout.compose.BaselineAnchorable getBaseline();
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
-    method public androidx.constraintlayout.compose.Dimension getHeight();
-    method public float getHorizontalBias();
-    method public float getHorizontalChainWeight();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getStart();
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getTop();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public float getVerticalBias();
-    method public float getVerticalChainWeight();
-    method public androidx.constraintlayout.compose.Visibility getVisibility();
-    method public androidx.constraintlayout.compose.Dimension getWidth();
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional @FloatRange(from=0.0, to=1.0) float horizontalBias, optional @FloatRange(from=0.0, to=1.0) float verticalBias);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void resetDimensions();
-    method public void resetTransforms();
-    method public void setAlpha(float);
-    method public void setHeight(androidx.constraintlayout.compose.Dimension);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalChainWeight(float);
-    method public void setPivotX(float);
-    method public void setPivotY(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    method public void setVerticalBias(float);
-    method public void setVerticalChainWeight(float);
-    method public void setVisibility(androidx.constraintlayout.compose.Visibility);
-    method public void setWidth(androidx.constraintlayout.compose.Dimension);
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteLeft;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteRight;
-    property public final float alpha;
-    property public final androidx.constraintlayout.compose.BaselineAnchorable baseline;
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable end;
-    property public final androidx.constraintlayout.compose.Dimension height;
-    property public final float horizontalBias;
-    property public final float horizontalChainWeight;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
-    property public final float pivotX;
-    property public final float pivotY;
-    property public final float rotationX;
-    property public final float rotationY;
-    property public final float rotationZ;
-    property public final float scaleX;
-    property public final float scaleY;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable start;
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable top;
-    property public final float translationX;
-    property public final float translationY;
-    property public final float translationZ;
-    property public final float verticalBias;
-    property public final float verticalChainWeight;
-    property public final androidx.constraintlayout.compose.Visibility visibility;
-    property public final androidx.constraintlayout.compose.Dimension width;
-  }
-
-  @androidx.compose.runtime.Stable public final class ConstrainedLayoutReference extends androidx.constraintlayout.compose.LayoutReference {
-    ctor public ConstrainedLayoutReference(Object id);
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteRight();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor getBaseline();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getBottom();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getEnd();
-    method public Object getId();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getStart();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getTop();
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteLeft;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteRight;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor baseline;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end;
-    property public Object id;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top;
-  }
-
-  public abstract class ConstraintLayoutBaseScope {
-    ctor public ConstraintLayoutBaseScope();
-    method public final void applyTo(androidx.constraintlayout.compose.State state);
-    method public final androidx.constraintlayout.compose.ConstrainScope constrain(androidx.constraintlayout.compose.ConstrainedLayoutReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstrainScope,kotlin.Unit> constrainBlock);
-    method public final void constrain(androidx.constraintlayout.compose.ConstrainedLayoutReference[] refs, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstrainScope,kotlin.Unit> constrainBlock);
-    method public final androidx.constraintlayout.compose.HorizontalChainScope constrain(androidx.constraintlayout.compose.HorizontalChainReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.HorizontalChainScope,kotlin.Unit> constrainBlock);
-    method public final androidx.constraintlayout.compose.VerticalChainScope constrain(androidx.constraintlayout.compose.VerticalChainReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.VerticalChainScope,kotlin.Unit> constrainBlock);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createAbsoluteLeftBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createAbsoluteRightBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createBottomBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference[] elements, optional float spacing, optional float[] weights);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createEndBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createFlow(androidx.constraintlayout.compose.LayoutReference?[] elements, optional boolean flowVertically, optional float verticalGap, optional float horizontalGap, optional int maxElement, optional float padding, optional androidx.constraintlayout.compose.Wrap wrapMode, optional androidx.constraintlayout.compose.VerticalAlign verticalAlign, optional androidx.constraintlayout.compose.HorizontalAlign horizontalAlign, optional float horizontalFlowBias, optional float verticalFlowBias, optional androidx.constraintlayout.compose.FlowStyle verticalStyle, optional androidx.constraintlayout.compose.FlowStyle horizontalStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createFlow(androidx.constraintlayout.compose.LayoutReference?[] elements, optional boolean flowVertically, optional float verticalGap, optional float horizontalGap, optional int maxElement, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.Wrap wrapMode, optional androidx.constraintlayout.compose.VerticalAlign verticalAlign, optional androidx.constraintlayout.compose.HorizontalAlign horizontalAlign, optional float horizontalFlowBias, optional float verticalFlowBias, optional androidx.constraintlayout.compose.FlowStyle verticalStyle, optional androidx.constraintlayout.compose.FlowStyle horizontalStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createFlow(androidx.constraintlayout.compose.LayoutReference?[] elements, optional boolean flowVertically, optional float verticalGap, optional float horizontalGap, optional int maxElement, optional float paddingLeft, optional float paddingTop, optional float paddingRight, optional float paddingBottom, optional androidx.constraintlayout.compose.Wrap wrapMode, optional androidx.constraintlayout.compose.VerticalAlign verticalAlign, optional androidx.constraintlayout.compose.HorizontalAlign horizontalAlign, optional float horizontalFlowBias, optional float verticalFlowBias, optional androidx.constraintlayout.compose.FlowStyle verticalStyle, optional androidx.constraintlayout.compose.FlowStyle horizontalStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference[] elements, @IntRange(from=1L) int rows, @IntRange(from=1L) int columns, optional boolean isHorizontalArrangement, optional float verticalSpacing, optional float horizontalSpacing, optional float[] rowWeights, optional float[] columnWeights, optional androidx.constraintlayout.compose.Skip[] skips, optional androidx.constraintlayout.compose.Span[] spans, optional int flags);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteLeft(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteLeft(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteRight(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteRight(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromBottom(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromBottom(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromEnd(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromEnd(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromStart(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromStart(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromTop(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromTop(float fraction);
-    method public final androidx.constraintlayout.compose.HorizontalChainReference createHorizontalChain(androidx.constraintlayout.compose.LayoutReference[] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference[] elements, optional float spacing, optional float[] weights);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createStartBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createTopBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference[] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
-    method @Deprecated protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
-    method public void reset();
-    method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
-    method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
-    method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
-    property @Deprecated protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
-  }
-
-  @androidx.compose.runtime.Stable public static final class ConstraintLayoutBaseScope.BaselineAnchor {
-    method public androidx.constraintlayout.compose.LayoutReference component2();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor copy(Object id, androidx.constraintlayout.compose.LayoutReference reference);
-    method public androidx.constraintlayout.compose.LayoutReference getReference();
-    property public final androidx.constraintlayout.compose.LayoutReference reference;
-  }
-
-  @androidx.compose.runtime.Stable public static final class ConstraintLayoutBaseScope.HorizontalAnchor {
-    method public androidx.constraintlayout.compose.LayoutReference component3();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor copy(Object id, int index, androidx.constraintlayout.compose.LayoutReference reference);
-    method public androidx.constraintlayout.compose.LayoutReference getReference();
-    property public final androidx.constraintlayout.compose.LayoutReference reference;
-  }
-
-  @androidx.compose.runtime.Stable public static final class ConstraintLayoutBaseScope.VerticalAnchor {
-    method public androidx.constraintlayout.compose.LayoutReference component3();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor copy(Object id, int index, androidx.constraintlayout.compose.LayoutReference reference);
-    method public androidx.constraintlayout.compose.LayoutReference getReference();
-    property public final androidx.constraintlayout.compose.LayoutReference reference;
-  }
-
-  public final class ConstraintLayoutKt {
-    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? animateChangesSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
-    method @Deprecated @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? animateChangesSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @Deprecated @androidx.compose.runtime.Composable public static inline void ConstraintLayout(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, @org.intellij.lang.annotations.Language("json5") String jsonContent);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> description);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(@org.intellij.lang.annotations.Language("json5") String jsonContent);
-    method @androidx.compose.runtime.Composable public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(@org.intellij.lang.annotations.Language("json5") String content, optional @org.intellij.lang.annotations.Language("json5") String? overrideVariables);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> description);
-    method public static androidx.constraintlayout.compose.Dimension.MaxCoercible atLeast(androidx.constraintlayout.compose.Dimension.Coercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension atLeast(androidx.constraintlayout.compose.Dimension.MinCoercible, float dp);
-    method @Deprecated public static androidx.constraintlayout.compose.Dimension atLeastWrapContent(androidx.constraintlayout.compose.Dimension.MinCoercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension.MinCoercible atMost(androidx.constraintlayout.compose.Dimension.Coercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension atMost(androidx.constraintlayout.compose.Dimension.MaxCoercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension.MaxCoercible getAtLeastWrapContent(androidx.constraintlayout.compose.Dimension.Coercible);
-    method public static androidx.constraintlayout.compose.Dimension getAtLeastWrapContent(androidx.constraintlayout.compose.Dimension.MinCoercible);
-    method public static androidx.constraintlayout.compose.Dimension.MinCoercible getAtMostWrapContent(androidx.constraintlayout.compose.Dimension.Coercible);
-    method public static androidx.constraintlayout.compose.Dimension getAtMostWrapContent(androidx.constraintlayout.compose.Dimension.MaxCoercible);
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker public final class ConstraintLayoutScope extends androidx.constraintlayout.compose.ConstraintLayoutBaseScope {
-    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier constrainAs(androidx.compose.ui.Modifier, androidx.constraintlayout.compose.ConstrainedLayoutReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstrainScope,kotlin.Unit> constrainBlock);
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRef();
-    method @androidx.compose.runtime.Stable public androidx.constraintlayout.compose.ConstraintLayoutScope.ConstrainedLayoutReferences createRefs();
-  }
-
-  public final class ConstraintLayoutScope.ConstrainedLayoutReferences {
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component1();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component10();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component11();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component12();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component13();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component14();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component15();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component16();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component2();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component3();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component4();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component5();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component6();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component7();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component8();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component9();
-  }
-
-  public final class ConstraintLayoutTagKt {
-    method public static Object? getConstraintLayoutId(androidx.compose.ui.layout.Measurable);
-    method public static Object? getConstraintLayoutTag(androidx.compose.ui.layout.Measurable);
-    method public static androidx.compose.ui.Modifier layoutId(androidx.compose.ui.Modifier, String layoutId, optional String? tag);
-  }
-
-  public interface ConstraintLayoutTagParentData {
-    method public String getConstraintLayoutId();
-    method public String getConstraintLayoutTag();
-    property public abstract String constraintLayoutId;
-    property public abstract String constraintLayoutTag;
-  }
-
-  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface ConstraintSet {
-    method public void applyTo(androidx.constraintlayout.compose.State state, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
-    method public default void applyTo(androidx.constraintlayout.core.state.Transition transition, int type);
-    method public default boolean isDirty(java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
-    method public default androidx.constraintlayout.compose.ConstraintSet override(String name, float value);
-  }
-
-  public final class ConstraintSetRef {
-    method public androidx.constraintlayout.compose.ConstraintSetRef copy(String name);
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker public final class ConstraintSetScope extends androidx.constraintlayout.compose.ConstraintLayoutBaseScope {
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRefFor(Object id);
-    method public androidx.constraintlayout.compose.ConstraintSetScope.ConstrainedLayoutReferences createRefsFor(java.lang.Object... ids);
-  }
-
-  public final class ConstraintSetScope.ConstrainedLayoutReferences {
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component1();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component10();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component11();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component12();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component13();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component14();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component15();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component16();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component2();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component3();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component4();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component5();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component6();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component7();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component8();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component9();
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class CurveFit {
-    method public String getName();
-    property public String name;
-    field public static final androidx.constraintlayout.compose.CurveFit.Companion Companion;
-  }
-
-  public static final class CurveFit.Companion {
-    method public androidx.constraintlayout.compose.CurveFit getLinear();
-    method public androidx.constraintlayout.compose.CurveFit getSpline();
-    property public final androidx.constraintlayout.compose.CurveFit Linear;
-    property public final androidx.constraintlayout.compose.CurveFit Spline;
-  }
-
-  @kotlin.jvm.JvmInline public final value class DebugFlags {
-    ctor public DebugFlags(optional boolean showBounds, optional boolean showPaths, optional boolean showKeyPositions);
-    method public boolean getShowBounds();
-    method public boolean getShowKeyPositions();
-    method public boolean getShowPaths();
-    property public final boolean showBounds;
-    property public final boolean showKeyPositions;
-    property public final boolean showPaths;
-    field public static final androidx.constraintlayout.compose.DebugFlags.Companion Companion;
-  }
-
-  public static final class DebugFlags.Companion {
-    method public int getAll();
-    method public int getNone();
-    property public final int All;
-    property public final int None;
-  }
-
-  public final class DesignElements {
-    method public void define(String name, kotlin.jvm.functions.Function2<? super java.lang.String,? super java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit> function);
-    method public java.util.HashMap<java.lang.String,kotlin.jvm.functions.Function2<java.lang.String,java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit>> getMap();
-    method public void setMap(java.util.HashMap<java.lang.String,kotlin.jvm.functions.Function2<java.lang.String,java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit>>);
-    property public final java.util.HashMap<java.lang.String,kotlin.jvm.functions.Function2<java.lang.String,java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit>> map;
-    field public static final androidx.constraintlayout.compose.DesignElements INSTANCE;
-  }
-
-  public interface DesignInfoProvider {
-    method public String getDesignInfo(int startX, int startY, String args);
-  }
-
-  public interface Dimension {
-    field public static final androidx.constraintlayout.compose.Dimension.Companion Companion;
-  }
-
-  public static interface Dimension.Coercible extends androidx.constraintlayout.compose.Dimension {
-  }
-
-  public static final class Dimension.Companion {
-    method public androidx.constraintlayout.compose.Dimension.Coercible getFillToConstraints();
-    method public androidx.constraintlayout.compose.Dimension getMatchParent();
-    method public androidx.constraintlayout.compose.Dimension.Coercible getPreferredWrapContent();
-    method public androidx.constraintlayout.compose.Dimension getWrapContent();
-    method public androidx.constraintlayout.compose.Dimension percent(float percent);
-    method public androidx.constraintlayout.compose.Dimension.MinCoercible preferredValue(float dp);
-    method public androidx.constraintlayout.compose.Dimension ratio(String ratio);
-    method public androidx.constraintlayout.compose.Dimension value(float dp);
-    property public final androidx.constraintlayout.compose.Dimension.Coercible fillToConstraints;
-    property public final androidx.constraintlayout.compose.Dimension matchParent;
-    property public final androidx.constraintlayout.compose.Dimension.Coercible preferredWrapContent;
-    property public final androidx.constraintlayout.compose.Dimension wrapContent;
-  }
-
-  public static interface Dimension.MaxCoercible extends androidx.constraintlayout.compose.Dimension {
-  }
-
-  public static interface Dimension.MinCoercible extends androidx.constraintlayout.compose.Dimension {
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class Easing {
-    method public String getName();
-    property public String name;
-    field public static final androidx.constraintlayout.compose.Easing.Companion Companion;
-  }
-
-  public static final class Easing.Companion {
-    method public androidx.constraintlayout.compose.Easing cubic(float x1, float y1, float x2, float y2);
-    method public androidx.constraintlayout.compose.Easing getAccelerate();
-    method public androidx.constraintlayout.compose.Easing getAnticipate();
-    method public androidx.constraintlayout.compose.Easing getDecelerate();
-    method public androidx.constraintlayout.compose.Easing getLinear();
-    method public androidx.constraintlayout.compose.Easing getOvershoot();
-    method public androidx.constraintlayout.compose.Easing getStandard();
-    property public final androidx.constraintlayout.compose.Easing Accelerate;
-    property public final androidx.constraintlayout.compose.Easing Anticipate;
-    property public final androidx.constraintlayout.compose.Easing Decelerate;
-    property public final androidx.constraintlayout.compose.Easing Linear;
-    property public final androidx.constraintlayout.compose.Easing Overshoot;
-    property public final androidx.constraintlayout.compose.Easing Standard;
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn(message="MotionLayout API is experimental and it is likely to change.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMotionApi {
-  }
-
-  @androidx.compose.runtime.Immutable public final class FlowStyle {
-    field public static final androidx.constraintlayout.compose.FlowStyle.Companion Companion;
-  }
-
-  public static final class FlowStyle.Companion {
-    method public androidx.constraintlayout.compose.FlowStyle getPacked();
-    method public androidx.constraintlayout.compose.FlowStyle getSpread();
-    method public androidx.constraintlayout.compose.FlowStyle getSpreadInside();
-    property public final androidx.constraintlayout.compose.FlowStyle Packed;
-    property public final androidx.constraintlayout.compose.FlowStyle Spread;
-    property public final androidx.constraintlayout.compose.FlowStyle SpreadInside;
-  }
-
-  @kotlin.jvm.JvmInline public final value class GridFlags {
-    ctor public GridFlags(optional boolean isPlaceLayoutsOnSpansFirst);
-    method public boolean isPlaceLayoutsOnSpansFirst();
-    property public final boolean isPlaceLayoutsOnSpansFirst;
-    field public static final androidx.constraintlayout.compose.GridFlags.Companion Companion;
-  }
-
-  public static final class GridFlags.Companion {
-    method public int getNone();
-    method public int getPlaceLayoutsOnSpansFirst();
-    property public final int None;
-    property public final int PlaceLayoutsOnSpansFirst;
-  }
-
-  @androidx.compose.runtime.Immutable public final class HorizontalAlign {
-    field public static final androidx.constraintlayout.compose.HorizontalAlign.Companion Companion;
-  }
-
-  public static final class HorizontalAlign.Companion {
-    method public androidx.constraintlayout.compose.HorizontalAlign getCenter();
-    method public androidx.constraintlayout.compose.HorizontalAlign getEnd();
-    method public androidx.constraintlayout.compose.HorizontalAlign getStart();
-    property public final androidx.constraintlayout.compose.HorizontalAlign Center;
-    property public final androidx.constraintlayout.compose.HorizontalAlign End;
-    property public final androidx.constraintlayout.compose.HorizontalAlign Start;
-  }
-
-  @kotlin.jvm.JvmDefaultWithCompatibility public interface HorizontalAnchorable {
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor anchor, optional float margin, optional float goneMargin);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor anchor, optional float margin, optional float goneMargin);
-  }
-
-  @androidx.compose.runtime.Stable public final class HorizontalChainReference extends androidx.constraintlayout.compose.LayoutReference {
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteRight();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getEnd();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getStart();
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteLeft;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteRight;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start;
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public final class HorizontalChainScope {
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteRight();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getStart();
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteLeft;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteRight;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable end;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable start;
-  }
-
-  public final class InvalidationStrategy {
-    ctor public InvalidationStrategy(optional kotlin.jvm.functions.Function3<? super androidx.constraintlayout.compose.InvalidationStrategySpecification,? super androidx.compose.ui.unit.Constraints,? super androidx.compose.ui.unit.Constraints,java.lang.Boolean>? onIncomingConstraints, kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange);
-    method public kotlin.jvm.functions.Function3<androidx.constraintlayout.compose.InvalidationStrategySpecification,androidx.compose.ui.unit.Constraints,androidx.compose.ui.unit.Constraints,java.lang.Boolean>? getOnIncomingConstraints();
-    method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnObservedStateChange();
-    property public final kotlin.jvm.functions.Function3<androidx.constraintlayout.compose.InvalidationStrategySpecification,androidx.compose.ui.unit.Constraints,androidx.compose.ui.unit.Constraints,java.lang.Boolean>? onIncomingConstraints;
-    property public final kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange;
-    field public static final androidx.constraintlayout.compose.InvalidationStrategy.Companion Companion;
-  }
-
-  public static final class InvalidationStrategy.Companion {
-    method public androidx.constraintlayout.compose.InvalidationStrategy getDefaultInvalidationStrategy();
-    property public final androidx.constraintlayout.compose.InvalidationStrategy DefaultInvalidationStrategy;
-  }
-
-  public final class InvalidationStrategySpecification {
-    method public boolean shouldInvalidateOnFixedHeight(long oldConstraints, long newConstraints, int skipCount, int threshold);
-    method public boolean shouldInvalidateOnFixedWidth(long oldConstraints, long newConstraints, int skipCount, int threshold);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyAttributeScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
-    method public float getAlpha();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public void setAlpha(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    property public final float alpha;
-    property public final float rotationX;
-    property public final float rotationY;
-    property public final float rotationZ;
-    property public final float scaleX;
-    property public final float scaleY;
-    property public final float translationX;
-    property public final float translationY;
-    property public final float translationZ;
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyAttributesScope extends androidx.constraintlayout.compose.BaseKeyFramesScope {
-    method public void frame(@IntRange(from=0L, to=100L) int frame, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyAttributeScope,kotlin.Unit> keyFrameContent);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyCycleScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
-    method public float getAlpha();
-    method public float getOffset();
-    method public float getPeriod();
-    method public float getPhase();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public void setAlpha(float);
-    method public void setOffset(float);
-    method public void setPeriod(float);
-    method public void setPhase(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    property public final float alpha;
-    property public final float offset;
-    property public final float period;
-    property public final float phase;
-    property public final float rotationX;
-    property public final float rotationY;
-    property public final float rotationZ;
-    property public final float scaleX;
-    property public final float scaleY;
-    property public final float translationX;
-    property public final float translationY;
-    property public final float translationZ;
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyCyclesScope extends androidx.constraintlayout.compose.BaseKeyFramesScope {
-    method public void frame(@IntRange(from=0L, to=100L) int frame, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyCycleScope,kotlin.Unit> keyFrameContent);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyPositionScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
-    method public androidx.constraintlayout.compose.CurveFit? getCurveFit();
-    method public float getPercentHeight();
-    method public float getPercentWidth();
-    method public float getPercentX();
-    method public float getPercentY();
-    method public void setCurveFit(androidx.constraintlayout.compose.CurveFit?);
-    method public void setPercentHeight(float);
-    method public void setPercentWidth(float);
-    method public void setPercentX(float);
-    method public void setPercentY(float);
-    property public final androidx.constraintlayout.compose.CurveFit? curveFit;
-    property public final float percentHeight;
-    property public final float percentWidth;
-    property public final float percentX;
-    property public final float percentY;
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyPositionsScope extends androidx.constraintlayout.compose.BaseKeyFramesScope {
-    method public void frame(@IntRange(from=0L, to=100L) int frame, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyPositionScope,kotlin.Unit> keyFrameContent);
-    method public androidx.constraintlayout.compose.RelativePosition getType();
-    method public void setType(androidx.constraintlayout.compose.RelativePosition);
-    property public final androidx.constraintlayout.compose.RelativePosition type;
-  }
-
-  public enum LayoutInfoFlags {
-    enum_constant public static final androidx.constraintlayout.compose.LayoutInfoFlags BOUNDS;
-    enum_constant public static final androidx.constraintlayout.compose.LayoutInfoFlags NONE;
-  }
-
-  public interface LayoutInformationReceiver {
-    method public androidx.constraintlayout.compose.MotionLayoutDebugFlags getForcedDrawDebug();
-    method public int getForcedHeight();
-    method public float getForcedProgress();
-    method public int getForcedWidth();
-    method public androidx.constraintlayout.compose.LayoutInfoFlags getLayoutInformationMode();
-    method public void onNewProgress(float progress);
-    method public void resetForcedProgress();
-    method public void setLayoutInformation(String information);
-    method public void setUpdateFlag(androidx.compose.runtime.MutableState<java.lang.Long> needsUpdate);
-  }
-
-  @androidx.compose.runtime.Stable public abstract class LayoutReference {
-  }
-
-  public final class MotionCarouselKt {
-    method @androidx.compose.runtime.Composable public static void ItemHolder(int i, String slotPrefix, boolean showSlot, kotlin.jvm.functions.Function0<kotlin.Unit> function);
-    method @androidx.compose.runtime.Composable public static void MotionCarousel(androidx.constraintlayout.compose.MotionScene motionScene, int initialSlotIndex, int numSlots, optional String backwardTransition, optional String forwardTransition, optional String slotPrefix, optional boolean showSlots, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionCarouselScope,kotlin.Unit> content);
-    method public static inline <T> void items(androidx.constraintlayout.compose.MotionCarouselScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsWithProperties(androidx.constraintlayout.compose.MotionCarouselScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super T,? super androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties>,kotlin.Unit> itemContent);
-  }
-
-  public interface MotionCarouselScope {
-    method public void items(int count, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> itemContent);
-    method public void itemsWithProperties(int count, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties>,kotlin.Unit> itemContent);
-  }
-
-  public interface MotionItemsProvider {
-    method public int count();
-    method public kotlin.jvm.functions.Function0<kotlin.Unit> getContent(int index);
-    method public kotlin.jvm.functions.Function0<kotlin.Unit> getContent(int index, androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties> properties);
-    method public boolean hasItemsWithProperties();
-  }
-
-  public enum MotionLayoutDebugFlags {
-    enum_constant public static final androidx.constraintlayout.compose.MotionLayoutDebugFlags NONE;
-    enum_constant public static final androidx.constraintlayout.compose.MotionLayoutDebugFlags SHOW_ALL;
-    enum_constant public static final androidx.constraintlayout.compose.MotionLayoutDebugFlags UNKNOWN;
-  }
-
-  @Deprecated public enum MotionLayoutFlag {
-    enum_constant @Deprecated public static final androidx.constraintlayout.compose.MotionLayoutFlag Default;
-    enum_constant @Deprecated public static final androidx.constraintlayout.compose.MotionLayoutFlag FullMeasure;
-  }
-
-  public final class MotionLayoutKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, float progress, optional androidx.compose.ui.Modifier modifier, optional String transitionName, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class MotionLayoutScope {
-    method public long customColor(String id, String name);
-    method public float customDistance(String id, String name);
-    method public float customFloat(String id, String name);
-    method public long customFontSize(String id, String name);
-    method public int customInt(String id, String name);
-    method public androidx.constraintlayout.compose.MotionLayoutScope.CustomProperties customProperties(String id);
-    method @Deprecated public long motionColor(String id, String name);
-    method @Deprecated public float motionDistance(String id, String name);
-    method @Deprecated public float motionFloat(String id, String name);
-    method @Deprecated public long motionFontSize(String id, String name);
-    method @Deprecated public int motionInt(String id, String name);
-    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties> motionProperties(String id);
-    method @Deprecated public androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties motionProperties(String id, String tag);
-    method public androidx.compose.ui.Modifier onStartEndBoundsChanged(androidx.compose.ui.Modifier, Object layoutId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.geometry.Rect,kotlin.Unit> onBoundsChanged);
-  }
-
-  public final class MotionLayoutScope.CustomProperties {
-    method public long color(String name);
-    method public float distance(String name);
-    method public float float(String name);
-    method public long fontSize(String name);
-    method public int int(String name);
-  }
-
-  public final class MotionLayoutScope.MotionProperties {
-    method public long color(String name);
-    method public float distance(String name);
-    method public float float(String name);
-    method public long fontSize(String name);
-    method public String id();
-    method public int int(String name);
-    method public String? tag();
-  }
-
-  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.constraintlayout.compose.ExperimentalMotionApi public interface MotionScene extends androidx.constraintlayout.core.state.CoreMotionScene {
-    method public androidx.constraintlayout.compose.ConstraintSet? getConstraintSetInstance(String name);
-    method public androidx.constraintlayout.compose.Transition? getTransitionInstance(String name);
-  }
-
-  public final class MotionSceneKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.MotionScene MotionScene(@org.intellij.lang.annotations.Language("json5") String content);
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class MotionSceneScope {
-    method public androidx.constraintlayout.compose.ConstraintSetRef addConstraintSet(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional String? name);
-    method public void addTransition(androidx.constraintlayout.compose.Transition transition, optional String? name);
-    method public androidx.constraintlayout.compose.ConstraintSetRef constraintSet(optional String? name, optional androidx.constraintlayout.compose.ConstraintSetRef? extendConstraintSet, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> constraintSetContent);
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRefFor(Object id);
-    method public androidx.constraintlayout.compose.MotionSceneScope.ConstrainedLayoutReferences createRefsFor(java.lang.Object... ids);
-    method public void customColor(androidx.constraintlayout.compose.ConstrainScope, String name, long value);
-    method public void customColor(androidx.constraintlayout.compose.KeyAttributeScope, String name, long value);
-    method public void customDistance(androidx.constraintlayout.compose.ConstrainScope, String name, float value);
-    method public void customDistance(androidx.constraintlayout.compose.KeyAttributeScope, String name, float value);
-    method public void customFloat(androidx.constraintlayout.compose.ConstrainScope, String name, float value);
-    method public void customFloat(androidx.constraintlayout.compose.KeyAttributeScope, String name, float value);
-    method public void customFontSize(androidx.constraintlayout.compose.ConstrainScope, String name, long value);
-    method public void customFontSize(androidx.constraintlayout.compose.KeyAttributeScope, String name, long value);
-    method public void customInt(androidx.constraintlayout.compose.ConstrainScope, String name, int value);
-    method public void customInt(androidx.constraintlayout.compose.KeyAttributeScope, String name, int value);
-    method public void defaultTransition(androidx.constraintlayout.compose.ConstraintSetRef from, androidx.constraintlayout.compose.ConstraintSetRef to, optional kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.TransitionScope,kotlin.Unit> transitionContent);
-    method public float getStaggeredWeight(androidx.constraintlayout.compose.ConstrainScope);
-    method public void setStaggeredWeight(androidx.constraintlayout.compose.ConstrainScope, float);
-    method public void transition(androidx.constraintlayout.compose.ConstraintSetRef from, androidx.constraintlayout.compose.ConstraintSetRef to, optional String? name, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.TransitionScope,kotlin.Unit> transitionContent);
-  }
-
-  public final class MotionSceneScope.ConstrainedLayoutReferences {
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component1();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component10();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component11();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component12();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component13();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component14();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component15();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component16();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component2();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component3();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component4();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component5();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component6();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component7();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component8();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component9();
-  }
-
-  public final class MotionSceneScopeKt {
-    method @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.MotionScene MotionScene(kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionSceneScope,kotlin.Unit> motionSceneContent);
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class OnSwipe {
-    ctor public OnSwipe(androidx.constraintlayout.compose.ConstrainedLayoutReference anchor, androidx.constraintlayout.compose.SwipeSide side, androidx.constraintlayout.compose.SwipeDirection direction, optional float dragScale, optional float dragThreshold, optional androidx.constraintlayout.compose.ConstrainedLayoutReference? dragAround, optional androidx.constraintlayout.compose.ConstrainedLayoutReference? limitBoundsTo, optional androidx.constraintlayout.compose.SwipeTouchUp onTouchUp, optional androidx.constraintlayout.compose.SwipeMode mode);
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getAnchor();
-    method public androidx.constraintlayout.compose.SwipeDirection getDirection();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference? getDragAround();
-    method public float getDragScale();
-    method public float getDragThreshold();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference? getLimitBoundsTo();
-    method public androidx.constraintlayout.compose.SwipeMode getMode();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getOnTouchUp();
-    method public androidx.constraintlayout.compose.SwipeSide getSide();
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference anchor;
-    property public final androidx.constraintlayout.compose.SwipeDirection direction;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference? dragAround;
-    property public final float dragScale;
-    property public final float dragThreshold;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference? limitBoundsTo;
-    property public final androidx.constraintlayout.compose.SwipeMode mode;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp onTouchUp;
-    property public final androidx.constraintlayout.compose.SwipeSide side;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class RelativePosition {
-    method public String getName();
-    property public String name;
-    field public static final androidx.constraintlayout.compose.RelativePosition.Companion Companion;
-  }
-
-  public static final class RelativePosition.Companion {
-    method public androidx.constraintlayout.compose.RelativePosition getDelta();
-    method public androidx.constraintlayout.compose.RelativePosition getParent();
-    method public androidx.constraintlayout.compose.RelativePosition getPath();
-    property public final androidx.constraintlayout.compose.RelativePosition Delta;
-    property public final androidx.constraintlayout.compose.RelativePosition Parent;
-    property public final androidx.constraintlayout.compose.RelativePosition Path;
-  }
-
-  @kotlin.jvm.JvmInline public final value class Skip {
-    ctor public Skip(@IntRange(from=0L) int position, @IntRange(from=1L) int size);
-    ctor public Skip(@IntRange(from=0L) int position, @IntRange(from=1L) int rows, @IntRange(from=1L) int columns);
-    method public String getDescription();
-    property public final String description;
-  }
-
-  @kotlin.jvm.JvmInline public final value class Span {
-    ctor public Span(@IntRange(from=0L) int position, @IntRange(from=1L) int size);
-    ctor public Span(@IntRange(from=0L) int position, @IntRange(from=1L) int rows, @IntRange(from=1L) int columns);
-    ctor public Span(String description);
-    method public String getDescription();
-    property public final String description;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SpringBoundary {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SpringBoundary.Companion Companion;
-  }
-
-  public static final class SpringBoundary.Companion {
-    method public androidx.constraintlayout.compose.SpringBoundary getBounceBoth();
-    method public androidx.constraintlayout.compose.SpringBoundary getBounceEnd();
-    method public androidx.constraintlayout.compose.SpringBoundary getBounceStart();
-    method public androidx.constraintlayout.compose.SpringBoundary getOvershoot();
-    property public final androidx.constraintlayout.compose.SpringBoundary BounceBoth;
-    property public final androidx.constraintlayout.compose.SpringBoundary BounceEnd;
-    property public final androidx.constraintlayout.compose.SpringBoundary BounceStart;
-    property public final androidx.constraintlayout.compose.SpringBoundary Overshoot;
-  }
-
-  public final class State extends androidx.constraintlayout.core.state.State {
-    ctor public State(androidx.compose.ui.unit.Density density);
-    method public androidx.compose.ui.unit.Density getDensity();
-    method @Deprecated public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public long getRootIncomingConstraints();
-    method @Deprecated public void setLayoutDirection(androidx.compose.ui.unit.LayoutDirection);
-    method public void setRootIncomingConstraints(long);
-    property public final androidx.compose.ui.unit.Density density;
-    property @Deprecated public final androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public final long rootIncomingConstraints;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeDirection {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeDirection.Companion Companion;
-  }
-
-  public static final class SwipeDirection.Companion {
-    method public androidx.constraintlayout.compose.SwipeDirection getClockwise();
-    method public androidx.constraintlayout.compose.SwipeDirection getCounterclockwise();
-    method public androidx.constraintlayout.compose.SwipeDirection getDown();
-    method public androidx.constraintlayout.compose.SwipeDirection getEnd();
-    method public androidx.constraintlayout.compose.SwipeDirection getLeft();
-    method public androidx.constraintlayout.compose.SwipeDirection getRight();
-    method public androidx.constraintlayout.compose.SwipeDirection getStart();
-    method public androidx.constraintlayout.compose.SwipeDirection getUp();
-    property public final androidx.constraintlayout.compose.SwipeDirection Clockwise;
-    property public final androidx.constraintlayout.compose.SwipeDirection Counterclockwise;
-    property public final androidx.constraintlayout.compose.SwipeDirection Down;
-    property public final androidx.constraintlayout.compose.SwipeDirection End;
-    property public final androidx.constraintlayout.compose.SwipeDirection Left;
-    property public final androidx.constraintlayout.compose.SwipeDirection Right;
-    property public final androidx.constraintlayout.compose.SwipeDirection Start;
-    property public final androidx.constraintlayout.compose.SwipeDirection Up;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeMode {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeMode.Companion Companion;
-  }
-
-  public static final class SwipeMode.Companion {
-    method public androidx.constraintlayout.compose.SwipeMode getSpring();
-    method public androidx.constraintlayout.compose.SwipeMode getVelocity();
-    method public androidx.constraintlayout.compose.SwipeMode spring(optional float mass, optional float stiffness, optional float damping, optional float threshold, optional androidx.constraintlayout.compose.SpringBoundary boundary);
-    method public androidx.constraintlayout.compose.SwipeMode velocity(optional float maxVelocity, optional float maxAcceleration);
-    property public final androidx.constraintlayout.compose.SwipeMode Spring;
-    property public final androidx.constraintlayout.compose.SwipeMode Velocity;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeSide {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeSide.Companion Companion;
-  }
-
-  public static final class SwipeSide.Companion {
-    method public androidx.constraintlayout.compose.SwipeSide getBottom();
-    method public androidx.constraintlayout.compose.SwipeSide getEnd();
-    method public androidx.constraintlayout.compose.SwipeSide getLeft();
-    method public androidx.constraintlayout.compose.SwipeSide getMiddle();
-    method public androidx.constraintlayout.compose.SwipeSide getRight();
-    method public androidx.constraintlayout.compose.SwipeSide getStart();
-    method public androidx.constraintlayout.compose.SwipeSide getTop();
-    property public final androidx.constraintlayout.compose.SwipeSide Bottom;
-    property public final androidx.constraintlayout.compose.SwipeSide End;
-    property public final androidx.constraintlayout.compose.SwipeSide Left;
-    property public final androidx.constraintlayout.compose.SwipeSide Middle;
-    property public final androidx.constraintlayout.compose.SwipeSide Right;
-    property public final androidx.constraintlayout.compose.SwipeSide Start;
-    property public final androidx.constraintlayout.compose.SwipeSide Top;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeTouchUp {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeTouchUp.Companion Companion;
-  }
-
-  public static final class SwipeTouchUp.Companion {
-    method public androidx.constraintlayout.compose.SwipeTouchUp getAutoComplete();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getDecelerate();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getNeverCompleteEnd();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getNeverCompleteStart();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getStop();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getToEnd();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getToStart();
-    property public final androidx.constraintlayout.compose.SwipeTouchUp AutoComplete;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp Decelerate;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp NeverCompleteEnd;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp NeverCompleteStart;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp Stop;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp ToEnd;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp ToStart;
-  }
-
-  public final class ToolingUtilsKt {
-    method public static androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.constraintlayout.compose.DesignInfoProvider> getDesignInfoDataKey();
-    property public static final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.constraintlayout.compose.DesignInfoProvider> DesignInfoDataKey;
-  }
-
-  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.constraintlayout.compose.ExperimentalMotionApi public interface Transition {
-    method public String getEndConstraintSetId();
-    method public String getStartConstraintSetId();
-  }
-
-  public final class TransitionKt {
-    method @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.Transition Transition(@org.intellij.lang.annotations.Language("json5") String content);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class TransitionScope {
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRefFor(Object id);
-    method public float getMaxStaggerDelay();
-    method public androidx.constraintlayout.compose.Arc getMotionArc();
-    method public androidx.constraintlayout.compose.OnSwipe? getOnSwipe();
-    method public void keyAttributes(androidx.constraintlayout.compose.ConstrainedLayoutReference[] targets, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyAttributesScope,kotlin.Unit> keyAttributesContent);
-    method public void keyCycles(androidx.constraintlayout.compose.ConstrainedLayoutReference[] targets, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyCyclesScope,kotlin.Unit> keyCyclesContent);
-    method public void keyPositions(androidx.constraintlayout.compose.ConstrainedLayoutReference[] targets, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyPositionsScope,kotlin.Unit> keyPositionsContent);
-    method public void setMaxStaggerDelay(float);
-    method public void setMotionArc(androidx.constraintlayout.compose.Arc);
-    method public void setOnSwipe(androidx.constraintlayout.compose.OnSwipe?);
-    property public final float maxStaggerDelay;
-    property public final androidx.constraintlayout.compose.Arc motionArc;
-    property public final androidx.constraintlayout.compose.OnSwipe? onSwipe;
-  }
-
-  public final class TransitionScopeKt {
-    method @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.Transition Transition(optional String from, optional String to, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.TransitionScope,kotlin.Unit> content);
-  }
-
-  @androidx.compose.runtime.Immutable public final class VerticalAlign {
-    field public static final androidx.constraintlayout.compose.VerticalAlign.Companion Companion;
-  }
-
-  public static final class VerticalAlign.Companion {
-    method public androidx.constraintlayout.compose.VerticalAlign getBaseline();
-    method public androidx.constraintlayout.compose.VerticalAlign getBottom();
-    method public androidx.constraintlayout.compose.VerticalAlign getCenter();
-    method public androidx.constraintlayout.compose.VerticalAlign getTop();
-    property public final androidx.constraintlayout.compose.VerticalAlign Baseline;
-    property public final androidx.constraintlayout.compose.VerticalAlign Bottom;
-    property public final androidx.constraintlayout.compose.VerticalAlign Center;
-    property public final androidx.constraintlayout.compose.VerticalAlign Top;
-  }
-
-  @kotlin.jvm.JvmDefaultWithCompatibility public interface VerticalAnchorable {
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor anchor, optional float margin, optional float goneMargin);
-  }
-
-  @androidx.compose.runtime.Stable public final class VerticalChainReference extends androidx.constraintlayout.compose.LayoutReference {
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getBottom();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getTop();
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top;
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public final class VerticalChainScope {
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getTop();
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable top;
-  }
-
-  @androidx.compose.runtime.Immutable public final class Visibility {
-    field public static final androidx.constraintlayout.compose.Visibility.Companion Companion;
-  }
-
-  public static final class Visibility.Companion {
-    method public androidx.constraintlayout.compose.Visibility getGone();
-    method public androidx.constraintlayout.compose.Visibility getInvisible();
-    method public androidx.constraintlayout.compose.Visibility getVisible();
-    property public final androidx.constraintlayout.compose.Visibility Gone;
-    property public final androidx.constraintlayout.compose.Visibility Invisible;
-    property public final androidx.constraintlayout.compose.Visibility Visible;
-  }
-
-  @androidx.compose.runtime.Immutable public final class Wrap {
-    field public static final androidx.constraintlayout.compose.Wrap.Companion Companion;
-  }
-
-  public static final class Wrap.Companion {
-    method public androidx.constraintlayout.compose.Wrap getAligned();
-    method public androidx.constraintlayout.compose.Wrap getChain();
-    method public androidx.constraintlayout.compose.Wrap getNone();
-    property public final androidx.constraintlayout.compose.Wrap Aligned;
-    property public final androidx.constraintlayout.compose.Wrap Chain;
-    property public final androidx.constraintlayout.compose.Wrap None;
-  }
-
-}
-
diff --git a/constraintlayout/constraintlayout-compose/api/current.txt b/constraintlayout/constraintlayout-compose/api/current.txt
index 0375873..f9829bc 100644
--- a/constraintlayout/constraintlayout-compose/api/current.txt
+++ b/constraintlayout/constraintlayout-compose/api/current.txt
@@ -425,14 +425,14 @@
     property public final androidx.constraintlayout.compose.FlowStyle SpreadInside;
   }
 
-  @kotlin.jvm.JvmInline public final value class GridFlags {
-    ctor public GridFlags(optional boolean isPlaceLayoutsOnSpansFirst);
+  @kotlin.jvm.JvmInline public final value class GridFlag {
     method public boolean isPlaceLayoutsOnSpansFirst();
+    method public infix int or(int other);
     property public final boolean isPlaceLayoutsOnSpansFirst;
-    field public static final androidx.constraintlayout.compose.GridFlags.Companion Companion;
+    field public static final androidx.constraintlayout.compose.GridFlag.Companion Companion;
   }
 
-  public static final class GridFlags.Companion {
+  public static final class GridFlag.Companion {
     method public int getNone();
     method public int getPlaceLayoutsOnSpansFirst();
     property public final int None;
diff --git a/constraintlayout/constraintlayout-compose/api/restricted_1.1.0-beta01.txt b/constraintlayout/constraintlayout-compose/api/restricted_1.1.0-beta01.txt
deleted file mode 100644
index 60bc75d..0000000
--- a/constraintlayout/constraintlayout-compose/api/restricted_1.1.0-beta01.txt
+++ /dev/null
@@ -1,1103 +0,0 @@
-// Signature format: 4.0
-package androidx.constraintlayout.compose {
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class Arc {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.Arc.Companion Companion;
-  }
-
-  public static final class Arc.Companion {
-    method public androidx.constraintlayout.compose.Arc getAbove();
-    method public androidx.constraintlayout.compose.Arc getBelow();
-    method public androidx.constraintlayout.compose.Arc getFlip();
-    method public androidx.constraintlayout.compose.Arc getNone();
-    method public androidx.constraintlayout.compose.Arc getStartHorizontal();
-    method public androidx.constraintlayout.compose.Arc getStartVertical();
-    property public final androidx.constraintlayout.compose.Arc Above;
-    property public final androidx.constraintlayout.compose.Arc Below;
-    property public final androidx.constraintlayout.compose.Arc Flip;
-    property public final androidx.constraintlayout.compose.Arc None;
-    property public final androidx.constraintlayout.compose.Arc StartHorizontal;
-    property public final androidx.constraintlayout.compose.Arc StartVertical;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public abstract sealed class BaseKeyFrameScope {
-    method protected final <E extends androidx.constraintlayout.compose.NamedPropertyOrValue> kotlin.properties.ObservableProperty<E> addNameOnPropertyChange(E initialValue, optional String? nameOverride);
-    method protected final <T> kotlin.properties.ObservableProperty<T> addOnPropertyChange(T initialValue, optional String? nameOverride);
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public abstract sealed class BaseKeyFramesScope {
-    method public final androidx.constraintlayout.compose.Easing getEasing();
-    method public final void setEasing(androidx.constraintlayout.compose.Easing);
-    property public final androidx.constraintlayout.compose.Easing easing;
-  }
-
-  @kotlin.jvm.JvmDefaultWithCompatibility public interface BaselineAnchorable {
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor anchor, optional float margin, optional float goneMargin);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor anchor, optional float margin, optional float goneMargin);
-  }
-
-  @androidx.compose.runtime.Immutable public final class ChainStyle {
-    field public static final androidx.constraintlayout.compose.ChainStyle.Companion Companion;
-  }
-
-  public static final class ChainStyle.Companion {
-    method @androidx.compose.runtime.Stable public androidx.constraintlayout.compose.ChainStyle Packed(float bias);
-    method public androidx.constraintlayout.compose.ChainStyle getPacked();
-    method public androidx.constraintlayout.compose.ChainStyle getSpread();
-    method public androidx.constraintlayout.compose.ChainStyle getSpreadInside();
-    property public final androidx.constraintlayout.compose.ChainStyle Packed;
-    property public final androidx.constraintlayout.compose.ChainStyle Spread;
-    property public final androidx.constraintlayout.compose.ChainStyle SpreadInside;
-  }
-
-  @kotlin.PublishedApi internal enum CompositionSource {
-    enum_constant public static final androidx.constraintlayout.compose.CompositionSource Content;
-    enum_constant public static final androidx.constraintlayout.compose.CompositionSource Unknown;
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public final class ConstrainScope {
-    method public androidx.constraintlayout.compose.Dimension asDimension(float);
-    method public void centerAround(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor anchor);
-    method public void centerAround(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor anchor);
-    method public void centerHorizontallyTo(androidx.constraintlayout.compose.ConstrainedLayoutReference other, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void centerTo(androidx.constraintlayout.compose.ConstrainedLayoutReference other);
-    method public void centerVerticallyTo(androidx.constraintlayout.compose.ConstrainedLayoutReference other, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void circular(androidx.constraintlayout.compose.ConstrainedLayoutReference other, float angle, float distance);
-    method public void clearConstraints();
-    method public void clearHorizontal();
-    method public void clearVertical();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteRight();
-    method public float getAlpha();
-    method public androidx.constraintlayout.compose.BaselineAnchorable getBaseline();
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
-    method public androidx.constraintlayout.compose.Dimension getHeight();
-    method public float getHorizontalBias();
-    method public float getHorizontalChainWeight();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getStart();
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getTop();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public float getVerticalBias();
-    method public float getVerticalChainWeight();
-    method public androidx.constraintlayout.compose.Visibility getVisibility();
-    method public androidx.constraintlayout.compose.Dimension getWidth();
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional @FloatRange(from=0.0, to=1.0) float horizontalBias, optional @FloatRange(from=0.0, to=1.0) float verticalBias);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start, androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional @FloatRange(from=0.0, to=1.0) float bias);
-    method public void resetDimensions();
-    method public void resetTransforms();
-    method public void setAlpha(float);
-    method public void setHeight(androidx.constraintlayout.compose.Dimension);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalChainWeight(float);
-    method public void setPivotX(float);
-    method public void setPivotY(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    method public void setVerticalBias(float);
-    method public void setVerticalChainWeight(float);
-    method public void setVisibility(androidx.constraintlayout.compose.Visibility);
-    method public void setWidth(androidx.constraintlayout.compose.Dimension);
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteLeft;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteRight;
-    property public final float alpha;
-    property public final androidx.constraintlayout.compose.BaselineAnchorable baseline;
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable end;
-    property public final androidx.constraintlayout.compose.Dimension height;
-    property public final float horizontalBias;
-    property public final float horizontalChainWeight;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
-    property public final float pivotX;
-    property public final float pivotY;
-    property public final float rotationX;
-    property public final float rotationY;
-    property public final float rotationZ;
-    property public final float scaleX;
-    property public final float scaleY;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable start;
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable top;
-    property public final float translationX;
-    property public final float translationY;
-    property public final float translationZ;
-    property public final float verticalBias;
-    property public final float verticalChainWeight;
-    property public final androidx.constraintlayout.compose.Visibility visibility;
-    property public final androidx.constraintlayout.compose.Dimension width;
-  }
-
-  @androidx.compose.runtime.Stable public final class ConstrainedLayoutReference extends androidx.constraintlayout.compose.LayoutReference {
-    ctor public ConstrainedLayoutReference(Object id);
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteRight();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor getBaseline();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getBottom();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getEnd();
-    method public Object getId();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getStart();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getTop();
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteLeft;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteRight;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor baseline;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end;
-    property public Object id;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top;
-  }
-
-  public abstract class ConstraintLayoutBaseScope {
-    ctor public ConstraintLayoutBaseScope();
-    method public final void applyTo(androidx.constraintlayout.compose.State state);
-    method public final androidx.constraintlayout.compose.ConstrainScope constrain(androidx.constraintlayout.compose.ConstrainedLayoutReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstrainScope,kotlin.Unit> constrainBlock);
-    method public final void constrain(androidx.constraintlayout.compose.ConstrainedLayoutReference[] refs, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstrainScope,kotlin.Unit> constrainBlock);
-    method public final androidx.constraintlayout.compose.HorizontalChainScope constrain(androidx.constraintlayout.compose.HorizontalChainReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.HorizontalChainScope,kotlin.Unit> constrainBlock);
-    method public final androidx.constraintlayout.compose.VerticalChainScope constrain(androidx.constraintlayout.compose.VerticalChainReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.VerticalChainScope,kotlin.Unit> constrainBlock);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createAbsoluteLeftBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createAbsoluteRightBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createBottomBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference[] elements, optional float spacing, optional float[] weights);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createEndBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createFlow(androidx.constraintlayout.compose.LayoutReference?[] elements, optional boolean flowVertically, optional float verticalGap, optional float horizontalGap, optional int maxElement, optional float padding, optional androidx.constraintlayout.compose.Wrap wrapMode, optional androidx.constraintlayout.compose.VerticalAlign verticalAlign, optional androidx.constraintlayout.compose.HorizontalAlign horizontalAlign, optional float horizontalFlowBias, optional float verticalFlowBias, optional androidx.constraintlayout.compose.FlowStyle verticalStyle, optional androidx.constraintlayout.compose.FlowStyle horizontalStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createFlow(androidx.constraintlayout.compose.LayoutReference?[] elements, optional boolean flowVertically, optional float verticalGap, optional float horizontalGap, optional int maxElement, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.Wrap wrapMode, optional androidx.constraintlayout.compose.VerticalAlign verticalAlign, optional androidx.constraintlayout.compose.HorizontalAlign horizontalAlign, optional float horizontalFlowBias, optional float verticalFlowBias, optional androidx.constraintlayout.compose.FlowStyle verticalStyle, optional androidx.constraintlayout.compose.FlowStyle horizontalStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createFlow(androidx.constraintlayout.compose.LayoutReference?[] elements, optional boolean flowVertically, optional float verticalGap, optional float horizontalGap, optional int maxElement, optional float paddingLeft, optional float paddingTop, optional float paddingRight, optional float paddingBottom, optional androidx.constraintlayout.compose.Wrap wrapMode, optional androidx.constraintlayout.compose.VerticalAlign verticalAlign, optional androidx.constraintlayout.compose.HorizontalAlign horizontalAlign, optional float horizontalFlowBias, optional float verticalFlowBias, optional androidx.constraintlayout.compose.FlowStyle verticalStyle, optional androidx.constraintlayout.compose.FlowStyle horizontalStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference[] elements, @IntRange(from=1L) int rows, @IntRange(from=1L) int columns, optional boolean isHorizontalArrangement, optional float verticalSpacing, optional float horizontalSpacing, optional float[] rowWeights, optional float[] columnWeights, optional androidx.constraintlayout.compose.Skip[] skips, optional androidx.constraintlayout.compose.Span[] spans, optional int flags);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteLeft(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteLeft(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteRight(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromAbsoluteRight(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromBottom(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromBottom(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromEnd(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromEnd(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromStart(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createGuidelineFromStart(float fraction);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromTop(float offset);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createGuidelineFromTop(float fraction);
-    method public final androidx.constraintlayout.compose.HorizontalChainReference createHorizontalChain(androidx.constraintlayout.compose.LayoutReference[] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference[] elements, optional float spacing, optional float[] weights);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor createStartBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor createTopBarrier(androidx.constraintlayout.compose.LayoutReference[] elements, optional float margin);
-    method public final androidx.constraintlayout.compose.VerticalChainReference createVerticalChain(androidx.constraintlayout.compose.LayoutReference[] elements, optional androidx.constraintlayout.compose.ChainStyle chainStyle);
-    method @Deprecated protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> getTasks();
-    method public void reset();
-    method public final androidx.constraintlayout.compose.LayoutReference withChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float topMargin, optional float endMargin, optional float bottomMargin, optional float startGoneMargin, optional float topGoneMargin, optional float endGoneMargin, optional float bottomGoneMargin, optional float weight);
-    method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
-    method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
-    property @Deprecated protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
-    field @kotlin.PublishedApi internal final androidx.constraintlayout.core.parser.CLObject containerObject;
-    field @kotlin.PublishedApi internal int helpersHashCode;
-  }
-
-  @androidx.compose.runtime.Stable public static final class ConstraintLayoutBaseScope.BaselineAnchor {
-    method public androidx.constraintlayout.compose.LayoutReference component2();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor copy(Object id, androidx.constraintlayout.compose.LayoutReference reference);
-    method public androidx.constraintlayout.compose.LayoutReference getReference();
-    property public final androidx.constraintlayout.compose.LayoutReference reference;
-  }
-
-  @androidx.compose.runtime.Stable public static final class ConstraintLayoutBaseScope.HorizontalAnchor {
-    method public androidx.constraintlayout.compose.LayoutReference component3();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor copy(Object id, int index, androidx.constraintlayout.compose.LayoutReference reference);
-    method public androidx.constraintlayout.compose.LayoutReference getReference();
-    property public final androidx.constraintlayout.compose.LayoutReference reference;
-  }
-
-  @androidx.compose.runtime.Stable public static final class ConstraintLayoutBaseScope.VerticalAnchor {
-    method public androidx.constraintlayout.compose.LayoutReference component3();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor copy(Object id, int index, androidx.constraintlayout.compose.LayoutReference reference);
-    method public androidx.constraintlayout.compose.LayoutReference getReference();
-    property public final androidx.constraintlayout.compose.LayoutReference reference;
-  }
-
-  public final class ConstraintLayoutKt {
-    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? animateChangesSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
-    method @Deprecated @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? animateChangesSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @Deprecated @androidx.compose.runtime.Composable public static inline void ConstraintLayout(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, @org.intellij.lang.annotations.Language("json5") String jsonContent);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> description);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(@org.intellij.lang.annotations.Language("json5") String jsonContent);
-    method @androidx.compose.runtime.Composable public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(@org.intellij.lang.annotations.Language("json5") String content, optional @org.intellij.lang.annotations.Language("json5") String? overrideVariables);
-    method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> description);
-    method public static androidx.constraintlayout.compose.Dimension.MaxCoercible atLeast(androidx.constraintlayout.compose.Dimension.Coercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension atLeast(androidx.constraintlayout.compose.Dimension.MinCoercible, float dp);
-    method @Deprecated public static androidx.constraintlayout.compose.Dimension atLeastWrapContent(androidx.constraintlayout.compose.Dimension.MinCoercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension.MinCoercible atMost(androidx.constraintlayout.compose.Dimension.Coercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension atMost(androidx.constraintlayout.compose.Dimension.MaxCoercible, float dp);
-    method public static androidx.constraintlayout.compose.Dimension.MaxCoercible getAtLeastWrapContent(androidx.constraintlayout.compose.Dimension.Coercible);
-    method public static androidx.constraintlayout.compose.Dimension getAtLeastWrapContent(androidx.constraintlayout.compose.Dimension.MinCoercible);
-    method public static androidx.constraintlayout.compose.Dimension.MinCoercible getAtMostWrapContent(androidx.constraintlayout.compose.Dimension.Coercible);
-    method public static androidx.constraintlayout.compose.Dimension getAtMostWrapContent(androidx.constraintlayout.compose.Dimension.MaxCoercible);
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker public final class ConstraintLayoutScope extends androidx.constraintlayout.compose.ConstraintLayoutBaseScope {
-    ctor @kotlin.PublishedApi internal ConstraintLayoutScope();
-    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier constrainAs(androidx.compose.ui.Modifier, androidx.constraintlayout.compose.ConstrainedLayoutReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstrainScope,kotlin.Unit> constrainBlock);
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRef();
-    method @androidx.compose.runtime.Stable public androidx.constraintlayout.compose.ConstraintLayoutScope.ConstrainedLayoutReferences createRefs();
-    field @kotlin.PublishedApi internal boolean isAnimateChanges;
-  }
-
-  public final class ConstraintLayoutScope.ConstrainedLayoutReferences {
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component1();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component10();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component11();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component12();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component13();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component14();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component15();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component16();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component2();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component3();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component4();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component5();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component6();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component7();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component8();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component9();
-  }
-
-  public final class ConstraintLayoutTagKt {
-    method public static Object? getConstraintLayoutId(androidx.compose.ui.layout.Measurable);
-    method public static Object? getConstraintLayoutTag(androidx.compose.ui.layout.Measurable);
-    method public static androidx.compose.ui.Modifier layoutId(androidx.compose.ui.Modifier, String layoutId, optional String? tag);
-  }
-
-  public interface ConstraintLayoutTagParentData {
-    method public String getConstraintLayoutId();
-    method public String getConstraintLayoutTag();
-    property public abstract String constraintLayoutId;
-    property public abstract String constraintLayoutTag;
-  }
-
-  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmDefaultWithCompatibility public interface ConstraintSet {
-    method public void applyTo(androidx.constraintlayout.compose.State state, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
-    method public default void applyTo(androidx.constraintlayout.core.state.Transition transition, int type);
-    method public default boolean isDirty(java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
-    method public default androidx.constraintlayout.compose.ConstraintSet override(String name, float value);
-  }
-
-  @kotlin.PublishedApi internal final class ConstraintSetForInlineDsl implements androidx.constraintlayout.compose.ConstraintSet androidx.compose.runtime.RememberObserver {
-    ctor public ConstraintSetForInlineDsl(androidx.constraintlayout.compose.ConstraintLayoutScope scope);
-    method public void applyTo(androidx.constraintlayout.compose.State state, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
-    method public boolean getKnownDirty();
-    method public androidx.constraintlayout.compose.ConstraintLayoutScope getScope();
-    method public void onAbandoned();
-    method public void onForgotten();
-    method public void onRemembered();
-    method public void setKnownDirty(boolean);
-    property public final boolean knownDirty;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutScope scope;
-  }
-
-  public final class ConstraintSetRef {
-    method public androidx.constraintlayout.compose.ConstraintSetRef copy(String name);
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker public final class ConstraintSetScope extends androidx.constraintlayout.compose.ConstraintLayoutBaseScope {
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRefFor(Object id);
-    method public androidx.constraintlayout.compose.ConstraintSetScope.ConstrainedLayoutReferences createRefsFor(java.lang.Object... ids);
-  }
-
-  public final class ConstraintSetScope.ConstrainedLayoutReferences {
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component1();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component10();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component11();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component12();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component13();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component14();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component15();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component16();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component2();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component3();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component4();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component5();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component6();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component7();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component8();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component9();
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class CurveFit {
-    method public String getName();
-    property public String name;
-    field public static final androidx.constraintlayout.compose.CurveFit.Companion Companion;
-  }
-
-  public static final class CurveFit.Companion {
-    method public androidx.constraintlayout.compose.CurveFit getLinear();
-    method public androidx.constraintlayout.compose.CurveFit getSpline();
-    property public final androidx.constraintlayout.compose.CurveFit Linear;
-    property public final androidx.constraintlayout.compose.CurveFit Spline;
-  }
-
-  @kotlin.jvm.JvmInline public final value class DebugFlags {
-    ctor public DebugFlags(optional boolean showBounds, optional boolean showPaths, optional boolean showKeyPositions);
-    method public boolean getShowBounds();
-    method public boolean getShowKeyPositions();
-    method public boolean getShowPaths();
-    property public final boolean showBounds;
-    property public final boolean showKeyPositions;
-    property public final boolean showPaths;
-    field public static final androidx.constraintlayout.compose.DebugFlags.Companion Companion;
-  }
-
-  public static final class DebugFlags.Companion {
-    method public int getAll();
-    method public int getNone();
-    property public final int All;
-    property public final int None;
-  }
-
-  public final class DesignElements {
-    method public void define(String name, kotlin.jvm.functions.Function2<? super java.lang.String,? super java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit> function);
-    method public java.util.HashMap<java.lang.String,kotlin.jvm.functions.Function2<java.lang.String,java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit>> getMap();
-    method public void setMap(java.util.HashMap<java.lang.String,kotlin.jvm.functions.Function2<java.lang.String,java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit>>);
-    property public final java.util.HashMap<java.lang.String,kotlin.jvm.functions.Function2<java.lang.String,java.util.HashMap<java.lang.String,java.lang.String>,kotlin.Unit>> map;
-    field public static final androidx.constraintlayout.compose.DesignElements INSTANCE;
-  }
-
-  public interface DesignInfoProvider {
-    method public String getDesignInfo(int startX, int startY, String args);
-  }
-
-  public interface Dimension {
-    field public static final androidx.constraintlayout.compose.Dimension.Companion Companion;
-  }
-
-  public static interface Dimension.Coercible extends androidx.constraintlayout.compose.Dimension {
-  }
-
-  public static final class Dimension.Companion {
-    method public androidx.constraintlayout.compose.Dimension.Coercible getFillToConstraints();
-    method public androidx.constraintlayout.compose.Dimension getMatchParent();
-    method public androidx.constraintlayout.compose.Dimension.Coercible getPreferredWrapContent();
-    method public androidx.constraintlayout.compose.Dimension getWrapContent();
-    method public androidx.constraintlayout.compose.Dimension percent(float percent);
-    method public androidx.constraintlayout.compose.Dimension.MinCoercible preferredValue(float dp);
-    method public androidx.constraintlayout.compose.Dimension ratio(String ratio);
-    method public androidx.constraintlayout.compose.Dimension value(float dp);
-    property public final androidx.constraintlayout.compose.Dimension.Coercible fillToConstraints;
-    property public final androidx.constraintlayout.compose.Dimension matchParent;
-    property public final androidx.constraintlayout.compose.Dimension.Coercible preferredWrapContent;
-    property public final androidx.constraintlayout.compose.Dimension wrapContent;
-  }
-
-  public static interface Dimension.MaxCoercible extends androidx.constraintlayout.compose.Dimension {
-  }
-
-  public static interface Dimension.MinCoercible extends androidx.constraintlayout.compose.Dimension {
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class Easing {
-    method public String getName();
-    property public String name;
-    field public static final androidx.constraintlayout.compose.Easing.Companion Companion;
-  }
-
-  public static final class Easing.Companion {
-    method public androidx.constraintlayout.compose.Easing cubic(float x1, float y1, float x2, float y2);
-    method public androidx.constraintlayout.compose.Easing getAccelerate();
-    method public androidx.constraintlayout.compose.Easing getAnticipate();
-    method public androidx.constraintlayout.compose.Easing getDecelerate();
-    method public androidx.constraintlayout.compose.Easing getLinear();
-    method public androidx.constraintlayout.compose.Easing getOvershoot();
-    method public androidx.constraintlayout.compose.Easing getStandard();
-    property public final androidx.constraintlayout.compose.Easing Accelerate;
-    property public final androidx.constraintlayout.compose.Easing Anticipate;
-    property public final androidx.constraintlayout.compose.Easing Decelerate;
-    property public final androidx.constraintlayout.compose.Easing Linear;
-    property public final androidx.constraintlayout.compose.Easing Overshoot;
-    property public final androidx.constraintlayout.compose.Easing Standard;
-  }
-
-  @kotlin.PublishedApi internal abstract class EditableJSONLayout implements androidx.constraintlayout.compose.LayoutInformationReceiver {
-    ctor public EditableJSONLayout(@org.intellij.lang.annotations.Language("json5") String content);
-    method public final String getCurrentContent();
-    method public final String? getDebugName();
-    method public androidx.constraintlayout.compose.MotionLayoutDebugFlags getForcedDrawDebug();
-    method public int getForcedHeight();
-    method public int getForcedWidth();
-    method public final String getLayoutInformation();
-    method public androidx.constraintlayout.compose.LayoutInfoFlags getLayoutInformationMode();
-    method protected final void initialization();
-    method protected final void onDrawDebug(int debugMode);
-    method protected final void onLayoutInformation(int mode);
-    method protected void onNewContent(String content);
-    method public final void onNewDimensions(int width, int height);
-    method public final void setCurrentContent(String content);
-    method public final void setDebugName(String? name);
-    method public void setLayoutInformation(String information);
-    method public void setUpdateFlag(androidx.compose.runtime.MutableState<java.lang.Long> needsUpdate);
-    method protected final void signalUpdate();
-  }
-
-  @SuppressCompatibility @kotlin.RequiresOptIn(message="MotionLayout API is experimental and it is likely to change.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMotionApi {
-  }
-
-  @androidx.compose.runtime.Immutable public final class FlowStyle {
-    field public static final androidx.constraintlayout.compose.FlowStyle.Companion Companion;
-  }
-
-  public static final class FlowStyle.Companion {
-    method public androidx.constraintlayout.compose.FlowStyle getPacked();
-    method public androidx.constraintlayout.compose.FlowStyle getSpread();
-    method public androidx.constraintlayout.compose.FlowStyle getSpreadInside();
-    property public final androidx.constraintlayout.compose.FlowStyle Packed;
-    property public final androidx.constraintlayout.compose.FlowStyle Spread;
-    property public final androidx.constraintlayout.compose.FlowStyle SpreadInside;
-  }
-
-  @kotlin.jvm.JvmInline public final value class GridFlags {
-    ctor public GridFlags(optional boolean isPlaceLayoutsOnSpansFirst);
-    method public boolean isPlaceLayoutsOnSpansFirst();
-    property public final boolean isPlaceLayoutsOnSpansFirst;
-    field public static final androidx.constraintlayout.compose.GridFlags.Companion Companion;
-  }
-
-  public static final class GridFlags.Companion {
-    method public int getNone();
-    method public int getPlaceLayoutsOnSpansFirst();
-    property public final int None;
-    property public final int PlaceLayoutsOnSpansFirst;
-  }
-
-  @androidx.compose.runtime.Immutable public final class HorizontalAlign {
-    field public static final androidx.constraintlayout.compose.HorizontalAlign.Companion Companion;
-  }
-
-  public static final class HorizontalAlign.Companion {
-    method public androidx.constraintlayout.compose.HorizontalAlign getCenter();
-    method public androidx.constraintlayout.compose.HorizontalAlign getEnd();
-    method public androidx.constraintlayout.compose.HorizontalAlign getStart();
-    property public final androidx.constraintlayout.compose.HorizontalAlign Center;
-    property public final androidx.constraintlayout.compose.HorizontalAlign End;
-    property public final androidx.constraintlayout.compose.HorizontalAlign Start;
-  }
-
-  @kotlin.jvm.JvmDefaultWithCompatibility public interface HorizontalAnchorable {
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.BaselineAnchor anchor, optional float margin, optional float goneMargin);
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor anchor, optional float margin, optional float goneMargin);
-  }
-
-  @androidx.compose.runtime.Stable public final class HorizontalChainReference extends androidx.constraintlayout.compose.LayoutReference {
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getAbsoluteRight();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getEnd();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor getStart();
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteLeft;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor absoluteRight;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor end;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor start;
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public final class HorizontalChainScope {
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteLeft();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getAbsoluteRight();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getEnd();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
-    method public androidx.constraintlayout.compose.VerticalAnchorable getStart();
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteLeft;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable absoluteRight;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable end;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
-    property public final androidx.constraintlayout.compose.VerticalAnchorable start;
-  }
-
-  public final class InvalidationStrategy {
-    ctor public InvalidationStrategy(optional kotlin.jvm.functions.Function3<? super androidx.constraintlayout.compose.InvalidationStrategySpecification,? super androidx.compose.ui.unit.Constraints,? super androidx.compose.ui.unit.Constraints,java.lang.Boolean>? onIncomingConstraints, kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange);
-    method public kotlin.jvm.functions.Function3<androidx.constraintlayout.compose.InvalidationStrategySpecification,androidx.compose.ui.unit.Constraints,androidx.compose.ui.unit.Constraints,java.lang.Boolean>? getOnIncomingConstraints();
-    method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnObservedStateChange();
-    property public final kotlin.jvm.functions.Function3<androidx.constraintlayout.compose.InvalidationStrategySpecification,androidx.compose.ui.unit.Constraints,androidx.compose.ui.unit.Constraints,java.lang.Boolean>? onIncomingConstraints;
-    property public final kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange;
-    field public static final androidx.constraintlayout.compose.InvalidationStrategy.Companion Companion;
-  }
-
-  public static final class InvalidationStrategy.Companion {
-    method public androidx.constraintlayout.compose.InvalidationStrategy getDefaultInvalidationStrategy();
-    property public final androidx.constraintlayout.compose.InvalidationStrategy DefaultInvalidationStrategy;
-  }
-
-  public final class InvalidationStrategySpecification {
-    method public boolean shouldInvalidateOnFixedHeight(long oldConstraints, long newConstraints, int skipCount, int threshold);
-    method public boolean shouldInvalidateOnFixedWidth(long oldConstraints, long newConstraints, int skipCount, int threshold);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyAttributeScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
-    method public float getAlpha();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public void setAlpha(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    property public final float alpha;
-    property public final float rotationX;
-    property public final float rotationY;
-    property public final float rotationZ;
-    property public final float scaleX;
-    property public final float scaleY;
-    property public final float translationX;
-    property public final float translationY;
-    property public final float translationZ;
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyAttributesScope extends androidx.constraintlayout.compose.BaseKeyFramesScope {
-    method public void frame(@IntRange(from=0L, to=100L) int frame, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyAttributeScope,kotlin.Unit> keyFrameContent);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyCycleScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
-    method public float getAlpha();
-    method public float getOffset();
-    method public float getPeriod();
-    method public float getPhase();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public void setAlpha(float);
-    method public void setOffset(float);
-    method public void setPeriod(float);
-    method public void setPhase(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    property public final float alpha;
-    property public final float offset;
-    property public final float period;
-    property public final float phase;
-    property public final float rotationX;
-    property public final float rotationY;
-    property public final float rotationZ;
-    property public final float scaleX;
-    property public final float scaleY;
-    property public final float translationX;
-    property public final float translationY;
-    property public final float translationZ;
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyCyclesScope extends androidx.constraintlayout.compose.BaseKeyFramesScope {
-    method public void frame(@IntRange(from=0L, to=100L) int frame, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyCycleScope,kotlin.Unit> keyFrameContent);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyPositionScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
-    method public androidx.constraintlayout.compose.CurveFit? getCurveFit();
-    method public float getPercentHeight();
-    method public float getPercentWidth();
-    method public float getPercentX();
-    method public float getPercentY();
-    method public void setCurveFit(androidx.constraintlayout.compose.CurveFit?);
-    method public void setPercentHeight(float);
-    method public void setPercentWidth(float);
-    method public void setPercentX(float);
-    method public void setPercentY(float);
-    property public final androidx.constraintlayout.compose.CurveFit? curveFit;
-    property public final float percentHeight;
-    property public final float percentWidth;
-    property public final float percentX;
-    property public final float percentY;
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class KeyPositionsScope extends androidx.constraintlayout.compose.BaseKeyFramesScope {
-    method public void frame(@IntRange(from=0L, to=100L) int frame, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyPositionScope,kotlin.Unit> keyFrameContent);
-    method public androidx.constraintlayout.compose.RelativePosition getType();
-    method public void setType(androidx.constraintlayout.compose.RelativePosition);
-    property public final androidx.constraintlayout.compose.RelativePosition type;
-  }
-
-  public final class LateMotionLayoutKt {
-    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void LateMotionLayout(androidx.compose.runtime.MutableState<androidx.constraintlayout.compose.ConstraintSet?> start, androidx.compose.runtime.MutableState<androidx.constraintlayout.compose.ConstraintSet?> end, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlinx.coroutines.channels.Channel<androidx.constraintlayout.compose.ConstraintSet> channel, androidx.compose.runtime.State<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, int optimizationLevel, kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-  }
-
-  public enum LayoutInfoFlags {
-    enum_constant public static final androidx.constraintlayout.compose.LayoutInfoFlags BOUNDS;
-    enum_constant public static final androidx.constraintlayout.compose.LayoutInfoFlags NONE;
-  }
-
-  public interface LayoutInformationReceiver {
-    method public androidx.constraintlayout.compose.MotionLayoutDebugFlags getForcedDrawDebug();
-    method public int getForcedHeight();
-    method public float getForcedProgress();
-    method public int getForcedWidth();
-    method public androidx.constraintlayout.compose.LayoutInfoFlags getLayoutInformationMode();
-    method public void onNewProgress(float progress);
-    method public void resetForcedProgress();
-    method public void setLayoutInformation(String information);
-    method public void setUpdateFlag(androidx.compose.runtime.MutableState<java.lang.Long> needsUpdate);
-  }
-
-  @androidx.compose.runtime.Stable public abstract class LayoutReference {
-  }
-
-  @kotlin.PublishedApi internal class Measurer implements androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer androidx.constraintlayout.compose.DesignInfoProvider {
-    ctor public Measurer(androidx.compose.ui.unit.Density density);
-    method public final void addLayoutInformationReceiver(androidx.constraintlayout.compose.LayoutInformationReceiver? layoutReceiver);
-    method protected final void applyRootSize(long constraints);
-    method public void computeLayoutResult();
-    method @androidx.compose.runtime.Composable public final void createDesignElements();
-    method public void didMeasures();
-    method @androidx.compose.runtime.Composable public final void drawDebugBounds(androidx.compose.foundation.layout.BoxScope, float forcedScaleFactor);
-    method public final void drawDebugBounds(androidx.compose.ui.graphics.drawscope.DrawScope, float forcedScaleFactor);
-    method public String getDesignInfo(int startX, int startY, String args);
-    method public final float getForcedScaleFactor();
-    method protected final java.util.Map<androidx.compose.ui.layout.Measurable,androidx.constraintlayout.core.state.WidgetFrame> getFrameCache();
-    method public final int getLayoutCurrentHeight();
-    method public final int getLayoutCurrentWidth();
-    method protected final androidx.constraintlayout.compose.LayoutInformationReceiver? getLayoutInformationReceiver();
-    method protected final java.util.Map<androidx.compose.ui.layout.Measurable,androidx.compose.ui.layout.Placeable> getPlaceables();
-    method protected final androidx.constraintlayout.core.widgets.ConstraintWidgetContainer getRoot();
-    method protected final androidx.constraintlayout.compose.State getState();
-    method public void measure(androidx.constraintlayout.core.widgets.ConstraintWidget constraintWidget, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure measure);
-    method public final void parseDesignElements(androidx.constraintlayout.compose.ConstraintSet constraintSet);
-    method public final void performLayout(androidx.compose.ui.layout.Placeable.PlacementScope, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
-    method public final long performMeasure(long constraints, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.constraintlayout.compose.ConstraintSet constraintSet, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables, int optimizationLevel);
-    method public final void setForcedScaleFactor(float);
-    method protected final void setLayoutInformationReceiver(androidx.constraintlayout.compose.LayoutInformationReceiver?);
-    property public final float forcedScaleFactor;
-    property protected final java.util.Map<androidx.compose.ui.layout.Measurable,androidx.constraintlayout.core.state.WidgetFrame> frameCache;
-    property public final int layoutCurrentHeight;
-    property public final int layoutCurrentWidth;
-    property protected final androidx.constraintlayout.compose.LayoutInformationReceiver? layoutInformationReceiver;
-    property protected final java.util.Map<androidx.compose.ui.layout.Measurable,androidx.compose.ui.layout.Placeable> placeables;
-    property protected final androidx.constraintlayout.core.widgets.ConstraintWidgetContainer root;
-    property protected final androidx.constraintlayout.compose.State state;
-  }
-
-  public final class MotionCarouselKt {
-    method @androidx.compose.runtime.Composable public static void ItemHolder(int i, String slotPrefix, boolean showSlot, kotlin.jvm.functions.Function0<kotlin.Unit> function);
-    method @androidx.compose.runtime.Composable public static void MotionCarousel(androidx.constraintlayout.compose.MotionScene motionScene, int initialSlotIndex, int numSlots, optional String backwardTransition, optional String forwardTransition, optional String slotPrefix, optional boolean showSlots, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionCarouselScope,kotlin.Unit> content);
-    method public static inline <T> void items(androidx.constraintlayout.compose.MotionCarouselScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> itemContent);
-    method public static inline <T> void itemsWithProperties(androidx.constraintlayout.compose.MotionCarouselScope, java.util.List<? extends T> items, kotlin.jvm.functions.Function2<? super T,? super androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties>,kotlin.Unit> itemContent);
-  }
-
-  public interface MotionCarouselScope {
-    method public void items(int count, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> itemContent);
-    method public void itemsWithProperties(int count, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties>,kotlin.Unit> itemContent);
-  }
-
-  public interface MotionItemsProvider {
-    method public int count();
-    method public kotlin.jvm.functions.Function0<kotlin.Unit> getContent(int index);
-    method public kotlin.jvm.functions.Function0<kotlin.Unit> getContent(int index, androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties> properties);
-    method public boolean hasItemsWithProperties();
-  }
-
-  public enum MotionLayoutDebugFlags {
-    enum_constant public static final androidx.constraintlayout.compose.MotionLayoutDebugFlags NONE;
-    enum_constant public static final androidx.constraintlayout.compose.MotionLayoutDebugFlags SHOW_ALL;
-    enum_constant public static final androidx.constraintlayout.compose.MotionLayoutDebugFlags UNKNOWN;
-  }
-
-  @Deprecated public enum MotionLayoutFlag {
-    enum_constant @Deprecated public static final androidx.constraintlayout.compose.MotionLayoutFlag Default;
-    enum_constant @Deprecated public static final androidx.constraintlayout.compose.MotionLayoutFlag FullMeasure;
-  }
-
-  public final class MotionLayoutKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, float progress, optional androidx.compose.ui.Modifier modifier, optional String transitionName, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, androidx.constraintlayout.compose.Transition? transition, float progress, androidx.constraintlayout.compose.LayoutInformationReceiver? informationReceiver, int optimizationLevel, boolean showBounds, boolean showPaths, boolean showKeyPositions, androidx.compose.ui.Modifier modifier, androidx.compose.runtime.MutableState<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, float progress, String transitionName, int optimizationLevel, int debugFlags, androidx.compose.ui.Modifier modifier, androidx.compose.runtime.MutableState<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, androidx.compose.runtime.MutableState<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class MotionLayoutScope {
-    method public long customColor(String id, String name);
-    method public float customDistance(String id, String name);
-    method public float customFloat(String id, String name);
-    method public long customFontSize(String id, String name);
-    method public int customInt(String id, String name);
-    method public androidx.constraintlayout.compose.MotionLayoutScope.CustomProperties customProperties(String id);
-    method @Deprecated public long motionColor(String id, String name);
-    method @Deprecated public float motionDistance(String id, String name);
-    method @Deprecated public float motionFloat(String id, String name);
-    method @Deprecated public long motionFontSize(String id, String name);
-    method @Deprecated public int motionInt(String id, String name);
-    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties> motionProperties(String id);
-    method @Deprecated public androidx.constraintlayout.compose.MotionLayoutScope.MotionProperties motionProperties(String id, String tag);
-    method public androidx.compose.ui.Modifier onStartEndBoundsChanged(androidx.compose.ui.Modifier, Object layoutId, kotlin.jvm.functions.Function2<? super androidx.compose.ui.geometry.Rect,? super androidx.compose.ui.geometry.Rect,kotlin.Unit> onBoundsChanged);
-  }
-
-  public final class MotionLayoutScope.CustomProperties {
-    method public long color(String name);
-    method public float distance(String name);
-    method public float float(String name);
-    method public long fontSize(String name);
-    method public int int(String name);
-  }
-
-  public final class MotionLayoutScope.MotionProperties {
-    method public long color(String name);
-    method public float distance(String name);
-    method public float float(String name);
-    method public long fontSize(String name);
-    method public String id();
-    method public int int(String name);
-    method public String? tag();
-  }
-
-  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.constraintlayout.compose.ExperimentalMotionApi public interface MotionScene extends androidx.constraintlayout.core.state.CoreMotionScene {
-    method public androidx.constraintlayout.compose.ConstraintSet? getConstraintSetInstance(String name);
-    method public androidx.constraintlayout.compose.Transition? getTransitionInstance(String name);
-  }
-
-  public final class MotionSceneKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.MotionScene MotionScene(@org.intellij.lang.annotations.Language("json5") String content);
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class MotionSceneScope {
-    method public androidx.constraintlayout.compose.ConstraintSetRef addConstraintSet(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional String? name);
-    method public void addTransition(androidx.constraintlayout.compose.Transition transition, optional String? name);
-    method public androidx.constraintlayout.compose.ConstraintSetRef constraintSet(optional String? name, optional androidx.constraintlayout.compose.ConstraintSetRef? extendConstraintSet, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> constraintSetContent);
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRefFor(Object id);
-    method public androidx.constraintlayout.compose.MotionSceneScope.ConstrainedLayoutReferences createRefsFor(java.lang.Object... ids);
-    method public void customColor(androidx.constraintlayout.compose.ConstrainScope, String name, long value);
-    method public void customColor(androidx.constraintlayout.compose.KeyAttributeScope, String name, long value);
-    method public void customDistance(androidx.constraintlayout.compose.ConstrainScope, String name, float value);
-    method public void customDistance(androidx.constraintlayout.compose.KeyAttributeScope, String name, float value);
-    method public void customFloat(androidx.constraintlayout.compose.ConstrainScope, String name, float value);
-    method public void customFloat(androidx.constraintlayout.compose.KeyAttributeScope, String name, float value);
-    method public void customFontSize(androidx.constraintlayout.compose.ConstrainScope, String name, long value);
-    method public void customFontSize(androidx.constraintlayout.compose.KeyAttributeScope, String name, long value);
-    method public void customInt(androidx.constraintlayout.compose.ConstrainScope, String name, int value);
-    method public void customInt(androidx.constraintlayout.compose.KeyAttributeScope, String name, int value);
-    method public void defaultTransition(androidx.constraintlayout.compose.ConstraintSetRef from, androidx.constraintlayout.compose.ConstraintSetRef to, optional kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.TransitionScope,kotlin.Unit> transitionContent);
-    method public float getStaggeredWeight(androidx.constraintlayout.compose.ConstrainScope);
-    method public void setStaggeredWeight(androidx.constraintlayout.compose.ConstrainScope, float);
-    method public void transition(androidx.constraintlayout.compose.ConstraintSetRef from, androidx.constraintlayout.compose.ConstraintSetRef to, optional String? name, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.TransitionScope,kotlin.Unit> transitionContent);
-  }
-
-  public final class MotionSceneScope.ConstrainedLayoutReferences {
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component1();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component10();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component11();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component12();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component13();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component14();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component15();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component16();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component2();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component3();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component4();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component5();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component6();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component7();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component8();
-    method public operator androidx.constraintlayout.compose.ConstrainedLayoutReference component9();
-  }
-
-  public final class MotionSceneScopeKt {
-    method @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.MotionScene MotionScene(kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionSceneScope,kotlin.Unit> motionSceneContent);
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class OnSwipe {
-    ctor public OnSwipe(androidx.constraintlayout.compose.ConstrainedLayoutReference anchor, androidx.constraintlayout.compose.SwipeSide side, androidx.constraintlayout.compose.SwipeDirection direction, optional float dragScale, optional float dragThreshold, optional androidx.constraintlayout.compose.ConstrainedLayoutReference? dragAround, optional androidx.constraintlayout.compose.ConstrainedLayoutReference? limitBoundsTo, optional androidx.constraintlayout.compose.SwipeTouchUp onTouchUp, optional androidx.constraintlayout.compose.SwipeMode mode);
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getAnchor();
-    method public androidx.constraintlayout.compose.SwipeDirection getDirection();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference? getDragAround();
-    method public float getDragScale();
-    method public float getDragThreshold();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference? getLimitBoundsTo();
-    method public androidx.constraintlayout.compose.SwipeMode getMode();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getOnTouchUp();
-    method public androidx.constraintlayout.compose.SwipeSide getSide();
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference anchor;
-    property public final androidx.constraintlayout.compose.SwipeDirection direction;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference? dragAround;
-    property public final float dragScale;
-    property public final float dragThreshold;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference? limitBoundsTo;
-    property public final androidx.constraintlayout.compose.SwipeMode mode;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp onTouchUp;
-    property public final androidx.constraintlayout.compose.SwipeSide side;
-  }
-
-  @androidx.compose.runtime.Immutable @kotlin.PublishedApi internal final class RawConstraintSet implements androidx.constraintlayout.compose.ConstraintSet {
-    ctor public RawConstraintSet(androidx.constraintlayout.core.parser.CLObject clObject);
-    method public void applyTo(androidx.constraintlayout.compose.State state, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class RelativePosition {
-    method public String getName();
-    property public String name;
-    field public static final androidx.constraintlayout.compose.RelativePosition.Companion Companion;
-  }
-
-  public static final class RelativePosition.Companion {
-    method public androidx.constraintlayout.compose.RelativePosition getDelta();
-    method public androidx.constraintlayout.compose.RelativePosition getParent();
-    method public androidx.constraintlayout.compose.RelativePosition getPath();
-    property public final androidx.constraintlayout.compose.RelativePosition Delta;
-    property public final androidx.constraintlayout.compose.RelativePosition Parent;
-    property public final androidx.constraintlayout.compose.RelativePosition Path;
-  }
-
-  @kotlin.jvm.JvmInline public final value class Skip {
-    ctor public Skip(@IntRange(from=0L) int position, @IntRange(from=1L) int size);
-    ctor public Skip(@IntRange(from=0L) int position, @IntRange(from=1L) int rows, @IntRange(from=1L) int columns);
-    method public String getDescription();
-    property public final String description;
-  }
-
-  @kotlin.jvm.JvmInline public final value class Span {
-    ctor public Span(@IntRange(from=0L) int position, @IntRange(from=1L) int size);
-    ctor public Span(@IntRange(from=0L) int position, @IntRange(from=1L) int rows, @IntRange(from=1L) int columns);
-    ctor public Span(String description);
-    method public String getDescription();
-    property public final String description;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SpringBoundary {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SpringBoundary.Companion Companion;
-  }
-
-  public static final class SpringBoundary.Companion {
-    method public androidx.constraintlayout.compose.SpringBoundary getBounceBoth();
-    method public androidx.constraintlayout.compose.SpringBoundary getBounceEnd();
-    method public androidx.constraintlayout.compose.SpringBoundary getBounceStart();
-    method public androidx.constraintlayout.compose.SpringBoundary getOvershoot();
-    property public final androidx.constraintlayout.compose.SpringBoundary BounceBoth;
-    property public final androidx.constraintlayout.compose.SpringBoundary BounceEnd;
-    property public final androidx.constraintlayout.compose.SpringBoundary BounceStart;
-    property public final androidx.constraintlayout.compose.SpringBoundary Overshoot;
-  }
-
-  public final class State extends androidx.constraintlayout.core.state.State {
-    ctor public State(androidx.compose.ui.unit.Density density);
-    method public androidx.compose.ui.unit.Density getDensity();
-    method @Deprecated public androidx.compose.ui.unit.LayoutDirection getLayoutDirection();
-    method public long getRootIncomingConstraints();
-    method @Deprecated public void setLayoutDirection(androidx.compose.ui.unit.LayoutDirection);
-    method public void setRootIncomingConstraints(long);
-    property public final androidx.compose.ui.unit.Density density;
-    property @Deprecated public final androidx.compose.ui.unit.LayoutDirection layoutDirection;
-    property public final long rootIncomingConstraints;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeDirection {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeDirection.Companion Companion;
-  }
-
-  public static final class SwipeDirection.Companion {
-    method public androidx.constraintlayout.compose.SwipeDirection getClockwise();
-    method public androidx.constraintlayout.compose.SwipeDirection getCounterclockwise();
-    method public androidx.constraintlayout.compose.SwipeDirection getDown();
-    method public androidx.constraintlayout.compose.SwipeDirection getEnd();
-    method public androidx.constraintlayout.compose.SwipeDirection getLeft();
-    method public androidx.constraintlayout.compose.SwipeDirection getRight();
-    method public androidx.constraintlayout.compose.SwipeDirection getStart();
-    method public androidx.constraintlayout.compose.SwipeDirection getUp();
-    property public final androidx.constraintlayout.compose.SwipeDirection Clockwise;
-    property public final androidx.constraintlayout.compose.SwipeDirection Counterclockwise;
-    property public final androidx.constraintlayout.compose.SwipeDirection Down;
-    property public final androidx.constraintlayout.compose.SwipeDirection End;
-    property public final androidx.constraintlayout.compose.SwipeDirection Left;
-    property public final androidx.constraintlayout.compose.SwipeDirection Right;
-    property public final androidx.constraintlayout.compose.SwipeDirection Start;
-    property public final androidx.constraintlayout.compose.SwipeDirection Up;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeMode {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeMode.Companion Companion;
-  }
-
-  public static final class SwipeMode.Companion {
-    method public androidx.constraintlayout.compose.SwipeMode getSpring();
-    method public androidx.constraintlayout.compose.SwipeMode getVelocity();
-    method public androidx.constraintlayout.compose.SwipeMode spring(optional float mass, optional float stiffness, optional float damping, optional float threshold, optional androidx.constraintlayout.compose.SpringBoundary boundary);
-    method public androidx.constraintlayout.compose.SwipeMode velocity(optional float maxVelocity, optional float maxAcceleration);
-    property public final androidx.constraintlayout.compose.SwipeMode Spring;
-    property public final androidx.constraintlayout.compose.SwipeMode Velocity;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeSide {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeSide.Companion Companion;
-  }
-
-  public static final class SwipeSide.Companion {
-    method public androidx.constraintlayout.compose.SwipeSide getBottom();
-    method public androidx.constraintlayout.compose.SwipeSide getEnd();
-    method public androidx.constraintlayout.compose.SwipeSide getLeft();
-    method public androidx.constraintlayout.compose.SwipeSide getMiddle();
-    method public androidx.constraintlayout.compose.SwipeSide getRight();
-    method public androidx.constraintlayout.compose.SwipeSide getStart();
-    method public androidx.constraintlayout.compose.SwipeSide getTop();
-    property public final androidx.constraintlayout.compose.SwipeSide Bottom;
-    property public final androidx.constraintlayout.compose.SwipeSide End;
-    property public final androidx.constraintlayout.compose.SwipeSide Left;
-    property public final androidx.constraintlayout.compose.SwipeSide Middle;
-    property public final androidx.constraintlayout.compose.SwipeSide Right;
-    property public final androidx.constraintlayout.compose.SwipeSide Start;
-    property public final androidx.constraintlayout.compose.SwipeSide Top;
-  }
-
-  @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SwipeTouchUp {
-    method public String getName();
-    property public final String name;
-    field public static final androidx.constraintlayout.compose.SwipeTouchUp.Companion Companion;
-  }
-
-  public static final class SwipeTouchUp.Companion {
-    method public androidx.constraintlayout.compose.SwipeTouchUp getAutoComplete();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getDecelerate();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getNeverCompleteEnd();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getNeverCompleteStart();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getStop();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getToEnd();
-    method public androidx.constraintlayout.compose.SwipeTouchUp getToStart();
-    property public final androidx.constraintlayout.compose.SwipeTouchUp AutoComplete;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp Decelerate;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp NeverCompleteEnd;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp NeverCompleteStart;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp Stop;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp ToEnd;
-    property public final androidx.constraintlayout.compose.SwipeTouchUp ToStart;
-  }
-
-  public final class ToolingUtilsKt {
-    method public static androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.constraintlayout.compose.DesignInfoProvider> getDesignInfoDataKey();
-    property public static final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.constraintlayout.compose.DesignInfoProvider> DesignInfoDataKey;
-    field @kotlin.PublishedApi internal static final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.constraintlayout.compose.DesignInfoProvider> designInfoProvider$delegate;
-  }
-
-  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.constraintlayout.compose.ExperimentalMotionApi public interface Transition {
-    method public String getEndConstraintSetId();
-    method public String getStartConstraintSetId();
-  }
-
-  public final class TransitionKt {
-    method @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.Transition Transition(@org.intellij.lang.annotations.Language("json5") String content);
-  }
-
-  @SuppressCompatibility @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.constraintlayout.compose.ExperimentalMotionApi public final class TransitionScope {
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRefFor(Object id);
-    method public float getMaxStaggerDelay();
-    method public androidx.constraintlayout.compose.Arc getMotionArc();
-    method public androidx.constraintlayout.compose.OnSwipe? getOnSwipe();
-    method public void keyAttributes(androidx.constraintlayout.compose.ConstrainedLayoutReference[] targets, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyAttributesScope,kotlin.Unit> keyAttributesContent);
-    method public void keyCycles(androidx.constraintlayout.compose.ConstrainedLayoutReference[] targets, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyCyclesScope,kotlin.Unit> keyCyclesContent);
-    method public void keyPositions(androidx.constraintlayout.compose.ConstrainedLayoutReference[] targets, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.KeyPositionsScope,kotlin.Unit> keyPositionsContent);
-    method public void setMaxStaggerDelay(float);
-    method public void setMotionArc(androidx.constraintlayout.compose.Arc);
-    method public void setOnSwipe(androidx.constraintlayout.compose.OnSwipe?);
-    property public final float maxStaggerDelay;
-    property public final androidx.constraintlayout.compose.Arc motionArc;
-    property public final androidx.constraintlayout.compose.OnSwipe? onSwipe;
-  }
-
-  public final class TransitionScopeKt {
-    method @SuppressCompatibility @androidx.constraintlayout.compose.ExperimentalMotionApi public static androidx.constraintlayout.compose.Transition Transition(optional String from, optional String to, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.TransitionScope,kotlin.Unit> content);
-  }
-
-  @androidx.compose.runtime.Immutable public final class VerticalAlign {
-    field public static final androidx.constraintlayout.compose.VerticalAlign.Companion Companion;
-  }
-
-  public static final class VerticalAlign.Companion {
-    method public androidx.constraintlayout.compose.VerticalAlign getBaseline();
-    method public androidx.constraintlayout.compose.VerticalAlign getBottom();
-    method public androidx.constraintlayout.compose.VerticalAlign getCenter();
-    method public androidx.constraintlayout.compose.VerticalAlign getTop();
-    property public final androidx.constraintlayout.compose.VerticalAlign Baseline;
-    property public final androidx.constraintlayout.compose.VerticalAlign Bottom;
-    property public final androidx.constraintlayout.compose.VerticalAlign Center;
-    property public final androidx.constraintlayout.compose.VerticalAlign Top;
-  }
-
-  @kotlin.jvm.JvmDefaultWithCompatibility public interface VerticalAnchorable {
-    method public void linkTo(androidx.constraintlayout.compose.ConstraintLayoutBaseScope.VerticalAnchor anchor, optional float margin, optional float goneMargin);
-  }
-
-  @androidx.compose.runtime.Stable public final class VerticalChainReference extends androidx.constraintlayout.compose.LayoutReference {
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getBottom();
-    method public androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor getTop();
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor bottom;
-    property public final androidx.constraintlayout.compose.ConstraintLayoutBaseScope.HorizontalAnchor top;
-  }
-
-  @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public final class VerticalChainScope {
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getBottom();
-    method public androidx.constraintlayout.compose.ConstrainedLayoutReference getParent();
-    method public androidx.constraintlayout.compose.HorizontalAnchorable getTop();
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable bottom;
-    property public final androidx.constraintlayout.compose.ConstrainedLayoutReference parent;
-    property public final androidx.constraintlayout.compose.HorizontalAnchorable top;
-  }
-
-  @androidx.compose.runtime.Immutable public final class Visibility {
-    field public static final androidx.constraintlayout.compose.Visibility.Companion Companion;
-  }
-
-  public static final class Visibility.Companion {
-    method public androidx.constraintlayout.compose.Visibility getGone();
-    method public androidx.constraintlayout.compose.Visibility getInvisible();
-    method public androidx.constraintlayout.compose.Visibility getVisible();
-    property public final androidx.constraintlayout.compose.Visibility Gone;
-    property public final androidx.constraintlayout.compose.Visibility Invisible;
-    property public final androidx.constraintlayout.compose.Visibility Visible;
-  }
-
-  @androidx.compose.runtime.Immutable public final class Wrap {
-    field public static final androidx.constraintlayout.compose.Wrap.Companion Companion;
-  }
-
-  public static final class Wrap.Companion {
-    method public androidx.constraintlayout.compose.Wrap getAligned();
-    method public androidx.constraintlayout.compose.Wrap getChain();
-    method public androidx.constraintlayout.compose.Wrap getNone();
-    property public final androidx.constraintlayout.compose.Wrap Aligned;
-    property public final androidx.constraintlayout.compose.Wrap Chain;
-    property public final androidx.constraintlayout.compose.Wrap None;
-  }
-
-}
-
diff --git a/constraintlayout/constraintlayout-compose/api/restricted_current.txt b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
index 60bc75d..f2836bb 100644
--- a/constraintlayout/constraintlayout-compose/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
@@ -468,14 +468,14 @@
     property public final androidx.constraintlayout.compose.FlowStyle SpreadInside;
   }
 
-  @kotlin.jvm.JvmInline public final value class GridFlags {
-    ctor public GridFlags(optional boolean isPlaceLayoutsOnSpansFirst);
+  @kotlin.jvm.JvmInline public final value class GridFlag {
     method public boolean isPlaceLayoutsOnSpansFirst();
+    method public infix int or(int other);
     property public final boolean isPlaceLayoutsOnSpansFirst;
-    field public static final androidx.constraintlayout.compose.GridFlags.Companion Companion;
+    field public static final androidx.constraintlayout.compose.GridFlag.Companion Companion;
   }
 
-  public static final class GridFlags.Companion {
+  public static final class GridFlag.Companion {
     method public int getNone();
     method public int getPlaceLayoutsOnSpansFirst();
     property public final int None;
diff --git a/constraintlayout/constraintlayout-compose/build.gradle b/constraintlayout/constraintlayout-compose/build.gradle
index 99ecbd1..399acda 100644
--- a/constraintlayout/constraintlayout-compose/build.gradle
+++ b/constraintlayout/constraintlayout-compose/build.gradle
@@ -35,13 +35,13 @@
     sourceSets {
         commonMain {
             dependencies {
-                implementation("androidx.compose.ui:ui:1.7.0-beta04")
-                implementation("androidx.compose.ui:ui-unit:1.7.0-beta04")
-                implementation("androidx.compose.ui:ui-util:1.7.0-beta04")
-                implementation("androidx.compose.foundation:foundation:1.7.0-beta04")
-                implementation("androidx.compose.foundation:foundation-layout:1.7.0-beta04")
+                implementation("androidx.compose.ui:ui:1.7.0-beta05")
+                implementation("androidx.compose.ui:ui-unit:1.7.0-beta05")
+                implementation("androidx.compose.ui:ui-util:1.7.0-beta05")
+                implementation("androidx.compose.foundation:foundation:1.7.0-beta05")
+                implementation("androidx.compose.foundation:foundation-layout:1.7.0-beta05")
                 implementation(project(":constraintlayout:constraintlayout-core"))
-                implementation("androidx.collection:collection:1.4.0")
+                implementation("androidx.collection:collection:1.4.1")
             }
         }
 
@@ -60,7 +60,7 @@
             dependsOn(commonMain)
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 implementation("androidx.core:core-ktx:1.5.0")
             }
         }
@@ -81,6 +81,7 @@
                 implementation(libs.truth)
                 implementation(project(":compose:foundation:foundation"))
                 implementation(project(":compose:material:material"))
+                implementation("androidx.compose.material:material-icons-core:1.6.7")
                 implementation(project(":compose:ui:ui-test-junit4"))
                 implementation(project(":compose:ui:ui-test-manifest"))
                 implementation(project(":compose:test-utils"))
@@ -110,8 +111,11 @@
     inceptionYear = "2022"
     description = "This library offers a flexible and adaptable way to position and animate widgets in Compose"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
+    samples(project(":constraintlayout:constraintlayout-compose:constraintlayout-compose-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.constraintlayout.compose"
 }
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/build.gradle
index 57aa1a8..1487928 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/build.gradle
+++ b/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/build.gradle
@@ -36,5 +36,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.constraintlayout.compose.benchmark"
 }
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
index db7fe29..f79e18c 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
@@ -30,11 +30,13 @@
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:animation:animation"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":compose:ui:ui-tooling-preview"))
     debugImplementation(project(":compose:ui:ui-tooling"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.constraintlayout.compose.demos"
 }
 
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/lint-baseline.xml b/constraintlayout/constraintlayout-compose/integration-tests/demos/lint-baseline.xml
index e6cb82a..2bd372a 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/lint-baseline.xml
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.2.0-beta01)" variant="all" version="8.2.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
@@ -49,7 +49,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable weights with type List&lt;? extends Float>: replace with FloatList"
-        errorLine1="                    val weights = when (mode) {"
+        errorLine1="                    val weights ="
         errorLine2="                    ^">
         <location
             file="src/main/java/androidx/constraintlayout/compose/demos/StaggeredDemo.kt"/>
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/build.gradle
index 7b273f9..0abe571 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/build.gradle
@@ -22,6 +22,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         minSdkVersion 26
     }
@@ -55,9 +56,10 @@
     implementation(project(":compose:foundation:foundation-layout"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:runtime:runtime-tracing"))
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:ui:ui-tooling"))
     implementation(project(":profileinstaller:profileinstaller"))
-}
\ No newline at end of file
+}
diff --git a/constraintlayout/constraintlayout-compose/lint-baseline.xml b/constraintlayout/constraintlayout-compose/lint-baseline.xml
index 11712b7..a499513 100644
--- a/constraintlayout/constraintlayout-compose/lint-baseline.xml
+++ b/constraintlayout/constraintlayout-compose/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanInlineOptIn"
@@ -48,7 +48,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method setAnchors$lint_module has parameter &lt;set-?> with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        message="method setAnchors$constraintlayout_compose has parameter anchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;Float, T>())"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -57,7 +57,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="return type Map&lt;Float, T> of getAnchors$lint_module: replace with FloatObjectMap"
+        message="return type Map&lt;Float, T> of getAnchors$constraintlayout_compose: replace with FloatObjectMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;Float, T>())"
         errorLine2="                 ~~~~~~~">
         <location
@@ -66,7 +66,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method ensureInit$lint_module has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        message="method ensureInit$constraintlayout_compose has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
         errorLine1="    internal fun ensureInit(newAnchors: Map&lt;Float, T>) {"
         errorLine2="                                        ~~~~~~~~~~~~~">
         <location
@@ -75,18 +75,18 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method processNewAnchors$lint_module has parameter oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        oldAnchors: Map&lt;Float, T>,"
-        errorLine2="                    ~~~~~~~~~~~~~">
+        message="method processNewAnchors$constraintlayout_compose has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        errorLine1="    internal suspend fun processNewAnchors(oldAnchors: Map&lt;Float, T>, newAnchors: Map&lt;Float, T>) {"
+        errorLine2="                                                                                  ~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt"/>
     </issue>
 
     <issue
         id="PrimitiveInCollection"
-        message="method processNewAnchors$lint_module has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        newAnchors: Map&lt;Float, T>"
-        errorLine2="                    ~~~~~~~~~~~~~">
+        message="method processNewAnchors$constraintlayout_compose has parameter oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        errorLine1="    internal suspend fun processNewAnchors(oldAnchors: Map&lt;Float, T>, newAnchors: Map&lt;Float, T>) {"
+        errorLine2="                                                       ~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt"/>
     </issue>
@@ -112,8 +112,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        val oldAnchors = state.anchors"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            val oldAnchors = state.anchors"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt"/>
     </issue>
@@ -121,8 +121,8 @@
     <issue
         id="PrimitiveInCollection"
         message="method findBounds has parameter anchors with type Set&lt;Float>: replace with FloatSet"
-        errorLine1="    anchors: Set&lt;Float>"
-        errorLine2="             ~~~~~~~~~~">
+        errorLine1="private fun findBounds(offset: Float, anchors: Set&lt;Float>): List&lt;Float> {"
+        errorLine2="                                               ~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt"/>
     </issue>
@@ -130,8 +130,8 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Float> of findBounds: replace with FloatList"
-        errorLine1="): List&lt;Float> {"
-        errorLine2="   ~~~~~~~~~~~">
+        errorLine1="private fun findBounds(offset: Float, anchors: Set&lt;Float>): List&lt;Float> {"
+        errorLine2="                                                            ~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/constraintlayout/compose/carousel/CarouselSwipeable.kt"/>
     </issue>
@@ -184,7 +184,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable anchors with type Map&lt;Float, ? extends String>: replace with FloatObjectMap"
-        errorLine1="    val anchors = if (currentIndex == 0) {"
+        errorLine1="    val anchors ="
         errorLine2="    ^">
         <location
             file="src/androidMain/kotlin/androidx/constraintlayout/compose/MotionCarousel.kt"/>
diff --git a/constraintlayout/constraintlayout-compose/samples/build.gradle b/constraintlayout/constraintlayout-compose/samples/build.gradle
new file mode 100644
index 0000000..4381aef
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/samples/build.gradle
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("AndroidXComposePlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    implementation(libs.kotlinStdlib)
+
+    compileOnly(project(":annotation:annotation-sampled"))
+
+    implementation(project(":constraintlayout:constraintlayout-compose"))
+    implementation("androidx.compose.runtime:runtime:1.6.8")
+    implementation("androidx.compose.material:material:1.6.8")
+}
+
+androidx {
+    name = "Constraintlayout Compose Samples"
+    type = LibraryType.SAMPLES
+    mavenVersion = LibraryVersions.CONSTRAINTLAYOUT_COMPOSE
+    inceptionYear = "2024"
+    description = "Contains the sample code for the Androidx Constraintlayout Compose Library"
+}
+
+android {
+    compileSdk 35
+    namespace "androidx.constraintlayout.compose.samples"
+}
diff --git a/constraintlayout/constraintlayout-compose/samples/src/main/java/androidx/constraintlayout/compose/samples/ConstraintLayoutSamples.kt b/constraintlayout/constraintlayout-compose/samples/src/main/java/androidx/constraintlayout/compose/samples/ConstraintLayoutSamples.kt
new file mode 100644
index 0000000..048c91a
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/samples/src/main/java/androidx/constraintlayout/compose/samples/ConstraintLayoutSamples.kt
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.ConstraintSet
+import androidx.constraintlayout.compose.Dimension
+import androidx.constraintlayout.compose.Skip
+import androidx.constraintlayout.compose.Span
+import androidx.constraintlayout.compose.atMost
+
+@Sampled
+@Composable
+fun Row_sample() {
+    ConstraintLayout(
+        constraintSet =
+            ConstraintSet {
+                val (a, b, c, d, e) = createRefsFor(0, 1, 2, 3, 4)
+                val row =
+                    createRow(
+                        a,
+                        b,
+                        c,
+                        d,
+                        e,
+                        spacing = 10.dp,
+                        weights = floatArrayOf(3f, 3f, 2f, 2f, 1f),
+                    )
+                constrain(row) {
+                    width = Dimension.matchParent
+                    height = Dimension.matchParent
+                }
+
+                constrain(a, b, c, d, e) { width = Dimension.fillToConstraints }
+            },
+        modifier = Modifier.fillMaxSize()
+    ) {
+        repeat(5) {
+            Text(text = "item$it", modifier = Modifier.layoutId(it).background(Color.LightGray))
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun Column_sample() {
+    ConstraintLayout(
+        constraintSet =
+            ConstraintSet {
+                val (a, b, c, d, e) = createRefsFor(0, 1, 2, 3, 4)
+                val column =
+                    createColumn(
+                        a,
+                        b,
+                        c,
+                        d,
+                        e,
+                        spacing = 10.dp,
+                        weights = floatArrayOf(3f, 3f, 2f, 2f, 1f),
+                    )
+                constrain(column) {
+                    width = Dimension.matchParent
+                    height = Dimension.matchParent
+                }
+
+                constrain(a, b, c, d, e) { height = Dimension.fillToConstraints }
+            },
+        modifier = Modifier.fillMaxSize()
+    ) {
+        repeat(5) {
+            Text(text = "item$it", modifier = Modifier.layoutId(it).background(Color.LightGray))
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun Grid_calculator_sample() {
+    // For most of the keys we can just use the displayed text as the ID.
+    val ids =
+        arrayOf(
+            // Text box will span all 4 columns and the first 2 of rows
+            "textBox",
+            "C",
+            "+/-",
+            "%",
+            "/",
+            "7",
+            "8",
+            "9",
+            "*",
+            "4",
+            "5",
+            "6",
+            "-",
+            "1",
+            "2",
+            "3",
+            "+",
+            // The '0' will span two columns, note that it's on the 24th position in the grid
+            "0",
+            ".",
+            "="
+        )
+    ConstraintLayout(
+        constraintSet =
+            ConstraintSet {
+                val idRefs = Array(ids.size) { createRefFor(ids[it]) }
+
+                val g1 =
+                    createGrid(
+                        elements = idRefs,
+                        rows = 7,
+                        columns = 4,
+                        verticalSpacing = 10.dp,
+                        horizontalSpacing = 10.dp,
+                        spans =
+                            arrayOf(
+                                // textBox
+                                Span(position = 0, rows = 2, columns = 4),
+                                // '0' key
+                                Span(position = 24, rows = 1, columns = 2)
+                            )
+                    )
+
+                constrain(g1) {
+                    width = Dimension.matchParent
+                    height = Dimension.matchParent
+                }
+
+                constrain(*idRefs) {
+                    // Make all the layouts fill up their space, you may still use coercing methods
+                    // such as `atMost(Dp)` or `atMostWrapContent()` to further limit their size.
+                    width = Dimension.fillToConstraints
+                    height = Dimension.fillToConstraints
+                }
+            },
+        modifier = Modifier.fillMaxSize()
+    ) {
+        ids.forEach { id ->
+            when (id) {
+                "textBox" -> {
+                    Box(
+                        modifier =
+                            Modifier.background(Color.Gray)
+                                // As usual, IDs should only be assigned on top-level children
+                                .layoutId(id),
+                        contentAlignment = Alignment.BottomEnd
+                    ) {
+                        Text(text = "100", fontSize = 80.sp)
+                    }
+                }
+                else -> {
+                    Button(onClick = {}, Modifier.layoutId(id)) {
+                        Text(text = id, fontSize = 30.sp)
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun Grid_navigationPad_sample() {
+    val keys =
+        arrayOf("Insert", "Home", "Page Up", "Delete", "End", "Page Down", "↑", "←", "↓", "→")
+    ConstraintLayout(
+        constraintSet =
+            ConstraintSet {
+                val keyRefs = Array(keys.size) { createRefFor(keys[it]) }
+
+                val g1 =
+                    createGrid(
+                        elements = keyRefs,
+                        rows = 5,
+                        columns = 3,
+                        verticalSpacing = 8.dp,
+                        horizontalSpacing = 8.dp,
+                        skips =
+                            arrayOf(
+                                // These positions follow the expected Grid cells indexing
+                                // Arranged horizontally by default:
+                                //   - 0 is top-left
+                                //   - 14 is bottom-right (5 rows x 3 columns - 1)
+                                Skip(position = 6, rows = 1, columns = 3),
+                                Skip(position = 9, rows = 1, columns = 1),
+                                Skip(position = 11, rows = 1, columns = 1)
+                            )
+                    )
+                constrain(g1) {
+                    width = Dimension.matchParent
+                    height = Dimension.matchParent
+                }
+
+                constrain(*keyRefs) {
+                    width = Dimension.fillToConstraints.atMost(100.dp)
+                    height = Dimension.fillToConstraints.atMost(100.dp)
+                }
+            },
+        modifier = Modifier.fillMaxSize()
+    ) {
+        keys.forEachIndexed { index, key ->
+            Box(
+                modifier = Modifier.layoutId(key).background(Color.LightGray),
+                contentAlignment = Alignment.Center
+            ) {
+                Text(
+                    text = key,
+                    textAlign = TextAlign.Center,
+                    // Make fontSize bigger for the arrow keys
+                    fontSize = if (index >= 6) 24.sp else TextUnit.Unspecified
+                )
+            }
+        }
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt b/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt
index 6d130abe..60a6bf2 100644
--- a/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt
@@ -30,6 +30,8 @@
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -69,7 +71,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var leftX = 0.dp
@@ -107,7 +109,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = false,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var leftX = 0.dp
@@ -145,7 +147,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var expectedX = 0.dp
@@ -183,7 +185,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var expectedX = 0.dp
@@ -221,7 +223,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var leftX = 0.dp
@@ -258,7 +260,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags(isSubGridByColRow = true)
+                gridFlags = GridFlag.SubGridByColRow
             )
         }
         var leftX = 0.dp
@@ -294,7 +296,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var leftX = 0.dp
@@ -332,7 +334,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.PlaceLayoutsOnSpansFirst,
+                gridFlags = GridFlag.PlaceLayoutsOnSpansFirst,
             )
         }
         var leftX = 0.dp
@@ -372,7 +374,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var topY = 0.dp
@@ -405,7 +407,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var leftX = 0.dp
@@ -443,7 +445,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags(isSubGridByColRow = true),
+                gridFlags = GridFlag.SubGridByColRow,
             )
         }
         var leftX = 0.dp
@@ -482,7 +484,7 @@
                 gridColumnWeights = floatArrayOf(),
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         val expectedLeft = (rootSize - 10.dp) / 2f
@@ -518,7 +520,7 @@
                 gridColumnWeights = weights,
                 boxesCount = boxesCount,
                 isHorizontalArrangement = true,
-                gridFlags = GridFlags.None,
+                gridFlags = GridFlag.None,
             )
         }
         var expectedLeft = 0.dp
@@ -538,6 +540,54 @@
     }
 
     @Test
+    fun testIconsistendRowWeightsThrows() {
+        val rowCount = 3
+        val error =
+            assertFailsWith<IllegalArgumentException> {
+                rule.setContent {
+                    gridComposableTest(
+                        modifier = Modifier.size(100.dp),
+                        numRows = rowCount,
+                        numColumns = 1,
+                        gridSpans = emptyArray(),
+                        gridSkips = emptyArray(),
+                        // Insufficient weights in array should throw
+                        gridRowWeights = FloatArray(rowCount - 1) { it.toFloat() },
+                        gridColumnWeights = floatArrayOf(),
+                        boxesCount = 1,
+                        isHorizontalArrangement = true,
+                        gridFlags = GridFlag.None
+                    )
+                }
+            }
+        assertEquals("Number of weights (2) should match number of rows (3).", error.message)
+    }
+
+    @Test
+    fun testInconsistentColumnWeightsThrows() {
+        val columnCount = 3
+        val error =
+            assertFailsWith<IllegalArgumentException> {
+                rule.setContent {
+                    gridComposableTest(
+                        modifier = Modifier.size(100.dp),
+                        numRows = 1,
+                        numColumns = columnCount,
+                        gridSpans = emptyArray(),
+                        gridSkips = emptyArray(),
+                        gridRowWeights = floatArrayOf(),
+                        // Excessive weights in array should throw
+                        gridColumnWeights = FloatArray(columnCount + 1) { it.toFloat() },
+                        boxesCount = 1,
+                        isHorizontalArrangement = true,
+                        gridFlags = GridFlag.None
+                    )
+                }
+            }
+        assertEquals("Number of weights (4) should match number of columns (3).", error.message)
+    }
+
+    @Test
     fun testGaps() {
         val rootSize = 200.dp
         val hGap = 10.dp
@@ -575,7 +625,7 @@
         gridColumnWeights: FloatArray,
         boxesCount: Int,
         isHorizontalArrangement: Boolean,
-        gridFlags: GridFlags,
+        gridFlags: GridFlag,
     ) {
         ConstraintLayout(
             ConstraintSet {
diff --git a/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt b/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt
index cf918c0..f447241 100644
--- a/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidInstrumentedTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt
@@ -32,6 +32,8 @@
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -241,6 +243,42 @@
         rule.onNodeWithTag("box3").assertPositionInRootIsEqualTo(expectedX, expectedY)
     }
 
+    @Test
+    fun testInconsistentWeightsOnColumnTrows() {
+        val count = 3
+        val error =
+            assertFailsWith<IllegalArgumentException> {
+                rule.setContent {
+                    ColumnComposableTest(
+                        modifier = Modifier.size(100.dp),
+                        boxesCount = count,
+                        // Insufficient weights should throw
+                        weights = FloatArray(count - 1) { it.toFloat() },
+                        skipIndices = emptyIntSet()
+                    )
+                }
+            }
+        assertEquals("Number of weights (2) should match number of elements (3).", error.message)
+    }
+
+    @Test
+    fun testInconsistentWeightsOnRowTrows() {
+        val count = 3
+        val error =
+            assertFailsWith<IllegalArgumentException> {
+                rule.setContent {
+                    RowComposableTest(
+                        modifier = Modifier.size(100.dp),
+                        boxesCount = count,
+                        // Excessive weights should throw
+                        weights = FloatArray(count + 1) { it.toFloat() },
+                        skipIndices = emptyIntSet()
+                    )
+                }
+            }
+        assertEquals("Number of weights (4) should match number of elements (3).", error.message)
+    }
+
     @Composable
     private fun ColumnComposableTest(
         modifier: Modifier = Modifier,
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
index d4868c6..777619f 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
@@ -643,44 +643,16 @@
     }
 
     /**
-     * Creates a Grid based helper that lays out its elements in a single Row. Example:
-     * ```
-     * ConstraintLayout(
-     *     constraintSet = ConstraintSet {
-     *         val (a, b, c, d, e) = createRefsFor(0, 1, 2, 3, 4)
-     *         val row = createRow(
-     *             a, b, c, d, e,
-     *             spacing = 10.dp,
-     *             weights = floatArrayOf(3f, 3f, 2f, 2f, 1f),
-     *         )
-     *         constrain(row) {
-     *             width = Dimension.matchParent
-     *             height = Dimension.matchParent
-     *         }
+     * Creates a Grid based helper that lays out its elements in a single Row.
      *
-     *         constrain(a, b, c, d, e) {
-     *             width = Dimension.fillToConstraints
-     *         }
-     *     },
-     *     modifier = Modifier.fillMaxSize()
-     * ) {
-     *     repeat(5) {
-     *         Text(
-     *             text = "item$it",
-     *             modifier = Modifier
-     *                 .layoutId(it)
-     *                 .background(Color.LightGray)
-     *         )
-     *     }
-     * }
-     * ```
+     * Example:
      *
+     * @sample androidx.constraintlayout.compose.samples.Row_sample
      * @param elements [LayoutReference]s to be laid out by the Grid-based Row helper.
      * @param spacing Defines the horizontal spacing between each item in the Row.
      * @param weights Defines the weight for each element in the Row. Note that the number of
-     *   weights provided are expected to match the number of [elements] given. Otherwise, weights
-     *   will be automatically appended (with a value of `1`), or trimmed to match the number of
-     *   given [elements].
+     *   weights provided are expected to match the number of [elements] given.
+     * @throws IllegalArgumentException When non empty [weights] don't match the number of elements.
      * @see createGrid
      */
     @SuppressLint("Range") // Enables internal grid mode for row and column
@@ -689,6 +661,11 @@
         spacing: Dp = 0.dp,
         weights: FloatArray = floatArrayOf(),
     ): ConstrainedLayoutReference {
+        if (weights.isNotEmpty() && elements.size != weights.size) {
+            throw IllegalArgumentException(
+                "Number of weights (${weights.size}) should match number of elements (${elements.size})."
+            )
+        }
         return createGrid(
             elements = elements,
             rows = 1,
@@ -702,43 +679,13 @@
      * Creates a Grid based helper that lays out its elements in a single Column.
      *
      * Example:
-     * ```
-     * ConstraintLayout(
-     *     constraintSet = ConstraintSet {
-     *         val (a, b, c, d, e) = createRefsFor(0, 1, 2, 3, 4)
-     *         val column = createColumn(
-     *             a, b, c, d, e,
-     *             spacing = 10.dp,
-     *             weights = floatArrayOf(3f, 3f, 2f, 2f, 1f),
-     *         )
-     *         constrain(column) {
-     *             width = Dimension.matchParent
-     *             height = Dimension.matchParent
-     *         }
      *
-     *         constrain(a, b, c, d, e) {
-     *             height = Dimension.fillToConstraints
-     *         }
-     *     },
-     *     modifier = Modifier.fillMaxSize()
-     * ) {
-     *     repeat(5) {
-     *         Text(
-     *             text = "item$it",
-     *             modifier = Modifier
-     *                 .layoutId(it)
-     *                 .background(Color.LightGray)
-     *         )
-     *     }
-     * }
-     * ```
-     *
+     * @sample androidx.constraintlayout.compose.samples.Column_sample
      * @param elements [LayoutReference]s to be laid out by the Grid-based Column helper
      * @param spacing Defines the vertical spacing between each item in the Column.
      * @param weights Defines the weight for each element in the Column. Note that the number of
-     *   weights provided are expected to match the number of [elements] given. Otherwise, weights
-     *   will be automatically appended (with a value of `1`), or trimmed to match the number of
-     *   given [elements].
+     *   weights provided are expected to match the number of [elements] given.
+     * @throws IllegalArgumentException When non empty [weights] don't match the number of elements.
      * @see createGrid
      */
     @SuppressLint("Range") // Enables internal grid mode for row and column
@@ -747,6 +694,11 @@
         spacing: Dp = 0.dp,
         weights: FloatArray = floatArrayOf(),
     ): ConstrainedLayoutReference {
+        if (weights.isNotEmpty() && elements.size != weights.size) {
+            throw IllegalArgumentException(
+                "Number of weights (${weights.size}) should match number of elements (${elements.size})."
+            )
+        }
         return createGrid(
             elements = elements,
             rows = 0,
@@ -772,131 +724,12 @@
      * priority, ignoring the overlapping [Span] definition.
      *
      * Here's an example showing how to build a calculator layout using a couple of [Span]s:
-     * ```
-     * // For most of the keys we can just use the displayed text as the ID.
-     * val ids = arrayOf(
-     *     // Text box will span all 4 columns and the first 2 of rows
-     *     "textBox",
-     *     "C", "+/-", "%", "/",
-     *     "7", "8", "9", "*",
-     *     "4", "5", "6", "-",
-     *     "1", "2", "3", "+",
-     *     // The '0' will span two columns, note that it's on the 24th position in the grid
-     *     "0", ".", "="
-     * )
-     * ConstraintLayout(
-     *     constraintSet = ConstraintSet {
-     *         val idRefs = Array(ids.size) { createRefFor(ids[it]) }
      *
-     *         val g1 = createGrid(
-     *             elements = idRefs,
-     *             rows = 7,
-     *             columns = 4,
-     *             verticalSpacing = 10.dp,
-     *             horizontalSpacing = 10.dp,
-     *             spans = arrayOf(
-     *                 // textBox
-     *                 Span(position = 0, rows = 2, columns = 4),
-     *                 // '0' key
-     *                 Span(position = 24, rows = 1, columns = 2)
-     *             )
-     *         )
-     *
-     *         constrain(g1) {
-     *             width = Dimension.matchParent
-     *             height = Dimension.matchParent
-     *         }
-     *
-     *         constrain(*idRefs) {
-     *             // Make all the layouts fill up their space, you may still use coercing methods
-     *             // such as `atMost(Dp)` or `atMostWrapContent()` to further limit their size.
-     *             width = Dimension.fillToConstraints
-     *             height = Dimension.fillToConstraints
-     *         }
-     *     },
-     *     modifier = Modifier.fillMaxSize()
-     * ) {
-     *     ids.forEach { id ->
-     *         when (id) {
-     *             "textBox" -> {
-     *                 Box(
-     *                     modifier = Modifier
-     *                         .background(Color.Gray)
-     *                         // As usual, IDs should only be assigned on top-level children
-     *                         .layoutId(id),
-     *                     contentAlignment = Alignment.BottomEnd
-     *                 ) {
-     *                     Text(text = "100", fontSize = 80.sp)
-     *                 }
-     *             }
-     *
-     *             else -> {
-     *                 Button(onClick = { }, Modifier.layoutId(id)) {
-     *                     Text(text = id, fontSize = 30.sp)
-     *                 }
-     *             }
-     *         }
-     *     }
-     * }
-     * ```
+     * @sample androidx.constraintlayout.compose.samples.Grid_calculator_sample
      *
      * Here's another example using [Skip]s to easily lay out the typical Keyboard navigation pad:
-     * ```
-     * val keys = arrayOf(
-     *     "Insert", "Home", "Page Up",
-     *     "Delete", "End", "Page Down",
-     *     "↑", "←", "↓", "→"
-     * )
-     * ConstraintLayout(
-     *     constraintSet = ConstraintSet {
-     *         val keyRefs = Array(keys.size) { createRefFor(keys[it]) }
      *
-     *         val g1 = createGrid(
-     *             elements = keyRefs,
-     *             rows = 5,
-     *             columns = 3,
-     *             verticalSpacing = 8.dp,
-     *             horizontalSpacing = 8.dp,
-     *             skips = arrayOf(
-     *                 // These positions follow the expected Grid cells indexing
-     *                 // Arranged horizontally by default:
-     *                 //   - 0 is top-left
-     *                 //   - 14 is bottom-right (5 rows x 3 columns - 1)
-     *                 Skip(position = 6, rows = 1, columns = 3),
-     *                 Skip(position = 9, rows = 1, columns = 1),
-     *                 Skip(position = 11, rows = 1, columns = 1)
-     *             )
-     *         )
-     *         constrain(g1) {
-     *             width = Dimension.matchParent
-     *             height = Dimension.matchParent
-     *         }
-     *
-     *         constrain(*keyRefs) {
-     *             width = Dimension.fillToConstraints.atMost(100.dp)
-     *             height = Dimension.fillToConstraints.atMost(100.dp)
-     *         }
-     *     },
-     *     modifier = Modifier.fillMaxSize()
-     * ) {
-     *     keys.forEachIndexed { index, key ->
-     *         Box(
-     *             modifier = Modifier
-     *                 .layoutId(key)
-     *                 .background(Color.LightGray),
-     *             contentAlignment = Alignment.Center
-     *         ) {
-     *             Text(
-     *                 text = key,
-     *                 textAlign = TextAlign.Center,
-     *                 // Make fontSize bigger for the arrow keys
-     *                 fontSize = if (index >= 6) 24.sp else TextUnit.Unspecified
-     *             )
-     *         }
-     *     }
-     * }
-     * ```
-     *
+     * @sample androidx.constraintlayout.compose.samples.Grid_navigationPad_sample
      * @param elements [LayoutReference]s to be laid out by the Grid helper. By default, they are
      *   positioned in the given order based on the arrangement. Horizontal arrangement by default.
      * @param rows Sets the number of rows in the Grid
@@ -908,12 +741,10 @@
      * @param horizontalSpacing Defines the gap between each column.
      * @param rowWeights Defines the weight for each row. The weight specifies how much space each
      *   row takes relative to each other. Should be either an empty array (all rows are the same
-     *   size), or have a value corresponding for each row. Otherwise, these weights will be trimmed
-     *   or padded (with trailing 1f) to match the given [rows].
+     *   size), or have a value corresponding for each row.
      * @param columnWeights Defines the weight for each column. The weight specifies how much space
      *   each column takes relative to each other. Should be either an empty array (all columns are
-     *   the same size), or have a value corresponding for each column. Otherwise, these weights
-     *   will be trimmed or padded (with trailing 1f) to match the given [columns].
+     *   the same size), or have a value corresponding for each column.
      * @param skips A [Skip] defines an area within the Grid where Layouts may **not** be placed.
      *   So, as the [elements] are being placed, they will skip any cell covered by the given skips.
      * @param spans A [Span] defines how much area should each cell occupy when placing an item on
@@ -921,8 +752,10 @@
      *   spanned area. In that sense, a [Span] works similarly to a [Skip], except that an item will
      *   be placed at the original spanned cell position. Also note, [skips] take priority over
      *   spans, meaning that defining a [Span] that overlaps a [Skip] is a no-op.
-     * @param flags A [GridFlags] definition that may change certain behaviors of the Grid helper.
-     *   [GridFlags.None] by default.
+     * @param flags A [GridFlag] definition that may change certain behaviors of the Grid helper.
+     *   [GridFlag.None] by default.
+     * @throws IllegalArgumentException When non empty weights don't match the number of columns or
+     *   rows respectively.
      * @see createColumn
      * @see createRow
      */
@@ -937,8 +770,19 @@
         columnWeights: FloatArray = floatArrayOf(),
         skips: Array<Skip> = arrayOf(),
         spans: Array<Span> = arrayOf(),
-        flags: GridFlags = GridFlags.None,
+        flags: GridFlag = GridFlag.None,
     ): ConstrainedLayoutReference {
+        if (rowWeights.isNotEmpty() && rows > 0 && rows != rowWeights.size) {
+            throw IllegalArgumentException(
+                "Number of weights (${rowWeights.size}) should match number of rows ($rows)."
+            )
+        }
+        if (columnWeights.isNotEmpty() && columns > 0 && columns != columnWeights.size) {
+            throw IllegalArgumentException(
+                "Number of weights (${columnWeights.size}) should match number of columns ($columns)."
+            )
+        }
+
         val ref = ConstrainedLayoutReference(createHelperId())
         val elementArray = CLArray(charArrayOf())
         elements.forEach { elementArray.add(CLString.from(it.id.toString())) }
@@ -1442,7 +1286,8 @@
 }
 
 /**
- * Set of individual options that may change the Grid helper behavior.
+ * Set of individual options that may change the Grid helper behavior, each flag can be combined
+ * with the [GridFlag.or] operator.
  *
  * By default, the Grid helper places its [LayoutReference]s as given in the `elements` parameter.
  * Following arrangement rules (skips, spans and orientation).
@@ -1462,46 +1307,63 @@
  * @see ConstraintLayoutBaseScope.createGrid
  */
 @JvmInline
-value class GridFlags private constructor(internal val value: Int) {
-    /**
-     * @param isPlaceLayoutsOnSpansFirst Whether to make it so that Layouts are first placed on
-     *   cells occupied by spans.
-     */
-    constructor(
-        isPlaceLayoutsOnSpansFirst: Boolean = false
-    ) : this((if (isPlaceLayoutsOnSpansFirst) 0 else GridCore.SPANS_RESPECT_WIDGET_ORDER))
+value class GridFlag private constructor(internal val value: Int) {
 
-    @TestOnly // Only for tests, the additional flag is not needed when using the DSL
-    internal constructor(
+    /**
+     * Handles the conversion of compose flags to :constraintlayout-core flags, handled like this
+     * since we invert the meaning of one the flags for API ergonomics in Compose.
+     */
+    private constructor(
         isPlaceLayoutsOnSpansFirst: Boolean = false,
+        // isSubGridByColRow is only expected to be used on tests
         isSubGridByColRow: Boolean = false
     ) : this(
         (if (isPlaceLayoutsOnSpansFirst) 0 else GridCore.SPANS_RESPECT_WIDGET_ORDER) or
             (if (isSubGridByColRow) GridCore.SUB_GRID_BY_COL_ROW else 0)
     )
 
+    /** `or` operator override to allow combining flags */
+    infix fun or(other: GridFlag): GridFlag =
+        // Again, implemented like this as the flag handling is non-standard. It differs from the
+        // :constraintlayout-core flag behaviors.
+        GridFlag(
+            isPlaceLayoutsOnSpansFirst or other.isPlaceLayoutsOnSpansFirst,
+            isSubGridByColRow or other.isSubGridByColRow
+        )
+
     /**
      * When true, the Grid helper will first place Layouts on cells occupied by spans, then fill the
      * remaining cells following the typical arrangement rules.
      */
     val isPlaceLayoutsOnSpansFirst: Boolean
-        get() = value and (GridCore.SPANS_RESPECT_WIDGET_ORDER) == 0
+        get() = value and GridCore.SPANS_RESPECT_WIDGET_ORDER == 0
+
+    /**
+     * Whether area definitions in Spans and Skips are treated as "columns by rows".
+     *
+     * Note that this property is only relevant for testing.
+     */
+    internal val isSubGridByColRow: Boolean
+        get() = value and GridCore.SUB_GRID_BY_COL_ROW > 0
 
     override fun toString(): String =
         "GridFlag(isPlaceLayoutsOnSpansFirst = $isPlaceLayoutsOnSpansFirst)"
 
     companion object {
         /** All default behaviors apply. */
-        val None = GridFlags(isPlaceLayoutsOnSpansFirst = false)
+        val None = GridFlag()
 
         /**
-         * Creates a [GridFlags] instance with `isPlaceLayoutsOnSpansFirst` as `true`.
+         * Creates a [GridFlag] instance with `isPlaceLayoutsOnSpansFirst` as `true`.
          *
          * Making it so that when placing the layouts, they are first placed on cells occupied by
          * spans, then, any remaining layouts are placed on the remaining cells following the
          * typical arrangement rules.
          */
-        val PlaceLayoutsOnSpansFirst = GridFlags(isPlaceLayoutsOnSpansFirst = true)
+        val PlaceLayoutsOnSpansFirst = GridFlag(isPlaceLayoutsOnSpansFirst = true)
+
+        /** Not relevant for the public API, only used now to test "internal" features. */
+        @TestOnly internal val SubGridByColRow = GridFlag(isSubGridByColRow = true)
     }
 }
 
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
index 6c1a01f..280dca8 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.view.View
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationSpec
@@ -1169,7 +1168,6 @@
 @RequiresApi(30)
 private object Api30Impl {
     @JvmStatic
-    @DoNotInline
     fun isShowingLayoutBounds(view: View): Boolean {
         return view.isShowingLayoutBounds
     }
diff --git a/constraintlayout/constraintlayout-compose/src/androidUnitTest/kotlin/androidx/constraintlayout/compose/GridFlagTest.kt b/constraintlayout/constraintlayout-compose/src/androidUnitTest/kotlin/androidx/constraintlayout/compose/GridFlagTest.kt
new file mode 100644
index 0000000..05fb256
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/src/androidUnitTest/kotlin/androidx/constraintlayout/compose/GridFlagTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose
+
+import kotlin.test.Test
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class GridFlagTest {
+    @Test
+    fun testFlags() {
+        var flag = GridFlag.None
+        assertFalse(flag.isPlaceLayoutsOnSpansFirst)
+        assertFalse(flag.isSubGridByColRow)
+
+        flag = GridFlag.PlaceLayoutsOnSpansFirst
+        assertTrue(flag.isPlaceLayoutsOnSpansFirst)
+        assertFalse(flag.isSubGridByColRow)
+
+        flag = GridFlag.SubGridByColRow
+        assertFalse(flag.isPlaceLayoutsOnSpansFirst)
+        assertTrue(flag.isSubGridByColRow)
+
+        flag = GridFlag.SubGridByColRow or GridFlag.PlaceLayoutsOnSpansFirst
+        assertTrue(flag.isPlaceLayoutsOnSpansFirst)
+        assertTrue(flag.isSubGridByColRow)
+    }
+}
diff --git a/constraintlayout/constraintlayout-core/api/1.1.0-beta01.txt b/constraintlayout/constraintlayout-core/api/1.1.0-beta01.txt
deleted file mode 100644
index 5be1d12..0000000
--- a/constraintlayout/constraintlayout-core/api/1.1.0-beta01.txt
+++ /dev/null
@@ -1,3393 +0,0 @@
-// Signature format: 4.0
-package androidx.constraintlayout.core {
-
-  public class ArrayLinkedVariables implements androidx.constraintlayout.core.ArrayRow.ArrayRowVariables {
-    method public void add(androidx.constraintlayout.core.SolverVariable!, float, boolean);
-    method public final void clear();
-    method public boolean contains(androidx.constraintlayout.core.SolverVariable!);
-    method public void display();
-    method public void divideByAmount(float);
-    method public final float get(androidx.constraintlayout.core.SolverVariable!);
-    method public int getCurrentSize();
-    method public int getHead();
-    method public final int getId(int);
-    method public final int getNextIndice(int);
-    method public final float getValue(int);
-    method public androidx.constraintlayout.core.SolverVariable! getVariable(int);
-    method public float getVariableValue(int);
-    method public int indexOf(androidx.constraintlayout.core.SolverVariable!);
-    method public void invert();
-    method public final void put(androidx.constraintlayout.core.SolverVariable!, float);
-    method public final float remove(androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public int sizeInBytes();
-    method public float use(androidx.constraintlayout.core.ArrayRow!, boolean);
-    field protected final androidx.constraintlayout.core.Cache! mCache;
-  }
-
-  public class ArrayRow {
-    ctor public ArrayRow();
-    ctor public ArrayRow(androidx.constraintlayout.core.Cache!);
-    method public androidx.constraintlayout.core.ArrayRow! addError(androidx.constraintlayout.core.LinearSystem!, int);
-    method public void addError(androidx.constraintlayout.core.SolverVariable!);
-    method public void clear();
-    method public androidx.constraintlayout.core.ArrayRow! createRowDimensionRatio(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEqualDimension(float, float, float, androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEqualMatchDimensions(float, float, float, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEquals(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEquals(androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowGreaterThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowGreaterThan(androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!);
-    method public androidx.constraintlayout.core.ArrayRow! createRowLowerThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowWithAngle(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public androidx.constraintlayout.core.SolverVariable! getKey();
-    method public androidx.constraintlayout.core.SolverVariable! getPivotCandidate(androidx.constraintlayout.core.LinearSystem!, boolean[]!);
-    method public void initFromRow(androidx.constraintlayout.core.LinearSystem.Row!);
-    method public boolean isEmpty();
-    method public androidx.constraintlayout.core.SolverVariable! pickPivot(androidx.constraintlayout.core.SolverVariable!);
-    method public void reset();
-    method public void updateFromFinalVariable(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public void updateFromRow(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.ArrayRow!, boolean);
-    method public void updateFromSynonymVariable(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public void updateFromSystem(androidx.constraintlayout.core.LinearSystem!);
-    field public androidx.constraintlayout.core.ArrayRow.ArrayRowVariables! variables;
-  }
-
-  public static interface ArrayRow.ArrayRowVariables {
-    method public void add(androidx.constraintlayout.core.SolverVariable!, float, boolean);
-    method public void clear();
-    method public boolean contains(androidx.constraintlayout.core.SolverVariable!);
-    method public void display();
-    method public void divideByAmount(float);
-    method public float get(androidx.constraintlayout.core.SolverVariable!);
-    method public int getCurrentSize();
-    method public androidx.constraintlayout.core.SolverVariable! getVariable(int);
-    method public float getVariableValue(int);
-    method public int indexOf(androidx.constraintlayout.core.SolverVariable!);
-    method public void invert();
-    method public void put(androidx.constraintlayout.core.SolverVariable!, float);
-    method public float remove(androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public int sizeInBytes();
-    method public float use(androidx.constraintlayout.core.ArrayRow!, boolean);
-  }
-
-  public class Cache {
-    ctor public Cache();
-  }
-
-  public class GoalRow extends androidx.constraintlayout.core.ArrayRow {
-    ctor public GoalRow(androidx.constraintlayout.core.Cache!);
-  }
-
-  public class LinearSystem {
-    ctor public LinearSystem();
-    method public void addCenterPoint(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintWidget!, float, int);
-    method public void addCentering(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, float, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addConstraint(androidx.constraintlayout.core.ArrayRow!);
-    method public androidx.constraintlayout.core.ArrayRow! addEquality(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addEquality(androidx.constraintlayout.core.SolverVariable!, int);
-    method public void addGreaterBarrier(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, boolean);
-    method public void addGreaterThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addLowerBarrier(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, boolean);
-    method public void addLowerThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addRatio(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float, int);
-    method public void addSynonym(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.SolverVariable! createErrorVariable(int, String!);
-    method public androidx.constraintlayout.core.SolverVariable! createExtraVariable();
-    method public androidx.constraintlayout.core.SolverVariable! createObjectVariable(Object!);
-    method public androidx.constraintlayout.core.ArrayRow! createRow();
-    method public static androidx.constraintlayout.core.ArrayRow! createRowDimensionPercent(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public androidx.constraintlayout.core.SolverVariable! createSlackVariable();
-    method public void displayReadableRows();
-    method public void displayVariablesReadableRows();
-    method public void fillMetrics(androidx.constraintlayout.core.Metrics!);
-    method public androidx.constraintlayout.core.Cache! getCache();
-    method public int getMemoryUsed();
-    method public static androidx.constraintlayout.core.Metrics! getMetrics();
-    method public int getNumEquations();
-    method public int getNumVariables();
-    method public int getObjectVariableValue(Object!);
-    method public void minimize() throws java.lang.Exception;
-    method public void removeRow(androidx.constraintlayout.core.ArrayRow!);
-    method public void reset();
-    field public static long ARRAY_ROW_CREATION;
-    field public static final boolean DEBUG = false;
-    field public static final boolean FULL_DEBUG = false;
-    field public static long OPTIMIZED_ARRAY_ROW_CREATION;
-    field public static boolean OPTIMIZED_ENGINE;
-    field public static boolean SIMPLIFY_SYNONYMS;
-    field public static boolean SKIP_COLUMNS;
-    field public static boolean USE_BASIC_SYNONYMS;
-    field public static boolean USE_DEPENDENCY_ORDERING;
-    field public static boolean USE_SYNONYMS;
-    field public boolean graphOptimizer;
-    field public boolean hasSimpleDefinition;
-    field public boolean newgraphOptimizer;
-    field public static androidx.constraintlayout.core.Metrics! sMetrics;
-  }
-
-  public class Metrics {
-    ctor public Metrics();
-    method public void copy(androidx.constraintlayout.core.Metrics!);
-    method public void reset();
-    field public long additionalMeasures;
-    field public long bfs;
-    field public long constraints;
-    field public long determineGroups;
-    field public long errors;
-    field public long extravariables;
-    field public long fullySolved;
-    field public long graphOptimizer;
-    field public long graphSolved;
-    field public long grouping;
-    field public long infeasibleDetermineGroups;
-    field public long iterations;
-    field public long lastTableSize;
-    field public long layouts;
-    field public long linearSolved;
-    field public long mChildCount;
-    field public long mEquations;
-    field public long mMeasureCalls;
-    field public long mMeasureDuration;
-    field public int mNumberOfLayouts;
-    field public int mNumberOfMeasures;
-    field public long mSimpleEquations;
-    field public long mSolverPasses;
-    field public long mVariables;
-    field public long maxRows;
-    field public long maxTableSize;
-    field public long maxVariables;
-    field public long measuredMatchWidgets;
-    field public long measuredWidgets;
-    field public long measures;
-    field public long measuresLayoutDuration;
-    field public long measuresWidgetsDuration;
-    field public long measuresWrap;
-    field public long measuresWrapInfeasible;
-    field public long minimize;
-    field public long minimizeGoal;
-    field public long nonresolvedWidgets;
-    field public long optimize;
-    field public long pivots;
-    field public java.util.ArrayList<java.lang.String!>! problematicLayouts;
-    field public long resolutions;
-    field public long resolvedWidgets;
-    field public long simpleconstraints;
-    field public long slackvariables;
-    field public long tableSizeIncrease;
-    field public long variables;
-    field public long widgets;
-  }
-
-  public class PriorityGoalRow extends androidx.constraintlayout.core.ArrayRow {
-    ctor public PriorityGoalRow(androidx.constraintlayout.core.Cache!);
-  }
-
-  public class SolverVariable implements java.lang.Comparable<androidx.constraintlayout.core.SolverVariable!> {
-    ctor public SolverVariable(androidx.constraintlayout.core.SolverVariable.Type!, String!);
-    ctor public SolverVariable(String!, androidx.constraintlayout.core.SolverVariable.Type!);
-    method public final void addToRow(androidx.constraintlayout.core.ArrayRow!);
-    method public int compareTo(androidx.constraintlayout.core.SolverVariable!);
-    method public String! getName();
-    method public final void removeFromRow(androidx.constraintlayout.core.ArrayRow!);
-    method public void reset();
-    method public void setFinalValue(androidx.constraintlayout.core.LinearSystem!, float);
-    method public void setName(String!);
-    method public void setSynonym(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public void setType(androidx.constraintlayout.core.SolverVariable.Type!, String!);
-    method public final void updateReferencesWithNewDefinition(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.ArrayRow!);
-    field public static final int STRENGTH_BARRIER = 6; // 0x6
-    field public static final int STRENGTH_CENTERING = 7; // 0x7
-    field public static final int STRENGTH_EQUALITY = 5; // 0x5
-    field public static final int STRENGTH_FIXED = 8; // 0x8
-    field public static final int STRENGTH_HIGH = 3; // 0x3
-    field public static final int STRENGTH_HIGHEST = 4; // 0x4
-    field public static final int STRENGTH_LOW = 1; // 0x1
-    field public static final int STRENGTH_MEDIUM = 2; // 0x2
-    field public static final int STRENGTH_NONE = 0; // 0x0
-    field public float computedValue;
-    field public int id;
-    field public boolean inGoal;
-    field public boolean isFinalValue;
-    field public int strength;
-    field public int usageInRowCount;
-  }
-
-  public enum SolverVariable.Type {
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type CONSTANT;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type ERROR;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type SLACK;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type UNKNOWN;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type UNRESTRICTED;
-  }
-
-  public class SolverVariableValues implements androidx.constraintlayout.core.ArrayRow.ArrayRowVariables {
-    method public void add(androidx.constraintlayout.core.SolverVariable!, float, boolean);
-    method public void clear();
-    method public boolean contains(androidx.constraintlayout.core.SolverVariable!);
-    method public void display();
-    method public void divideByAmount(float);
-    method public float get(androidx.constraintlayout.core.SolverVariable!);
-    method public int getCurrentSize();
-    method public androidx.constraintlayout.core.SolverVariable! getVariable(int);
-    method public float getVariableValue(int);
-    method public int indexOf(androidx.constraintlayout.core.SolverVariable!);
-    method public void invert();
-    method public void put(androidx.constraintlayout.core.SolverVariable!, float);
-    method public float remove(androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public int sizeInBytes();
-    method public float use(androidx.constraintlayout.core.ArrayRow!, boolean);
-    field protected final androidx.constraintlayout.core.Cache! mCache;
-  }
-
-}
-
-package androidx.constraintlayout.core.dsl {
-
-  public class Barrier extends androidx.constraintlayout.core.dsl.Helper {
-    ctor public Barrier(String!);
-    ctor public Barrier(String!, String!);
-    method public androidx.constraintlayout.core.dsl.Barrier! addReference(androidx.constraintlayout.core.dsl.Ref!);
-    method public androidx.constraintlayout.core.dsl.Barrier! addReference(String!);
-    method public androidx.constraintlayout.core.dsl.Constraint.Side! getDirection();
-    method public int getMargin();
-    method public String! referencesToString();
-    method public void setDirection(androidx.constraintlayout.core.dsl.Constraint.Side!);
-    method public void setMargin(int);
-  }
-
-  public abstract class Chain extends androidx.constraintlayout.core.dsl.Helper {
-    ctor public Chain(String!);
-    method public androidx.constraintlayout.core.dsl.Chain! addReference(androidx.constraintlayout.core.dsl.Ref!);
-    method public androidx.constraintlayout.core.dsl.Chain! addReference(String!);
-    method public androidx.constraintlayout.core.dsl.Chain.Style! getStyle();
-    method public String! referencesToString();
-    method public void setStyle(androidx.constraintlayout.core.dsl.Chain.Style!);
-    field protected java.util.ArrayList<androidx.constraintlayout.core.dsl.Ref!>! references;
-    field protected static final java.util.Map<androidx.constraintlayout.core.dsl.Chain.Style!,java.lang.String!>! styleMap;
-  }
-
-  public class Chain.Anchor {
-    method public void build(StringBuilder!);
-    method public String! getId();
-  }
-
-  public enum Chain.Style {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Chain.Style PACKED;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Chain.Style SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Chain.Style SPREAD_INSIDE;
-  }
-
-  public class Constraint {
-    ctor public Constraint(String!);
-    method protected void append(StringBuilder!, String!, float);
-    method public String! convertStringArrayToString(String![]!);
-    method public androidx.constraintlayout.core.dsl.Constraint.VAnchor! getBaseline();
-    method public androidx.constraintlayout.core.dsl.Constraint.VAnchor! getBottom();
-    method public float getCircleAngle();
-    method public String! getCircleConstraint();
-    method public int getCircleRadius();
-    method public String! getDimensionRatio();
-    method public int getEditorAbsoluteX();
-    method public int getEditorAbsoluteY();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getEnd();
-    method public int getHeight();
-    method public androidx.constraintlayout.core.dsl.Constraint.Behaviour! getHeightDefault();
-    method public int getHeightMax();
-    method public int getHeightMin();
-    method public float getHeightPercent();
-    method public float getHorizontalBias();
-    method public androidx.constraintlayout.core.dsl.Constraint.ChainMode! getHorizontalChainStyle();
-    method public float getHorizontalWeight();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getLeft();
-    method public String![]! getReferenceIds();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getRight();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getStart();
-    method public androidx.constraintlayout.core.dsl.Constraint.VAnchor! getTop();
-    method public float getVerticalBias();
-    method public androidx.constraintlayout.core.dsl.Constraint.ChainMode! getVerticalChainStyle();
-    method public float getVerticalWeight();
-    method public int getWidth();
-    method public androidx.constraintlayout.core.dsl.Constraint.Behaviour! getWidthDefault();
-    method public int getWidthMax();
-    method public int getWidthMin();
-    method public float getWidthPercent();
-    method public boolean isConstrainedHeight();
-    method public boolean isConstrainedWidth();
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void setCircleAngle(float);
-    method public void setCircleConstraint(String!);
-    method public void setCircleRadius(int);
-    method public void setConstrainedHeight(boolean);
-    method public void setConstrainedWidth(boolean);
-    method public void setDimensionRatio(String!);
-    method public void setEditorAbsoluteX(int);
-    method public void setEditorAbsoluteY(int);
-    method public void setHeight(int);
-    method public void setHeightDefault(androidx.constraintlayout.core.dsl.Constraint.Behaviour!);
-    method public void setHeightMax(int);
-    method public void setHeightMin(int);
-    method public void setHeightPercent(float);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalChainStyle(androidx.constraintlayout.core.dsl.Constraint.ChainMode!);
-    method public void setHorizontalWeight(float);
-    method public void setReferenceIds(String![]!);
-    method public void setVerticalBias(float);
-    method public void setVerticalChainStyle(androidx.constraintlayout.core.dsl.Constraint.ChainMode!);
-    method public void setVerticalWeight(float);
-    method public void setWidth(int);
-    method public void setWidthDefault(androidx.constraintlayout.core.dsl.Constraint.Behaviour!);
-    method public void setWidthMax(int);
-    method public void setWidthMin(int);
-    method public void setWidthPercent(float);
-    field public static final androidx.constraintlayout.core.dsl.Constraint! PARENT;
-  }
-
-  public class Constraint.Anchor {
-    method public void build(StringBuilder!);
-    method public String! getId();
-  }
-
-  public enum Constraint.Behaviour {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour PERCENT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour RATIO;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour RESOLVED;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour WRAP;
-  }
-
-  public enum Constraint.ChainMode {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.ChainMode PACKED;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.ChainMode SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.ChainMode SPREAD_INSIDE;
-  }
-
-  public class Constraint.HAnchor extends androidx.constraintlayout.core.dsl.Constraint.Anchor {
-  }
-
-  public enum Constraint.HSide {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide START;
-  }
-
-  public enum Constraint.Side {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side TOP;
-  }
-
-  public class Constraint.VAnchor extends androidx.constraintlayout.core.dsl.Constraint.Anchor {
-  }
-
-  public enum Constraint.VSide {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.VSide BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.VSide BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.VSide TOP;
-  }
-
-  public class ConstraintSet {
-    ctor public ConstraintSet(String!);
-    method public void add(androidx.constraintlayout.core.dsl.Constraint!);
-    method public void add(androidx.constraintlayout.core.dsl.Helper!);
-  }
-
-  public abstract class Guideline extends androidx.constraintlayout.core.dsl.Helper {
-    method public int getEnd();
-    method public float getPercent();
-    method public int getStart();
-    method public void setEnd(int);
-    method public void setPercent(float);
-    method public void setStart(int);
-  }
-
-  public class HChain extends androidx.constraintlayout.core.dsl.Chain {
-    ctor public HChain(String!);
-    ctor public HChain(String!, String!);
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getEnd();
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getLeft();
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getRight();
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getStart();
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-  }
-
-  public class HChain.HAnchor extends androidx.constraintlayout.core.dsl.Chain.Anchor {
-  }
-
-  public class Helper {
-    ctor public Helper(String!, androidx.constraintlayout.core.dsl.Helper.HelperType!);
-    ctor public Helper(String!, androidx.constraintlayout.core.dsl.Helper.HelperType!, String!);
-    method public void append(java.util.Map<java.lang.String!,java.lang.String!>!, StringBuilder!);
-    method public java.util.Map<java.lang.String!,java.lang.String!>! convertConfigToMap();
-    method public String! getConfig();
-    method public String! getId();
-    method public androidx.constraintlayout.core.dsl.Helper.HelperType! getType();
-    method public static void main(String![]!);
-    field protected String! config;
-    field protected java.util.Map<java.lang.String!,java.lang.String!>! configMap;
-    field protected final String! name;
-    field protected static final java.util.Map<androidx.constraintlayout.core.dsl.Constraint.Side!,java.lang.String!>! sideMap;
-    field protected androidx.constraintlayout.core.dsl.Helper.HelperType! type;
-    field protected static final java.util.Map<androidx.constraintlayout.core.dsl.Helper.Type!,java.lang.String!>! typeMap;
-  }
-
-  public static final class Helper.HelperType {
-    ctor public Helper.HelperType(String!);
-  }
-
-  public enum Helper.Type {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type BARRIER;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type HORIZONTAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type HORIZONTAL_GUIDELINE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type VERTICAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type VERTICAL_GUIDELINE;
-  }
-
-  public class KeyAttribute extends androidx.constraintlayout.core.dsl.Keys {
-    ctor public KeyAttribute(int, String!);
-    method protected void attributesToString(StringBuilder!);
-    method public float getAlpha();
-    method public androidx.constraintlayout.core.dsl.KeyAttribute.Fit! getCurveFit();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public float getRotation();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public String! getTarget();
-    method public String! getTransitionEasing();
-    method public float getTransitionPathRotate();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public androidx.constraintlayout.core.dsl.KeyAttribute.Visibility! getVisibility();
-    method public void setAlpha(float);
-    method public void setCurveFit(androidx.constraintlayout.core.dsl.KeyAttribute.Fit!);
-    method public void setPivotX(float);
-    method public void setPivotY(float);
-    method public void setRotation(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTarget(String!);
-    method public void setTransitionEasing(String!);
-    method public void setTransitionPathRotate(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    method public void setVisibility(androidx.constraintlayout.core.dsl.KeyAttribute.Visibility!);
-    field protected String! TYPE;
-  }
-
-  public enum KeyAttribute.Fit {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Fit LINEAR;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Fit SPLINE;
-  }
-
-  public enum KeyAttribute.Visibility {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Visibility GONE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Visibility INVISIBLE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Visibility VISIBLE;
-  }
-
-  public class KeyAttributes extends androidx.constraintlayout.core.dsl.Keys {
-    method protected void attributesToString(StringBuilder!);
-    method public float[]! getAlpha();
-    method public androidx.constraintlayout.core.dsl.KeyAttributes.Fit! getCurveFit();
-    method public float[]! getPivotX();
-    method public float[]! getPivotY();
-    method public float[]! getRotation();
-    method public float[]! getRotationX();
-    method public float[]! getRotationY();
-    method public float[]! getScaleX();
-    method public float[]! getScaleY();
-    method public String![]! getTarget();
-    method public String! getTransitionEasing();
-    method public float[]! getTransitionPathRotate();
-    method public float[]! getTranslationX();
-    method public float[]! getTranslationY();
-    method public float[]! getTranslationZ();
-    method public androidx.constraintlayout.core.dsl.KeyAttributes.Visibility![]! getVisibility();
-    method public void setAlpha(float...!);
-    method public void setCurveFit(androidx.constraintlayout.core.dsl.KeyAttributes.Fit!);
-    method public void setPivotX(float...!);
-    method public void setPivotY(float...!);
-    method public void setRotation(float...!);
-    method public void setRotationX(float...!);
-    method public void setRotationY(float...!);
-    method public void setScaleX(float[]!);
-    method public void setScaleY(float[]!);
-    method public void setTarget(String![]!);
-    method public void setTransitionEasing(String!);
-    method public void setTransitionPathRotate(float...!);
-    method public void setTranslationX(float[]!);
-    method public void setTranslationY(float[]!);
-    method public void setTranslationZ(float[]!);
-    method public void setVisibility(androidx.constraintlayout.core.dsl.KeyAttributes.Visibility!...!);
-    field protected String! TYPE;
-  }
-
-  public enum KeyAttributes.Fit {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Fit LINEAR;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Fit SPLINE;
-  }
-
-  public enum KeyAttributes.Visibility {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Visibility GONE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Visibility INVISIBLE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Visibility VISIBLE;
-  }
-
-  public class KeyCycle extends androidx.constraintlayout.core.dsl.KeyAttribute {
-    method public float getOffset();
-    method public float getPeriod();
-    method public float getPhase();
-    method public androidx.constraintlayout.core.dsl.KeyCycle.Wave! getShape();
-    method public void setOffset(float);
-    method public void setPeriod(float);
-    method public void setPhase(float);
-    method public void setShape(androidx.constraintlayout.core.dsl.KeyCycle.Wave!);
-  }
-
-  public enum KeyCycle.Wave {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave COS;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave REVERSE_SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave SIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave SQUARE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave TRIANGLE;
-  }
-
-  public class KeyCycles extends androidx.constraintlayout.core.dsl.KeyAttributes {
-    method public float[]! getWaveOffset();
-    method public float[]! getWavePeriod();
-    method public float[]! getWavePhase();
-    method public androidx.constraintlayout.core.dsl.KeyCycles.Wave! getWaveShape();
-    method public void setWaveOffset(float...!);
-    method public void setWavePeriod(float...!);
-    method public void setWavePhase(float...!);
-    method public void setWaveShape(androidx.constraintlayout.core.dsl.KeyCycles.Wave!);
-  }
-
-  public enum KeyCycles.Wave {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave COS;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave REVERSE_SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave SIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave SQUARE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave TRIANGLE;
-  }
-
-  public class KeyFrames {
-    ctor public KeyFrames();
-    method public void add(androidx.constraintlayout.core.dsl.Keys!);
-  }
-
-  public class KeyPosition extends androidx.constraintlayout.core.dsl.Keys {
-    ctor public KeyPosition(String!, int);
-    method public int getFrames();
-    method public float getPercentHeight();
-    method public float getPercentWidth();
-    method public float getPercentX();
-    method public float getPercentY();
-    method public androidx.constraintlayout.core.dsl.KeyPosition.Type! getPositionType();
-    method public String! getTarget();
-    method public String! getTransitionEasing();
-    method public void setFrames(int);
-    method public void setPercentHeight(float);
-    method public void setPercentWidth(float);
-    method public void setPercentX(float);
-    method public void setPercentY(float);
-    method public void setPositionType(androidx.constraintlayout.core.dsl.KeyPosition.Type!);
-    method public void setTarget(String!);
-    method public void setTransitionEasing(String!);
-  }
-
-  public enum KeyPosition.Type {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPosition.Type CARTESIAN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPosition.Type PATH;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPosition.Type SCREEN;
-  }
-
-  public class KeyPositions extends androidx.constraintlayout.core.dsl.Keys {
-    ctor public KeyPositions(int, java.lang.String!...!);
-    method public int[]! getFrames();
-    method public float[]! getPercentHeight();
-    method public float[]! getPercentWidth();
-    method public float[]! getPercentX();
-    method public float[]! getPercentY();
-    method public androidx.constraintlayout.core.dsl.KeyPositions.Type! getPositionType();
-    method public String![]! getTarget();
-    method public String! getTransitionEasing();
-    method public void setFrames(int...!);
-    method public void setPercentHeight(float...!);
-    method public void setPercentWidth(float...!);
-    method public void setPercentX(float...!);
-    method public void setPercentY(float...!);
-    method public void setPositionType(androidx.constraintlayout.core.dsl.KeyPositions.Type!);
-    method public void setTransitionEasing(String!);
-  }
-
-  public enum KeyPositions.Type {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPositions.Type CARTESIAN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPositions.Type PATH;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPositions.Type SCREEN;
-  }
-
-  public class Keys {
-    ctor public Keys();
-    method protected void append(StringBuilder!, String!, float);
-    method protected void append(StringBuilder!, String!, float[]!);
-    method protected void append(StringBuilder!, String!, int);
-    method protected void append(StringBuilder!, String!, String!);
-    method protected void append(StringBuilder!, String!, String![]!);
-    method protected String! unpack(String![]!);
-  }
-
-  public class MotionScene {
-    ctor public MotionScene();
-    method public void addConstraintSet(androidx.constraintlayout.core.dsl.ConstraintSet!);
-    method public void addTransition(androidx.constraintlayout.core.dsl.Transition!);
-  }
-
-  public class OnSwipe {
-    ctor public OnSwipe();
-    ctor public OnSwipe(String!, androidx.constraintlayout.core.dsl.OnSwipe.Side!, androidx.constraintlayout.core.dsl.OnSwipe.Drag!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Mode! getAutoCompleteMode();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Drag! getDragDirection();
-    method public float getDragScale();
-    method public float getDragThreshold();
-    method public String! getLimitBoundsTo();
-    method public float getMaxAcceleration();
-    method public float getMaxVelocity();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.TouchUp! getOnTouchUp();
-    method public String! getRotationCenterId();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Boundary! getSpringBoundary();
-    method public float getSpringDamping();
-    method public float getSpringMass();
-    method public float getSpringStiffness();
-    method public float getSpringStopThreshold();
-    method public String! getTouchAnchorId();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Side! getTouchAnchorSide();
-    method public void setAutoCompleteMode(androidx.constraintlayout.core.dsl.OnSwipe.Mode!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setDragDirection(androidx.constraintlayout.core.dsl.OnSwipe.Drag!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setDragScale(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setDragThreshold(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setLimitBoundsTo(String!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setMaxAcceleration(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setMaxVelocity(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setOnTouchUp(androidx.constraintlayout.core.dsl.OnSwipe.TouchUp!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setRotateCenter(String!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringBoundary(androidx.constraintlayout.core.dsl.OnSwipe.Boundary!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringDamping(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringMass(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringStiffness(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringStopThreshold(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setTouchAnchorId(String!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setTouchAnchorSide(androidx.constraintlayout.core.dsl.OnSwipe.Side!);
-    field public static final int FLAG_DISABLE_POST_SCROLL = 1; // 0x1
-    field public static final int FLAG_DISABLE_SCROLL = 2; // 0x2
-  }
-
-  public enum OnSwipe.Boundary {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary BOUNCE_BOTH;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary BOUNCE_END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary BOUNCE_START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary OVERSHOOT;
-  }
-
-  public enum OnSwipe.Drag {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag ANTICLOCKWISE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag CLOCKWISE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag DOWN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag UP;
-  }
-
-  public enum OnSwipe.Mode {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Mode SPRING;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Mode VELOCITY;
-  }
-
-  public enum OnSwipe.Side {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side MIDDLE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side TOP;
-  }
-
-  public enum OnSwipe.TouchUp {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp AUTOCOMPLETE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp DECELERATE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp DECELERATE_COMPLETE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp NEVER_COMPLETE_END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp NEVER_COMPLETE_START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp STOP;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp TO_END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp TO_START;
-  }
-
-  public class Ref {
-    method public static void addStringToReferences(String!, java.util.ArrayList<androidx.constraintlayout.core.dsl.Ref!>!);
-    method public String! getId();
-    method public float getPostMargin();
-    method public float getPreMargin();
-    method public float getWeight();
-    method public static float parseFloat(Object!);
-    method public static androidx.constraintlayout.core.dsl.Ref! parseStringToRef(String!);
-    method public void setId(String!);
-    method public void setPostMargin(float);
-    method public void setPreMargin(float);
-    method public void setWeight(float);
-  }
-
-  public class Transition {
-    ctor public Transition(String!, String!);
-    ctor public Transition(String!, String!, String!);
-    method public String! getId();
-    method public void setDuration(int);
-    method public void setFrom(String!);
-    method public void setId(String!);
-    method public void setKeyFrames(androidx.constraintlayout.core.dsl.Keys!);
-    method public void setOnSwipe(androidx.constraintlayout.core.dsl.OnSwipe!);
-    method public void setStagger(float);
-    method public void setTo(String!);
-  }
-
-  public class VChain extends androidx.constraintlayout.core.dsl.Chain {
-    ctor public VChain(String!);
-    ctor public VChain(String!, String!);
-    method public androidx.constraintlayout.core.dsl.VChain.VAnchor! getBaseline();
-    method public androidx.constraintlayout.core.dsl.VChain.VAnchor! getBottom();
-    method public androidx.constraintlayout.core.dsl.VChain.VAnchor! getTop();
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-  }
-
-  public class VChain.VAnchor extends androidx.constraintlayout.core.dsl.Chain.Anchor {
-  }
-
-  public class VGuideline extends androidx.constraintlayout.core.dsl.Guideline {
-    ctor public VGuideline(String!);
-    ctor public VGuideline(String!, String!);
-  }
-
-}
-
-package androidx.constraintlayout.core.motion {
-
-  public class CustomAttribute {
-    ctor public CustomAttribute(androidx.constraintlayout.core.motion.CustomAttribute!, Object!);
-    ctor public CustomAttribute(String!, androidx.constraintlayout.core.motion.CustomAttribute.AttributeType!);
-    ctor public CustomAttribute(String!, androidx.constraintlayout.core.motion.CustomAttribute.AttributeType!, Object!, boolean);
-    method public boolean diff(androidx.constraintlayout.core.motion.CustomAttribute!);
-    method public androidx.constraintlayout.core.motion.CustomAttribute.AttributeType! getType();
-    method public float getValueToInterpolate();
-    method public void getValuesToInterpolate(float[]!);
-    method public static int hsvToRgb(float, float, float);
-    method public boolean isContinuous();
-    method public int numberOfInterpolatedValues();
-    method public void setColorValue(int);
-    method public void setFloatValue(float);
-    method public void setIntValue(int);
-    method public void setStringValue(String!);
-    method public void setValue(float[]!);
-    method public void setValue(Object!);
-  }
-
-  public enum CustomAttribute.AttributeType {
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType BOOLEAN_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType COLOR_DRAWABLE_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType COLOR_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType DIMENSION_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType FLOAT_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType INT_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType REFERENCE_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType STRING_TYPE;
-  }
-
-  public class CustomVariable {
-    ctor public CustomVariable(androidx.constraintlayout.core.motion.CustomVariable!);
-    ctor public CustomVariable(androidx.constraintlayout.core.motion.CustomVariable!, Object!);
-    ctor public CustomVariable(String!, int);
-    ctor public CustomVariable(String!, int, boolean);
-    ctor public CustomVariable(String!, int, float);
-    ctor public CustomVariable(String!, int, int);
-    ctor public CustomVariable(String!, int, Object!);
-    ctor public CustomVariable(String!, int, String!);
-    method public void applyToWidget(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public static String! colorString(int);
-    method public androidx.constraintlayout.core.motion.CustomVariable! copy();
-    method public boolean diff(androidx.constraintlayout.core.motion.CustomVariable!);
-    method public boolean getBooleanValue();
-    method public int getColorValue();
-    method public float getFloatValue();
-    method public int getIntegerValue();
-    method public int getInterpolatedColor(float[]!);
-    method public String! getName();
-    method public String! getStringValue();
-    method public int getType();
-    method public float getValueToInterpolate();
-    method public void getValuesToInterpolate(float[]!);
-    method public static int hsvToRgb(float, float, float);
-    method public boolean isContinuous();
-    method public int numberOfInterpolatedValues();
-    method public static int rgbaTocColor(float, float, float, float);
-    method public void setBooleanValue(boolean);
-    method public void setFloatValue(float);
-    method public void setIntValue(int);
-    method public void setInterpolatedValue(androidx.constraintlayout.core.motion.MotionWidget!, float[]!);
-    method public void setStringValue(String!);
-    method public void setValue(float[]!);
-    method public void setValue(Object!);
-  }
-
-  public class Motion implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public Motion(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void addKey(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public int buildKeyFrames(float[]!, int[]!, int[]!);
-    method public void buildPath(float[]!, int);
-    method public void buildRect(float, float[]!, int);
-    method public String! getAnimateRelativeTo();
-    method public void getCenter(double, float[]!, float[]!);
-    method public float getCenterX();
-    method public float getCenterY();
-    method public void getDpDt(float, float, float, float[]!);
-    method public int getDrawPath();
-    method public float getFinalHeight();
-    method public float getFinalWidth();
-    method public float getFinalX();
-    method public float getFinalY();
-    method public int getId(String!);
-    method public androidx.constraintlayout.core.motion.MotionPaths! getKeyFrame(int);
-    method public int getKeyFrameInfo(int, int[]!);
-    method public int getKeyFramePositions(int[]!, float[]!);
-    method public float getMotionStagger();
-    method public float getStartHeight();
-    method public float getStartWidth();
-    method public float getStartX();
-    method public float getStartY();
-    method public int getTransformPivotTarget();
-    method public androidx.constraintlayout.core.motion.MotionWidget! getView();
-    method public boolean interpolate(androidx.constraintlayout.core.motion.MotionWidget!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-    method public void setDrawPath(int);
-    method public void setEnd(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void setIdString(String!);
-    method public void setPathMotionArc(int);
-    method public void setStaggerOffset(float);
-    method public void setStaggerScale(float);
-    method public void setStart(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void setStartState(androidx.constraintlayout.core.motion.utils.ViewState!, androidx.constraintlayout.core.motion.MotionWidget!, int, int, int);
-    method public void setTransformPivotTarget(int);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public void setView(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void setup(int, int, float, long);
-    method public void setupRelative(androidx.constraintlayout.core.motion.Motion!);
-    field public static final int DRAW_PATH_AS_CONFIGURED = 4; // 0x4
-    field public static final int DRAW_PATH_BASIC = 1; // 0x1
-    field public static final int DRAW_PATH_CARTESIAN = 3; // 0x3
-    field public static final int DRAW_PATH_NONE = 0; // 0x0
-    field public static final int DRAW_PATH_RECTANGLE = 5; // 0x5
-    field public static final int DRAW_PATH_RELATIVE = 2; // 0x2
-    field public static final int DRAW_PATH_SCREEN = 6; // 0x6
-    field public static final int HORIZONTAL_PATH_X = 2; // 0x2
-    field public static final int HORIZONTAL_PATH_Y = 3; // 0x3
-    field public static final int PATH_PERCENT = 0; // 0x0
-    field public static final int PATH_PERPENDICULAR = 1; // 0x1
-    field public static final int ROTATION_LEFT = 2; // 0x2
-    field public static final int ROTATION_RIGHT = 1; // 0x1
-    field public static final int VERTICAL_PATH_X = 4; // 0x4
-    field public static final int VERTICAL_PATH_Y = 5; // 0x5
-    field public String! mId;
-  }
-
-  public class MotionPaths implements java.lang.Comparable<androidx.constraintlayout.core.motion.MotionPaths!> {
-    ctor public MotionPaths();
-    ctor public MotionPaths(int, int, androidx.constraintlayout.core.motion.key.MotionKeyPosition!, androidx.constraintlayout.core.motion.MotionPaths!, androidx.constraintlayout.core.motion.MotionPaths!);
-    method public void applyParameters(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public int compareTo(androidx.constraintlayout.core.motion.MotionPaths!);
-    method public void configureRelativeTo(androidx.constraintlayout.core.motion.Motion!);
-    method public void setupRelative(androidx.constraintlayout.core.motion.Motion!, androidx.constraintlayout.core.motion.MotionPaths!);
-    field public static final int CARTESIAN = 0; // 0x0
-    field public static final boolean DEBUG = false;
-    field public static final boolean OLD_WAY = false;
-    field public static final int PERPENDICULAR = 1; // 0x1
-    field public static final int SCREEN = 2; // 0x2
-    field public static final String TAG = "MotionPaths";
-    field public String! mId;
-  }
-
-  public class MotionWidget implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public MotionWidget();
-    ctor public MotionWidget(androidx.constraintlayout.core.state.WidgetFrame!);
-    method public androidx.constraintlayout.core.motion.MotionWidget! findViewById(int);
-    method public float getAlpha();
-    method public int getBottom();
-    method public androidx.constraintlayout.core.motion.CustomVariable! getCustomAttribute(String!);
-    method public java.util.Set<java.lang.String!>! getCustomAttributeNames();
-    method public int getHeight();
-    method public int getId(String!);
-    method public int getLeft();
-    method public String! getName();
-    method public androidx.constraintlayout.core.motion.MotionWidget! getParent();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public int getRight();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public int getTop();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public float getValueAttributes(int);
-    method public int getVisibility();
-    method public androidx.constraintlayout.core.state.WidgetFrame! getWidgetFrame();
-    method public int getWidth();
-    method public int getX();
-    method public int getY();
-    method public void layout(int, int, int, int);
-    method public void setBounds(int, int, int, int);
-    method public void setCustomAttribute(String!, int, boolean);
-    method public void setCustomAttribute(String!, int, float);
-    method public void setCustomAttribute(String!, int, int);
-    method public void setCustomAttribute(String!, int, String!);
-    method public void setInterpolatedValue(androidx.constraintlayout.core.motion.CustomAttribute!, float[]!);
-    method public void setPivotX(float);
-    method public void setPivotY(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public boolean setValueAttributes(int, float);
-    method public boolean setValueMotion(int, float);
-    method public boolean setValueMotion(int, int);
-    method public boolean setValueMotion(int, String!);
-    method public void setVisibility(int);
-    method public void updateMotion(androidx.constraintlayout.core.motion.utils.TypedValues!);
-    field public static final int FILL_PARENT = -1; // 0xffffffff
-    field public static final int GONE_UNSET = -2147483648; // 0x80000000
-    field public static final int INVISIBLE = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int MATCH_PARENT = -1; // 0xffffffff
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int ROTATE_LEFT_OF_PORTRATE = 4; // 0x4
-    field public static final int ROTATE_NONE = 0; // 0x0
-    field public static final int ROTATE_PORTRATE_OF_LEFT = 2; // 0x2
-    field public static final int ROTATE_PORTRATE_OF_RIGHT = 1; // 0x1
-    field public static final int ROTATE_RIGHT_OF_PORTRATE = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int VISIBILITY_MODE_IGNORE = 1; // 0x1
-    field public static final int VISIBILITY_MODE_NORMAL = 0; // 0x0
-    field public static final int VISIBLE = 4; // 0x4
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-  }
-
-  public static class MotionWidget.Motion {
-    ctor public MotionWidget.Motion();
-    field public int mAnimateCircleAngleTo;
-    field public String! mAnimateRelativeTo;
-    field public int mDrawPath;
-    field public float mMotionStagger;
-    field public int mPathMotionArc;
-    field public float mPathRotate;
-    field public int mPolarRelativeTo;
-    field public int mQuantizeInterpolatorID;
-    field public String! mQuantizeInterpolatorString;
-    field public int mQuantizeInterpolatorType;
-    field public float mQuantizeMotionPhase;
-    field public int mQuantizeMotionSteps;
-    field public String! mTransitionEasing;
-  }
-
-  public static class MotionWidget.PropertySet {
-    ctor public MotionWidget.PropertySet();
-    field public float alpha;
-    field public float mProgress;
-    field public int mVisibilityMode;
-    field public int visibility;
-  }
-
-}
-
-package androidx.constraintlayout.core.motion.key {
-
-  public class MotionConstraintSet {
-    ctor public MotionConstraintSet();
-    field public static final int ROTATE_LEFT_OF_PORTRATE = 4; // 0x4
-    field public static final int ROTATE_NONE = 0; // 0x0
-    field public static final int ROTATE_PORTRATE_OF_LEFT = 2; // 0x2
-    field public static final int ROTATE_PORTRATE_OF_RIGHT = 1; // 0x1
-    field public static final int ROTATE_RIGHT_OF_PORTRATE = 3; // 0x3
-    field public String! mIdString;
-    field public int mRotate;
-  }
-
-  public abstract class MotionKey implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public MotionKey();
-    method public abstract void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public abstract androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public androidx.constraintlayout.core.motion.key.MotionKey! copy(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public abstract void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getFramePosition();
-    method public void setCustomAttribute(String!, int, boolean);
-    method public void setCustomAttribute(String!, int, float);
-    method public void setCustomAttribute(String!, int, int);
-    method public void setCustomAttribute(String!, int, String!);
-    method public void setFramePosition(int);
-    method public void setInterpolation(java.util.HashMap<java.lang.String!,java.lang.Integer!>!);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! setViewId(int);
-    field public static final String ALPHA = "alpha";
-    field public static final String CUSTOM = "CUSTOM";
-    field public static final String ELEVATION = "elevation";
-    field public static final String ROTATION = "rotationZ";
-    field public static final String ROTATION_X = "rotationX";
-    field public static final String SCALE_X = "scaleX";
-    field public static final String SCALE_Y = "scaleY";
-    field public static final String TRANSITION_PATH_ROTATE = "transitionPathRotate";
-    field public static final String TRANSLATION_X = "translationX";
-    field public static final String TRANSLATION_Y = "translationY";
-    field public static int UNSET;
-    field public static final String VISIBILITY = "visibility";
-    field public java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.CustomVariable!>! mCustom;
-    field public int mFramePosition;
-    field public int mType;
-  }
-
-  public class MotionKeyAttributes extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyAttributes();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getCurveFit();
-    method public int getId(String!);
-    method public void printAttributes();
-    field public static final int KEY_TYPE = 1; // 0x1
-  }
-
-  public class MotionKeyCycle extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyCycle();
-    method public void addCycleValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void dump();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    method public float getValue(String!);
-    method public void printAttributes();
-    field public static final int KEY_TYPE = 4; // 0x4
-    field public static final int SHAPE_BOUNCE = 6; // 0x6
-    field public static final int SHAPE_COS_WAVE = 5; // 0x5
-    field public static final int SHAPE_REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SHAPE_SAW_WAVE = 3; // 0x3
-    field public static final int SHAPE_SIN_WAVE = 0; // 0x0
-    field public static final int SHAPE_SQUARE_WAVE = 1; // 0x1
-    field public static final int SHAPE_TRIANGLE_WAVE = 2; // 0x2
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_PHASE = "wavePhase";
-    field public static final String WAVE_SHAPE = "waveShape";
-  }
-
-  public class MotionKeyPosition extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyPosition();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    method public boolean intersects(int, int, androidx.constraintlayout.core.motion.utils.FloatRect!, androidx.constraintlayout.core.motion.utils.FloatRect!, float, float);
-    method public void positionAttributes(androidx.constraintlayout.core.motion.MotionWidget!, androidx.constraintlayout.core.motion.utils.FloatRect!, androidx.constraintlayout.core.motion.utils.FloatRect!, float, float, String![]!, float[]!);
-    field protected static final float SELECTION_SLOPE = 20.0f;
-    field public static final int TYPE_CARTESIAN = 0; // 0x0
-    field public static final int TYPE_PATH = 1; // 0x1
-    field public static final int TYPE_SCREEN = 2; // 0x2
-    field public float mAltPercentX;
-    field public float mAltPercentY;
-    field public int mCurveFit;
-    field public int mDrawPath;
-    field public int mPathMotionArc;
-    field public float mPercentHeight;
-    field public float mPercentWidth;
-    field public float mPercentX;
-    field public float mPercentY;
-    field public int mPositionType;
-    field public String! mTransitionEasing;
-  }
-
-  public class MotionKeyTimeCycle extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyTimeCycle();
-    method public void addTimeValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public androidx.constraintlayout.core.motion.key.MotionKeyTimeCycle! copy(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    field public static final int KEY_TYPE = 3; // 0x3
-  }
-
-  public class MotionKeyTrigger extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyTrigger();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void conditionallyFire(float, androidx.constraintlayout.core.motion.MotionWidget!);
-    method public androidx.constraintlayout.core.motion.key.MotionKeyTrigger! copy(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    field public static final String CROSS = "CROSS";
-    field public static final int KEY_TYPE = 5; // 0x5
-    field public static final String NEGATIVE_CROSS = "negativeCross";
-    field public static final String POSITIVE_CROSS = "positiveCross";
-    field public static final String POST_LAYOUT = "postLayout";
-    field public static final String TRIGGER_COLLISION_ID = "triggerCollisionId";
-    field public static final String TRIGGER_COLLISION_VIEW = "triggerCollisionView";
-    field public static final String TRIGGER_ID = "triggerID";
-    field public static final String TRIGGER_RECEIVER = "triggerReceiver";
-    field public static final String TRIGGER_SLACK = "triggerSlack";
-    field public static final int TYPE_CROSS = 312; // 0x138
-    field public static final int TYPE_NEGATIVE_CROSS = 310; // 0x136
-    field public static final int TYPE_POSITIVE_CROSS = 309; // 0x135
-    field public static final int TYPE_POST_LAYOUT = 304; // 0x130
-    field public static final int TYPE_TRIGGER_COLLISION_ID = 307; // 0x133
-    field public static final int TYPE_TRIGGER_COLLISION_VIEW = 306; // 0x132
-    field public static final int TYPE_TRIGGER_ID = 308; // 0x134
-    field public static final int TYPE_TRIGGER_RECEIVER = 311; // 0x137
-    field public static final int TYPE_TRIGGER_SLACK = 305; // 0x131
-    field public static final int TYPE_VIEW_TRANSITION_ON_CROSS = 301; // 0x12d
-    field public static final int TYPE_VIEW_TRANSITION_ON_NEGATIVE_CROSS = 303; // 0x12f
-    field public static final int TYPE_VIEW_TRANSITION_ON_POSITIVE_CROSS = 302; // 0x12e
-    field public static final String VIEW_TRANSITION_ON_CROSS = "viewTransitionOnCross";
-    field public static final String VIEW_TRANSITION_ON_NEGATIVE_CROSS = "viewTransitionOnNegativeCross";
-    field public static final String VIEW_TRANSITION_ON_POSITIVE_CROSS = "viewTransitionOnPositiveCross";
-  }
-
-}
-
-package androidx.constraintlayout.core.motion.parse {
-
-  public class KeyParser {
-    ctor public KeyParser();
-    method public static void main(String![]!);
-    method public static androidx.constraintlayout.core.motion.utils.TypedBundle! parseAttributes(String!);
-  }
-
-}
-
-package androidx.constraintlayout.core.motion.utils {
-
-  public class ArcCurveFit extends androidx.constraintlayout.core.motion.utils.CurveFit {
-    ctor public ArcCurveFit(int[]!, double[]!, double[]![]!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getSlope(double, double[]!);
-    method public double getSlope(double, int);
-    method public double[]! getTimePoints();
-    field public static final int ARC_ABOVE = 5; // 0x5
-    field public static final int ARC_BELOW = 4; // 0x4
-    field public static final int ARC_START_FLIP = 3; // 0x3
-    field public static final int ARC_START_HORIZONTAL = 2; // 0x2
-    field public static final int ARC_START_LINEAR = 0; // 0x0
-    field public static final int ARC_START_VERTICAL = 1; // 0x1
-  }
-
-  public abstract class CurveFit {
-    ctor public CurveFit();
-    method public static androidx.constraintlayout.core.motion.utils.CurveFit! get(int, double[]!, double[]![]!);
-    method public static androidx.constraintlayout.core.motion.utils.CurveFit! getArc(int[]!, double[]!, double[]![]!);
-    method public abstract void getPos(double, double[]!);
-    method public abstract void getPos(double, float[]!);
-    method public abstract double getPos(double, int);
-    method public abstract void getSlope(double, double[]!);
-    method public abstract double getSlope(double, int);
-    method public abstract double[]! getTimePoints();
-    field public static final int CONSTANT = 2; // 0x2
-    field public static final int LINEAR = 1; // 0x1
-    field public static final int SPLINE = 0; // 0x0
-  }
-
-  public interface DifferentialInterpolator {
-    method public float getInterpolation(float);
-    method public float getVelocity();
-  }
-
-  public class Easing {
-    ctor public Easing();
-    method public double get(double);
-    method public double getDiff(double);
-    method public static androidx.constraintlayout.core.motion.utils.Easing! getInterpolator(String!);
-    field public static String![]! NAMED_EASING;
-  }
-
-  public class FloatRect {
-    ctor public FloatRect();
-    method public final float centerX();
-    method public final float centerY();
-    field public float bottom;
-    field public float left;
-    field public float right;
-    field public float top;
-  }
-
-  public class HyperSpline {
-    ctor public HyperSpline();
-    ctor public HyperSpline(double[]![]!);
-    method public double approxLength(androidx.constraintlayout.core.motion.utils.HyperSpline.Cubic![]!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getVelocity(double, double[]!);
-    method public void setup(double[]![]!);
-  }
-
-  public static class HyperSpline.Cubic {
-    ctor public HyperSpline.Cubic(double, double, double, double);
-    method public double eval(double);
-    method public double vel(double);
-  }
-
-  public class KeyCache {
-    ctor public KeyCache();
-    method public float getFloatValue(Object!, String!, int);
-    method public void setFloatValue(Object!, String!, int, float);
-  }
-
-  public abstract class KeyCycleOscillator {
-    ctor public KeyCycleOscillator();
-    method public float get(float);
-    method public androidx.constraintlayout.core.motion.utils.CurveFit! getCurveFit();
-    method public float getSlope(float);
-    method public static androidx.constraintlayout.core.motion.utils.KeyCycleOscillator! makeWidgetCycle(String!);
-    method protected void setCustom(Object!);
-    method public void setPoint(int, int, String!, int, float, float, float, float);
-    method public void setPoint(int, int, String!, int, float, float, float, float, Object!);
-    method public void setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float);
-    method public void setType(String!);
-    method public void setup(float);
-    method public boolean variesByPath();
-    field public int mVariesBy;
-  }
-
-  public static class KeyCycleOscillator.PathRotateSet extends androidx.constraintlayout.core.motion.utils.KeyCycleOscillator {
-    ctor public KeyCycleOscillator.PathRotateSet(String!);
-    method public void setPathRotate(androidx.constraintlayout.core.motion.MotionWidget!, float, double, double);
-  }
-
-  public class KeyFrameArray {
-    ctor public KeyFrameArray();
-  }
-
-  public static class KeyFrameArray.CustomArray {
-    ctor public KeyFrameArray.CustomArray();
-    method public void append(int, androidx.constraintlayout.core.motion.CustomAttribute!);
-    method public void clear();
-    method public void dump();
-    method public int keyAt(int);
-    method public void remove(int);
-    method public int size();
-    method public androidx.constraintlayout.core.motion.CustomAttribute! valueAt(int);
-  }
-
-  public static class KeyFrameArray.CustomVar {
-    ctor public KeyFrameArray.CustomVar();
-    method public void append(int, androidx.constraintlayout.core.motion.CustomVariable!);
-    method public void clear();
-    method public void dump();
-    method public int keyAt(int);
-    method public void remove(int);
-    method public int size();
-    method public androidx.constraintlayout.core.motion.CustomVariable! valueAt(int);
-  }
-
-  public class LinearCurveFit extends androidx.constraintlayout.core.motion.utils.CurveFit {
-    ctor public LinearCurveFit(double[]!, double[]![]!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getSlope(double, double[]!);
-    method public double getSlope(double, int);
-    method public double[]! getTimePoints();
-  }
-
-  public class MonotonicCurveFit extends androidx.constraintlayout.core.motion.utils.CurveFit {
-    ctor public MonotonicCurveFit(double[]!, double[]![]!);
-    method public static androidx.constraintlayout.core.motion.utils.MonotonicCurveFit! buildWave(String!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getSlope(double, double[]!);
-    method public double getSlope(double, int);
-    method public double[]! getTimePoints();
-  }
-
-  public class Oscillator {
-    ctor public Oscillator();
-    method public void addPoint(double, float);
-    method public double getSlope(double, double, double);
-    method public double getValue(double, double);
-    method public void normalize();
-    method public void setType(int, String!);
-    field public static final int BOUNCE = 6; // 0x6
-    field public static final int COS_WAVE = 5; // 0x5
-    field public static final int CUSTOM = 7; // 0x7
-    field public static final int REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SAW_WAVE = 3; // 0x3
-    field public static final int SIN_WAVE = 0; // 0x0
-    field public static final int SQUARE_WAVE = 1; // 0x1
-    field public static String! TAG;
-    field public static final int TRIANGLE_WAVE = 2; // 0x2
-  }
-
-  public class Rect {
-    ctor public Rect();
-    method public int height();
-    method public int width();
-    field public int bottom;
-    field public int left;
-    field public int right;
-    field public int top;
-  }
-
-  public class Schlick extends androidx.constraintlayout.core.motion.utils.Easing {
-  }
-
-  public abstract class SplineSet {
-    ctor public SplineSet();
-    method public float get(float);
-    method public androidx.constraintlayout.core.motion.utils.CurveFit! getCurveFit();
-    method public float getSlope(float);
-    method public static androidx.constraintlayout.core.motion.utils.SplineSet! makeCustomSpline(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomArray!);
-    method public static androidx.constraintlayout.core.motion.utils.SplineSet! makeCustomSplineSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomVar!);
-    method public static androidx.constraintlayout.core.motion.utils.SplineSet! makeSpline(String!, long);
-    method public void setPoint(int, float);
-    method public void setProperty(androidx.constraintlayout.core.motion.utils.TypedValues!, float);
-    method public void setType(String!);
-    method public void setup(int);
-    field protected androidx.constraintlayout.core.motion.utils.CurveFit! mCurveFit;
-    field protected int[]! mTimePoints;
-    field protected float[]! mValues;
-  }
-
-  public static class SplineSet.CustomSet extends androidx.constraintlayout.core.motion.utils.SplineSet {
-    ctor public SplineSet.CustomSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomArray!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomAttribute!);
-    method public void setProperty(androidx.constraintlayout.core.state.WidgetFrame!, float);
-  }
-
-  public static class SplineSet.CustomSpline extends androidx.constraintlayout.core.motion.utils.SplineSet {
-    ctor public SplineSet.CustomSpline(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomVar!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomVariable!);
-    method public void setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float);
-  }
-
-  public class SpringStopEngine implements androidx.constraintlayout.core.motion.utils.StopEngine {
-    ctor public SpringStopEngine();
-    method public String! debug(String!, float);
-    method public float getAcceleration();
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-    method public void springConfig(float, float, float, float, float, float, float, int);
-  }
-
-  public class StepCurve extends androidx.constraintlayout.core.motion.utils.Easing {
-  }
-
-  public interface StopEngine {
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-  }
-
-  public class StopLogicEngine implements androidx.constraintlayout.core.motion.utils.StopEngine {
-    ctor public StopLogicEngine();
-    method public void config(float, float, float, float, float, float);
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-  }
-
-  public static class StopLogicEngine.Decelerate implements androidx.constraintlayout.core.motion.utils.StopEngine {
-    ctor public StopLogicEngine.Decelerate();
-    method public void config(float, float, float);
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-  }
-
-  public abstract class TimeCycleSplineSet {
-    ctor public TimeCycleSplineSet();
-    method protected float calcWave(float);
-    method public androidx.constraintlayout.core.motion.utils.CurveFit! getCurveFit();
-    method public void setPoint(int, float, float, int, float);
-    method protected void setStartTime(long);
-    method public void setType(String!);
-    method public void setup(int);
-    field protected static final int CURVE_OFFSET = 2; // 0x2
-    field protected static final int CURVE_PERIOD = 1; // 0x1
-    field protected static final int CURVE_VALUE = 0; // 0x0
-    field protected float[]! mCache;
-    field protected boolean mContinue;
-    field protected int mCount;
-    field protected androidx.constraintlayout.core.motion.utils.CurveFit! mCurveFit;
-    field protected float mLastCycle;
-    field protected long mLastTime;
-    field protected int[]! mTimePoints;
-    field protected String! mType;
-    field protected float[]![]! mValues;
-    field protected int mWaveShape;
-    field protected static float sVal2PI;
-  }
-
-  public static class TimeCycleSplineSet.CustomSet extends androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet {
-    ctor public TimeCycleSplineSet.CustomSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomArray!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomAttribute!, float, int, float);
-    method public boolean setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  public static class TimeCycleSplineSet.CustomVarSet extends androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet {
-    ctor public TimeCycleSplineSet.CustomVarSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomVar!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomVariable!, float, int, float);
-    method public boolean setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  protected static class TimeCycleSplineSet.Sort {
-    ctor protected TimeCycleSplineSet.Sort();
-  }
-
-  public class TypedBundle {
-    ctor public TypedBundle();
-    method public void add(int, boolean);
-    method public void add(int, float);
-    method public void add(int, int);
-    method public void add(int, String!);
-    method public void addIfNotNull(int, String!);
-    method public void applyDelta(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void applyDelta(androidx.constraintlayout.core.motion.utils.TypedValues!);
-    method public void clear();
-    method public int getInteger(int);
-  }
-
-  public interface TypedValues {
-    method public int getId(String!);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    field public static final int BOOLEAN_MASK = 1; // 0x1
-    field public static final int FLOAT_MASK = 4; // 0x4
-    field public static final int INT_MASK = 2; // 0x2
-    field public static final int STRING_MASK = 8; // 0x8
-    field public static final String S_CUSTOM = "CUSTOM";
-    field public static final int TYPE_FRAME_POSITION = 100; // 0x64
-    field public static final int TYPE_TARGET = 101; // 0x65
-  }
-
-  public static interface TypedValues.AttributesType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyAttributes";
-    field public static final String S_ALPHA = "alpha";
-    field public static final String S_CURVE_FIT = "curveFit";
-    field public static final String S_CUSTOM = "CUSTOM";
-    field public static final String S_EASING = "easing";
-    field public static final String S_ELEVATION = "elevation";
-    field public static final String S_FRAME = "frame";
-    field public static final String S_PATH_ROTATE = "pathRotate";
-    field public static final String S_PIVOT_TARGET = "pivotTarget";
-    field public static final String S_PIVOT_X = "pivotX";
-    field public static final String S_PIVOT_Y = "pivotY";
-    field public static final String S_PROGRESS = "progress";
-    field public static final String S_ROTATION_X = "rotationX";
-    field public static final String S_ROTATION_Y = "rotationY";
-    field public static final String S_ROTATION_Z = "rotationZ";
-    field public static final String S_SCALE_X = "scaleX";
-    field public static final String S_SCALE_Y = "scaleY";
-    field public static final String S_TARGET = "target";
-    field public static final String S_TRANSLATION_X = "translationX";
-    field public static final String S_TRANSLATION_Y = "translationY";
-    field public static final String S_TRANSLATION_Z = "translationZ";
-    field public static final String S_VISIBILITY = "visibility";
-    field public static final int TYPE_ALPHA = 303; // 0x12f
-    field public static final int TYPE_CURVE_FIT = 301; // 0x12d
-    field public static final int TYPE_EASING = 317; // 0x13d
-    field public static final int TYPE_ELEVATION = 307; // 0x133
-    field public static final int TYPE_PATH_ROTATE = 316; // 0x13c
-    field public static final int TYPE_PIVOT_TARGET = 318; // 0x13e
-    field public static final int TYPE_PIVOT_X = 313; // 0x139
-    field public static final int TYPE_PIVOT_Y = 314; // 0x13a
-    field public static final int TYPE_PROGRESS = 315; // 0x13b
-    field public static final int TYPE_ROTATION_X = 308; // 0x134
-    field public static final int TYPE_ROTATION_Y = 309; // 0x135
-    field public static final int TYPE_ROTATION_Z = 310; // 0x136
-    field public static final int TYPE_SCALE_X = 311; // 0x137
-    field public static final int TYPE_SCALE_Y = 312; // 0x138
-    field public static final int TYPE_TRANSLATION_X = 304; // 0x130
-    field public static final int TYPE_TRANSLATION_Y = 305; // 0x131
-    field public static final int TYPE_TRANSLATION_Z = 306; // 0x132
-    field public static final int TYPE_VISIBILITY = 302; // 0x12e
-  }
-
-  public static interface TypedValues.Custom {
-    method public static int getId(String!);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "Custom";
-    field public static final String S_BOOLEAN = "boolean";
-    field public static final String S_COLOR = "color";
-    field public static final String S_DIMENSION = "dimension";
-    field public static final String S_FLOAT = "float";
-    field public static final String S_INT = "integer";
-    field public static final String S_REFERENCE = "reference";
-    field public static final String S_STRING = "string";
-    field public static final int TYPE_BOOLEAN = 904; // 0x388
-    field public static final int TYPE_COLOR = 902; // 0x386
-    field public static final int TYPE_DIMENSION = 905; // 0x389
-    field public static final int TYPE_FLOAT = 901; // 0x385
-    field public static final int TYPE_INT = 900; // 0x384
-    field public static final int TYPE_REFERENCE = 906; // 0x38a
-    field public static final int TYPE_STRING = 903; // 0x387
-  }
-
-  public static interface TypedValues.CycleType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyCycle";
-    field public static final String S_ALPHA = "alpha";
-    field public static final String S_CURVE_FIT = "curveFit";
-    field public static final String S_CUSTOM_WAVE_SHAPE = "customWave";
-    field public static final String S_EASING = "easing";
-    field public static final String S_ELEVATION = "elevation";
-    field public static final String S_PATH_ROTATE = "pathRotate";
-    field public static final String S_PIVOT_X = "pivotX";
-    field public static final String S_PIVOT_Y = "pivotY";
-    field public static final String S_PROGRESS = "progress";
-    field public static final String S_ROTATION_X = "rotationX";
-    field public static final String S_ROTATION_Y = "rotationY";
-    field public static final String S_ROTATION_Z = "rotationZ";
-    field public static final String S_SCALE_X = "scaleX";
-    field public static final String S_SCALE_Y = "scaleY";
-    field public static final String S_TRANSLATION_X = "translationX";
-    field public static final String S_TRANSLATION_Y = "translationY";
-    field public static final String S_TRANSLATION_Z = "translationZ";
-    field public static final String S_VISIBILITY = "visibility";
-    field public static final String S_WAVE_OFFSET = "offset";
-    field public static final String S_WAVE_PERIOD = "period";
-    field public static final String S_WAVE_PHASE = "phase";
-    field public static final String S_WAVE_SHAPE = "waveShape";
-    field public static final int TYPE_ALPHA = 403; // 0x193
-    field public static final int TYPE_CURVE_FIT = 401; // 0x191
-    field public static final int TYPE_CUSTOM_WAVE_SHAPE = 422; // 0x1a6
-    field public static final int TYPE_EASING = 420; // 0x1a4
-    field public static final int TYPE_ELEVATION = 307; // 0x133
-    field public static final int TYPE_PATH_ROTATE = 416; // 0x1a0
-    field public static final int TYPE_PIVOT_X = 313; // 0x139
-    field public static final int TYPE_PIVOT_Y = 314; // 0x13a
-    field public static final int TYPE_PROGRESS = 315; // 0x13b
-    field public static final int TYPE_ROTATION_X = 308; // 0x134
-    field public static final int TYPE_ROTATION_Y = 309; // 0x135
-    field public static final int TYPE_ROTATION_Z = 310; // 0x136
-    field public static final int TYPE_SCALE_X = 311; // 0x137
-    field public static final int TYPE_SCALE_Y = 312; // 0x138
-    field public static final int TYPE_TRANSLATION_X = 304; // 0x130
-    field public static final int TYPE_TRANSLATION_Y = 305; // 0x131
-    field public static final int TYPE_TRANSLATION_Z = 306; // 0x132
-    field public static final int TYPE_VISIBILITY = 402; // 0x192
-    field public static final int TYPE_WAVE_OFFSET = 424; // 0x1a8
-    field public static final int TYPE_WAVE_PERIOD = 423; // 0x1a7
-    field public static final int TYPE_WAVE_PHASE = 425; // 0x1a9
-    field public static final int TYPE_WAVE_SHAPE = 421; // 0x1a5
-  }
-
-  public static interface TypedValues.MotionScene {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "MotionScene";
-    field public static final String S_DEFAULT_DURATION = "defaultDuration";
-    field public static final String S_LAYOUT_DURING_TRANSITION = "layoutDuringTransition";
-    field public static final int TYPE_DEFAULT_DURATION = 600; // 0x258
-    field public static final int TYPE_LAYOUT_DURING_TRANSITION = 601; // 0x259
-  }
-
-  public static interface TypedValues.MotionType {
-    method public static int getId(String!);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "Motion";
-    field public static final String S_ANIMATE_CIRCLEANGLE_TO = "AnimateCircleAngleTo";
-    field public static final String S_ANIMATE_RELATIVE_TO = "AnimateRelativeTo";
-    field public static final String S_DRAW_PATH = "DrawPath";
-    field public static final String S_EASING = "TransitionEasing";
-    field public static final String S_PATHMOTION_ARC = "PathMotionArc";
-    field public static final String S_PATH_ROTATE = "PathRotate";
-    field public static final String S_POLAR_RELATIVETO = "PolarRelativeTo";
-    field public static final String S_QUANTIZE_INTERPOLATOR = "QuantizeInterpolator";
-    field public static final String S_QUANTIZE_INTERPOLATOR_ID = "QuantizeInterpolatorID";
-    field public static final String S_QUANTIZE_INTERPOLATOR_TYPE = "QuantizeInterpolatorType";
-    field public static final String S_QUANTIZE_MOTIONSTEPS = "QuantizeMotionSteps";
-    field public static final String S_QUANTIZE_MOTION_PHASE = "QuantizeMotionPhase";
-    field public static final String S_STAGGER = "Stagger";
-    field public static final int TYPE_ANIMATE_CIRCLEANGLE_TO = 606; // 0x25e
-    field public static final int TYPE_ANIMATE_RELATIVE_TO = 605; // 0x25d
-    field public static final int TYPE_DRAW_PATH = 608; // 0x260
-    field public static final int TYPE_EASING = 603; // 0x25b
-    field public static final int TYPE_PATHMOTION_ARC = 607; // 0x25f
-    field public static final int TYPE_PATH_ROTATE = 601; // 0x259
-    field public static final int TYPE_POLAR_RELATIVETO = 609; // 0x261
-    field public static final int TYPE_QUANTIZE_INTERPOLATOR = 604; // 0x25c
-    field public static final int TYPE_QUANTIZE_INTERPOLATOR_ID = 612; // 0x264
-    field public static final int TYPE_QUANTIZE_INTERPOLATOR_TYPE = 611; // 0x263
-    field public static final int TYPE_QUANTIZE_MOTIONSTEPS = 610; // 0x262
-    field public static final int TYPE_QUANTIZE_MOTION_PHASE = 602; // 0x25a
-    field public static final int TYPE_STAGGER = 600; // 0x258
-  }
-
-  public static interface TypedValues.OnSwipe {
-    field public static final String AUTOCOMPLETE_MODE = "autocompletemode";
-    field public static final String![]! AUTOCOMPLETE_MODE_ENUM;
-    field public static final String DRAG_DIRECTION = "dragdirection";
-    field public static final String DRAG_SCALE = "dragscale";
-    field public static final String DRAG_THRESHOLD = "dragthreshold";
-    field public static final String LIMIT_BOUNDS_TO = "limitboundsto";
-    field public static final String MAX_ACCELERATION = "maxacceleration";
-    field public static final String MAX_VELOCITY = "maxvelocity";
-    field public static final String MOVE_WHEN_SCROLLAT_TOP = "movewhenscrollattop";
-    field public static final String NESTED_SCROLL_FLAGS = "nestedscrollflags";
-    field public static final String![]! NESTED_SCROLL_FLAGS_ENUM;
-    field public static final String ON_TOUCH_UP = "ontouchup";
-    field public static final String![]! ON_TOUCH_UP_ENUM;
-    field public static final String ROTATION_CENTER_ID = "rotationcenterid";
-    field public static final String SPRINGS_TOP_THRESHOLD = "springstopthreshold";
-    field public static final String SPRING_BOUNDARY = "springboundary";
-    field public static final String![]! SPRING_BOUNDARY_ENUM;
-    field public static final String SPRING_DAMPING = "springdamping";
-    field public static final String SPRING_MASS = "springmass";
-    field public static final String SPRING_STIFFNESS = "springstiffness";
-    field public static final String TOUCH_ANCHOR_ID = "touchanchorid";
-    field public static final String TOUCH_ANCHOR_SIDE = "touchanchorside";
-    field public static final String TOUCH_REGION_ID = "touchregionid";
-  }
-
-  public static interface TypedValues.PositionType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyPosition";
-    field public static final String S_DRAWPATH = "drawPath";
-    field public static final String S_PERCENT_HEIGHT = "percentHeight";
-    field public static final String S_PERCENT_WIDTH = "percentWidth";
-    field public static final String S_PERCENT_X = "percentX";
-    field public static final String S_PERCENT_Y = "percentY";
-    field public static final String S_SIZE_PERCENT = "sizePercent";
-    field public static final String S_TRANSITION_EASING = "transitionEasing";
-    field public static final int TYPE_CURVE_FIT = 508; // 0x1fc
-    field public static final int TYPE_DRAWPATH = 502; // 0x1f6
-    field public static final int TYPE_PATH_MOTION_ARC = 509; // 0x1fd
-    field public static final int TYPE_PERCENT_HEIGHT = 504; // 0x1f8
-    field public static final int TYPE_PERCENT_WIDTH = 503; // 0x1f7
-    field public static final int TYPE_PERCENT_X = 506; // 0x1fa
-    field public static final int TYPE_PERCENT_Y = 507; // 0x1fb
-    field public static final int TYPE_POSITION_TYPE = 510; // 0x1fe
-    field public static final int TYPE_SIZE_PERCENT = 505; // 0x1f9
-    field public static final int TYPE_TRANSITION_EASING = 501; // 0x1f5
-  }
-
-  public static interface TypedValues.TransitionType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "Transitions";
-    field public static final String S_AUTO_TRANSITION = "autoTransition";
-    field public static final String S_DURATION = "duration";
-    field public static final String S_FROM = "from";
-    field public static final String S_INTERPOLATOR = "motionInterpolator";
-    field public static final String S_PATH_MOTION_ARC = "pathMotionArc";
-    field public static final String S_STAGGERED = "staggered";
-    field public static final String S_TO = "to";
-    field public static final String S_TRANSITION_FLAGS = "transitionFlags";
-    field public static final int TYPE_AUTO_TRANSITION = 704; // 0x2c0
-    field public static final int TYPE_DURATION = 700; // 0x2bc
-    field public static final int TYPE_FROM = 701; // 0x2bd
-    field public static final int TYPE_INTERPOLATOR = 705; // 0x2c1
-    field public static final int TYPE_PATH_MOTION_ARC = 509; // 0x1fd
-    field public static final int TYPE_STAGGERED = 706; // 0x2c2
-    field public static final int TYPE_TO = 702; // 0x2be
-    field public static final int TYPE_TRANSITION_FLAGS = 707; // 0x2c3
-  }
-
-  public static interface TypedValues.TriggerType {
-    method public static int getId(String!);
-    field public static final String CROSS = "CROSS";
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyTrigger";
-    field public static final String NEGATIVE_CROSS = "negativeCross";
-    field public static final String POSITIVE_CROSS = "positiveCross";
-    field public static final String POST_LAYOUT = "postLayout";
-    field public static final String TRIGGER_COLLISION_ID = "triggerCollisionId";
-    field public static final String TRIGGER_COLLISION_VIEW = "triggerCollisionView";
-    field public static final String TRIGGER_ID = "triggerID";
-    field public static final String TRIGGER_RECEIVER = "triggerReceiver";
-    field public static final String TRIGGER_SLACK = "triggerSlack";
-    field public static final int TYPE_CROSS = 312; // 0x138
-    field public static final int TYPE_NEGATIVE_CROSS = 310; // 0x136
-    field public static final int TYPE_POSITIVE_CROSS = 309; // 0x135
-    field public static final int TYPE_POST_LAYOUT = 304; // 0x130
-    field public static final int TYPE_TRIGGER_COLLISION_ID = 307; // 0x133
-    field public static final int TYPE_TRIGGER_COLLISION_VIEW = 306; // 0x132
-    field public static final int TYPE_TRIGGER_ID = 308; // 0x134
-    field public static final int TYPE_TRIGGER_RECEIVER = 311; // 0x137
-    field public static final int TYPE_TRIGGER_SLACK = 305; // 0x131
-    field public static final int TYPE_VIEW_TRANSITION_ON_CROSS = 301; // 0x12d
-    field public static final int TYPE_VIEW_TRANSITION_ON_NEGATIVE_CROSS = 303; // 0x12f
-    field public static final int TYPE_VIEW_TRANSITION_ON_POSITIVE_CROSS = 302; // 0x12e
-    field public static final String VIEW_TRANSITION_ON_CROSS = "viewTransitionOnCross";
-    field public static final String VIEW_TRANSITION_ON_NEGATIVE_CROSS = "viewTransitionOnNegativeCross";
-    field public static final String VIEW_TRANSITION_ON_POSITIVE_CROSS = "viewTransitionOnPositiveCross";
-  }
-
-  public class Utils {
-    ctor public Utils();
-    method public int getInterpolatedColor(float[]!);
-    method public static void log(String!);
-    method public static void log(String!, String!);
-    method public static void logStack(String!, int);
-    method public static void loge(String!, String!);
-    method public static int rgbaTocColor(float, float, float, float);
-    method public static void setDebugHandle(androidx.constraintlayout.core.motion.utils.Utils.DebugHandle!);
-    method public static void socketSend(String!);
-  }
-
-  public static interface Utils.DebugHandle {
-    method public void message(String!);
-  }
-
-  public class VelocityMatrix {
-    ctor public VelocityMatrix();
-    method public void applyTransform(float, float, int, int, float[]!);
-    method public void clear();
-    method public void setRotationVelocity(androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, float);
-    method public void setRotationVelocity(androidx.constraintlayout.core.motion.utils.SplineSet!, float);
-    method public void setScaleVelocity(androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, float);
-    method public void setScaleVelocity(androidx.constraintlayout.core.motion.utils.SplineSet!, androidx.constraintlayout.core.motion.utils.SplineSet!, float);
-    method public void setTranslationVelocity(androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, float);
-    method public void setTranslationVelocity(androidx.constraintlayout.core.motion.utils.SplineSet!, androidx.constraintlayout.core.motion.utils.SplineSet!, float);
-  }
-
-  public class ViewState {
-    ctor public ViewState();
-    method public void getState(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public int height();
-    method public int width();
-    field public int bottom;
-    field public int left;
-    field public int right;
-    field public float rotation;
-    field public int top;
-  }
-
-}
-
-package androidx.constraintlayout.core.parser {
-
-  public class CLArray extends androidx.constraintlayout.core.parser.CLContainer {
-    ctor public CLArray(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-  }
-
-  public class CLContainer extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLContainer(char[]!);
-    method public void add(androidx.constraintlayout.core.parser.CLElement!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public void clear();
-    method public androidx.constraintlayout.core.parser.CLContainer clone();
-    method public androidx.constraintlayout.core.parser.CLElement! get(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLElement! get(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLArray! getArray(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLArray! getArray(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLArray! getArrayOrCreate(String!);
-    method public androidx.constraintlayout.core.parser.CLArray! getArrayOrNull(String!);
-    method public boolean getBoolean(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public boolean getBoolean(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public float getFloat(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public float getFloat(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public float getFloatOrNaN(String!);
-    method public int getInt(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public int getInt(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLObject! getObject(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLObject! getObject(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLObject! getObjectOrNull(String!);
-    method public androidx.constraintlayout.core.parser.CLElement! getOrNull(int);
-    method public androidx.constraintlayout.core.parser.CLElement! getOrNull(String!);
-    method public String! getString(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public String! getString(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public String! getStringOrNull(int);
-    method public String! getStringOrNull(String!);
-    method public boolean has(String!);
-    method public java.util.ArrayList<java.lang.String!>! names();
-    method public void put(String!, androidx.constraintlayout.core.parser.CLElement!);
-    method public void putNumber(String!, float);
-    method public void putString(String!, String!);
-    method public void remove(String!);
-    method public int size();
-  }
-
-  public class CLElement implements java.lang.Cloneable {
-    ctor public CLElement(char[]!);
-    method protected void addIndent(StringBuilder!, int);
-    method public androidx.constraintlayout.core.parser.CLElement clone();
-    method public String! content();
-    method public androidx.constraintlayout.core.parser.CLElement! getContainer();
-    method protected String! getDebugName();
-    method public long getEnd();
-    method public float getFloat();
-    method public int getInt();
-    method public int getLine();
-    method public long getStart();
-    method protected String! getStrClass();
-    method public boolean hasContent();
-    method public boolean isDone();
-    method public boolean isStarted();
-    method public boolean notStarted();
-    method public void setContainer(androidx.constraintlayout.core.parser.CLContainer!);
-    method public void setEnd(long);
-    method public void setLine(int);
-    method public void setStart(long);
-    method protected String! toFormattedJSON(int, int);
-    method protected String! toJSON();
-    field protected androidx.constraintlayout.core.parser.CLContainer! mContainer;
-    field protected long mEnd;
-    field protected long mStart;
-    field protected static int sBaseIndent;
-    field protected static int sMaxLine;
-  }
-
-  public class CLKey extends androidx.constraintlayout.core.parser.CLContainer {
-    ctor public CLKey(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(String!, androidx.constraintlayout.core.parser.CLElement!);
-    method public String! getName();
-    method public androidx.constraintlayout.core.parser.CLElement! getValue();
-    method public void set(androidx.constraintlayout.core.parser.CLElement!);
-  }
-
-  public class CLNumber extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLNumber(char[]!);
-    ctor public CLNumber(float);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public boolean isInt();
-    method public void putValue(float);
-  }
-
-  public class CLObject extends androidx.constraintlayout.core.parser.CLContainer implements java.lang.Iterable<androidx.constraintlayout.core.parser.CLKey!> {
-    ctor public CLObject(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLObject! allocate(char[]!);
-    method public androidx.constraintlayout.core.parser.CLObject clone();
-    method public java.util.Iterator<androidx.constraintlayout.core.parser.CLKey!>! iterator();
-    method public String! toFormattedJSON();
-    method public String! toFormattedJSON(int, int);
-    method public String! toJSON();
-  }
-
-  public class CLParser {
-    ctor public CLParser(String!);
-    method public androidx.constraintlayout.core.parser.CLObject! parse() throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static androidx.constraintlayout.core.parser.CLObject! parse(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-  }
-
-  public class CLParsingException extends java.lang.Exception {
-    ctor public CLParsingException(String!, androidx.constraintlayout.core.parser.CLElement!);
-    method public String! reason();
-  }
-
-  public class CLString extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLString(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLString from(String);
-  }
-
-  public class CLToken extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLToken(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public boolean getBoolean() throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLToken.Type! getType();
-    method public boolean isNull() throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public boolean validate(char, long);
-  }
-
-}
-
-package androidx.constraintlayout.core.state {
-
-  public class ConstraintReference implements androidx.constraintlayout.core.state.Reference {
-    ctor public ConstraintReference(androidx.constraintlayout.core.state.State!);
-    method public void addCustomColor(String!, int);
-    method public void addCustomFloat(String!, float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! alpha(float);
-    method public void apply();
-    method public void applyWidgetConstraints();
-    method public androidx.constraintlayout.core.state.ConstraintReference! baseline();
-    method public androidx.constraintlayout.core.state.ConstraintReference! baselineToBaseline(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! baselineToBottom(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! baselineToTop(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! bias(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! bottom();
-    method public androidx.constraintlayout.core.state.ConstraintReference! bottomToBottom(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! bottomToTop(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! centerHorizontally(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! centerVertically(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! circularConstraint(Object!, float, float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! clear();
-    method public androidx.constraintlayout.core.state.ConstraintReference! clearAll();
-    method public androidx.constraintlayout.core.state.ConstraintReference! clearHorizontal();
-    method public androidx.constraintlayout.core.state.ConstraintReference! clearVertical();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! createConstraintWidget();
-    method public androidx.constraintlayout.core.state.ConstraintReference! end();
-    method public androidx.constraintlayout.core.state.ConstraintReference! endToEnd(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! endToStart(Object!);
-    method public float getAlpha();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public androidx.constraintlayout.core.state.helpers.Facade! getFacade();
-    method public androidx.constraintlayout.core.state.Dimension! getHeight();
-    method public int getHorizontalChainStyle();
-    method public float getHorizontalChainWeight();
-    method public Object! getKey();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public String! getTag();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public int getVerticalChainStyle(int);
-    method public float getVerticalChainWeight();
-    method public Object! getView();
-    method public androidx.constraintlayout.core.state.Dimension! getWidth();
-    method public androidx.constraintlayout.core.state.ConstraintReference! height(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! horizontalBias(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! left();
-    method public androidx.constraintlayout.core.state.ConstraintReference! leftToLeft(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! leftToRight(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! margin(int);
-    method public androidx.constraintlayout.core.state.ConstraintReference! margin(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! marginGone(int);
-    method public androidx.constraintlayout.core.state.ConstraintReference! marginGone(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! pivotX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! pivotY(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! right();
-    method public androidx.constraintlayout.core.state.ConstraintReference! rightToLeft(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rightToRight(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rotationX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rotationY(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rotationZ(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! scaleX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! scaleY(float);
-    method public void setConstraintWidget(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setFacade(androidx.constraintlayout.core.state.helpers.Facade!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! setHeight(androidx.constraintlayout.core.state.Dimension!);
-    method public void setHorizontalChainStyle(int);
-    method public void setHorizontalChainWeight(float);
-    method public void setKey(Object!);
-    method public void setTag(String!);
-    method public void setVerticalChainStyle(int);
-    method public void setVerticalChainWeight(float);
-    method public void setView(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! setWidth(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! start();
-    method public androidx.constraintlayout.core.state.ConstraintReference! startToEnd(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! startToStart(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! top();
-    method public androidx.constraintlayout.core.state.ConstraintReference! topToBottom(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! topToTop(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! translationX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! translationY(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! translationZ(float);
-    method public void validate() throws java.lang.Exception;
-    method public androidx.constraintlayout.core.state.ConstraintReference! verticalBias(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! visibility(int);
-    method public androidx.constraintlayout.core.state.ConstraintReference! width(androidx.constraintlayout.core.state.Dimension!);
-    field protected Object! mBottomToBottom;
-    field protected Object! mBottomToTop;
-    field protected Object! mEndToEnd;
-    field protected Object! mEndToStart;
-    field protected float mHorizontalBias;
-    field protected Object! mLeftToLeft;
-    field protected Object! mLeftToRight;
-    field protected int mMarginBottom;
-    field protected int mMarginBottomGone;
-    field protected int mMarginEnd;
-    field protected int mMarginEndGone;
-    field protected int mMarginLeft;
-    field protected int mMarginLeftGone;
-    field protected int mMarginRight;
-    field protected int mMarginRightGone;
-    field protected int mMarginStart;
-    field protected int mMarginStartGone;
-    field protected int mMarginTop;
-    field protected int mMarginTopGone;
-    field protected Object! mRightToLeft;
-    field protected Object! mRightToRight;
-    field protected Object! mStartToEnd;
-    field protected Object! mStartToStart;
-    field protected Object! mTopToBottom;
-    field protected Object! mTopToTop;
-    field protected float mVerticalBias;
-  }
-
-  public static interface ConstraintReference.ConstraintReferenceFactory {
-    method public androidx.constraintlayout.core.state.ConstraintReference! create(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class ConstraintSetParser {
-    ctor public ConstraintSetParser();
-    method public static void parseDesignElementsJSON(String!, java.util.ArrayList<androidx.constraintlayout.core.state.ConstraintSetParser.DesignElement!>!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static void parseJSON(String!, androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.ConstraintSetParser.LayoutVariables!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static void parseJSON(String!, androidx.constraintlayout.core.state.Transition!, int);
-    method public static void parseMotionSceneJSON(androidx.constraintlayout.core.state.CoreMotionScene!, String!);
-  }
-
-  public static class ConstraintSetParser.DesignElement {
-    method public String! getId();
-    method public java.util.HashMap<java.lang.String!,java.lang.String!>! getParams();
-    method public String! getType();
-  }
-
-  public static class ConstraintSetParser.LayoutVariables {
-    ctor public ConstraintSetParser.LayoutVariables();
-    method public void putOverride(String!, float);
-  }
-
-  public enum ConstraintSetParser.MotionLayoutDebugFlags {
-    enum_constant public static final androidx.constraintlayout.core.state.ConstraintSetParser.MotionLayoutDebugFlags NONE;
-    enum_constant public static final androidx.constraintlayout.core.state.ConstraintSetParser.MotionLayoutDebugFlags SHOW_ALL;
-    enum_constant public static final androidx.constraintlayout.core.state.ConstraintSetParser.MotionLayoutDebugFlags UNKNOWN;
-  }
-
-  public interface CoreMotionScene {
-    method public String! getConstraintSet(int);
-    method public String! getConstraintSet(String!);
-    method public String! getTransition(String!);
-    method public void setConstraintSetContent(String!, String!);
-    method public void setDebugName(String!);
-    method public void setTransitionContent(String!, String!);
-  }
-
-  public interface CorePixelDp {
-    method public float toPixels(float);
-  }
-
-  public class Dimension {
-    method public void apply(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-    method public static androidx.constraintlayout.core.state.Dimension! createFixed(int);
-    method public static androidx.constraintlayout.core.state.Dimension! createFixed(Object!);
-    method public static androidx.constraintlayout.core.state.Dimension! createParent();
-    method public static androidx.constraintlayout.core.state.Dimension! createPercent(Object!, float);
-    method public static androidx.constraintlayout.core.state.Dimension! createRatio(String!);
-    method public static androidx.constraintlayout.core.state.Dimension! createSpread();
-    method public static androidx.constraintlayout.core.state.Dimension! createSuggested(int);
-    method public static androidx.constraintlayout.core.state.Dimension! createSuggested(Object!);
-    method public static androidx.constraintlayout.core.state.Dimension! createWrap();
-    method public boolean equalsFixedValue(int);
-    method public androidx.constraintlayout.core.state.Dimension! fixed(int);
-    method public androidx.constraintlayout.core.state.Dimension! fixed(Object!);
-    method public androidx.constraintlayout.core.state.Dimension! max(int);
-    method public androidx.constraintlayout.core.state.Dimension! max(Object!);
-    method public androidx.constraintlayout.core.state.Dimension! min(int);
-    method public androidx.constraintlayout.core.state.Dimension! min(Object!);
-    method public androidx.constraintlayout.core.state.Dimension! percent(Object!, float);
-    method public androidx.constraintlayout.core.state.Dimension! ratio(String!);
-    method public androidx.constraintlayout.core.state.Dimension! suggested(int);
-    method public androidx.constraintlayout.core.state.Dimension! suggested(Object!);
-    field public static final Object! FIXED_DIMENSION;
-    field public static final Object! PARENT_DIMENSION;
-    field public static final Object! PERCENT_DIMENSION;
-    field public static final Object! RATIO_DIMENSION;
-    field public static final Object! SPREAD_DIMENSION;
-    field public static final Object! WRAP_DIMENSION;
-  }
-
-  public enum Dimension.Type {
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type FIXED;
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type MATCH_CONSTRAINT;
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type MATCH_PARENT;
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type WRAP;
-  }
-
-  public class HelperReference extends androidx.constraintlayout.core.state.ConstraintReference implements androidx.constraintlayout.core.state.helpers.Facade {
-    ctor public HelperReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
-    method public androidx.constraintlayout.core.state.HelperReference! add(java.lang.Object!...!);
-    method public void applyBase();
-    method public androidx.constraintlayout.core.widgets.HelperWidget! getHelperWidget();
-    method public androidx.constraintlayout.core.state.State.Helper! getType();
-    method public void setHelperWidget(androidx.constraintlayout.core.widgets.HelperWidget!);
-    field protected final androidx.constraintlayout.core.state.State! mHelperState;
-    field protected java.util.ArrayList<java.lang.Object!>! mReferences;
-  }
-
-  public interface Interpolator {
-    method public float getInterpolation(float);
-  }
-
-  public interface Reference {
-    method public void apply();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public androidx.constraintlayout.core.state.helpers.Facade! getFacade();
-    method public Object! getKey();
-    method public void setConstraintWidget(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setKey(Object!);
-  }
-
-  public class Registry {
-    ctor public Registry();
-    method public String! currentContent(String!);
-    method public String! currentLayoutInformation(String!);
-    method public static androidx.constraintlayout.core.state.Registry! getInstance();
-    method public long getLastModified(String!);
-    method public java.util.Set<java.lang.String!>! getLayoutList();
-    method public void register(String!, androidx.constraintlayout.core.state.RegistryCallback!);
-    method public void setDrawDebug(String!, int);
-    method public void setLayoutInformationMode(String!, int);
-    method public void unregister(String!, androidx.constraintlayout.core.state.RegistryCallback!);
-    method public void updateContent(String!, String!);
-    method public void updateDimensions(String!, int, int);
-    method public void updateProgress(String!, float);
-  }
-
-  public interface RegistryCallback {
-    method public String! currentLayoutInformation();
-    method public String! currentMotionScene();
-    method public long getLastModified();
-    method public void onDimensions(int, int);
-    method public void onNewMotionScene(String!);
-    method public void onProgress(float);
-    method public void setDrawDebug(int);
-    method public void setLayoutInformationMode(int);
-  }
-
-  public class State {
-    ctor public State();
-    method public void apply(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    method public androidx.constraintlayout.core.state.helpers.BarrierReference! barrier(Object!, androidx.constraintlayout.core.state.State.Direction!);
-    method public void baselineNeededFor(Object!);
-    method public androidx.constraintlayout.core.state.helpers.AlignHorizontallyReference! centerHorizontally(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.AlignVerticallyReference! centerVertically(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! constraints(Object!);
-    method public int convertDimension(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! createConstraintReference(Object!);
-    method public void directMapping();
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getFlow(Object!, boolean);
-    method public androidx.constraintlayout.core.state.helpers.GridReference getGrid(Object, String);
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getHorizontalFlow();
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getHorizontalFlow(java.lang.Object!...!);
-    method public java.util.ArrayList<java.lang.String!>! getIdsForTag(String!);
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getVerticalFlow();
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getVerticalFlow(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! guideline(Object!, int);
-    method public androidx.constraintlayout.core.state.State! height(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.HelperReference! helper(Object!, androidx.constraintlayout.core.state.State.Helper!);
-    method public androidx.constraintlayout.core.state.helpers.HorizontalChainReference! horizontalChain();
-    method public androidx.constraintlayout.core.state.helpers.HorizontalChainReference! horizontalChain(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! horizontalGuideline(Object!);
-    method public boolean isBaselineNeeded(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method @Deprecated public boolean isLtr();
-    method public boolean isRtl();
-    method public void map(Object!, Object!);
-    method public void reset();
-    method public boolean sameFixedHeight(int);
-    method public boolean sameFixedWidth(int);
-    method public void setDpToPixel(androidx.constraintlayout.core.state.CorePixelDp!);
-    method public androidx.constraintlayout.core.state.State! setHeight(androidx.constraintlayout.core.state.Dimension!);
-    method @Deprecated public void setLtr(boolean);
-    method public void setRtl(boolean);
-    method public void setTag(String!, String!);
-    method public androidx.constraintlayout.core.state.State! setWidth(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.helpers.VerticalChainReference! verticalChain();
-    method public androidx.constraintlayout.core.state.helpers.VerticalChainReference! verticalChain(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! verticalGuideline(Object!);
-    method public androidx.constraintlayout.core.state.State! width(androidx.constraintlayout.core.state.Dimension!);
-    field public static final Integer PARENT;
-    field protected java.util.HashMap<java.lang.Object!,androidx.constraintlayout.core.state.HelperReference!>! mHelperReferences;
-    field public final androidx.constraintlayout.core.state.ConstraintReference! mParent;
-    field protected java.util.HashMap<java.lang.Object!,androidx.constraintlayout.core.state.Reference!>! mReferences;
-  }
-
-  public enum State.Chain {
-    method public static androidx.constraintlayout.core.state.State.Chain! getChainByString(String!);
-    method public static int getValueByString(String!);
-    enum_constant public static final androidx.constraintlayout.core.state.State.Chain PACKED;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Chain SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Chain SPREAD_INSIDE;
-    field public static java.util.Map<java.lang.String!,androidx.constraintlayout.core.state.State.Chain!>! chainMap;
-    field public static java.util.Map<java.lang.String!,java.lang.Integer!>! valueMap;
-  }
-
-  public enum State.Constraint {
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BASELINE_TO_BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BASELINE_TO_BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BASELINE_TO_TOP;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BOTTOM_TO_BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BOTTOM_TO_BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BOTTOM_TO_TOP;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint CENTER_HORIZONTALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint CENTER_VERTICALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint CIRCULAR_CONSTRAINT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint END_TO_END;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint END_TO_START;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint LEFT_TO_LEFT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint LEFT_TO_RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint RIGHT_TO_LEFT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint RIGHT_TO_RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint START_TO_END;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint START_TO_START;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint TOP_TO_BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint TOP_TO_BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint TOP_TO_TOP;
-  }
-
-  public enum State.Direction {
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction END;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction LEFT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction START;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction TOP;
-  }
-
-  public enum State.Helper {
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper ALIGN_HORIZONTALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper ALIGN_VERTICALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper BARRIER;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper COLUMN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper FLOW;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper GRID;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper HORIZONTAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper HORIZONTAL_FLOW;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper LAYER;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper ROW;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper VERTICAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper VERTICAL_FLOW;
-  }
-
-  public enum State.Wrap {
-    method public static androidx.constraintlayout.core.state.State.Wrap! getChainByString(String!);
-    method public static int getValueByString(String!);
-    enum_constant public static final androidx.constraintlayout.core.state.State.Wrap ALIGNED;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Wrap CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Wrap NONE;
-    field public static java.util.Map<java.lang.String!,java.lang.Integer!>! valueMap;
-    field public static java.util.Map<java.lang.String!,androidx.constraintlayout.core.state.State.Wrap!>! wrapMap;
-  }
-
-  public class Transition implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public Transition(androidx.constraintlayout.core.state.CorePixelDp);
-    method public void addCustomColor(int, String!, String!, int);
-    method public void addCustomFloat(int, String!, String!, float);
-    method public void addKeyAttribute(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void addKeyAttribute(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!, androidx.constraintlayout.core.motion.CustomVariable![]!);
-    method public void addKeyCycle(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void addKeyPosition(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void addKeyPosition(String!, int, int, float, float);
-    method public void calcStagger();
-    method public void clear();
-    method public boolean contains(String!);
-    method public float dragToProgress(float, int, int, float, float);
-    method public void fillKeyPositions(androidx.constraintlayout.core.state.WidgetFrame!, float[]!, float[]!, float[]!);
-    method public androidx.constraintlayout.core.state.Transition.KeyPosition! findNextPosition(String!, int);
-    method public androidx.constraintlayout.core.state.Transition.KeyPosition! findPreviousPosition(String!, int);
-    method public int getAutoTransition();
-    method public androidx.constraintlayout.core.state.WidgetFrame! getEnd(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getEnd(String!);
-    method public int getId(String!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getInterpolated(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getInterpolated(String!);
-    method public int getInterpolatedHeight();
-    method public int getInterpolatedWidth();
-    method public androidx.constraintlayout.core.state.Interpolator! getInterpolator();
-    method public static androidx.constraintlayout.core.state.Interpolator! getInterpolator(int, String!);
-    method public int getKeyFrames(String!, float[]!, int[]!, int[]!);
-    method public androidx.constraintlayout.core.motion.Motion! getMotion(String!);
-    method public int getNumberKeyPositions(androidx.constraintlayout.core.state.WidgetFrame!);
-    method public float[]! getPath(String!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getStart(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getStart(String!);
-    method public float getTouchUpProgress(long);
-    method public androidx.constraintlayout.core.state.Transition.WidgetState! getWidgetState(String!, androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-    method public boolean hasOnSwipe();
-    method public boolean hasPositionKeyframes();
-    method public void interpolate(int, int, float);
-    method public boolean isEmpty();
-    method public boolean isTouchNotDone(float);
-    method public void setTouchUp(float, long, float, float);
-    method public void setTransitionProperties(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public void updateFrom(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int);
-    field public static final int END = 1; // 0x1
-    field public static final int INTERPOLATED = 2; // 0x2
-    field public static final int START = 0; // 0x0
-  }
-
-  public static class Transition.WidgetState {
-    ctor public Transition.WidgetState();
-    method public androidx.constraintlayout.core.state.WidgetFrame! getFrame(int);
-    method public void interpolate(int, int, float, androidx.constraintlayout.core.state.Transition!);
-    method public void setKeyAttribute(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void setKeyAttribute(androidx.constraintlayout.core.motion.utils.TypedBundle!, androidx.constraintlayout.core.motion.CustomVariable![]!);
-    method public void setKeyCycle(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void setKeyPosition(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void setPathRelative(androidx.constraintlayout.core.state.Transition.WidgetState!);
-    method public void update(androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-  }
-
-  public class TransitionParser {
-    ctor public TransitionParser();
-    method @Deprecated public static void parse(androidx.constraintlayout.core.parser.CLObject!, androidx.constraintlayout.core.state.Transition!, androidx.constraintlayout.core.state.CorePixelDp!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static void parseKeyFrames(androidx.constraintlayout.core.parser.CLObject!, androidx.constraintlayout.core.state.Transition!) throws androidx.constraintlayout.core.parser.CLParsingException;
-  }
-
-  public class WidgetFrame {
-    ctor public WidgetFrame();
-    ctor public WidgetFrame(androidx.constraintlayout.core.state.WidgetFrame!);
-    ctor public WidgetFrame(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void addCustomColor(String!, int);
-    method public void addCustomFloat(String!, float);
-    method public float centerX();
-    method public float centerY();
-    method public boolean containsCustom(String);
-    method public androidx.constraintlayout.core.motion.CustomVariable! getCustomAttribute(String!);
-    method public java.util.Set<java.lang.String!>! getCustomAttributeNames();
-    method public int getCustomColor(String!);
-    method public float getCustomFloat(String!);
-    method public String! getId();
-    method public androidx.constraintlayout.core.motion.utils.TypedBundle! getMotionProperties();
-    method public int height();
-    method public static void interpolate(int, int, androidx.constraintlayout.core.state.WidgetFrame!, androidx.constraintlayout.core.state.WidgetFrame!, androidx.constraintlayout.core.state.WidgetFrame!, androidx.constraintlayout.core.state.Transition!, float);
-    method public boolean isDefaultTransform();
-    method public StringBuilder! serialize(StringBuilder!);
-    method public StringBuilder! serialize(StringBuilder!, boolean);
-    method public void setCustomAttribute(String!, int, boolean);
-    method public void setCustomAttribute(String!, int, float);
-    method public void setCustomAttribute(String!, int, int);
-    method public void setCustomAttribute(String!, int, String!);
-    method public void setCustomValue(androidx.constraintlayout.core.motion.CustomAttribute!, float[]!);
-    method public boolean setValue(String!, androidx.constraintlayout.core.parser.CLElement!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.state.WidgetFrame! update();
-    method public androidx.constraintlayout.core.state.WidgetFrame! update(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void updateAttributes(androidx.constraintlayout.core.state.WidgetFrame!);
-    method public int width();
-    field public float alpha;
-    field public int bottom;
-    field public float interpolatedPos;
-    field public int left;
-    field public String! name;
-    field public static float phone_orientation;
-    field public float pivotX;
-    field public float pivotY;
-    field public int right;
-    field public float rotationX;
-    field public float rotationY;
-    field public float rotationZ;
-    field public float scaleX;
-    field public float scaleY;
-    field public int top;
-    field public float translationX;
-    field public float translationY;
-    field public float translationZ;
-    field public int visibility;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget! widget;
-  }
-
-}
-
-package androidx.constraintlayout.core.state.helpers {
-
-  public class AlignHorizontallyReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public AlignHorizontallyReference(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class AlignVerticallyReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public AlignVerticallyReference(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class BarrierReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public BarrierReference(androidx.constraintlayout.core.state.State!);
-    method public void setBarrierDirection(androidx.constraintlayout.core.state.State.Direction!);
-  }
-
-  public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
-    method public void addChainElement(String, float, float, float);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
-    method public float getBias();
-    method protected float getPostMargin(String);
-    method protected float getPreMargin(String);
-    method public androidx.constraintlayout.core.state.State.Chain getStyle();
-    method protected float getWeight(String);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
-    field protected float mBias;
-    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
-    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
-    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
-    field protected androidx.constraintlayout.core.state.State.Chain mStyle;
-  }
-
-  public interface Facade {
-    method public void apply();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-  }
-
-  public class FlowReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public FlowReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
-    method public void addFlowElement(String!, float, float, float);
-    method public float getFirstHorizontalBias();
-    method public int getFirstHorizontalStyle();
-    method public float getFirstVerticalBias();
-    method public int getFirstVerticalStyle();
-    method public int getHorizontalAlign();
-    method public float getHorizontalBias();
-    method public int getHorizontalGap();
-    method public int getHorizontalStyle();
-    method public float getLastHorizontalBias();
-    method public int getLastHorizontalStyle();
-    method public float getLastVerticalBias();
-    method public int getLastVerticalStyle();
-    method public int getMaxElementsWrap();
-    method public int getOrientation();
-    method public int getPaddingBottom();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
-    method public int getPaddingTop();
-    method protected float getPostMargin(String!);
-    method protected float getPreMargin(String!);
-    method public int getVerticalAlign();
-    method public float getVerticalBias();
-    method public int getVerticalGap();
-    method public int getVerticalStyle();
-    method protected float getWeight(String!);
-    method public int getWrapMode();
-    method public void setFirstHorizontalBias(float);
-    method public void setFirstHorizontalStyle(int);
-    method public void setFirstVerticalBias(float);
-    method public void setFirstVerticalStyle(int);
-    method public void setHorizontalAlign(int);
-    method public void setHorizontalGap(int);
-    method public void setHorizontalStyle(int);
-    method public void setLastHorizontalBias(float);
-    method public void setLastHorizontalStyle(int);
-    method public void setLastVerticalBias(float);
-    method public void setLastVerticalStyle(int);
-    method public void setMaxElementsWrap(int);
-    method public void setOrientation(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(int);
-    method public void setPaddingTop(int);
-    method public void setVerticalAlign(int);
-    method public void setVerticalGap(int);
-    method public void setVerticalStyle(int);
-    method public void setWrapMode(int);
-    field protected float mFirstHorizontalBias;
-    field protected int mFirstHorizontalStyle;
-    field protected float mFirstVerticalBias;
-    field protected int mFirstVerticalStyle;
-    field protected androidx.constraintlayout.core.widgets.Flow! mFlow;
-    field protected int mHorizontalAlign;
-    field protected int mHorizontalGap;
-    field protected int mHorizontalStyle;
-    field protected float mLastHorizontalBias;
-    field protected int mLastHorizontalStyle;
-    field protected float mLastVerticalBias;
-    field protected int mLastVerticalStyle;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
-    field protected int mMaxElementsWrap;
-    field protected int mOrientation;
-    field protected int mPaddingBottom;
-    field protected int mPaddingLeft;
-    field protected int mPaddingRight;
-    field protected int mPaddingTop;
-    field protected int mVerticalAlign;
-    field protected int mVerticalGap;
-    field protected int mVerticalStyle;
-    field protected int mWrapMode;
-  }
-
-  public class GridReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public GridReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
-    method public String? getColumnWeights();
-    method public int getColumnsSet();
-    method public int getFlags();
-    method public float getHorizontalGaps();
-    method public int getOrientation();
-    method public int getPaddingBottom();
-    method public int getPaddingEnd();
-    method public int getPaddingStart();
-    method public int getPaddingTop();
-    method public String? getRowWeights();
-    method public int getRowsSet();
-    method public String? getSkips();
-    method public String? getSpans();
-    method public float getVerticalGaps();
-    method public void setColumnWeights(String);
-    method public void setColumnsSet(int);
-    method public void setFlags(int);
-    method public void setFlags(String);
-    method public void setHorizontalGaps(float);
-    method public void setOrientation(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingEnd(int);
-    method public void setPaddingStart(int);
-    method public void setPaddingTop(int);
-    method public void setRowWeights(String);
-    method public void setRowsSet(int);
-    method public void setSkips(String);
-    method public void setSpans(String);
-    method public void setVerticalGaps(float);
-  }
-
-  public class GuidelineReference implements androidx.constraintlayout.core.state.helpers.Facade androidx.constraintlayout.core.state.Reference {
-    ctor public GuidelineReference(androidx.constraintlayout.core.state.State!);
-    method public void apply();
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! end(Object!);
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public androidx.constraintlayout.core.state.helpers.Facade! getFacade();
-    method public Object! getKey();
-    method public int getOrientation();
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! percent(float);
-    method public void setConstraintWidget(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setKey(Object!);
-    method public void setOrientation(int);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! start(Object!);
-  }
-
-  public class HorizontalChainReference extends androidx.constraintlayout.core.state.helpers.ChainReference {
-    ctor public HorizontalChainReference(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class VerticalChainReference extends androidx.constraintlayout.core.state.helpers.ChainReference {
-    ctor public VerticalChainReference(androidx.constraintlayout.core.state.State!);
-  }
-
-}
-
-package androidx.constraintlayout.core.utils {
-
-  public class GridCore extends androidx.constraintlayout.core.widgets.VirtualLayout {
-    ctor public GridCore();
-    ctor public GridCore(int, int);
-    method public String? getColumnWeights();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidgetContainer? getContainer();
-    method public int getFlags();
-    method public float getHorizontalGaps();
-    method public int getOrientation();
-    method public String? getRowWeights();
-    method public float getVerticalGaps();
-    method public void setColumnWeights(String);
-    method public void setColumns(int);
-    method public void setContainer(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer);
-    method public void setFlags(int);
-    method public void setHorizontalGaps(float);
-    method public void setOrientation(int);
-    method public void setRowWeights(String);
-    method public void setRows(int);
-    method public void setSkips(String);
-    method public void setSpans(CharSequence);
-    method public void setVerticalGaps(float);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int SPANS_RESPECT_WIDGET_ORDER = 2; // 0x2
-    field public static final int SUB_GRID_BY_COL_ROW = 1; // 0x1
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public class GridEngine {
-    ctor public GridEngine();
-    ctor public GridEngine(int, int);
-    ctor public GridEngine(int, int, int);
-    method public int bottomOfWidget(int);
-    method public int leftOfWidget(int);
-    method public int rightOfWidget(int);
-    method public void setColumns(int);
-    method public void setNumWidgets(int);
-    method public void setOrientation(int);
-    method public void setRows(int);
-    method public void setSkips(String!);
-    method public void setSpans(CharSequence!);
-    method public void setup();
-    method public int topOfWidget(int);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-}
-
-package androidx.constraintlayout.core.widgets {
-
-  public class Barrier extends androidx.constraintlayout.core.widgets.HelperWidget {
-    ctor public Barrier();
-    ctor public Barrier(String!);
-    method public boolean allSolved();
-    method @Deprecated public boolean allowsGoneWidget();
-    method public boolean getAllowsGoneWidget();
-    method public int getBarrierType();
-    method public int getMargin();
-    method public int getOrientation();
-    method protected void markWidgets();
-    method public void setAllowsGoneWidget(boolean);
-    method public void setBarrierType(int);
-    method public void setMargin(int);
-    field public static final int BOTTOM = 3; // 0x3
-    field public static final int LEFT = 0; // 0x0
-    field public static final int RIGHT = 1; // 0x1
-    field public static final int TOP = 2; // 0x2
-  }
-
-  public class Chain {
-    ctor public Chain();
-    method public static void applyChainConstraints(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.LinearSystem!, java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>!, int);
-    field public static final boolean USE_CHAIN_OPTIMIZATION = false;
-  }
-
-  public class ChainHead {
-    ctor public ChainHead(androidx.constraintlayout.core.widgets.ConstraintWidget!, int, boolean);
-    method public void define();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getFirst();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getFirstMatchConstraintWidget();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getFirstVisibleWidget();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getHead();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getLast();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getLastMatchConstraintWidget();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getLastVisibleWidget();
-    method public float getTotalWeight();
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mFirst;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mFirstMatchConstraintWidget;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mFirstVisibleWidget;
-    field protected boolean mHasComplexMatchWeights;
-    field protected boolean mHasDefinedWeights;
-    field protected boolean mHasRatio;
-    field protected boolean mHasUndefinedWeights;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mHead;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mLast;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mLastMatchConstraintWidget;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mLastVisibleWidget;
-    field protected float mTotalWeight;
-    field protected java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>! mWeightedMatchConstraintsWidgets;
-    field protected int mWidgetsCount;
-    field protected int mWidgetsMatchCount;
-  }
-
-  public class ConstraintAnchor {
-    ctor public ConstraintAnchor(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!);
-    method public boolean connect(androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method public boolean connect(androidx.constraintlayout.core.widgets.ConstraintAnchor!, int, int, boolean);
-    method public void copyFrom(androidx.constraintlayout.core.widgets.ConstraintAnchor!, java.util.HashMap<androidx.constraintlayout.core.widgets.ConstraintWidget!,androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void findDependents(int, java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public java.util.HashSet<androidx.constraintlayout.core.widgets.ConstraintAnchor!>! getDependents();
-    method public int getFinalValue();
-    method public int getMargin();
-    method public final androidx.constraintlayout.core.widgets.ConstraintAnchor! getOpposite();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getOwner();
-    method public androidx.constraintlayout.core.SolverVariable! getSolverVariable();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor! getTarget();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor.Type! getType();
-    method public boolean hasCenteredDependents();
-    method public boolean hasDependents();
-    method public boolean hasFinalValue();
-    method public boolean isConnected();
-    method public boolean isConnectionAllowed(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public boolean isConnectionAllowed(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public boolean isSideAnchor();
-    method public boolean isSimilarDimensionConnection(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public boolean isValidConnection(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public boolean isVerticalAnchor();
-    method public void reset();
-    method public void resetFinalResolution();
-    method public void resetSolverVariable(androidx.constraintlayout.core.Cache!);
-    method public void setFinalValue(int);
-    method public void setGoneMargin(int);
-    method public void setMargin(int);
-    field public int mMargin;
-    field public final androidx.constraintlayout.core.widgets.ConstraintWidget! mOwner;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mTarget;
-    field public final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type! mType;
-  }
-
-  public enum ConstraintAnchor.Type {
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type CENTER;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type CENTER_X;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type CENTER_Y;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type LEFT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type NONE;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type TOP;
-  }
-
-  public class ConstraintWidget {
-    ctor public ConstraintWidget();
-    ctor public ConstraintWidget(int, int);
-    ctor public ConstraintWidget(int, int, int, int);
-    ctor public ConstraintWidget(String!);
-    ctor public ConstraintWidget(String!, int, int);
-    ctor public ConstraintWidget(String!, int, int, int, int);
-    method public void addChildrenToSolverByDependency(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.LinearSystem!, java.util.HashSet<androidx.constraintlayout.core.widgets.ConstraintWidget!>!, int, boolean);
-    method public void addToSolver(androidx.constraintlayout.core.LinearSystem!, boolean);
-    method public boolean allowedInBarrier();
-    method public void connect(androidx.constraintlayout.core.widgets.ConstraintAnchor!, androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method public void connect(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!);
-    method public void connect(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, int);
-    method public void connectCircularConstraint(androidx.constraintlayout.core.widgets.ConstraintWidget!, float, int);
-    method public void copy(androidx.constraintlayout.core.widgets.ConstraintWidget!, java.util.HashMap<androidx.constraintlayout.core.widgets.ConstraintWidget!,androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void createObjectVariables(androidx.constraintlayout.core.LinearSystem!);
-    method public void ensureMeasureRequested();
-    method public void ensureWidgetRuns();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor! getAnchor(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!);
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintAnchor!>! getAnchors();
-    method public int getBaselineDistance();
-    method public float getBiasPercent(int);
-    method public int getBottom();
-    method public Object! getCompanionWidget();
-    method public int getContainerItemSkip();
-    method public String! getDebugName();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! getDimensionBehaviour(int);
-    method public float getDimensionRatio();
-    method public int getDimensionRatioSide();
-    method public boolean getHasBaseline();
-    method public int getHeight();
-    method public float getHorizontalBiasPercent();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getHorizontalChainControlWidget();
-    method public int getHorizontalChainStyle();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! getHorizontalDimensionBehaviour();
-    method public int getHorizontalMargin();
-    method public int getLastHorizontalMeasureSpec();
-    method public int getLastVerticalMeasureSpec();
-    method public int getLeft();
-    method public int getLength(int);
-    method public int getMaxHeight();
-    method public int getMaxWidth();
-    method public int getMinHeight();
-    method public int getMinWidth();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getNextChainMember(int);
-    method public int getOptimizerWrapHeight();
-    method public int getOptimizerWrapWidth();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getParent();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getPreviousChainMember(int);
-    method public int getRight();
-    method protected int getRootX();
-    method protected int getRootY();
-    method public androidx.constraintlayout.core.widgets.analyzer.WidgetRun! getRun(int);
-    method public void getSceneString(StringBuilder!);
-    method public int getTop();
-    method public String! getType();
-    method public float getVerticalBiasPercent();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getVerticalChainControlWidget();
-    method public int getVerticalChainStyle();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! getVerticalDimensionBehaviour();
-    method public int getVerticalMargin();
-    method public int getVisibility();
-    method public int getWidth();
-    method public int getWrapBehaviorInParent();
-    method public int getX();
-    method public int getY();
-    method public boolean hasBaseline();
-    method public boolean hasDanglingDimension(int);
-    method public boolean hasDependencies();
-    method public boolean hasDimensionOverride();
-    method public boolean hasResolvedTargets(int, int);
-    method public void immediateConnect(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, int, int);
-    method public boolean isAnimated();
-    method public boolean isHeightWrapContent();
-    method public boolean isHorizontalSolvingPassDone();
-    method public boolean isInBarrier(int);
-    method public boolean isInHorizontalChain();
-    method public boolean isInPlaceholder();
-    method public boolean isInVerticalChain();
-    method public boolean isInVirtualLayout();
-    method public boolean isMeasureRequested();
-    method public boolean isResolvedHorizontally();
-    method public boolean isResolvedVertically();
-    method public boolean isRoot();
-    method public boolean isSpreadHeight();
-    method public boolean isSpreadWidth();
-    method public boolean isVerticalSolvingPassDone();
-    method public boolean isWidthWrapContent();
-    method public void markHorizontalSolvingPassDone();
-    method public void markVerticalSolvingPassDone();
-    method public boolean oppositeDimensionDependsOn(int);
-    method public boolean oppositeDimensionsTied();
-    method public void reset();
-    method public void resetAllConstraints();
-    method public void resetAnchor(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public void resetAnchors();
-    method public void resetFinalResolution();
-    method public void resetSolverVariables(androidx.constraintlayout.core.Cache!);
-    method public void resetSolvingPassFlag();
-    method public StringBuilder! serialize(StringBuilder!);
-    method public void setAnimated(boolean);
-    method public void setBaselineDistance(int);
-    method public void setCompanionWidget(Object!);
-    method public void setContainerItemSkip(int);
-    method public void setDebugName(String!);
-    method public void setDebugSolverName(androidx.constraintlayout.core.LinearSystem!, String!);
-    method public void setDimension(int, int);
-    method public void setDimensionRatio(float, int);
-    method public void setDimensionRatio(String!);
-    method public void setFinalBaseline(int);
-    method public void setFinalFrame(int, int, int, int, int, int);
-    method public void setFinalHorizontal(int, int);
-    method public void setFinalLeft(int);
-    method public void setFinalTop(int);
-    method public void setFinalVertical(int, int);
-    method public void setFrame(int, int, int);
-    method public void setFrame(int, int, int, int);
-    method public void setGoneMargin(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, int);
-    method public void setHasBaseline(boolean);
-    method public void setHeight(int);
-    method public void setHeightWrapContent(boolean);
-    method public void setHorizontalBiasPercent(float);
-    method public void setHorizontalChainStyle(int);
-    method public void setHorizontalDimension(int, int);
-    method public void setHorizontalDimensionBehaviour(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-    method public void setHorizontalMatchStyle(int, int, int, float);
-    method public void setHorizontalWeight(float);
-    method protected void setInBarrier(int, boolean);
-    method public void setInPlaceholder(boolean);
-    method public void setInVirtualLayout(boolean);
-    method public void setLastMeasureSpec(int, int);
-    method public void setLength(int, int);
-    method public void setMaxHeight(int);
-    method public void setMaxWidth(int);
-    method public void setMeasureRequested(boolean);
-    method public void setMinHeight(int);
-    method public void setMinWidth(int);
-    method public void setOffset(int, int);
-    method public void setOrigin(int, int);
-    method public void setParent(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setType(String!);
-    method public void setVerticalBiasPercent(float);
-    method public void setVerticalChainStyle(int);
-    method public void setVerticalDimension(int, int);
-    method public void setVerticalDimensionBehaviour(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-    method public void setVerticalMatchStyle(int, int, int, float);
-    method public void setVerticalWeight(float);
-    method public void setVisibility(int);
-    method public void setWidth(int);
-    method public void setWidthWrapContent(boolean);
-    method public void setWrapBehaviorInParent(int);
-    method public void setX(int);
-    method public void setY(int);
-    method public void setupDimensionRatio(boolean, boolean, boolean, boolean);
-    method public void updateFromRuns(boolean, boolean);
-    method public void updateFromSolver(androidx.constraintlayout.core.LinearSystem!, boolean);
-    field public static final int ANCHOR_BASELINE = 4; // 0x4
-    field public static final int ANCHOR_BOTTOM = 3; // 0x3
-    field public static final int ANCHOR_LEFT = 0; // 0x0
-    field public static final int ANCHOR_RIGHT = 1; // 0x1
-    field public static final int ANCHOR_TOP = 2; // 0x2
-    field public static final int BOTH = 2; // 0x2
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static float DEFAULT_BIAS;
-    field protected static final int DIRECT = 2; // 0x2
-    field public static final int GONE = 8; // 0x8
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int INVISIBLE = 4; // 0x4
-    field public static final int MATCH_CONSTRAINT_PERCENT = 2; // 0x2
-    field public static final int MATCH_CONSTRAINT_RATIO = 3; // 0x3
-    field public static final int MATCH_CONSTRAINT_RATIO_RESOLVED = 4; // 0x4
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field protected static final int SOLVER = 1; // 0x1
-    field public static final int UNKNOWN = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int VISIBLE = 0; // 0x0
-    field public static final int WRAP_BEHAVIOR_HORIZONTAL_ONLY = 1; // 0x1
-    field public static final int WRAP_BEHAVIOR_INCLUDED = 0; // 0x0
-    field public static final int WRAP_BEHAVIOR_SKIPPED = 3; // 0x3
-    field public static final int WRAP_BEHAVIOR_VERTICAL_ONLY = 2; // 0x2
-    field public androidx.constraintlayout.core.state.WidgetFrame! frame;
-    field public androidx.constraintlayout.core.widgets.analyzer.ChainRun! horizontalChainRun;
-    field public int horizontalGroup;
-    field public boolean[]! isTerminalWidget;
-    field protected java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintAnchor!>! mAnchors;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mBaseline;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mBottom;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mCenter;
-    field public float mCircleConstraintAngle;
-    field public float mDimensionRatio;
-    field protected int mDimensionRatioSide;
-    field public int mHorizontalResolution;
-    field public androidx.constraintlayout.core.widgets.analyzer.HorizontalWidgetRun! mHorizontalRun;
-    field public boolean mIsHeightWrapContent;
-    field public boolean mIsWidthWrapContent;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mLeft;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor![]! mListAnchors;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour![]! mListDimensionBehaviors;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget![]! mListNextMatchConstraintsWidget;
-    field public int mMatchConstraintDefaultHeight;
-    field public int mMatchConstraintDefaultWidth;
-    field public int mMatchConstraintMaxHeight;
-    field public int mMatchConstraintMaxWidth;
-    field public int mMatchConstraintMinHeight;
-    field public int mMatchConstraintMinWidth;
-    field public float mMatchConstraintPercentHeight;
-    field public float mMatchConstraintPercentWidth;
-    field protected int mMinHeight;
-    field protected int mMinWidth;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget![]! mNextChainWidget;
-    field protected int mOffsetX;
-    field protected int mOffsetY;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget! mParent;
-    field public int[]! mResolvedMatchConstraintDefault;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mRight;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mTop;
-    field public int mVerticalResolution;
-    field public androidx.constraintlayout.core.widgets.analyzer.VerticalWidgetRun! mVerticalRun;
-    field public float[]! mWeight;
-    field protected int mX;
-    field protected int mY;
-    field public boolean measured;
-    field public androidx.constraintlayout.core.widgets.analyzer.WidgetRun![]! run;
-    field public String! stringId;
-    field public androidx.constraintlayout.core.widgets.analyzer.ChainRun! verticalChainRun;
-    field public int verticalGroup;
-  }
-
-  public enum ConstraintWidget.DimensionBehaviour {
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour FIXED;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour MATCH_CONSTRAINT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour MATCH_PARENT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour WRAP_CONTENT;
-  }
-
-  public class ConstraintWidgetContainer extends androidx.constraintlayout.core.widgets.WidgetContainer {
-    ctor public ConstraintWidgetContainer();
-    ctor public ConstraintWidgetContainer(int, int);
-    ctor public ConstraintWidgetContainer(int, int, int, int);
-    ctor public ConstraintWidgetContainer(String!, int, int);
-    method public boolean addChildrenToSolver(androidx.constraintlayout.core.LinearSystem!);
-    method public void addHorizontalWrapMaxVariable(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public void addHorizontalWrapMinVariable(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public void defineTerminalWidgets();
-    method public boolean directMeasure(boolean);
-    method public boolean directMeasureSetup(boolean);
-    method public boolean directMeasureWithOrientation(boolean, int);
-    method public void fillMetrics(androidx.constraintlayout.core.Metrics!);
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.Guideline!>! getHorizontalGuidelines();
-    method public androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer! getMeasurer();
-    method public int getOptimizationLevel();
-    method public androidx.constraintlayout.core.LinearSystem! getSystem();
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.Guideline!>! getVerticalGuidelines();
-    method public boolean handlesInternalConstraints();
-    method public void invalidateGraph();
-    method public void invalidateMeasures();
-    method public boolean isHeightMeasuredTooSmall();
-    method public boolean isRtl();
-    method public boolean isWidthMeasuredTooSmall();
-    method public static boolean measure(int, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure!, int);
-    method public long measure(int, int, int, int, int, int, int, int, int);
-    method public boolean optimizeFor(int);
-    method public void setMeasurer(androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-    method public void setOptimizationLevel(int);
-    method public void setPadding(int, int, int, int);
-    method public void setPass(int);
-    method public void setRtl(boolean);
-    method public boolean updateChildrenFromSolver(androidx.constraintlayout.core.LinearSystem!, boolean[]!);
-    method public void updateHierarchy();
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyGraph! mDependencyGraph;
-    field public boolean mGroupsWrapOptimized;
-    field public int mHorizontalChainsSize;
-    field public boolean mHorizontalWrapOptimized;
-    field public androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure! mMeasure;
-    field protected androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer! mMeasurer;
-    field public androidx.constraintlayout.core.Metrics! mMetrics;
-    field public boolean mSkipSolver;
-    field protected androidx.constraintlayout.core.LinearSystem! mSystem;
-    field public int mVerticalChainsSize;
-    field public boolean mVerticalWrapOptimized;
-    field public int mWrapFixedHeight;
-    field public int mWrapFixedWidth;
-  }
-
-  public class Flow extends androidx.constraintlayout.core.widgets.VirtualLayout {
-    ctor public Flow();
-    method public float getMaxElementsWrap();
-    method public void setFirstHorizontalBias(float);
-    method public void setFirstHorizontalStyle(int);
-    method public void setFirstVerticalBias(float);
-    method public void setFirstVerticalStyle(int);
-    method public void setHorizontalAlign(int);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalGap(int);
-    method public void setHorizontalStyle(int);
-    method public void setLastHorizontalBias(float);
-    method public void setLastHorizontalStyle(int);
-    method public void setLastVerticalBias(float);
-    method public void setLastVerticalStyle(int);
-    method public void setMaxElementsWrap(int);
-    method public void setOrientation(int);
-    method public void setVerticalAlign(int);
-    method public void setVerticalBias(float);
-    method public void setVerticalGap(int);
-    method public void setVerticalStyle(int);
-    method public void setWrapMode(int);
-    field public static final int HORIZONTAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int HORIZONTAL_ALIGN_END = 1; // 0x1
-    field public static final int HORIZONTAL_ALIGN_START = 0; // 0x0
-    field public static final int VERTICAL_ALIGN_BASELINE = 3; // 0x3
-    field public static final int VERTICAL_ALIGN_BOTTOM = 1; // 0x1
-    field public static final int VERTICAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int VERTICAL_ALIGN_TOP = 0; // 0x0
-    field public static final int WRAP_ALIGNED = 2; // 0x2
-    field public static final int WRAP_CHAIN = 1; // 0x1
-    field public static final int WRAP_CHAIN_NEW = 3; // 0x3
-    field public static final int WRAP_NONE = 0; // 0x0
-  }
-
-  public class Guideline extends androidx.constraintlayout.core.widgets.ConstraintWidget {
-    ctor public Guideline();
-    method public void cyclePosition();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor! getAnchor();
-    method public int getMinimumPosition();
-    method public int getOrientation();
-    method public int getRelativeBegin();
-    method public int getRelativeBehaviour();
-    method public int getRelativeEnd();
-    method public float getRelativePercent();
-    method public boolean isPercent();
-    method public void setFinalValue(int);
-    method public void setGuideBegin(int);
-    method public void setGuideEnd(int);
-    method public void setGuidePercent(float);
-    method public void setGuidePercent(int);
-    method public void setMinimumPosition(int);
-    method public void setOrientation(int);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int RELATIVE_BEGIN = 1; // 0x1
-    field public static final int RELATIVE_END = 2; // 0x2
-    field public static final int RELATIVE_PERCENT = 0; // 0x0
-    field public static final int RELATIVE_UNKNOWN = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field protected boolean mGuidelineUseRtl;
-    field protected int mRelativeBegin;
-    field protected int mRelativeEnd;
-    field protected float mRelativePercent;
-  }
-
-  public interface Helper {
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void removeAllIds();
-    method public void updateConstraints(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-  }
-
-  public class HelperWidget extends androidx.constraintlayout.core.widgets.ConstraintWidget implements androidx.constraintlayout.core.widgets.Helper {
-    ctor public HelperWidget();
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void addDependents(java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!, int, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public int findGroupInDependents(int);
-    method public void removeAllIds();
-    method public void updateConstraints(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget![]! mWidgets;
-    field public int mWidgetsCount;
-  }
-
-  public class Optimizer {
-    ctor public Optimizer();
-    method public static final boolean enabled(int, int);
-    field public static final int OPTIMIZATION_BARRIER = 2; // 0x2
-    field public static final int OPTIMIZATION_CACHE_MEASURES = 256; // 0x100
-    field public static final int OPTIMIZATION_CHAIN = 4; // 0x4
-    field public static final int OPTIMIZATION_DEPENDENCY_ORDERING = 512; // 0x200
-    field public static final int OPTIMIZATION_DIMENSIONS = 8; // 0x8
-    field public static final int OPTIMIZATION_DIRECT = 1; // 0x1
-    field public static final int OPTIMIZATION_GRAPH = 64; // 0x40
-    field public static final int OPTIMIZATION_GRAPH_WRAP = 128; // 0x80
-    field public static final int OPTIMIZATION_GROUPING = 1024; // 0x400
-    field public static final int OPTIMIZATION_GROUPS = 32; // 0x20
-    field public static final int OPTIMIZATION_NONE = 0; // 0x0
-    field public static final int OPTIMIZATION_RATIO = 16; // 0x10
-    field public static final int OPTIMIZATION_STANDARD = 257; // 0x101
-  }
-
-  public class Placeholder extends androidx.constraintlayout.core.widgets.VirtualLayout {
-    ctor public Placeholder();
-  }
-
-  public class Rectangle {
-    ctor public Rectangle();
-    method public boolean contains(int, int);
-    method public int getCenterX();
-    method public int getCenterY();
-    method public void setBounds(int, int, int, int);
-    field public int height;
-    field public int width;
-    field public int x;
-    field public int y;
-  }
-
-  public class VirtualLayout extends androidx.constraintlayout.core.widgets.HelperWidget {
-    ctor public VirtualLayout();
-    method public void applyRtl(boolean);
-    method public void captureWidgets();
-    method public boolean contains(java.util.HashSet<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public int getMeasuredHeight();
-    method public int getMeasuredWidth();
-    method public int getPaddingBottom();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
-    method public int getPaddingTop();
-    method protected void measure(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, int, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, int);
-    method public void measure(int, int, int, int);
-    method protected boolean measureChildren();
-    method public boolean needSolverPass();
-    method protected void needsCallbackFromSolver(boolean);
-    method public void setMeasure(int, int);
-    method public void setPadding(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingEnd(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(int);
-    method public void setPaddingStart(int);
-    method public void setPaddingTop(int);
-    field protected androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure! mMeasure;
-  }
-
-  public class WidgetContainer extends androidx.constraintlayout.core.widgets.ConstraintWidget {
-    ctor public WidgetContainer();
-    ctor public WidgetContainer(int, int);
-    ctor public WidgetContainer(int, int, int, int);
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!...!);
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>! getChildren();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidgetContainer! getRootConstraintContainer();
-    method public void layout();
-    method public void remove(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void removeAllChildren();
-    field public java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>! mChildren;
-  }
-
-}
-
-package androidx.constraintlayout.core.widgets.analyzer {
-
-  public class BasicMeasure {
-    ctor public BasicMeasure(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    method public long solverMeasure(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int, int, int, int, int, int, int, int, int);
-    method public void updateHierarchy(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    field public static final int AT_MOST = -2147483648; // 0x80000000
-    field public static final int EXACTLY = 1073741824; // 0x40000000
-    field public static final int FIXED = -3; // 0xfffffffd
-    field public static final int MATCH_PARENT = -1; // 0xffffffff
-    field public static final int UNSPECIFIED = 0; // 0x0
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-  }
-
-  public static class BasicMeasure.Measure {
-    ctor public BasicMeasure.Measure();
-    field public static int SELF_DIMENSIONS;
-    field public static int TRY_GIVEN_DIMENSIONS;
-    field public static int USE_GIVEN_DIMENSIONS;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! horizontalBehavior;
-    field public int horizontalDimension;
-    field public int measureStrategy;
-    field public int measuredBaseline;
-    field public boolean measuredHasBaseline;
-    field public int measuredHeight;
-    field public boolean measuredNeedsSolverPass;
-    field public int measuredWidth;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! verticalBehavior;
-    field public int verticalDimension;
-  }
-
-  public static interface BasicMeasure.Measurer {
-    method public void didMeasures();
-    method public void measure(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure!);
-  }
-
-  public class ChainRun extends androidx.constraintlayout.core.widgets.analyzer.WidgetRun {
-    ctor public ChainRun(androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-    method public void applyToWidget();
-  }
-
-  public interface Dependency {
-    method public void update(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-  }
-
-  public class DependencyGraph {
-    ctor public DependencyGraph(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    method public void buildGraph();
-    method public void buildGraph(java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetRun!>!);
-    method public void defineTerminalWidgets(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-    method public boolean directMeasure(boolean);
-    method public boolean directMeasureSetup(boolean);
-    method public boolean directMeasureWithOrientation(boolean, int);
-    method public void invalidateGraph();
-    method public void invalidateMeasures();
-    method public void measureWidgets();
-    method public void setMeasurer(androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-  }
-
-  public class DependencyNode implements androidx.constraintlayout.core.widgets.analyzer.Dependency {
-    ctor public DependencyNode(androidx.constraintlayout.core.widgets.analyzer.WidgetRun!);
-    method public void addDependency(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method public void clear();
-    method public String! name();
-    method public void resolve(int);
-    method public void update(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    field public boolean delegateToWidgetRun;
-    field public boolean readyToSolve;
-    field public boolean resolved;
-    field public androidx.constraintlayout.core.widgets.analyzer.Dependency! updateDelegate;
-    field public int value;
-  }
-
-  public class Direct {
-    ctor public Direct();
-    method public static String! ls(int);
-    method public static boolean solveChain(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.LinearSystem!, int, int, androidx.constraintlayout.core.widgets.ChainHead!, boolean, boolean, boolean);
-    method public static void solvingPass(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-  }
-
-  public class Grouping {
-    ctor public Grouping();
-    method public static androidx.constraintlayout.core.widgets.analyzer.WidgetGroup! findDependents(androidx.constraintlayout.core.widgets.ConstraintWidget!, int, java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public static boolean simpleSolvingPass(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-    method public static boolean validInGroup(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-  }
-
-  public class HorizontalWidgetRun extends androidx.constraintlayout.core.widgets.analyzer.WidgetRun {
-    ctor public HorizontalWidgetRun(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void applyToWidget();
-  }
-
-  public class VerticalWidgetRun extends androidx.constraintlayout.core.widgets.analyzer.WidgetRun {
-    ctor public VerticalWidgetRun(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void applyToWidget();
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyNode! baseline;
-  }
-
-  public class WidgetGroup {
-    ctor public WidgetGroup(int);
-    method public boolean add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void apply();
-    method public void cleanup(java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!);
-    method public void clear();
-    method public int getId();
-    method public int getOrientation();
-    method public boolean intersectWith(androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public boolean isAuthoritative();
-    method public int measureWrap(androidx.constraintlayout.core.LinearSystem!, int);
-    method public void moveTo(int, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public void setAuthoritative(boolean);
-    method public void setOrientation(int);
-    method public int size();
-  }
-
-  public abstract class WidgetRun implements androidx.constraintlayout.core.widgets.analyzer.Dependency {
-    ctor public WidgetRun(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method protected final void addTarget(androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, int);
-    method protected final void addTarget(androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, int, androidx.constraintlayout.core.widgets.analyzer.DimensionDependency!);
-    method protected final int getLimitedDimension(int, int);
-    method protected final androidx.constraintlayout.core.widgets.analyzer.DependencyNode! getTarget(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method protected final androidx.constraintlayout.core.widgets.analyzer.DependencyNode! getTarget(androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method public long getWrapDimension();
-    method public boolean isCenterConnection();
-    method public boolean isDimensionResolved();
-    method public boolean isResolved();
-    method public void update(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method protected void updateRunCenter(androidx.constraintlayout.core.widgets.analyzer.Dependency!, androidx.constraintlayout.core.widgets.ConstraintAnchor!, androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method protected void updateRunEnd(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method protected void updateRunStart(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method public long wrapSize(int);
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyNode! end;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! mDimensionBehavior;
-    field protected androidx.constraintlayout.core.widgets.analyzer.WidgetRun.RunType! mRunType;
-    field public int matchConstraintsType;
-    field public int orientation;
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyNode! start;
-  }
-
-}
-
diff --git a/constraintlayout/constraintlayout-core/api/restricted_1.1.0-beta01.txt b/constraintlayout/constraintlayout-core/api/restricted_1.1.0-beta01.txt
deleted file mode 100644
index daaf9e5..0000000
--- a/constraintlayout/constraintlayout-core/api/restricted_1.1.0-beta01.txt
+++ /dev/null
@@ -1,3397 +0,0 @@
-// Signature format: 4.0
-package androidx.constraintlayout.core {
-
-  public class ArrayLinkedVariables implements androidx.constraintlayout.core.ArrayRow.ArrayRowVariables {
-    method public void add(androidx.constraintlayout.core.SolverVariable!, float, boolean);
-    method public final void clear();
-    method public boolean contains(androidx.constraintlayout.core.SolverVariable!);
-    method public void display();
-    method public void divideByAmount(float);
-    method public final float get(androidx.constraintlayout.core.SolverVariable!);
-    method public int getCurrentSize();
-    method public int getHead();
-    method public final int getId(int);
-    method public final int getNextIndice(int);
-    method public final float getValue(int);
-    method public androidx.constraintlayout.core.SolverVariable! getVariable(int);
-    method public float getVariableValue(int);
-    method public int indexOf(androidx.constraintlayout.core.SolverVariable!);
-    method public void invert();
-    method public final void put(androidx.constraintlayout.core.SolverVariable!, float);
-    method public final float remove(androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public int sizeInBytes();
-    method public float use(androidx.constraintlayout.core.ArrayRow!, boolean);
-    field protected final androidx.constraintlayout.core.Cache! mCache;
-  }
-
-  public class ArrayRow {
-    ctor public ArrayRow();
-    ctor public ArrayRow(androidx.constraintlayout.core.Cache!);
-    method public androidx.constraintlayout.core.ArrayRow! addError(androidx.constraintlayout.core.LinearSystem!, int);
-    method public void addError(androidx.constraintlayout.core.SolverVariable!);
-    method public void clear();
-    method public androidx.constraintlayout.core.ArrayRow! createRowDimensionRatio(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEqualDimension(float, float, float, androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEqualMatchDimensions(float, float, float, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEquals(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowEquals(androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowGreaterThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowGreaterThan(androidx.constraintlayout.core.SolverVariable!, int, androidx.constraintlayout.core.SolverVariable!);
-    method public androidx.constraintlayout.core.ArrayRow! createRowLowerThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.ArrayRow! createRowWithAngle(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public androidx.constraintlayout.core.SolverVariable! getKey();
-    method public androidx.constraintlayout.core.SolverVariable! getPivotCandidate(androidx.constraintlayout.core.LinearSystem!, boolean[]!);
-    method public void initFromRow(androidx.constraintlayout.core.LinearSystem.Row!);
-    method public boolean isEmpty();
-    method public androidx.constraintlayout.core.SolverVariable! pickPivot(androidx.constraintlayout.core.SolverVariable!);
-    method public void reset();
-    method public void updateFromFinalVariable(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public void updateFromRow(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.ArrayRow!, boolean);
-    method public void updateFromSynonymVariable(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public void updateFromSystem(androidx.constraintlayout.core.LinearSystem!);
-    field public androidx.constraintlayout.core.ArrayRow.ArrayRowVariables! variables;
-  }
-
-  public static interface ArrayRow.ArrayRowVariables {
-    method public void add(androidx.constraintlayout.core.SolverVariable!, float, boolean);
-    method public void clear();
-    method public boolean contains(androidx.constraintlayout.core.SolverVariable!);
-    method public void display();
-    method public void divideByAmount(float);
-    method public float get(androidx.constraintlayout.core.SolverVariable!);
-    method public int getCurrentSize();
-    method public androidx.constraintlayout.core.SolverVariable! getVariable(int);
-    method public float getVariableValue(int);
-    method public int indexOf(androidx.constraintlayout.core.SolverVariable!);
-    method public void invert();
-    method public void put(androidx.constraintlayout.core.SolverVariable!, float);
-    method public float remove(androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public int sizeInBytes();
-    method public float use(androidx.constraintlayout.core.ArrayRow!, boolean);
-  }
-
-  public class Cache {
-    ctor public Cache();
-  }
-
-  public class GoalRow extends androidx.constraintlayout.core.ArrayRow {
-    ctor public GoalRow(androidx.constraintlayout.core.Cache!);
-  }
-
-  public class LinearSystem {
-    ctor public LinearSystem();
-    method public void addCenterPoint(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintWidget!, float, int);
-    method public void addCentering(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, float, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addConstraint(androidx.constraintlayout.core.ArrayRow!);
-    method public androidx.constraintlayout.core.ArrayRow! addEquality(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addEquality(androidx.constraintlayout.core.SolverVariable!, int);
-    method public void addGreaterBarrier(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, boolean);
-    method public void addGreaterThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addLowerBarrier(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, boolean);
-    method public void addLowerThan(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int, int);
-    method public void addRatio(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float, int);
-    method public void addSynonym(androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, int);
-    method public androidx.constraintlayout.core.SolverVariable! createErrorVariable(int, String!);
-    method public androidx.constraintlayout.core.SolverVariable! createExtraVariable();
-    method public androidx.constraintlayout.core.SolverVariable! createObjectVariable(Object!);
-    method public androidx.constraintlayout.core.ArrayRow! createRow();
-    method public static androidx.constraintlayout.core.ArrayRow! createRowDimensionPercent(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public androidx.constraintlayout.core.SolverVariable! createSlackVariable();
-    method public void displayReadableRows();
-    method public void displayVariablesReadableRows();
-    method public void fillMetrics(androidx.constraintlayout.core.Metrics!);
-    method public androidx.constraintlayout.core.Cache! getCache();
-    method public int getMemoryUsed();
-    method public static androidx.constraintlayout.core.Metrics! getMetrics();
-    method public int getNumEquations();
-    method public int getNumVariables();
-    method public int getObjectVariableValue(Object!);
-    method public void minimize() throws java.lang.Exception;
-    method public void removeRow(androidx.constraintlayout.core.ArrayRow!);
-    method public void reset();
-    field public static long ARRAY_ROW_CREATION;
-    field public static final boolean DEBUG = false;
-    field public static final boolean FULL_DEBUG = false;
-    field public static long OPTIMIZED_ARRAY_ROW_CREATION;
-    field public static boolean OPTIMIZED_ENGINE;
-    field public static boolean SIMPLIFY_SYNONYMS;
-    field public static boolean SKIP_COLUMNS;
-    field public static boolean USE_BASIC_SYNONYMS;
-    field public static boolean USE_DEPENDENCY_ORDERING;
-    field public static boolean USE_SYNONYMS;
-    field public boolean graphOptimizer;
-    field public boolean hasSimpleDefinition;
-    field public boolean newgraphOptimizer;
-    field public static androidx.constraintlayout.core.Metrics! sMetrics;
-  }
-
-  public class Metrics {
-    ctor public Metrics();
-    method public void copy(androidx.constraintlayout.core.Metrics!);
-    method public void reset();
-    field public long additionalMeasures;
-    field public long bfs;
-    field public long constraints;
-    field public long determineGroups;
-    field public long errors;
-    field public long extravariables;
-    field public long fullySolved;
-    field public long graphOptimizer;
-    field public long graphSolved;
-    field public long grouping;
-    field public long infeasibleDetermineGroups;
-    field public long iterations;
-    field public long lastTableSize;
-    field public long layouts;
-    field public long linearSolved;
-    field public long mChildCount;
-    field public long mEquations;
-    field public long mMeasureCalls;
-    field public long mMeasureDuration;
-    field public int mNumberOfLayouts;
-    field public int mNumberOfMeasures;
-    field public long mSimpleEquations;
-    field public long mSolverPasses;
-    field public long mVariables;
-    field public long maxRows;
-    field public long maxTableSize;
-    field public long maxVariables;
-    field public long measuredMatchWidgets;
-    field public long measuredWidgets;
-    field public long measures;
-    field public long measuresLayoutDuration;
-    field public long measuresWidgetsDuration;
-    field public long measuresWrap;
-    field public long measuresWrapInfeasible;
-    field public long minimize;
-    field public long minimizeGoal;
-    field public long nonresolvedWidgets;
-    field public long optimize;
-    field public long pivots;
-    field public java.util.ArrayList<java.lang.String!>! problematicLayouts;
-    field public long resolutions;
-    field public long resolvedWidgets;
-    field public long simpleconstraints;
-    field public long slackvariables;
-    field public long tableSizeIncrease;
-    field public long variables;
-    field public long widgets;
-  }
-
-  public class PriorityGoalRow extends androidx.constraintlayout.core.ArrayRow {
-    ctor public PriorityGoalRow(androidx.constraintlayout.core.Cache!);
-  }
-
-  public class SolverVariable implements java.lang.Comparable<androidx.constraintlayout.core.SolverVariable!> {
-    ctor public SolverVariable(androidx.constraintlayout.core.SolverVariable.Type!, String!);
-    ctor public SolverVariable(String!, androidx.constraintlayout.core.SolverVariable.Type!);
-    method public final void addToRow(androidx.constraintlayout.core.ArrayRow!);
-    method public int compareTo(androidx.constraintlayout.core.SolverVariable!);
-    method public String! getName();
-    method public final void removeFromRow(androidx.constraintlayout.core.ArrayRow!);
-    method public void reset();
-    method public void setFinalValue(androidx.constraintlayout.core.LinearSystem!, float);
-    method public void setName(String!);
-    method public void setSynonym(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.SolverVariable!, float);
-    method public void setType(androidx.constraintlayout.core.SolverVariable.Type!, String!);
-    method public final void updateReferencesWithNewDefinition(androidx.constraintlayout.core.LinearSystem!, androidx.constraintlayout.core.ArrayRow!);
-    field public static final int STRENGTH_BARRIER = 6; // 0x6
-    field public static final int STRENGTH_CENTERING = 7; // 0x7
-    field public static final int STRENGTH_EQUALITY = 5; // 0x5
-    field public static final int STRENGTH_FIXED = 8; // 0x8
-    field public static final int STRENGTH_HIGH = 3; // 0x3
-    field public static final int STRENGTH_HIGHEST = 4; // 0x4
-    field public static final int STRENGTH_LOW = 1; // 0x1
-    field public static final int STRENGTH_MEDIUM = 2; // 0x2
-    field public static final int STRENGTH_NONE = 0; // 0x0
-    field public float computedValue;
-    field public int id;
-    field public boolean inGoal;
-    field public boolean isFinalValue;
-    field public int strength;
-    field public int usageInRowCount;
-  }
-
-  public enum SolverVariable.Type {
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type CONSTANT;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type ERROR;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type SLACK;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type UNKNOWN;
-    enum_constant public static final androidx.constraintlayout.core.SolverVariable.Type UNRESTRICTED;
-  }
-
-  public class SolverVariableValues implements androidx.constraintlayout.core.ArrayRow.ArrayRowVariables {
-    method public void add(androidx.constraintlayout.core.SolverVariable!, float, boolean);
-    method public void clear();
-    method public boolean contains(androidx.constraintlayout.core.SolverVariable!);
-    method public void display();
-    method public void divideByAmount(float);
-    method public float get(androidx.constraintlayout.core.SolverVariable!);
-    method public int getCurrentSize();
-    method public androidx.constraintlayout.core.SolverVariable! getVariable(int);
-    method public float getVariableValue(int);
-    method public int indexOf(androidx.constraintlayout.core.SolverVariable!);
-    method public void invert();
-    method public void put(androidx.constraintlayout.core.SolverVariable!, float);
-    method public float remove(androidx.constraintlayout.core.SolverVariable!, boolean);
-    method public int sizeInBytes();
-    method public float use(androidx.constraintlayout.core.ArrayRow!, boolean);
-    field protected final androidx.constraintlayout.core.Cache! mCache;
-  }
-
-}
-
-package androidx.constraintlayout.core.dsl {
-
-  public class Barrier extends androidx.constraintlayout.core.dsl.Helper {
-    ctor public Barrier(String!);
-    ctor public Barrier(String!, String!);
-    method public androidx.constraintlayout.core.dsl.Barrier! addReference(androidx.constraintlayout.core.dsl.Ref!);
-    method public androidx.constraintlayout.core.dsl.Barrier! addReference(String!);
-    method public androidx.constraintlayout.core.dsl.Constraint.Side! getDirection();
-    method public int getMargin();
-    method public String! referencesToString();
-    method public void setDirection(androidx.constraintlayout.core.dsl.Constraint.Side!);
-    method public void setMargin(int);
-  }
-
-  public abstract class Chain extends androidx.constraintlayout.core.dsl.Helper {
-    ctor public Chain(String!);
-    method public androidx.constraintlayout.core.dsl.Chain! addReference(androidx.constraintlayout.core.dsl.Ref!);
-    method public androidx.constraintlayout.core.dsl.Chain! addReference(String!);
-    method public androidx.constraintlayout.core.dsl.Chain.Style! getStyle();
-    method public String! referencesToString();
-    method public void setStyle(androidx.constraintlayout.core.dsl.Chain.Style!);
-    field protected java.util.ArrayList<androidx.constraintlayout.core.dsl.Ref!>! references;
-    field protected static final java.util.Map<androidx.constraintlayout.core.dsl.Chain.Style!,java.lang.String!>! styleMap;
-  }
-
-  public class Chain.Anchor {
-    method public void build(StringBuilder!);
-    method public String! getId();
-  }
-
-  public enum Chain.Style {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Chain.Style PACKED;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Chain.Style SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Chain.Style SPREAD_INSIDE;
-  }
-
-  public class Constraint {
-    ctor public Constraint(String!);
-    method protected void append(StringBuilder!, String!, float);
-    method public String! convertStringArrayToString(String![]!);
-    method public androidx.constraintlayout.core.dsl.Constraint.VAnchor! getBaseline();
-    method public androidx.constraintlayout.core.dsl.Constraint.VAnchor! getBottom();
-    method public float getCircleAngle();
-    method public String! getCircleConstraint();
-    method public int getCircleRadius();
-    method public String! getDimensionRatio();
-    method public int getEditorAbsoluteX();
-    method public int getEditorAbsoluteY();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getEnd();
-    method public int getHeight();
-    method public androidx.constraintlayout.core.dsl.Constraint.Behaviour! getHeightDefault();
-    method public int getHeightMax();
-    method public int getHeightMin();
-    method public float getHeightPercent();
-    method public float getHorizontalBias();
-    method public androidx.constraintlayout.core.dsl.Constraint.ChainMode! getHorizontalChainStyle();
-    method public float getHorizontalWeight();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getLeft();
-    method public String![]! getReferenceIds();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getRight();
-    method public androidx.constraintlayout.core.dsl.Constraint.HAnchor! getStart();
-    method public androidx.constraintlayout.core.dsl.Constraint.VAnchor! getTop();
-    method public float getVerticalBias();
-    method public androidx.constraintlayout.core.dsl.Constraint.ChainMode! getVerticalChainStyle();
-    method public float getVerticalWeight();
-    method public int getWidth();
-    method public androidx.constraintlayout.core.dsl.Constraint.Behaviour! getWidthDefault();
-    method public int getWidthMax();
-    method public int getWidthMin();
-    method public float getWidthPercent();
-    method public boolean isConstrainedHeight();
-    method public boolean isConstrainedWidth();
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void setCircleAngle(float);
-    method public void setCircleConstraint(String!);
-    method public void setCircleRadius(int);
-    method public void setConstrainedHeight(boolean);
-    method public void setConstrainedWidth(boolean);
-    method public void setDimensionRatio(String!);
-    method public void setEditorAbsoluteX(int);
-    method public void setEditorAbsoluteY(int);
-    method public void setHeight(int);
-    method public void setHeightDefault(androidx.constraintlayout.core.dsl.Constraint.Behaviour!);
-    method public void setHeightMax(int);
-    method public void setHeightMin(int);
-    method public void setHeightPercent(float);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalChainStyle(androidx.constraintlayout.core.dsl.Constraint.ChainMode!);
-    method public void setHorizontalWeight(float);
-    method public void setReferenceIds(String![]!);
-    method public void setVerticalBias(float);
-    method public void setVerticalChainStyle(androidx.constraintlayout.core.dsl.Constraint.ChainMode!);
-    method public void setVerticalWeight(float);
-    method public void setWidth(int);
-    method public void setWidthDefault(androidx.constraintlayout.core.dsl.Constraint.Behaviour!);
-    method public void setWidthMax(int);
-    method public void setWidthMin(int);
-    method public void setWidthPercent(float);
-    field public static final androidx.constraintlayout.core.dsl.Constraint! PARENT;
-  }
-
-  public class Constraint.Anchor {
-    method public void build(StringBuilder!);
-    method public String! getId();
-  }
-
-  public enum Constraint.Behaviour {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour PERCENT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour RATIO;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour RESOLVED;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Behaviour WRAP;
-  }
-
-  public enum Constraint.ChainMode {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.ChainMode PACKED;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.ChainMode SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.ChainMode SPREAD_INSIDE;
-  }
-
-  public class Constraint.HAnchor extends androidx.constraintlayout.core.dsl.Constraint.Anchor {
-  }
-
-  public enum Constraint.HSide {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.HSide START;
-  }
-
-  public enum Constraint.Side {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.Side TOP;
-  }
-
-  public class Constraint.VAnchor extends androidx.constraintlayout.core.dsl.Constraint.Anchor {
-  }
-
-  public enum Constraint.VSide {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.VSide BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.VSide BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Constraint.VSide TOP;
-  }
-
-  public class ConstraintSet {
-    ctor public ConstraintSet(String!);
-    method public void add(androidx.constraintlayout.core.dsl.Constraint!);
-    method public void add(androidx.constraintlayout.core.dsl.Helper!);
-  }
-
-  public abstract class Guideline extends androidx.constraintlayout.core.dsl.Helper {
-    method public int getEnd();
-    method public float getPercent();
-    method public int getStart();
-    method public void setEnd(int);
-    method public void setPercent(float);
-    method public void setStart(int);
-  }
-
-  public class HChain extends androidx.constraintlayout.core.dsl.Chain {
-    ctor public HChain(String!);
-    ctor public HChain(String!, String!);
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getEnd();
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getLeft();
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getRight();
-    method public androidx.constraintlayout.core.dsl.HChain.HAnchor! getStart();
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToEnd(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToLeft(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToRight(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int);
-    method public void linkToStart(androidx.constraintlayout.core.dsl.Constraint.HAnchor!, int, int);
-  }
-
-  public class HChain.HAnchor extends androidx.constraintlayout.core.dsl.Chain.Anchor {
-  }
-
-  public class Helper {
-    ctor public Helper(String!, androidx.constraintlayout.core.dsl.Helper.HelperType!);
-    ctor public Helper(String!, androidx.constraintlayout.core.dsl.Helper.HelperType!, String!);
-    method public void append(java.util.Map<java.lang.String!,java.lang.String!>!, StringBuilder!);
-    method public java.util.Map<java.lang.String!,java.lang.String!>! convertConfigToMap();
-    method public String! getConfig();
-    method public String! getId();
-    method public androidx.constraintlayout.core.dsl.Helper.HelperType! getType();
-    method public static void main(String![]!);
-    field protected String! config;
-    field protected java.util.Map<java.lang.String!,java.lang.String!>! configMap;
-    field protected final String! name;
-    field protected static final java.util.Map<androidx.constraintlayout.core.dsl.Constraint.Side!,java.lang.String!>! sideMap;
-    field protected androidx.constraintlayout.core.dsl.Helper.HelperType! type;
-    field protected static final java.util.Map<androidx.constraintlayout.core.dsl.Helper.Type!,java.lang.String!>! typeMap;
-  }
-
-  public static final class Helper.HelperType {
-    ctor public Helper.HelperType(String!);
-  }
-
-  public enum Helper.Type {
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type BARRIER;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type HORIZONTAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type HORIZONTAL_GUIDELINE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type VERTICAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.Helper.Type VERTICAL_GUIDELINE;
-  }
-
-  public class KeyAttribute extends androidx.constraintlayout.core.dsl.Keys {
-    ctor public KeyAttribute(int, String!);
-    method protected void attributesToString(StringBuilder!);
-    method public float getAlpha();
-    method public androidx.constraintlayout.core.dsl.KeyAttribute.Fit! getCurveFit();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public float getRotation();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public String! getTarget();
-    method public String! getTransitionEasing();
-    method public float getTransitionPathRotate();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public androidx.constraintlayout.core.dsl.KeyAttribute.Visibility! getVisibility();
-    method public void setAlpha(float);
-    method public void setCurveFit(androidx.constraintlayout.core.dsl.KeyAttribute.Fit!);
-    method public void setPivotX(float);
-    method public void setPivotY(float);
-    method public void setRotation(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTarget(String!);
-    method public void setTransitionEasing(String!);
-    method public void setTransitionPathRotate(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    method public void setVisibility(androidx.constraintlayout.core.dsl.KeyAttribute.Visibility!);
-    field protected String! TYPE;
-  }
-
-  public enum KeyAttribute.Fit {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Fit LINEAR;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Fit SPLINE;
-  }
-
-  public enum KeyAttribute.Visibility {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Visibility GONE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Visibility INVISIBLE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttribute.Visibility VISIBLE;
-  }
-
-  public class KeyAttributes extends androidx.constraintlayout.core.dsl.Keys {
-    method protected void attributesToString(StringBuilder!);
-    method public float[]! getAlpha();
-    method public androidx.constraintlayout.core.dsl.KeyAttributes.Fit! getCurveFit();
-    method public float[]! getPivotX();
-    method public float[]! getPivotY();
-    method public float[]! getRotation();
-    method public float[]! getRotationX();
-    method public float[]! getRotationY();
-    method public float[]! getScaleX();
-    method public float[]! getScaleY();
-    method public String![]! getTarget();
-    method public String! getTransitionEasing();
-    method public float[]! getTransitionPathRotate();
-    method public float[]! getTranslationX();
-    method public float[]! getTranslationY();
-    method public float[]! getTranslationZ();
-    method public androidx.constraintlayout.core.dsl.KeyAttributes.Visibility![]! getVisibility();
-    method public void setAlpha(float...!);
-    method public void setCurveFit(androidx.constraintlayout.core.dsl.KeyAttributes.Fit!);
-    method public void setPivotX(float...!);
-    method public void setPivotY(float...!);
-    method public void setRotation(float...!);
-    method public void setRotationX(float...!);
-    method public void setRotationY(float...!);
-    method public void setScaleX(float[]!);
-    method public void setScaleY(float[]!);
-    method public void setTarget(String![]!);
-    method public void setTransitionEasing(String!);
-    method public void setTransitionPathRotate(float...!);
-    method public void setTranslationX(float[]!);
-    method public void setTranslationY(float[]!);
-    method public void setTranslationZ(float[]!);
-    method public void setVisibility(androidx.constraintlayout.core.dsl.KeyAttributes.Visibility!...!);
-    field protected String! TYPE;
-  }
-
-  public enum KeyAttributes.Fit {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Fit LINEAR;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Fit SPLINE;
-  }
-
-  public enum KeyAttributes.Visibility {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Visibility GONE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Visibility INVISIBLE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyAttributes.Visibility VISIBLE;
-  }
-
-  public class KeyCycle extends androidx.constraintlayout.core.dsl.KeyAttribute {
-    method public float getOffset();
-    method public float getPeriod();
-    method public float getPhase();
-    method public androidx.constraintlayout.core.dsl.KeyCycle.Wave! getShape();
-    method public void setOffset(float);
-    method public void setPeriod(float);
-    method public void setPhase(float);
-    method public void setShape(androidx.constraintlayout.core.dsl.KeyCycle.Wave!);
-  }
-
-  public enum KeyCycle.Wave {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave COS;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave REVERSE_SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave SIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave SQUARE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycle.Wave TRIANGLE;
-  }
-
-  public class KeyCycles extends androidx.constraintlayout.core.dsl.KeyAttributes {
-    method public float[]! getWaveOffset();
-    method public float[]! getWavePeriod();
-    method public float[]! getWavePhase();
-    method public androidx.constraintlayout.core.dsl.KeyCycles.Wave! getWaveShape();
-    method public void setWaveOffset(float...!);
-    method public void setWavePeriod(float...!);
-    method public void setWavePhase(float...!);
-    method public void setWaveShape(androidx.constraintlayout.core.dsl.KeyCycles.Wave!);
-  }
-
-  public enum KeyCycles.Wave {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave COS;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave REVERSE_SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave SAW;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave SIN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave SQUARE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyCycles.Wave TRIANGLE;
-  }
-
-  public class KeyFrames {
-    ctor public KeyFrames();
-    method public void add(androidx.constraintlayout.core.dsl.Keys!);
-  }
-
-  public class KeyPosition extends androidx.constraintlayout.core.dsl.Keys {
-    ctor public KeyPosition(String!, int);
-    method public int getFrames();
-    method public float getPercentHeight();
-    method public float getPercentWidth();
-    method public float getPercentX();
-    method public float getPercentY();
-    method public androidx.constraintlayout.core.dsl.KeyPosition.Type! getPositionType();
-    method public String! getTarget();
-    method public String! getTransitionEasing();
-    method public void setFrames(int);
-    method public void setPercentHeight(float);
-    method public void setPercentWidth(float);
-    method public void setPercentX(float);
-    method public void setPercentY(float);
-    method public void setPositionType(androidx.constraintlayout.core.dsl.KeyPosition.Type!);
-    method public void setTarget(String!);
-    method public void setTransitionEasing(String!);
-  }
-
-  public enum KeyPosition.Type {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPosition.Type CARTESIAN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPosition.Type PATH;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPosition.Type SCREEN;
-  }
-
-  public class KeyPositions extends androidx.constraintlayout.core.dsl.Keys {
-    ctor public KeyPositions(int, java.lang.String!...!);
-    method public int[]! getFrames();
-    method public float[]! getPercentHeight();
-    method public float[]! getPercentWidth();
-    method public float[]! getPercentX();
-    method public float[]! getPercentY();
-    method public androidx.constraintlayout.core.dsl.KeyPositions.Type! getPositionType();
-    method public String![]! getTarget();
-    method public String! getTransitionEasing();
-    method public void setFrames(int...!);
-    method public void setPercentHeight(float...!);
-    method public void setPercentWidth(float...!);
-    method public void setPercentX(float...!);
-    method public void setPercentY(float...!);
-    method public void setPositionType(androidx.constraintlayout.core.dsl.KeyPositions.Type!);
-    method public void setTransitionEasing(String!);
-  }
-
-  public enum KeyPositions.Type {
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPositions.Type CARTESIAN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPositions.Type PATH;
-    enum_constant public static final androidx.constraintlayout.core.dsl.KeyPositions.Type SCREEN;
-  }
-
-  public class Keys {
-    ctor public Keys();
-    method protected void append(StringBuilder!, String!, float);
-    method protected void append(StringBuilder!, String!, float[]!);
-    method protected void append(StringBuilder!, String!, int);
-    method protected void append(StringBuilder!, String!, String!);
-    method protected void append(StringBuilder!, String!, String![]!);
-    method protected String! unpack(String![]!);
-  }
-
-  public class MotionScene {
-    ctor public MotionScene();
-    method public void addConstraintSet(androidx.constraintlayout.core.dsl.ConstraintSet!);
-    method public void addTransition(androidx.constraintlayout.core.dsl.Transition!);
-  }
-
-  public class OnSwipe {
-    ctor public OnSwipe();
-    ctor public OnSwipe(String!, androidx.constraintlayout.core.dsl.OnSwipe.Side!, androidx.constraintlayout.core.dsl.OnSwipe.Drag!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Mode! getAutoCompleteMode();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Drag! getDragDirection();
-    method public float getDragScale();
-    method public float getDragThreshold();
-    method public String! getLimitBoundsTo();
-    method public float getMaxAcceleration();
-    method public float getMaxVelocity();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.TouchUp! getOnTouchUp();
-    method public String! getRotationCenterId();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Boundary! getSpringBoundary();
-    method public float getSpringDamping();
-    method public float getSpringMass();
-    method public float getSpringStiffness();
-    method public float getSpringStopThreshold();
-    method public String! getTouchAnchorId();
-    method public androidx.constraintlayout.core.dsl.OnSwipe.Side! getTouchAnchorSide();
-    method public void setAutoCompleteMode(androidx.constraintlayout.core.dsl.OnSwipe.Mode!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setDragDirection(androidx.constraintlayout.core.dsl.OnSwipe.Drag!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setDragScale(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setDragThreshold(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setLimitBoundsTo(String!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setMaxAcceleration(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setMaxVelocity(int);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setOnTouchUp(androidx.constraintlayout.core.dsl.OnSwipe.TouchUp!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setRotateCenter(String!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringBoundary(androidx.constraintlayout.core.dsl.OnSwipe.Boundary!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringDamping(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringMass(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringStiffness(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setSpringStopThreshold(float);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setTouchAnchorId(String!);
-    method public androidx.constraintlayout.core.dsl.OnSwipe! setTouchAnchorSide(androidx.constraintlayout.core.dsl.OnSwipe.Side!);
-    field public static final int FLAG_DISABLE_POST_SCROLL = 1; // 0x1
-    field public static final int FLAG_DISABLE_SCROLL = 2; // 0x2
-  }
-
-  public enum OnSwipe.Boundary {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary BOUNCE_BOTH;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary BOUNCE_END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary BOUNCE_START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Boundary OVERSHOOT;
-  }
-
-  public enum OnSwipe.Drag {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag ANTICLOCKWISE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag CLOCKWISE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag DOWN;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Drag UP;
-  }
-
-  public enum OnSwipe.Mode {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Mode SPRING;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Mode VELOCITY;
-  }
-
-  public enum OnSwipe.Side {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side LEFT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side MIDDLE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.Side TOP;
-  }
-
-  public enum OnSwipe.TouchUp {
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp AUTOCOMPLETE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp DECELERATE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp DECELERATE_COMPLETE;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp NEVER_COMPLETE_END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp NEVER_COMPLETE_START;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp STOP;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp TO_END;
-    enum_constant public static final androidx.constraintlayout.core.dsl.OnSwipe.TouchUp TO_START;
-  }
-
-  public class Ref {
-    method public static void addStringToReferences(String!, java.util.ArrayList<androidx.constraintlayout.core.dsl.Ref!>!);
-    method public String! getId();
-    method public float getPostMargin();
-    method public float getPreMargin();
-    method public float getWeight();
-    method public static float parseFloat(Object!);
-    method public static androidx.constraintlayout.core.dsl.Ref! parseStringToRef(String!);
-    method public void setId(String!);
-    method public void setPostMargin(float);
-    method public void setPreMargin(float);
-    method public void setWeight(float);
-  }
-
-  public class Transition {
-    ctor public Transition(String!, String!);
-    ctor public Transition(String!, String!, String!);
-    method public String! getId();
-    method public void setDuration(int);
-    method public void setFrom(String!);
-    method public void setId(String!);
-    method public void setKeyFrames(androidx.constraintlayout.core.dsl.Keys!);
-    method public void setOnSwipe(androidx.constraintlayout.core.dsl.OnSwipe!);
-    method public void setStagger(float);
-    method public void setTo(String!);
-  }
-
-  public class VChain extends androidx.constraintlayout.core.dsl.Chain {
-    ctor public VChain(String!);
-    ctor public VChain(String!, String!);
-    method public androidx.constraintlayout.core.dsl.VChain.VAnchor! getBaseline();
-    method public androidx.constraintlayout.core.dsl.VChain.VAnchor! getBottom();
-    method public androidx.constraintlayout.core.dsl.VChain.VAnchor! getTop();
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBaseline(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToBottom(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int);
-    method public void linkToTop(androidx.constraintlayout.core.dsl.Constraint.VAnchor!, int, int);
-  }
-
-  public class VChain.VAnchor extends androidx.constraintlayout.core.dsl.Chain.Anchor {
-  }
-
-  public class VGuideline extends androidx.constraintlayout.core.dsl.Guideline {
-    ctor public VGuideline(String!);
-    ctor public VGuideline(String!, String!);
-  }
-
-}
-
-package androidx.constraintlayout.core.motion {
-
-  public class CustomAttribute {
-    ctor public CustomAttribute(androidx.constraintlayout.core.motion.CustomAttribute!, Object!);
-    ctor public CustomAttribute(String!, androidx.constraintlayout.core.motion.CustomAttribute.AttributeType!);
-    ctor public CustomAttribute(String!, androidx.constraintlayout.core.motion.CustomAttribute.AttributeType!, Object!, boolean);
-    method public boolean diff(androidx.constraintlayout.core.motion.CustomAttribute!);
-    method public androidx.constraintlayout.core.motion.CustomAttribute.AttributeType! getType();
-    method public float getValueToInterpolate();
-    method public void getValuesToInterpolate(float[]!);
-    method public static int hsvToRgb(float, float, float);
-    method public boolean isContinuous();
-    method public int numberOfInterpolatedValues();
-    method public void setColorValue(int);
-    method public void setFloatValue(float);
-    method public void setIntValue(int);
-    method public void setStringValue(String!);
-    method public void setValue(float[]!);
-    method public void setValue(Object!);
-  }
-
-  public enum CustomAttribute.AttributeType {
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType BOOLEAN_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType COLOR_DRAWABLE_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType COLOR_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType DIMENSION_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType FLOAT_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType INT_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType REFERENCE_TYPE;
-    enum_constant public static final androidx.constraintlayout.core.motion.CustomAttribute.AttributeType STRING_TYPE;
-  }
-
-  public class CustomVariable {
-    ctor public CustomVariable(androidx.constraintlayout.core.motion.CustomVariable!);
-    ctor public CustomVariable(androidx.constraintlayout.core.motion.CustomVariable!, Object!);
-    ctor public CustomVariable(String!, int);
-    ctor public CustomVariable(String!, int, boolean);
-    ctor public CustomVariable(String!, int, float);
-    ctor public CustomVariable(String!, int, int);
-    ctor public CustomVariable(String!, int, Object!);
-    ctor public CustomVariable(String!, int, String!);
-    method public void applyToWidget(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public static String! colorString(int);
-    method public androidx.constraintlayout.core.motion.CustomVariable! copy();
-    method public boolean diff(androidx.constraintlayout.core.motion.CustomVariable!);
-    method public boolean getBooleanValue();
-    method public int getColorValue();
-    method public float getFloatValue();
-    method public int getIntegerValue();
-    method public int getInterpolatedColor(float[]!);
-    method public String! getName();
-    method public String! getStringValue();
-    method public int getType();
-    method public float getValueToInterpolate();
-    method public void getValuesToInterpolate(float[]!);
-    method public static int hsvToRgb(float, float, float);
-    method public boolean isContinuous();
-    method public int numberOfInterpolatedValues();
-    method public static int rgbaTocColor(float, float, float, float);
-    method public void setBooleanValue(boolean);
-    method public void setFloatValue(float);
-    method public void setIntValue(int);
-    method public void setInterpolatedValue(androidx.constraintlayout.core.motion.MotionWidget!, float[]!);
-    method public void setStringValue(String!);
-    method public void setValue(float[]!);
-    method public void setValue(Object!);
-  }
-
-  public class Motion implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public Motion(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void addKey(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public int buildKeyFrames(float[]!, int[]!, int[]!);
-    method public void buildPath(float[]!, int);
-    method public void buildRect(float, float[]!, int);
-    method public String! getAnimateRelativeTo();
-    method public void getCenter(double, float[]!, float[]!);
-    method public float getCenterX();
-    method public float getCenterY();
-    method public void getDpDt(float, float, float, float[]!);
-    method public int getDrawPath();
-    method public float getFinalHeight();
-    method public float getFinalWidth();
-    method public float getFinalX();
-    method public float getFinalY();
-    method public int getId(String!);
-    method public androidx.constraintlayout.core.motion.MotionPaths! getKeyFrame(int);
-    method public int getKeyFrameInfo(int, int[]!);
-    method public int getKeyFramePositions(int[]!, float[]!);
-    method public float getMotionStagger();
-    method public float getStartHeight();
-    method public float getStartWidth();
-    method public float getStartX();
-    method public float getStartY();
-    method public int getTransformPivotTarget();
-    method public androidx.constraintlayout.core.motion.MotionWidget! getView();
-    method public boolean interpolate(androidx.constraintlayout.core.motion.MotionWidget!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-    method public void setDrawPath(int);
-    method public void setEnd(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void setIdString(String!);
-    method public void setPathMotionArc(int);
-    method public void setStaggerOffset(float);
-    method public void setStaggerScale(float);
-    method public void setStart(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void setStartState(androidx.constraintlayout.core.motion.utils.ViewState!, androidx.constraintlayout.core.motion.MotionWidget!, int, int, int);
-    method public void setTransformPivotTarget(int);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public void setView(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public void setup(int, int, float, long);
-    method public void setupRelative(androidx.constraintlayout.core.motion.Motion!);
-    field public static final int DRAW_PATH_AS_CONFIGURED = 4; // 0x4
-    field public static final int DRAW_PATH_BASIC = 1; // 0x1
-    field public static final int DRAW_PATH_CARTESIAN = 3; // 0x3
-    field public static final int DRAW_PATH_NONE = 0; // 0x0
-    field public static final int DRAW_PATH_RECTANGLE = 5; // 0x5
-    field public static final int DRAW_PATH_RELATIVE = 2; // 0x2
-    field public static final int DRAW_PATH_SCREEN = 6; // 0x6
-    field public static final int HORIZONTAL_PATH_X = 2; // 0x2
-    field public static final int HORIZONTAL_PATH_Y = 3; // 0x3
-    field public static final int PATH_PERCENT = 0; // 0x0
-    field public static final int PATH_PERPENDICULAR = 1; // 0x1
-    field public static final int ROTATION_LEFT = 2; // 0x2
-    field public static final int ROTATION_RIGHT = 1; // 0x1
-    field public static final int VERTICAL_PATH_X = 4; // 0x4
-    field public static final int VERTICAL_PATH_Y = 5; // 0x5
-    field public String! mId;
-  }
-
-  public class MotionPaths implements java.lang.Comparable<androidx.constraintlayout.core.motion.MotionPaths!> {
-    ctor public MotionPaths();
-    ctor public MotionPaths(int, int, androidx.constraintlayout.core.motion.key.MotionKeyPosition!, androidx.constraintlayout.core.motion.MotionPaths!, androidx.constraintlayout.core.motion.MotionPaths!);
-    method public void applyParameters(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public int compareTo(androidx.constraintlayout.core.motion.MotionPaths!);
-    method public void configureRelativeTo(androidx.constraintlayout.core.motion.Motion!);
-    method public void setupRelative(androidx.constraintlayout.core.motion.Motion!, androidx.constraintlayout.core.motion.MotionPaths!);
-    field public static final int CARTESIAN = 0; // 0x0
-    field public static final boolean DEBUG = false;
-    field public static final boolean OLD_WAY = false;
-    field public static final int PERPENDICULAR = 1; // 0x1
-    field public static final int SCREEN = 2; // 0x2
-    field public static final String TAG = "MotionPaths";
-    field public String! mId;
-  }
-
-  public class MotionWidget implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public MotionWidget();
-    ctor public MotionWidget(androidx.constraintlayout.core.state.WidgetFrame!);
-    method public androidx.constraintlayout.core.motion.MotionWidget! findViewById(int);
-    method public float getAlpha();
-    method public int getBottom();
-    method public androidx.constraintlayout.core.motion.CustomVariable! getCustomAttribute(String!);
-    method public java.util.Set<java.lang.String!>! getCustomAttributeNames();
-    method public int getHeight();
-    method public int getId(String!);
-    method public int getLeft();
-    method public String! getName();
-    method public androidx.constraintlayout.core.motion.MotionWidget! getParent();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public int getRight();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public int getTop();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public float getValueAttributes(int);
-    method public int getVisibility();
-    method public androidx.constraintlayout.core.state.WidgetFrame! getWidgetFrame();
-    method public int getWidth();
-    method public int getX();
-    method public int getY();
-    method public void layout(int, int, int, int);
-    method public void setBounds(int, int, int, int);
-    method public void setCustomAttribute(String!, int, boolean);
-    method public void setCustomAttribute(String!, int, float);
-    method public void setCustomAttribute(String!, int, int);
-    method public void setCustomAttribute(String!, int, String!);
-    method public void setInterpolatedValue(androidx.constraintlayout.core.motion.CustomAttribute!, float[]!);
-    method public void setPivotX(float);
-    method public void setPivotY(float);
-    method public void setRotationX(float);
-    method public void setRotationY(float);
-    method public void setRotationZ(float);
-    method public void setScaleX(float);
-    method public void setScaleY(float);
-    method public void setTranslationX(float);
-    method public void setTranslationY(float);
-    method public void setTranslationZ(float);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public boolean setValueAttributes(int, float);
-    method public boolean setValueMotion(int, float);
-    method public boolean setValueMotion(int, int);
-    method public boolean setValueMotion(int, String!);
-    method public void setVisibility(int);
-    method public void updateMotion(androidx.constraintlayout.core.motion.utils.TypedValues!);
-    field public static final int FILL_PARENT = -1; // 0xffffffff
-    field public static final int GONE_UNSET = -2147483648; // 0x80000000
-    field public static final int INVISIBLE = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int MATCH_PARENT = -1; // 0xffffffff
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int ROTATE_LEFT_OF_PORTRATE = 4; // 0x4
-    field public static final int ROTATE_NONE = 0; // 0x0
-    field public static final int ROTATE_PORTRATE_OF_LEFT = 2; // 0x2
-    field public static final int ROTATE_PORTRATE_OF_RIGHT = 1; // 0x1
-    field public static final int ROTATE_RIGHT_OF_PORTRATE = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int VISIBILITY_MODE_IGNORE = 1; // 0x1
-    field public static final int VISIBILITY_MODE_NORMAL = 0; // 0x0
-    field public static final int VISIBLE = 4; // 0x4
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-  }
-
-  public static class MotionWidget.Motion {
-    ctor public MotionWidget.Motion();
-    field public int mAnimateCircleAngleTo;
-    field public String! mAnimateRelativeTo;
-    field public int mDrawPath;
-    field public float mMotionStagger;
-    field public int mPathMotionArc;
-    field public float mPathRotate;
-    field public int mPolarRelativeTo;
-    field public int mQuantizeInterpolatorID;
-    field public String! mQuantizeInterpolatorString;
-    field public int mQuantizeInterpolatorType;
-    field public float mQuantizeMotionPhase;
-    field public int mQuantizeMotionSteps;
-    field public String! mTransitionEasing;
-  }
-
-  public static class MotionWidget.PropertySet {
-    ctor public MotionWidget.PropertySet();
-    field public float alpha;
-    field public float mProgress;
-    field public int mVisibilityMode;
-    field public int visibility;
-  }
-
-}
-
-package androidx.constraintlayout.core.motion.key {
-
-  public class MotionConstraintSet {
-    ctor public MotionConstraintSet();
-    field public static final int ROTATE_LEFT_OF_PORTRATE = 4; // 0x4
-    field public static final int ROTATE_NONE = 0; // 0x0
-    field public static final int ROTATE_PORTRATE_OF_LEFT = 2; // 0x2
-    field public static final int ROTATE_PORTRATE_OF_RIGHT = 1; // 0x1
-    field public static final int ROTATE_RIGHT_OF_PORTRATE = 3; // 0x3
-    field public String! mIdString;
-    field public int mRotate;
-  }
-
-  public abstract class MotionKey implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public MotionKey();
-    method public abstract void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public abstract androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public androidx.constraintlayout.core.motion.key.MotionKey! copy(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public abstract void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getFramePosition();
-    method public void setCustomAttribute(String!, int, boolean);
-    method public void setCustomAttribute(String!, int, float);
-    method public void setCustomAttribute(String!, int, int);
-    method public void setCustomAttribute(String!, int, String!);
-    method public void setFramePosition(int);
-    method public void setInterpolation(java.util.HashMap<java.lang.String!,java.lang.Integer!>!);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! setViewId(int);
-    field public static final String ALPHA = "alpha";
-    field public static final String CUSTOM = "CUSTOM";
-    field public static final String ELEVATION = "elevation";
-    field public static final String ROTATION = "rotationZ";
-    field public static final String ROTATION_X = "rotationX";
-    field public static final String SCALE_X = "scaleX";
-    field public static final String SCALE_Y = "scaleY";
-    field public static final String TRANSITION_PATH_ROTATE = "transitionPathRotate";
-    field public static final String TRANSLATION_X = "translationX";
-    field public static final String TRANSLATION_Y = "translationY";
-    field public static int UNSET;
-    field public static final String VISIBILITY = "visibility";
-    field public java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.CustomVariable!>! mCustom;
-    field public int mFramePosition;
-    field public int mType;
-  }
-
-  public class MotionKeyAttributes extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyAttributes();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getCurveFit();
-    method public int getId(String!);
-    method public void printAttributes();
-    field public static final int KEY_TYPE = 1; // 0x1
-  }
-
-  public class MotionKeyCycle extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyCycle();
-    method public void addCycleValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void dump();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    method public float getValue(String!);
-    method public void printAttributes();
-    field public static final int KEY_TYPE = 4; // 0x4
-    field public static final int SHAPE_BOUNCE = 6; // 0x6
-    field public static final int SHAPE_COS_WAVE = 5; // 0x5
-    field public static final int SHAPE_REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SHAPE_SAW_WAVE = 3; // 0x3
-    field public static final int SHAPE_SIN_WAVE = 0; // 0x0
-    field public static final int SHAPE_SQUARE_WAVE = 1; // 0x1
-    field public static final int SHAPE_TRIANGLE_WAVE = 2; // 0x2
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_PHASE = "wavePhase";
-    field public static final String WAVE_SHAPE = "waveShape";
-  }
-
-  public class MotionKeyPosition extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyPosition();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    method public boolean intersects(int, int, androidx.constraintlayout.core.motion.utils.FloatRect!, androidx.constraintlayout.core.motion.utils.FloatRect!, float, float);
-    method public void positionAttributes(androidx.constraintlayout.core.motion.MotionWidget!, androidx.constraintlayout.core.motion.utils.FloatRect!, androidx.constraintlayout.core.motion.utils.FloatRect!, float, float, String![]!, float[]!);
-    field protected static final float SELECTION_SLOPE = 20.0f;
-    field public static final int TYPE_CARTESIAN = 0; // 0x0
-    field public static final int TYPE_PATH = 1; // 0x1
-    field public static final int TYPE_SCREEN = 2; // 0x2
-    field public float mAltPercentX;
-    field public float mAltPercentY;
-    field public int mCurveFit;
-    field public int mDrawPath;
-    field public int mPathMotionArc;
-    field public float mPercentHeight;
-    field public float mPercentWidth;
-    field public float mPercentX;
-    field public float mPercentY;
-    field public int mPositionType;
-    field public String! mTransitionEasing;
-  }
-
-  public class MotionKeyTimeCycle extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyTimeCycle();
-    method public void addTimeValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public androidx.constraintlayout.core.motion.key.MotionKeyTimeCycle! copy(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    field public static final int KEY_TYPE = 3; // 0x3
-  }
-
-  public class MotionKeyTrigger extends androidx.constraintlayout.core.motion.key.MotionKey {
-    ctor public MotionKeyTrigger();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.core.motion.utils.SplineSet!>!);
-    method public androidx.constraintlayout.core.motion.key.MotionKey! clone();
-    method public void conditionallyFire(float, androidx.constraintlayout.core.motion.MotionWidget!);
-    method public androidx.constraintlayout.core.motion.key.MotionKeyTrigger! copy(androidx.constraintlayout.core.motion.key.MotionKey!);
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public int getId(String!);
-    field public static final String CROSS = "CROSS";
-    field public static final int KEY_TYPE = 5; // 0x5
-    field public static final String NEGATIVE_CROSS = "negativeCross";
-    field public static final String POSITIVE_CROSS = "positiveCross";
-    field public static final String POST_LAYOUT = "postLayout";
-    field public static final String TRIGGER_COLLISION_ID = "triggerCollisionId";
-    field public static final String TRIGGER_COLLISION_VIEW = "triggerCollisionView";
-    field public static final String TRIGGER_ID = "triggerID";
-    field public static final String TRIGGER_RECEIVER = "triggerReceiver";
-    field public static final String TRIGGER_SLACK = "triggerSlack";
-    field public static final int TYPE_CROSS = 312; // 0x138
-    field public static final int TYPE_NEGATIVE_CROSS = 310; // 0x136
-    field public static final int TYPE_POSITIVE_CROSS = 309; // 0x135
-    field public static final int TYPE_POST_LAYOUT = 304; // 0x130
-    field public static final int TYPE_TRIGGER_COLLISION_ID = 307; // 0x133
-    field public static final int TYPE_TRIGGER_COLLISION_VIEW = 306; // 0x132
-    field public static final int TYPE_TRIGGER_ID = 308; // 0x134
-    field public static final int TYPE_TRIGGER_RECEIVER = 311; // 0x137
-    field public static final int TYPE_TRIGGER_SLACK = 305; // 0x131
-    field public static final int TYPE_VIEW_TRANSITION_ON_CROSS = 301; // 0x12d
-    field public static final int TYPE_VIEW_TRANSITION_ON_NEGATIVE_CROSS = 303; // 0x12f
-    field public static final int TYPE_VIEW_TRANSITION_ON_POSITIVE_CROSS = 302; // 0x12e
-    field public static final String VIEW_TRANSITION_ON_CROSS = "viewTransitionOnCross";
-    field public static final String VIEW_TRANSITION_ON_NEGATIVE_CROSS = "viewTransitionOnNegativeCross";
-    field public static final String VIEW_TRANSITION_ON_POSITIVE_CROSS = "viewTransitionOnPositiveCross";
-  }
-
-}
-
-package androidx.constraintlayout.core.motion.parse {
-
-  public class KeyParser {
-    ctor public KeyParser();
-    method public static void main(String![]!);
-    method public static androidx.constraintlayout.core.motion.utils.TypedBundle! parseAttributes(String!);
-  }
-
-}
-
-package androidx.constraintlayout.core.motion.utils {
-
-  public class ArcCurveFit extends androidx.constraintlayout.core.motion.utils.CurveFit {
-    ctor public ArcCurveFit(int[]!, double[]!, double[]![]!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getSlope(double, double[]!);
-    method public double getSlope(double, int);
-    method public double[]! getTimePoints();
-    field public static final int ARC_ABOVE = 5; // 0x5
-    field public static final int ARC_BELOW = 4; // 0x4
-    field public static final int ARC_START_FLIP = 3; // 0x3
-    field public static final int ARC_START_HORIZONTAL = 2; // 0x2
-    field public static final int ARC_START_LINEAR = 0; // 0x0
-    field public static final int ARC_START_VERTICAL = 1; // 0x1
-  }
-
-  public abstract class CurveFit {
-    ctor public CurveFit();
-    method public static androidx.constraintlayout.core.motion.utils.CurveFit! get(int, double[]!, double[]![]!);
-    method public static androidx.constraintlayout.core.motion.utils.CurveFit! getArc(int[]!, double[]!, double[]![]!);
-    method public abstract void getPos(double, double[]!);
-    method public abstract void getPos(double, float[]!);
-    method public abstract double getPos(double, int);
-    method public abstract void getSlope(double, double[]!);
-    method public abstract double getSlope(double, int);
-    method public abstract double[]! getTimePoints();
-    field public static final int CONSTANT = 2; // 0x2
-    field public static final int LINEAR = 1; // 0x1
-    field public static final int SPLINE = 0; // 0x0
-  }
-
-  public interface DifferentialInterpolator {
-    method public float getInterpolation(float);
-    method public float getVelocity();
-  }
-
-  public class Easing {
-    ctor public Easing();
-    method public double get(double);
-    method public double getDiff(double);
-    method public static androidx.constraintlayout.core.motion.utils.Easing! getInterpolator(String!);
-    field public static String![]! NAMED_EASING;
-  }
-
-  public class FloatRect {
-    ctor public FloatRect();
-    method public final float centerX();
-    method public final float centerY();
-    field public float bottom;
-    field public float left;
-    field public float right;
-    field public float top;
-  }
-
-  public class HyperSpline {
-    ctor public HyperSpline();
-    ctor public HyperSpline(double[]![]!);
-    method public double approxLength(androidx.constraintlayout.core.motion.utils.HyperSpline.Cubic![]!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getVelocity(double, double[]!);
-    method public void setup(double[]![]!);
-  }
-
-  public static class HyperSpline.Cubic {
-    ctor public HyperSpline.Cubic(double, double, double, double);
-    method public double eval(double);
-    method public double vel(double);
-  }
-
-  public class KeyCache {
-    ctor public KeyCache();
-    method public float getFloatValue(Object!, String!, int);
-    method public void setFloatValue(Object!, String!, int, float);
-  }
-
-  public abstract class KeyCycleOscillator {
-    ctor public KeyCycleOscillator();
-    method public float get(float);
-    method public androidx.constraintlayout.core.motion.utils.CurveFit! getCurveFit();
-    method public float getSlope(float);
-    method public static androidx.constraintlayout.core.motion.utils.KeyCycleOscillator! makeWidgetCycle(String!);
-    method protected void setCustom(Object!);
-    method public void setPoint(int, int, String!, int, float, float, float, float);
-    method public void setPoint(int, int, String!, int, float, float, float, float, Object!);
-    method public void setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float);
-    method public void setType(String!);
-    method public void setup(float);
-    method public boolean variesByPath();
-    field public int mVariesBy;
-  }
-
-  public static class KeyCycleOscillator.PathRotateSet extends androidx.constraintlayout.core.motion.utils.KeyCycleOscillator {
-    ctor public KeyCycleOscillator.PathRotateSet(String!);
-    method public void setPathRotate(androidx.constraintlayout.core.motion.MotionWidget!, float, double, double);
-  }
-
-  public class KeyFrameArray {
-    ctor public KeyFrameArray();
-  }
-
-  public static class KeyFrameArray.CustomArray {
-    ctor public KeyFrameArray.CustomArray();
-    method public void append(int, androidx.constraintlayout.core.motion.CustomAttribute!);
-    method public void clear();
-    method public void dump();
-    method public int keyAt(int);
-    method public void remove(int);
-    method public int size();
-    method public androidx.constraintlayout.core.motion.CustomAttribute! valueAt(int);
-  }
-
-  public static class KeyFrameArray.CustomVar {
-    ctor public KeyFrameArray.CustomVar();
-    method public void append(int, androidx.constraintlayout.core.motion.CustomVariable!);
-    method public void clear();
-    method public void dump();
-    method public int keyAt(int);
-    method public void remove(int);
-    method public int size();
-    method public androidx.constraintlayout.core.motion.CustomVariable! valueAt(int);
-  }
-
-  public class LinearCurveFit extends androidx.constraintlayout.core.motion.utils.CurveFit {
-    ctor public LinearCurveFit(double[]!, double[]![]!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getSlope(double, double[]!);
-    method public double getSlope(double, int);
-    method public double[]! getTimePoints();
-  }
-
-  public class MonotonicCurveFit extends androidx.constraintlayout.core.motion.utils.CurveFit {
-    ctor public MonotonicCurveFit(double[]!, double[]![]!);
-    method public static androidx.constraintlayout.core.motion.utils.MonotonicCurveFit! buildWave(String!);
-    method public void getPos(double, double[]!);
-    method public void getPos(double, float[]!);
-    method public double getPos(double, int);
-    method public void getSlope(double, double[]!);
-    method public double getSlope(double, int);
-    method public double[]! getTimePoints();
-  }
-
-  public class Oscillator {
-    ctor public Oscillator();
-    method public void addPoint(double, float);
-    method public double getSlope(double, double, double);
-    method public double getValue(double, double);
-    method public void normalize();
-    method public void setType(int, String!);
-    field public static final int BOUNCE = 6; // 0x6
-    field public static final int COS_WAVE = 5; // 0x5
-    field public static final int CUSTOM = 7; // 0x7
-    field public static final int REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SAW_WAVE = 3; // 0x3
-    field public static final int SIN_WAVE = 0; // 0x0
-    field public static final int SQUARE_WAVE = 1; // 0x1
-    field public static String! TAG;
-    field public static final int TRIANGLE_WAVE = 2; // 0x2
-  }
-
-  public class Rect {
-    ctor public Rect();
-    method public int height();
-    method public int width();
-    field public int bottom;
-    field public int left;
-    field public int right;
-    field public int top;
-  }
-
-  public class Schlick extends androidx.constraintlayout.core.motion.utils.Easing {
-  }
-
-  public abstract class SplineSet {
-    ctor public SplineSet();
-    method public float get(float);
-    method public androidx.constraintlayout.core.motion.utils.CurveFit! getCurveFit();
-    method public float getSlope(float);
-    method public static androidx.constraintlayout.core.motion.utils.SplineSet! makeCustomSpline(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomArray!);
-    method public static androidx.constraintlayout.core.motion.utils.SplineSet! makeCustomSplineSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomVar!);
-    method public static androidx.constraintlayout.core.motion.utils.SplineSet! makeSpline(String!, long);
-    method public void setPoint(int, float);
-    method public void setProperty(androidx.constraintlayout.core.motion.utils.TypedValues!, float);
-    method public void setType(String!);
-    method public void setup(int);
-    field protected androidx.constraintlayout.core.motion.utils.CurveFit! mCurveFit;
-    field protected int[]! mTimePoints;
-    field protected float[]! mValues;
-  }
-
-  public static class SplineSet.CustomSet extends androidx.constraintlayout.core.motion.utils.SplineSet {
-    ctor public SplineSet.CustomSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomArray!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomAttribute!);
-    method public void setProperty(androidx.constraintlayout.core.state.WidgetFrame!, float);
-  }
-
-  public static class SplineSet.CustomSpline extends androidx.constraintlayout.core.motion.utils.SplineSet {
-    ctor public SplineSet.CustomSpline(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomVar!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomVariable!);
-    method public void setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float);
-  }
-
-  public class SpringStopEngine implements androidx.constraintlayout.core.motion.utils.StopEngine {
-    ctor public SpringStopEngine();
-    method public String! debug(String!, float);
-    method public float getAcceleration();
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-    method public void springConfig(float, float, float, float, float, float, float, int);
-  }
-
-  public class StepCurve extends androidx.constraintlayout.core.motion.utils.Easing {
-  }
-
-  public interface StopEngine {
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-  }
-
-  public class StopLogicEngine implements androidx.constraintlayout.core.motion.utils.StopEngine {
-    ctor public StopLogicEngine();
-    method public void config(float, float, float, float, float, float);
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-  }
-
-  public static class StopLogicEngine.Decelerate implements androidx.constraintlayout.core.motion.utils.StopEngine {
-    ctor public StopLogicEngine.Decelerate();
-    method public void config(float, float, float);
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-  }
-
-  public abstract class TimeCycleSplineSet {
-    ctor public TimeCycleSplineSet();
-    method protected float calcWave(float);
-    method public androidx.constraintlayout.core.motion.utils.CurveFit! getCurveFit();
-    method public void setPoint(int, float, float, int, float);
-    method protected void setStartTime(long);
-    method public void setType(String!);
-    method public void setup(int);
-    field protected static final int CURVE_OFFSET = 2; // 0x2
-    field protected static final int CURVE_PERIOD = 1; // 0x1
-    field protected static final int CURVE_VALUE = 0; // 0x0
-    field protected float[]! mCache;
-    field protected boolean mContinue;
-    field protected int mCount;
-    field protected androidx.constraintlayout.core.motion.utils.CurveFit! mCurveFit;
-    field protected float mLastCycle;
-    field protected long mLastTime;
-    field protected int[]! mTimePoints;
-    field protected String! mType;
-    field protected float[]![]! mValues;
-    field protected int mWaveShape;
-    field protected static float sVal2PI;
-  }
-
-  public static class TimeCycleSplineSet.CustomSet extends androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet {
-    ctor public TimeCycleSplineSet.CustomSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomArray!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomAttribute!, float, int, float);
-    method public boolean setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  public static class TimeCycleSplineSet.CustomVarSet extends androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet {
-    ctor public TimeCycleSplineSet.CustomVarSet(String!, androidx.constraintlayout.core.motion.utils.KeyFrameArray.CustomVar!);
-    method public void setPoint(int, androidx.constraintlayout.core.motion.CustomVariable!, float, int, float);
-    method public boolean setProperty(androidx.constraintlayout.core.motion.MotionWidget!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  protected static class TimeCycleSplineSet.Sort {
-    ctor protected TimeCycleSplineSet.Sort();
-  }
-
-  public class TypedBundle {
-    ctor public TypedBundle();
-    method public void add(int, boolean);
-    method public void add(int, float);
-    method public void add(int, int);
-    method public void add(int, String!);
-    method public void addIfNotNull(int, String!);
-    method public void applyDelta(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void applyDelta(androidx.constraintlayout.core.motion.utils.TypedValues!);
-    method public void clear();
-    method public int getInteger(int);
-  }
-
-  public interface TypedValues {
-    method public int getId(String!);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    field public static final int BOOLEAN_MASK = 1; // 0x1
-    field public static final int FLOAT_MASK = 4; // 0x4
-    field public static final int INT_MASK = 2; // 0x2
-    field public static final int STRING_MASK = 8; // 0x8
-    field public static final String S_CUSTOM = "CUSTOM";
-    field public static final int TYPE_FRAME_POSITION = 100; // 0x64
-    field public static final int TYPE_TARGET = 101; // 0x65
-  }
-
-  public static interface TypedValues.AttributesType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyAttributes";
-    field public static final String S_ALPHA = "alpha";
-    field public static final String S_CURVE_FIT = "curveFit";
-    field public static final String S_CUSTOM = "CUSTOM";
-    field public static final String S_EASING = "easing";
-    field public static final String S_ELEVATION = "elevation";
-    field public static final String S_FRAME = "frame";
-    field public static final String S_PATH_ROTATE = "pathRotate";
-    field public static final String S_PIVOT_TARGET = "pivotTarget";
-    field public static final String S_PIVOT_X = "pivotX";
-    field public static final String S_PIVOT_Y = "pivotY";
-    field public static final String S_PROGRESS = "progress";
-    field public static final String S_ROTATION_X = "rotationX";
-    field public static final String S_ROTATION_Y = "rotationY";
-    field public static final String S_ROTATION_Z = "rotationZ";
-    field public static final String S_SCALE_X = "scaleX";
-    field public static final String S_SCALE_Y = "scaleY";
-    field public static final String S_TARGET = "target";
-    field public static final String S_TRANSLATION_X = "translationX";
-    field public static final String S_TRANSLATION_Y = "translationY";
-    field public static final String S_TRANSLATION_Z = "translationZ";
-    field public static final String S_VISIBILITY = "visibility";
-    field public static final int TYPE_ALPHA = 303; // 0x12f
-    field public static final int TYPE_CURVE_FIT = 301; // 0x12d
-    field public static final int TYPE_EASING = 317; // 0x13d
-    field public static final int TYPE_ELEVATION = 307; // 0x133
-    field public static final int TYPE_PATH_ROTATE = 316; // 0x13c
-    field public static final int TYPE_PIVOT_TARGET = 318; // 0x13e
-    field public static final int TYPE_PIVOT_X = 313; // 0x139
-    field public static final int TYPE_PIVOT_Y = 314; // 0x13a
-    field public static final int TYPE_PROGRESS = 315; // 0x13b
-    field public static final int TYPE_ROTATION_X = 308; // 0x134
-    field public static final int TYPE_ROTATION_Y = 309; // 0x135
-    field public static final int TYPE_ROTATION_Z = 310; // 0x136
-    field public static final int TYPE_SCALE_X = 311; // 0x137
-    field public static final int TYPE_SCALE_Y = 312; // 0x138
-    field public static final int TYPE_TRANSLATION_X = 304; // 0x130
-    field public static final int TYPE_TRANSLATION_Y = 305; // 0x131
-    field public static final int TYPE_TRANSLATION_Z = 306; // 0x132
-    field public static final int TYPE_VISIBILITY = 302; // 0x12e
-  }
-
-  public static interface TypedValues.Custom {
-    method public static int getId(String!);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "Custom";
-    field public static final String S_BOOLEAN = "boolean";
-    field public static final String S_COLOR = "color";
-    field public static final String S_DIMENSION = "dimension";
-    field public static final String S_FLOAT = "float";
-    field public static final String S_INT = "integer";
-    field public static final String S_REFERENCE = "reference";
-    field public static final String S_STRING = "string";
-    field public static final int TYPE_BOOLEAN = 904; // 0x388
-    field public static final int TYPE_COLOR = 902; // 0x386
-    field public static final int TYPE_DIMENSION = 905; // 0x389
-    field public static final int TYPE_FLOAT = 901; // 0x385
-    field public static final int TYPE_INT = 900; // 0x384
-    field public static final int TYPE_REFERENCE = 906; // 0x38a
-    field public static final int TYPE_STRING = 903; // 0x387
-  }
-
-  public static interface TypedValues.CycleType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyCycle";
-    field public static final String S_ALPHA = "alpha";
-    field public static final String S_CURVE_FIT = "curveFit";
-    field public static final String S_CUSTOM_WAVE_SHAPE = "customWave";
-    field public static final String S_EASING = "easing";
-    field public static final String S_ELEVATION = "elevation";
-    field public static final String S_PATH_ROTATE = "pathRotate";
-    field public static final String S_PIVOT_X = "pivotX";
-    field public static final String S_PIVOT_Y = "pivotY";
-    field public static final String S_PROGRESS = "progress";
-    field public static final String S_ROTATION_X = "rotationX";
-    field public static final String S_ROTATION_Y = "rotationY";
-    field public static final String S_ROTATION_Z = "rotationZ";
-    field public static final String S_SCALE_X = "scaleX";
-    field public static final String S_SCALE_Y = "scaleY";
-    field public static final String S_TRANSLATION_X = "translationX";
-    field public static final String S_TRANSLATION_Y = "translationY";
-    field public static final String S_TRANSLATION_Z = "translationZ";
-    field public static final String S_VISIBILITY = "visibility";
-    field public static final String S_WAVE_OFFSET = "offset";
-    field public static final String S_WAVE_PERIOD = "period";
-    field public static final String S_WAVE_PHASE = "phase";
-    field public static final String S_WAVE_SHAPE = "waveShape";
-    field public static final int TYPE_ALPHA = 403; // 0x193
-    field public static final int TYPE_CURVE_FIT = 401; // 0x191
-    field public static final int TYPE_CUSTOM_WAVE_SHAPE = 422; // 0x1a6
-    field public static final int TYPE_EASING = 420; // 0x1a4
-    field public static final int TYPE_ELEVATION = 307; // 0x133
-    field public static final int TYPE_PATH_ROTATE = 416; // 0x1a0
-    field public static final int TYPE_PIVOT_X = 313; // 0x139
-    field public static final int TYPE_PIVOT_Y = 314; // 0x13a
-    field public static final int TYPE_PROGRESS = 315; // 0x13b
-    field public static final int TYPE_ROTATION_X = 308; // 0x134
-    field public static final int TYPE_ROTATION_Y = 309; // 0x135
-    field public static final int TYPE_ROTATION_Z = 310; // 0x136
-    field public static final int TYPE_SCALE_X = 311; // 0x137
-    field public static final int TYPE_SCALE_Y = 312; // 0x138
-    field public static final int TYPE_TRANSLATION_X = 304; // 0x130
-    field public static final int TYPE_TRANSLATION_Y = 305; // 0x131
-    field public static final int TYPE_TRANSLATION_Z = 306; // 0x132
-    field public static final int TYPE_VISIBILITY = 402; // 0x192
-    field public static final int TYPE_WAVE_OFFSET = 424; // 0x1a8
-    field public static final int TYPE_WAVE_PERIOD = 423; // 0x1a7
-    field public static final int TYPE_WAVE_PHASE = 425; // 0x1a9
-    field public static final int TYPE_WAVE_SHAPE = 421; // 0x1a5
-  }
-
-  public static interface TypedValues.MotionScene {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "MotionScene";
-    field public static final String S_DEFAULT_DURATION = "defaultDuration";
-    field public static final String S_LAYOUT_DURING_TRANSITION = "layoutDuringTransition";
-    field public static final int TYPE_DEFAULT_DURATION = 600; // 0x258
-    field public static final int TYPE_LAYOUT_DURING_TRANSITION = 601; // 0x259
-  }
-
-  public static interface TypedValues.MotionType {
-    method public static int getId(String!);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "Motion";
-    field public static final String S_ANIMATE_CIRCLEANGLE_TO = "AnimateCircleAngleTo";
-    field public static final String S_ANIMATE_RELATIVE_TO = "AnimateRelativeTo";
-    field public static final String S_DRAW_PATH = "DrawPath";
-    field public static final String S_EASING = "TransitionEasing";
-    field public static final String S_PATHMOTION_ARC = "PathMotionArc";
-    field public static final String S_PATH_ROTATE = "PathRotate";
-    field public static final String S_POLAR_RELATIVETO = "PolarRelativeTo";
-    field public static final String S_QUANTIZE_INTERPOLATOR = "QuantizeInterpolator";
-    field public static final String S_QUANTIZE_INTERPOLATOR_ID = "QuantizeInterpolatorID";
-    field public static final String S_QUANTIZE_INTERPOLATOR_TYPE = "QuantizeInterpolatorType";
-    field public static final String S_QUANTIZE_MOTIONSTEPS = "QuantizeMotionSteps";
-    field public static final String S_QUANTIZE_MOTION_PHASE = "QuantizeMotionPhase";
-    field public static final String S_STAGGER = "Stagger";
-    field public static final int TYPE_ANIMATE_CIRCLEANGLE_TO = 606; // 0x25e
-    field public static final int TYPE_ANIMATE_RELATIVE_TO = 605; // 0x25d
-    field public static final int TYPE_DRAW_PATH = 608; // 0x260
-    field public static final int TYPE_EASING = 603; // 0x25b
-    field public static final int TYPE_PATHMOTION_ARC = 607; // 0x25f
-    field public static final int TYPE_PATH_ROTATE = 601; // 0x259
-    field public static final int TYPE_POLAR_RELATIVETO = 609; // 0x261
-    field public static final int TYPE_QUANTIZE_INTERPOLATOR = 604; // 0x25c
-    field public static final int TYPE_QUANTIZE_INTERPOLATOR_ID = 612; // 0x264
-    field public static final int TYPE_QUANTIZE_INTERPOLATOR_TYPE = 611; // 0x263
-    field public static final int TYPE_QUANTIZE_MOTIONSTEPS = 610; // 0x262
-    field public static final int TYPE_QUANTIZE_MOTION_PHASE = 602; // 0x25a
-    field public static final int TYPE_STAGGER = 600; // 0x258
-  }
-
-  public static interface TypedValues.OnSwipe {
-    field public static final String AUTOCOMPLETE_MODE = "autocompletemode";
-    field public static final String![]! AUTOCOMPLETE_MODE_ENUM;
-    field public static final String DRAG_DIRECTION = "dragdirection";
-    field public static final String DRAG_SCALE = "dragscale";
-    field public static final String DRAG_THRESHOLD = "dragthreshold";
-    field public static final String LIMIT_BOUNDS_TO = "limitboundsto";
-    field public static final String MAX_ACCELERATION = "maxacceleration";
-    field public static final String MAX_VELOCITY = "maxvelocity";
-    field public static final String MOVE_WHEN_SCROLLAT_TOP = "movewhenscrollattop";
-    field public static final String NESTED_SCROLL_FLAGS = "nestedscrollflags";
-    field public static final String![]! NESTED_SCROLL_FLAGS_ENUM;
-    field public static final String ON_TOUCH_UP = "ontouchup";
-    field public static final String![]! ON_TOUCH_UP_ENUM;
-    field public static final String ROTATION_CENTER_ID = "rotationcenterid";
-    field public static final String SPRINGS_TOP_THRESHOLD = "springstopthreshold";
-    field public static final String SPRING_BOUNDARY = "springboundary";
-    field public static final String![]! SPRING_BOUNDARY_ENUM;
-    field public static final String SPRING_DAMPING = "springdamping";
-    field public static final String SPRING_MASS = "springmass";
-    field public static final String SPRING_STIFFNESS = "springstiffness";
-    field public static final String TOUCH_ANCHOR_ID = "touchanchorid";
-    field public static final String TOUCH_ANCHOR_SIDE = "touchanchorside";
-    field public static final String TOUCH_REGION_ID = "touchregionid";
-  }
-
-  public static interface TypedValues.PositionType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyPosition";
-    field public static final String S_DRAWPATH = "drawPath";
-    field public static final String S_PERCENT_HEIGHT = "percentHeight";
-    field public static final String S_PERCENT_WIDTH = "percentWidth";
-    field public static final String S_PERCENT_X = "percentX";
-    field public static final String S_PERCENT_Y = "percentY";
-    field public static final String S_SIZE_PERCENT = "sizePercent";
-    field public static final String S_TRANSITION_EASING = "transitionEasing";
-    field public static final int TYPE_CURVE_FIT = 508; // 0x1fc
-    field public static final int TYPE_DRAWPATH = 502; // 0x1f6
-    field public static final int TYPE_PATH_MOTION_ARC = 509; // 0x1fd
-    field public static final int TYPE_PERCENT_HEIGHT = 504; // 0x1f8
-    field public static final int TYPE_PERCENT_WIDTH = 503; // 0x1f7
-    field public static final int TYPE_PERCENT_X = 506; // 0x1fa
-    field public static final int TYPE_PERCENT_Y = 507; // 0x1fb
-    field public static final int TYPE_POSITION_TYPE = 510; // 0x1fe
-    field public static final int TYPE_SIZE_PERCENT = 505; // 0x1f9
-    field public static final int TYPE_TRANSITION_EASING = 501; // 0x1f5
-  }
-
-  public static interface TypedValues.TransitionType {
-    method public static int getId(String!);
-    method public static int getType(int);
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "Transitions";
-    field public static final String S_AUTO_TRANSITION = "autoTransition";
-    field public static final String S_DURATION = "duration";
-    field public static final String S_FROM = "from";
-    field public static final String S_INTERPOLATOR = "motionInterpolator";
-    field public static final String S_PATH_MOTION_ARC = "pathMotionArc";
-    field public static final String S_STAGGERED = "staggered";
-    field public static final String S_TO = "to";
-    field public static final String S_TRANSITION_FLAGS = "transitionFlags";
-    field public static final int TYPE_AUTO_TRANSITION = 704; // 0x2c0
-    field public static final int TYPE_DURATION = 700; // 0x2bc
-    field public static final int TYPE_FROM = 701; // 0x2bd
-    field public static final int TYPE_INTERPOLATOR = 705; // 0x2c1
-    field public static final int TYPE_PATH_MOTION_ARC = 509; // 0x1fd
-    field public static final int TYPE_STAGGERED = 706; // 0x2c2
-    field public static final int TYPE_TO = 702; // 0x2be
-    field public static final int TYPE_TRANSITION_FLAGS = 707; // 0x2c3
-  }
-
-  public static interface TypedValues.TriggerType {
-    method public static int getId(String!);
-    field public static final String CROSS = "CROSS";
-    field public static final String![]! KEY_WORDS;
-    field public static final String NAME = "KeyTrigger";
-    field public static final String NEGATIVE_CROSS = "negativeCross";
-    field public static final String POSITIVE_CROSS = "positiveCross";
-    field public static final String POST_LAYOUT = "postLayout";
-    field public static final String TRIGGER_COLLISION_ID = "triggerCollisionId";
-    field public static final String TRIGGER_COLLISION_VIEW = "triggerCollisionView";
-    field public static final String TRIGGER_ID = "triggerID";
-    field public static final String TRIGGER_RECEIVER = "triggerReceiver";
-    field public static final String TRIGGER_SLACK = "triggerSlack";
-    field public static final int TYPE_CROSS = 312; // 0x138
-    field public static final int TYPE_NEGATIVE_CROSS = 310; // 0x136
-    field public static final int TYPE_POSITIVE_CROSS = 309; // 0x135
-    field public static final int TYPE_POST_LAYOUT = 304; // 0x130
-    field public static final int TYPE_TRIGGER_COLLISION_ID = 307; // 0x133
-    field public static final int TYPE_TRIGGER_COLLISION_VIEW = 306; // 0x132
-    field public static final int TYPE_TRIGGER_ID = 308; // 0x134
-    field public static final int TYPE_TRIGGER_RECEIVER = 311; // 0x137
-    field public static final int TYPE_TRIGGER_SLACK = 305; // 0x131
-    field public static final int TYPE_VIEW_TRANSITION_ON_CROSS = 301; // 0x12d
-    field public static final int TYPE_VIEW_TRANSITION_ON_NEGATIVE_CROSS = 303; // 0x12f
-    field public static final int TYPE_VIEW_TRANSITION_ON_POSITIVE_CROSS = 302; // 0x12e
-    field public static final String VIEW_TRANSITION_ON_CROSS = "viewTransitionOnCross";
-    field public static final String VIEW_TRANSITION_ON_NEGATIVE_CROSS = "viewTransitionOnNegativeCross";
-    field public static final String VIEW_TRANSITION_ON_POSITIVE_CROSS = "viewTransitionOnPositiveCross";
-  }
-
-  public class Utils {
-    ctor public Utils();
-    method public int getInterpolatedColor(float[]!);
-    method public static void log(String!);
-    method public static void log(String!, String!);
-    method public static void logStack(String!, int);
-    method public static void loge(String!, String!);
-    method public static int rgbaTocColor(float, float, float, float);
-    method public static void setDebugHandle(androidx.constraintlayout.core.motion.utils.Utils.DebugHandle!);
-    method public static void socketSend(String!);
-  }
-
-  public static interface Utils.DebugHandle {
-    method public void message(String!);
-  }
-
-  public class VelocityMatrix {
-    ctor public VelocityMatrix();
-    method public void applyTransform(float, float, int, int, float[]!);
-    method public void clear();
-    method public void setRotationVelocity(androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, float);
-    method public void setRotationVelocity(androidx.constraintlayout.core.motion.utils.SplineSet!, float);
-    method public void setScaleVelocity(androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, float);
-    method public void setScaleVelocity(androidx.constraintlayout.core.motion.utils.SplineSet!, androidx.constraintlayout.core.motion.utils.SplineSet!, float);
-    method public void setTranslationVelocity(androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, androidx.constraintlayout.core.motion.utils.KeyCycleOscillator!, float);
-    method public void setTranslationVelocity(androidx.constraintlayout.core.motion.utils.SplineSet!, androidx.constraintlayout.core.motion.utils.SplineSet!, float);
-  }
-
-  public class ViewState {
-    ctor public ViewState();
-    method public void getState(androidx.constraintlayout.core.motion.MotionWidget!);
-    method public int height();
-    method public int width();
-    field public int bottom;
-    field public int left;
-    field public int right;
-    field public float rotation;
-    field public int top;
-  }
-
-}
-
-package androidx.constraintlayout.core.parser {
-
-  public class CLArray extends androidx.constraintlayout.core.parser.CLContainer {
-    ctor public CLArray(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-  }
-
-  public class CLContainer extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLContainer(char[]!);
-    method public void add(androidx.constraintlayout.core.parser.CLElement!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public void clear();
-    method public androidx.constraintlayout.core.parser.CLContainer clone();
-    method public androidx.constraintlayout.core.parser.CLElement! get(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLElement! get(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLArray! getArray(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLArray! getArray(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLArray! getArrayOrCreate(String!);
-    method public androidx.constraintlayout.core.parser.CLArray! getArrayOrNull(String!);
-    method public boolean getBoolean(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public boolean getBoolean(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public float getFloat(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public float getFloat(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public float getFloatOrNaN(String!);
-    method public int getInt(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public int getInt(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLObject! getObject(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLObject! getObject(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLObject! getObjectOrNull(String!);
-    method public androidx.constraintlayout.core.parser.CLElement! getOrNull(int);
-    method public androidx.constraintlayout.core.parser.CLElement! getOrNull(String!);
-    method public String! getString(int) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public String! getString(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public String! getStringOrNull(int);
-    method public String! getStringOrNull(String!);
-    method public boolean has(String!);
-    method public java.util.ArrayList<java.lang.String!>! names();
-    method public void put(String!, androidx.constraintlayout.core.parser.CLElement!);
-    method public void putNumber(String!, float);
-    method public void putString(String!, String!);
-    method public void remove(String!);
-    method public int size();
-  }
-
-  public class CLElement implements java.lang.Cloneable {
-    ctor public CLElement(char[]!);
-    method protected void addIndent(StringBuilder!, int);
-    method public androidx.constraintlayout.core.parser.CLElement clone();
-    method public String! content();
-    method public androidx.constraintlayout.core.parser.CLElement! getContainer();
-    method protected String! getDebugName();
-    method public long getEnd();
-    method public float getFloat();
-    method public int getInt();
-    method public int getLine();
-    method public long getStart();
-    method protected String! getStrClass();
-    method public boolean hasContent();
-    method public boolean isDone();
-    method public boolean isStarted();
-    method public boolean notStarted();
-    method public void setContainer(androidx.constraintlayout.core.parser.CLContainer!);
-    method public void setEnd(long);
-    method public void setLine(int);
-    method public void setStart(long);
-    method protected String! toFormattedJSON(int, int);
-    method protected String! toJSON();
-    field protected androidx.constraintlayout.core.parser.CLContainer! mContainer;
-    field protected long mEnd;
-    field protected long mStart;
-    field protected static int sBaseIndent;
-    field protected static int sMaxLine;
-  }
-
-  public class CLKey extends androidx.constraintlayout.core.parser.CLContainer {
-    ctor public CLKey(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(String!, androidx.constraintlayout.core.parser.CLElement!);
-    method public String! getName();
-    method public androidx.constraintlayout.core.parser.CLElement! getValue();
-    method public void set(androidx.constraintlayout.core.parser.CLElement!);
-  }
-
-  public class CLNumber extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLNumber(char[]!);
-    ctor public CLNumber(float);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public boolean isInt();
-    method public void putValue(float);
-  }
-
-  public class CLObject extends androidx.constraintlayout.core.parser.CLContainer implements java.lang.Iterable<androidx.constraintlayout.core.parser.CLKey!> {
-    ctor public CLObject(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLObject! allocate(char[]!);
-    method public androidx.constraintlayout.core.parser.CLObject clone();
-    method public java.util.Iterator<androidx.constraintlayout.core.parser.CLKey!>! iterator();
-    method public String! toFormattedJSON();
-    method public String! toFormattedJSON(int, int);
-    method public String! toJSON();
-  }
-
-  public class CLParser {
-    ctor public CLParser(String!);
-    method public androidx.constraintlayout.core.parser.CLObject! parse() throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static androidx.constraintlayout.core.parser.CLObject! parse(String!) throws androidx.constraintlayout.core.parser.CLParsingException;
-  }
-
-  public class CLParsingException extends java.lang.Exception {
-    ctor public CLParsingException(String!, androidx.constraintlayout.core.parser.CLElement!);
-    method public String! reason();
-  }
-
-  public class CLString extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLString(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLString from(String);
-  }
-
-  public class CLToken extends androidx.constraintlayout.core.parser.CLElement {
-    ctor public CLToken(char[]!);
-    method public static androidx.constraintlayout.core.parser.CLElement! allocate(char[]!);
-    method public boolean getBoolean() throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.parser.CLToken.Type! getType();
-    method public boolean isNull() throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public boolean validate(char, long);
-  }
-
-}
-
-package androidx.constraintlayout.core.state {
-
-  public class ConstraintReference implements androidx.constraintlayout.core.state.Reference {
-    ctor public ConstraintReference(androidx.constraintlayout.core.state.State!);
-    method public void addCustomColor(String!, int);
-    method public void addCustomFloat(String!, float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! alpha(float);
-    method public void apply();
-    method public void applyWidgetConstraints();
-    method public androidx.constraintlayout.core.state.ConstraintReference! baseline();
-    method public androidx.constraintlayout.core.state.ConstraintReference! baselineToBaseline(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! baselineToBottom(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! baselineToTop(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! bias(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! bottom();
-    method public androidx.constraintlayout.core.state.ConstraintReference! bottomToBottom(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! bottomToTop(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! centerHorizontally(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! centerVertically(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! circularConstraint(Object!, float, float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! clear();
-    method public androidx.constraintlayout.core.state.ConstraintReference! clearAll();
-    method public androidx.constraintlayout.core.state.ConstraintReference! clearHorizontal();
-    method public androidx.constraintlayout.core.state.ConstraintReference! clearVertical();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! createConstraintWidget();
-    method public androidx.constraintlayout.core.state.ConstraintReference! end();
-    method public androidx.constraintlayout.core.state.ConstraintReference! endToEnd(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! endToStart(Object!);
-    method public float getAlpha();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public androidx.constraintlayout.core.state.helpers.Facade! getFacade();
-    method public androidx.constraintlayout.core.state.Dimension! getHeight();
-    method public int getHorizontalChainStyle();
-    method public float getHorizontalChainWeight();
-    method public Object! getKey();
-    method public float getPivotX();
-    method public float getPivotY();
-    method public float getRotationX();
-    method public float getRotationY();
-    method public float getRotationZ();
-    method public float getScaleX();
-    method public float getScaleY();
-    method public String! getTag();
-    method public float getTranslationX();
-    method public float getTranslationY();
-    method public float getTranslationZ();
-    method public int getVerticalChainStyle(int);
-    method public float getVerticalChainWeight();
-    method public Object! getView();
-    method public androidx.constraintlayout.core.state.Dimension! getWidth();
-    method public androidx.constraintlayout.core.state.ConstraintReference! height(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! horizontalBias(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! left();
-    method public androidx.constraintlayout.core.state.ConstraintReference! leftToLeft(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! leftToRight(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! margin(int);
-    method public androidx.constraintlayout.core.state.ConstraintReference! margin(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! marginGone(int);
-    method public androidx.constraintlayout.core.state.ConstraintReference! marginGone(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! pivotX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! pivotY(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! right();
-    method public androidx.constraintlayout.core.state.ConstraintReference! rightToLeft(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rightToRight(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rotationX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rotationY(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! rotationZ(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! scaleX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! scaleY(float);
-    method public void setConstraintWidget(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setFacade(androidx.constraintlayout.core.state.helpers.Facade!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! setHeight(androidx.constraintlayout.core.state.Dimension!);
-    method public void setHorizontalChainStyle(int);
-    method public void setHorizontalChainWeight(float);
-    method public void setKey(Object!);
-    method public void setTag(String!);
-    method public void setVerticalChainStyle(int);
-    method public void setVerticalChainWeight(float);
-    method public void setView(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! setWidth(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! start();
-    method public androidx.constraintlayout.core.state.ConstraintReference! startToEnd(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! startToStart(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! top();
-    method public androidx.constraintlayout.core.state.ConstraintReference! topToBottom(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! topToTop(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! translationX(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! translationY(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! translationZ(float);
-    method public void validate() throws java.lang.Exception;
-    method public androidx.constraintlayout.core.state.ConstraintReference! verticalBias(float);
-    method public androidx.constraintlayout.core.state.ConstraintReference! visibility(int);
-    method public androidx.constraintlayout.core.state.ConstraintReference! width(androidx.constraintlayout.core.state.Dimension!);
-    field protected Object! mBottomToBottom;
-    field protected Object! mBottomToTop;
-    field protected Object! mEndToEnd;
-    field protected Object! mEndToStart;
-    field protected float mHorizontalBias;
-    field protected Object! mLeftToLeft;
-    field protected Object! mLeftToRight;
-    field protected int mMarginBottom;
-    field protected int mMarginBottomGone;
-    field protected int mMarginEnd;
-    field protected int mMarginEndGone;
-    field protected int mMarginLeft;
-    field protected int mMarginLeftGone;
-    field protected int mMarginRight;
-    field protected int mMarginRightGone;
-    field protected int mMarginStart;
-    field protected int mMarginStartGone;
-    field protected int mMarginTop;
-    field protected int mMarginTopGone;
-    field protected Object! mRightToLeft;
-    field protected Object! mRightToRight;
-    field protected Object! mStartToEnd;
-    field protected Object! mStartToStart;
-    field protected Object! mTopToBottom;
-    field protected Object! mTopToTop;
-    field protected float mVerticalBias;
-  }
-
-  public static interface ConstraintReference.ConstraintReferenceFactory {
-    method public androidx.constraintlayout.core.state.ConstraintReference! create(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class ConstraintSetParser {
-    ctor public ConstraintSetParser();
-    method public static void parseDesignElementsJSON(String!, java.util.ArrayList<androidx.constraintlayout.core.state.ConstraintSetParser.DesignElement!>!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static void parseJSON(String!, androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.ConstraintSetParser.LayoutVariables!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static void parseJSON(String!, androidx.constraintlayout.core.state.Transition!, int);
-    method public static void parseMotionSceneJSON(androidx.constraintlayout.core.state.CoreMotionScene!, String!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static void populateState(androidx.constraintlayout.core.parser.CLObject, androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.ConstraintSetParser.LayoutVariables) throws androidx.constraintlayout.core.parser.CLParsingException;
-  }
-
-  public static class ConstraintSetParser.DesignElement {
-    method public String! getId();
-    method public java.util.HashMap<java.lang.String!,java.lang.String!>! getParams();
-    method public String! getType();
-  }
-
-  public static class ConstraintSetParser.LayoutVariables {
-    ctor public ConstraintSetParser.LayoutVariables();
-    method public void putOverride(String!, float);
-  }
-
-  public enum ConstraintSetParser.MotionLayoutDebugFlags {
-    enum_constant public static final androidx.constraintlayout.core.state.ConstraintSetParser.MotionLayoutDebugFlags NONE;
-    enum_constant public static final androidx.constraintlayout.core.state.ConstraintSetParser.MotionLayoutDebugFlags SHOW_ALL;
-    enum_constant public static final androidx.constraintlayout.core.state.ConstraintSetParser.MotionLayoutDebugFlags UNKNOWN;
-  }
-
-  public interface CoreMotionScene {
-    method public String! getConstraintSet(int);
-    method public String! getConstraintSet(String!);
-    method public String! getTransition(String!);
-    method public void setConstraintSetContent(String!, String!);
-    method public void setDebugName(String!);
-    method public void setTransitionContent(String!, String!);
-  }
-
-  public interface CorePixelDp {
-    method public float toPixels(float);
-  }
-
-  public class Dimension {
-    method public void apply(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-    method public static androidx.constraintlayout.core.state.Dimension! createFixed(int);
-    method public static androidx.constraintlayout.core.state.Dimension! createFixed(Object!);
-    method public static androidx.constraintlayout.core.state.Dimension! createParent();
-    method public static androidx.constraintlayout.core.state.Dimension! createPercent(Object!, float);
-    method public static androidx.constraintlayout.core.state.Dimension! createRatio(String!);
-    method public static androidx.constraintlayout.core.state.Dimension! createSpread();
-    method public static androidx.constraintlayout.core.state.Dimension! createSuggested(int);
-    method public static androidx.constraintlayout.core.state.Dimension! createSuggested(Object!);
-    method public static androidx.constraintlayout.core.state.Dimension! createWrap();
-    method public boolean equalsFixedValue(int);
-    method public androidx.constraintlayout.core.state.Dimension! fixed(int);
-    method public androidx.constraintlayout.core.state.Dimension! fixed(Object!);
-    method public androidx.constraintlayout.core.state.Dimension! max(int);
-    method public androidx.constraintlayout.core.state.Dimension! max(Object!);
-    method public androidx.constraintlayout.core.state.Dimension! min(int);
-    method public androidx.constraintlayout.core.state.Dimension! min(Object!);
-    method public androidx.constraintlayout.core.state.Dimension! percent(Object!, float);
-    method public androidx.constraintlayout.core.state.Dimension! ratio(String!);
-    method public androidx.constraintlayout.core.state.Dimension! suggested(int);
-    method public androidx.constraintlayout.core.state.Dimension! suggested(Object!);
-    field public static final Object! FIXED_DIMENSION;
-    field public static final Object! PARENT_DIMENSION;
-    field public static final Object! PERCENT_DIMENSION;
-    field public static final Object! RATIO_DIMENSION;
-    field public static final Object! SPREAD_DIMENSION;
-    field public static final Object! WRAP_DIMENSION;
-  }
-
-  public enum Dimension.Type {
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type FIXED;
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type MATCH_CONSTRAINT;
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type MATCH_PARENT;
-    enum_constant public static final androidx.constraintlayout.core.state.Dimension.Type WRAP;
-  }
-
-  public class HelperReference extends androidx.constraintlayout.core.state.ConstraintReference implements androidx.constraintlayout.core.state.helpers.Facade {
-    ctor public HelperReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
-    method public androidx.constraintlayout.core.state.HelperReference! add(java.lang.Object!...!);
-    method public void applyBase();
-    method public androidx.constraintlayout.core.widgets.HelperWidget! getHelperWidget();
-    method public androidx.constraintlayout.core.state.State.Helper! getType();
-    method public void setHelperWidget(androidx.constraintlayout.core.widgets.HelperWidget!);
-    field protected final androidx.constraintlayout.core.state.State! mHelperState;
-    field protected java.util.ArrayList<java.lang.Object!>! mReferences;
-  }
-
-  public interface Interpolator {
-    method public float getInterpolation(float);
-  }
-
-  public interface Reference {
-    method public void apply();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public androidx.constraintlayout.core.state.helpers.Facade! getFacade();
-    method public Object! getKey();
-    method public void setConstraintWidget(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setKey(Object!);
-  }
-
-  public class Registry {
-    ctor public Registry();
-    method public String! currentContent(String!);
-    method public String! currentLayoutInformation(String!);
-    method public static androidx.constraintlayout.core.state.Registry! getInstance();
-    method public long getLastModified(String!);
-    method public java.util.Set<java.lang.String!>! getLayoutList();
-    method public void register(String!, androidx.constraintlayout.core.state.RegistryCallback!);
-    method public void setDrawDebug(String!, int);
-    method public void setLayoutInformationMode(String!, int);
-    method public void unregister(String!, androidx.constraintlayout.core.state.RegistryCallback!);
-    method public void updateContent(String!, String!);
-    method public void updateDimensions(String!, int, int);
-    method public void updateProgress(String!, float);
-  }
-
-  public interface RegistryCallback {
-    method public String! currentLayoutInformation();
-    method public String! currentMotionScene();
-    method public long getLastModified();
-    method public void onDimensions(int, int);
-    method public void onNewMotionScene(String!);
-    method public void onProgress(float);
-    method public void setDrawDebug(int);
-    method public void setLayoutInformationMode(int);
-  }
-
-  public class State {
-    ctor public State();
-    method public void apply(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    method public androidx.constraintlayout.core.state.helpers.BarrierReference! barrier(Object!, androidx.constraintlayout.core.state.State.Direction!);
-    method public void baselineNeededFor(Object!);
-    method public androidx.constraintlayout.core.state.helpers.AlignHorizontallyReference! centerHorizontally(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.AlignVerticallyReference! centerVertically(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! constraints(Object!);
-    method public int convertDimension(Object!);
-    method public androidx.constraintlayout.core.state.ConstraintReference! createConstraintReference(Object!);
-    method public void directMapping();
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getFlow(Object!, boolean);
-    method public androidx.constraintlayout.core.state.helpers.GridReference getGrid(Object, String);
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getHorizontalFlow();
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getHorizontalFlow(java.lang.Object!...!);
-    method public java.util.ArrayList<java.lang.String!>! getIdsForTag(String!);
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getVerticalFlow();
-    method public androidx.constraintlayout.core.state.helpers.FlowReference! getVerticalFlow(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! guideline(Object!, int);
-    method public androidx.constraintlayout.core.state.State! height(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.HelperReference! helper(Object!, androidx.constraintlayout.core.state.State.Helper!);
-    method public androidx.constraintlayout.core.state.helpers.HorizontalChainReference! horizontalChain();
-    method public androidx.constraintlayout.core.state.helpers.HorizontalChainReference! horizontalChain(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! horizontalGuideline(Object!);
-    method public boolean isBaselineNeeded(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method @Deprecated public boolean isLtr();
-    method public boolean isRtl();
-    method public void map(Object!, Object!);
-    method public void reset();
-    method public boolean sameFixedHeight(int);
-    method public boolean sameFixedWidth(int);
-    method public void setDpToPixel(androidx.constraintlayout.core.state.CorePixelDp!);
-    method public androidx.constraintlayout.core.state.State! setHeight(androidx.constraintlayout.core.state.Dimension!);
-    method @Deprecated public void setLtr(boolean);
-    method public void setRtl(boolean);
-    method public void setTag(String!, String!);
-    method public androidx.constraintlayout.core.state.State! setWidth(androidx.constraintlayout.core.state.Dimension!);
-    method public androidx.constraintlayout.core.state.helpers.VerticalChainReference! verticalChain();
-    method public androidx.constraintlayout.core.state.helpers.VerticalChainReference! verticalChain(java.lang.Object!...!);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! verticalGuideline(Object!);
-    method public androidx.constraintlayout.core.state.State! width(androidx.constraintlayout.core.state.Dimension!);
-    field public static final Integer PARENT;
-    field protected java.util.HashMap<java.lang.Object!,androidx.constraintlayout.core.state.HelperReference!>! mHelperReferences;
-    field public final androidx.constraintlayout.core.state.ConstraintReference! mParent;
-    field protected java.util.HashMap<java.lang.Object!,androidx.constraintlayout.core.state.Reference!>! mReferences;
-  }
-
-  public enum State.Chain {
-    method public static androidx.constraintlayout.core.state.State.Chain! getChainByString(String!);
-    method public static int getValueByString(String!);
-    enum_constant public static final androidx.constraintlayout.core.state.State.Chain PACKED;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Chain SPREAD;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Chain SPREAD_INSIDE;
-    field public static java.util.Map<java.lang.String!,androidx.constraintlayout.core.state.State.Chain!>! chainMap;
-    field public static java.util.Map<java.lang.String!,java.lang.Integer!>! valueMap;
-  }
-
-  public enum State.Constraint {
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BASELINE_TO_BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BASELINE_TO_BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BASELINE_TO_TOP;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BOTTOM_TO_BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BOTTOM_TO_BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint BOTTOM_TO_TOP;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint CENTER_HORIZONTALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint CENTER_VERTICALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint CIRCULAR_CONSTRAINT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint END_TO_END;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint END_TO_START;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint LEFT_TO_LEFT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint LEFT_TO_RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint RIGHT_TO_LEFT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint RIGHT_TO_RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint START_TO_END;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint START_TO_START;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint TOP_TO_BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint TOP_TO_BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Constraint TOP_TO_TOP;
-  }
-
-  public enum State.Direction {
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction END;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction LEFT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction START;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Direction TOP;
-  }
-
-  public enum State.Helper {
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper ALIGN_HORIZONTALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper ALIGN_VERTICALLY;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper BARRIER;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper COLUMN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper FLOW;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper GRID;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper HORIZONTAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper HORIZONTAL_FLOW;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper LAYER;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper ROW;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper VERTICAL_CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Helper VERTICAL_FLOW;
-  }
-
-  public enum State.Wrap {
-    method public static androidx.constraintlayout.core.state.State.Wrap! getChainByString(String!);
-    method public static int getValueByString(String!);
-    enum_constant public static final androidx.constraintlayout.core.state.State.Wrap ALIGNED;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Wrap CHAIN;
-    enum_constant public static final androidx.constraintlayout.core.state.State.Wrap NONE;
-    field public static java.util.Map<java.lang.String!,java.lang.Integer!>! valueMap;
-    field public static java.util.Map<java.lang.String!,androidx.constraintlayout.core.state.State.Wrap!>! wrapMap;
-  }
-
-  public class Transition implements androidx.constraintlayout.core.motion.utils.TypedValues {
-    ctor public Transition(androidx.constraintlayout.core.state.CorePixelDp);
-    method public void addCustomColor(int, String!, String!, int);
-    method public void addCustomFloat(int, String!, String!, float);
-    method public void addKeyAttribute(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void addKeyAttribute(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!, androidx.constraintlayout.core.motion.CustomVariable![]!);
-    method public void addKeyCycle(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void addKeyPosition(String!, androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void addKeyPosition(String!, int, int, float, float);
-    method public void calcStagger();
-    method public void clear();
-    method public boolean contains(String!);
-    method public float dragToProgress(float, int, int, float, float);
-    method public void fillKeyPositions(androidx.constraintlayout.core.state.WidgetFrame!, float[]!, float[]!, float[]!);
-    method public androidx.constraintlayout.core.state.Transition.KeyPosition! findNextPosition(String!, int);
-    method public androidx.constraintlayout.core.state.Transition.KeyPosition! findPreviousPosition(String!, int);
-    method public int getAutoTransition();
-    method public androidx.constraintlayout.core.state.WidgetFrame! getEnd(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getEnd(String!);
-    method public int getId(String!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getInterpolated(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getInterpolated(String!);
-    method public int getInterpolatedHeight();
-    method public int getInterpolatedWidth();
-    method public androidx.constraintlayout.core.state.Interpolator! getInterpolator();
-    method public static androidx.constraintlayout.core.state.Interpolator! getInterpolator(int, String!);
-    method public int getKeyFrames(String!, float[]!, int[]!, int[]!);
-    method public androidx.constraintlayout.core.motion.Motion! getMotion(String!);
-    method public int getNumberKeyPositions(androidx.constraintlayout.core.state.WidgetFrame!);
-    method public float[]! getPath(String!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getStart(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public androidx.constraintlayout.core.state.WidgetFrame! getStart(String!);
-    method public float getTouchUpProgress(long);
-    method public androidx.constraintlayout.core.state.Transition.WidgetState! getWidgetState(String!, androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-    method public boolean hasOnSwipe();
-    method public boolean hasPositionKeyframes();
-    method public void interpolate(int, int, float);
-    method public boolean isEmpty();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public boolean isFirstDownAccepted(float, float);
-    method public boolean isTouchNotDone(float);
-    method public void setTouchUp(float, long, float, float);
-    method public void setTransitionProperties(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public boolean setValue(int, boolean);
-    method public boolean setValue(int, float);
-    method public boolean setValue(int, int);
-    method public boolean setValue(int, String!);
-    method public void updateFrom(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int);
-    field public static final int END = 1; // 0x1
-    field public static final int INTERPOLATED = 2; // 0x2
-    field public static final int START = 0; // 0x0
-  }
-
-  public static class Transition.WidgetState {
-    ctor public Transition.WidgetState();
-    method public androidx.constraintlayout.core.state.WidgetFrame! getFrame(int);
-    method public void interpolate(int, int, float, androidx.constraintlayout.core.state.Transition!);
-    method public void setKeyAttribute(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void setKeyAttribute(androidx.constraintlayout.core.motion.utils.TypedBundle!, androidx.constraintlayout.core.motion.CustomVariable![]!);
-    method public void setKeyCycle(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void setKeyPosition(androidx.constraintlayout.core.motion.utils.TypedBundle!);
-    method public void setPathRelative(androidx.constraintlayout.core.state.Transition.WidgetState!);
-    method public void update(androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-  }
-
-  public class TransitionParser {
-    ctor public TransitionParser();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static void parse(androidx.constraintlayout.core.parser.CLObject, androidx.constraintlayout.core.state.Transition) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method @Deprecated public static void parse(androidx.constraintlayout.core.parser.CLObject!, androidx.constraintlayout.core.state.Transition!, androidx.constraintlayout.core.state.CorePixelDp!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public static void parseKeyFrames(androidx.constraintlayout.core.parser.CLObject!, androidx.constraintlayout.core.state.Transition!) throws androidx.constraintlayout.core.parser.CLParsingException;
-  }
-
-  public class WidgetFrame {
-    ctor public WidgetFrame();
-    ctor public WidgetFrame(androidx.constraintlayout.core.state.WidgetFrame!);
-    ctor public WidgetFrame(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void addCustomColor(String!, int);
-    method public void addCustomFloat(String!, float);
-    method public float centerX();
-    method public float centerY();
-    method public boolean containsCustom(String);
-    method public androidx.constraintlayout.core.motion.CustomVariable! getCustomAttribute(String!);
-    method public java.util.Set<java.lang.String!>! getCustomAttributeNames();
-    method public int getCustomColor(String!);
-    method public float getCustomFloat(String!);
-    method public String! getId();
-    method public androidx.constraintlayout.core.motion.utils.TypedBundle! getMotionProperties();
-    method public int height();
-    method public static void interpolate(int, int, androidx.constraintlayout.core.state.WidgetFrame!, androidx.constraintlayout.core.state.WidgetFrame!, androidx.constraintlayout.core.state.WidgetFrame!, androidx.constraintlayout.core.state.Transition!, float);
-    method public boolean isDefaultTransform();
-    method public StringBuilder! serialize(StringBuilder!);
-    method public StringBuilder! serialize(StringBuilder!, boolean);
-    method public void setCustomAttribute(String!, int, boolean);
-    method public void setCustomAttribute(String!, int, float);
-    method public void setCustomAttribute(String!, int, int);
-    method public void setCustomAttribute(String!, int, String!);
-    method public void setCustomValue(androidx.constraintlayout.core.motion.CustomAttribute!, float[]!);
-    method public boolean setValue(String!, androidx.constraintlayout.core.parser.CLElement!) throws androidx.constraintlayout.core.parser.CLParsingException;
-    method public androidx.constraintlayout.core.state.WidgetFrame! update();
-    method public androidx.constraintlayout.core.state.WidgetFrame! update(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void updateAttributes(androidx.constraintlayout.core.state.WidgetFrame!);
-    method public int width();
-    field public float alpha;
-    field public int bottom;
-    field public float interpolatedPos;
-    field public int left;
-    field public String! name;
-    field public static float phone_orientation;
-    field public float pivotX;
-    field public float pivotY;
-    field public int right;
-    field public float rotationX;
-    field public float rotationY;
-    field public float rotationZ;
-    field public float scaleX;
-    field public float scaleY;
-    field public int top;
-    field public float translationX;
-    field public float translationY;
-    field public float translationZ;
-    field public int visibility;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget! widget;
-  }
-
-}
-
-package androidx.constraintlayout.core.state.helpers {
-
-  public class AlignHorizontallyReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public AlignHorizontallyReference(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class AlignVerticallyReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public AlignVerticallyReference(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class BarrierReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public BarrierReference(androidx.constraintlayout.core.state.State!);
-    method public void setBarrierDirection(androidx.constraintlayout.core.state.State.Direction!);
-  }
-
-  public class ChainReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public ChainReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void addChainElement(Object, float, float, float, float, float);
-    method public void addChainElement(String, float, float, float);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference bias(float);
-    method public float getBias();
-    method protected float getPostMargin(String);
-    method protected float getPreMargin(String);
-    method public androidx.constraintlayout.core.state.State.Chain getStyle();
-    method protected float getWeight(String);
-    method public androidx.constraintlayout.core.state.helpers.ChainReference style(androidx.constraintlayout.core.state.State.Chain);
-    field protected float mBias;
-    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPostMargin;
-    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapPreMargin;
-    field @Deprecated protected java.util.HashMap<java.lang.String!,java.lang.Float!> mMapWeights;
-    field protected androidx.constraintlayout.core.state.State.Chain mStyle;
-  }
-
-  public interface Facade {
-    method public void apply();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-  }
-
-  public class FlowReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public FlowReference(androidx.constraintlayout.core.state.State!, androidx.constraintlayout.core.state.State.Helper!);
-    method public void addFlowElement(String!, float, float, float);
-    method public float getFirstHorizontalBias();
-    method public int getFirstHorizontalStyle();
-    method public float getFirstVerticalBias();
-    method public int getFirstVerticalStyle();
-    method public int getHorizontalAlign();
-    method public float getHorizontalBias();
-    method public int getHorizontalGap();
-    method public int getHorizontalStyle();
-    method public float getLastHorizontalBias();
-    method public int getLastHorizontalStyle();
-    method public float getLastVerticalBias();
-    method public int getLastVerticalStyle();
-    method public int getMaxElementsWrap();
-    method public int getOrientation();
-    method public int getPaddingBottom();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
-    method public int getPaddingTop();
-    method protected float getPostMargin(String!);
-    method protected float getPreMargin(String!);
-    method public int getVerticalAlign();
-    method public float getVerticalBias();
-    method public int getVerticalGap();
-    method public int getVerticalStyle();
-    method protected float getWeight(String!);
-    method public int getWrapMode();
-    method public void setFirstHorizontalBias(float);
-    method public void setFirstHorizontalStyle(int);
-    method public void setFirstVerticalBias(float);
-    method public void setFirstVerticalStyle(int);
-    method public void setHorizontalAlign(int);
-    method public void setHorizontalGap(int);
-    method public void setHorizontalStyle(int);
-    method public void setLastHorizontalBias(float);
-    method public void setLastHorizontalStyle(int);
-    method public void setLastVerticalBias(float);
-    method public void setLastVerticalStyle(int);
-    method public void setMaxElementsWrap(int);
-    method public void setOrientation(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(int);
-    method public void setPaddingTop(int);
-    method public void setVerticalAlign(int);
-    method public void setVerticalGap(int);
-    method public void setVerticalStyle(int);
-    method public void setWrapMode(int);
-    field protected float mFirstHorizontalBias;
-    field protected int mFirstHorizontalStyle;
-    field protected float mFirstVerticalBias;
-    field protected int mFirstVerticalStyle;
-    field protected androidx.constraintlayout.core.widgets.Flow! mFlow;
-    field protected int mHorizontalAlign;
-    field protected int mHorizontalGap;
-    field protected int mHorizontalStyle;
-    field protected float mLastHorizontalBias;
-    field protected int mLastHorizontalStyle;
-    field protected float mLastVerticalBias;
-    field protected int mLastVerticalStyle;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPostMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapPreMargin;
-    field protected java.util.HashMap<java.lang.String!,java.lang.Float!>! mMapWeights;
-    field protected int mMaxElementsWrap;
-    field protected int mOrientation;
-    field protected int mPaddingBottom;
-    field protected int mPaddingLeft;
-    field protected int mPaddingRight;
-    field protected int mPaddingTop;
-    field protected int mVerticalAlign;
-    field protected int mVerticalGap;
-    field protected int mVerticalStyle;
-    field protected int mWrapMode;
-  }
-
-  public class GridReference extends androidx.constraintlayout.core.state.HelperReference {
-    ctor public GridReference(androidx.constraintlayout.core.state.State, androidx.constraintlayout.core.state.State.Helper);
-    method public String? getColumnWeights();
-    method public int getColumnsSet();
-    method public int getFlags();
-    method public float getHorizontalGaps();
-    method public int getOrientation();
-    method public int getPaddingBottom();
-    method public int getPaddingEnd();
-    method public int getPaddingStart();
-    method public int getPaddingTop();
-    method public String? getRowWeights();
-    method public int getRowsSet();
-    method public String? getSkips();
-    method public String? getSpans();
-    method public float getVerticalGaps();
-    method public void setColumnWeights(String);
-    method public void setColumnsSet(int);
-    method public void setFlags(int);
-    method public void setFlags(String);
-    method public void setHorizontalGaps(float);
-    method public void setOrientation(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingEnd(int);
-    method public void setPaddingStart(int);
-    method public void setPaddingTop(int);
-    method public void setRowWeights(String);
-    method public void setRowsSet(int);
-    method public void setSkips(String);
-    method public void setSpans(String);
-    method public void setVerticalGaps(float);
-  }
-
-  public class GuidelineReference implements androidx.constraintlayout.core.state.helpers.Facade androidx.constraintlayout.core.state.Reference {
-    ctor public GuidelineReference(androidx.constraintlayout.core.state.State!);
-    method public void apply();
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! end(Object!);
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public androidx.constraintlayout.core.state.helpers.Facade! getFacade();
-    method public Object! getKey();
-    method public int getOrientation();
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! percent(float);
-    method public void setConstraintWidget(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setKey(Object!);
-    method public void setOrientation(int);
-    method public androidx.constraintlayout.core.state.helpers.GuidelineReference! start(Object!);
-  }
-
-  public class HorizontalChainReference extends androidx.constraintlayout.core.state.helpers.ChainReference {
-    ctor public HorizontalChainReference(androidx.constraintlayout.core.state.State!);
-  }
-
-  public class VerticalChainReference extends androidx.constraintlayout.core.state.helpers.ChainReference {
-    ctor public VerticalChainReference(androidx.constraintlayout.core.state.State!);
-  }
-
-}
-
-package androidx.constraintlayout.core.utils {
-
-  public class GridCore extends androidx.constraintlayout.core.widgets.VirtualLayout {
-    ctor public GridCore();
-    ctor public GridCore(int, int);
-    method public String? getColumnWeights();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidgetContainer? getContainer();
-    method public int getFlags();
-    method public float getHorizontalGaps();
-    method public int getOrientation();
-    method public String? getRowWeights();
-    method public float getVerticalGaps();
-    method public void setColumnWeights(String);
-    method public void setColumns(int);
-    method public void setContainer(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer);
-    method public void setFlags(int);
-    method public void setHorizontalGaps(float);
-    method public void setOrientation(int);
-    method public void setRowWeights(String);
-    method public void setRows(int);
-    method public void setSkips(String);
-    method public void setSpans(CharSequence);
-    method public void setVerticalGaps(float);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int SPANS_RESPECT_WIDGET_ORDER = 2; // 0x2
-    field public static final int SUB_GRID_BY_COL_ROW = 1; // 0x1
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public class GridEngine {
-    ctor public GridEngine();
-    ctor public GridEngine(int, int);
-    ctor public GridEngine(int, int, int);
-    method public int bottomOfWidget(int);
-    method public int leftOfWidget(int);
-    method public int rightOfWidget(int);
-    method public void setColumns(int);
-    method public void setNumWidgets(int);
-    method public void setOrientation(int);
-    method public void setRows(int);
-    method public void setSkips(String!);
-    method public void setSpans(CharSequence!);
-    method public void setup();
-    method public int topOfWidget(int);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-}
-
-package androidx.constraintlayout.core.widgets {
-
-  public class Barrier extends androidx.constraintlayout.core.widgets.HelperWidget {
-    ctor public Barrier();
-    ctor public Barrier(String!);
-    method public boolean allSolved();
-    method @Deprecated public boolean allowsGoneWidget();
-    method public boolean getAllowsGoneWidget();
-    method public int getBarrierType();
-    method public int getMargin();
-    method public int getOrientation();
-    method protected void markWidgets();
-    method public void setAllowsGoneWidget(boolean);
-    method public void setBarrierType(int);
-    method public void setMargin(int);
-    field public static final int BOTTOM = 3; // 0x3
-    field public static final int LEFT = 0; // 0x0
-    field public static final int RIGHT = 1; // 0x1
-    field public static final int TOP = 2; // 0x2
-  }
-
-  public class Chain {
-    ctor public Chain();
-    method public static void applyChainConstraints(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.LinearSystem!, java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>!, int);
-    field public static final boolean USE_CHAIN_OPTIMIZATION = false;
-  }
-
-  public class ChainHead {
-    ctor public ChainHead(androidx.constraintlayout.core.widgets.ConstraintWidget!, int, boolean);
-    method public void define();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getFirst();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getFirstMatchConstraintWidget();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getFirstVisibleWidget();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getHead();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getLast();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getLastMatchConstraintWidget();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getLastVisibleWidget();
-    method public float getTotalWeight();
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mFirst;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mFirstMatchConstraintWidget;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mFirstVisibleWidget;
-    field protected boolean mHasComplexMatchWeights;
-    field protected boolean mHasDefinedWeights;
-    field protected boolean mHasRatio;
-    field protected boolean mHasUndefinedWeights;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mHead;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mLast;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mLastMatchConstraintWidget;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget! mLastVisibleWidget;
-    field protected float mTotalWeight;
-    field protected java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>! mWeightedMatchConstraintsWidgets;
-    field protected int mWidgetsCount;
-    field protected int mWidgetsMatchCount;
-  }
-
-  public class ConstraintAnchor {
-    ctor public ConstraintAnchor(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!);
-    method public boolean connect(androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method public boolean connect(androidx.constraintlayout.core.widgets.ConstraintAnchor!, int, int, boolean);
-    method public void copyFrom(androidx.constraintlayout.core.widgets.ConstraintAnchor!, java.util.HashMap<androidx.constraintlayout.core.widgets.ConstraintWidget!,androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void findDependents(int, java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public java.util.HashSet<androidx.constraintlayout.core.widgets.ConstraintAnchor!>! getDependents();
-    method public int getFinalValue();
-    method public int getMargin();
-    method public final androidx.constraintlayout.core.widgets.ConstraintAnchor! getOpposite();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getOwner();
-    method public androidx.constraintlayout.core.SolverVariable! getSolverVariable();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor! getTarget();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor.Type! getType();
-    method public boolean hasCenteredDependents();
-    method public boolean hasDependents();
-    method public boolean hasFinalValue();
-    method public boolean isConnected();
-    method public boolean isConnectionAllowed(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public boolean isConnectionAllowed(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public boolean isSideAnchor();
-    method public boolean isSimilarDimensionConnection(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public boolean isValidConnection(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public boolean isVerticalAnchor();
-    method public void reset();
-    method public void resetFinalResolution();
-    method public void resetSolverVariable(androidx.constraintlayout.core.Cache!);
-    method public void setFinalValue(int);
-    method public void setGoneMargin(int);
-    method public void setMargin(int);
-    field public int mMargin;
-    field public final androidx.constraintlayout.core.widgets.ConstraintWidget! mOwner;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mTarget;
-    field public final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type! mType;
-  }
-
-  public enum ConstraintAnchor.Type {
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type BASELINE;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type BOTTOM;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type CENTER;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type CENTER_X;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type CENTER_Y;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type LEFT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type NONE;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type RIGHT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintAnchor.Type TOP;
-  }
-
-  public class ConstraintWidget {
-    ctor public ConstraintWidget();
-    ctor public ConstraintWidget(int, int);
-    ctor public ConstraintWidget(int, int, int, int);
-    ctor public ConstraintWidget(String!);
-    ctor public ConstraintWidget(String!, int, int);
-    ctor public ConstraintWidget(String!, int, int, int, int);
-    method public void addChildrenToSolverByDependency(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.LinearSystem!, java.util.HashSet<androidx.constraintlayout.core.widgets.ConstraintWidget!>!, int, boolean);
-    method public void addToSolver(androidx.constraintlayout.core.LinearSystem!, boolean);
-    method public boolean allowedInBarrier();
-    method public void connect(androidx.constraintlayout.core.widgets.ConstraintAnchor!, androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method public void connect(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!);
-    method public void connect(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, int);
-    method public void connectCircularConstraint(androidx.constraintlayout.core.widgets.ConstraintWidget!, float, int);
-    method public void copy(androidx.constraintlayout.core.widgets.ConstraintWidget!, java.util.HashMap<androidx.constraintlayout.core.widgets.ConstraintWidget!,androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void createObjectVariables(androidx.constraintlayout.core.LinearSystem!);
-    method public void ensureMeasureRequested();
-    method public void ensureWidgetRuns();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor! getAnchor(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!);
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintAnchor!>! getAnchors();
-    method public int getBaselineDistance();
-    method public float getBiasPercent(int);
-    method public int getBottom();
-    method public Object! getCompanionWidget();
-    method public int getContainerItemSkip();
-    method public String! getDebugName();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! getDimensionBehaviour(int);
-    method public float getDimensionRatio();
-    method public int getDimensionRatioSide();
-    method public boolean getHasBaseline();
-    method public int getHeight();
-    method public float getHorizontalBiasPercent();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getHorizontalChainControlWidget();
-    method public int getHorizontalChainStyle();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! getHorizontalDimensionBehaviour();
-    method public int getHorizontalMargin();
-    method public int getLastHorizontalMeasureSpec();
-    method public int getLastVerticalMeasureSpec();
-    method public int getLeft();
-    method public int getLength(int);
-    method public int getMaxHeight();
-    method public int getMaxWidth();
-    method public int getMinHeight();
-    method public int getMinWidth();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getNextChainMember(int);
-    method public int getOptimizerWrapHeight();
-    method public int getOptimizerWrapWidth();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getParent();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getPreviousChainMember(int);
-    method public int getRight();
-    method protected int getRootX();
-    method protected int getRootY();
-    method public androidx.constraintlayout.core.widgets.analyzer.WidgetRun! getRun(int);
-    method public void getSceneString(StringBuilder!);
-    method public int getTop();
-    method public String! getType();
-    method public float getVerticalBiasPercent();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getVerticalChainControlWidget();
-    method public int getVerticalChainStyle();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! getVerticalDimensionBehaviour();
-    method public int getVerticalMargin();
-    method public int getVisibility();
-    method public int getWidth();
-    method public int getWrapBehaviorInParent();
-    method public int getX();
-    method public int getY();
-    method public boolean hasBaseline();
-    method public boolean hasDanglingDimension(int);
-    method public boolean hasDependencies();
-    method public boolean hasDimensionOverride();
-    method public boolean hasResolvedTargets(int, int);
-    method public void immediateConnect(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, int, int);
-    method public boolean isAnimated();
-    method public boolean isHeightWrapContent();
-    method public boolean isHorizontalSolvingPassDone();
-    method public boolean isInBarrier(int);
-    method public boolean isInHorizontalChain();
-    method public boolean isInPlaceholder();
-    method public boolean isInVerticalChain();
-    method public boolean isInVirtualLayout();
-    method public boolean isMeasureRequested();
-    method public boolean isResolvedHorizontally();
-    method public boolean isResolvedVertically();
-    method public boolean isRoot();
-    method public boolean isSpreadHeight();
-    method public boolean isSpreadWidth();
-    method public boolean isVerticalSolvingPassDone();
-    method public boolean isWidthWrapContent();
-    method public void markHorizontalSolvingPassDone();
-    method public void markVerticalSolvingPassDone();
-    method public boolean oppositeDimensionDependsOn(int);
-    method public boolean oppositeDimensionsTied();
-    method public void reset();
-    method public void resetAllConstraints();
-    method public void resetAnchor(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public void resetAnchors();
-    method public void resetFinalResolution();
-    method public void resetSolverVariables(androidx.constraintlayout.core.Cache!);
-    method public void resetSolvingPassFlag();
-    method public StringBuilder! serialize(StringBuilder!);
-    method public void setAnimated(boolean);
-    method public void setBaselineDistance(int);
-    method public void setCompanionWidget(Object!);
-    method public void setContainerItemSkip(int);
-    method public void setDebugName(String!);
-    method public void setDebugSolverName(androidx.constraintlayout.core.LinearSystem!, String!);
-    method public void setDimension(int, int);
-    method public void setDimensionRatio(float, int);
-    method public void setDimensionRatio(String!);
-    method public void setFinalBaseline(int);
-    method public void setFinalFrame(int, int, int, int, int, int);
-    method public void setFinalHorizontal(int, int);
-    method public void setFinalLeft(int);
-    method public void setFinalTop(int);
-    method public void setFinalVertical(int, int);
-    method public void setFrame(int, int, int);
-    method public void setFrame(int, int, int, int);
-    method public void setGoneMargin(androidx.constraintlayout.core.widgets.ConstraintAnchor.Type!, int);
-    method public void setHasBaseline(boolean);
-    method public void setHeight(int);
-    method public void setHeightWrapContent(boolean);
-    method public void setHorizontalBiasPercent(float);
-    method public void setHorizontalChainStyle(int);
-    method public void setHorizontalDimension(int, int);
-    method public void setHorizontalDimensionBehaviour(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-    method public void setHorizontalMatchStyle(int, int, int, float);
-    method public void setHorizontalWeight(float);
-    method protected void setInBarrier(int, boolean);
-    method public void setInPlaceholder(boolean);
-    method public void setInVirtualLayout(boolean);
-    method public void setLastMeasureSpec(int, int);
-    method public void setLength(int, int);
-    method public void setMaxHeight(int);
-    method public void setMaxWidth(int);
-    method public void setMeasureRequested(boolean);
-    method public void setMinHeight(int);
-    method public void setMinWidth(int);
-    method public void setOffset(int, int);
-    method public void setOrigin(int, int);
-    method public void setParent(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void setType(String!);
-    method public void setVerticalBiasPercent(float);
-    method public void setVerticalChainStyle(int);
-    method public void setVerticalDimension(int, int);
-    method public void setVerticalDimensionBehaviour(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-    method public void setVerticalMatchStyle(int, int, int, float);
-    method public void setVerticalWeight(float);
-    method public void setVisibility(int);
-    method public void setWidth(int);
-    method public void setWidthWrapContent(boolean);
-    method public void setWrapBehaviorInParent(int);
-    method public void setX(int);
-    method public void setY(int);
-    method public void setupDimensionRatio(boolean, boolean, boolean, boolean);
-    method public void updateFromRuns(boolean, boolean);
-    method public void updateFromSolver(androidx.constraintlayout.core.LinearSystem!, boolean);
-    field public static final int ANCHOR_BASELINE = 4; // 0x4
-    field public static final int ANCHOR_BOTTOM = 3; // 0x3
-    field public static final int ANCHOR_LEFT = 0; // 0x0
-    field public static final int ANCHOR_RIGHT = 1; // 0x1
-    field public static final int ANCHOR_TOP = 2; // 0x2
-    field public static final int BOTH = 2; // 0x2
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static float DEFAULT_BIAS;
-    field protected static final int DIRECT = 2; // 0x2
-    field public static final int GONE = 8; // 0x8
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int INVISIBLE = 4; // 0x4
-    field public static final int MATCH_CONSTRAINT_PERCENT = 2; // 0x2
-    field public static final int MATCH_CONSTRAINT_RATIO = 3; // 0x3
-    field public static final int MATCH_CONSTRAINT_RATIO_RESOLVED = 4; // 0x4
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field protected static final int SOLVER = 1; // 0x1
-    field public static final int UNKNOWN = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int VISIBLE = 0; // 0x0
-    field public static final int WRAP_BEHAVIOR_HORIZONTAL_ONLY = 1; // 0x1
-    field public static final int WRAP_BEHAVIOR_INCLUDED = 0; // 0x0
-    field public static final int WRAP_BEHAVIOR_SKIPPED = 3; // 0x3
-    field public static final int WRAP_BEHAVIOR_VERTICAL_ONLY = 2; // 0x2
-    field public androidx.constraintlayout.core.state.WidgetFrame! frame;
-    field public androidx.constraintlayout.core.widgets.analyzer.ChainRun! horizontalChainRun;
-    field public int horizontalGroup;
-    field public boolean[]! isTerminalWidget;
-    field protected java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintAnchor!>! mAnchors;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mBaseline;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mBottom;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mCenter;
-    field public float mCircleConstraintAngle;
-    field public float mDimensionRatio;
-    field protected int mDimensionRatioSide;
-    field public int mHorizontalResolution;
-    field public androidx.constraintlayout.core.widgets.analyzer.HorizontalWidgetRun! mHorizontalRun;
-    field public boolean mIsHeightWrapContent;
-    field public boolean mIsWidthWrapContent;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mLeft;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor![]! mListAnchors;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour![]! mListDimensionBehaviors;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget![]! mListNextMatchConstraintsWidget;
-    field public int mMatchConstraintDefaultHeight;
-    field public int mMatchConstraintDefaultWidth;
-    field public int mMatchConstraintMaxHeight;
-    field public int mMatchConstraintMaxWidth;
-    field public int mMatchConstraintMinHeight;
-    field public int mMatchConstraintMinWidth;
-    field public float mMatchConstraintPercentHeight;
-    field public float mMatchConstraintPercentWidth;
-    field protected int mMinHeight;
-    field protected int mMinWidth;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget![]! mNextChainWidget;
-    field protected int mOffsetX;
-    field protected int mOffsetY;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget! mParent;
-    field public int[]! mResolvedMatchConstraintDefault;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mRight;
-    field public androidx.constraintlayout.core.widgets.ConstraintAnchor! mTop;
-    field public int mVerticalResolution;
-    field public androidx.constraintlayout.core.widgets.analyzer.VerticalWidgetRun! mVerticalRun;
-    field public float[]! mWeight;
-    field protected int mX;
-    field protected int mY;
-    field public boolean measured;
-    field public androidx.constraintlayout.core.widgets.analyzer.WidgetRun![]! run;
-    field public String! stringId;
-    field public androidx.constraintlayout.core.widgets.analyzer.ChainRun! verticalChainRun;
-    field public int verticalGroup;
-  }
-
-  public enum ConstraintWidget.DimensionBehaviour {
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour FIXED;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour MATCH_CONSTRAINT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour MATCH_PARENT;
-    enum_constant public static final androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour WRAP_CONTENT;
-  }
-
-  public class ConstraintWidgetContainer extends androidx.constraintlayout.core.widgets.WidgetContainer {
-    ctor public ConstraintWidgetContainer();
-    ctor public ConstraintWidgetContainer(int, int);
-    ctor public ConstraintWidgetContainer(int, int, int, int);
-    ctor public ConstraintWidgetContainer(String!, int, int);
-    method public boolean addChildrenToSolver(androidx.constraintlayout.core.LinearSystem!);
-    method public void addHorizontalWrapMaxVariable(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public void addHorizontalWrapMinVariable(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method public void defineTerminalWidgets();
-    method public boolean directMeasure(boolean);
-    method public boolean directMeasureSetup(boolean);
-    method public boolean directMeasureWithOrientation(boolean, int);
-    method public void fillMetrics(androidx.constraintlayout.core.Metrics!);
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.Guideline!>! getHorizontalGuidelines();
-    method public androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer! getMeasurer();
-    method public int getOptimizationLevel();
-    method public androidx.constraintlayout.core.LinearSystem! getSystem();
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.Guideline!>! getVerticalGuidelines();
-    method public boolean handlesInternalConstraints();
-    method public void invalidateGraph();
-    method public void invalidateMeasures();
-    method public boolean isHeightMeasuredTooSmall();
-    method public boolean isRtl();
-    method public boolean isWidthMeasuredTooSmall();
-    method public static boolean measure(int, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure!, int);
-    method public long measure(int, int, int, int, int, int, int, int, int);
-    method public boolean optimizeFor(int);
-    method public void setMeasurer(androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-    method public void setOptimizationLevel(int);
-    method public void setPadding(int, int, int, int);
-    method public void setPass(int);
-    method public void setRtl(boolean);
-    method public boolean updateChildrenFromSolver(androidx.constraintlayout.core.LinearSystem!, boolean[]!);
-    method public void updateHierarchy();
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyGraph! mDependencyGraph;
-    field public boolean mGroupsWrapOptimized;
-    field public int mHorizontalChainsSize;
-    field public boolean mHorizontalWrapOptimized;
-    field public androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure! mMeasure;
-    field protected androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer! mMeasurer;
-    field public androidx.constraintlayout.core.Metrics! mMetrics;
-    field public boolean mSkipSolver;
-    field protected androidx.constraintlayout.core.LinearSystem! mSystem;
-    field public int mVerticalChainsSize;
-    field public boolean mVerticalWrapOptimized;
-    field public int mWrapFixedHeight;
-    field public int mWrapFixedWidth;
-  }
-
-  public class Flow extends androidx.constraintlayout.core.widgets.VirtualLayout {
-    ctor public Flow();
-    method public float getMaxElementsWrap();
-    method public void setFirstHorizontalBias(float);
-    method public void setFirstHorizontalStyle(int);
-    method public void setFirstVerticalBias(float);
-    method public void setFirstVerticalStyle(int);
-    method public void setHorizontalAlign(int);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalGap(int);
-    method public void setHorizontalStyle(int);
-    method public void setLastHorizontalBias(float);
-    method public void setLastHorizontalStyle(int);
-    method public void setLastVerticalBias(float);
-    method public void setLastVerticalStyle(int);
-    method public void setMaxElementsWrap(int);
-    method public void setOrientation(int);
-    method public void setVerticalAlign(int);
-    method public void setVerticalBias(float);
-    method public void setVerticalGap(int);
-    method public void setVerticalStyle(int);
-    method public void setWrapMode(int);
-    field public static final int HORIZONTAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int HORIZONTAL_ALIGN_END = 1; // 0x1
-    field public static final int HORIZONTAL_ALIGN_START = 0; // 0x0
-    field public static final int VERTICAL_ALIGN_BASELINE = 3; // 0x3
-    field public static final int VERTICAL_ALIGN_BOTTOM = 1; // 0x1
-    field public static final int VERTICAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int VERTICAL_ALIGN_TOP = 0; // 0x0
-    field public static final int WRAP_ALIGNED = 2; // 0x2
-    field public static final int WRAP_CHAIN = 1; // 0x1
-    field public static final int WRAP_CHAIN_NEW = 3; // 0x3
-    field public static final int WRAP_NONE = 0; // 0x0
-  }
-
-  public class Guideline extends androidx.constraintlayout.core.widgets.ConstraintWidget {
-    ctor public Guideline();
-    method public void cyclePosition();
-    method public androidx.constraintlayout.core.widgets.ConstraintAnchor! getAnchor();
-    method public int getMinimumPosition();
-    method public int getOrientation();
-    method public int getRelativeBegin();
-    method public int getRelativeBehaviour();
-    method public int getRelativeEnd();
-    method public float getRelativePercent();
-    method public boolean isPercent();
-    method public void setFinalValue(int);
-    method public void setGuideBegin(int);
-    method public void setGuideEnd(int);
-    method public void setGuidePercent(float);
-    method public void setGuidePercent(int);
-    method public void setMinimumPosition(int);
-    method public void setOrientation(int);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int RELATIVE_BEGIN = 1; // 0x1
-    field public static final int RELATIVE_END = 2; // 0x2
-    field public static final int RELATIVE_PERCENT = 0; // 0x0
-    field public static final int RELATIVE_UNKNOWN = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field protected boolean mGuidelineUseRtl;
-    field protected int mRelativeBegin;
-    field protected int mRelativeEnd;
-    field protected float mRelativePercent;
-  }
-
-  public interface Helper {
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void removeAllIds();
-    method public void updateConstraints(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-  }
-
-  public class HelperWidget extends androidx.constraintlayout.core.widgets.ConstraintWidget implements androidx.constraintlayout.core.widgets.Helper {
-    ctor public HelperWidget();
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void addDependents(java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!, int, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public int findGroupInDependents(int);
-    method public void removeAllIds();
-    method public void updateConstraints(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget![]! mWidgets;
-    field public int mWidgetsCount;
-  }
-
-  public class Optimizer {
-    ctor public Optimizer();
-    method public static final boolean enabled(int, int);
-    field public static final int OPTIMIZATION_BARRIER = 2; // 0x2
-    field public static final int OPTIMIZATION_CACHE_MEASURES = 256; // 0x100
-    field public static final int OPTIMIZATION_CHAIN = 4; // 0x4
-    field public static final int OPTIMIZATION_DEPENDENCY_ORDERING = 512; // 0x200
-    field public static final int OPTIMIZATION_DIMENSIONS = 8; // 0x8
-    field public static final int OPTIMIZATION_DIRECT = 1; // 0x1
-    field public static final int OPTIMIZATION_GRAPH = 64; // 0x40
-    field public static final int OPTIMIZATION_GRAPH_WRAP = 128; // 0x80
-    field public static final int OPTIMIZATION_GROUPING = 1024; // 0x400
-    field public static final int OPTIMIZATION_GROUPS = 32; // 0x20
-    field public static final int OPTIMIZATION_NONE = 0; // 0x0
-    field public static final int OPTIMIZATION_RATIO = 16; // 0x10
-    field public static final int OPTIMIZATION_STANDARD = 257; // 0x101
-  }
-
-  public class Placeholder extends androidx.constraintlayout.core.widgets.VirtualLayout {
-    ctor public Placeholder();
-  }
-
-  public class Rectangle {
-    ctor public Rectangle();
-    method public boolean contains(int, int);
-    method public int getCenterX();
-    method public int getCenterY();
-    method public void setBounds(int, int, int, int);
-    field public int height;
-    field public int width;
-    field public int x;
-    field public int y;
-  }
-
-  public class VirtualLayout extends androidx.constraintlayout.core.widgets.HelperWidget {
-    ctor public VirtualLayout();
-    method public void applyRtl(boolean);
-    method public void captureWidgets();
-    method public boolean contains(java.util.HashSet<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public int getMeasuredHeight();
-    method public int getMeasuredWidth();
-    method public int getPaddingBottom();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
-    method public int getPaddingTop();
-    method protected void measure(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, int, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, int);
-    method public void measure(int, int, int, int);
-    method protected boolean measureChildren();
-    method public boolean needSolverPass();
-    method protected void needsCallbackFromSolver(boolean);
-    method public void setMeasure(int, int);
-    method public void setPadding(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingEnd(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(int);
-    method public void setPaddingStart(int);
-    method public void setPaddingTop(int);
-    field protected androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure! mMeasure;
-  }
-
-  public class WidgetContainer extends androidx.constraintlayout.core.widgets.ConstraintWidget {
-    ctor public WidgetContainer();
-    ctor public WidgetContainer(int, int);
-    ctor public WidgetContainer(int, int, int, int);
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void add(androidx.constraintlayout.core.widgets.ConstraintWidget!...!);
-    method public java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>! getChildren();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidgetContainer! getRootConstraintContainer();
-    method public void layout();
-    method public void remove(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void removeAllChildren();
-    field public java.util.ArrayList<androidx.constraintlayout.core.widgets.ConstraintWidget!>! mChildren;
-  }
-
-}
-
-package androidx.constraintlayout.core.widgets.analyzer {
-
-  public class BasicMeasure {
-    ctor public BasicMeasure(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    method public long solverMeasure(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int, int, int, int, int, int, int, int, int);
-    method public void updateHierarchy(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    field public static final int AT_MOST = -2147483648; // 0x80000000
-    field public static final int EXACTLY = 1073741824; // 0x40000000
-    field public static final int FIXED = -3; // 0xfffffffd
-    field public static final int MATCH_PARENT = -1; // 0xffffffff
-    field public static final int UNSPECIFIED = 0; // 0x0
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-  }
-
-  public static class BasicMeasure.Measure {
-    ctor public BasicMeasure.Measure();
-    field public static int SELF_DIMENSIONS;
-    field public static int TRY_GIVEN_DIMENSIONS;
-    field public static int USE_GIVEN_DIMENSIONS;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! horizontalBehavior;
-    field public int horizontalDimension;
-    field public int measureStrategy;
-    field public int measuredBaseline;
-    field public boolean measuredHasBaseline;
-    field public int measuredHeight;
-    field public boolean measuredNeedsSolverPass;
-    field public int measuredWidth;
-    field public androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! verticalBehavior;
-    field public int verticalDimension;
-  }
-
-  public static interface BasicMeasure.Measurer {
-    method public void didMeasures();
-    method public void measure(androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measure!);
-  }
-
-  public class ChainRun extends androidx.constraintlayout.core.widgets.analyzer.WidgetRun {
-    ctor public ChainRun(androidx.constraintlayout.core.widgets.ConstraintWidget!, int);
-    method public void applyToWidget();
-  }
-
-  public interface Dependency {
-    method public void update(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-  }
-
-  public class DependencyGraph {
-    ctor public DependencyGraph(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!);
-    method public void buildGraph();
-    method public void buildGraph(java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetRun!>!);
-    method public void defineTerminalWidgets(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-    method public boolean directMeasure(boolean);
-    method public boolean directMeasureSetup(boolean);
-    method public boolean directMeasureWithOrientation(boolean, int);
-    method public void invalidateGraph();
-    method public void invalidateMeasures();
-    method public void measureWidgets();
-    method public void setMeasurer(androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-  }
-
-  public class DependencyNode implements androidx.constraintlayout.core.widgets.analyzer.Dependency {
-    ctor public DependencyNode(androidx.constraintlayout.core.widgets.analyzer.WidgetRun!);
-    method public void addDependency(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method public void clear();
-    method public String! name();
-    method public void resolve(int);
-    method public void update(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    field public boolean delegateToWidgetRun;
-    field public boolean readyToSolve;
-    field public boolean resolved;
-    field public androidx.constraintlayout.core.widgets.analyzer.Dependency! updateDelegate;
-    field public int value;
-  }
-
-  public class Direct {
-    ctor public Direct();
-    method public static String! ls(int);
-    method public static boolean solveChain(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.LinearSystem!, int, int, androidx.constraintlayout.core.widgets.ChainHead!, boolean, boolean, boolean);
-    method public static void solvingPass(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-  }
-
-  public class Grouping {
-    ctor public Grouping();
-    method public static androidx.constraintlayout.core.widgets.analyzer.WidgetGroup! findDependents(androidx.constraintlayout.core.widgets.ConstraintWidget!, int, java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public static boolean simpleSolvingPass(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.Measurer!);
-    method public static boolean validInGroup(androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!, androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour!);
-  }
-
-  public class HorizontalWidgetRun extends androidx.constraintlayout.core.widgets.analyzer.WidgetRun {
-    ctor public HorizontalWidgetRun(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void applyToWidget();
-  }
-
-  public class VerticalWidgetRun extends androidx.constraintlayout.core.widgets.analyzer.WidgetRun {
-    ctor public VerticalWidgetRun(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void applyToWidget();
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyNode! baseline;
-  }
-
-  public class WidgetGroup {
-    ctor public WidgetGroup(int);
-    method public boolean add(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method public void apply();
-    method public void cleanup(java.util.ArrayList<androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!>!);
-    method public void clear();
-    method public int getId();
-    method public int getOrientation();
-    method public boolean intersectWith(androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public boolean isAuthoritative();
-    method public int measureWrap(androidx.constraintlayout.core.LinearSystem!, int);
-    method public void moveTo(int, androidx.constraintlayout.core.widgets.analyzer.WidgetGroup!);
-    method public void setAuthoritative(boolean);
-    method public void setOrientation(int);
-    method public int size();
-  }
-
-  public abstract class WidgetRun implements androidx.constraintlayout.core.widgets.analyzer.Dependency {
-    ctor public WidgetRun(androidx.constraintlayout.core.widgets.ConstraintWidget!);
-    method protected final void addTarget(androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, int);
-    method protected final void addTarget(androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, androidx.constraintlayout.core.widgets.analyzer.DependencyNode!, int, androidx.constraintlayout.core.widgets.analyzer.DimensionDependency!);
-    method protected final int getLimitedDimension(int, int);
-    method protected final androidx.constraintlayout.core.widgets.analyzer.DependencyNode! getTarget(androidx.constraintlayout.core.widgets.ConstraintAnchor!);
-    method protected final androidx.constraintlayout.core.widgets.analyzer.DependencyNode! getTarget(androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method public long getWrapDimension();
-    method public boolean isCenterConnection();
-    method public boolean isDimensionResolved();
-    method public boolean isResolved();
-    method public void update(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method protected void updateRunCenter(androidx.constraintlayout.core.widgets.analyzer.Dependency!, androidx.constraintlayout.core.widgets.ConstraintAnchor!, androidx.constraintlayout.core.widgets.ConstraintAnchor!, int);
-    method protected void updateRunEnd(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method protected void updateRunStart(androidx.constraintlayout.core.widgets.analyzer.Dependency!);
-    method public long wrapSize(int);
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyNode! end;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour! mDimensionBehavior;
-    field protected androidx.constraintlayout.core.widgets.analyzer.WidgetRun.RunType! mRunType;
-    field public int matchConstraintsType;
-    field public int orientation;
-    field public androidx.constraintlayout.core.widgets.analyzer.DependencyNode! start;
-  }
-
-}
-
diff --git a/constraintlayout/constraintlayout-core/build.gradle b/constraintlayout/constraintlayout-core/build.gradle
index 717357f..e4b1570 100644
--- a/constraintlayout/constraintlayout-core/build.gradle
+++ b/constraintlayout/constraintlayout-core/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.5.0")
+    api("androidx.annotation:annotation:1.8.1")
     testImplementation(libs.junit)
 }
 
@@ -40,5 +40,4 @@
     mavenVersion = LibraryVersions.CONSTRAINTLAYOUT_CORE
     inceptionYear = "2022"
     description = "This library contains engines and algorithms for constraint based layout and complex animations (it is used by the ConstraintLayout library)"
-    metalavaK2UastEnabled = true
 }
diff --git a/constraintlayout/constraintlayout/api/2.2.0-beta01.txt b/constraintlayout/constraintlayout/api/2.2.0-beta01.txt
deleted file mode 100644
index 2d187a8..0000000
--- a/constraintlayout/constraintlayout/api/2.2.0-beta01.txt
+++ /dev/null
@@ -1,1714 +0,0 @@
-// Signature format: 4.0
-package androidx.constraintlayout.helper.widget {
-
-  public class Carousel extends androidx.constraintlayout.motion.widget.MotionHelper {
-    ctor public Carousel(android.content.Context!);
-    ctor public Carousel(android.content.Context!, android.util.AttributeSet!);
-    ctor public Carousel(android.content.Context!, android.util.AttributeSet!, int);
-    method public int getCount();
-    method public int getCurrentIndex();
-    method public boolean isInfinite();
-    method public void jumpToIndex(int);
-    method public void refresh();
-    method public void setAdapter(androidx.constraintlayout.helper.widget.Carousel.Adapter!);
-    method public void setInfinite(boolean);
-    method public void transitionToIndex(int, int);
-    field public static final int TOUCH_UP_CARRY_ON = 2; // 0x2
-    field public static final int TOUCH_UP_IMMEDIATE_STOP = 1; // 0x1
-  }
-
-  public static interface Carousel.Adapter {
-    method public int count();
-    method public void onNewItem(int);
-    method public void populate(android.view.View!, int);
-  }
-
-  public class CircularFlow extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public CircularFlow(android.content.Context!);
-    ctor public CircularFlow(android.content.Context!, android.util.AttributeSet!);
-    ctor public CircularFlow(android.content.Context!, android.util.AttributeSet!, int);
-    method public void addViewToCircularFlow(android.view.View!, int, float);
-    method public float[]! getAngles();
-    method public int[]! getRadius();
-    method public boolean isUpdatable(android.view.View!);
-    method public void setDefaultAngle(float);
-    method public void setDefaultRadius(int);
-    method public void updateAngle(android.view.View!, float);
-    method public void updateRadius(android.view.View!, int);
-    method public void updateReference(android.view.View!, int, float);
-  }
-
-  public class Flow extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public Flow(android.content.Context!);
-    ctor public Flow(android.content.Context!, android.util.AttributeSet!);
-    ctor public Flow(android.content.Context!, android.util.AttributeSet!, int);
-    method public void setFirstHorizontalBias(float);
-    method public void setFirstHorizontalStyle(int);
-    method public void setFirstVerticalBias(float);
-    method public void setFirstVerticalStyle(int);
-    method public void setHorizontalAlign(int);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalGap(int);
-    method public void setHorizontalStyle(int);
-    method public void setLastHorizontalBias(float);
-    method public void setLastHorizontalStyle(int);
-    method public void setLastVerticalBias(float);
-    method public void setLastVerticalStyle(int);
-    method public void setMaxElementsWrap(int);
-    method public void setOrientation(int);
-    method public void setPadding(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(int);
-    method public void setPaddingTop(int);
-    method public void setVerticalAlign(int);
-    method public void setVerticalBias(float);
-    method public void setVerticalGap(int);
-    method public void setVerticalStyle(int);
-    method public void setWrapMode(int);
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int HORIZONTAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int HORIZONTAL_ALIGN_END = 1; // 0x1
-    field public static final int HORIZONTAL_ALIGN_START = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int VERTICAL_ALIGN_BASELINE = 3; // 0x3
-    field public static final int VERTICAL_ALIGN_BOTTOM = 1; // 0x1
-    field public static final int VERTICAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int VERTICAL_ALIGN_TOP = 0; // 0x0
-    field public static final int WRAP_ALIGNED = 2; // 0x2
-    field public static final int WRAP_CHAIN = 1; // 0x1
-    field public static final int WRAP_NONE = 0; // 0x0
-  }
-
-  public class Grid extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public Grid(android.content.Context!);
-    ctor public Grid(android.content.Context!, android.util.AttributeSet!);
-    ctor public Grid(android.content.Context!, android.util.AttributeSet!, int);
-    method public String! getColumnWeights();
-    method public int getColumns();
-    method public float getHorizontalGaps();
-    method public int getOrientation();
-    method public String! getRowWeights();
-    method public int getRows();
-    method public String! getSkips();
-    method public String! getSpans();
-    method public float getVerticalGaps();
-    method public void setColumnWeights(String!);
-    method public void setColumns(int);
-    method public void setHorizontalGaps(float);
-    method public void setOrientation(int);
-    method public void setRowWeights(String!);
-    method public void setRows(int);
-    method public void setSkips(String!);
-    method public void setSpans(CharSequence!);
-    method public void setVerticalGaps(float);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public class Layer extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public Layer(android.content.Context!);
-    ctor public Layer(android.content.Context!, android.util.AttributeSet!);
-    ctor public Layer(android.content.Context!, android.util.AttributeSet!, int);
-    method protected void calcCenters();
-    field protected float mComputedCenterX;
-    field protected float mComputedCenterY;
-    field protected float mComputedMaxX;
-    field protected float mComputedMaxY;
-    field protected float mComputedMinX;
-    field protected float mComputedMinY;
-  }
-
-  public class MotionEffect extends androidx.constraintlayout.motion.widget.MotionHelper {
-    ctor public MotionEffect(android.content.Context!);
-    ctor public MotionEffect(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionEffect(android.content.Context!, android.util.AttributeSet!, int);
-    field public static final int AUTO = -1; // 0xffffffff
-    field public static final int EAST = 2; // 0x2
-    field public static final int NORTH = 0; // 0x0
-    field public static final int SOUTH = 1; // 0x1
-    field public static final String TAG = "FadeMove";
-    field public static final int WEST = 3; // 0x3
-  }
-
-  public class MotionPlaceholder extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public MotionPlaceholder(android.content.Context!);
-    ctor public MotionPlaceholder(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionPlaceholder(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public MotionPlaceholder(android.content.Context!, android.util.AttributeSet!, int, int);
-  }
-
-}
-
-package androidx.constraintlayout.motion.utils {
-
-  public class CustomSupport {
-    ctor public CustomSupport();
-    method public static void setInterpolatedValue(androidx.constraintlayout.widget.ConstraintAttribute!, android.view.View!, float[]!);
-  }
-
-  public class StopLogic extends androidx.constraintlayout.motion.widget.MotionInterpolator {
-    ctor public StopLogic();
-    method public void config(float, float, float, float, float, float);
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-    method public void springConfig(float, float, float, float, float, float, float, int);
-  }
-
-  public abstract class ViewOscillator extends androidx.constraintlayout.core.motion.utils.KeyCycleOscillator {
-    ctor public ViewOscillator();
-    method public static androidx.constraintlayout.motion.utils.ViewOscillator! makeSpline(String!);
-    method public abstract void setProperty(android.view.View!, float);
-  }
-
-  public static class ViewOscillator.PathRotateSet extends androidx.constraintlayout.motion.utils.ViewOscillator {
-    ctor public ViewOscillator.PathRotateSet();
-    method public void setPathRotate(android.view.View!, float, double, double);
-    method public void setProperty(android.view.View!, float);
-  }
-
-  public abstract class ViewSpline extends androidx.constraintlayout.core.motion.utils.SplineSet {
-    ctor public ViewSpline();
-    method public static androidx.constraintlayout.motion.utils.ViewSpline! makeCustomSpline(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public static androidx.constraintlayout.motion.utils.ViewSpline! makeSpline(String!);
-    method public abstract void setProperty(android.view.View!, float);
-  }
-
-  public static class ViewSpline.CustomSet extends androidx.constraintlayout.motion.utils.ViewSpline {
-    ctor public ViewSpline.CustomSet(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public void setPoint(int, androidx.constraintlayout.widget.ConstraintAttribute!);
-    method public void setProperty(android.view.View!, float);
-  }
-
-  public static class ViewSpline.PathRotate extends androidx.constraintlayout.motion.utils.ViewSpline {
-    ctor public ViewSpline.PathRotate();
-    method public void setPathRotate(android.view.View!, float, double, double);
-    method public void setProperty(android.view.View!, float);
-  }
-
-  public class ViewState {
-    ctor public ViewState();
-    method public void getState(android.view.View!);
-    method public int height();
-    method public int width();
-    field public int bottom;
-    field public int left;
-    field public int right;
-    field public float rotation;
-    field public int top;
-  }
-
-  public abstract class ViewTimeCycle extends androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet {
-    ctor public ViewTimeCycle();
-    method public float get(float, long, android.view.View!, androidx.constraintlayout.core.motion.utils.KeyCache!);
-    method public static androidx.constraintlayout.motion.utils.ViewTimeCycle! makeCustomSpline(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public static androidx.constraintlayout.motion.utils.ViewTimeCycle! makeSpline(String!, long);
-    method public abstract boolean setProperty(android.view.View!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  public static class ViewTimeCycle.CustomSet extends androidx.constraintlayout.motion.utils.ViewTimeCycle {
-    ctor public ViewTimeCycle.CustomSet(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public void setPoint(int, androidx.constraintlayout.widget.ConstraintAttribute!, float, int, float);
-    method public boolean setProperty(android.view.View!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  public static class ViewTimeCycle.PathRotate extends androidx.constraintlayout.motion.utils.ViewTimeCycle {
-    ctor public ViewTimeCycle.PathRotate();
-    method public boolean setPathRotate(android.view.View!, androidx.constraintlayout.core.motion.utils.KeyCache!, float, long, double, double);
-    method public boolean setProperty(android.view.View!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-}
-
-package androidx.constraintlayout.motion.widget {
-
-  public interface Animatable {
-    method public float getProgress();
-    method public void setProgress(float);
-  }
-
-  public interface CustomFloatAttributes {
-    method public float get(String!);
-    method public String![]! getListOfAttributes();
-    method public void set(String!, float);
-  }
-
-  public class Debug {
-    ctor public Debug();
-    method public static void dumpLayoutParams(android.view.ViewGroup!, String!);
-    method public static void dumpLayoutParams(android.view.ViewGroup.LayoutParams!, String!);
-    method public static void dumpPoc(Object!);
-    method public static String! getActionType(android.view.MotionEvent!);
-    method public static String! getCallFrom(int);
-    method public static String! getLoc();
-    method public static String! getLocation();
-    method public static String! getLocation2();
-    method public static String! getName(android.content.Context!, int);
-    method public static String! getName(android.content.Context!, int[]!);
-    method public static String! getName(android.view.View!);
-    method public static String! getState(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public static String! getState(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public static void logStack(String!, String!, int);
-    method public static void printStack(String!, int);
-  }
-
-  public class DesignTool {
-    ctor public DesignTool(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public int designAccess(int, String!, Object!, float[]!, int, float[]!, int);
-    method public void disableAutoTransition(boolean);
-    method public void dumpConstraintSet(String!);
-    method public int getAnimationKeyFrames(Object!, float[]!);
-    method public int getAnimationPath(Object!, float[]!, int);
-    method public void getAnimationRectangles(Object!, float[]!);
-    method public String! getEndState();
-    method public int getKeyFrameInfo(Object!, int, int[]!);
-    method public float getKeyFramePosition(Object!, int, float, float);
-    method public int getKeyFramePositions(Object!, int[]!, float[]!);
-    method public Object! getKeyframe(int, int, int);
-    method public Object! getKeyframe(Object!, int, int);
-    method public Object! getKeyframeAtLocation(Object!, float, float);
-    method public Boolean! getPositionKeyframe(Object!, Object!, float, float, String![]!, float[]!);
-    method public float getProgress();
-    method public String! getStartState();
-    method public String! getState();
-    method public long getTransitionTimeMs();
-    method public boolean isInTransition();
-    method public void setAttributes(int, String!, Object!, Object!);
-    method public void setKeyFrame(Object!, int, String!, Object!);
-    method public boolean setKeyFramePosition(Object!, int, int, float, float);
-    method public void setKeyframe(Object!, String!, Object!);
-    method public void setState(String!);
-    method public void setToolPosition(float);
-    method public void setTransition(String!, String!);
-    method public void setViewDebug(Object!, int);
-  }
-
-  public interface FloatLayout {
-    method public void layout(float, float, float, float);
-  }
-
-  public abstract class Key {
-    ctor public Key();
-    method public abstract void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public abstract androidx.constraintlayout.motion.widget.Key! clone();
-    method public androidx.constraintlayout.motion.widget.Key! copy(androidx.constraintlayout.motion.widget.Key!);
-    method public int getFramePosition();
-    method public void setFramePosition(int);
-    method public void setInterpolation(java.util.HashMap<java.lang.String!,java.lang.Integer!>!);
-    method public abstract void setValue(String!, Object!);
-    method public androidx.constraintlayout.motion.widget.Key! setViewId(int);
-    field public static final String ALPHA = "alpha";
-    field public static final String CURVEFIT = "curveFit";
-    field public static final String CUSTOM = "CUSTOM";
-    field public static final String ELEVATION = "elevation";
-    field public static final String MOTIONPROGRESS = "motionProgress";
-    field public static final String PIVOT_X = "transformPivotX";
-    field public static final String PIVOT_Y = "transformPivotY";
-    field public static final String PROGRESS = "progress";
-    field public static final String ROTATION = "rotation";
-    field public static final String ROTATION_X = "rotationX";
-    field public static final String ROTATION_Y = "rotationY";
-    field public static final String SCALE_X = "scaleX";
-    field public static final String SCALE_Y = "scaleY";
-    field public static final String TRANSITIONEASING = "transitionEasing";
-    field public static final String TRANSITION_PATH_ROTATE = "transitionPathRotate";
-    field public static final String TRANSLATION_X = "translationX";
-    field public static final String TRANSLATION_Y = "translationY";
-    field public static final String TRANSLATION_Z = "translationZ";
-    field public static int UNSET;
-    field public static final String VISIBILITY = "visibility";
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_PHASE = "wavePhase";
-    field public static final String WAVE_VARIES_BY = "waveVariesBy";
-    field protected int mType;
-  }
-
-  public class KeyAttributes extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyAttributes();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final int KEY_TYPE = 1; // 0x1
-  }
-
-  public class KeyCycle extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyCycle();
-    method public void addCycleValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewOscillator!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public float getValue(String!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final int KEY_TYPE = 4; // 0x4
-    field public static final int SHAPE_BOUNCE = 6; // 0x6
-    field public static final int SHAPE_COS_WAVE = 5; // 0x5
-    field public static final int SHAPE_REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SHAPE_SAW_WAVE = 3; // 0x3
-    field public static final int SHAPE_SIN_WAVE = 0; // 0x0
-    field public static final int SHAPE_SQUARE_WAVE = 1; // 0x1
-    field public static final int SHAPE_TRIANGLE_WAVE = 2; // 0x2
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_PHASE = "wavePhase";
-    field public static final String WAVE_SHAPE = "waveShape";
-  }
-
-  public class KeyFrames {
-    ctor public KeyFrames();
-    ctor public KeyFrames(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public void addAllFrames(androidx.constraintlayout.motion.widget.MotionController!);
-    method public void addFrames(androidx.constraintlayout.motion.widget.MotionController!);
-    method public void addKey(androidx.constraintlayout.motion.widget.Key!);
-    method public java.util.ArrayList<androidx.constraintlayout.motion.widget.Key!>! getKeyFramesForView(int);
-    method public java.util.Set<java.lang.Integer!>! getKeys();
-    field public static final int UNSET = -1; // 0xffffffff
-  }
-
-  public class KeyPosition extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyPosition();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public boolean intersects(int, int, android.graphics.RectF!, android.graphics.RectF!, float, float);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void positionAttributes(android.view.View!, android.graphics.RectF!, android.graphics.RectF!, float, float, String![]!, float[]!);
-    method public void setType(int);
-    method public void setValue(String!, Object!);
-    field public static final String DRAWPATH = "drawPath";
-    field public static final String PERCENT_HEIGHT = "percentHeight";
-    field public static final String PERCENT_WIDTH = "percentWidth";
-    field public static final String PERCENT_X = "percentX";
-    field public static final String PERCENT_Y = "percentY";
-    field public static final String SIZE_PERCENT = "sizePercent";
-    field public static final String TRANSITION_EASING = "transitionEasing";
-    field public static final int TYPE_AXIS = 3; // 0x3
-    field public static final int TYPE_CARTESIAN = 0; // 0x0
-    field public static final int TYPE_PATH = 1; // 0x1
-    field public static final int TYPE_SCREEN = 2; // 0x2
-  }
-
-  public class KeyTimeCycle extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyTimeCycle();
-    method public void addTimeValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewTimeCycle!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final int KEY_TYPE = 3; // 0x3
-    field public static final int SHAPE_BOUNCE = 6; // 0x6
-    field public static final int SHAPE_COS_WAVE = 5; // 0x5
-    field public static final int SHAPE_REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SHAPE_SAW_WAVE = 3; // 0x3
-    field public static final int SHAPE_SIN_WAVE = 0; // 0x0
-    field public static final int SHAPE_SQUARE_WAVE = 1; // 0x1
-    field public static final int SHAPE_TRIANGLE_WAVE = 2; // 0x2
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_SHAPE = "waveShape";
-  }
-
-  public class KeyTrigger extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyTrigger();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void conditionallyFire(float, android.view.View!);
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final String CROSS = "CROSS";
-    field public static final int KEY_TYPE = 5; // 0x5
-    field public static final String NEGATIVE_CROSS = "negativeCross";
-    field public static final String POSITIVE_CROSS = "positiveCross";
-    field public static final String POST_LAYOUT = "postLayout";
-    field public static final String TRIGGER_COLLISION_ID = "triggerCollisionId";
-    field public static final String TRIGGER_COLLISION_VIEW = "triggerCollisionView";
-    field public static final String TRIGGER_ID = "triggerID";
-    field public static final String TRIGGER_RECEIVER = "triggerReceiver";
-    field public static final String TRIGGER_SLACK = "triggerSlack";
-    field public static final String VIEW_TRANSITION_ON_CROSS = "viewTransitionOnCross";
-    field public static final String VIEW_TRANSITION_ON_NEGATIVE_CROSS = "viewTransitionOnNegativeCross";
-    field public static final String VIEW_TRANSITION_ON_POSITIVE_CROSS = "viewTransitionOnPositiveCross";
-  }
-
-  public class MotionController {
-    method public void addKey(androidx.constraintlayout.motion.widget.Key!);
-    method public int getAnimateRelativeTo();
-    method public void getCenter(double, float[]!, float[]!);
-    method public float getCenterX();
-    method public float getCenterY();
-    method public int getDrawPath();
-    method public float getFinalHeight();
-    method public float getFinalWidth();
-    method public float getFinalX();
-    method public float getFinalY();
-    method public int getKeyFrameInfo(int, int[]!);
-    method public int getKeyFramePositions(int[]!, float[]!);
-    method public float getStartHeight();
-    method public float getStartWidth();
-    method public float getStartX();
-    method public float getStartY();
-    method public int getTransformPivotTarget();
-    method public android.view.View! getView();
-    method public void remeasure();
-    method public void setDrawPath(int);
-    method public void setPathMotionArc(int);
-    method public void setStartState(androidx.constraintlayout.motion.utils.ViewState!, android.view.View!, int, int, int);
-    method public void setTransformPivotTarget(int);
-    method public void setView(android.view.View!);
-    method public void setup(int, int, float, long);
-    method public void setupRelative(androidx.constraintlayout.motion.widget.MotionController!);
-    field public static final int DRAW_PATH_AS_CONFIGURED = 4; // 0x4
-    field public static final int DRAW_PATH_BASIC = 1; // 0x1
-    field public static final int DRAW_PATH_CARTESIAN = 3; // 0x3
-    field public static final int DRAW_PATH_NONE = 0; // 0x0
-    field public static final int DRAW_PATH_RECTANGLE = 5; // 0x5
-    field public static final int DRAW_PATH_RELATIVE = 2; // 0x2
-    field public static final int DRAW_PATH_SCREEN = 6; // 0x6
-    field public static final int HORIZONTAL_PATH_X = 2; // 0x2
-    field public static final int HORIZONTAL_PATH_Y = 3; // 0x3
-    field public static final int PATH_PERCENT = 0; // 0x0
-    field public static final int PATH_PERPENDICULAR = 1; // 0x1
-    field public static final int ROTATION_LEFT = 2; // 0x2
-    field public static final int ROTATION_RIGHT = 1; // 0x1
-    field public static final int VERTICAL_PATH_X = 4; // 0x4
-    field public static final int VERTICAL_PATH_Y = 5; // 0x5
-  }
-
-  public class MotionHelper extends androidx.constraintlayout.widget.ConstraintHelper implements androidx.constraintlayout.motion.widget.MotionHelperInterface {
-    ctor public MotionHelper(android.content.Context!);
-    ctor public MotionHelper(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionHelper(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getProgress();
-    method public boolean isDecorator();
-    method public boolean isUseOnHide();
-    method public boolean isUsedOnShow();
-    method public void onFinishedMotionScene(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void onPostDraw(android.graphics.Canvas!);
-    method public void onPreDraw(android.graphics.Canvas!);
-    method public void onPreSetup(androidx.constraintlayout.motion.widget.MotionLayout!, java.util.HashMap<android.view.View!,androidx.constraintlayout.motion.widget.MotionController!>!);
-    method public void onTransitionChange(androidx.constraintlayout.motion.widget.MotionLayout!, int, int, float);
-    method public void onTransitionCompleted(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void onTransitionStarted(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public void onTransitionTrigger(androidx.constraintlayout.motion.widget.MotionLayout!, int, boolean, float);
-    method public void setProgress(android.view.View!, float);
-    method public void setProgress(float);
-    field protected android.view.View![]! views;
-  }
-
-  public interface MotionHelperInterface extends androidx.constraintlayout.motion.widget.Animatable androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener {
-    method public boolean isDecorator();
-    method public boolean isUseOnHide();
-    method public boolean isUsedOnShow();
-    method public void onFinishedMotionScene(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void onPostDraw(android.graphics.Canvas!);
-    method public void onPreDraw(android.graphics.Canvas!);
-    method public void onPreSetup(androidx.constraintlayout.motion.widget.MotionLayout!, java.util.HashMap<android.view.View!,androidx.constraintlayout.motion.widget.MotionController!>!);
-  }
-
-  public abstract class MotionInterpolator implements android.view.animation.Interpolator {
-    ctor public MotionInterpolator();
-    method public abstract float getVelocity();
-  }
-
-  public class MotionLayout extends androidx.constraintlayout.widget.ConstraintLayout implements androidx.core.view.NestedScrollingParent3 {
-    ctor public MotionLayout(android.content.Context);
-    ctor public MotionLayout(android.content.Context, android.util.AttributeSet?);
-    ctor public MotionLayout(android.content.Context, android.util.AttributeSet?, int);
-    method public void addTransitionListener(androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener!);
-    method public boolean applyViewTransition(int, androidx.constraintlayout.motion.widget.MotionController!);
-    method public androidx.constraintlayout.widget.ConstraintSet! cloneConstraintSet(int);
-    method public void enableTransition(int, boolean);
-    method public void enableViewTransition(int, boolean);
-    method protected void fireTransitionCompleted();
-    method public void fireTrigger(int, boolean, float);
-    method public androidx.constraintlayout.widget.ConstraintSet! getConstraintSet(int);
-    method @IdRes public int[]! getConstraintSetIds();
-    method public int getCurrentState();
-    method public java.util.ArrayList<androidx.constraintlayout.motion.widget.MotionScene.Transition!>! getDefinedTransitions();
-    method public androidx.constraintlayout.motion.widget.DesignTool! getDesignTool();
-    method public int getEndState();
-    method public int[]! getMatchingConstraintSetIds(java.lang.String!...!);
-    method protected long getNanoTime();
-    method public float getProgress();
-    method public androidx.constraintlayout.motion.widget.MotionScene! getScene();
-    method public int getStartState();
-    method public float getTargetPosition();
-    method public androidx.constraintlayout.motion.widget.MotionScene.Transition! getTransition(int);
-    method public android.os.Bundle! getTransitionState();
-    method public long getTransitionTimeMs();
-    method public float getVelocity();
-    method public void getViewVelocity(android.view.View!, float, float, float[]!, int);
-    method public boolean isDelayedApplicationOfInitialState();
-    method public boolean isInRotation();
-    method public boolean isInteractionEnabled();
-    method public boolean isViewTransitionEnabled(int);
-    method public void jumpToState(int);
-    method protected androidx.constraintlayout.motion.widget.MotionLayout.MotionTracker! obtainVelocityTracker();
-    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
-    method public void onNestedScroll(android.view.View, int, int, int, int, int);
-    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]!);
-    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
-    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
-    method public void onStopNestedScroll(android.view.View, int);
-    method @Deprecated public void rebuildMotion();
-    method public void rebuildScene();
-    method public boolean removeTransitionListener(androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener!);
-    method public void rotateTo(int, int);
-    method public void scheduleTransitionTo(int);
-    method public void setDebugMode(int);
-    method public void setDelayedApplicationOfInitialState(boolean);
-    method public void setInteractionEnabled(boolean);
-    method public void setInterpolatedProgress(float);
-    method public void setOnHide(float);
-    method public void setOnShow(float);
-    method public void setProgress(float);
-    method public void setProgress(float, float);
-    method public void setScene(androidx.constraintlayout.motion.widget.MotionScene!);
-    method protected void setTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public void setTransition(int);
-    method public void setTransition(int, int);
-    method public void setTransitionDuration(int);
-    method public void setTransitionListener(androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener!);
-    method public void setTransitionState(android.os.Bundle!);
-    method public void touchAnimateTo(int, float, float);
-    method public void touchSpringTo(float, float);
-    method public void transitionToEnd();
-    method public void transitionToEnd(Runnable!);
-    method public void transitionToStart();
-    method public void transitionToStart(Runnable!);
-    method public void transitionToState(int);
-    method public void transitionToState(int, int);
-    method public void transitionToState(int, int, int);
-    method public void transitionToState(int, int, int, int);
-    method public void updateState();
-    method public void updateState(int, androidx.constraintlayout.widget.ConstraintSet!);
-    method public void updateStateAnimate(int, androidx.constraintlayout.widget.ConstraintSet!, int);
-    method public void viewTransition(int, android.view.View!...!);
-    field public static final int DEBUG_SHOW_NONE = 0; // 0x0
-    field public static final int DEBUG_SHOW_PATH = 2; // 0x2
-    field public static final int DEBUG_SHOW_PROGRESS = 1; // 0x1
-    field public static boolean IS_IN_EDIT_MODE;
-    field public static final int TOUCH_UP_COMPLETE = 0; // 0x0
-    field public static final int TOUCH_UP_COMPLETE_TO_END = 2; // 0x2
-    field public static final int TOUCH_UP_COMPLETE_TO_START = 1; // 0x1
-    field public static final int TOUCH_UP_DECELERATE = 4; // 0x4
-    field public static final int TOUCH_UP_DECELERATE_AND_COMPLETE = 5; // 0x5
-    field public static final int TOUCH_UP_NEVER_TO_END = 7; // 0x7
-    field public static final int TOUCH_UP_NEVER_TO_START = 6; // 0x6
-    field public static final int TOUCH_UP_STOP = 3; // 0x3
-    field public static final int VELOCITY_LAYOUT = 1; // 0x1
-    field public static final int VELOCITY_POST_LAYOUT = 0; // 0x0
-    field public static final int VELOCITY_STATIC_LAYOUT = 3; // 0x3
-    field public static final int VELOCITY_STATIC_POST_LAYOUT = 2; // 0x2
-    field protected boolean mMeasureDuringTransition;
-  }
-
-  protected static interface MotionLayout.MotionTracker {
-    method public void addMovement(android.view.MotionEvent!);
-    method public void clear();
-    method public void computeCurrentVelocity(int);
-    method public void computeCurrentVelocity(int, float);
-    method public float getXVelocity();
-    method public float getXVelocity(int);
-    method public float getYVelocity();
-    method public float getYVelocity(int);
-    method public void recycle();
-  }
-
-  public static interface MotionLayout.TransitionListener {
-    method public void onTransitionChange(androidx.constraintlayout.motion.widget.MotionLayout!, int, int, float);
-    method public void onTransitionCompleted(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void onTransitionStarted(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public void onTransitionTrigger(androidx.constraintlayout.motion.widget.MotionLayout!, int, boolean, float);
-  }
-
-  public class MotionScene {
-    ctor public MotionScene(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void addOnClickListeners(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void addTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public boolean applyViewTransition(int, androidx.constraintlayout.motion.widget.MotionController!);
-    method public androidx.constraintlayout.motion.widget.MotionScene.Transition! bestTransitionFor(int, float, float, android.view.MotionEvent!);
-    method public void disableAutoTransition(boolean);
-    method public void enableViewTransition(int, boolean);
-    method public int gatPathMotionArc();
-    method public androidx.constraintlayout.widget.ConstraintSet! getConstraintSet(android.content.Context!, String!);
-    method public int[]! getConstraintSetIds();
-    method public java.util.ArrayList<androidx.constraintlayout.motion.widget.MotionScene.Transition!>! getDefinedTransitions();
-    method public int getDuration();
-    method public android.view.animation.Interpolator! getInterpolator();
-    method public void getKeyFrames(androidx.constraintlayout.motion.widget.MotionController!);
-    method public int[]! getMatchingStateLabels(java.lang.String!...!);
-    method public float getPathPercent(android.view.View!, int);
-    method public float getStaggered();
-    method public androidx.constraintlayout.motion.widget.MotionScene.Transition! getTransitionById(int);
-    method public java.util.List<androidx.constraintlayout.motion.widget.MotionScene.Transition!>! getTransitionsWithState(int);
-    method public boolean isViewTransitionEnabled(int);
-    method public int lookUpConstraintId(String!);
-    method public String! lookUpConstraintName(int);
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void removeTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public void setConstraintSet(int, androidx.constraintlayout.widget.ConstraintSet!);
-    method public void setDuration(int);
-    method public void setKeyframe(android.view.View!, int, String!, Object!);
-    method public void setRtl(boolean);
-    method public void setTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public static String! stripID(String!);
-    method public boolean validateLayout(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void viewTransition(int, android.view.View!...!);
-    field public static final int LAYOUT_CALL_MEASURE = 2; // 0x2
-    field public static final int LAYOUT_HONOR_REQUEST = 1; // 0x1
-    field public static final int LAYOUT_IGNORE_REQUEST = 0; // 0x0
-    field public static final int UNSET = -1; // 0xffffffff
-  }
-
-  public static class MotionScene.Transition {
-    ctor public MotionScene.Transition(int, androidx.constraintlayout.motion.widget.MotionScene!, int, int);
-    method public void addKeyFrame(androidx.constraintlayout.motion.widget.KeyFrames!);
-    method public void addOnClick(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public void addOnClick(int, int);
-    method public String! debugString(android.content.Context!);
-    method public int getAutoTransition();
-    method public int getDuration();
-    method public int getEndConstraintSetId();
-    method public int getId();
-    method public java.util.List<androidx.constraintlayout.motion.widget.KeyFrames!>! getKeyFrameList();
-    method public int getLayoutDuringTransition();
-    method public java.util.List<androidx.constraintlayout.motion.widget.MotionScene.Transition.TransitionOnClick!>! getOnClickList();
-    method public int getPathMotionArc();
-    method public float getStagger();
-    method public int getStartConstraintSetId();
-    method public androidx.constraintlayout.motion.widget.TouchResponse! getTouchResponse();
-    method public boolean isEnabled();
-    method public boolean isTransitionFlag(int);
-    method public void removeOnClick(int);
-    method public void setAutoTransition(int);
-    method public void setDuration(int);
-    method public void setEnabled(boolean);
-    method public void setInterpolatorInfo(int, String!, int);
-    method public void setLayoutDuringTransition(int);
-    method public void setOnSwipe(androidx.constraintlayout.motion.widget.OnSwipe!);
-    method public void setOnTouchUp(int);
-    method public void setPathMotionArc(int);
-    method public void setStagger(float);
-    method public void setTransitionFlag(int);
-    field public static final int AUTO_ANIMATE_TO_END = 4; // 0x4
-    field public static final int AUTO_ANIMATE_TO_START = 3; // 0x3
-    field public static final int AUTO_JUMP_TO_END = 2; // 0x2
-    field public static final int AUTO_JUMP_TO_START = 1; // 0x1
-    field public static final int AUTO_NONE = 0; // 0x0
-    field public static final int INTERPOLATE_ANTICIPATE = 6; // 0x6
-    field public static final int INTERPOLATE_BOUNCE = 4; // 0x4
-    field public static final int INTERPOLATE_EASE_IN = 1; // 0x1
-    field public static final int INTERPOLATE_EASE_IN_OUT = 0; // 0x0
-    field public static final int INTERPOLATE_EASE_OUT = 2; // 0x2
-    field public static final int INTERPOLATE_LINEAR = 3; // 0x3
-    field public static final int INTERPOLATE_OVERSHOOT = 5; // 0x5
-    field public static final int INTERPOLATE_REFERENCE_ID = -2; // 0xfffffffe
-    field public static final int INTERPOLATE_SPLINE_STRING = -1; // 0xffffffff
-  }
-
-  public static class MotionScene.Transition.TransitionOnClick implements android.view.View.OnClickListener {
-    ctor public MotionScene.Transition.TransitionOnClick(android.content.Context!, androidx.constraintlayout.motion.widget.MotionScene.Transition!, org.xmlpull.v1.XmlPullParser!);
-    ctor public MotionScene.Transition.TransitionOnClick(androidx.constraintlayout.motion.widget.MotionScene.Transition!, int, int);
-    method public void addOnClickListeners(androidx.constraintlayout.motion.widget.MotionLayout!, int, androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public void onClick(android.view.View!);
-    method public void removeOnClickListeners(androidx.constraintlayout.motion.widget.MotionLayout!);
-    field public static final int ANIM_TOGGLE = 17; // 0x11
-    field public static final int ANIM_TO_END = 1; // 0x1
-    field public static final int ANIM_TO_START = 16; // 0x10
-    field public static final int JUMP_TO_END = 256; // 0x100
-    field public static final int JUMP_TO_START = 4096; // 0x1000
-  }
-
-  public class OnSwipe {
-    ctor public OnSwipe();
-    method public int getAutoCompleteMode();
-    method public int getDragDirection();
-    method public float getDragScale();
-    method public float getDragThreshold();
-    method public int getLimitBoundsTo();
-    method public float getMaxAcceleration();
-    method public float getMaxVelocity();
-    method public boolean getMoveWhenScrollAtTop();
-    method public int getNestedScrollFlags();
-    method public int getOnTouchUp();
-    method public int getRotationCenterId();
-    method public int getSpringBoundary();
-    method public float getSpringDamping();
-    method public float getSpringMass();
-    method public float getSpringStiffness();
-    method public float getSpringStopThreshold();
-    method public int getTouchAnchorId();
-    method public int getTouchAnchorSide();
-    method public int getTouchRegionId();
-    method public void setAutoCompleteMode(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setDragDirection(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setDragScale(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setDragThreshold(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setLimitBoundsTo(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setMaxAcceleration(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setMaxVelocity(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setMoveWhenScrollAtTop(boolean);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setNestedScrollFlags(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setOnTouchUp(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setRotateCenter(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringBoundary(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringDamping(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringMass(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringStiffness(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringStopThreshold(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setTouchAnchorId(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setTouchAnchorSide(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setTouchRegionId(int);
-    field public static final int COMPLETE_MODE_CONTINUOUS_VELOCITY = 0; // 0x0
-    field public static final int COMPLETE_MODE_SPRING = 1; // 0x1
-    field public static final int DRAG_ANTICLOCKWISE = 7; // 0x7
-    field public static final int DRAG_CLOCKWISE = 6; // 0x6
-    field public static final int DRAG_DOWN = 1; // 0x1
-    field public static final int DRAG_END = 5; // 0x5
-    field public static final int DRAG_LEFT = 2; // 0x2
-    field public static final int DRAG_RIGHT = 3; // 0x3
-    field public static final int DRAG_START = 4; // 0x4
-    field public static final int DRAG_UP = 0; // 0x0
-    field public static final int FLAG_DISABLE_POST_SCROLL = 1; // 0x1
-    field public static final int FLAG_DISABLE_SCROLL = 2; // 0x2
-    field public static final int ON_UP_AUTOCOMPLETE = 0; // 0x0
-    field public static final int ON_UP_AUTOCOMPLETE_TO_END = 2; // 0x2
-    field public static final int ON_UP_AUTOCOMPLETE_TO_START = 1; // 0x1
-    field public static final int ON_UP_DECELERATE = 4; // 0x4
-    field public static final int ON_UP_DECELERATE_AND_COMPLETE = 5; // 0x5
-    field public static final int ON_UP_NEVER_TO_END = 7; // 0x7
-    field public static final int ON_UP_NEVER_TO_START = 6; // 0x6
-    field public static final int ON_UP_STOP = 3; // 0x3
-    field public static final int SIDE_BOTTOM = 3; // 0x3
-    field public static final int SIDE_END = 6; // 0x6
-    field public static final int SIDE_LEFT = 1; // 0x1
-    field public static final int SIDE_MIDDLE = 4; // 0x4
-    field public static final int SIDE_RIGHT = 2; // 0x2
-    field public static final int SIDE_START = 5; // 0x5
-    field public static final int SIDE_TOP = 0; // 0x0
-    field public static final int SPRING_BOUNDARY_BOUNCEBOTH = 3; // 0x3
-    field public static final int SPRING_BOUNDARY_BOUNCEEND = 2; // 0x2
-    field public static final int SPRING_BOUNDARY_BOUNCESTART = 1; // 0x1
-    field public static final int SPRING_BOUNDARY_OVERSHOOT = 0; // 0x0
-  }
-
-  public abstract class TransitionAdapter implements androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener {
-    ctor public TransitionAdapter();
-    method public void onTransitionChange(androidx.constraintlayout.motion.widget.MotionLayout!, int, int, float);
-    method public void onTransitionCompleted(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void onTransitionStarted(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public void onTransitionTrigger(androidx.constraintlayout.motion.widget.MotionLayout!, int, boolean, float);
-  }
-
-  public class TransitionBuilder {
-    ctor public TransitionBuilder();
-    method public static androidx.constraintlayout.motion.widget.MotionScene.Transition! buildTransition(androidx.constraintlayout.motion.widget.MotionScene!, int, int, androidx.constraintlayout.widget.ConstraintSet!, int, androidx.constraintlayout.widget.ConstraintSet!);
-    method public static void validate(androidx.constraintlayout.motion.widget.MotionLayout!);
-  }
-
-  public class ViewTransition {
-    method public int getSharedValue();
-    method public int getSharedValueCurrent();
-    method public int getSharedValueID();
-    method public int getStateTransition();
-    method public void setSharedValue(int);
-    method public void setSharedValueCurrent(int);
-    method public void setSharedValueID(int);
-    method public void setStateTransition(int);
-    field public static final String CONSTRAINT_OVERRIDE = "ConstraintOverride";
-    field public static final String CUSTOM_ATTRIBUTE = "CustomAttribute";
-    field public static final String CUSTOM_METHOD = "CustomMethod";
-    field public static final String KEY_FRAME_SET_TAG = "KeyFrameSet";
-    field public static final int ONSTATE_ACTION_DOWN = 1; // 0x1
-    field public static final int ONSTATE_ACTION_DOWN_UP = 3; // 0x3
-    field public static final int ONSTATE_ACTION_UP = 2; // 0x2
-    field public static final int ONSTATE_SHARED_VALUE_SET = 4; // 0x4
-    field public static final int ONSTATE_SHARED_VALUE_UNSET = 5; // 0x5
-    field public static final String VIEW_TRANSITION_TAG = "ViewTransition";
-  }
-
-  public class ViewTransitionController {
-    ctor public ViewTransitionController(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void add(androidx.constraintlayout.motion.widget.ViewTransition!);
-  }
-
-}
-
-package androidx.constraintlayout.utils.widget {
-
-  public class ImageFilterButton extends androidx.appcompat.widget.AppCompatImageButton {
-    ctor public ImageFilterButton(android.content.Context!);
-    ctor public ImageFilterButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public ImageFilterButton(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getContrast();
-    method public float getCrossfade();
-    method public float getImagePanX();
-    method public float getImagePanY();
-    method public float getImageRotate();
-    method public float getImageZoom();
-    method public float getRound();
-    method public float getRoundPercent();
-    method public float getSaturation();
-    method public float getWarmth();
-    method public void setAltImageResource(int);
-    method public void setBrightness(float);
-    method public void setContrast(float);
-    method public void setCrossfade(float);
-    method public void setImagePanX(float);
-    method public void setImagePanY(float);
-    method public void setImageRotate(float);
-    method public void setImageZoom(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-    method public void setSaturation(float);
-    method public void setWarmth(float);
-  }
-
-  public class ImageFilterView extends androidx.appcompat.widget.AppCompatImageView {
-    ctor public ImageFilterView(android.content.Context!);
-    ctor public ImageFilterView(android.content.Context!, android.util.AttributeSet!);
-    ctor public ImageFilterView(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getBrightness();
-    method public float getContrast();
-    method public float getCrossfade();
-    method public float getImagePanX();
-    method public float getImagePanY();
-    method public float getImageRotate();
-    method public float getImageZoom();
-    method public float getRound();
-    method public float getRoundPercent();
-    method public float getSaturation();
-    method public float getWarmth();
-    method public void setAltImageDrawable(android.graphics.drawable.Drawable!);
-    method public void setAltImageResource(int);
-    method public void setBrightness(float);
-    method public void setContrast(float);
-    method public void setCrossfade(float);
-    method public void setImagePanX(float);
-    method public void setImagePanY(float);
-    method public void setImageRotate(float);
-    method public void setImageZoom(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-    method public void setSaturation(float);
-    method public void setWarmth(float);
-  }
-
-  public class MockView extends android.view.View {
-    ctor public MockView(android.content.Context!);
-    ctor public MockView(android.content.Context!, android.util.AttributeSet!);
-    ctor public MockView(android.content.Context!, android.util.AttributeSet!, int);
-    method public void onDraw(android.graphics.Canvas);
-    field protected String! mText;
-  }
-
-  public class MotionButton extends androidx.appcompat.widget.AppCompatButton {
-    ctor public MotionButton(android.content.Context!);
-    ctor public MotionButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionButton(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getRound();
-    method public float getRoundPercent();
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-  }
-
-  public class MotionLabel extends android.view.View implements androidx.constraintlayout.motion.widget.FloatLayout {
-    ctor public MotionLabel(android.content.Context!);
-    ctor public MotionLabel(android.content.Context!, android.util.AttributeSet?);
-    ctor public MotionLabel(android.content.Context!, android.util.AttributeSet?, int);
-    method public float getRound();
-    method public float getRoundPercent();
-    method public float getScaleFromTextSize();
-    method public float getTextBackgroundPanX();
-    method public float getTextBackgroundPanY();
-    method public float getTextBackgroundRotate();
-    method public float getTextBackgroundZoom();
-    method public int getTextOutlineColor();
-    method public float getTextPanX();
-    method public float getTextPanY();
-    method public float getTextureHeight();
-    method public float getTextureWidth();
-    method public android.graphics.Typeface! getTypeface();
-    method public void layout(float, float, float, float);
-    method public void setGravity(int);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-    method public void setScaleFromTextSize(float);
-    method public void setText(CharSequence!);
-    method public void setTextBackgroundPanX(float);
-    method public void setTextBackgroundPanY(float);
-    method public void setTextBackgroundRotate(float);
-    method public void setTextBackgroundZoom(float);
-    method public void setTextFillColor(int);
-    method public void setTextOutlineColor(int);
-    method public void setTextOutlineThickness(float);
-    method public void setTextPanX(float);
-    method public void setTextPanY(float);
-    method public void setTextSize(float);
-    method public void setTextureHeight(float);
-    method public void setTextureWidth(float);
-    method public void setTypeface(android.graphics.Typeface!);
-  }
-
-  public class MotionTelltales extends androidx.constraintlayout.utils.widget.MockView {
-    ctor public MotionTelltales(android.content.Context!);
-    ctor public MotionTelltales(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionTelltales(android.content.Context!, android.util.AttributeSet!, int);
-    method public void setText(CharSequence!);
-  }
-
-}
-
-package androidx.constraintlayout.widget {
-
-  public class Barrier extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public Barrier(android.content.Context!);
-    ctor public Barrier(android.content.Context!, android.util.AttributeSet!);
-    ctor public Barrier(android.content.Context!, android.util.AttributeSet!, int);
-    method @Deprecated public boolean allowsGoneWidget();
-    method public boolean getAllowsGoneWidget();
-    method public int getMargin();
-    method public int getType();
-    method public void setAllowsGoneWidget(boolean);
-    method public void setDpMargin(int);
-    method public void setMargin(int);
-    method public void setType(int);
-    field public static final int BOTTOM = 3; // 0x3
-    field public static final int END = 6; // 0x6
-    field public static final int LEFT = 0; // 0x0
-    field public static final int RIGHT = 1; // 0x1
-    field public static final int START = 5; // 0x5
-    field public static final int TOP = 2; // 0x2
-  }
-
-  public class ConstraintAttribute {
-    ctor public ConstraintAttribute(androidx.constraintlayout.widget.ConstraintAttribute!, Object!);
-    ctor public ConstraintAttribute(String!, androidx.constraintlayout.widget.ConstraintAttribute.AttributeType!);
-    ctor public ConstraintAttribute(String!, androidx.constraintlayout.widget.ConstraintAttribute.AttributeType!, Object!, boolean);
-    method public void applyCustom(android.view.View!);
-    method public boolean diff(androidx.constraintlayout.widget.ConstraintAttribute!);
-    method public static java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>! extractAttributes(java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>!, android.view.View!);
-    method public int getColorValue();
-    method public float getFloatValue();
-    method public int getIntegerValue();
-    method public String! getName();
-    method public String! getStringValue();
-    method public androidx.constraintlayout.widget.ConstraintAttribute.AttributeType! getType();
-    method public float getValueToInterpolate();
-    method public void getValuesToInterpolate(float[]!);
-    method public boolean isBooleanValue();
-    method public boolean isContinuous();
-    method public boolean isMethod();
-    method public int numberOfInterpolatedValues();
-    method public static void parse(android.content.Context!, org.xmlpull.v1.XmlPullParser!, java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public static void setAttributes(android.view.View!, java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public void setColorValue(int);
-    method public void setFloatValue(float);
-    method public void setIntValue(int);
-    method public void setStringValue(String!);
-    method public void setValue(float[]!);
-    method public void setValue(Object!);
-  }
-
-  public enum ConstraintAttribute.AttributeType {
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType BOOLEAN_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType COLOR_DRAWABLE_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType COLOR_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType DIMENSION_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType FLOAT_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType INT_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType REFERENCE_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType STRING_TYPE;
-  }
-
-  public abstract class ConstraintHelper extends android.view.View {
-    ctor public ConstraintHelper(android.content.Context!);
-    ctor public ConstraintHelper(android.content.Context!, android.util.AttributeSet!);
-    ctor public ConstraintHelper(android.content.Context!, android.util.AttributeSet!, int);
-    method public void addView(android.view.View!);
-    method public void applyHelperParams();
-    method protected void applyLayoutFeatures();
-    method protected void applyLayoutFeatures(androidx.constraintlayout.widget.ConstraintLayout!);
-    method protected void applyLayoutFeaturesInConstraintSet(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public boolean containsId(int);
-    method public int[]! getReferencedIds();
-    method protected android.view.View![]! getViews(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public int indexFromId(int);
-    method protected void init(android.util.AttributeSet!);
-    method public static boolean isChildOfHelper(android.view.View!);
-    method public void loadParameters(androidx.constraintlayout.widget.ConstraintSet.Constraint!, androidx.constraintlayout.core.widgets.HelperWidget!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void onDraw(android.graphics.Canvas);
-    method public int removeView(android.view.View!);
-    method public void resolveRtl(androidx.constraintlayout.core.widgets.ConstraintWidget!, boolean);
-    method protected void setIds(String!);
-    method protected void setReferenceTags(String!);
-    method public void setReferencedIds(int[]!);
-    method public void updatePostConstraints(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePostLayout(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePostMeasure(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePreDraw(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePreLayout(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.widgets.Helper!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void updatePreLayout(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void validateParams();
-    field protected static final String CHILD_TAG = "CONSTRAINT_LAYOUT_HELPER_CHILD";
-    field protected int mCount;
-    field protected androidx.constraintlayout.core.widgets.Helper! mHelperWidget;
-    field protected int[]! mIds;
-    field protected java.util.HashMap<java.lang.Integer!,java.lang.String!>! mMap;
-    field protected String! mReferenceIds;
-    field protected String! mReferenceTags;
-    field protected boolean mUseViewMeasure;
-    field protected android.content.Context! myContext;
-  }
-
-  public class ConstraintLayout extends android.view.ViewGroup {
-    ctor public ConstraintLayout(android.content.Context);
-    ctor public ConstraintLayout(android.content.Context, android.util.AttributeSet?);
-    ctor public ConstraintLayout(android.content.Context, android.util.AttributeSet?, int);
-    ctor public ConstraintLayout(android.content.Context, android.util.AttributeSet?, int, int);
-    method public void addValueModifier(androidx.constraintlayout.widget.ConstraintLayout.ValueModifier!);
-    method protected void applyConstraintsFromLayoutParams(boolean, android.view.View!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method protected boolean dynamicUpdateConstraints(int, int);
-    method public void fillMetrics(androidx.constraintlayout.core.Metrics!);
-    method protected androidx.constraintlayout.widget.ConstraintLayout.LayoutParams! generateDefaultLayoutParams();
-    method public androidx.constraintlayout.widget.ConstraintLayout.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
-    method public Object! getDesignInformation(int, Object!);
-    method public int getMaxHeight();
-    method public int getMaxWidth();
-    method public int getMinHeight();
-    method public int getMinWidth();
-    method public int getOptimizationLevel();
-    method public String! getSceneString();
-    method public static androidx.constraintlayout.widget.SharedValues! getSharedValues();
-    method public android.view.View! getViewById(int);
-    method public final androidx.constraintlayout.core.widgets.ConstraintWidget! getViewWidget(android.view.View!);
-    method protected boolean isRtl();
-    method public void loadLayoutDescription(int);
-    method protected void parseLayoutDescription(int);
-    method protected void resolveMeasuredDimension(int, int, int, int, boolean, boolean);
-    method protected void resolveSystem(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int, int, int);
-    method public void setConstraintSet(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void setDesignInformation(int, Object!, Object!);
-    method public void setMaxHeight(int);
-    method public void setMaxWidth(int);
-    method public void setMinHeight(int);
-    method public void setMinWidth(int);
-    method public void setOnConstraintsChanged(androidx.constraintlayout.widget.ConstraintsChangedListener!);
-    method public void setOptimizationLevel(int);
-    method protected void setSelfDimensionBehaviour(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int, int, int, int);
-    method public void setState(int, int, int);
-    field public static final int DESIGN_INFO_ID = 0; // 0x0
-    field public static final String VERSION = "ConstraintLayout-2.2.0-alpha04";
-    field protected androidx.constraintlayout.widget.ConstraintLayoutStates! mConstraintLayoutSpec;
-    field protected boolean mDirtyHierarchy;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidgetContainer! mLayoutWidget;
-  }
-
-  public static class ConstraintLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public ConstraintLayout.LayoutParams(android.content.Context!, android.util.AttributeSet!);
-    ctor public ConstraintLayout.LayoutParams(android.view.ViewGroup.LayoutParams!);
-    ctor public ConstraintLayout.LayoutParams(int, int);
-    method public String! getConstraintTag();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public void reset();
-    method public void setWidgetDebugName(String!);
-    method public void validate();
-    field public static final int BASELINE = 5; // 0x5
-    field public static final int BOTTOM = 4; // 0x4
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static final int CIRCLE = 8; // 0x8
-    field public static final int END = 7; // 0x7
-    field public static final int GONE_UNSET = -2147483648; // 0x80000000
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int LEFT = 1; // 0x1
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_PERCENT = 2; // 0x2
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int RIGHT = 2; // 0x2
-    field public static final int START = 6; // 0x6
-    field public static final int TOP = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int WRAP_BEHAVIOR_HORIZONTAL_ONLY = 1; // 0x1
-    field public static final int WRAP_BEHAVIOR_INCLUDED = 0; // 0x0
-    field public static final int WRAP_BEHAVIOR_SKIPPED = 3; // 0x3
-    field public static final int WRAP_BEHAVIOR_VERTICAL_ONLY = 2; // 0x2
-    field public int baselineMargin;
-    field public int baselineToBaseline;
-    field public int baselineToBottom;
-    field public int baselineToTop;
-    field public int bottomToBottom;
-    field public int bottomToTop;
-    field public float circleAngle;
-    field public int circleConstraint;
-    field public int circleRadius;
-    field public boolean constrainedHeight;
-    field public boolean constrainedWidth;
-    field public String! constraintTag;
-    field public String! dimensionRatio;
-    field public int editorAbsoluteX;
-    field public int editorAbsoluteY;
-    field public int endToEnd;
-    field public int endToStart;
-    field public int goneBaselineMargin;
-    field public int goneBottomMargin;
-    field public int goneEndMargin;
-    field public int goneLeftMargin;
-    field public int goneRightMargin;
-    field public int goneStartMargin;
-    field public int goneTopMargin;
-    field public int guideBegin;
-    field public int guideEnd;
-    field public float guidePercent;
-    field public boolean guidelineUseRtl;
-    field public boolean helped;
-    field public float horizontalBias;
-    field public int horizontalChainStyle;
-    field public float horizontalWeight;
-    field public int leftToLeft;
-    field public int leftToRight;
-    field public int matchConstraintDefaultHeight;
-    field public int matchConstraintDefaultWidth;
-    field public int matchConstraintMaxHeight;
-    field public int matchConstraintMaxWidth;
-    field public int matchConstraintMinHeight;
-    field public int matchConstraintMinWidth;
-    field public float matchConstraintPercentHeight;
-    field public float matchConstraintPercentWidth;
-    field public int orientation;
-    field public int rightToLeft;
-    field public int rightToRight;
-    field public int startToEnd;
-    field public int startToStart;
-    field public int topToBottom;
-    field public int topToTop;
-    field public float verticalBias;
-    field public int verticalChainStyle;
-    field public float verticalWeight;
-    field public int wrapBehaviorInParent;
-  }
-
-  public static interface ConstraintLayout.ValueModifier {
-    method public boolean update(int, int, int, android.view.View!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!);
-  }
-
-  public class ConstraintLayoutStates {
-    method public boolean needsToChange(int, float, float);
-    method public void setOnConstraintsChanged(androidx.constraintlayout.widget.ConstraintsChangedListener!);
-    method public void updateConstraints(int, float, float);
-    field public static final String TAG = "ConstraintLayoutStates";
-  }
-
-  public class ConstraintLayoutStatistics {
-    ctor public ConstraintLayoutStatistics(androidx.constraintlayout.widget.ConstraintLayout!);
-    ctor public ConstraintLayoutStatistics(androidx.constraintlayout.widget.ConstraintLayoutStatistics!);
-    method public void attach(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public androidx.constraintlayout.widget.ConstraintLayoutStatistics! clone();
-    method public void detach();
-    method public long getValue(int);
-    method public void logSummary(String!);
-    method public void logSummary(String!, androidx.constraintlayout.widget.ConstraintLayoutStatistics!);
-    method public void reset();
-    field public static final int DURATION_OF_CHILD_MEASURES = 5; // 0x5
-    field public static final int DURATION_OF_LAYOUT = 7; // 0x7
-    field public static final int DURATION_OF_MEASURES = 6; // 0x6
-    field public static final int NUMBER_OF_CHILD_MEASURES = 4; // 0x4
-    field public static final int NUMBER_OF_CHILD_VIEWS = 3; // 0x3
-    field public static final int NUMBER_OF_EQUATIONS = 9; // 0x9
-    field public static final int NUMBER_OF_LAYOUTS = 1; // 0x1
-    field public static final int NUMBER_OF_ON_MEASURES = 2; // 0x2
-    field public static final int NUMBER_OF_SIMPLE_EQUATIONS = 10; // 0xa
-    field public static final int NUMBER_OF_VARIABLES = 8; // 0x8
-  }
-
-  public class ConstraintProperties {
-    ctor public ConstraintProperties(android.view.View!);
-    method public androidx.constraintlayout.widget.ConstraintProperties! addToHorizontalChain(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! addToHorizontalChainRTL(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! addToVerticalChain(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! alpha(float);
-    method public void apply();
-    method public androidx.constraintlayout.widget.ConstraintProperties! center(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontally(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontally(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontallyRtl(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontallyRtl(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerVertically(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerVertically(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! connect(int, int, int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainDefaultHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainDefaultWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMaxHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMaxWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMinHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMinWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! dimensionRatio(String!);
-    method public androidx.constraintlayout.widget.ConstraintProperties! elevation(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! goneMargin(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! horizontalBias(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! horizontalChainStyle(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! horizontalWeight(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! margin(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! removeConstraints(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! removeFromHorizontalChain();
-    method public androidx.constraintlayout.widget.ConstraintProperties! removeFromVerticalChain();
-    method public androidx.constraintlayout.widget.ConstraintProperties! rotation(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! rotationX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! rotationY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! scaleX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! scaleY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! transformPivot(float, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! transformPivotX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! transformPivotY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translation(float, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translationX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translationY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translationZ(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! verticalBias(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! verticalChainStyle(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! verticalWeight(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! visibility(int);
-    field public static final int BASELINE = 5; // 0x5
-    field public static final int BOTTOM = 4; // 0x4
-    field public static final int END = 7; // 0x7
-    field public static final int LEFT = 1; // 0x1
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int RIGHT = 2; // 0x2
-    field public static final int START = 6; // 0x6
-    field public static final int TOP = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-  }
-
-  public class ConstraintSet {
-    ctor public ConstraintSet();
-    method public void addColorAttributes(java.lang.String!...!);
-    method public void addFloatAttributes(java.lang.String!...!);
-    method public void addIntAttributes(java.lang.String!...!);
-    method public void addStringAttributes(java.lang.String!...!);
-    method public void addToHorizontalChain(int, int, int);
-    method public void addToHorizontalChainRTL(int, int, int);
-    method public void addToVerticalChain(int, int, int);
-    method public void applyCustomAttributes(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void applyDeltaFrom(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void applyTo(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void applyToHelper(androidx.constraintlayout.widget.ConstraintHelper!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void applyToLayoutParams(int, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!);
-    method public void applyToWithoutCustom(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public static androidx.constraintlayout.widget.ConstraintSet.Constraint! buildDelta(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public void center(int, int, int, int, int, int, int, float);
-    method public void centerHorizontally(int, int);
-    method public void centerHorizontally(int, int, int, int, int, int, int, float);
-    method public void centerHorizontallyRtl(int, int);
-    method public void centerHorizontallyRtl(int, int, int, int, int, int, int, float);
-    method public void centerVertically(int, int);
-    method public void centerVertically(int, int, int, int, int, int, int, float);
-    method public void clear(int);
-    method public void clear(int, int);
-    method public void clone(android.content.Context!, int);
-    method public void clone(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void clone(androidx.constraintlayout.widget.Constraints!);
-    method public void clone(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void connect(int, int, int, int);
-    method public void connect(int, int, int, int, int);
-    method public void constrainCircle(int, int, int, float);
-    method public void constrainDefaultHeight(int, int);
-    method public void constrainDefaultWidth(int, int);
-    method public void constrainHeight(int, int);
-    method public void constrainMaxHeight(int, int);
-    method public void constrainMaxWidth(int, int);
-    method public void constrainMinHeight(int, int);
-    method public void constrainMinWidth(int, int);
-    method public void constrainPercentHeight(int, float);
-    method public void constrainPercentWidth(int, float);
-    method public void constrainWidth(int, int);
-    method public void constrainedHeight(int, boolean);
-    method public void constrainedWidth(int, boolean);
-    method public void create(int, int);
-    method public void createBarrier(int, int, int, int...!);
-    method public void createHorizontalChain(int, int, int, int, int[]!, float[]!, int);
-    method public void createHorizontalChainRtl(int, int, int, int, int[]!, float[]!, int);
-    method public void createVerticalChain(int, int, int, int, int[]!, float[]!, int);
-    method public void dump(androidx.constraintlayout.motion.widget.MotionScene!, int...!);
-    method public boolean getApplyElevation(int);
-    method public androidx.constraintlayout.widget.ConstraintSet.Constraint! getConstraint(int);
-    method public java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>! getCustomAttributeSet();
-    method public int getHeight(int);
-    method public int[]! getKnownIds();
-    method public androidx.constraintlayout.widget.ConstraintSet.Constraint! getParameters(int);
-    method public int[]! getReferencedIds(int);
-    method public String![]! getStateLabels();
-    method public int getVisibility(int);
-    method public int getVisibilityMode(int);
-    method public int getWidth(int);
-    method public boolean isForceId();
-    method public boolean isValidateOnParse();
-    method public void load(android.content.Context!, int);
-    method public void load(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public boolean matchesLabels(java.lang.String!...!);
-    method public void parseColorAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void parseFloatAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void parseIntAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void parseStringAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void readFallback(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void readFallback(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void removeAttribute(String!);
-    method public void removeFromHorizontalChain(int);
-    method public void removeFromVerticalChain(int);
-    method public void setAlpha(int, float);
-    method public void setApplyElevation(int, boolean);
-    method public void setBarrierType(int, int);
-    method public void setColorValue(int, String!, int);
-    method public void setDimensionRatio(int, String!);
-    method public void setEditorAbsoluteX(int, int);
-    method public void setEditorAbsoluteY(int, int);
-    method public void setElevation(int, float);
-    method public void setFloatValue(int, String!, float);
-    method public void setForceId(boolean);
-    method public void setGoneMargin(int, int, int);
-    method public void setGuidelineBegin(int, int);
-    method public void setGuidelineEnd(int, int);
-    method public void setGuidelinePercent(int, float);
-    method public void setHorizontalBias(int, float);
-    method public void setHorizontalChainStyle(int, int);
-    method public void setHorizontalWeight(int, float);
-    method public void setIntValue(int, String!, int);
-    method public void setLayoutWrapBehavior(int, int);
-    method public void setMargin(int, int, int);
-    method public void setReferencedIds(int, int...!);
-    method public void setRotation(int, float);
-    method public void setRotationX(int, float);
-    method public void setRotationY(int, float);
-    method public void setScaleX(int, float);
-    method public void setScaleY(int, float);
-    method public void setStateLabels(String!);
-    method public void setStateLabelsList(java.lang.String!...!);
-    method public void setStringValue(int, String!, String!);
-    method public void setTransformPivot(int, float, float);
-    method public void setTransformPivotX(int, float);
-    method public void setTransformPivotY(int, float);
-    method public void setTranslation(int, float, float);
-    method public void setTranslationX(int, float);
-    method public void setTranslationY(int, float);
-    method public void setTranslationZ(int, float);
-    method public void setValidateOnParse(boolean);
-    method public void setVerticalBias(int, float);
-    method public void setVerticalChainStyle(int, int);
-    method public void setVerticalWeight(int, float);
-    method public void setVisibility(int, int);
-    method public void setVisibilityMode(int, int);
-    method public void writeState(java.io.Writer!, androidx.constraintlayout.widget.ConstraintLayout!, int) throws java.io.IOException;
-    field public static final int BASELINE = 5; // 0x5
-    field public static final int BOTTOM = 4; // 0x4
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static final int CIRCLE_REFERENCE = 8; // 0x8
-    field public static final int END = 7; // 0x7
-    field public static final int GONE = 8; // 0x8
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int HORIZONTAL_GUIDELINE = 0; // 0x0
-    field public static final int INVISIBLE = 4; // 0x4
-    field public static final int LEFT = 1; // 0x1
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_PERCENT = 2; // 0x2
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int RIGHT = 2; // 0x2
-    field public static final int ROTATE_LEFT_OF_PORTRATE = 4; // 0x4
-    field public static final int ROTATE_NONE = 0; // 0x0
-    field public static final int ROTATE_PORTRATE_OF_LEFT = 2; // 0x2
-    field public static final int ROTATE_PORTRATE_OF_RIGHT = 1; // 0x1
-    field public static final int ROTATE_RIGHT_OF_PORTRATE = 3; // 0x3
-    field public static final int START = 6; // 0x6
-    field public static final int TOP = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int VERTICAL_GUIDELINE = 1; // 0x1
-    field public static final int VISIBILITY_MODE_IGNORE = 1; // 0x1
-    field public static final int VISIBILITY_MODE_NORMAL = 0; // 0x0
-    field public static final int VISIBLE = 0; // 0x0
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-    field public String! derivedState;
-    field public String! mIdString;
-    field public int mRotate;
-  }
-
-  public static class ConstraintSet.Constraint {
-    ctor public ConstraintSet.Constraint();
-    method public void applyDelta(androidx.constraintlayout.widget.ConstraintSet.Constraint!);
-    method public void applyTo(androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!);
-    method public androidx.constraintlayout.widget.ConstraintSet.Constraint! clone();
-    method public void printDelta(String!);
-    field public final androidx.constraintlayout.widget.ConstraintSet.Layout! layout;
-    field public java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>! mCustomConstraints;
-    field public final androidx.constraintlayout.widget.ConstraintSet.Motion! motion;
-    field public final androidx.constraintlayout.widget.ConstraintSet.PropertySet! propertySet;
-    field public final androidx.constraintlayout.widget.ConstraintSet.Transform! transform;
-  }
-
-  public static class ConstraintSet.Layout {
-    ctor public ConstraintSet.Layout();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.Layout!);
-    method public void dump(androidx.constraintlayout.motion.widget.MotionScene!, StringBuilder!);
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int UNSET_GONE_MARGIN = -2147483648; // 0x80000000
-    field public int baselineMargin;
-    field public int baselineToBaseline;
-    field public int baselineToBottom;
-    field public int baselineToTop;
-    field public int bottomMargin;
-    field public int bottomToBottom;
-    field public int bottomToTop;
-    field public float circleAngle;
-    field public int circleConstraint;
-    field public int circleRadius;
-    field public boolean constrainedHeight;
-    field public boolean constrainedWidth;
-    field public String! dimensionRatio;
-    field public int editorAbsoluteX;
-    field public int editorAbsoluteY;
-    field public int endMargin;
-    field public int endToEnd;
-    field public int endToStart;
-    field public int goneBaselineMargin;
-    field public int goneBottomMargin;
-    field public int goneEndMargin;
-    field public int goneLeftMargin;
-    field public int goneRightMargin;
-    field public int goneStartMargin;
-    field public int goneTopMargin;
-    field public int guideBegin;
-    field public int guideEnd;
-    field public float guidePercent;
-    field public boolean guidelineUseRtl;
-    field public int heightDefault;
-    field public int heightMax;
-    field public int heightMin;
-    field public float heightPercent;
-    field public float horizontalBias;
-    field public int horizontalChainStyle;
-    field public float horizontalWeight;
-    field public int leftMargin;
-    field public int leftToLeft;
-    field public int leftToRight;
-    field public boolean mApply;
-    field public boolean mBarrierAllowsGoneWidgets;
-    field public int mBarrierDirection;
-    field public int mBarrierMargin;
-    field public String! mConstraintTag;
-    field public int mHeight;
-    field public int mHelperType;
-    field public boolean mIsGuideline;
-    field public boolean mOverride;
-    field public String! mReferenceIdString;
-    field public int[]! mReferenceIds;
-    field public int mWidth;
-    field public int mWrapBehavior;
-    field public int orientation;
-    field public int rightMargin;
-    field public int rightToLeft;
-    field public int rightToRight;
-    field public int startMargin;
-    field public int startToEnd;
-    field public int startToStart;
-    field public int topMargin;
-    field public int topToBottom;
-    field public int topToTop;
-    field public float verticalBias;
-    field public int verticalChainStyle;
-    field public float verticalWeight;
-    field public int widthDefault;
-    field public int widthMax;
-    field public int widthMin;
-    field public float widthPercent;
-  }
-
-  public static class ConstraintSet.Motion {
-    ctor public ConstraintSet.Motion();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.Motion!);
-    field public int mAnimateCircleAngleTo;
-    field public int mAnimateRelativeTo;
-    field public boolean mApply;
-    field public int mDrawPath;
-    field public float mMotionStagger;
-    field public int mPathMotionArc;
-    field public float mPathRotate;
-    field public int mPolarRelativeTo;
-    field public int mQuantizeInterpolatorID;
-    field public String! mQuantizeInterpolatorString;
-    field public int mQuantizeInterpolatorType;
-    field public float mQuantizeMotionPhase;
-    field public int mQuantizeMotionSteps;
-    field public String! mTransitionEasing;
-  }
-
-  public static class ConstraintSet.PropertySet {
-    ctor public ConstraintSet.PropertySet();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.PropertySet!);
-    field public float alpha;
-    field public boolean mApply;
-    field public float mProgress;
-    field public int mVisibilityMode;
-    field public int visibility;
-  }
-
-  public static class ConstraintSet.Transform {
-    ctor public ConstraintSet.Transform();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.Transform!);
-    field public boolean applyElevation;
-    field public float elevation;
-    field public boolean mApply;
-    field public float rotation;
-    field public float rotationX;
-    field public float rotationY;
-    field public float scaleX;
-    field public float scaleY;
-    field public int transformPivotTarget;
-    field public float transformPivotX;
-    field public float transformPivotY;
-    field public float translationX;
-    field public float translationY;
-    field public float translationZ;
-  }
-
-  public class Constraints extends android.view.ViewGroup {
-    ctor public Constraints(android.content.Context!);
-    ctor public Constraints(android.content.Context!, android.util.AttributeSet!);
-    ctor public Constraints(android.content.Context!, android.util.AttributeSet!, int);
-    method protected androidx.constraintlayout.widget.Constraints.LayoutParams! generateDefaultLayoutParams();
-    method public androidx.constraintlayout.widget.Constraints.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
-    method public androidx.constraintlayout.widget.ConstraintSet! getConstraintSet();
-    field public static final String TAG = "Constraints";
-  }
-
-  public static class Constraints.LayoutParams extends androidx.constraintlayout.widget.ConstraintLayout.LayoutParams {
-    ctor public Constraints.LayoutParams(android.content.Context!, android.util.AttributeSet!);
-    ctor public Constraints.LayoutParams(androidx.constraintlayout.widget.Constraints.LayoutParams!);
-    ctor public Constraints.LayoutParams(int, int);
-    field public float alpha;
-    field public boolean applyElevation;
-    field public float elevation;
-    field public float rotation;
-    field public float rotationX;
-    field public float rotationY;
-    field public float scaleX;
-    field public float scaleY;
-    field public float transformPivotX;
-    field public float transformPivotY;
-    field public float translationX;
-    field public float translationY;
-    field public float translationZ;
-  }
-
-  public abstract class ConstraintsChangedListener {
-    ctor public ConstraintsChangedListener();
-    method public void postLayoutChange(int, int);
-    method public void preLayoutChange(int, int);
-  }
-
-  public class Group extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public Group(android.content.Context!);
-    ctor public Group(android.content.Context!, android.util.AttributeSet!);
-    ctor public Group(android.content.Context!, android.util.AttributeSet!, int);
-    method public void onAttachedToWindow();
-  }
-
-  public class Guideline extends android.view.View {
-    ctor public Guideline(android.content.Context!);
-    ctor public Guideline(android.content.Context!, android.util.AttributeSet!);
-    ctor public Guideline(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public Guideline(android.content.Context!, android.util.AttributeSet!, int, int);
-    method public void setFilterRedundantCalls(boolean);
-    method public void setGuidelineBegin(int);
-    method public void setGuidelineEnd(int);
-    method public void setGuidelinePercent(float);
-  }
-
-  public class Placeholder extends android.view.View {
-    ctor public Placeholder(android.content.Context!);
-    ctor public Placeholder(android.content.Context!, android.util.AttributeSet!);
-    ctor public Placeholder(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public Placeholder(android.content.Context!, android.util.AttributeSet!, int, int);
-    method public android.view.View! getContent();
-    method public int getEmptyVisibility();
-    method public void onDraw(android.graphics.Canvas);
-    method public void setContentId(int);
-    method public void setEmptyVisibility(int);
-    method public void updatePostMeasure(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePreLayout(androidx.constraintlayout.widget.ConstraintLayout!);
-  }
-
-  public class ReactiveGuide extends android.view.View implements androidx.constraintlayout.widget.SharedValues.SharedValuesListener {
-    ctor public ReactiveGuide(android.content.Context!);
-    ctor public ReactiveGuide(android.content.Context!, android.util.AttributeSet!);
-    ctor public ReactiveGuide(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public ReactiveGuide(android.content.Context!, android.util.AttributeSet!, int, int);
-    method public int getApplyToConstraintSetId();
-    method public int getAttributeId();
-    method public boolean isAnimatingChange();
-    method public void onNewValue(int, int, int);
-    method public void setAnimateChange(boolean);
-    method public void setApplyToConstraintSetId(int);
-    method public void setAttributeId(int);
-    method public void setGuidelineBegin(int);
-    method public void setGuidelineEnd(int);
-    method public void setGuidelinePercent(float);
-  }
-
-  public class SharedValues {
-    ctor public SharedValues();
-    method public void addListener(int, androidx.constraintlayout.widget.SharedValues.SharedValuesListener!);
-    method public void clearListeners();
-    method public void fireNewValue(int, int);
-    method public int getValue(int);
-    method public void removeListener(androidx.constraintlayout.widget.SharedValues.SharedValuesListener!);
-    method public void removeListener(int, androidx.constraintlayout.widget.SharedValues.SharedValuesListener!);
-    field public static final int UNSET = -1; // 0xffffffff
-  }
-
-  public static interface SharedValues.SharedValuesListener {
-    method public void onNewValue(int, int, int);
-  }
-
-  public class StateSet {
-    ctor public StateSet(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public int convertToConstraintSet(int, int, float, float);
-    method public boolean needsToChange(int, float, float);
-    method public void setOnConstraintsChanged(androidx.constraintlayout.widget.ConstraintsChangedListener!);
-    method public int stateGetConstraintID(int, int, int);
-    method public int updateConstraints(int, int, float, float);
-    field public static final String TAG = "ConstraintLayoutStates";
-  }
-
-  public abstract class VirtualLayout extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public VirtualLayout(android.content.Context!);
-    ctor public VirtualLayout(android.content.Context!, android.util.AttributeSet!);
-    ctor public VirtualLayout(android.content.Context!, android.util.AttributeSet!, int);
-    method public void onAttachedToWindow();
-    method public void onMeasure(androidx.constraintlayout.core.widgets.VirtualLayout!, int, int);
-  }
-
-}
-
diff --git a/constraintlayout/constraintlayout/api/res-2.2.0-beta01.txt b/constraintlayout/constraintlayout/api/res-2.2.0-beta01.txt
deleted file mode 100644
index e69de29..0000000
--- a/constraintlayout/constraintlayout/api/res-2.2.0-beta01.txt
+++ /dev/null
diff --git a/constraintlayout/constraintlayout/api/restricted_2.2.0-beta01.txt b/constraintlayout/constraintlayout/api/restricted_2.2.0-beta01.txt
deleted file mode 100644
index 2d187a8..0000000
--- a/constraintlayout/constraintlayout/api/restricted_2.2.0-beta01.txt
+++ /dev/null
@@ -1,1714 +0,0 @@
-// Signature format: 4.0
-package androidx.constraintlayout.helper.widget {
-
-  public class Carousel extends androidx.constraintlayout.motion.widget.MotionHelper {
-    ctor public Carousel(android.content.Context!);
-    ctor public Carousel(android.content.Context!, android.util.AttributeSet!);
-    ctor public Carousel(android.content.Context!, android.util.AttributeSet!, int);
-    method public int getCount();
-    method public int getCurrentIndex();
-    method public boolean isInfinite();
-    method public void jumpToIndex(int);
-    method public void refresh();
-    method public void setAdapter(androidx.constraintlayout.helper.widget.Carousel.Adapter!);
-    method public void setInfinite(boolean);
-    method public void transitionToIndex(int, int);
-    field public static final int TOUCH_UP_CARRY_ON = 2; // 0x2
-    field public static final int TOUCH_UP_IMMEDIATE_STOP = 1; // 0x1
-  }
-
-  public static interface Carousel.Adapter {
-    method public int count();
-    method public void onNewItem(int);
-    method public void populate(android.view.View!, int);
-  }
-
-  public class CircularFlow extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public CircularFlow(android.content.Context!);
-    ctor public CircularFlow(android.content.Context!, android.util.AttributeSet!);
-    ctor public CircularFlow(android.content.Context!, android.util.AttributeSet!, int);
-    method public void addViewToCircularFlow(android.view.View!, int, float);
-    method public float[]! getAngles();
-    method public int[]! getRadius();
-    method public boolean isUpdatable(android.view.View!);
-    method public void setDefaultAngle(float);
-    method public void setDefaultRadius(int);
-    method public void updateAngle(android.view.View!, float);
-    method public void updateRadius(android.view.View!, int);
-    method public void updateReference(android.view.View!, int, float);
-  }
-
-  public class Flow extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public Flow(android.content.Context!);
-    ctor public Flow(android.content.Context!, android.util.AttributeSet!);
-    ctor public Flow(android.content.Context!, android.util.AttributeSet!, int);
-    method public void setFirstHorizontalBias(float);
-    method public void setFirstHorizontalStyle(int);
-    method public void setFirstVerticalBias(float);
-    method public void setFirstVerticalStyle(int);
-    method public void setHorizontalAlign(int);
-    method public void setHorizontalBias(float);
-    method public void setHorizontalGap(int);
-    method public void setHorizontalStyle(int);
-    method public void setLastHorizontalBias(float);
-    method public void setLastHorizontalStyle(int);
-    method public void setLastVerticalBias(float);
-    method public void setLastVerticalStyle(int);
-    method public void setMaxElementsWrap(int);
-    method public void setOrientation(int);
-    method public void setPadding(int);
-    method public void setPaddingBottom(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(int);
-    method public void setPaddingTop(int);
-    method public void setVerticalAlign(int);
-    method public void setVerticalBias(float);
-    method public void setVerticalGap(int);
-    method public void setVerticalStyle(int);
-    method public void setWrapMode(int);
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int HORIZONTAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int HORIZONTAL_ALIGN_END = 1; // 0x1
-    field public static final int HORIZONTAL_ALIGN_START = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int VERTICAL_ALIGN_BASELINE = 3; // 0x3
-    field public static final int VERTICAL_ALIGN_BOTTOM = 1; // 0x1
-    field public static final int VERTICAL_ALIGN_CENTER = 2; // 0x2
-    field public static final int VERTICAL_ALIGN_TOP = 0; // 0x0
-    field public static final int WRAP_ALIGNED = 2; // 0x2
-    field public static final int WRAP_CHAIN = 1; // 0x1
-    field public static final int WRAP_NONE = 0; // 0x0
-  }
-
-  public class Grid extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public Grid(android.content.Context!);
-    ctor public Grid(android.content.Context!, android.util.AttributeSet!);
-    ctor public Grid(android.content.Context!, android.util.AttributeSet!, int);
-    method public String! getColumnWeights();
-    method public int getColumns();
-    method public float getHorizontalGaps();
-    method public int getOrientation();
-    method public String! getRowWeights();
-    method public int getRows();
-    method public String! getSkips();
-    method public String! getSpans();
-    method public float getVerticalGaps();
-    method public void setColumnWeights(String!);
-    method public void setColumns(int);
-    method public void setHorizontalGaps(float);
-    method public void setOrientation(int);
-    method public void setRowWeights(String!);
-    method public void setRows(int);
-    method public void setSkips(String!);
-    method public void setSpans(CharSequence!);
-    method public void setVerticalGaps(float);
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int VERTICAL = 1; // 0x1
-  }
-
-  public class Layer extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public Layer(android.content.Context!);
-    ctor public Layer(android.content.Context!, android.util.AttributeSet!);
-    ctor public Layer(android.content.Context!, android.util.AttributeSet!, int);
-    method protected void calcCenters();
-    field protected float mComputedCenterX;
-    field protected float mComputedCenterY;
-    field protected float mComputedMaxX;
-    field protected float mComputedMaxY;
-    field protected float mComputedMinX;
-    field protected float mComputedMinY;
-  }
-
-  public class MotionEffect extends androidx.constraintlayout.motion.widget.MotionHelper {
-    ctor public MotionEffect(android.content.Context!);
-    ctor public MotionEffect(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionEffect(android.content.Context!, android.util.AttributeSet!, int);
-    field public static final int AUTO = -1; // 0xffffffff
-    field public static final int EAST = 2; // 0x2
-    field public static final int NORTH = 0; // 0x0
-    field public static final int SOUTH = 1; // 0x1
-    field public static final String TAG = "FadeMove";
-    field public static final int WEST = 3; // 0x3
-  }
-
-  public class MotionPlaceholder extends androidx.constraintlayout.widget.VirtualLayout {
-    ctor public MotionPlaceholder(android.content.Context!);
-    ctor public MotionPlaceholder(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionPlaceholder(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public MotionPlaceholder(android.content.Context!, android.util.AttributeSet!, int, int);
-  }
-
-}
-
-package androidx.constraintlayout.motion.utils {
-
-  public class CustomSupport {
-    ctor public CustomSupport();
-    method public static void setInterpolatedValue(androidx.constraintlayout.widget.ConstraintAttribute!, android.view.View!, float[]!);
-  }
-
-  public class StopLogic extends androidx.constraintlayout.motion.widget.MotionInterpolator {
-    ctor public StopLogic();
-    method public void config(float, float, float, float, float, float);
-    method public String! debug(String!, float);
-    method public float getInterpolation(float);
-    method public float getVelocity();
-    method public float getVelocity(float);
-    method public boolean isStopped();
-    method public void springConfig(float, float, float, float, float, float, float, int);
-  }
-
-  public abstract class ViewOscillator extends androidx.constraintlayout.core.motion.utils.KeyCycleOscillator {
-    ctor public ViewOscillator();
-    method public static androidx.constraintlayout.motion.utils.ViewOscillator! makeSpline(String!);
-    method public abstract void setProperty(android.view.View!, float);
-  }
-
-  public static class ViewOscillator.PathRotateSet extends androidx.constraintlayout.motion.utils.ViewOscillator {
-    ctor public ViewOscillator.PathRotateSet();
-    method public void setPathRotate(android.view.View!, float, double, double);
-    method public void setProperty(android.view.View!, float);
-  }
-
-  public abstract class ViewSpline extends androidx.constraintlayout.core.motion.utils.SplineSet {
-    ctor public ViewSpline();
-    method public static androidx.constraintlayout.motion.utils.ViewSpline! makeCustomSpline(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public static androidx.constraintlayout.motion.utils.ViewSpline! makeSpline(String!);
-    method public abstract void setProperty(android.view.View!, float);
-  }
-
-  public static class ViewSpline.CustomSet extends androidx.constraintlayout.motion.utils.ViewSpline {
-    ctor public ViewSpline.CustomSet(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public void setPoint(int, androidx.constraintlayout.widget.ConstraintAttribute!);
-    method public void setProperty(android.view.View!, float);
-  }
-
-  public static class ViewSpline.PathRotate extends androidx.constraintlayout.motion.utils.ViewSpline {
-    ctor public ViewSpline.PathRotate();
-    method public void setPathRotate(android.view.View!, float, double, double);
-    method public void setProperty(android.view.View!, float);
-  }
-
-  public class ViewState {
-    ctor public ViewState();
-    method public void getState(android.view.View!);
-    method public int height();
-    method public int width();
-    field public int bottom;
-    field public int left;
-    field public int right;
-    field public float rotation;
-    field public int top;
-  }
-
-  public abstract class ViewTimeCycle extends androidx.constraintlayout.core.motion.utils.TimeCycleSplineSet {
-    ctor public ViewTimeCycle();
-    method public float get(float, long, android.view.View!, androidx.constraintlayout.core.motion.utils.KeyCache!);
-    method public static androidx.constraintlayout.motion.utils.ViewTimeCycle! makeCustomSpline(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public static androidx.constraintlayout.motion.utils.ViewTimeCycle! makeSpline(String!, long);
-    method public abstract boolean setProperty(android.view.View!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  public static class ViewTimeCycle.CustomSet extends androidx.constraintlayout.motion.utils.ViewTimeCycle {
-    ctor public ViewTimeCycle.CustomSet(String!, android.util.SparseArray<androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public void setPoint(int, androidx.constraintlayout.widget.ConstraintAttribute!, float, int, float);
-    method public boolean setProperty(android.view.View!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-  public static class ViewTimeCycle.PathRotate extends androidx.constraintlayout.motion.utils.ViewTimeCycle {
-    ctor public ViewTimeCycle.PathRotate();
-    method public boolean setPathRotate(android.view.View!, androidx.constraintlayout.core.motion.utils.KeyCache!, float, long, double, double);
-    method public boolean setProperty(android.view.View!, float, long, androidx.constraintlayout.core.motion.utils.KeyCache!);
-  }
-
-}
-
-package androidx.constraintlayout.motion.widget {
-
-  public interface Animatable {
-    method public float getProgress();
-    method public void setProgress(float);
-  }
-
-  public interface CustomFloatAttributes {
-    method public float get(String!);
-    method public String![]! getListOfAttributes();
-    method public void set(String!, float);
-  }
-
-  public class Debug {
-    ctor public Debug();
-    method public static void dumpLayoutParams(android.view.ViewGroup!, String!);
-    method public static void dumpLayoutParams(android.view.ViewGroup.LayoutParams!, String!);
-    method public static void dumpPoc(Object!);
-    method public static String! getActionType(android.view.MotionEvent!);
-    method public static String! getCallFrom(int);
-    method public static String! getLoc();
-    method public static String! getLocation();
-    method public static String! getLocation2();
-    method public static String! getName(android.content.Context!, int);
-    method public static String! getName(android.content.Context!, int[]!);
-    method public static String! getName(android.view.View!);
-    method public static String! getState(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public static String! getState(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public static void logStack(String!, String!, int);
-    method public static void printStack(String!, int);
-  }
-
-  public class DesignTool {
-    ctor public DesignTool(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public int designAccess(int, String!, Object!, float[]!, int, float[]!, int);
-    method public void disableAutoTransition(boolean);
-    method public void dumpConstraintSet(String!);
-    method public int getAnimationKeyFrames(Object!, float[]!);
-    method public int getAnimationPath(Object!, float[]!, int);
-    method public void getAnimationRectangles(Object!, float[]!);
-    method public String! getEndState();
-    method public int getKeyFrameInfo(Object!, int, int[]!);
-    method public float getKeyFramePosition(Object!, int, float, float);
-    method public int getKeyFramePositions(Object!, int[]!, float[]!);
-    method public Object! getKeyframe(int, int, int);
-    method public Object! getKeyframe(Object!, int, int);
-    method public Object! getKeyframeAtLocation(Object!, float, float);
-    method public Boolean! getPositionKeyframe(Object!, Object!, float, float, String![]!, float[]!);
-    method public float getProgress();
-    method public String! getStartState();
-    method public String! getState();
-    method public long getTransitionTimeMs();
-    method public boolean isInTransition();
-    method public void setAttributes(int, String!, Object!, Object!);
-    method public void setKeyFrame(Object!, int, String!, Object!);
-    method public boolean setKeyFramePosition(Object!, int, int, float, float);
-    method public void setKeyframe(Object!, String!, Object!);
-    method public void setState(String!);
-    method public void setToolPosition(float);
-    method public void setTransition(String!, String!);
-    method public void setViewDebug(Object!, int);
-  }
-
-  public interface FloatLayout {
-    method public void layout(float, float, float, float);
-  }
-
-  public abstract class Key {
-    ctor public Key();
-    method public abstract void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public abstract androidx.constraintlayout.motion.widget.Key! clone();
-    method public androidx.constraintlayout.motion.widget.Key! copy(androidx.constraintlayout.motion.widget.Key!);
-    method public int getFramePosition();
-    method public void setFramePosition(int);
-    method public void setInterpolation(java.util.HashMap<java.lang.String!,java.lang.Integer!>!);
-    method public abstract void setValue(String!, Object!);
-    method public androidx.constraintlayout.motion.widget.Key! setViewId(int);
-    field public static final String ALPHA = "alpha";
-    field public static final String CURVEFIT = "curveFit";
-    field public static final String CUSTOM = "CUSTOM";
-    field public static final String ELEVATION = "elevation";
-    field public static final String MOTIONPROGRESS = "motionProgress";
-    field public static final String PIVOT_X = "transformPivotX";
-    field public static final String PIVOT_Y = "transformPivotY";
-    field public static final String PROGRESS = "progress";
-    field public static final String ROTATION = "rotation";
-    field public static final String ROTATION_X = "rotationX";
-    field public static final String ROTATION_Y = "rotationY";
-    field public static final String SCALE_X = "scaleX";
-    field public static final String SCALE_Y = "scaleY";
-    field public static final String TRANSITIONEASING = "transitionEasing";
-    field public static final String TRANSITION_PATH_ROTATE = "transitionPathRotate";
-    field public static final String TRANSLATION_X = "translationX";
-    field public static final String TRANSLATION_Y = "translationY";
-    field public static final String TRANSLATION_Z = "translationZ";
-    field public static int UNSET;
-    field public static final String VISIBILITY = "visibility";
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_PHASE = "wavePhase";
-    field public static final String WAVE_VARIES_BY = "waveVariesBy";
-    field protected int mType;
-  }
-
-  public class KeyAttributes extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyAttributes();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final int KEY_TYPE = 1; // 0x1
-  }
-
-  public class KeyCycle extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyCycle();
-    method public void addCycleValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewOscillator!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public float getValue(String!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final int KEY_TYPE = 4; // 0x4
-    field public static final int SHAPE_BOUNCE = 6; // 0x6
-    field public static final int SHAPE_COS_WAVE = 5; // 0x5
-    field public static final int SHAPE_REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SHAPE_SAW_WAVE = 3; // 0x3
-    field public static final int SHAPE_SIN_WAVE = 0; // 0x0
-    field public static final int SHAPE_SQUARE_WAVE = 1; // 0x1
-    field public static final int SHAPE_TRIANGLE_WAVE = 2; // 0x2
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_PHASE = "wavePhase";
-    field public static final String WAVE_SHAPE = "waveShape";
-  }
-
-  public class KeyFrames {
-    ctor public KeyFrames();
-    ctor public KeyFrames(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public void addAllFrames(androidx.constraintlayout.motion.widget.MotionController!);
-    method public void addFrames(androidx.constraintlayout.motion.widget.MotionController!);
-    method public void addKey(androidx.constraintlayout.motion.widget.Key!);
-    method public java.util.ArrayList<androidx.constraintlayout.motion.widget.Key!>! getKeyFramesForView(int);
-    method public java.util.Set<java.lang.Integer!>! getKeys();
-    field public static final int UNSET = -1; // 0xffffffff
-  }
-
-  public class KeyPosition extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyPosition();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public boolean intersects(int, int, android.graphics.RectF!, android.graphics.RectF!, float, float);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void positionAttributes(android.view.View!, android.graphics.RectF!, android.graphics.RectF!, float, float, String![]!, float[]!);
-    method public void setType(int);
-    method public void setValue(String!, Object!);
-    field public static final String DRAWPATH = "drawPath";
-    field public static final String PERCENT_HEIGHT = "percentHeight";
-    field public static final String PERCENT_WIDTH = "percentWidth";
-    field public static final String PERCENT_X = "percentX";
-    field public static final String PERCENT_Y = "percentY";
-    field public static final String SIZE_PERCENT = "sizePercent";
-    field public static final String TRANSITION_EASING = "transitionEasing";
-    field public static final int TYPE_AXIS = 3; // 0x3
-    field public static final int TYPE_CARTESIAN = 0; // 0x0
-    field public static final int TYPE_PATH = 1; // 0x1
-    field public static final int TYPE_SCREEN = 2; // 0x2
-  }
-
-  public class KeyTimeCycle extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyTimeCycle();
-    method public void addTimeValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewTimeCycle!>!);
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final int KEY_TYPE = 3; // 0x3
-    field public static final int SHAPE_BOUNCE = 6; // 0x6
-    field public static final int SHAPE_COS_WAVE = 5; // 0x5
-    field public static final int SHAPE_REVERSE_SAW_WAVE = 4; // 0x4
-    field public static final int SHAPE_SAW_WAVE = 3; // 0x3
-    field public static final int SHAPE_SIN_WAVE = 0; // 0x0
-    field public static final int SHAPE_SQUARE_WAVE = 1; // 0x1
-    field public static final int SHAPE_TRIANGLE_WAVE = 2; // 0x2
-    field public static final String WAVE_OFFSET = "waveOffset";
-    field public static final String WAVE_PERIOD = "wavePeriod";
-    field public static final String WAVE_SHAPE = "waveShape";
-  }
-
-  public class KeyTrigger extends androidx.constraintlayout.motion.widget.Key {
-    ctor public KeyTrigger();
-    method public void addValues(java.util.HashMap<java.lang.String!,androidx.constraintlayout.motion.utils.ViewSpline!>!);
-    method public androidx.constraintlayout.motion.widget.Key! clone();
-    method public void conditionallyFire(float, android.view.View!);
-    method public void getAttributeNames(java.util.HashSet<java.lang.String!>!);
-    method public void load(android.content.Context!, android.util.AttributeSet!);
-    method public void setValue(String!, Object!);
-    field public static final String CROSS = "CROSS";
-    field public static final int KEY_TYPE = 5; // 0x5
-    field public static final String NEGATIVE_CROSS = "negativeCross";
-    field public static final String POSITIVE_CROSS = "positiveCross";
-    field public static final String POST_LAYOUT = "postLayout";
-    field public static final String TRIGGER_COLLISION_ID = "triggerCollisionId";
-    field public static final String TRIGGER_COLLISION_VIEW = "triggerCollisionView";
-    field public static final String TRIGGER_ID = "triggerID";
-    field public static final String TRIGGER_RECEIVER = "triggerReceiver";
-    field public static final String TRIGGER_SLACK = "triggerSlack";
-    field public static final String VIEW_TRANSITION_ON_CROSS = "viewTransitionOnCross";
-    field public static final String VIEW_TRANSITION_ON_NEGATIVE_CROSS = "viewTransitionOnNegativeCross";
-    field public static final String VIEW_TRANSITION_ON_POSITIVE_CROSS = "viewTransitionOnPositiveCross";
-  }
-
-  public class MotionController {
-    method public void addKey(androidx.constraintlayout.motion.widget.Key!);
-    method public int getAnimateRelativeTo();
-    method public void getCenter(double, float[]!, float[]!);
-    method public float getCenterX();
-    method public float getCenterY();
-    method public int getDrawPath();
-    method public float getFinalHeight();
-    method public float getFinalWidth();
-    method public float getFinalX();
-    method public float getFinalY();
-    method public int getKeyFrameInfo(int, int[]!);
-    method public int getKeyFramePositions(int[]!, float[]!);
-    method public float getStartHeight();
-    method public float getStartWidth();
-    method public float getStartX();
-    method public float getStartY();
-    method public int getTransformPivotTarget();
-    method public android.view.View! getView();
-    method public void remeasure();
-    method public void setDrawPath(int);
-    method public void setPathMotionArc(int);
-    method public void setStartState(androidx.constraintlayout.motion.utils.ViewState!, android.view.View!, int, int, int);
-    method public void setTransformPivotTarget(int);
-    method public void setView(android.view.View!);
-    method public void setup(int, int, float, long);
-    method public void setupRelative(androidx.constraintlayout.motion.widget.MotionController!);
-    field public static final int DRAW_PATH_AS_CONFIGURED = 4; // 0x4
-    field public static final int DRAW_PATH_BASIC = 1; // 0x1
-    field public static final int DRAW_PATH_CARTESIAN = 3; // 0x3
-    field public static final int DRAW_PATH_NONE = 0; // 0x0
-    field public static final int DRAW_PATH_RECTANGLE = 5; // 0x5
-    field public static final int DRAW_PATH_RELATIVE = 2; // 0x2
-    field public static final int DRAW_PATH_SCREEN = 6; // 0x6
-    field public static final int HORIZONTAL_PATH_X = 2; // 0x2
-    field public static final int HORIZONTAL_PATH_Y = 3; // 0x3
-    field public static final int PATH_PERCENT = 0; // 0x0
-    field public static final int PATH_PERPENDICULAR = 1; // 0x1
-    field public static final int ROTATION_LEFT = 2; // 0x2
-    field public static final int ROTATION_RIGHT = 1; // 0x1
-    field public static final int VERTICAL_PATH_X = 4; // 0x4
-    field public static final int VERTICAL_PATH_Y = 5; // 0x5
-  }
-
-  public class MotionHelper extends androidx.constraintlayout.widget.ConstraintHelper implements androidx.constraintlayout.motion.widget.MotionHelperInterface {
-    ctor public MotionHelper(android.content.Context!);
-    ctor public MotionHelper(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionHelper(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getProgress();
-    method public boolean isDecorator();
-    method public boolean isUseOnHide();
-    method public boolean isUsedOnShow();
-    method public void onFinishedMotionScene(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void onPostDraw(android.graphics.Canvas!);
-    method public void onPreDraw(android.graphics.Canvas!);
-    method public void onPreSetup(androidx.constraintlayout.motion.widget.MotionLayout!, java.util.HashMap<android.view.View!,androidx.constraintlayout.motion.widget.MotionController!>!);
-    method public void onTransitionChange(androidx.constraintlayout.motion.widget.MotionLayout!, int, int, float);
-    method public void onTransitionCompleted(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void onTransitionStarted(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public void onTransitionTrigger(androidx.constraintlayout.motion.widget.MotionLayout!, int, boolean, float);
-    method public void setProgress(android.view.View!, float);
-    method public void setProgress(float);
-    field protected android.view.View![]! views;
-  }
-
-  public interface MotionHelperInterface extends androidx.constraintlayout.motion.widget.Animatable androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener {
-    method public boolean isDecorator();
-    method public boolean isUseOnHide();
-    method public boolean isUsedOnShow();
-    method public void onFinishedMotionScene(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void onPostDraw(android.graphics.Canvas!);
-    method public void onPreDraw(android.graphics.Canvas!);
-    method public void onPreSetup(androidx.constraintlayout.motion.widget.MotionLayout!, java.util.HashMap<android.view.View!,androidx.constraintlayout.motion.widget.MotionController!>!);
-  }
-
-  public abstract class MotionInterpolator implements android.view.animation.Interpolator {
-    ctor public MotionInterpolator();
-    method public abstract float getVelocity();
-  }
-
-  public class MotionLayout extends androidx.constraintlayout.widget.ConstraintLayout implements androidx.core.view.NestedScrollingParent3 {
-    ctor public MotionLayout(android.content.Context);
-    ctor public MotionLayout(android.content.Context, android.util.AttributeSet?);
-    ctor public MotionLayout(android.content.Context, android.util.AttributeSet?, int);
-    method public void addTransitionListener(androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener!);
-    method public boolean applyViewTransition(int, androidx.constraintlayout.motion.widget.MotionController!);
-    method public androidx.constraintlayout.widget.ConstraintSet! cloneConstraintSet(int);
-    method public void enableTransition(int, boolean);
-    method public void enableViewTransition(int, boolean);
-    method protected void fireTransitionCompleted();
-    method public void fireTrigger(int, boolean, float);
-    method public androidx.constraintlayout.widget.ConstraintSet! getConstraintSet(int);
-    method @IdRes public int[]! getConstraintSetIds();
-    method public int getCurrentState();
-    method public java.util.ArrayList<androidx.constraintlayout.motion.widget.MotionScene.Transition!>! getDefinedTransitions();
-    method public androidx.constraintlayout.motion.widget.DesignTool! getDesignTool();
-    method public int getEndState();
-    method public int[]! getMatchingConstraintSetIds(java.lang.String!...!);
-    method protected long getNanoTime();
-    method public float getProgress();
-    method public androidx.constraintlayout.motion.widget.MotionScene! getScene();
-    method public int getStartState();
-    method public float getTargetPosition();
-    method public androidx.constraintlayout.motion.widget.MotionScene.Transition! getTransition(int);
-    method public android.os.Bundle! getTransitionState();
-    method public long getTransitionTimeMs();
-    method public float getVelocity();
-    method public void getViewVelocity(android.view.View!, float, float, float[]!, int);
-    method public boolean isDelayedApplicationOfInitialState();
-    method public boolean isInRotation();
-    method public boolean isInteractionEnabled();
-    method public boolean isViewTransitionEnabled(int);
-    method public void jumpToState(int);
-    method protected androidx.constraintlayout.motion.widget.MotionLayout.MotionTracker! obtainVelocityTracker();
-    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
-    method public void onNestedScroll(android.view.View, int, int, int, int, int);
-    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]!);
-    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
-    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
-    method public void onStopNestedScroll(android.view.View, int);
-    method @Deprecated public void rebuildMotion();
-    method public void rebuildScene();
-    method public boolean removeTransitionListener(androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener!);
-    method public void rotateTo(int, int);
-    method public void scheduleTransitionTo(int);
-    method public void setDebugMode(int);
-    method public void setDelayedApplicationOfInitialState(boolean);
-    method public void setInteractionEnabled(boolean);
-    method public void setInterpolatedProgress(float);
-    method public void setOnHide(float);
-    method public void setOnShow(float);
-    method public void setProgress(float);
-    method public void setProgress(float, float);
-    method public void setScene(androidx.constraintlayout.motion.widget.MotionScene!);
-    method protected void setTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public void setTransition(int);
-    method public void setTransition(int, int);
-    method public void setTransitionDuration(int);
-    method public void setTransitionListener(androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener!);
-    method public void setTransitionState(android.os.Bundle!);
-    method public void touchAnimateTo(int, float, float);
-    method public void touchSpringTo(float, float);
-    method public void transitionToEnd();
-    method public void transitionToEnd(Runnable!);
-    method public void transitionToStart();
-    method public void transitionToStart(Runnable!);
-    method public void transitionToState(int);
-    method public void transitionToState(int, int);
-    method public void transitionToState(int, int, int);
-    method public void transitionToState(int, int, int, int);
-    method public void updateState();
-    method public void updateState(int, androidx.constraintlayout.widget.ConstraintSet!);
-    method public void updateStateAnimate(int, androidx.constraintlayout.widget.ConstraintSet!, int);
-    method public void viewTransition(int, android.view.View!...!);
-    field public static final int DEBUG_SHOW_NONE = 0; // 0x0
-    field public static final int DEBUG_SHOW_PATH = 2; // 0x2
-    field public static final int DEBUG_SHOW_PROGRESS = 1; // 0x1
-    field public static boolean IS_IN_EDIT_MODE;
-    field public static final int TOUCH_UP_COMPLETE = 0; // 0x0
-    field public static final int TOUCH_UP_COMPLETE_TO_END = 2; // 0x2
-    field public static final int TOUCH_UP_COMPLETE_TO_START = 1; // 0x1
-    field public static final int TOUCH_UP_DECELERATE = 4; // 0x4
-    field public static final int TOUCH_UP_DECELERATE_AND_COMPLETE = 5; // 0x5
-    field public static final int TOUCH_UP_NEVER_TO_END = 7; // 0x7
-    field public static final int TOUCH_UP_NEVER_TO_START = 6; // 0x6
-    field public static final int TOUCH_UP_STOP = 3; // 0x3
-    field public static final int VELOCITY_LAYOUT = 1; // 0x1
-    field public static final int VELOCITY_POST_LAYOUT = 0; // 0x0
-    field public static final int VELOCITY_STATIC_LAYOUT = 3; // 0x3
-    field public static final int VELOCITY_STATIC_POST_LAYOUT = 2; // 0x2
-    field protected boolean mMeasureDuringTransition;
-  }
-
-  protected static interface MotionLayout.MotionTracker {
-    method public void addMovement(android.view.MotionEvent!);
-    method public void clear();
-    method public void computeCurrentVelocity(int);
-    method public void computeCurrentVelocity(int, float);
-    method public float getXVelocity();
-    method public float getXVelocity(int);
-    method public float getYVelocity();
-    method public float getYVelocity(int);
-    method public void recycle();
-  }
-
-  public static interface MotionLayout.TransitionListener {
-    method public void onTransitionChange(androidx.constraintlayout.motion.widget.MotionLayout!, int, int, float);
-    method public void onTransitionCompleted(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void onTransitionStarted(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public void onTransitionTrigger(androidx.constraintlayout.motion.widget.MotionLayout!, int, boolean, float);
-  }
-
-  public class MotionScene {
-    ctor public MotionScene(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void addOnClickListeners(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void addTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public boolean applyViewTransition(int, androidx.constraintlayout.motion.widget.MotionController!);
-    method public androidx.constraintlayout.motion.widget.MotionScene.Transition! bestTransitionFor(int, float, float, android.view.MotionEvent!);
-    method public void disableAutoTransition(boolean);
-    method public void enableViewTransition(int, boolean);
-    method public int gatPathMotionArc();
-    method public androidx.constraintlayout.widget.ConstraintSet! getConstraintSet(android.content.Context!, String!);
-    method public int[]! getConstraintSetIds();
-    method public java.util.ArrayList<androidx.constraintlayout.motion.widget.MotionScene.Transition!>! getDefinedTransitions();
-    method public int getDuration();
-    method public android.view.animation.Interpolator! getInterpolator();
-    method public void getKeyFrames(androidx.constraintlayout.motion.widget.MotionController!);
-    method public int[]! getMatchingStateLabels(java.lang.String!...!);
-    method public float getPathPercent(android.view.View!, int);
-    method public float getStaggered();
-    method public androidx.constraintlayout.motion.widget.MotionScene.Transition! getTransitionById(int);
-    method public java.util.List<androidx.constraintlayout.motion.widget.MotionScene.Transition!>! getTransitionsWithState(int);
-    method public boolean isViewTransitionEnabled(int);
-    method public int lookUpConstraintId(String!);
-    method public String! lookUpConstraintName(int);
-    method protected void onLayout(boolean, int, int, int, int);
-    method public void removeTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public void setConstraintSet(int, androidx.constraintlayout.widget.ConstraintSet!);
-    method public void setDuration(int);
-    method public void setKeyframe(android.view.View!, int, String!, Object!);
-    method public void setRtl(boolean);
-    method public void setTransition(androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public static String! stripID(String!);
-    method public boolean validateLayout(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void viewTransition(int, android.view.View!...!);
-    field public static final int LAYOUT_CALL_MEASURE = 2; // 0x2
-    field public static final int LAYOUT_HONOR_REQUEST = 1; // 0x1
-    field public static final int LAYOUT_IGNORE_REQUEST = 0; // 0x0
-    field public static final int UNSET = -1; // 0xffffffff
-  }
-
-  public static class MotionScene.Transition {
-    ctor public MotionScene.Transition(int, androidx.constraintlayout.motion.widget.MotionScene!, int, int);
-    method public void addKeyFrame(androidx.constraintlayout.motion.widget.KeyFrames!);
-    method public void addOnClick(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public void addOnClick(int, int);
-    method public String! debugString(android.content.Context!);
-    method public int getAutoTransition();
-    method public int getDuration();
-    method public int getEndConstraintSetId();
-    method public int getId();
-    method public java.util.List<androidx.constraintlayout.motion.widget.KeyFrames!>! getKeyFrameList();
-    method public int getLayoutDuringTransition();
-    method public java.util.List<androidx.constraintlayout.motion.widget.MotionScene.Transition.TransitionOnClick!>! getOnClickList();
-    method public int getPathMotionArc();
-    method public float getStagger();
-    method public int getStartConstraintSetId();
-    method public androidx.constraintlayout.motion.widget.TouchResponse! getTouchResponse();
-    method public boolean isEnabled();
-    method public boolean isTransitionFlag(int);
-    method public void removeOnClick(int);
-    method public void setAutoTransition(int);
-    method public void setDuration(int);
-    method public void setEnabled(boolean);
-    method public void setInterpolatorInfo(int, String!, int);
-    method public void setLayoutDuringTransition(int);
-    method public void setOnSwipe(androidx.constraintlayout.motion.widget.OnSwipe!);
-    method public void setOnTouchUp(int);
-    method public void setPathMotionArc(int);
-    method public void setStagger(float);
-    method public void setTransitionFlag(int);
-    field public static final int AUTO_ANIMATE_TO_END = 4; // 0x4
-    field public static final int AUTO_ANIMATE_TO_START = 3; // 0x3
-    field public static final int AUTO_JUMP_TO_END = 2; // 0x2
-    field public static final int AUTO_JUMP_TO_START = 1; // 0x1
-    field public static final int AUTO_NONE = 0; // 0x0
-    field public static final int INTERPOLATE_ANTICIPATE = 6; // 0x6
-    field public static final int INTERPOLATE_BOUNCE = 4; // 0x4
-    field public static final int INTERPOLATE_EASE_IN = 1; // 0x1
-    field public static final int INTERPOLATE_EASE_IN_OUT = 0; // 0x0
-    field public static final int INTERPOLATE_EASE_OUT = 2; // 0x2
-    field public static final int INTERPOLATE_LINEAR = 3; // 0x3
-    field public static final int INTERPOLATE_OVERSHOOT = 5; // 0x5
-    field public static final int INTERPOLATE_REFERENCE_ID = -2; // 0xfffffffe
-    field public static final int INTERPOLATE_SPLINE_STRING = -1; // 0xffffffff
-  }
-
-  public static class MotionScene.Transition.TransitionOnClick implements android.view.View.OnClickListener {
-    ctor public MotionScene.Transition.TransitionOnClick(android.content.Context!, androidx.constraintlayout.motion.widget.MotionScene.Transition!, org.xmlpull.v1.XmlPullParser!);
-    ctor public MotionScene.Transition.TransitionOnClick(androidx.constraintlayout.motion.widget.MotionScene.Transition!, int, int);
-    method public void addOnClickListeners(androidx.constraintlayout.motion.widget.MotionLayout!, int, androidx.constraintlayout.motion.widget.MotionScene.Transition!);
-    method public void onClick(android.view.View!);
-    method public void removeOnClickListeners(androidx.constraintlayout.motion.widget.MotionLayout!);
-    field public static final int ANIM_TOGGLE = 17; // 0x11
-    field public static final int ANIM_TO_END = 1; // 0x1
-    field public static final int ANIM_TO_START = 16; // 0x10
-    field public static final int JUMP_TO_END = 256; // 0x100
-    field public static final int JUMP_TO_START = 4096; // 0x1000
-  }
-
-  public class OnSwipe {
-    ctor public OnSwipe();
-    method public int getAutoCompleteMode();
-    method public int getDragDirection();
-    method public float getDragScale();
-    method public float getDragThreshold();
-    method public int getLimitBoundsTo();
-    method public float getMaxAcceleration();
-    method public float getMaxVelocity();
-    method public boolean getMoveWhenScrollAtTop();
-    method public int getNestedScrollFlags();
-    method public int getOnTouchUp();
-    method public int getRotationCenterId();
-    method public int getSpringBoundary();
-    method public float getSpringDamping();
-    method public float getSpringMass();
-    method public float getSpringStiffness();
-    method public float getSpringStopThreshold();
-    method public int getTouchAnchorId();
-    method public int getTouchAnchorSide();
-    method public int getTouchRegionId();
-    method public void setAutoCompleteMode(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setDragDirection(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setDragScale(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setDragThreshold(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setLimitBoundsTo(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setMaxAcceleration(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setMaxVelocity(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setMoveWhenScrollAtTop(boolean);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setNestedScrollFlags(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setOnTouchUp(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setRotateCenter(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringBoundary(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringDamping(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringMass(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringStiffness(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setSpringStopThreshold(float);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setTouchAnchorId(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setTouchAnchorSide(int);
-    method public androidx.constraintlayout.motion.widget.OnSwipe! setTouchRegionId(int);
-    field public static final int COMPLETE_MODE_CONTINUOUS_VELOCITY = 0; // 0x0
-    field public static final int COMPLETE_MODE_SPRING = 1; // 0x1
-    field public static final int DRAG_ANTICLOCKWISE = 7; // 0x7
-    field public static final int DRAG_CLOCKWISE = 6; // 0x6
-    field public static final int DRAG_DOWN = 1; // 0x1
-    field public static final int DRAG_END = 5; // 0x5
-    field public static final int DRAG_LEFT = 2; // 0x2
-    field public static final int DRAG_RIGHT = 3; // 0x3
-    field public static final int DRAG_START = 4; // 0x4
-    field public static final int DRAG_UP = 0; // 0x0
-    field public static final int FLAG_DISABLE_POST_SCROLL = 1; // 0x1
-    field public static final int FLAG_DISABLE_SCROLL = 2; // 0x2
-    field public static final int ON_UP_AUTOCOMPLETE = 0; // 0x0
-    field public static final int ON_UP_AUTOCOMPLETE_TO_END = 2; // 0x2
-    field public static final int ON_UP_AUTOCOMPLETE_TO_START = 1; // 0x1
-    field public static final int ON_UP_DECELERATE = 4; // 0x4
-    field public static final int ON_UP_DECELERATE_AND_COMPLETE = 5; // 0x5
-    field public static final int ON_UP_NEVER_TO_END = 7; // 0x7
-    field public static final int ON_UP_NEVER_TO_START = 6; // 0x6
-    field public static final int ON_UP_STOP = 3; // 0x3
-    field public static final int SIDE_BOTTOM = 3; // 0x3
-    field public static final int SIDE_END = 6; // 0x6
-    field public static final int SIDE_LEFT = 1; // 0x1
-    field public static final int SIDE_MIDDLE = 4; // 0x4
-    field public static final int SIDE_RIGHT = 2; // 0x2
-    field public static final int SIDE_START = 5; // 0x5
-    field public static final int SIDE_TOP = 0; // 0x0
-    field public static final int SPRING_BOUNDARY_BOUNCEBOTH = 3; // 0x3
-    field public static final int SPRING_BOUNDARY_BOUNCEEND = 2; // 0x2
-    field public static final int SPRING_BOUNDARY_BOUNCESTART = 1; // 0x1
-    field public static final int SPRING_BOUNDARY_OVERSHOOT = 0; // 0x0
-  }
-
-  public abstract class TransitionAdapter implements androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener {
-    ctor public TransitionAdapter();
-    method public void onTransitionChange(androidx.constraintlayout.motion.widget.MotionLayout!, int, int, float);
-    method public void onTransitionCompleted(androidx.constraintlayout.motion.widget.MotionLayout!, int);
-    method public void onTransitionStarted(androidx.constraintlayout.motion.widget.MotionLayout!, int, int);
-    method public void onTransitionTrigger(androidx.constraintlayout.motion.widget.MotionLayout!, int, boolean, float);
-  }
-
-  public class TransitionBuilder {
-    ctor public TransitionBuilder();
-    method public static androidx.constraintlayout.motion.widget.MotionScene.Transition! buildTransition(androidx.constraintlayout.motion.widget.MotionScene!, int, int, androidx.constraintlayout.widget.ConstraintSet!, int, androidx.constraintlayout.widget.ConstraintSet!);
-    method public static void validate(androidx.constraintlayout.motion.widget.MotionLayout!);
-  }
-
-  public class ViewTransition {
-    method public int getSharedValue();
-    method public int getSharedValueCurrent();
-    method public int getSharedValueID();
-    method public int getStateTransition();
-    method public void setSharedValue(int);
-    method public void setSharedValueCurrent(int);
-    method public void setSharedValueID(int);
-    method public void setStateTransition(int);
-    field public static final String CONSTRAINT_OVERRIDE = "ConstraintOverride";
-    field public static final String CUSTOM_ATTRIBUTE = "CustomAttribute";
-    field public static final String CUSTOM_METHOD = "CustomMethod";
-    field public static final String KEY_FRAME_SET_TAG = "KeyFrameSet";
-    field public static final int ONSTATE_ACTION_DOWN = 1; // 0x1
-    field public static final int ONSTATE_ACTION_DOWN_UP = 3; // 0x3
-    field public static final int ONSTATE_ACTION_UP = 2; // 0x2
-    field public static final int ONSTATE_SHARED_VALUE_SET = 4; // 0x4
-    field public static final int ONSTATE_SHARED_VALUE_UNSET = 5; // 0x5
-    field public static final String VIEW_TRANSITION_TAG = "ViewTransition";
-  }
-
-  public class ViewTransitionController {
-    ctor public ViewTransitionController(androidx.constraintlayout.motion.widget.MotionLayout!);
-    method public void add(androidx.constraintlayout.motion.widget.ViewTransition!);
-  }
-
-}
-
-package androidx.constraintlayout.utils.widget {
-
-  public class ImageFilterButton extends androidx.appcompat.widget.AppCompatImageButton {
-    ctor public ImageFilterButton(android.content.Context!);
-    ctor public ImageFilterButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public ImageFilterButton(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getContrast();
-    method public float getCrossfade();
-    method public float getImagePanX();
-    method public float getImagePanY();
-    method public float getImageRotate();
-    method public float getImageZoom();
-    method public float getRound();
-    method public float getRoundPercent();
-    method public float getSaturation();
-    method public float getWarmth();
-    method public void setAltImageResource(int);
-    method public void setBrightness(float);
-    method public void setContrast(float);
-    method public void setCrossfade(float);
-    method public void setImagePanX(float);
-    method public void setImagePanY(float);
-    method public void setImageRotate(float);
-    method public void setImageZoom(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-    method public void setSaturation(float);
-    method public void setWarmth(float);
-  }
-
-  public class ImageFilterView extends androidx.appcompat.widget.AppCompatImageView {
-    ctor public ImageFilterView(android.content.Context!);
-    ctor public ImageFilterView(android.content.Context!, android.util.AttributeSet!);
-    ctor public ImageFilterView(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getBrightness();
-    method public float getContrast();
-    method public float getCrossfade();
-    method public float getImagePanX();
-    method public float getImagePanY();
-    method public float getImageRotate();
-    method public float getImageZoom();
-    method public float getRound();
-    method public float getRoundPercent();
-    method public float getSaturation();
-    method public float getWarmth();
-    method public void setAltImageDrawable(android.graphics.drawable.Drawable!);
-    method public void setAltImageResource(int);
-    method public void setBrightness(float);
-    method public void setContrast(float);
-    method public void setCrossfade(float);
-    method public void setImagePanX(float);
-    method public void setImagePanY(float);
-    method public void setImageRotate(float);
-    method public void setImageZoom(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-    method public void setSaturation(float);
-    method public void setWarmth(float);
-  }
-
-  public class MockView extends android.view.View {
-    ctor public MockView(android.content.Context!);
-    ctor public MockView(android.content.Context!, android.util.AttributeSet!);
-    ctor public MockView(android.content.Context!, android.util.AttributeSet!, int);
-    method public void onDraw(android.graphics.Canvas);
-    field protected String! mText;
-  }
-
-  public class MotionButton extends androidx.appcompat.widget.AppCompatButton {
-    ctor public MotionButton(android.content.Context!);
-    ctor public MotionButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionButton(android.content.Context!, android.util.AttributeSet!, int);
-    method public float getRound();
-    method public float getRoundPercent();
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-  }
-
-  public class MotionLabel extends android.view.View implements androidx.constraintlayout.motion.widget.FloatLayout {
-    ctor public MotionLabel(android.content.Context!);
-    ctor public MotionLabel(android.content.Context!, android.util.AttributeSet?);
-    ctor public MotionLabel(android.content.Context!, android.util.AttributeSet?, int);
-    method public float getRound();
-    method public float getRoundPercent();
-    method public float getScaleFromTextSize();
-    method public float getTextBackgroundPanX();
-    method public float getTextBackgroundPanY();
-    method public float getTextBackgroundRotate();
-    method public float getTextBackgroundZoom();
-    method public int getTextOutlineColor();
-    method public float getTextPanX();
-    method public float getTextPanY();
-    method public float getTextureHeight();
-    method public float getTextureWidth();
-    method public android.graphics.Typeface! getTypeface();
-    method public void layout(float, float, float, float);
-    method public void setGravity(int);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRound(float);
-    method @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public void setRoundPercent(float);
-    method public void setScaleFromTextSize(float);
-    method public void setText(CharSequence!);
-    method public void setTextBackgroundPanX(float);
-    method public void setTextBackgroundPanY(float);
-    method public void setTextBackgroundRotate(float);
-    method public void setTextBackgroundZoom(float);
-    method public void setTextFillColor(int);
-    method public void setTextOutlineColor(int);
-    method public void setTextOutlineThickness(float);
-    method public void setTextPanX(float);
-    method public void setTextPanY(float);
-    method public void setTextSize(float);
-    method public void setTextureHeight(float);
-    method public void setTextureWidth(float);
-    method public void setTypeface(android.graphics.Typeface!);
-  }
-
-  public class MotionTelltales extends androidx.constraintlayout.utils.widget.MockView {
-    ctor public MotionTelltales(android.content.Context!);
-    ctor public MotionTelltales(android.content.Context!, android.util.AttributeSet!);
-    ctor public MotionTelltales(android.content.Context!, android.util.AttributeSet!, int);
-    method public void setText(CharSequence!);
-  }
-
-}
-
-package androidx.constraintlayout.widget {
-
-  public class Barrier extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public Barrier(android.content.Context!);
-    ctor public Barrier(android.content.Context!, android.util.AttributeSet!);
-    ctor public Barrier(android.content.Context!, android.util.AttributeSet!, int);
-    method @Deprecated public boolean allowsGoneWidget();
-    method public boolean getAllowsGoneWidget();
-    method public int getMargin();
-    method public int getType();
-    method public void setAllowsGoneWidget(boolean);
-    method public void setDpMargin(int);
-    method public void setMargin(int);
-    method public void setType(int);
-    field public static final int BOTTOM = 3; // 0x3
-    field public static final int END = 6; // 0x6
-    field public static final int LEFT = 0; // 0x0
-    field public static final int RIGHT = 1; // 0x1
-    field public static final int START = 5; // 0x5
-    field public static final int TOP = 2; // 0x2
-  }
-
-  public class ConstraintAttribute {
-    ctor public ConstraintAttribute(androidx.constraintlayout.widget.ConstraintAttribute!, Object!);
-    ctor public ConstraintAttribute(String!, androidx.constraintlayout.widget.ConstraintAttribute.AttributeType!);
-    ctor public ConstraintAttribute(String!, androidx.constraintlayout.widget.ConstraintAttribute.AttributeType!, Object!, boolean);
-    method public void applyCustom(android.view.View!);
-    method public boolean diff(androidx.constraintlayout.widget.ConstraintAttribute!);
-    method public static java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>! extractAttributes(java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>!, android.view.View!);
-    method public int getColorValue();
-    method public float getFloatValue();
-    method public int getIntegerValue();
-    method public String! getName();
-    method public String! getStringValue();
-    method public androidx.constraintlayout.widget.ConstraintAttribute.AttributeType! getType();
-    method public float getValueToInterpolate();
-    method public void getValuesToInterpolate(float[]!);
-    method public boolean isBooleanValue();
-    method public boolean isContinuous();
-    method public boolean isMethod();
-    method public int numberOfInterpolatedValues();
-    method public static void parse(android.content.Context!, org.xmlpull.v1.XmlPullParser!, java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public static void setAttributes(android.view.View!, java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>!);
-    method public void setColorValue(int);
-    method public void setFloatValue(float);
-    method public void setIntValue(int);
-    method public void setStringValue(String!);
-    method public void setValue(float[]!);
-    method public void setValue(Object!);
-  }
-
-  public enum ConstraintAttribute.AttributeType {
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType BOOLEAN_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType COLOR_DRAWABLE_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType COLOR_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType DIMENSION_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType FLOAT_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType INT_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType REFERENCE_TYPE;
-    enum_constant public static final androidx.constraintlayout.widget.ConstraintAttribute.AttributeType STRING_TYPE;
-  }
-
-  public abstract class ConstraintHelper extends android.view.View {
-    ctor public ConstraintHelper(android.content.Context!);
-    ctor public ConstraintHelper(android.content.Context!, android.util.AttributeSet!);
-    ctor public ConstraintHelper(android.content.Context!, android.util.AttributeSet!, int);
-    method public void addView(android.view.View!);
-    method public void applyHelperParams();
-    method protected void applyLayoutFeatures();
-    method protected void applyLayoutFeatures(androidx.constraintlayout.widget.ConstraintLayout!);
-    method protected void applyLayoutFeaturesInConstraintSet(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public boolean containsId(int);
-    method public int[]! getReferencedIds();
-    method protected android.view.View![]! getViews(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public int indexFromId(int);
-    method protected void init(android.util.AttributeSet!);
-    method public static boolean isChildOfHelper(android.view.View!);
-    method public void loadParameters(androidx.constraintlayout.widget.ConstraintSet.Constraint!, androidx.constraintlayout.core.widgets.HelperWidget!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void onDraw(android.graphics.Canvas);
-    method public int removeView(android.view.View!);
-    method public void resolveRtl(androidx.constraintlayout.core.widgets.ConstraintWidget!, boolean);
-    method protected void setIds(String!);
-    method protected void setReferenceTags(String!);
-    method public void setReferencedIds(int[]!);
-    method public void updatePostConstraints(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePostLayout(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePostMeasure(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePreDraw(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePreLayout(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, androidx.constraintlayout.core.widgets.Helper!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void updatePreLayout(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void validateParams();
-    field protected static final String CHILD_TAG = "CONSTRAINT_LAYOUT_HELPER_CHILD";
-    field protected int mCount;
-    field protected androidx.constraintlayout.core.widgets.Helper! mHelperWidget;
-    field protected int[]! mIds;
-    field protected java.util.HashMap<java.lang.Integer!,java.lang.String!>! mMap;
-    field protected String! mReferenceIds;
-    field protected String! mReferenceTags;
-    field protected boolean mUseViewMeasure;
-    field protected android.content.Context! myContext;
-  }
-
-  public class ConstraintLayout extends android.view.ViewGroup {
-    ctor public ConstraintLayout(android.content.Context);
-    ctor public ConstraintLayout(android.content.Context, android.util.AttributeSet?);
-    ctor public ConstraintLayout(android.content.Context, android.util.AttributeSet?, int);
-    ctor public ConstraintLayout(android.content.Context, android.util.AttributeSet?, int, int);
-    method public void addValueModifier(androidx.constraintlayout.widget.ConstraintLayout.ValueModifier!);
-    method protected void applyConstraintsFromLayoutParams(boolean, android.view.View!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method protected boolean dynamicUpdateConstraints(int, int);
-    method public void fillMetrics(androidx.constraintlayout.core.Metrics!);
-    method protected androidx.constraintlayout.widget.ConstraintLayout.LayoutParams! generateDefaultLayoutParams();
-    method public androidx.constraintlayout.widget.ConstraintLayout.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
-    method public Object! getDesignInformation(int, Object!);
-    method public int getMaxHeight();
-    method public int getMaxWidth();
-    method public int getMinHeight();
-    method public int getMinWidth();
-    method public int getOptimizationLevel();
-    method public String! getSceneString();
-    method public static androidx.constraintlayout.widget.SharedValues! getSharedValues();
-    method public android.view.View! getViewById(int);
-    method public final androidx.constraintlayout.core.widgets.ConstraintWidget! getViewWidget(android.view.View!);
-    method protected boolean isRtl();
-    method public void loadLayoutDescription(int);
-    method protected void parseLayoutDescription(int);
-    method protected void resolveMeasuredDimension(int, int, int, int, boolean, boolean);
-    method protected void resolveSystem(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int, int, int);
-    method public void setConstraintSet(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void setDesignInformation(int, Object!, Object!);
-    method public void setMaxHeight(int);
-    method public void setMaxWidth(int);
-    method public void setMinHeight(int);
-    method public void setMinWidth(int);
-    method public void setOnConstraintsChanged(androidx.constraintlayout.widget.ConstraintsChangedListener!);
-    method public void setOptimizationLevel(int);
-    method protected void setSelfDimensionBehaviour(androidx.constraintlayout.core.widgets.ConstraintWidgetContainer!, int, int, int, int);
-    method public void setState(int, int, int);
-    field public static final int DESIGN_INFO_ID = 0; // 0x0
-    field public static final String VERSION = "ConstraintLayout-2.2.0-alpha04";
-    field protected androidx.constraintlayout.widget.ConstraintLayoutStates! mConstraintLayoutSpec;
-    field protected boolean mDirtyHierarchy;
-    field protected androidx.constraintlayout.core.widgets.ConstraintWidgetContainer! mLayoutWidget;
-  }
-
-  public static class ConstraintLayout.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-    ctor public ConstraintLayout.LayoutParams(android.content.Context!, android.util.AttributeSet!);
-    ctor public ConstraintLayout.LayoutParams(android.view.ViewGroup.LayoutParams!);
-    ctor public ConstraintLayout.LayoutParams(int, int);
-    method public String! getConstraintTag();
-    method public androidx.constraintlayout.core.widgets.ConstraintWidget! getConstraintWidget();
-    method public void reset();
-    method public void setWidgetDebugName(String!);
-    method public void validate();
-    field public static final int BASELINE = 5; // 0x5
-    field public static final int BOTTOM = 4; // 0x4
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static final int CIRCLE = 8; // 0x8
-    field public static final int END = 7; // 0x7
-    field public static final int GONE_UNSET = -2147483648; // 0x80000000
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int LEFT = 1; // 0x1
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_PERCENT = 2; // 0x2
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int RIGHT = 2; // 0x2
-    field public static final int START = 6; // 0x6
-    field public static final int TOP = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int WRAP_BEHAVIOR_HORIZONTAL_ONLY = 1; // 0x1
-    field public static final int WRAP_BEHAVIOR_INCLUDED = 0; // 0x0
-    field public static final int WRAP_BEHAVIOR_SKIPPED = 3; // 0x3
-    field public static final int WRAP_BEHAVIOR_VERTICAL_ONLY = 2; // 0x2
-    field public int baselineMargin;
-    field public int baselineToBaseline;
-    field public int baselineToBottom;
-    field public int baselineToTop;
-    field public int bottomToBottom;
-    field public int bottomToTop;
-    field public float circleAngle;
-    field public int circleConstraint;
-    field public int circleRadius;
-    field public boolean constrainedHeight;
-    field public boolean constrainedWidth;
-    field public String! constraintTag;
-    field public String! dimensionRatio;
-    field public int editorAbsoluteX;
-    field public int editorAbsoluteY;
-    field public int endToEnd;
-    field public int endToStart;
-    field public int goneBaselineMargin;
-    field public int goneBottomMargin;
-    field public int goneEndMargin;
-    field public int goneLeftMargin;
-    field public int goneRightMargin;
-    field public int goneStartMargin;
-    field public int goneTopMargin;
-    field public int guideBegin;
-    field public int guideEnd;
-    field public float guidePercent;
-    field public boolean guidelineUseRtl;
-    field public boolean helped;
-    field public float horizontalBias;
-    field public int horizontalChainStyle;
-    field public float horizontalWeight;
-    field public int leftToLeft;
-    field public int leftToRight;
-    field public int matchConstraintDefaultHeight;
-    field public int matchConstraintDefaultWidth;
-    field public int matchConstraintMaxHeight;
-    field public int matchConstraintMaxWidth;
-    field public int matchConstraintMinHeight;
-    field public int matchConstraintMinWidth;
-    field public float matchConstraintPercentHeight;
-    field public float matchConstraintPercentWidth;
-    field public int orientation;
-    field public int rightToLeft;
-    field public int rightToRight;
-    field public int startToEnd;
-    field public int startToStart;
-    field public int topToBottom;
-    field public int topToTop;
-    field public float verticalBias;
-    field public int verticalChainStyle;
-    field public float verticalWeight;
-    field public int wrapBehaviorInParent;
-  }
-
-  public static interface ConstraintLayout.ValueModifier {
-    method public boolean update(int, int, int, android.view.View!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!);
-  }
-
-  public class ConstraintLayoutStates {
-    method public boolean needsToChange(int, float, float);
-    method public void setOnConstraintsChanged(androidx.constraintlayout.widget.ConstraintsChangedListener!);
-    method public void updateConstraints(int, float, float);
-    field public static final String TAG = "ConstraintLayoutStates";
-  }
-
-  public class ConstraintLayoutStatistics {
-    ctor public ConstraintLayoutStatistics(androidx.constraintlayout.widget.ConstraintLayout!);
-    ctor public ConstraintLayoutStatistics(androidx.constraintlayout.widget.ConstraintLayoutStatistics!);
-    method public void attach(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public androidx.constraintlayout.widget.ConstraintLayoutStatistics! clone();
-    method public void detach();
-    method public long getValue(int);
-    method public void logSummary(String!);
-    method public void logSummary(String!, androidx.constraintlayout.widget.ConstraintLayoutStatistics!);
-    method public void reset();
-    field public static final int DURATION_OF_CHILD_MEASURES = 5; // 0x5
-    field public static final int DURATION_OF_LAYOUT = 7; // 0x7
-    field public static final int DURATION_OF_MEASURES = 6; // 0x6
-    field public static final int NUMBER_OF_CHILD_MEASURES = 4; // 0x4
-    field public static final int NUMBER_OF_CHILD_VIEWS = 3; // 0x3
-    field public static final int NUMBER_OF_EQUATIONS = 9; // 0x9
-    field public static final int NUMBER_OF_LAYOUTS = 1; // 0x1
-    field public static final int NUMBER_OF_ON_MEASURES = 2; // 0x2
-    field public static final int NUMBER_OF_SIMPLE_EQUATIONS = 10; // 0xa
-    field public static final int NUMBER_OF_VARIABLES = 8; // 0x8
-  }
-
-  public class ConstraintProperties {
-    ctor public ConstraintProperties(android.view.View!);
-    method public androidx.constraintlayout.widget.ConstraintProperties! addToHorizontalChain(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! addToHorizontalChainRTL(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! addToVerticalChain(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! alpha(float);
-    method public void apply();
-    method public androidx.constraintlayout.widget.ConstraintProperties! center(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontally(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontally(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontallyRtl(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerHorizontallyRtl(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerVertically(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! centerVertically(int, int, int, int, int, int, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! connect(int, int, int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainDefaultHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainDefaultWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMaxHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMaxWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMinHeight(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainMinWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! constrainWidth(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! dimensionRatio(String!);
-    method public androidx.constraintlayout.widget.ConstraintProperties! elevation(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! goneMargin(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! horizontalBias(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! horizontalChainStyle(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! horizontalWeight(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! margin(int, int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! removeConstraints(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! removeFromHorizontalChain();
-    method public androidx.constraintlayout.widget.ConstraintProperties! removeFromVerticalChain();
-    method public androidx.constraintlayout.widget.ConstraintProperties! rotation(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! rotationX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! rotationY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! scaleX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! scaleY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! transformPivot(float, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! transformPivotX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! transformPivotY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translation(float, float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translationX(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translationY(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! translationZ(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! verticalBias(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! verticalChainStyle(int);
-    method public androidx.constraintlayout.widget.ConstraintProperties! verticalWeight(float);
-    method public androidx.constraintlayout.widget.ConstraintProperties! visibility(int);
-    field public static final int BASELINE = 5; // 0x5
-    field public static final int BOTTOM = 4; // 0x4
-    field public static final int END = 7; // 0x7
-    field public static final int LEFT = 1; // 0x1
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int RIGHT = 2; // 0x2
-    field public static final int START = 6; // 0x6
-    field public static final int TOP = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-  }
-
-  public class ConstraintSet {
-    ctor public ConstraintSet();
-    method public void addColorAttributes(java.lang.String!...!);
-    method public void addFloatAttributes(java.lang.String!...!);
-    method public void addIntAttributes(java.lang.String!...!);
-    method public void addStringAttributes(java.lang.String!...!);
-    method public void addToHorizontalChain(int, int, int);
-    method public void addToHorizontalChainRTL(int, int, int);
-    method public void addToVerticalChain(int, int, int);
-    method public void applyCustomAttributes(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void applyDeltaFrom(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void applyTo(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void applyToHelper(androidx.constraintlayout.widget.ConstraintHelper!, androidx.constraintlayout.core.widgets.ConstraintWidget!, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!, android.util.SparseArray<androidx.constraintlayout.core.widgets.ConstraintWidget!>!);
-    method public void applyToLayoutParams(int, androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!);
-    method public void applyToWithoutCustom(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public static androidx.constraintlayout.widget.ConstraintSet.Constraint! buildDelta(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public void center(int, int, int, int, int, int, int, float);
-    method public void centerHorizontally(int, int);
-    method public void centerHorizontally(int, int, int, int, int, int, int, float);
-    method public void centerHorizontallyRtl(int, int);
-    method public void centerHorizontallyRtl(int, int, int, int, int, int, int, float);
-    method public void centerVertically(int, int);
-    method public void centerVertically(int, int, int, int, int, int, int, float);
-    method public void clear(int);
-    method public void clear(int, int);
-    method public void clone(android.content.Context!, int);
-    method public void clone(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void clone(androidx.constraintlayout.widget.Constraints!);
-    method public void clone(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void connect(int, int, int, int);
-    method public void connect(int, int, int, int, int);
-    method public void constrainCircle(int, int, int, float);
-    method public void constrainDefaultHeight(int, int);
-    method public void constrainDefaultWidth(int, int);
-    method public void constrainHeight(int, int);
-    method public void constrainMaxHeight(int, int);
-    method public void constrainMaxWidth(int, int);
-    method public void constrainMinHeight(int, int);
-    method public void constrainMinWidth(int, int);
-    method public void constrainPercentHeight(int, float);
-    method public void constrainPercentWidth(int, float);
-    method public void constrainWidth(int, int);
-    method public void constrainedHeight(int, boolean);
-    method public void constrainedWidth(int, boolean);
-    method public void create(int, int);
-    method public void createBarrier(int, int, int, int...!);
-    method public void createHorizontalChain(int, int, int, int, int[]!, float[]!, int);
-    method public void createHorizontalChainRtl(int, int, int, int, int[]!, float[]!, int);
-    method public void createVerticalChain(int, int, int, int, int[]!, float[]!, int);
-    method public void dump(androidx.constraintlayout.motion.widget.MotionScene!, int...!);
-    method public boolean getApplyElevation(int);
-    method public androidx.constraintlayout.widget.ConstraintSet.Constraint! getConstraint(int);
-    method public java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>! getCustomAttributeSet();
-    method public int getHeight(int);
-    method public int[]! getKnownIds();
-    method public androidx.constraintlayout.widget.ConstraintSet.Constraint! getParameters(int);
-    method public int[]! getReferencedIds(int);
-    method public String![]! getStateLabels();
-    method public int getVisibility(int);
-    method public int getVisibilityMode(int);
-    method public int getWidth(int);
-    method public boolean isForceId();
-    method public boolean isValidateOnParse();
-    method public void load(android.content.Context!, int);
-    method public void load(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public boolean matchesLabels(java.lang.String!...!);
-    method public void parseColorAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void parseFloatAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void parseIntAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void parseStringAttributes(androidx.constraintlayout.widget.ConstraintSet.Constraint!, String!);
-    method public void readFallback(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void readFallback(androidx.constraintlayout.widget.ConstraintSet!);
-    method public void removeAttribute(String!);
-    method public void removeFromHorizontalChain(int);
-    method public void removeFromVerticalChain(int);
-    method public void setAlpha(int, float);
-    method public void setApplyElevation(int, boolean);
-    method public void setBarrierType(int, int);
-    method public void setColorValue(int, String!, int);
-    method public void setDimensionRatio(int, String!);
-    method public void setEditorAbsoluteX(int, int);
-    method public void setEditorAbsoluteY(int, int);
-    method public void setElevation(int, float);
-    method public void setFloatValue(int, String!, float);
-    method public void setForceId(boolean);
-    method public void setGoneMargin(int, int, int);
-    method public void setGuidelineBegin(int, int);
-    method public void setGuidelineEnd(int, int);
-    method public void setGuidelinePercent(int, float);
-    method public void setHorizontalBias(int, float);
-    method public void setHorizontalChainStyle(int, int);
-    method public void setHorizontalWeight(int, float);
-    method public void setIntValue(int, String!, int);
-    method public void setLayoutWrapBehavior(int, int);
-    method public void setMargin(int, int, int);
-    method public void setReferencedIds(int, int...!);
-    method public void setRotation(int, float);
-    method public void setRotationX(int, float);
-    method public void setRotationY(int, float);
-    method public void setScaleX(int, float);
-    method public void setScaleY(int, float);
-    method public void setStateLabels(String!);
-    method public void setStateLabelsList(java.lang.String!...!);
-    method public void setStringValue(int, String!, String!);
-    method public void setTransformPivot(int, float, float);
-    method public void setTransformPivotX(int, float);
-    method public void setTransformPivotY(int, float);
-    method public void setTranslation(int, float, float);
-    method public void setTranslationX(int, float);
-    method public void setTranslationY(int, float);
-    method public void setTranslationZ(int, float);
-    method public void setValidateOnParse(boolean);
-    method public void setVerticalBias(int, float);
-    method public void setVerticalChainStyle(int, int);
-    method public void setVerticalWeight(int, float);
-    method public void setVisibility(int, int);
-    method public void setVisibilityMode(int, int);
-    method public void writeState(java.io.Writer!, androidx.constraintlayout.widget.ConstraintLayout!, int) throws java.io.IOException;
-    field public static final int BASELINE = 5; // 0x5
-    field public static final int BOTTOM = 4; // 0x4
-    field public static final int CHAIN_PACKED = 2; // 0x2
-    field public static final int CHAIN_SPREAD = 0; // 0x0
-    field public static final int CHAIN_SPREAD_INSIDE = 1; // 0x1
-    field public static final int CIRCLE_REFERENCE = 8; // 0x8
-    field public static final int END = 7; // 0x7
-    field public static final int GONE = 8; // 0x8
-    field public static final int HORIZONTAL = 0; // 0x0
-    field public static final int HORIZONTAL_GUIDELINE = 0; // 0x0
-    field public static final int INVISIBLE = 4; // 0x4
-    field public static final int LEFT = 1; // 0x1
-    field public static final int MATCH_CONSTRAINT = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_PERCENT = 2; // 0x2
-    field public static final int MATCH_CONSTRAINT_SPREAD = 0; // 0x0
-    field public static final int MATCH_CONSTRAINT_WRAP = 1; // 0x1
-    field public static final int PARENT_ID = 0; // 0x0
-    field public static final int RIGHT = 2; // 0x2
-    field public static final int ROTATE_LEFT_OF_PORTRATE = 4; // 0x4
-    field public static final int ROTATE_NONE = 0; // 0x0
-    field public static final int ROTATE_PORTRATE_OF_LEFT = 2; // 0x2
-    field public static final int ROTATE_PORTRATE_OF_RIGHT = 1; // 0x1
-    field public static final int ROTATE_RIGHT_OF_PORTRATE = 3; // 0x3
-    field public static final int START = 6; // 0x6
-    field public static final int TOP = 3; // 0x3
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int VERTICAL = 1; // 0x1
-    field public static final int VERTICAL_GUIDELINE = 1; // 0x1
-    field public static final int VISIBILITY_MODE_IGNORE = 1; // 0x1
-    field public static final int VISIBILITY_MODE_NORMAL = 0; // 0x0
-    field public static final int VISIBLE = 0; // 0x0
-    field public static final int WRAP_CONTENT = -2; // 0xfffffffe
-    field public String! derivedState;
-    field public String! mIdString;
-    field public int mRotate;
-  }
-
-  public static class ConstraintSet.Constraint {
-    ctor public ConstraintSet.Constraint();
-    method public void applyDelta(androidx.constraintlayout.widget.ConstraintSet.Constraint!);
-    method public void applyTo(androidx.constraintlayout.widget.ConstraintLayout.LayoutParams!);
-    method public androidx.constraintlayout.widget.ConstraintSet.Constraint! clone();
-    method public void printDelta(String!);
-    field public final androidx.constraintlayout.widget.ConstraintSet.Layout! layout;
-    field public java.util.HashMap<java.lang.String!,androidx.constraintlayout.widget.ConstraintAttribute!>! mCustomConstraints;
-    field public final androidx.constraintlayout.widget.ConstraintSet.Motion! motion;
-    field public final androidx.constraintlayout.widget.ConstraintSet.PropertySet! propertySet;
-    field public final androidx.constraintlayout.widget.ConstraintSet.Transform! transform;
-  }
-
-  public static class ConstraintSet.Layout {
-    ctor public ConstraintSet.Layout();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.Layout!);
-    method public void dump(androidx.constraintlayout.motion.widget.MotionScene!, StringBuilder!);
-    field public static final int UNSET = -1; // 0xffffffff
-    field public static final int UNSET_GONE_MARGIN = -2147483648; // 0x80000000
-    field public int baselineMargin;
-    field public int baselineToBaseline;
-    field public int baselineToBottom;
-    field public int baselineToTop;
-    field public int bottomMargin;
-    field public int bottomToBottom;
-    field public int bottomToTop;
-    field public float circleAngle;
-    field public int circleConstraint;
-    field public int circleRadius;
-    field public boolean constrainedHeight;
-    field public boolean constrainedWidth;
-    field public String! dimensionRatio;
-    field public int editorAbsoluteX;
-    field public int editorAbsoluteY;
-    field public int endMargin;
-    field public int endToEnd;
-    field public int endToStart;
-    field public int goneBaselineMargin;
-    field public int goneBottomMargin;
-    field public int goneEndMargin;
-    field public int goneLeftMargin;
-    field public int goneRightMargin;
-    field public int goneStartMargin;
-    field public int goneTopMargin;
-    field public int guideBegin;
-    field public int guideEnd;
-    field public float guidePercent;
-    field public boolean guidelineUseRtl;
-    field public int heightDefault;
-    field public int heightMax;
-    field public int heightMin;
-    field public float heightPercent;
-    field public float horizontalBias;
-    field public int horizontalChainStyle;
-    field public float horizontalWeight;
-    field public int leftMargin;
-    field public int leftToLeft;
-    field public int leftToRight;
-    field public boolean mApply;
-    field public boolean mBarrierAllowsGoneWidgets;
-    field public int mBarrierDirection;
-    field public int mBarrierMargin;
-    field public String! mConstraintTag;
-    field public int mHeight;
-    field public int mHelperType;
-    field public boolean mIsGuideline;
-    field public boolean mOverride;
-    field public String! mReferenceIdString;
-    field public int[]! mReferenceIds;
-    field public int mWidth;
-    field public int mWrapBehavior;
-    field public int orientation;
-    field public int rightMargin;
-    field public int rightToLeft;
-    field public int rightToRight;
-    field public int startMargin;
-    field public int startToEnd;
-    field public int startToStart;
-    field public int topMargin;
-    field public int topToBottom;
-    field public int topToTop;
-    field public float verticalBias;
-    field public int verticalChainStyle;
-    field public float verticalWeight;
-    field public int widthDefault;
-    field public int widthMax;
-    field public int widthMin;
-    field public float widthPercent;
-  }
-
-  public static class ConstraintSet.Motion {
-    ctor public ConstraintSet.Motion();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.Motion!);
-    field public int mAnimateCircleAngleTo;
-    field public int mAnimateRelativeTo;
-    field public boolean mApply;
-    field public int mDrawPath;
-    field public float mMotionStagger;
-    field public int mPathMotionArc;
-    field public float mPathRotate;
-    field public int mPolarRelativeTo;
-    field public int mQuantizeInterpolatorID;
-    field public String! mQuantizeInterpolatorString;
-    field public int mQuantizeInterpolatorType;
-    field public float mQuantizeMotionPhase;
-    field public int mQuantizeMotionSteps;
-    field public String! mTransitionEasing;
-  }
-
-  public static class ConstraintSet.PropertySet {
-    ctor public ConstraintSet.PropertySet();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.PropertySet!);
-    field public float alpha;
-    field public boolean mApply;
-    field public float mProgress;
-    field public int mVisibilityMode;
-    field public int visibility;
-  }
-
-  public static class ConstraintSet.Transform {
-    ctor public ConstraintSet.Transform();
-    method public void copyFrom(androidx.constraintlayout.widget.ConstraintSet.Transform!);
-    field public boolean applyElevation;
-    field public float elevation;
-    field public boolean mApply;
-    field public float rotation;
-    field public float rotationX;
-    field public float rotationY;
-    field public float scaleX;
-    field public float scaleY;
-    field public int transformPivotTarget;
-    field public float transformPivotX;
-    field public float transformPivotY;
-    field public float translationX;
-    field public float translationY;
-    field public float translationZ;
-  }
-
-  public class Constraints extends android.view.ViewGroup {
-    ctor public Constraints(android.content.Context!);
-    ctor public Constraints(android.content.Context!, android.util.AttributeSet!);
-    ctor public Constraints(android.content.Context!, android.util.AttributeSet!, int);
-    method protected androidx.constraintlayout.widget.Constraints.LayoutParams! generateDefaultLayoutParams();
-    method public androidx.constraintlayout.widget.Constraints.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
-    method public androidx.constraintlayout.widget.ConstraintSet! getConstraintSet();
-    field public static final String TAG = "Constraints";
-  }
-
-  public static class Constraints.LayoutParams extends androidx.constraintlayout.widget.ConstraintLayout.LayoutParams {
-    ctor public Constraints.LayoutParams(android.content.Context!, android.util.AttributeSet!);
-    ctor public Constraints.LayoutParams(androidx.constraintlayout.widget.Constraints.LayoutParams!);
-    ctor public Constraints.LayoutParams(int, int);
-    field public float alpha;
-    field public boolean applyElevation;
-    field public float elevation;
-    field public float rotation;
-    field public float rotationX;
-    field public float rotationY;
-    field public float scaleX;
-    field public float scaleY;
-    field public float transformPivotX;
-    field public float transformPivotY;
-    field public float translationX;
-    field public float translationY;
-    field public float translationZ;
-  }
-
-  public abstract class ConstraintsChangedListener {
-    ctor public ConstraintsChangedListener();
-    method public void postLayoutChange(int, int);
-    method public void preLayoutChange(int, int);
-  }
-
-  public class Group extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public Group(android.content.Context!);
-    ctor public Group(android.content.Context!, android.util.AttributeSet!);
-    ctor public Group(android.content.Context!, android.util.AttributeSet!, int);
-    method public void onAttachedToWindow();
-  }
-
-  public class Guideline extends android.view.View {
-    ctor public Guideline(android.content.Context!);
-    ctor public Guideline(android.content.Context!, android.util.AttributeSet!);
-    ctor public Guideline(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public Guideline(android.content.Context!, android.util.AttributeSet!, int, int);
-    method public void setFilterRedundantCalls(boolean);
-    method public void setGuidelineBegin(int);
-    method public void setGuidelineEnd(int);
-    method public void setGuidelinePercent(float);
-  }
-
-  public class Placeholder extends android.view.View {
-    ctor public Placeholder(android.content.Context!);
-    ctor public Placeholder(android.content.Context!, android.util.AttributeSet!);
-    ctor public Placeholder(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public Placeholder(android.content.Context!, android.util.AttributeSet!, int, int);
-    method public android.view.View! getContent();
-    method public int getEmptyVisibility();
-    method public void onDraw(android.graphics.Canvas);
-    method public void setContentId(int);
-    method public void setEmptyVisibility(int);
-    method public void updatePostMeasure(androidx.constraintlayout.widget.ConstraintLayout!);
-    method public void updatePreLayout(androidx.constraintlayout.widget.ConstraintLayout!);
-  }
-
-  public class ReactiveGuide extends android.view.View implements androidx.constraintlayout.widget.SharedValues.SharedValuesListener {
-    ctor public ReactiveGuide(android.content.Context!);
-    ctor public ReactiveGuide(android.content.Context!, android.util.AttributeSet!);
-    ctor public ReactiveGuide(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public ReactiveGuide(android.content.Context!, android.util.AttributeSet!, int, int);
-    method public int getApplyToConstraintSetId();
-    method public int getAttributeId();
-    method public boolean isAnimatingChange();
-    method public void onNewValue(int, int, int);
-    method public void setAnimateChange(boolean);
-    method public void setApplyToConstraintSetId(int);
-    method public void setAttributeId(int);
-    method public void setGuidelineBegin(int);
-    method public void setGuidelineEnd(int);
-    method public void setGuidelinePercent(float);
-  }
-
-  public class SharedValues {
-    ctor public SharedValues();
-    method public void addListener(int, androidx.constraintlayout.widget.SharedValues.SharedValuesListener!);
-    method public void clearListeners();
-    method public void fireNewValue(int, int);
-    method public int getValue(int);
-    method public void removeListener(androidx.constraintlayout.widget.SharedValues.SharedValuesListener!);
-    method public void removeListener(int, androidx.constraintlayout.widget.SharedValues.SharedValuesListener!);
-    field public static final int UNSET = -1; // 0xffffffff
-  }
-
-  public static interface SharedValues.SharedValuesListener {
-    method public void onNewValue(int, int, int);
-  }
-
-  public class StateSet {
-    ctor public StateSet(android.content.Context!, org.xmlpull.v1.XmlPullParser!);
-    method public int convertToConstraintSet(int, int, float, float);
-    method public boolean needsToChange(int, float, float);
-    method public void setOnConstraintsChanged(androidx.constraintlayout.widget.ConstraintsChangedListener!);
-    method public int stateGetConstraintID(int, int, int);
-    method public int updateConstraints(int, int, float, float);
-    field public static final String TAG = "ConstraintLayoutStates";
-  }
-
-  public abstract class VirtualLayout extends androidx.constraintlayout.widget.ConstraintHelper {
-    ctor public VirtualLayout(android.content.Context!);
-    ctor public VirtualLayout(android.content.Context!, android.util.AttributeSet!);
-    ctor public VirtualLayout(android.content.Context!, android.util.AttributeSet!, int);
-    method public void onAttachedToWindow();
-    method public void onMeasure(androidx.constraintlayout.core.widgets.VirtualLayout!, int, int);
-  }
-
-}
-
diff --git a/constraintlayout/constraintlayout/build.gradle b/constraintlayout/constraintlayout/build.gradle
index 93842ed..9b2ddcd 100644
--- a/constraintlayout/constraintlayout/build.gradle
+++ b/constraintlayout/constraintlayout/build.gradle
@@ -49,5 +49,4 @@
     mavenVersion = LibraryVersions.CONSTRAINTLAYOUT
     inceptionYear = "2022"
     description = "This library offers a flexible and adaptable way to position and animate widgets"
-    metalavaK2UastEnabled = true
 }
diff --git a/contentpager/contentpager/build.gradle b/contentpager/contentpager/build.gradle
index 0a2cad7..c53f748 100644
--- a/contentpager/contentpager/build.gradle
+++ b/contentpager/contentpager/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
@@ -47,7 +47,6 @@
     inceptionYear = "2017"
     description = "Library providing support for paging across content exposed via a ContentProvider. Use of this library allows a client to avoid expensive interprocess \"cursor window swaps\" on the UI thread."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/contentpager/contentpager/lint-baseline.xml b/contentpager/contentpager/lint-baseline.xml
index 31b44c8..929c588a 100644
--- a/contentpager/contentpager/lint-baseline.xml
+++ b/contentpager/contentpager/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="NewApi"
@@ -20,24 +20,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.contentpager.content.ContentPager is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            extras = extras.deepCopy();"
-        errorLine2="                            ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/contentpager/content/ContentPager.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.contentpager.content.Query is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return resolver.query("
-        errorLine2="                            ~~~~~">
-        <location
-            file="src/main/java/androidx/contentpager/content/Query.java"/>
-    </issue>
-
-    <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
         errorLine1="    public ContentPager(ContentResolver resolver, QueryRunner queryRunner) {"
diff --git a/coordinatorlayout/coordinatorlayout/build.gradle b/coordinatorlayout/coordinatorlayout/build.gradle
index 4ac5cf4..554883a 100644
--- a/coordinatorlayout/coordinatorlayout/build.gradle
+++ b/coordinatorlayout/coordinatorlayout/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.3.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.customview:customview:1.0.0")
@@ -58,5 +58,4 @@
     inceptionYear = "2011"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/core/OWNERS b/core/OWNERS
index f3e0b3a..11981dc 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -11,8 +11,16 @@
 [email protected]
 
 # For text related files
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
 [email protected]
[email protected]
[email protected]
 [email protected]
[email protected]
 
 # For shortcut related files
 [email protected]
diff --git a/core/core-animation-integration-tests/testapp/build.gradle b/core/core-animation-integration-tests/testapp/build.gradle
index 6d99f5a..163dbce 100644
--- a/core/core-animation-integration-tests/testapp/build.gradle
+++ b/core/core-animation-integration-tests/testapp/build.gradle
@@ -20,7 +20,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.1.0")
     implementation(project(":core:core-animation"))
     implementation(project(":core:core-animation-testing"))
diff --git a/core/core-animation-testing/build.gradle b/core/core-animation-testing/build.gradle
index 22bfdb3..a2ce659 100644
--- a/core/core-animation-testing/build.gradle
+++ b/core/core-animation-testing/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.3.1")
     implementation(project(":core:core-animation"))
     implementation(libs.junit)
@@ -41,7 +41,6 @@
     mavenVersion = LibraryVersions.CORE_ANIMATION_TESTING
     inceptionYear = "2018"
     description = "This library provides functionalities for testing animations for API 14 and above."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/core/core-animation/build.gradle b/core/core-animation/build.gradle
index 0305a67..8e86935 100644
--- a/core/core-animation/build.gradle
+++ b/core/core-animation/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.13.0")
     implementation("androidx.collection:collection:1.1.0")
     implementation("androidx.tracing:tracing:1.0.0")
@@ -44,7 +44,6 @@
     mavenVersion = LibraryVersions.CORE_ANIMATION
     inceptionYear = "2018"
     description = "This library provides functionalities for creating and manipulating animations for API 14 and above."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java b/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java
index f55e292..849cf92 100644
--- a/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java
+++ b/core/core-animation/src/main/java/androidx/core/animation/PathUtils.java
@@ -19,7 +19,6 @@
 import android.graphics.PathMeasure;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 
 import java.util.ArrayList;
@@ -171,7 +170,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static float[] approximate(Path path, float acceptableError) {
             return path.approximate(acceptableError);
         }
diff --git a/core/core-appdigest/build.gradle b/core/core-appdigest/build.gradle
index 62c3512..f60ce51 100644
--- a/core/core-appdigest/build.gradle
+++ b/core/core-appdigest/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.0.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.7.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("org.bouncycastle:bcprov-jdk15on:1.65")
@@ -46,9 +46,9 @@
     mavenVersion = LibraryVersions.CORE_APPDIGEST
     inceptionYear = "2020"
     description = "AndroidX AppDigest Library"
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.core.appdigest"
 }
diff --git a/core/core-google-shortcuts/build.gradle b/core/core-google-shortcuts/build.gradle
index 83f4b43..c3aa5c6 100644
--- a/core/core-google-shortcuts/build.gradle
+++ b/core/core-google-shortcuts/build.gradle
@@ -54,6 +54,5 @@
     mavenVersion = LibraryVersions.CORE_GOOGLE_SHORTCUTS
     inceptionYear = "2021"
     description = "Library for powering Google features with Android app shortcuts"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core-graphics-integration-tests/testapp/build.gradle b/core/core-graphics-integration-tests/testapp/build.gradle
index ea79373..5ed44fb 100644
--- a/core/core-graphics-integration-tests/testapp/build.gradle
+++ b/core/core-graphics-integration-tests/testapp/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         applicationId "androidx.core.graphics.sample"
     }
@@ -30,7 +31,7 @@
 dependencies {
     implementation(project(":core:core-ktx"))
     implementation(projectOrArtifact(":appcompat:appcompat"))
-    implementation("androidx.annotation:annotation:1.8.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     compileOnly(project(":annotation:annotation-sampled"))
 }
 
diff --git a/core/core-i18n/api/current.txt b/core/core-i18n/api/current.txt
index 5a85814..8c19f6c 100644
--- a/core/core-i18n/api/current.txt
+++ b/core/core-i18n/api/current.txt
@@ -73,6 +73,7 @@
   }
 
   public static final class DateTimeFormatterSkeletonOptions.Builder {
+    ctor public DateTimeFormatterSkeletonOptions.Builder();
     ctor public DateTimeFormatterSkeletonOptions.Builder(optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Era era, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Year year, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Month month, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Day day, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.WeekDay weekDay, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Period period, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Hour hour, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Minute minute, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Second second, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.FractionalSecond fractionalSecond, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Timezone timezone);
     method public androidx.core.i18n.DateTimeFormatterSkeletonOptions build();
     method public androidx.core.i18n.DateTimeFormatterSkeletonOptions.Builder setDay(androidx.core.i18n.DateTimeFormatterSkeletonOptions.Day day);
diff --git a/core/core-i18n/api/restricted_current.txt b/core/core-i18n/api/restricted_current.txt
index 5a85814..8c19f6c 100644
--- a/core/core-i18n/api/restricted_current.txt
+++ b/core/core-i18n/api/restricted_current.txt
@@ -73,6 +73,7 @@
   }
 
   public static final class DateTimeFormatterSkeletonOptions.Builder {
+    ctor public DateTimeFormatterSkeletonOptions.Builder();
     ctor public DateTimeFormatterSkeletonOptions.Builder(optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Era era, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Year year, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Month month, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Day day, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.WeekDay weekDay, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Period period, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Hour hour, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Minute minute, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Second second, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.FractionalSecond fractionalSecond, optional androidx.core.i18n.DateTimeFormatterSkeletonOptions.Timezone timezone);
     method public androidx.core.i18n.DateTimeFormatterSkeletonOptions build();
     method public androidx.core.i18n.DateTimeFormatterSkeletonOptions.Builder setDay(androidx.core.i18n.DateTimeFormatterSkeletonOptions.Day day);
diff --git a/core/core-i18n/build.gradle b/core/core-i18n/build.gradle
index bdbdfdf..eca06e5 100644
--- a/core/core-i18n/build.gradle
+++ b/core/core-i18n/build.gradle
@@ -32,7 +32,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     // Add dependencies here
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     androidTestImplementation(libs.guavaAndroid)
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.kotlinTest)
@@ -48,11 +48,11 @@
     mavenVersion = LibraryVersions.CORE_I18N
     inceptionYear = "2022"
     description = "This library provides functionality for good internationalization (messages, plurals, date / time formatting)."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.core.i18n"
     defaultConfig {
     }
diff --git a/core/core-i18n/src/main/java/androidx/core/i18n/DateTimeFormatter.kt b/core/core-i18n/src/main/java/androidx/core/i18n/DateTimeFormatter.kt
index eb7e29d..00dad2c 100644
--- a/core/core-i18n/src/main/java/androidx/core/i18n/DateTimeFormatter.kt
+++ b/core/core-i18n/src/main/java/androidx/core/i18n/DateTimeFormatter.kt
@@ -21,7 +21,6 @@
 import android.os.Build
 import android.provider.Settings
 import android.text.format.DateFormat
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.i18n.LocaleCompatUtils.getDefaultFormattingLocale
 import java.util.Calendar
@@ -160,7 +159,6 @@
         @RequiresApi(Build.VERSION_CODES.N)
         private class Api24Utils {
             companion object {
-                @DoNotInline
                 fun is24HourLocale(locale: Locale): Boolean {
                     val tmpDf = android.icu.text.DateFormat.getInstanceForSkeleton("jm", locale)
                     val tmpPattern =
diff --git a/core/core-ktx/build.gradle b/core/core-ktx/build.gradle
index f637c66..39b3cef 100644
--- a/core/core-ktx/build.gradle
+++ b/core/core-ktx/build.gradle
@@ -20,7 +20,7 @@
     }
 
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":core:core"))
 
     androidTestImplementation(libs.junit)
@@ -38,10 +38,10 @@
     mavenVersion = LibraryVersions.CORE
     inceptionYear = "2018"
     description = "Kotlin extensions for 'core' artifact"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.core.ktx"
 }
diff --git a/core/core-ktx/lint-baseline.xml b/core/core-ktx/lint-baseline.xml
index 7597446..7ddaf5f 100644
--- a/core/core-ktx/lint-baseline.xml
+++ b/core/core-ktx/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="                } else if (Build.VERSION.SDK_INT >= 21 &amp;&amp; value is Size) {"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                    } else if (Build.VERSION.SDK_INT >= 21 &amp;&amp; value is Size) {"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/os/Bundle.kt"/>
     </issue>
@@ -13,8 +13,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="                } else if (Build.VERSION.SDK_INT >= 21 &amp;&amp; value is SizeF) {"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                    } else if (Build.VERSION.SDK_INT >= 21 &amp;&amp; value is SizeF) {"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/os/Bundle.kt"/>
     </issue>
@@ -121,7 +121,7 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(21)"
+        errorLine1="@RequiresApi(21) public inline operator fun Size.component1(): Int = width"
         errorLine2="~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/util/Size.kt"/>
@@ -130,7 +130,7 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(21)"
+        errorLine1="@RequiresApi(21) public inline operator fun Size.component2(): Int = height"
         errorLine2="~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/util/Size.kt"/>
@@ -139,7 +139,7 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(21)"
+        errorLine1="@RequiresApi(21) public inline operator fun SizeF.component1(): Float = width"
         errorLine2="~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/util/Size.kt"/>
@@ -148,7 +148,7 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(21)"
+        errorLine1="@RequiresApi(21) public inline operator fun SizeF.component2(): Float = height"
         errorLine2="~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/core/util/Size.kt"/>
diff --git a/core/core-ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt b/core/core-ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt
index 0cd76e1..1aa472a 100644
--- a/core/core-ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt
+++ b/core/core-ktx/src/androidTest/java/androidx/core/graphics/PathTest.kt
@@ -60,7 +60,7 @@
 
         val p = r1 + r2
         val r = RectF()
-        p.computeBounds(r, true)
+        @Suppress("DEPRECATION") p.computeBounds(r, true)
 
         assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
     }
@@ -72,7 +72,7 @@
 
         val p = r1 or r2
         val r = RectF()
-        p.computeBounds(r, true)
+        @Suppress("DEPRECATION") p.computeBounds(r, true)
 
         assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
     }
@@ -84,7 +84,7 @@
 
         val p = r1 - r2
         val r = RectF()
-        p.computeBounds(r, true)
+        @Suppress("DEPRECATION") p.computeBounds(r, true)
 
         assertEquals(RectF(0.0f, 0.0f, 5.0f, 10.0f), r)
     }
@@ -96,7 +96,7 @@
 
         val p = r1 and r2
         val r = RectF()
-        p.computeBounds(r, true)
+        @Suppress("DEPRECATION") p.computeBounds(r, true)
 
         assertEquals(RectF(5.0f, 0.0f, 10.0f, 10.0f), r)
     }
@@ -117,7 +117,7 @@
 
         val p = r1 xor r2
         val r = RectF()
-        p.computeBounds(r, true)
+        @Suppress("DEPRECATION") p.computeBounds(r, true)
 
         assertEquals(RectF(0.0f, 0.0f, 15.0f, 15.0f), r)
     }
diff --git a/core/core-ktx/src/main/java/androidx/core/content/res/TypedArray.kt b/core/core-ktx/src/main/java/androidx/core/content/res/TypedArray.kt
index a05cc64..90cb99d 100644
--- a/core/core-ktx/src/main/java/androidx/core/content/res/TypedArray.kt
+++ b/core/core-ktx/src/main/java/androidx/core/content/res/TypedArray.kt
@@ -23,7 +23,6 @@
 import androidx.annotation.AnyRes
 import androidx.annotation.ColorInt
 import androidx.annotation.Dimension
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.StyleableRes
 
@@ -231,7 +230,6 @@
 
 @RequiresApi(26)
 private object TypedArrayApi26ImplKt {
-    @DoNotInline
     @JvmStatic
     fun getFont(typedArray: TypedArray, @StyleableRes index: Int): Typeface {
         return typedArray.getFont(index)!!
diff --git a/core/core-ktx/src/main/java/androidx/core/os/Bundle.kt b/core/core-ktx/src/main/java/androidx/core/os/Bundle.kt
index 98b13b3..6081d81 100644
--- a/core/core-ktx/src/main/java/androidx/core/os/Bundle.kt
+++ b/core/core-ktx/src/main/java/androidx/core/os/Bundle.kt
@@ -22,7 +22,6 @@
 import android.os.Parcelable
 import android.util.Size
 import android.util.SizeF
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import java.io.Serializable
 
@@ -114,11 +113,8 @@
 
 @RequiresApi(21)
 private object BundleApi21ImplKt {
-    @DoNotInline
-    @JvmStatic
-    fun putSize(bundle: Bundle, key: String, value: Size?) = bundle.putSize(key, value)
+    @JvmStatic fun putSize(bundle: Bundle, key: String, value: Size?) = bundle.putSize(key, value)
 
-    @DoNotInline
     @JvmStatic
     fun putSizeF(bundle: Bundle, key: String, value: SizeF?) = bundle.putSizeF(key, value)
 }
diff --git a/core/core-ktx/src/main/java/androidx/core/os/PersistableBundle.kt b/core/core-ktx/src/main/java/androidx/core/os/PersistableBundle.kt
index f5c8a31..e5f4ef2 100644
--- a/core/core-ktx/src/main/java/androidx/core/os/PersistableBundle.kt
+++ b/core/core-ktx/src/main/java/androidx/core/os/PersistableBundle.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.os.PersistableBundle
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 /**
@@ -68,11 +67,9 @@
 // Jetifier to keep them grouped with other members of the core-ktx module.
 @RequiresApi(21)
 private object PersistableBundleApi21ImplKt {
-    @DoNotInline
     @JvmStatic
     fun createPersistableBundle(capacity: Int): PersistableBundle = PersistableBundle(capacity)
 
-    @DoNotInline
     @JvmStatic
     fun putValue(persistableBundle: PersistableBundle, key: String?, value: Any?) {
         persistableBundle.apply {
@@ -142,13 +139,11 @@
 
 @RequiresApi(22)
 private object PersistableBundleApi22ImplKt {
-    @DoNotInline
     @JvmStatic
     fun putBoolean(persistableBundle: PersistableBundle, key: String?, value: Boolean) {
         persistableBundle.putBoolean(key, value)
     }
 
-    @DoNotInline
     @JvmStatic
     fun putBooleanArray(persistableBundle: PersistableBundle, key: String?, value: BooleanArray) {
         persistableBundle.putBooleanArray(key, value)
diff --git a/core/core-ktx/src/main/java/androidx/core/view/ViewGroup.kt b/core/core-ktx/src/main/java/androidx/core/view/ViewGroup.kt
index 2577ae6..915b7a2 100644
--- a/core/core-ktx/src/main/java/androidx/core/view/ViewGroup.kt
+++ b/core/core-ktx/src/main/java/androidx/core/view/ViewGroup.kt
@@ -21,6 +21,7 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.annotation.Px
+import kotlin.collections.removeLast as removeLastKt
 
 /**
  * Returns the view at [index].
@@ -170,7 +171,10 @@
         } else {
             while (!iterator.hasNext() && stack.isNotEmpty()) {
                 iterator = stack.last()
-                stack.removeLast()
+                // MutableCollections.removeLast() is shadowed by java.util.list.removeAt()
+                // which was added in sdk 35 making this call unsafe
+                // stack.removeLast()
+                stack.removeLastKt()
             }
         }
     }
diff --git a/core/core-location-altitude/build.gradle b/core/core-location-altitude/build.gradle
index cc1cc8c..aa41391 100644
--- a/core/core-location-altitude/build.gradle
+++ b/core/core-location-altitude/build.gradle
@@ -41,7 +41,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.5.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation(project(":core:core-location-altitude-proto"))
     implementation(libs.autoValueAnnotations)
@@ -64,6 +64,5 @@
     mavenVersion = LibraryVersions.CORE_LOCATION_ALTITUDE
     inceptionYear = "2022"
     description = "Provides compatibility APIs concerning location altitudes."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java
index 555aaa0..c172d59 100644
--- a/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java
+++ b/core/core-location-altitude/src/main/java/androidx/core/location/altitude/AltitudeConverterCompat.java
@@ -20,7 +20,6 @@
 import android.location.Location;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -99,7 +98,6 @@
         private Api34Impl() {
         }
 
-        @DoNotInline
         static void addMslAltitudeToLocation(@NonNull Context context,
                 @NonNull Location location) throws IOException {
             android.location.altitude.AltitudeConverter altitudeConverter;
diff --git a/core/core-performance-play-services/build.gradle b/core/core-performance-play-services/build.gradle
index cb6cd55..ee10d9a 100644
--- a/core/core-performance-play-services/build.gradle
+++ b/core/core-performance-play-services/build.gradle
@@ -58,6 +58,5 @@
     mavenVersion = LibraryVersions.CORE_PERFORMANCE
     inceptionYear = "2023"
     description = "Get media performance class data from Google."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core-performance-testing/build.gradle b/core/core-performance-testing/build.gradle
index 48d6565..9062bf1 100644
--- a/core/core-performance-testing/build.gradle
+++ b/core/core-performance-testing/build.gradle
@@ -52,6 +52,5 @@
     mavenVersion = LibraryVersions.CORE_PERFORMANCE
     inceptionYear = "2023"
     description = "Test support for core-performance."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core-performance/build.gradle b/core/core-performance/build.gradle
index c10645d..7b6a8bb 100644
--- a/core/core-performance/build.gradle
+++ b/core/core-performance/build.gradle
@@ -49,7 +49,6 @@
     mavenVersion = LibraryVersions.CORE_PERFORMANCE
     inceptionYear = "2021"
     description = "This library makes it easy for developers to make UI and feature choices based on Android Performance Class level for GMS devices."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":core:core-performance:core-performance-samples"))
 }
diff --git a/core/core-remoteviews/build.gradle b/core/core-remoteviews/build.gradle
index 94271650..f8704fb 100644
--- a/core/core-remoteviews/build.gradle
+++ b/core/core-remoteviews/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.8.0")
 
     androidTestImplementation("androidx.core:core:1.8.0")
@@ -69,6 +69,5 @@
     mavenVersion = LibraryVersions.CORE_REMOTEVIEWS
     inceptionYear = "2021"
     description = "AndroidX RemoteViews Support"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core-remoteviews/src/main/java/androidx/core/widget/AppWidgetManagerCompat.kt b/core/core-remoteviews/src/main/java/androidx/core/widget/AppWidgetManagerCompat.kt
index da73cb4..071601c 100644
--- a/core/core-remoteviews/src/main/java/androidx/core/widget/AppWidgetManagerCompat.kt
+++ b/core/core-remoteviews/src/main/java/androidx/core/widget/AppWidgetManagerCompat.kt
@@ -24,7 +24,6 @@
 import android.util.Log
 import android.util.SizeF
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.util.SizeFCompat
 import kotlin.math.ceil
@@ -183,7 +182,6 @@
 @RequiresApi(31)
 @Suppress("DEPRECATION")
 private object AppWidgetManagerApi31Impl {
-    @DoNotInline
     fun createExactSizeAppWidget(
         appWidgetManager: AppWidgetManager,
         appWidgetId: Int,
@@ -202,7 +200,6 @@
         return RemoteViews(sizes.associateWith { factory(it.toSizeFCompat()) })
     }
 
-    @DoNotInline
     fun createResponsiveSizeAppWidget(
         dpSizes: Collection<SizeFCompat>,
         factory: (SizeFCompat) -> RemoteViews
diff --git a/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt b/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt
index 5c71ce5..44558cc 100644
--- a/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt
+++ b/core/core-remoteviews/src/main/java/androidx/core/widget/RemoteViewsCompat.kt
@@ -30,7 +30,6 @@
 import androidx.annotation.ColorInt
 import androidx.annotation.ColorRes
 import androidx.annotation.DimenRes
-import androidx.annotation.DoNotInline
 import androidx.annotation.DrawableRes
 import androidx.annotation.IdRes
 import androidx.annotation.LayoutRes
@@ -256,7 +255,6 @@
      */
     @RequiresApi(31)
     private object CollectionItemsApi31Impl {
-        @DoNotInline
         fun setRemoteAdapter(remoteViews: RemoteViews, viewId: Int, items: RemoteCollectionItems) {
             remoteViews.setRemoteAdapter(viewId, toPlatformCollectionItems(items))
         }
@@ -264,7 +262,6 @@
         /**
          * Returns a [RemoteViews.RemoteCollectionItems] equivalent to this [RemoteCollectionItems].
          */
-        @DoNotInline
         private fun toPlatformCollectionItems(
             items: RemoteCollectionItems
         ): RemoteViews.RemoteCollectionItems {
@@ -3763,7 +3760,6 @@
 
     @RequiresApi(23)
     private object Api23Impl {
-        @DoNotInline
         @JvmStatic
         fun setIcon(rv: RemoteViews, @IdRes id: Int, method: String, icon: Icon?) {
             rv.setIcon(id, method, icon)
@@ -3772,13 +3768,11 @@
 
     @RequiresApi(31)
     private object Api31Impl {
-        @DoNotInline
         @JvmStatic
         fun setBlendMode(rv: RemoteViews, @IdRes id: Int, method: String, mode: BlendMode?) {
             rv.setBlendMode(id, method, mode)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setCharSequence(
             rv: RemoteViews,
@@ -3789,7 +3783,6 @@
             rv.setCharSequence(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setCharSequenceAttr(
             rv: RemoteViews,
@@ -3800,19 +3793,16 @@
             rv.setCharSequenceAttr(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setColor(rv: RemoteViews, @IdRes id: Int, method: String, @ColorRes resId: Int) {
             rv.setColor(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setColorAttr(rv: RemoteViews, @IdRes id: Int, method: String, @AttrRes resId: Int) {
             rv.setColorAttr(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setColorInt(
             rv: RemoteViews,
@@ -3824,7 +3814,6 @@
             rv.setColorInt(id, method, notNight, night)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setColorStateList(
             rv: RemoteViews,
@@ -3835,7 +3824,6 @@
             rv.setColorStateList(id, method, colorStateList)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setColorStateList(
             rv: RemoteViews,
@@ -3847,7 +3835,6 @@
             rv.setColorStateList(id, method, notNight, night)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setColorStateList(
             rv: RemoteViews,
@@ -3858,7 +3845,6 @@
             rv.setColorStateList(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setColorStateListAttr(
             rv: RemoteViews,
@@ -3869,7 +3855,6 @@
             rv.setColorStateListAttr(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setIcon(
             rv: RemoteViews,
@@ -3881,25 +3866,21 @@
             rv.setIcon(id, method, notNight, night)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setIntDimen(rv: RemoteViews, @IdRes id: Int, method: String, value: Float, unit: Int) {
             rv.setIntDimen(id, method, value, unit)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setIntDimen(rv: RemoteViews, @IdRes id: Int, method: String, @DimenRes resId: Int) {
             rv.setIntDimen(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setIntDimenAttr(rv: RemoteViews, @IdRes id: Int, method: String, @AttrRes resId: Int) {
             rv.setIntDimenAttr(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setFloatDimen(
             rv: RemoteViews,
@@ -3911,13 +3892,11 @@
             rv.setFloatDimen(id, method, value, unit)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setFloatDimen(rv: RemoteViews, @IdRes id: Int, method: String, @DimenRes resId: Int) {
             rv.setFloatDimen(id, method, resId)
         }
 
-        @DoNotInline
         @JvmStatic
         fun setFloatDimenAttr(
             rv: RemoteViews,
diff --git a/core/core-role/build.gradle b/core/core-role/build.gradle
index 71f0515..a31f66f 100644
--- a/core/core-role/build.gradle
+++ b/core/core-role/build.gradle
@@ -26,7 +26,6 @@
     mavenVersion = LibraryVersions.CORE_ROLE
     inceptionYear = "2019"
     description = "This Support Library provides names and documentation for roles."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/core/core-splashscreen/build.gradle b/core/core-splashscreen/build.gradle
index b84cd7d..04bff57 100644
--- a/core/core-splashscreen/build.gradle
+++ b/core/core-splashscreen/build.gradle
@@ -30,13 +30,15 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.core.splashscreen"
 }
 
 dependencies {
     api(libs.kotlinStdlib)
 
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
+    implementation("androidx.appcompat:appcompat-resources:1.7.0")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRunner)
@@ -54,6 +56,5 @@
     inceptionYear = "2021"
     description = "This library provides the compatibility APIs for SplashScreen " +
             "and helper method to enable a splashscreen on devices prior Android 12"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core-splashscreen/samples/build.gradle b/core/core-splashscreen/samples/build.gradle
index 73c9942..404af32 100644
--- a/core/core-splashscreen/samples/build.gradle
+++ b/core/core-splashscreen/samples/build.gradle
@@ -31,6 +31,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         applicationId "androidx.core.splashscreen.samples"
     }
@@ -41,6 +42,6 @@
     implementation(project(":core:core-splashscreen"))
     implementation(project(":core:core-ktx"))
     implementation(projectOrArtifact(":appcompat:appcompat"))
-    implementation("androidx.annotation:annotation:1.8.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation(projectOrArtifact(":interpolator:interpolator"))
 }
diff --git a/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt b/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt
index 08298e9..5026388 100644
--- a/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt
+++ b/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt
@@ -35,6 +35,7 @@
 import android.window.SplashScreenView
 import androidx.annotation.MainThread
 import androidx.annotation.RequiresApi
+import androidx.appcompat.content.res.AppCompatResources
 import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
 import androidx.core.splashscreen.SplashScreen.KeepOnScreenCondition
 
@@ -253,7 +254,7 @@
                     true
                 )
             ) {
-                icon = currentTheme.getDrawable(typedValue.resourceId)
+                icon = AppCompatResources.getDrawable(activity, typedValue.resourceId)
             }
 
             if (currentTheme.resolveAttribute(R.attr.splashScreenIconSize, typedValue, true)) {
@@ -346,7 +347,8 @@
                 if (hasBackground) {
                     // If the splash screen has an icon background we need to mask both the
                     // background and foreground.
-                    val iconBackgroundDrawable = context.getDrawable(R.drawable.icon_background)
+                    val iconBackgroundDrawable =
+                        AppCompatResources.getDrawable(context, R.drawable.icon_background)
 
                     val iconSize =
                         resources.getDimension(R.dimen.splashscreen_icon_size_with_background)
@@ -466,6 +468,7 @@
          *
          * To fix this, we apply these attributes as soon as the [SplashScreenView] is visible.
          */
+        @Suppress("DEPRECATION")
         private fun applyAppSystemUiTheme() {
             val tv = TypedValue()
             val theme = activity.theme
diff --git a/core/core-splashscreen/src/main/java/androidx/core/splashscreen/ThemeUtils.kt b/core/core-splashscreen/src/main/java/androidx/core/splashscreen/ThemeUtils.kt
index 653e9a6..beb2b10 100644
--- a/core/core-splashscreen/src/main/java/androidx/core/splashscreen/ThemeUtils.kt
+++ b/core/core-splashscreen/src/main/java/androidx/core/splashscreen/ThemeUtils.kt
@@ -20,7 +20,6 @@
 import android.util.TypedValue
 import android.view.View
 import android.view.WindowInsetsController
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 /**
@@ -39,7 +38,6 @@
          */
         @JvmStatic
         @JvmOverloads
-        @DoNotInline
         fun applyThemesSystemBarAppearance(
             theme: Resources.Theme,
             decor: View,
diff --git a/core/core-telecom/api/current.txt b/core/core-telecom/api/current.txt
index d4a855f..6dd11ef 100644
--- a/core/core-telecom/api/current.txt
+++ b/core/core-telecom/api/current.txt
@@ -75,6 +75,7 @@
   }
 
   public final class CallException extends java.lang.RuntimeException {
+    ctor public CallException();
     ctor public CallException(optional int code);
     method public int getCode();
     property public final int code;
diff --git a/core/core-telecom/api/restricted_current.txt b/core/core-telecom/api/restricted_current.txt
index d4a855f..6dd11ef 100644
--- a/core/core-telecom/api/restricted_current.txt
+++ b/core/core-telecom/api/restricted_current.txt
@@ -75,6 +75,7 @@
   }
 
   public final class CallException extends java.lang.RuntimeException {
+    ctor public CallException();
     ctor public CallException(optional int code);
     method public int getCode();
     property public final int code;
diff --git a/core/core-telecom/build.gradle b/core/core-telecom/build.gradle
index 6600cb1..81079a0 100644
--- a/core/core-telecom/build.gradle
+++ b/core/core-telecom/build.gradle
@@ -33,7 +33,7 @@
     // core-telecom dependencies
     api(libs.kotlinStdlib)
     api(libs.guavaListenableFuture)
-    implementation("androidx.annotation:annotation:1.4.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     // @OptIn annotations
     api("androidx.annotation:annotation-experimental:1.4.1")
     implementation("androidx.core:core:1.9.0")
@@ -65,6 +65,5 @@
     mavenVersion = LibraryVersions.CORE_TELECOM
     inceptionYear = "2023"
     description = "Integrate VoIP calls with the Telecom framework."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core-telecom/integration-tests/testapp/build.gradle b/core/core-telecom/integration-tests/testapp/build.gradle
index a679012..a4d9ade 100644
--- a/core/core-telecom/integration-tests/testapp/build.gradle
+++ b/core/core-telecom/integration-tests/testapp/build.gradle
@@ -41,7 +41,7 @@
 
 dependencies {
     implementation(libs.constraintLayout)
-    implementation("androidx.annotation:annotation:1.4.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.9.0")
     implementation(project(":core:core-telecom"))
     implementation('androidx.appcompat:appcompat:1.6.1')
diff --git a/core/core-telecom/lint-baseline.xml b/core/core-telecom/lint-baseline.xml
deleted file mode 100644
index f0d5969..0000000
--- a/core/core-telecom/lint-baseline.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.core.telecom.util.ExperimentalAppActions` or `@OptIn(markerClass = androidx.core.telecom.util.ExperimentalAppActions.class)`"
-        errorLine1="    val voipParticipantActionRequestsChannel: Channel&lt;VoipParticipantActionRequest> ="
-        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/telecom/internal/CallChannels.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.core.telecom.util.ExperimentalAppActions` or `@OptIn(markerClass = androidx.core.telecom.util.ExperimentalAppActions.class)`"
-        errorLine1="        fun toCallCompat(call: Call, scope: CoroutineScope, init: CallCompat.() -> Unit):"
-        errorLine2="                                                                  ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/telecom/internal/CallCompat.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.core.telecom.util.ExperimentalAppActions` or `@OptIn(markerClass = androidx.core.telecom.util.ExperimentalAppActions.class)`"
-        errorLine1="    private var mCapabilities: MutableList&lt;androidx.core.telecom.extensions.Capability> ="
-        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/telecom/CallsManager.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.core.telecom.util.ExperimentalAppActions` or `@OptIn(markerClass = androidx.core.telecom.util.ExperimentalAppActions.class)`"
-        errorLine1="        capabilities: List&lt;androidx.core.telecom.extensions.Capability>"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/telecom/CallsManager.kt"/>
-    </issue>
-
-    <issue
-        id="UnsafeOptInUsageError"
-        message="This declaration is opt-in and its usage should be marked with `@androidx.core.telecom.util.ExperimentalAppActions` or `@OptIn(markerClass = androidx.core.telecom.util.ExperimentalAppActions.class)`"
-        errorLine1="    val participantsStateFlow: StateFlow&lt;Set&lt;Participant>>"
-        errorLine2="                                             ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/telecom/extensions/ParticipantClientExtension.kt"/>
-    </issue>
-
-</issues>
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallSessionTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallSessionTest.kt
index 375ad1b..9dd0e39 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallSessionTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallSessionTest.kt
@@ -20,6 +20,7 @@
 import android.os.ParcelUuid
 import android.telecom.CallEndpoint
 import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallEndpointCompat
 import androidx.core.telecom.internal.CallChannels
 import androidx.core.telecom.internal.CallSession
 import androidx.core.telecom.test.utils.BaseTelecomTest
@@ -49,6 +50,60 @@
 @RequiresApi(VERSION_CODES.UPSIDE_DOWN_CAKE)
 @RunWith(AndroidJUnit4::class)
 class CallSessionTest : BaseTelecomTest() {
+    val mEarpieceEndpoint = CallEndpointCompat("EARPIECE", CallEndpoint.TYPE_EARPIECE)
+    val mSpeakerEndpoint = CallEndpointCompat("SPEAKER", CallEndpoint.TYPE_SPEAKER)
+    val mBluetoothEndpoint = CallEndpointCompat("BLUETOOTH", CallEndpoint.TYPE_BLUETOOTH)
+    val mEarAndSpeakerEndpoints = listOf(mEarpieceEndpoint, mSpeakerEndpoint)
+    val mEarAndSpeakerAndBtEndpoints =
+        listOf(mEarpieceEndpoint, mSpeakerEndpoint, mBluetoothEndpoint)
+
+    /**
+     * verify maybeDelaySwitchToSpeaker does NOT switch to speakerphone if the bluetooth device
+     * connects after 1 second
+     */
+    @SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @SmallTest
+    @Test
+    fun testDelayedSwitchToSpeakerBluetoothConnects() {
+        setUpV2Test()
+        runBlocking {
+            val callSession = initCallSession(coroutineContext, CallChannels())
+            callSession.setCurrentCallEndpoint(mBluetoothEndpoint)
+            callSession.setAvailableCallEndpoints(mEarAndSpeakerAndBtEndpoints)
+            assertFalse(callSession.maybeDelaySwitchToSpeaker(mSpeakerEndpoint))
+        }
+    }
+
+    /**
+     * verify maybeDelaySwitchToSpeaker switches to speaker if a BT device is not in the available
+     * list of call endpoints
+     */
+    @SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @SmallTest
+    @Test
+    fun testDelayedSwitchToSpeakerNoBluetoothAvailable() {
+        setUpV2Test()
+        runBlocking {
+            val callSession = initCallSession(coroutineContext, CallChannels())
+            callSession.setCurrentCallEndpoint(mEarpieceEndpoint)
+            callSession.setAvailableCallEndpoints(mEarAndSpeakerEndpoints)
+            assertTrue(callSession.maybeDelaySwitchToSpeaker(mSpeakerEndpoint))
+        }
+    }
+
+    /** verify maybeDelaySwitchToSpeaker switches to speaker if a BT failed to connect in time */
+    @SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @SmallTest
+    @Test
+    fun testDelayedSwitchToSpeakerBluetoothDidNotConnectInTime() {
+        setUpV2Test()
+        runBlocking {
+            val callSession = initCallSession(coroutineContext, CallChannels())
+            callSession.setCurrentCallEndpoint(mEarpieceEndpoint)
+            callSession.setAvailableCallEndpoints(mEarAndSpeakerAndBtEndpoints)
+            assertTrue(callSession.maybeDelaySwitchToSpeaker(mSpeakerEndpoint))
+        }
+    }
 
     /** verify the CallEvent CompletableDeferred objects complete after endpoints are echoed. */
     @SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -58,16 +113,16 @@
         setUpV2Test()
         runBlocking {
             val callChannels = CallChannels()
-            val callEvents = initCallEvents(coroutineContext, callChannels)
+            val callSession = initCallSession(coroutineContext, callChannels)
 
-            assertFalse(callEvents.getIsAvailableEndpointsSet().isCompleted)
-            assertFalse(callEvents.getIsCurrentEndpointSet().isCompleted)
+            assertFalse(callSession.getIsAvailableEndpointsSet().isCompleted)
+            assertFalse(callSession.getIsCurrentEndpointSet().isCompleted)
 
-            callEvents.onCallEndpointChanged(getCurrentEndpoint())
-            callEvents.onAvailableCallEndpointsChanged(getAvailableEndpoint())
+            callSession.onCallEndpointChanged(getCurrentEndpoint())
+            callSession.onAvailableCallEndpointsChanged(getAvailableEndpoint())
 
-            assertTrue(callEvents.getIsAvailableEndpointsSet().isCompleted)
-            assertTrue(callEvents.getIsCurrentEndpointSet().isCompleted)
+            assertTrue(callSession.getIsAvailableEndpointsSet().isCompleted)
+            assertTrue(callSession.getIsCurrentEndpointSet().isCompleted)
             callChannels.closeAllChannels()
         }
     }
@@ -83,10 +138,10 @@
         setUpV2Test()
         runBlocking {
             val callChannels = CallChannels()
-            val callEvents = initCallEvents(coroutineContext, callChannels)
+            val callSession = initCallSession(coroutineContext, callChannels)
 
-            callEvents.onCallEndpointChanged(getCurrentEndpoint())
-            callEvents.onAvailableCallEndpointsChanged(getAvailableEndpoint())
+            callSession.onCallEndpointChanged(getCurrentEndpoint())
+            callSession.onAvailableCallEndpointsChanged(getAvailableEndpoint())
 
             assertEquals(
                 getAvailableEndpoint().size,
@@ -97,7 +152,7 @@
         }
     }
 
-    private fun initCallEvents(
+    private fun initCallSession(
         coroutineContext: CoroutineContext,
         callChannels: CallChannels
     ): CallSession {
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt
index 0c6f8f2..cd56b97 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt
@@ -111,6 +111,7 @@
             mCallsManager.registerAppWithTelecom(CallsManager.CAPABILITY_BASELINE)
             val account = mCallsManager.getBuiltPhoneAccount()!!
             assertNotNull(account.extras)
+            assertTrue(account.extras.getBoolean(CallsManager.PLACEHOLDER_VALUE_ACCOUNT_BUNDLE))
             if (Utils.hasPlatformV2Apis()) {
                 assertTrue(
                     Utils.hasCapability(
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2ECallExtensionExtrasTests.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2ECallExtensionExtrasTests.kt
index bab37be..a7bb3c5 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2ECallExtensionExtrasTests.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2ECallExtensionExtrasTests.kt
@@ -101,7 +101,7 @@
     @LargeTest
     @Test(timeout = 10000)
     fun testCapabilityExchangeIncoming_V2() {
-        setUpV2TestWithExtensions()
+        setUpV2TestWithExtensionsOld()
         addAndVerifyCallExtensionTypeE2E(TestUtils.INCOMING_CALL_ATTRIBUTES)
     }
 
@@ -114,7 +114,7 @@
     @LargeTest
     @Test(timeout = 10000)
     fun testCapabilityExchangeOutgoing_V2() {
-        setUpV2TestWithExtensions()
+        setUpV2TestWithExtensionsOld()
         addAndVerifyCallExtensionTypeE2E(TestUtils.OUTGOING_CALL_ATTRIBUTES)
     }
 
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2EExtensionTests.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2EExtensionTests.kt
index 08c30da..3f731f8 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2EExtensionTests.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/E2EExtensionTests.kt
@@ -22,11 +22,15 @@
 import android.os.Build.VERSION_CODES
 import androidx.core.telecom.CallAttributesCompat
 import androidx.core.telecom.CallsManager
+import androidx.core.telecom.extensions.CallExtensionCreationDelegate
 import androidx.core.telecom.extensions.CallExtensionsScope
 import androidx.core.telecom.extensions.Capability
 import androidx.core.telecom.extensions.Participant
 import androidx.core.telecom.extensions.ParticipantClientActions
-import androidx.core.telecom.extensions.connectExtensions
+import androidx.core.telecom.extensions.ParticipantClientExtensionNew
+import androidx.core.telecom.extensions.addKickParticipantAction
+import androidx.core.telecom.extensions.addParticipantExtension
+import androidx.core.telecom.extensions.addRaiseHandAction
 import androidx.core.telecom.extensions.getParticipantActions
 import androidx.core.telecom.internal.CallCompat
 import androidx.core.telecom.internal.CapabilityExchangeListenerRemote
@@ -48,6 +52,8 @@
 import junit.framework.TestCase.assertNull
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeoutOrNull
 import kotlinx.coroutines.yield
@@ -70,6 +76,7 @@
 @RunWith(Parameterized::class)
 class E2EExtensionTests(private val parameters: TestParameters) : BaseTelecomTest() {
     companion object {
+        private const val ICS_EXTENSION_UPDATE_TIMEOUT_MS = 1000L
         // Use the VOIP service that uses V2 APIs (VoipAppExtensionControl)
         private const val SERVICE_SOURCE_V2 = 1
         // Use the VOIP service that uses bkwds compat APIs (VoipAppExtensionControl)
@@ -148,7 +155,7 @@
     @LargeTest
     @Test(timeout = 10000)
     fun testVoipWithExtensionsAndInCallServiceWithout() = runBlocking {
-        setupParameterizedTest(includeIcsExtensions = false)
+        setupParameterizedTest(icsExtensionsConfig = InCallServiceType.ICS_WITHOUT_EXTENSIONS)
         val voipAppControl = bindToVoipAppWithExtensions()
         // No Capability Exchange sequence occurs between VoIP app and ICS because ICS doesn't
         // support extensions
@@ -167,19 +174,14 @@
     }
 
     /**
-     * Refactor Part 1: Move ICS logic from InCallServiceCompat -> CallExtensions
-     *
-     * <p>
      * Create a new VOIP call and use [connectExtensions] in the ICS to connect to the VOIP call.
-     * Once complete, use the [CallExtensionsScope.registerExtension] method to register an
-     * extension and ensure that for valid capabilities (PARTICIPANT), we get the remote interface
-     * used to setup/maintain the connection. For capabilities that do not exist on the remote,
-     * ensure we get the correct null indication.
+     * Once complete, use the [CallExtensionsScope.registerExtension] method to register an unknown
+     * extension and ensure we get the correct null indication.
      */
     @LargeTest
     @Test(timeout = 10000)
-    fun testIcsExtensionsCreation() = runBlocking {
-        setupParameterizedTest()
+    fun testIcsExtensionsCreationUnknownCapability() = runBlocking {
+        setupParameterizedTest(icsExtensionsConfig = InCallServiceType.ICS_WITH_EXTENSIONS_NEW)
         val voipAppControl = bindToVoipAppWithExtensions()
         createAndVerifyVoipCall(
             voipAppControl,
@@ -189,46 +191,81 @@
 
         val call = TestUtils.waitOnInCallServiceToReachXCalls(1)!!
         var hasConnected = false
-        val icsCapability =
-            createCapability(
-                id = CallsManager.PARTICIPANT,
-                version = 2,
-                actions = setOf(CallsManager.RAISE_HAND_ACTION)
-            )
         // Manually connect extensions here to exercise the CallExtensionsScope class
-        connectExtensions(mContext, call) {
-            val remote =
-                registerExtension(
-                    this,
-                    icsCapability,
-                    negotiatedCapability =
-                        createCapability(
-                            id = CallsManager.PARTICIPANT,
-                            version = 1,
-                            actions = setOf(CallsManager.RAISE_HAND_ACTION)
-                        )
-                )
-            // Create an extension that the VOIP app does not know about and ensure that
-            // we receive a null response during negotiation so we can notify the ICS of the state
-            // of that extension
-            val nonexistentRemote =
-                registerExtension(
-                    this,
-                    icsCapability =
-                        createCapability(id = 8675309, version = 42, actions = emptySet()),
-                    negotiatedCapability = null
-                )
-            onConnected {
-                hasConnected = true
-                assertNotNull(
-                    "Connection to remote should not be null for VOIP supported features",
-                    remote.await()
-                )
-                assertNull(
-                    "Connection to remote should be null for features with no VOIP support",
-                    nonexistentRemote.await()
-                )
-                call.disconnect()
+        with(MockInCallServiceDelegate.getServiceWithExtensions()!!) {
+            connectExtensions(call) {
+                // Create an extension that the VOIP app does not know about and ensure that
+                // we receive a null response during negotiation so we can notify the ICS of the
+                // state of that extension
+                val nonexistentRemote =
+                    registerExtension(
+                        this,
+                        icsCapability =
+                            createCapability(id = 8675309, version = 42, actions = emptySet()),
+                        negotiatedCapability = null
+                    )
+                onConnected {
+                    hasConnected = true
+                    assertNull(
+                        "Connection to remote should be null for features with no VOIP support",
+                        nonexistentRemote.await()
+                    )
+                    call.disconnect()
+                }
+            }
+        }
+        assertTrue("onConnected never received", hasConnected)
+    }
+
+    /**
+     * Create a VOIP call with a participants extension and attach participant Call extensions.
+     * Verify that all of the participant extension functions work as expected.
+     */
+    @LargeTest
+    @Test(timeout = 10000)
+    fun testVoipAndIcsWithParticipantsNew() = runBlocking {
+        setupParameterizedTest(icsExtensionsConfig = InCallServiceType.ICS_WITH_EXTENSIONS_NEW)
+        val voipAppControl = bindToVoipAppWithExtensions()
+        val callback = TestCallCallbackListener(this)
+        voipAppControl.setCallback(callback)
+        val voipCallId =
+            createAndVerifyVoipCall(
+                voipAppControl,
+                listOf(CAPABILITY_PARTICIPANT_WITH_ACTIONS),
+                parameters.direction
+            )
+
+        val call = TestUtils.waitOnInCallServiceToReachXCalls(1)!!
+        var hasConnected = false
+        with(MockInCallServiceDelegate.getServiceWithExtensions()!!) {
+            connectExtensions(call) {
+                val participants = CachedParticipants(this)
+                val raiseHandAction = CachedRaisedHands(participants.extension)
+                val kickParticipantAction = participants.extension.addKickParticipantAction()
+                onConnected {
+                    hasConnected = true
+                    // Test VOIP -> ICS connection by updating state
+                    participants.waitForParticipants(emptySet())
+                    participants.waitForActiveParticipant(null)
+
+                    voipAppControl.updateParticipants(listOf(TestUtils.getDefaultParticipant()))
+                    participants.waitForParticipants(setOf(TestUtils.getDefaultParticipant()))
+
+                    voipAppControl.updateActiveParticipant(TestUtils.getDefaultParticipant())
+                    participants.waitForActiveParticipant(TestUtils.getDefaultParticipant())
+
+                    voipAppControl.updateRaisedHands(listOf(TestUtils.getDefaultParticipant()))
+                    raiseHandAction.waitForRaisedHands(setOf(TestUtils.getDefaultParticipant()))
+
+                    // Test ICS -> VOIP connection by sending events
+                    raiseHandAction.action.requestRaisedHandStateChange(true)
+                    callback.waitForRaiseHandState(voipCallId, true)
+
+                    kickParticipantAction.requestKickParticipant(TestUtils.getDefaultParticipant())
+                    callback.waitForKickParticipant(voipCallId, TestUtils.getDefaultParticipant())
+
+                    call.disconnect()
+                }
             }
         }
         assertTrue("onConnected never received", hasConnected)
@@ -238,12 +275,13 @@
      * Validate that for a bound VOIP app, when a new call is added, the InCallService receives the
      * new call and can use all Participant extension interfaces.
      */
+    // TODO: Remove in a follow up to use the new version of the API instead.
     @LargeTest
     @Test(timeout = 10000)
     fun testVoipAndIcsWithParticipants() = runBlocking {
         // test set up
         setupParameterizedTest(
-            includeIcsExtensions = true,
+            icsExtensionsConfig = InCallServiceType.ICS_WITH_EXTENSIONS_OLD,
             setOf(CAPABILITY_PARTICIPANT_WITH_ACTIONS)
         )
 
@@ -313,23 +351,28 @@
     ): CompletableDeferred<CapabilityExchangeListenerRemote?> {
         val deferredVal = CompletableDeferred<CapabilityExchangeListenerRemote?>()
         // Register a test extension that will receive the PARTICIPANT capability
-        scope.registerExtension(icsCapability) { capability, remote ->
-            assertEquals(
-                "Expected PARTICIPANT capability",
-                negotiatedCapability?.featureId,
-                capability?.featureId
+        scope.registerExtension {
+            CallExtensionCreationDelegate(
+                capability = icsCapability,
+                receiver = { capability, remote ->
+                    assertEquals(
+                        "Expected PARTICIPANT capability",
+                        negotiatedCapability?.featureId,
+                        capability?.featureId
+                    )
+                    assertEquals(
+                        "Expected version to equal the lowest common version",
+                        negotiatedCapability?.featureVersion,
+                        capability?.featureVersion
+                    )
+                    assertEquals(
+                        "The negotiated actions should be the actions supported by both",
+                        negotiatedCapability?.supportedActions?.toSet(),
+                        capability?.supportedActions?.toSet()
+                    )
+                    deferredVal.complete(remote)
+                }
             )
-            assertEquals(
-                "Expected version to equal the lowest common version",
-                negotiatedCapability?.featureVersion,
-                capability?.featureVersion
-            )
-            assertEquals(
-                "The negotiated actions should be the actions supported by both",
-                negotiatedCapability?.supportedActions?.toSet(),
-                capability?.supportedActions?.toSet()
-            )
-            deferredVal.complete(remote)
         }
         return deferredVal
     }
@@ -411,7 +454,7 @@
 
     /** Sets up the test based on the parameters set for the run */
     private fun setupParameterizedTest(
-        includeIcsExtensions: Boolean = false,
+        icsExtensionsConfig: InCallServiceType = InCallServiceType.ICS_WITHOUT_EXTENSIONS,
         icsCapabilities: Set<Capability> = emptySet()
     ) {
         if (Build.VERSION.SDK_INT < VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -422,18 +465,26 @@
         }
         when (parameters.serviceSource) {
             SERVICE_SOURCE_V2 -> {
-                if (includeIcsExtensions) {
-                    setUpV2TestWithExtensions(icsCapabilities)
-                } else {
-                    setUpV2Test()
+                when (icsExtensionsConfig) {
+                    InCallServiceType.ICS_WITH_EXTENSIONS_NEW -> setUpV2TestWithExtensionsNew()
+                    InCallServiceType.ICS_WITH_EXTENSIONS_OLD -> {
+                        setUpV2TestWithExtensionsOld(icsCapabilities)
+                    }
+                    InCallServiceType.ICS_WITHOUT_EXTENSIONS -> setUpV2Test()
                 }
             }
             SERVICE_SOURCE_CONNSRV -> {
                 setUpBackwardsCompatTest()
-                if (includeIcsExtensions) {
-                    setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS, icsCapabilities)
-                } else {
-                    setInCallService(InCallServiceType.ICS_WITHOUT_EXTENSIONS)
+                when (icsExtensionsConfig) {
+                    InCallServiceType.ICS_WITH_EXTENSIONS_NEW -> {
+                        setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS_NEW)
+                    }
+                    InCallServiceType.ICS_WITH_EXTENSIONS_OLD -> {
+                        setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS_OLD, icsCapabilities)
+                    }
+                    InCallServiceType.ICS_WITHOUT_EXTENSIONS -> {
+                        setInCallService(InCallServiceType.ICS_WITHOUT_EXTENSIONS)
+                    }
                 }
             }
         }
@@ -485,4 +536,43 @@
         val result = waitForResult({ it == expectedResult }, consumer)
         assertEquals(failMessage, expectedResult, result)
     }
+
+    internal class CachedParticipants(scope: CallExtensionsScope) {
+        private val participantState = MutableStateFlow<Set<Participant>>(emptySet())
+        private val activeParticipantState = MutableStateFlow<Participant?>(null)
+        val extension =
+            scope.addParticipantExtension(
+                activeParticipantsUpdate = activeParticipantState::emit,
+                participantsUpdate = participantState::emit
+            )
+
+        suspend fun waitForParticipants(expected: Set<Participant>) {
+            val result =
+                withTimeoutOrNull(ICS_EXTENSION_UPDATE_TIMEOUT_MS) {
+                    participantState.first { it == expected }
+                }
+            assertEquals("Never received expected participants update", expected, result)
+        }
+
+        suspend fun waitForActiveParticipant(expected: Participant?) {
+            val result =
+                withTimeoutOrNull(ICS_EXTENSION_UPDATE_TIMEOUT_MS) {
+                    activeParticipantState.first { it == expected }
+                }
+            assertEquals("Never received expected active participant", expected, result)
+        }
+    }
+
+    internal class CachedRaisedHands(extension: ParticipantClientExtensionNew) {
+        private val raisedHands = MutableStateFlow<Set<Participant>>(emptySet())
+        val action = extension.addRaiseHandAction(stateUpdate = raisedHands::emit)
+
+        suspend fun waitForRaisedHands(expected: Set<Participant>) {
+            val result =
+                withTimeoutOrNull(ICS_EXTENSION_UPDATE_TIMEOUT_MS) {
+                    raisedHands.first { it == expected }
+                }
+            assertEquals("Never received expected raised hands update", expected, result)
+        }
+    }
 }
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallServiceCompatTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallServiceCompatTest.kt
index d32f069..d110221 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallServiceCompatTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallServiceCompatTest.kt
@@ -75,7 +75,7 @@
     @Before
     fun setUp() {
         Utils.resetUtils()
-        setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS, emptySet())
+        setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS_OLD, emptySet())
     }
 
     @After
@@ -227,7 +227,7 @@
     private fun configureCapabilityExchangeTypeTest(): Pair<String, Boolean>? {
         if (Utils.hasPlatformV2Apis()) {
             Log.w(CallCompatTest.TAG, "Setting up v2 tests for U+ device")
-            setUpV2TestWithExtensions()
+            setUpV2TestWithExtensionsOld()
         } else {
             Log.w(CallCompatTest.TAG, "Setting up backwards compatibility tests for pre-U device")
             setUpBackwardsCompatTest()
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt
index 9e6c8f4..534fe9b 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt
@@ -102,11 +102,20 @@
     }
 
     @ExperimentalAppActions
-    fun setUpV2TestWithExtensions(capabilities: Set<Capability> = emptySet()) {
-        Log.i(L_TAG, "setUpV2Test: core-telecom w/ [V2] APIs + Extension support")
+    fun setUpV2TestWithExtensionsNew() {
+        Log.i(L_TAG, "setUpV2Test: core-telecom w/ [V2] APIs + NEW Extension support")
         Utils.setUtils(TestUtils.mV2Build)
         mCallsManager.registerAppWithTelecom(CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)
-        setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS, capabilities)
+        setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS_NEW)
+        logTelecomState()
+    }
+
+    @ExperimentalAppActions
+    fun setUpV2TestWithExtensionsOld(capabilities: Set<Capability> = emptySet()) {
+        Log.i(L_TAG, "setUpV2Test: core-telecom w/ [V2] APIs + OLD Extension support")
+        Utils.setUtils(TestUtils.mV2Build)
+        mCallsManager.registerAppWithTelecom(CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)
+        setInCallService(InCallServiceType.ICS_WITH_EXTENSIONS_OLD, capabilities)
         logTelecomState()
     }
 
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/InCallServiceType.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/InCallServiceType.kt
index 477b71eb..ae76ea1 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/InCallServiceType.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/InCallServiceType.kt
@@ -17,6 +17,7 @@
 package androidx.core.telecom.test.utils
 
 enum class InCallServiceType {
-    ICS_WITH_EXTENSIONS,
+    ICS_WITH_EXTENSIONS_OLD, // TODO: Remove this
+    ICS_WITH_EXTENSIONS_NEW,
     ICS_WITHOUT_EXTENSIONS
 }
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/MockInCallServiceDelegate.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/MockInCallServiceDelegate.kt
index 9c83eac..e4c5ad2 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/MockInCallServiceDelegate.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/MockInCallServiceDelegate.kt
@@ -69,8 +69,31 @@
         }
     }
 
+    @OptIn(ExperimentalAppActions::class)
+    class InCallServiceWExtensionsNew(context: Context) : InCallServiceCompat() {
+        init {
+            // Icky hack, but since we are using a delegate, we need to attach the Context manually.
+            if (baseContext == null) {
+                attachBaseContext(context)
+            }
+        }
+
+        override fun onCallAdded(call: Call) {
+            val callCompat = call.let { c -> CallCompat.toCallCompat(c) {} }
+            if (!mCalls.contains(callCompat)) {
+                Log.i(LOG_TAG, "ICSCN.onCallAdded: added the new call to static call list")
+                mCalls.add(callCompat)
+            }
+        }
+
+        override fun onCallRemoved(call: Call?) {
+            Log.i(LOG_TAG, String.format("ICSCN.onCallRemoved: call=[%s]", call))
+            mCalls.removeIf { c -> c.toCall() == call }
+        }
+    }
+
     @ExperimentalAppActions
-    class InCallServiceWExtensions(context: Context, val capabilities: Set<Capability>) :
+    class InCallServiceWExtensionsOld(context: Context, val capabilities: Set<Capability>) :
         InCallServiceCompat() {
         init {
             // Icky hack, but since we are using a delegate, we need to attach the Context manually.
@@ -187,8 +210,11 @@
         Log.i(LOG_TAG, "Delegate service onCreate")
         mServiceFlow.tryEmit(
             when (mInCallServiceType) {
-                InCallServiceType.ICS_WITH_EXTENSIONS -> {
-                    InCallServiceWExtensions(applicationContext, mExtensions)
+                InCallServiceType.ICS_WITH_EXTENSIONS_OLD -> {
+                    InCallServiceWExtensionsOld(applicationContext, mExtensions)
+                }
+                InCallServiceType.ICS_WITH_EXTENSIONS_NEW -> {
+                    InCallServiceWExtensionsNew(applicationContext)
                 }
                 InCallServiceType.ICS_WITHOUT_EXTENSIONS -> {
                     InCallServiceWoExtensions(applicationContext)
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt
index 06132a0..7636fd6 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt
@@ -431,9 +431,10 @@
 
 @ExperimentalAppActions
 class TestCallCallbackListener(private val scope: CoroutineScope) : ITestAppControlCallback.Stub() {
-    private val raisedHandFlow: MutableSharedFlow<Pair<String, Boolean>> = MutableSharedFlow()
+    private val raisedHandFlow: MutableSharedFlow<Pair<String, Boolean>> =
+        MutableSharedFlow(replay = 1)
     private val kickParticipantFlow: MutableSharedFlow<Pair<String, Participant?>> =
-        MutableSharedFlow()
+        MutableSharedFlow(replay = 1)
 
     override fun raiseHandStateAction(callId: String?, isHandRaised: Boolean) {
         if (callId == null) return
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
index 81f271e..1641a18 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
@@ -51,6 +51,7 @@
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.job
 import kotlinx.coroutines.withTimeout
 
@@ -183,6 +184,7 @@
         internal const val PACKAGE_LABEL: String = "Telecom-Jetpack"
         internal const val CONNECTION_SERVICE_CLASS =
             "androidx.core.telecom.internal.JetpackConnectionService"
+        internal const val PLACEHOLDER_VALUE_ACCOUNT_BUNDLE = "isCoreTelecomAccount"
 
         // fail messages specific to addCall
         internal const val CALL_CREATION_FAILURE_MSG = "The call failed to be added."
@@ -213,7 +215,11 @@
         // remap and set capabilities
         phoneAccountBuilder.setCapabilities(remapJetpackCapsToPlatformCaps(capabilities))
         // see b/343674176. Some OEMs expect the PhoneAccount.getExtras() to be non-null
-        phoneAccountBuilder.setExtras(Bundle())
+        // see b/352526256. The bundle must contain a placeholder value. otherwise, the bundle
+        // empty bundle will be nulled out on reboot.
+        val defaultBundle = Bundle()
+        defaultBundle.putBoolean(PLACEHOLDER_VALUE_ACCOUNT_BUNDLE, true)
+        phoneAccountBuilder.setExtras(defaultBundle)
 
         // build and register the PhoneAccount via the Platform API
         mPhoneAccount = phoneAccountBuilder.build()
@@ -286,7 +292,7 @@
         onSetActive: suspend () -> Unit,
         onSetInactive: suspend () -> Unit,
         block: CallControlScope.() -> Unit
-    ) {
+    ) = coroutineScope {
         // Provide a default empty handler for onEvent
         addCall(
             callAttributes,
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallExtensions.kt b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallExtensions.kt
index d0f69a4..c183e8e 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallExtensions.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallExtensions.kt
@@ -32,7 +32,6 @@
 import android.telecom.PhoneAccount
 import android.telecom.TelecomManager
 import android.util.Log
-import androidx.annotation.DoNotInline
 import androidx.annotation.IntDef
 import androidx.annotation.RequiresApi
 import androidx.core.content.ContextCompat
@@ -44,6 +43,8 @@
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
 import kotlin.math.min
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.channels.trySendBlocking
 import kotlinx.coroutines.flow.Flow
@@ -53,55 +54,24 @@
 import kotlinx.coroutines.withTimeoutOrNull
 
 /**
- * Connects extensions to the provided [Call], allowing the call to support additional optional
- * behaviors beyond the traditional call state management.
- *
- * For example, an extension may allow the participants of a meeting to be surfaced to this
- * application so that the user can view and manage the participants in the meeting on different
- * surfaces:
- * ```
- * class InCallServiceImpl : InCallServiceCompat() {
- * ...
- *   override fun onCallAdded(call: Call) {
- *     lifecycleScope.launch {
- *       connectExtensions(context, call) {
- *         // Initialize extensions
- *         onConnected { call ->
- *           // change call states & listen/update extensions
- *         }
- *       }
- *       // Once the call is destroyed, control flow will resume again
- *     }
- *   }
- *  ...
- * }
- * ```
- */
-@ExperimentalAppActions
-@RequiresApi(Build.VERSION_CODES.O)
-internal suspend fun connectExtensions(
-    applicationContext: Context,
-    call: Call,
-    init: CallExtensionsScope.() -> Unit
-) {
-    val scope = CallExtensionsScope(applicationContext, call)
-    scope.init()
-    when (val type = scope.resolveCallExtensionsType()) {
-        CallExtensionsScope.CAPABILITY_EXCHANGE -> scope.performCapabilityExchange()
-        else -> Log.w(CallExtensionsScope.TAG, "connectExtensions: unexpected type: $type")
-    }
-    scope.invokeDelegate()
-    scope.waitForDestroy()
-}
-
-/**
  * Extensions registering to handle Extension must implement this receiver in order to create and
  * maintain the connection to the extension that they support.
  *
  * @see [CallExtensionsScope.registerExtension]
  */
 @OptIn(ExperimentalAppActions::class)
-internal typealias ExtensionReceiver = (Capability?, CapabilityExchangeListenerRemote?) -> Unit
+internal typealias ExtensionCreationReceiver =
+    suspend (Capability?, CapabilityExchangeListenerRemote?) -> Unit
+
+/**
+ * Encapsulates the [capability] associated with a call extension and its [receiver] to call when
+ * capability exchange has completed and the extension should be initialized.
+ */
+@ExperimentalAppActions
+internal data class CallExtensionCreationDelegate(
+    val capability: Capability,
+    val receiver: ExtensionCreationReceiver
+)
 
 /**
  * Represents the result of performing capability exchange with the underlying VOIP application.
@@ -129,10 +99,12 @@
  * }
  * ```
  */
+// TODO: Refactor to Public API
 @RequiresApi(Build.VERSION_CODES.O)
 @ExperimentalAppActions
 internal class CallExtensionsScope(
     private val applicationContext: Context,
+    internal val callScope: CoroutineScope,
     private val call: Call
 ) {
 
@@ -156,7 +128,9 @@
     private var delegate: (suspend (Call) -> Unit)? = null
     // Maps a Capability that has been registered to the Receiver that will be used to create and
     // maintain the extension connection with the remote VOIP application.
-    private val callExtensions = HashMap<Capability, ExtensionReceiver>()
+    // This has to be done this way because actions are set AFTER the extension is registered, so we
+    // need to query the Capability after CallExtensionScope initialization has completed.
+    private val callExtensions = HashSet<() -> CallExtensionCreationDelegate>()
 
     /**
      * Called when the [Call] extensions have been successfully set up and are ready to be used.
@@ -168,28 +142,26 @@
     }
 
     /**
-     * Register an extension with this call, whose [capability] will be negotiated with the VOIP
+     * Register an extension with this call, whose capability will be negotiated with the VOIP
      * application.
      *
      * Once capability exchange completes, the shared [Capability.featureId] will be used to map the
      * negotiated capability with this extension and [receiver] will be called with a valid
      * negotiated [Capability] and interface to use to create/manage this extension with the remote.
      *
-     * @param capability The [Capability] that will be registered to this extension and used during
-     *   feature negotiation.
      * @param receiver The receiver that will be called once capability exchange completes and we
      *   either have a valid negotiated capability or a `null` Capability if the remote side does
      *   not support this capability.
      */
-    internal fun registerExtension(capability: Capability, receiver: ExtensionReceiver) {
-        callExtensions[capability] = receiver
+    internal fun registerExtension(receiver: () -> CallExtensionCreationDelegate) {
+        callExtensions.add(receiver)
     }
 
     /**
      * Invoke the stored [onConnected] block once capability exchange has completed and the
      * associated extensions have been set up.
      */
-    internal suspend fun invokeDelegate() {
+    private suspend fun invokeDelegate() {
         Log.i(TAG, "invokeDelegate")
         delegate?.invoke(call)
     }
@@ -220,7 +192,7 @@
      *
      * @return the extension type [CapabilityExchangeType] resolved for the call.
      */
-    internal suspend fun resolveCallExtensionsType(): Int {
+    private suspend fun resolveCallExtensionsType(): Int {
         var details = call.details
         var type = NONE
         // Android CallsManager V+ check
@@ -279,39 +251,70 @@
         return type
     }
 
+    /** Perform the operation to connect the extensions to the call. */
+    internal suspend fun connectExtensionSession() {
+        val type = resolveCallExtensionsType()
+        Log.d(TAG, "connectExtensionsSession: type=$type")
+        // When we support EXTRAs, extensions should wrap this detail into a generic interface
+        val extensions = performExchangeWithRemote()
+        try {
+            when (type) {
+                CAPABILITY_EXCHANGE -> initializeExtensions(extensions)
+                else -> Log.w(TAG, "connectExtensions: unexpected type: $type")
+            }
+            invokeDelegate()
+            waitForDestroy()
+        } finally {
+            Log.i(TAG, "setupExtensionSession: scope closing, calling onRemoveExtensions")
+            callScope.cancel()
+            extensions?.extensionInitializationBinder?.onRemoveExtensions()
+        }
+    }
+
     /**
-     * Perform the capability exchange procedure with the calling application and negotiate
-     * capabilities. When complete, call all extension that were registered with [registerExtension]
-     * and provide the negotiated capability or null.
+     * Register the extensions interface with the remote application and get the result.
      *
      * If negotiation takes longer than [CAPABILITY_EXCHANGE_TIMEOUT_MS], we will assume the remote
      * does not support extensions at all.
      */
-    internal suspend fun performCapabilityExchange() {
-        Log.i(TAG, "performCapabilityExchange: Starting capability negotiation with VOIP app...")
+    private suspend fun performExchangeWithRemote(): CapabilityExchangeResult? {
+        Log.d(TAG, "requestExtensions: requesting extensions from remote")
         val extensions =
             withTimeoutOrNull(CAPABILITY_EXCHANGE_TIMEOUT_MS) { registerWithRemoteService() }
         if (extensions == null) {
-            Log.w(TAG, "performCapabilityExchange: never received response")
-            for (initializer in callExtensions.entries) {
-                initializer.value.invoke(null, null)
+            Log.w(TAG, "startCapabilityExchange: never received response")
+        }
+        return extensions
+    }
+
+    /**
+     * Initialize all extensions that were registered with [registerExtension] and provide the
+     * negotiated capability or null if the remote doesn't support this extension.
+     */
+    private suspend fun initializeExtensions(extensions: CapabilityExchangeResult?) {
+        Log.i(TAG, "initializeExtensions: Initializing extensions...")
+        val delegates = callExtensions.map { it() }
+        if (extensions == null) {
+            for (initializer in delegates) {
+                initializer.receiver(null, null)
             }
             return
         }
 
-        for (initializer in callExtensions.entries) {
+        for (initializer in delegates) {
+            Log.d(TAG, "initializeExtensions: capability=${initializer.capability}")
             val remoteCap =
                 extensions.voipCapabilities.firstOrNull {
-                    it.featureId == initializer.key.featureId
+                    it.featureId == initializer.capability.featureId
                 }
             if (remoteCap == null) {
-                initializer.value.invoke(null, null)
+                Log.d(TAG, "initializeExtensions: no VOIP capability, skipping...")
+                initializer.receiver.invoke(null, null)
                 continue
             }
-            initializer.value.invoke(
-                calculateNegotiatedCapability(initializer.key, remoteCap),
-                extensions.extensionInitializationBinder
-            )
+            val negotiatedCap = calculateNegotiatedCapability(initializer.capability, remoteCap)
+            Log.d(TAG, "initializeExtensions: negotiated cap=$negotiatedCap")
+            initializer.receiver.invoke(negotiatedCap, extensions.extensionInitializationBinder)
         }
     }
 
@@ -329,6 +332,11 @@
                         capabilities: MutableList<Capability>?,
                         l: ICapabilityExchangeListener?
                     ) {
+                        Log.v(
+                            TAG,
+                            "registerWithRemoteService: received remote result," +
+                                " caps=$capabilities, listener is null=${l == null}"
+                        )
                         continuation.resume(
                             l?.let {
                                 CapabilityExchangeResult(
@@ -339,6 +347,7 @@
                         )
                     }
                 }
+            Log.v(TAG, "registerWithRemoteService: sending event")
             val extras = setExtras(binder)
             call.sendCallEvent(CallsManager.EVENT_JETPACK_CAPABILITY_EXCHANGE, extras)
         }
@@ -393,7 +402,7 @@
     }
 
     /** Wait for the call to be destroyed. */
-    internal suspend fun waitForDestroy() = suspendCancellableCoroutine { continuation ->
+    private suspend fun waitForDestroy() = suspendCancellableCoroutine { continuation ->
         val callback =
             object : Callback() {
                 override fun onCallDestroyed(targetCall: Call?) {
@@ -415,7 +424,6 @@
 private object Api26Impl {
     @Suppress("DEPRECATION")
     @JvmStatic
-    @DoNotInline
     fun getCallState(call: Call): Int {
         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
             Api31Impl.getCallState(call)
@@ -429,7 +437,6 @@
 @RequiresApi(Build.VERSION_CODES.S)
 private object Api31Impl {
     @JvmStatic
-    @DoNotInline
     fun getCallState(call: Call): Int {
         return call.details.state
     }
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallsManagerExtensions.kt b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallsManagerExtensions.kt
index 167f6d2..be40159 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallsManagerExtensions.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/CallsManagerExtensions.kt
@@ -31,9 +31,9 @@
 import androidx.core.telecom.internal.ParticipantStateListenerRemote
 import androidx.core.telecom.util.ExperimentalAppActions
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.onCompletion
@@ -115,6 +115,7 @@
  *   implementation of [ExtensionInitializationScope.onCall] will be called.
  * @see CallsManager.addCall
  */
+// TODO: Refactor to Public API
 @RequiresApi(Build.VERSION_CODES.O)
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
@@ -125,12 +126,15 @@
     onSetActive: suspend () -> Unit,
     onSetInactive: suspend () -> Unit,
     init: suspend ExtensionInitializationScope.() -> Unit
-) {
+) = coroutineScope {
     Log.v(CallsManagerExtensions.LOG_TAG, "addCall: begin")
     val eventFlow = MutableSharedFlow<CallEvent>()
     val scope = ExtensionInitializationScope()
-    var extensionJob: Job? = null
     scope.init()
+    val extensionJob = launch {
+        Log.d(CallsManagerExtensions.LOG_TAG, "addCall: connecting extensions")
+        scope.collectEvents(this, eventFlow)
+    }
     Log.v(CallsManagerExtensions.LOG_TAG, "addCall: init complete")
     addCall(
         callAttributes,
@@ -150,16 +154,12 @@
             foundEvent?.let { eventFlow.emit(CallEvent(it, extras)) }
         }
     ) {
-        extensionJob = launch {
-            Log.d(CallsManagerExtensions.LOG_TAG, "addCall: connecting extensions")
-            scope.collectEvents(this, eventFlow)
-        }
         Log.i(CallsManagerExtensions.LOG_TAG, "addCall: invoking delegates")
         scope.invokeDelegate(this)
     }
     // Ensure that when the call ends, we also cancel any ongoing coroutines/flows as part of
     // extension work
-    extensionJob?.cancelAndJoin()
+    extensionJob.cancelAndJoin()
 }
 
 /**
@@ -170,6 +170,7 @@
  * implementation of [onCall] will be run, which should manage the call and extension states during
  * the lifetime of when the call is active.
  */
+// TODO: Refactor to Public API
 @RequiresApi(Build.VERSION_CODES.O)
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/extensions/ParticipantClientExtensionNew.kt b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/ParticipantClientExtensionNew.kt
new file mode 100644
index 0000000..bca2e73
--- /dev/null
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/ParticipantClientExtensionNew.kt
@@ -0,0 +1,541 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("ParticipantClientExtensions")
+
+package androidx.core.telecom.extensions
+
+import android.os.Build
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.core.telecom.CallControlResult
+import androidx.core.telecom.CallException
+import androidx.core.telecom.CallsManager
+import androidx.core.telecom.internal.CapabilityExchangeListenerRemote
+import androidx.core.telecom.internal.ParticipantActionsRemote
+import androidx.core.telecom.internal.ParticipantStateListener
+import androidx.core.telecom.util.ExperimentalAppActions
+import kotlin.properties.Delegates
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@ExperimentalAppActions
+internal fun interface ParticipantsUpdate {
+    /**
+     * The participants in the call have been updated.
+     *
+     * @param participants The new Set of Participants
+     */
+    suspend fun onParticipantsUpdated(participants: Set<Participant>)
+}
+
+@ExperimentalAppActions
+internal fun interface ActiveParticipantsUpdate {
+    /**
+     * The active participant in the call has changed
+     *
+     * @param participant The Participant that is active in the call or `null` if there is no active
+     *   participant
+     */
+    suspend fun onActiveParticipantChanged(participant: Participant?)
+}
+
+@ExperimentalAppActions
+internal fun interface RaisedHandsUpdate {
+    /**
+     * The participants in the call with their hands raised has changed.
+     *
+     * @param participants The Set of Participants with their hands raised.
+     */
+    suspend fun onRaisedHandsChanged(participants: Set<Participant>)
+}
+
+/**
+ * Add support for representing Participants in this call.
+ *
+ * ```
+ * connectExtensions(call) {
+ *     val participantExtension = addParticipantExtension(
+ *         // consume participant changed events
+ *     )
+ *     onConnected {
+ *         // extensions have been negotiated and actions are ready to be used
+ *     }
+ * }
+ * ```
+ *
+ * @param activeParticipantsUpdate The callback that will be used to notify this client when the
+ *   Participant considered active has changed.
+ * @param participantsUpdate The callback that will be used to notify this client when the
+ *   Participants in the Call have changed.
+ */
+// TODO: Refactor to Public API
+@RequiresApi(Build.VERSION_CODES.O)
+@ExperimentalAppActions
+internal fun CallExtensionsScope.addParticipantExtension(
+    activeParticipantsUpdate: ActiveParticipantsUpdate,
+    participantsUpdate: ParticipantsUpdate
+): ParticipantClientExtensionNew {
+    val extension =
+        ParticipantClientExtensionNew(callScope, activeParticipantsUpdate, participantsUpdate)
+    registerExtension {
+        CallExtensionCreationDelegate(
+            capability =
+                Capability().apply {
+                    featureId = CallsManager.PARTICIPANT
+                    featureVersion = 1
+                    supportedActions = extension.actions.keys.toIntArray()
+                },
+            receiver = extension::onNegotiated
+        )
+    }
+    return extension
+}
+
+/** Repository containing the callbacks associated with the Participant extension state changes */
+@ExperimentalAppActions
+internal class ParticipantStateCallbackRepository {
+    var raisedHandsStateCallback: (suspend (Set<Int>) -> Unit)? = null
+
+    /** Called by the remote application when this state changes */
+    suspend fun raisedHandsStateChanged(newState: Set<Int>) {
+        raisedHandsStateCallback?.invoke(newState)
+    }
+}
+
+/**
+ * Initializer allowing an action to first register callbacks to
+ * [ParticipantStateCallbackRepository] as part of initialization as well as provides a
+ * [CoroutineScope] to attach callbacks from the remote party to and whether or not the action is
+ * supported.
+ */
+@OptIn(ExperimentalAppActions::class)
+internal typealias ActionInitializer =
+    ParticipantStateCallbackRepository.(CoroutineScope, Boolean) -> Unit
+
+/**
+ * Called with the remote interface that will be used to notify the remote application of when an
+ * there is an event to send related to action.
+ */
+@OptIn(ExperimentalAppActions::class)
+internal typealias ActionConnectedCallback = (ParticipantActionsRemote?) -> Unit
+
+/**
+ * Contains the callbacks used by Actions during creation. [onInitialization] is called when
+ * capability exchange has completed and
+ */
+@ExperimentalAppActions
+internal data class ActionExchangeResult(
+    val onInitialization: ActionInitializer,
+    val onActionConnected: ActionConnectedCallback
+)
+
+/**
+ * Implements the Participant extension and provides a method for actions to use to register
+ * themselves.
+ *
+ * @param callScope The CoroutineScope of the underlying call
+ * @param activeParticipantsUpdate The update callback used whenever the active participants change
+ * @param participantsUpdate The update callback used whenever the participants in the call change
+ */
+// TODO: Refactor to Public API
+// TODO: Remove old version of ParticipantClientExtension in a follow up CL with this impl.
+@RequiresApi(Build.VERSION_CODES.O)
+@ExperimentalAppActions
+internal class ParticipantClientExtensionNew(
+    private val callScope: CoroutineScope,
+    private val activeParticipantsUpdate: ActiveParticipantsUpdate,
+    private val participantsUpdate: ParticipantsUpdate
+) {
+    /**
+     * Whether or not the participants extension is supported by the remote.
+     *
+     * if `true`, then updates about call participants will be notified. If `false`, then the remote
+     * doesn't support this extension and participants will not be notified to the caller nor will
+     * associated actions receive state updates.
+     *
+     * Should not be queried until [CallExtensionsScope.onConnected] is called.
+     */
+    var isSupported by Delegates.notNull<Boolean>()
+    // Maps a Capability to a receiver that allows the action to register itself with a listener
+    // and then return a Receiver that gets called when Cap exchange completes.
+    internal val actions = HashMap<Int, ActionExchangeResult>()
+    // Manages callbacks that are applicable to sub-actions of the Participants
+    private val callbacks = ParticipantStateCallbackRepository()
+
+    // Participant specific state
+    internal val participants = MutableStateFlow<Set<Participant>>(emptySet())
+    private val activeParticipant = MutableStateFlow<Int?>(null)
+
+    // Holds the remote Binder interface that is used to send action events back to the remote
+    // application.
+    private val remoteBinderListener = MutableSharedFlow<ParticipantActionsRemote?>()
+    // Leapfrogs from the AIDL callback to scheduling delivery of these updates via the callScope
+    // coroutine
+    private val participantStateListener =
+        ParticipantStateListener(
+            updateParticipants = { newParticipants ->
+                callScope.launch {
+                    Log.v(TAG, "updateParticipants: $newParticipants")
+                    participants.emit(newParticipants)
+                }
+            },
+            updateActiveParticipant = { newActiveParticipant ->
+                callScope.launch {
+                    Log.v(TAG, "activeParticipant=$newActiveParticipant")
+                    activeParticipant.emit(newActiveParticipant)
+                }
+            },
+            updateRaisedHands = { newRaisedHands ->
+                callScope.launch {
+                    Log.v(TAG, "raisedHands=$newRaisedHands")
+                    callbacks.raisedHandsStateChanged(newRaisedHands)
+                }
+            },
+            finishSync = { remoteBinder ->
+                callScope.launch {
+                    Log.v(TAG, "finishSync complete, isNull=${remoteBinder == null}")
+                    remoteBinderListener.emit(remoteBinder)
+                }
+            }
+        )
+
+    companion object {
+        const val TAG = CallExtensionsScope.TAG + "(PCE)"
+    }
+
+    /**
+     * Register an Action on the Participants extension.
+     *
+     * @param action An `Int` describing the action
+     * @param onRemoteConnected The callback called when the remote has connected and action events
+     *   can be sent to the remote via [ParticipantActionsRemote]
+     * @param onInitialization The Action initializer, which allows the action to setup callbacks
+     *   via [ParticipantStateCallbackRepository] and determine if the action is supported.
+     */
+    fun registerAction(
+        action: Int,
+        onRemoteConnected: ActionConnectedCallback,
+        onInitialization: ActionInitializer
+    ) {
+        actions[action] = ActionExchangeResult(onInitialization, onRemoteConnected)
+    }
+
+    /**
+     * The Participant extension has been negotiated
+     *
+     * @param negotiatedCapability The negotiated Participant capability or null if the remote
+     *   doesn't support this capability.
+     * @param remote The remote interface to use to create the Participant extension on the remote
+     *   side using the negotiated capability.
+     */
+    suspend fun onNegotiated(
+        negotiatedCapability: Capability?,
+        remote: CapabilityExchangeListenerRemote?
+    ) {
+        if (negotiatedCapability == null || remote == null) {
+            Log.i(TAG, "onNegotiated: remote is not capable")
+            isSupported = false
+            initializeNotSupportedActions()
+            return
+        }
+        Log.d(TAG, "onNegotiated: setup updates")
+        initializeParticipantUpdates()
+        initializeActions(negotiatedCapability)
+        remote.onCreateParticipantExtension(
+            negotiatedCapability.featureVersion,
+            negotiatedCapability.supportedActions,
+            participantStateListener
+        )
+        val remoteBinder = remoteBinderListener.firstOrNull()
+        actions.forEach { connector -> connector.value.onActionConnected(remoteBinder) }
+    }
+
+    /** Setup callback updates when [participants] or [activeParticipant] changes */
+    private fun initializeParticipantUpdates() {
+        participants
+            .onEach { participantsState ->
+                participantsUpdate.onParticipantsUpdated(participantsState)
+            }
+            .combine(activeParticipant) { p, ap ->
+                ap?.let { p.firstOrNull { participant -> participant.id == ap } }
+            }
+            .distinctUntilChanged()
+            .onEach { activeParticipant ->
+                activeParticipantsUpdate.onActiveParticipantChanged(activeParticipant)
+            }
+            .onCompletion { Log.d(TAG, "participant flow complete") }
+            .launchIn(callScope)
+    }
+
+    /**
+     * Call the [ActionInitializer] callback on each action to initialize the action with whether or
+     * not the action is supported and provide the ability for the action to register for remote
+     * state callbacks.
+     */
+    private fun initializeActions(negotiatedCapability: Capability) {
+        for (action in actions) {
+            Log.d(TAG, "initializeActions: setup action=${action.key}")
+            if (negotiatedCapability.supportedActions.contains(action.key)) {
+                Log.d(TAG, "initializeActions: action=${action.key} supported")
+                action.value.onInitialization(callbacks, callScope, true)
+            } else {
+                Log.d(TAG, "initializeActions: action=${action.key} not supported")
+                action.value.onInitialization(callbacks, callScope, false)
+            }
+        }
+    }
+
+    /**
+     * In the case that participants are not supported, notify all actions that they are also not
+     * supported.
+     */
+    private fun initializeNotSupportedActions() {
+        Log.d(TAG, "initializeActions: no actions supported")
+        for (action in actions) {
+            action.value.onInitialization(callbacks, callScope, false)
+        }
+    }
+}
+
+/**
+ * Adds the ability for participants to raise their hands.
+ *
+ * ```
+ * connectExtensions(call) {
+ *     val participantExtension = addParticipantExtension(
+ *         // consume participant changed events
+ *     )
+ *     val raiseHandAction = participantExtension.addRaiseHandAction { raisedHands ->
+ *         // consume changes of participants with their hands raised
+ *     }
+ *     onConnected {
+ *         // extensions have been negotiated and actions are ready to be used
+ *         ...
+ *         // notify the remote that this user has changed their hand raised state
+ *         val raisedHandResult = raiseHandAction.setRaisedHandState(userHandRaisedState)
+ *     }
+ * }
+ * ```
+ */
+// TODO: Refactor to Public API
+@RequiresApi(Build.VERSION_CODES.O)
+@ExperimentalAppActions
+internal fun ParticipantClientExtensionNew.addRaiseHandAction(
+    stateUpdate: RaisedHandsUpdate
+): RaiseHandClientAction {
+    val action = RaiseHandClientAction(participants, stateUpdate)
+    registerAction(CallsManager.RAISE_HAND_ACTION, action::connect) { scope, isSupported ->
+        Log.d(ParticipantClientExtensionNew.TAG, "addRaiseHandAction: initialize")
+        raisedHandsStateCallback = action::raisedHandsStateChanged
+        action.initialize(scope, isSupported)
+    }
+    return action
+}
+
+/**
+ * Implements the ability for the user to raise/lower their hand as well as allow the user to listen
+ * to the hand raised states of all other participants
+ *
+ * @param participants The StateFlow containing the current set of participants in the call at any
+ *   given time.
+ * @param stateUpdate The callback that allows the user to listen to the state of participants that
+ *   have their hand raised
+ */
+// TODO: Refactor to Public API
+@RequiresApi(Build.VERSION_CODES.O)
+@ExperimentalAppActions
+internal class RaiseHandClientAction(
+    private val participants: StateFlow<Set<Participant>>,
+    private val stateUpdate: RaisedHandsUpdate
+) {
+    companion object {
+        const val TAG = CallExtensionsScope.TAG + "(RHCA)"
+    }
+
+    /**
+     * Whether or not raising/lowering hands is supported by the remote.
+     *
+     * if `true`, then updates about raised hands will be notified. If `false`, then the remote
+     * doesn't support this action this state will not be notified to the caller.
+     *
+     * Should not be queried until [CallExtensionsScope.onConnected] is called.
+     */
+    var isSupported by Delegates.notNull<Boolean>()
+
+    // Contains the remote Binder interface used to notify the remote application of events
+    private var remoteActions: ParticipantActionsRemote? = null
+    // Contains the current state of participants that have their hands raised
+    private val raisedHandState = MutableStateFlow<Set<Int>>(emptySet())
+
+    /**
+     * Request the remote application to raise or lower this user's hand.
+     *
+     * Note: This operation succeeding does not mean that the raised hand state of the user has
+     * changed. It only means that the request was received by the remote application.
+     *
+     * @param isRaised `true` if this user has raised their hand, `false` if they have lowered their
+     *   hand
+     * @return Whether or not the remote application received this event. This does not mean that
+     *   the operation succeeded, but rather the remote received the event successfully.
+     */
+    suspend fun requestRaisedHandStateChange(isRaised: Boolean): CallControlResult {
+        Log.d(TAG, "setRaisedHandState: isRaised=$isRaised")
+        if (remoteActions == null) {
+            Log.w(TAG, "setRaisedHandState: no binder, isSupported=$isSupported")
+            // TODO: This needs to have its own CallException result
+            return CallControlResult.Error(CallException.ERROR_UNKNOWN)
+        }
+        val cb = ActionsResultCallback()
+        remoteActions?.setHandRaised(isRaised, cb)
+        val result = cb.waitForResponse()
+        Log.d(TAG, "setRaisedHandState: isRaised=$isRaised, result=$result")
+        return result
+    }
+
+    /** Called when the remote application has changed the raised hands state */
+    internal suspend fun raisedHandsStateChanged(raisedHands: Set<Int>) {
+        Log.d(TAG, "raisedHandsStateChanged to $raisedHands")
+        raisedHandState.emit(raisedHands)
+    }
+
+    /** Called when capability exchange has completed and we should setup the action */
+    internal fun initialize(callScope: CoroutineScope, isSupported: Boolean) {
+        Log.d(TAG, "initialize, isSupported=$isSupported")
+        this.isSupported = isSupported
+        if (isSupported) {
+            participants
+                .combine(raisedHandState) { p, rhs -> p.filter { rhs.contains(it.id) } }
+                .distinctUntilChanged()
+                .onEach { filtered -> stateUpdate.onRaisedHandsChanged(filtered.toSet()) }
+                .onCompletion { Log.d(TAG, "raised hands flow complete") }
+                .launchIn(callScope)
+        }
+    }
+
+    /** Called when the remote has connected for Actions and events are available */
+    internal fun connect(remote: ParticipantActionsRemote?) {
+        Log.d(TAG, "connect: remote is null=${remote == null}")
+        remoteActions = remote
+    }
+}
+
+/**
+ * Adds the ability for the user to kick participants.
+ *
+ * ```
+ * connectExtensions(call) {
+ *     val participantExtension = addParticipantExtension(
+ *         // consume participant changed events
+ *     )
+ *     val kickParticipantAction = participantExtension.addKickParticipantAction()
+ *
+ *     onConnected {
+ *         // extensions have been negotiated and actions are ready to be used
+ *         ...
+ *         // kick a participant
+ *         val kickResult = kickParticipantAction.kickParticipant(participant)
+ *     }
+ * }
+ * ```
+ */
+// TODO: Refactor to Public API
+@RequiresApi(Build.VERSION_CODES.O)
+@ExperimentalAppActions
+internal fun ParticipantClientExtensionNew.addKickParticipantAction(): KickParticipantClientAction {
+    val action = KickParticipantClientAction(participants)
+    registerAction(CallsManager.KICK_PARTICIPANT_ACTION, action::connect) { _, isSupported ->
+        action.initialize(isSupported)
+    }
+    return action
+}
+
+/**
+ * Implements the action to kick a participant
+ *
+ * @param participants The current set of participants
+ */
+// TODO: Refactor to Public API
+@RequiresApi(Build.VERSION_CODES.O)
+@ExperimentalAppActions
+internal class KickParticipantClientAction(
+    private val participants: StateFlow<Set<Participant>>,
+) {
+    companion object {
+        const val TAG = CallExtensionsScope.TAG + "(KPCA)"
+    }
+
+    /**
+     * Whether or not kicking participants is supported by the remote.
+     *
+     * if `true`, then requests to kick participants will be sent to the remote application. If
+     * `false`, then the remote doesn't support this action and requests will fail.
+     *
+     * Should not be queried until [CallExtensionsScope.onConnected] is called.
+     */
+    var isSupported by Delegates.notNull<Boolean>()
+    // The binder interface that allows this action to send events to the remote
+    private var remoteActions: ParticipantActionsRemote? = null
+
+    /**
+     * Request to kick a [participant] in the call.
+     *
+     * Note: This operation succeeding does not mean that the participant was kicked, it only means
+     * that the request was received by the remote application.
+     *
+     * @param participant The participant to kick
+     * @return The result of whether or not this request was successfully sent to the remote
+     *   application
+     */
+    suspend fun requestKickParticipant(participant: Participant): CallControlResult {
+        Log.d(TAG, "kickParticipant: participant=$participant")
+        if (remoteActions == null) {
+            Log.w(TAG, "kickParticipant: no binder, isSupported=$isSupported")
+            // TODO: This needs to have its own CallException result
+            return CallControlResult.Error(CallException.ERROR_UNKNOWN)
+        }
+        if (!participants.value.contains(participant)) {
+            Log.d(TAG, "kickParticipant: couldn't find participant=$participant")
+            return CallControlResult.Success()
+        }
+        val cb = ActionsResultCallback()
+        remoteActions?.kickParticipant(participant, cb)
+        val result = cb.waitForResponse()
+        Log.d(TAG, "kickParticipant: participant=$participant, result=$result")
+        return result
+    }
+
+    /** Called when capability exchange has completed and we can initialize this action */
+    fun initialize(isSupported: Boolean) {
+        Log.d(TAG, "initialize: isSupported=$isSupported")
+        this.isSupported = isSupported
+    }
+
+    /** Called when the remote application has connected and will receive action event requests */
+    internal fun connect(remote: ParticipantActionsRemote?) {
+        Log.d(TAG, "connect: remote is null=${remote == null}")
+        remoteActions = remote
+    }
+}
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/extensions/ParticipantExtension.kt b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/ParticipantExtension.kt
index 4b75ee0..aab00e3 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/extensions/ParticipantExtension.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/extensions/ParticipantExtension.kt
@@ -43,6 +43,7 @@
  * @param initialActiveParticipant The initial participant that is active in the call
  * @return The interface used to update the participant state to remote InCallServices
  */
+// TODO: Refactor to Public API
 @RequiresApi(Build.VERSION_CODES.O)
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
@@ -112,6 +113,7 @@
  * @param initialParticipants The initial set of Participants that are associated with this call.
  * @param initialActiveParticipant The initial active Participant that is associated with this call.
  */
+// TODO: Refactor to Public API
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
 @RequiresApi(VERSION_CODES.O)
@@ -264,6 +266,7 @@
 }
 
 /** Action interface implemented by the listener of the RaiseHandAction */
+// TODO: Refactor to Public API
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
 fun interface RaiseHandAction {
@@ -280,6 +283,7 @@
  * Interface used to communicate with remote InCallServices in order to update the current raised
  * hand state of all Participants
  */
+// TODO: Refactor to Public API
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
 fun interface RaiseHandActionState {
@@ -300,6 +304,7 @@
  * @return The interface used to update the current raised hand state of all participants in the
  *   call.
  */
+// TODO: Refactor to Public API
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
 @RequiresApi(VERSION_CODES.O)
@@ -370,6 +375,7 @@
  * Interface used to notify callers when the remote InCallService has requested to kick a
  * participant.
  */
+// TODO: Refactor to Public API
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
 fun interface KickParticipantAction {
@@ -386,6 +392,7 @@
  *
  * @param action The action to perform when the user requests to kick a participant
  */
+// TODO: Refactor to Public API
 @ExperimentalAppActions
 @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)
 @RequiresApi(VERSION_CODES.O)
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/AidlExtensions.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/AidlExtensions.kt
index 9e90998..ff2d3b1 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/AidlExtensions.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/AidlExtensions.kt
@@ -16,6 +16,7 @@
 
 package androidx.core.telecom.internal
 
+import android.util.Log
 import androidx.core.telecom.extensions.IActionsResultCallback
 import androidx.core.telecom.extensions.ICallDetailsListener
 import androidx.core.telecom.extensions.ICapabilityExchange
@@ -53,6 +54,11 @@
     }
 }
 
+/** Remote interface used by InCallServices to send action events to the VOIP application. */
+@ExperimentalAppActions
+internal class ParticipantActionsRemote(binder: IParticipantActions) :
+    IParticipantActions by binder
+
 /**
  * Remote interface used to notify the ICS of participant state information
  *
@@ -71,11 +77,49 @@
 internal class CapabilityExchangeRemote(binder: ICapabilityExchange) :
     ICapabilityExchange by binder
 
+/**
+ * Remote interface for [ICapabilityExchangeListener] that InCallServices use to communicate with
+ * the remote VOIP application.
+ */
 @ExperimentalAppActions
 internal class CapabilityExchangeListenerRemote(binder: ICapabilityExchangeListener) :
     ICapabilityExchangeListener by binder
 
 /**
+ * Adapter class that implements [IParticipantStateListener] AIDL and calls the associated callbacks
+ */
+@ExperimentalAppActions
+internal class ParticipantStateListener(
+    private val updateParticipants: (Set<Participant>) -> Unit,
+    private val updateActiveParticipant: (Int?) -> Unit,
+    private val updateRaisedHands: (Set<Int>) -> Unit,
+    private val finishSync: (ParticipantActionsRemote?) -> Unit
+) : IParticipantStateListener.Stub() {
+    override fun updateParticipants(participants: Array<out Participant>?) {
+        updateParticipants.invoke(participants?.toSet() ?: emptySet())
+    }
+
+    override fun updateActiveParticipant(activeParticipant: Int) {
+        if (activeParticipant < 0) {
+            updateActiveParticipant.invoke(null)
+        } else {
+            updateActiveParticipant.invoke(activeParticipant)
+        }
+    }
+
+    override fun updateRaisedHandsAction(participants: IntArray?) {
+        updateRaisedHands.invoke(participants?.toSet() ?: emptySet())
+    }
+
+    override fun finishSync(cb: IParticipantActions?) {
+        if (cb == null) {
+            Log.w("AidlExtensions", "finishSync returned null actions!")
+        }
+        finishSync.invoke(cb?.let { ParticipantActionsRemote(it) })
+    }
+}
+
+/**
  * The implementation of the capability exchange listener, which is used by the InCallService to
  * create and remove extensions.
  *
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
index d9ec3f0..8777b6c 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
@@ -32,6 +32,7 @@
 import androidx.core.telecom.CallEndpointCompat
 import androidx.core.telecom.internal.utils.EndpointUtils
 import androidx.core.telecom.internal.utils.EndpointUtils.Companion.getSpeakerEndpoint
+import androidx.core.telecom.internal.utils.EndpointUtils.Companion.isBluetoothAvailable
 import androidx.core.telecom.internal.utils.EndpointUtils.Companion.isEarpieceEndpoint
 import androidx.core.telecom.internal.utils.EndpointUtils.Companion.isWiredHeadsetOrBtEndpoint
 import java.util.function.Consumer
@@ -39,6 +40,7 @@
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.launch
@@ -71,7 +73,8 @@
 
     companion object {
         private val TAG: String = CallSession::class.java.simpleName
-        private const val SWITCH_TO_SPEAKER_TIMEOUT: Long = 1000L
+        private const val WAIT_FOR_BT_TO_CONNECT_TIMEOUT: Long = 1000L
+        private const val SWITCH_TO_SPEAKER_TIMEOUT: Long = WAIT_FOR_BT_TO_CONNECT_TIMEOUT + 1000L
     }
 
     fun getIsCurrentEndpointSet(): CompletableDeferred<Unit> {
@@ -82,6 +85,16 @@
         return mIsAvailableEndpointsSet
     }
 
+    @VisibleForTesting
+    fun setCurrentCallEndpoint(endpoint: CallEndpointCompat) {
+        mCurrentCallEndpoint = endpoint
+    }
+
+    @VisibleForTesting
+    fun setAvailableCallEndpoints(endpoints: List<CallEndpointCompat>) {
+        mAvailableEndpoints = endpoints
+    }
+
     override fun onCallEndpointChanged(endpoint: CallEndpoint) {
         val previousCallEndpoint = mCurrentCallEndpoint
         mCurrentCallEndpoint = EndpointUtils.Api34PlusImpl.toCallEndpointCompat(endpoint)
@@ -141,11 +154,7 @@
                         "maybeSwitchToSpeaker: detected a video call that started" +
                             " with the earpiece audio route. requesting switch to speaker."
                     )
-                    mPlatformInterface?.requestCallEndpointChange(
-                        EndpointUtils.Api34PlusImpl.toCallEndpoint(speakerCompat),
-                        Runnable::run,
-                        {}
-                    )
+                    maybeDelaySwitchToSpeaker(speakerCompat)
                 }
             }
         } catch (e: Exception) {
@@ -153,6 +162,44 @@
         }
     }
 
+    // Users reported in b/345309071 that the call started on speakerphone instead
+    // of bluetooth.  Upon inspection, the platform was echoing the earpiece audio
+    // route first while BT was still connecting. Avoid overriding the BT route by
+    // waiting a second. TODO:: b/351899854
+    suspend fun maybeDelaySwitchToSpeaker(speakerCompat: CallEndpointCompat): Boolean {
+        if (isBluetoothAvailable(mAvailableEndpoints)) {
+            // The platform could potentially be connecting to BT. wait...
+            delay(WAIT_FOR_BT_TO_CONNECT_TIMEOUT)
+            // only switch to speaker if BT did not connect
+            if (!isBluetoothConnected()) {
+                Log.i(TAG, "maybeDelaySwitchToSpeaker: BT did not connect in time!")
+                switchToEndpoint(speakerCompat)
+                return true
+            }
+            Log.i(TAG, "maybeDelaySwitchToSpeaker: BT connected! voiding speaker switch.")
+            return false
+        } else {
+            // otherwise, immediately change from earpiece to speaker because the platform is
+            // not in the process of connecting a BT device.
+            Log.i(TAG, "maybeDelaySwitchToSpeaker: no BT route available.")
+            switchToEndpoint(speakerCompat)
+            return true
+        }
+    }
+
+    private fun isBluetoothConnected(): Boolean {
+        return mCurrentCallEndpoint != null &&
+            mCurrentCallEndpoint!!.type == CallEndpoint.TYPE_BLUETOOTH
+    }
+
+    private fun switchToEndpoint(endpoint: CallEndpointCompat) {
+        mPlatformInterface?.requestCallEndpointChange(
+            EndpointUtils.Api34PlusImpl.toCallEndpoint(endpoint),
+            Runnable::run,
+            {}
+        )
+    }
+
     /**
      * Due to the fact that OEMs may diverge from AOSP telecom platform behavior, Core-Telecom needs
      * to ensure that if a video calls headset disconnects, the speakerphone is defaulted instead of
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
index 814996e..b450a38 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
@@ -23,9 +23,9 @@
 import android.os.ParcelUuid
 import android.telecom.Call
 import android.telecom.CallAudioState
+import android.telecom.CallEndpoint
 import android.telecom.DisconnectCause
 import android.util.Log
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.core.telecom.CallAttributesCompat
@@ -35,11 +35,13 @@
 import androidx.core.telecom.CallException
 import androidx.core.telecom.internal.utils.EndpointUtils
 import androidx.core.telecom.internal.utils.EndpointUtils.Companion.getSpeakerEndpoint
+import androidx.core.telecom.internal.utils.EndpointUtils.Companion.isBluetoothAvailable
 import androidx.core.telecom.internal.utils.EndpointUtils.Companion.isEarpieceEndpoint
 import androidx.core.telecom.internal.utils.EndpointUtils.Companion.isWiredHeadsetOrBtEndpoint
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.launch
@@ -66,7 +68,7 @@
 
     companion object {
         private val TAG: String = CallSessionLegacy::class.java.simpleName
-
+        private const val WAIT_FOR_BT_TO_CONNECT_TIMEOUT: Long = 1000L
         // CallStates. All these states mirror the values in the platform.
         const val STATE_INITIALIZING = 0
         const val STATE_NEW = 1
@@ -146,7 +148,29 @@
                         "maybeSwitchToSpeaker: detected a video call that started" +
                             " with the earpiece audio route. requesting switch to speaker."
                     )
-                    requestEndpointChange(speakerEndpoint)
+                    CoroutineScope(coroutineContext).launch {
+                        // Users reported in b/345309071 that the call started on speakerphone
+                        // instead of bluetooth.  Upon inspection, the platform was echoing the
+                        // earpiece audio route first while BT was still connecting. Avoid
+                        // overriding the BT route by waiting a second. TODO:: b/351899854
+                        if (isBluetoothAvailable(availableEndpoints)) {
+                            delay(WAIT_FOR_BT_TO_CONNECT_TIMEOUT)
+                            if (!isBluetoothConnected()) {
+                                Log.i(TAG, "maybeSwitchToSpeaker: BT did not connect in time!")
+                                requestEndpointChange(speakerEndpoint)
+                            } else {
+                                Log.i(
+                                    TAG,
+                                    "maybeSwitchToSpeaker: BT connected! void speaker switch"
+                                )
+                            }
+                        } else {
+                            // otherwise, immediately change from earpiece to speaker because the
+                            // platform is
+                            // not in the process of connecting a BT device.
+                            requestEndpointChange(speakerEndpoint)
+                        }
+                    }
                 }
             } catch (e: Exception) {
                 Log.e(TAG, "maybeSwitchToSpeaker: hit exception=[$e]")
@@ -155,6 +179,11 @@
         }
     }
 
+    private fun isBluetoothConnected(): Boolean {
+        return mCurrentCallEndpoint != null &&
+            mCurrentCallEndpoint!!.type == CallEndpoint.TYPE_BLUETOOTH
+    }
+
     /**
      * Due to the fact that OEMs may diverge from AOSP telecom platform behavior, Core-Telecom needs
      * to ensure that if a video calls headset disconnects, the speakerphone is defaulted instead of
@@ -256,7 +285,6 @@
     @RequiresApi(VERSION_CODES.O)
     private object Api26PlusImpl {
         @JvmStatic
-        @DoNotInline
         fun setAudio(callEndpoint: CallEndpointCompat, connection: CallSessionLegacy) {
             connection.setAudioRoute(EndpointUtils.mapTypeToRoute(callEndpoint.type))
         }
@@ -266,7 +294,6 @@
     @RequiresApi(VERSION_CODES.P)
     private object Api28PlusImpl {
         @JvmStatic
-        @DoNotInline
         fun setAudio(
             callEndpoint: CallEndpointCompat,
             connection: CallSessionLegacy,
@@ -286,7 +313,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun refreshBluetoothDeviceCache(
             btCacheList: ArrayList<BluetoothDevice>,
             state: CallAudioState
@@ -296,7 +322,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun getBluetoothDeviceFromEndpoint(
             btCacheList: ArrayList<BluetoothDevice>,
             endpoint: CallEndpointCompat
@@ -386,7 +411,7 @@
             } catch (e: Exception) {
                 throw e
             } finally {
-                setDisconnected(DisconnectCause(DisconnectCause.REJECTED))
+                setConnectionDisconnect(DisconnectCause(DisconnectCause.REJECTED))
                 blockingSessionExecution.complete(Unit)
             }
         }
@@ -401,7 +426,7 @@
             } catch (e: Exception) {
                 throw e
             } finally {
-                setDisconnected(DisconnectCause(DisconnectCause.REJECTED))
+                setConnectionDisconnect(DisconnectCause(DisconnectCause.REJECTED))
                 blockingSessionExecution.complete(Unit)
             }
         }
@@ -416,7 +441,7 @@
             } catch (e: Exception) {
                 throw e
             } finally {
-                setDisconnected(DisconnectCause(DisconnectCause.REJECTED))
+                setConnectionDisconnect(DisconnectCause(DisconnectCause.REJECTED))
                 blockingSessionExecution.complete(Unit)
             }
         }
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/InCallServiceCompat.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/InCallServiceCompat.kt
index b371b25..5826459 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/InCallServiceCompat.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/InCallServiceCompat.kt
@@ -38,6 +38,7 @@
 import androidx.annotation.VisibleForTesting
 import androidx.core.content.ContextCompat
 import androidx.core.telecom.CallsManager
+import androidx.core.telecom.extensions.CallExtensionsScope
 import androidx.core.telecom.util.ExperimentalAppActions
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
@@ -119,14 +120,16 @@
         super.onDestroy()
     }
 
+    // TODO: Refactor away onAddCallCompat
     @RequiresApi(Build.VERSION_CODES.O)
-    final override fun onCallAdded(@NonNull call: Call) {
+    override fun onCallAdded(@NonNull call: Call) {
         super.onCallAdded(call)
         processCallAdded(call)
     }
 
+    // TODO: Refactor away onRemoveCallCompat
     @RequiresApi(Build.VERSION_CODES.O)
-    final override fun onCallRemoved(call: Call?) {
+    override fun onCallRemoved(call: Call?) {
         if (call == null) return
         mCallCompats
             .find { Objects.equals(it.toCall(), call) }
@@ -136,6 +139,51 @@
             }
     }
 
+    /**
+     * Connects extensions to the provided [Call], allowing the call to support additional optional
+     * behaviors beyond the traditional call state management.
+     *
+     * For example, an extension may allow the participants of a meeting to be surfaced to this
+     * application so that the user can view and manage the participants in the meeting on different
+     * surfaces:
+     * ```
+     * class InCallServiceImpl : InCallServiceCompat() {
+     * ...
+     *   override fun onCallAdded(call: Call) {
+     *     lifecycleScope.launch {
+     *       connectExtensions(context, call) {
+     *         // Initialize extensions
+     *         onConnected { call ->
+     *           // change call states & listen/update extensions
+     *         }
+     *       }
+     *       // Once the call is destroyed, control flow will resume again
+     *     }
+     *   }
+     *  ...
+     * }
+     * ```
+     *
+     * @param call The Call to connect extensions on.
+     * @param init The scope used to initialize and manage extensions in the scope of the Call.
+     */
+    // TODO: Refactor to Public API
+    @ExperimentalAppActions
+    @RequiresApi(Build.VERSION_CODES.O)
+    suspend fun connectExtensions(call: Call, init: CallExtensionsScope.() -> Unit) {
+        // Attach this to the scope of the InCallService so it does not outlive its lifecycle
+        lifecycleScope
+            .launch {
+                val scope = CallExtensionsScope(applicationContext, this, call)
+                Log.v(TAG, "connectExtensions: calling init")
+                scope.init()
+                Log.v(TAG, "connectExtensions: connecting extensions")
+                scope.connectExtensionSession()
+            }
+            .join()
+        Log.d(TAG, "connectExtensions: complete")
+    }
+
     /** Create a flow that reports changes to [Call.Details] provided by the [Call.Callback]. */
     private suspend fun detailsFlow(call: Call): Flow<Call.Details> = callbackFlow {
         val callback =
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/CallAttributesUtils.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/CallAttributesUtils.kt
index 7ae880d..3285fb2 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/CallAttributesUtils.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/CallAttributesUtils.kt
@@ -18,7 +18,6 @@
 
 import android.net.Uri
 import android.telecom.PhoneAccountHandle
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.telecom.CallAttributesCompat
 
@@ -26,7 +25,6 @@
 internal class CallAttributesUtils {
     internal object Api34PlusImpl {
         @JvmStatic
-        @DoNotInline
         fun toTelecomCallAttributes(
             phoneAccountHandle: PhoneAccountHandle,
             direction: Int,
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/EndpointUtils.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/EndpointUtils.kt
index eae8af6..d2ea85a 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/EndpointUtils.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/EndpointUtils.kt
@@ -21,7 +21,6 @@
 import android.os.Build.VERSION.SDK_INT
 import android.os.Build.VERSION_CODES.P
 import android.telecom.CallAudioState
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.telecom.CallEndpointCompat
 
@@ -38,6 +37,15 @@
             return null
         }
 
+        fun isBluetoothAvailable(endpoints: List<CallEndpointCompat>): Boolean {
+            for (e in endpoints) {
+                if (e.type == CallEndpointCompat.TYPE_BLUETOOTH) {
+                    return true
+                }
+            }
+            return false
+        }
+
         fun isEarpieceEndpoint(endpoint: CallEndpointCompat?): Boolean {
             if (endpoint == null) {
                 return false
@@ -170,7 +178,6 @@
     @RequiresApi(34)
     object Api34PlusImpl {
         @JvmStatic
-        @DoNotInline
         fun toCallEndpointCompat(endpoint: android.telecom.CallEndpoint): CallEndpointCompat {
             return CallEndpointCompat(
                 endpoint.endpointName,
@@ -180,7 +187,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun toCallEndpointsCompat(
             endpoints: List<android.telecom.CallEndpoint>
         ): List<CallEndpointCompat> {
@@ -192,7 +198,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun toCallEndpoint(e: CallEndpointCompat): android.telecom.CallEndpoint {
             return android.telecom.CallEndpoint(e.name, e.type, e.identifier)
         }
@@ -201,7 +206,6 @@
     @RequiresApi(28)
     object BluetoothApi28PlusImpl {
         @JvmStatic
-        @DoNotInline
         fun getBluetoothEndpoints(state: CallAudioState): ArrayList<CallEndpointCompat> {
             val endpoints: ArrayList<CallEndpointCompat> = ArrayList()
             val supportedBluetoothDevices = state.supportedBluetoothDevices
@@ -212,7 +216,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun getCallEndpointFromBluetoothDevice(btDevice: BluetoothDevice?): CallEndpointCompat {
             var endpointName: String = "Bluetooth Device"
             var endpointIdentity: String = "Unknown Address"
@@ -232,7 +235,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun getCallEndpointFromAudioState(state: CallAudioState): CallEndpointCompat {
             return getCallEndpointFromBluetoothDevice(state.activeBluetoothDevice)
         }
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/Utils.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/Utils.kt
index db906a3..a665edc 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/Utils.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/utils/Utils.kt
@@ -23,7 +23,6 @@
 import android.telecom.PhoneAccountHandle
 import android.telecom.TelecomManager
 import android.util.Log
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.telecom.CallAttributesCompat
 import androidx.core.telecom.CallsManager
@@ -134,7 +133,6 @@
         @RequiresApi(VERSION_CODES.M)
         private object Api23PlusImpl {
             @JvmStatic
-            @DoNotInline
             fun createExtras(
                 callAttributes: CallAttributesCompat,
                 handle: PhoneAccountHandle
diff --git a/core/core-testing/build.gradle b/core/core-testing/build.gradle
index ce43285..2893777 100644
--- a/core/core-testing/build.gradle
+++ b/core/core-testing/build.gradle
@@ -35,7 +35,7 @@
         implementation(project(":core:core"))
     }
     api(libs.kotlinStdlib)
-    implementation("androidx.annotation:annotation:1.6.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     api(project(":core:core"))
 
     testImplementation(libs.testCore)
@@ -44,6 +44,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.core.testing"
 }
 
@@ -53,6 +54,5 @@
     mavenVersion = LibraryVersions.CORE
     inceptionYear = "2023"
     description = "Provides extensions for tests using Core APIs."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 4ca6ed5..6448a6d 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1850,6 +1850,13 @@
 
 package androidx.core.os {
 
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public enum BufferFillPolicy {
+    method public int getValue();
+    property public final int value;
+    enum_constant public static final androidx.core.os.BufferFillPolicy DISCARD;
+    enum_constant public static final androidx.core.os.BufferFillPolicy RING_BUFFER;
+  }
+
   public final class BuildCompat {
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
@@ -1917,6 +1924,17 @@
     method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
   }
 
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class HeapProfileRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.HeapProfileRequestBuilder> {
+    method public androidx.core.os.HeapProfileRequestBuilder setBufferSizeKb(int bufferSizeKb);
+    method public androidx.core.os.HeapProfileRequestBuilder setDurationMs(int durationMs);
+    method public androidx.core.os.HeapProfileRequestBuilder setSamplingIntervalBytes(long samplingIntervalBytes);
+    method public androidx.core.os.HeapProfileRequestBuilder setTrackJavaAllocations(boolean traceJavaAllocations);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class JavaHeapDumpRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.JavaHeapDumpRequestBuilder> {
+    method public androidx.core.os.JavaHeapDumpRequestBuilder setBufferSizeKb(int bufferSizeKb);
+  }
+
   public final class LocaleListCompat {
     method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
     method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
@@ -1975,6 +1993,34 @@
     method public static boolean isApplicationUid(int);
   }
 
+  public final class Profiling {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static kotlinx.coroutines.flow.Flow<android.os.ProfilingResult> registerForAllProfilingResults(android.content.Context context);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static void registerForAllProfilingResults(android.content.Context context, java.util.concurrent.Executor executor, java.util.function.Consumer<android.os.ProfilingResult> listener);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.HeapProfileRequestBuilder requestHeapProfile();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.JavaHeapDumpRequestBuilder requestJavaHeapDump();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.StackSamplingRequestBuilder requestStackSampling();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.SystemTraceRequestBuilder requestSystemTrace();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static void unregisterForAllProfilingResults(android.content.Context context, java.util.function.Consumer<android.os.ProfilingResult> listener);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public abstract class RequestBuilder<T extends androidx.core.os.RequestBuilder<T>> {
+    method public final void request(android.content.Context context, java.util.concurrent.Executor? executor, java.util.function.Consumer<android.os.ProfilingResult>? listener);
+    method public final T setCancellationSignal(android.os.CancellationSignal cancellationSignal);
+    method public final T setTag(String tag);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class StackSamplingRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.StackSamplingRequestBuilder> {
+    method public androidx.core.os.StackSamplingRequestBuilder setBufferSizeKb(int bufferSizeKb);
+    method public androidx.core.os.StackSamplingRequestBuilder setDurationMs(int durationMs);
+    method public androidx.core.os.StackSamplingRequestBuilder setSamplingFrequencyHz(int samplingFrequencyHz);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class SystemTraceRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.SystemTraceRequestBuilder> {
+    method public androidx.core.os.SystemTraceRequestBuilder setBufferFillPolicy(androidx.core.os.BufferFillPolicy bufferFillPolicy);
+    method public androidx.core.os.SystemTraceRequestBuilder setBufferSizeKb(int bufferSizeKb);
+    method public androidx.core.os.SystemTraceRequestBuilder setDurationMs(int durationMs);
+  }
+
   @Deprecated public final class TraceCompat {
     method @Deprecated public static void beginAsyncSection(String, int);
     method @Deprecated public static void beginSection(String);
@@ -2030,6 +2076,7 @@
     method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Deprecated public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
     method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, int, java.util.concurrent.Executor?, java.util.concurrent.Executor, androidx.core.provider.FontsContractCompat.FontRequestCallback);
+    method public static void requestFontWithFallbackChain(android.content.Context, java.util.List<androidx.core.provider.FontRequest!>, int, java.util.concurrent.Executor?, java.util.concurrent.Executor, androidx.core.provider.FontsContractCompat.FontRequestCallback);
   }
 
   public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
@@ -2048,6 +2095,7 @@
 
   public static class FontsContractCompat.FontFamilyResult {
     method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+    method public java.util.List<androidx.core.provider.FontsContractCompat.FontInfo![]!> getFontsWithFallbacks();
     method public int getStatusCode();
     field public static final int STATUS_OK = 0; // 0x0
     field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 93b61a0..7e2492c 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1593,6 +1593,7 @@
 
   public static final class FontResourcesParserCompat.ProviderResourceEntry implements androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry {
     ctor public FontResourcesParserCompat.ProviderResourceEntry(androidx.core.provider.FontRequest, @androidx.core.content.res.FontResourcesParserCompat.FetchStrategy int, int);
+    method public androidx.core.provider.FontRequest? getFallbackRequest();
     method @androidx.core.content.res.FontResourcesParserCompat.FetchStrategy public int getFetchStrategy();
     method public androidx.core.provider.FontRequest getRequest();
     method public int getTimeout();
@@ -1808,6 +1809,7 @@
     method protected android.graphics.Typeface? createFromFamiliesWithDefault(Object!);
     method public android.graphics.Typeface? createFromFontFamilyFilesResourceEntry(android.content.Context!, androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry!, android.content.res.Resources!, int);
     method public android.graphics.Typeface? createFromFontInfo(android.content.Context!, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method @RequiresApi(29) public android.graphics.Typeface? createFromFontInfoWithFallback(android.content.Context, android.os.CancellationSignal?, java.util.List<androidx.core.provider.FontsContractCompat.FontInfo![]!>, int);
     method public android.graphics.Typeface? createFromResourcesFontFile(android.content.Context!, android.content.res.Resources!, int, String!, int);
     method protected java.lang.reflect.Method! obtainAbortCreationMethod(Class<? extends java.lang.Object!>!) throws java.lang.NoSuchMethodException;
     method protected java.lang.reflect.Method! obtainAddFontFromAssetManagerMethod(Class<? extends java.lang.Object!>!) throws java.lang.NoSuchMethodException;
@@ -1833,6 +1835,7 @@
     ctor public TypefaceCompatApi29Impl();
     method public android.graphics.Typeface? createFromFontFamilyFilesResourceEntry(android.content.Context!, androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry!, android.content.res.Resources!, int);
     method public android.graphics.Typeface? createFromFontInfo(android.content.Context!, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method public android.graphics.Typeface? createFromFontInfoWithFallback(android.content.Context, android.os.CancellationSignal?, java.util.List<androidx.core.provider.FontsContractCompat.FontInfo![]!>, int);
     method protected android.graphics.Typeface! createFromInputStream(android.content.Context!, java.io.InputStream!);
     method public android.graphics.Typeface? createFromResourcesFontFile(android.content.Context!, android.content.res.Resources!, int, String!, int);
     method protected androidx.core.provider.FontsContractCompat.FontInfo! findBestInfo(androidx.core.provider.FontsContractCompat.FontInfo![]!, int);
@@ -2240,6 +2243,13 @@
 
 package androidx.core.os {
 
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public enum BufferFillPolicy {
+    method public int getValue();
+    property public final int value;
+    enum_constant public static final androidx.core.os.BufferFillPolicy DISCARD;
+    enum_constant public static final androidx.core.os.BufferFillPolicy RING_BUFFER;
+  }
+
   public final class BuildCompat {
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
@@ -2307,6 +2317,23 @@
     method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
   }
 
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class HeapProfileRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.HeapProfileRequestBuilder> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected android.os.Bundle getParams();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected int getProfilingType();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected androidx.core.os.HeapProfileRequestBuilder getThis();
+    method public androidx.core.os.HeapProfileRequestBuilder setBufferSizeKb(int bufferSizeKb);
+    method public androidx.core.os.HeapProfileRequestBuilder setDurationMs(int durationMs);
+    method public androidx.core.os.HeapProfileRequestBuilder setSamplingIntervalBytes(long samplingIntervalBytes);
+    method public androidx.core.os.HeapProfileRequestBuilder setTrackJavaAllocations(boolean traceJavaAllocations);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class JavaHeapDumpRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.JavaHeapDumpRequestBuilder> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected android.os.Bundle getParams();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected int getProfilingType();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected androidx.core.os.JavaHeapDumpRequestBuilder getThis();
+    method public androidx.core.os.JavaHeapDumpRequestBuilder setBufferSizeKb(int bufferSizeKb);
+  }
+
   public final class LocaleListCompat {
     method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
     method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
@@ -2365,6 +2392,43 @@
     method public static boolean isApplicationUid(int);
   }
 
+  public final class Profiling {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static kotlinx.coroutines.flow.Flow<android.os.ProfilingResult> registerForAllProfilingResults(android.content.Context context);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static void registerForAllProfilingResults(android.content.Context context, java.util.concurrent.Executor executor, java.util.function.Consumer<android.os.ProfilingResult> listener);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.HeapProfileRequestBuilder requestHeapProfile();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.JavaHeapDumpRequestBuilder requestJavaHeapDump();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.StackSamplingRequestBuilder requestStackSampling();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static androidx.core.os.SystemTraceRequestBuilder requestSystemTrace();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public static void unregisterForAllProfilingResults(android.content.Context context, java.util.function.Consumer<android.os.ProfilingResult> listener);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public abstract class RequestBuilder<T extends androidx.core.os.RequestBuilder<T>> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected abstract android.os.Bundle getParams();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected abstract int getProfilingType();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected abstract T getThis();
+    method public final void request(android.content.Context context, java.util.concurrent.Executor? executor, java.util.function.Consumer<android.os.ProfilingResult>? listener);
+    method public final T setCancellationSignal(android.os.CancellationSignal cancellationSignal);
+    method public final T setTag(String tag);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class StackSamplingRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.StackSamplingRequestBuilder> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected android.os.Bundle getParams();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected int getProfilingType();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected androidx.core.os.StackSamplingRequestBuilder getThis();
+    method public androidx.core.os.StackSamplingRequestBuilder setBufferSizeKb(int bufferSizeKb);
+    method public androidx.core.os.StackSamplingRequestBuilder setDurationMs(int durationMs);
+    method public androidx.core.os.StackSamplingRequestBuilder setSamplingFrequencyHz(int samplingFrequencyHz);
+  }
+
+  @RequiresApi(api=android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final class SystemTraceRequestBuilder extends androidx.core.os.RequestBuilder<androidx.core.os.SystemTraceRequestBuilder> {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected android.os.Bundle getParams();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected int getProfilingType();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.SUBCLASSES) protected androidx.core.os.SystemTraceRequestBuilder getThis();
+    method public androidx.core.os.SystemTraceRequestBuilder setBufferFillPolicy(androidx.core.os.BufferFillPolicy bufferFillPolicy);
+    method public androidx.core.os.SystemTraceRequestBuilder setBufferSizeKb(int bufferSizeKb);
+    method public androidx.core.os.SystemTraceRequestBuilder setDurationMs(int durationMs);
+  }
+
   @Deprecated public final class TraceCompat {
     method @Deprecated public static void beginAsyncSection(String, int);
     method @Deprecated public static void beginSection(String);
@@ -2425,6 +2489,7 @@
     method @Deprecated public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? requestFont(android.content.Context, androidx.core.provider.FontRequest, int, boolean, @IntRange(from=0) int, android.os.Handler, androidx.core.provider.FontsContractCompat.FontRequestCallback);
     method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, int, java.util.concurrent.Executor?, java.util.concurrent.Executor, androidx.core.provider.FontsContractCompat.FontRequestCallback);
+    method public static void requestFontWithFallbackChain(android.content.Context, java.util.List<androidx.core.provider.FontRequest!>, int, java.util.concurrent.Executor?, java.util.concurrent.Executor, androidx.core.provider.FontsContractCompat.FontRequestCallback);
     method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void resetCache();
     field @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String PARCEL_FONT_RESULTS = "font_results";
   }
@@ -2446,6 +2511,7 @@
   public static class FontsContractCompat.FontFamilyResult {
     ctor @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public FontsContractCompat.FontFamilyResult(int, androidx.core.provider.FontsContractCompat.FontInfo![]?);
     method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+    method public java.util.List<androidx.core.provider.FontsContractCompat.FontInfo![]!> getFontsWithFallbacks();
     method public int getStatusCode();
     field public static final int STATUS_OK = 0; // 0x0
     field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
diff --git a/core/core/build.gradle b/core/core/build.gradle
index d4cac38..9c2d947 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -21,13 +21,14 @@
         implementation(project(":core:core-testing"))
     }
 
-    api("androidx.annotation:annotation:1.8.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.annotation:annotation-experimental:1.4.1")
     api("androidx.lifecycle:lifecycle-runtime:2.6.2")
     api("androidx.versionedparcelable:versionedparcelable:1.1.1")
     implementation("androidx.collection:collection:1.0.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.interpolator:interpolator:1.0.0")
+    implementation("androidx.tracing:tracing:1.2.0")
 
     api(libs.kotlinStdlib)
 
@@ -71,6 +72,7 @@
 }
 
 android {
+    compileSdk = 35
     buildFeatures {
         aidl = true
     }
@@ -104,6 +106,5 @@
     description = "Provides backward-compatible implementations of Android platform APIs and " +
             "features."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/core/lint-baseline.xml b/core/core/lint-baseline.xml
index d68cb9b1..6a88d3f 100644
--- a/core/core/lint-baseline.xml
+++ b/core/core/lint-baseline.xml
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Optional#of`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Optional#of`"
         errorLine1="                return Optional.of(modeCompat);"
         errorLine2="                                ~~">
         <location
@@ -12,7 +12,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Optional#empty`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Optional#empty`"
         errorLine1="        return Optional.empty();"
         errorLine2="                        ~~~~~">
         <location
@@ -21,7 +21,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Optional#get`"
         errorLine1="                findNativeMode(DisplayCompat.getSupportedModes(mContext, mDefaultDisplay)).get();"
         errorLine2="                                                                                           ~~~">
         <location
@@ -30,7 +30,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Optional#get`"
         errorLine1="                findNativeMode(DisplayCompat.getSupportedModes(mContext, secondDisplay)).get();"
         errorLine2="                                                                                         ~~~">
         <location
@@ -39,7 +39,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Optional#get`"
         errorLine1="                findNativeMode(DisplayCompat.getSupportedModes(mContext, mDefaultDisplay)).get();"
         errorLine2="                                                                                           ~~~">
         <location
@@ -48,7 +48,7 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 24 (current min is 21): `java.util.Optional#get`"
+        message="Call requires API level 24, or core library desugaring (current min is 21): `java.util.Optional#get`"
         errorLine1="                findNativeMode(DisplayCompat.getSupportedModes(mContext, mDefaultDisplay)).get();"
         errorLine2="                                                                                           ~~~">
         <location
@@ -776,1347 +776,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mInfo = new TouchDelegateInfo(targetMap);"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                return mInfo.getRegionCount();"
-        errorLine2="                             ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                return mInfo.getRegionAt(index);"
-        errorLine2="                             ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                AccessibilityNodeInfo info = mInfo.getTargetForRegion(region);"
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.isImportantForAccessibility();"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setImportantForAccessibility(important);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.getDrawingOrder();"
-        errorLine2="                         ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setDrawingOrder(drawingOrderInParent);"
-        errorLine2="                  ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.isContextClickable();"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setContextClickable(contextClickable);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.getHintText();"
-        errorLine2="                         ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setHintText(hintText);"
-        errorLine2="                  ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.getAvailableExtraData();"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setAvailableExtraData(extraDataKeys);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalBefore());"
-        errorLine2="                                                                         ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setTraversalBefore(view);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setTraversalBefore(root, virtualDescendantId);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalAfter());"
-        errorLine2="                                                                         ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setTraversalAfter(view);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setTraversalAfter(root, virtualDescendantId);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.getTooltipText();"
-        errorLine2="                         ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setTooltipText(tooltipText);"
-        errorLine2="                  ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setPaneTitle(paneTitle);"
-        errorLine2="                  ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.getPaneTitle();"
-        errorLine2="                         ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.isScreenReaderFocusable();"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setScreenReaderFocusable(screenReaderFocusable);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.isShowingHintText();"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setShowingHintText(showingHintText);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.isHeading();"
-        errorLine2="                         ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setHeading(isHeading);"
-        errorLine2="                  ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mInfo.isTextEntryKey();"
-        errorLine2="                         ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setTextEntryKey(isTextEntryKey);"
-        errorLine2="                  ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            TouchDelegateInfo delegateInfo = mInfo.getTouchDelegateInfo();"
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.view.accessibility.AccessibilityNodeInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.setTouchDelegateInfo(delegatedInfo.mInfo);"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 33; however, the containing class androidx.core.os.LocaleListCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return LocaleList.matchesLanguageAndScript(supported, desired);"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/os/LocaleListCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.app.NotificationManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    mContext.checkSelfPermission(Manifest.permission.USE_FULL_SCREEN_INTENT);"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/app/NotificationManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mWrapped = new PrecomputedText.Params.Builder(paint)"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .setBreakStrategy(strategy)"
-        errorLine2="                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .setHyphenationFrequency(frequency)"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .setTextDirection(textDir)"
-        errorLine2="                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .build();"
-        errorLine2="                         ~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mPaint = wrapped.getTextPaint();"
-        errorLine2="                             ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mTextDir = wrapped.getTextDirection();"
-        errorLine2="                               ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mBreakStrategy = wrapped.getBreakStrategy();"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mHyphenationFrequency = wrapped.getHyphenationFrequency();"
-        errorLine2="                                            ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (!mPaint.getTextLocales().equals(other.getTextPaint().getTextLocales())) {"
-        errorLine2="                            ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (!mPaint.getTextLocales().equals(other.getTextPaint().getTextLocales())) {"
-        errorLine2="                                                                         ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        mPaint.getTextLocales(), mPaint.getTypeface(), mPaint.isElegantTextHeight(),"
-        errorLine2="                               ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                sb.append(&quot;, textLocale=&quot; + mPaint.getTextLocales());"
-        errorLine2="                                                   ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.text.PrecomputedTextCompat.Params is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                sb.append(&quot;, variationSettings=&quot; + mPaint.getFontVariationSettings());"
-        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        PrecomputedText.create(text, params.mWrapped), params);"
-        errorLine2="                                        ~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                StaticLayout.Builder.obtain(text, 0, text.length(), params.getTextPaint(),"
-        errorLine2="                                     ~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .setBreakStrategy(params.getBreakStrategy())"
-        errorLine2="                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .setHyphenationFrequency(params.getHyphenationFrequency())"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .setTextDirection(params.getTextDirection())"
-        errorLine2="                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .build();"
-        errorLine2="                         ~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mWrapped.getParagraphCount();"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mWrapped.getParagraphStart(paraIndex);"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.text.PrecomputedTextCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return mWrapped.getParagraphEnd(paraIndex);"
-        errorLine2="                            ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mContext, mId)"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .setShortLabel(mLabel)"
-        errorLine2="                 ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .setIntents(mIntents);"
-        errorLine2="                 ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setIcon(mIcon.toIcon(mContext));"
-        errorLine2="                    ~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setLongLabel(mLongLabel);"
-        errorLine2="                    ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setDisabledMessage(mDisabledMessage);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setActivity(mActivity);"
-        errorLine2="                    ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setCategories(mCategories);"
-        errorLine2="                    ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        builder.setRank(mRank);"
-        errorLine2="                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setExtras(mExtras);"
-        errorLine2="                    ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                builder.setPersons(persons);"
-        errorLine2="                        ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                builder.setLocusId(mLocusId.toLocusId());"
-        errorLine2="                        ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setLongLived(mIsLongLived);"
-        errorLine2="                    ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            builder.setExtras(buildLegacyExtrasBundle());"
-        errorLine2="                    ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return builder.build();"
-        errorLine2="                       ~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mExtras.putBoolean(EXTRA_LONG_LIVED, mIsLongLived);"
-        errorLine2="                ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 22; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return bundle.getBoolean(EXTRA_LONG_LIVED);"
-        errorLine2="                      ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (shortcutInfo.getLocusId() == null) return null;"
-        errorLine2="                             ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return LocusIdCompat.toLocusIdCompat(shortcutInfo.getLocusId());"
-        errorLine2="                                                              ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return getLocusIdFromExtra(shortcutInfo.getExtras());"
-        errorLine2="                                                    ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mId = shortcutInfo.getId();"
-        errorLine2="                                     ~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mPackageName = shortcutInfo.getPackage();"
-        errorLine2="                                              ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            Intent[] intents = shortcutInfo.getIntents();"
-        errorLine2="                                            ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mActivity = shortcutInfo.getActivity();"
-        errorLine2="                                           ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mLabel = shortcutInfo.getShortLabel();"
-        errorLine2="                                        ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mLongLabel = shortcutInfo.getLongLabel();"
-        errorLine2="                                            ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mDisabledMessage = shortcutInfo.getDisabledMessage();"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mInfo.mDisabledReason = shortcutInfo.getDisabledReason();"
-        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mInfo.mDisabledReason = shortcutInfo.isEnabled()"
-        errorLine2="                                                     ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mCategories = shortcutInfo.getCategories();"
-        errorLine2="                                             ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mPersons = ShortcutInfoCompat.getPersonsFromExtra(shortcutInfo.getExtras());"
-        errorLine2="                                                                                 ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mUser = shortcutInfo.getUserHandle();"
-        errorLine2="                                       ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mLastChangedTimestamp = shortcutInfo.getLastChangedTimestamp();"
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mInfo.mIsCached = shortcutInfo.isCached();"
-        errorLine2="                                               ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mIsDynamic = shortcutInfo.isDynamic();"
-        errorLine2="                                            ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mIsPinned = shortcutInfo.isPinned();"
-        errorLine2="                                           ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mIsDeclaredInManifest = shortcutInfo.isDeclaredInManifest();"
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mIsImmutable = shortcutInfo.isImmutable();"
-        errorLine2="                                              ~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mIsEnabled = shortcutInfo.isEnabled();"
-        errorLine2="                                            ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mHasKeyFieldsOnly = shortcutInfo.hasKeyFieldsOnly();"
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mRank = shortcutInfo.getRank();"
-        errorLine2="                                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutInfoCompat.Builder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            mInfo.mExtras = shortcutInfo.getExtras();"
-        errorLine2="                                         ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported();"
-        errorLine2="                                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).requestPinShortcut("
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).requestPinShortcut("
-        errorLine2="                                                                   ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            result = context.getSystemService(ShortcutManager.class)"
-        errorLine2="                             ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .createShortcutResultIntent(shortcut.toShortcutInfo());"
-        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    context.getSystemService(ShortcutManager.class).getShortcuts(matchFlags);"
-        errorLine2="                            ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    context.getSystemService(ShortcutManager.class).getShortcuts(matchFlags);"
-        errorLine2="                                                                    ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            final ShortcutManager manager = context.getSystemService(ShortcutManager.class);"
-        errorLine2="                                                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                shortcuts.addAll(manager.getManifestShortcuts());"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                shortcuts.addAll(manager.getDynamicShortcuts());"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                shortcuts.addAll(manager.getPinnedShortcuts());"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (!context.getSystemService(ShortcutManager.class).addDynamicShortcuts(shortcuts)) {"
-        errorLine2="                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (!context.getSystemService(ShortcutManager.class).addDynamicShortcuts(shortcuts)) {"
-        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).getMaxShortcutCountPerActivity();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).getMaxShortcutCountPerActivity();"
-        errorLine2="                                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).isRateLimitingActive();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).isRateLimitingActive();"
-        errorLine2="                                                                   ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).getIconMaxWidth();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).getIconMaxWidth();"
-        errorLine2="                                                                   ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).getIconMaxHeight();"
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(ShortcutManager.class).getIconMaxHeight();"
-        errorLine2="                                                                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).reportShortcutUsed(shortcutId);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).reportShortcutUsed(shortcutId);"
-        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (!context.getSystemService(ShortcutManager.class).setDynamicShortcuts(shortcuts)) {"
-        errorLine2="                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (!context.getSystemService(ShortcutManager.class).setDynamicShortcuts(shortcuts)) {"
-        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            List&lt;ShortcutInfo> shortcuts = context.getSystemService("
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    ShortcutManager.class).getDynamicShortcuts();"
-        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (!context.getSystemService(ShortcutManager.class).updateShortcuts(shortcuts)) {"
-        errorLine2="                         ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (!context.getSystemService(ShortcutManager.class).updateShortcuts(shortcuts)) {"
-        errorLine2="                                                                 ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class)"
-        errorLine2="                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .disableShortcuts(shortcutIds, disabledMessage);"
-        errorLine2="                     ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).enableShortcuts(shortcutIds);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).enableShortcuts(shortcutIds);"
-        errorLine2="                                                            ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).removeDynamicShortcuts(shortcutIds);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).removeDynamicShortcuts(shortcutIds);"
-        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).removeAllDynamicShortcuts();"
-        errorLine2="                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).removeAllDynamicShortcuts();"
-        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        context.getSystemService(ShortcutManager.class).removeLongLivedShortcuts(shortcutIds);"
-        errorLine2="                ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        context.getSystemService(ShortcutManager.class).removeLongLivedShortcuts(shortcutIds);"
-        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).pushDynamicShortcut("
-        errorLine2="                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            context.getSystemService(ShortcutManager.class).pushDynamicShortcut("
-        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            final ShortcutManager sm = context.getSystemService(ShortcutManager.class);"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (sm.isRateLimitingActive()) {"
-        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            final List&lt;ShortcutInfo> shortcuts = sm.getDynamicShortcuts();"
-        errorLine2="                                                    ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                sm.removeDynamicShortcuts(Arrays.asList("
-        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 25; however, the containing class androidx.core.content.pm.ShortcutManagerCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            sm.addDynamicShortcuts(Arrays.asList(shortcut.toShortcutInfo()));"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.core.widget.TextViewCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            textView.setTextAppearance(resId);"
-        errorLine2="                     ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/core/widget/TextViewCompat.java"/>
-    </issue>
-
-    <issue
         id="PrivateConstructorForUtilityClass"
         message="Utility class is missing private constructor"
         errorLine1="public class NotificationCompat {"
diff --git a/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java b/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
index 7775b44..5f1ca3a 100644
--- a/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -2456,7 +2456,7 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 20)
+    @SdkSuppress(minSdkVersion = 20, maxSdkVersion = 32) // Failing on API 33 emulator, b/355507696
     public void testCallStyle_preservesCustomActions() {
         PendingIntent hangupIntent = createIntent("hangup");
         Person person = new Person.Builder().setName("test name").build();
diff --git a/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java b/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
index c557eaf..eb5eb6b 100644
--- a/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/graphics/TypefaceCompatTest.java
@@ -146,8 +146,8 @@
         final FontRequest parsedRequest = entry.getRequest();
         final FontRequest request = new FontRequest(parsedRequest.getProviderAuthority(),
                 parsedRequest.getProviderPackage(), parsedRequest.getQuery(), SIGNATURE);
-        return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout(),
-                entry.getSystemFontFamilyName());
+        return new ProviderResourceEntry(request, null /* fallbackRequest */,
+                entry.getFetchStrategy(), entry.getTimeout(), entry.getSystemFontFamilyName());
     }
 
     public static class FontCallback extends ResourcesCompat.FontCallback {
diff --git a/core/core/src/androidTest/java/androidx/core/location/LocationRequestCompatTestApi19.java b/core/core/src/androidTest/java/androidx/core/location/LocationRequestCompatTestApi19.java
index 7ca9524..93f7588 100644
--- a/core/core/src/androidTest/java/androidx/core/location/LocationRequestCompatTestApi19.java
+++ b/core/core/src/androidTest/java/androidx/core/location/LocationRequestCompatTestApi19.java
@@ -29,7 +29,6 @@
 import android.os.Build.VERSION;
 import android.os.SystemClock;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 import androidx.core.util.Preconditions;
 import androidx.test.filters.SdkSuppress;
@@ -171,7 +170,6 @@
         }
 
         @RequiresApi(Build.VERSION_CODES.S) // Work around a bug in NewApi check.
-        @DoNotInline
         private static long getExpireIn(LocationRequest request)
                 throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
             if (sGetExpireInMethod == null) {
diff --git a/core/core/src/androidTest/java/androidx/core/os/ProfilingTest.kt b/core/core/src/androidTest/java/androidx/core/os/ProfilingTest.kt
new file mode 100644
index 0000000..5df8859
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/os/ProfilingTest.kt
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.os
+
+import android.app.UiAutomation
+import android.os.CancellationSignal
+import android.os.Handler
+import android.os.Looper
+import android.os.ProfilingResult
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 35)
+class ProfilingTest {
+
+    private val mUiAutomation: UiAutomation =
+        InstrumentationRegistry.getInstrumentation().uiAutomation
+
+    @Before
+    fun setup() {
+        // Override the rate limiter
+        mUiAutomation.executeShellCommand(
+            "device_config put profiling_testing rate_limiter.disabled true"
+        )
+    }
+
+    /** Test requesting a java heap dump with all parameters set. */
+    @Test
+    fun testRequestJavaHeapDump() {
+        val listener = ResultListener()
+
+        requestJavaHeapDump()
+            .setBufferSizeKb(1000)
+            .request(
+                ApplicationProvider.getApplicationContext(),
+                Executors.newSingleThreadExecutor(),
+                listener
+            )
+
+        waitForSignal(listener.mAcceptedSignal)
+
+        assertNotNull(listener.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener.mResult!!.errorCode)
+    }
+
+    /** Test requesting a heap profile with all parameters set. */
+    @Test
+    fun testRequestHeapProfile() {
+        val listener = ResultListener()
+
+        requestHeapProfile()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5000)
+            .setTrackJavaAllocations(false)
+            .setSamplingIntervalBytes(100)
+            .request(
+                ApplicationProvider.getApplicationContext(),
+                Executors.newSingleThreadExecutor(),
+                listener
+            )
+
+        waitForSignal(listener.mAcceptedSignal)
+
+        assertNotNull(listener.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener.mResult!!.errorCode)
+    }
+
+    /** Test requesting stack sampling with all parameters set. */
+    @Test
+    fun testRequestStackSampling() {
+        val listener = ResultListener()
+
+        requestStackSampling()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5000)
+            .setSamplingFrequencyHz(100)
+            .request(
+                ApplicationProvider.getApplicationContext(),
+                Executors.newSingleThreadExecutor(),
+                listener
+            )
+
+        waitForSignal(listener.mAcceptedSignal)
+
+        assertNotNull(listener.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener.mResult!!.errorCode)
+    }
+
+    /** Test requesting a system trace with all parameters set. */
+    @Test
+    fun testRequestSystemTrace() {
+        val listener = ResultListener()
+
+        requestSystemTrace()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5000)
+            .setBufferFillPolicy(BufferFillPolicy.DISCARD)
+            .request(
+                ApplicationProvider.getApplicationContext(),
+                Executors.newSingleThreadExecutor(),
+                listener
+            )
+
+        waitForSignal(listener.mAcceptedSignal)
+
+        assertNotNull(listener.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener.mResult!!.errorCode)
+    }
+
+    /**
+     * Test requesting cancellation. Duration is set longer than test timeout to ensure the
+     * cancellation worked.
+     */
+    @Test
+    fun testCancellation() {
+        val cancellationSignal = CancellationSignal()
+        val listener = ResultListener()
+
+        requestSystemTrace()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5 * 60 * 1000)
+            .setBufferFillPolicy(BufferFillPolicy.RING_BUFFER)
+            .setCancellationSignal(cancellationSignal)
+            .request(
+                ApplicationProvider.getApplicationContext(),
+                Executors.newSingleThreadExecutor(),
+                listener
+            )
+
+        // Schedule cancellation to occur after some short wait
+        Handler(Looper.getMainLooper())
+            .postDelayed({ cancellationSignal.cancel() }, (5 * 1000).toLong())
+
+        waitForSignal(listener.mAcceptedSignal)
+
+        assertNotNull(listener.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener.mResult!!.errorCode)
+    }
+
+    /**
+     * Test that multiple global listeners are all triggered, and that the request executes
+     * correctly even though no listener is added directly to the request.
+     */
+    @Test
+    fun testGlobalListener() {
+        val listener1 = ResultListener()
+        val listener2 = ResultListener()
+
+        registerForAllProfilingResults(
+            ApplicationProvider.getApplicationContext(),
+            Executors.newSingleThreadExecutor(),
+            listener1
+        )
+        registerForAllProfilingResults(
+            ApplicationProvider.getApplicationContext(),
+            Executors.newSingleThreadExecutor(),
+            listener2
+        )
+
+        requestHeapProfile()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5000)
+            .setTrackJavaAllocations(true)
+            .setSamplingIntervalBytes(100)
+            .request(ApplicationProvider.getApplicationContext(), null, null)
+
+        waitForSignal(listener1.mAcceptedSignal)
+        waitForSignal(listener2.mAcceptedSignal)
+
+        assertNotNull(listener1.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener1.mResult!!.errorCode)
+        assertNotNull(listener2.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener2.mResult!!.errorCode)
+    }
+
+    /** Test that global listener flows are triggered with each result. */
+    @Test
+    fun testGlobalListenerFlow() {
+        var acceptedSignal = CountDownLatch(1)
+
+        val resultList = ArrayList<ProfilingResult>()
+
+        val flow = registerForAllProfilingResults(ApplicationProvider.getApplicationContext())
+        CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher()).launch {
+            flow.collect { result ->
+                resultList.add(result)
+                acceptedSignal.countDown()
+            }
+        }
+
+        // Wait for the other thread to actually register its listener
+        runBlocking { delay(1000) }
+
+        requestHeapProfile()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5000)
+            .setTrackJavaAllocations(true)
+            .setSamplingIntervalBytes(100)
+            .request(ApplicationProvider.getApplicationContext(), null, null)
+
+        waitForSignal(acceptedSignal)
+
+        // Reset the latch
+        acceptedSignal = CountDownLatch(1)
+
+        requestStackSampling()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5000)
+            .setSamplingFrequencyHz(100)
+            .request(ApplicationProvider.getApplicationContext(), null, null)
+
+        waitForSignal(acceptedSignal)
+
+        assertEquals(2, resultList.size)
+        assertEquals(ProfilingResult.ERROR_NONE, resultList[0].errorCode)
+        assertEquals(ProfilingResult.ERROR_NONE, resultList[1].errorCode)
+    }
+
+    /**
+     * Test unregistering a global listener by adding two listeners and then removing one. Ensure
+     * that the removed listener isn't triggered. Ensure that the remaining listener is triggered.
+     */
+    @Test
+    fun testUnregisterGlobalListener() {
+        val listener1 = ResultListener()
+        val listener2 = ResultListener()
+
+        registerForAllProfilingResults(
+            ApplicationProvider.getApplicationContext(),
+            Executors.newSingleThreadExecutor(),
+            listener1
+        )
+        registerForAllProfilingResults(
+            ApplicationProvider.getApplicationContext(),
+            Executors.newSingleThreadExecutor(),
+            listener2
+        )
+
+        requestHeapProfile()
+            .setBufferSizeKb(1000)
+            .setDurationMs(5000)
+            .setSamplingIntervalBytes(4096L)
+            .request(ApplicationProvider.getApplicationContext(), null, null)
+        unregisterForAllProfilingResults(ApplicationProvider.getApplicationContext(), listener2)
+
+        waitForSignal(listener1.mAcceptedSignal)
+
+        assertNotNull(listener1.mResult)
+        assertEquals(ProfilingResult.ERROR_NONE, listener1.mResult!!.errorCode)
+        assertNull(listener2.mResult)
+    }
+
+    /**
+     * Test unregistering a global listener flow by adding two listener flows and then cancelling
+     * one. Ensure that the removed flow doesn't collect a result. Ensure that the remaining flow
+     * collects a result.
+     */
+    @Test
+    fun testUnregisterGlobalListenerFlow() {
+        val acceptedSignal = CountDownLatch(1)
+
+        var profilingResultRegistered: ProfilingResult? = null
+
+        val flowRegistered =
+            registerForAllProfilingResults(ApplicationProvider.getApplicationContext())
+        val flowUnregistered =
+            registerForAllProfilingResults(ApplicationProvider.getApplicationContext())
+
+        CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher()).launch {
+            flowRegistered.collect { result ->
+                profilingResultRegistered = result
+                acceptedSignal.countDown()
+            }
+        }
+        val scopeToUnregister =
+            CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher()).launch {
+                flowUnregistered.collect { _ ->
+                    // This flow should have been unregistered and not collecting, fail the test.
+                    fail("Unregistered flow collected a result")
+                }
+            }
+
+        // Wait for the other thread to actually register its listener
+        runBlocking { delay(1000) }
+
+        requestHeapProfile()
+            .setBufferSizeKb(1000)
+            .setDurationMs(10 * 1000)
+            .setTrackJavaAllocations(true)
+            .setSamplingIntervalBytes(100)
+            .request(ApplicationProvider.getApplicationContext(), null, null)
+
+        // Schedule cancellation to occur after some short wait
+        Handler(Looper.getMainLooper()).postDelayed({ scopeToUnregister.cancel() }, 1000L)
+
+        waitForSignal(acceptedSignal)
+
+        // Wait an extra second to confirm the other callback flow wasn't triggered.
+        runBlocking { delay(1000) }
+
+        assertNotNull(profilingResultRegistered)
+        assertEquals(ProfilingResult.ERROR_NONE, profilingResultRegistered!!.errorCode)
+    }
+
+    /**
+     * Wait for completed countdown signal to be received for up to 1 minute. Fails the test if it
+     * times out waiting for the signal.
+     */
+    private fun waitForSignal(acceptedSignal: CountDownLatch) {
+        val succeeded = acceptedSignal.await(1, TimeUnit.MINUTES)
+        if (!succeeded) {
+            fail("Test timed out waiting for callback")
+        }
+    }
+
+    private class ResultListener : Consumer<ProfilingResult> {
+        val mAcceptedSignal: CountDownLatch = CountDownLatch(1)
+        var mResult: ProfilingResult? = null
+
+        override fun accept(profilingResult: ProfilingResult) {
+            mResult = profilingResult
+            mAcceptedSignal.countDown()
+        }
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/provider/FontsContractCompatTest.java b/core/core/src/androidTest/java/androidx/core/provider/FontsContractCompatTest.java
index 5112fed..3b65ec1 100644
--- a/core/core/src/androidTest/java/androidx/core/provider/FontsContractCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/provider/FontsContractCompatTest.java
@@ -108,6 +108,7 @@
     public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mContext = mInstrumentation.getTargetContext();
+        FontProvider.clearProviderCache();
         MockFontProvider.prepareFontFiles(
                 InstrumentationRegistry.getInstrumentation().getTargetContext());
     }
@@ -407,7 +408,7 @@
             @Override
             public void run() {
                 Handler handler = new Handler(Looper.getMainLooper());
-                FontsContractCompat.requestFont(mContext, request, Typeface.NORMAL,
+                FontsContractCompat.requestFont(mContext, List.of(request), Typeface.NORMAL,
                         false /* isBlockingFetch */, 300 /* timeout */, handler, callback);
             }
         });
diff --git a/core/core/src/androidTest/java/androidx/core/view/inputmethod/EditorInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/inputmethod/EditorInfoCompatTest.java
index d213e1f..70bf03c 100644
--- a/core/core/src/androidTest/java/androidx/core/view/inputmethod/EditorInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/inputmethod/EditorInfoCompatTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.os.Bundle;
 import android.os.Parcel;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.text.SpannableStringBuilder;
@@ -91,6 +92,8 @@
     @Test
     public void testSetStylusHandwritingEnabled() {
         EditorInfo editorInfo = new EditorInfo();
+        assertFalse(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
+
         EditorInfoCompat.setStylusHandwritingEnabled(editorInfo, true /* enabled */);
         assertTrue(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
 
@@ -99,6 +102,45 @@
     }
 
     @Test
+    public void testSetStylusHandwritingEnabled_compatWithCoreVersion1_13() {
+        EditorInfo editorInfo = new EditorInfo();
+        setStylusHandwritingEnabled_coreVersion1_13(editorInfo, false);
+        assertFalse(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
+
+        editorInfo = new EditorInfo();
+        setStylusHandwritingEnabled_coreVersion1_13(editorInfo, true);
+        assertTrue(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
+
+        editorInfo = new EditorInfo();
+        EditorInfoCompat.setStylusHandwritingEnabled(editorInfo, false);
+        assertFalse(isStylusHandwritingEnabled_coreVersion1_13(editorInfo));
+
+        editorInfo = new EditorInfo();
+        EditorInfoCompat.setStylusHandwritingEnabled(editorInfo, true);
+        assertTrue(isStylusHandwritingEnabled_coreVersion1_13(editorInfo));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 35)
+    public void testSetStylusHandwritingEnabled_compatWithAndroidV() {
+        EditorInfo editorInfo = new EditorInfo();
+        editorInfo.setStylusHandwritingEnabled(false);
+        assertFalse(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
+
+        editorInfo = new EditorInfo();
+        editorInfo.setStylusHandwritingEnabled(true);
+        assertTrue(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
+
+        editorInfo = new EditorInfo();
+        EditorInfoCompat.setStylusHandwritingEnabled(editorInfo, false);
+        assertFalse(editorInfo.isStylusHandwritingEnabled());
+
+        editorInfo = new EditorInfo();
+        EditorInfoCompat.setStylusHandwritingEnabled(editorInfo, true);
+        assertTrue(editorInfo.isStylusHandwritingEnabled());
+    }
+
+    @Test
     public void setInitialText_nullInputText_throwsException() {
         final CharSequence testText = null;
         final EditorInfo editorInfo = new EditorInfo();
@@ -319,4 +361,22 @@
         }
         return builder;
     }
+
+    /** This is the version in AndroidX Core library 1.13. */
+    private static void setStylusHandwritingEnabled_coreVersion1_13(
+            EditorInfo editorInfo, boolean enabled) {
+        if (editorInfo.extras == null) {
+            editorInfo.extras = new Bundle();
+        }
+        editorInfo.extras.putBoolean(EditorInfoCompat.STYLUS_HANDWRITING_ENABLED_KEY, enabled);
+    }
+
+    /** This is the version in AndroidX Core library 1.13. */
+    public static boolean isStylusHandwritingEnabled_coreVersion1_13(EditorInfo editorInfo) {
+        if (editorInfo.extras == null) {
+            // disabled by default
+            return false;
+        }
+        return editorInfo.extras.getBoolean(EditorInfoCompat.STYLUS_HANDWRITING_ENABLED_KEY);
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/app/ActivityCompat.java b/core/core/src/main/java/androidx/core/app/ActivityCompat.java
index d89856a..fd0a998 100644
--- a/core/core/src/main/java/androidx/core/app/ActivityCompat.java
+++ b/core/core/src/main/java/androidx/core/app/ActivityCompat.java
@@ -37,7 +37,6 @@
 import android.view.DragEvent;
 import android.view.View;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IdRes;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
@@ -766,13 +765,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setLocusContext(@NonNull final Activity activity,
                 @Nullable final LocusIdCompat locusId, @Nullable final Bundle bundle) {
             activity.setLocusContext(locusId == null ? null : locusId.toLocusId(), bundle);
         }
 
-        @DoNotInline
         static Display getDisplay(ContextWrapper contextWrapper) {
             return contextWrapper.getDisplay();
         }
@@ -784,7 +781,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isLaunchedFromBubble(@NonNull final Activity activity)  {
             return activity.isLaunchedFromBubble();
         }
@@ -796,7 +792,6 @@
          * </a>
          */
         @SuppressLint("BanUncheckedReflection")
-        @DoNotInline
         static boolean shouldShowRequestPermissionRationale(Activity activity, String permission) {
             try {
                 // 1. Background of the problem:Fix shouldShowRequestPermissionRationale causing memory leak in Android 12,
@@ -828,7 +823,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean shouldShowRequestPermissionRationale(Activity activity, String permission) {
             return activity.shouldShowRequestPermissionRationale(permission);
         }
@@ -840,29 +834,24 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void finishAfterTransition(Activity activity) {
             activity.finishAfterTransition();
         }
 
-        @DoNotInline
         static void setEnterSharedElementCallback(Activity activity,
                 android.app.SharedElementCallback callback) {
             activity.setEnterSharedElementCallback(callback);
         }
 
-        @DoNotInline
         static void setExitSharedElementCallback(Activity activity,
                 android.app.SharedElementCallback callback) {
             activity.setExitSharedElementCallback(callback);
         }
 
-        @DoNotInline
         static void postponeEnterTransition(Activity activity) {
             activity.postponeEnterTransition();
         }
 
-        @DoNotInline
         static void startPostponedEnterTransition(Activity activity) {
             activity.startPostponedEnterTransition();
         }
@@ -874,7 +863,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Uri getReferrer(Activity activity) {
             return activity.getReferrer();
         }
@@ -887,7 +875,6 @@
         }
 
         @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
-        @DoNotInline
         static <T> T requireViewById(Activity activity, int id) {
             return (T) activity.requireViewById(id);
         }
@@ -899,17 +886,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void requestPermissions(Activity activity, String[] permissions, int requestCode) {
             activity.requestPermissions(permissions, requestCode);
         }
 
-        @DoNotInline
         static boolean shouldShowRequestPermissionRationale(Activity activity, String permission) {
             return activity.shouldShowRequestPermissionRationale(permission);
         }
 
-        @DoNotInline
         static void onSharedElementsReady(Object onSharedElementsReadyListener) {
             ((android.app.SharedElementCallback.OnSharedElementsReadyListener)
                     onSharedElementsReadyListener).onSharedElementsReady();
diff --git a/core/core/src/main/java/androidx/core/app/ActivityOptionsCompat.java b/core/core/src/main/java/androidx/core/app/ActivityOptionsCompat.java
index 630ccda..adff7aa 100644
--- a/core/core/src/main/java/androidx/core/app/ActivityOptionsCompat.java
+++ b/core/core/src/main/java/androidx/core/app/ActivityOptionsCompat.java
@@ -27,7 +27,6 @@
 import android.os.Bundle;
 import android.view.View;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -459,18 +458,15 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ActivityOptions makeClipRevealAnimation(View source, int startX, int startY,
                 int width, int height) {
             return ActivityOptions.makeClipRevealAnimation(source, startX, startY, width, height);
         }
 
-        @DoNotInline
         static ActivityOptions makeBasic() {
             return ActivityOptions.makeBasic();
         }
 
-        @DoNotInline
         static void requestUsageTimeReport(ActivityOptions activityOptions,
                 PendingIntent receiver) {
             activityOptions.requestUsageTimeReport(receiver);
@@ -483,7 +479,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ActivityOptions makeSceneTransitionAnimation(Activity activity, View sharedElement,
                 String sharedElementName) {
             return ActivityOptions.makeSceneTransitionAnimation(activity, sharedElement,
@@ -491,13 +486,11 @@
         }
 
         @SafeVarargs
-        @DoNotInline
         static ActivityOptions makeSceneTransitionAnimation(Activity activity,
                 android.util.Pair<View, String>... sharedElements) {
             return ActivityOptions.makeSceneTransitionAnimation(activity, sharedElements);
         }
 
-        @DoNotInline
         static ActivityOptions makeTaskLaunchBehind() {
             return ActivityOptions.makeTaskLaunchBehind();
         }
@@ -509,13 +502,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ActivityOptions setLaunchBounds(ActivityOptions activityOptions,
                 Rect screenSpacePixelRect) {
             return activityOptions.setLaunchBounds(screenSpacePixelRect);
         }
 
-        @DoNotInline
         static Rect getLaunchBounds(ActivityOptions activityOptions) {
             return activityOptions.getLaunchBounds();
         }
@@ -528,7 +519,6 @@
         }
 
         @SuppressWarnings("deprecation")
-        @DoNotInline
         static void setPendingIntentBackgroundActivityLaunchAllowed(ActivityOptions activityOptions,
                 boolean allowed) {
             activityOptions.setPendingIntentBackgroundActivityLaunchAllowed(allowed);
@@ -541,13 +531,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ActivityOptions setShareIdentityEnabled(ActivityOptions activityOptions,
                 boolean shareIdentity) {
             return activityOptions.setShareIdentityEnabled(shareIdentity);
         }
 
-        @DoNotInline
         static ActivityOptions setPendingIntentBackgroundActivityStartMode(
                 ActivityOptions activityOptions, int state) {
             return activityOptions.setPendingIntentBackgroundActivityStartMode(state);
diff --git a/core/core/src/main/java/androidx/core/app/AlarmManagerCompat.java b/core/core/src/main/java/androidx/core/app/AlarmManagerCompat.java
index 2242966..c841e77 100644
--- a/core/core/src/main/java/androidx/core/app/AlarmManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/AlarmManagerCompat.java
@@ -24,7 +24,6 @@
 import android.app.PendingIntent;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -271,13 +270,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setAlarmClock(AlarmManager alarmManager, Object info,
                 PendingIntent operation) {
             alarmManager.setAlarmClock((AlarmManager.AlarmClockInfo) info, operation);
         }
 
-        @DoNotInline
         static AlarmManager.AlarmClockInfo createAlarmClockInfo(long triggerTime,
                 PendingIntent showIntent) {
             return new AlarmManager.AlarmClockInfo(triggerTime, showIntent);
@@ -290,13 +287,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setAndAllowWhileIdle(AlarmManager alarmManager, int type, long triggerAtMillis,
                 PendingIntent operation) {
             alarmManager.setAndAllowWhileIdle(type, triggerAtMillis, operation);
         }
 
-        @DoNotInline
         static void setExactAndAllowWhileIdle(AlarmManager alarmManager, int type,
                 long triggerAtMillis, PendingIntent operation) {
             alarmManager.setExactAndAllowWhileIdle(type, triggerAtMillis, operation);
@@ -309,7 +304,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean canScheduleExactAlarms(AlarmManager alarmManager) {
             return alarmManager.canScheduleExactAlarms();
         }
diff --git a/core/core/src/main/java/androidx/core/app/AppOpsManagerCompat.java b/core/core/src/main/java/androidx/core/app/AppOpsManagerCompat.java
index 52e248a..9f18fd6 100644
--- a/core/core/src/main/java/androidx/core/app/AppOpsManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/AppOpsManagerCompat.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.os.Binder;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -220,7 +219,6 @@
         /**
          * Return the AppOpsManager system service.
          */
-        @DoNotInline
         static @Nullable AppOpsManager getSystemService(@NonNull Context context) {
             return context.getSystemService(AppOpsManager.class);
         }
@@ -228,7 +226,6 @@
         /**
          * Use the AppOpsManager to perform checkOp().
          */
-        @DoNotInline
         static int checkOpNoThrow(@Nullable AppOpsManager appOpsManager,
                 @NonNull String op, int uid, @NonNull String packageName) {
             if (appOpsManager == null) {
@@ -241,7 +238,6 @@
         /**
          * Return the packageName from the context.
          */
-        @DoNotInline
         static @NonNull String getOpPackageName(@NonNull Context context) {
             return context.getOpPackageName();
         }
@@ -253,22 +249,18 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static String permissionToOp(String permission) {
             return AppOpsManager.permissionToOp(permission);
         }
 
-        @DoNotInline
         static <T> T getSystemService(Context context, Class<T> serviceClass) {
             return context.getSystemService(serviceClass);
         }
 
-        @DoNotInline
         static int noteProxyOp(AppOpsManager appOpsManager, String op, String proxiedPackageName) {
             return appOpsManager.noteProxyOp(op, proxiedPackageName);
         }
 
-        @DoNotInline
         static int noteProxyOpNoThrow(AppOpsManager appOpsManager, String op,
                 String proxiedPackageName) {
             return appOpsManager.noteProxyOpNoThrow(op, proxiedPackageName);
diff --git a/core/core/src/main/java/androidx/core/app/DialogCompat.java b/core/core/src/main/java/androidx/core/app/DialogCompat.java
index eda5c6c..0d195ae 100644
--- a/core/core/src/main/java/androidx/core/app/DialogCompat.java
+++ b/core/core/src/main/java/androidx/core/app/DialogCompat.java
@@ -20,7 +20,6 @@
 import android.os.Build;
 import android.view.View;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -73,7 +72,6 @@
         }
 
         @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
-        @DoNotInline
         static <T> T requireViewById(Dialog dialog, int id) {
             return (T) dialog.requireViewById(id);
         }
diff --git a/core/core/src/main/java/androidx/core/app/GrammaticalInflectionManagerCompat.java b/core/core/src/main/java/androidx/core/app/GrammaticalInflectionManagerCompat.java
index 07e24f0..a95e565 100644
--- a/core/core/src/main/java/androidx/core/app/GrammaticalInflectionManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/GrammaticalInflectionManagerCompat.java
@@ -21,7 +21,6 @@
 import android.os.Build;
 
 import androidx.annotation.AnyThread;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.OptIn;
@@ -117,12 +116,10 @@
     static class Api34Impl {
         private Api34Impl() {}
 
-        @DoNotInline
         static int getApplicationGrammaticalGender(Context context) {
             return getGrammaticalInflectionManager(context).getApplicationGrammaticalGender();
         }
 
-        @DoNotInline
         static void setRequestedApplicationGrammaticalGender(
                 Context context, int grammaticalGender) {
             getGrammaticalInflectionManager(context)
diff --git a/core/core/src/main/java/androidx/core/app/LocaleManagerCompat.java b/core/core/src/main/java/androidx/core/app/LocaleManagerCompat.java
index 7fe6b57..91ac468 100644
--- a/core/core/src/main/java/androidx/core/app/LocaleManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/LocaleManagerCompat.java
@@ -24,7 +24,6 @@
 import android.os.LocaleList;
 
 import androidx.annotation.AnyThread;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
@@ -116,7 +115,6 @@
     static class Api21Impl {
         private Api21Impl() {}
 
-        @DoNotInline
         static String toLanguageTag(Locale locale) {
             return locale.toLanguageTag();
         }
@@ -126,7 +124,6 @@
     static class Api24Impl {
         private Api24Impl() {}
 
-        @DoNotInline
         static LocaleListCompat getLocales(Configuration configuration) {
             return LocaleListCompat.forLanguageTags(configuration.getLocales().toLanguageTags());
         }
@@ -136,13 +133,11 @@
     static class Api33Impl {
         private Api33Impl() {}
 
-        @DoNotInline
         static LocaleList localeManagerGetSystemLocales(Object localeManager) {
             LocaleManager mLocaleManager = (LocaleManager) localeManager;
             return mLocaleManager.getSystemLocales();
         }
 
-        @DoNotInline
         static LocaleList localeManagerGetApplicationLocales(Object localeManager) {
             LocaleManager mLocaleManager = (LocaleManager) localeManager;
             return mLocaleManager.getApplicationLocales();
diff --git a/core/core/src/main/java/androidx/core/app/NotificationChannelCompat.java b/core/core/src/main/java/androidx/core/app/NotificationChannelCompat.java
index 6935ca6..af5365c 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationChannelCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationChannelCompat.java
@@ -25,7 +25,6 @@
 import android.os.Build;
 import android.provider.Settings;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -533,120 +532,97 @@
     static class Api26Impl {
         private Api26Impl() { }
 
-        @DoNotInline
         static NotificationChannel createNotificationChannel(String id, CharSequence name,
                 int importance) {
             return new NotificationChannel(id, name, importance);
         }
 
-        @DoNotInline
         static String getId(NotificationChannel notificationChannel) {
             return notificationChannel.getId();
         }
 
-        @DoNotInline
         static int getImportance(NotificationChannel notificationChannel) {
             return notificationChannel.getImportance();
         }
 
-        @DoNotInline
         static CharSequence getName(NotificationChannel notificationChannel) {
             return notificationChannel.getName();
         }
 
-        @DoNotInline
         static String getDescription(NotificationChannel notificationChannel) {
             return notificationChannel.getDescription();
         }
 
-        @DoNotInline
         static void setDescription(NotificationChannel notificationChannel, String description) {
             notificationChannel.setDescription(description);
         }
 
-        @DoNotInline
         static String getGroup(NotificationChannel notificationChannel) {
             return notificationChannel.getGroup();
         }
 
-        @DoNotInline
         static void setGroup(NotificationChannel notificationChannel, String groupId) {
             notificationChannel.setGroup(groupId);
         }
 
-        @DoNotInline
         static boolean canShowBadge(NotificationChannel notificationChannel) {
             return notificationChannel.canShowBadge();
         }
 
-        @DoNotInline
         static void setShowBadge(NotificationChannel notificationChannel, boolean showBadge) {
             notificationChannel.setShowBadge(showBadge);
         }
 
-        @DoNotInline
         static Uri getSound(NotificationChannel notificationChannel) {
             return notificationChannel.getSound();
         }
 
-        @DoNotInline
         static void setSound(NotificationChannel notificationChannel, Uri sound,
                 AudioAttributes audioAttributes) {
             notificationChannel.setSound(sound, audioAttributes);
         }
 
-        @DoNotInline
         static AudioAttributes getAudioAttributes(NotificationChannel notificationChannel) {
             return notificationChannel.getAudioAttributes();
         }
 
-        @DoNotInline
         static boolean shouldShowLights(NotificationChannel notificationChannel) {
             return notificationChannel.shouldShowLights();
         }
 
-        @DoNotInline
         static void enableLights(NotificationChannel notificationChannel, boolean lights) {
             notificationChannel.enableLights(lights);
         }
 
-        @DoNotInline
         static int getLightColor(NotificationChannel notificationChannel) {
             return notificationChannel.getLightColor();
         }
 
-        @DoNotInline
         static void setLightColor(NotificationChannel notificationChannel, int argb) {
             notificationChannel.setLightColor(argb);
         }
 
-        @DoNotInline
         static boolean shouldVibrate(NotificationChannel notificationChannel) {
             return notificationChannel.shouldVibrate();
         }
 
-        @DoNotInline
         static void enableVibration(NotificationChannel notificationChannel, boolean vibration) {
             notificationChannel.enableVibration(vibration);
         }
 
-        @DoNotInline
         static long[] getVibrationPattern(NotificationChannel notificationChannel) {
             return notificationChannel.getVibrationPattern();
         }
 
-        @DoNotInline
         static void setVibrationPattern(NotificationChannel notificationChannel,
                 long[] vibrationPattern) {
             notificationChannel.setVibrationPattern(vibrationPattern);
         }
 
-        @DoNotInline
         static boolean canBypassDnd(NotificationChannel notificationChannel) {
             return notificationChannel.canBypassDnd();
         }
 
-        @DoNotInline
         static int getLockscreenVisibility(NotificationChannel notificationChannel) {
             return notificationChannel.getLockscreenVisibility();
         }
@@ -662,7 +638,6 @@
     static class Api29Impl {
         private Api29Impl() { }
 
-        @DoNotInline
         static boolean canBubble(NotificationChannel notificationChannel) {
             return notificationChannel.canBubble();
         }
@@ -677,23 +652,19 @@
     static class Api30Impl {
         private Api30Impl() { }
 
-        @DoNotInline
         static String getParentChannelId(NotificationChannel notificationChannel) {
             return notificationChannel.getParentChannelId();
         }
 
-        @DoNotInline
         static String getConversationId(NotificationChannel notificationChannel) {
             return notificationChannel.getConversationId();
         }
 
-        @DoNotInline
         static void setConversationId(NotificationChannel notificationChannel,
                 String parentChannelId, String conversationId) {
             notificationChannel.setConversationId(parentChannelId, conversationId);
         }
 
-        @DoNotInline
         static boolean isImportantConversation(NotificationChannel notificationChannel) {
             return notificationChannel.isImportantConversation();
         }
diff --git a/core/core/src/main/java/androidx/core/app/NotificationChannelGroupCompat.java b/core/core/src/main/java/androidx/core/app/NotificationChannelGroupCompat.java
index 1776992..ea2ca37 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationChannelGroupCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationChannelGroupCompat.java
@@ -21,7 +21,6 @@
 import android.content.Intent;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -226,29 +225,24 @@
     static class Api26Impl {
         private Api26Impl() { }
 
-        @DoNotInline
         static NotificationChannelGroup createNotificationChannelGroup(String id,
                 CharSequence name) {
             return new NotificationChannelGroup(id, name);
         }
 
-        @DoNotInline
         static String getId(NotificationChannelGroup notificationChannelGroup) {
             return notificationChannelGroup.getId();
         }
 
-        @DoNotInline
         static CharSequence getName(NotificationChannelGroup notificationChannelGroup) {
             return notificationChannelGroup.getName();
         }
 
-        @DoNotInline
         static List<NotificationChannel> getChannels(
                 NotificationChannelGroup notificationChannelGroup) {
             return notificationChannelGroup.getChannels();
         }
 
-        @DoNotInline
         static String getGroup(NotificationChannel notificationChannel) {
             return notificationChannel.getGroup();
         }
@@ -263,17 +257,14 @@
     static class Api28Impl {
         private Api28Impl() { }
 
-        @DoNotInline
         static boolean isBlocked(NotificationChannelGroup notificationChannelGroup) {
             return notificationChannelGroup.isBlocked();
         }
 
-        @DoNotInline
         static String getDescription(NotificationChannelGroup notificationChannelGroup) {
             return notificationChannelGroup.getDescription();
         }
 
-        @DoNotInline
         static void setDescription(NotificationChannelGroup notificationChannelGroup,
                 String description) {
             notificationChannelGroup.setDescription(description);
diff --git a/core/core/src/main/java/androidx/core/app/NotificationCompat.java b/core/core/src/main/java/androidx/core/app/NotificationCompat.java
index 2b6ee42..d1317ab 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompat.java
@@ -63,7 +63,6 @@
 import androidx.annotation.ColorInt;
 import androidx.annotation.DimenRes;
 import androidx.annotation.Dimension;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -2617,29 +2616,24 @@
         static class Api21Impl {
             private Api21Impl() { }
 
-            @DoNotInline
             static AudioAttributes.Builder createBuilder() {
                 return new AudioAttributes.Builder();
             }
 
-            @DoNotInline
             static AudioAttributes.Builder setContentType(AudioAttributes.Builder builder,
                     int contentType) {
                 return builder.setContentType(contentType);
             }
 
-            @DoNotInline
             static AudioAttributes.Builder setUsage(AudioAttributes.Builder builder, int usage) {
                 return builder.setUsage(usage);
             }
 
-            @DoNotInline
             static AudioAttributes.Builder setLegacyStreamType(AudioAttributes.Builder builder,
                     int streamType) {
                 return builder.setLegacyStreamType(streamType);
             }
 
-            @DoNotInline
             static AudioAttributes build(AudioAttributes.Builder builder) {
                 return builder.build();
             }
@@ -2654,12 +2648,10 @@
         static class Api23Impl {
             private Api23Impl() { }
 
-            @DoNotInline
             static Icon getSmallIcon(Notification notification) {
                 return notification.getSmallIcon();
             }
 
-            @DoNotInline
             static Icon getLargeIcon(Notification notification) {
                 return notification.getLargeIcon();
             }
@@ -2674,22 +2666,18 @@
         static class Api24Impl {
             private Api24Impl() { }
 
-            @DoNotInline
             static Notification.Builder recoverBuilder(Context context, Notification n) {
                 return Notification.Builder.recoverBuilder(context, n);
             }
 
-            @DoNotInline
             static RemoteViews createContentView(Notification.Builder builder) {
                 return builder.createContentView();
             }
 
-            @DoNotInline
             static RemoteViews createHeadsUpContentView(Notification.Builder builder) {
                 return builder.createHeadsUpContentView();
             }
 
-            @DoNotInline
             static RemoteViews createBigContentView(Notification.Builder builder) {
                 return builder.createHeadsUpContentView();
             }
@@ -3181,7 +3169,6 @@
         static class Api24Impl {
             private Api24Impl() { }
 
-            @DoNotInline
             static void setChronometerCountDown(RemoteViews remoteViews, int viewId,
                     boolean isCountDown) {
                 remoteViews.setChronometerCountDown(viewId, isCountDown);
@@ -4398,13 +4385,11 @@
                     // This class is not instantiable.
                 }
 
-                @DoNotInline
                 static Notification.MessagingStyle.Message createMessage(CharSequence text,
                         long timestamp, CharSequence sender) {
                     return new Notification.MessagingStyle.Message(text, timestamp, sender);
                 }
 
-                @DoNotInline
                 static Notification.MessagingStyle.Message setData(
                         Notification.MessagingStyle.Message message, String dataMimeType,
                         Uri dataUri) {
@@ -4423,13 +4408,11 @@
                     // This class is not instantiable.
                 }
 
-                @DoNotInline
                 static Notification.MessagingStyle.Message createMessage(CharSequence text,
                         long timestamp, android.app.Person sender) {
                     return new Notification.MessagingStyle.Message(text, timestamp, sender);
                 }
 
-                @DoNotInline
                 static Parcelable castToParcelable(android.app.Person person) {
                     return person;
                 }
@@ -4445,19 +4428,16 @@
         static class Api24Impl {
             private Api24Impl() { }
 
-            @DoNotInline
             static Notification.MessagingStyle createMessagingStyle(CharSequence userDisplayName) {
                 return new Notification.MessagingStyle(userDisplayName);
             }
 
-            @DoNotInline
             static Notification.MessagingStyle addMessage(
                     Notification.MessagingStyle messagingStyle,
                     Notification.MessagingStyle.Message message) {
                 return messagingStyle.addMessage(message);
             }
 
-            @DoNotInline
             static Notification.MessagingStyle setConversationTitle(
                     Notification.MessagingStyle messagingStyle, CharSequence conversationTitle) {
                 return messagingStyle.setConversationTitle(conversationTitle);
@@ -4473,7 +4453,6 @@
         static class Api26Impl {
             private Api26Impl() { }
 
-            @DoNotInline
             static Notification.MessagingStyle addHistoricMessage(
                     Notification.MessagingStyle messagingStyle,
                     Notification.MessagingStyle.Message message) {
@@ -4491,12 +4470,10 @@
         static class Api28Impl {
             private Api28Impl() { }
 
-            @DoNotInline
             static Notification.MessagingStyle createMessagingStyle(android.app.Person user) {
                 return new Notification.MessagingStyle(user);
             }
 
-            @DoNotInline
             static Notification.MessagingStyle setGroupConversation(
                     Notification.MessagingStyle messagingStyle, boolean isGroupConversation) {
                 return messagingStyle.setGroupConversation(isGroupConversation);
@@ -5082,12 +5059,10 @@
             private Api20Impl() {
             }
 
-            @DoNotInline
             static Notification.Action build(Notification.Action.Builder builder) {
                 return builder.build();
             }
 
-            @DoNotInline
             static Notification.Action.Builder createActionBuilder(int icon,
                     CharSequence title,
                     android.app.PendingIntent intent) {
@@ -5095,13 +5070,11 @@
 
             }
 
-            @DoNotInline
             static Notification.Action.Builder addExtras(Notification.Action.Builder builder,
                     android.os.Bundle extras) {
                 return builder.addExtras(extras);
             }
 
-            @DoNotInline
             static Notification.Action.Builder addRemoteInput(Notification.Action.Builder builder,
                     android.app.RemoteInput remoteInput) {
                 return builder.addRemoteInput(remoteInput);
@@ -5118,12 +5091,10 @@
             private Api21Impl() {
             }
 
-            @DoNotInline
             static Notification.Builder addPerson(Notification.Builder builder, String uri) {
                 return builder.addPerson(uri);
             }
 
-            @DoNotInline
             static Notification.Builder setCategory(Notification.Builder builder, String category) {
                 return builder.setCategory(category);
             }
@@ -5139,13 +5110,11 @@
             private Api23Impl() {
             }
 
-            @DoNotInline
             static void setLargeIcon(Notification.Builder builder,
                     Icon icon) {
                 builder.setLargeIcon(icon);
             }
 
-            @DoNotInline
             static Notification.Action.Builder createActionBuilder(
                     Icon icon,
                     CharSequence title,
@@ -5153,7 +5122,6 @@
                 return new Notification.Action.Builder(icon, title, intent);
             }
 
-            @DoNotInline
             static Parcelable castToParcelable(Icon icon) {
                 return icon;
             }
@@ -5169,7 +5137,6 @@
             private Api24Impl() {
             }
 
-            @DoNotInline
             static Notification.Action.Builder setAllowGeneratedReplies(
                     Notification.Action.Builder builder, boolean allowGeneratedReplies) {
                 return builder.setAllowGeneratedReplies(allowGeneratedReplies);
@@ -5186,13 +5153,11 @@
             private Api28Impl() {
             }
 
-            @DoNotInline
             static Notification.Builder addPerson(Notification.Builder builder,
                     android.app.Person person) {
                 return builder.addPerson(person);
             }
 
-            @DoNotInline
             static Parcelable castToParcelable(android.app.Person person) {
                 return person;
             }
@@ -5208,55 +5173,46 @@
             private Api31Impl() {
             }
 
-            @DoNotInline
             static Notification.CallStyle forIncomingCall(@NonNull android.app.Person person,
                     @NonNull PendingIntent declineIntent, @NonNull PendingIntent answerIntent) {
                 return Notification.CallStyle.forIncomingCall(person, declineIntent, answerIntent);
             }
 
-            @DoNotInline
             static Notification.CallStyle forOngoingCall(@NonNull android.app.Person person,
                     @NonNull PendingIntent hangUpIntent) {
                 return Notification.CallStyle.forOngoingCall(person, hangUpIntent);
             }
 
-            @DoNotInline
             static Notification.CallStyle forScreeningCall(@NonNull android.app.Person person,
                     @NonNull PendingIntent hangUpIntent, @NonNull PendingIntent answerIntent) {
                 return Notification.CallStyle.forScreeningCall(person, hangUpIntent, answerIntent);
             }
 
-            @DoNotInline
             static Notification.CallStyle setIsVideo(Notification.CallStyle callStyle,
                     boolean isVideo) {
                 return callStyle.setIsVideo(isVideo);
             }
 
-            @DoNotInline
             static Notification.CallStyle setVerificationIcon(Notification.CallStyle callStyle,
                     @Nullable Icon verificationIcon) {
                 return callStyle.setVerificationIcon(verificationIcon);
             }
 
-            @DoNotInline
             static Notification.CallStyle setVerificationText(Notification.CallStyle callStyle,
                     @Nullable CharSequence verificationText) {
                 return callStyle.setVerificationText(verificationText);
             }
 
-            @DoNotInline
             static Notification.CallStyle setAnswerButtonColorHint(Notification.CallStyle callStyle,
                     @ColorInt int color) {
                 return callStyle.setAnswerButtonColorHint(color);
             }
 
-            @DoNotInline
             static Notification.CallStyle setDeclineButtonColorHint(
                     Notification.CallStyle callStyle, @ColorInt int color) {
                 return callStyle.setDeclineButtonColorHint(color);
             }
 
-            @DoNotInline
             static Notification.Action.Builder setAuthenticationRequired(
                     Notification.Action.Builder actionBuilder, boolean authenticationRequired) {
                 return actionBuilder.setAuthenticationRequired(authenticationRequired);
@@ -5643,7 +5599,6 @@
         static class Api24Impl {
             private Api24Impl() { }
 
-            @DoNotInline
             static Notification.Style createDecoratedCustomViewStyle() {
                 return new Notification.DecoratedCustomViewStyle();
             }
@@ -6213,12 +6168,10 @@
             static class Api20Impl {
                 private Api20Impl() { }
 
-                @DoNotInline
                 static android.app.RemoteInput[] getRemoteInputs(Notification.Action action) {
                     return action.getRemoteInputs();
                 }
 
-                @DoNotInline
                 static Bundle getExtras(Notification.Action action) {
                     return action.getExtras();
                 }
@@ -6233,7 +6186,6 @@
             static class Api23Impl {
                 private Api23Impl() { }
 
-                @DoNotInline
                 static Icon getIcon(Notification.Action action) {
                     return action.getIcon();
                 }
@@ -6248,7 +6200,6 @@
             static class Api24Impl {
                 private Api24Impl() { }
 
-                @DoNotInline
                 static boolean getAllowGeneratedReplies(Notification.Action action) {
                     return action.getAllowGeneratedReplies();
                 }
@@ -6263,7 +6214,6 @@
             static class Api28Impl {
                 private Api28Impl() { }
 
-                @DoNotInline
                 static int getSemanticAction(Notification.Action action) {
                     return action.getSemanticAction();
                 }
@@ -6278,7 +6228,6 @@
             static class Api29Impl {
                 private Api29Impl() { }
 
-                @DoNotInline
                 static boolean isContextual(Notification.Action action) {
                     return action.isContextual();
                 }
@@ -6293,7 +6242,6 @@
             static class Api31Impl {
                 private Api31Impl() { }
 
-                @DoNotInline
                 static boolean isAuthenticationRequired(Notification.Action action) {
                     return action.isAuthenticationRequired();
                 }
@@ -7592,30 +7540,25 @@
         static class Api20Impl {
             private Api20Impl() { }
 
-            @DoNotInline
             static Notification.Action.Builder createBuilder(int icon, CharSequence title,
                     PendingIntent intent) {
                 return new Notification.Action.Builder(icon, title, intent);
             }
 
-            @DoNotInline
             static Notification.Action.Builder addExtras(Notification.Action.Builder builder,
                     Bundle extras) {
                 return builder.addExtras(extras);
             }
 
-            @DoNotInline
             static Notification.Action.Builder addRemoteInput(Notification.Action.Builder builder,
                     android.app.RemoteInput remoteInput) {
                 return builder.addRemoteInput(remoteInput);
             }
 
-            @DoNotInline
             static Notification.Action build(Notification.Action.Builder builder) {
                 return builder.build();
             }
 
-            @DoNotInline
             public static Action getActionCompatFromAction(ArrayList<Parcelable> parcelables,
                     int i) {
                 // Cast to Notification.Action (added in API 19) must happen in static inner class.
@@ -7633,7 +7576,6 @@
         static class Api23Impl {
             private Api23Impl() { }
 
-            @DoNotInline
             static Notification.Action.Builder createBuilder(Icon icon, CharSequence title,
                     PendingIntent intent) {
                 return new Notification.Action.Builder(icon, title, intent);
@@ -7649,7 +7591,6 @@
         static class Api24Impl {
             private Api24Impl() { }
 
-            @DoNotInline
             static Notification.Action.Builder setAllowGeneratedReplies(
                     Notification.Action.Builder builder, boolean allowGeneratedReplies) {
                 return builder.setAllowGeneratedReplies(allowGeneratedReplies);
@@ -7665,7 +7606,6 @@
         static class Api31Impl {
             private Api31Impl() { }
 
-            @DoNotInline
             static Notification.Action.Builder setAuthenticationRequired(
                     Notification.Action.Builder builder, boolean authenticationRequired) {
                 return builder.setAuthenticationRequired(authenticationRequired);
@@ -8133,66 +8073,54 @@
                 // This class is not instantiable.
             }
 
-            @DoNotInline
             static android.app.RemoteInput.Builder createBuilder(String resultKey) {
                 return new android.app.RemoteInput.Builder(resultKey);
             }
 
-            @DoNotInline
             static android.app.RemoteInput build(android.app.RemoteInput.Builder builder) {
                 return builder.build();
             }
 
-            @DoNotInline
             static String getResultKey(android.app.RemoteInput remoteInput) {
                 return remoteInput.getResultKey();
             }
 
-            @DoNotInline
             static CharSequence[] getChoices(android.app.RemoteInput remoteInput) {
                 return remoteInput.getChoices();
             }
 
-            @DoNotInline
             static android.app.RemoteInput.Builder setChoices(
                     android.app.RemoteInput.Builder builder, CharSequence[] choices) {
                 return builder.setChoices(choices);
             }
 
-            @DoNotInline
             static CharSequence getLabel(android.app.RemoteInput remoteInput) {
                 return remoteInput.getLabel();
             }
 
-            @DoNotInline
             static android.app.RemoteInput.Builder setLabel(android.app.RemoteInput.Builder builder,
                     CharSequence label) {
                 return builder.setLabel(label);
             }
 
-            @DoNotInline
             static boolean getAllowFreeFormInput(android.app.RemoteInput remoteInput) {
                 return remoteInput.getAllowFreeFormInput();
             }
 
-            @DoNotInline
             static android.app.RemoteInput.Builder setAllowFreeFormInput(
                     android.app.RemoteInput.Builder builder, boolean allowFreeFormInput) {
                 return builder.setAllowFreeFormInput(allowFreeFormInput);
             }
 
-            @DoNotInline
             static Bundle getExtras(android.app.RemoteInput remoteInput) {
                 return remoteInput.getExtras();
             }
 
-            @DoNotInline
             static android.app.RemoteInput.Builder addExtras(
                     android.app.RemoteInput.Builder builder, Bundle extras) {
                 return builder.addExtras(extras);
             }
 
-            @DoNotInline
             static Parcelable castToParcelable(android.app.RemoteInput remoteInput) {
                 return remoteInput;
             }
@@ -8207,7 +8135,6 @@
         static class Api29Impl {
             private Api29Impl() { }
 
-            @DoNotInline
             static int getEditChoicesBeforeSending(android.app.RemoteInput remoteInput) {
                 return remoteInput.getEditChoicesBeforeSending();
             }
@@ -9514,47 +9441,38 @@
     static class Api20Impl {
         private Api20Impl() { }
 
-        @DoNotInline
         static boolean getAllowFreeFormInput(android.app.RemoteInput remoteInput) {
             return remoteInput.getAllowFreeFormInput();
         }
 
-        @DoNotInline
         static CharSequence[] getChoices(android.app.RemoteInput remoteInput) {
             return remoteInput.getChoices();
         }
 
-        @DoNotInline
         static CharSequence getLabel(android.app.RemoteInput remoteInput) {
             return remoteInput.getLabel();
         }
 
-        @DoNotInline
         static String getResultKey(android.app.RemoteInput remoteInput) {
             return remoteInput.getResultKey();
         }
 
-        @DoNotInline
         static android.app.RemoteInput[] getRemoteInputs(Notification.Action action) {
             return action.getRemoteInputs();
         }
 
-        @DoNotInline
         static String getSortKey(Notification notification) {
             return notification.getSortKey();
         }
 
-        @DoNotInline
         static String getGroup(Notification notification) {
             return notification.getGroup();
         }
 
-        @DoNotInline
         static Bundle getExtras(Notification.Action action) {
             return action.getExtras();
         }
 
-        @DoNotInline
         static Bundle getExtras(android.app.RemoteInput remoteInput) {
             return remoteInput.getExtras();
         }
@@ -9569,7 +9487,6 @@
     static class Api23Impl {
         private Api23Impl() { }
 
-        @DoNotInline
         static Icon getIcon(Notification.Action action) {
             return action.getIcon();
         }
@@ -9584,7 +9501,6 @@
     static class Api24Impl {
         private Api24Impl() { }
 
-        @DoNotInline
         static boolean getAllowGeneratedReplies(Notification.Action action) {
             return action.getAllowGeneratedReplies();
         }
@@ -9600,32 +9516,26 @@
     static class Api26Impl {
         private Api26Impl() { }
 
-        @DoNotInline
         static int getGroupAlertBehavior(Notification notification) {
             return notification.getGroupAlertBehavior();
         }
 
-        @DoNotInline
         static CharSequence getSettingsText(Notification notification) {
             return notification.getSettingsText();
         }
 
-        @DoNotInline
         static String getShortcutId(Notification notification) {
             return notification.getShortcutId();
         }
 
-        @DoNotInline
         static int getBadgeIconType(Notification notification) {
             return notification.getBadgeIconType();
         }
 
-        @DoNotInline
         static long getTimeoutAfter(Notification notification) {
             return notification.getTimeoutAfter();
         }
 
-        @DoNotInline
         static String getChannelId(Notification notification) {
             return notification.getChannelId();
         }
@@ -9640,7 +9550,6 @@
     static class Api28Impl {
         private Api28Impl() { }
 
-        @DoNotInline
         static int getSemanticAction(Notification.Action action) {
             return action.getSemanticAction();
         }
@@ -9655,27 +9564,22 @@
     static class Api29Impl {
         private Api29Impl() { }
 
-        @DoNotInline
         static boolean getAllowSystemGeneratedContextualActions(Notification notification) {
             return notification.getAllowSystemGeneratedContextualActions();
         }
 
-        @DoNotInline
         static LocusId getLocusId(Notification notification) {
             return notification.getLocusId();
         }
 
-        @DoNotInline
         static boolean isContextual(Notification.Action action) {
             return action.isContextual();
         }
 
-        @DoNotInline
         static int getEditChoicesBeforeSending(android.app.RemoteInput remoteInput) {
             return remoteInput.getEditChoicesBeforeSending();
         }
 
-        @DoNotInline
         static Notification.BubbleMetadata getBubbleMetadata(Notification notification) {
             return notification.getBubbleMetadata();
         }
@@ -9690,7 +9594,6 @@
     static class Api31Impl {
         private Api31Impl() { }
 
-        @DoNotInline
         static boolean isAuthenticationRequired(Notification.Action action) {
             return action.isAuthenticationRequired();
         }
diff --git a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
index b68188b..8dbcd23 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
@@ -37,7 +37,6 @@
 import android.util.SparseArray;
 import android.widget.RemoteViews;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -537,58 +536,48 @@
     static class Api20Impl {
         private Api20Impl() { }
 
-        @DoNotInline
         static Notification.Action.Builder createBuilder(int icon, CharSequence title,
                 PendingIntent intent) {
             return new Notification.Action.Builder(icon, title, intent);
         }
 
-        @DoNotInline
         static Notification.Action.Builder addRemoteInput(Notification.Action.Builder builder,
                 android.app.RemoteInput remoteInput) {
             return builder.addRemoteInput(remoteInput);
         }
 
-        @DoNotInline
         static Notification.Action.Builder addExtras(Notification.Action.Builder builder,
                 Bundle extras) {
             return builder.addExtras(extras);
         }
 
 
-        @DoNotInline
         static Notification.Builder addAction(Notification.Builder builder,
                 Notification.Action action) {
             return builder.addAction(action);
         }
 
-        @DoNotInline
         static Notification.Action build(Notification.Action.Builder builder) {
             return builder.build();
         }
 
-        @DoNotInline
         static String getGroup(Notification notification) {
             return notification.getGroup();
         }
 
-        @DoNotInline
         static Notification.Builder setGroup(Notification.Builder builder, String groupKey) {
             return builder.setGroup(groupKey);
         }
 
-        @DoNotInline
         static Notification.Builder setGroupSummary(Notification.Builder builder,
                 boolean isGroupSummary) {
             return builder.setGroupSummary(isGroupSummary);
         }
 
-        @DoNotInline
         static Notification.Builder setLocalOnly(Notification.Builder builder, boolean localOnly) {
             return builder.setLocalOnly(localOnly);
         }
 
-        @DoNotInline
         static Notification.Builder setSortKey(Notification.Builder builder, String sortKey) {
             return builder.setSortKey(sortKey);
         }
@@ -603,32 +592,26 @@
     static class Api21Impl {
         private Api21Impl() { }
 
-        @DoNotInline
         static Notification.Builder addPerson(Notification.Builder builder, String uri) {
             return builder.addPerson(uri);
         }
 
-        @DoNotInline
         static Notification.Builder setCategory(Notification.Builder builder, String category) {
             return builder.setCategory(category);
         }
 
-        @DoNotInline
         static Notification.Builder setColor(Notification.Builder builder, int argb) {
             return builder.setColor(argb);
         }
 
-        @DoNotInline
         static Notification.Builder setVisibility(Notification.Builder builder, int visibility) {
             return builder.setVisibility(visibility);
         }
 
-        @DoNotInline
         static Notification.Builder setPublicVersion(Notification.Builder builder, Notification n) {
             return builder.setPublicVersion(n);
         }
 
-        @DoNotInline
         static Notification.Builder setSound(Notification.Builder builder, Uri sound,
                 Object audioAttributes /* AudioAttributes */) {
             return builder.setSound(sound, (AudioAttributes) audioAttributes);
@@ -644,19 +627,16 @@
     static class Api23Impl {
         private Api23Impl() { }
 
-        @DoNotInline
         static Notification.Action.Builder createBuilder(Icon icon, CharSequence title,
                 PendingIntent intent) {
             return new Notification.Action.Builder(icon, title, intent);
         }
 
-        @DoNotInline
         static Notification.Builder setSmallIcon(Notification.Builder builder,
                 Object icon /* Icon */) {
             return builder.setSmallIcon((Icon) icon);
         }
 
-        @DoNotInline
         static Notification.Builder setLargeIcon(Notification.Builder builder, Icon icon) {
             return builder.setLargeIcon(icon);
         }
@@ -671,31 +651,26 @@
     static class Api24Impl {
         private Api24Impl() { }
 
-        @DoNotInline
         static Notification.Action.Builder setAllowGeneratedReplies(
                 Notification.Action.Builder builder, boolean allowGeneratedReplies) {
             return builder.setAllowGeneratedReplies(allowGeneratedReplies);
         }
 
-        @DoNotInline
         static Notification.Builder setRemoteInputHistory(Notification.Builder builder,
                 CharSequence[] text) {
             return builder.setRemoteInputHistory(text);
         }
 
-        @DoNotInline
         static Notification.Builder setCustomContentView(Notification.Builder builder,
                 RemoteViews contentView) {
             return builder.setCustomContentView(contentView);
         }
 
-        @DoNotInline
         static Notification.Builder setCustomBigContentView(Notification.Builder builder,
                 RemoteViews contentView) {
             return builder.setCustomBigContentView(contentView);
         }
 
-        @DoNotInline
         static Notification.Builder setCustomHeadsUpContentView(Notification.Builder builder,
                 RemoteViews contentView) {
             return builder.setCustomHeadsUpContentView(contentView);
@@ -711,39 +686,32 @@
     static class Api26Impl {
         private Api26Impl() { }
 
-        @DoNotInline
         static Notification.Builder createBuilder(Context context, String channelId) {
             return new Notification.Builder(context, channelId);
         }
 
-        @DoNotInline
         static Notification.Builder setGroupAlertBehavior(Notification.Builder builder,
                 int groupAlertBehavior) {
             return builder.setGroupAlertBehavior(groupAlertBehavior);
         }
 
-        @DoNotInline
         static Notification.Builder setColorized(Notification.Builder builder, boolean colorize) {
             return builder.setColorized(colorize);
         }
 
-        @DoNotInline
         static Notification.Builder setBadgeIconType(Notification.Builder builder, int icon) {
             return builder.setBadgeIconType(icon);
         }
 
-        @DoNotInline
         static Notification.Builder setSettingsText(Notification.Builder builder,
                 CharSequence text) {
             return builder.setSettingsText(text);
         }
 
-        @DoNotInline
         static Notification.Builder setShortcutId(Notification.Builder builder, String shortcutId) {
             return builder.setShortcutId(shortcutId);
         }
 
-        @DoNotInline
         static Notification.Builder setTimeoutAfter(Notification.Builder builder, long durationMs) {
             return builder.setTimeoutAfter(durationMs);
         }
@@ -759,13 +727,11 @@
         private Api28Impl() {
         }
 
-        @DoNotInline
         static Notification.Action.Builder setSemanticAction(Notification.Action.Builder builder,
                 int semanticAction) {
             return builder.setSemanticAction(semanticAction);
         }
 
-        @DoNotInline
         static Notification.Builder addPerson(Notification.Builder builder,
                 android.app.Person person) {
             return builder.addPerson(person);
@@ -781,25 +747,21 @@
     static class Api29Impl {
         private Api29Impl() { }
 
-        @DoNotInline
         static Notification.Action.Builder setContextual(Notification.Action.Builder builder,
                 boolean isContextual) {
             return builder.setContextual(isContextual);
         }
 
-        @DoNotInline
         static Notification.Builder setLocusId(Notification.Builder builder,
                 Object locusId /* LocusId */) {
             return builder.setLocusId((LocusId) locusId);
         }
 
-        @DoNotInline
         static Notification.Builder setBubbleMetadata(Notification.Builder builder,
                 Notification.BubbleMetadata data) {
             return builder.setBubbleMetadata(data);
         }
 
-        @DoNotInline
         static Notification.Builder setAllowSystemGeneratedContextualActions(
                 Notification.Builder builder, boolean allowed) {
             return builder.setAllowSystemGeneratedContextualActions(allowed);
@@ -816,13 +778,11 @@
         private Api31Impl() {
         }
 
-        @DoNotInline
         static Notification.Action.Builder setAuthenticationRequired(
                 Notification.Action.Builder builder, boolean authenticationRequired) {
             return builder.setAuthenticationRequired(authenticationRequired);
         }
 
-        @DoNotInline
         static Notification.Builder setForegroundServiceBehavior(Notification.Builder builder,
                 int behavior) {
             return builder.setForegroundServiceBehavior(behavior);
diff --git a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
index 1fcf6fc..7e005fd 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
@@ -48,7 +48,6 @@
 import android.support.v4.app.INotificationSideChannel;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -1246,7 +1245,6 @@
     static class Api23Impl {
         private Api23Impl() { }
 
-        @DoNotInline
         static List<StatusBarNotification> getActiveNotifications(
                 NotificationManager notificationManager) {
             StatusBarNotification[] notifs = notificationManager.getActiveNotifications();
@@ -1256,7 +1254,6 @@
             return Arrays.asList(notifs);
         }
 
-        @DoNotInline
         static int getCurrentInterruptionFilter(
                 NotificationManager notificationManager) {
             return notificationManager.getCurrentInterruptionFilter();
@@ -1272,12 +1269,10 @@
     static class Api24Impl {
         private Api24Impl() { }
 
-        @DoNotInline
         static boolean areNotificationsEnabled(NotificationManager notificationManager) {
             return notificationManager.areNotificationsEnabled();
         }
 
-        @DoNotInline
         static int getImportance(NotificationManager notificationManager) {
             return notificationManager.getImportance();
         }
@@ -1294,67 +1289,56 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void createNotificationChannel(NotificationManager notificationManager,
                 NotificationChannel channel) {
             notificationManager.createNotificationChannel(channel);
         }
 
-        @DoNotInline
         static NotificationChannel getNotificationChannel(NotificationManager notificationManager,
                 String channelId) {
             return notificationManager.getNotificationChannel(channelId);
         }
 
-        @DoNotInline
         static void createNotificationChannels(
                 NotificationManager notificationManager, List<NotificationChannel> channels) {
             notificationManager.createNotificationChannels(channels);
         }
 
-        @DoNotInline
         static List<NotificationChannel> getNotificationChannels(
                 NotificationManager notificationManager) {
             return notificationManager.getNotificationChannels();
         }
 
-        @DoNotInline
         static void createNotificationChannelGroup(NotificationManager notificationManager,
                 NotificationChannelGroup group) {
             notificationManager.createNotificationChannelGroup(group);
         }
 
-        @DoNotInline
         static void createNotificationChannelGroups(NotificationManager notificationManager,
                 List<NotificationChannelGroup> groups) {
             notificationManager.createNotificationChannelGroups(groups);
         }
 
-        @DoNotInline
         static List<NotificationChannelGroup> getNotificationChannelGroups(
                 NotificationManager notificationManager) {
             return notificationManager.getNotificationChannelGroups();
         }
 
-        @DoNotInline
         static void deleteNotificationChannel(NotificationManager notificationManager,
                 String channelId) {
             notificationManager.deleteNotificationChannel(channelId);
         }
 
-        @DoNotInline
         static void deleteNotificationChannelGroup(NotificationManager notificationManager,
                 String groupId) {
             notificationManager.deleteNotificationChannelGroup(groupId);
         }
 
 
-        @DoNotInline
         static String getId(NotificationChannel notificationChannel) {
             return notificationChannel.getId();
         }
 
-        @DoNotInline
         static String getId(NotificationChannelGroup notificationChannelGroup) {
             return notificationChannelGroup.getId();
         }
@@ -1369,7 +1353,6 @@
     static class Api28Impl {
         private Api28Impl() { }
 
-        @DoNotInline
         static NotificationChannelGroup getNotificationChannelGroup(
                 NotificationManager notificationManager, String channelGroupId) {
             return notificationManager.getNotificationChannelGroup(channelGroupId);
@@ -1385,12 +1368,10 @@
     static class Api30Impl {
         private Api30Impl() { }
 
-        @DoNotInline
         static String getParentChannelId(NotificationChannel notificationChannel) {
             return notificationChannel.getParentChannelId();
         }
 
-        @DoNotInline
         static NotificationChannel getNotificationChannel(NotificationManager notificationManager,
                 String channelId, String conversationId) {
             return notificationManager.getNotificationChannel(channelId, conversationId);
@@ -1406,7 +1387,6 @@
     static class Api34Impl {
         private Api34Impl() { }
 
-        @DoNotInline
         static boolean canUseFullScreenIntent(NotificationManager notificationManager) {
             return notificationManager.canUseFullScreenIntent();
         }
diff --git a/core/core/src/main/java/androidx/core/app/PendingIntentCompat.java b/core/core/src/main/java/androidx/core/app/PendingIntentCompat.java
index 66a61a9..f801a70 100644
--- a/core/core/src/main/java/androidx/core/app/PendingIntentCompat.java
+++ b/core/core/src/main/java/androidx/core/app/PendingIntentCompat.java
@@ -28,7 +28,6 @@
 import android.os.Bundle;

 import android.os.Handler;

 

-import androidx.annotation.DoNotInline;

 import androidx.annotation.IntDef;

 import androidx.annotation.NonNull;

 import androidx.annotation.Nullable;

@@ -295,7 +294,6 @@
     private static class Api23Impl {

         private Api23Impl() {}

 

-        @DoNotInline

         public static void send(

                 @NonNull PendingIntent pendingIntent,

                 @NonNull Context context,

@@ -320,7 +318,6 @@
     private static class Api26Impl {

         private Api26Impl() {}

 

-        @DoNotInline

         public static PendingIntent getForegroundService(

                 Context context, int requestCode, Intent intent, int flags) {

             return PendingIntent.getForegroundService(context, requestCode, intent, flags);

diff --git a/core/core/src/main/java/androidx/core/app/Person.java b/core/core/src/main/java/androidx/core/app/Person.java
index 030001e..441de1b 100644
--- a/core/core/src/main/java/androidx/core/app/Person.java
+++ b/core/core/src/main/java/androidx/core/app/Person.java
@@ -21,7 +21,6 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -375,7 +374,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Person fromPersistableBundle(PersistableBundle bundle) {
             return new Builder()
                     .setName(bundle.getString(NAME_KEY))
@@ -386,7 +384,6 @@
                     .build();
         }
 
-        @DoNotInline
         static PersistableBundle toPersistableBundle(Person person) {
             PersistableBundle result = new PersistableBundle();
             result.putString(NAME_KEY, person.mName != null ? person.mName.toString() : null);
@@ -404,7 +401,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Person fromAndroidPerson(android.app.Person person) {
             return new Builder()
                     .setName(person.getName())
@@ -420,7 +416,6 @@
         }
 
         @SuppressWarnings("deprecation")
-        @DoNotInline
         static android.app.Person toAndroidPerson(Person person) {
             return new android.app.Person.Builder()
                     .setName(person.getName())
diff --git a/core/core/src/main/java/androidx/core/app/RemoteActionCompat.java b/core/core/src/main/java/androidx/core/app/RemoteActionCompat.java
index 61cc975..120350c 100644
--- a/core/core/src/main/java/androidx/core/app/RemoteActionCompat.java
+++ b/core/core/src/main/java/androidx/core/app/RemoteActionCompat.java
@@ -24,7 +24,6 @@
 import android.graphics.drawable.Icon;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -210,12 +209,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean shouldShowIcon(RemoteAction remoteAction) {
             return remoteAction.shouldShowIcon();
         }
 
-        @DoNotInline
         static void setShouldShowIcon(RemoteAction remoteAction, boolean shouldShowIcon) {
             remoteAction.setShouldShowIcon(shouldShowIcon);
         }
@@ -227,38 +224,31 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static CharSequence getContentDescription(RemoteAction remoteAction) {
             return remoteAction.getContentDescription();
         }
 
-        @DoNotInline
         static PendingIntent getActionIntent(RemoteAction remoteAction) {
             return remoteAction.getActionIntent();
         }
 
-        @DoNotInline
         static CharSequence getTitle(RemoteAction remoteAction) {
             return remoteAction.getTitle();
         }
 
-        @DoNotInline
         static Icon getIcon(RemoteAction remoteAction) {
             return remoteAction.getIcon();
         }
 
-        @DoNotInline
         static boolean isEnabled(RemoteAction remoteAction) {
             return remoteAction.isEnabled();
         }
 
-        @DoNotInline
         static RemoteAction createRemoteAction(Icon icon, CharSequence title,
                 CharSequence contentDescription, PendingIntent intent) {
             return new RemoteAction(icon, title, contentDescription, intent);
         }
 
-        @DoNotInline
         static void setEnabled(RemoteAction remoteAction, boolean enabled) {
             remoteAction.setEnabled(enabled);
         }
diff --git a/core/core/src/main/java/androidx/core/app/RemoteInput.java b/core/core/src/main/java/androidx/core/app/RemoteInput.java
index a0ade5f..4dd86fc 100644
--- a/core/core/src/main/java/androidx/core/app/RemoteInput.java
+++ b/core/core/src/main/java/androidx/core/app/RemoteInput.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 import android.os.Bundle;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -591,24 +590,20 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Map<String, Uri> getDataResultsFromIntent(Intent intent,
                 String remoteInputResultKey) {
             return android.app.RemoteInput.getDataResultsFromIntent(intent, remoteInputResultKey);
         }
 
-        @DoNotInline
         static Set<String> getAllowedDataTypes(Object remoteInput) {
             return ((android.app.RemoteInput) remoteInput).getAllowedDataTypes();
         }
 
-        @DoNotInline
         static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
                 Map<String, Uri> results) {
             android.app.RemoteInput.addDataResultToIntent(fromCompat(remoteInput), intent, results);
         }
 
-        @DoNotInline
         static android.app.RemoteInput.Builder setAllowDataType(
                 android.app.RemoteInput.Builder builder, String mimeType, boolean doAllow) {
             return builder.setAllowDataType(mimeType, doAllow);
@@ -621,12 +616,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Bundle getResultsFromIntent(Intent intent) {
             return android.app.RemoteInput.getResultsFromIntent(intent);
         }
 
-        @DoNotInline
         static void addResultsToIntent(Object remoteInputs, Intent intent, Bundle results) {
             android.app.RemoteInput.addResultsToIntent((android.app.RemoteInput[]) remoteInputs,
                     intent, results);
@@ -682,12 +675,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getEditChoicesBeforeSending(Object remoteInput) {
             return ((android.app.RemoteInput) remoteInput).getEditChoicesBeforeSending();
         }
 
-        @DoNotInline
         static android.app.RemoteInput.Builder setEditChoicesBeforeSending(
                 android.app.RemoteInput.Builder builder, int editChoicesBeforeSending) {
             return builder.setEditChoicesBeforeSending(editChoicesBeforeSending);
@@ -700,12 +691,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setResultsSource(Intent intent, int source) {
             android.app.RemoteInput.setResultsSource(intent, source);
         }
 
-        @DoNotInline
         static int getResultsSource(Intent intent) {
             return android.app.RemoteInput.getResultsSource(intent);
         }
diff --git a/core/core/src/main/java/androidx/core/app/ServiceCompat.java b/core/core/src/main/java/androidx/core/app/ServiceCompat.java
index 043850e..444e0c0 100644
--- a/core/core/src/main/java/androidx/core/app/ServiceCompat.java
+++ b/core/core/src/main/java/androidx/core/app/ServiceCompat.java
@@ -26,7 +26,6 @@
 import android.content.pm.ServiceInfo;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -200,7 +199,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void stopForeground(Service service, int flags) {
             service.stopForeground(flags);
         }
@@ -212,7 +210,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void startForeground(Service service, int id, Notification notification,
                 int foregroundServiceType) {
             if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_NONE
@@ -231,7 +228,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void startForeground(Service service, int id, Notification notification,
                 int foregroundServiceType) {
             if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_NONE
diff --git a/core/core/src/main/java/androidx/core/content/ContextCompat.java b/core/core/src/main/java/androidx/core/content/ContextCompat.java
index d6b1f9c..08a8559 100644
--- a/core/core/src/main/java/androidx/core/content/ContextCompat.java
+++ b/core/core/src/main/java/androidx/core/content/ContextCompat.java
@@ -143,7 +143,6 @@
 import androidx.annotation.ColorInt;
 import androidx.annotation.ColorRes;
 import androidx.annotation.DisplayContext;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -1058,17 +1057,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Drawable getDrawable(Context obj, int id) {
             return obj.getDrawable(id);
         }
 
-        @DoNotInline
         static File getNoBackupFilesDir(Context obj) {
             return obj.getNoBackupFilesDir();
         }
 
-        @DoNotInline
         static File getCodeCacheDir(Context obj) {
             return obj.getCodeCacheDir();
         }
@@ -1080,17 +1076,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getColor(Context obj, int id) {
             return obj.getColor(id);
         }
 
-        @DoNotInline
         static <T> T getSystemService(Context obj, Class<T> serviceClass) {
             return obj.getSystemService(serviceClass);
         }
 
-        @DoNotInline
         static String getSystemServiceName(Context obj, Class<?> serviceClass) {
             return obj.getSystemServiceName(serviceClass);
         }
@@ -1102,17 +1095,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static File getDataDir(Context obj) {
             return obj.getDataDir();
         }
 
-        @DoNotInline
         static Context createDeviceProtectedStorageContext(Context obj) {
             return obj.createDeviceProtectedStorageContext();
         }
 
-        @DoNotInline
         static boolean isDeviceProtectedStorage(Context obj) {
             return obj.isDeviceProtectedStorage();
         }
@@ -1124,7 +1114,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Intent registerReceiver(Context obj, @Nullable BroadcastReceiver receiver,
                 IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
             if ((flags & RECEIVER_NOT_EXPORTED) != 0 && broadcastPermission == null) {
@@ -1137,7 +1126,6 @@
         }
 
         @SuppressWarnings("UnusedReturnValue")
-        @DoNotInline
         static ComponentName startForegroundService(Context obj, Intent service) {
             return obj.startForegroundService(service);
         }
@@ -1149,7 +1137,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Executor getMainExecutor(Context obj) {
             return obj.getMainExecutor();
         }
@@ -1161,12 +1148,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static String getAttributionTag(Context obj) {
             return obj.getAttributionTag();
         }
 
-        @DoNotInline
         static Display getDisplayOrDefault(Context obj) {
             try {
                 return obj.getDisplay();
@@ -1179,7 +1164,6 @@
             }
         }
 
-        @DoNotInline
         @NonNull
         static Context createAttributionContext(@NonNull Context context,
                 @Nullable String attributionTag) {
@@ -1193,7 +1177,6 @@
             // This class is not instantiable
         }
 
-        @DoNotInline
         static Intent registerReceiver(Context obj, @Nullable BroadcastReceiver receiver,
                 IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
             return obj.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags);
diff --git a/core/core/src/main/java/androidx/core/content/FileProvider.java b/core/core/src/main/java/androidx/core/content/FileProvider.java
index 5fe4b8d..2dfb577 100644
--- a/core/core/src/main/java/androidx/core/content/FileProvider.java
+++ b/core/core/src/main/java/androidx/core/content/FileProvider.java
@@ -45,7 +45,6 @@
 import android.webkit.MimeTypeMap;
 
 import androidx.annotation.CallSuper;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -993,7 +992,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static File[] getExternalMediaDirs(Context context) {
             // Deprecated, otherwise this would belong on ContextCompat as a public method.
             return context.getExternalMediaDirs();
diff --git a/core/core/src/main/java/androidx/core/content/IntentCompat.java b/core/core/src/main/java/androidx/core/content/IntentCompat.java
index 4cbd632..7730a74 100644
--- a/core/core/src/main/java/androidx/core/content/IntentCompat.java
+++ b/core/core/src/main/java/androidx/core/content/IntentCompat.java
@@ -31,7 +31,6 @@
 import android.os.Build;
 import android.os.Parcelable;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -331,25 +330,21 @@
             // This class is non-instantiable.
         }
 
-        @DoNotInline
         static <T> T getParcelableExtra(@NonNull Intent in, @Nullable String name,
                 @NonNull Class<T> clazz) {
             return in.getParcelableExtra(name, clazz);
         }
 
-        @DoNotInline
         static <T> T[] getParcelableArrayExtra(@NonNull Intent in, @Nullable String name,
                 @NonNull Class<T> clazz) {
             return in.getParcelableArrayExtra(name, clazz);
         }
 
-        @DoNotInline
         static <T> ArrayList<T> getParcelableArrayListExtra(@NonNull Intent in,
                 @Nullable String name, @NonNull Class<? extends T> clazz) {
             return in.getParcelableArrayListExtra(name, clazz);
         }
 
-        @DoNotInline
         static <T extends Serializable> T getSerializableExtra(@NonNull Intent in,
                 @Nullable String name, @NonNull Class<T> clazz) {
             return in.getSerializableExtra(name, clazz);
diff --git a/core/core/src/main/java/androidx/core/content/IntentSanitizer.java b/core/core/src/main/java/androidx/core/content/IntentSanitizer.java
index 421ba7c..9297ccf 100644
--- a/core/core/src/main/java/androidx/core/content/IntentSanitizer.java
+++ b/core/core/src/main/java/androidx/core/content/IntentSanitizer.java
@@ -29,7 +29,6 @@
 import android.os.strictmode.UnsafeIntentLaunchViolation;
 import android.provider.MediaStore;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.core.util.Consumer;
@@ -950,7 +949,6 @@
         private Api31Impl() {
         }
 
-        @DoNotInline
         static void checkOtherMembers(int i, ClipData.Item item, Consumer<String> penalty) {
             if (item.getHtmlText() != null || item.getIntent() != null
                     || item.getTextLinks() != null) {
@@ -966,12 +964,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Intent setIdentifier(Intent intent, String identifier) {
             return intent.setIdentifier(identifier);
         }
 
-        @DoNotInline
         static String getIdentifier(Intent intent) {
             return intent.getIdentifier();
         }
diff --git a/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java b/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
index 31372ce..be98811 100644
--- a/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
@@ -23,7 +23,6 @@
 import android.content.pm.SigningInfo;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -269,30 +268,25 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean hasSigningCertificate(@NonNull PackageManager packageManager,
                 @NonNull String packageName, @NonNull byte[] bytes, int type) {
             return packageManager.hasSigningCertificate(packageName, bytes, type);
         }
 
-        @DoNotInline
         static boolean hasMultipleSigners(@NonNull SigningInfo signingInfo) {
             return signingInfo.hasMultipleSigners();
         }
 
-        @DoNotInline
         @Nullable
         static Signature[] getApkContentsSigners(@NonNull SigningInfo signingInfo) {
             return signingInfo.getApkContentsSigners();
         }
 
-        @DoNotInline
         @Nullable
         static Signature[] getSigningCertificateHistory(@NonNull SigningInfo signingInfo) {
             return signingInfo.getSigningCertificateHistory();
         }
 
-        @DoNotInline
         static long getLongVersionCode(PackageInfo packageInfo) {
             return packageInfo.getLongVersionCode();
         }
diff --git a/core/core/src/main/java/androidx/core/content/pm/PermissionInfoCompat.java b/core/core/src/main/java/androidx/core/content/pm/PermissionInfoCompat.java
index bee25f3..8cf8f7e 100644
--- a/core/core/src/main/java/androidx/core/content/pm/PermissionInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/PermissionInfoCompat.java
@@ -20,7 +20,6 @@
 import android.content.pm.PermissionInfo;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -98,12 +97,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getProtection(PermissionInfo permissionInfo) {
             return permissionInfo.getProtection();
         }
 
-        @DoNotInline
         static int getProtectionFlags(PermissionInfo permissionInfo) {
             return permissionInfo.getProtectionFlags();
         }
diff --git a/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java b/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
index ffece79..55b813c 100644
--- a/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
+++ b/core/core/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
@@ -28,7 +28,6 @@
 import android.util.Xml;
 
 import androidx.annotation.ArrayRes;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -77,14 +76,17 @@
      */
     public static final class ProviderResourceEntry implements FamilyResourceEntry {
         private final @NonNull FontRequest mRequest;
+        private final @Nullable FontRequest mFallbackRequest;
         private final int mTimeoutMs;
         private final @FetchStrategy int mStrategy;
         private final @Nullable String mSystemFontFamilyName;
 
         @RestrictTo(LIBRARY)
-        public ProviderResourceEntry(@NonNull FontRequest request, @FetchStrategy int strategy,
+        public ProviderResourceEntry(@NonNull FontRequest request,
+                @Nullable FontRequest fallbackRequest, @FetchStrategy int strategy,
                 int timeoutMs, @Nullable String systemFontFamilyName) {
             mRequest = request;
+            mFallbackRequest = fallbackRequest;
             mStrategy = strategy;
             mTimeoutMs = timeoutMs;
             mSystemFontFamilyName = systemFontFamilyName;
@@ -92,13 +94,18 @@
 
         public ProviderResourceEntry(@NonNull FontRequest request, @FetchStrategy int strategy,
                 int timeoutMs) {
-            this(request, strategy, timeoutMs, null /*systemFontFamilyName*/);
+            this(request, null, strategy, timeoutMs, null /*systemFontFamilyName*/);
         }
 
         public @NonNull FontRequest getRequest() {
             return mRequest;
         }
 
+        @Nullable
+        public FontRequest getFallbackRequest() {
+            return mFallbackRequest;
+        }
+
         public @FetchStrategy int getFetchStrategy() {
             return mStrategy;
         }
@@ -210,6 +217,7 @@
         String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
         String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
         String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
+        String fallbackQuery = array.getString(R.styleable.FontFamily_fontProviderFallbackQuery);
         int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0);
         int strategy = array.getInteger(R.styleable.FontFamily_fontProviderFetchStrategy,
                 FETCH_STRATEGY_ASYNC);
@@ -224,8 +232,15 @@
                 skip(parser);
             }
             List<List<byte[]>> certs = readCerts(resources, certsId);
+            FontRequest fallbackRequest;
+            if (fallbackQuery != null) {
+                fallbackRequest = new FontRequest(authority, providerPackage, fallbackQuery, certs);
+            } else {
+                fallbackRequest = null;
+            }
             return new ProviderResourceEntry(
                     new FontRequest(authority, providerPackage, query, certs),
+                    fallbackRequest,
                     strategy,
                     timeoutMs,
                     systemFontFamilyName
@@ -364,7 +379,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getType(TypedArray typedArray, int index) {
             return typedArray.getType(index);
         }
diff --git a/core/core/src/main/java/androidx/core/content/res/ResourcesCompat.java b/core/core/src/main/java/androidx/core/content/res/ResourcesCompat.java
index a46e557..9ba0cc71 100644
--- a/core/core/src/main/java/androidx/core/content/res/ResourcesCompat.java
+++ b/core/core/src/main/java/androidx/core/content/res/ResourcesCompat.java
@@ -42,7 +42,6 @@
 import androidx.annotation.ColorInt;
 import androidx.annotation.ColorRes;
 import androidx.annotation.DimenRes;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.FontRes;
 import androidx.annotation.GuardedBy;
@@ -668,7 +667,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static float getFloat(@NonNull Resources res, @DimenRes int id) {
             return res.getFloat(id);
         }
@@ -680,14 +678,12 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         @NonNull
         static ColorStateList getColorStateList(@NonNull Resources res, @ColorRes int id,
                 @Nullable Theme theme) {
             return res.getColorStateList(id, theme);
         }
 
-        @DoNotInline
         static int getColor(Resources resources, int id, Theme theme) {
             return resources.getColor(id, theme);
         }
@@ -699,12 +695,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Drawable getDrawable(Resources resources, int id, Theme theme) {
             return resources.getDrawable(id, theme);
         }
 
-        @DoNotInline
         static Drawable getDrawableForDensity(Resources resources, int id, int density,
                 Theme theme) {
             return resources.getDrawableForDensity(id, density, theme);
@@ -749,7 +743,6 @@
                 // This class is not instantiable.
             }
 
-            @DoNotInline
             static void rebase(@NonNull Theme theme) {
                 theme.rebase();
             }
diff --git a/core/core/src/main/java/androidx/core/database/CursorWindowCompat.java b/core/core/src/main/java/androidx/core/database/CursorWindowCompat.java
index a2c5681..04202eb 100644
--- a/core/core/src/main/java/androidx/core/database/CursorWindowCompat.java
+++ b/core/core/src/main/java/androidx/core/database/CursorWindowCompat.java
@@ -19,7 +19,6 @@
 import android.database.CursorWindow;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -54,7 +53,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static CursorWindow createCursorWindow(String name, long windowSizeBytes) {
             return new CursorWindow(name, windowSizeBytes);
         }
diff --git a/core/core/src/main/java/androidx/core/database/sqlite/SQLiteCursorCompat.java b/core/core/src/main/java/androidx/core/database/sqlite/SQLiteCursorCompat.java
index 1a2225e..00746f4 100644
--- a/core/core/src/main/java/androidx/core/database/sqlite/SQLiteCursorCompat.java
+++ b/core/core/src/main/java/androidx/core/database/sqlite/SQLiteCursorCompat.java
@@ -20,7 +20,6 @@
 import android.database.sqlite.SQLiteCursor;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -56,7 +55,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setFillWindowForwardOnly(SQLiteCursor cursor, boolean fillWindowForwardOnly) {
             cursor.setFillWindowForwardOnly(fillWindowForwardOnly);
         }
diff --git a/core/core/src/main/java/androidx/core/graphics/BitmapCompat.java b/core/core/src/main/java/androidx/core/graphics/BitmapCompat.java
index 547918d..ea4f04a 100644
--- a/core/core/src/main/java/androidx/core/graphics/BitmapCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/BitmapCompat.java
@@ -26,7 +26,6 @@
 import android.hardware.HardwareBuffer;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -340,7 +339,6 @@
         private Api27Impl() {
         }
 
-        @DoNotInline
         static Bitmap createBitmapWithSourceColorspace(int w, int h, Bitmap src, boolean linear) {
             Bitmap.Config config = src.getConfig();
             ColorSpace colorSpace = src.getColorSpace();
@@ -358,13 +356,11 @@
             return Bitmap.createBitmap(w, h, config, src.hasAlpha(), colorSpace);
         }
 
-        @DoNotInline
         static boolean isAlreadyF16AndLinear(Bitmap b) {
             ColorSpace linearCs = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
             return b.getConfig() == Bitmap.Config.RGBA_F16 && b.getColorSpace().equals(linearCs);
         }
 
-        @DoNotInline
         static Bitmap copyBitmapIfHardware(Bitmap bm) {
             if (bm.getConfig() == Bitmap.Config.HARDWARE) {
                 Bitmap.Config newConfig = Bitmap.Config.ARGB_8888;
@@ -383,7 +379,6 @@
         private Api29Impl() {
         }
 
-        @DoNotInline
         static void setPaintBlendMode(Paint paint) {
             paint.setBlendMode(BlendMode.SRC);
         }
@@ -394,7 +389,6 @@
         private Api31Impl() {
         }
 
-        @DoNotInline
         static Bitmap.Config getHardwareBitmapConfig(Bitmap bm) {
             if (bm.getHardwareBuffer().getFormat() == HardwareBuffer.RGBA_FP16) {
                 return Bitmap.Config.RGBA_F16;
diff --git a/core/core/src/main/java/androidx/core/graphics/BlendModeColorFilterCompat.java b/core/core/src/main/java/androidx/core/graphics/BlendModeColorFilterCompat.java
index cff8d8f..f12da61 100644
--- a/core/core/src/main/java/androidx/core/graphics/BlendModeColorFilterCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/BlendModeColorFilterCompat.java
@@ -23,7 +23,6 @@
 import android.graphics.PorterDuffColorFilter;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -65,7 +64,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ColorFilter createBlendModeColorFilter(int color, Object mode) {
             return new BlendModeColorFilter(color, (BlendMode) mode);
         }
diff --git a/core/core/src/main/java/androidx/core/graphics/BlendModeUtils.java b/core/core/src/main/java/androidx/core/graphics/BlendModeUtils.java
index feb339b..f610f9a 100644
--- a/core/core/src/main/java/androidx/core/graphics/BlendModeUtils.java
+++ b/core/core/src/main/java/androidx/core/graphics/BlendModeUtils.java
@@ -19,7 +19,6 @@
 import android.graphics.BlendMode;
 import android.graphics.PorterDuff;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -39,7 +38,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         @Nullable
         static Object obtainBlendModeFromCompat(@NonNull BlendModeCompat blendModeCompat) {
             switch (blendModeCompat) {
diff --git a/core/core/src/main/java/androidx/core/graphics/ColorUtils.java b/core/core/src/main/java/androidx/core/graphics/ColorUtils.java
index 7d26837..a7b93c7 100644
--- a/core/core/src/main/java/androidx/core/graphics/ColorUtils.java
+++ b/core/core/src/main/java/androidx/core/graphics/ColorUtils.java
@@ -20,7 +20,6 @@
 import android.graphics.Color;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.FloatRange;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
@@ -101,7 +100,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Color compositeColors(Color foreground, Color background) {
             if (!Objects.equals(foreground.getModel(), background.getModel())) {
                 throw new IllegalArgumentException(
diff --git a/core/core/src/main/java/androidx/core/graphics/Insets.java b/core/core/src/main/java/androidx/core/graphics/Insets.java
index 35a5df7..f88bff3 100644
--- a/core/core/src/main/java/androidx/core/graphics/Insets.java
+++ b/core/core/src/main/java/androidx/core/graphics/Insets.java
@@ -20,7 +20,6 @@
 
 import android.graphics.Rect;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -207,7 +206,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static android.graphics.Insets of(int left, int top, int right, int bottom) {
             return android.graphics.Insets.of(left, top, right, bottom);
         }
diff --git a/core/core/src/main/java/androidx/core/graphics/PaintCompat.java b/core/core/src/main/java/androidx/core/graphics/PaintCompat.java
index 4503d2c..e2e51b4 100644
--- a/core/core/src/main/java/androidx/core/graphics/PaintCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/PaintCompat.java
@@ -25,7 +25,6 @@
 import android.graphics.Rect;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -166,7 +165,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setBlendMode(Paint paint, Object blendmode) {
             paint.setBlendMode((BlendMode) blendmode);
         }
@@ -178,7 +176,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean hasGlyph(Paint paint, String string) {
             return paint.hasGlyph(string);
         }
diff --git a/core/core/src/main/java/androidx/core/graphics/PathUtils.java b/core/core/src/main/java/androidx/core/graphics/PathUtils.java
index d08541d..043f1c4 100644
--- a/core/core/src/main/java/androidx/core/graphics/PathUtils.java
+++ b/core/core/src/main/java/androidx/core/graphics/PathUtils.java
@@ -19,7 +19,6 @@
 import android.graphics.Path;
 import android.graphics.PointF;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.FloatRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -92,7 +91,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static float[] approximate(Path path, float acceptableError) {
             return path.approximate(acceptableError);
         }
diff --git a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
index 1a9f76b..b165f5c 100644
--- a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
@@ -29,6 +29,7 @@
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.collection.LruCache;
@@ -37,14 +38,27 @@
 import androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
 import androidx.core.content.res.FontResourcesParserCompat.ProviderResourceEntry;
 import androidx.core.content.res.ResourcesCompat;
+import androidx.core.provider.FontRequest;
 import androidx.core.provider.FontsContractCompat;
 import androidx.core.provider.FontsContractCompat.FontInfo;
 import androidx.core.util.Preconditions;
+import androidx.tracing.Trace;
+
+import java.util.List;
 
 /**
  * Helper for accessing features in {@link Typeface}.
  */
 public class TypefaceCompat {
+    @RestrictTo(LIBRARY)
+    public static final boolean DOWNLOADABLE_FALLBACK_DEBUG = false;
+
+    @RestrictTo(LIBRARY)
+    public static final boolean DOWNLOADABLE_FONT_TRACING = true;
+
+    static {
+        Trace.beginSection("TypefaceCompat static init");
+    }
     private static final TypefaceCompatBaseImpl sTypefaceCompatImpl;
     static {
         if (Build.VERSION.SDK_INT >= 29) {
@@ -165,7 +179,14 @@
 
             Handler newHandler = ResourcesCompat.FontCallback.getHandler(handler);
             ResourcesCallbackAdapter newCallback = new ResourcesCallbackAdapter(fontCallback);
-            typeface = FontsContractCompat.requestFont(context, providerEntry.getRequest(),
+            List<FontRequest> requests;
+            if (providerEntry.getFallbackRequest() != null) {
+                requests = List.of(providerEntry.getRequest(),
+                        providerEntry.getFallbackRequest());
+            } else {
+                requests = List.of(providerEntry.getRequest());
+            }
+            typeface = FontsContractCompat.requestFont(context, requests,
                     style, isBlocking, timeout, newHandler, newCallback);
         } else {
             typeface = sTypefaceCompatImpl.createFromFontFamilyFilesResourceEntry(
@@ -243,7 +264,42 @@
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     public static Typeface createFromFontInfo(@NonNull Context context,
             @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
-        return sTypefaceCompatImpl.createFromFontInfo(context, cancellationSignal, fonts, style);
+        if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+            Trace.beginSection("TypefaceCompat.createFromFontInfo");
+        }
+        try {
+            return sTypefaceCompatImpl.createFromFontInfo(context, cancellationSignal, fonts,
+                    style);
+        } finally {
+            if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                Trace.endSection();
+            }
+        }
+    }
+
+    /**
+     * Create a Typeface from a given list of FontInfo lists.
+     * <p>
+     * This currently throws an exception if used below API 29.
+     */
+    @Nullable
+    @RestrictTo(LIBRARY)
+    @RequiresApi(29)
+    public static Typeface createFromFontInfoWithFallback(@NonNull Context context,
+            @Nullable CancellationSignal cancellationSignal, @NonNull List<FontInfo[]> fonts,
+            int style) {
+        if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+            Trace.beginSection("TypefaceCompat.createFromFontInfoWithFallback");
+        }
+        try {
+            return sTypefaceCompatImpl.createFromFontInfoWithFallback(
+                    context, cancellationSignal, fonts,
+                    style);
+        } finally {
+            if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                Trace.endSection();
+            }
+        }
     }
 
     /**
@@ -376,4 +432,8 @@
             }
         }
     }
+
+    static {
+        Trace.endSection();
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java b/core/core/src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java
index fbd2e17..ff8d800 100644
--- a/core/core/src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java
+++ b/core/core/src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java
@@ -27,6 +27,7 @@
 import android.graphics.fonts.FontStyle;
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -37,10 +38,12 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.List;
 
 @RestrictTo(LIBRARY_GROUP)
 @RequiresApi(29)
 public class TypefaceCompatApi29Impl extends TypefaceCompatBaseImpl {
+    private static final String TAG = "TypefaceCompatApi29Impl";
 
     private static int getMatchScore(@NonNull FontStyle o1, @NonNull FontStyle o2) {
         // Logic from FontStyle.java#getMatchScore introduced in API 29
@@ -85,38 +88,77 @@
     public Typeface createFromFontInfo(Context context,
             @Nullable CancellationSignal cancellationSignal,
             @NonNull FontsContractCompat.FontInfo[] fonts, int style) {
-        FontFamily.Builder familyBuilder = null;
         final ContentResolver resolver = context.getContentResolver();
         try {
-            for (FontsContractCompat.FontInfo font : fonts) {
-                try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(font.getUri(), "r",
-                        cancellationSignal)) {
-                    if (pfd == null) {
-                        continue;  // keep adding succeeded fonts.
-                    }
-                    final Font platformFont = new Font.Builder(pfd)
-                            .setWeight(font.getWeight())
-                            .setSlant(font.isItalic() ? FontStyle.FONT_SLANT_ITALIC
-                                    : FontStyle.FONT_SLANT_UPRIGHT)
-                            .setTtcIndex(font.getTtcIndex())
-                            .build();  // TODO: font variation settings?
-                    if (familyBuilder == null) {
-                        familyBuilder = new FontFamily.Builder(platformFont);
-                    } else {
-                        familyBuilder.addFont(platformFont);
-                    }
-                } catch (IOException e) {
-                    // keep adding succeeded fonts.
-                }
-            }
-            if (familyBuilder == null) {
-                return null;  // No font is added. Give up.
-            }
-            final FontFamily family = familyBuilder.build();
+            final FontFamily family = getFontFamily(cancellationSignal, fonts, resolver);
+            if (family == null) return null;  // No font is added. Give up.
             return new Typeface.CustomFallbackBuilder(family)
                     .setStyle(findBaseFont(family, style).getStyle())
                     .build();
         } catch (Exception e) {
+            Log.w(TAG, "Font load failed", e);
+            return null;
+        }
+    }
+
+    private static @Nullable FontFamily getFontFamily(
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull FontsContractCompat.FontInfo[] fonts, ContentResolver resolver) {
+        FontFamily.Builder familyBuilder = null;
+        for (FontsContractCompat.FontInfo font : fonts) {
+            try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(font.getUri(), "r",
+                    cancellationSignal)) {
+                if (pfd == null) {
+                    continue;  // keep adding succeeded fonts.
+                }
+                final Font platformFont = new Font.Builder(pfd)
+                        .setWeight(font.getWeight())
+                        .setSlant(font.isItalic() ? FontStyle.FONT_SLANT_ITALIC
+                                : FontStyle.FONT_SLANT_UPRIGHT)
+                        .setTtcIndex(font.getTtcIndex())
+                        .build();  // TODO: font variation settings?
+                if (familyBuilder == null) {
+                    familyBuilder = new FontFamily.Builder(platformFont);
+                } else {
+                    familyBuilder.addFont(platformFont);
+                }
+            } catch (IOException e) {
+                Log.w(TAG, "Font load failed", e);
+                // keep adding succeeded fonts.
+            }
+        }
+        if (familyBuilder == null) {
+            return null;
+        }
+        final FontFamily family = familyBuilder.build();
+        return family;
+    }
+
+    @Nullable
+    @Override
+    public Typeface createFromFontInfoWithFallback(@NonNull Context context,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull List<FontsContractCompat.FontInfo[]> fonts, int style) {
+        final ContentResolver resolver = context.getContentResolver();
+        try {
+            final FontFamily family = getFontFamily(cancellationSignal, fonts.get(0), resolver);
+            if (family == null) return null;  // No font is added. Give up.
+            Typeface.CustomFallbackBuilder builder = new Typeface.CustomFallbackBuilder(family);
+            for (int i = 1 /* because 0 is handled above */; i < fonts.size(); i++) {
+                final FontFamily fallbackFamily = getFontFamily(cancellationSignal, fonts.get(i),
+                        resolver);
+                if (fallbackFamily != null) {
+                    builder.addCustomFallback(fallbackFamily);
+                } else {
+                    if (TypefaceCompat.DOWNLOADABLE_FALLBACK_DEBUG) {
+                        // TODO(b/352510076): Do we need to handle this somehow?
+                        throw new IllegalStateException("Font load failed");
+                    }
+                }
+            }
+            return builder.setStyle(findBaseFont(family, style).getStyle()).build();
+        } catch (Exception e) {
+            Log.w(TAG, "Font load failed", e);
             return null;
         }
     }
@@ -154,6 +196,7 @@
                     .setStyle(findBaseFont(family, style).getStyle())
                     .build();
         } catch (Exception e) {
+            Log.w(TAG, "Font load failed", e);
             return null;
         }
     }
@@ -175,6 +218,7 @@
                     .setStyle(font.getStyle())
                     .build();
         } catch (Exception e) {
+            Log.w(TAG, "Font load failed", e);
             return null;
         }
     }
diff --git a/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java b/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java
index 2ed7eb3..15e4e20 100644
--- a/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java
+++ b/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java
@@ -27,6 +27,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
 import androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry;
@@ -36,6 +37,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Field;
+import java.util.List;
 
 /**
  * Implementation of the Typeface compat methods for API 14 and above.
@@ -54,6 +56,7 @@
 
     private interface StyleExtractor<T> {
         int getWeight(T t);
+
         boolean isItalic(T t);
     }
 
@@ -153,6 +156,15 @@
         }
     }
 
+    @Nullable
+    @RequiresApi(29)
+    public Typeface createFromFontInfoWithFallback(@NonNull Context context,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull List<FontInfo[]> fonts, int style) {
+        throw new IllegalStateException(
+                "createFromFontInfoWithFallback must only be called on API 29+");
+    }
+
     private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry, int style) {
         return findBestFont(entry.getEntries(), style, new StyleExtractor<FontFileResourceEntry>() {
             @Override
diff --git a/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java b/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java
index 3eb185e..4d4ad3b 100644
--- a/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java
@@ -29,7 +29,6 @@
 import android.view.View;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -438,49 +437,40 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setHotspot(Drawable drawable, float x, float y) {
             drawable.setHotspot(x, y);
         }
 
-        @DoNotInline
         static void setTint(Drawable drawable, int tintColor) {
             drawable.setTint(tintColor);
         }
 
-        @DoNotInline
         static void setTintList(Drawable drawable, ColorStateList tint) {
             drawable.setTintList(tint);
         }
 
-        @DoNotInline
         static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) {
             drawable.setTintMode(tintMode);
         }
 
-        @DoNotInline
         static void applyTheme(Drawable drawable, Resources.Theme t) {
             drawable.applyTheme(t);
         }
 
-        @DoNotInline
         static boolean canApplyTheme(Drawable drawable) {
             return drawable.canApplyTheme();
         }
 
-        @DoNotInline
         static ColorFilter getColorFilter(Drawable drawable) {
             return drawable.getColorFilter();
         }
 
-        @DoNotInline
         static void inflate(Drawable drawable, Resources r, XmlPullParser parser,
                 AttributeSet attrs, Resources.Theme theme)
                 throws XmlPullParserException, IOException {
             drawable.inflate(r, parser, attrs, theme);
         }
 
-        @DoNotInline
         static void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) {
             drawable.setHotspotBounds(left, top, right, bottom);
         }
@@ -492,12 +482,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
             return drawable.setLayoutDirection(layoutDirection);
         }
 
-        @DoNotInline
         static int getLayoutDirection(Drawable drawable) {
             return drawable.getLayoutDirection();
         }
diff --git a/core/core/src/main/java/androidx/core/graphics/drawable/IconCompat.java b/core/core/src/main/java/androidx/core/graphics/drawable/IconCompat.java
index c31083f..881d8ec 100644
--- a/core/core/src/main/java/androidx/core/graphics/drawable/IconCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/drawable/IconCompat.java
@@ -49,7 +49,6 @@
 import android.util.Log;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.IdRes;
 import androidx.annotation.IntDef;
@@ -1048,22 +1047,18 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static String getResPackage(Object icon) {
             return ((Icon) icon).getResPackage();
         }
 
-        @DoNotInline
         static int getType(Object icon) {
             return ((Icon) icon).getType();
         }
 
-        @DoNotInline
         static int getResId(Object icon) {
             return ((Icon) icon).getResId();
         }
 
-        @DoNotInline
         static Uri getUri(Object icon) {
             return ((Icon) icon).getUri();
         }
@@ -1075,13 +1070,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Drawable createAdaptiveIconDrawable(Drawable backgroundDrawable,
                 Drawable foregroundDrawable) {
             return new AdaptiveIconDrawable(backgroundDrawable, foregroundDrawable);
         }
 
-        @DoNotInline
         static Icon createWithAdaptiveBitmap(Bitmap bits) {
             return Icon.createWithAdaptiveBitmap(bits);
         }
@@ -1093,7 +1086,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Icon createWithAdaptiveBitmapContentUri(Uri uri) {
             return Icon.createWithAdaptiveBitmapContentUri(uri);
         }
@@ -1238,7 +1230,6 @@
          * Returns {@code null} if the uri cannot be gotten.
          */
         @Nullable
-        @DoNotInline
         static Uri getUri(@NonNull Object icon) {
             if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.getUri(icon);
@@ -1258,7 +1249,6 @@
             }
         }
 
-        @DoNotInline
         static Icon toIcon(IconCompat iconCompat, Context context) {
             Icon icon;
             switch (iconCompat.mType) {
@@ -1320,7 +1310,6 @@
             return icon;
         }
 
-        @DoNotInline
         static Drawable loadDrawable(Icon icon, Context context) {
             return icon.loadDrawable(context);
         }
diff --git a/core/core/src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java b/core/core/src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java
index e8e1bbf3..0244fbc 100644
--- a/core/core/src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java
@@ -24,7 +24,6 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -304,19 +303,16 @@
         }
 
         @RequiresPermission(Manifest.permission.USE_FINGERPRINT)
-        @DoNotInline
         static boolean hasEnrolledFingerprints(Object fingerprintManager) {
             return ((FingerprintManager) fingerprintManager).hasEnrolledFingerprints();
         }
 
         @RequiresPermission(Manifest.permission.USE_FINGERPRINT)
-        @DoNotInline
         static boolean isHardwareDetected(Object fingerprintManager) {
             return ((FingerprintManager) fingerprintManager).isHardwareDetected();
         }
 
         @RequiresPermission(Manifest.permission.USE_FINGERPRINT)
-        @DoNotInline
         static void authenticate(Object fingerprintManager, Object crypto,
                 CancellationSignal cancel, int flags, Object callback, Handler handler) {
             ((FingerprintManager) fingerprintManager).authenticate(
@@ -324,13 +320,11 @@
                     (FingerprintManager.AuthenticationCallback) callback, handler);
         }
 
-        @DoNotInline
         static FingerprintManager.CryptoObject getCryptoObject(Object authenticationResult) {
             return ((FingerprintManager.AuthenticationResult) authenticationResult)
                     .getCryptoObject();
         }
 
-        @DoNotInline
         public static FingerprintManager getFingerprintManagerOrNull(Context context) {
             if (Build.VERSION.SDK_INT == 23) {
                 return context.getSystemService(FingerprintManager.class);
@@ -342,7 +336,6 @@
             }
         }
 
-        @DoNotInline
         public static FingerprintManager.CryptoObject wrapCryptoObject(CryptoObject cryptoObject) {
             if (cryptoObject == null) {
                 return null;
@@ -357,7 +350,6 @@
             }
         }
 
-        @DoNotInline
         public static CryptoObject unwrapCryptoObject(Object cryptoObjectObj) {
             FingerprintManager.CryptoObject cryptoObject =
                     (FingerprintManager.CryptoObject) cryptoObjectObj;
diff --git a/core/core/src/main/java/androidx/core/location/GnssStatusWrapper.java b/core/core/src/main/java/androidx/core/location/GnssStatusWrapper.java
index 86454f9..e851891 100644
--- a/core/core/src/main/java/androidx/core/location/GnssStatusWrapper.java
+++ b/core/core/src/main/java/androidx/core/location/GnssStatusWrapper.java
@@ -22,7 +22,6 @@
 
 import android.location.GnssStatus;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.util.Preconditions;
@@ -141,12 +140,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static float getCarrierFrequencyHz(GnssStatus gnssStatus, int satelliteIndex) {
             return gnssStatus.getCarrierFrequencyHz(satelliteIndex);
         }
 
-        @DoNotInline
         static boolean hasCarrierFrequencyHz(GnssStatus gnssStatus, int satelliteIndex) {
             return gnssStatus.hasCarrierFrequencyHz(satelliteIndex);
         }
@@ -158,12 +155,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean hasBasebandCn0DbHz(GnssStatus gnssStatus, int satelliteIndex) {
             return gnssStatus.hasBasebandCn0DbHz(satelliteIndex);
         }
 
-        @DoNotInline
         static float getBasebandCn0DbHz(GnssStatus gnssStatus, int satelliteIndex) {
             return gnssStatus.getBasebandCn0DbHz(satelliteIndex);
         }
diff --git a/core/core/src/main/java/androidx/core/location/LocationCompat.java b/core/core/src/main/java/androidx/core/location/LocationCompat.java
index a0d21f3..49c4c79 100644
--- a/core/core/src/main/java/androidx/core/location/LocationCompat.java
+++ b/core/core/src/main/java/androidx/core/location/LocationCompat.java
@@ -23,7 +23,6 @@
 import android.os.Build.VERSION;
 import android.os.Bundle;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.FloatRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -535,43 +534,35 @@
         private Api34Impl() {
         }
 
-        @DoNotInline
         static double getMslAltitudeMeters(Location location) {
             return location.getMslAltitudeMeters();
         }
 
-        @DoNotInline
         static void setMslAltitudeMeters(Location location, double mslAltitudeMeters) {
             location.setMslAltitudeMeters(mslAltitudeMeters);
         }
 
-        @DoNotInline
         static boolean hasMslAltitude(Location location) {
             return location.hasMslAltitude();
         }
 
-        @DoNotInline
         static void removeMslAltitude(Location location) {
             location.removeMslAltitude();
         }
 
-        @DoNotInline
         static float getMslAltitudeAccuracyMeters(Location location) {
             return location.getMslAltitudeAccuracyMeters();
         }
 
-        @DoNotInline
         static void setMslAltitudeAccuracyMeters(Location location,
                 float mslAltitudeAccuracyMeters) {
             location.setMslAltitudeAccuracyMeters(mslAltitudeAccuracyMeters);
         }
 
-        @DoNotInline
         static boolean hasMslAltitudeAccuracy(Location location) {
             return location.hasMslAltitudeAccuracy();
         }
 
-        @DoNotInline
         static void removeMslAltitudeAccuracy(Location location) {
             location.removeMslAltitudeAccuracy();
         }
@@ -582,17 +573,14 @@
 
         private Api33Impl() {}
 
-        @DoNotInline
         static void removeVerticalAccuracy(Location location) {
             location.removeVerticalAccuracy();
         }
 
-        @DoNotInline
         static void removeSpeedAccuracy(Location location) {
             location.removeSpeedAccuracy();
         }
 
-        @DoNotInline
         static void removeBearingAccuracy(Location location) {
             location.removeBearingAccuracy();
         }
@@ -603,7 +591,6 @@
 
         private Api29Impl() {}
 
-        @DoNotInline
         static void removeVerticalAccuracy(Location location) {
             if (!location.hasVerticalAccuracy()) {
                 return;
@@ -615,7 +602,6 @@
             location.setElapsedRealtimeUncertaintyNanos(elapsedRealtimeUncertaintyNs);
         }
 
-        @DoNotInline
         static void removeSpeedAccuracy(Location location) {
             if (!location.hasSpeedAccuracy()) {
                 return;
@@ -627,7 +613,6 @@
             location.setElapsedRealtimeUncertaintyNanos(elapsedRealtimeUncertaintyNs);
         }
 
-        @DoNotInline
         static void removeBearingAccuracy(Location location) {
             if (!location.hasBearingAccuracy()) {
                 return;
@@ -645,7 +630,6 @@
 
         private Api28Impl() {}
 
-        @DoNotInline
         static void removeVerticalAccuracy(Location location) {
             if (!location.hasVerticalAccuracy()) {
                 return;
@@ -702,7 +686,6 @@
             }
         }
 
-        @DoNotInline
         static void removeSpeedAccuracy(Location location) {
             if (!location.hasSpeedAccuracy()) {
                 return;
@@ -759,7 +742,6 @@
             }
         }
 
-        @DoNotInline
         static void removeBearingAccuracy(Location location) {
             if (!location.hasBearingAccuracy()) {
                 return;
@@ -823,22 +805,18 @@
         private Api26Impl() {
         }
 
-        @DoNotInline
         static boolean hasVerticalAccuracy(Location location) {
             return location.hasVerticalAccuracy();
         }
 
-        @DoNotInline
         static float getVerticalAccuracyMeters(Location location) {
             return location.getVerticalAccuracyMeters();
         }
 
-        @DoNotInline
         static void setVerticalAccuracyMeters(Location location, float verticalAccuracyM) {
             location.setVerticalAccuracyMeters(verticalAccuracyM);
         }
 
-        @DoNotInline
         static void removeVerticalAccuracy(Location location) {
             try {
                 byte fieldsMask = getFieldsMaskField().getByte(location);
@@ -851,22 +829,18 @@
             }
         }
 
-        @DoNotInline
         static boolean hasSpeedAccuracy(Location location) {
             return location.hasSpeedAccuracy();
         }
 
-        @DoNotInline
         static float getSpeedAccuracyMetersPerSecond(Location location) {
             return location.getSpeedAccuracyMetersPerSecond();
         }
 
-        @DoNotInline
         static void setSpeedAccuracyMetersPerSecond(Location location, float speedAccuracyMps) {
             location.setSpeedAccuracyMetersPerSecond(speedAccuracyMps);
         }
 
-        @DoNotInline
         static void removeSpeedAccuracy(Location location) {
             try {
                 byte fieldsMask = getFieldsMaskField().getByte(location);
@@ -883,22 +857,18 @@
             }
         }
 
-        @DoNotInline
         static boolean hasBearingAccuracy(Location location) {
             return location.hasBearingAccuracy();
         }
 
-        @DoNotInline
         static float getBearingAccuracyDegrees(Location location) {
             return location.getBearingAccuracyDegrees();
         }
 
-        @DoNotInline
         static void setBearingAccuracyDegrees(Location location, float bearingAccuracyD) {
             location.setBearingAccuracyDegrees(bearingAccuracyD);
         }
 
-        @DoNotInline
         static void removeBearingAccuracy(Location location) {
             try {
                 byte fieldsMask = getFieldsMaskField().getByte(location);
@@ -1006,7 +976,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isMock(Location location) {
             return location.isMock();
         }
diff --git a/core/core/src/main/java/androidx/core/location/LocationManagerCompat.java b/core/core/src/main/java/androidx/core/location/LocationManagerCompat.java
index c53c2e4..f735336 100644
--- a/core/core/src/main/java/androidx/core/location/LocationManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/location/LocationManagerCompat.java
@@ -46,7 +46,6 @@
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -1195,13 +1194,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean hasProvider(LocationManager locationManager, @NonNull String provider) {
             return locationManager.hasProvider(provider);
         }
 
         @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
-        @DoNotInline
         static void requestLocationUpdates(LocationManager locationManager,
                 @NonNull String provider, @NonNull LocationRequest locationRequest,
                 @NonNull Executor executor, @NonNull LocationListener listener) {
@@ -1209,7 +1206,6 @@
         }
 
         @RequiresPermission(ACCESS_FINE_LOCATION)
-        @DoNotInline
         static boolean registerGnssMeasurementsCallback(@NonNull LocationManager locationManager,
                 @NonNull Executor executor, @NonNull GnssMeasurementsEvent.Callback callback) {
             return locationManager.registerGnssMeasurementsCallback(executor, callback);
@@ -1226,7 +1222,6 @@
         }
 
         @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
-        @DoNotInline
         static void getCurrentLocation(LocationManager locationManager, @NonNull String provider,
                 @Nullable CancellationSignal cancellationSignal,
                 @NonNull Executor executor, final @NonNull Consumer<Location> consumer) {
@@ -1237,7 +1232,6 @@
         }
 
         @SuppressWarnings("JavaReflectionMemberAccess")
-        @DoNotInline
         public static boolean tryRequestLocationUpdates(LocationManager locationManager,
                 String provider, LocationRequestCompat locationRequest, Executor executor,
                 LocationListenerCompat listener) {
@@ -1274,7 +1268,6 @@
         }
 
         @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
-        @DoNotInline
         public static boolean registerGnssStatusCallback(LocationManager locationManager,
                 Handler baseHandler, Executor executor, GnssStatusCompat.Callback callback) {
             synchronized (GnssListenersHolder.sGnssStatusListeners) {
@@ -1300,17 +1293,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isLocationEnabled(LocationManager locationManager) {
             return locationManager.isLocationEnabled();
         }
 
-        @DoNotInline
         static String getGnssHardwareModelName(LocationManager locationManager) {
             return locationManager.getGnssHardwareModelName();
         }
 
-        @DoNotInline
         static int getGnssYearOfHardware(LocationManager locationManager) {
             return locationManager.getGnssYearOfHardware();
         }
@@ -1327,7 +1317,6 @@
         @SuppressLint("BanUncheckedReflection")
         @SuppressWarnings("JavaReflectionMemberAccess")
         @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
-        @DoNotInline
         static boolean tryRequestLocationUpdates(LocationManager locationManager,
                 String provider, LocationRequestCompat locationRequest,
                 LocationListenerTransport transport) {
@@ -1366,7 +1355,6 @@
 
         @SuppressLint("BanUncheckedReflection")
         @SuppressWarnings("JavaReflectionMemberAccess")
-        @DoNotInline
         static boolean tryRequestLocationUpdates(LocationManager locationManager, String provider,
                 LocationRequestCompat locationRequest, LocationListenerCompat listener,
                 Looper looper) {
@@ -1409,27 +1397,23 @@
         }
 
         @RequiresPermission(ACCESS_FINE_LOCATION)
-        @DoNotInline
         static boolean registerGnssMeasurementsCallback(@NonNull LocationManager locationManager,
                 @NonNull GnssMeasurementsEvent.Callback callback) {
             return locationManager.registerGnssMeasurementsCallback(callback);
         }
 
         @RequiresPermission(ACCESS_FINE_LOCATION)
-        @DoNotInline
         static boolean registerGnssMeasurementsCallback(@NonNull LocationManager locationManager,
                 @NonNull GnssMeasurementsEvent.Callback callback, @NonNull Handler handler) {
             return locationManager.registerGnssMeasurementsCallback(callback, handler);
         }
 
-        @DoNotInline
         static void unregisterGnssMeasurementsCallback(@NonNull LocationManager locationManager,
                 @NonNull GnssMeasurementsEvent.Callback callback) {
             locationManager.unregisterGnssMeasurementsCallback(callback);
         }
 
         @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
-        @DoNotInline
         static boolean registerGnssStatusCallback(LocationManager locationManager,
                 Handler baseHandler, Executor executor, GnssStatusCompat.Callback callback) {
             Preconditions.checkArgument(baseHandler != null);
@@ -1454,7 +1438,6 @@
             }
         }
 
-        @DoNotInline
         static void unregisterGnssStatusCallback(LocationManager locationManager, Object callback) {
             if (callback instanceof PreRGnssStatusTransport) {
                 ((PreRGnssStatusTransport) callback).unregister();
diff --git a/core/core/src/main/java/androidx/core/location/LocationRequestCompat.java b/core/core/src/main/java/androidx/core/location/LocationRequestCompat.java
index ffe36fc..2e0872b 100644
--- a/core/core/src/main/java/androidx/core/location/LocationRequestCompat.java
+++ b/core/core/src/main/java/androidx/core/location/LocationRequestCompat.java
@@ -24,7 +24,6 @@
 import android.location.LocationRequest;
 import android.os.Build.VERSION;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.FloatRange;
 import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
@@ -504,7 +503,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         public static LocationRequest toLocationRequest(LocationRequestCompat obj) {
             return new LocationRequest.Builder(obj.getIntervalMillis())
                     .setQuality(obj.getQuality())
diff --git a/core/core/src/main/java/androidx/core/net/ConnectivityManagerCompat.java b/core/core/src/main/java/androidx/core/net/ConnectivityManagerCompat.java
index f678649..b1eba5e 100644
--- a/core/core/src/main/java/androidx/core/net/ConnectivityManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/net/ConnectivityManagerCompat.java
@@ -25,7 +25,6 @@
 import android.net.NetworkInfo;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -142,7 +141,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getRestrictBackgroundStatus(ConnectivityManager connectivityManager) {
             return connectivityManager.getRestrictBackgroundStatus();
         }
diff --git a/core/core/src/main/java/androidx/core/net/TrafficStatsCompat.java b/core/core/src/main/java/androidx/core/net/TrafficStatsCompat.java
index 2ce60ed..ee3f6b7 100644
--- a/core/core/src/main/java/androidx/core/net/TrafficStatsCompat.java
+++ b/core/core/src/main/java/androidx/core/net/TrafficStatsCompat.java
@@ -20,7 +20,6 @@
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -175,12 +174,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
             TrafficStats.tagDatagramSocket(socket);
         }
 
-        @DoNotInline
         static void untagDatagramSocket(DatagramSocket socket) throws SocketException {
             TrafficStats.untagDatagramSocket(socket);
         }
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.kt b/core/core/src/main/java/androidx/core/os/BuildCompat.kt
index 2e3d082..5b8144e 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.kt
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.kt
@@ -19,7 +19,6 @@
 import android.os.Build
 import android.os.ext.SdkExtensions
 import androidx.annotation.ChecksSdkIntAtLeast
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
@@ -342,7 +341,6 @@
     @RequiresApi(30)
     private object Api30Impl {
 
-        @DoNotInline
         fun getExtensionVersion(extension: Int): Int {
             return SdkExtensions.getExtensionVersion(extension)
         }
diff --git a/core/core/src/main/java/androidx/core/os/BundleCompat.java b/core/core/src/main/java/androidx/core/os/BundleCompat.java
index 3038657..4e47124 100644
--- a/core/core/src/main/java/androidx/core/os/BundleCompat.java
+++ b/core/core/src/main/java/androidx/core/os/BundleCompat.java
@@ -23,7 +23,6 @@
 import android.os.Parcelable;
 import android.util.SparseArray;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -251,31 +250,26 @@
             // This class is non-instantiable.
         }
 
-        @DoNotInline
         static <T> T getParcelable(@NonNull Bundle in, @Nullable String key,
                 @NonNull Class<T> clazz) {
             return in.getParcelable(key, clazz);
         }
 
-        @DoNotInline
         static <T> T[] getParcelableArray(@NonNull Bundle in, @Nullable String key,
                 @NonNull Class<T> clazz) {
             return in.getParcelableArray(key, clazz);
         }
 
-        @DoNotInline
         static <T> ArrayList<T> getParcelableArrayList(@NonNull Bundle in, @Nullable String key,
                 @NonNull Class<? extends T> clazz) {
             return in.getParcelableArrayList(key, clazz);
         }
 
-        @DoNotInline
         static <T> SparseArray<T> getSparseParcelableArray(@NonNull Bundle in, @Nullable String key,
                 @NonNull Class<? extends T> clazz) {
             return in.getSparseParcelableArray(key, clazz);
         }
 
-        @DoNotInline
         static <T extends Serializable> T getSerializable(@NonNull Bundle in, @Nullable String key,
                 @NonNull Class<T> clazz) {
             return in.getSerializable(key, clazz);
diff --git a/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java b/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java
index d4438ce..a1360cc 100644
--- a/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java
+++ b/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java
@@ -21,7 +21,6 @@
 import android.content.res.Configuration;
 import android.os.LocaleList;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -70,12 +69,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static android.os.LocaleList getLocales(Configuration configuration) {
             return configuration.getLocales();
         }
 
-        @DoNotInline
         static void setLocales(
                 @NonNull Configuration configuration, @NonNull LocaleListCompat locales) {
             configuration.setLocales((LocaleList) locales.unwrap());
diff --git a/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java b/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java
index cc38d10..e49fe27 100644
--- a/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java
+++ b/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java
@@ -19,7 +19,6 @@
 import android.os.Build;
 import android.os.Environment;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -74,7 +73,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static String getExternalStorageState(File path) {
             return Environment.getExternalStorageState(path);
         }
diff --git a/core/core/src/main/java/androidx/core/os/LocaleListCompat.java b/core/core/src/main/java/androidx/core/os/LocaleListCompat.java
index f742a7e9..b24537f 100644
--- a/core/core/src/main/java/androidx/core/os/LocaleListCompat.java
+++ b/core/core/src/main/java/androidx/core/os/LocaleListCompat.java
@@ -19,7 +19,6 @@
 import android.os.Build;
 import android.os.LocaleList;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -263,7 +262,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean matchesLanguageAndScript(@NonNull Locale supported,
                 @NonNull Locale desired) {
             if (supported.equals(desired)) {
@@ -306,7 +304,6 @@
             return false;
         }
 
-        @DoNotInline
         static Locale forLanguageTag(String languageTag) {
             return Locale.forLanguageTag(languageTag);
         }
@@ -334,17 +331,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static LocaleList createLocaleList(Locale... list) {
             return new LocaleList(list);
         }
 
-        @DoNotInline
         static LocaleList getAdjustedDefault() {
             return LocaleList.getAdjustedDefault();
         }
 
-        @DoNotInline
         static LocaleList getDefault() {
             return LocaleList.getDefault();
         }
diff --git a/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java b/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java
index 94ebde2..09a6189 100644
--- a/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java
+++ b/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java
@@ -18,7 +18,6 @@
 
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -277,7 +276,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static String getScript(Locale locale) {
             return locale.getScript();
         }
diff --git a/core/core/src/main/java/androidx/core/os/MessageCompat.java b/core/core/src/main/java/androidx/core/os/MessageCompat.java
index 25b04cc..7a09d89 100644
--- a/core/core/src/main/java/androidx/core/os/MessageCompat.java
+++ b/core/core/src/main/java/androidx/core/os/MessageCompat.java
@@ -22,7 +22,6 @@
 import android.os.Message;
 import android.view.View;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -120,12 +119,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isAsynchronous(Message message) {
             return message.isAsynchronous();
         }
 
-        @DoNotInline
         static void setAsynchronous(Message message, boolean async) {
             message.setAsynchronous(async);
         }
diff --git a/core/core/src/main/java/androidx/core/os/ParcelCompat.java b/core/core/src/main/java/androidx/core/os/ParcelCompat.java
index 52de196..425a77ac 100644
--- a/core/core/src/main/java/androidx/core/os/ParcelCompat.java
+++ b/core/core/src/main/java/androidx/core/os/ParcelCompat.java
@@ -23,7 +23,6 @@
 import android.os.Parcelable;
 import android.util.SparseArray;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -399,13 +398,11 @@
             // This class is non-instantiable.
         }
 
-        @DoNotInline
         static <T extends Parcelable> List<T> readParcelableList(@NonNull Parcel in,
                 @NonNull List<T> list, @Nullable ClassLoader cl) {
             return in.readParcelableList(list, cl);
         }
 
-        @DoNotInline
         static void writeBoolean(@NonNull Parcel parcel, boolean val) {
             parcel.writeBoolean(val);
         }
@@ -417,7 +414,6 @@
             // This class is non-instantiable.
         }
 
-        @DoNotInline
         static Parcelable.Creator<?> readParcelableCreator(@NonNull Parcel in,
                 @Nullable ClassLoader loader) {
             return in.readParcelableCreator(loader);
@@ -430,66 +426,55 @@
             // This class is non-instantiable.
         }
 
-        @DoNotInline
         static <T extends Serializable> T readSerializable(@NonNull Parcel in,
                 @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
             return in.readSerializable(loader, clazz);
         }
 
-        @DoNotInline
         static <T extends Parcelable> T readParcelable(@NonNull Parcel in,
                 @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
             return in.readParcelable(loader, clazz);
         }
 
-        @DoNotInline
         static <T> Parcelable.Creator<T> readParcelableCreator(Parcel in, ClassLoader loader,
                 Class<T> clazz) {
             return in.readParcelableCreator(loader, clazz);
         }
 
-        @DoNotInline
         static <T> T[] readParcelableArray(@NonNull Parcel in, @Nullable ClassLoader loader,
                 @NonNull Class<T> clazz) {
             return in.readParcelableArray(loader, clazz);
         }
 
-        @DoNotInline
         static <T> List<T> readParcelableList(@NonNull Parcel in, @NonNull List<T> list,
                 @Nullable ClassLoader cl, @NonNull Class<T> clazz) {
             return in.readParcelableList(list, cl, clazz);
         }
 
-        @DoNotInline
         static <T> void readList(@NonNull Parcel in, @NonNull List<? super T> outVal,
                 @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
             in.readList(outVal, loader, clazz);
         }
 
-        @DoNotInline
         static <T> ArrayList<T> readArrayList(Parcel in, ClassLoader loader,
                 Class<? extends T> clazz) {
             return in.readArrayList(loader, clazz);
         }
 
-        @DoNotInline
         static <T> T[] readArray(Parcel in, ClassLoader loader, Class<T> clazz) {
             return in.readArray(loader, clazz);
         }
 
-        @DoNotInline
         static <T> SparseArray<T> readSparseArray(Parcel in, ClassLoader loader,
                 Class<? extends T> clazz) {
             return in.readSparseArray(loader, clazz);
         }
 
-        @DoNotInline
         static <K, V> void readMap(Parcel in, Map<? super K, ? super V> outVal,
                 ClassLoader loader, Class<K> clazzKey, Class<V> clazzValue) {
             in.readMap(outVal, loader, clazzKey, clazzValue);
         }
 
-        @DoNotInline
         static <V, K> HashMap<K, V> readHashMap(Parcel in, ClassLoader loader,
                 Class<? extends K> clazzKey, Class<? extends V> clazzValue) {
             return in.readHashMap(loader, clazzKey, clazzValue);
diff --git a/core/core/src/main/java/androidx/core/os/Profiling.kt b/core/core/src/main/java/androidx/core/os/Profiling.kt
new file mode 100644
index 0000000..c13e934
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/os/Profiling.kt
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("Profiling")
+
+package androidx.core.os
+
+import android.content.Context
+import android.os.Build
+import android.os.Bundle
+import android.os.CancellationSignal
+import android.os.ProfilingManager
+import android.os.ProfilingResult
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+
+/** Helpers providing simple wrapper APIs for {@link ProfilingManager}. */
+
+// Begin section: Keep in sync with {@link ProfilingManager}
+private const val KEY_DURATION_MS: String = "KEY_DURATION_MS"
+private const val KEY_SAMPLING_INTERVAL_BYTES: String = "KEY_SAMPLING_INTERVAL_BYTES"
+private const val KEY_TRACK_JAVA_ALLOCATIONS: String = "KEY_TRACK_JAVA_ALLOCATIONS"
+private const val KEY_FREQUENCY_HZ: String = "KEY_FREQUENCY_HZ"
+private const val KEY_SIZE_KB: String = "KEY_SIZE_KB"
+private const val KEY_BUFFER_FILL_POLICY: String = "KEY_BUFFER_FILL_POLICY"
+
+private const val VALUE_BUFFER_FILL_POLICY_DISCARD: Int = 1
+private const val VALUE_BUFFER_FILL_POLICY_RING_BUFFER: Int = 2
+
+// End section: Keep in sync with ProfilingManager
+
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+enum class BufferFillPolicy(val value: Int) {
+    DISCARD(VALUE_BUFFER_FILL_POLICY_DISCARD),
+    RING_BUFFER(VALUE_BUFFER_FILL_POLICY_RING_BUFFER),
+}
+
+/** Obtain a flow to be called with all profiling results for this UID. */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+fun registerForAllProfilingResults(context: Context): Flow<ProfilingResult> = callbackFlow {
+    val listener = Consumer<ProfilingResult> { result -> trySend(result) }
+
+    val service = context.getSystemService(ProfilingManager::class.java)
+    service.registerForAllProfilingResults({ runnable -> runnable.run() }, listener)
+
+    awaitClose { service.unregisterForAllProfilingResults(listener) }
+}
+
+/** Register a listener to be called with all profiling results for this UID. */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+fun registerForAllProfilingResults(
+    context: Context,
+    executor: Executor,
+    listener: Consumer<ProfilingResult>
+) {
+    val service = context.getSystemService(ProfilingManager::class.java)
+    service.registerForAllProfilingResults(executor, listener)
+}
+
+/** Unregister a listener that was to be called for all profiling results. */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+fun unregisterForAllProfilingResults(context: Context, listener: Consumer<ProfilingResult>) {
+    val service = context.getSystemService(ProfilingManager::class.java)
+    service.unregisterForAllProfilingResults(listener)
+}
+
+/**
+ * Obtain a builder to create a request for a java heap dump from {@link ProfilingManager}.
+ *
+ * Request is submitted by calling {@link JavaHeapDumpRequestBuilder#request}.
+ */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+fun requestJavaHeapDump(): JavaHeapDumpRequestBuilder {
+    return JavaHeapDumpRequestBuilder()
+}
+
+/**
+ * Obtain a builder to create a request for a heap profile from {@link ProfilingManager}.
+ *
+ * Request is submitted by calling {@link HeapProfileRequestBuilder#request}.
+ */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+fun requestHeapProfile(): HeapProfileRequestBuilder {
+    return HeapProfileRequestBuilder()
+}
+
+/**
+ * Obtain a builder to create a request for stack sampling from {@link ProfilingManager}.
+ *
+ * Request is submitted by calling {@link StackSamplingRequestBuilder#request}.
+ */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+fun requestStackSampling(): StackSamplingRequestBuilder {
+    return StackSamplingRequestBuilder()
+}
+
+/**
+ * Obtain a builder to create a request for a system trace from {@link ProfilingManager}.
+ *
+ * Request is submitted by calling {@link SystemTraceRequestBuilder#request}.
+ */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+fun requestSystemTrace(): SystemTraceRequestBuilder {
+    return SystemTraceRequestBuilder()
+}
+
+/** Base class for request builders. */
+@SuppressWarnings("StaticFinalBuilder", "MissingBuildMethod", "TopLevelBuilder")
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+abstract class RequestBuilder<T : RequestBuilder<T>> internal constructor() {
+    private var mTag: String? = null
+    private var mCancellationSignal: CancellationSignal? = null
+
+    /**
+     * Add data to help identify the output. The first 20 alphanumeric characters, plus dashes, will
+     * be lowercased and included in the output filename.
+     */
+    fun setTag(tag: String): T {
+        mTag = tag
+        return getThis()
+    }
+
+    /**
+     * Set a CancellationSignal to request cancellation of the requested trace. Results will be
+     * returned if available.
+     */
+    fun setCancellationSignal(cancellationSignal: CancellationSignal): T {
+        mCancellationSignal = cancellationSignal
+        return getThis()
+    }
+
+    /**
+     * Submit the profiling request with the provided executor and listener.
+     *
+     * If the executor and/or listener are null, and if no global listener and executor combinations
+     * are registered using {@link registerForAllProfilingResults}, the request will be dropped.
+     */
+    @SuppressWarnings("BuilderSetStyle")
+    fun request(context: Context, executor: Executor?, listener: Consumer<ProfilingResult>?) {
+        val service = context.getSystemService(ProfilingManager::class.java)
+        service.requestProfiling(
+            getProfilingType(),
+            getParams(),
+            mTag,
+            mCancellationSignal,
+            executor,
+            listener
+        )
+    }
+
+    @SuppressWarnings("HiddenAbstractMethod")
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    protected abstract fun getProfilingType(): Int
+
+    @SuppressWarnings("HiddenAbstractMethod")
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    protected abstract fun getThis(): T
+
+    @SuppressWarnings("HiddenAbstractMethod")
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    protected abstract fun getParams(): Bundle
+}
+
+/** Request builder for a java heap dump. */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+class JavaHeapDumpRequestBuilder internal constructor() :
+    RequestBuilder<JavaHeapDumpRequestBuilder>() {
+    private val mParams: Bundle = Bundle()
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getParams(): Bundle {
+        return mParams
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getProfilingType(): Int {
+        return ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getThis(): JavaHeapDumpRequestBuilder {
+        return this
+    }
+
+    /** Set the buffer size in kilobytes for this profiling request. */
+    fun setBufferSizeKb(bufferSizeKb: Int): JavaHeapDumpRequestBuilder {
+        mParams.putInt(KEY_SIZE_KB, bufferSizeKb)
+        return this
+    }
+}
+
+/** Request builder for a heap profile. */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+class HeapProfileRequestBuilder internal constructor() :
+    RequestBuilder<HeapProfileRequestBuilder>() {
+    private val mParams: Bundle = Bundle()
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getParams(): Bundle {
+        return mParams
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getProfilingType(): Int {
+        return ProfilingManager.PROFILING_TYPE_HEAP_PROFILE
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getThis(): HeapProfileRequestBuilder {
+        return this
+    }
+
+    /** Set the buffer size in kilobytes for this profiling request. */
+    fun setBufferSizeKb(bufferSizeKb: Int): HeapProfileRequestBuilder {
+        mParams.putInt(KEY_SIZE_KB, bufferSizeKb)
+        return this
+    }
+
+    /** Set the duration in milliseconds for this profiling request. */
+    fun setDurationMs(durationMs: Int): HeapProfileRequestBuilder {
+        mParams.putInt(KEY_DURATION_MS, durationMs)
+        return this
+    }
+
+    /** Set the sampling interval in bytes for this profiling request. */
+    fun setSamplingIntervalBytes(samplingIntervalBytes: Long): HeapProfileRequestBuilder {
+        mParams.putLong(KEY_SAMPLING_INTERVAL_BYTES, samplingIntervalBytes)
+        return this
+    }
+
+    /** Set whether to track Java allocations rather than native ones. */
+    fun setTrackJavaAllocations(traceJavaAllocations: Boolean): HeapProfileRequestBuilder {
+        mParams.putBoolean(KEY_TRACK_JAVA_ALLOCATIONS, traceJavaAllocations)
+        return this
+    }
+}
+
+/** Request builder for stack sampling. */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+class StackSamplingRequestBuilder internal constructor() :
+    RequestBuilder<StackSamplingRequestBuilder>() {
+    private val mParams: Bundle = Bundle()
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getParams(): Bundle {
+        return mParams
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getProfilingType(): Int {
+        return ProfilingManager.PROFILING_TYPE_STACK_SAMPLING
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getThis(): StackSamplingRequestBuilder {
+        return this
+    }
+
+    /** Set the buffer size in kilobytes for this profiling request. */
+    fun setBufferSizeKb(bufferSizeKb: Int): StackSamplingRequestBuilder {
+        mParams.putInt(KEY_SIZE_KB, bufferSizeKb)
+        return this
+    }
+
+    /** Set the duration in milliseconds for this profiling request. */
+    fun setDurationMs(durationMs: Int): StackSamplingRequestBuilder {
+        mParams.putInt(KEY_DURATION_MS, durationMs)
+        return this
+    }
+
+    /** Set the cpu sampling frequency. */
+    fun setSamplingFrequencyHz(samplingFrequencyHz: Int): StackSamplingRequestBuilder {
+        mParams.putInt(KEY_FREQUENCY_HZ, samplingFrequencyHz)
+        return this
+    }
+}
+
+/** Request builder for a system trace. */
+@RequiresApi(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+class SystemTraceRequestBuilder internal constructor() :
+    RequestBuilder<SystemTraceRequestBuilder>() {
+    private val mParams: Bundle = Bundle()
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getParams(): Bundle {
+        return mParams
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getProfilingType(): Int {
+        return ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE
+    }
+
+    @RestrictTo(RestrictTo.Scope.SUBCLASSES)
+    override fun getThis(): SystemTraceRequestBuilder {
+        return this
+    }
+
+    /** Set the buffer size in kilobytes for this profiling request. */
+    fun setBufferSizeKb(bufferSizeKb: Int): SystemTraceRequestBuilder {
+        mParams.putInt(KEY_SIZE_KB, bufferSizeKb)
+        return this
+    }
+
+    /** Set the duration in milliseconds for this profiling request. */
+    fun setDurationMs(durationMs: Int): SystemTraceRequestBuilder {
+        mParams.putInt(KEY_DURATION_MS, durationMs)
+        return this
+    }
+
+    /** Set the buffer fill policy. */
+    fun setBufferFillPolicy(bufferFillPolicy: BufferFillPolicy): SystemTraceRequestBuilder {
+        mParams.putInt(KEY_BUFFER_FILL_POLICY, bufferFillPolicy.value)
+        return this
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/os/TraceCompat.java b/core/core/src/main/java/androidx/core/os/TraceCompat.java
index 5c3ff7b..07074ec 100644
--- a/core/core/src/main/java/androidx/core/os/TraceCompat.java
+++ b/core/core/src/main/java/androidx/core/os/TraceCompat.java
@@ -17,7 +17,6 @@
 import android.os.Trace;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -189,22 +188,18 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isEnabled() {
             return Trace.isEnabled();
         }
 
-        @DoNotInline
         static void endAsyncSection(String methodName, int cookie) {
             Trace.endAsyncSection(methodName, cookie);
         }
 
-        @DoNotInline
         static void beginAsyncSection(String methodName, int cookie) {
             Trace.beginAsyncSection(methodName, cookie);
         }
 
-        @DoNotInline
         static void setCounter(String counterName, long counterValue) {
             Trace.setCounter(counterName, counterValue);
         }
diff --git a/core/core/src/main/java/androidx/core/os/UserManagerCompat.java b/core/core/src/main/java/androidx/core/os/UserManagerCompat.java
index 82b5167..596f69a 100644
--- a/core/core/src/main/java/androidx/core/os/UserManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/os/UserManagerCompat.java
@@ -20,7 +20,6 @@
 import android.os.Build;
 import android.os.UserManager;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -53,7 +52,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isUserUnlocked(Context context) {
             return context.getSystemService(UserManager.class).isUserUnlocked();
         }
diff --git a/core/core/src/main/java/androidx/core/provider/DocumentsContractCompat.java b/core/core/src/main/java/androidx/core/provider/DocumentsContractCompat.java
index 923f3c4..11a631f 100644
--- a/core/core/src/main/java/androidx/core/provider/DocumentsContractCompat.java
+++ b/core/core/src/main/java/androidx/core/provider/DocumentsContractCompat.java
@@ -24,7 +24,6 @@
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsProvider;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -241,39 +240,32 @@
 
     @RequiresApi(21)
     private static class DocumentsContractApi21Impl {
-        @DoNotInline
         static String getTreeDocumentId(Uri documentUri) {
             return DocumentsContract.getTreeDocumentId(documentUri);
         }
 
-        @DoNotInline
         public static Uri buildTreeDocumentUri(String authority, String documentId) {
             return DocumentsContract.buildTreeDocumentUri(authority, documentId);
         }
 
-        @DoNotInline
         static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) {
             return DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId);
         }
 
-        @DoNotInline
         static Uri buildChildDocumentsUri(String authority, String parentDocumentId) {
             return DocumentsContract.buildChildDocumentsUri(authority, parentDocumentId);
         }
 
-        @DoNotInline
         static Uri buildChildDocumentsUriUsingTree(Uri treeUri, String parentDocumentId) {
             return DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, parentDocumentId);
         }
 
-        @DoNotInline
         static Uri createDocument(ContentResolver content, Uri parentDocumentUri,
                 String mimeType, String displayName) throws FileNotFoundException {
             return DocumentsContract.createDocument(content, parentDocumentUri, mimeType,
                     displayName);
         }
 
-        @DoNotInline
         static Uri renameDocument(@NonNull ContentResolver content,
                 @NonNull Uri documentUri, @NonNull String displayName)
                 throws FileNotFoundException {
@@ -286,12 +278,10 @@
 
     @RequiresApi(24)
     private static class DocumentsContractApi24Impl {
-        @DoNotInline
         static boolean isTreeUri(@NonNull Uri uri) {
             return DocumentsContract.isTreeUri(uri);
         }
 
-        @DoNotInline
         static boolean removeDocument(ContentResolver content, Uri documentUri,
                 Uri parentDocumentUri) throws FileNotFoundException {
             return DocumentsContract.removeDocument(content, documentUri, parentDocumentUri);
diff --git a/core/core/src/main/java/androidx/core/provider/FontProvider.java b/core/core/src/main/java/androidx/core/provider/FontProvider.java
index 1559dff..387b0ad 100644
--- a/core/core/src/main/java/androidx/core/provider/FontProvider.java
+++ b/core/core/src/main/java/androidx/core/provider/FontProvider.java
@@ -37,32 +37,95 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
+import androidx.collection.LruCache;
 import androidx.core.content.res.FontResourcesParserCompat;
+import androidx.core.graphics.TypefaceCompat;
 import androidx.core.provider.FontsContractCompat.FontFamilyResult;
 import androidx.core.provider.FontsContractCompat.FontInfo;
+import androidx.tracing.Trace;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 
 class FontProvider {
     private FontProvider() {}
 
     @NonNull
     static FontFamilyResult getFontFamilyResult(@NonNull Context context,
-            @NonNull FontRequest request, @Nullable CancellationSignal cancellationSignal)
+            @NonNull List<FontRequest> requests, @Nullable CancellationSignal cancellationSignal)
             throws PackageManager.NameNotFoundException {
-        ProviderInfo providerInfo = getProvider(
-                context.getPackageManager(), request, context.getResources());
-        if (providerInfo == null) {
-            return FontFamilyResult.create(FontFamilyResult.STATUS_WRONG_CERTIFICATES, null);
-
+        if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+            Trace.beginSection("FontProvider.getFontFamilyResult");
         }
-        FontInfo[] fonts = query(
-                context, request, providerInfo.authority, cancellationSignal);
-        return FontFamilyResult.create(FontFamilyResult.STATUS_OK, fonts);
+        try {
+            ArrayList<FontInfo[]> queryResults = new ArrayList<>();
+            for (int i = 0; i < requests.size(); i++) {
+                FontRequest request = requests.get(i);
+                ProviderInfo providerInfo = getProvider(
+                        context.getPackageManager(), request, context.getResources());
+                if (providerInfo == null) {
+                    return FontFamilyResult.create(FontFamilyResult.STATUS_WRONG_CERTIFICATES,
+                            (FontInfo[]) null);
+
+                }
+                FontInfo[] fonts = query(
+                        context, request, providerInfo.authority, cancellationSignal);
+                queryResults.add(fonts);
+            }
+
+            return FontFamilyResult.create(FontFamilyResult.STATUS_OK, queryResults);
+        } finally {
+            if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                Trace.endSection();
+            }
+        }
+    }
+
+    private static class ProviderCacheKey {
+        String mAuthority;
+        String mPackageName;
+        List<List<byte[]>> mCertificates;
+
+        ProviderCacheKey(String authority, String packageName,
+                List<List<byte[]>> certificates) {
+            this.mAuthority = authority;
+            this.mPackageName = packageName;
+            this.mCertificates = certificates;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ProviderCacheKey)) return false;
+            ProviderCacheKey that = (ProviderCacheKey) o;
+            return Objects.equals(mAuthority, that.mAuthority) && Objects.equals(
+                    mPackageName, that.mPackageName) && Objects.equals(mCertificates,
+                    that.mCertificates);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mAuthority, mPackageName, mCertificates);
+        }
+    }
+
+    /**
+     * Cache of font providers.
+     * The realistic number of providers is 1, so we'll be generous and store up to 2.
+     */
+    private static final LruCache<ProviderCacheKey, ProviderInfo> sProviderCache =
+            new LruCache<>(2);
+
+    /**
+     * Clear font provider cache so that tests reset to a blank slate
+     */
+    @VisibleForTesting
+    static void clearProviderCache() {
+        sProviderCache.evictAll();
     }
 
     /**
@@ -76,36 +139,52 @@
             @Nullable Resources resources
     )
             throws PackageManager.NameNotFoundException {
-        String providerAuthority = request.getProviderAuthority();
-        ProviderInfo info = packageManager.resolveContentProvider(providerAuthority, 0);
-        if (info == null) {
-            throw new PackageManager.NameNotFoundException("No package found for authority: "
-                    + providerAuthority);
+        if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+            Trace.beginSection("FontProvider.getProvider");
         }
+        try {
+            List<List<byte[]>> requestCertificatesList = getCertificates(request, resources);
+            ProviderCacheKey cacheKey = new ProviderCacheKey(request.getProviderAuthority(),
+                    request.getProviderPackage(), requestCertificatesList);
+            ProviderInfo cachedPackageInfo = sProviderCache.get(cacheKey);
+            if (cachedPackageInfo != null) {
+                return cachedPackageInfo;
+            }
+            String providerAuthority = request.getProviderAuthority();
+            ProviderInfo info = packageManager.resolveContentProvider(providerAuthority, 0);
+            if (info == null) {
+                throw new PackageManager.NameNotFoundException("No package found for authority: "
+                        + providerAuthority);
+            }
 
-        if (!info.packageName.equals(request.getProviderPackage())) {
-            throw new PackageManager.NameNotFoundException("Found content provider "
-                    + providerAuthority
-                    + ", but package was not " + request.getProviderPackage());
-        }
+            if (!info.packageName.equals(request.getProviderPackage())) {
+                throw new PackageManager.NameNotFoundException("Found content provider "
+                        + providerAuthority
+                        + ", but package was not " + request.getProviderPackage());
+            }
 
-        List<byte[]> signatures;
-        // We correctly check all signatures returned, as advised in the lint error.
-        @SuppressLint("PackageManagerGetSignatures")
-        PackageInfo packageInfo = packageManager.getPackageInfo(info.packageName,
-                PackageManager.GET_SIGNATURES);
-        signatures = convertToByteArrayList(packageInfo.signatures);
-        Collections.sort(signatures, sByteArrayComparator);
-        List<List<byte[]>> requestCertificatesList = getCertificates(request, resources);
-        for (int i = 0; i < requestCertificatesList.size(); ++i) {
-            // Make a copy so we can sort it without modifying the incoming data.
-            List<byte[]> requestSignatures = new ArrayList<>(requestCertificatesList.get(i));
-            Collections.sort(requestSignatures, sByteArrayComparator);
-            if (equalsByteArrayList(signatures, requestSignatures)) {
-                return info;
+            List<byte[]> signatures;
+            // We correctly check all signatures returned, as advised in the lint error.
+            @SuppressLint("PackageManagerGetSignatures")
+            PackageInfo packageInfo = packageManager.getPackageInfo(info.packageName,
+                    PackageManager.GET_SIGNATURES);
+            signatures = convertToByteArrayList(packageInfo.signatures);
+            Collections.sort(signatures, sByteArrayComparator);
+            for (int i = 0; i < requestCertificatesList.size(); ++i) {
+                // Make a copy so we can sort it without modifying the incoming data.
+                List<byte[]> requestSignatures = new ArrayList<>(requestCertificatesList.get(i));
+                Collections.sort(requestSignatures, sByteArrayComparator);
+                if (equalsByteArrayList(signatures, requestSignatures)) {
+                    sProviderCache.put(cacheKey, info);
+                    return info;
+                }
+            }
+            return null;
+        } finally {
+            if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                Trace.endSection();
             }
         }
-        return null;
     }
 
     /**
@@ -119,68 +198,87 @@
             String authority,
             CancellationSignal cancellationSignal
     ) {
-        ArrayList<FontInfo> result = new ArrayList<>();
-        final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(authority)
-                .build();
-        final Uri fileBaseUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(authority)
-                .appendPath("file")
-                .build();
-        Cursor cursor = null;
-        ContentQueryWrapper queryWrapper = ContentQueryWrapper.make(context, uri);
-        try {
-            String[] projection = {
-                    FontsContractCompat.Columns._ID, FontsContractCompat.Columns.FILE_ID,
-                    FontsContractCompat.Columns.TTC_INDEX,
-                    FontsContractCompat.Columns.VARIATION_SETTINGS,
-                    FontsContractCompat.Columns.WEIGHT, FontsContractCompat.Columns.ITALIC,
-                    FontsContractCompat.Columns.RESULT_CODE};
-
-            cursor = queryWrapper.query(uri, projection, "query = ?",
-                        new String[]{request.getQuery()}, null, cancellationSignal);
-
-            if (cursor != null && cursor.getCount() > 0) {
-                final int resultCodeColumnIndex = cursor.getColumnIndex(
-                        FontsContractCompat.Columns.RESULT_CODE);
-                result = new ArrayList<>();
-                final int idColumnIndex = cursor.getColumnIndex(FontsContractCompat.Columns._ID);
-                final int fileIdColumnIndex = cursor.getColumnIndex(
-                        FontsContractCompat.Columns.FILE_ID);
-                final int ttcIndexColumnIndex = cursor.getColumnIndex(
-                        FontsContractCompat.Columns.TTC_INDEX);
-                final int weightColumnIndex = cursor.getColumnIndex(
-                        FontsContractCompat.Columns.WEIGHT);
-                final int italicColumnIndex = cursor.getColumnIndex(
-                        FontsContractCompat.Columns.ITALIC);
-                while (cursor.moveToNext()) {
-                    int resultCode = resultCodeColumnIndex != -1
-                            ? cursor.getInt(resultCodeColumnIndex)
-                            : FontsContractCompat.Columns.RESULT_CODE_OK;
-                    final int ttcIndex = ttcIndexColumnIndex != -1
-                            ? cursor.getInt(ttcIndexColumnIndex) : 0;
-                    Uri fileUri;
-                    if (fileIdColumnIndex == -1) {
-                        long id = cursor.getLong(idColumnIndex);
-                        fileUri = ContentUris.withAppendedId(uri, id);
-                    } else {
-                        long id = cursor.getLong(fileIdColumnIndex);
-                        fileUri = ContentUris.withAppendedId(fileBaseUri, id);
-                    }
-
-                    int weight = weightColumnIndex != -1 ? cursor.getInt(weightColumnIndex) : 400;
-                    boolean italic = italicColumnIndex != -1 && cursor.getInt(italicColumnIndex)
-                            == 1;
-                    result.add(FontInfo.create(fileUri, ttcIndex, weight, italic, resultCode));
-                }
-            }
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-            queryWrapper.close();
+        if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+            Trace.beginSection("FontProvider.query");
         }
-        return result.toArray(new FontInfo[0]);
+        try {
+            ArrayList<FontInfo> result = new ArrayList<>();
+            final Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(authority)
+                    .build();
+            final Uri fileBaseUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(authority)
+                    .appendPath("file")
+                    .build();
+            Cursor cursor = null;
+            ContentQueryWrapper queryWrapper = ContentQueryWrapper.make(context, uri);
+            try {
+                String[] projection = {
+                        FontsContractCompat.Columns._ID, FontsContractCompat.Columns.FILE_ID,
+                        FontsContractCompat.Columns.TTC_INDEX,
+                        FontsContractCompat.Columns.VARIATION_SETTINGS,
+                        FontsContractCompat.Columns.WEIGHT, FontsContractCompat.Columns.ITALIC,
+                        FontsContractCompat.Columns.RESULT_CODE};
+                if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                    Trace.beginSection("ContentQueryWrapper.query");
+                }
+                try {
+                    cursor = queryWrapper.query(uri, projection, "query = ?",
+                            new String[]{request.getQuery()}, null, cancellationSignal);
+                } finally {
+                    if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                        Trace.endSection();
+                    }
+                }
+
+                if (cursor != null && cursor.getCount() > 0) {
+                    final int resultCodeColumnIndex = cursor.getColumnIndex(
+                            FontsContractCompat.Columns.RESULT_CODE);
+                    result = new ArrayList<>();
+                    final int idColumnIndex = cursor.getColumnIndex(
+                            FontsContractCompat.Columns._ID);
+                    final int fileIdColumnIndex = cursor.getColumnIndex(
+                            FontsContractCompat.Columns.FILE_ID);
+                    final int ttcIndexColumnIndex = cursor.getColumnIndex(
+                            FontsContractCompat.Columns.TTC_INDEX);
+                    final int weightColumnIndex = cursor.getColumnIndex(
+                            FontsContractCompat.Columns.WEIGHT);
+                    final int italicColumnIndex = cursor.getColumnIndex(
+                            FontsContractCompat.Columns.ITALIC);
+                    while (cursor.moveToNext()) {
+                        int resultCode = resultCodeColumnIndex != -1
+                                ? cursor.getInt(resultCodeColumnIndex)
+                                : FontsContractCompat.Columns.RESULT_CODE_OK;
+                        final int ttcIndex = ttcIndexColumnIndex != -1
+                                ? cursor.getInt(ttcIndexColumnIndex) : 0;
+                        Uri fileUri;
+                        if (fileIdColumnIndex == -1) {
+                            long id = cursor.getLong(idColumnIndex);
+                            fileUri = ContentUris.withAppendedId(uri, id);
+                        } else {
+                            long id = cursor.getLong(fileIdColumnIndex);
+                            fileUri = ContentUris.withAppendedId(fileBaseUri, id);
+                        }
+
+                        int weight = weightColumnIndex != -1 ? cursor.getInt(weightColumnIndex)
+                                : 400;
+                        boolean italic = italicColumnIndex != -1 && cursor.getInt(italicColumnIndex)
+                                == 1;
+                        result.add(FontInfo.create(fileUri, ttcIndex, weight, italic, resultCode));
+                    }
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+                queryWrapper.close();
+            }
+            return result.toArray(new FontInfo[0]);
+        } finally {
+            if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                Trace.endSection();
+            }
+        }
     }
 
     private static List<List<byte[]>> getCertificates(FontRequest request, Resources resources) {
diff --git a/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java b/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java
index a70b086..761c25b 100644
--- a/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java
+++ b/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Typeface;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Process;
 
@@ -40,8 +41,10 @@
 import androidx.core.provider.FontsContractCompat.FontFamilyResult;
 import androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason;
 import androidx.core.util.Consumer;
+import androidx.tracing.Trace;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
@@ -99,7 +102,7 @@
             final int style,
             int timeoutInMillis
     ) {
-        final String id = createCacheId(request, style);
+        final String id = createCacheId(List.of(request), style);
         Typeface cached = sTypefaceCache.get(id);
         if (cached != null) {
             callback.onTypefaceResult(new TypefaceResult(cached));
@@ -109,7 +112,8 @@
         // when timeout is infinite, do not post to bg thread, since it will block other requests
         if (timeoutInMillis == FontResourcesParserCompat.INFINITE_TIMEOUT_VALUE) {
             // Wait forever. No need to post to the thread.
-            TypefaceResult typefaceResult = getFontSync(id, context, request, style);
+            TypefaceResult typefaceResult = getFontSync(id, context, List.of(request),
+                    style);
             callback.onTypefaceResult(typefaceResult);
             return typefaceResult.mTypeface;
         }
@@ -117,7 +121,7 @@
         final Callable<TypefaceResult> fetcher = new Callable<TypefaceResult>() {
             @Override
             public TypefaceResult call() {
-                return getFontSync(id, context, request, style);
+                return getFontSync(id, context, List.of(request), style);
             }
         };
 
@@ -143,7 +147,7 @@
      *
      *
      * @param context
-     * @param request FontRequest for the font to be loaded.
+     * @param requests FontRequest for the font to be loaded (and any fallbacks).
      * @param style Typeface Style such as {@link Typeface#NORMAL}, {@link Typeface#BOLD} ads asd
      *             {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}.
      * @param executor Executor instance to execute the request. If null is provided
@@ -155,13 +159,12 @@
      */
     static Typeface requestFontAsync(
             @NonNull final Context context,
-            @NonNull final FontRequest request,
+            @NonNull final List<FontRequest> requests,
             final int style,
             @Nullable final Executor executor,
             @NonNull final CallbackWrapper callback
     ) {
-
-        final String id = createCacheId(request, style);
+        final String id = createCacheId(requests, style);
         Typeface cached = sTypefaceCache.get(id);
         if (cached != null) {
             callback.onTypefaceResult(new TypefaceResult(cached));
@@ -195,7 +198,7 @@
             @Override
             public TypefaceResult call() {
                 try {
-                    return getFontSync(id, context, request, style);
+                    return getFontSync(id, context, requests, style);
                 } catch (Throwable t) {
                     return new TypefaceResult(FAIL_REASON_FONT_LOAD_ERROR);
                 }
@@ -223,8 +226,16 @@
         return null;
     }
 
-    private static String createCacheId(@NonNull FontRequest request, int style) {
-        return request.getId() + "-" + style;
+    private static String createCacheId(@NonNull List<FontRequest> requests, int style) {
+        StringBuilder cacheId = new StringBuilder();
+        for (int i = 0; i < requests.size(); i++) {
+            cacheId.append(requests.get(i).getId()).append("-").append(style);
+            if (i < requests.size() - 1) {
+                cacheId.append(";");
+            }
+        }
+
+        return cacheId.toString();
     }
 
     /** Package protected to prevent synthetic accessor */
@@ -232,34 +243,52 @@
     static TypefaceResult getFontSync(
             @NonNull final String cacheId,
             @NonNull final Context context,
-            @NonNull final FontRequest request,
+            @NonNull final List<FontRequest> requests,
             int style
     ) {
-        Typeface cached = sTypefaceCache.get(cacheId);
-        if (cached != null) {
-            return new TypefaceResult(cached);
+        if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+            Trace.beginSection("getFontSync");
         }
-
-        FontFamilyResult result;
         try {
-            result = FontProvider.getFontFamilyResult(context, request, null);
-        } catch (PackageManager.NameNotFoundException e) {
-            return new TypefaceResult(FAIL_REASON_PROVIDER_NOT_FOUND);
-        }
+            Typeface cached = sTypefaceCache.get(cacheId);
+            if (cached != null) {
+                return new TypefaceResult(cached);
+            }
 
-        int fontFamilyResultStatus = getFontFamilyResultStatus(result);
-        if (fontFamilyResultStatus != RESULT_SUCCESS) {
-            return new TypefaceResult(fontFamilyResultStatus);
-        }
+            FontFamilyResult result;
+            try {
+                result = FontProvider.getFontFamilyResult(context, requests, null);
+            } catch (PackageManager.NameNotFoundException e) {
+                return new TypefaceResult(FAIL_REASON_PROVIDER_NOT_FOUND);
+            }
 
-        final Typeface typeface = TypefaceCompat.createFromFontInfo(
-                context, null /* CancellationSignal */, result.getFonts(), style);
+            int fontFamilyResultStatus = getFontFamilyResultStatus(result);
+            if (fontFamilyResultStatus != RESULT_SUCCESS) {
+                return new TypefaceResult(fontFamilyResultStatus);
+            }
+            final Typeface typeface;
+            // Fallbacks are only supported on API 29+; ignore them otherwise
+            if (result.hasFallback() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                typeface = TypefaceCompat.createFromFontInfoWithFallback(
+                        context, null /* CancellationSignal */, result.getFontsWithFallbacks(),
+                        style);
+            } else {
+                typeface = TypefaceCompat.createFromFontInfo(
+                        context, null /* CancellationSignal */, result.getFonts(), style);
+            }
 
-        if (typeface != null) {
-            sTypefaceCache.put(cacheId, typeface);
-            return new TypefaceResult(typeface);
-        } else {
-            return new TypefaceResult(FAIL_REASON_FONT_LOAD_ERROR);
+            if (typeface != null) {
+                // TODO(b/352510076): we probably need to validate that we got *all* the fonts we
+                //  requested
+                sTypefaceCache.put(cacheId, typeface);
+                return new TypefaceResult(typeface);
+            } else {
+                return new TypefaceResult(FAIL_REASON_FONT_LOAD_ERROR);
+            }
+        } finally {
+            if (TypefaceCompat.DOWNLOADABLE_FONT_TRACING) {
+                Trace.endSection();
+            }
         }
     }
 
diff --git a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
index 89f114f..e53545f 100644
--- a/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
+++ b/core/core/src/main/java/androidx/core/provider/FontsContractCompat.java
@@ -44,6 +44,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
 
@@ -97,7 +99,7 @@
             @Nullable CancellationSignal cancellationSignal,
             @NonNull FontRequest request
     ) throws PackageManager.NameNotFoundException {
-        return FontProvider.getFontFamilyResult(context, request, cancellationSignal);
+        return FontProvider.getFontFamilyResult(context, List.of(request), cancellationSignal);
     }
 
     /**
@@ -129,8 +131,8 @@
     ) {
         CallbackWrapper callbackWrapper = new CallbackWrapper(callback);
         Executor executor = RequestExecutor.createHandlerExecutor(handler);
-        FontRequestWorker.requestFontAsync(context.getApplicationContext(), request,
-                Typeface.NORMAL, executor, callbackWrapper);
+        FontRequestWorker.requestFontAsync(context.getApplicationContext(),
+                List.of(request), Typeface.NORMAL, executor, callbackWrapper);
     }
 
     /**
@@ -161,8 +163,45 @@
     ) {
         CallbackWrapper callbacKWrapper = new CallbackWrapper(callback, callbackExecutor);
         Context applicationContext = context.getApplicationContext();
-        FontRequestWorker.requestFontAsync(applicationContext, request, style, loadingExecutor,
-                callbacKWrapper);
+        FontRequestWorker.requestFontAsync(applicationContext, List.of(request),
+                style, loadingExecutor, callbacKWrapper);
+    }
+
+    /**
+     * Request a font async as specified with {@link FontRequest}
+     *
+     * Loading may take several seconds, and the {@code loadingExecutor} passed should be available
+     * to run blocking requests for several seconds. Results will be returned via
+     * {@code callbackExecutor}.
+     *
+     * @param context A context to be used for fetching from font provider
+     * @param requests An array of {@link FontRequest} objects that identify the provider and query
+     *                 for the request, followed by any fallbacks.
+     *                 Fallbacks are used in the order specified.
+     *                 Note that the performance implications of font fallback scale with the number
+     *                 of fonts involved, so the length of this parameter should be kept to a
+     *                 minimum; we recommend no more than 2 (that is, the primary font and a single
+     *                 downloadable custom fallback).
+     * @param style Typeface Style such as {@link Typeface#NORMAL}, {@link Typeface#BOLD}
+     *              {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}.
+     * @param loadingExecutor executor to load font on. Loading may take several _seconds_. If
+     *                       {@code null}, a default executor shared with other null-requests will
+     *                        be used.
+     * @param callbackExecutor Used to dispatch callback
+     * @param callback A callback that will be triggered when results are obtained.
+     */
+    public static void requestFontWithFallbackChain(
+            @NonNull Context context,
+            @NonNull List<FontRequest> requests,
+            int style,
+            @Nullable Executor loadingExecutor,
+            @NonNull Executor callbackExecutor,
+            @NonNull FontRequestCallback callback
+    ) {
+        CallbackWrapper callbacKWrapper = new CallbackWrapper(callback, callbackExecutor);
+        Context applicationContext = context.getApplicationContext();
+        FontRequestWorker.requestFontAsync(applicationContext, requests,
+                style, loadingExecutor, callbacKWrapper);
     }
 
     /**
@@ -175,7 +214,59 @@
      * Used by TypefaceCompat and tests.
      *
      * @param context Context
-     * @param request FontRequest that defines the font to be loaded.
+     * @param requests List of FontRequests that define the font to be loaded, followed by any
+     *                 custom fallbacks, in order
+     * @param style Typeface Style such as {@link Typeface#NORMAL}, {@link Typeface#BOLD}
+     *              {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}.
+     * @param isBlockingFetch when true the call will be synchronous.
+     * @param timeout timeout in milliseconds for the request. It is not used for async
+     *                request.
+     * @param handler the handler to call the callback on.
+     * @param callback the callback to be called.
+     *
+     * @return the resulting Typeface if the requested font is in the cache or the request is a
+     * sync request.
+     *
+     */
+    @RestrictTo(LIBRARY)
+    @Nullable
+    public static Typeface requestFont(
+            @NonNull final Context context,
+            @NonNull final List<FontRequest> requests,
+            final int style,
+            boolean isBlockingFetch,
+            @IntRange(from = 0) int timeout,
+            @NonNull final Handler handler,
+            @NonNull final FontRequestCallback callback
+    ) {
+        CallbackWrapper callbackWrapper = new CallbackWrapper(
+                callback, RequestExecutor.createHandlerExecutor(handler));
+
+        if (isBlockingFetch) {
+            if (requests.size() > 1) {
+                throw new IllegalArgumentException(
+                        "Fallbacks with blocking fetches are not supported for performance "
+                                + "reasons");
+            }
+            return FontRequestWorker.requestFontSync(context, requests.get(0), callbackWrapper,
+                    style, timeout);
+        } else {
+            return FontRequestWorker.requestFontAsync(context, requests, style, null /*executor*/,
+                    callbackWrapper);
+        }
+    }
+
+    /**
+     * Loads a Typeface. Based on the parameters isBlockingFetch, and timeoutInMillis, the fetch
+     * is either sync or async.
+     * - If timeoutInMillis is infinite, and isBlockingFetch is true -> sync
+     * - If timeoutInMillis is NOT infinite, and isBlockingFetch is true -> sync with timeout
+     * - else -> async without timeout.
+     *
+     * Used by TypefaceCompat and tests.
+     *
+     * @param context Context
+     * @param request FontRequest that define the font to be loaded and any fallbacks
      * @param style Typeface Style such as {@link Typeface#NORMAL}, {@link Typeface#BOLD}
      *              {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}.
      * @param isBlockingFetch when true the call will be synchronous.
@@ -199,16 +290,8 @@
             @NonNull final Handler handler,
             @NonNull final FontRequestCallback callback
     ) {
-        CallbackWrapper callbackWrapper = new CallbackWrapper(
-                callback, RequestExecutor.createHandlerExecutor(handler));
-
-        if (isBlockingFetch) {
-            return FontRequestWorker.requestFontSync(context, request, callbackWrapper, style,
-                    timeout);
-        } else {
-            return FontRequestWorker.requestFontAsync(context, request, style, null /*executor*/,
-                    callbackWrapper);
-        }
+        return requestFont(context, List.of(request), style, isBlockingFetch, timeout, handler,
+                callback);
     }
 
     @RestrictTo(LIBRARY)
@@ -414,7 +497,7 @@
         @interface FontResultStatus {}
 
         private final @FontResultStatus int mStatusCode;
-        private final FontInfo[] mFonts;
+        private final List<FontInfo[]> mFonts;
 
         /**
          * @deprecated Not being used by any cross library, and should not be used, internal
@@ -425,6 +508,11 @@
         @RestrictTo(LIBRARY_GROUP_PREFIX)
         public FontFamilyResult(@FontResultStatus int statusCode, @Nullable FontInfo[] fonts) {
             mStatusCode = statusCode;
+            mFonts = Collections.singletonList(fonts);
+        }
+
+        FontFamilyResult(@FontResultStatus int statusCode, @NonNull List<FontInfo[]> fonts) {
+            mStatusCode = statusCode;
             mFonts = fonts;
         }
 
@@ -432,7 +520,25 @@
             return mStatusCode;
         }
 
+        /**
+         * For a single request, returns an array of the fonts making up the family.
+         * <p>
+         * For a request with fallbacks, use {@link #getFontsWithFallbacks()},
+         * as this will only return the information for the first requested family.
+         */
         public FontInfo[] getFonts() {
+            return mFonts.get(0);
+        }
+
+        boolean hasFallback() {
+            return mFonts.size() > 1;
+        }
+
+        /**
+         * Returns a list of arrays of fonts for each font family requested, in order.
+         */
+        @NonNull
+        public List<FontInfo[]> getFontsWithFallbacks() {
             return mFonts;
         }
 
@@ -442,6 +548,12 @@
                 @Nullable FontInfo[] fonts) {
             return new FontFamilyResult(statusCode, fonts);
         }
+
+        static FontFamilyResult create(
+                @FontResultStatus int statusCode,
+                @Nullable List<FontInfo[]> fonts) {
+            return new FontFamilyResult(statusCode, fonts);
+        }
     }
 
     /**
@@ -581,8 +693,8 @@
     ) {
         FontRequestCallback newCallback = new TypefaceCompat.ResourcesCallbackAdapter(fontCallback);
         Handler newHandler = ResourcesCompat.FontCallback.getHandler(handler);
-        return requestFont(context, request, style, isBlockingFetch, timeout, newHandler,
-                newCallback
+        return requestFont(context, List.of(request), style, isBlockingFetch, timeout,
+                newHandler, newCallback
         );
     }
 
diff --git a/core/core/src/main/java/androidx/core/service/quicksettings/TileServiceCompat.java b/core/core/src/main/java/androidx/core/service/quicksettings/TileServiceCompat.java
index bb55828..436cd1a 100644
--- a/core/core/src/main/java/androidx/core/service/quicksettings/TileServiceCompat.java
+++ b/core/core/src/main/java/androidx/core/service/quicksettings/TileServiceCompat.java
@@ -22,7 +22,6 @@
 import android.content.Intent;
 import android.service.quicksettings.TileService;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -67,7 +66,6 @@
 
     @RequiresApi(34)
     private static class Api34Impl {
-        @DoNotInline
         static void startActivityAndCollapse(TileService service,
                 PendingIntent pendingIntent) {
             service.startActivityAndCollapse(pendingIntent);
@@ -76,7 +74,6 @@
 
     @RequiresApi(24)
     private static class Api24Impl {
-        @DoNotInline
         static void startActivityAndCollapse(TileService service, Intent intent) {
             service.startActivityAndCollapse(intent);
         }
diff --git a/core/core/src/main/java/androidx/core/telephony/SubscriptionManagerCompat.java b/core/core/src/main/java/androidx/core/telephony/SubscriptionManagerCompat.java
index 0bd6994..999085d 100644
--- a/core/core/src/main/java/androidx/core/telephony/SubscriptionManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/telephony/SubscriptionManagerCompat.java
@@ -22,7 +22,6 @@
 import android.os.Build;
 import android.telephony.SubscriptionManager;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 
 import java.lang.reflect.InvocationTargetException;
@@ -78,7 +77,6 @@
     private static class Api29Impl {
         private Api29Impl() {}
 
-        @DoNotInline
         static int getSlotIndex(int subId) {
             return SubscriptionManager.getSlotIndex(subId);
         }
diff --git a/core/core/src/main/java/androidx/core/telephony/TelephonyManagerCompat.java b/core/core/src/main/java/androidx/core/telephony/TelephonyManagerCompat.java
index 4751a51..e29978e 100644
--- a/core/core/src/main/java/androidx/core/telephony/TelephonyManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/telephony/TelephonyManagerCompat.java
@@ -23,7 +23,6 @@
 import android.os.Build.VERSION;
 import android.telephony.TelephonyManager;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -137,7 +136,6 @@
     private static class Api30Impl {
         private Api30Impl() {}
 
-        @DoNotInline
         static int getSubscriptionId(TelephonyManager telephonyManager) {
             return telephonyManager.getSubscriptionId();
         }
@@ -149,7 +147,6 @@
 
         @SuppressLint("MissingPermission")
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        @DoNotInline
         @Nullable
         static String getImei(TelephonyManager telephonyManager) {
             return telephonyManager.getImei();
@@ -162,7 +159,6 @@
 
         @SuppressLint("MissingPermission")
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        @DoNotInline
         @Nullable
         static String getDeviceId(TelephonyManager telephonyManager, int slotIndex) {
             return telephonyManager.getDeviceId(slotIndex);
diff --git a/core/core/src/main/java/androidx/core/text/HtmlCompat.java b/core/core/src/main/java/androidx/core/text/HtmlCompat.java
index 61ec500..930a601 100644
--- a/core/core/src/main/java/androidx/core/text/HtmlCompat.java
+++ b/core/core/src/main/java/androidx/core/text/HtmlCompat.java
@@ -30,7 +30,6 @@
 import android.text.style.BulletSpan;
 import android.text.style.ParagraphStyle;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -187,18 +186,15 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Spanned fromHtml(String source, int flags) {
             return Html.fromHtml(source, flags);
         }
 
-        @DoNotInline
         static Spanned fromHtml(String source, int flags, ImageGetter imageGetter,
                 TagHandler tagHandler) {
             return Html.fromHtml(source, flags, imageGetter, tagHandler);
         }
 
-        @DoNotInline
         static String toHtml(Spanned text, int option) {
             return Html.toHtml(text, option);
         }
diff --git a/core/core/src/main/java/androidx/core/text/ICUCompat.java b/core/core/src/main/java/androidx/core/text/ICUCompat.java
index 06674f1..b3b58c9 100644
--- a/core/core/src/main/java/androidx/core/text/ICUCompat.java
+++ b/core/core/src/main/java/androidx/core/text/ICUCompat.java
@@ -21,7 +21,6 @@
 import android.os.Build;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -155,17 +154,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ULocale forLocale(Locale loc) {
             return ULocale.forLocale(loc);
         }
 
-        @DoNotInline
         static ULocale addLikelySubtags(Object loc) {
             return ULocale.addLikelySubtags((ULocale) loc);
         }
 
-        @DoNotInline
         static String getScript(Object uLocale) {
             return ((ULocale) uLocale).getScript();
         }
@@ -177,7 +173,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static String getScript(Locale locale) {
             return locale.getScript();
         }
diff --git a/core/core/src/main/java/androidx/core/text/PrecomputedTextCompat.java b/core/core/src/main/java/androidx/core/text/PrecomputedTextCompat.java
index 05017e1..34d6050c 100644
--- a/core/core/src/main/java/androidx/core/text/PrecomputedTextCompat.java
+++ b/core/core/src/main/java/androidx/core/text/PrecomputedTextCompat.java
@@ -32,7 +32,6 @@
 import android.text.TextUtils;
 import android.text.style.MetricAffectingSpan;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
@@ -744,7 +743,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Spannable castToSpannable(PrecomputedText precomputedText) {
             return precomputedText;
         }
diff --git a/core/core/src/main/java/androidx/core/text/util/LinkifyCompat.java b/core/core/src/main/java/androidx/core/text/util/LinkifyCompat.java
index 0d4edc6..d5ddfc9 100644
--- a/core/core/src/main/java/androidx/core/text/util/LinkifyCompat.java
+++ b/core/core/src/main/java/androidx/core/text/util/LinkifyCompat.java
@@ -31,7 +31,6 @@
 import android.webkit.WebView;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -546,13 +545,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void addLinks(TextView text, Pattern pattern, String defaultScheme, String[] schemes,
                 MatchFilter matchFilter, TransformFilter transformFilter) {
             Linkify.addLinks(text, pattern, defaultScheme, schemes, matchFilter, transformFilter);
         }
 
-        @DoNotInline
         static boolean addLinks(Spannable spannable, Pattern pattern, String defaultScheme,
                 String[] schemes, MatchFilter matchFilter, TransformFilter transformFilter) {
             return Linkify.addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
diff --git a/core/core/src/main/java/androidx/core/text/util/LocalePreferences.java b/core/core/src/main/java/androidx/core/text/util/LocalePreferences.java
index a30c03a..846f4c2 100644
--- a/core/core/src/main/java/androidx/core/text/util/LocalePreferences.java
+++ b/core/core/src/main/java/androidx/core/text/util/LocalePreferences.java
@@ -24,7 +24,6 @@
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -562,13 +561,11 @@
 
     @RequiresApi(VERSION_CODES.N)
     private static class Api24Impl {
-        @DoNotInline
         @CalendarType.CalendarTypes
         static String getCalendarType(@NonNull Locale locale) {
             return android.icu.util.Calendar.getInstance(locale).getType();
         }
 
-        @DoNotInline
         static Locale getDefaultLocale() {
             return Locale.getDefault(Category.FORMAT);
         }
@@ -579,7 +576,6 @@
 
     @RequiresApi(VERSION_CODES.TIRAMISU)
     private static class Api33Impl {
-        @DoNotInline
         @TemperatureUnit.TemperatureUnits
         static String getResolvedTemperatureUnit(@NonNull Locale locale) {
             LocalizedNumberFormatter nf = NumberFormatter.with()
@@ -593,7 +589,6 @@
             return unit;
         }
 
-        @DoNotInline
         @HourCycle.HourCycleTypes
         static String getHourCycle(@NonNull Locale locale) {
             return getHourCycleType(
diff --git a/core/core/src/main/java/androidx/core/util/SizeFCompat.java b/core/core/src/main/java/androidx/core/util/SizeFCompat.java
index 80ef53d..4c6e307 100644
--- a/core/core/src/main/java/androidx/core/util/SizeFCompat.java
+++ b/core/core/src/main/java/androidx/core/util/SizeFCompat.java
@@ -18,7 +18,6 @@
 
 import android.util.SizeF;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -89,14 +88,12 @@
 
     @RequiresApi(21)
     private static final class Api21Impl {
-        @DoNotInline
         @NonNull
         static SizeFCompat toSizeFCompat(@NonNull SizeF size) {
             Preconditions.checkNotNull(size);
             return new SizeFCompat(size.getWidth(), size.getHeight());
         }
 
-        @DoNotInline
         @NonNull
         static SizeF toSizeF(@NonNull SizeFCompat size) {
             Preconditions.checkNotNull(size);
diff --git a/core/core/src/main/java/androidx/core/util/TypedValueCompat.java b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
index b106e9a..614f50a 100644
--- a/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
+++ b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
@@ -28,7 +28,6 @@
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -199,7 +198,6 @@
 
     @RequiresApi(34)
     private static class Api34Impl {
-        @DoNotInline
         public static float deriveDimension(
                 @ComplexDimensionUnit int unitToConvertTo,
                 float pixelValue,
diff --git a/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java b/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
index 1f3ffb0..f789419 100644
--- a/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
@@ -24,7 +24,6 @@
 import android.util.Pair;
 import android.view.ContentInfo;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -329,7 +328,6 @@
     private static final class Api31Impl {
         private Api31Impl() {}
 
-        @DoNotInline
         @NonNull
         public static Pair<ContentInfo, ContentInfo> partition(@NonNull ContentInfo payload,
                 @NonNull Predicate<ClipData.Item> itemPredicate) {
diff --git a/core/core/src/main/java/androidx/core/view/DisplayCompat.java b/core/core/src/main/java/androidx/core/view/DisplayCompat.java
index 64a170e..6629da5 100644
--- a/core/core/src/main/java/androidx/core/view/DisplayCompat.java
+++ b/core/core/src/main/java/androidx/core/view/DisplayCompat.java
@@ -27,7 +27,6 @@
 import android.text.TextUtils;
 import android.view.Display;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -397,12 +396,10 @@
                 // This class is not instantiable.
             }
 
-            @DoNotInline
             static int getPhysicalWidth(Display.Mode mode) {
                 return mode.getPhysicalWidth();
             }
 
-            @DoNotInline
             static int getPhysicalHeight(Display.Mode mode) {
                 return mode.getPhysicalHeight();
             }
diff --git a/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java b/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java
index ce16f73..0c4bd43 100644
--- a/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java
+++ b/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 import android.view.DisplayCutout;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -271,32 +270,26 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static DisplayCutout createDisplayCutout(Rect safeInsets, List<Rect> boundingRects) {
             return new DisplayCutout(safeInsets, boundingRects);
         }
 
-        @DoNotInline
         static int getSafeInsetTop(DisplayCutout displayCutout) {
             return displayCutout.getSafeInsetTop();
         }
 
-        @DoNotInline
         static int getSafeInsetBottom(DisplayCutout displayCutout) {
             return displayCutout.getSafeInsetBottom();
         }
 
-        @DoNotInline
         static int getSafeInsetLeft(DisplayCutout displayCutout) {
             return displayCutout.getSafeInsetLeft();
         }
 
-        @DoNotInline
         static int getSafeInsetRight(DisplayCutout displayCutout) {
             return displayCutout.getSafeInsetRight();
         }
 
-        @DoNotInline
         static List<Rect> getBoundingRects(DisplayCutout displayCutout) {
             return displayCutout.getBoundingRects();
         }
@@ -308,7 +301,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static DisplayCutout createDisplayCutout(android.graphics.Insets safeInsets, Rect boundLeft,
                 Rect boundTop, Rect boundRight, Rect boundBottom) {
             return new DisplayCutout(safeInsets, boundLeft, boundTop, boundRight, boundBottom);
@@ -321,7 +313,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static DisplayCutout createDisplayCutout(android.graphics.Insets safeInsets, Rect boundLeft,
                 Rect boundTop, Rect boundRight, Rect boundBottom,
                 android.graphics.Insets waterfallInsets) {
@@ -329,7 +320,6 @@
                     waterfallInsets);
         }
 
-        @DoNotInline
         static android.graphics.Insets getWaterfallInsets(DisplayCutout displayCutout) {
             return displayCutout.getWaterfallInsets();
         }
@@ -341,7 +331,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         @Nullable
         static Path getCutoutPath(DisplayCutout displayCutout) {
             return displayCutout.getCutoutPath();
@@ -354,7 +343,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static DisplayCutout createDisplayCutout(android.graphics.Insets safeInsets, Rect boundLeft,
                 Rect boundTop, Rect boundRight, Rect boundBottom,
                 android.graphics.Insets waterfallInsets, Path cutoutPath) {
diff --git a/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java b/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java
index c4c8e5a..316da25 100644
--- a/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java
+++ b/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java
@@ -23,7 +23,6 @@
 import android.view.DragAndDropPermissions;
 import android.view.DragEvent;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -75,13 +74,11 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static DragAndDropPermissions requestDragAndDropPermissions(Activity activity,
                 DragEvent event) {
             return activity.requestDragAndDropPermissions(event);
         }
 
-        @DoNotInline
         static void release(DragAndDropPermissions dragAndDropPermissions) {
             dragAndDropPermissions.release();
         }
diff --git a/core/core/src/main/java/androidx/core/view/MenuCompat.java b/core/core/src/main/java/androidx/core/view/MenuCompat.java
index 36b96d9..e8b7630 100644
--- a/core/core/src/main/java/androidx/core/view/MenuCompat.java
+++ b/core/core/src/main/java/androidx/core/view/MenuCompat.java
@@ -20,7 +20,6 @@
 import android.view.Menu;
 import android.view.MenuItem;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.core.internal.view.SupportMenu;
@@ -65,7 +64,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setGroupDividerEnabled(Menu menu, boolean groupDividerEnabled) {
             menu.setGroupDividerEnabled(groupDividerEnabled);
         }
diff --git a/core/core/src/main/java/androidx/core/view/MenuItemCompat.java b/core/core/src/main/java/androidx/core/view/MenuItemCompat.java
index 5df61da..5dbad58f 100644
--- a/core/core/src/main/java/androidx/core/view/MenuItemCompat.java
+++ b/core/core/src/main/java/androidx/core/view/MenuItemCompat.java
@@ -26,7 +26,6 @@
 import android.view.MenuItem;
 import android.view.View;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -612,70 +611,57 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static MenuItem setContentDescription(MenuItem menuItem, CharSequence contentDescription) {
             return menuItem.setContentDescription(contentDescription);
         }
 
-        @DoNotInline
         static CharSequence getContentDescription(MenuItem menuItem) {
             return menuItem.getContentDescription();
         }
 
-        @DoNotInline
         static MenuItem setTooltipText(MenuItem menuItem, CharSequence tooltipText) {
             return menuItem.setTooltipText(tooltipText);
         }
 
-        @DoNotInline
         static CharSequence getTooltipText(MenuItem menuItem) {
             return menuItem.getTooltipText();
         }
 
-        @DoNotInline
         static MenuItem setShortcut(MenuItem menuItem, char numericChar, char alphaChar,
                 int numericModifiers, int alphaModifiers) {
             return menuItem.setShortcut(numericChar, alphaChar, numericModifiers, alphaModifiers);
         }
 
-        @DoNotInline
         static MenuItem setNumericShortcut(MenuItem menuItem, char numericChar,
                 int numericModifiers) {
             return menuItem.setNumericShortcut(numericChar, numericModifiers);
         }
 
-        @DoNotInline
         static int getNumericModifiers(MenuItem menuItem) {
             return menuItem.getNumericModifiers();
         }
 
-        @DoNotInline
         static MenuItem setAlphabeticShortcut(MenuItem menuItem, char alphaChar,
                 int alphaModifiers) {
             return menuItem.setAlphabeticShortcut(alphaChar, alphaModifiers);
         }
 
-        @DoNotInline
         static int getAlphabeticModifiers(MenuItem menuItem) {
             return menuItem.getAlphabeticModifiers();
         }
 
-        @DoNotInline
         static MenuItem setIconTintList(MenuItem menuItem, ColorStateList tint) {
             return menuItem.setIconTintList(tint);
         }
 
-        @DoNotInline
         static ColorStateList getIconTintList(MenuItem menuItem) {
             return menuItem.getIconTintList();
         }
 
-        @DoNotInline
         static MenuItem setIconTintMode(MenuItem menuItem, PorterDuff.Mode tintMode) {
             return menuItem.setIconTintMode(tintMode);
         }
 
-        @DoNotInline
         static PorterDuff.Mode getIconTintMode(MenuItem menuItem) {
             return menuItem.getIconTintMode();
         }
diff --git a/core/core/src/main/java/androidx/core/view/PointerIconCompat.java b/core/core/src/main/java/androidx/core/view/PointerIconCompat.java
index f47836b..b07a397 100644
--- a/core/core/src/main/java/androidx/core/view/PointerIconCompat.java
+++ b/core/core/src/main/java/androidx/core/view/PointerIconCompat.java
@@ -25,7 +25,6 @@
 import android.graphics.Bitmap;
 import android.view.PointerIcon;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -193,17 +192,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static PointerIcon getSystemIcon(Context context, int type) {
             return PointerIcon.getSystemIcon(context, type);
         }
 
-        @DoNotInline
         static PointerIcon create(Bitmap bitmap, float hotSpotX, float hotSpotY) {
             return PointerIcon.create(bitmap, hotSpotX, hotSpotY);
         }
 
-        @DoNotInline
         static PointerIcon load(Resources resources, int resourceId) {
             return PointerIcon.load(resources, resourceId);
         }
diff --git a/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java b/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java
index aedbab2..e5b812f 100644
--- a/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/VelocityTrackerCompat.java
@@ -25,7 +25,6 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -308,17 +307,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isAxisSupported(VelocityTracker velocityTracker, int axis) {
             return velocityTracker.isAxisSupported(axis);
         }
 
-        @DoNotInline
         static float getAxisVelocity(VelocityTracker velocityTracker, int axis, int id) {
             return velocityTracker.getAxisVelocity(axis, id);
         }
 
-        @DoNotInline
         static float getAxisVelocity(VelocityTracker velocityTracker, int axis) {
             return velocityTracker.getAxisVelocity(axis);
         }
diff --git a/core/core/src/main/java/androidx/core/view/ViewCompat.java b/core/core/src/main/java/androidx/core/view/ViewCompat.java
index 46875b0..33ac147 100644
--- a/core/core/src/main/java/androidx/core/view/ViewCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewCompat.java
@@ -62,7 +62,6 @@
 import android.view.contentcapture.ContentCaptureSession;
 import android.view.inputmethod.InputConnection;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.FloatRange;
 import androidx.annotation.IdRes;
 import androidx.annotation.IntDef;
@@ -3293,7 +3292,6 @@
     private static final class Api31Impl {
         private Api31Impl() {}
 
-        @DoNotInline
         public static void setOnReceiveContentListener(@NonNull View view,
                 @Nullable String[] mimeTypes, @Nullable final OnReceiveContentListener listener) {
             if (listener == null) {
@@ -3304,13 +3302,11 @@
             }
         }
 
-        @DoNotInline
         @Nullable
         public static String[] getReceiveContentMimeTypes(@NonNull View view) {
             return view.getReceiveContentMimeTypes();
         }
 
-        @DoNotInline
         @Nullable
         public static ContentInfoCompat performReceiveContent(@NonNull View view,
                 @NonNull ContentInfoCompat payload) {
@@ -5250,13 +5246,11 @@
         }
 
         // Only called on SDK 21 and 22
-        @DoNotInline
         @Nullable
         public static WindowInsetsCompat getRootWindowInsets(@NonNull View v) {
             return WindowInsetsCompat.Api21ReflectionHolder.getRootWindowInsets(v);
         }
 
-        @DoNotInline
         static WindowInsetsCompat computeSystemWindowInsets(@NonNull View v,
                 @NonNull WindowInsetsCompat insets, @NonNull Rect outLocalInsets) {
             WindowInsets platformInsets = insets.toWindowInsets();
@@ -5269,7 +5263,6 @@
             }
         }
 
-        @DoNotInline
         static void setOnApplyWindowInsetsListener(final @NonNull View v,
                 final @Nullable OnApplyWindowInsetsListener listener) {
             // For backward compatibility of WindowInsetsAnimation, we use an
@@ -5329,7 +5322,6 @@
          * The backport of {@link WindowInsetsAnimationCompat.Callback} on API < 30 relies on
          * onApplyWindowInsetsListener, so if this callback is set, we'll call it in this method
          */
-        @DoNotInline
         static void callCompatInsetAnimationCallback(final @NonNull WindowInsets insets,
                 final @NonNull View v) {
             // In case a WindowInsetsAnimationCompat.Callback is set, make sure to
@@ -5342,116 +5334,94 @@
             }
         }
 
-        @DoNotInline
         static boolean dispatchNestedFling(@NonNull View view, float velocityX, float velocityY,
                 boolean consumed) {
             return view.dispatchNestedFling(velocityX, velocityY, consumed);
         }
 
-        @DoNotInline
         static boolean dispatchNestedPreFling(@NonNull View view, float velocityX,
                 float velocityY) {
             return view.dispatchNestedPreFling(velocityX, velocityY);
         }
 
-        @DoNotInline
         static float getZ(@NonNull View view) {
             return view.getZ();
         }
 
-        @DoNotInline
         static void setZ(@NonNull View view, float z) {
             view.setZ(z);
         }
 
-        @DoNotInline
         static void setElevation(View view, float elevation) {
             view.setElevation(elevation);
         }
 
-        @DoNotInline
         static void setTranslationZ(View view, float translationZ) {
             view.setTranslationZ(translationZ);
         }
 
-        @DoNotInline
         static float getTranslationZ(View view) {
             return view.getTranslationZ();
         }
 
-        @DoNotInline
         static void setTransitionName(View view, String transitionName) {
             view.setTransitionName(transitionName);
         }
 
-        @DoNotInline
         static boolean isImportantForAccessibility(View view) {
             return view.isImportantForAccessibility();
         }
 
-        @DoNotInline
         static float getElevation(View view) {
             return view.getElevation();
         }
 
-        @DoNotInline
         static String getTransitionName(View view) {
             return view.getTransitionName();
         }
 
-        @DoNotInline
         static void setBackgroundTintList(View view, ColorStateList tint) {
             view.setBackgroundTintList(tint);
         }
 
-        @DoNotInline
         static ColorStateList getBackgroundTintList(View view) {
             return view.getBackgroundTintList();
         }
 
-        @DoNotInline
         static PorterDuff.Mode getBackgroundTintMode(View view) {
             return view.getBackgroundTintMode();
         }
 
-        @DoNotInline
         static void setBackgroundTintMode(View view, PorterDuff.Mode tintMode) {
             view.setBackgroundTintMode(tintMode);
         }
 
-        @DoNotInline
         static void setNestedScrollingEnabled(View view, boolean enabled) {
             view.setNestedScrollingEnabled(enabled);
         }
 
-        @DoNotInline
         static boolean isNestedScrollingEnabled(View view) {
             return view.isNestedScrollingEnabled();
         }
 
-        @DoNotInline
         static boolean startNestedScroll(View view, int axes) {
             return view.startNestedScroll(axes);
         }
 
-        @DoNotInline
         static void stopNestedScroll(View view) {
             view.stopNestedScroll();
         }
 
-        @DoNotInline
         static boolean hasNestedScrollingParent(View view) {
             return view.hasNestedScrollingParent();
         }
 
-        @DoNotInline
         static boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
             return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                     offsetInWindow);
         }
 
-        @DoNotInline
         static boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
                 int[] offsetInWindow) {
             return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
@@ -5477,17 +5447,14 @@
             return insets;
         }
 
-        @DoNotInline
         static void setScrollIndicators(@NonNull View view, int indicators) {
             view.setScrollIndicators(indicators);
         }
 
-        @DoNotInline
         static void setScrollIndicators(@NonNull View view, int indicators, int mask) {
             view.setScrollIndicators(indicators, mask);
         }
 
-        @DoNotInline
         static int getScrollIndicators(@NonNull View view) {
             return view.getScrollIndicators();
         }
@@ -5499,7 +5466,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void saveAttributeDataForStyleable(@NonNull View view,
                 @NonNull Context context, @NonNull int[] styleable, @Nullable AttributeSet attrs,
                 @NonNull TypedArray t, int defStyleAttr, int defStyleRes) {
@@ -5507,27 +5473,22 @@
                     context, styleable, attrs, t, defStyleAttr, defStyleRes);
         }
 
-        @DoNotInline
         static View.AccessibilityDelegate getAccessibilityDelegate(View view) {
             return view.getAccessibilityDelegate();
         }
 
-        @DoNotInline
         static void setSystemGestureExclusionRects(View view, List<Rect> rects) {
             view.setSystemGestureExclusionRects(rects);
         }
 
-        @DoNotInline
         static List<Rect> getSystemGestureExclusionRects(View view) {
             return view.getSystemGestureExclusionRects();
         }
 
-        @DoNotInline
         static ContentCaptureSession getContentCaptureSession(View view) {
             return view.getContentCaptureSession();
         }
 
-        @DoNotInline
         static void setContentCaptureSession(View view,
                 ContentCaptureSessionCompat contentCaptureSession) {
             view.setContentCaptureSession(contentCaptureSession == null
@@ -5549,27 +5510,22 @@
                     windowInsetsController) : null;
         }
 
-        @DoNotInline
         static void setStateDescription(View view, CharSequence stateDescription) {
             view.setStateDescription(stateDescription);
         }
 
-        @DoNotInline
         static CharSequence getStateDescription(View view) {
             return view.getStateDescription();
         }
 
-        @DoNotInline
         static void setImportantForContentCapture(View view, int mode) {
             view.setImportantForContentCapture(mode);
         }
 
-        @DoNotInline
         static boolean isImportantForContentCapture(View view) {
             return view.isImportantForContentCapture();
         }
 
-        @DoNotInline
         static int getImportantForContentCapture(View view) {
             return view.getImportantForContentCapture();
         }
@@ -5581,84 +5537,68 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setAutofillHints(@NonNull View view, String... autofillHints) {
             view.setAutofillHints(autofillHints);
         }
 
-        @DoNotInline
         static void setTooltipText(@NonNull View view, CharSequence tooltipText) {
             view.setTooltipText(tooltipText);
         }
 
-        @DoNotInline
         static int getNextClusterForwardId(@NonNull View view) {
             return view.getNextClusterForwardId();
         }
 
-        @DoNotInline
         static void setNextClusterForwardId(View view, int nextClusterForwardId) {
             view.setNextClusterForwardId(nextClusterForwardId);
         }
 
-        @DoNotInline
         static boolean isKeyboardNavigationCluster(@NonNull View view) {
             return view.isKeyboardNavigationCluster();
         }
 
-        @DoNotInline
         static void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) {
             view.setKeyboardNavigationCluster(isCluster);
         }
 
-        @DoNotInline
         static boolean isFocusedByDefault(@NonNull View view) {
             return view.isFocusedByDefault();
         }
 
-        @DoNotInline
         static void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) {
             view.setFocusedByDefault(isFocusedByDefault);
         }
 
-        @DoNotInline
         static View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster,
                 int direction) {
             return view.keyboardNavigationClusterSearch(currentCluster, direction);
         }
 
-        @DoNotInline
         static void addKeyboardNavigationClusters(@NonNull View view, Collection<View> views,
                 int direction) {
             view.addKeyboardNavigationClusters(views, direction);
         }
 
-        @DoNotInline
         static boolean restoreDefaultFocus(@NonNull View view) {
             return view.restoreDefaultFocus();
         }
 
-        @DoNotInline
         static boolean hasExplicitFocusable(@NonNull View view) {
             return view.hasExplicitFocusable();
         }
 
-        @DoNotInline
         static int getImportantForAutofill(View view) {
             return view.getImportantForAutofill();
         }
 
-        @DoNotInline
         static void setImportantForAutofill(View view, int mode) {
             view.setImportantForAutofill(mode);
         }
 
-        @DoNotInline
         static boolean isImportantForAutofill(View view) {
             return view.isImportantForAutofill();
         }
 
-        @DoNotInline
         public static AutofillId getAutofillId(View view) {
             return view.getAutofillId();
         }
@@ -5670,35 +5610,29 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setPointerIcon(@NonNull View view, PointerIcon pointerIcon) {
             view.setPointerIcon(pointerIcon);
         }
 
-        @DoNotInline
         static boolean startDragAndDrop(@NonNull View view, @Nullable ClipData data,
                 @NonNull View.DragShadowBuilder shadowBuilder, @Nullable Object myLocalState,
                 int flags) {
             return view.startDragAndDrop(data, shadowBuilder, myLocalState, flags);
         }
 
-        @DoNotInline
         static void cancelDragAndDrop(@NonNull View view) {
             view.cancelDragAndDrop();
         }
 
-        @DoNotInline
         static void updateDragShadow(@NonNull View view,
                 @NonNull View.DragShadowBuilder shadowBuilder) {
             view.updateDragShadow(shadowBuilder);
         }
 
-        @DoNotInline
         static void dispatchStartTemporaryDetach(View view) {
             view.dispatchStartTemporaryDetach();
         }
 
-        @DoNotInline
         static void dispatchFinishTemporaryDetach(View view) {
             view.dispatchFinishTemporaryDetach();
         }
@@ -5711,43 +5645,35 @@
         }
 
         @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
-        @DoNotInline
         static <T> T requireViewById(View view, int id) {
             return (T) view.requireViewById(id);
         }
 
-        @DoNotInline
         static CharSequence getAccessibilityPaneTitle(View view) {
             return view.getAccessibilityPaneTitle();
         }
 
-        @DoNotInline
         static void setAccessibilityPaneTitle(View view,
                 CharSequence accessibilityPaneTitle) {
             view.setAccessibilityPaneTitle(accessibilityPaneTitle);
         }
 
-        @DoNotInline
         static void setAccessibilityHeading(View view, boolean isHeading) {
             view.setAccessibilityHeading(isHeading);
         }
 
-        @DoNotInline
         static boolean isAccessibilityHeading(View view) {
             return view.isAccessibilityHeading();
         }
 
-        @DoNotInline
         static boolean isScreenReaderFocusable(View view) {
             return view.isScreenReaderFocusable();
         }
 
-        @DoNotInline
         static void setScreenReaderFocusable(View view, boolean screenReaderFocusable) {
             view.setScreenReaderFocusable(screenReaderFocusable);
         }
 
-        @DoNotInline
         @SuppressWarnings("unchecked")
         static void addOnUnhandledKeyEventListener(@NonNull View v,
                 final @NonNull OnUnhandledKeyEventListenerCompat listener) {
@@ -5766,7 +5692,6 @@
             v.addOnUnhandledKeyEventListener(fwListener);
         }
 
-        @DoNotInline
         @SuppressWarnings("unchecked")
         static void removeOnUnhandledKeyEventListener(@NonNull View v,
                 @NonNull OnUnhandledKeyEventListenerCompat listener) {
@@ -5783,7 +5708,6 @@
             }
         }
 
-        @DoNotInline
         public static void setAutofillId(View view, AutofillIdCompat id) {
             view.setAutofillId(id == null ? null : id.toAutofillId());
         }
@@ -5795,17 +5719,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void requestApplyInsets(View view) {
             view.requestApplyInsets();
         }
 
-        @DoNotInline
         static WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
             return view.onApplyWindowInsets(insets);
         }
 
-        @DoNotInline
         static WindowInsets dispatchApplyWindowInsets(View view, WindowInsets insets) {
             return view.dispatchApplyWindowInsets(insets);
         }
diff --git a/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java b/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java
index 157aa0c..b4e425d 100644
--- a/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewConfigurationCompat.java
@@ -27,7 +27,6 @@
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.core.util.Supplier;
@@ -295,12 +294,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static float getScaledHorizontalScrollFactor(ViewConfiguration viewConfiguration) {
             return viewConfiguration.getScaledHorizontalScrollFactor();
         }
 
-        @DoNotInline
         static float getScaledVerticalScrollFactor(ViewConfiguration viewConfiguration) {
             return viewConfiguration.getScaledVerticalScrollFactor();
         }
@@ -312,12 +309,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getScaledHoverSlop(ViewConfiguration viewConfiguration) {
             return viewConfiguration.getScaledHoverSlop();
         }
 
-        @DoNotInline
         static boolean shouldShowMenuShortcutsWhenKeyboardPresent(
                 ViewConfiguration viewConfiguration) {
             return viewConfiguration.shouldShowMenuShortcutsWhenKeyboardPresent();
@@ -330,7 +325,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getScaledMaximumFlingVelocity(
                 @NonNull ViewConfiguration viewConfiguration,
                 int inputDeviceId,
@@ -339,7 +333,6 @@
             return viewConfiguration.getScaledMaximumFlingVelocity(inputDeviceId, axis, source);
         }
 
-        @DoNotInline
         static int getScaledMinimumFlingVelocity(
                 @NonNull ViewConfiguration viewConfiguration,
                 int inputDeviceId,
diff --git a/core/core/src/main/java/androidx/core/view/ViewGroupCompat.java b/core/core/src/main/java/androidx/core/view/ViewGroupCompat.java
index e35464f..7924476 100644
--- a/core/core/src/main/java/androidx/core/view/ViewGroupCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewGroupCompat.java
@@ -21,7 +21,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.core.R;
@@ -200,17 +199,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setTransitionGroup(ViewGroup viewGroup, boolean isTransitionGroup) {
             viewGroup.setTransitionGroup(isTransitionGroup);
         }
 
-        @DoNotInline
         static boolean isTransitionGroup(ViewGroup viewGroup) {
             return viewGroup.isTransitionGroup();
         }
 
-        @DoNotInline
         static int getNestedScrollAxes(ViewGroup viewGroup) {
             return viewGroup.getNestedScrollAxes();
         }
diff --git a/core/core/src/main/java/androidx/core/view/ViewParentCompat.java b/core/core/src/main/java/androidx/core/view/ViewParentCompat.java
index 2a28972..c10dc12 100644
--- a/core/core/src/main/java/androidx/core/view/ViewParentCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewParentCompat.java
@@ -25,7 +25,6 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -530,39 +529,32 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean onStartNestedScroll(ViewParent viewParent, View view, View view1, int i) {
             return viewParent.onStartNestedScroll(view, view1, i);
         }
 
-        @DoNotInline
         static void onNestedScrollAccepted(ViewParent viewParent, View view, View view1, int i) {
             viewParent.onNestedScrollAccepted(view, view1, i);
         }
 
-        @DoNotInline
         static void onStopNestedScroll(ViewParent viewParent, View view) {
             viewParent.onStopNestedScroll(view);
         }
 
-        @DoNotInline
         static void onNestedScroll(ViewParent viewParent, View view, int i, int i1, int i2,
                 int i3) {
             viewParent.onNestedScroll(view, i, i1, i2, i3);
         }
 
-        @DoNotInline
         static void onNestedPreScroll(ViewParent viewParent, View view, int i, int i1, int[] ints) {
             viewParent.onNestedPreScroll(view, i, i1, ints);
         }
 
-        @DoNotInline
         static boolean onNestedFling(ViewParent viewParent, View view, float v, float v1,
                 boolean b) {
             return viewParent.onNestedFling(view, v, v1, b);
         }
 
-        @DoNotInline
         static boolean onNestedPreFling(ViewParent viewParent, View view, float v, float v1) {
             return viewParent.onNestedPreFling(view, v, v1);
         }
diff --git a/core/core/src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java b/core/core/src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java
index 4c6238e..18d981b 100644
--- a/core/core/src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java
@@ -25,7 +25,6 @@
 import android.view.ViewPropertyAnimator;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -728,24 +727,20 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ViewPropertyAnimator translationZBy(ViewPropertyAnimator viewPropertyAnimator,
                 float value) {
             return viewPropertyAnimator.translationZBy(value);
         }
 
-        @DoNotInline
         static ViewPropertyAnimator translationZ(ViewPropertyAnimator viewPropertyAnimator,
                 float value) {
             return viewPropertyAnimator.translationZ(value);
         }
 
-        @DoNotInline
         static ViewPropertyAnimator z(ViewPropertyAnimator viewPropertyAnimator, float value) {
             return viewPropertyAnimator.z(value);
         }
 
-        @DoNotInline
         static ViewPropertyAnimator zBy(ViewPropertyAnimator viewPropertyAnimator, float value) {
             return viewPropertyAnimator.zBy(value);
         }
diff --git a/core/core/src/main/java/androidx/core/view/ViewStructureCompat.java b/core/core/src/main/java/androidx/core/view/ViewStructureCompat.java
index 1c671b7..6e1f28e 100644
--- a/core/core/src/main/java/androidx/core/view/ViewStructureCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewStructureCompat.java
@@ -20,7 +20,6 @@
 
 import android.view.ViewStructure;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -150,23 +149,19 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setDimens(ViewStructure viewStructure, int left, int top, int scrollX,
                 int scrollY, int width, int height) {
             viewStructure.setDimens(left, top, scrollX, scrollY, width, height);
         }
 
-        @DoNotInline
         static void setText(ViewStructure viewStructure, CharSequence charSequence) {
             viewStructure.setText(charSequence);
         }
 
-        @DoNotInline
         static void setClassName(ViewStructure viewStructure, String string) {
             viewStructure.setClassName(string);
         }
 
-        @DoNotInline
         static void setContentDescription(ViewStructure viewStructure, CharSequence charSequence) {
             viewStructure.setContentDescription(charSequence);
         }
diff --git a/core/core/src/main/java/androidx/core/view/WindowCompat.java b/core/core/src/main/java/androidx/core/view/WindowCompat.java
index 89ffb5e..679f4cf 100644
--- a/core/core/src/main/java/androidx/core/view/WindowCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowCompat.java
@@ -22,7 +22,6 @@
 import android.view.View;
 import android.view.Window;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IdRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -163,7 +162,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setDecorFitsSystemWindows(@NonNull Window window,
                 final boolean decorFitsSystemWindows) {
             final View decorView = window.getDecorView();
@@ -179,7 +177,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setDecorFitsSystemWindows(@NonNull Window window,
                 final boolean decorFitsSystemWindows) {
             window.setDecorFitsSystemWindows(decorFitsSystemWindows);
@@ -193,7 +190,6 @@
         }
 
         @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
-        @DoNotInline
         static <T> T requireViewById(Window window, int id) {
             return (T) window.requireViewById(id);
         }
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
index b5002e8..307aa6b 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
@@ -25,7 +25,6 @@
 import android.view.accessibility.AccessibilityRecord;
 import android.widget.EditText;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -582,12 +581,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isAccessibilityDataSensitive(AccessibilityEvent event) {
             return event.isAccessibilityDataSensitive();
         }
 
-        @DoNotInline
         static void setAccessibilityDataSensitive(AccessibilityEvent event,
                 boolean accessibilityDataSensitive) {
             event.setAccessibilityDataSensitive(accessibilityDataSensitive);
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java
index db071eb..12be024 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java
@@ -20,7 +20,6 @@
 import android.os.Build;
 import android.view.accessibility.AccessibilityManager;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -313,7 +312,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean isRequestFromAccessibilityTool(AccessibilityManager accessibilityManager) {
             return accessibilityManager.isRequestFromAccessibilityTool();
         }
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index 373ebd6..7fed70d 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -42,7 +42,6 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -5168,7 +5167,6 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         public static CollectionItemInfoCompat createCollectionItemInfo(int rowIndex, int rowSpan,
                 int columnIndex, int columnSpan, boolean heading, boolean selected) {
             return new CollectionItemInfoCompat(
@@ -5183,18 +5181,15 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         public static void setStateDescription(AccessibilityNodeInfo info,
                 CharSequence stateDescription) {
             info.setStateDescription(stateDescription);
         }
 
-        @DoNotInline
         public static CharSequence getStateDescription(AccessibilityNodeInfo info) {
             return info.getStateDescription();
         }
 
-        @DoNotInline
         public static Object createRangeInfo(int type, float min, float max, float current) {
             return new AccessibilityNodeInfo.RangeInfo(type, min, max, current);
         }
@@ -5206,23 +5201,19 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         public static AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo(
                 AccessibilityNodeInfo info) {
             return info.getExtraRenderingInfo();
         }
 
-        @DoNotInline
         public static boolean isTextSelectable(AccessibilityNodeInfo info) {
             return info.isTextSelectable();
         }
 
-        @DoNotInline
         public static void setTextSelectable(AccessibilityNodeInfo info, boolean selectable) {
             info.setTextSelectable(selectable);
         }
 
-        @DoNotInline
         public static CollectionItemInfoCompat buildCollectionItemInfoCompat(
                 boolean heading, int columnIndex, int rowIndex, int columnSpan,
                 int rowSpan, boolean selected, String rowTitle, String columnTitle) {
@@ -5238,37 +5229,31 @@
                     .build());
         }
 
-        @DoNotInline
         public static AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfo info, int index,
                 int prefetchingStrategy) {
             return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getChild(index,
                     prefetchingStrategy));
         }
 
-        @DoNotInline
         public static AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfo info,
                 int prefetchingStrategy) {
             return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getParent(
                     prefetchingStrategy));
         }
 
-        @DoNotInline
         public static String getUniqueId(AccessibilityNodeInfo info) {
             return info.getUniqueId();
         }
 
-        @DoNotInline
         public static void setUniqueId(AccessibilityNodeInfo info, String uniqueId) {
             info.setUniqueId(uniqueId);
         }
 
-        @DoNotInline
         public static String getCollectionItemRowTitle(Object info) {
             return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowTitle();
 
         }
 
-        @DoNotInline
         public static String getCollectionItemColumnTitle(Object info) {
             return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnTitle();
         }
@@ -5280,67 +5265,55 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         public static boolean isAccessibilityDataSensitive(AccessibilityNodeInfo info) {
             return info.isAccessibilityDataSensitive();
         }
 
-        @DoNotInline
         public static void setAccessibilityDataSensitive(AccessibilityNodeInfo info,
                 boolean accessibilityDataSensitive) {
             info.setAccessibilityDataSensitive(accessibilityDataSensitive);
         }
 
-        @DoNotInline
         public static CharSequence getContainerTitle(AccessibilityNodeInfo info) {
             return info.getContainerTitle();
         }
 
-        @DoNotInline
         public static void setContainerTitle(AccessibilityNodeInfo info,
                 CharSequence containerTitle) {
             info.setContainerTitle(containerTitle);
         }
 
-        @DoNotInline
         public static void getBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
             info.getBoundsInWindow(bounds);
         }
 
-        @DoNotInline
         public static void setBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
             info.setBoundsInWindow(bounds);
         }
 
-        @DoNotInline
         public static boolean hasRequestInitialAccessibilityFocus(AccessibilityNodeInfo info) {
             return info.hasRequestInitialAccessibilityFocus();
         }
 
-        @DoNotInline
         public static void setRequestInitialAccessibilityFocus(AccessibilityNodeInfo info,
                 boolean requestInitialAccessibilityFocus) {
             info.setRequestInitialAccessibilityFocus(requestInitialAccessibilityFocus);
         }
 
-        @DoNotInline
         public static long getMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info) {
             return info.getMinDurationBetweenContentChanges().toMillis();
         }
 
-        @DoNotInline
         public static void setMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info,
                 long duration) {
             info.setMinDurationBetweenContentChanges(Duration.ofMillis(duration));
         }
 
-        @DoNotInline
         public static void setQueryFromAppProcessEnabled(AccessibilityNodeInfo info, View view,
                 boolean enabled) {
             info.setQueryFromAppProcessEnabled(view, enabled);
         }
 
-        @DoNotInline
         public static AccessibilityNodeInfo.AccessibilityAction getActionScrollInDirection() {
             return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_IN_DIRECTION;
         }
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
index ab41730..ecb83c1 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
@@ -27,7 +27,6 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -543,67 +542,54 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void getBoundsInScreen(AccessibilityWindowInfo info, Rect outBounds) {
             info.getBoundsInScreen(outBounds);
         }
 
-        @DoNotInline
         static AccessibilityWindowInfo getChild(AccessibilityWindowInfo info, int index) {
             return info.getChild(index);
         }
 
-        @DoNotInline
         static int getChildCount(AccessibilityWindowInfo info) {
             return info.getChildCount();
         }
 
-        @DoNotInline
         static int getId(AccessibilityWindowInfo info) {
             return info.getId();
         }
 
-        @DoNotInline
         static int getLayer(AccessibilityWindowInfo info) {
             return info.getLayer();
         }
 
-        @DoNotInline
         static AccessibilityWindowInfo getParent(AccessibilityWindowInfo info) {
             return info.getParent();
         }
 
-        @DoNotInline
         static AccessibilityNodeInfo getRoot(AccessibilityWindowInfo info) {
             return info.getRoot();
         }
 
-        @DoNotInline
         static int getType(AccessibilityWindowInfo info) {
             return info.getType();
         }
 
-        @DoNotInline
         static boolean isAccessibilityFocused(AccessibilityWindowInfo info) {
             return info.isAccessibilityFocused();
         }
 
-        @DoNotInline
         static boolean isActive(AccessibilityWindowInfo info) {
             return info.isActive();
         }
 
-        @DoNotInline
         static boolean isFocused(AccessibilityWindowInfo info) {
             return info.isFocused();
         }
 
-        @DoNotInline
         static AccessibilityWindowInfo obtain() {
             return AccessibilityWindowInfo.obtain();
         }
 
-        @DoNotInline
         static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
             return AccessibilityWindowInfo.obtain(info);
         }
@@ -615,12 +601,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static AccessibilityNodeInfo getAnchor(AccessibilityWindowInfo info) {
             return info.getAnchor();
         }
 
-        @DoNotInline
         static CharSequence getTitle(AccessibilityWindowInfo info) {
             return info.getTitle();
         }
@@ -632,7 +616,6 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         static boolean isInPictureInPictureMode(AccessibilityWindowInfo info) {
             return info.isInPictureInPictureMode();
         }
@@ -644,7 +627,6 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         static AccessibilityWindowInfo instantiateAccessibilityWindowInfo() {
             return new AccessibilityWindowInfo();
         }
@@ -656,17 +638,14 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         static int getDisplayId(AccessibilityWindowInfo info) {
             return info.getDisplayId();
         }
 
-        @DoNotInline
         static void getRegionInScreen(AccessibilityWindowInfo info, Region outRegion) {
             info.getRegionInScreen(outRegion);
         }
 
-        @DoNotInline
         public static AccessibilityNodeInfoCompat getRoot(Object info, int prefetchingStrategy) {
             return AccessibilityNodeInfoCompat.wrapNonNullInstance(
                     ((AccessibilityWindowInfo) info).getRoot(prefetchingStrategy));
@@ -679,12 +658,10 @@
             // This class is non instantiable.
         }
 
-        @DoNotInline
         public static long getTransitionTimeMillis(AccessibilityWindowInfo info) {
             return info.getTransitionTimeMillis();
         }
 
-        @DoNotInline
         static LocaleList getLocales(AccessibilityWindowInfo info) {
             return info.getLocales();
         }
diff --git a/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java b/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java
index 94a6dbd..fe65e9f 100644
--- a/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java
+++ b/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java
@@ -21,7 +21,6 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -97,17 +96,14 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Interpolator createPathInterpolator(Path path) {
             return new PathInterpolator(path);
         }
 
-        @DoNotInline
         static Interpolator createPathInterpolator(float controlX, float controlY) {
             return new PathInterpolator(controlX, controlY);
         }
 
-        @DoNotInline
         static Interpolator createPathInterpolator(float controlX1, float controlY1,
                 float controlX2, float controlY2) {
             return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
diff --git a/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java b/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java
index c5c89d7..0af5435 100644
--- a/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java
+++ b/core/core/src/main/java/androidx/core/view/contentcapture/ContentCaptureSessionCompat.java
@@ -24,7 +24,6 @@
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ContentCaptureSession;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -243,7 +242,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void notifyViewsAppeared(
                 ContentCaptureSession contentCaptureSession, List<ViewStructure> appearedNodes) {
             contentCaptureSession.notifyViewsAppeared(appearedNodes);
@@ -255,37 +253,31 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void notifyViewsDisappeared(
                 ContentCaptureSession contentCaptureSession, AutofillId hostId, long[] virtualIds) {
             contentCaptureSession.notifyViewsDisappeared(hostId, virtualIds);
         }
 
-        @DoNotInline
         static void notifyViewAppeared(
                 ContentCaptureSession contentCaptureSession, ViewStructure node) {
             contentCaptureSession.notifyViewAppeared(node);
         }
-        @DoNotInline
         static ViewStructure newViewStructure(
                 ContentCaptureSession contentCaptureSession, View view) {
             return contentCaptureSession.newViewStructure(view);
         }
 
-        @DoNotInline
         static ViewStructure newVirtualViewStructure(ContentCaptureSession contentCaptureSession,
                 AutofillId parentId, long virtualId) {
             return contentCaptureSession.newVirtualViewStructure(parentId, virtualId);
         }
 
 
-        @DoNotInline
         static AutofillId newAutofillId(ContentCaptureSession contentCaptureSession,
                 AutofillId hostId, long virtualChildId) {
             return contentCaptureSession.newAutofillId(hostId, virtualChildId);
         }
 
-        @DoNotInline
         public static void notifyViewTextChanged(ContentCaptureSession contentCaptureSession,
                 AutofillId id, CharSequence charSequence) {
             contentCaptureSession.notifyViewTextChanged(id, charSequence);
@@ -298,7 +290,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Bundle getExtras(ViewStructure viewStructure) {
             return viewStructure.getExtras();
         }
diff --git a/core/core/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java b/core/core/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
index 7ef503a..0686848 100644
--- a/core/core/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
@@ -41,6 +41,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.os.BuildCompat;
 import androidx.core.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -104,7 +105,8 @@
     private static final String CONTENT_SELECTION_END_KEY =
             "androidx.core.view.inputmethod.EditorInfoCompat.CONTENT_SELECTION_END";
 
-    private static final String STYLUS_HANDWRITING_ENABLED_KEY =
+    @VisibleForTesting
+    static final String STYLUS_HANDWRITING_ENABLED_KEY =
             "androidx.core.view.inputmethod.EditorInfoCompat.STYLUS_HANDWRITING_ENABLED";
 
     @Retention(SOURCE)
@@ -209,6 +211,9 @@
      */
     public static void setStylusHandwritingEnabled(@NonNull EditorInfo editorInfo,
             boolean enabled) {
+        if (BuildCompat.isAtLeastV()) {
+            Api35Impl.setStylusHandwritingEnabled(editorInfo, enabled);
+        }
         if (editorInfo.extras == null) {
             editorInfo.extras = new Bundle();
         }
@@ -222,11 +227,14 @@
      * @see InputMethodManager#isStylusHandwritingAvailable()
      */
     public static boolean isStylusHandwritingEnabled(@NonNull EditorInfo editorInfo) {
-        if (editorInfo.extras == null) {
-            // disabled by default
-            return false;
+        if (editorInfo.extras != null
+                && editorInfo.extras.containsKey(STYLUS_HANDWRITING_ENABLED_KEY)) {
+            return editorInfo.extras.getBoolean(STYLUS_HANDWRITING_ENABLED_KEY);
         }
-        return editorInfo.extras.getBoolean(STYLUS_HANDWRITING_ENABLED_KEY);
+        if (BuildCompat.isAtLeastV()) {
+            return Api35Impl.isStylusHandwritingEnabled(editorInfo);
+        }
+        return false;
     }
 
     /**
@@ -589,4 +597,17 @@
             return editorInfo.getInitialTextAfterCursor(length, flags);
         }
     }
+
+    @RequiresApi(35)
+    private static class Api35Impl {
+        private Api35Impl() {}
+
+        static void setStylusHandwritingEnabled(@NonNull EditorInfo editorInfo, boolean enabled) {
+            editorInfo.setStylusHandwritingEnabled(enabled);
+        }
+
+        static boolean isStylusHandwritingEnabled(@NonNull EditorInfo editorInfo) {
+            return editorInfo.isStylusHandwritingEnabled();
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java b/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
index a5d317d..6bb2927 100644
--- a/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
+++ b/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
@@ -37,7 +37,6 @@
 import android.view.inputmethod.InputConnectionWrapper;
 import android.view.inputmethod.InputContentInfo;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -410,7 +409,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean commitContent(InputConnection inputConnection,
                 InputContentInfo inputContentInfo, int i, Bundle bundle) {
             return inputConnection.commitContent(inputContentInfo, i, bundle);
diff --git a/core/core/src/main/java/androidx/core/widget/CompoundButtonCompat.java b/core/core/src/main/java/androidx/core/widget/CompoundButtonCompat.java
index 9c45f41..03f2904 100644
--- a/core/core/src/main/java/androidx/core/widget/CompoundButtonCompat.java
+++ b/core/core/src/main/java/androidx/core/widget/CompoundButtonCompat.java
@@ -23,7 +23,6 @@
 import android.util.Log;
 import android.widget.CompoundButton;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -155,22 +154,18 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setButtonTintList(CompoundButton compoundButton, ColorStateList tint) {
             compoundButton.setButtonTintList(tint);
         }
 
-        @DoNotInline
         static ColorStateList getButtonTintList(CompoundButton compoundButton) {
             return compoundButton.getButtonTintList();
         }
 
-        @DoNotInline
         static void setButtonTintMode(CompoundButton compoundButton, PorterDuff.Mode tintMode) {
             compoundButton.setButtonTintMode(tintMode);
         }
 
-        @DoNotInline
         static PorterDuff.Mode getButtonTintMode(CompoundButton compoundButton) {
             return compoundButton.getButtonTintMode();
         }
@@ -182,7 +177,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Drawable getButtonDrawable(CompoundButton compoundButton) {
             return compoundButton.getButtonDrawable();
         }
diff --git a/core/core/src/main/java/androidx/core/widget/EdgeEffectCompat.java b/core/core/src/main/java/androidx/core/widget/EdgeEffectCompat.java
index 3723d24..9c4fe05 100644
--- a/core/core/src/main/java/androidx/core/widget/EdgeEffectCompat.java
+++ b/core/core/src/main/java/androidx/core/widget/EdgeEffectCompat.java
@@ -25,7 +25,6 @@
 import android.widget.OverScroller;
 import android.widget.Scroller;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -306,7 +305,6 @@
     private static class Api31Impl {
         private Api31Impl() {}
 
-        @DoNotInline
         public static EdgeEffect create(Context context, AttributeSet attrs) {
             try {
                 return new EdgeEffect(context, attrs);
@@ -315,7 +313,6 @@
             }
         }
 
-        @DoNotInline
         public static float onPullDistance(
                 EdgeEffect edgeEffect,
                 float deltaDistance,
@@ -329,7 +326,6 @@
             }
         }
 
-        @DoNotInline
         public static float getDistance(EdgeEffect edgeEffect) {
             try {
                 return edgeEffect.getDistance();
@@ -345,7 +341,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
             edgeEffect.onPull(deltaDistance, displacement);
         }
diff --git a/core/core/src/main/java/androidx/core/widget/ImageViewCompat.java b/core/core/src/main/java/androidx/core/widget/ImageViewCompat.java
index 1610002..bf73151 100644
--- a/core/core/src/main/java/androidx/core/widget/ImageViewCompat.java
+++ b/core/core/src/main/java/androidx/core/widget/ImageViewCompat.java
@@ -22,7 +22,6 @@
 import android.os.Build;
 import android.widget.ImageView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -115,22 +114,18 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ColorStateList getImageTintList(ImageView imageView) {
             return imageView.getImageTintList();
         }
 
-        @DoNotInline
         static void setImageTintList(ImageView imageView, ColorStateList tint) {
             imageView.setImageTintList(tint);
         }
 
-        @DoNotInline
         static PorterDuff.Mode getImageTintMode(ImageView imageView) {
             return imageView.getImageTintMode();
         }
 
-        @DoNotInline
         static void setImageTintMode(ImageView imageView, PorterDuff.Mode tintMode) {
             imageView.setImageTintMode(tintMode);
         }
diff --git a/core/core/src/main/java/androidx/core/widget/NestedScrollView.java b/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
index 8eebc91..c7dd035 100644
--- a/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
+++ b/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
@@ -48,7 +48,6 @@
 import android.widget.OverScroller;
 import android.widget.ScrollView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -2600,7 +2599,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean getClipToPadding(ViewGroup viewGroup) {
             return viewGroup.getClipToPadding();
         }
diff --git a/core/core/src/main/java/androidx/core/widget/PopupWindowCompat.java b/core/core/src/main/java/androidx/core/widget/PopupWindowCompat.java
index 93e4bf0..f23c596 100644
--- a/core/core/src/main/java/androidx/core/widget/PopupWindowCompat.java
+++ b/core/core/src/main/java/androidx/core/widget/PopupWindowCompat.java
@@ -21,7 +21,6 @@
 import android.view.View;
 import android.widget.PopupWindow;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -204,22 +203,18 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
             popupWindow.setOverlapAnchor(overlapAnchor);
         }
 
-        @DoNotInline
         static boolean getOverlapAnchor(PopupWindow popupWindow) {
             return popupWindow.getOverlapAnchor();
         }
 
-        @DoNotInline
         static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
             popupWindow.setWindowLayoutType(layoutType);
         }
 
-        @DoNotInline
         static int getWindowLayoutType(PopupWindow popupWindow) {
             return popupWindow.getWindowLayoutType();
         }
diff --git a/core/core/src/main/java/androidx/core/widget/TextViewCompat.java b/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
index b02cad1..1bdbdd9 100644
--- a/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
+++ b/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
@@ -51,7 +51,6 @@
 import android.view.inputmethod.EditorInfo;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.FloatRange;
 import androidx.annotation.IntDef;
@@ -1026,12 +1025,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setAutoSizeTextTypeWithDefaults(TextView textView, int autoSizeTextType) {
             textView.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
         }
 
-        @DoNotInline
         static void setAutoSizeTextTypeUniformWithConfiguration(TextView textView,
                 int autoSizeMinTextSize, int autoSizeMaxTextSize, int autoSizeStepGranularity,
                 int unit) {
@@ -1039,33 +1036,27 @@
                     autoSizeMaxTextSize, autoSizeStepGranularity, unit);
         }
 
-        @DoNotInline
         static void setAutoSizeTextTypeUniformWithPresetSizes(TextView textView, int[] presetSizes,
                 int unit) {
             textView.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
         }
 
-        @DoNotInline
         static int getAutoSizeTextType(TextView textView) {
             return textView.getAutoSizeTextType();
         }
 
-        @DoNotInline
         static int getAutoSizeStepGranularity(TextView textView) {
             return textView.getAutoSizeStepGranularity();
         }
 
-        @DoNotInline
         static int getAutoSizeMinTextSize(TextView textView) {
             return textView.getAutoSizeMinTextSize();
         }
 
-        @DoNotInline
         static int getAutoSizeMaxTextSize(TextView textView) {
             return textView.getAutoSizeMaxTextSize();
         }
 
-        @DoNotInline
         static int[] getAutoSizeTextAvailableSizes(TextView textView) {
             return textView.getAutoSizeTextAvailableSizes();
         }
@@ -1077,22 +1068,18 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setFirstBaselineToTopHeight(TextView textView, int firstBaselineToTopHeight) {
             textView.setFirstBaselineToTopHeight(firstBaselineToTopHeight);
         }
 
-        @DoNotInline
         static PrecomputedText.Params getTextMetricsParams(TextView textView) {
             return textView.getTextMetricsParams();
         }
 
-        @DoNotInline
         static String[] getDigitStrings(DecimalFormatSymbols decimalFormatSymbols) {
             return decimalFormatSymbols.getDigitStrings();
         }
 
-        @DoNotInline
         static CharSequence castToCharSequence(PrecomputedText precomputedText) {
             return precomputedText;
         }
@@ -1105,42 +1092,34 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getBreakStrategy(TextView textView) {
             return textView.getBreakStrategy();
         }
 
-        @DoNotInline
         static void setBreakStrategy(TextView textView, int breakStrategy) {
             textView.setBreakStrategy(breakStrategy);
         }
 
-        @DoNotInline
         static int getHyphenationFrequency(TextView textView) {
             return textView.getHyphenationFrequency();
         }
 
-        @DoNotInline
         static void setHyphenationFrequency(TextView textView, int hyphenationFrequency) {
             textView.setHyphenationFrequency(hyphenationFrequency);
         }
 
-        @DoNotInline
         static PorterDuff.Mode getCompoundDrawableTintMode(TextView textView) {
             return textView.getCompoundDrawableTintMode();
         }
 
-        @DoNotInline
         static ColorStateList getCompoundDrawableTintList(TextView textView) {
             return textView.getCompoundDrawableTintList();
         }
 
-        @DoNotInline
         static void setCompoundDrawableTintList(TextView textView, ColorStateList tint) {
             textView.setCompoundDrawableTintList(tint);
         }
 
-        @DoNotInline
         static void setCompoundDrawableTintMode(TextView textView, PorterDuff.Mode tintMode) {
             textView.setCompoundDrawableTintMode(tintMode);
         }
@@ -1152,7 +1131,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static DecimalFormatSymbols getInstance(Locale locale) {
             return DecimalFormatSymbols.getInstance(locale);
         }
@@ -1164,7 +1142,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         public static void setLineHeight(
                 @NonNull TextView textView,
                 int unit,
diff --git a/core/core/src/main/res/values/attrs.xml b/core/core/src/main/res/values/attrs.xml
index 45ab302..dc968df 100644
--- a/core/core/src/main/res/values/attrs.xml
+++ b/core/core/src/main/res/values/attrs.xml
@@ -28,6 +28,10 @@
         <!-- The query to be sent over to the provider. Refer to your font provider's documentation
         on the format of this string. -->
         <attr name="fontProviderQuery" format="string" />
+        <!-- The query (for the same font provider) for a font to be used for
+         {@link android.graphics.Typeface.CustomFallbackBuilder custom font fallback}.
+         Ignored prior to API 29. -->
+        <attr name="fontProviderFallbackQuery" format="string" />
         <!-- The sets of hashes for the certificates the provider should be signed with. This is
         used to verify the identity of the provider, and is only required if the provider is not
         part of the system image. This value may point to one list or a list of lists, where each
@@ -49,7 +53,7 @@
               default typeface will be used instead. -->
             <enum name="blocking" value="0" />
             <!-- The async font fetch works as follows.
-              First, check the local cache, then if the requeted font is not cached, trigger a
+              First, check the local cache, then if the requested font is not cached, trigger a
               request the font and continue with layout inflation. Once the font fetch succeeds, the
               target text view will be refreshed with the downloaded font data. The
               fontProviderFetchTimeout will be ignored if async loading is specified. -->
@@ -79,7 +83,7 @@
             <enum name="italic" value="1" />
         </attr>
         <!-- The reference to the font file to be used. This should be a file in the res/font folder
-         and should therefore have an R reference value. E.g. @font/myfont -->
+         and should therefore have an R reference value. e.g. @font/myfont -->
         <attr name="font" format="reference" />
         <!-- The weight of the given font file. This will be used when the font is being loaded into
          the font stack and will override any weight information in the font's header tables. Must
diff --git a/core/haptics/haptics/build.gradle b/core/haptics/haptics/build.gradle
index 9fcb353..4fc8139 100644
--- a/core/haptics/haptics/build.gradle
+++ b/core/haptics/haptics/build.gradle
@@ -33,7 +33,7 @@
 dependencies {
     api(libs.kotlinStdlib)
 
-    implementation("androidx.annotation:annotation:1.8.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation(projectOrArtifact(":core:core"))
     implementation(projectOrArtifact(":media:media"))
 
@@ -43,6 +43,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.core.haptics"
 }
 
@@ -52,7 +53,6 @@
     inceptionYear = "2023"
     description = "Core Haptics Libraries to help navigate different device and Android SDK " +
             "functionalities and create reliable haptic effects across all of Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":core:haptics:haptics-samples"))
 }
diff --git a/core/haptics/haptics/integration-tests/demos/build.gradle b/core/haptics/haptics/integration-tests/demos/build.gradle
index 9695d73..d4f87ae 100644
--- a/core/haptics/haptics/integration-tests/demos/build.gradle
+++ b/core/haptics/haptics/integration-tests/demos/build.gradle
@@ -28,6 +28,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         applicationId "androidx.core.haptics.demos"
     }
diff --git a/core/haptics/haptics/samples/build.gradle b/core/haptics/haptics/samples/build.gradle
index 2f6769e..3892ef1 100644
--- a/core/haptics/haptics/samples/build.gradle
+++ b/core/haptics/haptics/samples/build.gradle
@@ -25,6 +25,7 @@
     implementation(project(":core:haptics:haptics"))
 }
 android {
+    compileSdk 35
     namespace "androidx.core.haptics.samples"
 }
 androidx {
diff --git a/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt b/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt
index 7ae3b60..09ade0c 100644
--- a/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt
+++ b/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticAttributesConverter.kt
@@ -20,7 +20,6 @@
 import android.media.AudioAttributes
 import android.os.Build
 import android.os.VibrationAttributes
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.haptics.AttributesWrapper
 import androidx.core.haptics.AudioAttributesWrapper
@@ -92,7 +91,6 @@
     private object Api33Impl {
 
         @JvmStatic
-        @DoNotInline
         fun toVibrationAttributesUsage(@HapticAttributes.Usage usage: Int): Int =
             when (usage) {
                 // Check this usage constant exists in this SDK level.
@@ -118,19 +116,16 @@
     private object Api30Impl {
 
         @JvmStatic
-        @DoNotInline
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @HapticAttributes.Usage
         fun fromVibrationAttributesUsage(attrs: VibrationAttributes): Int = attrs.usage
 
         @JvmStatic
-        @DoNotInline
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @HapticAttributes.Flag
         fun fromVibrationAttributesFlags(attrs: VibrationAttributes): Int = attrs.flags
 
         @JvmStatic
-        @DoNotInline
         fun createVibrationAttributes(
             vibrationUsage: Int,
             vibrationFlags: Int
@@ -142,7 +137,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun toVibrationAttributesUsage(@HapticAttributes.Usage usage: Int): Int =
             when (usage) {
                 // Check this usage constant exists in this SDK level.
@@ -157,7 +151,6 @@
             }
 
         @JvmStatic
-        @DoNotInline
         fun toVibrationAttributesFlags(@HapticAttributes.Flag flags: Int): Int =
             flags and VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
     }
@@ -167,7 +160,6 @@
     private object Api26Impl {
 
         @JvmStatic
-        @DoNotInline
         @HapticAttributes.Usage
         fun fromAudioAttributesUsage(attrs: AudioAttributes): Int =
             when (attrs.usage) {
@@ -181,7 +173,6 @@
     private object Api21Impl {
 
         @JvmStatic
-        @DoNotInline
         @Suppress("DEPRECATION") // ApkVariant for compatibility
         @HapticAttributes.Usage
         fun fromAudioAttributesUsage(attrs: AudioAttributes): Int =
@@ -206,10 +197,9 @@
                 else -> HapticAttributes.USAGE_UNKNOWN
             }
 
-        @JvmStatic @DoNotInline @HapticAttributes.Flag fun fromAudioAttributesFlags(): Int = 0
+        @JvmStatic @HapticAttributes.Flag fun fromAudioAttributesFlags(): Int = 0
 
         @JvmStatic
-        @DoNotInline
         fun createAudioAttributes(
             usage: Int,
             contentType: Int,
diff --git a/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticSignalConverter.kt b/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticSignalConverter.kt
index 8f3b40f..7e99ca6 100644
--- a/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticSignalConverter.kt
+++ b/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/HapticSignalConverter.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.os.VibrationEffect
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.core.haptics.PatternVibrationWrapper
 import androidx.core.haptics.VibrationEffectWrapper
@@ -76,7 +75,6 @@
     @RequiresApi(31)
     private object Api31Impl {
         @JvmStatic
-        @DoNotInline
         fun toVibrationEffect(composition: CompositionSignal): VibrationEffectWrapper? =
             if (composition.minSdk() <= 31) {
                 // Use same API to create composition from API 30, but allow constants from API 31.
@@ -90,7 +88,6 @@
     @RequiresApi(30)
     private object Api30Impl {
         @JvmStatic
-        @DoNotInline
         fun toVibrationEffect(composition: CompositionSignal): VibrationEffectWrapper? =
             if (composition.minSdk() <= 30) {
                 createComposition(composition)
@@ -99,7 +96,6 @@
             }
 
         @JvmStatic
-        @DoNotInline
         fun createComposition(composition: CompositionSignal): VibrationEffectWrapper? {
             val platformComposition = VibrationEffect.startComposition()
             var delayMs = 0
@@ -128,7 +124,6 @@
     @RequiresApi(29)
     private object Api29Impl {
         @JvmStatic
-        @DoNotInline
         fun toVibrationEffect(effect: PredefinedEffectSignal): VibrationEffectWrapper? =
             if (effect.minSdk() <= 29) {
                 VibrationEffectWrapper(VibrationEffect.createPredefined(effect.type))
@@ -142,7 +137,6 @@
     private object Api26Impl {
 
         @JvmStatic
-        @DoNotInline
         fun toVibrationEffect(
             initialWaveform: WaveformSignal? = null,
             repeatingWaveform: WaveformSignal? = null,
diff --git a/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/VibratorWrapperImpl.kt b/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/VibratorWrapperImpl.kt
index 25cac36..943a52f 100644
--- a/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/VibratorWrapperImpl.kt
+++ b/core/haptics/haptics/src/main/java/androidx/core/haptics/impl/VibratorWrapperImpl.kt
@@ -22,7 +22,6 @@
 import android.os.VibrationAttributes
 import android.os.VibrationEffect
 import android.os.Vibrator
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RequiresPermission
 import androidx.core.haptics.AttributesWrapper
@@ -116,7 +115,6 @@
     private object Api33Impl {
 
         @JvmStatic
-        @DoNotInline
         @RequiresPermission(android.Manifest.permission.VIBRATE)
         fun vibrate(
             vibrator: Vibrator,
@@ -140,7 +138,6 @@
 
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @JvmStatic
-        @DoNotInline
         fun getPrimitivesDurations(
             vibrator: Vibrator,
             primitives: IntArray,
@@ -155,7 +152,6 @@
 
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @JvmStatic
-        @DoNotInline
         fun areEffectsSupported(
             vibrator: Vibrator,
             effects: IntArray,
@@ -174,7 +170,6 @@
 
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @JvmStatic
-        @DoNotInline
         fun arePrimitivesSupported(
             vibrator: Vibrator,
             primitives: IntArray,
@@ -187,12 +182,9 @@
     @RequiresApi(26)
     private object Api26Impl {
 
-        @JvmStatic
-        @DoNotInline
-        fun hasAmplitudeControl(vibrator: Vibrator) = vibrator.hasAmplitudeControl()
+        @JvmStatic fun hasAmplitudeControl(vibrator: Vibrator) = vibrator.hasAmplitudeControl()
 
         @JvmStatic
-        @DoNotInline
         @Suppress("DEPRECATION") // ApkVariant for compatibility
         @RequiresPermission(android.Manifest.permission.VIBRATE)
         fun vibrate(
@@ -215,7 +207,6 @@
     private object Api21Impl {
 
         @JvmStatic
-        @DoNotInline
         @Suppress("DEPRECATION") // ApkVariant for compatibility
         @RequiresPermission(android.Manifest.permission.VIBRATE)
         fun vibrate(
diff --git a/core/uwb/uwb-rxjava3/build.gradle b/core/uwb/uwb-rxjava3/build.gradle
index 209bb343..adac4bb 100644
--- a/core/uwb/uwb-rxjava3/build.gradle
+++ b/core/uwb/uwb-rxjava3/build.gradle
@@ -61,6 +61,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "RxJava3 integration for UWB module"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/core/uwb/uwb/build.gradle b/core/uwb/uwb/build.gradle
index 337d95b..4151710 100644
--- a/core/uwb/uwb/build.gradle
+++ b/core/uwb/uwb/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core-ktx:1.2.0")
     api(libs.kotlinCoroutinesAndroid)
     implementation(libs.guavaAndroid)
@@ -56,7 +56,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Public API surface for apps to use UWB (ultra-wideband) on supported devices."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/credentials/credentials-e2ee/build.gradle b/credentials/credentials-e2ee/build.gradle
index a3fabe6..4216c7a 100644
--- a/credentials/credentials-e2ee/build.gradle
+++ b/credentials/credentials-e2ee/build.gradle
@@ -24,7 +24,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.5.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("com.google.crypto.tink:tink-android:1.8.0")
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.testExtJunit)
diff --git a/credentials/credentials-e2ee/src/main/java/androidx/credentials/playservices/e2ee/androidx-credentials-credentials-play-services-e2ee-documentation.md b/credentials/credentials-e2ee/src/main/java/androidx/credentials/playservices/e2ee/androidx-credentials-credentials-play-services-e2ee-documentation.md
new file mode 100644
index 0000000..85f3a1e
--- /dev/null
+++ b/credentials/credentials-e2ee/src/main/java/androidx/credentials/playservices/e2ee/androidx-credentials-credentials-play-services-e2ee-documentation.md
@@ -0,0 +1,7 @@
+# Module root
+
+CREDENTIALS CREDENTIALS PLAY SERVICES E2EE
+
+# Package androidx.credentials.playservices.e2ee
+
+This package allows developers to use E2EE contact keys.
diff --git a/credentials/credentials-fido/build.gradle b/credentials/credentials-fido/build.gradle
index 8d0fd93..89b1cd52e 100644
--- a/credentials/credentials-fido/build.gradle
+++ b/credentials/credentials-fido/build.gradle
@@ -46,6 +46,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.credentials.fido"
 }
 
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta02.txt b/credentials/credentials-play-services-auth/api/1.3.0-beta02.txt
deleted file mode 100644
index e6f50d0..0000000
--- a/credentials/credentials-play-services-auth/api/1.3.0-beta02.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 4.0
diff --git a/credentials/credentials-play-services-auth/api/res-1.3.0-beta02.txt b/credentials/credentials-play-services-auth/api/res-1.3.0-beta02.txt
deleted file mode 100644
index e69de29..0000000
--- a/credentials/credentials-play-services-auth/api/res-1.3.0-beta02.txt
+++ /dev/null
diff --git a/credentials/credentials-play-services-auth/api/restricted_1.3.0-beta01.txt b/credentials/credentials-play-services-auth/api/restricted_1.3.0-beta01.txt
deleted file mode 100644
index e6f50d0..0000000
--- a/credentials/credentials-play-services-auth/api/restricted_1.3.0-beta01.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 4.0
diff --git a/credentials/credentials-play-services-auth/api/restricted_1.3.0-beta02.txt b/credentials/credentials-play-services-auth/api/restricted_1.3.0-beta02.txt
deleted file mode 100644
index e6f50d0..0000000
--- a/credentials/credentials-play-services-auth/api/restricted_1.3.0-beta02.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 4.0
diff --git a/credentials/credentials-play-services-auth/build.gradle b/credentials/credentials-play-services-auth/build.gradle
index 6b0091c..abd64a6 100644
--- a/credentials/credentials-play-services-auth/build.gradle
+++ b/credentials/credentials-play-services-auth/build.gradle
@@ -48,6 +48,11 @@
         exclude group: "androidx.core"
     }
 
+    implementation(libs.playServicesBlockstore){
+        exclude group: "androidx.loader"
+        exclude group: "androidx.core"
+    }
+
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
@@ -63,6 +68,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.credentials.play.services.auth"
 
     buildTypes.configureEach {
@@ -75,6 +81,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "sign into apps using play-services-auth library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/credentials/credentials-play-services-auth/lint-baseline.xml b/credentials/credentials-play-services-auth/lint-baseline.xml
index 85ca277..d1ed771 100644
--- a/credentials/credentials-play-services-auth/lint-baseline.xml
+++ b/credentials/credentials-play-services-auth/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="UsesNonDefaultVisibleForTesting"
@@ -13,7 +13,7 @@
     <issue
         id="UsesNonDefaultVisibleForTesting"
         message="Found non-default `otherwise` value for @VisibleForTesting"
-        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) lateinit var executor: Executor"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt"/>
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
index 45a4a08..8bed458 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
@@ -21,26 +21,37 @@
 import android.util.Log
 import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
+import androidx.credentials.ClearCredentialRequestTypes
 import androidx.credentials.ClearCredentialStateRequest
 import androidx.credentials.CreateCredentialRequest
 import androidx.credentials.CreateCredentialResponse
 import androidx.credentials.CreatePasswordRequest
 import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.CreateRestoreCredentialRequest
 import androidx.credentials.CredentialManagerCallback
 import androidx.credentials.CredentialProvider
 import androidx.credentials.GetCredentialRequest
 import androidx.credentials.GetCredentialResponse
+import androidx.credentials.GetRestoreCredentialOption
 import androidx.credentials.exceptions.ClearCredentialException
+import androidx.credentials.exceptions.ClearCredentialProviderConfigurationException
 import androidx.credentials.exceptions.ClearCredentialUnknownException
 import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException
 import androidx.credentials.exceptions.GetCredentialException
+import androidx.credentials.exceptions.GetCredentialProviderConfigurationException
 import androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController
 import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController
 import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.CredentialProviderCreatePublicKeyCredentialController
+import androidx.credentials.playservices.controllers.CreateRestoreCredential.CredentialProviderCreateRestoreCredentialController
+import androidx.credentials.playservices.controllers.GetRestoreCredential.CredentialProviderGetRestoreCredentialController
 import androidx.credentials.playservices.controllers.GetSignInIntent.CredentialProviderGetSignInIntentController
 import com.google.android.gms.auth.api.identity.Identity
+import com.google.android.gms.auth.blockstore.restorecredential.RestoreCredential
+import com.google.android.gms.auth.blockstore.restorecredential.RestoreCredentialStatusCodes
 import com.google.android.gms.common.ConnectionResult
 import com.google.android.gms.common.GoogleApiAvailability
+import com.google.android.gms.common.api.ApiException
 import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
 import java.util.concurrent.Executor
 
@@ -61,7 +72,23 @@
         if (cancellationReviewer(cancellationSignal)) {
             return
         }
-        if (isGetSignInIntentRequest(request)) {
+        if (isGetRestoreCredentialRequest(request)) {
+            if (!isAvailableOnDevice(MIN_GMS_APK_VERSION_RESTORE_CRED)) {
+                cancellationReviewerWithCallback(cancellationSignal) {
+                    executor.execute {
+                        callback.onError(
+                            GetCredentialProviderConfigurationException(
+                                "getCredentialAsync no provider dependencies found - please ensure " +
+                                    "the desired provider dependencies are added"
+                            )
+                        )
+                    }
+                }
+                return
+            }
+            CredentialProviderGetRestoreCredentialController(context)
+                .invokePlayServices(request, callback, executor, cancellationSignal)
+        } else if (isGetSignInIntentRequest(request)) {
             CredentialProviderGetSignInIntentController(context)
                 .invokePlayServices(request, callback, executor, cancellationSignal)
         } else {
@@ -90,6 +117,23 @@
                 CredentialProviderCreatePublicKeyCredentialController.getInstance(context)
                     .invokePlayServices(request, callback, executor, cancellationSignal)
             }
+            is CreateRestoreCredentialRequest -> {
+                if (!isAvailableOnDevice(MIN_GMS_APK_VERSION_RESTORE_CRED)) {
+                    cancellationReviewerWithCallback(cancellationSignal) {
+                        executor.execute {
+                            callback.onError(
+                                CreateCredentialProviderConfigurationException(
+                                    "createCredentialAsync no provider dependencies found - please ensure the " +
+                                        "desired provider dependencies are added"
+                                )
+                            )
+                        }
+                    }
+                    return
+                }
+                CredentialProviderCreateRestoreCredentialController(context)
+                    .invokePlayServices(request, callback, executor, cancellationSignal)
+            }
             else -> {
                 throw UnsupportedOperationException(
                     "Create Credential request is unsupported, not password or " +
@@ -100,7 +144,11 @@
     }
 
     override fun isAvailableOnDevice(): Boolean {
-        val resultCode = isGooglePlayServicesAvailable(context)
+        return isAvailableOnDevice(MIN_GMS_APK_VERSION)
+    }
+
+    fun isAvailableOnDevice(minApkVersion: Int): Boolean {
+        val resultCode = isGooglePlayServicesAvailable(context, minApkVersion)
         val isSuccessful = resultCode == ConnectionResult.SUCCESS
         if (!isSuccessful) {
             val connectionResult = ConnectionResult(resultCode)
@@ -118,10 +166,10 @@
     // There is one error code that supports retry API_DISABLED_FOR_CONNECTION but it would not
     // be useful to retry that one because our connection to GMSCore is a static variable
     // (see GoogleApiAvailability.getInstance()) so we cannot recreate the connection to retry.
-    private fun isGooglePlayServicesAvailable(context: Context): Int {
+    private fun isGooglePlayServicesAvailable(context: Context, minApkVersion: Int): Int {
         return googleApiAvailability.isGooglePlayServicesAvailable(
             context,
-            /*minApkVersion=*/ MIN_GMS_APK_VERSION
+            /*minApkVersion=*/ minApkVersion
         )
     }
 
@@ -134,30 +182,77 @@
         if (cancellationReviewer(cancellationSignal)) {
             return
         }
-        Identity.getSignInClient(context)
-            .signOut()
-            .addOnSuccessListener {
-                cancellationReviewerWithCallback(
-                    cancellationSignal,
-                    {
-                        Log.i(TAG, "During clear credential, signed out successfully!")
+        if (request.requestType == ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL) {
+            if (!isAvailableOnDevice(MIN_GMS_APK_VERSION_RESTORE_CRED)) {
+                cancellationReviewerWithCallback(cancellationSignal) {
+                    executor.execute {
+                        callback.onError(
+                            ClearCredentialProviderConfigurationException(
+                                "clearCredentialStateAsync no provider dependencies found - please ensure the " +
+                                    "desired provider dependencies are added"
+                            )
+                        )
+                    }
+                }
+                return
+            }
+            RestoreCredential.getRestoreCredentialClient(context)
+                .clearRestoreCredential(
+                    com.google.android.gms.auth.blockstore.restorecredential
+                        .ClearRestoreCredentialRequest(request.requestBundle)
+                )
+                .addOnSuccessListener {
+                    cancellationReviewerWithCallback(cancellationSignal) {
+                        Log.i(TAG, "Cleared restore credential successfully!")
                         executor.execute { callback.onResult(null) }
                     }
-                )
-            }
-            .addOnFailureListener { e ->
-                run {
+                }
+                .addOnFailureListener { e ->
+                    Log.w(TAG, "Clearing restore credential failed", e)
+                    var clearException: ClearCredentialException =
+                        ClearCredentialUnknownException(
+                            "Clear restore credential failed for unknown reason."
+                        )
+                    if (e is ApiException) {
+                        when (e.statusCode) {
+                            RestoreCredentialStatusCodes.RESTORE_CREDENTIAL_INTERNAL_FAILURE -> {
+                                clearException =
+                                    ClearCredentialUnknownException(
+                                        "The restore credential internal service had a failure."
+                                    )
+                            }
+                        }
+                    }
+                    cancellationReviewerWithCallback(cancellationSignal) {
+                        executor.execute { callback.onError(clearException) }
+                    }
+                }
+        } else {
+            Identity.getSignInClient(context)
+                .signOut()
+                .addOnSuccessListener {
                     cancellationReviewerWithCallback(
                         cancellationSignal,
                         {
-                            Log.w(TAG, "During clear credential sign out failed with $e")
-                            executor.execute {
-                                callback.onError(ClearCredentialUnknownException(e.message))
-                            }
+                            Log.i(TAG, "During clear credential, signed out successfully!")
+                            executor.execute { callback.onResult(null) }
                         }
                     )
                 }
-            }
+                .addOnFailureListener { e ->
+                    run {
+                        cancellationReviewerWithCallback(
+                            cancellationSignal,
+                            {
+                                Log.w(TAG, "During clear credential sign out failed with $e")
+                                executor.execute {
+                                    callback.onError(ClearCredentialUnknownException(e.message))
+                                }
+                            }
+                        )
+                    }
+                }
+        }
     }
 
     companion object {
@@ -166,6 +261,8 @@
         // This points to the min APK version of GMS that contains required changes
         // to make passkeys work well
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) const val MIN_GMS_APK_VERSION = 230815045
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        const val MIN_GMS_APK_VERSION_RESTORE_CRED = 242200000
 
         internal fun cancellationReviewerWithCallback(
             cancellationSignal: CancellationSignal?,
@@ -196,5 +293,14 @@
             }
             return false
         }
+
+        internal fun isGetRestoreCredentialRequest(request: GetCredentialRequest): Boolean {
+            for (option in request.credentialOptions) {
+                if (option is GetRestoreCredentialOption) {
+                    return true
+                }
+            }
+            return false
+        }
     }
 }
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
index 74932be..f76e8ab 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
@@ -24,7 +24,6 @@
 import android.os.Build
 import android.util.Base64
 import android.util.Log
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.credentials.CreatePublicKeyCredentialRequest
 import androidx.credentials.GetPublicKeyCredentialOption
@@ -599,8 +598,6 @@
 
     @RequiresApi(28)
     private object GetGMSVersion {
-        @JvmStatic
-        @DoNotInline
-        fun getVersionLong(info: PackageInfo): Long = info.getLongVersionCode()
+        @JvmStatic fun getVersionLong(info: PackageInfo): Long = info.getLongVersionCode()
     }
 }
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreateRestoreCredential/CredentialProviderCreateRestoreCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreateRestoreCredential/CredentialProviderCreateRestoreCredentialController.kt
new file mode 100644
index 0000000..84eec66
--- /dev/null
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreateRestoreCredential/CredentialProviderCreateRestoreCredentialController.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.playservices.controllers.CreateRestoreCredential
+
+import android.content.Context
+import android.os.CancellationSignal
+import androidx.credentials.CreateCredentialResponse
+import androidx.credentials.CreateRestoreCredentialRequest
+import androidx.credentials.CredentialManagerCallback
+import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.CreateCredentialUnknownException
+import androidx.credentials.exceptions.domerrors.DataError
+import androidx.credentials.exceptions.restorecredential.CreateRestoreCredentialDomException
+import androidx.credentials.exceptions.restorecredential.E2eeUnavailableException
+import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
+import androidx.credentials.playservices.controllers.CredentialProviderController
+import com.google.android.gms.auth.blockstore.restorecredential.CreateRestoreCredentialResponse
+import com.google.android.gms.auth.blockstore.restorecredential.RestoreCredential
+import com.google.android.gms.auth.blockstore.restorecredential.RestoreCredentialStatusCodes
+import com.google.android.gms.common.api.ApiException
+import java.util.concurrent.Executor
+
+/** A controller to handle the CreateRestoreCredential flow with play services. */
+internal class CredentialProviderCreateRestoreCredentialController(private val context: Context) :
+    CredentialProviderController<
+        CreateRestoreCredentialRequest,
+        com.google.android.gms.auth.blockstore.restorecredential.CreateRestoreCredentialRequest,
+        CreateRestoreCredentialResponse,
+        CreateCredentialResponse,
+        CreateCredentialException
+    >(context) {
+
+    override fun invokePlayServices(
+        request: CreateRestoreCredentialRequest,
+        callback: CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>,
+        executor: Executor,
+        cancellationSignal: CancellationSignal?
+    ) {
+        if (CredentialProviderPlayServicesImpl.cancellationReviewer(cancellationSignal)) {
+            return
+        }
+
+        val convertedRequest = this.convertRequestToPlayServices(request)
+        RestoreCredential.getRestoreCredentialClient(context)
+            .createRestoreCredential(convertedRequest)
+            .addOnSuccessListener {
+                try {
+                    val response = this.convertResponseToCredentialManager(it)
+                    cancelOrCallbackExceptionOrResult(cancellationSignal) {
+                        executor.execute { callback.onResult(response) }
+                    }
+                } catch (e: Exception) {
+                    cancelOrCallbackExceptionOrResult(cancellationSignal) {
+                        executor.execute {
+                            callback.onError(CreateCredentialUnknownException(e.message))
+                        }
+                    }
+                }
+            }
+            .addOnFailureListener { e ->
+                var createException: CreateCredentialException =
+                    CreateCredentialUnknownException(
+                        "Create restore credential failed for unknown reason, failure: ${e.message}"
+                    )
+                if (e is ApiException) {
+                    when (e.statusCode) {
+                        RestoreCredentialStatusCodes.RESTORE_CREDENTIAL_E2EE_UNAVAILABLE -> {
+                            createException =
+                                E2eeUnavailableException(
+                                    "E2ee is not available on the device. Check whether the backup and screen lock are enabled."
+                                )
+                        }
+                        RestoreCredentialStatusCodes.RESTORE_CREDENTIAL_FIDO_FAILURE -> {
+                            createException =
+                                CreateRestoreCredentialDomException(
+                                    DataError(),
+                                    "The request did not match the fido spec, failure: ${e.message}"
+                                )
+                        }
+                        RestoreCredentialStatusCodes.RESTORE_CREDENTIAL_INTERNAL_FAILURE -> {
+                            createException =
+                                CreateCredentialUnknownException(
+                                    "The restore credential internal service had a failure, failure: ${e.message}"
+                                )
+                        }
+                        else -> {
+                            createException =
+                                CreateCredentialUnknownException(
+                                    "The restore credential service failed with unsupported status code, failure: ${e.message}, " +
+                                        "status code: ${e.statusCode}"
+                                )
+                        }
+                    }
+                }
+                cancelOrCallbackExceptionOrResult(cancellationSignal) {
+                    executor.execute { callback.onError(createException) }
+                }
+            }
+    }
+
+    public override fun convertRequestToPlayServices(
+        request: CreateRestoreCredentialRequest
+    ): com.google.android.gms.auth.blockstore.restorecredential.CreateRestoreCredentialRequest {
+        return com.google.android.gms.auth.blockstore.restorecredential
+            .CreateRestoreCredentialRequest(request.credentialData)
+    }
+
+    public override fun convertResponseToCredentialManager(
+        response: CreateRestoreCredentialResponse
+    ): CreateCredentialResponse {
+        return androidx.credentials.CreateRestoreCredentialResponse.createFrom(
+            response.responseBundle
+        )
+    }
+}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/GetRestoreCredential/CredentialProviderGetRestoreCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/GetRestoreCredential/CredentialProviderGetRestoreCredentialController.kt
new file mode 100644
index 0000000..db23b03
--- /dev/null
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/GetRestoreCredential/CredentialProviderGetRestoreCredentialController.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.playservices.controllers.GetRestoreCredential
+
+import android.content.Context
+import android.os.CancellationSignal
+import androidx.credentials.Credential
+import androidx.credentials.CredentialManagerCallback
+import androidx.credentials.GetCredentialRequest
+import androidx.credentials.GetCredentialResponse
+import androidx.credentials.GetRestoreCredentialOption
+import androidx.credentials.exceptions.GetCredentialException
+import androidx.credentials.exceptions.GetCredentialUnknownException
+import androidx.credentials.exceptions.NoCredentialException
+import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
+import androidx.credentials.playservices.controllers.CredentialProviderController
+import com.google.android.gms.auth.blockstore.restorecredential.GetRestoreCredentialRequest
+import com.google.android.gms.auth.blockstore.restorecredential.GetRestoreCredentialResponse
+import com.google.android.gms.auth.blockstore.restorecredential.RestoreCredential
+import com.google.android.gms.auth.blockstore.restorecredential.RestoreCredentialStatusCodes
+import com.google.android.gms.common.api.ApiException
+import java.util.concurrent.Executor
+
+/** A controller to handle the GetRestoreCredential flow with play services. */
+internal class CredentialProviderGetRestoreCredentialController(private val context: Context) :
+    CredentialProviderController<
+        GetCredentialRequest,
+        GetRestoreCredentialRequest,
+        GetRestoreCredentialResponse,
+        GetCredentialResponse,
+        GetCredentialException
+    >(context) {
+
+    override fun invokePlayServices(
+        request: GetCredentialRequest,
+        callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
+        executor: Executor,
+        cancellationSignal: CancellationSignal?
+    ) {
+        if (CredentialProviderPlayServicesImpl.cancellationReviewer(cancellationSignal)) {
+            return
+        }
+
+        val convertedRequest = this.convertRequestToPlayServices(request)
+        RestoreCredential.getRestoreCredentialClient(context)
+            .getRestoreCredential(convertedRequest)
+            .addOnSuccessListener {
+                try {
+                    val response = this.convertResponseToCredentialManager(it)
+                    cancelOrCallbackExceptionOrResult(cancellationSignal) {
+                        executor.execute { callback.onResult(response) }
+                    }
+                } catch (e: Exception) {
+                    cancelOrCallbackExceptionOrResult(cancellationSignal) {
+                        executor.execute {
+                            callback.onError(
+                                if (e is NoCredentialException) e
+                                else GetCredentialUnknownException(e.message)
+                            )
+                        }
+                    }
+                }
+            }
+            .addOnFailureListener { e ->
+                var getException: GetCredentialException =
+                    GetCredentialUnknownException(
+                        "Get restore credential failed for unknown reason, failure: ${e.message}"
+                    )
+                if (e is ApiException) {
+                    when (e.statusCode) {
+                        RestoreCredentialStatusCodes.RESTORE_CREDENTIAL_INTERNAL_FAILURE -> {
+                            getException =
+                                GetCredentialUnknownException(
+                                    "The restore credential internal service had a failure, failure: ${e.message}"
+                                )
+                        }
+                        else -> {
+                            getException =
+                                GetCredentialUnknownException(
+                                    "The restore credential service failed with unsupported status code, failure: ${e.message}, " +
+                                        "status code: ${e.statusCode}"
+                                )
+                        }
+                    }
+                }
+                cancelOrCallbackExceptionOrResult(cancellationSignal) {
+                    executor.execute { callback.onError(getException) }
+                }
+            }
+    }
+
+    public override fun convertRequestToPlayServices(
+        request: GetCredentialRequest
+    ): GetRestoreCredentialRequest {
+        lateinit var credentialOption: GetRestoreCredentialOption
+        for (option in request.credentialOptions) {
+            if (option is GetRestoreCredentialOption) {
+                // there should be a single restore credential option
+                credentialOption = option
+                break
+            }
+        }
+        return GetRestoreCredentialRequest(credentialOption.requestData)
+    }
+
+    public override fun convertResponseToCredentialManager(
+        response: GetRestoreCredentialResponse
+    ): GetCredentialResponse {
+        return GetCredentialResponse(
+            Credential.createFrom(
+                androidx.credentials.RestoreCredential.TYPE_RESTORE_CREDENTIAL,
+                response.responseBundle
+            )
+        )
+    }
+}
diff --git a/credentials/credentials-play-services-auth/src/main/res/values-v21/themes.xml b/credentials/credentials-play-services-auth/src/main/res/values-v21/themes.xml
deleted file mode 100644
index 79777ba..0000000
--- a/credentials/credentials-play-services-auth/src/main/res/values-v21/themes.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-    <style name="Theme.Hidden" parent="@android:style/Theme.DeviceDefault">
-        <item name="android:windowContentOverlay">@null</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:windowIsTranslucent">true</item>
-        <item name="android:statusBarColor">@android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/credentials/credentials-play-services-e2ee/OWNERS b/credentials/credentials-play-services-e2ee/OWNERS
new file mode 100644
index 0000000..ce145c2
--- /dev/null
+++ b/credentials/credentials-play-services-e2ee/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1487500
[email protected]
[email protected]
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/credentials/credentials-play-services-e2ee/api/current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to credentials/credentials-play-services-e2ee/api/current.txt
diff --git a/credentials/credentials-play-services-auth/api/res-1.3.0-beta01.txt b/credentials/credentials-play-services-e2ee/api/res-current.txt
similarity index 100%
rename from credentials/credentials-play-services-auth/api/res-1.3.0-beta01.txt
rename to credentials/credentials-play-services-e2ee/api/res-current.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/credentials/credentials-play-services-e2ee/api/restricted_current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to credentials/credentials-play-services-e2ee/api/restricted_current.txt
diff --git a/credentials/credentials-play-services-e2ee/build.gradle b/credentials/credentials-play-services-e2ee/build.gradle
new file mode 100644
index 0000000..a8359e8
--- /dev/null
+++ b/credentials/credentials-play-services-e2ee/build.gradle
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import androidx.build.LibraryType
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+dependencies {
+    api(libs.kotlinStdlib)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.truth)
+}
+android {
+    namespace "androidx.credentials.playservices.e2ee"
+}
+androidx {
+    name = "androidx.credentials:credentials.playservices.e2ee"
+    type = LibraryType.PUBLISHED_LIBRARY
+    inceptionYear = "2024"
+    description = "Play Services API for E2EE contact keys functionality."
+    mavenVersion = LibraryVersions.CREDENTIALS_E2EE_QUARANTINE
+}
diff --git a/credentials/credentials-play-services-e2ee/src/androidTest/java/androidx/credentials/playservices/e2ee/CredentialsPlayServicesE2eeInstrumentedTest.java b/credentials/credentials-play-services-e2ee/src/androidTest/java/androidx/credentials/playservices/e2ee/CredentialsPlayServicesE2eeInstrumentedTest.java
new file mode 100644
index 0000000..bf3d391
--- /dev/null
+++ b/credentials/credentials-play-services-e2ee/src/androidTest/java/androidx/credentials/playservices/e2ee/CredentialsPlayServicesE2eeInstrumentedTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.playservices.e2ee;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CredentialsPlayServicesE2eeInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("androidx.credentials.playservices.e2ee.test", appContext.getPackageName());
+    }
+}
diff --git a/credentials/credentials/api/1.3.0-beta01.txt b/credentials/credentials/api/1.3.0-beta01.txt
deleted file mode 100644
index a0d0eda..0000000
--- a/credentials/credentials/api/1.3.0-beta01.txt
+++ /dev/null
@@ -1,1001 +0,0 @@
-// Signature format: 4.0
-package androidx.credentials {
-
-  public final class ClearCredentialStateRequest {
-    ctor public ClearCredentialStateRequest();
-  }
-
-  public abstract class CreateCredentialRequest {
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getCredentialData();
-    method public final androidx.credentials.CreateCredentialRequest.DisplayInfo getDisplayInfo();
-    method public final String? getOrigin();
-    method public final String getType();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    method public final boolean preferImmediatelyAvailableCredentials();
-    property public final android.os.Bundle candidateQueryData;
-    property public final android.os.Bundle credentialData;
-    property public final androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final String? origin;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final String type;
-    field public static final androidx.credentials.CreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo {
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, optional CharSequence? userDisplayName);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, CharSequence? userDisplayName, String? preferDefaultProvider);
-    method @RequiresApi(23) public static androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-    method public CharSequence? getUserDisplayName();
-    method public CharSequence getUserId();
-    property public final CharSequence? userDisplayName;
-    property public final CharSequence userId;
-    field public static final androidx.credentials.CreateCredentialRequest.DisplayInfo.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-  }
-
-  public abstract class CreateCredentialResponse {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-  }
-
-  public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
-  }
-
-  public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePasswordRequest(String id, String password);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials, optional boolean isAutoSelectAllowed);
-    ctor public CreatePasswordRequest(String id, String password, String? origin, String? preferDefaultProvider, boolean preferImmediatelyAvailableCredentials, boolean isAutoSelectAllowed);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-  }
-
-  public final class CreatePasswordResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePasswordResponse();
-  }
-
-  public final class CreatePublicKeyCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePublicKeyCredentialRequest(String requestJson);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin, optional boolean isAutoSelectAllowed);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, byte[]? clientDataHash, boolean preferImmediatelyAvailableCredentials, String? origin, String? preferDefaultProvider, boolean isAutoSelectAllowed);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CreatePublicKeyCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePublicKeyCredentialResponse(String registrationResponseJson);
-    method public String getRegistrationResponseJson();
-    property public final String registrationResponseJson;
-  }
-
-  public abstract class Credential {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public interface CredentialManager {
-    method public default suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public void clearCredentialStateAsync(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public static androidx.credentials.CredentialManager create(android.content.Context context);
-    method public default suspend Object? createCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
-    method public void createCredentialAsync(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method @RequiresApi(34) public android.app.PendingIntent createSettingsPendingIntent();
-    method public default suspend Object? getCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method @RequiresApi(34) public default suspend Object? getCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method public void getCredentialAsync(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public void getCredentialAsync(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default suspend Object? prepareGetCredential(androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.PrepareGetCredentialResponse>);
-    method @RequiresApi(34) public void prepareGetCredentialAsync(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    field public static final androidx.credentials.CredentialManager.Companion Companion;
-  }
-
-  public static final class CredentialManager.Companion {
-    method public androidx.credentials.CredentialManager create(android.content.Context context);
-  }
-
-  public interface CredentialManagerCallback<R, E> {
-    method public void onError(E e);
-    method public void onResult(R result);
-  }
-
-  public abstract class CredentialOption {
-    method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getRequestData();
-    method public final String getType();
-    method public final int getTypePriorityHint();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    property public final java.util.Set<android.content.ComponentName> allowedProviders;
-    property public final android.os.Bundle candidateQueryData;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final android.os.Bundle requestData;
-    property public final String type;
-    property public final int typePriorityHint;
-    field public static final androidx.credentials.CredentialOption.Companion Companion;
-    field public static final int PRIORITY_DEFAULT = 2000; // 0x7d0
-    field public static final int PRIORITY_OIDC_OR_SIMILAR = 500; // 0x1f4
-    field public static final int PRIORITY_PASSKEY_OR_SIMILAR = 100; // 0x64
-    field public static final int PRIORITY_PASSWORD_OR_SIMILAR = 1000; // 0x3e8
-  }
-
-  public static final class CredentialOption.Companion {
-  }
-
-  public interface CredentialProvider {
-    method public boolean isAvailableOnDevice();
-    method public void onClearCredential(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public void onCreateCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public void onGetCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onGetCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onPrepareCredential(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-  }
-
-  public class CustomCredential extends androidx.credentials.Credential {
-    ctor public CustomCredential(String type, android.os.Bundle data);
-  }
-
-  public final class GetCredentialRequest {
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName, optional boolean preferImmediatelyAvailableCredentials);
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    method public String? getOrigin();
-    method public boolean getPreferIdentityDocUi();
-    method public android.content.ComponentName? getPreferUiBrandingComponentName();
-    method public boolean preferImmediatelyAvailableCredentials();
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-    property public final String? origin;
-    property public final boolean preferIdentityDocUi;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final android.content.ComponentName? preferUiBrandingComponentName;
-  }
-
-  public static final class GetCredentialRequest.Builder {
-    ctor public GetCredentialRequest.Builder();
-    method public androidx.credentials.GetCredentialRequest.Builder addCredentialOption(androidx.credentials.CredentialOption credentialOption);
-    method public androidx.credentials.GetCredentialRequest build();
-    method public androidx.credentials.GetCredentialRequest.Builder setCredentialOptions(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    method public androidx.credentials.GetCredentialRequest.Builder setOrigin(String origin);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferIdentityDocUi(boolean preferIdentityDocUi);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferImmediatelyAvailableCredentials(boolean preferImmediatelyAvailableCredentials);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferUiBrandingComponentName(android.content.ComponentName? component);
-  }
-
-  public final class GetCredentialResponse {
-    ctor public GetCredentialResponse(androidx.credentials.Credential credential);
-    method public androidx.credentials.Credential getCredential();
-    property public final androidx.credentials.Credential credential;
-  }
-
-  public class GetCustomCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders, optional int typePriorityHint);
-  }
-
-  public final class GetPasswordOption extends androidx.credentials.CredentialOption {
-    ctor public GetPasswordOption();
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class GetPublicKeyCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetPublicKeyCredentialOption(String requestJson);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class PasswordCredential extends androidx.credentials.Credential {
-    ctor public PasswordCredential(String id, String password);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-    field public static final androidx.credentials.PasswordCredential.Companion Companion;
-    field public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
-  }
-
-  public static final class PasswordCredential.Companion {
-  }
-
-  @RequiresApi(34) public final class PrepareGetCredentialResponse {
-    method public kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? getCredentialTypeDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasAuthResultsDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasRemoteResultsDelegate();
-    method public androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? getPendingGetCredentialHandle();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(String credentialType);
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults();
-    method public boolean isNullHandlesForTest();
-    property public final kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? credentialTypeDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasAuthResultsDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasRemoteResultsDelegate;
-    property public final boolean isNullHandlesForTest;
-    property public final androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? pendingGetCredentialHandle;
-  }
-
-  @RequiresApi(34) public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle {
-    ctor public PrepareGetCredentialResponse.PendingGetCredentialHandle(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? frameworkHandle);
-  }
-
-  @VisibleForTesting public static final class PrepareGetCredentialResponse.TestBuilder {
-    ctor public PrepareGetCredentialResponse.TestBuilder();
-    method public androidx.credentials.PrepareGetCredentialResponse build();
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setCredentialTypeDelegate(kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasAuthResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasRemoteResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-  }
-
-  public final class PublicKeyCredential extends androidx.credentials.Credential {
-    ctor public PublicKeyCredential(String authenticationResponseJson);
-    method public String getAuthenticationResponseJson();
-    property public final String authenticationResponseJson;
-    field public static final androidx.credentials.PublicKeyCredential.Companion Companion;
-    field public static final String TYPE_PUBLIC_KEY_CREDENTIAL = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
-  }
-
-  public static final class PublicKeyCredential.Companion {
-  }
-
-}
-
-package androidx.credentials.exceptions {
-
-  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialCustomException(String type);
-    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class ClearCredentialException extends java.lang.Exception {
-  }
-
-  public final class ClearCredentialInterruptedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialInterruptedException();
-    ctor public ClearCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialProviderConfigurationException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialProviderConfigurationException();
-    ctor public ClearCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnknownException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnknownException();
-    ctor public ClearCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnsupportedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnsupportedException();
-    ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCancellationException();
-    ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCustomException(String type);
-    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class CreateCredentialException extends java.lang.Exception {
-  }
-
-  public final class CreateCredentialInterruptedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialInterruptedException();
-    ctor public CreateCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialNoCreateOptionException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialNoCreateOptionException();
-    ctor public CreateCredentialNoCreateOptionException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialProviderConfigurationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialProviderConfigurationException();
-    ctor public CreateCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnknownException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnknownException();
-    ctor public CreateCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnsupportedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnsupportedException();
-    ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCancellationException();
-    ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCustomException(String type);
-    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class GetCredentialException extends java.lang.Exception {
-  }
-
-  public final class GetCredentialInterruptedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialInterruptedException();
-    ctor public GetCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialProviderConfigurationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialProviderConfigurationException();
-    ctor public GetCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnknownException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnknownException();
-    ctor public GetCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnsupportedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnsupportedException();
-    ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public NoCredentialException();
-    ctor public NoCredentialException(optional CharSequence? errorMessage);
-  }
-
-}
-
-package androidx.credentials.exceptions.domerrors {
-
-  public final class AbortError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public AbortError();
-  }
-
-  public final class ConstraintError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ConstraintError();
-  }
-
-  public final class DataCloneError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataCloneError();
-  }
-
-  public final class DataError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataError();
-  }
-
-  public abstract class DomError {
-    ctor public DomError(String type);
-  }
-
-  public final class EncodingError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public EncodingError();
-  }
-
-  public final class HierarchyRequestError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public HierarchyRequestError();
-  }
-
-  public final class InUseAttributeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InUseAttributeError();
-  }
-
-  public final class InvalidCharacterError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidCharacterError();
-  }
-
-  public final class InvalidModificationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidModificationError();
-  }
-
-  public final class InvalidNodeTypeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidNodeTypeError();
-  }
-
-  public final class InvalidStateError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidStateError();
-  }
-
-  public final class NamespaceError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NamespaceError();
-  }
-
-  public final class NetworkError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NetworkError();
-  }
-
-  public final class NoModificationAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NoModificationAllowedError();
-  }
-
-  public final class NotAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotAllowedError();
-  }
-
-  public final class NotFoundError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotFoundError();
-  }
-
-  public final class NotReadableError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotReadableError();
-  }
-
-  public final class NotSupportedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotSupportedError();
-  }
-
-  public final class OperationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OperationError();
-  }
-
-  public final class OptOutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OptOutError();
-  }
-
-  public final class QuotaExceededError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public QuotaExceededError();
-  }
-
-  public final class ReadOnlyError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ReadOnlyError();
-  }
-
-  public final class SecurityError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SecurityError();
-  }
-
-  public final class SyntaxError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SyntaxError();
-  }
-
-  public final class TimeoutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TimeoutError();
-  }
-
-  public final class TransactionInactiveError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TransactionInactiveError();
-  }
-
-  public final class UnknownError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public UnknownError();
-  }
-
-  public final class VersionError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public VersionError();
-  }
-
-  public final class WrongDocumentError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public WrongDocumentError();
-  }
-
-}
-
-package androidx.credentials.exceptions.publickeycredential {
-
-  public final class CreatePublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialException {
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class CreatePublicKeyCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-  }
-
-  public final class GetPublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialException {
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class GetPublicKeyCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-  }
-
-}
-
-package androidx.credentials.provider {
-
-  public final class Action {
-    ctor public Action(CharSequence title, android.app.PendingIntent pendingIntent, optional CharSequence? subtitle);
-    method public static androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.Action.Companion Companion;
-  }
-
-  public static final class Action.Builder {
-    ctor public Action.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.Action build();
-    method public androidx.credentials.provider.Action.Builder setSubtitle(CharSequence? subtitle);
-  }
-
-  public static final class Action.Companion {
-    method public androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-  }
-
-  public final class AuthenticationAction {
-    ctor public AuthenticationAction(CharSequence title, android.app.PendingIntent pendingIntent);
-    method @RequiresApi(34) public static androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.AuthenticationAction.Companion Companion;
-  }
-
-  public static final class AuthenticationAction.Builder {
-    ctor public AuthenticationAction.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.AuthenticationAction build();
-  }
-
-  public static final class AuthenticationAction.Companion {
-    method @RequiresApi(34) public androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-  }
-
-  public abstract class BeginCreateCredentialRequest {
-    ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static final android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public static final androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public final androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getType();
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    property public final android.os.Bundle candidateQueryData;
-    property public final String type;
-    field public static final androidx.credentials.provider.BeginCreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginCreateCredentialResponse {
-    ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public static androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.CreateEntry> getCreateEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.CreateEntry> createEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginCreateCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialResponse.Builder {
-    ctor public BeginCreateCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder addCreateEntry(androidx.credentials.provider.CreateEntry createEntry);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse build();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setCreateEntries(java.util.List<androidx.credentials.provider.CreateEntry> createEntries);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginCreateCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginCreateCustomCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreateCustomCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-  }
-
-  public final class BeginCreatePasswordCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePasswordCredentialRequest(androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginCreatePublicKeyCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData, optional byte[]? clientDataHash);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest createForTest(android.os.Bundle data, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public abstract class BeginGetCredentialOption {
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getId();
-    method public final String getType();
-    property public final android.os.Bundle candidateQueryData;
-    property public final String id;
-    property public final String type;
-  }
-
-  public final class BeginGetCredentialRequest {
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions);
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions, optional androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.BeginGetCredentialOption> getBeginGetCredentialOptions();
-    method public androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    property public final java.util.List<androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions;
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    field public static final androidx.credentials.provider.BeginGetCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginGetCredentialResponse {
-    ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.Action> getActions();
-    method public java.util.List<androidx.credentials.provider.AuthenticationAction> getAuthenticationActions();
-    method public java.util.List<androidx.credentials.provider.CredentialEntry> getCredentialEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.Action> actions;
-    property public final java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions;
-    property public final java.util.List<androidx.credentials.provider.CredentialEntry> credentialEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginGetCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialResponse.Builder {
-    ctor public BeginGetCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAction(androidx.credentials.provider.Action action);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAuthenticationAction(androidx.credentials.provider.AuthenticationAction authenticationAction);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addCredentialEntry(androidx.credentials.provider.CredentialEntry entry);
-    method public androidx.credentials.provider.BeginGetCredentialResponse build();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setActions(java.util.List<androidx.credentials.provider.Action> actions);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setAuthenticationActions(java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationEntries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setCredentialEntries(java.util.List<? extends androidx.credentials.provider.CredentialEntry> entries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginGetCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginGetCustomCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetCustomCredentialOption(String id, String type, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginGetPasswordOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPasswordOption(java.util.Set<java.lang.String> allowedUserIds, android.os.Bundle candidateQueryData, String id);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginGetPasswordOption createForTest(android.os.Bundle data, String id);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class BeginGetPublicKeyCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson);
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson, optional byte[]? clientDataHash);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CallingAppInfo {
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo);
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo, optional String? origin);
-    method public String? getOrigin(String privilegedAllowlist);
-    method public String getPackageName();
-    method public android.content.pm.SigningInfo getSigningInfo();
-    method public boolean isOriginPopulated();
-    property public final String packageName;
-    property public final android.content.pm.SigningInfo signingInfo;
-  }
-
-  @RequiresApi(26) public final class CreateEntry {
-    ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
-    method public static androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-    method public CharSequence getAccountName();
-    method public CharSequence? getDescription();
-    method public android.graphics.drawable.Icon? getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public Integer? getPasswordCredentialCount();
-    method public android.app.PendingIntent getPendingIntent();
-    method public Integer? getPublicKeyCredentialCount();
-    method public Integer? getTotalCredentialCount();
-    method public boolean isAutoSelectAllowed();
-    property public final CharSequence accountName;
-    property public final CharSequence? description;
-    property public final android.graphics.drawable.Icon? icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.CreateEntry.Companion Companion;
-  }
-
-  public static final class CreateEntry.Builder {
-    ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.CreateEntry build();
-    method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
-    method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
-    method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CreateEntry.Builder setPasswordCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setPublicKeyCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setTotalCredentialCount(int count);
-  }
-
-  public static final class CreateEntry.Companion {
-    method public androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-  }
-
-  public abstract class CredentialEntry {
-    method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public final CharSequence? getAffiliatedDomain();
-    method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
-    method public final CharSequence getEntryGroupId();
-    method public final boolean isDefaultIconPreferredAsSingleProvider();
-    property public final CharSequence? affiliatedDomain;
-    property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
-    property public final CharSequence entryGroupId;
-    property public final boolean isDefaultIconPreferredAsSingleProvider;
-    field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
-  }
-
-  public static final class CredentialEntry.Companion {
-    method public androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public abstract class CredentialProviderService extends android.service.credentials.CredentialProviderService {
-    ctor public CredentialProviderService();
-    method public final void onBeginCreateCredential(android.service.credentials.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.credentials.CreateCredentialException> callback);
-    method public abstract void onBeginCreateCredentialRequest(androidx.credentials.provider.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginCreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public final void onBeginGetCredential(android.service.credentials.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException> callback);
-    method public abstract void onBeginGetCredentialRequest(androidx.credentials.provider.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method public final void onClearCredentialState(android.service.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException> callback);
-    method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-  }
-
-  @RequiresApi(26) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    method public String getType();
-    method public CharSequence? getTypeDisplayName();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    property public String type;
-    property public final CharSequence? typeDisplayName;
-    field public static final androidx.credentials.provider.CustomCredentialEntry.Companion Companion;
-  }
-
-  public static final class CustomCredentialEntry.Builder {
-    ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
-    method public androidx.credentials.provider.CustomCredentialEntry build();
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setEntryGroupId(CharSequence entryGroupId);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setSubtitle(CharSequence? subtitle);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setTypeDisplayName(CharSequence? typeDisplayName);
-  }
-
-  public static final class CustomCredentialEntry.Companion {
-    method public androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class IntentHandlerConverters {
-    method @RequiresApi(34) public static androidx.credentials.provider.BeginGetCredentialResponse? getBeginGetResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialResponse? getCreateCredentialCredentialResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialException? getCreateCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialException? getGetCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialResponse? getGetCredentialResponse(android.content.Intent);
-  }
-
-  @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PasswordCredentialEntry.Companion Companion;
-  }
-
-  public static final class PasswordCredentialEntry.Builder {
-    ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
-    method public androidx.credentials.provider.PasswordCredentialEntry build();
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PasswordCredentialEntry.Companion {
-    method public androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public final class PendingIntentHandler {
-    ctor public PendingIntentHandler();
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public static void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public static void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public static void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public static void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-    field public static final androidx.credentials.provider.PendingIntentHandler.Companion Companion;
-  }
-
-  public static final class PendingIntentHandler.Companion {
-    method public androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-  }
-
-  public final class ProviderClearCredentialStateRequest {
-    ctor public ProviderClearCredentialStateRequest(androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-  }
-
-  public final class ProviderCreateCredentialRequest {
-    ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public androidx.credentials.CreateCredentialRequest getCallingRequest();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final androidx.credentials.CreateCredentialRequest callingRequest;
-  }
-
-  public final class ProviderGetCredentialRequest {
-    ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-  }
-
-  @RequiresApi(26) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PublicKeyCredentialEntry.Companion Companion;
-  }
-
-  public static final class PublicKeyCredentialEntry.Builder {
-    ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry build();
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PublicKeyCredentialEntry.Companion {
-    method public androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class RemoteEntry {
-    ctor public RemoteEntry(android.app.PendingIntent pendingIntent);
-    method public static androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-    method public android.app.PendingIntent getPendingIntent();
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.RemoteEntry.Companion Companion;
-  }
-
-  public static final class RemoteEntry.Builder {
-    ctor public RemoteEntry.Builder(android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.RemoteEntry build();
-  }
-
-  public static final class RemoteEntry.Companion {
-    method public androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-  }
-
-}
-
diff --git a/credentials/credentials/api/1.3.0-beta02.txt b/credentials/credentials/api/1.3.0-beta02.txt
deleted file mode 100644
index a0d0eda..0000000
--- a/credentials/credentials/api/1.3.0-beta02.txt
+++ /dev/null
@@ -1,1001 +0,0 @@
-// Signature format: 4.0
-package androidx.credentials {
-
-  public final class ClearCredentialStateRequest {
-    ctor public ClearCredentialStateRequest();
-  }
-
-  public abstract class CreateCredentialRequest {
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getCredentialData();
-    method public final androidx.credentials.CreateCredentialRequest.DisplayInfo getDisplayInfo();
-    method public final String? getOrigin();
-    method public final String getType();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    method public final boolean preferImmediatelyAvailableCredentials();
-    property public final android.os.Bundle candidateQueryData;
-    property public final android.os.Bundle credentialData;
-    property public final androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final String? origin;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final String type;
-    field public static final androidx.credentials.CreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo {
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, optional CharSequence? userDisplayName);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, CharSequence? userDisplayName, String? preferDefaultProvider);
-    method @RequiresApi(23) public static androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-    method public CharSequence? getUserDisplayName();
-    method public CharSequence getUserId();
-    property public final CharSequence? userDisplayName;
-    property public final CharSequence userId;
-    field public static final androidx.credentials.CreateCredentialRequest.DisplayInfo.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-  }
-
-  public abstract class CreateCredentialResponse {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-  }
-
-  public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
-  }
-
-  public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePasswordRequest(String id, String password);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials, optional boolean isAutoSelectAllowed);
-    ctor public CreatePasswordRequest(String id, String password, String? origin, String? preferDefaultProvider, boolean preferImmediatelyAvailableCredentials, boolean isAutoSelectAllowed);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-  }
-
-  public final class CreatePasswordResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePasswordResponse();
-  }
-
-  public final class CreatePublicKeyCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePublicKeyCredentialRequest(String requestJson);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin, optional boolean isAutoSelectAllowed);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, byte[]? clientDataHash, boolean preferImmediatelyAvailableCredentials, String? origin, String? preferDefaultProvider, boolean isAutoSelectAllowed);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CreatePublicKeyCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePublicKeyCredentialResponse(String registrationResponseJson);
-    method public String getRegistrationResponseJson();
-    property public final String registrationResponseJson;
-  }
-
-  public abstract class Credential {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public interface CredentialManager {
-    method public default suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public void clearCredentialStateAsync(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public static androidx.credentials.CredentialManager create(android.content.Context context);
-    method public default suspend Object? createCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
-    method public void createCredentialAsync(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method @RequiresApi(34) public android.app.PendingIntent createSettingsPendingIntent();
-    method public default suspend Object? getCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method @RequiresApi(34) public default suspend Object? getCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method public void getCredentialAsync(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public void getCredentialAsync(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default suspend Object? prepareGetCredential(androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.PrepareGetCredentialResponse>);
-    method @RequiresApi(34) public void prepareGetCredentialAsync(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    field public static final androidx.credentials.CredentialManager.Companion Companion;
-  }
-
-  public static final class CredentialManager.Companion {
-    method public androidx.credentials.CredentialManager create(android.content.Context context);
-  }
-
-  public interface CredentialManagerCallback<R, E> {
-    method public void onError(E e);
-    method public void onResult(R result);
-  }
-
-  public abstract class CredentialOption {
-    method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getRequestData();
-    method public final String getType();
-    method public final int getTypePriorityHint();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    property public final java.util.Set<android.content.ComponentName> allowedProviders;
-    property public final android.os.Bundle candidateQueryData;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final android.os.Bundle requestData;
-    property public final String type;
-    property public final int typePriorityHint;
-    field public static final androidx.credentials.CredentialOption.Companion Companion;
-    field public static final int PRIORITY_DEFAULT = 2000; // 0x7d0
-    field public static final int PRIORITY_OIDC_OR_SIMILAR = 500; // 0x1f4
-    field public static final int PRIORITY_PASSKEY_OR_SIMILAR = 100; // 0x64
-    field public static final int PRIORITY_PASSWORD_OR_SIMILAR = 1000; // 0x3e8
-  }
-
-  public static final class CredentialOption.Companion {
-  }
-
-  public interface CredentialProvider {
-    method public boolean isAvailableOnDevice();
-    method public void onClearCredential(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public void onCreateCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public void onGetCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onGetCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onPrepareCredential(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-  }
-
-  public class CustomCredential extends androidx.credentials.Credential {
-    ctor public CustomCredential(String type, android.os.Bundle data);
-  }
-
-  public final class GetCredentialRequest {
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName, optional boolean preferImmediatelyAvailableCredentials);
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    method public String? getOrigin();
-    method public boolean getPreferIdentityDocUi();
-    method public android.content.ComponentName? getPreferUiBrandingComponentName();
-    method public boolean preferImmediatelyAvailableCredentials();
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-    property public final String? origin;
-    property public final boolean preferIdentityDocUi;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final android.content.ComponentName? preferUiBrandingComponentName;
-  }
-
-  public static final class GetCredentialRequest.Builder {
-    ctor public GetCredentialRequest.Builder();
-    method public androidx.credentials.GetCredentialRequest.Builder addCredentialOption(androidx.credentials.CredentialOption credentialOption);
-    method public androidx.credentials.GetCredentialRequest build();
-    method public androidx.credentials.GetCredentialRequest.Builder setCredentialOptions(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    method public androidx.credentials.GetCredentialRequest.Builder setOrigin(String origin);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferIdentityDocUi(boolean preferIdentityDocUi);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferImmediatelyAvailableCredentials(boolean preferImmediatelyAvailableCredentials);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferUiBrandingComponentName(android.content.ComponentName? component);
-  }
-
-  public final class GetCredentialResponse {
-    ctor public GetCredentialResponse(androidx.credentials.Credential credential);
-    method public androidx.credentials.Credential getCredential();
-    property public final androidx.credentials.Credential credential;
-  }
-
-  public class GetCustomCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders, optional int typePriorityHint);
-  }
-
-  public final class GetPasswordOption extends androidx.credentials.CredentialOption {
-    ctor public GetPasswordOption();
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class GetPublicKeyCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetPublicKeyCredentialOption(String requestJson);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class PasswordCredential extends androidx.credentials.Credential {
-    ctor public PasswordCredential(String id, String password);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-    field public static final androidx.credentials.PasswordCredential.Companion Companion;
-    field public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
-  }
-
-  public static final class PasswordCredential.Companion {
-  }
-
-  @RequiresApi(34) public final class PrepareGetCredentialResponse {
-    method public kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? getCredentialTypeDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasAuthResultsDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasRemoteResultsDelegate();
-    method public androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? getPendingGetCredentialHandle();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(String credentialType);
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults();
-    method public boolean isNullHandlesForTest();
-    property public final kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? credentialTypeDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasAuthResultsDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasRemoteResultsDelegate;
-    property public final boolean isNullHandlesForTest;
-    property public final androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? pendingGetCredentialHandle;
-  }
-
-  @RequiresApi(34) public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle {
-    ctor public PrepareGetCredentialResponse.PendingGetCredentialHandle(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? frameworkHandle);
-  }
-
-  @VisibleForTesting public static final class PrepareGetCredentialResponse.TestBuilder {
-    ctor public PrepareGetCredentialResponse.TestBuilder();
-    method public androidx.credentials.PrepareGetCredentialResponse build();
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setCredentialTypeDelegate(kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasAuthResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasRemoteResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-  }
-
-  public final class PublicKeyCredential extends androidx.credentials.Credential {
-    ctor public PublicKeyCredential(String authenticationResponseJson);
-    method public String getAuthenticationResponseJson();
-    property public final String authenticationResponseJson;
-    field public static final androidx.credentials.PublicKeyCredential.Companion Companion;
-    field public static final String TYPE_PUBLIC_KEY_CREDENTIAL = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
-  }
-
-  public static final class PublicKeyCredential.Companion {
-  }
-
-}
-
-package androidx.credentials.exceptions {
-
-  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialCustomException(String type);
-    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class ClearCredentialException extends java.lang.Exception {
-  }
-
-  public final class ClearCredentialInterruptedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialInterruptedException();
-    ctor public ClearCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialProviderConfigurationException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialProviderConfigurationException();
-    ctor public ClearCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnknownException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnknownException();
-    ctor public ClearCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnsupportedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnsupportedException();
-    ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCancellationException();
-    ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCustomException(String type);
-    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class CreateCredentialException extends java.lang.Exception {
-  }
-
-  public final class CreateCredentialInterruptedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialInterruptedException();
-    ctor public CreateCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialNoCreateOptionException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialNoCreateOptionException();
-    ctor public CreateCredentialNoCreateOptionException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialProviderConfigurationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialProviderConfigurationException();
-    ctor public CreateCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnknownException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnknownException();
-    ctor public CreateCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnsupportedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnsupportedException();
-    ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCancellationException();
-    ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCustomException(String type);
-    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class GetCredentialException extends java.lang.Exception {
-  }
-
-  public final class GetCredentialInterruptedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialInterruptedException();
-    ctor public GetCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialProviderConfigurationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialProviderConfigurationException();
-    ctor public GetCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnknownException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnknownException();
-    ctor public GetCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnsupportedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnsupportedException();
-    ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public NoCredentialException();
-    ctor public NoCredentialException(optional CharSequence? errorMessage);
-  }
-
-}
-
-package androidx.credentials.exceptions.domerrors {
-
-  public final class AbortError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public AbortError();
-  }
-
-  public final class ConstraintError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ConstraintError();
-  }
-
-  public final class DataCloneError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataCloneError();
-  }
-
-  public final class DataError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataError();
-  }
-
-  public abstract class DomError {
-    ctor public DomError(String type);
-  }
-
-  public final class EncodingError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public EncodingError();
-  }
-
-  public final class HierarchyRequestError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public HierarchyRequestError();
-  }
-
-  public final class InUseAttributeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InUseAttributeError();
-  }
-
-  public final class InvalidCharacterError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidCharacterError();
-  }
-
-  public final class InvalidModificationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidModificationError();
-  }
-
-  public final class InvalidNodeTypeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidNodeTypeError();
-  }
-
-  public final class InvalidStateError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidStateError();
-  }
-
-  public final class NamespaceError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NamespaceError();
-  }
-
-  public final class NetworkError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NetworkError();
-  }
-
-  public final class NoModificationAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NoModificationAllowedError();
-  }
-
-  public final class NotAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotAllowedError();
-  }
-
-  public final class NotFoundError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotFoundError();
-  }
-
-  public final class NotReadableError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotReadableError();
-  }
-
-  public final class NotSupportedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotSupportedError();
-  }
-
-  public final class OperationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OperationError();
-  }
-
-  public final class OptOutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OptOutError();
-  }
-
-  public final class QuotaExceededError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public QuotaExceededError();
-  }
-
-  public final class ReadOnlyError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ReadOnlyError();
-  }
-
-  public final class SecurityError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SecurityError();
-  }
-
-  public final class SyntaxError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SyntaxError();
-  }
-
-  public final class TimeoutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TimeoutError();
-  }
-
-  public final class TransactionInactiveError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TransactionInactiveError();
-  }
-
-  public final class UnknownError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public UnknownError();
-  }
-
-  public final class VersionError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public VersionError();
-  }
-
-  public final class WrongDocumentError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public WrongDocumentError();
-  }
-
-}
-
-package androidx.credentials.exceptions.publickeycredential {
-
-  public final class CreatePublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialException {
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class CreatePublicKeyCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-  }
-
-  public final class GetPublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialException {
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class GetPublicKeyCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-  }
-
-}
-
-package androidx.credentials.provider {
-
-  public final class Action {
-    ctor public Action(CharSequence title, android.app.PendingIntent pendingIntent, optional CharSequence? subtitle);
-    method public static androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.Action.Companion Companion;
-  }
-
-  public static final class Action.Builder {
-    ctor public Action.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.Action build();
-    method public androidx.credentials.provider.Action.Builder setSubtitle(CharSequence? subtitle);
-  }
-
-  public static final class Action.Companion {
-    method public androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-  }
-
-  public final class AuthenticationAction {
-    ctor public AuthenticationAction(CharSequence title, android.app.PendingIntent pendingIntent);
-    method @RequiresApi(34) public static androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.AuthenticationAction.Companion Companion;
-  }
-
-  public static final class AuthenticationAction.Builder {
-    ctor public AuthenticationAction.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.AuthenticationAction build();
-  }
-
-  public static final class AuthenticationAction.Companion {
-    method @RequiresApi(34) public androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-  }
-
-  public abstract class BeginCreateCredentialRequest {
-    ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static final android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public static final androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public final androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getType();
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    property public final android.os.Bundle candidateQueryData;
-    property public final String type;
-    field public static final androidx.credentials.provider.BeginCreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginCreateCredentialResponse {
-    ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public static androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.CreateEntry> getCreateEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.CreateEntry> createEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginCreateCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialResponse.Builder {
-    ctor public BeginCreateCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder addCreateEntry(androidx.credentials.provider.CreateEntry createEntry);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse build();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setCreateEntries(java.util.List<androidx.credentials.provider.CreateEntry> createEntries);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginCreateCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginCreateCustomCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreateCustomCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-  }
-
-  public final class BeginCreatePasswordCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePasswordCredentialRequest(androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginCreatePublicKeyCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData, optional byte[]? clientDataHash);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest createForTest(android.os.Bundle data, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public abstract class BeginGetCredentialOption {
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getId();
-    method public final String getType();
-    property public final android.os.Bundle candidateQueryData;
-    property public final String id;
-    property public final String type;
-  }
-
-  public final class BeginGetCredentialRequest {
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions);
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions, optional androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.BeginGetCredentialOption> getBeginGetCredentialOptions();
-    method public androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    property public final java.util.List<androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions;
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    field public static final androidx.credentials.provider.BeginGetCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginGetCredentialResponse {
-    ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.Action> getActions();
-    method public java.util.List<androidx.credentials.provider.AuthenticationAction> getAuthenticationActions();
-    method public java.util.List<androidx.credentials.provider.CredentialEntry> getCredentialEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.Action> actions;
-    property public final java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions;
-    property public final java.util.List<androidx.credentials.provider.CredentialEntry> credentialEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginGetCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialResponse.Builder {
-    ctor public BeginGetCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAction(androidx.credentials.provider.Action action);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAuthenticationAction(androidx.credentials.provider.AuthenticationAction authenticationAction);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addCredentialEntry(androidx.credentials.provider.CredentialEntry entry);
-    method public androidx.credentials.provider.BeginGetCredentialResponse build();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setActions(java.util.List<androidx.credentials.provider.Action> actions);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setAuthenticationActions(java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationEntries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setCredentialEntries(java.util.List<? extends androidx.credentials.provider.CredentialEntry> entries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginGetCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginGetCustomCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetCustomCredentialOption(String id, String type, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginGetPasswordOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPasswordOption(java.util.Set<java.lang.String> allowedUserIds, android.os.Bundle candidateQueryData, String id);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginGetPasswordOption createForTest(android.os.Bundle data, String id);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class BeginGetPublicKeyCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson);
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson, optional byte[]? clientDataHash);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CallingAppInfo {
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo);
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo, optional String? origin);
-    method public String? getOrigin(String privilegedAllowlist);
-    method public String getPackageName();
-    method public android.content.pm.SigningInfo getSigningInfo();
-    method public boolean isOriginPopulated();
-    property public final String packageName;
-    property public final android.content.pm.SigningInfo signingInfo;
-  }
-
-  @RequiresApi(26) public final class CreateEntry {
-    ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
-    method public static androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-    method public CharSequence getAccountName();
-    method public CharSequence? getDescription();
-    method public android.graphics.drawable.Icon? getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public Integer? getPasswordCredentialCount();
-    method public android.app.PendingIntent getPendingIntent();
-    method public Integer? getPublicKeyCredentialCount();
-    method public Integer? getTotalCredentialCount();
-    method public boolean isAutoSelectAllowed();
-    property public final CharSequence accountName;
-    property public final CharSequence? description;
-    property public final android.graphics.drawable.Icon? icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.CreateEntry.Companion Companion;
-  }
-
-  public static final class CreateEntry.Builder {
-    ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.CreateEntry build();
-    method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
-    method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
-    method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CreateEntry.Builder setPasswordCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setPublicKeyCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setTotalCredentialCount(int count);
-  }
-
-  public static final class CreateEntry.Companion {
-    method public androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-  }
-
-  public abstract class CredentialEntry {
-    method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public final CharSequence? getAffiliatedDomain();
-    method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
-    method public final CharSequence getEntryGroupId();
-    method public final boolean isDefaultIconPreferredAsSingleProvider();
-    property public final CharSequence? affiliatedDomain;
-    property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
-    property public final CharSequence entryGroupId;
-    property public final boolean isDefaultIconPreferredAsSingleProvider;
-    field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
-  }
-
-  public static final class CredentialEntry.Companion {
-    method public androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public abstract class CredentialProviderService extends android.service.credentials.CredentialProviderService {
-    ctor public CredentialProviderService();
-    method public final void onBeginCreateCredential(android.service.credentials.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.credentials.CreateCredentialException> callback);
-    method public abstract void onBeginCreateCredentialRequest(androidx.credentials.provider.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginCreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public final void onBeginGetCredential(android.service.credentials.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException> callback);
-    method public abstract void onBeginGetCredentialRequest(androidx.credentials.provider.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method public final void onClearCredentialState(android.service.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException> callback);
-    method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-  }
-
-  @RequiresApi(26) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    method public String getType();
-    method public CharSequence? getTypeDisplayName();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    property public String type;
-    property public final CharSequence? typeDisplayName;
-    field public static final androidx.credentials.provider.CustomCredentialEntry.Companion Companion;
-  }
-
-  public static final class CustomCredentialEntry.Builder {
-    ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
-    method public androidx.credentials.provider.CustomCredentialEntry build();
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setEntryGroupId(CharSequence entryGroupId);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setSubtitle(CharSequence? subtitle);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setTypeDisplayName(CharSequence? typeDisplayName);
-  }
-
-  public static final class CustomCredentialEntry.Companion {
-    method public androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class IntentHandlerConverters {
-    method @RequiresApi(34) public static androidx.credentials.provider.BeginGetCredentialResponse? getBeginGetResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialResponse? getCreateCredentialCredentialResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialException? getCreateCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialException? getGetCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialResponse? getGetCredentialResponse(android.content.Intent);
-  }
-
-  @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PasswordCredentialEntry.Companion Companion;
-  }
-
-  public static final class PasswordCredentialEntry.Builder {
-    ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
-    method public androidx.credentials.provider.PasswordCredentialEntry build();
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PasswordCredentialEntry.Companion {
-    method public androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public final class PendingIntentHandler {
-    ctor public PendingIntentHandler();
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public static void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public static void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public static void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public static void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-    field public static final androidx.credentials.provider.PendingIntentHandler.Companion Companion;
-  }
-
-  public static final class PendingIntentHandler.Companion {
-    method public androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-  }
-
-  public final class ProviderClearCredentialStateRequest {
-    ctor public ProviderClearCredentialStateRequest(androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-  }
-
-  public final class ProviderCreateCredentialRequest {
-    ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public androidx.credentials.CreateCredentialRequest getCallingRequest();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final androidx.credentials.CreateCredentialRequest callingRequest;
-  }
-
-  public final class ProviderGetCredentialRequest {
-    ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-  }
-
-  @RequiresApi(26) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PublicKeyCredentialEntry.Companion Companion;
-  }
-
-  public static final class PublicKeyCredentialEntry.Builder {
-    ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry build();
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PublicKeyCredentialEntry.Companion {
-    method public androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class RemoteEntry {
-    ctor public RemoteEntry(android.app.PendingIntent pendingIntent);
-    method public static androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-    method public android.app.PendingIntent getPendingIntent();
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.RemoteEntry.Companion Companion;
-  }
-
-  public static final class RemoteEntry.Builder {
-    ctor public RemoteEntry.Builder(android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.RemoteEntry build();
-  }
-
-  public static final class RemoteEntry.Companion {
-    method public androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-  }
-
-}
-
diff --git a/credentials/credentials/api/current.txt b/credentials/credentials/api/current.txt
index a0d0eda..98c8008 100644
--- a/credentials/credentials/api/current.txt
+++ b/credentials/credentials/api/current.txt
@@ -3,6 +3,11 @@
 
   public final class ClearCredentialStateRequest {
     ctor public ClearCredentialStateRequest();
+    ctor public ClearCredentialStateRequest(int requestType);
+    method public android.os.Bundle getRequestBundle();
+    method public int getRequestType();
+    property public final android.os.Bundle requestBundle;
+    property public final int requestType;
   }
 
   public abstract class CreateCredentialRequest {
@@ -101,11 +106,40 @@
     property public final String registrationResponseJson;
   }
 
+  public final class CreateRestoreCredentialRequest extends androidx.credentials.CreateCredentialRequest {
+    ctor public CreateRestoreCredentialRequest(String requestJson, boolean isCloudBackupEnabled);
+    method public String getRequestJson();
+    method public boolean isCloudBackupEnabled();
+    property public final boolean isCloudBackupEnabled;
+    property public final String requestJson;
+    field public static final androidx.credentials.CreateRestoreCredentialRequest.Companion Companion;
+  }
+
+  public static final class CreateRestoreCredentialRequest.Companion {
+  }
+
+  public final class CreateRestoreCredentialResponse extends androidx.credentials.CreateCredentialResponse {
+    ctor public CreateRestoreCredentialResponse(String responseJson, android.os.Bundle data);
+    method public static androidx.credentials.CreateRestoreCredentialResponse createFrom(android.os.Bundle data);
+    method public String getResponseJson();
+    property public final String responseJson;
+    field public static final String BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_RESPONSE = "androidx.credentials.BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_RESPONSE";
+    field public static final androidx.credentials.CreateRestoreCredentialResponse.Companion Companion;
+  }
+
+  public static final class CreateRestoreCredentialResponse.Companion {
+    method public androidx.credentials.CreateRestoreCredentialResponse createFrom(android.os.Bundle data);
+  }
+
   public abstract class Credential {
     method public final android.os.Bundle getData();
     method public final String getType();
     property public final android.os.Bundle data;
     property public final String type;
+    field public static final androidx.credentials.Credential.Companion Companion;
+  }
+
+  public static final class Credential.Companion {
   }
 
   public interface CredentialManager {
@@ -133,6 +167,11 @@
     method public void onResult(R result);
   }
 
+  public final class CredentialManagerViewHandler {
+    method public static androidx.credentials.PendingGetCredentialRequest? getPendingGetCredentialRequest(android.view.View);
+    method public static void setPendingGetCredentialRequest(android.view.View, androidx.credentials.PendingGetCredentialRequest?);
+  }
+
   public abstract class CredentialOption {
     method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
     method public final android.os.Bundle getCandidateQueryData();
@@ -232,6 +271,12 @@
     property public final String requestJson;
   }
 
+  public final class GetRestoreCredentialOption extends androidx.credentials.CredentialOption {
+    ctor public GetRestoreCredentialOption(String requestJson);
+    method public String getRequestJson();
+    property public final String requestJson;
+  }
+
   public final class PasswordCredential extends androidx.credentials.Credential {
     ctor public PasswordCredential(String id, String password);
     method public String getId();
@@ -245,6 +290,14 @@
   public static final class PasswordCredential.Companion {
   }
 
+  public final class PendingGetCredentialRequest {
+    ctor public PendingGetCredentialRequest(androidx.credentials.GetCredentialRequest request, kotlin.jvm.functions.Function1<? super androidx.credentials.GetCredentialResponse,kotlin.Unit> callback);
+    method public kotlin.jvm.functions.Function1<androidx.credentials.GetCredentialResponse,kotlin.Unit> getCallback();
+    method public androidx.credentials.GetCredentialRequest getRequest();
+    property public final kotlin.jvm.functions.Function1<androidx.credentials.GetCredentialResponse,kotlin.Unit> callback;
+    property public final androidx.credentials.GetCredentialRequest request;
+  }
+
   @RequiresApi(34) public final class PrepareGetCredentialResponse {
     method public kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? getCredentialTypeDelegate();
     method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasAuthResultsDelegate();
@@ -284,6 +337,16 @@
   public static final class PublicKeyCredential.Companion {
   }
 
+  public final class RestoreCredential extends androidx.credentials.Credential {
+    method public String getAuthenticationResponseJson();
+    property public final String authenticationResponseJson;
+    field public static final androidx.credentials.RestoreCredential.Companion Companion;
+    field public static final String TYPE_RESTORE_CREDENTIAL = "androidx.credentials.TYPE_RESTORE_CREDENTIAL";
+  }
+
+  public static final class RestoreCredential.Companion {
+  }
+
 }
 
 package androidx.credentials.exceptions {
@@ -548,6 +611,20 @@
 
 }
 
+package androidx.credentials.exceptions.restorecredential {
+
+  public final class CreateRestoreCredentialDomException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public CreateRestoreCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, CharSequence errorMessage);
+    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
+    property public final androidx.credentials.exceptions.domerrors.DomError domError;
+  }
+
+  public final class E2eeUnavailableException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public E2eeUnavailableException(CharSequence errorMessage);
+  }
+
+}
+
 package androidx.credentials.provider {
 
   public final class Action {
@@ -591,6 +668,21 @@
     method @RequiresApi(34) public androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
   }
 
+  public final class AuthenticationError {
+    ctor public AuthenticationError(int errorCode);
+    ctor public AuthenticationError(int errorCode, optional CharSequence? errorMsg);
+    method public int getErrorCode();
+    method public CharSequence? getErrorMsg();
+    property public final int errorCode;
+    property public final CharSequence? errorMsg;
+  }
+
+  public final class AuthenticationResult {
+    ctor public AuthenticationResult(int authenticationType);
+    method public int getAuthenticationType();
+    property public final int authenticationType;
+  }
+
   public abstract class BeginCreateCredentialRequest {
     ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
     method public static final android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
@@ -610,6 +702,7 @@
   }
 
   public final class BeginCreateCredentialResponse {
+    ctor public BeginCreateCredentialResponse();
     ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
     method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
     method public static androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
@@ -678,6 +771,7 @@
   }
 
   public final class BeginGetCredentialResponse {
+    ctor public BeginGetCredentialResponse();
     ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
     method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
     method public static androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
@@ -729,6 +823,35 @@
     property public final String requestJson;
   }
 
+  @RequiresApi(35) public final class BiometricPromptData {
+    ctor public BiometricPromptData();
+    ctor public BiometricPromptData();
+    ctor public BiometricPromptData(optional androidx.biometric.BiometricPrompt.CryptoObject? cryptoObject);
+    ctor public BiometricPromptData(optional androidx.biometric.BiometricPrompt.CryptoObject? cryptoObject, optional int allowedAuthenticators);
+    method public int getAllowedAuthenticators();
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCryptoObject();
+    property public final int allowedAuthenticators;
+    property public final androidx.biometric.BiometricPrompt.CryptoObject? cryptoObject;
+  }
+
+  public static final class BiometricPromptData.Builder {
+    ctor public BiometricPromptData.Builder();
+    method public androidx.credentials.provider.BiometricPromptData build();
+    method public androidx.credentials.provider.BiometricPromptData.Builder setAllowedAuthenticators(int allowedAuthenticators);
+    method public androidx.credentials.provider.BiometricPromptData.Builder setCryptoObject(androidx.biometric.BiometricPrompt.CryptoObject cryptoObject);
+  }
+
+  public final class BiometricPromptResult {
+    ctor public BiometricPromptResult(androidx.credentials.provider.AuthenticationError authenticationError);
+    ctor public BiometricPromptResult(androidx.credentials.provider.AuthenticationResult authenticationResult);
+    method public androidx.credentials.provider.AuthenticationError? getAuthenticationError();
+    method public androidx.credentials.provider.AuthenticationResult? getAuthenticationResult();
+    method public boolean isSuccessful();
+    property public final androidx.credentials.provider.AuthenticationError? authenticationError;
+    property public final androidx.credentials.provider.AuthenticationResult? authenticationResult;
+    property public final boolean isSuccessful;
+  }
+
   public final class CallingAppInfo {
     ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo);
     ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo, optional String? origin);
@@ -740,10 +863,12 @@
     property public final android.content.pm.SigningInfo signingInfo;
   }
 
-  @RequiresApi(26) public final class CreateEntry {
+  @RequiresApi(23) public final class CreateEntry {
     ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
     method public CharSequence getAccountName();
+    method public androidx.credentials.provider.BiometricPromptData? getBiometricPromptData();
     method public CharSequence? getDescription();
     method public android.graphics.drawable.Icon? getIcon();
     method public java.time.Instant? getLastUsedTime();
@@ -753,6 +878,7 @@
     method public Integer? getTotalCredentialCount();
     method public boolean isAutoSelectAllowed();
     property public final CharSequence accountName;
+    property public final androidx.credentials.provider.BiometricPromptData? biometricPromptData;
     property public final CharSequence? description;
     property public final android.graphics.drawable.Icon? icon;
     property public final boolean isAutoSelectAllowed;
@@ -765,6 +891,7 @@
     ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
     method public androidx.credentials.provider.CreateEntry build();
     method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.CreateEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
     method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
     method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
@@ -781,10 +908,12 @@
     method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public final CharSequence? getAffiliatedDomain();
     method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
+    method public final androidx.credentials.provider.BiometricPromptData? getBiometricPromptData();
     method public final CharSequence getEntryGroupId();
     method public final boolean isDefaultIconPreferredAsSingleProvider();
     property public final CharSequence? affiliatedDomain;
     property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
+    property public final androidx.credentials.provider.BiometricPromptData? biometricPromptData;
     property public final CharSequence entryGroupId;
     property public final boolean isDefaultIconPreferredAsSingleProvider;
     field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
@@ -804,9 +933,10 @@
     method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
   }
 
-  @RequiresApi(26) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+  @RequiresApi(23) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
     ctor @Deprecated public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
     ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public android.graphics.drawable.Icon getIcon();
     method public java.time.Instant? getLastUsedTime();
@@ -835,6 +965,7 @@
     ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
     method public androidx.credentials.provider.CustomCredentialEntry build();
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.CustomCredentialEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setEntryGroupId(CharSequence entryGroupId);
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
@@ -855,9 +986,10 @@
     method @RequiresApi(34) public static android.credentials.GetCredentialResponse? getGetCredentialResponse(android.content.Intent);
   }
 
-  @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+  @RequiresApi(23) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
     ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
     ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public CharSequence? getDisplayName();
     method public android.graphics.drawable.Icon getIcon();
@@ -885,6 +1017,7 @@
     method public androidx.credentials.provider.PasswordCredentialEntry build();
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.PasswordCredentialEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
@@ -927,23 +1060,30 @@
 
   public final class ProviderCreateCredentialRequest {
     ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo);
+    ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo, optional androidx.credentials.provider.BiometricPromptResult? biometricPromptResult);
+    method public androidx.credentials.provider.BiometricPromptResult? getBiometricPromptResult();
     method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
     method public androidx.credentials.CreateCredentialRequest getCallingRequest();
+    property public final androidx.credentials.provider.BiometricPromptResult? biometricPromptResult;
     property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
     property public final androidx.credentials.CreateCredentialRequest callingRequest;
   }
 
   public final class ProviderGetCredentialRequest {
     ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo);
+    ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo, optional androidx.credentials.provider.BiometricPromptResult? biometricPromptResult);
+    method public androidx.credentials.provider.BiometricPromptResult? getBiometricPromptResult();
     method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
     method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
+    property public final androidx.credentials.provider.BiometricPromptResult? biometricPromptResult;
     property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
     property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
   }
 
-  @RequiresApi(26) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+  @RequiresApi(23) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
     ctor @Deprecated public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
     ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public CharSequence? getDisplayName();
     method public android.graphics.drawable.Icon getIcon();
@@ -970,6 +1110,7 @@
     ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
     method public androidx.credentials.provider.PublicKeyCredentialEntry build();
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
diff --git a/credentials/credentials/api/res-1.3.0-beta01.txt b/credentials/credentials/api/res-1.3.0-beta01.txt
deleted file mode 100644
index e69de29..0000000
--- a/credentials/credentials/api/res-1.3.0-beta01.txt
+++ /dev/null
diff --git a/credentials/credentials/api/res-1.3.0-beta02.txt b/credentials/credentials/api/res-1.3.0-beta02.txt
deleted file mode 100644
index e69de29..0000000
--- a/credentials/credentials/api/res-1.3.0-beta02.txt
+++ /dev/null
diff --git a/credentials/credentials/api/restricted_1.3.0-beta01.txt b/credentials/credentials/api/restricted_1.3.0-beta01.txt
deleted file mode 100644
index a0d0eda..0000000
--- a/credentials/credentials/api/restricted_1.3.0-beta01.txt
+++ /dev/null
@@ -1,1001 +0,0 @@
-// Signature format: 4.0
-package androidx.credentials {
-
-  public final class ClearCredentialStateRequest {
-    ctor public ClearCredentialStateRequest();
-  }
-
-  public abstract class CreateCredentialRequest {
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getCredentialData();
-    method public final androidx.credentials.CreateCredentialRequest.DisplayInfo getDisplayInfo();
-    method public final String? getOrigin();
-    method public final String getType();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    method public final boolean preferImmediatelyAvailableCredentials();
-    property public final android.os.Bundle candidateQueryData;
-    property public final android.os.Bundle credentialData;
-    property public final androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final String? origin;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final String type;
-    field public static final androidx.credentials.CreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo {
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, optional CharSequence? userDisplayName);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, CharSequence? userDisplayName, String? preferDefaultProvider);
-    method @RequiresApi(23) public static androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-    method public CharSequence? getUserDisplayName();
-    method public CharSequence getUserId();
-    property public final CharSequence? userDisplayName;
-    property public final CharSequence userId;
-    field public static final androidx.credentials.CreateCredentialRequest.DisplayInfo.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-  }
-
-  public abstract class CreateCredentialResponse {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-  }
-
-  public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
-  }
-
-  public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePasswordRequest(String id, String password);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials, optional boolean isAutoSelectAllowed);
-    ctor public CreatePasswordRequest(String id, String password, String? origin, String? preferDefaultProvider, boolean preferImmediatelyAvailableCredentials, boolean isAutoSelectAllowed);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-  }
-
-  public final class CreatePasswordResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePasswordResponse();
-  }
-
-  public final class CreatePublicKeyCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePublicKeyCredentialRequest(String requestJson);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin, optional boolean isAutoSelectAllowed);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, byte[]? clientDataHash, boolean preferImmediatelyAvailableCredentials, String? origin, String? preferDefaultProvider, boolean isAutoSelectAllowed);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CreatePublicKeyCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePublicKeyCredentialResponse(String registrationResponseJson);
-    method public String getRegistrationResponseJson();
-    property public final String registrationResponseJson;
-  }
-
-  public abstract class Credential {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public interface CredentialManager {
-    method public default suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public void clearCredentialStateAsync(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public static androidx.credentials.CredentialManager create(android.content.Context context);
-    method public default suspend Object? createCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
-    method public void createCredentialAsync(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method @RequiresApi(34) public android.app.PendingIntent createSettingsPendingIntent();
-    method public default suspend Object? getCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method @RequiresApi(34) public default suspend Object? getCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method public void getCredentialAsync(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public void getCredentialAsync(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default suspend Object? prepareGetCredential(androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.PrepareGetCredentialResponse>);
-    method @RequiresApi(34) public void prepareGetCredentialAsync(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    field public static final androidx.credentials.CredentialManager.Companion Companion;
-  }
-
-  public static final class CredentialManager.Companion {
-    method public androidx.credentials.CredentialManager create(android.content.Context context);
-  }
-
-  public interface CredentialManagerCallback<R, E> {
-    method public void onError(E e);
-    method public void onResult(R result);
-  }
-
-  public abstract class CredentialOption {
-    method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getRequestData();
-    method public final String getType();
-    method public final int getTypePriorityHint();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    property public final java.util.Set<android.content.ComponentName> allowedProviders;
-    property public final android.os.Bundle candidateQueryData;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final android.os.Bundle requestData;
-    property public final String type;
-    property public final int typePriorityHint;
-    field public static final androidx.credentials.CredentialOption.Companion Companion;
-    field public static final int PRIORITY_DEFAULT = 2000; // 0x7d0
-    field public static final int PRIORITY_OIDC_OR_SIMILAR = 500; // 0x1f4
-    field public static final int PRIORITY_PASSKEY_OR_SIMILAR = 100; // 0x64
-    field public static final int PRIORITY_PASSWORD_OR_SIMILAR = 1000; // 0x3e8
-  }
-
-  public static final class CredentialOption.Companion {
-  }
-
-  public interface CredentialProvider {
-    method public boolean isAvailableOnDevice();
-    method public void onClearCredential(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public void onCreateCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public void onGetCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onGetCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onPrepareCredential(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-  }
-
-  public class CustomCredential extends androidx.credentials.Credential {
-    ctor public CustomCredential(String type, android.os.Bundle data);
-  }
-
-  public final class GetCredentialRequest {
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName, optional boolean preferImmediatelyAvailableCredentials);
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    method public String? getOrigin();
-    method public boolean getPreferIdentityDocUi();
-    method public android.content.ComponentName? getPreferUiBrandingComponentName();
-    method public boolean preferImmediatelyAvailableCredentials();
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-    property public final String? origin;
-    property public final boolean preferIdentityDocUi;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final android.content.ComponentName? preferUiBrandingComponentName;
-  }
-
-  public static final class GetCredentialRequest.Builder {
-    ctor public GetCredentialRequest.Builder();
-    method public androidx.credentials.GetCredentialRequest.Builder addCredentialOption(androidx.credentials.CredentialOption credentialOption);
-    method public androidx.credentials.GetCredentialRequest build();
-    method public androidx.credentials.GetCredentialRequest.Builder setCredentialOptions(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    method public androidx.credentials.GetCredentialRequest.Builder setOrigin(String origin);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferIdentityDocUi(boolean preferIdentityDocUi);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferImmediatelyAvailableCredentials(boolean preferImmediatelyAvailableCredentials);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferUiBrandingComponentName(android.content.ComponentName? component);
-  }
-
-  public final class GetCredentialResponse {
-    ctor public GetCredentialResponse(androidx.credentials.Credential credential);
-    method public androidx.credentials.Credential getCredential();
-    property public final androidx.credentials.Credential credential;
-  }
-
-  public class GetCustomCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders, optional int typePriorityHint);
-  }
-
-  public final class GetPasswordOption extends androidx.credentials.CredentialOption {
-    ctor public GetPasswordOption();
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class GetPublicKeyCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetPublicKeyCredentialOption(String requestJson);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class PasswordCredential extends androidx.credentials.Credential {
-    ctor public PasswordCredential(String id, String password);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-    field public static final androidx.credentials.PasswordCredential.Companion Companion;
-    field public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
-  }
-
-  public static final class PasswordCredential.Companion {
-  }
-
-  @RequiresApi(34) public final class PrepareGetCredentialResponse {
-    method public kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? getCredentialTypeDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasAuthResultsDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasRemoteResultsDelegate();
-    method public androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? getPendingGetCredentialHandle();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(String credentialType);
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults();
-    method public boolean isNullHandlesForTest();
-    property public final kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? credentialTypeDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasAuthResultsDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasRemoteResultsDelegate;
-    property public final boolean isNullHandlesForTest;
-    property public final androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? pendingGetCredentialHandle;
-  }
-
-  @RequiresApi(34) public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle {
-    ctor public PrepareGetCredentialResponse.PendingGetCredentialHandle(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? frameworkHandle);
-  }
-
-  @VisibleForTesting public static final class PrepareGetCredentialResponse.TestBuilder {
-    ctor public PrepareGetCredentialResponse.TestBuilder();
-    method public androidx.credentials.PrepareGetCredentialResponse build();
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setCredentialTypeDelegate(kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasAuthResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasRemoteResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-  }
-
-  public final class PublicKeyCredential extends androidx.credentials.Credential {
-    ctor public PublicKeyCredential(String authenticationResponseJson);
-    method public String getAuthenticationResponseJson();
-    property public final String authenticationResponseJson;
-    field public static final androidx.credentials.PublicKeyCredential.Companion Companion;
-    field public static final String TYPE_PUBLIC_KEY_CREDENTIAL = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
-  }
-
-  public static final class PublicKeyCredential.Companion {
-  }
-
-}
-
-package androidx.credentials.exceptions {
-
-  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialCustomException(String type);
-    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class ClearCredentialException extends java.lang.Exception {
-  }
-
-  public final class ClearCredentialInterruptedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialInterruptedException();
-    ctor public ClearCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialProviderConfigurationException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialProviderConfigurationException();
-    ctor public ClearCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnknownException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnknownException();
-    ctor public ClearCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnsupportedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnsupportedException();
-    ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCancellationException();
-    ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCustomException(String type);
-    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class CreateCredentialException extends java.lang.Exception {
-  }
-
-  public final class CreateCredentialInterruptedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialInterruptedException();
-    ctor public CreateCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialNoCreateOptionException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialNoCreateOptionException();
-    ctor public CreateCredentialNoCreateOptionException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialProviderConfigurationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialProviderConfigurationException();
-    ctor public CreateCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnknownException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnknownException();
-    ctor public CreateCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnsupportedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnsupportedException();
-    ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCancellationException();
-    ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCustomException(String type);
-    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class GetCredentialException extends java.lang.Exception {
-  }
-
-  public final class GetCredentialInterruptedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialInterruptedException();
-    ctor public GetCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialProviderConfigurationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialProviderConfigurationException();
-    ctor public GetCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnknownException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnknownException();
-    ctor public GetCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnsupportedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnsupportedException();
-    ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public NoCredentialException();
-    ctor public NoCredentialException(optional CharSequence? errorMessage);
-  }
-
-}
-
-package androidx.credentials.exceptions.domerrors {
-
-  public final class AbortError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public AbortError();
-  }
-
-  public final class ConstraintError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ConstraintError();
-  }
-
-  public final class DataCloneError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataCloneError();
-  }
-
-  public final class DataError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataError();
-  }
-
-  public abstract class DomError {
-    ctor public DomError(String type);
-  }
-
-  public final class EncodingError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public EncodingError();
-  }
-
-  public final class HierarchyRequestError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public HierarchyRequestError();
-  }
-
-  public final class InUseAttributeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InUseAttributeError();
-  }
-
-  public final class InvalidCharacterError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidCharacterError();
-  }
-
-  public final class InvalidModificationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidModificationError();
-  }
-
-  public final class InvalidNodeTypeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidNodeTypeError();
-  }
-
-  public final class InvalidStateError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidStateError();
-  }
-
-  public final class NamespaceError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NamespaceError();
-  }
-
-  public final class NetworkError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NetworkError();
-  }
-
-  public final class NoModificationAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NoModificationAllowedError();
-  }
-
-  public final class NotAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotAllowedError();
-  }
-
-  public final class NotFoundError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotFoundError();
-  }
-
-  public final class NotReadableError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotReadableError();
-  }
-
-  public final class NotSupportedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotSupportedError();
-  }
-
-  public final class OperationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OperationError();
-  }
-
-  public final class OptOutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OptOutError();
-  }
-
-  public final class QuotaExceededError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public QuotaExceededError();
-  }
-
-  public final class ReadOnlyError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ReadOnlyError();
-  }
-
-  public final class SecurityError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SecurityError();
-  }
-
-  public final class SyntaxError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SyntaxError();
-  }
-
-  public final class TimeoutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TimeoutError();
-  }
-
-  public final class TransactionInactiveError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TransactionInactiveError();
-  }
-
-  public final class UnknownError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public UnknownError();
-  }
-
-  public final class VersionError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public VersionError();
-  }
-
-  public final class WrongDocumentError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public WrongDocumentError();
-  }
-
-}
-
-package androidx.credentials.exceptions.publickeycredential {
-
-  public final class CreatePublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialException {
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class CreatePublicKeyCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-  }
-
-  public final class GetPublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialException {
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class GetPublicKeyCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-  }
-
-}
-
-package androidx.credentials.provider {
-
-  public final class Action {
-    ctor public Action(CharSequence title, android.app.PendingIntent pendingIntent, optional CharSequence? subtitle);
-    method public static androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.Action.Companion Companion;
-  }
-
-  public static final class Action.Builder {
-    ctor public Action.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.Action build();
-    method public androidx.credentials.provider.Action.Builder setSubtitle(CharSequence? subtitle);
-  }
-
-  public static final class Action.Companion {
-    method public androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-  }
-
-  public final class AuthenticationAction {
-    ctor public AuthenticationAction(CharSequence title, android.app.PendingIntent pendingIntent);
-    method @RequiresApi(34) public static androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.AuthenticationAction.Companion Companion;
-  }
-
-  public static final class AuthenticationAction.Builder {
-    ctor public AuthenticationAction.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.AuthenticationAction build();
-  }
-
-  public static final class AuthenticationAction.Companion {
-    method @RequiresApi(34) public androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-  }
-
-  public abstract class BeginCreateCredentialRequest {
-    ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static final android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public static final androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public final androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getType();
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    property public final android.os.Bundle candidateQueryData;
-    property public final String type;
-    field public static final androidx.credentials.provider.BeginCreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginCreateCredentialResponse {
-    ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public static androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.CreateEntry> getCreateEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.CreateEntry> createEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginCreateCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialResponse.Builder {
-    ctor public BeginCreateCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder addCreateEntry(androidx.credentials.provider.CreateEntry createEntry);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse build();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setCreateEntries(java.util.List<androidx.credentials.provider.CreateEntry> createEntries);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginCreateCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginCreateCustomCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreateCustomCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-  }
-
-  public final class BeginCreatePasswordCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePasswordCredentialRequest(androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginCreatePublicKeyCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData, optional byte[]? clientDataHash);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest createForTest(android.os.Bundle data, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public abstract class BeginGetCredentialOption {
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getId();
-    method public final String getType();
-    property public final android.os.Bundle candidateQueryData;
-    property public final String id;
-    property public final String type;
-  }
-
-  public final class BeginGetCredentialRequest {
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions);
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions, optional androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.BeginGetCredentialOption> getBeginGetCredentialOptions();
-    method public androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    property public final java.util.List<androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions;
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    field public static final androidx.credentials.provider.BeginGetCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginGetCredentialResponse {
-    ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.Action> getActions();
-    method public java.util.List<androidx.credentials.provider.AuthenticationAction> getAuthenticationActions();
-    method public java.util.List<androidx.credentials.provider.CredentialEntry> getCredentialEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.Action> actions;
-    property public final java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions;
-    property public final java.util.List<androidx.credentials.provider.CredentialEntry> credentialEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginGetCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialResponse.Builder {
-    ctor public BeginGetCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAction(androidx.credentials.provider.Action action);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAuthenticationAction(androidx.credentials.provider.AuthenticationAction authenticationAction);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addCredentialEntry(androidx.credentials.provider.CredentialEntry entry);
-    method public androidx.credentials.provider.BeginGetCredentialResponse build();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setActions(java.util.List<androidx.credentials.provider.Action> actions);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setAuthenticationActions(java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationEntries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setCredentialEntries(java.util.List<? extends androidx.credentials.provider.CredentialEntry> entries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginGetCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginGetCustomCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetCustomCredentialOption(String id, String type, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginGetPasswordOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPasswordOption(java.util.Set<java.lang.String> allowedUserIds, android.os.Bundle candidateQueryData, String id);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginGetPasswordOption createForTest(android.os.Bundle data, String id);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class BeginGetPublicKeyCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson);
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson, optional byte[]? clientDataHash);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CallingAppInfo {
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo);
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo, optional String? origin);
-    method public String? getOrigin(String privilegedAllowlist);
-    method public String getPackageName();
-    method public android.content.pm.SigningInfo getSigningInfo();
-    method public boolean isOriginPopulated();
-    property public final String packageName;
-    property public final android.content.pm.SigningInfo signingInfo;
-  }
-
-  @RequiresApi(26) public final class CreateEntry {
-    ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
-    method public static androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-    method public CharSequence getAccountName();
-    method public CharSequence? getDescription();
-    method public android.graphics.drawable.Icon? getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public Integer? getPasswordCredentialCount();
-    method public android.app.PendingIntent getPendingIntent();
-    method public Integer? getPublicKeyCredentialCount();
-    method public Integer? getTotalCredentialCount();
-    method public boolean isAutoSelectAllowed();
-    property public final CharSequence accountName;
-    property public final CharSequence? description;
-    property public final android.graphics.drawable.Icon? icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.CreateEntry.Companion Companion;
-  }
-
-  public static final class CreateEntry.Builder {
-    ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.CreateEntry build();
-    method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
-    method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
-    method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CreateEntry.Builder setPasswordCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setPublicKeyCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setTotalCredentialCount(int count);
-  }
-
-  public static final class CreateEntry.Companion {
-    method public androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-  }
-
-  public abstract class CredentialEntry {
-    method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public final CharSequence? getAffiliatedDomain();
-    method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
-    method public final CharSequence getEntryGroupId();
-    method public final boolean isDefaultIconPreferredAsSingleProvider();
-    property public final CharSequence? affiliatedDomain;
-    property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
-    property public final CharSequence entryGroupId;
-    property public final boolean isDefaultIconPreferredAsSingleProvider;
-    field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
-  }
-
-  public static final class CredentialEntry.Companion {
-    method public androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public abstract class CredentialProviderService extends android.service.credentials.CredentialProviderService {
-    ctor public CredentialProviderService();
-    method public final void onBeginCreateCredential(android.service.credentials.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.credentials.CreateCredentialException> callback);
-    method public abstract void onBeginCreateCredentialRequest(androidx.credentials.provider.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginCreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public final void onBeginGetCredential(android.service.credentials.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException> callback);
-    method public abstract void onBeginGetCredentialRequest(androidx.credentials.provider.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method public final void onClearCredentialState(android.service.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException> callback);
-    method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-  }
-
-  @RequiresApi(26) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    method public String getType();
-    method public CharSequence? getTypeDisplayName();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    property public String type;
-    property public final CharSequence? typeDisplayName;
-    field public static final androidx.credentials.provider.CustomCredentialEntry.Companion Companion;
-  }
-
-  public static final class CustomCredentialEntry.Builder {
-    ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
-    method public androidx.credentials.provider.CustomCredentialEntry build();
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setEntryGroupId(CharSequence entryGroupId);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setSubtitle(CharSequence? subtitle);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setTypeDisplayName(CharSequence? typeDisplayName);
-  }
-
-  public static final class CustomCredentialEntry.Companion {
-    method public androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class IntentHandlerConverters {
-    method @RequiresApi(34) public static androidx.credentials.provider.BeginGetCredentialResponse? getBeginGetResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialResponse? getCreateCredentialCredentialResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialException? getCreateCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialException? getGetCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialResponse? getGetCredentialResponse(android.content.Intent);
-  }
-
-  @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PasswordCredentialEntry.Companion Companion;
-  }
-
-  public static final class PasswordCredentialEntry.Builder {
-    ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
-    method public androidx.credentials.provider.PasswordCredentialEntry build();
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PasswordCredentialEntry.Companion {
-    method public androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public final class PendingIntentHandler {
-    ctor public PendingIntentHandler();
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public static void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public static void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public static void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public static void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-    field public static final androidx.credentials.provider.PendingIntentHandler.Companion Companion;
-  }
-
-  public static final class PendingIntentHandler.Companion {
-    method public androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-  }
-
-  public final class ProviderClearCredentialStateRequest {
-    ctor public ProviderClearCredentialStateRequest(androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-  }
-
-  public final class ProviderCreateCredentialRequest {
-    ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public androidx.credentials.CreateCredentialRequest getCallingRequest();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final androidx.credentials.CreateCredentialRequest callingRequest;
-  }
-
-  public final class ProviderGetCredentialRequest {
-    ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-  }
-
-  @RequiresApi(26) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PublicKeyCredentialEntry.Companion Companion;
-  }
-
-  public static final class PublicKeyCredentialEntry.Builder {
-    ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry build();
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PublicKeyCredentialEntry.Companion {
-    method public androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class RemoteEntry {
-    ctor public RemoteEntry(android.app.PendingIntent pendingIntent);
-    method public static androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-    method public android.app.PendingIntent getPendingIntent();
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.RemoteEntry.Companion Companion;
-  }
-
-  public static final class RemoteEntry.Builder {
-    ctor public RemoteEntry.Builder(android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.RemoteEntry build();
-  }
-
-  public static final class RemoteEntry.Companion {
-    method public androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-  }
-
-}
-
diff --git a/credentials/credentials/api/restricted_1.3.0-beta02.txt b/credentials/credentials/api/restricted_1.3.0-beta02.txt
deleted file mode 100644
index a0d0eda..0000000
--- a/credentials/credentials/api/restricted_1.3.0-beta02.txt
+++ /dev/null
@@ -1,1001 +0,0 @@
-// Signature format: 4.0
-package androidx.credentials {
-
-  public final class ClearCredentialStateRequest {
-    ctor public ClearCredentialStateRequest();
-  }
-
-  public abstract class CreateCredentialRequest {
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public static final androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getCredentialData();
-    method public final androidx.credentials.CreateCredentialRequest.DisplayInfo getDisplayInfo();
-    method public final String? getOrigin();
-    method public final String getType();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    method public final boolean preferImmediatelyAvailableCredentials();
-    property public final android.os.Bundle candidateQueryData;
-    property public final android.os.Bundle credentialData;
-    property public final androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final String? origin;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final String type;
-    field public static final androidx.credentials.CreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider);
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest createFrom(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean requireSystemProvider, optional String? origin);
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo {
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, optional CharSequence? userDisplayName);
-    ctor public CreateCredentialRequest.DisplayInfo(CharSequence userId, CharSequence? userDisplayName, String? preferDefaultProvider);
-    method @RequiresApi(23) public static androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-    method public CharSequence? getUserDisplayName();
-    method public CharSequence getUserId();
-    property public final CharSequence? userDisplayName;
-    property public final CharSequence userId;
-    field public static final androidx.credentials.CreateCredentialRequest.DisplayInfo.Companion Companion;
-  }
-
-  public static final class CreateCredentialRequest.DisplayInfo.Companion {
-    method @RequiresApi(23) public androidx.credentials.CreateCredentialRequest.DisplayInfo createFrom(android.os.Bundle from);
-  }
-
-  public abstract class CreateCredentialResponse {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public class CreateCustomCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin);
-    ctor public CreateCustomCredentialRequest(String type, android.os.Bundle credentialData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, androidx.credentials.CreateCredentialRequest.DisplayInfo displayInfo, optional boolean isAutoSelectAllowed, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-  }
-
-  public class CreateCustomCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreateCustomCredentialResponse(String type, android.os.Bundle data);
-  }
-
-  public final class CreatePasswordRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePasswordRequest(String id, String password);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePasswordRequest(String id, String password, optional String? origin, optional boolean preferImmediatelyAvailableCredentials, optional boolean isAutoSelectAllowed);
-    ctor public CreatePasswordRequest(String id, String password, String? origin, String? preferDefaultProvider, boolean preferImmediatelyAvailableCredentials, boolean isAutoSelectAllowed);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-  }
-
-  public final class CreatePasswordResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePasswordResponse();
-  }
-
-  public final class CreatePublicKeyCredentialRequest extends androidx.credentials.CreateCredentialRequest {
-    ctor public CreatePublicKeyCredentialRequest(String requestJson);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, optional byte[]? clientDataHash, optional boolean preferImmediatelyAvailableCredentials, optional String? origin, optional boolean isAutoSelectAllowed);
-    ctor public CreatePublicKeyCredentialRequest(String requestJson, byte[]? clientDataHash, boolean preferImmediatelyAvailableCredentials, String? origin, String? preferDefaultProvider, boolean isAutoSelectAllowed);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CreatePublicKeyCredentialResponse extends androidx.credentials.CreateCredentialResponse {
-    ctor public CreatePublicKeyCredentialResponse(String registrationResponseJson);
-    method public String getRegistrationResponseJson();
-    property public final String registrationResponseJson;
-  }
-
-  public abstract class Credential {
-    method public final android.os.Bundle getData();
-    method public final String getType();
-    property public final android.os.Bundle data;
-    property public final String type;
-  }
-
-  public interface CredentialManager {
-    method public default suspend Object? clearCredentialState(androidx.credentials.ClearCredentialStateRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public void clearCredentialStateAsync(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public static androidx.credentials.CredentialManager create(android.content.Context context);
-    method public default suspend Object? createCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.CreateCredentialResponse>);
-    method public void createCredentialAsync(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method @RequiresApi(34) public android.app.PendingIntent createSettingsPendingIntent();
-    method public default suspend Object? getCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method @RequiresApi(34) public default suspend Object? getCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, kotlin.coroutines.Continuation<? super androidx.credentials.GetCredentialResponse>);
-    method public void getCredentialAsync(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public void getCredentialAsync(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default suspend Object? prepareGetCredential(androidx.credentials.GetCredentialRequest request, kotlin.coroutines.Continuation<? super androidx.credentials.PrepareGetCredentialResponse>);
-    method @RequiresApi(34) public void prepareGetCredentialAsync(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    field public static final androidx.credentials.CredentialManager.Companion Companion;
-  }
-
-  public static final class CredentialManager.Companion {
-    method public androidx.credentials.CredentialManager create(android.content.Context context);
-  }
-
-  public interface CredentialManagerCallback<R, E> {
-    method public void onError(E e);
-    method public void onResult(R result);
-  }
-
-  public abstract class CredentialOption {
-    method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final android.os.Bundle getRequestData();
-    method public final String getType();
-    method public final int getTypePriorityHint();
-    method public final boolean isAutoSelectAllowed();
-    method public final boolean isSystemProviderRequired();
-    property public final java.util.Set<android.content.ComponentName> allowedProviders;
-    property public final android.os.Bundle candidateQueryData;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isSystemProviderRequired;
-    property public final android.os.Bundle requestData;
-    property public final String type;
-    property public final int typePriorityHint;
-    field public static final androidx.credentials.CredentialOption.Companion Companion;
-    field public static final int PRIORITY_DEFAULT = 2000; // 0x7d0
-    field public static final int PRIORITY_OIDC_OR_SIMILAR = 500; // 0x1f4
-    field public static final int PRIORITY_PASSKEY_OR_SIMILAR = 100; // 0x64
-    field public static final int PRIORITY_PASSWORD_OR_SIMILAR = 1000; // 0x3e8
-  }
-
-  public static final class CredentialOption.Companion {
-  }
-
-  public interface CredentialProvider {
-    method public boolean isAvailableOnDevice();
-    method public void onClearCredential(androidx.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-    method public void onCreateCredential(android.content.Context context, androidx.credentials.CreateCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.CreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public void onGetCredential(android.content.Context context, androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onGetCredential(android.content.Context context, androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle pendingGetCredentialHandle, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.GetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method @RequiresApi(34) public default void onPrepareCredential(androidx.credentials.GetCredentialRequest request, android.os.CancellationSignal? cancellationSignal, java.util.concurrent.Executor executor, androidx.credentials.CredentialManagerCallback<androidx.credentials.PrepareGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-  }
-
-  public class CustomCredential extends androidx.credentials.Credential {
-    ctor public CustomCredential(String type, android.os.Bundle data);
-  }
-
-  public final class GetCredentialRequest {
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName);
-    ctor public GetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, optional String? origin, optional boolean preferIdentityDocUi, optional android.content.ComponentName? preferUiBrandingComponentName, optional boolean preferImmediatelyAvailableCredentials);
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    method public String? getOrigin();
-    method public boolean getPreferIdentityDocUi();
-    method public android.content.ComponentName? getPreferUiBrandingComponentName();
-    method public boolean preferImmediatelyAvailableCredentials();
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-    property public final String? origin;
-    property public final boolean preferIdentityDocUi;
-    property public final boolean preferImmediatelyAvailableCredentials;
-    property public final android.content.ComponentName? preferUiBrandingComponentName;
-  }
-
-  public static final class GetCredentialRequest.Builder {
-    ctor public GetCredentialRequest.Builder();
-    method public androidx.credentials.GetCredentialRequest.Builder addCredentialOption(androidx.credentials.CredentialOption credentialOption);
-    method public androidx.credentials.GetCredentialRequest build();
-    method public androidx.credentials.GetCredentialRequest.Builder setCredentialOptions(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions);
-    method public androidx.credentials.GetCredentialRequest.Builder setOrigin(String origin);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferIdentityDocUi(boolean preferIdentityDocUi);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferImmediatelyAvailableCredentials(boolean preferImmediatelyAvailableCredentials);
-    method public androidx.credentials.GetCredentialRequest.Builder setPreferUiBrandingComponentName(android.content.ComponentName? component);
-  }
-
-  public final class GetCredentialResponse {
-    ctor public GetCredentialResponse(androidx.credentials.Credential credential);
-    method public androidx.credentials.Credential getCredential();
-    property public final androidx.credentials.Credential credential;
-  }
-
-  public class GetCustomCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    ctor public GetCustomCredentialOption(String type, android.os.Bundle requestData, android.os.Bundle candidateQueryData, boolean isSystemProviderRequired, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders, optional int typePriorityHint);
-  }
-
-  public final class GetPasswordOption extends androidx.credentials.CredentialOption {
-    ctor public GetPasswordOption();
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed);
-    ctor public GetPasswordOption(optional java.util.Set<java.lang.String> allowedUserIds, optional boolean isAutoSelectAllowed, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class GetPublicKeyCredentialOption extends androidx.credentials.CredentialOption {
-    ctor public GetPublicKeyCredentialOption(String requestJson);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash);
-    ctor public GetPublicKeyCredentialOption(String requestJson, optional byte[]? clientDataHash, optional java.util.Set<android.content.ComponentName> allowedProviders);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class PasswordCredential extends androidx.credentials.Credential {
-    ctor public PasswordCredential(String id, String password);
-    method public String getId();
-    method public String getPassword();
-    property public final String id;
-    property public final String password;
-    field public static final androidx.credentials.PasswordCredential.Companion Companion;
-    field public static final String TYPE_PASSWORD_CREDENTIAL = "android.credentials.TYPE_PASSWORD_CREDENTIAL";
-  }
-
-  public static final class PasswordCredential.Companion {
-  }
-
-  @RequiresApi(34) public final class PrepareGetCredentialResponse {
-    method public kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? getCredentialTypeDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasAuthResultsDelegate();
-    method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasRemoteResultsDelegate();
-    method public androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? getPendingGetCredentialHandle();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasAuthenticationResults();
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasCredentialResults(String credentialType);
-    method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS) public boolean hasRemoteResults();
-    method public boolean isNullHandlesForTest();
-    property public final kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? credentialTypeDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasAuthResultsDelegate;
-    property public final kotlin.jvm.functions.Function0<java.lang.Boolean>? hasRemoteResultsDelegate;
-    property public final boolean isNullHandlesForTest;
-    property public final androidx.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? pendingGetCredentialHandle;
-  }
-
-  @RequiresApi(34) public static final class PrepareGetCredentialResponse.PendingGetCredentialHandle {
-    ctor public PrepareGetCredentialResponse.PendingGetCredentialHandle(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle? frameworkHandle);
-  }
-
-  @VisibleForTesting public static final class PrepareGetCredentialResponse.TestBuilder {
-    ctor public PrepareGetCredentialResponse.TestBuilder();
-    method public androidx.credentials.PrepareGetCredentialResponse build();
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setCredentialTypeDelegate(kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasAuthResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-    method @VisibleForTesting public androidx.credentials.PrepareGetCredentialResponse.TestBuilder setHasRemoteResultsDelegate(kotlin.jvm.functions.Function0<java.lang.Boolean> handler);
-  }
-
-  public final class PublicKeyCredential extends androidx.credentials.Credential {
-    ctor public PublicKeyCredential(String authenticationResponseJson);
-    method public String getAuthenticationResponseJson();
-    property public final String authenticationResponseJson;
-    field public static final androidx.credentials.PublicKeyCredential.Companion Companion;
-    field public static final String TYPE_PUBLIC_KEY_CREDENTIAL = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL";
-  }
-
-  public static final class PublicKeyCredential.Companion {
-  }
-
-}
-
-package androidx.credentials.exceptions {
-
-  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialCustomException(String type);
-    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class ClearCredentialException extends java.lang.Exception {
-  }
-
-  public final class ClearCredentialInterruptedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialInterruptedException();
-    ctor public ClearCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialProviderConfigurationException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialProviderConfigurationException();
-    ctor public ClearCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnknownException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnknownException();
-    ctor public ClearCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class ClearCredentialUnsupportedException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCredentialUnsupportedException();
-    ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCancellationException();
-    ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialCustomException(String type);
-    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class CreateCredentialException extends java.lang.Exception {
-  }
-
-  public final class CreateCredentialInterruptedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialInterruptedException();
-    ctor public CreateCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialNoCreateOptionException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialNoCreateOptionException();
-    ctor public CreateCredentialNoCreateOptionException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialProviderConfigurationException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialProviderConfigurationException();
-    ctor public CreateCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnknownException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnknownException();
-    ctor public CreateCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class CreateCredentialUnsupportedException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCredentialUnsupportedException();
-    ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCancellationException();
-    ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialCustomException(String type);
-    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
-  public abstract class GetCredentialException extends java.lang.Exception {
-  }
-
-  public final class GetCredentialInterruptedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialInterruptedException();
-    ctor public GetCredentialInterruptedException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialProviderConfigurationException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialProviderConfigurationException();
-    ctor public GetCredentialProviderConfigurationException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnknownException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnknownException();
-    ctor public GetCredentialUnknownException(optional CharSequence? errorMessage);
-  }
-
-  public final class GetCredentialUnsupportedException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCredentialUnsupportedException();
-    ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
-  }
-
-  public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public NoCredentialException();
-    ctor public NoCredentialException(optional CharSequence? errorMessage);
-  }
-
-}
-
-package androidx.credentials.exceptions.domerrors {
-
-  public final class AbortError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public AbortError();
-  }
-
-  public final class ConstraintError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ConstraintError();
-  }
-
-  public final class DataCloneError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataCloneError();
-  }
-
-  public final class DataError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public DataError();
-  }
-
-  public abstract class DomError {
-    ctor public DomError(String type);
-  }
-
-  public final class EncodingError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public EncodingError();
-  }
-
-  public final class HierarchyRequestError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public HierarchyRequestError();
-  }
-
-  public final class InUseAttributeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InUseAttributeError();
-  }
-
-  public final class InvalidCharacterError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidCharacterError();
-  }
-
-  public final class InvalidModificationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidModificationError();
-  }
-
-  public final class InvalidNodeTypeError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidNodeTypeError();
-  }
-
-  public final class InvalidStateError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public InvalidStateError();
-  }
-
-  public final class NamespaceError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NamespaceError();
-  }
-
-  public final class NetworkError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NetworkError();
-  }
-
-  public final class NoModificationAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NoModificationAllowedError();
-  }
-
-  public final class NotAllowedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotAllowedError();
-  }
-
-  public final class NotFoundError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotFoundError();
-  }
-
-  public final class NotReadableError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotReadableError();
-  }
-
-  public final class NotSupportedError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public NotSupportedError();
-  }
-
-  public final class OperationError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OperationError();
-  }
-
-  public final class OptOutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public OptOutError();
-  }
-
-  public final class QuotaExceededError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public QuotaExceededError();
-  }
-
-  public final class ReadOnlyError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public ReadOnlyError();
-  }
-
-  public final class SecurityError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SecurityError();
-  }
-
-  public final class SyntaxError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public SyntaxError();
-  }
-
-  public final class TimeoutError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TimeoutError();
-  }
-
-  public final class TransactionInactiveError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public TransactionInactiveError();
-  }
-
-  public final class UnknownError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public UnknownError();
-  }
-
-  public final class VersionError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public VersionError();
-  }
-
-  public final class WrongDocumentError extends androidx.credentials.exceptions.domerrors.DomError {
-    ctor public WrongDocumentError();
-  }
-
-}
-
-package androidx.credentials.exceptions.publickeycredential {
-
-  public final class CreatePublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialException {
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public CreatePublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class CreatePublicKeyCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-  }
-
-  public final class GetPublicKeyCredentialDomException extends androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialException {
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError);
-    ctor public GetPublicKeyCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, optional CharSequence? errorMessage);
-    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
-    property public final androidx.credentials.exceptions.domerrors.DomError domError;
-  }
-
-  public class GetPublicKeyCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-  }
-
-}
-
-package androidx.credentials.provider {
-
-  public final class Action {
-    ctor public Action(CharSequence title, android.app.PendingIntent pendingIntent, optional CharSequence? subtitle);
-    method public static androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.Action.Companion Companion;
-  }
-
-  public static final class Action.Builder {
-    ctor public Action.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.Action build();
-    method public androidx.credentials.provider.Action.Builder setSubtitle(CharSequence? subtitle);
-  }
-
-  public static final class Action.Companion {
-    method public androidx.credentials.provider.Action? fromAction(android.service.credentials.Action action);
-  }
-
-  public final class AuthenticationAction {
-    ctor public AuthenticationAction(CharSequence title, android.app.PendingIntent pendingIntent);
-    method @RequiresApi(34) public static androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTitle();
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence title;
-    field public static final androidx.credentials.provider.AuthenticationAction.Companion Companion;
-  }
-
-  public static final class AuthenticationAction.Builder {
-    ctor public AuthenticationAction.Builder(CharSequence title, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.AuthenticationAction build();
-  }
-
-  public static final class AuthenticationAction.Companion {
-    method @RequiresApi(34) public androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
-  }
-
-  public abstract class BeginCreateCredentialRequest {
-    ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static final android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public static final androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public final androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getType();
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    property public final android.os.Bundle candidateQueryData;
-    property public final String type;
-    field public static final androidx.credentials.provider.BeginCreateCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
-    method public androidx.credentials.provider.BeginCreateCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginCreateCredentialResponse {
-    ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public static androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.CreateEntry> getCreateEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.CreateEntry> createEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginCreateCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginCreateCredentialResponse.Builder {
-    ctor public BeginCreateCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder addCreateEntry(androidx.credentials.provider.CreateEntry createEntry);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse build();
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setCreateEntries(java.util.List<androidx.credentials.provider.CreateEntry> createEntries);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginCreateCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
-    method public androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginCreateCustomCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreateCustomCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-  }
-
-  public final class BeginCreatePasswordCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePasswordCredentialRequest(androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginCreatePublicKeyCredentialRequest extends androidx.credentials.provider.BeginCreateCredentialRequest {
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData);
-    ctor public BeginCreatePublicKeyCredentialRequest(String requestJson, androidx.credentials.provider.CallingAppInfo? callingAppInfo, android.os.Bundle candidateQueryData, optional byte[]? clientDataHash);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginCreatePublicKeyCredentialRequest createForTest(android.os.Bundle data, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public abstract class BeginGetCredentialOption {
-    method public final android.os.Bundle getCandidateQueryData();
-    method public final String getId();
-    method public final String getType();
-    property public final android.os.Bundle candidateQueryData;
-    property public final String id;
-    property public final String type;
-  }
-
-  public final class BeginGetCredentialRequest {
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions);
-    ctor public BeginGetCredentialRequest(java.util.List<? extends androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions, optional androidx.credentials.provider.CallingAppInfo? callingAppInfo);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.BeginGetCredentialOption> getBeginGetCredentialOptions();
-    method public androidx.credentials.provider.CallingAppInfo? getCallingAppInfo();
-    property public final java.util.List<androidx.credentials.provider.BeginGetCredentialOption> beginGetCredentialOptions;
-    property public final androidx.credentials.provider.CallingAppInfo? callingAppInfo;
-    field public static final androidx.credentials.provider.BeginGetCredentialRequest.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialRequest.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialRequest request);
-    method public androidx.credentials.provider.BeginGetCredentialRequest? fromBundle(android.os.Bundle bundle);
-  }
-
-  public final class BeginGetCredentialResponse {
-    ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
-    method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-    method public java.util.List<androidx.credentials.provider.Action> getActions();
-    method public java.util.List<androidx.credentials.provider.AuthenticationAction> getAuthenticationActions();
-    method public java.util.List<androidx.credentials.provider.CredentialEntry> getCredentialEntries();
-    method public androidx.credentials.provider.RemoteEntry? getRemoteEntry();
-    property public final java.util.List<androidx.credentials.provider.Action> actions;
-    property public final java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions;
-    property public final java.util.List<androidx.credentials.provider.CredentialEntry> credentialEntries;
-    property public final androidx.credentials.provider.RemoteEntry? remoteEntry;
-    field public static final androidx.credentials.provider.BeginGetCredentialResponse.Companion Companion;
-  }
-
-  public static final class BeginGetCredentialResponse.Builder {
-    ctor public BeginGetCredentialResponse.Builder();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAction(androidx.credentials.provider.Action action);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addAuthenticationAction(androidx.credentials.provider.AuthenticationAction authenticationAction);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder addCredentialEntry(androidx.credentials.provider.CredentialEntry entry);
-    method public androidx.credentials.provider.BeginGetCredentialResponse build();
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setActions(java.util.List<androidx.credentials.provider.Action> actions);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setAuthenticationActions(java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationEntries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setCredentialEntries(java.util.List<? extends androidx.credentials.provider.CredentialEntry> entries);
-    method public androidx.credentials.provider.BeginGetCredentialResponse.Builder setRemoteEntry(androidx.credentials.provider.RemoteEntry? remoteEntry);
-  }
-
-  public static final class BeginGetCredentialResponse.Companion {
-    method public android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
-  }
-
-  public class BeginGetCustomCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetCustomCredentialOption(String id, String type, android.os.Bundle candidateQueryData);
-  }
-
-  public final class BeginGetPasswordOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPasswordOption(java.util.Set<java.lang.String> allowedUserIds, android.os.Bundle candidateQueryData, String id);
-    method @VisibleForTesting public static androidx.credentials.provider.BeginGetPasswordOption createForTest(android.os.Bundle data, String id);
-    method public java.util.Set<java.lang.String> getAllowedUserIds();
-    property public final java.util.Set<java.lang.String> allowedUserIds;
-  }
-
-  public final class BeginGetPublicKeyCredentialOption extends androidx.credentials.provider.BeginGetCredentialOption {
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson);
-    ctor public BeginGetPublicKeyCredentialOption(android.os.Bundle candidateQueryData, String id, String requestJson, optional byte[]? clientDataHash);
-    method public byte[]? getClientDataHash();
-    method public String getRequestJson();
-    property public final byte[]? clientDataHash;
-    property public final String requestJson;
-  }
-
-  public final class CallingAppInfo {
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo);
-    ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo, optional String? origin);
-    method public String? getOrigin(String privilegedAllowlist);
-    method public String getPackageName();
-    method public android.content.pm.SigningInfo getSigningInfo();
-    method public boolean isOriginPopulated();
-    property public final String packageName;
-    property public final android.content.pm.SigningInfo signingInfo;
-  }
-
-  @RequiresApi(26) public final class CreateEntry {
-    ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
-    method public static androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-    method public CharSequence getAccountName();
-    method public CharSequence? getDescription();
-    method public android.graphics.drawable.Icon? getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public Integer? getPasswordCredentialCount();
-    method public android.app.PendingIntent getPendingIntent();
-    method public Integer? getPublicKeyCredentialCount();
-    method public Integer? getTotalCredentialCount();
-    method public boolean isAutoSelectAllowed();
-    property public final CharSequence accountName;
-    property public final CharSequence? description;
-    property public final android.graphics.drawable.Icon? icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.CreateEntry.Companion Companion;
-  }
-
-  public static final class CreateEntry.Builder {
-    ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.CreateEntry build();
-    method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
-    method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
-    method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CreateEntry.Builder setPasswordCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setPublicKeyCredentialCount(int count);
-    method public androidx.credentials.provider.CreateEntry.Builder setTotalCredentialCount(int count);
-  }
-
-  public static final class CreateEntry.Companion {
-    method public androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
-  }
-
-  public abstract class CredentialEntry {
-    method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public final CharSequence? getAffiliatedDomain();
-    method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
-    method public final CharSequence getEntryGroupId();
-    method public final boolean isDefaultIconPreferredAsSingleProvider();
-    property public final CharSequence? affiliatedDomain;
-    property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
-    property public final CharSequence entryGroupId;
-    property public final boolean isDefaultIconPreferredAsSingleProvider;
-    field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
-  }
-
-  public static final class CredentialEntry.Companion {
-    method public androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public abstract class CredentialProviderService extends android.service.credentials.CredentialProviderService {
-    ctor public CredentialProviderService();
-    method public final void onBeginCreateCredential(android.service.credentials.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.credentials.CreateCredentialException> callback);
-    method public abstract void onBeginCreateCredentialRequest(androidx.credentials.provider.BeginCreateCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginCreateCredentialResponse,androidx.credentials.exceptions.CreateCredentialException> callback);
-    method public final void onBeginGetCredential(android.service.credentials.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException> callback);
-    method public abstract void onBeginGetCredentialRequest(androidx.credentials.provider.BeginGetCredentialRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,androidx.credentials.exceptions.GetCredentialException> callback);
-    method public final void onClearCredentialState(android.service.credentials.ClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException> callback);
-    method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
-  }
-
-  @RequiresApi(26) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence? getSubtitle();
-    method public CharSequence getTitle();
-    method public String getType();
-    method public CharSequence? getTypeDisplayName();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence? subtitle;
-    property public final CharSequence title;
-    property public String type;
-    property public final CharSequence? typeDisplayName;
-    field public static final androidx.credentials.provider.CustomCredentialEntry.Companion Companion;
-  }
-
-  public static final class CustomCredentialEntry.Builder {
-    ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
-    method public androidx.credentials.provider.CustomCredentialEntry build();
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setEntryGroupId(CharSequence entryGroupId);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setSubtitle(CharSequence? subtitle);
-    method public androidx.credentials.provider.CustomCredentialEntry.Builder setTypeDisplayName(CharSequence? typeDisplayName);
-  }
-
-  public static final class CustomCredentialEntry.Companion {
-    method public androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class IntentHandlerConverters {
-    method @RequiresApi(34) public static androidx.credentials.provider.BeginGetCredentialResponse? getBeginGetResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialResponse? getCreateCredentialCredentialResponse(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.CreateCredentialException? getCreateCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialException? getGetCredentialException(android.content.Intent);
-    method @RequiresApi(34) public static android.credentials.GetCredentialResponse? getGetCredentialResponse(android.content.Intent);
-  }
-
-  @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PasswordCredentialEntry.Companion Companion;
-  }
-
-  public static final class PasswordCredentialEntry.Builder {
-    ctor public PasswordCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption);
-    method public androidx.credentials.provider.PasswordCredentialEntry build();
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PasswordCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PasswordCredentialEntry.Companion {
-    method public androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  @RequiresApi(34) public final class PendingIntentHandler {
-    ctor public PendingIntentHandler();
-    method public static androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public static androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public static void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public static void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public static void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public static void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public static void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-    field public static final androidx.credentials.provider.PendingIntentHandler.Companion Companion;
-  }
-
-  public static final class PendingIntentHandler.Companion {
-    method public androidx.credentials.provider.BeginGetCredentialRequest? retrieveBeginGetCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderCreateCredentialRequest? retrieveProviderCreateCredentialRequest(android.content.Intent intent);
-    method public androidx.credentials.provider.ProviderGetCredentialRequest? retrieveProviderGetCredentialRequest(android.content.Intent intent);
-    method public void setBeginGetCredentialResponse(android.content.Intent intent, androidx.credentials.provider.BeginGetCredentialResponse response);
-    method public void setCreateCredentialException(android.content.Intent intent, androidx.credentials.exceptions.CreateCredentialException exception);
-    method public void setCreateCredentialResponse(android.content.Intent intent, androidx.credentials.CreateCredentialResponse response);
-    method public void setGetCredentialException(android.content.Intent intent, androidx.credentials.exceptions.GetCredentialException exception);
-    method public void setGetCredentialResponse(android.content.Intent intent, androidx.credentials.GetCredentialResponse response);
-  }
-
-  public final class ProviderClearCredentialStateRequest {
-    ctor public ProviderClearCredentialStateRequest(androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-  }
-
-  public final class ProviderCreateCredentialRequest {
-    ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public androidx.credentials.CreateCredentialRequest getCallingRequest();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final androidx.credentials.CreateCredentialRequest callingRequest;
-  }
-
-  public final class ProviderGetCredentialRequest {
-    ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo);
-    method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
-    method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
-    property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
-    property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
-  }
-
-  @RequiresApi(26) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
-    ctor @Deprecated public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
-    ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider);
-    method public static androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-    method public CharSequence? getDisplayName();
-    method public android.graphics.drawable.Icon getIcon();
-    method public java.time.Instant? getLastUsedTime();
-    method public android.app.PendingIntent getPendingIntent();
-    method public CharSequence getTypeDisplayName();
-    method public CharSequence getUsername();
-    method public boolean hasDefaultIcon();
-    method public boolean isAutoSelectAllowed();
-    method public boolean isAutoSelectAllowedFromOption();
-    property public final CharSequence? displayName;
-    property public final boolean hasDefaultIcon;
-    property public final android.graphics.drawable.Icon icon;
-    property public final boolean isAutoSelectAllowed;
-    property public final boolean isAutoSelectAllowedFromOption;
-    property public final java.time.Instant? lastUsedTime;
-    property public final android.app.PendingIntent pendingIntent;
-    property public final CharSequence typeDisplayName;
-    property public final CharSequence username;
-    field public static final androidx.credentials.provider.PublicKeyCredentialEntry.Companion Companion;
-  }
-
-  public static final class PublicKeyCredentialEntry.Builder {
-    ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry build();
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
-    method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
-  }
-
-  public static final class PublicKeyCredentialEntry.Companion {
-    method public androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
-  }
-
-  public final class RemoteEntry {
-    ctor public RemoteEntry(android.app.PendingIntent pendingIntent);
-    method public static androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-    method public android.app.PendingIntent getPendingIntent();
-    property public final android.app.PendingIntent pendingIntent;
-    field public static final androidx.credentials.provider.RemoteEntry.Companion Companion;
-  }
-
-  public static final class RemoteEntry.Builder {
-    ctor public RemoteEntry.Builder(android.app.PendingIntent pendingIntent);
-    method public androidx.credentials.provider.RemoteEntry build();
-  }
-
-  public static final class RemoteEntry.Companion {
-    method public androidx.credentials.provider.RemoteEntry? fromRemoteEntry(android.service.credentials.RemoteEntry remoteEntry);
-  }
-
-}
-
diff --git a/credentials/credentials/api/restricted_current.txt b/credentials/credentials/api/restricted_current.txt
index a0d0eda..98c8008 100644
--- a/credentials/credentials/api/restricted_current.txt
+++ b/credentials/credentials/api/restricted_current.txt
@@ -3,6 +3,11 @@
 
   public final class ClearCredentialStateRequest {
     ctor public ClearCredentialStateRequest();
+    ctor public ClearCredentialStateRequest(int requestType);
+    method public android.os.Bundle getRequestBundle();
+    method public int getRequestType();
+    property public final android.os.Bundle requestBundle;
+    property public final int requestType;
   }
 
   public abstract class CreateCredentialRequest {
@@ -101,11 +106,40 @@
     property public final String registrationResponseJson;
   }
 
+  public final class CreateRestoreCredentialRequest extends androidx.credentials.CreateCredentialRequest {
+    ctor public CreateRestoreCredentialRequest(String requestJson, boolean isCloudBackupEnabled);
+    method public String getRequestJson();
+    method public boolean isCloudBackupEnabled();
+    property public final boolean isCloudBackupEnabled;
+    property public final String requestJson;
+    field public static final androidx.credentials.CreateRestoreCredentialRequest.Companion Companion;
+  }
+
+  public static final class CreateRestoreCredentialRequest.Companion {
+  }
+
+  public final class CreateRestoreCredentialResponse extends androidx.credentials.CreateCredentialResponse {
+    ctor public CreateRestoreCredentialResponse(String responseJson, android.os.Bundle data);
+    method public static androidx.credentials.CreateRestoreCredentialResponse createFrom(android.os.Bundle data);
+    method public String getResponseJson();
+    property public final String responseJson;
+    field public static final String BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_RESPONSE = "androidx.credentials.BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_RESPONSE";
+    field public static final androidx.credentials.CreateRestoreCredentialResponse.Companion Companion;
+  }
+
+  public static final class CreateRestoreCredentialResponse.Companion {
+    method public androidx.credentials.CreateRestoreCredentialResponse createFrom(android.os.Bundle data);
+  }
+
   public abstract class Credential {
     method public final android.os.Bundle getData();
     method public final String getType();
     property public final android.os.Bundle data;
     property public final String type;
+    field public static final androidx.credentials.Credential.Companion Companion;
+  }
+
+  public static final class Credential.Companion {
   }
 
   public interface CredentialManager {
@@ -133,6 +167,11 @@
     method public void onResult(R result);
   }
 
+  public final class CredentialManagerViewHandler {
+    method public static androidx.credentials.PendingGetCredentialRequest? getPendingGetCredentialRequest(android.view.View);
+    method public static void setPendingGetCredentialRequest(android.view.View, androidx.credentials.PendingGetCredentialRequest?);
+  }
+
   public abstract class CredentialOption {
     method public final java.util.Set<android.content.ComponentName> getAllowedProviders();
     method public final android.os.Bundle getCandidateQueryData();
@@ -232,6 +271,12 @@
     property public final String requestJson;
   }
 
+  public final class GetRestoreCredentialOption extends androidx.credentials.CredentialOption {
+    ctor public GetRestoreCredentialOption(String requestJson);
+    method public String getRequestJson();
+    property public final String requestJson;
+  }
+
   public final class PasswordCredential extends androidx.credentials.Credential {
     ctor public PasswordCredential(String id, String password);
     method public String getId();
@@ -245,6 +290,14 @@
   public static final class PasswordCredential.Companion {
   }
 
+  public final class PendingGetCredentialRequest {
+    ctor public PendingGetCredentialRequest(androidx.credentials.GetCredentialRequest request, kotlin.jvm.functions.Function1<? super androidx.credentials.GetCredentialResponse,kotlin.Unit> callback);
+    method public kotlin.jvm.functions.Function1<androidx.credentials.GetCredentialResponse,kotlin.Unit> getCallback();
+    method public androidx.credentials.GetCredentialRequest getRequest();
+    property public final kotlin.jvm.functions.Function1<androidx.credentials.GetCredentialResponse,kotlin.Unit> callback;
+    property public final androidx.credentials.GetCredentialRequest request;
+  }
+
   @RequiresApi(34) public final class PrepareGetCredentialResponse {
     method public kotlin.jvm.functions.Function1<java.lang.String,java.lang.Boolean>? getCredentialTypeDelegate();
     method public kotlin.jvm.functions.Function0<java.lang.Boolean>? getHasAuthResultsDelegate();
@@ -284,6 +337,16 @@
   public static final class PublicKeyCredential.Companion {
   }
 
+  public final class RestoreCredential extends androidx.credentials.Credential {
+    method public String getAuthenticationResponseJson();
+    property public final String authenticationResponseJson;
+    field public static final androidx.credentials.RestoreCredential.Companion Companion;
+    field public static final String TYPE_RESTORE_CREDENTIAL = "androidx.credentials.TYPE_RESTORE_CREDENTIAL";
+  }
+
+  public static final class RestoreCredential.Companion {
+  }
+
 }
 
 package androidx.credentials.exceptions {
@@ -548,6 +611,20 @@
 
 }
 
+package androidx.credentials.exceptions.restorecredential {
+
+  public final class CreateRestoreCredentialDomException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public CreateRestoreCredentialDomException(androidx.credentials.exceptions.domerrors.DomError domError, CharSequence errorMessage);
+    method public androidx.credentials.exceptions.domerrors.DomError getDomError();
+    property public final androidx.credentials.exceptions.domerrors.DomError domError;
+  }
+
+  public final class E2eeUnavailableException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public E2eeUnavailableException(CharSequence errorMessage);
+  }
+
+}
+
 package androidx.credentials.provider {
 
   public final class Action {
@@ -591,6 +668,21 @@
     method @RequiresApi(34) public androidx.credentials.provider.AuthenticationAction? fromAction(android.service.credentials.Action authenticationAction);
   }
 
+  public final class AuthenticationError {
+    ctor public AuthenticationError(int errorCode);
+    ctor public AuthenticationError(int errorCode, optional CharSequence? errorMsg);
+    method public int getErrorCode();
+    method public CharSequence? getErrorMsg();
+    property public final int errorCode;
+    property public final CharSequence? errorMsg;
+  }
+
+  public final class AuthenticationResult {
+    ctor public AuthenticationResult(int authenticationType);
+    method public int getAuthenticationType();
+    property public final int authenticationType;
+  }
+
   public abstract class BeginCreateCredentialRequest {
     ctor public BeginCreateCredentialRequest(String type, android.os.Bundle candidateQueryData, androidx.credentials.provider.CallingAppInfo? callingAppInfo);
     method public static final android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialRequest request);
@@ -610,6 +702,7 @@
   }
 
   public final class BeginCreateCredentialResponse {
+    ctor public BeginCreateCredentialResponse();
     ctor public BeginCreateCredentialResponse(optional java.util.List<androidx.credentials.provider.CreateEntry> createEntries, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
     method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginCreateCredentialResponse response);
     method public static androidx.credentials.provider.BeginCreateCredentialResponse? fromBundle(android.os.Bundle bundle);
@@ -678,6 +771,7 @@
   }
 
   public final class BeginGetCredentialResponse {
+    ctor public BeginGetCredentialResponse();
     ctor public BeginGetCredentialResponse(optional java.util.List<? extends androidx.credentials.provider.CredentialEntry> credentialEntries, optional java.util.List<androidx.credentials.provider.Action> actions, optional java.util.List<androidx.credentials.provider.AuthenticationAction> authenticationActions, optional androidx.credentials.provider.RemoteEntry? remoteEntry);
     method public static android.os.Bundle asBundle(androidx.credentials.provider.BeginGetCredentialResponse response);
     method public static androidx.credentials.provider.BeginGetCredentialResponse? fromBundle(android.os.Bundle bundle);
@@ -729,6 +823,35 @@
     property public final String requestJson;
   }
 
+  @RequiresApi(35) public final class BiometricPromptData {
+    ctor public BiometricPromptData();
+    ctor public BiometricPromptData();
+    ctor public BiometricPromptData(optional androidx.biometric.BiometricPrompt.CryptoObject? cryptoObject);
+    ctor public BiometricPromptData(optional androidx.biometric.BiometricPrompt.CryptoObject? cryptoObject, optional int allowedAuthenticators);
+    method public int getAllowedAuthenticators();
+    method public androidx.biometric.BiometricPrompt.CryptoObject? getCryptoObject();
+    property public final int allowedAuthenticators;
+    property public final androidx.biometric.BiometricPrompt.CryptoObject? cryptoObject;
+  }
+
+  public static final class BiometricPromptData.Builder {
+    ctor public BiometricPromptData.Builder();
+    method public androidx.credentials.provider.BiometricPromptData build();
+    method public androidx.credentials.provider.BiometricPromptData.Builder setAllowedAuthenticators(int allowedAuthenticators);
+    method public androidx.credentials.provider.BiometricPromptData.Builder setCryptoObject(androidx.biometric.BiometricPrompt.CryptoObject cryptoObject);
+  }
+
+  public final class BiometricPromptResult {
+    ctor public BiometricPromptResult(androidx.credentials.provider.AuthenticationError authenticationError);
+    ctor public BiometricPromptResult(androidx.credentials.provider.AuthenticationResult authenticationResult);
+    method public androidx.credentials.provider.AuthenticationError? getAuthenticationError();
+    method public androidx.credentials.provider.AuthenticationResult? getAuthenticationResult();
+    method public boolean isSuccessful();
+    property public final androidx.credentials.provider.AuthenticationError? authenticationError;
+    property public final androidx.credentials.provider.AuthenticationResult? authenticationResult;
+    property public final boolean isSuccessful;
+  }
+
   public final class CallingAppInfo {
     ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo);
     ctor public CallingAppInfo(String packageName, android.content.pm.SigningInfo signingInfo, optional String? origin);
@@ -740,10 +863,12 @@
     property public final android.content.pm.SigningInfo signingInfo;
   }
 
-  @RequiresApi(26) public final class CreateEntry {
+  @RequiresApi(23) public final class CreateEntry {
     ctor public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public CreateEntry(CharSequence accountName, android.app.PendingIntent pendingIntent, optional CharSequence? description, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon? icon, optional Integer? passwordCredentialCount, optional Integer? publicKeyCredentialCount, optional Integer? totalCredentialCount, optional boolean isAutoSelectAllowed, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.CreateEntry? fromCreateEntry(android.service.credentials.CreateEntry createEntry);
     method public CharSequence getAccountName();
+    method public androidx.credentials.provider.BiometricPromptData? getBiometricPromptData();
     method public CharSequence? getDescription();
     method public android.graphics.drawable.Icon? getIcon();
     method public java.time.Instant? getLastUsedTime();
@@ -753,6 +878,7 @@
     method public Integer? getTotalCredentialCount();
     method public boolean isAutoSelectAllowed();
     property public final CharSequence accountName;
+    property public final androidx.credentials.provider.BiometricPromptData? biometricPromptData;
     property public final CharSequence? description;
     property public final android.graphics.drawable.Icon? icon;
     property public final boolean isAutoSelectAllowed;
@@ -765,6 +891,7 @@
     ctor public CreateEntry.Builder(CharSequence accountName, android.app.PendingIntent pendingIntent);
     method public androidx.credentials.provider.CreateEntry build();
     method public androidx.credentials.provider.CreateEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.CreateEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.CreateEntry.Builder setDescription(CharSequence? description);
     method public androidx.credentials.provider.CreateEntry.Builder setIcon(android.graphics.drawable.Icon? icon);
     method public androidx.credentials.provider.CreateEntry.Builder setLastUsedTime(java.time.Instant? lastUsedTime);
@@ -781,10 +908,12 @@
     method public static final androidx.credentials.provider.CredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public final CharSequence? getAffiliatedDomain();
     method public final androidx.credentials.provider.BeginGetCredentialOption getBeginGetCredentialOption();
+    method public final androidx.credentials.provider.BiometricPromptData? getBiometricPromptData();
     method public final CharSequence getEntryGroupId();
     method public final boolean isDefaultIconPreferredAsSingleProvider();
     property public final CharSequence? affiliatedDomain;
     property public final androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption;
+    property public final androidx.credentials.provider.BiometricPromptData? biometricPromptData;
     property public final CharSequence entryGroupId;
     property public final boolean isDefaultIconPreferredAsSingleProvider;
     field public static final androidx.credentials.provider.CredentialEntry.Companion Companion;
@@ -804,9 +933,10 @@
     method public abstract void onClearCredentialStateRequest(androidx.credentials.provider.ProviderClearCredentialStateRequest request, android.os.CancellationSignal cancellationSignal, android.os.OutcomeReceiver<java.lang.Void?,androidx.credentials.exceptions.ClearCredentialException> callback);
   }
 
-  @RequiresApi(26) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+  @RequiresApi(23) public final class CustomCredentialEntry extends androidx.credentials.provider.CredentialEntry {
     ctor @Deprecated public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
     ctor public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public CustomCredentialEntry(android.content.Context context, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption, optional CharSequence? subtitle, optional CharSequence? typeDisplayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence entryGroupId, optional boolean isDefaultIconPreferredAsSingleProvider, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.CustomCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public android.graphics.drawable.Icon getIcon();
     method public java.time.Instant? getLastUsedTime();
@@ -835,6 +965,7 @@
     ctor public CustomCredentialEntry.Builder(android.content.Context context, String type, CharSequence title, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetCredentialOption beginGetCredentialOption);
     method public androidx.credentials.provider.CustomCredentialEntry build();
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.CustomCredentialEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setEntryGroupId(CharSequence entryGroupId);
     method public androidx.credentials.provider.CustomCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
@@ -855,9 +986,10 @@
     method @RequiresApi(34) public static android.credentials.GetCredentialResponse? getGetCredentialResponse(android.content.Intent);
   }
 
-  @RequiresApi(26) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+  @RequiresApi(23) public final class PasswordCredentialEntry extends androidx.credentials.provider.CredentialEntry {
     ctor @Deprecated public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
     ctor public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public PasswordCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPasswordOption beginGetPasswordOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional CharSequence? affiliatedDomain, optional boolean isDefaultIconPreferredAsSingleProvider, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.PasswordCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public CharSequence? getDisplayName();
     method public android.graphics.drawable.Icon getIcon();
@@ -885,6 +1017,7 @@
     method public androidx.credentials.provider.PasswordCredentialEntry build();
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAffiliatedDomain(CharSequence? affiliatedDomain);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.PasswordCredentialEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setDisplayName(CharSequence? displayName);
     method public androidx.credentials.provider.PasswordCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
@@ -927,23 +1060,30 @@
 
   public final class ProviderCreateCredentialRequest {
     ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo);
+    ctor public ProviderCreateCredentialRequest(androidx.credentials.CreateCredentialRequest callingRequest, androidx.credentials.provider.CallingAppInfo callingAppInfo, optional androidx.credentials.provider.BiometricPromptResult? biometricPromptResult);
+    method public androidx.credentials.provider.BiometricPromptResult? getBiometricPromptResult();
     method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
     method public androidx.credentials.CreateCredentialRequest getCallingRequest();
+    property public final androidx.credentials.provider.BiometricPromptResult? biometricPromptResult;
     property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
     property public final androidx.credentials.CreateCredentialRequest callingRequest;
   }
 
   public final class ProviderGetCredentialRequest {
     ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo);
+    ctor public ProviderGetCredentialRequest(java.util.List<? extends androidx.credentials.CredentialOption> credentialOptions, androidx.credentials.provider.CallingAppInfo callingAppInfo, optional androidx.credentials.provider.BiometricPromptResult? biometricPromptResult);
+    method public androidx.credentials.provider.BiometricPromptResult? getBiometricPromptResult();
     method public androidx.credentials.provider.CallingAppInfo getCallingAppInfo();
     method public java.util.List<androidx.credentials.CredentialOption> getCredentialOptions();
+    property public final androidx.credentials.provider.BiometricPromptResult? biometricPromptResult;
     property public final androidx.credentials.provider.CallingAppInfo callingAppInfo;
     property public final java.util.List<androidx.credentials.CredentialOption> credentialOptions;
   }
 
-  @RequiresApi(26) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
+  @RequiresApi(23) public final class PublicKeyCredentialEntry extends androidx.credentials.provider.CredentialEntry {
     ctor @Deprecated public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed);
     ctor public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public PublicKeyCredentialEntry(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption, optional CharSequence? displayName, optional java.time.Instant? lastUsedTime, optional android.graphics.drawable.Icon icon, optional boolean isAutoSelectAllowed, optional boolean isDefaultIconPreferredAsSingleProvider, optional androidx.credentials.provider.BiometricPromptData? biometricPromptData);
     method public static androidx.credentials.provider.PublicKeyCredentialEntry? fromCredentialEntry(android.service.credentials.CredentialEntry credentialEntry);
     method public CharSequence? getDisplayName();
     method public android.graphics.drawable.Icon getIcon();
@@ -970,6 +1110,7 @@
     ctor public PublicKeyCredentialEntry.Builder(android.content.Context context, CharSequence username, android.app.PendingIntent pendingIntent, androidx.credentials.provider.BeginGetPublicKeyCredentialOption beginGetPublicKeyCredentialOption);
     method public androidx.credentials.provider.PublicKeyCredentialEntry build();
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setAutoSelectAllowed(boolean autoSelectAllowed);
+    method @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setBiometricPromptData(androidx.credentials.provider.BiometricPromptData biometricPromptData);
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDefaultIconPreferredAsSingleProvider(boolean isDefaultIconPreferredAsSingleProvider);
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setDisplayName(CharSequence? displayName);
     method public androidx.credentials.provider.PublicKeyCredentialEntry.Builder setIcon(android.graphics.drawable.Icon icon);
diff --git a/credentials/credentials/build.gradle b/credentials/credentials/build.gradle
index da177eb..eb89d1a 100644
--- a/credentials/credentials/build.gradle
+++ b/credentials/credentials/build.gradle
@@ -30,9 +30,11 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.5.0")
+    api("androidx.annotation:annotation:1.8.1")
+    api(project(":biometric:biometric-ktx"))
     api(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesCore)
+    implementation("androidx.core:core:1.15.0-alpha01")
 
     androidTestImplementation("androidx.activity:activity:1.2.0")
     androidTestImplementation(libs.junit)
@@ -49,6 +51,7 @@
 }
 
 android {
+    compileSdk = 35
     namespace "androidx.credentials"
 
     defaultConfig {
@@ -60,7 +63,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Android Credentials Library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":credentials:credentials-samples"))
 }
diff --git a/credentials/credentials/lint-baseline.xml b/credentials/credentials/lint-baseline.xml
index 71eb501..5caa612 100644
--- a/credentials/credentials/lint-baseline.xml
+++ b/credentials/credentials/lint-baseline.xml
@@ -1,743 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder("
-        errorLine2="                                     ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder("
-        errorLine2="                                     ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Uri.EMPTY, SliceSpec("
-        errorLine2="                           ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Uri.EMPTY, SliceSpec("
-        errorLine2="                           ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addText("
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addText("
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addText("
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addText("
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            sliceBuilder.addAction("
-        errorLine2="                         ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            sliceBuilder.addAction("
-        errorLine2="                         ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Slice.Builder(sliceBuilder)"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Slice.Builder(sliceBuilder)"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
-        errorLine2="                     ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
-        errorLine2="                     ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .build(),"
-        errorLine2="                     ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .build(),"
-        errorLine2="                     ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return sliceBuilder.build()"
-        errorLine2="                                ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return sliceBuilder.build()"
-        errorLine2="                                ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            slice.items.forEach {"
-        errorLine2="                  ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            slice.items.forEach {"
-        errorLine2="                  ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (it.hasHint(SLICE_HINT_TITLE)) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (it.hasHint(SLICE_HINT_TITLE)) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    title = it.text"
-        errorLine2="                               ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    title = it.text"
-        errorLine2="                               ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    subtitle = it.text"
-        errorLine2="                                  ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    subtitle = it.text"
-        errorLine2="                                  ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    pendingIntent = it.action"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.Action.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    pendingIntent = it.action"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/Action.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder("
-        errorLine2="                                     ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder("
-        errorLine2="                                     ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Uri.EMPTY, SliceSpec("
-        errorLine2="                           ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Uri.EMPTY, SliceSpec("
-        errorLine2="                           ^">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addAction("
-        errorLine2="                 ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addAction("
-        errorLine2="                 ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    Slice.Builder(sliceBuilder)"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    Slice.Builder(sliceBuilder)"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
-        errorLine2="                         ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
-        errorLine2="                         ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .build(),"
-        errorLine2="                         ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .build(),"
-        errorLine2="                         ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addText(title, /*subType=*/null, listOf(SLICE_HINT_TITLE))"
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                .addText(title, /*subType=*/null, listOf(SLICE_HINT_TITLE))"
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return sliceBuilder.build()"
-        errorLine2="                                ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return sliceBuilder.build()"
-        errorLine2="                                ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            slice.items.forEach {"
-        errorLine2="                  ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            slice.items.forEach {"
-        errorLine2="                  ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    pendingIntent = it.action"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    pendingIntent = it.action"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                } else if (it.hasHint(SLICE_HINT_TITLE)) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                } else if (it.hasHint(SLICE_HINT_TITLE)) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    title = it.text"
-        errorLine2="                               ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.AuthenticationAction.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    title = it.text"
-        errorLine2="                               ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/AuthenticationAction.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                when (slice.spec?.type) {"
-        errorLine2="                            ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                when (slice.spec?.type) {"
-        errorLine2="                                  ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                when (slice.spec?.type) {"
-        errorLine2="                            ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.CredentialEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                when (slice.spec?.type) {"
-        errorLine2="                                  ~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/CredentialEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(SLICE_SPEC_TYPE, REVISION_ID))"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(SLICE_SPEC_TYPE, REVISION_ID))"
-        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(SLICE_SPEC_TYPE, REVISION_ID))"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(SLICE_SPEC_TYPE, REVISION_ID))"
-        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            sliceBuilder.addAction("
-        errorLine2="                         ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            sliceBuilder.addAction("
-        errorLine2="                         ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Slice.Builder(sliceBuilder)"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                Slice.Builder(sliceBuilder)"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
-        errorLine2="                     ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))"
-        errorLine2="                     ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .build(), /*subType=*/null"
-        errorLine2="                     ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    .build(), /*subType=*/null"
-        errorLine2="                     ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return sliceBuilder.build()"
-        errorLine2="                                ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return sliceBuilder.build()"
-        errorLine2="                                ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            slice.items.forEach {"
-        errorLine2="                  ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            slice.items.forEach {"
-        errorLine2="                  ~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    pendingIntent = it.action"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.credentials.provider.RemoteEntry.Companion is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    pendingIntent = it.action"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/credentials/provider/RemoteEntry.kt"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="UsesNonDefaultVisibleForTesting"
diff --git a/credentials/credentials/samples/build.gradle b/credentials/credentials/samples/build.gradle
index b0b0178..fa9238c 100644
--- a/credentials/credentials/samples/build.gradle
+++ b/credentials/credentials/samples/build.gradle
@@ -30,6 +30,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.credentials.samples"
 
     defaultConfig {
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
index 81f3196..390b9db 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
@@ -116,7 +116,7 @@
         assertThat(displayInfo.getPreferDefaultProvider()).isEqualTo(expectedDefaultProvider);
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Test
     public void constructFromBundle_success() {
         String expectedUserId = "userId";
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
index ab9bb18..a4a07e29 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
@@ -101,7 +101,7 @@
         assertThat(displayInfo.preferDefaultProvider).isEqualTo(expectedDefaultProvider)
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Test
     fun constructFromBundle_success() {
         val expectedUserId = "userId"
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
index 1e8d7ad..a8579df 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestJavaTest.java
@@ -139,7 +139,7 @@
         assertThat(request.getPassword()).isEqualTo(passwordExpected);
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @SuppressWarnings("deprecation") // bundle.get(key)
     @Test
     public void getter_frameworkProperties() {
@@ -191,7 +191,7 @@
         ).isEqualTo(R.drawable.ic_password);
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Test
     public void frameworkConversion_success() {
         String idExpected = "id";
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
index 3f9e8dd..db25fc5 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePasswordRequestTest.kt
@@ -126,7 +126,7 @@
         assertThat(request.password).isEqualTo(passwordExpected)
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Suppress("DEPRECATION") // bundle.get(key)
     @Test
     fun getter_frameworkProperties() {
@@ -189,7 +189,7 @@
             .isEqualTo(R.drawable.ic_password)
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Test
     fun frameworkConversion_success() {
         val idExpected = "id"
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
index cb095bb..893d0f9 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
@@ -154,7 +154,7 @@
         assertThat(testJsonActual).isEqualTo(testJsonExpected);
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @SuppressWarnings("deprecation") // bundle.get(key)
     @Test
     public void getter_frameworkProperties_success() {
@@ -210,7 +210,7 @@
         ).isEqualTo(R.drawable.ic_passkey);
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Test
     public void frameworkConversion_success() {
         byte[] clientDataHashExpected = "hash".getBytes();
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
index 4715c85..cd3b53c 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
@@ -140,7 +140,7 @@
         assertThat(testJsonActual).isEqualTo(testJsonExpected)
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Suppress("DEPRECATION") // bundle.get(key)
     @Test
     fun getter_frameworkProperties_success() {
@@ -211,7 +211,7 @@
             .isEqualTo(R.drawable.ic_passkey)
     }
 
-    @SdkSuppress(minSdkVersion = 28)
+    @SdkSuppress(minSdkVersion = 34)
     @Test
     fun frameworkConversion_success() {
         val clientDataHashExpected = "hash".toByteArray()
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerViewHandlerJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerViewHandlerJavaTest.java
new file mode 100644
index 0000000..06f71a5
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerViewHandlerJavaTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.credentials.Credential;
+import android.os.OutcomeReceiver;
+import android.widget.EditText;
+
+import androidx.annotation.RequiresApi;
+import androidx.credentials.internal.FrameworkImplHelper;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SdkSuppress;
+
+import kotlin.Unit;
+
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@SdkSuppress(minSdkVersion = 35, codeName = "VanillaIceCream")
+public class CredentialManagerViewHandlerJavaTest {
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    private static final GetCredentialRequest GET_CRED_PASSWORD_REQ =
+            new GetCredentialRequest.Builder()
+                    .setCredentialOptions(Collections.singletonList(
+                            new GetPasswordOption())).build();
+    private static final android.credentials.GetCredentialRequest GET_CRED_PASSWORD_FRAMEWORK_REQ =
+            FrameworkImplHelper.convertGetRequestToFrameworkClass(GET_CRED_PASSWORD_REQ);
+
+    @Test
+    @RequiresApi(35)
+    public void setPendingCredentialRequest_frameworkAttrSetSuccessfully() {
+        EditText editText = new EditText(mContext);
+
+        PendingGetCredentialRequest pendingGetCredentialRequest = new PendingGetCredentialRequest(
+                GET_CRED_PASSWORD_REQ,
+                (response) -> Unit.INSTANCE);
+
+        CredentialManagerViewHandler.setPendingGetCredentialRequest(editText,
+                pendingGetCredentialRequest);
+
+        assertNotNull(editText.getPendingCredentialRequest());
+        TestUtilsKt.equals(editText.getPendingCredentialRequest(),
+                GET_CRED_PASSWORD_FRAMEWORK_REQ);
+        assertThat(editText.getPendingCredentialCallback()).isInstanceOf(
+                OutcomeReceiver.class
+        );
+    }
+
+    @Test
+    @RequiresApi(35)
+    public void setPendingCredentialRequest_callbackInvokedSuccessfully()
+            throws InterruptedException {
+        CountDownLatch latch1 = new CountDownLatch(1);
+        AtomicReference<GetCredentialResponse> getCredentialResponse = new AtomicReference<>();
+        EditText editText = new EditText(mContext);
+
+        PendingGetCredentialRequest pendingGetCredentialRequest = new PendingGetCredentialRequest(
+                GET_CRED_PASSWORD_REQ,
+                (response) -> {
+                    getCredentialResponse.set(response);
+                    latch1.countDown();
+                    return Unit.INSTANCE;
+                });
+
+        CredentialManagerViewHandler.setPendingGetCredentialRequest(editText,
+                pendingGetCredentialRequest);
+
+        assertNotNull(editText.getPendingCredentialRequest());
+        TestUtilsKt.equals(editText.getPendingCredentialRequest(), GET_CRED_PASSWORD_FRAMEWORK_REQ);
+        assertThat(editText.getPendingCredentialCallback()).isInstanceOf(
+                OutcomeReceiver.class
+        );
+
+        PasswordCredential passwordCredential = new PasswordCredential("id", "password");
+        android.credentials.GetCredentialResponse frameworkPasswordResponse =
+                new android.credentials.GetCredentialResponse(new Credential(
+                        passwordCredential.getType(), passwordCredential.getData()));
+        assertNotNull(editText.getPendingCredentialCallback());
+        editText.getPendingCredentialCallback().onResult(frameworkPasswordResponse);
+        latch1.await(50L, TimeUnit.MILLISECONDS);
+
+        assertThat(getCredentialResponse.get()).isNotNull();
+        GetCredentialResponse expectedGetCredentialResponse = FrameworkImplHelper
+                .convertGetResponseToJetpackClass(frameworkPasswordResponse);
+        TestUtilsKt.equals(expectedGetCredentialResponse, getCredentialResponse.get());
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerViewHandlerTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerViewHandlerTest.kt
new file mode 100644
index 0000000..d0e4b6b
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerViewHandlerTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import android.content.Context
+import android.credentials.Credential
+import android.os.OutcomeReceiver
+import android.widget.EditText
+import androidx.annotation.RequiresApi
+import androidx.credentials.internal.FrameworkImplHelper.Companion.convertGetRequestToFrameworkClass
+import androidx.credentials.internal.FrameworkImplHelper.Companion.convertGetResponseToJetpackClass
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicReference
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 35, codeName = "VanillaIceCream")
+class CredentialManagerViewHandlerTest {
+    private val mContext: Context = ApplicationProvider.getApplicationContext()
+
+    companion object {
+        private val GET_CRED_PASSWORD_REQ =
+            GetCredentialRequest.Builder().setCredentialOptions(listOf(GetPasswordOption())).build()
+        private val GET_CRED_PASSWORD_FRAMEWORK_REQ =
+            convertGetRequestToFrameworkClass(GET_CRED_PASSWORD_REQ)
+    }
+
+    @Test
+    @RequiresApi(35)
+    fun setPendingCredentialRequest_frameworkAttrSetSuccessfully() {
+        val editText = EditText(mContext)
+        val pendingGetCredentialRequest =
+            PendingGetCredentialRequest(GET_CRED_PASSWORD_REQ) { _: GetCredentialResponse? -> }
+
+        editText.pendingGetCredentialRequest = pendingGetCredentialRequest
+
+        equals(editText.pendingCredentialRequest!!, GET_CRED_PASSWORD_FRAMEWORK_REQ)
+        assertThat(editText.pendingCredentialCallback).isInstanceOf(OutcomeReceiver::class.java)
+    }
+
+    @Test
+    @RequiresApi(35)
+    @Throws(InterruptedException::class)
+    fun setPendingCredentialRequest_callbackInvokedSuccessfully() {
+        val latch1 = CountDownLatch(1)
+        val getCredentialResponse = AtomicReference<GetCredentialResponse>()
+        val editText = EditText(mContext)
+
+        editText.pendingGetCredentialRequest =
+            PendingGetCredentialRequest(GET_CRED_PASSWORD_REQ) { response ->
+                getCredentialResponse.set(response)
+                latch1.countDown()
+            }
+
+        equals(editText.pendingCredentialRequest!!, GET_CRED_PASSWORD_FRAMEWORK_REQ)
+        assertThat(editText.pendingCredentialCallback).isInstanceOf(OutcomeReceiver::class.java)
+
+        val passwordCredential = PasswordCredential("id", "password")
+        val frameworkPasswordResponse =
+            android.credentials.GetCredentialResponse(
+                Credential(passwordCredential.type, passwordCredential.data)
+            )
+
+        editText.pendingCredentialCallback!!.onResult(frameworkPasswordResponse)
+        latch1.await(50L, TimeUnit.MILLISECONDS)
+
+        assertThat(getCredentialResponse.get()).isNotNull()
+        val expectedGetCredentialResponse =
+            convertGetResponseToJetpackClass(frameworkPasswordResponse)
+        equals(expectedGetCredentialResponse, getCredentialResponse.get())
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PendingGetCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PendingGetCredentialRequestJavaTest.java
new file mode 100644
index 0000000..3d43bfd6
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PendingGetCredentialRequestJavaTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.annotation.RequiresApi;
+import androidx.test.filters.SdkSuppress;
+
+import kotlin.Unit;
+
+import org.junit.Test;
+
+import java.util.Collections;
+
+@SdkSuppress(minSdkVersion = 35, codeName = "VanillaIceCream")
+public class PendingGetCredentialRequestJavaTest {
+    @Test
+    @RequiresApi(35)
+    public void constructor_setAndGetRequestThroughViewTag() {
+        GetCredentialRequest request = new GetCredentialRequest.Builder()
+                .setCredentialOptions(Collections.singletonList(new GetPasswordOption()))
+                .build();
+        PendingGetCredentialRequest pendingGetCredentialRequest =
+                new PendingGetCredentialRequest(request,
+                        (response) -> Unit.INSTANCE);
+
+        assertThat(pendingGetCredentialRequest.getRequest())
+                .isSameInstanceAs(request);
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PendingGetCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PendingGetCredentialRequestTest.kt
new file mode 100644
index 0000000..1e3dad7
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PendingGetCredentialRequestTest.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import androidx.annotation.RequiresApi
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = 35, codeName = "VanillaIceCream")
+class PendingGetCredentialRequestTest {
+
+    @Test
+    @RequiresApi(35)
+    fun constructor_setAndGetRequestThroughViewTag() {
+        val request =
+            GetCredentialRequest.Builder().setCredentialOptions(listOf(GetPasswordOption())).build()
+
+        val pendingGetCredentialRequest =
+            PendingGetCredentialRequest(request) { _: GetCredentialResponse? -> }
+
+        assertThat(pendingGetCredentialRequest.request).isSameInstanceAs(request)
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
index c486318..37479ec 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/TestUtils.kt
@@ -16,10 +16,16 @@
 
 package androidx.credentials
 
+import android.content.pm.SigningInfo
 import android.graphics.drawable.Icon
 import android.os.Build
 import android.os.Bundle
+import androidx.annotation.RequiresApi
 import androidx.credentials.provider.CallingAppInfo
+import androidx.credentials.provider.ProviderCreateCredentialRequest
+import androidx.credentials.provider.ProviderGetCredentialRequest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
 
 /** True if the two Bundles contain the same elements, and false otherwise. */
 @Suppress("DEPRECATION")
@@ -85,3 +91,115 @@
 fun equals(a: CallingAppInfo, b: CallingAppInfo): Boolean {
     return a.packageName == b.packageName && a.origin == b.origin
 }
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+fun equals(
+    createCredentialRequest: android.service.credentials.CreateCredentialRequest,
+    request: ProviderCreateCredentialRequest
+) {
+    assertThat(createCredentialRequest.type).isEqualTo(request.callingRequest.type)
+    equals(createCredentialRequest.data, request.callingRequest.credentialData)
+    Assert.assertEquals(
+        createCredentialRequest.callingAppInfo.packageName,
+        request.callingAppInfo.packageName
+    )
+    Assert.assertEquals(
+        createCredentialRequest.callingAppInfo.origin,
+        request.callingAppInfo.origin
+    )
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+fun equals(
+    getCredentialRequest: android.service.credentials.GetCredentialRequest,
+    request: ProviderGetCredentialRequest
+) {
+    Assert.assertEquals(
+        getCredentialRequest.callingAppInfo.packageName,
+        request.callingAppInfo.packageName
+    )
+    Assert.assertEquals(getCredentialRequest.callingAppInfo.origin, request.callingAppInfo.origin)
+    equals(getCredentialRequest.credentialOptions, request.credentialOptions)
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+private fun equals(
+    credentialOptions: List<android.credentials.CredentialOption>,
+    credentialOptions1: List<CredentialOption>
+) {
+    assertThat(credentialOptions.size).isEqualTo(credentialOptions1.size)
+    for (i in credentialOptions.indices) {
+        equals(credentialOptions[i], credentialOptions1[i])
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+fun equals(
+    frameworkRequest1: android.credentials.GetCredentialRequest,
+    frameworkRequest2: android.credentials.GetCredentialRequest
+) {
+    equals(frameworkRequest1.data, frameworkRequest2.data)
+    credentialOptionsEqual(frameworkRequest1.credentialOptions, frameworkRequest2.credentialOptions)
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+private fun credentialOptionsEqual(
+    credentialOptions1: List<android.credentials.CredentialOption>,
+    credentialOptions2: List<android.credentials.CredentialOption>
+) {
+    assertThat(credentialOptions1.size).isEqualTo(credentialOptions2.size)
+    for (i in credentialOptions1.indices) {
+        equals(credentialOptions1[i], credentialOptions2[i])
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+fun equals(
+    credentialOption: android.credentials.CredentialOption,
+    credentialOption1: CredentialOption
+) {
+    assertThat(credentialOption.type).isEqualTo(credentialOption1.type)
+    assertThat(credentialOption.isSystemProviderRequired)
+        .isEqualTo(credentialOption1.isSystemProviderRequired)
+    equals(credentialOption.credentialRetrievalData, credentialOption1.requestData)
+    equals(credentialOption.candidateQueryData, credentialOption1.candidateQueryData)
+    assertThat(credentialOption.allowedProviders).isEqualTo(credentialOption1.allowedProviders)
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+fun setUpCreatePasswordRequest(): android.service.credentials.CreateCredentialRequest {
+    val passwordReq: CreateCredentialRequest =
+        CreatePasswordRequest("test-user-id", "test-password")
+    val request =
+        android.service.credentials.CreateCredentialRequest(
+            android.service.credentials.CallingAppInfo("calling_package", SigningInfo()),
+            PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+            passwordReq.credentialData
+        )
+    return request
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+fun equals(
+    credentialOption1: android.credentials.CredentialOption,
+    credentialOption2: android.credentials.CredentialOption
+) {
+    equals(credentialOption1.candidateQueryData, credentialOption2.candidateQueryData)
+    equals(credentialOption1.credentialRetrievalData, credentialOption2.credentialRetrievalData)
+    assertThat(credentialOption1.type).isEqualTo(credentialOption2.type)
+    assertThat(credentialOption1.allowedProviders).isEqualTo(credentialOption2.allowedProviders)
+    assertThat(credentialOption1.isSystemProviderRequired)
+        .isEqualTo(credentialOption2.isSystemProviderRequired)
+}
+
+fun equals(
+    getCredentialResponse1: GetCredentialResponse,
+    getCredentialResponse2: GetCredentialResponse
+) {
+    equals(getCredentialResponse1.credential, getCredentialResponse2.credential)
+}
+
+fun equals(credential1: Credential, credential2: Credential) {
+    assertThat(credential1.type).isEqualTo(credential2.type)
+    equals(credential1.data, credential2.data)
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataJavaTest.java
new file mode 100644
index 0000000..1e5593c
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataJavaTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider;
+
+import static androidx.credentials.provider.BiometricPromptData.BUNDLE_HINT_ALLOWED_AUTHENTICATORS;
+import static androidx.credentials.provider.BiometricPromptData.BUNDLE_HINT_CRYPTO_OP_ID;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.crypto.NullCipher;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 35)
+public class BiometricPromptDataJavaTest {
+
+    private static final BiometricPrompt.CryptoObject TEST_CRYPTO_OBJECT = new
+            BiometricPrompt.CryptoObject(new NullCipher());
+
+    private static final long DEFAULT_BUNDLE_LONG_FOR_CRYPTO_ID = 0L;
+
+    private static final  int TEST_ALLOWED_AUTHENTICATOR = BiometricManager.Authenticators
+            .BIOMETRIC_STRONG;
+
+    @Test
+    public void construct_cryptoObjectStrongAllowedAuthenticator_success() {
+        BiometricPromptData biometricPromptData = new BiometricPromptData(
+                /*cryptoObject=*/TEST_CRYPTO_OBJECT,
+                /*allowedAuthenticators=*/TEST_ALLOWED_AUTHENTICATOR
+        );
+
+        assertThat(biometricPromptData.getAllowedAuthenticators())
+                .isEqualTo(TEST_ALLOWED_AUTHENTICATOR);
+        assertThat(biometricPromptData.getCryptoObject()).isEqualTo(TEST_CRYPTO_OBJECT);
+    }
+
+    @Test
+    public void construct_cryptoObjectNullAuthenticatorNotProvided_successWithWeakAuthenticator() {
+        int expectedAuthenticator = BiometricManager.Authenticators.BIOMETRIC_WEAK;
+
+        BiometricPromptData biometricPromptData = new BiometricPromptData.Builder().build();
+
+        assertThat(biometricPromptData.getCryptoObject()).isNull();
+        assertThat(biometricPromptData.getAllowedAuthenticators()).isEqualTo(expectedAuthenticator);
+    }
+
+    @Test
+    public void construct_cryptoObjectExistsAuthenticatorNotProvided__defaultThrowsIAE() {
+        assertThrows("Expected cryptoObject without strong authenticator to throw "
+                        + "IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> new BiometricPromptData.Builder()
+                        .setCryptoObject(TEST_CRYPTO_OBJECT).build()
+        );
+    }
+
+    @Test
+    public void construct_cryptoObjectNullAuthenticatorNonNull_successPassedInAuthenticator() {
+        BiometricPromptData biometricPromptData = new BiometricPromptData(
+                /*cryptoObject=*/null,
+                /*allowedAuthenticator=*/TEST_ALLOWED_AUTHENTICATOR
+        );
+
+        assertThat(biometricPromptData.getCryptoObject()).isNull();
+        assertThat(biometricPromptData.getAllowedAuthenticators()).isEqualTo(
+                TEST_ALLOWED_AUTHENTICATOR);
+    }
+
+    @Test
+    public void construct_authenticatorNotAccepted_throwsIAE() {
+        assertThrows("Expected invalid allowed authenticator to throw "
+                        + "IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> new BiometricPromptData(
+                        /*cryptoObject=*/null,
+                        /*allowedAuthenticator=*/Integer.MIN_VALUE
+                )
+        );
+    }
+
+    @Test
+    public void build_requiredParamsOnly_success() {
+        int expectedAllowedAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK;
+
+        BiometricPromptData actualBiometricPromptData = new BiometricPromptData.Builder().build();
+
+        assertThat(actualBiometricPromptData.getAllowedAuthenticators()).isEqualTo(
+                expectedAllowedAuthenticators);
+        assertThat(actualBiometricPromptData.getCryptoObject()).isNull();
+    }
+
+    @Test
+    public void build_setCryptoObjectWithStrongAuthenticator_success() {
+        BiometricPromptData actualBiometricPromptData = new BiometricPromptData.Builder()
+                .setCryptoObject(TEST_CRYPTO_OBJECT)
+                .setAllowedAuthenticators(TEST_ALLOWED_AUTHENTICATOR).build();
+
+        assertThat(actualBiometricPromptData.getCryptoObject()).isEqualTo(TEST_CRYPTO_OBJECT);
+        assertThat(actualBiometricPromptData.getAllowedAuthenticators())
+                .isEqualTo(TEST_ALLOWED_AUTHENTICATOR);
+    }
+
+    @Test
+    public void build_setAllowedAuthenticator_success() {
+        BiometricPromptData actualBiometricPromptData = new BiometricPromptData.Builder()
+                .setAllowedAuthenticators(TEST_ALLOWED_AUTHENTICATOR).build();
+
+        assertThat(actualBiometricPromptData.getAllowedAuthenticators())
+                .isEqualTo(TEST_ALLOWED_AUTHENTICATOR);
+    }
+
+    @SdkSuppress(maxSdkVersion = 34)
+    @Test
+    public void fromBundle_validAllowedAuthenticator_success() {
+        Bundle inputBundle = new Bundle();
+        inputBundle.putInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS, TEST_ALLOWED_AUTHENTICATOR);
+
+        BiometricPromptData actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle);
+
+        assertThat(actualBiometricPromptData).isNotNull();
+        assertThat(actualBiometricPromptData.getAllowedAuthenticators()).isEqualTo(
+                TEST_ALLOWED_AUTHENTICATOR);
+        assertThat(actualBiometricPromptData.getCryptoObject()).isNull();
+    }
+
+    @SdkSuppress(minSdkVersion = 35)
+    @Test
+    public void fromBundle_validAllowedAuthenticatorAboveApi35_success() {
+        int expectedOpId = Integer.MIN_VALUE;
+        Bundle inputBundle = new Bundle();
+        inputBundle.putInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS, TEST_ALLOWED_AUTHENTICATOR);
+        inputBundle.putInt(BUNDLE_HINT_CRYPTO_OP_ID, expectedOpId);
+
+        BiometricPromptData actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle);
+
+        assertThat(actualBiometricPromptData).isNotNull();
+        assertThat(actualBiometricPromptData.getAllowedAuthenticators()).isEqualTo(
+                TEST_ALLOWED_AUTHENTICATOR);
+        assertThat(actualBiometricPromptData.getCryptoObject()).isNotNull();
+        assertThat(actualBiometricPromptData.getCryptoObject().hashCode())
+                .isEqualTo(expectedOpId);
+    }
+
+    @Test
+    public void fromBundle_unrecognizedAllowedAuthenticator_success() {
+        int expectedOpId = Integer.MIN_VALUE;
+        Bundle inputBundle = new Bundle();
+        int unrecognizedAuthenticator = Integer.MAX_VALUE;
+        inputBundle.putInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS, unrecognizedAuthenticator);
+        inputBundle.putInt(BUNDLE_HINT_CRYPTO_OP_ID, expectedOpId);
+
+        BiometricPromptData actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle);
+
+        assertThat(actualBiometricPromptData).isNotNull();
+        assertThat(actualBiometricPromptData.getAllowedAuthenticators())
+                .isEqualTo(unrecognizedAuthenticator);
+
+    }
+
+    @Test
+    public void fromBundle_invalidBundleKey_nullBiometricPromptData() {
+        int expectedOpId = Integer.MIN_VALUE;
+        Bundle inputBundle = new Bundle();
+        int unrecognizedAuthenticator = Integer.MAX_VALUE;
+        inputBundle.putInt("invalidKey", unrecognizedAuthenticator);
+        inputBundle.putInt(BUNDLE_HINT_CRYPTO_OP_ID, expectedOpId);
+
+        BiometricPromptData actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle);
+
+        assertThat(actualBiometricPromptData).isNull();
+    }
+
+    @SdkSuppress(maxSdkVersion = 34)
+    @Test
+    public void toBundle_success() {
+        BiometricPromptData testBiometricPromptData = new BiometricPromptData(/*cryptoObject=*/null,
+                TEST_ALLOWED_AUTHENTICATOR);
+
+        Bundle actualBundle = BiometricPromptData.toBundle(
+                testBiometricPromptData);
+
+        assertThat(actualBundle).isNotNull();
+        assertThat(actualBundle.getInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS)).isEqualTo(
+                TEST_ALLOWED_AUTHENTICATOR
+        );
+        assertThat(actualBundle.getInt(BUNDLE_HINT_CRYPTO_OP_ID)).isEqualTo(
+                DEFAULT_BUNDLE_LONG_FOR_CRYPTO_ID);
+    }
+
+    @SdkSuppress(minSdkVersion = 35)
+    @Test
+    public void toBundle_api35AndAboveWithOpId_success() {
+        BiometricPromptData testBiometricPromptData = new BiometricPromptData(TEST_CRYPTO_OBJECT,
+                TEST_ALLOWED_AUTHENTICATOR);
+        long expectedOpId = TEST_CRYPTO_OBJECT.hashCode();
+
+        Bundle actualBundle = BiometricPromptData.toBundle(
+                testBiometricPromptData);
+
+        assertThat(actualBundle).isNotNull();
+        assertThat(actualBundle.getInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS)).isEqualTo(
+                TEST_ALLOWED_AUTHENTICATOR
+        );
+        assertThat(actualBundle.getInt(BUNDLE_HINT_CRYPTO_OP_ID)).isEqualTo(expectedOpId);
+    }
+
+    @Test
+    public void build_setInvalidAllowedAuthenticator_throwsIAE() {
+        assertThrows("Expected invalid allowed authenticator to throw "
+                        + "IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> new BiometricPromptData.Builder().setAllowedAuthenticators(-10000).build()
+        );
+    }
+
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataTest.kt
new file mode 100644
index 0000000..399a783
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/BiometricPromptDataTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import android.os.Bundle
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricPrompt
+import androidx.credentials.provider.BiometricPromptData.Companion.BUNDLE_HINT_ALLOWED_AUTHENTICATORS
+import androidx.credentials.provider.BiometricPromptData.Companion.BUNDLE_HINT_CRYPTO_OP_ID
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import javax.crypto.NullCipher
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 35)
+@SmallTest
+class BiometricPromptDataTest {
+    @Test
+    fun construct_cryptoObjectStrongAllowedAuthenticator_success() {
+        val biometricPromptData =
+            BiometricPromptData(TEST_CRYPTO_OBJECT, TEST_ALLOWED_AUTHENTICATOR)
+
+        assertThat(biometricPromptData.allowedAuthenticators).isEqualTo(TEST_ALLOWED_AUTHENTICATOR)
+        assertThat(biometricPromptData.cryptoObject).isEqualTo(TEST_CRYPTO_OBJECT)
+    }
+
+    @Test
+    fun construct_cryptoObjectNullAuthenticatorNotProvided_successWithWeakAuthenticator() {
+        val expectedAuthenticator = BiometricManager.Authenticators.BIOMETRIC_WEAK
+
+        val biometricPromptData = BiometricPromptData()
+
+        assertThat(biometricPromptData.cryptoObject).isNull()
+        assertThat(biometricPromptData.allowedAuthenticators).isEqualTo(expectedAuthenticator)
+    }
+
+    @Test
+    fun construct_cryptoObjectExistsAuthenticatorNotProvided_defaultWeakAuthenticatorThrowsIAE() {
+        assertThrows(
+            "Expected invalid allowed authenticator with cryptoObject to throw " +
+                "IllegalArgumentException",
+            java.lang.IllegalArgumentException::class.java
+        ) {
+            BiometricPromptData(TEST_CRYPTO_OBJECT)
+        }
+    }
+
+    @Test
+    fun construct_cryptoObjectNullAuthenticatorNonNull_successPassedInAuthenticator() {
+        val expectedAuthenticator = BiometricManager.Authenticators.BIOMETRIC_STRONG
+
+        val biometricPromptData = BiometricPromptData(cryptoObject = null, expectedAuthenticator)
+
+        assertThat(biometricPromptData.cryptoObject).isNull()
+        assertThat(biometricPromptData.allowedAuthenticators).isEqualTo(expectedAuthenticator)
+    }
+
+    @Test
+    fun construct_authenticatorNotAccepted_throwsIAE() {
+        assertThrows(
+            "Expected invalid allowed authenticator IllegalArgumentException",
+            java.lang.IllegalArgumentException::class.java
+        ) {
+            BiometricPromptData(null, allowedAuthenticators = Int.MIN_VALUE)
+        }
+    }
+
+    @Test
+    fun build_requiredParamsOnly_success() {
+        val expectedAllowedAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
+
+        val actualBiometricPromptData = BiometricPromptData.Builder().build()
+
+        assertThat(actualBiometricPromptData.allowedAuthenticators)
+            .isEqualTo(expectedAllowedAuthenticators)
+        assertThat(actualBiometricPromptData.cryptoObject).isNull()
+    }
+
+    @Test
+    fun build_setCryptoObjectWithStrongAuthenticatorOnly_success() {
+        val actualBiometricPromptData =
+            BiometricPromptData.Builder()
+                .setCryptoObject(TEST_CRYPTO_OBJECT)
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build()
+
+        assertThat(actualBiometricPromptData.cryptoObject).isEqualTo(TEST_CRYPTO_OBJECT)
+        assertThat(actualBiometricPromptData.allowedAuthenticators)
+            .isEqualTo(TEST_ALLOWED_AUTHENTICATOR)
+    }
+
+    @Test
+    fun build_setAllowedAuthenticator_success() {
+        val actualBiometricPromptData =
+            BiometricPromptData.Builder()
+                .setAllowedAuthenticators(TEST_ALLOWED_AUTHENTICATOR)
+                .build()
+
+        assertThat(actualBiometricPromptData.allowedAuthenticators)
+            .isEqualTo(TEST_ALLOWED_AUTHENTICATOR)
+    }
+
+    @Test
+    fun build_setInvalidAllowedAuthenticator_success() {
+        assertThrows(
+            "Expected builder invalid allowed authenticator to throw " + "IllegalArgumentException",
+            java.lang.IllegalArgumentException::class.java
+        ) {
+            BiometricPromptData.Builder().setAllowedAuthenticators(-10000).build()
+        }
+    }
+
+    @SdkSuppress(maxSdkVersion = 34)
+    @Test
+    fun fromBundle_validAllowedAuthenticator_success() {
+        val inputBundle = Bundle()
+        inputBundle.putInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS, TEST_ALLOWED_AUTHENTICATOR)
+
+        val actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle)
+
+        assertThat(actualBiometricPromptData).isNotNull()
+        assertThat(actualBiometricPromptData!!.allowedAuthenticators)
+            .isEqualTo(TEST_ALLOWED_AUTHENTICATOR)
+        assertThat(actualBiometricPromptData.cryptoObject).isNull()
+    }
+
+    @SdkSuppress(minSdkVersion = 35)
+    @Test
+    fun fromBundle_validAllowedAuthenticatorAboveApi35_success() {
+        val expectedOpId = Integer.MIN_VALUE
+        val inputBundle = Bundle()
+        inputBundle.putInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS, TEST_ALLOWED_AUTHENTICATOR)
+        inputBundle.putInt(BUNDLE_HINT_CRYPTO_OP_ID, expectedOpId)
+
+        val actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle)
+
+        assertThat(actualBiometricPromptData).isNotNull()
+        assertThat(actualBiometricPromptData!!.allowedAuthenticators)
+            .isEqualTo(TEST_ALLOWED_AUTHENTICATOR)
+        assertThat(actualBiometricPromptData.cryptoObject).isNotNull()
+        assertThat(actualBiometricPromptData.cryptoObject!!.hashCode()).isEqualTo(expectedOpId)
+    }
+
+    @Test
+    fun fromBundle_unrecognizedAllowedAuthenticator_success() {
+        val inputBundle = Bundle()
+        val unrecognizedAuthenticator = Integer.MAX_VALUE
+        inputBundle.putInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS, unrecognizedAuthenticator)
+
+        val actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle)
+
+        assertThat(actualBiometricPromptData).isNotNull()
+        assertThat(actualBiometricPromptData!!.allowedAuthenticators)
+            .isEqualTo(unrecognizedAuthenticator)
+    }
+
+    @Test
+    fun fromBundle_invalidBundleKey_nullBiometricPromptData() {
+        val expectedOpId = Integer.MIN_VALUE
+        val inputBundle = Bundle()
+        val unrecognizedAuthenticator = Integer.MAX_VALUE
+        inputBundle.putInt("invalid key", unrecognizedAuthenticator)
+        inputBundle.putInt(BUNDLE_HINT_CRYPTO_OP_ID, expectedOpId)
+
+        val actualBiometricPromptData = BiometricPromptData.fromBundle(inputBundle)
+
+        assertThat(actualBiometricPromptData).isNull()
+    }
+
+    @SdkSuppress(maxSdkVersion = 34)
+    @Test
+    fun toBundle_success() {
+        val testBiometricPromptData =
+            BiometricPromptData(TEST_CRYPTO_OBJECT, TEST_ALLOWED_AUTHENTICATOR)
+
+        val actualBundle = BiometricPromptData.toBundle(testBiometricPromptData)
+
+        assertThat(actualBundle).isNotNull()
+        assertThat(actualBundle.getInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS))
+            .isEqualTo(TEST_ALLOWED_AUTHENTICATOR)
+        assertThat(actualBundle.getInt(BUNDLE_HINT_CRYPTO_OP_ID))
+            .isEqualTo(DEFAULT_BUNDLE_LONG_FOR_CRYPTO_ID)
+    }
+
+    @SdkSuppress(minSdkVersion = 35)
+    @Test
+    fun toBundle_api35AndAboveWithOpId_success() {
+        val testBiometricPromptData =
+            BiometricPromptData(TEST_CRYPTO_OBJECT, TEST_ALLOWED_AUTHENTICATOR)
+        val expectedOpId = TEST_CRYPTO_OBJECT.hashCode()
+
+        val actualBundle = BiometricPromptData.toBundle(testBiometricPromptData)
+
+        assertThat(actualBundle).isNotNull()
+        assertThat(actualBundle.getInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS))
+            .isEqualTo(TEST_ALLOWED_AUTHENTICATOR)
+        assertThat(actualBundle.getInt(BUNDLE_HINT_CRYPTO_OP_ID)).isEqualTo(expectedOpId)
+    }
+
+    private companion object {
+        private val TEST_CRYPTO_OBJECT = BiometricPrompt.CryptoObject(NullCipher())
+
+        private const val DEFAULT_BUNDLE_LONG_FOR_CRYPTO_ID = 0L
+
+        private const val TEST_ALLOWED_AUTHENTICATOR =
+            BiometricManager.Authenticators.BIOMETRIC_STRONG
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt
index baaf9ee..1b9d081 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertThrows
 import org.junit.Assert.assertTrue
 import org.junit.BeforeClass
@@ -192,7 +193,8 @@
                         packageName,
                         PackageManager.GET_SIGNING_CERTIFICATES
                     )
-                signingInfo = packageInfo.signingInfo
+                assertNotNull(packageInfo.signingInfo)
+                signingInfo = packageInfo.signingInfo!!
             } catch (_: PackageManager.NameNotFoundException) {}
         }
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerJavaTest.java
index 5b332ac..3f3e018 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerJavaTest.java
@@ -18,13 +18,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 import android.content.Intent;
+import android.content.pm.SigningInfo;
+import android.credentials.CredentialOption;
 import android.os.Build;
+import android.os.Bundle;
+import android.service.credentials.CallingAppInfo;
 
 import androidx.annotation.RequiresApi;
 import androidx.credentials.CreatePasswordResponse;
 import androidx.credentials.GetCredentialResponse;
 import androidx.credentials.PasswordCredential;
+import androidx.credentials.TestUtilsKt;
 import androidx.credentials.exceptions.CreateCredentialInterruptedException;
 import androidx.credentials.exceptions.GetCredentialInterruptedException;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -34,6 +42,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Collections;
+
 @RequiresApi(34)
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -41,6 +52,244 @@
 public class PendingIntentHandlerJavaTest {
     private static final Intent BLANK_INTENT = new Intent();
 
+    private static final android.credentials.CredentialOption
+            GET_CREDENTIAL_OPTION = new CredentialOption.Builder(
+            "type", new Bundle(), new Bundle())
+            .build();
+
+    private static final android.service.credentials.GetCredentialRequest
+            GET_CREDENTIAL_REQUEST = new android.service.credentials.GetCredentialRequest(
+                    new CallingAppInfo(
+                            "package_name", new SigningInfo()), new ArrayList<>(
+                                    Collections.singleton(GET_CREDENTIAL_OPTION)));
+
+    private static final int BIOMETRIC_AUTHENTICATOR_TYPE = 1;
+
+    private static final int BIOMETRIC_AUTHENTICATOR_ERROR_CODE = 5;
+
+    private static final String BIOMETRIC_AUTHENTICATOR_ERROR_MSG = "error";
+
+    @Test
+    public void test_retrieveProviderCreateCredReqWithSuccessBpAuthJetpack_retrieveJetpackResult() {
+        for (int jetpackResult :
+                AuthenticationResult.Companion
+                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().values()) {
+            BiometricPromptResult biometricPromptResult =
+                    new BiometricPromptResult(new AuthenticationResult(jetpackResult));
+            android.service.credentials.CreateCredentialRequest request =
+                    TestUtilsKt.setUpCreatePasswordRequest();
+            Intent intent = prepareIntentWithCreateRequest(
+                    request,
+                    biometricPromptResult);
+
+            ProviderCreateCredentialRequest retrievedRequest =
+                    PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent);
+
+            assertNotNull(request);
+            TestUtilsKt.equals(request, retrievedRequest);
+            assertNotNull(biometricPromptResult.getAuthenticationResult());
+            assertEquals(retrievedRequest.getBiometricPromptResult().getAuthenticationResult()
+                    .getAuthenticationType(), jetpackResult);
+        }
+    }
+
+    @Test
+    public void test_retrieveProviderGetCredReqWithSuccessBpAuthJetpack_retrieveJetpackResult() {
+        for (int jetpackResult :
+                AuthenticationResult.Companion
+                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().values()) {
+            BiometricPromptResult biometricPromptResult =
+                    new BiometricPromptResult(new AuthenticationResult(jetpackResult));
+            Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
+                    biometricPromptResult);
+
+            ProviderGetCredentialRequest retrievedRequest =
+                    PendingIntentHandler.retrieveProviderGetCredentialRequest(intent);
+
+            assertNotNull(retrievedRequest);
+            TestUtilsKt.equals(GET_CREDENTIAL_REQUEST, retrievedRequest);
+            assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+            assertEquals(retrievedRequest.getBiometricPromptResult().getAuthenticationResult()
+                    .getAuthenticationType(), jetpackResult);
+        }
+    }
+
+    @Test
+    public void test_retrieveProviderCreateCredReqWithSuccessBpAuthFramework_resultConverted() {
+        for (int frameworkResult :
+                AuthenticationResult.Companion
+                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().keySet()) {
+            BiometricPromptResult biometricPromptResult =
+                    new BiometricPromptResult(
+                            AuthenticationResult.Companion.createFrom$credentials_debug(
+                                    frameworkResult,
+                                    /*isFrameworkBiometricPrompt=*/true
+                            ));
+            android.service.credentials.CreateCredentialRequest request =
+                    TestUtilsKt.setUpCreatePasswordRequest();
+            int expectedResult =
+                    AuthenticationResult.Companion
+                            .getBiometricFrameworkToJetpackResultMap$credentials_debug()
+                            .get(frameworkResult);
+            Intent intent = prepareIntentWithCreateRequest(
+                    request,
+                    biometricPromptResult);
+
+            ProviderCreateCredentialRequest retrievedRequest =
+                    PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent);
+
+            assertNotNull(request);
+            TestUtilsKt.equals(request, retrievedRequest);
+            assertNotNull(biometricPromptResult.getAuthenticationResult());
+            assertEquals(retrievedRequest.getBiometricPromptResult().getAuthenticationResult()
+                    .getAuthenticationType(), expectedResult);
+        }
+    }
+
+    @Test
+    public void test_retrieveProviderGetCredReqWithSuccessBpAuthFramework_resultConverted() {
+        for (int frameworkResult :
+                AuthenticationResult.Companion
+                        .getBiometricFrameworkToJetpackResultMap$credentials_debug().keySet()) {
+            BiometricPromptResult biometricPromptResult =
+                    new BiometricPromptResult(
+                            AuthenticationResult.Companion.createFrom$credentials_debug(
+                                    frameworkResult,
+                                    /*isFrameworkBiometricPrompt=*/true
+                            ));
+            int expectedResult =
+                    AuthenticationResult.Companion
+                            .getBiometricFrameworkToJetpackResultMap$credentials_debug()
+                            .get(frameworkResult);
+            Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
+                    biometricPromptResult);
+
+            ProviderGetCredentialRequest retrievedRequest =
+                    PendingIntentHandler.retrieveProviderGetCredentialRequest(intent);
+
+            assertNotNull(retrievedRequest);
+            TestUtilsKt.equals(GET_CREDENTIAL_REQUEST, retrievedRequest);
+            assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+            assertEquals(retrievedRequest.getBiometricPromptResult().getAuthenticationResult()
+                    .getAuthenticationType(), expectedResult);
+        }
+    }
+
+
+    @Test
+    public void test_retrieveProviderCreateCredReqWithFailureBpAuthJetpack_retrieveJetpackError() {
+        for (int jetpackError :
+                AuthenticationError.Companion
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().values()) {
+            BiometricPromptResult biometricPromptResult =
+                    new BiometricPromptResult(
+                            new AuthenticationError(
+                                    jetpackError,
+                                    BIOMETRIC_AUTHENTICATOR_ERROR_MSG));
+            android.service.credentials.CreateCredentialRequest request =
+                    TestUtilsKt.setUpCreatePasswordRequest();
+            Intent intent = prepareIntentWithCreateRequest(
+                    request, biometricPromptResult);
+
+            ProviderCreateCredentialRequest retrievedRequest = PendingIntentHandler
+                    .retrieveProviderCreateCredentialRequest(intent);
+
+            assertNotNull(retrievedRequest);
+            TestUtilsKt.equals(request, retrievedRequest);
+            assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+            assertNotNull(retrievedRequest.getBiometricPromptResult().getAuthenticationError());
+            assertEquals(retrievedRequest.getBiometricPromptResult().getAuthenticationError()
+                    .getErrorCode(), jetpackError);
+        }
+    }
+
+    @Test
+    public void test_retrieveProviderGetCredReqWithFailureBpAuthJetpack_retrieveJetpackError() {
+        for (int jetpackError :
+                AuthenticationError.Companion
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().values()) {
+            BiometricPromptResult biometricPromptResult = new BiometricPromptResult(
+                    new AuthenticationError(
+                            jetpackError,
+                            BIOMETRIC_AUTHENTICATOR_ERROR_MSG));
+            Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
+                    biometricPromptResult);
+
+            ProviderGetCredentialRequest retrievedRequest = PendingIntentHandler
+                    .retrieveProviderGetCredentialRequest(intent);
+
+            assertNotNull(retrievedRequest);
+            TestUtilsKt.equals(GET_CREDENTIAL_REQUEST, retrievedRequest);
+            assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+            assertNotNull(retrievedRequest.getBiometricPromptResult().getAuthenticationError());
+            assertEquals(
+                    retrievedRequest.getBiometricPromptResult().getAuthenticationError()
+                            .getErrorCode(), jetpackError);
+        }
+    }
+
+    @Test
+    public void test_retrieveProviderCreateCredReqWithFailureBpAuthFramework_errorConverted() {
+        for (int frameworkError :
+                AuthenticationError.Companion
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().keySet()) {
+            BiometricPromptResult biometricPromptResult =
+                    new BiometricPromptResult(
+                            AuthenticationError.Companion.createFrom$credentials_debug(
+                                    frameworkError, BIOMETRIC_AUTHENTICATOR_ERROR_MSG,
+                                    /*isFrameworkBiometricPrompt=*/true
+                            ));
+            android.service.credentials.CreateCredentialRequest request =
+                    TestUtilsKt.setUpCreatePasswordRequest();
+            int expectedErrorCode =
+                    AuthenticationError.Companion
+                            .getBiometricFrameworkToJetpackErrorMap$credentials_debug()
+                            .get(frameworkError);
+            Intent intent = prepareIntentWithCreateRequest(
+                    request, biometricPromptResult);
+
+            ProviderCreateCredentialRequest retrievedRequest = PendingIntentHandler
+                    .retrieveProviderCreateCredentialRequest(intent);
+
+            assertNotNull(retrievedRequest);
+            TestUtilsKt.equals(request, retrievedRequest);
+            assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+            assertNotNull(retrievedRequest.getBiometricPromptResult().getAuthenticationError());
+            assertEquals(retrievedRequest.getBiometricPromptResult().getAuthenticationError()
+                    .getErrorCode(), expectedErrorCode);
+        }
+    }
+
+    @Test
+    public void test_retrieveProviderGetCredReqWithFailureBpAuthFramework_correctlyConvertedErr() {
+        for (int frameworkError :
+                AuthenticationError.Companion
+                        .getBiometricFrameworkToJetpackErrorMap$credentials_debug().keySet()) {
+            BiometricPromptResult biometricPromptResult = new BiometricPromptResult(
+                    AuthenticationError.Companion.createFrom$credentials_debug(
+                            frameworkError, BIOMETRIC_AUTHENTICATOR_ERROR_MSG,
+                            /*isFrameworkBiometricPrompt=*/true
+                    ));
+            Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
+                    biometricPromptResult);
+            int expectedErrorCode =
+                    AuthenticationError.Companion
+                            .getBiometricFrameworkToJetpackErrorMap$credentials_debug()
+                            .get(frameworkError);
+
+            ProviderGetCredentialRequest retrievedRequest = PendingIntentHandler
+                    .retrieveProviderGetCredentialRequest(intent);
+
+            assertNotNull(retrievedRequest);
+            TestUtilsKt.equals(GET_CREDENTIAL_REQUEST, retrievedRequest);
+            assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+            assertNotNull(retrievedRequest.getBiometricPromptResult().getAuthenticationError());
+            assertEquals(
+                    retrievedRequest.getBiometricPromptResult().getAuthenticationError()
+                            .getErrorCode(), expectedErrorCode);
+        }
+    }
+
     @Test
     public void test_setGetCreateCredentialException() {
         if (Build.VERSION.SDK_INT >= 34) {
@@ -157,6 +406,118 @@
     }
 
     @Test
+    public void test_retrieveProviderCreateCredReqWithSuccessfulBpAuth() {
+        BiometricPromptResult biometricPromptResult = new BiometricPromptResult(
+                new AuthenticationResult(BIOMETRIC_AUTHENTICATOR_TYPE));
+
+        android.service.credentials.CreateCredentialRequest request =
+                TestUtilsKt.setUpCreatePasswordRequest();
+
+        Intent intent = prepareIntentWithCreateRequest(request,
+                biometricPromptResult);
+
+        ProviderCreateCredentialRequest retrievedRequest = PendingIntentHandler
+                .retrieveProviderCreateCredentialRequest(intent);
+
+        assertNotNull(retrievedRequest);
+        TestUtilsKt.equals(request, retrievedRequest);
+        assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+    }
+
+    @Test
+    public void test_retrieveProviderCreateCredReqWithFailureBpAuth() {
+        BiometricPromptResult biometricPromptResult =
+                new BiometricPromptResult(
+                        new AuthenticationError(
+                                BIOMETRIC_AUTHENTICATOR_ERROR_CODE,
+                                BIOMETRIC_AUTHENTICATOR_ERROR_MSG));
+        android.service.credentials.CreateCredentialRequest request =
+                TestUtilsKt.setUpCreatePasswordRequest();
+        Intent intent = prepareIntentWithCreateRequest(
+                request, biometricPromptResult);
+
+        ProviderCreateCredentialRequest retrievedRequest = PendingIntentHandler
+                .retrieveProviderCreateCredentialRequest(intent);
+
+        assertNotNull(retrievedRequest);
+        TestUtilsKt.equals(request, retrievedRequest);
+        assertEquals(biometricPromptResult, retrievedRequest.getBiometricPromptResult());
+    }
+
+    @Test
+    public void test_retrieveProviderGetCredReqWithSuccessfulBpAuth() {
+        BiometricPromptResult biometricPromptResult = new BiometricPromptResult(
+                new AuthenticationResult(
+                BIOMETRIC_AUTHENTICATOR_TYPE));
+        Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
+                biometricPromptResult);
+
+        ProviderGetCredentialRequest request = PendingIntentHandler
+                .retrieveProviderGetCredentialRequest(intent);
+
+        assertNotNull(request);
+        TestUtilsKt.equals(GET_CREDENTIAL_REQUEST, request);
+        assertEquals(biometricPromptResult, request.getBiometricPromptResult());
+    }
+
+    @Test
+    public void test_retrieveProviderGetCredReqWithFailingBpAuth() {
+        BiometricPromptResult biometricPromptResult = new BiometricPromptResult(
+                new AuthenticationError(
+                        BIOMETRIC_AUTHENTICATOR_ERROR_CODE,
+                        BIOMETRIC_AUTHENTICATOR_ERROR_MSG));
+        Intent intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST,
+                biometricPromptResult);
+
+        ProviderGetCredentialRequest request = PendingIntentHandler
+                .retrieveProviderGetCredentialRequest(intent);
+
+        assertNotNull(request);
+        TestUtilsKt.equals(GET_CREDENTIAL_REQUEST, request);
+        assertEquals(biometricPromptResult, request.getBiometricPromptResult());
+    }
+
+    private Intent prepareIntentWithGetRequest(
+            android.service.credentials.GetCredentialRequest request,
+            BiometricPromptResult biometricPromptResult
+    ) {
+        Intent intent = new Intent();
+        intent.putExtra(CredentialProviderService
+                        .EXTRA_GET_CREDENTIAL_REQUEST, request);
+        prepareIntentWithBiometricResult(intent, biometricPromptResult);
+        return intent;
+    }
+
+    private Intent prepareIntentWithCreateRequest(
+            android.service.credentials.CreateCredentialRequest request,
+            BiometricPromptResult biometricPromptResult) {
+        Intent intent = new Intent();
+        intent.putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
+                request);
+        prepareIntentWithBiometricResult(intent, biometricPromptResult);
+        return intent;
+    }
+
+    private void prepareIntentWithBiometricResult(Intent intent,
+            BiometricPromptResult biometricPromptResult) {
+        String buildId = Build.ID;
+        if (biometricPromptResult.isSuccessful()) {
+            assertNotNull(biometricPromptResult.getAuthenticationResult());
+            String extraResultKey = AuthenticationResult.EXTRA_BIOMETRIC_AUTH_RESULT_TYPE;
+            intent.putExtra(extraResultKey,
+                    biometricPromptResult.getAuthenticationResult().getAuthenticationType());
+        } else {
+            assertNotNull(biometricPromptResult.getAuthenticationError());
+            String extraErrorKey = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR;
+            String extraErrorMessageKey = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR_MESSAGE;
+            intent.putExtra(extraErrorKey,
+                    biometricPromptResult.getAuthenticationError().getErrorCode());
+            intent.putExtra(extraErrorMessageKey,
+                    biometricPromptResult.getAuthenticationError().getErrorMsg());
+        }
+    }
+
+    @Test
     public void test_createCredentialCredentialResponse() {
         if (Build.VERSION.SDK_INT >= 34) {
             return;
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerTest.kt
index c9ed499..31f5f91 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerTest.kt
@@ -16,17 +16,26 @@
 package androidx.credentials.provider
 
 import android.content.Intent
+import android.content.pm.SigningInfo
+import android.credentials.CredentialOption
 import android.os.Build
+import android.os.Bundle
+import android.service.credentials.CallingAppInfo
+import android.service.credentials.CreateCredentialRequest
+import android.service.credentials.GetCredentialRequest
 import androidx.annotation.RequiresApi
 import androidx.credentials.CreatePasswordResponse
 import androidx.credentials.GetCredentialResponse
 import androidx.credentials.PasswordCredential
+import androidx.credentials.equals
 import androidx.credentials.exceptions.CreateCredentialInterruptedException
 import androidx.credentials.exceptions.GetCredentialInterruptedException
+import androidx.credentials.setUpCreatePasswordRequest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assert
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -35,6 +44,238 @@
 @RequiresApi(34)
 @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake")
 class PendingIntentHandlerTest {
+    companion object {
+        private val GET_CREDENTIAL_OPTION =
+            CredentialOption.Builder("type", Bundle(), Bundle()).build()
+
+        private val GET_CREDENTIAL_REQUEST =
+            GetCredentialRequest(
+                CallingAppInfo("package_name", SigningInfo()),
+                ArrayList(setOf(GET_CREDENTIAL_OPTION))
+            )
+
+        private const val BIOMETRIC_AUTHENTICATOR_TYPE = 1
+
+        private const val BIOMETRIC_AUTHENTICATOR_ERROR_CODE = 5
+
+        private const val BIOMETRIC_AUTHENTICATOR_ERROR_MSG = "error"
+
+        private const val FRAMEWORK_EXPECTED_CONSTANT_ERROR_CODE =
+            "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_CODE"
+
+        private const val FRAMEWORK_EXPECTED_CONSTANT_ERROR_MESSAGE =
+            "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_MESSAGE"
+
+        private const val FRAMEWORK_EXPECTED_CONSTANT_AUTH_RESULT =
+            "androidx.credentials.provider.BIOMETRIC_AUTH_RESULT"
+    }
+
+    @Test
+    fun test_constantsMatchFrameworkExpectations_success() {
+        assertThat(AuthenticationResult.EXTRA_BIOMETRIC_AUTH_RESULT_TYPE)
+            .isEqualTo(FRAMEWORK_EXPECTED_CONSTANT_AUTH_RESULT)
+        assertThat(AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR)
+            .isEqualTo(FRAMEWORK_EXPECTED_CONSTANT_ERROR_CODE)
+        assertThat(AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR_MESSAGE)
+            .isEqualTo(FRAMEWORK_EXPECTED_CONSTANT_ERROR_MESSAGE)
+    }
+
+    @Test
+    fun test_retrieveProviderCreateCredReqWithSuccessBpAuthJetpack_retrieveJetpackResult() {
+        for (jetpackResult in AuthenticationResult.biometricFrameworkToJetpackResultMap.values) {
+            val biometricPromptResult = BiometricPromptResult(AuthenticationResult(jetpackResult))
+            val request = setUpCreatePasswordRequest()
+            val intent = prepareIntentWithCreateRequest(request, biometricPromptResult)
+
+            val retrievedRequest =
+                PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
+
+            Assert.assertNotNull(request)
+            equals(request, retrievedRequest!!)
+            Assert.assertNotNull(biometricPromptResult.authenticationResult)
+            Assert.assertEquals(
+                retrievedRequest.biometricPromptResult!!.authenticationResult!!.authenticationType,
+                jetpackResult
+            )
+        }
+    }
+
+    @Test
+    fun test_retrieveProviderGetCredReqWithSuccessBpAuthJetpack_retrieveJetpackResult() {
+        for (jetpackResult in AuthenticationResult.biometricFrameworkToJetpackResultMap.values) {
+            val biometricPromptResult = BiometricPromptResult(AuthenticationResult(jetpackResult))
+            val intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST, biometricPromptResult)
+
+            val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
+
+            Assert.assertNotNull(request)
+            equals(GET_CREDENTIAL_REQUEST, request!!)
+            Assert.assertEquals(biometricPromptResult, request.biometricPromptResult)
+            Assert.assertEquals(
+                request.biometricPromptResult!!.authenticationResult!!.authenticationType,
+                jetpackResult
+            )
+        }
+    }
+
+    // While possible to test non-conversion logic, that would equate functionally to the normal
+    // jetpack tests as there is no validation.
+    @Test
+    fun test_retrieveProviderCreateCredReqWithSuccessBpAuthFramework_correctlyConvertedResult() {
+        for (frameworkResult in AuthenticationResult.biometricFrameworkToJetpackResultMap.keys) {
+            val biometricPromptResult =
+                BiometricPromptResult(
+                    AuthenticationResult.createFrom(
+                        uiAuthenticationType = frameworkResult,
+                        isFrameworkBiometricPrompt = true
+                    )
+                )
+            val request = setUpCreatePasswordRequest()
+            val expectedResult =
+                AuthenticationResult.biometricFrameworkToJetpackResultMap[frameworkResult]
+            val intent = prepareIntentWithCreateRequest(request, biometricPromptResult)
+
+            val retrievedRequest =
+                PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
+
+            Assert.assertNotNull(request)
+            equals(request, retrievedRequest!!)
+            Assert.assertNotNull(biometricPromptResult.authenticationResult)
+            Assert.assertEquals(
+                retrievedRequest.biometricPromptResult!!.authenticationResult!!.authenticationType,
+                expectedResult
+            )
+        }
+    }
+
+    // While possible to test non-conversion logic, that would equate functionally to the normal
+    // jetpack tests as there is no validation.
+    @Test
+    fun test_retrieveProviderGetCredReqWithSuccessBpAuthFramework_correctlyConvertedResult() {
+        for (frameworkResult in AuthenticationResult.biometricFrameworkToJetpackResultMap.keys) {
+            val biometricPromptResult =
+                BiometricPromptResult(
+                    AuthenticationResult.createFrom(
+                        uiAuthenticationType = frameworkResult,
+                        isFrameworkBiometricPrompt = true
+                    )
+                )
+            val expectedResult =
+                AuthenticationResult.biometricFrameworkToJetpackResultMap[frameworkResult]
+            val intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST, biometricPromptResult)
+
+            val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
+
+            Assert.assertNotNull(request)
+            equals(GET_CREDENTIAL_REQUEST, request!!)
+            Assert.assertEquals(biometricPromptResult, request.biometricPromptResult)
+            Assert.assertEquals(
+                request.biometricPromptResult!!.authenticationResult!!.authenticationType,
+                expectedResult
+            )
+        }
+    }
+
+    @Test
+    fun test_retrieveProviderCreateCredReqWithFailureBpAuthJetpack_retrieveJetpackError() {
+        for (jetpackError in AuthenticationError.biometricFrameworkToJetpackErrorMap.values) {
+            val biometricPromptResult =
+                BiometricPromptResult(
+                    AuthenticationError(jetpackError, BIOMETRIC_AUTHENTICATOR_ERROR_MSG)
+                )
+            val request = setUpCreatePasswordRequest()
+            val intent = prepareIntentWithCreateRequest(request, biometricPromptResult)
+
+            val retrievedRequest =
+                PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
+
+            Assert.assertNotNull(retrievedRequest)
+            equals(request, retrievedRequest!!)
+            Assert.assertNotNull(retrievedRequest.biometricPromptResult!!.authenticationError)
+            Assert.assertEquals(
+                retrievedRequest.biometricPromptResult!!.authenticationError!!.errorCode,
+                jetpackError
+            )
+        }
+    }
+
+    @Test
+    fun test_retrieveProviderGetCredReqWithFailureBpAuthJetpack_retrieveJetpackError() {
+        for (jetpackError in AuthenticationError.biometricFrameworkToJetpackErrorMap.values) {
+            val biometricPromptResult =
+                BiometricPromptResult(
+                    AuthenticationError(jetpackError, BIOMETRIC_AUTHENTICATOR_ERROR_MSG)
+                )
+            val intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST, biometricPromptResult)
+
+            val retrievedRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
+
+            Assert.assertNotNull(retrievedRequest)
+            equals(GET_CREDENTIAL_REQUEST, retrievedRequest!!)
+            Assert.assertNotNull(retrievedRequest.biometricPromptResult!!.authenticationError)
+            Assert.assertEquals(
+                retrievedRequest.biometricPromptResult!!.authenticationError!!.errorCode,
+                jetpackError
+            )
+        }
+    }
+
+    @Test
+    fun test_retrieveProviderCreateCredReqWithFailureBpAuthFramework_correctlyConvertedError() {
+        for (frameworkError in AuthenticationError.biometricFrameworkToJetpackErrorMap.keys) {
+            val biometricPromptResult =
+                BiometricPromptResult(
+                    AuthenticationError.createFrom(
+                        uiErrorCode = frameworkError,
+                        uiErrorMessage = BIOMETRIC_AUTHENTICATOR_ERROR_MSG,
+                        isFrameworkBiometricPrompt = true
+                    )
+                )
+            val expectedErrorCode =
+                AuthenticationError.biometricFrameworkToJetpackErrorMap[frameworkError]
+            val request = setUpCreatePasswordRequest()
+            val intent = prepareIntentWithCreateRequest(request, biometricPromptResult)
+
+            val retrievedRequest =
+                PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
+
+            Assert.assertNotNull(retrievedRequest)
+            equals(request, retrievedRequest!!)
+            Assert.assertNotNull(retrievedRequest.biometricPromptResult!!.authenticationError)
+            Assert.assertEquals(
+                retrievedRequest.biometricPromptResult!!.authenticationError!!.errorCode,
+                expectedErrorCode
+            )
+        }
+    }
+
+    @Test
+    fun test_retrieveProviderGetCredReqWithFailureBpAuthFramework_correctlyConvertedError() {
+        for (frameworkError in AuthenticationError.biometricFrameworkToJetpackErrorMap.keys) {
+            val biometricPromptResult =
+                BiometricPromptResult(
+                    AuthenticationError.createFrom(
+                        uiErrorCode = frameworkError,
+                        uiErrorMessage = BIOMETRIC_AUTHENTICATOR_ERROR_MSG,
+                        isFrameworkBiometricPrompt = true
+                    )
+                )
+            val expectedErrorCode =
+                AuthenticationError.biometricFrameworkToJetpackErrorMap[frameworkError]
+            val intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST, biometricPromptResult)
+
+            val retrievedRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
+
+            Assert.assertNotNull(retrievedRequest)
+            equals(GET_CREDENTIAL_REQUEST, retrievedRequest!!)
+            Assert.assertNotNull(retrievedRequest.biometricPromptResult!!.authenticationError)
+            Assert.assertEquals(
+                retrievedRequest.biometricPromptResult!!.authenticationError!!.errorCode,
+                expectedErrorCode
+            )
+        }
+    }
+
     @Test
     fun test_createCredentialException() {
         if (Build.VERSION.SDK_INT >= 34) {
@@ -51,7 +292,7 @@
         assertThat(finalException).isEqualTo(initialException)
     }
 
-    @Test()
+    @Test
     fun test_createCredentialException_throwsWhenEmptyIntent() {
         if (Build.VERSION.SDK_INT >= 34) {
             return
@@ -62,6 +303,119 @@
     }
 
     @Test
+    fun test_retrieveProviderCreateCredReqWithSuccessfulBpAuth() {
+        val biometricPromptResult =
+            BiometricPromptResult(AuthenticationResult(BIOMETRIC_AUTHENTICATOR_TYPE))
+        val request = setUpCreatePasswordRequest()
+        val intent = prepareIntentWithCreateRequest(request, biometricPromptResult)
+
+        val retrievedRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
+
+        Assert.assertNotNull(request)
+        equals(request, retrievedRequest!!)
+        Assert.assertNotNull(biometricPromptResult.authenticationResult)
+    }
+
+    @Test
+    fun test_retrieveProviderCreateCredReqWithFailureBpAuth() {
+        val biometricPromptResult =
+            BiometricPromptResult(
+                AuthenticationError(
+                    BIOMETRIC_AUTHENTICATOR_ERROR_CODE,
+                    BIOMETRIC_AUTHENTICATOR_ERROR_MSG
+                )
+            )
+        val request = setUpCreatePasswordRequest()
+        val intent = prepareIntentWithCreateRequest(request, biometricPromptResult)
+
+        val retrievedRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
+
+        Assert.assertNotNull(retrievedRequest)
+        equals(request, retrievedRequest!!)
+        Assert.assertEquals(biometricPromptResult, retrievedRequest.biometricPromptResult)
+    }
+
+    @Test
+    fun test_retrieveProviderGetCredReqWithSuccessfulBpAuth() {
+        val biometricPromptResult =
+            BiometricPromptResult(AuthenticationResult(BIOMETRIC_AUTHENTICATOR_TYPE))
+        val intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST, biometricPromptResult)
+
+        val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
+
+        Assert.assertNotNull(request)
+        equals(GET_CREDENTIAL_REQUEST, request!!)
+        Assert.assertEquals(biometricPromptResult, request.biometricPromptResult)
+    }
+
+    @Test
+    fun test_retrieveProviderGetCredReqWithFailingBpAuth() {
+        val biometricPromptResult =
+            BiometricPromptResult(
+                AuthenticationError(
+                    BIOMETRIC_AUTHENTICATOR_ERROR_CODE,
+                    BIOMETRIC_AUTHENTICATOR_ERROR_MSG
+                )
+            )
+        val intent = prepareIntentWithGetRequest(GET_CREDENTIAL_REQUEST, biometricPromptResult)
+
+        val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
+
+        Assert.assertNotNull(request)
+        equals(GET_CREDENTIAL_REQUEST, request!!)
+        Assert.assertEquals(biometricPromptResult, request.biometricPromptResult)
+    }
+
+    private fun prepareIntentWithGetRequest(
+        request: GetCredentialRequest,
+        biometricPromptResult: BiometricPromptResult
+    ): Intent {
+        val intent = Intent()
+        intent.putExtra(
+            android.service.credentials.CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
+            request
+        )
+        prepareIntentWithBiometricResult(intent, biometricPromptResult)
+        return intent
+    }
+
+    private fun prepareIntentWithCreateRequest(
+        request: CreateCredentialRequest,
+        biometricPromptResult: BiometricPromptResult
+    ): Intent {
+        val intent = Intent()
+        intent.putExtra(
+            android.service.credentials.CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
+            request
+        )
+        prepareIntentWithBiometricResult(intent, biometricPromptResult)
+        return intent
+    }
+
+    private fun prepareIntentWithBiometricResult(
+        intent: Intent,
+        biometricPromptResult: BiometricPromptResult
+    ) {
+        if (biometricPromptResult.isSuccessful) {
+            Assert.assertNotNull(biometricPromptResult.authenticationResult)
+            var extraResultKey = AuthenticationResult.EXTRA_BIOMETRIC_AUTH_RESULT_TYPE
+            intent.putExtra(
+                extraResultKey,
+                biometricPromptResult.authenticationResult!!.authenticationType
+            )
+        } else {
+            Assert.assertNotNull(biometricPromptResult.authenticationError)
+            var extraErrorKey = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR
+            var extraErrorMessageKey = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR_MESSAGE
+            intent.putExtra(extraErrorKey, biometricPromptResult.authenticationError!!.errorCode)
+            intent.putExtra(
+                extraErrorMessageKey,
+                biometricPromptResult.authenticationError!!.errorMsg
+            )
+        }
+    }
+
+    @Test
     fun test_credentialException() {
         if (Build.VERSION.SDK_INT >= 34) {
             return
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionJavaTest.java
index 3f86607..22d51e6 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/ActionJavaTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertThrows;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
 import android.content.Context;
 import android.content.Intent;
 
@@ -80,9 +79,10 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void fromSlice_success() {
         Action originalAction = new Action(TITLE, mPendingIntent, SUBTITLE);
-        Slice slice = Action.toSlice(originalAction);
+        android.app.slice.Slice slice = Action.toSlice(originalAction);
 
         Action fromSlice = Action.fromSlice(slice);
 
@@ -94,9 +94,10 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 34)
+    @SuppressWarnings("deprecation")
     public void fromAction_success() {
         Action originalAction = new Action(TITLE, mPendingIntent, SUBTITLE);
-        Slice slice = Action.toSlice(originalAction);
+        android.app.slice.Slice slice = Action.toSlice(originalAction);
 
         Action action = Action.fromAction(new android.service.credentials.Action(slice));
 
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionJavaTest.java
index d628df5..d5a75c5 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/AuthenticationActionJavaTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertThrows;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
 import android.content.Context;
 import android.content.Intent;
 
@@ -75,9 +74,10 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void fromSlice_success() {
         AuthenticationAction originalAction = new AuthenticationAction(TITLE, mPendingIntent);
-        Slice slice = AuthenticationAction.toSlice(originalAction);
+        android.app.slice.Slice slice = AuthenticationAction.toSlice(originalAction);
 
         AuthenticationAction fromSlice = AuthenticationAction.fromSlice(slice);
 
@@ -87,9 +87,10 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 34)
+    @SuppressWarnings("deprecation")
     public void fromAction_success() {
         AuthenticationAction originalAction = new AuthenticationAction(TITLE, mPendingIntent);
-        Slice slice = AuthenticationAction.toSlice(originalAction);
+        android.app.slice.Slice slice = AuthenticationAction.toSlice(originalAction);
         assertNotNull(slice);
 
         AuthenticationAction action = AuthenticationAction.fromAction(
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryJavaTest.java
index 0de5cc2..83a2d1d 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryJavaTest.java
@@ -23,12 +23,17 @@
 import static org.junit.Assert.assertThrows;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
+import android.os.Build;
 
+import androidx.annotation.RequiresApi;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.core.os.BuildCompat;
+import androidx.credentials.provider.BiometricPromptData;
 import androidx.credentials.provider.CreateEntry;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -40,8 +45,10 @@
 
 import java.time.Instant;
 
+import javax.crypto.NullCipher;
+
 @RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @SmallTest
 public class CreateEntryJavaTest {
     private static final CharSequence ACCOUNT_NAME = "account_name";
@@ -73,7 +80,17 @@
     }
 
     @Test
-    public void constructor_allParameters_success() {
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    public void constructor_allParametersAboveApiO_success() {
+        CreateEntry entry = constructEntryWithAllParams();
+
+        assertNotNull(entry);
+        assertEntryWithAllParams(entry);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    public void constructor_allParametersApiOAndBelow_success() {
         CreateEntry entry = constructEntryWithAllParams();
 
         assertNotNull(entry);
@@ -128,9 +145,10 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 34)
+    @SuppressWarnings("deprecation")
     public void fromCreateEntry_allParams_success() {
         CreateEntry originalEntry = constructEntryWithAllParams();
-        Slice slice = CreateEntry.toSlice(originalEntry);
+        android.app.slice.Slice slice = CreateEntry.toSlice(originalEntry);
         assertNotNull(slice);
 
         CreateEntry entry = CreateEntry.fromCreateEntry(
@@ -147,18 +165,22 @@
     private void assertEntryWithRequiredParams(CreateEntry entry) {
         assertThat(ACCOUNT_NAME.equals(entry.getAccountName()));
         assertThat(mPendingIntent).isEqualTo(entry.getPendingIntent());
+        assertThat(entry.getBiometricPromptData()).isNull();
     }
 
     private CreateEntry constructEntryWithAllParams() {
-        return new CreateEntry.Builder(
+        CreateEntry.Builder testBuilder = new CreateEntry.Builder(
                 ACCOUNT_NAME,
                 mPendingIntent)
                 .setIcon(ICON)
                 .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
                 .setPasswordCredentialCount(PASSWORD_COUNT)
                 .setPublicKeyCredentialCount(PUBLIC_KEY_CREDENTIAL_COUNT)
-                .setTotalCredentialCount(TOTAL_COUNT)
-                .build();
+                .setTotalCredentialCount(TOTAL_COUNT);
+        if (BuildCompat.isAtLeastV()) {
+            testBuilder.setBiometricPromptData(testBiometricPromptData());
+        }
+        return testBuilder.build();
     }
 
     private void assertEntryWithAllParams(CreateEntry entry) {
@@ -169,5 +191,25 @@
         assertThat(PASSWORD_COUNT).isEqualTo(entry.getPasswordCredentialCount());
         assertThat(PUBLIC_KEY_CREDENTIAL_COUNT).isEqualTo(entry.getPublicKeyCredentialCount());
         assertThat(TOTAL_COUNT).isEqualTo(entry.getTotalCredentialCount());
+        if (BuildCompat.isAtLeastV() && entry.getBiometricPromptData() != null) {
+            assertAboveApiV(entry);
+        } else {
+            assertThat(entry.getBiometricPromptData()).isNull();
+        }
+    }
+
+    private static void assertAboveApiV(CreateEntry entry) {
+        if (BuildCompat.isAtLeastV()) {
+            assertThat(entry.getBiometricPromptData().getAllowedAuthenticators()).isEqualTo(
+                    testBiometricPromptData().getAllowedAuthenticators());
+        }
+    }
+
+    @RequiresApi(35)
+    private static BiometricPromptData testBiometricPromptData() {
+        return new BiometricPromptData.Builder()
+                .setCryptoObject(new BiometricPrompt.CryptoObject(new NullCipher()))
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build();
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryTest.kt
index 606a8fa..4c6810e 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CreateEntryTest.kt
@@ -20,6 +20,12 @@
 import android.content.Intent
 import android.graphics.Bitmap
 import android.graphics.drawable.Icon
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricPrompt
+import androidx.core.os.BuildCompat
+import androidx.credentials.provider.BiometricPromptData
 import androidx.credentials.provider.CreateEntry
 import androidx.credentials.provider.CreateEntry.Companion.fromCreateEntry
 import androidx.credentials.provider.CreateEntry.Companion.fromSlice
@@ -28,8 +34,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import javax.crypto.NullCipher
 import org.junit.Assert
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
@@ -38,7 +45,7 @@
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @SmallTest
 class CreateEntryTest {
     private val mContext = ApplicationProvider.getApplicationContext<Context>()
@@ -74,7 +81,8 @@
     }
 
     @Test
-    fun constructor_allParameters_success() {
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    fun constructor_allParametersAboveApiO_success() {
         val entry = constructEntryWithAllParams()
 
         assertNotNull(entry)
@@ -139,33 +147,55 @@
     }
 
     private fun assertEntryWithRequiredParams(entry: CreateEntry) {
-        Truth.assertThat(ACCOUNT_NAME == entry.accountName)
-        Truth.assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+        assertThat(ACCOUNT_NAME == entry.accountName)
+        assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     private fun constructEntryWithAllParams(): CreateEntry {
-        return CreateEntry(
-            ACCOUNT_NAME,
-            mPendingIntent,
-            DESCRIPTION,
-            Instant.ofEpochMilli(LAST_USED_TIME),
-            ICON,
-            PASSWORD_COUNT,
-            PUBLIC_KEY_CREDENTIAL_COUNT,
-            TOTAL_COUNT,
-            AUTO_SELECT_BIT
-        )
+        if (BuildCompat.isAtLeastV()) {
+            return CreateEntry(
+                ACCOUNT_NAME,
+                mPendingIntent,
+                DESCRIPTION,
+                Instant.ofEpochMilli(LAST_USED_TIME),
+                ICON,
+                PASSWORD_COUNT,
+                PUBLIC_KEY_CREDENTIAL_COUNT,
+                TOTAL_COUNT,
+                AUTO_SELECT_BIT,
+                testBiometricPromptData()
+            )
+        } else {
+            return CreateEntry(
+                ACCOUNT_NAME,
+                mPendingIntent,
+                DESCRIPTION,
+                Instant.ofEpochMilli(LAST_USED_TIME),
+                ICON,
+                PASSWORD_COUNT,
+                PUBLIC_KEY_CREDENTIAL_COUNT,
+                TOTAL_COUNT,
+                AUTO_SELECT_BIT
+            )
+        }
     }
 
     private fun assertEntryWithAllParams(entry: CreateEntry) {
-        Truth.assertThat(ACCOUNT_NAME).isEqualTo(entry.accountName)
-        Truth.assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
-        Truth.assertThat(ICON).isEqualTo(entry.icon)
-        Truth.assertThat(LAST_USED_TIME).isEqualTo(entry.lastUsedTime?.toEpochMilli())
-        Truth.assertThat(PASSWORD_COUNT).isEqualTo(entry.getPasswordCredentialCount())
-        Truth.assertThat(PUBLIC_KEY_CREDENTIAL_COUNT).isEqualTo(entry.getPublicKeyCredentialCount())
-        Truth.assertThat(TOTAL_COUNT).isEqualTo(entry.getTotalCredentialCount())
-        Truth.assertThat(AUTO_SELECT_BIT).isTrue()
+        assertThat(ACCOUNT_NAME).isEqualTo(entry.accountName)
+        assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
+        assertThat(ICON).isEqualTo(entry.icon)
+        assertThat(LAST_USED_TIME).isEqualTo(entry.lastUsedTime?.toEpochMilli())
+        assertThat(PASSWORD_COUNT).isEqualTo(entry.getPasswordCredentialCount())
+        assertThat(PUBLIC_KEY_CREDENTIAL_COUNT).isEqualTo(entry.getPublicKeyCredentialCount())
+        assertThat(TOTAL_COUNT).isEqualTo(entry.getTotalCredentialCount())
+        assertThat(AUTO_SELECT_BIT).isTrue()
+        if (BuildCompat.isAtLeastV() && entry.biometricPromptData != null) {
+            assertThat(entry.biometricPromptData!!.allowedAuthenticators)
+                .isEqualTo(testBiometricPromptData().allowedAuthenticators)
+        } else {
+            assertThat(entry.biometricPromptData).isNull()
+        }
     }
 
     companion object {
@@ -178,5 +208,13 @@
         private const val LAST_USED_TIME = 10L
         private val ICON =
             Icon.createWithBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888))
+
+        @RequiresApi(35)
+        private fun testBiometricPromptData(): BiometricPromptData {
+            return BiometricPromptData.Builder()
+                .setCryptoObject(BiometricPrompt.CryptoObject(NullCipher()))
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build()
+        }
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryJavaTest.java
index cd6ab67..d93cd88 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryJavaTest.java
@@ -25,7 +25,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -33,10 +32,15 @@
 import android.os.Bundle;
 import android.service.credentials.CredentialEntry;
 
+import androidx.annotation.RequiresApi;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.core.os.BuildCompat;
 import androidx.credentials.R;
 import androidx.credentials.TestUtilsKt;
 import androidx.credentials.provider.BeginGetCredentialOption;
 import androidx.credentials.provider.BeginGetCustomCredentialOption;
+import androidx.credentials.provider.BiometricPromptData;
 import androidx.credentials.provider.CustomCredentialEntry;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -47,8 +51,11 @@
 import org.junit.runner.RunWith;
 
 import java.time.Instant;
+
+import javax.crypto.NullCipher;
+
 @RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @SmallTest
 public class CustomCredentialEntryJavaTest {
     private static final CharSequence TITLE = "title";
@@ -149,6 +156,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 28)
     public void builder_constructDefault_containsOnlySetPropertiesAndDefaultValues() {
         CustomCredentialEntry entry = constructEntryWithRequiredParams();
 
@@ -180,9 +188,11 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void fromSlice_requiredParams_success() {
         CustomCredentialEntry originalEntry = constructEntryWithRequiredParams();
-        Slice slice = CustomCredentialEntry.toSlice(originalEntry);
+        android.app.slice.Slice slice = CustomCredentialEntry
+                .toSlice(originalEntry);
         CustomCredentialEntry entry = CustomCredentialEntry.fromSlice(
                 slice);
         assertNotNull(entry);
@@ -190,18 +200,21 @@
     }
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void fromSlice_allParams_success() {
         CustomCredentialEntry originalEntry = constructEntryWithAllParams();
-        Slice slice = CustomCredentialEntry.toSlice(originalEntry);
+        android.app.slice.Slice slice = CustomCredentialEntry
+                .toSlice(originalEntry);
         CustomCredentialEntry entry = CustomCredentialEntry.fromSlice(slice);
         assertNotNull(entry);
         assertEntryWithAllParamsFromSlice(entry);
     }
     @Test
     @SdkSuppress(minSdkVersion = 34)
+    @SuppressWarnings("deprecation")
     public void fromCredentialEntry_allParams_success() {
         CustomCredentialEntry originalEntry = constructEntryWithAllParams();
-        Slice slice = CustomCredentialEntry.toSlice(originalEntry);
+        android.app.slice.Slice slice = CustomCredentialEntry.toSlice(originalEntry);
         assertNotNull(slice);
         CustomCredentialEntry entry = CustomCredentialEntry.fromCredentialEntry(
                 new CredentialEntry("id", slice));
@@ -220,11 +233,12 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void isDefaultIcon_noIconSetFromSlice_returnsTrue() {
         CustomCredentialEntry entry = new CustomCredentialEntry
                 .Builder(mContext, TYPE, TITLE, mPendingIntent, mBeginCredentialOption).build();
 
-        Slice slice = CustomCredentialEntry.toSlice(entry);
+        android.app.slice.Slice slice = CustomCredentialEntry.toSlice(entry);
 
         assertNotNull(slice);
 
@@ -237,12 +251,13 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void isDefaultIcon_customIconSetFromSlice_returnsTrue() {
         CustomCredentialEntry entry = new CustomCredentialEntry
                 .Builder(mContext, TYPE, TITLE, mPendingIntent, mBeginCredentialOption)
                 .setIcon(ICON).build();
 
-        Slice slice = CustomCredentialEntry.toSlice(entry);
+        android.app.slice.Slice slice = CustomCredentialEntry.toSlice(entry);
 
         assertNotNull(slice);
 
@@ -291,7 +306,7 @@
         ).build();
     }
     private CustomCredentialEntry constructEntryWithAllParams() {
-        return new CustomCredentialEntry.Builder(
+        CustomCredentialEntry.Builder testBuilder = new CustomCredentialEntry.Builder(
                 mContext,
                 TYPE,
                 TITLE,
@@ -302,8 +317,12 @@
                 .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
                 .setTypeDisplayName(TYPE_DISPLAY_NAME)
                 .setEntryGroupId(ENTRY_GROUP_ID)
-                .setDefaultIconPreferredAsSingleProvider(SINGLE_PROVIDER_ICON_BIT)
-                .build();
+                .setDefaultIconPreferredAsSingleProvider(SINGLE_PROVIDER_ICON_BIT);
+
+        if (BuildCompat.isAtLeastV()) {
+            testBuilder.setBiometricPromptData(testBiometricPromptData());
+        }
+        return testBuilder.build();
     }
     private void assertEntryWithRequiredParams(CustomCredentialEntry entry) {
         assertThat(TITLE.equals(entry.getTitle()));
@@ -313,6 +332,7 @@
         assertThat(entry.getEntryGroupId()).isEqualTo(TITLE);
         assertThat(entry.isDefaultIconPreferredAsSingleProvider()).isEqualTo(
                 DEFAULT_SINGLE_PROVIDER_ICON_BIT);
+        assertThat(entry.getBiometricPromptData()).isNull();
     }
     private void assertEntryWithRequiredParamsFromSlice(CustomCredentialEntry entry) {
         assertThat(TITLE.equals(entry.getTitle()));
@@ -322,6 +342,7 @@
         assertThat(entry.getEntryGroupId()).isEqualTo(TITLE);
         assertThat(entry.isDefaultIconPreferredAsSingleProvider()).isEqualTo(
                 DEFAULT_SINGLE_PROVIDER_ICON_BIT);
+        assertThat(entry.getBiometricPromptData()).isNull();
     }
     private void assertEntryWithAllParams(CustomCredentialEntry entry) {
         assertThat(TITLE.equals(entry.getTitle()));
@@ -338,6 +359,12 @@
         assertThat(entry.getEntryGroupId()).isEqualTo(ENTRY_GROUP_ID);
         assertThat(entry.isDefaultIconPreferredAsSingleProvider()).isEqualTo(
                 SINGLE_PROVIDER_ICON_BIT);
+        if (BuildCompat.isAtLeastV() && entry.getBiometricPromptData() != null) {
+            assertThat(entry.getBiometricPromptData().getAllowedAuthenticators()).isEqualTo(
+                    testBiometricPromptData().getAllowedAuthenticators());
+        } else {
+            assertThat(entry.getBiometricPromptData()).isNull();
+        }
     }
     private void assertEntryWithAllParamsFromSlice(CustomCredentialEntry entry) {
         assertThat(TITLE.equals(entry.getTitle()));
@@ -353,5 +380,19 @@
         assertThat(entry.getEntryGroupId()).isEqualTo(ENTRY_GROUP_ID);
         assertThat(entry.isDefaultIconPreferredAsSingleProvider()).isEqualTo(
                 SINGLE_PROVIDER_ICON_BIT);
+        if (BuildCompat.isAtLeastV() && entry.getBiometricPromptData() != null) {
+            assertThat(entry.getBiometricPromptData().getAllowedAuthenticators()).isEqualTo(
+                    testBiometricPromptData().getAllowedAuthenticators());
+        } else {
+            assertThat(entry.getBiometricPromptData()).isNull();
+        }
+    }
+
+    @RequiresApi(35)
+    private static BiometricPromptData testBiometricPromptData() {
+        return new BiometricPromptData.Builder()
+            .setCryptoObject(new BiometricPrompt.CryptoObject(new NullCipher()))
+            .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+            .build();
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryTest.kt
index 6669355..70aa96d 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/CustomCredentialEntryTest.kt
@@ -22,11 +22,16 @@
 import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.service.credentials.CredentialEntry
+import androidx.annotation.RequiresApi
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricPrompt
+import androidx.core.os.BuildCompat
 import androidx.credentials.CredentialOption
 import androidx.credentials.R
 import androidx.credentials.equals
 import androidx.credentials.provider.BeginGetCredentialOption
 import androidx.credentials.provider.BeginGetCustomCredentialOption
+import androidx.credentials.provider.BiometricPromptData
 import androidx.credentials.provider.CustomCredentialEntry
 import androidx.credentials.provider.CustomCredentialEntry.Companion.fromCredentialEntry
 import androidx.credentials.provider.CustomCredentialEntry.Companion.fromSlice
@@ -37,6 +42,7 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import javax.crypto.NullCipher
 import org.junit.Assert
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertThrows
@@ -44,7 +50,7 @@
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @SmallTest
 class CustomCredentialEntryTest {
     private val mContext = ApplicationProvider.getApplicationContext<Context>()
@@ -53,7 +59,6 @@
         PendingIntent.getActivity(mContext, 0, mIntent, PendingIntent.FLAG_IMMUTABLE)
 
     @Test
-    @SdkSuppress(minSdkVersion = 28)
     fun constructor_requiredParams_success() {
         val entry = constructEntryWithRequiredParams()
         assertNotNull(entry)
@@ -100,7 +105,7 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 23)
+    @SdkSuppress(minSdkVersion = 28)
     fun constructor_nullIcon_defaultIconSet() {
         val entry = constructEntryWithRequiredParams()
         assertThat(
@@ -183,6 +188,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 28)
     fun builder_constructDefault_containsOnlySetPropertiesAndDefaultValues() {
         val entry =
             CustomCredentialEntry.Builder(mContext, TYPE, TITLE, mPendingIntent, BEGIN_OPTION)
@@ -201,6 +207,7 @@
         assertThat(entry.entryGroupId).isEqualTo(TITLE)
         assertThat(entry.isDefaultIconPreferredAsSingleProvider)
             .isEqualTo(DEFAULT_SINGLE_PROVIDER_ICON_BIT)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     @Test
@@ -318,19 +325,36 @@
     }
 
     private fun constructEntryWithAllParams(): CustomCredentialEntry {
-        return CustomCredentialEntry(
-            mContext,
-            TITLE,
-            mPendingIntent,
-            BEGIN_OPTION,
-            SUBTITLE,
-            TYPE_DISPLAY_NAME,
-            Instant.ofEpochMilli(LAST_USED_TIME),
-            ICON,
-            IS_AUTO_SELECT_ALLOWED,
-            ENTRY_GROUP_ID,
-            SINGLE_PROVIDER_ICON_BIT
-        )
+        return if (BuildCompat.isAtLeastV()) {
+            CustomCredentialEntry(
+                mContext,
+                TITLE,
+                mPendingIntent,
+                BEGIN_OPTION,
+                SUBTITLE,
+                TYPE_DISPLAY_NAME,
+                Instant.ofEpochMilli(LAST_USED_TIME),
+                ICON,
+                IS_AUTO_SELECT_ALLOWED,
+                ENTRY_GROUP_ID,
+                SINGLE_PROVIDER_ICON_BIT,
+                testBiometricPromptData()
+            )
+        } else {
+            CustomCredentialEntry(
+                mContext,
+                TITLE,
+                mPendingIntent,
+                BEGIN_OPTION,
+                SUBTITLE,
+                TYPE_DISPLAY_NAME,
+                Instant.ofEpochMilli(LAST_USED_TIME),
+                ICON,
+                IS_AUTO_SELECT_ALLOWED,
+                ENTRY_GROUP_ID,
+                SINGLE_PROVIDER_ICON_BIT
+            )
+        }
     }
 
     private fun assertEntryWithAllParams(entry: CustomCredentialEntry) {
@@ -344,6 +368,12 @@
         assertThat(mPendingIntent).isEqualTo(entry.pendingIntent)
         assertThat(entry.isDefaultIconPreferredAsSingleProvider).isEqualTo(SINGLE_PROVIDER_ICON_BIT)
         assertThat(ENTRY_GROUP_ID).isEqualTo(entry.entryGroupId)
+        if (BuildCompat.isAtLeastV() && entry.biometricPromptData != null) {
+            assertThat(entry.biometricPromptData!!.allowedAuthenticators)
+                .isEqualTo(testBiometricPromptData().allowedAuthenticators)
+        } else {
+            assertThat(entry.biometricPromptData).isNull()
+        }
     }
 
     private fun assertEntryWithAllParamsFromSlice(entry: CustomCredentialEntry) {
@@ -358,6 +388,12 @@
         assertThat(BEGIN_OPTION.type).isEqualTo(entry.type)
         assertThat(entry.isDefaultIconPreferredAsSingleProvider).isEqualTo(SINGLE_PROVIDER_ICON_BIT)
         assertThat(ENTRY_GROUP_ID).isEqualTo(entry.entryGroupId)
+        if (BuildCompat.isAtLeastV() && entry.biometricPromptData != null) {
+            assertThat(entry.biometricPromptData!!.allowedAuthenticators)
+                .isEqualTo(testBiometricPromptData().allowedAuthenticators)
+        } else {
+            assertThat(entry.biometricPromptData).isNull()
+        }
     }
 
     private fun assertEntryWithRequiredParams(entry: CustomCredentialEntry) {
@@ -368,6 +404,7 @@
         assertThat(entry.isDefaultIconPreferredAsSingleProvider)
             .isEqualTo(DEFAULT_SINGLE_PROVIDER_ICON_BIT)
         assertThat(entry.entryGroupId).isEqualTo(TITLE)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     private fun assertEntryWithRequiredParamsFromSlice(entry: CustomCredentialEntry) {
@@ -377,6 +414,7 @@
         assertThat(entry.isDefaultIconPreferredAsSingleProvider)
             .isEqualTo(DEFAULT_SINGLE_PROVIDER_ICON_BIT)
         assertThat(entry.entryGroupId).isEqualTo(TITLE)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     companion object {
@@ -393,5 +431,13 @@
         private const val DEFAULT_SINGLE_PROVIDER_ICON_BIT = false
         private const val SINGLE_PROVIDER_ICON_BIT = true
         private const val ENTRY_GROUP_ID = "entryGroupId"
+
+        @RequiresApi(35)
+        private fun testBiometricPromptData(): BiometricPromptData {
+            return BiometricPromptData.Builder()
+                .setCryptoObject(BiometricPrompt.CryptoObject(NullCipher()))
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build()
+        }
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
index 639f2ad..48ab2a9 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryJavaTest.java
@@ -25,7 +25,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -33,10 +32,15 @@
 import android.os.Bundle;
 import android.service.credentials.CredentialEntry;
 
+import androidx.annotation.RequiresApi;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.core.os.BuildCompat;
 import androidx.credentials.PasswordCredential;
 import androidx.credentials.R;
 import androidx.credentials.TestUtilsKt;
 import androidx.credentials.provider.BeginGetPasswordOption;
+import androidx.credentials.provider.BiometricPromptData;
 import androidx.credentials.provider.PasswordCredentialEntry;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -48,8 +52,11 @@
 
 import java.time.Instant;
 import java.util.HashSet;
+
+import javax.crypto.NullCipher;
+
 @RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @SmallTest
 public class PasswordCredentialEntryJavaTest {
     private static final CharSequence USERNAME = "title";
@@ -95,6 +102,7 @@
 
     @SdkSuppress(minSdkVersion = 28)
     @Test
+    @SuppressWarnings("deprecation")
     public void isDefaultIcon_customIconSetFromSlice_returnsFalse() {
         PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(
                 mContext,
@@ -103,7 +111,7 @@
                 mBeginGetPasswordOption
         ).setIcon(ICON).build();
 
-        Slice slice = PasswordCredentialEntry.toSlice(entry);
+        android.app.slice.Slice slice = PasswordCredentialEntry.toSlice(entry);
 
         assertNotNull(slice);
 
@@ -117,6 +125,7 @@
 
     @SdkSuppress(minSdkVersion = 28)
     @Test
+    @SuppressWarnings("deprecation")
     public void isDefaultIcon_noIconSetFromSlice_returnsTrue() {
         PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(
                 mContext,
@@ -125,7 +134,7 @@
                 mBeginGetPasswordOption
         ).build();
 
-        Slice slice = PasswordCredentialEntry.toSlice(entry);
+        android.app.slice.Slice slice = PasswordCredentialEntry.toSlice(entry);
         assertNotNull(slice);
         PasswordCredentialEntry entryFromSlice = PasswordCredentialEntry
                 .fromSlice(slice);
@@ -223,16 +232,20 @@
     @Test
     public void build_isAutoSelectAllowedDefault_false() {
         PasswordCredentialEntry entry = constructEntryWithRequiredParamsOnly();
+
         assertFalse(entry.isAutoSelectAllowed());
     }
     @Test
     public void constructor_defaultAffiliatedDomain() {
         PasswordCredentialEntry entry = constructEntryWithRequiredParamsOnly();
+
         assertThat(entry.getAffiliatedDomain()).isNull();
     }
+
     @Test
     public void constructor_nonEmptyAffiliatedDomainSet_nonEmptyAffiliatedDomainRetrieved() {
         String expectedAffiliatedDomain = "non-empty";
+
         PasswordCredentialEntry entryWithAffiliatedDomain = new PasswordCredentialEntry(
                 mContext,
                 USERNAME,
@@ -245,39 +258,47 @@
                 expectedAffiliatedDomain,
                 false
         );
+
         assertThat(entryWithAffiliatedDomain.getAffiliatedDomain())
                 .isEqualTo(expectedAffiliatedDomain);
     }
     @Test
+    @SdkSuppress(minSdkVersion = 34)
     public void builder_constructDefault_containsOnlyDefaultValuesForSettableParameters() {
         PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(mContext, USERNAME,
                 mPendingIntent, mBeginGetPasswordOption).build();
+
         assertThat(entry.getAffiliatedDomain()).isNull();
         assertThat(entry.getDisplayName()).isNull();
         assertThat(entry.getLastUsedTime()).isNull();
         assertThat(entry.isAutoSelectAllowed()).isFalse();
         assertThat(entry.getEntryGroupId()).isEqualTo(USERNAME);
+        assertThat(entry.getBiometricPromptData()).isNull();
     }
     @Test
     public void builder_setAffiliatedDomainNull_retrieveNullAffiliatedDomain() {
         PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(mContext, USERNAME,
                 mPendingIntent, mBeginGetPasswordOption).setAffiliatedDomain(null).build();
+
         assertThat(entry.getAffiliatedDomain()).isNull();
     }
     @Test
     public void builder_setAffiliatedDomainNonNull_retrieveNonNullAffiliatedDomain() {
         String expectedAffiliatedDomain = "affiliated-domain";
+
         PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(
                 mContext,
                 USERNAME,
                 mPendingIntent,
                 mBeginGetPasswordOption
         ).setAffiliatedDomain(expectedAffiliatedDomain).build();
+
         assertThat(entry.getAffiliatedDomain()).isEqualTo(expectedAffiliatedDomain);
     }
     @Test
     public void builder_setPreferredDefaultIconBit_retrieveSetIconBit() {
         boolean expectedPreferredDefaultIconBit = SINGLE_PROVIDER_ICON_BIT;
+
         PasswordCredentialEntry entry = new PasswordCredentialEntry.Builder(
                 mContext,
                 USERNAME,
@@ -285,6 +306,7 @@
                 mBeginGetPasswordOption
         ).setDefaultIconPreferredAsSingleProvider(expectedPreferredDefaultIconBit)
                 .build();
+
         assertThat(entry.isDefaultIconPreferredAsSingleProvider())
                 .isEqualTo(expectedPreferredDefaultIconBit);
     }
@@ -323,20 +345,31 @@
                 mPendingIntent,
                 mBeginGetPasswordOption).build();
     }
+
     private PasswordCredentialEntry constructEntryWithAllParams() {
-        return new PasswordCredentialEntry.Builder(
-                mContext,
-                USERNAME,
-                mPendingIntent,
-                mBeginGetPasswordOption)
-                .setDisplayName(DISPLAYNAME)
-                .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
-                .setIcon(ICON)
-                .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
-                .setAffiliatedDomain(AFFILIATED_DOMAIN)
-                .setDefaultIconPreferredAsSingleProvider(SINGLE_PROVIDER_ICON_BIT)
-                .build();
+        if (BuildCompat.isAtLeastV()) {
+            return new PasswordCredentialEntry.Builder(
+                    mContext, USERNAME, mPendingIntent, mBeginGetPasswordOption)
+                    .setDisplayName(DISPLAYNAME)
+                    .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
+                    .setIcon(ICON)
+                    .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
+                    .setAffiliatedDomain(AFFILIATED_DOMAIN)
+                    .setDefaultIconPreferredAsSingleProvider(SINGLE_PROVIDER_ICON_BIT)
+                    .setBiometricPromptData(testBiometricPromptData()).build();
+        } else {
+            return new PasswordCredentialEntry.Builder(
+                    mContext, USERNAME, mPendingIntent, mBeginGetPasswordOption)
+                    .setDisplayName(DISPLAYNAME)
+                    .setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME))
+                    .setIcon(ICON)
+                    .setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED)
+                    .setAffiliatedDomain(AFFILIATED_DOMAIN)
+                    .setDefaultIconPreferredAsSingleProvider(SINGLE_PROVIDER_ICON_BIT)
+                    .build();
+        }
     }
+
     private void assertEntryWithRequiredParamsOnly(PasswordCredentialEntry entry,
             Boolean assertOptionIdOnly) {
         assertThat(USERNAME.equals(entry.getUsername()));
@@ -346,6 +379,7 @@
         assertThat(entry.isDefaultIconPreferredAsSingleProvider()).isEqualTo(
                 DEFAULT_SINGLE_PROVIDER_ICON_BIT);
         assertThat(entry.getEntryGroupId()).isEqualTo(USERNAME);
+        assertThat(entry.getBiometricPromptData()).isNull();
     }
     private void assertEntryWithAllParams(PasswordCredentialEntry entry) {
         assertThat(USERNAME.equals(entry.getUsername()));
@@ -360,5 +394,19 @@
         assertThat(entry.isDefaultIconPreferredAsSingleProvider()).isEqualTo(
                 SINGLE_PROVIDER_ICON_BIT);
         assertThat(entry.getEntryGroupId()).isEqualTo(USERNAME);
+        if (BuildCompat.isAtLeastV() && entry.getBiometricPromptData() != null) {
+            assertThat(entry.getBiometricPromptData().getAllowedAuthenticators()).isEqualTo(
+                    testBiometricPromptData().getAllowedAuthenticators());
+        } else {
+            assertThat(entry.getBiometricPromptData()).isNull();
+        }
+    }
+
+    @RequiresApi(35)
+    private static BiometricPromptData testBiometricPromptData() {
+        return new BiometricPromptData.Builder()
+                .setCryptoObject(new BiometricPrompt.CryptoObject(new NullCipher()))
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build();
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
index 83b0853..bd72745 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PasswordCredentialEntryTest.kt
@@ -22,11 +22,16 @@
 import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.service.credentials.CredentialEntry
+import androidx.annotation.RequiresApi
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricPrompt
+import androidx.core.os.BuildCompat
 import androidx.credentials.CredentialOption
 import androidx.credentials.PasswordCredential
 import androidx.credentials.R
 import androidx.credentials.equals
 import androidx.credentials.provider.BeginGetPasswordOption
+import androidx.credentials.provider.BiometricPromptData
 import androidx.credentials.provider.PasswordCredentialEntry
 import androidx.credentials.provider.PasswordCredentialEntry.Companion.fromSlice
 import androidx.test.core.app.ApplicationProvider
@@ -35,6 +40,7 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import javax.crypto.NullCipher
 import junit.framework.TestCase.assertFalse
 import junit.framework.TestCase.assertNotNull
 import org.junit.Assert
@@ -43,7 +49,7 @@
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @SmallTest
 class PasswordCredentialEntryTest {
     private val mContext = ApplicationProvider.getApplicationContext<Context>()
@@ -182,12 +188,21 @@
     @Test
     fun constructor_defaultAffiliatedDomain() {
         val defaultEntry = constructEntryWithRequiredParamsOnly()
+
         assertThat(defaultEntry.affiliatedDomain).isNull()
     }
 
     @Test
+    fun constructor_defaultBiometricPromptData() {
+        val defaultEntry = constructEntryWithRequiredParamsOnly()
+
+        assertThat(defaultEntry.biometricPromptData).isNull()
+    }
+
+    @Test
     fun constructor_nonEmptyAffiliatedDomainSet_nonEmptyAffiliatedDomainRetrieved() {
         val expectedAffiliatedDomain = "non-empty"
+
         val entryWithAffiliationType =
             PasswordCredentialEntry(
                 mContext,
@@ -199,6 +214,7 @@
                 ICON,
                 affiliatedDomain = expectedAffiliatedDomain
             )
+
         assertThat(entryWithAffiliationType.affiliatedDomain).isEqualTo(expectedAffiliatedDomain)
     }
 
@@ -216,6 +232,7 @@
                 ICON,
                 isDefaultIconPreferredAsSingleProvider = expectedPreferredDefaultIconBit
             )
+
         assertThat(entry.isDefaultIconPreferredAsSingleProvider)
             .isEqualTo(expectedPreferredDefaultIconBit)
     }
@@ -223,6 +240,7 @@
     @Test
     fun constructor_preferredIconBitNotProvided_retrieveDefaultPreferredIconBit() {
         val entry = PasswordCredentialEntry(mContext, USERNAME, mPendingIntent, BEGIN_OPTION)
+
         assertThat(entry.isDefaultIconPreferredAsSingleProvider)
             .isEqualTo(DEFAULT_SINGLE_PROVIDER_ICON_BIT)
     }
@@ -252,6 +270,7 @@
         assertThat(entry.beginGetCredentialOption).isEqualTo(BEGIN_OPTION)
         assertThat(entry.affiliatedDomain).isNull()
         assertThat(entry.entryGroupId).isEqualTo(USERNAME)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     @Test
@@ -260,16 +279,19 @@
             PasswordCredentialEntry.Builder(mContext, USERNAME, mPendingIntent, BEGIN_OPTION)
                 .setAffiliatedDomain(null)
                 .build()
+
         assertThat(entry.affiliatedDomain).isNull()
     }
 
     @Test
     fun builder_setAffiliatedDomainNonNull_retrieveNonNullAffiliatedDomain() {
         val expectedAffiliatedDomain = "name"
+
         val entry =
             PasswordCredentialEntry.Builder(mContext, USERNAME, mPendingIntent, BEGIN_OPTION)
                 .setAffiliatedDomain(expectedAffiliatedDomain)
                 .build()
+
         assertThat(entry.affiliatedDomain).isEqualTo(expectedAffiliatedDomain)
     }
 
@@ -304,18 +326,34 @@
     }
 
     private fun constructEntryWithAllParams(): PasswordCredentialEntry {
-        return PasswordCredentialEntry(
-            mContext,
-            USERNAME,
-            mPendingIntent,
-            BEGIN_OPTION,
-            DISPLAYNAME,
-            LAST_USED_TIME,
-            ICON,
-            IS_AUTO_SELECT_ALLOWED,
-            AFFILIATED_DOMAIN,
-            SINGLE_PROVIDER_ICON_BIT
-        )
+        return if (BuildCompat.isAtLeastV()) {
+            PasswordCredentialEntry(
+                mContext,
+                USERNAME,
+                mPendingIntent,
+                BEGIN_OPTION,
+                DISPLAYNAME,
+                LAST_USED_TIME,
+                ICON,
+                IS_AUTO_SELECT_ALLOWED,
+                AFFILIATED_DOMAIN,
+                SINGLE_PROVIDER_ICON_BIT,
+                testBiometricPromptData(),
+            )
+        } else {
+            PasswordCredentialEntry(
+                mContext,
+                USERNAME,
+                mPendingIntent,
+                BEGIN_OPTION,
+                DISPLAYNAME,
+                LAST_USED_TIME,
+                ICON,
+                IS_AUTO_SELECT_ALLOWED,
+                AFFILIATED_DOMAIN,
+                SINGLE_PROVIDER_ICON_BIT,
+            )
+        }
     }
 
     private fun assertEntryWithRequiredParamsOnly(entry: PasswordCredentialEntry) {
@@ -325,6 +363,7 @@
         assertThat(entry.isDefaultIconPreferredAsSingleProvider)
             .isEqualTo(DEFAULT_SINGLE_PROVIDER_ICON_BIT)
         assertThat(entry.entryGroupId).isEqualTo(USERNAME)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     private fun assertEntryWithAllParams(entry: PasswordCredentialEntry) {
@@ -341,6 +380,13 @@
         assertThat(entry.affiliatedDomain).isEqualTo(AFFILIATED_DOMAIN)
         assertThat(entry.isDefaultIconPreferredAsSingleProvider).isEqualTo(SINGLE_PROVIDER_ICON_BIT)
         assertThat(entry.entryGroupId).isEqualTo(USERNAME)
+        if (BuildCompat.isAtLeastV()) {
+            // TODO(b/325469910) : Add cryptoObject tests once opId is retrievable
+            assertThat(entry.biometricPromptData!!.allowedAuthenticators)
+                .isEqualTo(testBiometricPromptData().allowedAuthenticators)
+        } else {
+            assertThat(entry.biometricPromptData).isNull()
+        }
     }
 
     companion object {
@@ -355,5 +401,13 @@
         private val AFFILIATED_DOMAIN = "affiliation-name"
         private const val DEFAULT_SINGLE_PROVIDER_ICON_BIT = false
         private const val SINGLE_PROVIDER_ICON_BIT = true
+
+        @RequiresApi(35)
+        private fun testBiometricPromptData(): BiometricPromptData {
+            return BiometricPromptData.Builder()
+                .setCryptoObject(BiometricPrompt.CryptoObject(NullCipher()))
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build()
+        }
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryJavaTest.java
index 43cbd13..fff3b7e 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryJavaTest.java
@@ -25,17 +25,21 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 
+import androidx.annotation.RequiresApi;
+import androidx.biometric.BiometricManager;
+import androidx.biometric.BiometricPrompt;
+import androidx.core.os.BuildCompat;
 import androidx.credentials.PublicKeyCredential;
 import androidx.credentials.R;
 import androidx.credentials.TestUtilsKt;
 import androidx.credentials.provider.BeginGetPublicKeyCredentialOption;
+import androidx.credentials.provider.BiometricPromptData;
 import androidx.credentials.provider.PublicKeyCredentialEntry;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -46,8 +50,11 @@
 import org.junit.runner.RunWith;
 
 import java.time.Instant;
+
+import javax.crypto.NullCipher;
+
 @RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @SmallTest
 public class PublicKeyCredentialEntryJavaTest {
     private static final CharSequence USERNAME = "title";
@@ -64,8 +71,10 @@
                     "{\"key1\":{\"key2\":{\"key3\":\"value3\"}}}");
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private final Intent mIntent = new Intent();
-    private final PendingIntent mPendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+    private final PendingIntent mPendingIntent = PendingIntent.getActivity(mContext, 0,
+            mIntent,
             PendingIntent.FLAG_IMMUTABLE);
+
     @Test
     public void build_requiredParamsOnly_success() {
         PublicKeyCredentialEntry entry = constructWithRequiredParamsOnly();
@@ -139,9 +148,10 @@
     }
     @Test
     @SdkSuppress(minSdkVersion = 34)
+    @SuppressWarnings("deprecation")
     public void fromCredentialEntry_success() {
         PublicKeyCredentialEntry originalEntry = constructWithAllParams();
-        Slice slice = PublicKeyCredentialEntry.toSlice(originalEntry);
+        android.app.slice.Slice slice = PublicKeyCredentialEntry.toSlice(originalEntry);
         assertNotNull(slice);
         PublicKeyCredentialEntry entry = PublicKeyCredentialEntry.fromCredentialEntry(
                 new android.service.credentials.CredentialEntry("id", slice));
@@ -160,10 +170,11 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void isDefaultIcon_noIconSetFromSlice_returnsTrue() {
         PublicKeyCredentialEntry entry = new PublicKeyCredentialEntry
                 .Builder(mContext, USERNAME, mPendingIntent, mBeginOption).build();
-        Slice slice = PublicKeyCredentialEntry.toSlice(entry);
+        android.app.slice.Slice slice = PublicKeyCredentialEntry.toSlice(entry);
 
         assertNotNull(slice);
 
@@ -175,11 +186,12 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 28)
+    @SuppressWarnings("deprecation")
     public void isDefaultIcon_customIconAfterSlice_returnsFalse() {
         PublicKeyCredentialEntry entry = new PublicKeyCredentialEntry
                 .Builder(mContext, USERNAME, mPendingIntent, mBeginOption)
                 .setIcon(ICON).build();
-        Slice slice = PublicKeyCredentialEntry.toSlice(entry);
+        android.app.slice.Slice slice = PublicKeyCredentialEntry.toSlice(entry);
 
         assertNotNull(slice);
 
@@ -222,10 +234,15 @@
                 mBeginOption).build();
     }
     private PublicKeyCredentialEntry constructWithAllParams() {
-        return new PublicKeyCredentialEntry.Builder(mContext, USERNAME, mPendingIntent,
+        PublicKeyCredentialEntry.Builder testBuilder = new PublicKeyCredentialEntry
+                .Builder(mContext, USERNAME, mPendingIntent,
                 mBeginOption).setAutoSelectAllowed(IS_AUTO_SELECT_ALLOWED).setDisplayName(
                 DISPLAYNAME).setLastUsedTime(Instant.ofEpochMilli(LAST_USED_TIME)).setIcon(
-                ICON).setDefaultIconPreferredAsSingleProvider(SINGLE_PROVIDER_ICON_BIT).build();
+                ICON).setDefaultIconPreferredAsSingleProvider(SINGLE_PROVIDER_ICON_BIT);
+        if (BuildCompat.isAtLeastV()) {
+            testBuilder.setBiometricPromptData(testBiometricPromptData());
+        }
+        return testBuilder.build();
     }
     private void assertEntryWithRequiredParams(PublicKeyCredentialEntry entry) {
         assertThat(USERNAME.equals(entry.getUsername()));
@@ -234,6 +251,7 @@
                 DEFAULT_SINGLE_PROVIDER_ICON_BIT);
         assertThat(entry.getAffiliatedDomain()).isNull();
         assertThat(entry.getEntryGroupId()).isEqualTo(USERNAME);
+        assertThat(entry.getBiometricPromptData()).isNull();
     }
     private void assertEntryWithAllParams(PublicKeyCredentialEntry entry) {
         assertThat(USERNAME.equals(entry.getUsername()));
@@ -247,5 +265,19 @@
                 SINGLE_PROVIDER_ICON_BIT);
         assertThat(entry.getAffiliatedDomain()).isNull();
         assertThat(entry.getEntryGroupId()).isEqualTo(USERNAME);
+        if (BuildCompat.isAtLeastV() && entry.getBiometricPromptData() != null) {
+            assertThat(entry.getBiometricPromptData().getAllowedAuthenticators()).isEqualTo(
+                    testBiometricPromptData().getAllowedAuthenticators());
+        } else {
+            assertThat(entry.getBiometricPromptData()).isNull();
+        }
+    }
+
+    @RequiresApi(35)
+    private static BiometricPromptData testBiometricPromptData() {
+        return new BiometricPromptData.Builder()
+                .setCryptoObject(new BiometricPrompt.CryptoObject(new NullCipher()))
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build();
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryTest.kt
index ed23e72..d4e521e 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/PublicKeyCredentialEntryTest.kt
@@ -22,11 +22,16 @@
 import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.service.credentials.CredentialEntry
+import androidx.annotation.RequiresApi
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricPrompt
+import androidx.core.os.BuildCompat
 import androidx.credentials.CredentialOption
 import androidx.credentials.PublicKeyCredential
 import androidx.credentials.R
 import androidx.credentials.equals
 import androidx.credentials.provider.BeginGetPublicKeyCredentialOption
+import androidx.credentials.provider.BiometricPromptData
 import androidx.credentials.provider.PublicKeyCredentialEntry
 import androidx.credentials.provider.PublicKeyCredentialEntry.Companion.fromCredentialEntry
 import androidx.credentials.provider.PublicKeyCredentialEntry.Companion.fromSlice
@@ -37,13 +42,14 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import javax.crypto.NullCipher
 import junit.framework.TestCase.assertNotNull
 import org.junit.Assert
 import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@SdkSuppress(minSdkVersion = 26)
+@SdkSuppress(minSdkVersion = 26) // Instant usage
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class PublicKeyCredentialEntryTest {
@@ -143,6 +149,7 @@
         assertThat(entry.beginGetCredentialOption).isEqualTo(BEGIN_OPTION)
         assertThat(entry.affiliatedDomain).isNull()
         assertThat(entry.entryGroupId).isEqualTo(USERNAME)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     @Test
@@ -247,17 +254,32 @@
     }
 
     private fun constructWithAllParams(): PublicKeyCredentialEntry {
-        return PublicKeyCredentialEntry(
-            mContext,
-            USERNAME,
-            mPendingIntent,
-            BEGIN_OPTION,
-            DISPLAYNAME,
-            Instant.ofEpochMilli(LAST_USED_TIME),
-            ICON,
-            IS_AUTO_SELECT_ALLOWED,
-            SINGLE_PROVIDER_ICON_BIT
-        )
+        return if (BuildCompat.isAtLeastV()) {
+            PublicKeyCredentialEntry(
+                mContext,
+                USERNAME,
+                mPendingIntent,
+                BEGIN_OPTION,
+                DISPLAYNAME,
+                Instant.ofEpochMilli(LAST_USED_TIME),
+                ICON,
+                IS_AUTO_SELECT_ALLOWED,
+                SINGLE_PROVIDER_ICON_BIT,
+                testBiometricPromptData()
+            )
+        } else {
+            PublicKeyCredentialEntry(
+                mContext,
+                USERNAME,
+                mPendingIntent,
+                BEGIN_OPTION,
+                DISPLAYNAME,
+                Instant.ofEpochMilli(LAST_USED_TIME),
+                ICON,
+                IS_AUTO_SELECT_ALLOWED,
+                SINGLE_PROVIDER_ICON_BIT,
+            )
+        }
     }
 
     private fun assertEntryWithRequiredParams(entry: PublicKeyCredentialEntry) {
@@ -267,6 +289,7 @@
             .isEqualTo(DEFAULT_SINGLE_PROVIDER_ICON_BIT)
         assertThat(entry.affiliatedDomain).isNull()
         assertThat(entry.entryGroupId).isEqualTo(USERNAME)
+        assertThat(entry.biometricPromptData).isNull()
     }
 
     private fun assertEntryWithAllParams(entry: PublicKeyCredentialEntry) {
@@ -280,6 +303,12 @@
         assertThat(entry.isDefaultIconPreferredAsSingleProvider).isEqualTo(SINGLE_PROVIDER_ICON_BIT)
         assertThat(entry.affiliatedDomain).isNull()
         assertThat(entry.entryGroupId).isEqualTo(USERNAME)
+        if (BuildCompat.isAtLeastV() && entry.biometricPromptData != null) {
+            assertThat(entry.biometricPromptData!!.allowedAuthenticators)
+                .isEqualTo(testBiometricPromptData().allowedAuthenticators)
+        } else {
+            assertThat(entry.biometricPromptData).isNull()
+        }
     }
 
     companion object {
@@ -298,5 +327,13 @@
         private const val IS_AUTO_SELECT_ALLOWED = true
         private const val DEFAULT_SINGLE_PROVIDER_ICON_BIT = false
         private const val SINGLE_PROVIDER_ICON_BIT = true
+
+        @RequiresApi(35)
+        private fun testBiometricPromptData(): BiometricPromptData {
+            return BiometricPromptData.Builder()
+                .setCryptoObject(BiometricPrompt.CryptoObject(NullCipher()))
+                .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+                .build()
+        }
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryJavaTest.java
index 8a44a88..fe81ce4 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/ui/RemoteEntryJavaTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertThrows;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
 import android.content.Context;
 import android.content.Intent;
 
@@ -80,9 +79,10 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 34)
+    @SuppressWarnings("deprecation")
     public void fromRemoteEntry_success() {
         RemoteEntry originalEntry = new RemoteEntry(mPendingIntent);
-        Slice slice = RemoteEntry.toSlice(originalEntry);
+        android.app.slice.Slice slice = RemoteEntry.toSlice(originalEntry);
         assertNotNull(slice);
 
         RemoteEntry remoteEntry = RemoteEntry.fromRemoteEntry(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/ClearCredentialRequestTypes.kt b/credentials/credentials/src/main/java/androidx/credentials/ClearCredentialRequestTypes.kt
new file mode 100644
index 0000000..e2c122c
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/ClearCredentialRequestTypes.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import androidx.credentials.ClearCredentialRequestTypes.Companion.CLEAR_CREDENTIAL_STATE
+import androidx.credentials.ClearCredentialRequestTypes.Companion.CLEAR_RESTORE_CREDENTIAL
+
+/**
+ * This allows verification when the user passes in the request type for their
+ * [ClearCredentialStateRequest].
+ *
+ * If the request type is [ClearCredentialRequestTypes.CLEAR_CREDENTIAL_STATE], then the request
+ * will be sent to the credential providers to clear the user's credential state.
+ *
+ * If the request type is [ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL], then the request
+ * will be sent to the restore credential provider to delete any stored [RestoreCredential].
+ */
+@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(value = [CLEAR_CREDENTIAL_STATE, CLEAR_RESTORE_CREDENTIAL])
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+annotation class ClearCredentialRequestTypes {
+    companion object {
+        /** Clears credential state from the credential providers */
+        const val CLEAR_CREDENTIAL_STATE = 0
+        /** Clears restore credential stored on device as well as cloud. */
+        const val CLEAR_RESTORE_CREDENTIAL = 1
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/ClearCredentialStateRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/ClearCredentialStateRequest.kt
index a72d2c3..2eec3ab 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/ClearCredentialStateRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/ClearCredentialStateRequest.kt
@@ -16,5 +16,38 @@
 
 package androidx.credentials
 
-/** Request class for clearing a user's credential state from the credential providers. */
-class ClearCredentialStateRequest
+import android.os.Bundle
+
+/**
+ * Request class for clearing a user's credential state from the credential providers.
+ *
+ * If the request type is [ClearCredentialRequestTypes.CLEAR_CREDENTIAL_STATE], then the request
+ * will be sent to the credential providers to clear the user's credential state.
+ *
+ * If the request type is [ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL], then the request
+ * will be sent to the restore credential provider to delete any stored [RestoreCredential].
+ *
+ * @throws IllegalArgumentException if the [requestType] is unsupported type.
+ */
+class ClearCredentialStateRequest(val requestType: @ClearCredentialRequestTypes Int) {
+    constructor() : this(ClearCredentialRequestTypes.CLEAR_CREDENTIAL_STATE)
+
+    val requestBundle: Bundle = Bundle()
+
+    init {
+        if (
+            requestType != ClearCredentialRequestTypes.CLEAR_CREDENTIAL_STATE &&
+                requestType != ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL
+        ) {
+            throw IllegalArgumentException("The request type $requestType is not supported.")
+        }
+        if (requestType == ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL) {
+            requestBundle.putBoolean(BUNDLE_KEY_CLEAR_RESTORE_CREDENTIAL_REQUEST, true)
+        }
+    }
+
+    private companion object {
+        private const val BUNDLE_KEY_CLEAR_RESTORE_CREDENTIAL_REQUEST =
+            "androidx.credentials.BUNDLE_KEY_CLEAR_RESTORE_CREDENTIAL_REQUEST"
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateRestoreCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateRestoreCredentialRequest.kt
new file mode 100644
index 0000000..686ab729
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateRestoreCredentialRequest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import android.os.Bundle
+import androidx.credentials.exceptions.restorecredential.CreateRestoreCredentialDomException
+import androidx.credentials.exceptions.restorecredential.E2eeUnavailableException
+import org.json.JSONObject
+
+/**
+ * A request to create a restore credential used for app restore purpose.
+ * [App restore feature](https://www.android.com/transfer-data-android-to-android/) is a feature
+ * that allows users to copy an app and its data to a new Android device.
+ *
+ * If the [isCloudBackupEnabled] is true, the restore credential service will periodically backup
+ * the restore credential to cloud. In order to use this setting, the user device must enable backup
+ * and have end-to-end-encryption enabled, such as screen lock. More about cloud backup can be found
+ * [here](https://developer.android.com/identity/data/backup). Cloud backup is supported on Android
+ * 2.2 and above. If the cloud backup is not enabled, catch the [E2eeUnavailableException] and retry
+ * without cloud backup.
+ *
+ * @param requestJson the request in JSON format in the standard webauthn web json
+ * @param isCloudBackupEnabled whether the credential should be backed up to cloud.
+ * @throws E2eeUnavailableException if [isCloudBackupEnabled] was requested but the user device did
+ *   not enable backup or e2ee (screen lock).
+ * @throws CreateRestoreCredentialDomException if the requestJson is an invalid Json that does not
+ *   follow the standard webauthn web json format
+ */
+class CreateRestoreCredentialRequest(
+    val requestJson: String,
+    val isCloudBackupEnabled: Boolean,
+) :
+    CreateCredentialRequest(
+        type = RestoreCredential.TYPE_RESTORE_CREDENTIAL,
+        credentialData = toCredentialDataBundle(requestJson, isCloudBackupEnabled),
+        isSystemProviderRequired = false,
+        displayInfo = getDisplayInfoFromJson(requestJson),
+        origin = null,
+        preferImmediatelyAvailableCredentials = false,
+        isAutoSelectAllowed = false,
+        candidateQueryData = Bundle()
+    ) {
+    companion object {
+        private const val BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_REQUEST =
+            "androidx.credentials.BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_REQUEST"
+        private const val BUNDLE_KEY_SHOULD_BACKUP_TO_CLOUD =
+            "androidx.credentials.BUNDLE_KEY_SHOULD_BACKUP_TO_CLOUD"
+
+        private fun getDisplayInfoFromJson(requestJson: String): DisplayInfo {
+            val json = JSONObject(requestJson)
+            val userJson = json.getJSONObject("user")
+            return DisplayInfo(userJson.getString("id"))
+        }
+
+        private fun toCredentialDataBundle(
+            requestJson: String,
+            isCloudBackupEnabled: Boolean
+        ): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_REQUEST, requestJson)
+            bundle.putBoolean(BUNDLE_KEY_SHOULD_BACKUP_TO_CLOUD, isCloudBackupEnabled)
+            return bundle
+        }
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateRestoreCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateRestoreCredentialResponse.kt
new file mode 100644
index 0000000..dcf9f2f
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateRestoreCredentialResponse.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import android.os.Bundle
+import androidx.credentials.exceptions.CreateCredentialUnknownException
+
+/**
+ * A response of the [RestoreCredential] flow.
+ *
+ * @property responseJson the public key credential registration response in JSON format.
+ */
+class CreateRestoreCredentialResponse(
+    val responseJson: String,
+    data: Bundle,
+) : CreateCredentialResponse(RestoreCredential.TYPE_RESTORE_CREDENTIAL, data) {
+    companion object {
+        const val BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_RESPONSE =
+            "androidx.credentials.BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_RESPONSE"
+
+        @JvmStatic
+        fun createFrom(data: Bundle): CreateRestoreCredentialResponse {
+            val responseJson =
+                data.getString(BUNDLE_KEY_CREATE_RESTORE_CREDENTIAL_RESPONSE)
+                    ?: throw CreateCredentialUnknownException(
+                        "The response bundle did not contain the response data. This should not happen."
+                    )
+            return CreateRestoreCredentialResponse(responseJson, data)
+        }
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
index 5911e11..77bfdc7 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/Credential.kt
@@ -34,9 +34,9 @@
     val type: String,
     val data: Bundle,
 ) {
-    internal companion object {
+    companion object {
         @JvmStatic
-        @RestrictTo(RestrictTo.Scope.LIBRARY) // used from java tests
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // used from java tests
         fun createFrom(type: String, data: Bundle): Credential {
             return try {
                 when (type) {
@@ -44,6 +44,7 @@
                         PasswordCredential.createFrom(data)
                     PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
                         PublicKeyCredential.createFrom(data)
+                    RestoreCredential.TYPE_RESTORE_CREDENTIAL -> RestoreCredential.createFrom(data)
                     else -> throw FrameworkClassParsingException()
                 }
             } catch (e: FrameworkClassParsingException) {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt
index 0fc653a..1c910c3 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialManager.kt
@@ -299,6 +299,9 @@
      * app and in order to get the holistic sign-in options the next time, you should call this API
      * to let the provider clear any stored credential session.
      *
+     * If the API is called with [ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL] then any
+     * restore credential stored on device will be cleared.
+     *
      * @param request the request for clearing the app user's credential state
      * @throws ClearCredentialException If the request fails
      */
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerImpl.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerImpl.kt
index 6f1353f..6967280 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerImpl.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerImpl.kt
@@ -117,7 +117,7 @@
         callback: CredentialManagerCallback<GetCredentialResponse, GetCredentialException>,
     ) {
         val provider: CredentialProvider? =
-            CredentialProviderFactory(context).getBestAvailableProvider()
+            CredentialProviderFactory(context).getBestAvailableProvider(request)
         if (provider == null) {
             callback.onError(
                 GetCredentialProviderConfigurationException(
@@ -236,7 +236,7 @@
         callback: CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>,
     ) {
         val provider: CredentialProvider? =
-            CredentialProviderFactory(this.context).getBestAvailableProvider()
+            CredentialProviderFactory(this.context).getBestAvailableProvider(request)
         if (provider == null) {
             callback.onError(
                 CreateCredentialProviderConfigurationException(
@@ -263,6 +263,9 @@
      * app and in order to get the holistic sign-in options the next time, you should call this API
      * to let the provider clear any stored credential session.
      *
+     * If the API is called with [ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL] then any
+     * restore credential stored on device will be cleared.
+     *
      * @param request the request for clearing the app user's credential state
      * @param cancellationSignal an optional signal that allows for cancelling this call
      * @param executor the callback will take place on this executor
@@ -275,7 +278,7 @@
         callback: CredentialManagerCallback<Void?, ClearCredentialException>,
     ) {
         val provider: CredentialProvider? =
-            CredentialProviderFactory(context).getBestAvailableProvider()
+            CredentialProviderFactory(context).getBestAvailableProvider(request.requestType)
         if (provider == null) {
             callback.onError(
                 ClearCredentialProviderConfigurationException(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerViewHandler.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerViewHandler.kt
new file mode 100644
index 0000000..2635f98
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialManagerViewHandler.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("CredentialManagerViewHandler")
+
+package androidx.credentials
+
+import android.os.Build
+import android.os.OutcomeReceiver
+import android.util.Log
+import android.view.View
+import androidx.annotation.RequiresApi
+import androidx.credentials.internal.FrameworkImplHelper
+
+/**
+ * An extension API to the [View] class that allows setting of a [PendingGetCredentialRequest], that
+ * in-turn contains a [GetCredentialRequest], and a callback to deliver the final
+ * [GetCredentialResponse]. The associated request is invoked when the [View] is focused/clicked by
+ * the user.
+ *
+ * A typical scenario for setting this request is a login screen with a username & password field.
+ * We recommend calling the [CredentialManager.getCredential] API when this login screen loads, so
+ * that the user can be presented with a selector with all credential options to choose from. In
+ * addition, we recommend using this API to set the same [GetCredentialRequest] that is passed to
+ * [CredentialManager.getCredential], on the username & password views. With that, if the user
+ * dismisses the initial selector, and then taps on either the username or the password field, they
+ * would see the same suggestions that they saw on the selector, but now on fallback UI experiences
+ * such as keyboard suggestions or drop-down lists, depending on the device capabilities.
+ *
+ * If you have multiple views on the screen that should invoke different requests as opposed to the
+ * same, you can simply use this API to set different requests on corresponding views, and hence a
+ * different set of suggestions will appear.
+ *
+ * Note that no errors are propagated to the [PendingGetCredentialRequest.callback]. In a scenario
+ * where multiple suggestions are presented to the user as part of the keyboard suggestions for
+ * instance, it is possible that the user selects one, but the flow ends up in an error state, due
+ * to which the final [GetCredentialResponse] cannot be propagated. In that case, user will be taken
+ * back to the suggestions, and can very well select a different suggestion which would this time
+ * result in a success. The intermediate error states are not propagated to the developer, and only
+ * a final response, if any, is propagated.
+ *
+ * @property pendingGetCredentialRequest the [GetCredentialRequest] and the associated callback to
+ *   be set on the view, and to be exercised when user focused on the view in question
+ */
+private const val TAG = "ViewHandler"
+
+@Suppress("NewApi")
+var View.pendingGetCredentialRequest: PendingGetCredentialRequest?
+    get() =
+        getTag(R.id.androidx_credential_pendingCredentialRequest) as? PendingGetCredentialRequest
+    set(value) {
+        setTag(R.id.androidx_credential_pendingCredentialRequest, value)
+        if (value != null) {
+            if (
+                Build.VERSION.SDK_INT >= 35 ||
+                    (Build.VERSION.SDK_INT == 34 && Build.VERSION.PREVIEW_SDK_INT > 0)
+            ) {
+                Api35Impl.setPendingGetCredentialRequest(this, value.request, value.callback)
+            }
+        } else {
+            if (
+                Build.VERSION.SDK_INT >= 35 ||
+                    (Build.VERSION.SDK_INT == 34 && Build.VERSION.PREVIEW_SDK_INT > 0)
+            ) {
+                Api35Impl.clearPendingGetCredentialRequest(this)
+            }
+        }
+    }
+
+@RequiresApi(35)
+private object Api35Impl {
+    fun setPendingGetCredentialRequest(
+        view: View,
+        request: GetCredentialRequest,
+        callback: (GetCredentialResponse) -> Unit,
+    ) {
+        val frameworkRequest = FrameworkImplHelper.convertGetRequestToFrameworkClass(request)
+        val frameworkCallback =
+            object :
+                OutcomeReceiver<
+                    android.credentials.GetCredentialResponse,
+                    android.credentials.GetCredentialException
+                > {
+                override fun onResult(response: android.credentials.GetCredentialResponse) {
+                    callback.invoke(FrameworkImplHelper.convertGetResponseToJetpackClass(response))
+                }
+
+                override fun onError(error: android.credentials.GetCredentialException) {
+                    Log.w(TAG, "Error: " + error.type + " , " + error.message)
+                }
+            }
+        view.setPendingCredentialRequest(frameworkRequest, frameworkCallback)
+    }
+
+    fun clearPendingGetCredentialRequest(view: View) {
+        view.clearPendingCredentialRequest()
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
index bedcc62..ad97445 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
@@ -57,6 +57,35 @@
     }
 
     /**
+     * Returns the best available provider. The best available provider is determined by the
+     * provided [request]. If the provided request is for the use-case of [RestoreCredential], then
+     * the pre-U provider is used. If not, then the provider is determined by the API level.
+     *
+     * @param request is a credential request of either [CreateRestoreCredentialRequest],
+     *   [ClearCredentialRequestTypes.ClearRestoreCredential], or [GetCredentialRequest] that can
+     *   determine [CredentialProvider] type.
+     * @return the best available provider, or null if no provider is available.
+     */
+    fun getBestAvailableProvider(
+        request: Any,
+        shouldFallbackToPreU: Boolean = true
+    ): CredentialProvider? {
+        if (
+            request is CreateRestoreCredentialRequest ||
+                request == ClearCredentialRequestTypes.CLEAR_RESTORE_CREDENTIAL
+        ) {
+            return tryCreatePreUOemProvider()
+        } else if (request is GetCredentialRequest) {
+            for (option in request.credentialOptions) {
+                if (option is GetRestoreCredentialOption) {
+                    return tryCreatePreUOemProvider()
+                }
+            }
+        }
+        return getBestAvailableProvider(shouldFallbackToPreU)
+    }
+
+    /**
      * Returns the best available provider. Pre-U, the provider is determined by the provider
      * library that the developer includes in the app. Developer must not add more than one provider
      * library. Post-U, providers will be registered with the framework, and enabled by the user.
@@ -148,7 +177,7 @@
 
         val classNames = mutableListOf<String>()
         if (packageInfo.services != null) {
-            for (serviceInfo in packageInfo.services) {
+            for (serviceInfo in packageInfo.services!!) {
                 if (serviceInfo.metaData != null) {
                     val className = serviceInfo.metaData.getString(CREDENTIAL_PROVIDER_KEY)
                     if (className != null) {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt
index 63b889c..786acbb 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetCredentialRequest.kt
@@ -58,7 +58,9 @@
  * @property preferImmediatelyAvailableCredentials true if you prefer the operation to return
  *   immediately when there is no available credentials instead of falling back to discovering
  *   remote options, and false (default) otherwise
- * @throws IllegalArgumentException If [credentialOptions] is empty
+ * @throws IllegalArgumentException If [credentialOptions] is empty or contains
+ *   [GetRestoreCredentialOption] with another option (i.e. [GetPasswordOption] or
+ *   [GetPublicKeyCredentialOption]).
  */
 class GetCredentialRequest
 @JvmOverloads
@@ -73,6 +75,15 @@
 
     init {
         require(credentialOptions.isNotEmpty()) { "credentialOptions should not be empty" }
+        if (credentialOptions.size > 1) {
+            for (option in credentialOptions) {
+                if (option is GetRestoreCredentialOption) {
+                    throw IllegalArgumentException(
+                        "Only a single GetRestoreCredentialOption should be provided."
+                    )
+                }
+            }
+        }
     }
 
     /** A builder for [GetCredentialRequest]. */
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetRestoreCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetRestoreCredentialOption.kt
new file mode 100644
index 0000000..feabf25
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetRestoreCredentialOption.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import android.os.Bundle
+import androidx.credentials.exceptions.NoCredentialException
+
+/**
+ * A request to get the restore credential from the restore credential provider.
+ *
+ * @property requestJson the request in JSON format in the standard webauthn web json
+ *   (https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptionsjson).
+ *
+ * Note that the userVerification field of the requestJson will always be overridden to
+ * `discouraged` to support passive authentication during restore flow.
+ *
+ * [GetRestoreCredentialOption] cannot be requested with other credential options because of
+ * conflicting user experience. When requesting restore credential, only a single
+ * [GetRestoreCredentialOption] must be supplied.
+ *
+ * @throws IllegalArgumentException if the requestJson is an invalid Json that does not follow the
+ *   standard webauthn web json format
+ * @throws NoCredentialException if no viable restore credential is found
+ * @throws IllegalArgumentException if the option is mixed with another [CredentialOption]
+ */
+class GetRestoreCredentialOption(val requestJson: String) :
+    CredentialOption(
+        type = RestoreCredential.TYPE_RESTORE_CREDENTIAL,
+        requestData = toRequestDataBundle(requestJson),
+        candidateQueryData = Bundle(),
+        isSystemProviderRequired = false,
+        isAutoSelectAllowed = false,
+        allowedProviders = emptySet(),
+        typePriorityHint = PRIORITY_DEFAULT,
+    ) {
+    private companion object {
+        private const val BUNDLE_KEY_GET_RESTORE_CREDENTIAL_REQUEST =
+            "androidx.credentials.BUNDLE_KEY_GET_RESTORE_CREDENTIAL_REQUEST"
+
+        private fun toRequestDataBundle(requestJson: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_GET_RESTORE_CREDENTIAL_REQUEST, requestJson)
+            return bundle
+        }
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PendingGetCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/PendingGetCredentialRequest.kt
new file mode 100644
index 0000000..ae97214
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/PendingGetCredentialRequest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import android.view.View
+
+/**
+ * Request to be set on an Android [View], which will be invoked when the [View] is focused/clicked
+ * by the user.
+ *
+ * Note that the [callback] only handles a final [GetCredentialResponse] and no errors are
+ * propagated to the callback.
+ *
+ * See [View.setPendingCredentialRequest] for details on how this request will be used.
+ *
+ * @property request the [GetCredentialRequest] to be invoked when a given view on which this
+ *   request is set is focused
+ * @property callback the callback on which the final [GetCredentialResponse] is returned, after the
+ *   user has made its selections
+ */
+class PendingGetCredentialRequest(
+    val request: GetCredentialRequest,
+    val callback: (GetCredentialResponse) -> Unit
+)
diff --git a/credentials/credentials/src/main/java/androidx/credentials/RestoreCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/RestoreCredential.kt
new file mode 100644
index 0000000..be80e65
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/RestoreCredential.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials
+
+import android.os.Bundle
+import androidx.credentials.exceptions.NoCredentialException
+import androidx.credentials.internal.RequestValidationHelper
+
+/**
+ * Represents the user's restore credential for the app sign in. The restore credential is used to
+ * restore the user's credential from the previous device to a new Android device.
+ *
+ * By creating a [RestoreCredential] for the user, the credential will be automatically transferred
+ * over to the user's new device if the user selects the app to be transferred from the old device
+ * during the setup stage.
+ *
+ * The [RestoreCredential] can only be used for apps that the users have selected to be restored.
+ * The [RestoreCredential] for all other apps will not be transferred over from the old device. This
+ * Credential can be used to create a seamless authentication user experience by providing a 0-tap
+ * sign-in experience. If the [RestoreCredential] is available for an app, the user can be signed in
+ * programmatically without the user's input.
+ *
+ * @param authenticationResponseJson the request the public key credential authentication response
+ *   in JSON format that follows the standard webauthn json format shown at
+ *   (https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson)
+ * @throws IllegalArgumentException If [authenticationResponseJson] is empty, or if it is not a
+ *   valid JSON
+ * @see CreateRestoreCredentialRequest on how to create a [RestoreCredential] instance.
+ */
+class RestoreCredential private constructor(val authenticationResponseJson: String, data: Bundle) :
+    Credential(TYPE_RESTORE_CREDENTIAL, data) {
+
+    init {
+        require(RequestValidationHelper.isValidJSON(authenticationResponseJson)) {
+            "authenticationResponseJson must not be empty, and must be a valid JSON"
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        internal fun createFrom(data: Bundle): RestoreCredential {
+            val responseJson =
+                data.getString(BUNDLE_KEY_GET_RESTORE_CREDENTIAL_RESPONSE)
+                    ?: throw NoCredentialException(
+                        "The device does not contain a restore credential."
+                    )
+            return RestoreCredential(responseJson, data)
+        }
+
+        /** The type value for restore credential related operations. */
+        const val TYPE_RESTORE_CREDENTIAL: String = "androidx.credentials.TYPE_RESTORE_CREDENTIAL"
+        private const val BUNDLE_KEY_GET_RESTORE_CREDENTIAL_RESPONSE =
+            "androidx.credentials.BUNDLE_KEY_GET_RESTORE_CREDENTIAL_RESPONSE"
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/restorecredential/CreateRestoreCredentialDomException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/restorecredential/CreateRestoreCredentialDomException.kt
new file mode 100644
index 0000000..5fc63b7
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/restorecredential/CreateRestoreCredentialDomException.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.exceptions.restorecredential
+
+import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.exceptions.domerrors.DomError
+import androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.Companion.SEPARATOR
+
+/**
+ * During the create restore credential flow, this is thrown when a DOM Exception is thrown,
+ * indicating the operation contains a DOMException error type. The fido spec can be found
+ * [here](https://webidl.spec.whatwg.org/#idl-DOMException-error-names). The full list of
+ * implemented DOMErrors extends from and can be seen at [DomError].
+ *
+ * @property domError the specific error from the DOMException types defined in the fido spec found
+ *   [here](https://webidl.spec.whatwg.org/#idl-DOMException-error-names)
+ */
+class CreateRestoreCredentialDomException(val domError: DomError, errorMessage: CharSequence) :
+    CreateCredentialException(
+        TYPE_CREATE_RESTORE_CREDENTIAL_DOM_EXCEPTION + SEPARATOR + domError.type,
+        errorMessage
+    ) {
+    internal companion object {
+        internal const val TYPE_CREATE_RESTORE_CREDENTIAL_DOM_EXCEPTION =
+            "androidx.credentials.TYPE_CREATE_RESTORE_CREDENTIAL_DOM_EXCEPTION"
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/restorecredential/E2eeUnavailableException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/restorecredential/E2eeUnavailableException.kt
new file mode 100644
index 0000000..eb02c67
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/restorecredential/E2eeUnavailableException.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.exceptions.restorecredential
+
+import androidx.credentials.exceptions.CreateCredentialException
+
+/**
+ * During the Restore Credential creation, this is thrown when the developer requests backup to
+ * cloud but the user device did not enable end-to-end-encryption or backup.
+ */
+class E2eeUnavailableException(errorMessage: CharSequence) :
+    CreateCredentialException(TYPE_E2EE_UNAVAILABLE_EXCEPTION, errorMessage) {
+    internal companion object {
+        internal const val TYPE_E2EE_UNAVAILABLE_EXCEPTION =
+            "androidx.credentials.TYPE_E2EE_UNAVAILABLE_EXCEPTION"
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt b/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt
index 9800eff..a70417d 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/internal/FrameworkImplHelper.kt
@@ -16,23 +16,28 @@
 
 package androidx.credentials.internal
 
+import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.drawable.Icon
+import android.os.Build
 import android.os.Bundle
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
 import androidx.credentials.CreateCredentialRequest
 import androidx.credentials.CreatePasswordRequest
 import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.Credential
+import androidx.credentials.GetCredentialRequest
+import androidx.credentials.GetCredentialResponse
 import androidx.credentials.R
 
-@RequiresApi(23)
-internal class FrameworkImplHelper {
+@RequiresApi(34)
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class FrameworkImplHelper {
     companion object {
         /** Take the create request's `credentialData` and add SDK specific values to it. */
-        @RestrictTo(RestrictTo.Scope.LIBRARY) // used from java tests
         @JvmStatic
-        @RequiresApi(23)
         fun getFinalCreateCredentialData(
             request: CreateCredentialRequest,
             context: Context,
@@ -56,5 +61,52 @@
             )
             return createCredentialData
         }
+
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        @JvmStatic
+        fun convertGetResponseToJetpackClass(
+            response: android.credentials.GetCredentialResponse
+        ): GetCredentialResponse {
+            val credential = response.credential
+            return GetCredentialResponse(Credential.createFrom(credential.type, credential.data))
+        }
+
+        @JvmStatic
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        fun convertGetRequestToFrameworkClass(
+            request: GetCredentialRequest
+        ): android.credentials.GetCredentialRequest {
+            val builder =
+                android.credentials.GetCredentialRequest.Builder(
+                    GetCredentialRequest.toRequestDataBundle(request)
+                )
+            request.credentialOptions.forEach {
+                builder.addCredentialOption(
+                    android.credentials.CredentialOption.Builder(
+                            it.type,
+                            it.requestData,
+                            it.candidateQueryData
+                        )
+                        .setIsSystemProviderRequired(it.isSystemProviderRequired)
+                        .setAllowedProviders(it.allowedProviders)
+                        .build()
+                )
+            }
+            setOriginForGetRequest(request, builder)
+            return builder.build()
+        }
+
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        @SuppressLint("MissingPermission")
+        @VisibleForTesting
+        @JvmStatic
+        fun setOriginForGetRequest(
+            request: GetCredentialRequest,
+            builder: android.credentials.GetCredentialRequest.Builder
+        ) {
+            if (request.origin != null) {
+                builder.setOrigin(request.origin)
+            }
+        }
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
index daf3548..2957965 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
+
 package androidx.credentials.provider
 
 import android.annotation.SuppressLint
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
index e473b36..fb82267 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
+
 package androidx.credentials.provider
 
 import android.annotation.SuppressLint
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationError.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationError.kt
new file mode 100644
index 0000000..6d71c64
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationError.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import android.hardware.biometrics.BiometricPrompt
+import android.util.Log
+import androidx.annotation.RestrictTo
+import java.util.Objects
+import org.jetbrains.annotations.VisibleForTesting
+
+/**
+ * Error returned from the Biometric Prompt flow that is executed by
+ * [androidx.credentials.CredentialManager] after the user makes a selection on the Credential
+ * Manager account selector.
+ *
+ * @property errorCode the error code denoting what kind of error was encountered while the
+ *   biometric prompt flow failed, must be one of the error codes defined in
+ *   [androidx.biometric.BiometricPrompt] such as
+ *   [androidx.biometric.BiometricPrompt.ERROR_HW_UNAVAILABLE] or
+ *   [androidx.biometric.BiometricPrompt.ERROR_TIMEOUT]
+ * @property errorMsg the message associated with the [errorCode] in the form that can be displayed
+ *   on a UI.
+ * @see AuthenticationErrorTypes
+ */
+class AuthenticationError
+@JvmOverloads
+constructor(
+    val errorCode: @AuthenticationErrorTypes Int,
+    val errorMsg: CharSequence? = null,
+) {
+    internal companion object {
+        internal val TAG = "AuthenticationError"
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        const val EXTRA_BIOMETRIC_AUTH_ERROR =
+            "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_CODE"
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        const val EXTRA_BIOMETRIC_AUTH_ERROR_FALLBACK = "BIOMETRIC_AUTH_ERROR_CODE"
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        const val EXTRA_BIOMETRIC_AUTH_ERROR_MESSAGE =
+            "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_MESSAGE"
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        const val EXTRA_BIOMETRIC_AUTH_ERROR_MESSAGE_FALLBACK = "BIOMETRIC_AUTH_ERROR_MESSAGE"
+        // The majority of this is unexpected to be sent, or the values are equal,
+        // but should it arrive for any reason, is handled properly. This way
+        // providers can be confident the Jetpack codes alone are enough.
+        @VisibleForTesting
+        internal val biometricFrameworkToJetpackErrorMap =
+            linkedMapOf(
+                BiometricPrompt.BIOMETRIC_ERROR_CANCELED to
+                    androidx.biometric.BiometricPrompt.ERROR_CANCELED,
+                BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT to
+                    androidx.biometric.BiometricPrompt.ERROR_HW_NOT_PRESENT,
+                BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE to
+                    androidx.biometric.BiometricPrompt.ERROR_HW_UNAVAILABLE,
+                BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT to
+                    androidx.biometric.BiometricPrompt.ERROR_LOCKOUT,
+                BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT_PERMANENT to
+                    androidx.biometric.BiometricPrompt.ERROR_LOCKOUT_PERMANENT,
+                BiometricPrompt.BIOMETRIC_ERROR_NO_BIOMETRICS to
+                    androidx.biometric.BiometricPrompt.ERROR_NO_BIOMETRICS,
+                BiometricPrompt.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL to
+                    androidx.biometric.BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL,
+                BiometricPrompt.BIOMETRIC_ERROR_NO_SPACE to
+                    androidx.biometric.BiometricPrompt.ERROR_NO_SPACE,
+                BiometricPrompt.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED to
+                    androidx.biometric.BiometricPrompt.ERROR_SECURITY_UPDATE_REQUIRED,
+                BiometricPrompt.BIOMETRIC_ERROR_TIMEOUT to
+                    androidx.biometric.BiometricPrompt.ERROR_TIMEOUT,
+                BiometricPrompt.BIOMETRIC_ERROR_UNABLE_TO_PROCESS to
+                    androidx.biometric.BiometricPrompt.ERROR_UNABLE_TO_PROCESS,
+                BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED to
+                    androidx.biometric.BiometricPrompt.ERROR_USER_CANCELED,
+                BiometricPrompt.BIOMETRIC_ERROR_VENDOR to
+                    androidx.biometric.BiometricPrompt.ERROR_VENDOR
+                // TODO(b/340334264) : Add NEGATIVE_BUTTON from FW once avail, or wrap this in
+                // a credential manager specific error.
+            )
+
+        internal fun convertFrameworkBiometricErrorToJetpack(frameworkCode: Int): Int {
+            // Ignoring getOrDefault to allow this object down to API 21
+            return if (biometricFrameworkToJetpackErrorMap.containsKey(frameworkCode)) {
+                biometricFrameworkToJetpackErrorMap[frameworkCode]!!
+            } else {
+                Log.i(TAG, "Unexpected error code, $frameworkCode, ")
+                frameworkCode
+            }
+        }
+
+        /**
+         * Generates an instance of this class, to be called by an UI consumer that calls
+         * [BiometricPrompt] API and needs the result to be wrapped by this class. The caller of
+         * this API must specify whether the framework [android.hardware.biometrics.BiometricPrompt]
+         * API or the jetpack [androidx.biometric.BiometricPrompt] API is used through
+         * [isFrameworkBiometricPrompt].
+         *
+         * @param uiErrorCode the error code used to create this error instance, typically using the
+         *   [androidx.biometric.BiometricPrompt]'s constants if conversion isn't desired, or
+         *   [android.hardware.biometrics.BiometricPrompt]'s constants if conversion *is* desired.
+         * @param uiErrorMessage the message associated with the [uiErrorCode] in the form that can
+         *   be displayed on a UI.
+         * @param isFrameworkBiometricPrompt the bit indicating whether or not this error code
+         *   requires conversion or not, set to true by default
+         * @return an authentication error that has properly handled conversion of the err code
+         */
+        @JvmOverloads
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        internal fun createFrom(
+            uiErrorCode: Int,
+            uiErrorMessage: CharSequence,
+            isFrameworkBiometricPrompt: Boolean = true,
+        ): AuthenticationError =
+            AuthenticationError(
+                errorCode =
+                    if (isFrameworkBiometricPrompt)
+                        convertFrameworkBiometricErrorToJetpack(uiErrorCode)
+                    else uiErrorCode,
+                errorMsg = uiErrorMessage,
+            )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other is AuthenticationError) {
+            return this.errorCode == other.errorCode && this.errorMsg == other.errorMsg
+        }
+        return false
+    }
+
+    override fun hashCode(): Int {
+        return Objects.hash(this.errorCode, this.errorMsg)
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationErrorTypes.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationErrorTypes.kt
new file mode 100644
index 0000000..1bcab70
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationErrorTypes.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import androidx.biometric.BiometricPrompt
+import androidx.biometric.BiometricPrompt.ERROR_CANCELED
+import androidx.biometric.BiometricPrompt.ERROR_HW_NOT_PRESENT
+import androidx.biometric.BiometricPrompt.ERROR_HW_UNAVAILABLE
+import androidx.biometric.BiometricPrompt.ERROR_LOCKOUT
+import androidx.biometric.BiometricPrompt.ERROR_LOCKOUT_PERMANENT
+import androidx.biometric.BiometricPrompt.ERROR_NO_BIOMETRICS
+import androidx.biometric.BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL
+import androidx.biometric.BiometricPrompt.ERROR_NO_SPACE
+import androidx.biometric.BiometricPrompt.ERROR_SECURITY_UPDATE_REQUIRED
+import androidx.biometric.BiometricPrompt.ERROR_TIMEOUT
+import androidx.biometric.BiometricPrompt.ERROR_UNABLE_TO_PROCESS
+import androidx.biometric.BiometricPrompt.ERROR_USER_CANCELED
+import androidx.biometric.BiometricPrompt.ERROR_VENDOR
+
+/**
+ * This acts as a parameter hint for what [BiometricPrompt]'s error constants should be. You can
+ * learn more about the constants from [BiometricPrompt] to utilize best practices.
+ *
+ * @see BiometricPrompt
+ */
+@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(
+    value =
+        [
+            ERROR_CANCELED,
+            ERROR_HW_NOT_PRESENT,
+            ERROR_HW_UNAVAILABLE,
+            ERROR_LOCKOUT,
+            ERROR_LOCKOUT_PERMANENT,
+            ERROR_NO_BIOMETRICS,
+            ERROR_NO_DEVICE_CREDENTIAL,
+            ERROR_NO_SPACE,
+            ERROR_SECURITY_UPDATE_REQUIRED,
+            ERROR_TIMEOUT,
+            ERROR_UNABLE_TO_PROCESS,
+            ERROR_USER_CANCELED,
+            ERROR_VENDOR
+        ]
+)
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+annotation class AuthenticationErrorTypes
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationResult.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationResult.kt
new file mode 100644
index 0000000..8810199
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationResult.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import android.hardware.biometrics.BiometricPrompt
+import android.util.Log
+import androidx.annotation.RestrictTo
+import androidx.credentials.provider.AuthenticationError.Companion.TAG
+import java.util.Objects
+import org.jetbrains.annotations.VisibleForTesting
+
+/**
+ * Successful result returned from the Biometric Prompt authentication flow handled by
+ * [androidx.credentials.CredentialManager].
+ *
+ * @property authenticationType the type of authentication (e.g. device credential or biometric)
+ *   that was requested from and successfully provided by the user, corresponds to constants defined
+ *   in [androidx.biometric.BiometricPrompt] such as
+ *   [androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC] or
+ *   [androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL]
+ */
+class AuthenticationResult(
+    val authenticationType: @AuthenticationResultTypes Int,
+) {
+    internal companion object {
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        const val EXTRA_BIOMETRIC_AUTH_RESULT_TYPE =
+            "androidx.credentials.provider.BIOMETRIC_AUTH_RESULT"
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        const val EXTRA_BIOMETRIC_AUTH_RESULT_TYPE_FALLBACK = "BIOMETRIC_AUTH_RESULT"
+        @VisibleForTesting
+        internal val biometricFrameworkToJetpackResultMap =
+            linkedMapOf(
+                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC to
+                    androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC,
+                BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL to
+                    androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL,
+                // TODO(b/340334264) : Add TYPE_UNKNOWN once avail from fw, though unexpected unless
+                // very low API level, and may be ignored until jp only impl added in QPR, or other
+                // ctr can be used directly once avail/ready
+            )
+
+        internal fun convertFrameworkBiometricResultToJetpack(frameworkCode: Int): Int {
+            // Ignoring getOrDefault to allow this object down to API 21
+            return if (biometricFrameworkToJetpackResultMap.containsKey(frameworkCode)) {
+                biometricFrameworkToJetpackResultMap[frameworkCode]!!
+            } else {
+                Log.i(TAG, "Non framework result code, $frameworkCode, ")
+                frameworkCode
+            }
+        }
+
+        /**
+         * Generates an instance of this class, to be called by an UI consumer that calls
+         * [BiometricPrompt] API and needs the result to be wrapped by this class. The caller of
+         * this API must specify whether the framework [android.hardware.biometrics.BiometricPrompt]
+         * API or the jetpack [androidx.biometric.BiometricPrompt] API is used through
+         * [isFrameworkBiometricPrompt].
+         *
+         * @param uiAuthenticationType the type of authentication (e.g. device credential or
+         *   biometric) that was requested from and successfully provided by the user, corresponds
+         *   to constants defined in [androidx.biometric.BiometricPrompt] if conversion is not
+         *   desired, or in [android.hardware.biometrics.BiometricPrompt] if conversion is desired
+         * @param isFrameworkBiometricPrompt the bit indicating whether or not this error code
+         *   requires conversion or not, set to true by default
+         * @return an authentication result that has properly handled conversion of the result types
+         */
+        @JvmOverloads
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        internal fun createFrom(
+            uiAuthenticationType: Int,
+            isFrameworkBiometricPrompt: Boolean = true,
+        ): AuthenticationResult =
+            AuthenticationResult(
+                authenticationType =
+                    if (isFrameworkBiometricPrompt)
+                        convertFrameworkBiometricResultToJetpack(uiAuthenticationType)
+                    else uiAuthenticationType
+            )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other is AuthenticationResult) {
+            return this.authenticationType == other.authenticationType
+        }
+        return false
+    }
+
+    override fun hashCode(): Int {
+        return Objects.hash(this.authenticationType)
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationResultTypes.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationResultTypes.kt
new file mode 100644
index 0000000..120688c
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationResultTypes.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import androidx.biometric.BiometricPrompt
+import androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC
+import androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL
+import androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_UNKNOWN
+
+/**
+ * This acts as a parameter hint for what [BiometricPrompt]'s result constants should be. You can
+ * learn more about the constants from [BiometricPrompt] to utilize best practices.
+ *
+ * @see BiometricPrompt
+ */
+@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(
+    value =
+        [
+            AUTHENTICATION_RESULT_TYPE_BIOMETRIC,
+            AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL,
+            AUTHENTICATION_RESULT_TYPE_UNKNOWN
+        ]
+)
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+annotation class AuthenticationResultTypes
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticatorTypes.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticatorTypes.kt
new file mode 100644
index 0000000..f008628
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticatorTypes.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import android.hardware.biometrics.BiometricManager
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
+import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
+import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
+
+/**
+ * This allows verification when users pass in [BiometricManager.Authenticators] constants; namely
+ * we can have a parameter hint that indicates what they should be. You can learn more about the
+ * constants from [BiometricManager.Authenticators] to utilize best practices.
+ *
+ * @see BiometricManager.Authenticators
+ */
+@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(value = [BIOMETRIC_STRONG, BIOMETRIC_WEAK, DEVICE_CREDENTIAL])
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+annotation class AuthenticatorTypes
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialRequest.kt
index c08ab46..9221f87 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialRequest.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.credentials.provider.utils.BeginCreateCredentialUtil
 
@@ -35,7 +34,6 @@
         private const val REQUEST_KEY = "androidx.credentials.provider.BeginCreateCredentialRequest"
 
         @JvmStatic
-        @DoNotInline
         fun asBundle(bundle: Bundle, request: BeginCreateCredentialRequest) {
             bundle.putParcelable(
                 REQUEST_KEY,
@@ -44,7 +42,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun fromBundle(bundle: Bundle): BeginCreateCredentialRequest? {
             val frameworkRequest =
                 bundle.getParcelable(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialResponse.kt
index d4dff8ce..2e44157 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginCreateCredentialResponse.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.credentials.provider.utils.BeginCreateCredentialUtil
 
@@ -105,7 +104,6 @@
             "androidx.credentials.provider.BeginCreateCredentialResponse"
 
         @JvmStatic
-        @DoNotInline
         fun asBundle(bundle: Bundle, response: BeginCreateCredentialResponse) {
             bundle.putParcelable(
                 REQUEST_KEY,
@@ -114,7 +112,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun fromBundle(bundle: Bundle): BeginCreateCredentialResponse? {
             val frameworkResponse =
                 bundle.getParcelable(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialRequest.kt
index 7e17f1e..4efedd6 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialRequest.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.credentials.provider.utils.BeginGetCredentialUtil
 
@@ -49,7 +48,6 @@
         private const val REQUEST_KEY = "androidx.credentials.provider.BeginGetCredentialRequest"
 
         @JvmStatic
-        @DoNotInline
         fun asBundle(bundle: Bundle, request: BeginGetCredentialRequest) {
             bundle.putParcelable(
                 REQUEST_KEY,
@@ -58,7 +56,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun fromBundle(bundle: Bundle): BeginGetCredentialRequest? {
             val frameworkRequest =
                 bundle.getParcelable(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialResponse.kt
index 6d8b754..4e8fc09 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialResponse.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BeginGetCredentialResponse.kt
@@ -18,7 +18,6 @@
 
 import android.os.Build
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.credentials.provider.utils.BeginGetCredentialUtil
 
@@ -153,7 +152,6 @@
         private const val REQUEST_KEY = "androidx.credentials.provider.BeginGetCredentialResponse"
 
         @JvmStatic
-        @DoNotInline
         fun asBundle(bundle: Bundle, response: BeginGetCredentialResponse) {
             bundle.putParcelable(
                 REQUEST_KEY,
@@ -162,7 +160,6 @@
         }
 
         @JvmStatic
-        @DoNotInline
         fun fromBundle(bundle: Bundle): BeginGetCredentialResponse? {
             val frameworkResponse =
                 bundle.getParcelable(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BiometricPromptData.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BiometricPromptData.kt
new file mode 100644
index 0000000..c279cca
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BiometricPromptData.kt
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import android.os.Bundle
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricManager.Authenticators
+import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
+import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
+import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
+import androidx.biometric.BiometricPrompt
+import androidx.biometric.BiometricPrompt.CryptoObject
+import androidx.core.os.BuildCompat
+
+/**
+ * Biometric prompt data that can be optionally used to provide information needed for the system to
+ * show a biometric prompt directly embedded into the Credential Manager selector.
+ *
+ * If you opt to use this, the meta-data provided through the [CreateEntry] or [CredentialEntry]
+ * will be shown along with a biometric / device credential capture mechanism, on a single dialog,
+ * hence avoiding navigation through multiple screens. When user confirmation is retrieved through
+ * the aforementioned biometric / device capture mechanism, the [android.app.PendingIntent]
+ * associated with the entry is invoked, and the flow continues as explained in [CreateEntry] or
+ * [CredentialEntry].
+ *
+ * Note that the value of [allowedAuthenticators] together with the features of a given device,
+ * determines whether a biometric auth or a device credential mechanism will / can be shown. The
+ * value for this property is found in [Authenticators].
+ *
+ * @property allowedAuthenticators specifies the type(s) of authenticators that may be invoked by
+ *   the [BiometricPrompt] to authenticate the user, defaults to [BIOMETRIC_WEAK] if not set
+ * @property cryptoObject a crypto object to be unlocked after successful authentication; When set,
+ *   the value of [allowedAuthenticators] must be [BIOMETRIC_STRONG] or else an
+ *   [IllegalArgumentException] is thrown
+ * @throws IllegalArgumentException if [cryptoObject] is not null, and the [allowedAuthenticators]
+ *   is not set to [BIOMETRIC_STRONG]
+ * @see Authenticators
+ */
+@RequiresApi(35)
+class BiometricPromptData
+internal constructor(
+    val cryptoObject: BiometricPrompt.CryptoObject? = null,
+    val allowedAuthenticators: @AuthenticatorTypes Int = BIOMETRIC_WEAK,
+    private var isCreatedFromBundle: Boolean = false,
+) {
+
+    /**
+     * Biometric prompt data that can be optionally used to provide information needed for the
+     * system to show a biometric prompt directly embedded into the Credential Manager selector.
+     *
+     * If you opt to use this, the meta-data provided through the [CreateEntry] or [CredentialEntry]
+     * will be shown along with a biometric / device credential capture mechanism, on a single
+     * dialog, hence avoiding navigation through multiple screens. When user confirmation is
+     * retrieved through the aforementioned biometric / device capture mechanism, the
+     * [android.app.PendingIntent] associated with the entry is invoked, and the flow continues as
+     * explained in [CreateEntry] or [CredentialEntry].
+     *
+     * Note that the value of [allowedAuthenticators] together with the features of a given device,
+     * determines whether a biometric auth or a device credential mechanism will / can be shown. The
+     * value for this property is found in [Authenticators].
+     *
+     * @param allowedAuthenticators specifies the type(s) of authenticators that may be invoked by
+     *   the [BiometricPrompt] to authenticate the user, defaults to [BIOMETRIC_WEAK] if not set
+     * @param cryptoObject a crypto object to be unlocked after successful authentication; When set,
+     *   the value of [allowedAuthenticators] must be [BIOMETRIC_STRONG] or else an
+     *   [IllegalArgumentException] is thrown
+     * @throws IllegalArgumentException if [cryptoObject] is not null, and the
+     *   [allowedAuthenticators] is not set to [BIOMETRIC_STRONG]
+     * @see Authenticators
+     */
+    @JvmOverloads
+    constructor(
+        cryptoObject: BiometricPrompt.CryptoObject? = null,
+        allowedAuthenticators: @AuthenticatorTypes Int = BIOMETRIC_WEAK
+    ) : this(cryptoObject, allowedAuthenticators, isCreatedFromBundle = false)
+
+    init {
+        if (!isCreatedFromBundle) {
+            // This is not expected to throw for certain eligible callers who utilize the
+            // isCreatedFromBundle hidden property.
+            require(ALLOWED_AUTHENTICATOR_VALUES.contains(allowedAuthenticators)) {
+                "The allowed authenticator must be specified according to the BiometricPrompt spec."
+            }
+        }
+        if (cryptoObject != null) {
+            require(isStrongAuthenticationType(allowedAuthenticators)) {
+                "If the cryptoObject is non-null, the allowedAuthenticator value must be " +
+                    "Authenticators.BIOMETRIC_STRONG."
+            }
+        }
+    }
+
+    internal companion object {
+
+        private const val TAG = "BiometricPromptData"
+
+        internal const val BUNDLE_HINT_ALLOWED_AUTHENTICATORS =
+            "androidx.credentials.provider.BUNDLE_HINT_ALLOWED_AUTHENTICATORS"
+
+        internal const val BUNDLE_HINT_CRYPTO_OP_ID =
+            "androidx.credentials.provider.BUNDLE_HINT_CRYPTO_OP_ID"
+
+        /**
+         * Returns an instance of [BiometricPromptData] derived from a [Bundle] object.
+         *
+         * @param bundle the [Bundle] object constructed through [toBundle] method, often
+         */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun fromBundle(bundle: Bundle): BiometricPromptData? {
+            return try {
+                if (!bundle.containsKey(BUNDLE_HINT_ALLOWED_AUTHENTICATORS)) {
+                    throw IllegalArgumentException("Bundle lacks allowed authenticator key.")
+                }
+                if (BuildCompat.isAtLeastV()) {
+                    Api35Impl.fromBundle(bundle)
+                } else {
+                    ApiMinImpl.fromBundle(bundle)
+                }
+            } catch (e: Exception) {
+                Log.i(TAG, "fromSlice failed with: " + e.message)
+                null
+            }
+        }
+
+        /** Returns a [Bundle] that contains the [BiometricPromptData] representation. */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun toBundle(biometricPromptData: BiometricPromptData): Bundle {
+            return if (BuildCompat.isAtLeastV()) {
+                Api35Impl.toBundle(biometricPromptData)
+            } else {
+                ApiMinImpl.toBundle(biometricPromptData)
+            }
+        }
+
+        private fun isStrongAuthenticationType(authenticationTypes: Int?): Boolean {
+            if (authenticationTypes == null) {
+                return false
+            }
+            val biometricStrength: Int = authenticationTypes and BIOMETRIC_WEAK
+            if (biometricStrength and BiometricManager.Authenticators.BIOMETRIC_STRONG.inv() != 0) {
+                return false
+            }
+            return true
+        }
+
+        private val ALLOWED_AUTHENTICATOR_VALUES =
+            setOf(
+                BIOMETRIC_STRONG,
+                BIOMETRIC_WEAK,
+                DEVICE_CREDENTIAL,
+                BIOMETRIC_STRONG or DEVICE_CREDENTIAL,
+                BIOMETRIC_WEAK or DEVICE_CREDENTIAL
+            )
+    }
+
+    /** Builder for constructing an instance of [BiometricPromptData] */
+    class Builder {
+        private var cryptoObject: CryptoObject? = null
+        private var allowedAuthenticators: Int? = null
+
+        /**
+         * Sets whether this [BiometricPromptData] should have a crypto object associated with this
+         * authentication. If opting to pass in a value, the [allowedAuthenticators] must be
+         * [BIOMETRIC_STRONG].
+         *
+         * @param cryptoObject the [CryptoObject] to be associated with this biometric
+         *   authentication flow
+         */
+        fun setCryptoObject(cryptoObject: CryptoObject): Builder {
+            this.cryptoObject = cryptoObject
+            return this
+        }
+
+        /**
+         * Specifies the type(s) of authenticators that may be invoked to authenticate the user.
+         * Available authenticator types are defined in [Authenticators] and can be combined via
+         * bitwise OR. Defaults to [BIOMETRIC_WEAK].
+         *
+         * If this method is used and no authenticator of any of the specified types is available at
+         * the time an error code will be supplied as part of [android.content.Intent] that will be
+         * launched by the containing [CredentialEntry] or [CreateEntry]'s corresponding
+         * [android.app.PendingIntent].
+         *
+         * @param allowedAuthenticators A bit field representing all valid authenticator types that
+         *   may be invoked by the Credential Manager selector.
+         */
+        fun setAllowedAuthenticators(allowedAuthenticators: @AuthenticatorTypes Int): Builder {
+            this.allowedAuthenticators = allowedAuthenticators
+            return this
+        }
+
+        /**
+         * Builds the [BiometricPromptData] instance.
+         *
+         * @throws IllegalArgumentException If [cryptoObject] is not null, and the
+         *   [allowedAuthenticators] is not set to [BIOMETRIC_STRONG]
+         */
+        fun build(): BiometricPromptData {
+            val allowedAuthenticators = this.allowedAuthenticators ?: BIOMETRIC_WEAK
+            return BiometricPromptData(
+                cryptoObject = cryptoObject,
+                allowedAuthenticators = allowedAuthenticators,
+            )
+        }
+    }
+
+    private object ApiMinImpl {
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun toBundle(biometricPromptData: BiometricPromptData): Bundle {
+            val bundle = Bundle()
+            bundle.putInt(
+                BUNDLE_HINT_ALLOWED_AUTHENTICATORS,
+                biometricPromptData.allowedAuthenticators
+            )
+            return bundle
+        }
+
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun fromBundle(bundle: Bundle): BiometricPromptData {
+            val biometricPromptData =
+                BiometricPromptData(
+                    allowedAuthenticators = bundle.getInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS),
+                    isCreatedFromBundle = true,
+                )
+            return biometricPromptData
+        }
+    }
+
+    private object Api35Impl {
+
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun toBundle(biometricPromptData: BiometricPromptData): Bundle {
+            val bundle = Bundle()
+            bundle.putInt(
+                BUNDLE_HINT_ALLOWED_AUTHENTICATORS,
+                biometricPromptData.allowedAuthenticators
+            )
+            biometricPromptData.cryptoObject?.let {
+                bundle.putLong(BUNDLE_HINT_CRYPTO_OP_ID, it.operationHandle)
+            }
+
+            return bundle
+        }
+
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun fromBundle(bundle: Bundle): BiometricPromptData {
+            var cryptoObject: CryptoObject? = null
+            if (bundle.containsKey(BUNDLE_HINT_CRYPTO_OP_ID)) {
+                val opId = bundle.getLong(BUNDLE_HINT_CRYPTO_OP_ID)
+                cryptoObject = CryptoObject(opId)
+            }
+            val biometricPromptData =
+                BiometricPromptData(
+                    allowedAuthenticators = bundle.getInt(BUNDLE_HINT_ALLOWED_AUTHENTICATORS),
+                    cryptoObject = cryptoObject,
+                    isCreatedFromBundle = true,
+                )
+            return biometricPromptData
+        }
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/BiometricPromptResult.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/BiometricPromptResult.kt
new file mode 100644
index 0000000..3e70c7a
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/BiometricPromptResult.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentials.provider
+
+import java.util.Objects
+
+/**
+ * The result of a Biometric Prompt authentication flow, that is propagated to the provider if the
+ * provider requested for [androidx.credentials.CredentialManager] to handle the authentication
+ * flow.
+ *
+ * An instance of this class will always be part of the final provider request, either the
+ * [ProviderGetCredentialRequest] or the [ProviderCreateCredentialRequest] that the provider
+ * receives after the user selects a [CredentialEntry] or a [CreateEntry] respectively.
+ *
+ * @property isSuccessful whether the result is a success result, in which case
+ *   [authenticationResult] should be non-null
+ * @property authenticationResult the result of the authentication flow, non-null if the
+ *   authentication flow was successful
+ * @property authenticationError error information, non-null if the authentication flow has
+ *   failured, meaning that [isSuccessful] will be false in this case
+ */
+class BiometricPromptResult
+internal constructor(
+    val authenticationResult: AuthenticationResult? = null,
+    val authenticationError: AuthenticationError? = null
+) {
+    val isSuccessful: Boolean = authenticationResult != null
+
+    /**
+     * An unsuccessful biometric prompt result, denoting that authentication has failed.
+     *
+     * @param authenticationError the error that caused the biometric prompt authentication flow to
+     *   fail
+     */
+    constructor(
+        authenticationError: AuthenticationError
+    ) : this(authenticationResult = null, authenticationError = authenticationError)
+
+    /**
+     * A successful biometric prompt result, denoting that authentication has succeeded.
+     *
+     * @param authenticationResult the result after a successful biometric prompt authentication
+     *   operation
+     */
+    constructor(
+        authenticationResult: AuthenticationResult
+    ) : this(authenticationResult = authenticationResult, authenticationError = null)
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) {
+            return true
+        }
+        if (other is BiometricPromptResult) {
+            return this.isSuccessful == other.isSuccessful &&
+                this.authenticationResult == other.authenticationResult &&
+                this.authenticationError == other.authenticationError
+        }
+        return false
+    }
+
+    override fun hashCode(): Int {
+        return Objects.hash(this.isSuccessful, this.authenticationResult, this.authenticationError)
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
index 81ef2ff..bb62c78 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
+
 package androidx.credentials.provider
 
 import android.annotation.SuppressLint
@@ -29,6 +31,7 @@
 import androidx.credentials.CredentialManager
 import androidx.credentials.PasswordCredential
 import androidx.credentials.PublicKeyCredential
+import androidx.credentials.provider.CreateEntry.Api28Impl.addToSlice
 import java.time.Instant
 import java.util.Collections
 
@@ -40,9 +43,27 @@
  * registered. When user selects this entry, the corresponding [PendingIntent] is fired, and the
  * credential creation can be completed.
  *
+ * @property accountName the name of the account where the credential will be saved
+ * @property pendingIntent the [PendingIntent] that will get invoked when the user selects this
+ *   entry, must be created with a unique request code per entry, with flag
+ *   [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the final request, and NOT
+ *   with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple times
+ * @property description the localized description shown on UI about where the credential is stored
+ * @property icon the icon to be displayed with this entry on the UI, must be created using
+ *   [Icon.createWithResource] when possible, and especially not with [Icon.createWithBitmap] as the
+ *   latter consumes more memory and may cause undefined behavior due to memory implications on
+ *   internal transactions
+ * @property lastUsedTime the last time the account underlying this entry was used by the user,
+ *   distinguishable up to the milli second mark only such that if two entries have the same
+ *   millisecond precision, they will be considered to have been used at the same time
+ * @property isAutoSelectAllowed whether this entry should be auto selected if it is the only entry
+ *   on the selector
+ * @property biometricPromptData the data that is set optionally to utilize a credential manager
+ *   flow that directly handles the biometric verification and presents back the response; set to
+ *   null by default, so if not opted in, the embedded biometric prompt flow will not show
  * @throws IllegalArgumentException If [accountName] is empty
  */
-@RequiresApi(26)
+@RequiresApi(23)
 class CreateEntry
 internal constructor(
     val accountName: CharSequence,
@@ -51,9 +72,9 @@
     val description: CharSequence?,
     val lastUsedTime: Instant?,
     private val credentialCountInformationMap: MutableMap<String, Int?>,
-    val isAutoSelectAllowed: Boolean
+    val isAutoSelectAllowed: Boolean,
+    val biometricPromptData: BiometricPromptData? = null,
 ) {
-
     /**
      * Creates an entry to be displayed on the selector during create flows.
      *
@@ -92,17 +113,76 @@
         @Suppress("AutoBoxing") totalCredentialCount: Int? = null,
         isAutoSelectAllowed: Boolean = false
     ) : this(
-        accountName,
-        pendingIntent,
-        icon,
-        description,
-        lastUsedTime,
-        mutableMapOf(
-            PasswordCredential.TYPE_PASSWORD_CREDENTIAL to passwordCredentialCount,
-            PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL to publicKeyCredentialCount,
-            TYPE_TOTAL_CREDENTIAL to totalCredentialCount
-        ),
-        isAutoSelectAllowed
+        accountName = accountName,
+        pendingIntent = pendingIntent,
+        icon = icon,
+        description = description,
+        lastUsedTime = lastUsedTime,
+        credentialCountInformationMap =
+            mutableMapOf(
+                PasswordCredential.TYPE_PASSWORD_CREDENTIAL to passwordCredentialCount,
+                PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL to publicKeyCredentialCount,
+                TYPE_TOTAL_CREDENTIAL to totalCredentialCount
+            ),
+        isAutoSelectAllowed = isAutoSelectAllowed
+    )
+
+    /**
+     * Creates an entry to be displayed on the selector during create flows.
+     *
+     * @param accountName the name of the account where the credential will be saved
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     *   entry, must be created with a unique request code per entry, with flag
+     *   [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the final request, and
+     *   NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple times
+     * @param description the localized description shown on UI about where the credential is stored
+     * @param icon the icon to be displayed with this entry on the UI, must be created using
+     *   [Icon.createWithResource] when possible, and especially not with [Icon.createWithBitmap] as
+     *   the latter consumes more memory and may cause undefined behavior due to memory implications
+     *   on internal transactions
+     * @param lastUsedTime the last time the account underlying this entry was used by the user,
+     *   distinguishable up to the milli second mark only such that if two entries have the same
+     *   millisecond precision, they will be considered to have been used at the same time
+     * @param passwordCredentialCount the no. of password credentials contained by the provider
+     * @param publicKeyCredentialCount the no. of public key credentials contained by the provider
+     * @param totalCredentialCount the total no. of credentials contained by the provider
+     * @param isAutoSelectAllowed whether this entry should be auto selected if it is the only entry
+     *   on the selector
+     * @param biometricPromptData the data that is set optionally to utilize a credential manager
+     *   flow that directly handles the biometric verification and presents back the response; set
+     *   to null by default, so if not opted in, the embedded biometric prompt flow will not show
+     * @constructor constructs an instance of [CreateEntry]
+     * @throws IllegalArgumentException If [accountName] is empty, or if [description] is longer
+     *   than 300 characters (important: make sure your descriptions across all locales are within
+     *   this limit)
+     * @throws NullPointerException If [accountName] or [pendingIntent] is null
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    constructor(
+        accountName: CharSequence,
+        pendingIntent: PendingIntent,
+        description: CharSequence? = null,
+        lastUsedTime: Instant? = null,
+        icon: Icon? = null,
+        @Suppress("AutoBoxing") passwordCredentialCount: Int? = null,
+        @Suppress("AutoBoxing") publicKeyCredentialCount: Int? = null,
+        @Suppress("AutoBoxing") totalCredentialCount: Int? = null,
+        isAutoSelectAllowed: Boolean = false,
+        biometricPromptData: BiometricPromptData? = null,
+    ) : this(
+        accountName = accountName,
+        pendingIntent = pendingIntent,
+        icon = icon,
+        description = description,
+        lastUsedTime = lastUsedTime,
+        credentialCountInformationMap =
+            mutableMapOf(
+                PasswordCredential.TYPE_PASSWORD_CREDENTIAL to passwordCredentialCount,
+                PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL to publicKeyCredentialCount,
+                TYPE_TOTAL_CREDENTIAL to totalCredentialCount
+            ),
+        isAutoSelectAllowed = isAutoSelectAllowed,
+        biometricPromptData = biometricPromptData,
     )
 
     init {
@@ -149,7 +229,6 @@
      */
     class Builder
     constructor(private val accountName: CharSequence, private val pendingIntent: PendingIntent) {
-
         private var credentialCountInformationMap: MutableMap<String, Int?> = mutableMapOf()
         private var icon: Icon? = null
         private var description: CharSequence? = null
@@ -158,6 +237,7 @@
         private var publicKeyCredentialCount: Int? = null
         private var totalCredentialCount: Int? = null
         private var autoSelectAllowed: Boolean = false
+        private var biometricPromptData: BiometricPromptData? = null
 
         /** Sets whether the entry should be auto-selected. The value is false by default. */
         @Suppress("MissingGetterMatchingBuilder")
@@ -237,19 +317,32 @@
         }
 
         /**
+         * Sets the biometric prompt data to optionally utilize a credential manager flow that
+         * directly handles the biometric verification for you and gives you the response; set to
+         * null by default, indicating the default behavior is to not utilize this embedded
+         * biometric prompt flow.
+         */
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        fun setBiometricPromptData(biometricPromptData: BiometricPromptData): Builder {
+            this.biometricPromptData = biometricPromptData
+            return this
+        }
+
+        /**
          * Builds an instance of [CreateEntry]
          *
          * @throws IllegalArgumentException If [accountName] is empty
          */
         fun build(): CreateEntry {
             return CreateEntry(
-                accountName,
-                pendingIntent,
-                icon,
-                description,
-                lastUsedTime,
-                credentialCountInformationMap,
-                autoSelectAllowed
+                accountName = accountName,
+                pendingIntent = pendingIntent,
+                icon = icon,
+                description = description,
+                lastUsedTime = lastUsedTime,
+                credentialCountInformationMap = credentialCountInformationMap,
+                isAutoSelectAllowed = autoSelectAllowed,
+                biometricPromptData = biometricPromptData
             )
         }
     }
@@ -263,12 +356,89 @@
         }
     }
 
-    @RequiresApi(28)
-    private object Api28Impl {
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private object Api35Impl {
+        private fun addToSlice(createEntry: CreateEntry, sliceBuilder: Slice.Builder) {
+            val biometricPromptData = createEntry.biometricPromptData
+            if (biometricPromptData != null) {
+                // TODO(b/353798766) : Remove non bundles once beta users have finalized testing
+                sliceBuilder.addInt(
+                    biometricPromptData.allowedAuthenticators,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_ALLOWED_AUTHENTICATORS)
+                )
+                biometricPromptData.cryptoObject?.let {
+                    sliceBuilder.addLong(
+                        biometricPromptData.cryptoObject.operationHandle,
+                        /*subType=*/ null,
+                        listOf(SLICE_HINT_CRYPTO_OP_ID)
+                    )
+                }
+                val biometricBundle = BiometricPromptData.toBundle(biometricPromptData)
+                sliceBuilder.addBundle(
+                    biometricBundle,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_BIOMETRIC_PROMPT_DATA)
+                )
+            }
+        }
 
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(createEntry: CreateEntry): Slice {
+            val sliceBuilder = Api28Impl.addToSlice(createEntry)
+            addToSlice(createEntry, sliceBuilder)
+            return sliceBuilder.build()
+        }
+
+        /**
+         * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+         *
+         * @param slice the [Slice] object constructed through [addToSlice]
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+        @JvmStatic
+        fun fromSlice(slice: Slice): CreateEntry? {
+            val createEntry = Api28Impl.fromSlice(slice) ?: return null
+            var biometricPromptDataBundle: Bundle? = null
+            slice.items.forEach {
+                if (it.hasHint(CredentialEntry.SLICE_HINT_BIOMETRIC_PROMPT_DATA)) {
+                    biometricPromptDataBundle = it.bundle
+                }
+            }
+            return try {
+                CreateEntry(
+                    accountName = createEntry.accountName,
+                    pendingIntent = createEntry.pendingIntent,
+                    icon = createEntry.icon,
+                    description = createEntry.description,
+                    lastUsedTime = createEntry.lastUsedTime,
+                    credentialCountInformationMap = createEntry.credentialCountInformationMap,
+                    isAutoSelectAllowed = createEntry.isAutoSelectAllowed,
+                    biometricPromptData =
+                        if (biometricPromptDataBundle != null)
+                            BiometricPromptData.fromBundle(biometricPromptDataBundle!!)
+                        else null
+                )
+            } catch (e: Exception) {
+                Log.i(TAG, "fromSlice failed with: " + e.message)
+                null
+            }
+        }
+    }
+
+    @RequiresApi(28)
+    private object Api28Impl {
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @JvmStatic
+        fun toSlice(createEntry: CreateEntry): Slice {
+            val sliceBuilder = addToSlice(createEntry)
+            return sliceBuilder.build()
+        }
+
+        // Specific to only this create entry, but shared across API levels > P
+        fun addToSlice(createEntry: CreateEntry): Slice.Builder {
             val accountName = createEntry.accountName
             val icon = createEntry.icon
             val description = createEntry.description
@@ -276,14 +446,12 @@
             val credentialCountInformationMap = createEntry.credentialCountInformationMap
             val pendingIntent = createEntry.pendingIntent
             val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(SLICE_SPEC_TYPE, REVISION_ID))
-
             val autoSelectAllowed =
                 if (createEntry.isAutoSelectAllowed) {
                     AUTO_SELECT_TRUE_STRING
                 } else {
                     AUTO_SELECT_FALSE_STRING
                 }
-
             sliceBuilder.addText(accountName, /* subType= */ null, listOf(SLICE_HINT_ACCOUNT_NAME))
             if (lastUsedTime != null) {
                 sliceBuilder.addLong(
@@ -320,7 +488,7 @@
                     /*subType=*/ null,
                     listOf(SLICE_HINT_AUTO_SELECT_ALLOWED)
                 )
-            return sliceBuilder.build()
+            return sliceBuilder
         }
 
         @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -359,13 +527,13 @@
             }
             return try {
                 CreateEntry(
-                    accountName!!,
-                    pendingIntent!!,
-                    icon,
-                    description,
-                    lastUsedTime,
-                    credentialCountInfo,
-                    autoSelectAllowed
+                    accountName = accountName!!,
+                    pendingIntent = pendingIntent!!,
+                    icon = icon,
+                    description = description,
+                    lastUsedTime = lastUsedTime,
+                    credentialCountInformationMap = credentialCountInfo,
+                    isAutoSelectAllowed = autoSelectAllowed,
                 )
             } catch (e: Exception) {
                 Log.i(TAG, "fromSlice failed with: " + e.message)
@@ -411,36 +579,30 @@
     companion object {
         private const val TAG = "CreateEntry"
         private const val DESCRIPTION_MAX_CHAR_LIMIT = 300
-
         internal const val TYPE_TOTAL_CREDENTIAL = "TOTAL_CREDENTIAL_COUNT_TYPE"
-
         private const val SLICE_HINT_ACCOUNT_NAME =
             "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
-
         private const val SLICE_HINT_NOTE =
             "androidx.credentials.provider.createEntry.SLICE_HINT_NOTE"
-
         private const val SLICE_HINT_ICON =
             "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
-
         private const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
             "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
-
         private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
             "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-
         private const val SLICE_HINT_PENDING_INTENT =
             "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
-
         private const val SLICE_HINT_AUTO_SELECT_ALLOWED =
             "androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_SELECT_ALLOWED"
-
+        private const val SLICE_HINT_BIOMETRIC_PROMPT_DATA =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_BIOMETRIC_PROMPT_DATA"
+        private const val SLICE_HINT_ALLOWED_AUTHENTICATORS =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_ALLOWED_AUTHENTICATORS"
+        private const val SLICE_HINT_CRYPTO_OP_ID =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_CRYPTO_OP_ID"
         private const val AUTO_SELECT_TRUE_STRING = "true"
-
         private const val AUTO_SELECT_FALSE_STRING = "false"
-
         private const val SLICE_SPEC_TYPE = "CreateEntry"
-
         private const val REVISION_ID = 1
 
         /**
@@ -452,7 +614,9 @@
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         fun toSlice(createEntry: CreateEntry): Slice? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.toSlice(createEntry)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.toSlice(createEntry)
             }
             return null
@@ -466,7 +630,9 @@
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         fun fromSlice(slice: Slice): CreateEntry? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.fromSlice(slice)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.fromSlice(slice)
             }
             return null
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
index 55cdd7d..efddf65 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CredentialEntry.kt
@@ -13,9 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
 
 package androidx.credentials.provider
 
+import android.annotation.SuppressLint
 import android.app.slice.Slice
 import android.os.Build
 import androidx.annotation.RequiresApi
@@ -50,6 +52,9 @@
  *   default credential type icon when you are the only available provider; see individual
  *   subclasses for these default icons (e.g. for [PublicKeyCredentialEntry], it is based on
  *   [R.drawable.ic_password])
+ * @property biometricPromptData the data that is set optionally to utilize a credential manager
+ *   flow that directly handles the biometric verification and presents back the response; set to
+ *   null by default, so if not opted in, the embedded biometric prompt flow will not show
  */
 abstract class CredentialEntry
 internal constructor(
@@ -58,8 +63,8 @@
     val entryGroupId: CharSequence,
     val isDefaultIconPreferredAsSingleProvider: Boolean,
     val affiliatedDomain: CharSequence? = null,
+    val biometricPromptData: BiometricPromptData? = null,
 ) {
-
     @RequiresApi(34)
     private object Api34Impl {
         @JvmStatic
@@ -71,7 +76,102 @@
         }
     }
 
+    @RequiresApi(35)
+    internal object Api35Impl {
+        @JvmStatic
+        fun toSlice(entry: CredentialEntry): Slice? {
+            when (entry) {
+                is PasswordCredentialEntry -> return PasswordCredentialEntry.toSlice(entry)
+                is PublicKeyCredentialEntry -> return PublicKeyCredentialEntry.toSlice(entry)
+                is CustomCredentialEntry -> return CustomCredentialEntry.toSlice(entry)
+            }
+            return null
+        }
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+        @JvmStatic
+        fun fromSlice(slice: Slice): CredentialEntry? {
+            return try {
+                when (slice.spec?.type) {
+                    TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(slice)!!
+                    TYPE_PUBLIC_KEY_CREDENTIAL -> PublicKeyCredentialEntry.fromSlice(slice)!!
+                    else -> CustomCredentialEntry.fromSlice(slice)!!
+                }
+            } catch (e: Exception) {
+                // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+                // password / passkey parsing attempt.
+                CustomCredentialEntry.fromSlice(slice)
+            }
+        }
+    }
+
+    @RequiresApi(28)
+    internal object Api28Impl {
+        @JvmStatic
+        fun toSlice(entry: CredentialEntry): Slice? {
+            when (entry) {
+                is PasswordCredentialEntry -> return PasswordCredentialEntry.toSlice(entry)
+                is PublicKeyCredentialEntry -> return PublicKeyCredentialEntry.toSlice(entry)
+                is CustomCredentialEntry -> return CustomCredentialEntry.toSlice(entry)
+            }
+            return null
+        }
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+        @JvmStatic
+        fun fromSlice(slice: Slice): CredentialEntry? {
+            return try {
+                when (slice.spec?.type) {
+                    TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(slice)!!
+                    TYPE_PUBLIC_KEY_CREDENTIAL -> PublicKeyCredentialEntry.fromSlice(slice)!!
+                    else -> CustomCredentialEntry.fromSlice(slice)!!
+                }
+            } catch (e: Exception) {
+                // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+                // password / passkey parsing attempt.
+                CustomCredentialEntry.fromSlice(slice)
+            }
+        }
+    }
+
     companion object {
+        internal const val TRUE_STRING = "true"
+        internal const val FALSE_STRING = "false"
+        internal const val REVISION_ID = 1
+        internal const val SLICE_HINT_TYPE_DISPLAY_NAME =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+        internal const val SLICE_HINT_TITLE =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
+        internal const val SLICE_HINT_SUBTITLE =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
+        internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+        internal const val SLICE_HINT_ICON =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
+        internal const val SLICE_HINT_PENDING_INTENT =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
+        internal const val SLICE_HINT_AUTO_ALLOWED =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
+        internal const val SLICE_HINT_IS_DEFAULT_ICON_PREFERRED =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_IS_DEFAULT_ICON_PREFERRED"
+        internal const val SLICE_HINT_OPTION_ID =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_OPTION_ID"
+        internal const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
+        internal const val SLICE_HINT_DEFAULT_ICON_RES_ID =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEFAULT_ICON_RES_ID"
+        internal const val SLICE_HINT_AFFILIATED_DOMAIN =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AFFILIATED_DOMAIN"
+        internal const val SLICE_HINT_DEDUPLICATION_ID =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEDUPLICATION_ID"
+        internal const val SLICE_HINT_BIOMETRIC_PROMPT_DATA =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_BIOMETRIC_PROMPT_DATA"
+        internal const val SLICE_HINT_ALLOWED_AUTHENTICATORS =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_ALLOWED_AUTHENTICATORS"
+        internal const val SLICE_HINT_CRYPTO_OP_ID =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_CRYPTO_OP_ID"
 
         /**
          * Converts a framework [android.service.credentials.CredentialEntry] class to a Jetpack
@@ -94,31 +194,26 @@
         }
 
         @JvmStatic
-        @RequiresApi(28)
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         internal fun fromSlice(slice: Slice): CredentialEntry? {
-            return try {
-                when (slice.spec?.type) {
-                    TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(slice)!!
-                    TYPE_PUBLIC_KEY_CREDENTIAL -> PublicKeyCredentialEntry.fromSlice(slice)!!
-                    else -> CustomCredentialEntry.fromSlice(slice)!!
-                }
-            } catch (e: Exception) {
-                // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
-                // password / passkey parsing attempt.
-                CustomCredentialEntry.fromSlice(slice)
+            return if (Build.VERSION.SDK_INT >= 35) {
+                Api35Impl.fromSlice(slice)
+            } else if (Build.VERSION.SDK_INT >= 28) {
+                Api28Impl.fromSlice(slice)
+            } else {
+                null
             }
         }
 
         @JvmStatic
-        @RequiresApi(28)
         internal fun toSlice(entry: CredentialEntry): Slice? {
-            when (entry) {
-                is PasswordCredentialEntry -> return PasswordCredentialEntry.toSlice(entry)
-                is PublicKeyCredentialEntry -> return PublicKeyCredentialEntry.toSlice(entry)
-                is CustomCredentialEntry -> return CustomCredentialEntry.toSlice(entry)
+            return if (Build.VERSION.SDK_INT >= 35) {
+                Api35Impl.toSlice(entry)
+            } else if (Build.VERSION.SDK_INT >= 28) {
+                Api28Impl.toSlice(entry)
+            } else {
+                null
             }
-            return null
         }
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
index 299c051..fd10ee5 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
+
 package androidx.credentials.provider
 
 import android.annotation.SuppressLint
@@ -29,6 +31,7 @@
 import androidx.annotation.RestrictTo
 import androidx.credentials.CredentialOption
 import androidx.credentials.R
+import androidx.credentials.provider.CustomCredentialEntry.Api28Impl.addToSlice
 import java.time.Instant
 import java.util.Collections
 
@@ -69,10 +72,13 @@
  *   this entry was created allows this entry to be auto-selected
  * @property hasDefaultIcon whether this entry was created without a custom icon and hence contains
  *   a default icon set by the library, only to be used in Android API levels >= 28
+ * @property biometricPromptData the data that is set optionally to utilize a credential manager
+ *   flow that directly handles the biometric verification and presents back the response; set to
+ *   null by default, so if not opted in, the embedded biometric prompt flow will not show
  * @throws IllegalArgumentException If [type] or [title] are empty
  * @see CredentialEntry
  */
-@RequiresApi(26)
+@RequiresApi(23)
 class CustomCredentialEntry
 internal constructor(
     override val type: String,
@@ -87,20 +93,21 @@
     isDefaultIconPreferredAsSingleProvider: Boolean,
     entryGroupId: CharSequence? = title,
     affiliatedDomain: CharSequence? = null,
+    biometricPromptData: BiometricPromptData? = null,
     autoSelectAllowedFromOption: Boolean =
         CredentialOption.extractAutoSelectValue(beginGetCredentialOption.candidateQueryData),
     private var isCreatedFromSlice: Boolean = false,
     private var isDefaultIconFromSlice: Boolean = false,
 ) :
     CredentialEntry(
-        type,
-        beginGetCredentialOption,
-        entryGroupId ?: title,
+        type = type,
+        beginGetCredentialOption = beginGetCredentialOption,
+        entryGroupId = entryGroupId ?: title,
         isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
         affiliatedDomain = affiliatedDomain,
+        biometricPromptData = biometricPromptData
     ) {
     val isAutoSelectAllowedFromOption = autoSelectAllowedFromOption
-
     @get:JvmName("hasDefaultIcon")
     val hasDefaultIcon: Boolean
         get() {
@@ -143,7 +150,8 @@
             ReplaceWith(
                 "CustomCredentialEntry(context, title, pendingIntent," +
                     "beginGetCredentialOption, subtitle, typeDisplayName, lastUsedTime, icon, " +
-                    "isAutoSelectAllowed, entryGroupId, isDefaultIconPreferredAsSingleProvider)"
+                    "isAutoSelectAllowed, entryGroupId, isDefaultIconPreferredAsSingleProvider," +
+                    "biometricPromptData)"
             ),
         level = DeprecationLevel.HIDDEN
     )
@@ -158,6 +166,63 @@
         icon: Icon = Icon.createWithResource(context, R.drawable.ic_other_sign_in),
         @Suppress("AutoBoxing") isAutoSelectAllowed: Boolean = false,
     ) : this(
+        type = beginGetCredentialOption.type,
+        title = title,
+        pendingIntent = pendingIntent,
+        isAutoSelectAllowed = isAutoSelectAllowed,
+        subtitle = subtitle,
+        typeDisplayName = typeDisplayName,
+        icon = icon,
+        lastUsedTime = lastUsedTime,
+        beginGetCredentialOption = beginGetCredentialOption,
+        isDefaultIconPreferredAsSingleProvider = false
+    )
+
+    /**
+     * @param context the context of the calling app, required to retrieve fallback resources
+     * @param title the title shown with this entry on the selector UI
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     *   entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system
+     *   to attach the final request
+     * @param beginGetCredentialOption the option from the original [BeginGetCredentialRequest], for
+     *   which this credential entry is being added
+     * @param subtitle the subTitle shown with this entry on the selector UI
+     * @param lastUsedTime the last used time the credential underlying this entry was used by the
+     *   user, distinguishable up to the milli second mark only such that if two entries have the
+     *   same millisecond precision, they will be considered to have been used at the same time
+     * @param typeDisplayName the friendly name to be displayed on the UI for the type of the
+     *   credential
+     * @param icon the icon to be displayed with this entry on the selector UI, if not set a default
+     *   icon representing a custom credential type is set by the library
+     * @param isAutoSelectAllowed whether this entry is allowed to be auto selected if it is the
+     *   only one on the UI, only takes effect if the app requesting for credentials also opts for
+     *   auto select
+     * @param entryGroupId an ID to uniquely mark this entry for deduplication or to group entries
+     *   during display, set to [title] by default
+     * @param isDefaultIconPreferredAsSingleProvider when set to true, the UI prefers to render the
+     *   default credential type icon (see the default value of [icon]) when you are the only
+     *   available provider; false by default
+     * @param biometricPromptData the data that is set optionally to utilize a credential manager
+     *   flow that directly handles the biometric verification and presents back the response; set
+     *   to null by default, so if not opted in, the embedded biometric prompt flow will not show
+     * @constructor constructs an instance of [CustomCredentialEntry]
+     * @throws IllegalArgumentException If [type] or [title] are empty
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    constructor(
+        context: Context,
+        title: CharSequence,
+        pendingIntent: PendingIntent,
+        beginGetCredentialOption: BeginGetCredentialOption,
+        subtitle: CharSequence? = null,
+        typeDisplayName: CharSequence? = null,
+        lastUsedTime: Instant? = null,
+        icon: Icon = Icon.createWithResource(context, R.drawable.ic_other_sign_in),
+        @Suppress("AutoBoxing") isAutoSelectAllowed: Boolean = false,
+        entryGroupId: CharSequence = title,
+        isDefaultIconPreferredAsSingleProvider: Boolean = false,
+        biometricPromptData: BiometricPromptData? = null,
+    ) : this(
         beginGetCredentialOption.type,
         title,
         pendingIntent,
@@ -167,7 +232,9 @@
         icon,
         lastUsedTime,
         beginGetCredentialOption,
-        isDefaultIconPreferredAsSingleProvider = false
+        isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
+        entryGroupId = entryGroupId.ifEmpty { title },
+        biometricPromptData = biometricPromptData,
     )
 
     /**
@@ -208,7 +275,7 @@
         icon: Icon = Icon.createWithResource(context, R.drawable.ic_other_sign_in),
         @Suppress("AutoBoxing") isAutoSelectAllowed: Boolean = false,
         entryGroupId: CharSequence = title,
-        isDefaultIconPreferredAsSingleProvider: Boolean = false
+        isDefaultIconPreferredAsSingleProvider: Boolean = false,
     ) : this(
         beginGetCredentialOption.type,
         title,
@@ -220,7 +287,7 @@
         lastUsedTime,
         beginGetCredentialOption,
         isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
-        entryGroupId.ifEmpty { title },
+        entryGroupId = entryGroupId.ifEmpty { title },
     )
 
     @RequiresApi(34)
@@ -234,6 +301,92 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private object Api35Impl {
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @JvmStatic
+        fun toSlice(entry: CustomCredentialEntry): Slice {
+            val type = entry.type
+            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
+            Api28Impl.addToSlice(entry, sliceBuilder)
+            addToSlice(entry, sliceBuilder)
+            return sliceBuilder.build()
+        }
+
+        // Given multiple API dependencies, this captures common builds across all API levels > V
+        // and across all subclasses for the toSlice method
+        fun addToSlice(entry: CustomCredentialEntry, sliceBuilder: Slice.Builder) {
+            val biometricPromptData = entry.biometricPromptData
+            if (biometricPromptData != null) {
+                // TODO(b/353798766) : Remove non bundles once beta users have finalized testing
+                sliceBuilder.addInt(
+                    biometricPromptData.allowedAuthenticators,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_ALLOWED_AUTHENTICATORS)
+                )
+                biometricPromptData.cryptoObject?.let {
+                    sliceBuilder.addLong(
+                        biometricPromptData.cryptoObject.operationHandle,
+                        /*subType=*/ null,
+                        listOf(SLICE_HINT_CRYPTO_OP_ID)
+                    )
+                }
+                val biometricBundle = BiometricPromptData.toBundle(biometricPromptData)
+                sliceBuilder.addBundle(
+                    biometricBundle,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_BIOMETRIC_PROMPT_DATA)
+                )
+            }
+        }
+
+        /**
+         * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+         *
+         * @param slice the [Slice] object constructed through [addToSlice]
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+        @JvmStatic
+        fun fromSlice(slice: Slice): CustomCredentialEntry? {
+            val customCredentialEntry = Api28Impl.fromSlice(slice) ?: return null
+            var biometricPromptDataBundle: Bundle? = null
+            slice.items.forEach {
+                if (it.hasHint(SLICE_HINT_BIOMETRIC_PROMPT_DATA)) {
+                    biometricPromptDataBundle = it.bundle
+                }
+            }
+            return try {
+                CustomCredentialEntry(
+                    type = customCredentialEntry.type,
+                    title = customCredentialEntry.title,
+                    pendingIntent = customCredentialEntry.pendingIntent,
+                    isAutoSelectAllowed = customCredentialEntry.isAutoSelectAllowed,
+                    subtitle = customCredentialEntry.subtitle,
+                    typeDisplayName = customCredentialEntry.typeDisplayName,
+                    icon = customCredentialEntry.icon,
+                    lastUsedTime = customCredentialEntry.lastUsedTime,
+                    beginGetCredentialOption = customCredentialEntry.beginGetCredentialOption,
+                    isDefaultIconPreferredAsSingleProvider =
+                        customCredentialEntry.isDefaultIconPreferredAsSingleProvider,
+                    entryGroupId = customCredentialEntry.entryGroupId,
+                    affiliatedDomain = customCredentialEntry.affiliatedDomain,
+                    autoSelectAllowedFromOption =
+                        customCredentialEntry.isAutoSelectAllowedFromOption,
+                    isCreatedFromSlice = true,
+                    isDefaultIconFromSlice = customCredentialEntry.isDefaultIconFromSlice,
+                    biometricPromptData =
+                        if (biometricPromptDataBundle != null)
+                            BiometricPromptData.fromBundle(biometricPromptDataBundle!!)
+                        else null
+                )
+            } catch (e: Exception) {
+                Log.i(TAG, "fromSlice failed with: " + e.message)
+                null
+            }
+        }
+    }
+
     @RequiresApi(28)
     private object Api28Impl {
         @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -250,6 +403,41 @@
         @JvmStatic
         fun toSlice(entry: CustomCredentialEntry): Slice {
             val type = entry.type
+            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
+            addToSlice(entry, sliceBuilder)
+            return sliceBuilder.build()
+        }
+
+        // Specific to only this custom credential entry, but shared across API levels > P
+        fun addToSlice(entry: CustomCredentialEntry, sliceBuilder: Slice.Builder) {
+            val beginGetCredentialOption = entry.beginGetCredentialOption
+            val entryGroupId = entry.entryGroupId
+            val isDefaultIconPreferredAsSingleProvider =
+                entry.isDefaultIconPreferredAsSingleProvider
+            val affiliatedDomain = entry.affiliatedDomain
+            val isUsingDefaultIcon =
+                if (isDefaultIconPreferredAsSingleProvider) {
+                    TRUE_STRING
+                } else {
+                    FALSE_STRING
+                }
+            sliceBuilder
+                .addText(
+                    beginGetCredentialOption.id,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_OPTION_ID)
+                )
+                .addText(entryGroupId, /* subTypes= */ null, listOf(SLICE_HINT_DEDUPLICATION_ID))
+                .addText(
+                    isUsingDefaultIcon,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)
+                )
+                .addText(
+                    affiliatedDomain,
+                    /*subTypes=*/ null,
+                    listOf(SLICE_HINT_AFFILIATED_DOMAIN)
+                )
             val title = entry.title
             val subtitle = entry.subtitle
             val pendingIntent = entry.pendingIntent
@@ -257,61 +445,18 @@
             val lastUsedTime = entry.lastUsedTime
             val icon = entry.icon
             val isAutoSelectAllowed = entry.isAutoSelectAllowed
-            val beginGetCredentialOption = entry.beginGetCredentialOption
-            val entryGroupId = entry.entryGroupId
-            val affiliatedDomain = entry.affiliatedDomain
-            val isDefaultIconPreferredAsSingleProvider =
-                entry.isDefaultIconPreferredAsSingleProvider
-
             val autoSelectAllowed =
                 if (isAutoSelectAllowed) {
                     TRUE_STRING
                 } else {
                     FALSE_STRING
                 }
-
-            val isUsingDefaultIconPreferred =
-                if (isDefaultIconPreferredAsSingleProvider) {
-                    TRUE_STRING
-                } else {
-                    FALSE_STRING
-                }
-            val sliceBuilder =
-                Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
-                    .addText(
-                        typeDisplayName,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
-                    )
-                    .addText(title, /* subType= */ null, listOf(SLICE_HINT_TITLE))
-                    .addText(subtitle, /* subType= */ null, listOf(SLICE_HINT_SUBTITLE))
-                    .addText(
-                        autoSelectAllowed,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_AUTO_ALLOWED)
-                    )
-                    .addText(
-                        beginGetCredentialOption.id,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_OPTION_ID)
-                    )
-                    .addText(
-                        entryGroupId,
-                        /*subTypes=*/ null,
-                        listOf(SLICE_HINT_DEDUPLICATION_ID)
-                    )
-                    .addText(
-                        affiliatedDomain,
-                        /*subTypes=*/ null,
-                        listOf(SLICE_HINT_AFFILIATED_DOMAIN)
-                    )
-                    .addIcon(icon, /* subType= */ null, listOf(SLICE_HINT_ICON))
-                    .addText(
-                        isUsingDefaultIconPreferred,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)
-                    )
-
+            sliceBuilder
+                .addText(typeDisplayName, /* subType= */ null, listOf(SLICE_HINT_TYPE_DISPLAY_NAME))
+                .addText(title, /* subType= */ null, listOf(SLICE_HINT_TITLE))
+                .addText(subtitle, /* subType= */ null, listOf(SLICE_HINT_SUBTITLE))
+                .addText(autoSelectAllowed, /* subType= */ null, listOf(SLICE_HINT_AUTO_ALLOWED))
+                .addIcon(icon, /* subType= */ null, listOf(SLICE_HINT_ICON))
             try {
                 if (entry.hasDefaultIcon) {
                     sliceBuilder.addInt(
@@ -321,7 +466,6 @@
                     )
                 }
             } catch (_: IllegalStateException) {}
-
             if (entry.isAutoSelectAllowedFromOption) {
                 sliceBuilder.addInt(
                     /*true=*/ 1,
@@ -343,19 +487,22 @@
                     .build(),
                 /*subType=*/ null
             )
-            return sliceBuilder.build()
         }
 
         /**
          * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
          *
-         * @param slice the [Slice] object constructed through [toSlice]
+         * @param slice the [Slice] object constructed through [addToSlice]
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY) // used from java tests
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @JvmStatic
         fun fromSlice(slice: Slice): CustomCredentialEntry? {
             val type: String = slice.spec!!.type
+            var entryGroupId: CharSequence? = null
+            var affiliatedDomain: CharSequence? = null
+            var isDefaultIconPreferredAsSingleProvider = false
+            var beginGetCredentialOptionId: CharSequence? = null
             var typeDisplayName: CharSequence? = null
             var title: CharSequence? = null
             var subtitle: CharSequence? = null
@@ -363,15 +510,21 @@
             var pendingIntent: PendingIntent? = null
             var lastUsedTime: Instant? = null
             var autoSelectAllowed = false
-            var beginGetCredentialOptionId: CharSequence? = null
-            var entryGroupId: CharSequence? = null
             var autoSelectAllowedFromOption = false
-            var isDefaultIconPreferredAsSingleProvider = false
             var isDefaultIcon = false
-            var affiliatedDomain: CharSequence? = null
-
             slice.items.forEach {
-                if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
+                if (it.hasHint(SLICE_HINT_OPTION_ID)) {
+                    beginGetCredentialOptionId = it.text
+                } else if (it.hasHint(SLICE_HINT_DEDUPLICATION_ID)) {
+                    entryGroupId = it.text
+                } else if (it.hasHint(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)) {
+                    val defaultIconValue = it.text
+                    if (defaultIconValue == TRUE_STRING) {
+                        isDefaultIconPreferredAsSingleProvider = true
+                    }
+                } else if (it.hasHint(SLICE_HINT_AFFILIATED_DOMAIN)) {
+                    affiliatedDomain = it.text
+                } else if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
                     typeDisplayName = it.text
                 } else if (it.hasHint(SLICE_HINT_TITLE)) {
                     title = it.text
@@ -381,8 +534,6 @@
                     icon = it.icon
                 } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
                     pendingIntent = it.action
-                } else if (it.hasHint(SLICE_HINT_OPTION_ID)) {
-                    beginGetCredentialOptionId = it.text
                 } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
                     lastUsedTime = Instant.ofEpochMilli(it.long)
                 } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
@@ -390,22 +541,12 @@
                     if (autoSelectValue == TRUE_STRING) {
                         autoSelectAllowed = true
                     }
-                } else if (it.hasHint(SLICE_HINT_DEDUPLICATION_ID)) {
-                    entryGroupId = it.text
                 } else if (it.hasHint(SLICE_HINT_AUTO_SELECT_FROM_OPTION)) {
                     autoSelectAllowedFromOption = true
-                } else if (it.hasHint(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)) {
-                    val defaultIconValue = it.text
-                    if (defaultIconValue == TRUE_STRING) {
-                        isDefaultIconPreferredAsSingleProvider = true
-                    }
                 } else if (it.hasHint(SLICE_HINT_DEFAULT_ICON_RES_ID)) {
                     isDefaultIcon = true
-                } else if (it.hasHint(SLICE_HINT_AFFILIATED_DOMAIN)) {
-                    affiliatedDomain = it.text
                 }
             }
-
             return try {
                 CustomCredentialEntry(
                     type = type,
@@ -439,51 +580,6 @@
     companion object {
         private const val TAG = "CredentialEntry"
 
-        private const val SLICE_HINT_TYPE_DISPLAY_NAME =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-
-        private const val SLICE_HINT_TITLE =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
-
-        private const val SLICE_HINT_SUBTITLE =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
-
-        private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-
-        private const val SLICE_HINT_ICON =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
-
-        private const val SLICE_HINT_PENDING_INTENT =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
-
-        private const val SLICE_HINT_AUTO_ALLOWED =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
-
-        private const val SLICE_HINT_IS_DEFAULT_ICON_PREFERRED =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_IS_DEFAULT_ICON_PREFERRED"
-
-        private const val SLICE_HINT_OPTION_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_OPTION_ID"
-
-        private const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
-
-        private const val SLICE_HINT_DEDUPLICATION_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEDUPLICATION_ID"
-
-        private const val SLICE_HINT_AFFILIATED_DOMAIN =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AFFILIATED_DOMAIN"
-
-        private const val SLICE_HINT_DEFAULT_ICON_RES_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEFAULT_ICON_RES_ID"
-
-        private const val TRUE_STRING = "true"
-
-        private const val FALSE_STRING = "false"
-
-        private const val REVISION_ID = 1
-
         /**
          * Converts an instance of [CustomCredentialEntry] to a [Slice].
          *
@@ -493,7 +589,9 @@
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(entry: CustomCredentialEntry): Slice? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.toSlice(entry)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.toSlice(entry)
             }
             return null
@@ -502,13 +600,15 @@
         /**
          * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
          *
-         * @param slice the [Slice] object constructed through [toSlice]
+         * @param slice the [Slice] object constructed through [addToSlice]
          */
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         fun fromSlice(slice: Slice): CustomCredentialEntry? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.fromSlice(slice)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.fromSlice(slice)
             }
             return null
@@ -560,6 +660,7 @@
         private var autoSelectAllowed = false
         private var entryGroupId: CharSequence = title
         private var isDefaultIconPreferredAsSingleProvider = false
+        private var biometricPromptData: BiometricPromptData? = null
 
         /** Sets a displayName to be shown on the UI with this entry. */
         fun setSubtitle(subtitle: CharSequence?): Builder {
@@ -582,6 +683,18 @@
             return this
         }
 
+        /**
+         * Sets the biometric prompt data to optionally utilize a credential manager flow that
+         * directly handles the biometric verification for you and gives you the response; set to
+         * null by default, indicating the default behavior is to not utilize this embedded
+         * biometric prompt flow.
+         */
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        fun setBiometricPromptData(biometricPromptData: BiometricPromptData): Builder {
+            this.biometricPromptData = biometricPromptData
+            return this
+        }
+
         /** Sets whether the entry should be auto-selected. The value is false by default. */
         @Suppress("MissingGetterMatchingBuilder")
         fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
@@ -627,17 +740,18 @@
                 icon = Icon.createWithResource(context, R.drawable.ic_other_sign_in)
             }
             return CustomCredentialEntry(
-                type,
-                title,
-                pendingIntent,
-                autoSelectAllowed,
-                subtitle,
-                typeDisplayName,
-                icon!!,
-                lastUsedTime,
-                beginGetCredentialOption,
+                type = type,
+                title = title,
+                pendingIntent = pendingIntent,
+                isAutoSelectAllowed = autoSelectAllowed,
+                subtitle = subtitle,
+                typeDisplayName = typeDisplayName,
+                icon = icon!!,
+                lastUsedTime = lastUsedTime,
+                beginGetCredentialOption = beginGetCredentialOption,
                 isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
                 entryGroupId = entryGroupId,
+                biometricPromptData = biometricPromptData,
             )
         }
     }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
index 034b0af..e426b62 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
 
 package androidx.credentials.provider
 
@@ -31,6 +32,7 @@
 import androidx.credentials.CredentialOption
 import androidx.credentials.PasswordCredential
 import androidx.credentials.R
+import androidx.credentials.provider.PasswordCredentialEntry.Api28Impl.toSlice
 import androidx.credentials.provider.PasswordCredentialEntry.Companion.toSlice
 import java.time.Instant
 import java.util.Collections
@@ -64,11 +66,14 @@
  *   this entry was created allows this entry to be auto-selected
  * @property hasDefaultIcon whether this entry was created without a custom icon and hence contains
  *   a default icon set by the library, only to be used in Android API levels >= 28
+ * @property biometricPromptData the data that is set optionally to utilize a credential manager
+ *   flow that directly handles the biometric verification and presents back the response; set to
+ *   null by default, so if not opted in, the embedded biometric prompt flow will not show
  * @throws IllegalArgumentException If [username] is empty
  * @see CustomCredentialEntry
  * @see CredentialEntry
  */
-@RequiresApi(26)
+@RequiresApi(23)
 class PasswordCredentialEntry
 internal constructor(
     val username: CharSequence,
@@ -82,21 +87,21 @@
     isDefaultIconPreferredAsSingleProvider: Boolean,
     entryGroupId: CharSequence? = username,
     affiliatedDomain: CharSequence? = null,
+    biometricPromptData: BiometricPromptData? = null,
     autoSelectAllowedFromOption: Boolean =
         CredentialOption.extractAutoSelectValue(beginGetPasswordOption.candidateQueryData),
     private var isCreatedFromSlice: Boolean = false,
     private var isDefaultIconFromSlice: Boolean = false
 ) :
     CredentialEntry(
-        PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
-        beginGetPasswordOption,
-        entryGroupId ?: username,
+        type = PasswordCredential.TYPE_PASSWORD_CREDENTIAL,
+        beginGetCredentialOption = beginGetPasswordOption,
+        entryGroupId = entryGroupId ?: username,
         isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
         affiliatedDomain = affiliatedDomain,
+        biometricPromptData = biometricPromptData,
     ) {
-
     val isAutoSelectAllowedFromOption = autoSelectAllowedFromOption
-
     @get:JvmName("hasDefaultIcon")
     val hasDefaultIcon: Boolean
         get() {
@@ -185,6 +190,70 @@
      * @param isAutoSelectAllowed whether this entry is allowed to be auto selected if it is the
      *   only one on the UI, only takes effect if the app requesting for credentials also opts for
      *   auto select
+     * @param affiliatedDomain the user visible affiliated domain, a CharSequence representation of
+     *   a web domain or an app package name that the given credential in this entry is associated
+     *   with when it is different from the requesting entity, default null
+     * @param isDefaultIconPreferredAsSingleProvider when set to true, the UI prefers to render the
+     *   default credential type icon (see the default value of [icon]) when you are the only
+     *   available provider; false by default
+     * @param biometricPromptData the data that is set optionally to utilize a credential manager
+     *   flow that directly handles the biometric verification and presents back the response; set
+     *   to null by default, so if not opted in, the embedded biometric prompt flow will not show
+     * @constructor constructs an instance of [PasswordCredentialEntry]
+     *
+     * The [affiliatedDomain] parameter is filled if you provide a credential that is not directly
+     * associated with the requesting entity, but rather originates from an entity that is
+     * determined as being associated with the requesting entity through mechanisms such as digital
+     * asset links.
+     *
+     * @throws IllegalArgumentException If [username] is empty
+     * @throws NullPointerException If [context], [username], [pendingIntent], or
+     *   [beginGetPasswordOption] is null
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    constructor(
+        context: Context,
+        username: CharSequence,
+        pendingIntent: PendingIntent,
+        beginGetPasswordOption: BeginGetPasswordOption,
+        displayName: CharSequence? = null,
+        lastUsedTime: Instant? = null,
+        icon: Icon = Icon.createWithResource(context, R.drawable.ic_password),
+        isAutoSelectAllowed: Boolean = false,
+        affiliatedDomain: CharSequence? = null,
+        isDefaultIconPreferredAsSingleProvider: Boolean = false,
+        biometricPromptData: BiometricPromptData? = null
+    ) : this(
+        username,
+        displayName,
+        typeDisplayName = context.getString(R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL),
+        pendingIntent,
+        lastUsedTime,
+        icon,
+        isAutoSelectAllowed,
+        beginGetPasswordOption,
+        isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
+        affiliatedDomain = affiliatedDomain,
+        biometricPromptData = biometricPromptData,
+    )
+
+    /**
+     * @param context the context of the calling app, required to retrieve fallback resources
+     * @param username the username of the account holding the password credential
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     *   entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system
+     *   to attach the final request
+     * @param beginGetPasswordOption the option from the original [BeginGetCredentialRequest], for
+     *   which this credential entry is being added
+     * @param displayName the displayName of the account holding the password credential
+     * @param lastUsedTime the last used time the credential underlying this entry was used by the
+     *   user, distinguishable up to the milli second mark only such that if two entries have the
+     *   same millisecond precision, they will be considered to have been used at the same time
+     * @param icon the icon to be displayed with this entry on the selector, if not set, a default
+     *   icon representing a password credential type is set by the library
+     * @param isAutoSelectAllowed whether this entry is allowed to be auto selected if it is the
+     *   only one on the UI, only takes effect if the app requesting for credentials also opts for
+     *   auto select
      * @constructor constructs an instance of [PasswordCredentialEntry]
      * @throws IllegalArgumentException If [username] is empty
      * @throws NullPointerException If [context], [username], [pendingIntent], or
@@ -196,7 +265,8 @@
             ReplaceWith(
                 "PasswordCredentialEntry(context, username, " +
                     "pendingIntent, beginGetPasswordOption, displayName, lastUsedTime, icon, " +
-                    "isAutoSelectAllowed, affiliatedDomain, isDefaultIconPreferredAsSingleProvider)"
+                    "isAutoSelectAllowed, affiliatedDomain, isDefaultIconPreferredAsSingleProvider, " +
+                    "biometricPromptData)"
             ),
         level = DeprecationLevel.HIDDEN
     )
@@ -210,14 +280,14 @@
         icon: Icon = Icon.createWithResource(context, R.drawable.ic_password),
         isAutoSelectAllowed: Boolean = false
     ) : this(
-        username,
-        displayName,
+        username = username,
+        displayName = displayName,
         typeDisplayName = context.getString(R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL),
-        pendingIntent,
-        lastUsedTime,
-        icon,
-        isAutoSelectAllowed,
-        beginGetPasswordOption,
+        pendingIntent = pendingIntent,
+        lastUsedTime = lastUsedTime,
+        icon = icon,
+        isAutoSelectAllowed = isAutoSelectAllowed,
+        beginGetPasswordOption = beginGetPasswordOption,
         isDefaultIconPreferredAsSingleProvider = false
     )
 
@@ -232,6 +302,92 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private object Api35Impl {
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @JvmStatic
+        fun toSlice(entry: PasswordCredentialEntry): Slice {
+            val type = entry.type
+            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
+            Api28Impl.addToSlice(entry, sliceBuilder)
+            addToSlice(entry, sliceBuilder)
+            return sliceBuilder.build()
+        }
+
+        // Given multiple API dependencies, this captures common builds across all API levels > V
+        // and across all subclasses for the toSlice method
+        fun addToSlice(entry: PasswordCredentialEntry, sliceBuilder: Slice.Builder) {
+            val biometricPromptData = entry.biometricPromptData
+            if (biometricPromptData != null) {
+                // TODO(b/353798766) : Remove non bundles once beta users have finalized testing
+                sliceBuilder.addInt(
+                    biometricPromptData.allowedAuthenticators,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_ALLOWED_AUTHENTICATORS)
+                )
+                biometricPromptData.cryptoObject?.let {
+                    sliceBuilder.addLong(
+                        biometricPromptData.cryptoObject.operationHandle,
+                        /*subType=*/ null,
+                        listOf(SLICE_HINT_CRYPTO_OP_ID)
+                    )
+                }
+                val biometricBundle = BiometricPromptData.toBundle(biometricPromptData)
+                sliceBuilder.addBundle(
+                    biometricBundle,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_BIOMETRIC_PROMPT_DATA)
+                )
+            }
+        }
+
+        /**
+         * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+         *
+         * @param slice the [Slice] object constructed through [toSlice]
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+        @JvmStatic
+        fun fromSlice(slice: Slice): PasswordCredentialEntry? {
+            val passwordCredentialEntry = Api28Impl.fromSlice(slice) ?: return null
+            var biometricPromptDataBundle: Bundle? = null
+            slice.items.forEach {
+                if (it.hasHint(SLICE_HINT_BIOMETRIC_PROMPT_DATA)) {
+                    biometricPromptDataBundle = it.bundle
+                }
+            }
+            return try {
+                PasswordCredentialEntry(
+                    username = passwordCredentialEntry.username,
+                    displayName = passwordCredentialEntry.displayName,
+                    typeDisplayName = passwordCredentialEntry.typeDisplayName,
+                    pendingIntent = passwordCredentialEntry.pendingIntent,
+                    lastUsedTime = passwordCredentialEntry.lastUsedTime,
+                    icon = passwordCredentialEntry.icon,
+                    isAutoSelectAllowed = passwordCredentialEntry.isAutoSelectAllowed,
+                    beginGetPasswordOption =
+                        passwordCredentialEntry.beginGetCredentialOption as BeginGetPasswordOption,
+                    entryGroupId = passwordCredentialEntry.entryGroupId,
+                    isDefaultIconPreferredAsSingleProvider =
+                        passwordCredentialEntry.isDefaultIconPreferredAsSingleProvider,
+                    affiliatedDomain = passwordCredentialEntry.affiliatedDomain,
+                    autoSelectAllowedFromOption =
+                        passwordCredentialEntry.isAutoSelectAllowedFromOption,
+                    isCreatedFromSlice = true,
+                    isDefaultIconFromSlice = passwordCredentialEntry.isDefaultIconFromSlice,
+                    biometricPromptData =
+                        if (biometricPromptDataBundle != null)
+                            BiometricPromptData.fromBundle(biometricPromptDataBundle!!)
+                        else null
+                )
+            } catch (e: Exception) {
+                Log.i(TAG, "fromSlice failed with: " + e.message)
+                null
+            }
+        }
+    }
+
     @RequiresApi(28)
     private object Api28Impl {
         @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -248,6 +404,41 @@
         @JvmStatic
         fun toSlice(entry: PasswordCredentialEntry): Slice {
             val type = entry.type
+            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
+            addToSlice(entry, sliceBuilder)
+            return sliceBuilder.build()
+        }
+
+        // Specific to only this custom credential entry, but shared across API levels > P
+        fun addToSlice(entry: PasswordCredentialEntry, sliceBuilder: Slice.Builder) {
+            val beginGetCredentialOption = entry.beginGetCredentialOption
+            val entryGroupId = entry.entryGroupId
+            val isDefaultIconPreferredAsSingleProvider =
+                entry.isDefaultIconPreferredAsSingleProvider
+            val affiliatedDomain = entry.affiliatedDomain
+            val isUsingDefaultIcon =
+                if (isDefaultIconPreferredAsSingleProvider) {
+                    TRUE_STRING
+                } else {
+                    FALSE_STRING
+                }
+            sliceBuilder
+                .addText(
+                    beginGetCredentialOption.id,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_OPTION_ID)
+                )
+                .addText(entryGroupId, /* subTypes= */ null, listOf(SLICE_HINT_DEDUPLICATION_ID))
+                .addText(
+                    isUsingDefaultIcon,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)
+                )
+                .addText(
+                    affiliatedDomain,
+                    /*subTypes=*/ null,
+                    listOf(SLICE_HINT_AFFILIATED_DOMAIN)
+                )
             val title = entry.username
             val subtitle = entry.displayName
             val pendingIntent = entry.pendingIntent
@@ -255,55 +446,18 @@
             val lastUsedTime = entry.lastUsedTime
             val icon = entry.icon
             val isAutoSelectAllowed = entry.isAutoSelectAllowed
-            val beginGetPasswordCredentialOption = entry.beginGetCredentialOption
-            val affiliatedDomain = entry.affiliatedDomain
-            val entryGroupId = entry.entryGroupId
-            var isDefaultIconPreferredAsSingleProvider =
-                entry.isDefaultIconPreferredAsSingleProvider
-
             val autoSelectAllowed =
                 if (isAutoSelectAllowed) {
                     TRUE_STRING
                 } else {
                     FALSE_STRING
                 }
-            val isUsingDefaultIcon =
-                if (isDefaultIconPreferredAsSingleProvider) TRUE_STRING else FALSE_STRING
-            val sliceBuilder =
-                Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
-                    .addText(
-                        typeDisplayName,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
-                    )
-                    .addText(title, /* subType= */ null, listOf(SLICE_HINT_TITLE))
-                    .addText(subtitle, /* subType= */ null, listOf(SLICE_HINT_SUBTITLE))
-                    .addText(
-                        autoSelectAllowed,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_AUTO_ALLOWED)
-                    )
-                    .addText(
-                        beginGetPasswordCredentialOption.id,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_OPTION_ID)
-                    )
-                    .addIcon(icon, /* subType= */ null, listOf(SLICE_HINT_ICON))
-                    .addText(
-                        entryGroupId,
-                        /*subTypes=*/ null,
-                        listOf(SLICE_HINT_DEDUPLICATION_ID)
-                    )
-                    .addText(
-                        affiliatedDomain,
-                        /*subTypes=*/ null,
-                        listOf(SLICE_HINT_AFFILIATED_DOMAIN)
-                    )
-                    .addText(
-                        isUsingDefaultIcon,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)
-                    )
+            sliceBuilder
+                .addText(typeDisplayName, /* subType= */ null, listOf(SLICE_HINT_TYPE_DISPLAY_NAME))
+                .addText(title, /* subType= */ null, listOf(SLICE_HINT_TITLE))
+                .addText(subtitle, /* subType= */ null, listOf(SLICE_HINT_SUBTITLE))
+                .addText(autoSelectAllowed, /* subType= */ null, listOf(SLICE_HINT_AUTO_ALLOWED))
+                .addIcon(icon, /* subType= */ null, listOf(SLICE_HINT_ICON))
             try {
                 if (entry.hasDefaultIcon) {
                     sliceBuilder.addInt(
@@ -313,7 +467,6 @@
                     )
                 }
             } catch (_: IllegalStateException) {}
-
             if (entry.isAutoSelectAllowedFromOption) {
                 sliceBuilder.addInt(
                     /*true=*/ 1,
@@ -335,7 +488,6 @@
                     .build(),
                 /*subType=*/ null
             )
-            return sliceBuilder.build()
         }
 
         /**
@@ -347,33 +499,41 @@
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @JvmStatic
         fun fromSlice(slice: Slice): PasswordCredentialEntry? {
+            var entryGroupId: CharSequence? = null
+            var affiliatedDomain: CharSequence? = null
+            var isDefaultIconPreferredAsSingleProvider = false
+            var beginGetCredentialOptionId: CharSequence? = null
             var typeDisplayName: CharSequence? = null
             var title: CharSequence? = null
-            var subTitle: CharSequence? = null
+            var subtitle: CharSequence? = null
             var icon: Icon? = null
             var pendingIntent: PendingIntent? = null
             var lastUsedTime: Instant? = null
             var autoSelectAllowed = false
             var autoSelectAllowedFromOption = false
-            var beginGetPasswordOptionId: CharSequence? = null
-            var isDefaultIconPreferredAsSingleProvider = false
-            var affiliatedDomain: CharSequence? = null
-            var entryGroupId: CharSequence? = null
             var isDefaultIcon = false
-
             slice.items.forEach {
-                if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
+                if (it.hasHint(SLICE_HINT_OPTION_ID)) {
+                    beginGetCredentialOptionId = it.text
+                } else if (it.hasHint(SLICE_HINT_DEDUPLICATION_ID)) {
+                    entryGroupId = it.text
+                } else if (it.hasHint(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)) {
+                    val defaultIconValue = it.text
+                    if (defaultIconValue == TRUE_STRING) {
+                        isDefaultIconPreferredAsSingleProvider = true
+                    }
+                } else if (it.hasHint(SLICE_HINT_AFFILIATED_DOMAIN)) {
+                    affiliatedDomain = it.text
+                } else if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
                     typeDisplayName = it.text
                 } else if (it.hasHint(SLICE_HINT_TITLE)) {
                     title = it.text
                 } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
-                    subTitle = it.text
+                    subtitle = it.text
                 } else if (it.hasHint(SLICE_HINT_ICON)) {
                     icon = it.icon
                 } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
                     pendingIntent = it.action
-                } else if (it.hasHint(SLICE_HINT_OPTION_ID)) {
-                    beginGetPasswordOptionId = it.text
                 } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
                     lastUsedTime = Instant.ofEpochMilli(it.long)
                 } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
@@ -383,24 +543,14 @@
                     }
                 } else if (it.hasHint(SLICE_HINT_AUTO_SELECT_FROM_OPTION)) {
                     autoSelectAllowedFromOption = true
-                } else if (it.hasHint(SLICE_HINT_AFFILIATED_DOMAIN)) {
-                    affiliatedDomain = it.text
-                } else if (it.hasHint(SLICE_HINT_DEDUPLICATION_ID)) {
-                    entryGroupId = it.text
-                } else if (it.hasHint(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)) {
-                    val defaultIconValue = it.text
-                    if (defaultIconValue == TRUE_STRING) {
-                        isDefaultIconPreferredAsSingleProvider = true
-                    }
                 } else if (it.hasHint(SLICE_HINT_DEFAULT_ICON_RES_ID)) {
                     isDefaultIcon = true
                 }
             }
-
             return try {
                 PasswordCredentialEntry(
                     username = title!!,
-                    displayName = subTitle,
+                    displayName = subtitle,
                     typeDisplayName = typeDisplayName!!,
                     pendingIntent = pendingIntent!!,
                     lastUsedTime = lastUsedTime,
@@ -409,7 +559,7 @@
                     beginGetPasswordOption =
                         BeginGetPasswordOption.createFrom(
                             Bundle(),
-                            beginGetPasswordOptionId!!.toString()
+                            beginGetCredentialOptionId!!.toString()
                         ),
                     entryGroupId = entryGroupId,
                     isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
@@ -428,51 +578,6 @@
     companion object {
         private const val TAG = "PasswordCredentialEntry"
 
-        private const val SLICE_HINT_TYPE_DISPLAY_NAME =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-
-        private const val SLICE_HINT_TITLE =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
-
-        private const val SLICE_HINT_SUBTITLE =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
-
-        private const val SLICE_HINT_DEFAULT_ICON_RES_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEFAULT_ICON_RES_ID"
-
-        private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-
-        private const val SLICE_HINT_ICON =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
-
-        private const val SLICE_HINT_PENDING_INTENT =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
-
-        private const val SLICE_HINT_OPTION_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_OPTION_ID"
-
-        private const val SLICE_HINT_AUTO_ALLOWED =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
-
-        private const val SLICE_HINT_IS_DEFAULT_ICON_PREFERRED =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_IS_DEFAULT_ICON_PREFERRED"
-
-        private const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
-
-        private const val SLICE_HINT_DEDUPLICATION_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEDUPLICATION_ID"
-
-        private const val SLICE_HINT_AFFILIATED_DOMAIN =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AFFILIATED_DOMAIN"
-
-        private const val TRUE_STRING = "true"
-
-        private const val FALSE_STRING = "false"
-
-        private const val REVISION_ID = 1
-
         /**
          * Converts an instance of [PasswordCredentialEntry] to a [Slice].
          *
@@ -482,7 +587,9 @@
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(entry: PasswordCredentialEntry): Slice? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.toSlice(entry)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.toSlice(entry)
             }
             return null
@@ -491,7 +598,9 @@
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         fun fromSlice(slice: Slice): PasswordCredentialEntry? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.fromSlice(slice)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.fromSlice(slice)
             }
             return null
@@ -546,6 +655,7 @@
         private var autoSelectAllowed = false
         private var affiliatedDomain: CharSequence? = null
         private var isDefaultIconPreferredAsSingleProvider: Boolean = false
+        private var biometricPromptData: BiometricPromptData? = null
 
         /** Sets a displayName to be shown on the UI with this entry. */
         fun setDisplayName(displayName: CharSequence?): Builder {
@@ -559,6 +669,18 @@
             return this
         }
 
+        /**
+         * Sets the biometric prompt data to optionally utilize a credential manager flow that
+         * directly handles the biometric verification for you and gives you the response; set to
+         * null by default, indicating the default behavior is to not utilize this embedded
+         * biometric prompt flow.
+         */
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        fun setBiometricPromptData(biometricPromptData: BiometricPromptData): Builder {
+            this.biometricPromptData = biometricPromptData
+            return this
+        }
+
         /** Sets whether the entry should be auto-selected. The value is false by default. */
         @Suppress("MissingGetterMatchingBuilder")
         fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
@@ -605,16 +727,17 @@
             val typeDisplayName =
                 context.getString(R.string.android_credentials_TYPE_PASSWORD_CREDENTIAL)
             return PasswordCredentialEntry(
-                username,
-                displayName,
-                typeDisplayName,
-                pendingIntent,
-                lastUsedTime,
-                icon!!,
-                autoSelectAllowed,
-                beginGetPasswordOption,
+                username = username,
+                displayName = displayName,
+                typeDisplayName = typeDisplayName,
+                pendingIntent = pendingIntent,
+                lastUsedTime = lastUsedTime,
+                icon = icon!!,
+                isAutoSelectAllowed = autoSelectAllowed,
+                beginGetPasswordOption = beginGetPasswordOption,
                 isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
                 affiliatedDomain = affiliatedDomain,
+                biometricPromptData = biometricPromptData,
             )
         }
     }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PendingIntentHandler.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PendingIntentHandler.kt
index e4971fa..58b571cb 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/PendingIntentHandler.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PendingIntentHandler.kt
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package androidx.credentials.provider
 
 import android.app.Activity
@@ -77,26 +76,80 @@
                 Log.i(TAG, "Request not found in pendingIntent")
                 return frameworkReq
             }
+            var biometricPromptResult = retrieveBiometricPromptResult(intent)
+            if (biometricPromptResult == null) {
+                biometricPromptResult = retrieveBiometricPromptResultFallback(intent)
+            }
             return try {
                 ProviderCreateCredentialRequest(
-                    androidx.credentials.CreateCredentialRequest.createFrom(
-                        frameworkReq.type,
-                        frameworkReq.data,
-                        frameworkReq.data,
-                        requireSystemProvider = false,
-                        frameworkReq.callingAppInfo.origin
-                    ),
-                    CallingAppInfo(
-                        frameworkReq.callingAppInfo.packageName,
-                        frameworkReq.callingAppInfo.signingInfo,
-                        frameworkReq.callingAppInfo.origin
-                    )
+                    callingRequest =
+                        androidx.credentials.CreateCredentialRequest.createFrom(
+                            frameworkReq.type,
+                            frameworkReq.data,
+                            frameworkReq.data,
+                            requireSystemProvider = false,
+                            frameworkReq.callingAppInfo.origin
+                        ),
+                    callingAppInfo =
+                        CallingAppInfo(
+                            frameworkReq.callingAppInfo.packageName,
+                            frameworkReq.callingAppInfo.signingInfo,
+                            frameworkReq.callingAppInfo.origin
+                        ),
+                    biometricPromptResult = biometricPromptResult
                 )
             } catch (e: IllegalArgumentException) {
                 return null
             }
         }
 
+        private fun retrieveBiometricPromptResult(
+            intent: Intent,
+            resultKey: String? = AuthenticationResult.EXTRA_BIOMETRIC_AUTH_RESULT_TYPE,
+            errorKey: String? = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR,
+            errorMessageKey: String? = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR_MESSAGE
+        ): BiometricPromptResult? {
+            if (intent.extras == null) {
+                return null
+            }
+            if (intent.extras!!.containsKey(resultKey)) {
+                val authResultType = intent.extras!!.getInt(resultKey)
+                return BiometricPromptResult(
+                    authenticationResult = AuthenticationResult(authResultType)
+                )
+            } else if (intent.extras!!.containsKey(errorKey)) {
+                val authResultError = intent.extras!!.getInt(errorKey)
+                return BiometricPromptResult(
+                    authenticationError =
+                        AuthenticationError(
+                            authResultError,
+                            intent.extras?.getCharSequence(errorMessageKey)
+                        )
+                )
+            }
+            return null
+        }
+
+        private fun retrieveBiometricPromptResultFallback(intent: Intent): BiometricPromptResult? {
+            // TODO(b/353798766) : Remove fallback keys once beta users have finalized testing
+            val fallbackResultKey = AuthenticationResult.EXTRA_BIOMETRIC_AUTH_RESULT_TYPE_FALLBACK
+            val fallbackErrorKey = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR_FALLBACK
+            if (
+                intent.extras != null &&
+                    (intent.extras!!.containsKey(fallbackResultKey) ||
+                        intent.extras!!.containsKey(fallbackErrorKey))
+            ) {
+                return retrieveBiometricPromptResult(
+                    intent,
+                    resultKey = AuthenticationResult.EXTRA_BIOMETRIC_AUTH_RESULT_TYPE_FALLBACK,
+                    errorKey = AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR_FALLBACK,
+                    errorMessageKey =
+                        AuthenticationError.EXTRA_BIOMETRIC_AUTH_ERROR_MESSAGE_FALLBACK
+                )
+            }
+            return null
+        }
+
         /**
          * Extracts the [BeginGetCredentialRequest] from the provider's [PendingIntent] invoked by
          * the Android system when the user selects an [AuthenticationAction].
@@ -160,7 +213,10 @@
                 Log.i(TAG, "Get request from framework is null")
                 return null
             }
-
+            var biometricPromptResult = retrieveBiometricPromptResult(intent)
+            if (biometricPromptResult == null) {
+                biometricPromptResult = retrieveBiometricPromptResultFallback(intent)
+            }
             return ProviderGetCredentialRequest.createFrom(
                 frameworkReq.credentialOptions
                     .stream()
@@ -178,7 +234,8 @@
                     frameworkReq.callingAppInfo.packageName,
                     frameworkReq.callingAppInfo.signingInfo,
                     frameworkReq.callingAppInfo.origin
-                )
+                ),
+                biometricPromptResult
             )
         }
 
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderCreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderCreateCredentialRequest.kt
index 9077238..aae640f 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderCreateCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderCreateCredentialRequest.kt
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package androidx.credentials.provider
 
 import androidx.credentials.CreateCredentialRequest
@@ -25,14 +24,22 @@
  * This request contains the actual request coming from the calling app, and the application
  * information associated with the calling app.
  *
- * @param callingRequest the complete [CreateCredentialRequest] coming from the calling app that is
- *   requesting for credential creation
- * @param callingAppInfo information pertaining to the calling app making the request
  * @constructor constructs an instance of [ProviderCreateCredentialRequest]
+ * @property callingRequest the complete [CreateCredentialRequest] coming from the calling app that
+ *   is requesting for credential creation
+ * @property callingAppInfo information pertaining to the calling app making the request
+ * @property biometricPromptResult the result of a Biometric Prompt authentication flow, that is
+ *   propagated to the provider if the provider requested for
+ *   [androidx.credentials.CredentialManager] to handle the authentication flow
  * @throws NullPointerException If [callingRequest], or [callingAppInfo] is null
  *
  * Note : Credential providers are not expected to utilize the constructor in this class for any
  * production flow. This constructor must only be used for testing purposes.
  */
 class ProviderCreateCredentialRequest
-constructor(val callingRequest: CreateCredentialRequest, val callingAppInfo: CallingAppInfo)
+@JvmOverloads
+constructor(
+    val callingRequest: CreateCredentialRequest,
+    val callingAppInfo: CallingAppInfo,
+    val biometricPromptResult: BiometricPromptResult? = null
+)
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt
index f733715..ab957ed 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/ProviderGetCredentialRequest.kt
@@ -27,28 +27,36 @@
  * set on the [CredentialEntry] that the user selected. The request must be extracted using the
  * [PendingIntentHandler.retrieveProviderGetCredentialRequest] helper API.
  *
- * @param credentialOptions the list of credential retrieval options containing the required
+ * @constructor constructs an instance of [ProviderGetCredentialRequest]
+ * @property credentialOptions the list of credential retrieval options containing the required
  *   parameters, expected to contain a single [CredentialOption] when this request is retrieved from
  *   the [android.app.Activity] invoked by the [android.app.PendingIntent] set on a
  *   [PasswordCredentialEntry] or a [PublicKeyCredentialEntry], or expected to contain multiple
  *   [CredentialOption] when this request is retrieved from the [android.app.Activity] invoked by
  *   the [android.app.PendingIntent] set on a [RemoteEntry]
- * @param callingAppInfo information pertaining to the calling application
+ * @property callingAppInfo information pertaining to the calling application
+ * @property biometricPromptResult the result of a Biometric Prompt authentication flow, that is
+ *   propagated to the provider if the provider requested for
+ *   [androidx.credentials.CredentialManager] to handle the authentication flow
  *
  * Note : Credential providers are not expected to utilize the constructor in this class for any
  * production flow. This constructor must only be used for testing purposes.
- *
- * @constructor constructs an instance of [ProviderGetCredentialRequest]
  */
 class ProviderGetCredentialRequest
-constructor(val credentialOptions: List<CredentialOption>, val callingAppInfo: CallingAppInfo) {
+@JvmOverloads
+constructor(
+    val credentialOptions: List<CredentialOption>,
+    val callingAppInfo: CallingAppInfo,
+    val biometricPromptResult: BiometricPromptResult? = null,
+) {
     internal companion object {
         @JvmStatic
         internal fun createFrom(
             options: List<CredentialOption>,
-            callingAppInfo: CallingAppInfo
+            callingAppInfo: CallingAppInfo,
+            biometricPromptResult: BiometricPromptResult? = null
         ): ProviderGetCredentialRequest {
-            return ProviderGetCredentialRequest(options, callingAppInfo)
+            return ProviderGetCredentialRequest(options, callingAppInfo, biometricPromptResult)
         }
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
index 8d1b8f1..308018a 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
 
 package androidx.credentials.provider
 
@@ -31,6 +32,7 @@
 import androidx.credentials.CredentialOption
 import androidx.credentials.PublicKeyCredential
 import androidx.credentials.R
+import androidx.credentials.provider.PublicKeyCredentialEntry.Api28Impl.toSlice
 import androidx.credentials.provider.PublicKeyCredentialEntry.Companion.toSlice
 import java.time.Instant
 import java.util.Collections
@@ -67,10 +69,14 @@
  *   this entry was created allows this entry to be auto-selected
  * @property hasDefaultIcon whether this entry was created without a custom icon and hence contains
  *   a default icon set by the library, only to be used in Android API levels >= 28
+ * @property biometricPromptData the data that is set optionally to utilize a credential manager
+ *   flow that directly handles the biometric verification and presents back the response; set to
+ *   null by default, so if not opted in, the embedded biometric prompt flow will not show
  * @throws IllegalArgumentException If [username] is empty
  * @see CredentialEntry
  */
-@RequiresApi(26)
+@RequiresApi(23)
+@Suppress("DEPRECATION") // For usage of slice
 class PublicKeyCredentialEntry
 internal constructor(
     val username: CharSequence,
@@ -84,6 +90,7 @@
     isDefaultIconPreferredAsSingleProvider: Boolean,
     entryGroupId: CharSequence? = username,
     affiliatedDomain: CharSequence? = null,
+    biometricPromptData: BiometricPromptData? = null,
     autoSelectAllowedFromOption: Boolean =
         CredentialOption.extractAutoSelectValue(
             beginGetPublicKeyCredentialOption.candidateQueryData
@@ -92,14 +99,14 @@
     private val isDefaultIconFromSlice: Boolean = false,
 ) :
     CredentialEntry(
-        PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
-        beginGetPublicKeyCredentialOption,
-        entryGroupId ?: username,
+        type = PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL,
+        beginGetCredentialOption = beginGetPublicKeyCredentialOption,
+        entryGroupId = entryGroupId ?: username,
         isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
         affiliatedDomain = affiliatedDomain,
+        biometricPromptData = biometricPromptData,
     ) {
     val isAutoSelectAllowedFromOption = autoSelectAllowedFromOption
-
     @get:JvmName("hasDefaultIcon")
     val hasDefaultIcon: Boolean
         get() {
@@ -159,7 +166,61 @@
         lastUsedTime,
         isAutoSelectAllowed,
         beginGetPublicKeyCredentialOption,
-        isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider
+        isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
+    )
+
+    /**
+     * @param context the context of the calling app, required to retrieve fallback resources
+     * @param username the username of the account holding the public key credential
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     *   entry, must be created with a unique request code per entry, with flag
+     *   [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the final request, and
+     *   NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple times
+     * @param beginGetPublicKeyCredentialOption the option from the original
+     *   [BeginGetCredentialRequest], for which this credential entry is being added
+     * @param displayName the displayName of the account holding the public key credential
+     * @param lastUsedTime the last used time the credential underlying this entry was used by the
+     *   user, distinguishable up to the milli second mark only such that if two entries have the
+     *   same millisecond precision, they will be considered to have been used at the same time
+     * @param icon the icon to be displayed with this entry on the selector, if not set, a default
+     *   icon representing a public key credential type is set by the library
+     * @param isAutoSelectAllowed whether this entry is allowed to be auto selected if it is the
+     *   only one on the UI, only takes effect if the app requesting for credentials also opts for
+     *   auto select
+     * @param isDefaultIconPreferredAsSingleProvider when set to true, the UI prefers to render the
+     *   default credential type icon (see the default value of [icon]) when you are the only
+     *   available provider; false by default
+     * @param biometricPromptData the data that is set optionally to utilize a credential manager
+     *   flow that directly handles the biometric verification and presents back the response; set
+     *   to null by default, so if not opted in, the embedded biometric prompt flow will not show
+     * @constructor constructs an instance of [PublicKeyCredentialEntry]
+     * @throws NullPointerException If [context], [username], [pendingIntent], or
+     *   [beginGetPublicKeyCredentialOption] is null
+     * @throws IllegalArgumentException if [username] is empty
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    constructor(
+        context: Context,
+        username: CharSequence,
+        pendingIntent: PendingIntent,
+        beginGetPublicKeyCredentialOption: BeginGetPublicKeyCredentialOption,
+        displayName: CharSequence? = null,
+        lastUsedTime: Instant? = null,
+        icon: Icon = Icon.createWithResource(context, R.drawable.ic_passkey),
+        isAutoSelectAllowed: Boolean = false,
+        isDefaultIconPreferredAsSingleProvider: Boolean = false,
+        biometricPromptData: BiometricPromptData? = null,
+    ) : this(
+        username,
+        displayName,
+        context.getString(R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL),
+        pendingIntent,
+        icon,
+        lastUsedTime,
+        isAutoSelectAllowed,
+        beginGetPublicKeyCredentialOption,
+        isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
+        biometricPromptData = biometricPromptData,
     )
 
     /**
@@ -186,12 +247,12 @@
      * @throws IllegalArgumentException if [username] is empty
      */
     @Deprecated(
-        "Use the constructor that allows setting all parameters.",
+        "Use the constructor with all parameters dependent on API levels",
         replaceWith =
             ReplaceWith(
                 "PublicKeyCredentialEntry(context, username, pendingIntent," +
                     "beginGetPublicKeyCredentialOption, displayName, lastUsedTime, icon, " +
-                    "isAutoSelectAllowed, isDefaultIconPreferredAsSingleProvider)"
+                    "isAutoSelectAllowed, isDefaultIconPreferredAsSingleProvider, biometricPromptData)"
             ),
         level = DeprecationLevel.HIDDEN
     )
@@ -205,20 +266,20 @@
         icon: Icon = Icon.createWithResource(context, R.drawable.ic_passkey),
         isAutoSelectAllowed: Boolean = false,
     ) : this(
-        username,
-        displayName,
-        context.getString(R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL),
-        pendingIntent,
-        icon,
-        lastUsedTime,
-        isAutoSelectAllowed,
-        beginGetPublicKeyCredentialOption,
+        username = username,
+        displayName = displayName,
+        typeDisplayName =
+            context.getString(R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL),
+        pendingIntent = pendingIntent,
+        icon = icon,
+        lastUsedTime = lastUsedTime,
+        isAutoSelectAllowed = isAutoSelectAllowed,
+        beginGetPublicKeyCredentialOption = beginGetPublicKeyCredentialOption,
         isDefaultIconPreferredAsSingleProvider = false
     )
 
     @RequiresApi(34)
     private object Api34Impl {
-
         @JvmStatic
         fun fromCredentialEntry(
             credentialEntry: android.service.credentials.CredentialEntry
@@ -228,6 +289,93 @@
         }
     }
 
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private object Api35Impl {
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @JvmStatic
+        fun toSlice(entry: PublicKeyCredentialEntry): Slice {
+            val type = entry.type
+            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
+            Api28Impl.addToSlice(entry, sliceBuilder)
+            addToSlice(entry, sliceBuilder)
+            return sliceBuilder.build()
+        }
+
+        // Given multiple API dependencies, this captures common builds across all API levels > V
+        // and across all subclasses for the toSlice method
+        fun addToSlice(entry: PublicKeyCredentialEntry, sliceBuilder: Slice.Builder) {
+            val biometricPromptData = entry.biometricPromptData
+            if (biometricPromptData != null) {
+                // TODO(b/353798766) : Remove non bundles once beta users have finalized testing
+                sliceBuilder.addInt(
+                    biometricPromptData.allowedAuthenticators,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_ALLOWED_AUTHENTICATORS)
+                )
+                biometricPromptData.cryptoObject?.let {
+                    sliceBuilder.addLong(
+                        biometricPromptData.cryptoObject.operationHandle,
+                        /*subType=*/ null,
+                        listOf(SLICE_HINT_CRYPTO_OP_ID)
+                    )
+                }
+                val biometricBundle = BiometricPromptData.toBundle(biometricPromptData)
+                sliceBuilder.addBundle(
+                    biometricBundle,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_BIOMETRIC_PROMPT_DATA)
+                )
+            }
+        }
+
+        /**
+         * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+         *
+         * @param slice the [Slice] object constructed through [toSlice]
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+        @JvmStatic
+        fun fromSlice(slice: Slice): PublicKeyCredentialEntry? {
+            val publicKeyCredentialEntry = Api28Impl.fromSlice(slice) ?: return null
+            var biometricPromptDataBundle: Bundle? = null
+            slice.items.forEach {
+                if (it.hasHint(SLICE_HINT_BIOMETRIC_PROMPT_DATA)) {
+                    biometricPromptDataBundle = it.bundle
+                }
+            }
+            return try {
+                PublicKeyCredentialEntry(
+                    username = publicKeyCredentialEntry.username,
+                    displayName = publicKeyCredentialEntry.displayName,
+                    typeDisplayName = publicKeyCredentialEntry.typeDisplayName,
+                    pendingIntent = publicKeyCredentialEntry.pendingIntent,
+                    icon = publicKeyCredentialEntry.icon,
+                    lastUsedTime = publicKeyCredentialEntry.lastUsedTime,
+                    isAutoSelectAllowed = publicKeyCredentialEntry.isAutoSelectAllowed,
+                    beginGetPublicKeyCredentialOption =
+                        publicKeyCredentialEntry.beginGetCredentialOption
+                            as BeginGetPublicKeyCredentialOption,
+                    entryGroupId = publicKeyCredentialEntry.entryGroupId,
+                    isDefaultIconPreferredAsSingleProvider =
+                        publicKeyCredentialEntry.isDefaultIconPreferredAsSingleProvider,
+                    affiliatedDomain = publicKeyCredentialEntry.affiliatedDomain,
+                    autoSelectAllowedFromOption =
+                        publicKeyCredentialEntry.isAutoSelectAllowedFromOption,
+                    isCreatedFromSlice = true,
+                    isDefaultIconFromSlice = publicKeyCredentialEntry.isDefaultIconFromSlice,
+                    biometricPromptData =
+                        if (biometricPromptDataBundle != null)
+                            BiometricPromptData.fromBundle(biometricPromptDataBundle!!)
+                        else null
+                )
+            } catch (e: Exception) {
+                Log.i(TAG, "fromSlice failed with: " + e.message)
+                null
+            }
+        }
+    }
+
     @RequiresApi(28)
     private object Api28Impl {
         @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -244,66 +392,60 @@
         @JvmStatic
         fun toSlice(entry: PublicKeyCredentialEntry): Slice {
             val type = entry.type
-            val title = entry.username
-            val subTitle = entry.displayName
-            val pendingIntent = entry.pendingIntent
-            val typeDisplayName = entry.typeDisplayName
-            val lastUsedTime = entry.lastUsedTime
-            val icon = entry.icon
-            val isAutoSelectAllowed = entry.isAutoSelectAllowed
-            val beginGetPublicKeyCredentialOption = entry.beginGetCredentialOption
+            val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
+            addToSlice(entry, sliceBuilder)
+            return sliceBuilder.build()
+        }
+
+        // Specific to only this custom credential entry, but shared across API levels > P
+        fun addToSlice(entry: PublicKeyCredentialEntry, sliceBuilder: Slice.Builder) {
+            val beginGetCredentialOption = entry.beginGetCredentialOption
             val entryGroupId = entry.entryGroupId
-            val affiliatedDomain = entry.affiliatedDomain
             val isDefaultIconPreferredAsSingleProvider =
                 entry.isDefaultIconPreferredAsSingleProvider
-
-            val autoSelectAllowed =
-                if (isAutoSelectAllowed) {
-                    TRUE_STRING
-                } else {
-                    FALSE_STRING
-                }
+            val affiliatedDomain = entry.affiliatedDomain
             val isUsingDefaultIcon =
                 if (isDefaultIconPreferredAsSingleProvider) {
                     TRUE_STRING
                 } else {
                     FALSE_STRING
                 }
-            val sliceBuilder =
-                Slice.Builder(Uri.EMPTY, SliceSpec(type, REVISION_ID))
-                    .addText(
-                        typeDisplayName,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
-                    )
-                    .addText(title, /* subType= */ null, listOf(SLICE_HINT_TITLE))
-                    .addText(subTitle, /* subType= */ null, listOf(SLICE_HINT_SUBTITLE))
-                    .addText(
-                        autoSelectAllowed,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_AUTO_ALLOWED)
-                    )
-                    .addText(
-                        beginGetPublicKeyCredentialOption.id,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_OPTION_ID)
-                    )
-                    .addIcon(icon, /* subType= */ null, listOf(SLICE_HINT_ICON))
-                    .addText(
-                        entryGroupId,
-                        /*subTypes=*/ null,
-                        listOf(SLICE_HINT_DEDUPLICATION_ID)
-                    )
-                    .addText(
-                        affiliatedDomain,
-                        /*subTypes=*/ null,
-                        listOf(SLICE_HINT_AFFILIATED_DOMAIN)
-                    )
-                    .addText(
-                        isUsingDefaultIcon,
-                        /*subType=*/ null,
-                        listOf(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)
-                    )
+            sliceBuilder
+                .addText(
+                    beginGetCredentialOption.id,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_OPTION_ID)
+                )
+                .addText(entryGroupId, /* subTypes= */ null, listOf(SLICE_HINT_DEDUPLICATION_ID))
+                .addText(
+                    isUsingDefaultIcon,
+                    /*subType=*/ null,
+                    listOf(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)
+                )
+                .addText(
+                    affiliatedDomain,
+                    /*subTypes=*/ null,
+                    listOf(SLICE_HINT_AFFILIATED_DOMAIN)
+                )
+            val title = entry.username
+            val subtitle = entry.displayName
+            val pendingIntent = entry.pendingIntent
+            val typeDisplayName = entry.typeDisplayName
+            val lastUsedTime = entry.lastUsedTime
+            val icon = entry.icon
+            val isAutoSelectAllowed = entry.isAutoSelectAllowed
+            val autoSelectAllowed =
+                if (isAutoSelectAllowed) {
+                    TRUE_STRING
+                } else {
+                    FALSE_STRING
+                }
+            sliceBuilder
+                .addText(typeDisplayName, /* subType= */ null, listOf(SLICE_HINT_TYPE_DISPLAY_NAME))
+                .addText(title, /* subType= */ null, listOf(SLICE_HINT_TITLE))
+                .addText(subtitle, /* subType= */ null, listOf(SLICE_HINT_SUBTITLE))
+                .addText(autoSelectAllowed, /* subType= */ null, listOf(SLICE_HINT_AUTO_ALLOWED))
+                .addIcon(icon, /* subType= */ null, listOf(SLICE_HINT_ICON))
             try {
                 if (entry.hasDefaultIcon) {
                     sliceBuilder.addInt(
@@ -313,7 +455,6 @@
                     )
                 }
             } catch (_: IllegalStateException) {}
-
             if (entry.isAutoSelectAllowedFromOption) {
                 sliceBuilder.addInt(
                     /*true=*/ 1,
@@ -335,7 +476,6 @@
                     .build(),
                 /*subType=*/ null
             )
-            return sliceBuilder.build()
         }
 
         /**
@@ -347,6 +487,10 @@
         @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
         @JvmStatic
         fun fromSlice(slice: Slice): PublicKeyCredentialEntry? {
+            var entryGroupId: CharSequence? = null
+            var affiliatedDomain: CharSequence? = null
+            var isDefaultIconPreferredAsSingleProvider = false
+            var beginGetCredentialOptionId: CharSequence? = null
             var typeDisplayName: CharSequence? = null
             var title: CharSequence? = null
             var subtitle: CharSequence? = null
@@ -354,15 +498,21 @@
             var pendingIntent: PendingIntent? = null
             var lastUsedTime: Instant? = null
             var autoSelectAllowed = false
-            var beginGetPublicKeyCredentialOptionId: CharSequence? = null
             var autoSelectAllowedFromOption = false
-            var isDefaultIconPreferredAsSingleProvider = false
             var isDefaultIcon = false
-            var entryGroupId: CharSequence? = null
-            var affiliatedDomain: CharSequence? = null
-
             slice.items.forEach {
-                if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
+                if (it.hasHint(SLICE_HINT_OPTION_ID)) {
+                    beginGetCredentialOptionId = it.text
+                } else if (it.hasHint(SLICE_HINT_DEDUPLICATION_ID)) {
+                    entryGroupId = it.text
+                } else if (it.hasHint(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)) {
+                    val defaultIconValue = it.text
+                    if (defaultIconValue == TRUE_STRING) {
+                        isDefaultIconPreferredAsSingleProvider = true
+                    }
+                } else if (it.hasHint(SLICE_HINT_AFFILIATED_DOMAIN)) {
+                    affiliatedDomain = it.text
+                } else if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
                     typeDisplayName = it.text
                 } else if (it.hasHint(SLICE_HINT_TITLE)) {
                     title = it.text
@@ -372,8 +522,6 @@
                     icon = it.icon
                 } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
                     pendingIntent = it.action
-                } else if (it.hasHint(SLICE_HINT_OPTION_ID)) {
-                    beginGetPublicKeyCredentialOptionId = it.text
                 } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
                     lastUsedTime = Instant.ofEpochMilli(it.long)
                 } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
@@ -383,20 +531,10 @@
                     }
                 } else if (it.hasHint(SLICE_HINT_AUTO_SELECT_FROM_OPTION)) {
                     autoSelectAllowedFromOption = true
-                } else if (it.hasHint(SLICE_HINT_IS_DEFAULT_ICON_PREFERRED)) {
-                    val defaultIconValue = it.text
-                    if (defaultIconValue == TRUE_STRING) {
-                        isDefaultIconPreferredAsSingleProvider = true
-                    }
                 } else if (it.hasHint(SLICE_HINT_DEFAULT_ICON_RES_ID)) {
                     isDefaultIcon = true
-                } else if (it.hasHint(SLICE_HINT_DEDUPLICATION_ID)) {
-                    entryGroupId = it.text
-                } else if (it.hasHint(SLICE_HINT_AFFILIATED_DOMAIN)) {
-                    affiliatedDomain = it.text
                 }
             }
-
             return try {
                 PublicKeyCredentialEntry(
                     username = title!!,
@@ -409,7 +547,7 @@
                     beginGetPublicKeyCredentialOption =
                         BeginGetPublicKeyCredentialOption.createFromEntrySlice(
                             Bundle(),
-                            beginGetPublicKeyCredentialOptionId!!.toString(),
+                            beginGetCredentialOptionId!!.toString(),
                         ),
                     entryGroupId = entryGroupId,
                     isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
@@ -428,51 +566,6 @@
     companion object {
         private const val TAG = "PublicKeyCredEntry"
 
-        private const val SLICE_HINT_TYPE_DISPLAY_NAME =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-
-        private const val SLICE_HINT_TITLE =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
-
-        private const val SLICE_HINT_SUBTITLE =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
-
-        private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-
-        private const val SLICE_HINT_ICON =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
-
-        private const val SLICE_HINT_PENDING_INTENT =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
-
-        private const val SLICE_HINT_AUTO_ALLOWED =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
-
-        private const val SLICE_HINT_IS_DEFAULT_ICON_PREFERRED =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_IS_DEFAULT_ICON_PREFERRED"
-
-        private const val SLICE_HINT_OPTION_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_OPTION_ID"
-
-        private const val SLICE_HINT_AUTO_SELECT_FROM_OPTION =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_SELECT_FROM_OPTION"
-
-        private const val SLICE_HINT_DEFAULT_ICON_RES_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEFAULT_ICON_RES_ID"
-
-        private const val SLICE_HINT_AFFILIATED_DOMAIN =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AFFILIATED_DOMAIN"
-
-        private const val SLICE_HINT_DEDUPLICATION_ID =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_DEDUPLICATION_ID"
-
-        private const val TRUE_STRING = "true"
-
-        private const val FALSE_STRING = "false"
-
-        private const val REVISION_ID = 1
-
         /**
          * Converts an instance of [PublicKeyCredentialEntry] to a [Slice].
          *
@@ -482,14 +575,16 @@
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(entry: PublicKeyCredentialEntry): Slice? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.toSlice(entry)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.toSlice(entry)
             }
             return null
         }
 
         /**
-         * Returns an instance of [CustomCredentialEntry] derived from a [Slice] object.
+         * Returns an instance of [PublicKeyCredentialEntry] derived from a [Slice] object.
          *
          * @param slice the [Slice] object constructed through [toSlice]
          */
@@ -497,7 +592,9 @@
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun fromSlice(slice: Slice): PublicKeyCredentialEntry? {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 35) {
+                return Api35Impl.fromSlice(slice)
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 return Api28Impl.fromSlice(slice)
             }
             return null
@@ -536,6 +633,7 @@
         private var icon: Icon? = null
         private var autoSelectAllowed: Boolean = false
         private var isDefaultIconPreferredAsSingleProvider: Boolean = false
+        private var biometricPromptData: BiometricPromptData? = null
 
         /** Sets a displayName to be shown on the UI with this entry */
         fun setDisplayName(displayName: CharSequence?): Builder {
@@ -549,6 +647,18 @@
             return this
         }
 
+        /**
+         * Sets the biometric prompt data to optionally utilize a credential manager flow that
+         * directly handles the biometric verification for you and gives you the response; set to
+         * null by default, indicating the default behavior is to not utilize this embedded
+         * biometric prompt flow.
+         */
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        fun setBiometricPromptData(biometricPromptData: BiometricPromptData): Builder {
+            this.biometricPromptData = biometricPromptData
+            return this
+        }
+
         /** Sets whether the entry should be auto-selected. The value is false by default */
         @Suppress("MissingGetterMatchingBuilder")
         fun setAutoSelectAllowed(autoSelectAllowed: Boolean): Builder {
@@ -585,15 +695,16 @@
             val typeDisplayName =
                 context.getString(R.string.androidx_credentials_TYPE_PUBLIC_KEY_CREDENTIAL)
             return PublicKeyCredentialEntry(
-                username,
-                displayName,
-                typeDisplayName,
-                pendingIntent,
-                icon!!,
-                lastUsedTime,
-                autoSelectAllowed,
-                beginGetPublicKeyCredentialOption,
-                isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider
+                username = username,
+                displayName = displayName,
+                typeDisplayName = typeDisplayName,
+                pendingIntent = pendingIntent,
+                icon = icon!!,
+                lastUsedTime = lastUsedTime,
+                isAutoSelectAllowed = autoSelectAllowed,
+                beginGetPublicKeyCredentialOption = beginGetPublicKeyCredentialOption,
+                isDefaultIconPreferredAsSingleProvider = isDefaultIconPreferredAsSingleProvider,
+                biometricPromptData = biometricPromptData,
             )
         }
     }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
index 1ee7c00..d542e03 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:Suppress("deprecation") // For usage of Slice
+
 package androidx.credentials.provider
 
 import android.annotation.SuppressLint
diff --git a/credentials/credentials/src/main/res/values-hy/strings.xml b/credentials/credentials/src/main/res/values-hy/strings.xml
index 617300a0..2dfb774f 100644
--- a/credentials/credentials/src/main/res/values-hy/strings.xml
+++ b/credentials/credentials/src/main/res/values-hy/strings.xml
@@ -17,6 +17,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Անցաբառ"</string>
+    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Մուտքի բանալի"</string>
     <string name="android.credentials.TYPE_PASSWORD_CREDENTIAL" msgid="8397015543330865059">"Գաղտնաբառ"</string>
 </resources>
diff --git a/credentials/credentials/src/main/res/values-nb/strings.xml b/credentials/credentials/src/main/res/values-nb/strings.xml
index a72318a..9eb70fe 100644
--- a/credentials/credentials/src/main/res/values-nb/strings.xml
+++ b/credentials/credentials/src/main/res/values-nb/strings.xml
@@ -17,6 +17,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Tilgangsnøkkel"</string>
+    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Passnøkkel"</string>
     <string name="android.credentials.TYPE_PASSWORD_CREDENTIAL" msgid="8397015543330865059">"Passord"</string>
 </resources>
diff --git a/credentials/credentials/src/main/res/values-tr/strings.xml b/credentials/credentials/src/main/res/values-tr/strings.xml
index f00b298..02256b8 100644
--- a/credentials/credentials/src/main/res/values-tr/strings.xml
+++ b/credentials/credentials/src/main/res/values-tr/strings.xml
@@ -17,6 +17,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Şifre anahtarı"</string>
+    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Geçiş anahtarı"</string>
     <string name="android.credentials.TYPE_PASSWORD_CREDENTIAL" msgid="8397015543330865059">"Şifre"</string>
 </resources>
diff --git a/credentials/credentials/src/main/res/values-uz/strings.xml b/credentials/credentials/src/main/res/values-uz/strings.xml
index 7f1bb8c..77100e0 100644
--- a/credentials/credentials/src/main/res/values-uz/strings.xml
+++ b/credentials/credentials/src/main/res/values-uz/strings.xml
@@ -17,6 +17,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Kod"</string>
+    <string name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" msgid="3929015085059320822">"Kirish kaliti"</string>
     <string name="android.credentials.TYPE_PASSWORD_CREDENTIAL" msgid="8397015543330865059">"Parol"</string>
 </resources>
diff --git a/credentials/credentials/src/main/res/values/ids.xml b/credentials/credentials/src/main/res/values/ids.xml
new file mode 100644
index 0000000..3aac762
--- /dev/null
+++ b/credentials/credentials/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item name="androidx_credential_pendingCredentialRequest" type="id"/>
+</resources>
\ No newline at end of file
diff --git a/cursoradapter/cursoradapter/build.gradle b/cursoradapter/cursoradapter/build.gradle
index 6e6dcb7..75d916b 100644
--- a/cursoradapter/cursoradapter/build.gradle
+++ b/cursoradapter/cursoradapter/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
@@ -21,7 +21,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/customview/customview-poolingcontainer/build.gradle b/customview/customview-poolingcontainer/build.gradle
index c4f373f..65371f4 100644
--- a/customview/customview-poolingcontainer/build.gradle
+++ b/customview/customview-poolingcontainer/build.gradle
@@ -48,7 +48,6 @@
     mavenVersion = LibraryVersions.CUSTOMVIEW_POOLINGCONTAINER
     inceptionYear = "2021"
     description = "Utilities for listening to the lifecycle of containers that manage their child Views' lifecycle, such as RecyclerView"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/customview/customview/build.gradle b/customview/customview/build.gradle
index 5b192b5..55666fe 100644
--- a/customview/customview/build.gradle
+++ b/customview/customview/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.3.0")
     implementation("androidx.collection:collection:1.1.0")
 
@@ -33,7 +33,6 @@
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/datastore/datastore-compose-samples/build.gradle b/datastore/datastore-compose-samples/build.gradle
index 2e5f736..07d5fe6 100644
--- a/datastore/datastore-compose-samples/build.gradle
+++ b/datastore/datastore-compose-samples/build.gradle
@@ -54,6 +54,7 @@
     implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
 }
 android {
+    compileSdk 35
     namespace 'com.example.datastorecomposesamples'
     defaultConfig {
         minSdk 28
diff --git a/datastore/datastore-core-okio/bcv/native/current.txt b/datastore/datastore-core-okio/bcv/native/current.txt
index 68573e5..a4e9063 100644
--- a/datastore/datastore-core-okio/bcv/native/current.txt
+++ b/datastore/datastore-core-okio/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/datastore/datastore-core-okio/build.gradle b/datastore/datastore-core-okio/build.gradle
index 77da1fe..dc55a3d 100644
--- a/datastore/datastore-core-okio/build.gradle
+++ b/datastore/datastore-core-okio/build.gradle
@@ -31,8 +31,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm()
     mac()
     linux()
@@ -101,4 +99,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android DataStore Core Okio- contains APIs to use datastore-core in multiplatform via okio"
+    metalavaK2UastEnabled = false
 }
diff --git a/datastore/datastore-core/bcv/native/current.txt b/datastore/datastore-core/bcv/native/current.txt
index 9478e80..76b70d8 100644
--- a/datastore/datastore-core/bcv/native/current.txt
+++ b/datastore/datastore-core/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/datastore/datastore-core/build.gradle b/datastore/datastore-core/build.gradle
index 57876b2..0a0a304 100644
--- a/datastore/datastore-core/build.gradle
+++ b/datastore/datastore-core/build.gradle
@@ -63,8 +63,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm()
     mac()
     linux()
@@ -82,7 +80,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
-                api("androidx.annotation:annotation:1.7.0")
+                api(project(":annotation:annotation"))
             }
         }
 
@@ -178,4 +176,5 @@
     inceptionYear = "2020"
     description = "Android DataStore Core - contains the underlying store used by each serialization method"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/datastore/datastore-preferences-core/bcv/native/current.txt b/datastore/datastore-preferences-core/bcv/native/current.txt
index fbf1501..3953afe 100644
--- a/datastore/datastore-preferences-core/bcv/native/current.txt
+++ b/datastore/datastore-preferences-core/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/datastore/datastore-preferences-core/build.gradle b/datastore/datastore-preferences-core/build.gradle
index 18d894a..94c61c8 100644
--- a/datastore/datastore-preferences-core/build.gradle
+++ b/datastore/datastore-preferences-core/build.gradle
@@ -31,8 +31,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm() {
         withJava()
     }
@@ -117,4 +115,5 @@
     inceptionYear = "2020"
     description = "Android Preferences DataStore without the Android Dependencies"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/datastore/datastore-preferences-rxjava2/build.gradle b/datastore/datastore-preferences-rxjava2/build.gradle
index faa866e..aef9070 100644
--- a/datastore/datastore-preferences-rxjava2/build.gradle
+++ b/datastore/datastore-preferences-rxjava2/build.gradle
@@ -40,7 +40,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.rxjava2)
 
     api(project(":datastore:datastore"))
@@ -68,5 +68,4 @@
     inceptionYear = "2020"
     description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
 }
diff --git a/datastore/datastore-preferences-rxjava3/build.gradle b/datastore/datastore-preferences-rxjava3/build.gradle
index 52f6567..d168af4 100644
--- a/datastore/datastore-preferences-rxjava3/build.gradle
+++ b/datastore/datastore-preferences-rxjava3/build.gradle
@@ -40,7 +40,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.rxjava3)
 
     api(project(":datastore:datastore"))
@@ -68,5 +68,4 @@
     inceptionYear = "2020"
     description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
 }
diff --git a/datastore/datastore-preferences/bcv/native/current.txt b/datastore/datastore-preferences/bcv/native/current.txt
index b5ddb34..68d64eb 100644
--- a/datastore/datastore-preferences/bcv/native/current.txt
+++ b/datastore/datastore-preferences/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/datastore/datastore-preferences/build.gradle b/datastore/datastore-preferences/build.gradle
index e8e22c8..6fcf34e 100644
--- a/datastore/datastore-preferences/build.gradle
+++ b/datastore/datastore-preferences/build.gradle
@@ -33,8 +33,6 @@
     namespace "androidx.datastore.preferences"
 }
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm()
     mac()
     linux()
diff --git a/datastore/datastore-rxjava2/build.gradle b/datastore/datastore-rxjava2/build.gradle
index 47dae21..db861b7 100644
--- a/datastore/datastore-rxjava2/build.gradle
+++ b/datastore/datastore-rxjava2/build.gradle
@@ -40,7 +40,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.rxjava2)
 
     api(project(":datastore:datastore"))
@@ -64,5 +64,4 @@
     inceptionYear = "2020"
     description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
 }
diff --git a/datastore/datastore-rxjava3/build.gradle b/datastore/datastore-rxjava3/build.gradle
index ba40189..58fe0d7 100644
--- a/datastore/datastore-rxjava3/build.gradle
+++ b/datastore/datastore-rxjava3/build.gradle
@@ -40,7 +40,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.rxjava3)
 
     api(project(":datastore:datastore"))
@@ -64,5 +64,4 @@
     inceptionYear = "2020"
     description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
 }
diff --git a/datastore/datastore/bcv/native/current.txt b/datastore/datastore/bcv/native/current.txt
index 3960e71..3c9cae7 100644
--- a/datastore/datastore/bcv/native/current.txt
+++ b/datastore/datastore/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/datastore/datastore/build.gradle b/datastore/datastore/build.gradle
index dd4b6a2..c59b19f 100644
--- a/datastore/datastore/build.gradle
+++ b/datastore/datastore/build.gradle
@@ -34,8 +34,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm()
     mac()
     ios()
@@ -49,7 +47,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
-                api("androidx.annotation:annotation:1.2.0")
+                api(project(":annotation:annotation"))
                 api(project(":datastore:datastore-core"))
                 api(project(":datastore:datastore-core-okio"))
             }
diff --git a/datastore/datastore/src/androidMain/kotlin/androidx/datastore/migrations/SharedPreferencesMigration.android.kt b/datastore/datastore/src/androidMain/kotlin/androidx/datastore/migrations/SharedPreferencesMigration.android.kt
index 55c02ee..df850ea 100644
--- a/datastore/datastore/src/androidMain/kotlin/androidx/datastore/migrations/SharedPreferencesMigration.android.kt
+++ b/datastore/datastore/src/androidMain/kotlin/androidx/datastore/migrations/SharedPreferencesMigration.android.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.content.SharedPreferences
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.datastore.core.DataMigration
 import java.io.File
@@ -205,7 +204,6 @@
     @RequiresApi(24)
     private object Api24Impl {
         @JvmStatic
-        @DoNotInline
         fun deleteSharedPreferences(context: Context, name: String): Boolean {
             return context.deleteSharedPreferences(name)
         }
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 0a37038..a732f26 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -153,7 +153,7 @@
 WARN: .*\/unzippedJvmSources\/androidx\/wear\/watchface\/ComplicationSlot\.kt:[0-9]+ Missing @param tag for parameter `boundingArc` in DFunction createEdgeComplicationSlotBuilder
 WARN: .*\/unzippedJvmSources\/androidx\/wear\/watchface\/ComplicationSlot\.kt:[0-9]+ Missing @param tag for parameter `complicationTapFilter` in DFunction createEdgeComplicationSlotBuilder
 WARN: .*\/unzippedJvmSources\/androidx\/wear\/watchface\/style\/CurrentUserStyleRepository\.kt:[0-9]+ Unable to find reference @param copySelectedOptions in DClass UserStyle\. Are you trying to refer to something not visible to users\?
-WARN\: .*\/unzippedMultiplatformSources\/nonJvmMain\/androidx\/lifecycle\/LifecycleRegistry\.nonJvm\.kt\:[0-9]+ Link does not resolve for \@throws IllegalStateException in DFunction addObserver\. Is it from a package that the containing file does not import\? Are docs inherited by an un\-documented override function\, but the exception class is not in scope in the inheriting class\? The general fix for these is to fully qualify the exception name\, e\.g\. \`\@throws java\.io\.IOException under some conditions\`\.
+WARN: .*\/unzippedMultiplatformSources\/nonJvm.*Main\/androidx\/lifecycle\/LifecycleRegistry\.nonJvm\.kt\:[0-9]+ Link does not resolve for \@throws IllegalStateException in DFunction addObserver\. Is it from a package that the containing file does not import\? Are docs inherited by an un\-documented override function\, but the exception class is not in scope in the inheriting class\? The general fix for these is to fully qualify the exception name\, e\.g\. \`\@throws java\.io\.IOException under some conditions\`\.
 WARN: .*\/unzippedJvmSources\/androidx\/webkit\/CookieManagerCompat\.java:[0-9]+ Missing @param tag for parameter `cookieManager` in DFunction getCookieInfo
 WARN: .*\/unzippedJvmSources\/androidx\/webkit\/WebSettingsCompat\.java:[0-9]+ Missing @param tag for parameter `settings` in DFunction setSafeBrowsingEnabled
 WARN: .*\/unzippedJvmSources\/androidx\/webkit\/WebSettingsCompat\.java:[0-9]+ Missing @param tag for parameter `settings` in DFunction setDisabledActionModeMenuItems
@@ -228,6 +228,9 @@
 WARN: File location could not be determined\. Failed to resolve See <a href="https:\/\/developer\.android\.com\/training\/transitions">Transition API Guide<\/a> in DPackage androidx\.transition
 [0-9]+ cpus, so not storing build metrics.
 64 cpus, so storing build metrics.
+# b/351805708: upstream bug in Kotlin: can't have a nested class with different expect from parent
+ERROR: An attempt to write .*\/reference\/androidx\/lifecycle\/ViewModelProvider\.NewInstanceFactory\.html several times!
+ERROR: An attempt to write .*\/reference\/kotlin\/androidx\/lifecycle\/ViewModelProvider\.NewInstanceFactory\.html several times!
 # > Task :compose:ui:ui-tooling:processDebugAndroidTestManifest
 \$SUPPORT/compose/ui/ui\-tooling/src/androidInstrumentedTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
 # ./gradlew tasks warns as we have warnings present
@@ -295,6 +298,8 @@
 docs-public:docs completing
 docs-tip-of-tree:docs starting
 docs-tip-of-tree:docs completing
+# > Configure project :androidx-demos
+The current default is 'false'\.
 # Investigate more b/325465332
 # > Task :room:integration-tests:room-testapp-multiplatform:ksp.*
 i: \[ksp\] loaded provider\(s\): \[androidx\.room\.RoomKspProcessor\$Provider\]
@@ -307,4 +312,15 @@
 objc\[[0-9]+\]: Class AMSupportURLSession is implemented in both /usr/lib/libauthinstall\.dylib \([0-9]+x[0-9]+f[0-9]+cf[0-9]+\) and /System/Library/PrivateFrameworks/MobileDevice\.framework/Versions/A/MobileDevice \([0-9]+x[0-9]+e[0-9]+bc[0-9]+\)\. One of the two will be used\. Which one is undefined\.
 # > Task :collection:collection-benchmark:assembleAndroidXDarwinBenchmarksReleaseXCFramework
 objc\[[0-9]+\]: Class AMSupportURLConnectionDelegate is implemented in both /usr/lib/libauthinstall\.dylib \([0-9]+x[0-9]+f[0-9]+cf[0-9]+e[0-9]+\) and /System/Library/PrivateFrameworks/MobileDevice\.framework/Versions/A/MobileDevice \([0-9]+x[0-9]+c[0-9]+c[0-9]+\)\. One of the two will be used\. Which one is undefined\.
-objc\[[0-9]+\]: Class AMSupportURLSession is implemented in both /usr/lib/libauthinstall\.dylib \([0-9]+x[0-9]+f[0-9]+cf[0-9]+\) and /System/Library/PrivateFrameworks/MobileDevice\.framework/Versions/A/MobileDevice \([0-9]+x[0-9]+c[0-9]+\)\. One of the two will be used\. Which one is undefined\.
\ No newline at end of file
+objc\[[0-9]+\]: Class AMSupportURLSession is implemented in both /usr/lib/libauthinstall\.dylib \([0-9]+x[0-9]+f[0-9]+cf[0-9]+\) and /System/Library/PrivateFrameworks/MobileDevice\.framework/Versions/A/MobileDevice \([0-9]+x[0-9]+c[0-9]+\)\. One of the two will be used\. Which one is undefined\.
+# > Task :privacysandbox:sdkruntime:integration-tests:testsdk-asb:minifyBundleWithR8
+WARNING: R[0-9]+: Missing class android\.os\.ProfilingManager \(referenced from: android\.os\.ProfilingManager androidx\.core\.os\.Profiling\$registerForAllProfilingResults\$[0-9]+\$[0-9]+\.\$service and [0-9]+ other contexts\)
+Missing class android\.os\.ProfilingResult \(referenced from: java\.lang\.Object androidx\.core\.os\.Profiling\$registerForAllProfilingResults\$[0-9]+\.invokeSuspend\(java\.lang\.Object\) and [0-9]+ other context\)
+Missing class android\.os\.ProfilingResult \(referenced from: java\.lang\.Object androidx\.core\.os\.Profiling\$registerForAllProfilingResults\$[0-9]+\.invokeSuspend\(java\.lang\.Object\) and [0-9]+ other contexts\)
+New 'wasm' target is Work-in-Progress and is subject to change without notice.
+Yarn packages will be fetched from the offline mirror.*
+# Kotlin executes Yarn with the --ignore-scripts flag: https://github.com/JetBrains/kotlin/blob/master/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt
+# causing Yarn to throw the warning: https://github.com/yarnpkg/yarn/blob/master/src/reporters/lang/en.js#L105
+warning Ignored scripts due to flag.
+# > Task :camera:camera-camera2-pipe:kspReleaseKotlin
+exception: info: \[ksp\] loaded provider\(s\): \[dagger\.internal\.codegen\.KspComponentProcessor\$Provider\]
diff --git a/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt b/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
index 1e8cb27..cccd79d 100644
--- a/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
+++ b/development/importMaven/src/main/kotlin/androidx/build/importMaven/ArtifactResolver.kt
@@ -40,6 +40,7 @@
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
+import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetAttribute
 import org.jetbrains.kotlin.konan.target.KonanTarget
 
 /**
@@ -411,6 +412,22 @@
                     }
                 }
             }
+
+            val wasmJs = KOTlIN_USAGES.map { kotlinUsage ->
+                createConfiguration(*dependencies) {
+                    attributes.apply {
+                        attribute(KotlinPlatformType.attribute, KotlinPlatformType.wasm)
+                        attribute(Usage.USAGE_ATTRIBUTE, kotlinUsage)
+                        attribute(
+                            KotlinWasmTargetAttribute.wasmTargetAttribute,
+                            KotlinWasmTargetAttribute.js
+                        )
+                        attribute(Category.CATEGORY_ATTRIBUTE, Category.LIBRARY)
+                        attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, "non-jvm")
+                    }
+                }
+            }
+
             val commonArtifacts = KOTlIN_USAGES.map { kotlinUsage ->
                 createConfiguration(*dependencies) {
                     attributes.apply {
@@ -420,7 +437,7 @@
                     }
                 }
             }
-            return jvmAndAndroid + konanTargetConfigurations + commonArtifacts
+            return jvmAndAndroid + wasmJs + konanTargetConfigurations + commonArtifacts
         }
 
         private fun createKonanTargetConfiguration(
diff --git a/development/importMaven/src/main/kotlin/androidx/build/importMaven/CustomMetadataRules.kt b/development/importMaven/src/main/kotlin/androidx/build/importMaven/CustomMetadataRules.kt
index 60abae9..a5f040d 100644
--- a/development/importMaven/src/main/kotlin/androidx/build/importMaven/CustomMetadataRules.kt
+++ b/development/importMaven/src/main/kotlin/androidx/build/importMaven/CustomMetadataRules.kt
@@ -35,6 +35,8 @@
                 it.addFile("${id.name}-${id.version}-sources.jar")
                 // if it does not have gradle metadata, we might miss aar; add it
                 it.addFile("${id.name}-${id.version}.aar")
+                // if it does not have gradle metadata, we might miss klib; add it
+                it.addFile("${id.name}-${id.version}.klib")
                 // pom.asc files are not always resolved when .module files are present
                 it.addFile("${id.name}-${id.version}.pom.asc")
             }
diff --git a/development/project-creator/compose-template/groupId/artifactId/build.gradle b/development/project-creator/compose-template/groupId/artifactId/build.gradle
index ce8f88d..9cde1b1 100644
--- a/development/project-creator/compose-template/groupId/artifactId/build.gradle
+++ b/development/project-creator/compose-template/groupId/artifactId/build.gradle
@@ -21,6 +21,7 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
+
 import androidx.build.LibraryType
 import androidx.build.PlatformIdentifier
 
@@ -32,18 +33,17 @@
 
 androidXMultiplatform {
     android()
-    desktop()
+    jvmStubs()
+    linuxX64Stubs()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
     sourceSets {
         commonMain {
             dependencies {
-                implementation(libs.kotlinStdlibCommon)
+                implementation(libs.kotlinStdlib)
             }
         }
-        androidMain.dependencies {
-        }
 
         commonTest {
             dependencies {
@@ -60,18 +60,10 @@
             }
         }
 
-
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
-            }
-        }
-
-        desktopMain {
-            dependsOn(jvmMain)
-            dependencies {
-                implementation(libs.kotlinStdlib)
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
@@ -91,10 +83,16 @@
             }
         }
 
-        desktopTest {
-            dependsOn(jvmTest)
-            dependencies {
-            }
+        commonStubsMain {
+            dependsOn(commonMain)
+        }
+
+        jvmStubsMain {
+            dependsOn(commonStubsMain)
+        }
+
+        linuxx64StubsMain {
+            dependsOn(commonStubsMain)
         }
     }
 }
diff --git a/development/studio/idea.properties b/development/studio/idea.properties
index 6f9146c..d4650ca 100644
--- a/development/studio/idea.properties
+++ b/development/studio/idea.properties
@@ -184,7 +184,7 @@
 #-----------------------------------------------------------------------
 # Enable internal actions menu
 #-----------------------------------------------------------------------
-idea.is.internal=false
+idea.is.internal=true
 
 #-----------------------------------------------------------------------
 # Disable automatic update checks
diff --git a/development/update-verification-metadata.sh b/development/update-verification-metadata.sh
index 46e959a..ee42feb 100755
--- a/development/update-verification-metadata.sh
+++ b/development/update-verification-metadata.sh
@@ -69,7 +69,11 @@
   fi
 
   # next, remove 'version=' lines https://github.com/gradle/gradle/issues/20192
-  sed -i 's/\(trusted-key.*\)version="[^"]*"/\1/' gradle/verification-metadata.xml
+  if [ "$(uname)" = "Darwin" ]; then
+      sed -i '' 's/\(trusted-key.*\)version="[^"]*"/\1/' gradle/verification-metadata.xml
+  else
+      sed -i 's/\(trusted-key.*\)version="[^"]*"/\1/' gradle/verification-metadata.xml
+  fi
 
   # rename keyring
   if [ "$dryrun" == "true" ]; then
diff --git a/development/update_studio.sh b/development/update_studio.sh
index b1c814e..a17838f 100755
--- a/development/update_studio.sh
+++ b/development/update_studio.sh
@@ -7,8 +7,8 @@
 
 # Versions that the user should update when running this script
 echo Getting Studio version and link
-AGP_VERSION=${1:-8.6.0-alpha07}
-STUDIO_VERSION_STRING=${2:-"Android Studio Koala Feature Drop | 2024.1.2 Canary 7"}
+AGP_VERSION=${1:-8.6.0-beta01}
+STUDIO_VERSION_STRING=${2:-"Android Studio Koala Feature Drop | 2024.1.2 Beta 1"}
 
 # Get studio version number from version name
 STUDIO_IFRAME_LINK=`curl "https://developer.android.com/studio/archive.html" | grep "<iframe " | sed "s/.* src=\"\([^\"]*\)\".*/\1/g"`
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 66a9dd9..40a5eb5 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -22,7 +22,7 @@
     docsWithoutApiSince("androidx.ads:ads-identifier:1.0.0-alpha05")
     docsWithoutApiSince("androidx.ads:ads-identifier-common:1.0.0-alpha05")
     docsWithoutApiSince("androidx.ads:ads-identifier-provider:1.0.0-alpha05")
-    kmpDocs("androidx.annotation:annotation:1.9.0-alpha01")
+    kmpDocs("androidx.annotation:annotation:1.8.1")
     docs("androidx.annotation:annotation-experimental:1.4.1")
     docs("androidx.appcompat:appcompat:1.7.0")
     docs("androidx.appcompat:appcompat-resources:1.7.0")
@@ -38,10 +38,10 @@
     docs("androidx.asynclayoutinflater:asynclayoutinflater:1.1.0-alpha01")
     docs("androidx.asynclayoutinflater:asynclayoutinflater-appcompat:1.1.0-alpha01")
     docs("androidx.autofill:autofill:1.3.0-alpha01")
-    docs("androidx.benchmark:benchmark-common:1.3.0-beta01")
-    docs("androidx.benchmark:benchmark-junit4:1.3.0-beta01")
-    docs("androidx.benchmark:benchmark-macro:1.3.0-beta01")
-    docs("androidx.benchmark:benchmark-macro-junit4:1.3.0-beta01")
+    docs("androidx.benchmark:benchmark-common:1.3.0-beta02")
+    docs("androidx.benchmark:benchmark-junit4:1.3.0-beta02")
+    docs("androidx.benchmark:benchmark-macro:1.3.0-beta02")
+    docs("androidx.benchmark:benchmark-macro-junit4:1.3.0-beta02")
     docs("androidx.biometric:biometric:1.4.0-alpha01")
     docs("androidx.biometric:biometric-ktx:1.4.0-alpha01")
     docs("androidx.bluetooth:bluetooth:1.0.0-alpha02")
@@ -66,41 +66,42 @@
     docs("androidx.cardview:cardview:1.0.0")
     kmpDocs("androidx.collection:collection:1.4.0")
     docs("androidx.collection:collection-ktx:1.4.0-rc01")
-    kmpDocs("androidx.compose.animation:animation:1.7.0-beta04")
-    kmpDocs("androidx.compose.animation:animation-core:1.7.0-beta04")
-    kmpDocs("androidx.compose.animation:animation-graphics:1.7.0-beta04")
-    kmpDocs("androidx.compose.foundation:foundation:1.7.0-beta04")
-    kmpDocs("androidx.compose.foundation:foundation-layout:1.7.0-beta04")
+    kmpDocs("androidx.compose.animation:animation:1.7.0-beta06")
+    kmpDocs("androidx.compose.animation:animation-core:1.7.0-beta06")
+    kmpDocs("androidx.compose.animation:animation-graphics:1.7.0-beta06")
+    kmpDocs("androidx.compose.foundation:foundation:1.7.0-beta06")
+    kmpDocs("androidx.compose.foundation:foundation-layout:1.7.0-beta06")
     kmpDocs("androidx.compose.material3.adaptive:adaptive:1.0.0-beta04")
     kmpDocs("androidx.compose.material3.adaptive:adaptive-layout:1.0.0-beta04")
     kmpDocs("androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-beta04")
-    kmpDocs("androidx.compose.material3:material3:1.3.0-beta04")
-    kmpDocs("androidx.compose.material3:material3-adaptive-navigation-suite:1.3.0-beta04")
+    kmpDocs("androidx.compose.material3:material3:1.3.0-beta05")
+    kmpDocs("androidx.compose.material3:material3-adaptive-navigation-suite:1.3.0-beta05")
     kmpDocs("androidx.compose.material3:material3-common:1.0.0-alpha01")
-    kmpDocs("androidx.compose.material3:material3-window-size-class:1.3.0-beta04")
-    kmpDocs("androidx.compose.material:material:1.7.0-beta04")
-    kmpDocs("androidx.compose.material:material-icons-core:1.7.0-beta04")
+    kmpDocs("androidx.compose.material3:material3-window-size-class:1.3.0-beta05")
+    kmpDocs("androidx.compose.material:material:1.7.0-beta06")
+    kmpDocs("androidx.compose.material:material-icons-core:1.7.0-beta06")
     docs("androidx.compose.material:material-navigation:1.7.0-beta03")
-    kmpDocs("androidx.compose.material:material-ripple:1.7.0-beta04")
-    kmpDocs("androidx.compose.runtime:runtime:1.7.0-beta04")
-    docs("androidx.compose.runtime:runtime-livedata:1.7.0-beta04")
-    docs("androidx.compose.runtime:runtime-rxjava2:1.7.0-beta04")
-    docs("androidx.compose.runtime:runtime-rxjava3:1.7.0-beta04")
-    kmpDocs("androidx.compose.runtime:runtime-saveable:1.7.0-beta04")
+    kmpDocs("androidx.compose.material:material-ripple:1.7.0-beta06")
+    kmpDocs("androidx.compose.runtime:runtime:1.7.0-beta06")
+    docs("androidx.compose.runtime:runtime-livedata:1.7.0-beta06")
+    docs("androidx.compose.runtime:runtime-rxjava2:1.7.0-beta06")
+    docs("androidx.compose.runtime:runtime-rxjava3:1.7.0-beta06")
+    kmpDocs("androidx.compose.runtime:runtime-saveable:1.7.0-beta06")
     docs("androidx.compose.runtime:runtime-tracing:1.0.0-beta01")
-    kmpDocs("androidx.compose.ui:ui:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-geometry:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-graphics:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-test:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-test-junit4:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-text:1.7.0-beta04")
-    docs("androidx.compose.ui:ui-text-google-fonts:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-tooling:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-tooling-data:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-tooling-preview:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-unit:1.7.0-beta04")
-    kmpDocs("androidx.compose.ui:ui-util:1.7.0-beta04")
-    docs("androidx.compose.ui:ui-viewbinding:1.7.0-beta04")
+    kmpDocs("androidx.compose.ui:ui:1.7.0-beta06")
+    docs("androidx.compose.ui:ui-android-stubs:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-geometry:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-graphics:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-test:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-test-junit4:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-text:1.7.0-beta06")
+    docs("androidx.compose.ui:ui-text-google-fonts:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-tooling:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-tooling-data:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-tooling-preview:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-unit:1.7.0-beta06")
+    kmpDocs("androidx.compose.ui:ui-util:1.7.0-beta06")
+    docs("androidx.compose.ui:ui-viewbinding:1.7.0-beta06")
     docs("androidx.concurrent:concurrent-futures:1.2.0")
     docs("androidx.concurrent:concurrent-futures-ktx:1.2.0")
     docs("androidx.constraintlayout:constraintlayout:2.2.0-alpha13")
@@ -127,10 +128,10 @@
     docs("androidx.core:core-testing:1.15.0-alpha01")
     docs("androidx.core.uwb:uwb:1.0.0-alpha08")
     docs("androidx.core.uwb:uwb-rxjava3:1.0.0-alpha08")
-    docs("androidx.credentials:credentials:1.5.0-alpha02")
+    docs("androidx.credentials:credentials:1.5.0-alpha03")
     docs("androidx.credentials:credentials-e2ee:1.0.0-alpha02")
     docs("androidx.credentials:credentials-fido:1.0.0-alpha02")
-    docs("androidx.credentials:credentials-play-services-auth:1.5.0-alpha02")
+    docs("androidx.credentials:credentials-play-services-auth:1.5.0-alpha03")
     docs("androidx.cursoradapter:cursoradapter:1.0.0")
     docs("androidx.customview:customview:1.2.0-alpha02")
     // TODO(b/294531403): Turn on apiSince for customview-poolingcontainer when it releases as alpha
@@ -149,21 +150,21 @@
     docs("androidx.drawerlayout:drawerlayout:1.2.0")
     docs("androidx.dynamicanimation:dynamicanimation:1.1.0-alpha02")
     docs("androidx.dynamicanimation:dynamicanimation-ktx:1.0.0-alpha03")
-    docs("androidx.emoji2:emoji2:1.5.0-alpha01")
-    docs("androidx.emoji2:emoji2-bundled:1.5.0-alpha01")
-    docs("androidx.emoji2:emoji2-emojipicker:1.5.0-alpha01")
-    docs("androidx.emoji2:emoji2-views:1.5.0-alpha01")
-    docs("androidx.emoji2:emoji2-views-helper:1.5.0-alpha01")
+    docs("androidx.emoji2:emoji2:1.5.0-beta01")
+    docs("androidx.emoji2:emoji2-bundled:1.5.0-beta01")
+    docs("androidx.emoji2:emoji2-emojipicker:1.5.0-beta01")
+    docs("androidx.emoji2:emoji2-views:1.5.0-beta01")
+    docs("androidx.emoji2:emoji2-views-helper:1.5.0-beta01")
     docs("androidx.emoji:emoji:1.2.0-alpha03")
     docs("androidx.emoji:emoji-appcompat:1.2.0-alpha03")
     docs("androidx.emoji:emoji-bundled:1.2.0-alpha03")
     docs("androidx.enterprise:enterprise-feedback:1.1.0")
     docs("androidx.enterprise:enterprise-feedback-testing:1.1.0")
     docs("androidx.exifinterface:exifinterface:1.3.6")
-    docs("androidx.fragment:fragment:1.8.1")
-    docs("androidx.fragment:fragment-compose:1.8.1")
-    docs("androidx.fragment:fragment-ktx:1.8.1")
-    docs("androidx.fragment:fragment-testing:1.8.1")
+    docs("androidx.fragment:fragment:1.8.2")
+    docs("androidx.fragment:fragment-compose:1.8.2")
+    docs("androidx.fragment:fragment-ktx:1.8.2")
+    docs("androidx.fragment:fragment-testing:1.8.2")
     docs("androidx.glance:glance:1.1.0")
     docs("androidx.glance:glance-appwidget:1.1.0")
     docs("androidx.glance:glance-appwidget-preview:1.1.0")
@@ -176,7 +177,7 @@
     docs("androidx.glance:glance-wear-tiles:1.0.0-alpha06")
     docs("androidx.graphics:graphics-core:1.0.0")
     docs("androidx.graphics:graphics-path:1.0.0")
-    kmpDocs("androidx.graphics:graphics-shapes:1.0.0-beta01")
+    kmpDocs("androidx.graphics:graphics-shapes:1.0.0-rc01")
     docs("androidx.gridlayout:gridlayout:1.1.0-beta01")
     docs("androidx.health.connect:connect-client:1.1.0-alpha07")
     samples("androidx.health.connect:connect-client-samples:1.1.0-alpha07")
@@ -195,25 +196,25 @@
     docs("androidx.leanback:leanback-paging:1.1.0-alpha11")
     docs("androidx.leanback:leanback-preference:1.2.0-alpha04")
     docs("androidx.leanback:leanback-tab:1.1.0-beta01")
-    kmpDocs("androidx.lifecycle:lifecycle-common:2.8.1")
-    docs("androidx.lifecycle:lifecycle-common-java8:2.8.1")
+    kmpDocs("androidx.lifecycle:lifecycle-common:2.8.4")
+    docs("androidx.lifecycle:lifecycle-common-java8:2.8.4")
     docs("androidx.lifecycle:lifecycle-extensions:2.2.0")
-    docs("androidx.lifecycle:lifecycle-livedata:2.8.2")
-    docs("androidx.lifecycle:lifecycle-livedata-core:2.8.2")
-    docs("androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.1")
-    docs("androidx.lifecycle:lifecycle-livedata-ktx:2.8.1")
-    docs("androidx.lifecycle:lifecycle-process:2.8.2")
-    docs("androidx.lifecycle:lifecycle-reactivestreams:2.8.1")
-    docs("androidx.lifecycle:lifecycle-reactivestreams-ktx:2.8.1")
-    kmpDocs("androidx.lifecycle:lifecycle-runtime:2.8.1")
-    kmpDocs("androidx.lifecycle:lifecycle-runtime-compose:2.8.1")
-    kmpDocs("androidx.lifecycle:lifecycle-runtime-ktx:2.8.1")
-    docs("androidx.lifecycle:lifecycle-runtime-testing:2.8.1")
-    docs("androidx.lifecycle:lifecycle-service:2.8.1")
-    kmpDocs("androidx.lifecycle:lifecycle-viewmodel:2.8.1")
-    kmpDocs("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.1")
-    docs("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.1")
-    docs("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.1")
+    docs("androidx.lifecycle:lifecycle-livedata:2.8.4")
+    docs("androidx.lifecycle:lifecycle-livedata-core:2.8.4")
+    docs("androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4")
+    docs("androidx.lifecycle:lifecycle-livedata-ktx:2.8.4")
+    docs("androidx.lifecycle:lifecycle-process:2.8.4")
+    docs("androidx.lifecycle:lifecycle-reactivestreams:2.8.4")
+    docs("androidx.lifecycle:lifecycle-reactivestreams-ktx:2.8.4")
+    kmpDocs("androidx.lifecycle:lifecycle-runtime:2.8.4")
+    kmpDocs("androidx.lifecycle:lifecycle-runtime-compose:2.8.4")
+    kmpDocs("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4")
+    docs("androidx.lifecycle:lifecycle-runtime-testing:2.8.4")
+    docs("androidx.lifecycle:lifecycle-service:2.8.4")
+    kmpDocs("androidx.lifecycle:lifecycle-viewmodel:2.8.4")
+    kmpDocs("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4")
+    docs("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4")
+    docs("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4")
     docs("androidx.loader:loader:1.1.0")
     // localbroadcastmanager is deprecated
     docsWithoutApiSince("androidx.localbroadcastmanager:localbroadcastmanager:1.1.0")
@@ -223,57 +224,57 @@
     docs("androidx.media2:media2-widget:1.3.0")
     docs("androidx.media:media:1.7.0")
     // androidx.media3 is not hosted in androidx
-    docsWithoutApiSince("androidx.media3:media3-cast:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-common:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-container:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-database:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-datasource:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-decoder:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-effect:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-extractor:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-muxer:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-session:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-test-utils:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-transformer:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-ui:1.4.0-rc01")
-    docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.4.0-rc01")
+    docsWithoutApiSince("androidx.media3:media3-cast:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-common:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-container:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-database:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-datasource:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-decoder:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-effect:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-extractor:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-muxer:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-session:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-test-utils:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-transformer:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-ui:1.4.0")
+    docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.4.0")
     docs("androidx.mediarouter:mediarouter:1.7.0")
     docs("androidx.mediarouter:mediarouter-testing:1.7.0")
     docs("androidx.metrics:metrics-performance:1.0.0-beta01")
-    docs("androidx.navigation:navigation-common:2.8.0-beta04")
-    docs("androidx.navigation:navigation-common-ktx:2.8.0-beta04")
-    docs("androidx.navigation:navigation-compose:2.8.0-beta04")
-    docs("androidx.navigation:navigation-dynamic-features-fragment:2.8.0-beta04")
-    docs("androidx.navigation:navigation-dynamic-features-runtime:2.8.0-beta04")
-    docs("androidx.navigation:navigation-fragment:2.8.0-beta04")
-    docs("androidx.navigation:navigation-fragment-compose:2.8.0-beta04")
-    docs("androidx.navigation:navigation-fragment-ktx:2.8.0-beta04")
-    docs("androidx.navigation:navigation-runtime:2.8.0-beta04")
-    docs("androidx.navigation:navigation-runtime-ktx:2.8.0-beta04")
-    docs("androidx.navigation:navigation-testing:2.8.0-beta04")
-    docs("androidx.navigation:navigation-ui:2.8.0-beta04")
-    docs("androidx.navigation:navigation-ui-ktx:2.8.0-beta04")
-    kmpDocs("androidx.paging:paging-common:3.3.0")
-    docs("androidx.paging:paging-common-ktx:3.3.0")
-    kmpDocs("androidx.paging:paging-compose:3.3.0")
-    docs("androidx.paging:paging-guava:3.3.0")
-    docs("androidx.paging:paging-runtime:3.3.0")
-    docs("androidx.paging:paging-runtime-ktx:3.3.0")
-    docs("androidx.paging:paging-rxjava2:3.3.0")
-    docs("androidx.paging:paging-rxjava2-ktx:3.3.0")
-    docs("androidx.paging:paging-rxjava3:3.3.0")
-    kmpDocs("androidx.paging:paging-testing:3.3.0")
+    docs("androidx.navigation:navigation-common:2.8.0-beta06")
+    docs("androidx.navigation:navigation-common-ktx:2.8.0-beta06")
+    docs("androidx.navigation:navigation-compose:2.8.0-beta06")
+    docs("androidx.navigation:navigation-dynamic-features-fragment:2.8.0-beta06")
+    docs("androidx.navigation:navigation-dynamic-features-runtime:2.8.0-beta06")
+    docs("androidx.navigation:navigation-fragment:2.8.0-beta06")
+    docs("androidx.navigation:navigation-fragment-compose:2.8.0-beta06")
+    docs("androidx.navigation:navigation-fragment-ktx:2.8.0-beta06")
+    docs("androidx.navigation:navigation-runtime:2.8.0-beta06")
+    docs("androidx.navigation:navigation-runtime-ktx:2.8.0-beta06")
+    docs("androidx.navigation:navigation-testing:2.8.0-beta06")
+    docs("androidx.navigation:navigation-ui:2.8.0-beta06")
+    docs("androidx.navigation:navigation-ui-ktx:2.8.0-beta06")
+    kmpDocs("androidx.paging:paging-common:3.3.1")
+    docs("androidx.paging:paging-common-ktx:3.3.1")
+    kmpDocs("androidx.paging:paging-compose:3.3.1")
+    docs("androidx.paging:paging-guava:3.3.1")
+    docs("androidx.paging:paging-runtime:3.3.1")
+    docs("androidx.paging:paging-runtime-ktx:3.3.1")
+    docs("androidx.paging:paging-rxjava2:3.3.1")
+    docs("androidx.paging:paging-rxjava2-ktx:3.3.1")
+    docs("androidx.paging:paging-rxjava3:3.3.1")
+    kmpDocs("androidx.paging:paging-testing:3.3.1")
     docs("androidx.palette:palette:1.0.0")
     docs("androidx.palette:palette-ktx:1.0.0")
     docs("androidx.percentlayout:percentlayout:1.0.1")
@@ -283,11 +284,11 @@
     docs("androidx.privacysandbox.activity:activity-client:1.0.0-alpha01")
     docs("androidx.privacysandbox.activity:activity-core:1.0.0-alpha01")
     docs("androidx.privacysandbox.activity:activity-provider:1.0.0-alpha01")
-    docs("androidx.privacysandbox.ads:ads-adservices:1.1.0-beta08")
-    docs("androidx.privacysandbox.ads:ads-adservices-java:1.1.0-beta08")
-    docs("androidx.privacysandbox.sdkruntime:sdkruntime-client:1.0.0-alpha13")
-    docs("androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha13")
-    docs("androidx.privacysandbox.sdkruntime:sdkruntime-provider:1.0.0-alpha13")
+    docs("androidx.privacysandbox.ads:ads-adservices:1.1.0-beta09")
+    docs("androidx.privacysandbox.ads:ads-adservices-java:1.1.0-beta09")
+    docs("androidx.privacysandbox.sdkruntime:sdkruntime-client:1.0.0-alpha14")
+    docs("androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha14")
+    docs("androidx.privacysandbox.sdkruntime:sdkruntime-provider:1.0.0-alpha14")
     docs("androidx.privacysandbox.tools:tools:1.0.0-alpha09")
     docs("androidx.privacysandbox.ui:ui-client:1.0.0-alpha09")
     docs("androidx.privacysandbox.ui:ui-core:1.0.0-alpha09")
@@ -298,18 +299,18 @@
     docs("androidx.recyclerview:recyclerview-selection:2.0.0-alpha01")
     docs("androidx.remotecallback:remotecallback:1.0.0-alpha02")
     docs("androidx.resourceinspection:resourceinspection-annotation:1.0.1")
-    kmpDocs("androidx.room:room-common:2.7.0-alpha04")
-    docs("androidx.room:room-guava:2.7.0-alpha04")
-    docs("androidx.room:room-ktx:2.7.0-alpha04")
-    kmpDocs("androidx.room:room-migration:2.7.0-alpha04")
-    docs("androidx.room:room-paging:2.7.0-alpha04")
-    docs("androidx.room:room-paging-guava:2.7.0-alpha04")
-    docs("androidx.room:room-paging-rxjava2:2.7.0-alpha04")
-    docs("androidx.room:room-paging-rxjava3:2.7.0-alpha04")
-    kmpDocs("androidx.room:room-runtime:2.7.0-alpha04")
-    docs("androidx.room:room-rxjava2:2.7.0-alpha04")
-    docs("androidx.room:room-rxjava3:2.7.0-alpha04")
-    kmpDocs("androidx.room:room-testing:2.7.0-alpha04")
+    kmpDocs("androidx.room:room-common:2.7.0-alpha05")
+    docs("androidx.room:room-guava:2.7.0-alpha05")
+    docs("androidx.room:room-ktx:2.7.0-alpha05")
+    kmpDocs("androidx.room:room-migration:2.7.0-alpha05")
+    kmpDocs("androidx.room:room-paging:2.7.0-alpha05")
+    docs("androidx.room:room-paging-guava:2.7.0-alpha05")
+    docs("androidx.room:room-paging-rxjava2:2.7.0-alpha05")
+    docs("androidx.room:room-paging-rxjava3:2.7.0-alpha05")
+    kmpDocs("androidx.room:room-runtime:2.7.0-alpha05")
+    docs("androidx.room:room-rxjava2:2.7.0-alpha05")
+    docs("androidx.room:room-rxjava3:2.7.0-alpha05")
+    kmpDocs("androidx.room:room-testing:2.7.0-alpha05")
     docs("androidx.savedstate:savedstate:1.2.1")
     docs("androidx.savedstate:savedstate-ktx:1.2.1")
     docs("androidx.security:security-app-authenticator:1.0.0-beta01")
@@ -317,17 +318,17 @@
     docs("androidx.security:security-crypto:1.1.0-alpha06")
     docs("androidx.security:security-crypto-ktx:1.1.0-alpha06")
     docs("androidx.security:security-identity-credential:1.0.0-alpha03")
-    docs("androidx.security:security-state:1.0.0-alpha02")
+    docs("androidx.security:security-state:1.0.0-alpha03")
     docs("androidx.sharetarget:sharetarget:1.2.0")
     docs("androidx.slice:slice-builders:1.1.0-alpha02")
     docs("androidx.slice:slice-builders-ktx:1.0.0-alpha08")
     docs("androidx.slice:slice-core:1.1.0-alpha02")
     docs("androidx.slice:slice-view:1.1.0-alpha02")
     docs("androidx.slidingpanelayout:slidingpanelayout:1.2.0")
-    kmpDocs("androidx.sqlite:sqlite:2.5.0-alpha04")
-    kmpDocs("androidx.sqlite:sqlite-bundled:2.5.0-alpha04")
-    kmpDocs("androidx.sqlite:sqlite-framework:2.5.0-alpha04")
-    docs("androidx.sqlite:sqlite-ktx:2.5.0-alpha04")
+    kmpDocs("androidx.sqlite:sqlite:2.5.0-alpha05")
+    kmpDocs("androidx.sqlite:sqlite-bundled:2.5.0-alpha05")
+    kmpDocs("androidx.sqlite:sqlite-framework:2.5.0-alpha05")
+    docs("androidx.sqlite:sqlite-ktx:2.5.0-alpha05")
     docs("androidx.startup:startup-runtime:1.2.0-alpha02")
     docs("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
     // androidx.test is not hosted in androidx
@@ -357,10 +358,10 @@
     // TODO(243405142) clean-up
     docsWithoutApiSince("androidx.tracing:tracing-perfetto-common:1.0.0-alpha16")
     docs("androidx.tracing:tracing-perfetto-handshake:1.0.0")
-    docs("androidx.transition:transition:1.5.0")
-    docs("androidx.transition:transition-ktx:1.5.0")
-    docs("androidx.tv:tv-foundation:1.0.0-alpha10")
-    docs("androidx.tv:tv-material:1.0.0-beta01")
+    docs("androidx.transition:transition:1.5.1")
+    docs("androidx.transition:transition-ktx:1.5.1")
+    docs("androidx.tv:tv-foundation:1.0.0-alpha11")
+    docs("androidx.tv:tv-material:1.0.0-rc01")
     docs("androidx.tvprovider:tvprovider:1.1.0-alpha01")
     docs("androidx.vectordrawable:vectordrawable:1.2.0")
     docs("androidx.vectordrawable:vectordrawable-animated:1.2.0")
@@ -374,18 +375,18 @@
     docs("androidx.wear.compose:compose-material3:1.0.0-alpha23")
     docs("androidx.wear.compose:compose-navigation:1.4.0-beta03")
     docs("androidx.wear.compose:compose-ui-tooling:1.4.0-beta03")
-    docs("androidx.wear.protolayout:protolayout:1.2.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-expression:1.2.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-expression-pipeline:1.2.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-material:1.2.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-material-core:1.2.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-renderer:1.2.0-alpha05")
-    docs("androidx.wear.tiles:tiles:1.4.0-alpha05")
-    docs("androidx.wear.tiles:tiles-material:1.4.0-alpha05")
-    docs("androidx.wear.tiles:tiles-renderer:1.4.0-alpha05")
-    docs("androidx.wear.tiles:tiles-testing:1.4.0-alpha05")
-    docs("androidx.wear.tiles:tiles-tooling:1.4.0-alpha05")
-    docs("androidx.wear.tiles:tiles-tooling-preview:1.4.0-alpha05")
+    docs("androidx.wear.protolayout:protolayout:1.2.0-rc01")
+    docs("androidx.wear.protolayout:protolayout-expression:1.2.0-rc01")
+    docs("androidx.wear.protolayout:protolayout-expression-pipeline:1.2.0-rc01")
+    docs("androidx.wear.protolayout:protolayout-material:1.2.0-rc01")
+    docs("androidx.wear.protolayout:protolayout-material-core:1.2.0-rc01")
+    docs("androidx.wear.protolayout:protolayout-renderer:1.2.0-rc01")
+    docs("androidx.wear.tiles:tiles:1.4.0-rc01")
+    docs("androidx.wear.tiles:tiles-material:1.4.0-rc01")
+    docs("androidx.wear.tiles:tiles-renderer:1.4.0-rc01")
+    docs("androidx.wear.tiles:tiles-testing:1.4.0-rc01")
+    docs("androidx.wear.tiles:tiles-tooling:1.4.0-rc01")
+    docs("androidx.wear.tiles:tiles-tooling-preview:1.4.0-rc01")
     docs("androidx.wear.watchface:watchface:1.3.0-alpha03")
     docs("androidx.wear.watchface:watchface-client:1.3.0-alpha03")
     docs("androidx.wear.watchface:watchface-client-guava:1.3.0-alpha03")
@@ -408,7 +409,7 @@
     docs("androidx.wear:wear-ongoing:1.1.0-alpha01")
     docs("androidx.wear:wear-phone-interactions:1.1.0-alpha04")
     samples("androidx.wear:wear-phone-interactions-samples:1.1.0-alpha04")
-    docs("androidx.wear:wear-remote-interactions:1.1.0-alpha02")
+    docs("androidx.wear:wear-remote-interactions:1.1.0-beta01")
     samples("androidx.wear:wear-remote-interactions-samples:1.1.0-alpha02")
     docs("androidx.wear:wear-tooling-preview:1.0.0")
     docs("androidx.webkit:webkit:1.12.0-alpha02")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 38c3dfb..049336a 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -21,6 +21,7 @@
     samples("androidx.window:window-samples:1.3.0-alpha03")
 
     docsForOptionalProject(":xr:xr")
+    docsForOptionalProject(":xr:xr-material3-adaptive")
     docs(project(":activity:activity"))
     docs(project(":activity:activity-compose"))
     docs(project(":activity:activity-ktx"))
@@ -88,7 +89,7 @@
     kmpDocs(project(":compose:material3:material3-window-size-class"))
     kmpDocs(project(":compose:material:material"))
     kmpDocs("androidx.compose.material:material-icons-core:1.7.0-beta01")
-    samples("androidx.compose.material:material-icons-core-samples:1.7.0-alpha07")
+    samples("androidx.compose.material:material-icons-core-samples:1.7.0-beta01")
     kmpDocs(project(":compose:material:material-ripple"))
     docs(project(":compose:material:material-navigation"))
     kmpDocs(project(":compose:runtime:runtime"))
@@ -140,6 +141,7 @@
     docs(project(":credentials:credentials-fido"))
     docs(project(":credentials:credentials-play-services-auth"))
     docs(project(":credentials:credentials-e2ee"))
+    docs(project(":credentials:credentials-play-services-e2ee"))
     docs(project(":cursoradapter:cursoradapter"))
     docs(project(":customview:customview"))
     docs(project(":customview:customview-poolingcontainer"))
@@ -197,6 +199,8 @@
     docs(project(":hilt:hilt-navigation-fragment"))
     docs(project(":hilt:hilt-work"))
     kmpDocs(project(":ink:ink-brush"))
+    kmpDocs(project(":ink:ink-geometry"))
+    kmpDocs(project(":ink:ink-nativeloader"))
     docs(project(":input:input-motionprediction"))
     docs(project(":interpolator:interpolator"))
     docs(project(":javascriptengine:javascriptengine"))
@@ -218,12 +222,13 @@
     kmpDocs(project(":lifecycle:lifecycle-runtime"))
     kmpDocs(project(":lifecycle:lifecycle-runtime-compose"))
     kmpDocs(project(":lifecycle:lifecycle-runtime-ktx"))
-    docs(project(":lifecycle:lifecycle-runtime-testing"))
+    kmpDocs(project(":lifecycle:lifecycle-runtime-testing"))
     docs(project(":lifecycle:lifecycle-service"))
     kmpDocs(project(":lifecycle:lifecycle-viewmodel"))
     kmpDocs(project(":lifecycle:lifecycle-viewmodel-compose"))
     docs(project(":lifecycle:lifecycle-viewmodel-ktx"))
     docs(project(":lifecycle:lifecycle-viewmodel-savedstate"))
+    kmpDocs(project(":lifecycle:lifecycle-viewmodel-testing"))
     docs(project(":loader:loader"))
     docs(project(":loader:loader-ktx"))
     // localbroadcastmanager is deprecated
@@ -258,6 +263,7 @@
     docs(project(":palette:palette"))
     docs(project(":palette:palette-ktx"))
     docs(project(":pdf:pdf-viewer"))
+    docs(project(":pdf:pdf-viewer-fragment"))
     docs(project(":percentlayout:percentlayout"))
     docs(project(":preference:preference"))
     docs(project(":preference:preference-ktx"))
diff --git a/docs/api_guidelines/compat.md b/docs/api_guidelines/compat.md
index 6a72ec4..b5885b4 100644
--- a/docs/api_guidelines/compat.md
+++ b/docs/api_guidelines/compat.md
@@ -17,50 +17,6 @@
 for example, the SDK in which a method first appeared or in which a critical bug
 was first fixed.
 
-Non-reflective calls to new APIs gated on `SDK_INT` **must** be made from
-version-specific static inner classes to avoid verification errors that
-negatively affect run-time performance. This is enforced at build time by the
-`ClassVerificationFailure` lint check, which offers auto-fixes in Java sources.
-
-For more information, see Chromium's (deprecated but still accurate) guide to
-[Class Verification Failures](https://chromium.googlesource.com/chromium/src/+/HEAD/build/android/docs/class_verification_failures.md).
-
-NOTE As noted in the Chromium guide, the latest versions of R8 have added
-support for automatically out-of-lining calls to new platform SDK APIs; however,
-this depends on clients using the latest versions of R8. Since Jetpack libraries
-cannot make assertions about versions of tools used by clients, we must continue
-to manually out-of-line such calls.
-
-Methods in implementation-specific classes **must** be paired with the
-`@DoNotInline` annotation to prevent them from being inlined.
-
-```java {.good}
-public static void saveAttributeDataForStyleable(@NonNull View view, ...) {
-  if (Build.VERSION.SDK_INT >= 29) {
-    Api29Impl.saveAttributeDataForStyleable(view, ...);
-  }
-}
-
-@RequiresApi(29)
-private static class Api29Impl {
-  @DoNotInline
-  static void saveAttributeDataForStyleable(@NonNull View view, ...) {
-    view.saveAttributeDataForStyleable(...);
-  }
-}
-```
-
-Alternatively, in Kotlin sources:
-
-```kotlin {.good}
-@RequiresApi(29)
-private object Api29Impl {
-  @JvmStatic
-  @DoNotInline
-  fun saveAttributeDataForStyleable(view: View, ...) { ... }
-}
-```
-
 When developing against pre-release SDKs where the `SDK_INT` has not been
 finalized, SDK checks **must** use `BuildCompat.isAtLeastX()` methods and
 **must** use a tip-of-tree `project` dependency to ensure that the
@@ -102,8 +58,7 @@
 as a `B` without an explicit cast. However, adding an explicit cast to `B` won't
 fix this, because the compiler will see the cast as redundant (as it normally
 would be). So, implicit casts between types introduced at different API levels
-should be moved out to version-specific static inner classes, as described
-[above](#compat-sdk).
+should be moved out to version-specific static inner classes.
 
 The `ImplicitCastClassVerificationFailure` lint check detects and provides
 autofixes for instances of invalid implicit casts.
@@ -380,10 +335,10 @@
 In cases where a hidden API is a constant value, **do not** inline the value.
 Hidden APIs cannot be tested by CTS and carry no stability guarantees.
 
-On earlier devices or in cases where an API is marked with
-`@UnsupportedAppUsage`, reflection on hidden platform APIs is allowed **only**
-when an alternative public platform API exists in a later revision of the
-Android SDK. For example, the following implementation is allowed:
+Per go/platform-parity, on earlier devices or in cases where an API is marked
+with `@UnsupportedAppUsage`, reflection on hidden platform APIs is allowed
+**only** when an alternative public platform API exists in a later revision of
+the Android SDK. For example, the following implementation is allowed:
 
 ```java
 public AccessibilityDelegate getAccessibilityDelegate(View v) {
diff --git a/docs/api_guidelines/platform_compat.md b/docs/api_guidelines/platform_compat.md
index e9b075f..40b4a74 100644
--- a/docs/api_guidelines/platform_compat.md
+++ b/docs/api_guidelines/platform_compat.md
@@ -124,11 +124,6 @@
         introduced
 *   Implementation *may* delegate to `PlatformClass` methods when available (see
     below note for caveats)
-*   To avoid runtime class verification issues, all operations that interact
-    with the internal structure of `PlatformClass` must be implemented in inner
-    classes targeted to the SDK level at which the operation was added.
-    *   See the [sample](#wrapper-sample) for an example of interacting with a
-        method that was added in SDK level 23.
 
 #### Sample {#wrapper-sample}
 
@@ -210,23 +205,6 @@
     // Default behavior.
     return false;
   }
-
-  // All references to class members -- including the constructor -- must be
-  // made on an inner class to avoid soft-verification errors that slow class
-  // loading and prevent optimization.
-  @RequiresApi(23)
-  private static class Api23Impl {
-    @DoNotInline
-    @NonNull
-    static ModemInfo create() {
-      return new ModemInfo();
-    }
-
-    @DoNotInline
-    static boolean isLteSupported(Object obj) {
-      return ((ModemInfo) obj).isLteSupported();
-    }
-  }
 }
 ```
 
diff --git a/docs/lint_guide.md b/docs/lint_guide.md
index 844a77c..eda7e8b 100644
--- a/docs/lint_guide.md
+++ b/docs/lint_guide.md
@@ -712,7 +712,18 @@
   ...
 ```
 
-Here are the steps to fix this:
+To fix this, you will need access to `kotlinc` (Kotlin Compiler) to generate a
+bytecode replacement. While Studio does include `kotlinc`, it is not available
+to the terminal and the permissions can not be changed to allow you to use it.
+That means you need your own `kotlinc` available in the terminal.
+
+For instructions on how install the compiler, please read
+[this Kotlin page](https://kotlinlang.org/docs/command-line.html#install-the-compiler).
+
+Important Note: It's best practice to match the compiler version you install to
+the kotlin version in your project to avoid issues.
+
+Otherwise, below are the steps to generate the bytecode:
 
 1.  Remove the arguments in `compiled()`:
 
diff --git a/docs/onboarding.md b/docs/onboarding.md
index 6355b24..489b507 100644
--- a/docs/onboarding.md
+++ b/docs/onboarding.md
@@ -573,6 +573,14 @@
     [Experimental APIs](/docs/api_guidelines/index.md#experimental-api))
     and API review
 
+NOTE: Experimental API tracking for KLib is enabled by default for KMP projects
+via parallel `updateAbi` and `checkAbi` tasks. If you have a problem with these
+tools,
+[please file an issue](https://issuetracker.google.com/issues/new?component=1102332&template=1780493).
+As a workaround, you may opt-out by setting
+`enableBinaryCompatibilityValidator = false` under
+`AndroidxMultiplatformExtension` in your library's `build.gradle` file.
+
 ### Release notes & the `Relnote:` tag {#relnote}
 
 Prior to releasing, release notes are pre-populated using a script and placed
@@ -1022,26 +1030,6 @@
 Make sure the library versions are the same before and after replacement. Then
 you can build the Android platform code with the new `androidx` code.
 
-### How do I measure library size? {#library-size}
-
-Method count and bytecode size are tracked in CI
-[alongside benchmarks](/docs/benchmarking.md#monitoring) to
-detect regressions.
-
-For local measurements, use the `:reportLibraryMetrics` task. For example:
-
-```shell
-./gradlew benchmark:benchmark-macro:reportLibraryMetrics
-cat ../../out/dist/librarymetrics/androidx.benchmark_benchmark-macro.json
-```
-
-Will output something like: `{"method_count":1256,"bytecode_size":178822}`
-
-Note: this only counts the weight of your library's jar/aar, including
-resources. It does not count library dependencies. It does not account for a
-minification step (e.g. with R8), as that is dynamic, and done at app build time
-(and depend on which entrypoints the app uses).
-
 ### How do I add content to a library's Overview reference doc page?
 
 Put content in a markdown file that ends with `-documentation.md` in the
diff --git a/docs/onboarding_images/image10.png b/docs/onboarding_images/image10.png
index ed1fd74..96f8fd2 100644
--- a/docs/onboarding_images/image10.png
+++ b/docs/onboarding_images/image10.png
Binary files differ
diff --git a/docs/onboarding_images/image11.png b/docs/onboarding_images/image11.png
new file mode 100644
index 0000000..ed1fd74
--- /dev/null
+++ b/docs/onboarding_images/image11.png
Binary files differ
diff --git a/docs/onboarding_images/image8.png b/docs/onboarding_images/image8.png
index da0dc66..fef51f2 100644
--- a/docs/onboarding_images/image8.png
+++ b/docs/onboarding_images/image8.png
Binary files differ
diff --git a/docs/onboarding_images/image9.png b/docs/onboarding_images/image9.png
index d90dcc2..a30d32b 100644
--- a/docs/onboarding_images/image9.png
+++ b/docs/onboarding_images/image9.png
Binary files differ
diff --git a/docs/testing.md b/docs/testing.md
index 2df00c1..51439e4 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -61,14 +61,6 @@
 users -- and library developers -- to write tests, see the
 [Testability](/docs/testability.md) guide.
 
-### Adding a JVM based screenshot test
-
-For UI heavy libraries, it might make sense to add screenshot tests to verify
-that everything still renders as expected. For that you need to write the test
-([example](https://r.android.com/2428035)) and add new goldens
-([example](https://r.android.com/2428721)). You can run these tests just like
-any other JVM test using `test` Gradle task.
-
 ### Adding screenshots tests using scuba library
 
 #### Prerequisites
@@ -173,16 +165,22 @@
 Step 2: Click on the “Update scuba goldens” below:
 ![alt_text](onboarding_images/image8.png "Update scuba button")
 
-Step 3: You should see a dashboard similar to the example below. Check-out if
-the new screenshots look as expected and if yes click approve. This will create
-a new CL.
+Step 3: Select the tests for which you want to update the golden images. Confirm
+the images look correct and click on “Approve Changes”
 ![alt_text](onboarding_images/image9.png "Button to approve scuba changes")
 
-Step 4: Link your original CL with the new goldens CL by setting the same Topic
+Step 4: In the Approve changes dialog box, enter the following details and click
+on Approve: \
+Select gerrit host as shown in image below \
+Repo: platform/frameworks/support-golden \
+Branch: androidx-main
+![alt_text](onboarding_images/image10.png "Approve changes dialog box with dropdown field to select gerrit host and textboxes to select repo and branch")
+
+Step 5: Link your original CL with the new goldens CL by setting the same Topic
 field in both CLs (any arbitrary string will do). This tells Gerrit to submit
 the CLs together, effectively providing a reference from the original CL to the
 new goldens. And re-run presubmit. Your tests should now pass!
-![alt_text](onboarding_images/image10.png "Topic for connecting cls")
+![alt_text](onboarding_images/image11.png "Topic for connecting cls, so they can run together")
 
 #### Running manually / debugging
 
diff --git a/documentfile/documentfile/build.gradle b/documentfile/documentfile/build.gradle
index b42d3fa..11fecf4 100644
--- a/documentfile/documentfile/build.gradle
+++ b/documentfile/documentfile/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.7.0")
 
     annotationProcessor(libs.nullaway)
@@ -28,7 +28,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/draganddrop/draganddrop/build.gradle b/draganddrop/draganddrop/build.gradle
index 746f3d5..f58b809 100644
--- a/draganddrop/draganddrop/build.gradle
+++ b/draganddrop/draganddrop/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.appcompat:appcompat:1.4.0")
     api("androidx.core:core:1.7.0")
     annotationProcessor(libs.nullaway)
@@ -54,5 +54,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "This library makes it easy for developers to accept data dragged-and-dropped from another app, and show a consistent affordance."
-    metalavaK2UastEnabled = true
 }
diff --git a/draganddrop/draganddrop/src/main/java/androidx/draganddrop/DropAffordanceHighlighter.java b/draganddrop/draganddrop/src/main/java/androidx/draganddrop/DropAffordanceHighlighter.java
index adb0647..d557e59 100644
--- a/draganddrop/draganddrop/src/main/java/androidx/draganddrop/DropAffordanceHighlighter.java
+++ b/draganddrop/draganddrop/src/main/java/androidx/draganddrop/DropAffordanceHighlighter.java
@@ -39,7 +39,6 @@
 import android.view.View;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -223,7 +222,6 @@
 
     @RequiresApi(Build.VERSION_CODES.Q)
     private static class Api29BackUpImpl {
-        @DoNotInline
         static void backUp(DropAffordanceHighlighter highlighter) {
             highlighter.mOriginalForegroundTintBlendMode =
                     highlighter.mViewToHighlight.getForegroundTintBlendMode();
@@ -233,7 +231,6 @@
 
     @RequiresApi(Build.VERSION_CODES.Q)
     private static class Api29RestoreImpl {
-        @DoNotInline
         static void restore(DropAffordanceHighlighter highlighter) {
             highlighter.mViewToHighlight.setForegroundTintBlendMode(
                     highlighter.mOriginalForegroundTintBlendMode);
diff --git a/drawerlayout/drawerlayout/build.gradle b/drawerlayout/drawerlayout/build.gradle
index abf80df..73fca30 100644
--- a/drawerlayout/drawerlayout/build.gradle
+++ b/drawerlayout/drawerlayout/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.2.0")
     api("androidx.customview:customview:1.1.0")
 
@@ -33,7 +33,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/drawerlayout/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java b/drawerlayout/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java
index 1ee2af5..da9fcb2 100644
--- a/drawerlayout/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java
+++ b/drawerlayout/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java
@@ -46,7 +46,6 @@
 import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -2550,7 +2549,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void tryRegisterOnBackInvokedCallback(@NonNull Object dispatcherObj,
                 @NonNull Object callback) {
             OnBackInvokedDispatcher dispatcher = (OnBackInvokedDispatcher) dispatcherObj;
@@ -2558,7 +2556,6 @@
                     (OnBackInvokedCallback) callback);
         }
 
-        @DoNotInline
         static void tryUnregisterOnBackInvokedCallback(@NonNull Object dispatcherObj,
                 @NonNull Object callbackObj) {
             OnBackInvokedDispatcher dispatcher = (OnBackInvokedDispatcher) dispatcherObj;
@@ -2566,13 +2563,11 @@
         }
 
         @Nullable
-        @DoNotInline
         static OnBackInvokedDispatcher findOnBackInvokedDispatcher(@NonNull DrawerLayout view) {
             return view.findOnBackInvokedDispatcher();
         }
 
         @NonNull
-        @DoNotInline
         static OnBackInvokedCallback newOnBackInvokedCallback(@NonNull Runnable action) {
             return action::run;
         }
diff --git a/dynamicanimation/dynamicanimation-ktx/build.gradle b/dynamicanimation/dynamicanimation-ktx/build.gradle
index ff51a75..f7e8e34 100644
--- a/dynamicanimation/dynamicanimation-ktx/build.gradle
+++ b/dynamicanimation/dynamicanimation-ktx/build.gradle
@@ -49,7 +49,6 @@
     mavenVersion = LibraryVersions.DYNAMICANIMATION_KTX
     inceptionYear = "2018"
     description = "Kotlin extensions for 'dynamicanimation' artifact"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/dynamicanimation/dynamicanimation/build.gradle b/dynamicanimation/dynamicanimation/build.gradle
index 627eae3..5d79510 100644
--- a/dynamicanimation/dynamicanimation/build.gradle
+++ b/dynamicanimation/dynamicanimation/build.gradle
@@ -31,7 +31,6 @@
     mavenVersion = LibraryVersions.DYNAMICANIMATION
     inceptionYear = "2017"
     description = "Physics-based animation in support library, where the animations are driven by physics force. You can use this Animation library to create smooth and realistic animations."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/dynamicanimation/dynamicanimation/lint-baseline.xml b/dynamicanimation/dynamicanimation/lint-baseline.xml
index e438e8b..9d5c665 100644
--- a/dynamicanimation/dynamicanimation/lint-baseline.xml
+++ b/dynamicanimation/dynamicanimation/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -11,24 +11,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 33; however, the containing class androidx.dynamicanimation.animation.AnimationHandler is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mDurationScale = ValueAnimator.getDurationScale();"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/dynamicanimation/animation/AnimationHandler.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                property.setValue(object, value);"
-        errorLine2="                         ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/dynamicanimation/animation/FloatPropertyCompat.java"/>
-    </issue>
-
-    <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
         errorLine1="        float durationScale = getAnimationHandler().getDurationScale();"
diff --git a/emoji/emoji-appcompat/build.gradle b/emoji/emoji-appcompat/build.gradle
index 79a58de..f29260e 100644
--- a/emoji/emoji-appcompat/build.gradle
+++ b/emoji/emoji-appcompat/build.gradle
@@ -39,7 +39,6 @@
     mavenVersion = LibraryVersions.EMOJI
     inceptionYear = "2017"
     description = "EmojiCompat Widgets for AppCompat integration"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/emoji/emoji-bundled/build.gradle b/emoji/emoji-bundled/build.gradle
index 6d2f2b8..d989aea 100644
--- a/emoji/emoji-bundled/build.gradle
+++ b/emoji/emoji-bundled/build.gradle
@@ -33,7 +33,6 @@
     mavenVersion = LibraryVersions.EMOJI
     inceptionYear = "2017"
     description = "Library bundled with assets to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters."
-    metalavaK2UastEnabled = true
 
     extraLicense {
         name = "SIL Open Font License, Version 1.1"
diff --git a/emoji/emoji/build.gradle b/emoji/emoji/build.gradle
index f9e3b1b..9dadcbe 100644
--- a/emoji/emoji/build.gradle
+++ b/emoji/emoji/build.gradle
@@ -66,7 +66,6 @@
     mavenVersion = LibraryVersions.EMOJI
     inceptionYear = "2017"
     description = "Core library to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters."
-    metalavaK2UastEnabled = true
 
     extraLicense {
         name = "SIL Open Font License, Version 1.1"
diff --git a/emoji2/emoji2-bundled/build.gradle b/emoji2/emoji2-bundled/build.gradle
index 0d92b1c..d48ac83 100644
--- a/emoji2/emoji2-bundled/build.gradle
+++ b/emoji2/emoji2-bundled/build.gradle
@@ -58,7 +58,6 @@
     inceptionYear = "2017"
     description = "Library bundled with assets to enable emoji compatibility in Kitkat and newer " +
             "devices to avoid the empty emoji characters."
-    metalavaK2UastEnabled = true
 
     extraLicense {
         name = "SIL Open Font License, Version 1.1"
diff --git a/emoji2/emoji2-emojipicker/build.gradle b/emoji2/emoji2-emojipicker/build.gradle
index f9e72b1..10baa7f 100644
--- a/emoji2/emoji2-emojipicker/build.gradle
+++ b/emoji2/emoji2-emojipicker/build.gradle
@@ -68,6 +68,5 @@
     inceptionYear = "2022"
     description = "This library provides the latest emoji support and emoji picker UI to input " +
             "emoji in current and older devices"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/emoji2/emoji2-emojipicker/samples/build.gradle b/emoji2/emoji2-emojipicker/samples/build.gradle
index 6824517..3af2482 100644
--- a/emoji2/emoji2-emojipicker/samples/build.gradle
+++ b/emoji2/emoji2-emojipicker/samples/build.gradle
@@ -34,6 +34,7 @@
     implementation(project(":compose:foundation:foundation"))
 }
 android {
+    compileSdk 35
     namespace "androidx.emoji2.emojipicker.samples"
 }
 
diff --git a/emoji2/emoji2-views-helper/build.gradle b/emoji2/emoji2-views-helper/build.gradle
index c1f335c..e6c2766 100644
--- a/emoji2/emoji2-views-helper/build.gradle
+++ b/emoji2/emoji2-views-helper/build.gradle
@@ -38,5 +38,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Provide helper classes for Emoji2 views."
-    metalavaK2UastEnabled = true
 }
diff --git a/emoji2/emoji2-views/build.gradle b/emoji2/emoji2-views/build.gradle
index 485bc01e..a5c86d4 100644
--- a/emoji2/emoji2-views/build.gradle
+++ b/emoji2/emoji2-views/build.gradle
@@ -44,5 +44,4 @@
     inceptionYear = "2017"
     description = "Support for using emoji2 directly with Android Views, for use in apps without " +
             "appcompat"
-    metalavaK2UastEnabled = true
 }
diff --git a/emoji2/emoji2/build.gradle b/emoji2/emoji2/build.gradle
index d7e02b1..b741272 100644
--- a/emoji2/emoji2/build.gradle
+++ b/emoji2/emoji2/build.gradle
@@ -30,7 +30,7 @@
     api("androidx.core:core:1.3.0")
     api("androidx.startup:startup-runtime:1.0.0")
     implementation("androidx.collection:collection:1.1.0")
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.lifecycle:lifecycle-process:2.4.1")
 
     androidTestImplementation(libs.testExtJunit)
@@ -48,7 +48,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Core library to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/ConcurrencyHelpers.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/ConcurrencyHelpers.java
index 8b13e60..060086e 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/ConcurrencyHelpers.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/ConcurrencyHelpers.java
@@ -21,7 +21,6 @@
 import android.os.Looper;
 import android.os.Process;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -104,7 +103,6 @@
             // Non-instantiable.
         }
 
-        @DoNotInline
         public static Handler createAsync(Looper looper) {
             return Handler.createAsync(looper);
         }
diff --git a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiExclusions.java b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiExclusions.java
index f93959c..bfe5806 100644
--- a/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiExclusions.java
+++ b/emoji2/emoji2/src/main/java/androidx/emoji2/text/EmojiExclusions.java
@@ -19,7 +19,6 @@
 import android.annotation.SuppressLint;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -44,7 +43,6 @@
         private EmojiExclusions_Api34() { /* cannot instantiate */ }
 
         @NonNull
-        @DoNotInline
         static Set<int[]> getExclusions() {
             // TODO: Call directly when API34 is published
             return EmojiExclusions_Reflections.getExclusions();
diff --git a/emoji2/integration-tests/init-disabled-macrobenchmark-target/build.gradle b/emoji2/integration-tests/init-disabled-macrobenchmark-target/build.gradle
index fbbe188..0eda6405 100644
--- a/emoji2/integration-tests/init-disabled-macrobenchmark-target/build.gradle
+++ b/emoji2/integration-tests/init-disabled-macrobenchmark-target/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdk 35
     buildTypes {
         release {
             minifyEnabled true
diff --git a/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle b/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
index 52f133f..66b2b6c 100644
--- a/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
+++ b/emoji2/integration-tests/init-enabled-macrobenchmark-target/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdk 35
     buildTypes {
         release {
             minifyEnabled true
diff --git a/enterprise/enterprise-feedback-testing/build.gradle b/enterprise/enterprise-feedback-testing/build.gradle
index 6b59159..8769718 100644
--- a/enterprise/enterprise-feedback-testing/build.gradle
+++ b/enterprise/enterprise-feedback-testing/build.gradle
@@ -40,5 +40,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2019"
     description = "Test utilities for enterprise-feedback."
-    metalavaK2UastEnabled = true
 }
diff --git a/enterprise/enterprise-feedback/build.gradle b/enterprise/enterprise-feedback/build.gradle
index a1bc13a..2819247 100644
--- a/enterprise/enterprise-feedback/build.gradle
+++ b/enterprise/enterprise-feedback/build.gradle
@@ -12,7 +12,7 @@
     id("com.android.library")
 }
 dependencies {
-    api("androidx.annotation:annotation:1.0.1")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.autoValueAnnotations)
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
@@ -32,5 +32,4 @@
     inceptionYear = "2019"
     description = "A channel to enable communication between an app and an EMM (enterprise " +
             "mobility management)"
-    metalavaK2UastEnabled = true
 }
diff --git a/exifinterface/exifinterface/build.gradle b/exifinterface/exifinterface/build.gradle
index a141bd7..4734d18 100644
--- a/exifinterface/exifinterface/build.gradle
+++ b/exifinterface/exifinterface/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testExtTruth)
@@ -27,7 +27,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2016"
     description = "Android Support ExifInterface"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
index 1e4745c..0d41640 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
@@ -18,6 +18,7 @@
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
@@ -193,33 +194,88 @@
     }
 
     /**
-     * Returns the number of times {@code pattern} appears in {@code source}.
+     * {@link R.raw#jpeg_with_xmp_in_exif_first_then_separate_app1} contains an Exif APP1 segment
+     * with the same XMP as {@link R.raw#jpeg_with_exif_with_xmp}, a separate XMP APP1 segment
+     * containing {@link #TEST_XMP}.
      *
-     * <p>Overlapping occurrences are counted multiple times, e.g. {@code countOccurrences([0, 1, 0,
-     * 1, 0], [0, 1, 0])} will return 2.
+     * <p>This test asserts that the Exif XMP is returned, but that the separate XMP APP1 segment is
+     * preserved when saving.
      */
-    private static int countOccurrences(byte[] source, byte[] pattern) {
-        int count = 0;
-        for (int i = 0; i < source.length - pattern.length; i++) {
-            if (containsAtIndex(source, i, pattern)) {
-                count++;
-            }
-        }
-        return count;
+    @Test
+    @LargeTest
+    public void testJpegWithXmpInTwoSegments_exifFirst_exifXmpReturned_separateXmpPreserved()
+            throws Throwable {
+        File imageFile =
+                copyFromResourceToFile(
+                        R.raw.jpeg_with_xmp_in_exif_first_then_separate_app1,
+                        "jpeg_with_xmp_in_exif_first_then_separate_app1.jpg");
+        ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+
+        String xmp =
+                new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+
+        String expectedXmp =
+                ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.getXmp(
+                        getApplicationContext().getResources());
+        assertThat(xmp).isEqualTo(expectedXmp);
+
+        exifInterface.saveAttributes();
+
+        xmp =
+                new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+        assertThat(xmp).isEqualTo(expectedXmp);
+        byte[] imageBytes = Files.toByteArray(imageFile);
+        assertThat(countOccurrences(imageBytes, TEST_XMP.getBytes(Charsets.UTF_8))).isEqualTo(1);
     }
 
     /**
-     * Returns {@code true} if {@code source} contains {@code pattern} starting at {@code index}.
-     *
-     * @throws IndexOutOfBoundsException if {@code source.length < index + pattern.length}.
+     * Same as {@link
+     * #testJpegWithXmpInTwoSegments_exifFirst_exifXmpReturned_separateXmpPreserved()} but with the
+     * standalone XMP APP1 segment before the Exif one.
      */
-    private static boolean containsAtIndex(byte[] source, int index, byte[] pattern) {
-        for (int i = 0; i < pattern.length; i++) {
-            if (pattern[i] != source[index + i]) {
-                return false;
-            }
-        }
-        return true;
+    @Test
+    @LargeTest
+    public void
+            testJpegWithXmpInTwoSegmentsWithSeparateApp1First_exifXmpReturnedSeparateXmpPreserved()
+                    throws Throwable {
+        File imageFile =
+                copyFromResourceToFile(
+                        R.raw.jpeg_with_xmp_in_separate_app1_first_then_exif,
+                        "jpeg_with_xmp_in_separate_app1_first_then_exif.jpg");
+        ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+
+        String xmp =
+                new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+
+        String expectedXmp =
+                ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.getXmp(
+                        getApplicationContext().getResources());
+        assertThat(xmp).isEqualTo(expectedXmp);
+
+        exifInterface.saveAttributes();
+
+        xmp =
+                new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+        assertThat(xmp).isEqualTo(expectedXmp);
+        byte[] imageBytes = Files.toByteArray(imageFile);
+        assertThat(countOccurrences(imageBytes, TEST_XMP.getBytes(Charsets.UTF_8))).isEqualTo(1);
+    }
+
+    @Test
+    @LargeTest
+    public void testJpeg_noXmp_addXmp_writtenInSeparateSegment() throws Throwable {
+        File imageFile =
+                copyFromResourceToFile(
+                        R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+        ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+
+        checkState(!exifInterface.hasAttribute(ExifInterface.TAG_XMP));
+        exifInterface.setAttribute(ExifInterface.TAG_XMP, TEST_XMP);
+        exifInterface.saveAttributes();
+
+        byte[] imageBytes = Files.toByteArray(imageFile);
+        byte[] xmpApp1SegmentMarker = "http://ns.adobe.com/xap/1.0/\0".getBytes(Charsets.US_ASCII);
+        assertThat(countOccurrences(imageBytes, xmpApp1SegmentMarker)).isEqualTo(1);
     }
 
     // https://issuetracker.google.com/264729367
@@ -2129,6 +2185,36 @@
     }
 
     /**
+     * Returns the number of times {@code pattern} appears in {@code source}.
+     *
+     * <p>Overlapping occurrences are counted multiple times, e.g. {@code countOccurrences([0, 1, 0,
+     * 1, 0], [0, 1, 0])} will return 2.
+     */
+    private static int countOccurrences(byte[] source, byte[] pattern) {
+        int count = 0;
+        for (int i = 0; i < source.length - pattern.length; i++) {
+            if (containsAtIndex(source, i, pattern)) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Returns {@code true} if {@code source} contains {@code pattern} starting at {@code index}.
+     *
+     * @throws IndexOutOfBoundsException if {@code source.length < index + pattern.length}.
+     */
+    private static boolean containsAtIndex(byte[] source, int index, byte[] pattern) {
+        for (int i = 0; i < pattern.length; i++) {
+            if (pattern[i] != source[index + i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * An operation that can be applied to an {@link ExifInterface} instance.
      *
      * <p>We would use java.util.Consumer but it's not available before API 24, and there's no Guava
diff --git a/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_exif_first_then_separate_app1.jpg b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_exif_first_then_separate_app1.jpg
new file mode 100644
index 0000000..fb1ca3e
--- /dev/null
+++ b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_exif_first_then_separate_app1.jpg
Binary files differ
diff --git a/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_separate_app1_first_then_exif.jpg b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_separate_app1_first_then_exif.jpg
new file mode 100644
index 0000000..4b187fd
--- /dev/null
+++ b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_separate_app1_first_then_exif.jpg
Binary files differ
diff --git a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
index 863584a0..e347d51 100644
--- a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
+++ b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
@@ -87,11 +87,42 @@
 
 /**
  * This is a class for reading and writing Exif tags in various image file formats.
+ *
+ * <p>Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW,
+ * RAF.
+ *
+ * <p>Supported for writing: JPEG, PNG, WebP.
+ *
  * <p>
- * Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF.
- * <p>
- * Supported for writing: JPEG, PNG, WebP.
- * <p>
+ *
+ * <h3>XMP Support</h3>
+ *
+ * This class can read raw XMP data from the supported image file formats.
+ *
+ * <p>XMP data can be stored within Exif data (under tag 700), but many of the formats also define a
+ * separate storage location for XMP. ExifInterface handles this ambiguity as follows:
+ *
+ * <ul>
+ *   <li>JPEG
+ *       <ul>
+ *         <li>The XMP spec part 3 section 3.3.2 forbids the XMP tag (700) being present in the Exif
+ *             segment of JPEG files (i.e. XMP should always be in a separate APP1 segment).
+ *         <li>If XMP is present in both Exif and separate segments, the XMP from the Exif segment
+ *             is returned from {@link #getAttributeBytes} and modifications to the XMP with {@link
+ *             #setAttribute} are written back to the XMP in the Exif segment, the XMP in the
+ *             separate segment is preserved unmodified. This is contrary to the spec described
+ *             above (which suggests the standalone XMP should be preferred over the XMP in the Exif
+ *             segment).
+ *         <li>If XMP is not present in either location, and is added with {@link #setAttribute}, it
+ *             is written as a standalone segment, in line with the spec described above.
+ *       </ul>
+ *   <li>HEIF
+ *       <ul>
+ *         <li>If XMP is present in both Exif and separate segments, the XMP from the Exif segment
+ *             is returned from {@link #getAttributeBytes}.
+ *       </ul>
+ * </ul>
+ *
  * Note: JPEG and HEIF files may contain XMP data either inside the Exif data chunk or outside of
  * it. This class will search both locations for XMP data, but if XMP data exist both inside and
  * outside Exif, will favor the XMP data inside Exif over the one outside.
@@ -2234,8 +2265,11 @@
     public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
     /**
      * Type is byte[]. See <a href=
-     * "https://en.wikipedia.org/wiki/Extensible_Metadata_Platform">Extensible
-     * Metadata Platform (XMP)</a> for details on contents.
+     * "https://en.wikipedia.org/wiki/Extensible_Metadata_Platform">Extensible Metadata Platform
+     * (XMP)</a> for details on contents.
+     *
+     * <p>See also notes about XMP handling in different containers in the class-level javadoc of
+     * this class.
      */
     public static final String TAG_XMP = "Xmp";
     /** Type is int. See JEITA CP-3451C Spec Section 3: Bilevel Images. */
@@ -5707,6 +5741,7 @@
                     length = 0;
 
                     if (startsWith(bytes, IDENTIFIER_EXIF_APP1)) {
+                        byte[] xmpBeforeReadingExif = getAttributeBytes(TAG_XMP);
                         final byte[] value = Arrays.copyOfRange(bytes, IDENTIFIER_EXIF_APP1.length,
                                 bytes.length);
                         // Save offset to EXIF data for handling thumbnail and attribute offsets.
@@ -5716,6 +5751,16 @@
                         readExifSegment(value, imageType);
 
                         setThumbnailData(new ByteOrderedDataInputStream(value));
+
+                        if (getAttributeBytes(TAG_XMP) == null) {
+                            // XMP should be stored in a separate APP1 segment (see XMP spec part 3
+                            // section 3.3.2). If the Exif segment didn't contain XMP then we set
+                            // this to true to ensure any XMP data added will get written out to a
+                            // separate segment.
+                            mXmpIsFromSeparateMarker = true;
+                        } else if (xmpBeforeReadingExif != getAttributeBytes(TAG_XMP)) {
+                            mXmpIsFromSeparateMarker = false;
+                        }
                     } else if (startsWith(bytes, IDENTIFIER_XMP_APP1)) {
                         // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
                         final int offset = start + IDENTIFIER_XMP_APP1.length;
@@ -6449,7 +6494,8 @@
                     if (identifier != null) {
                         dataInputStream.readFully(identifier);
                         if (startsWith(identifier, IDENTIFIER_EXIF_APP1)
-                                || startsWith(identifier, IDENTIFIER_XMP_APP1)) {
+                                || (startsWith(identifier, IDENTIFIER_XMP_APP1)
+                                        && mXmpIsFromSeparateMarker)) {
                             // Skip the original EXIF or XMP APP1 segment.
                             dataInputStream.skipFully(length - identifier.length);
                             break;
diff --git a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java
index a7033b4..5958a03 100644
--- a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java
+++ b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java
@@ -23,7 +23,6 @@
 import android.system.Os;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 
 import java.io.Closeable;
@@ -164,17 +163,14 @@
     static class Api21Impl {
         private Api21Impl() {}
 
-        @DoNotInline
         static FileDescriptor dup(FileDescriptor fileDescriptor) throws ErrnoException {
             return Os.dup(fileDescriptor);
         }
 
-        @DoNotInline
         static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
             return Os.lseek(fd, offset, whence);
         }
 
-        @DoNotInline
         static void close(FileDescriptor fd) throws ErrnoException {
             Os.close(fd);
         }
@@ -184,7 +180,6 @@
     static class Api23Impl {
         private Api23Impl() {}
 
-        @DoNotInline
         static void setDataSource(MediaMetadataRetriever retriever, MediaDataSource dataSource) {
             retriever.setDataSource(dataSource);
         }
diff --git a/fragment/fragment-compose/api/current.txt b/fragment/fragment-compose/api/current.txt
index bd7f6f1..e0225e0 100644
--- a/fragment/fragment-compose/api/current.txt
+++ b/fragment/fragment-compose/api/current.txt
@@ -11,6 +11,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class FragmentState {
+    ctor public FragmentState();
     ctor public FragmentState(optional androidx.compose.runtime.MutableState<androidx.fragment.app.Fragment.SavedState?> state);
   }
 
diff --git a/fragment/fragment-compose/api/restricted_current.txt b/fragment/fragment-compose/api/restricted_current.txt
index bd7f6f1..e0225e0 100644
--- a/fragment/fragment-compose/api/restricted_current.txt
+++ b/fragment/fragment-compose/api/restricted_current.txt
@@ -11,6 +11,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class FragmentState {
+    ctor public FragmentState();
     ctor public FragmentState(optional androidx.compose.runtime.MutableState<androidx.fragment.app.Fragment.SavedState?> state);
   }
 
diff --git a/fragment/fragment-compose/build.gradle b/fragment/fragment-compose/build.gradle
index 1660f2b..97fd475 100644
--- a/fragment/fragment-compose/build.gradle
+++ b/fragment/fragment-compose/build.gradle
@@ -38,21 +38,36 @@
     api(project(":fragment:fragment-ktx"))
     api("androidx.compose.runtime:runtime:1.5.4")
     api("androidx.compose.ui:ui:1.5.4")
+    implementation("androidx.compose.runtime:runtime-saveable:1.5.4")
 
+    androidTestImplementation("androidx.activity:activity:1.8.2")
     androidTestImplementation("androidx.activity:activity-compose:1.8.2")
-    androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+    androidTestImplementation("androidx.lifecycle:lifecycle-common:2.8.2")
+    androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel:2.8.2")
+    androidTestImplementation("androidx.compose.foundation:foundation-layout:1.6.0")
     androidTestImplementation(project(":compose:material:material"))
+    androidTestImplementation(project(":compose:runtime:runtime"))
     androidTestImplementation(project(":compose:test-utils"))
+    androidTestImplementation(project(":compose:ui:ui"))
+    androidTestImplementation(project(":compose:ui:ui-test"))
+    androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+    androidTestImplementation(project(":compose:ui:ui-text"))
+    androidTestImplementation(project(":compose:ui:ui-unit"))
     androidTestImplementation(libs.espressoCore, excludes.espresso)
+    androidTestImplementation(libs.hamcrestCore)
     androidTestImplementation(project(":internal-testutils-runtime"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testCore)
 }
 
 android {
+    compileSdk 35
     namespace "androidx.fragment.compose"
+    // TODO(b/313699418): need to update compose.runtime version to 1.6.0+
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
diff --git a/fragment/fragment-compose/samples/build.gradle b/fragment/fragment-compose/samples/build.gradle
index 2c5b6d4..9266e64 100644
--- a/fragment/fragment-compose/samples/build.gradle
+++ b/fragment/fragment-compose/samples/build.gradle
@@ -27,12 +27,12 @@
     implementation(libs.kotlinStdlib)
 
     compileOnly(project(":annotation:annotation-sampled"))
-    implementation("androidx.compose.foundation:foundation:1.0.1")
     implementation("androidx.compose.ui:ui-tooling:1.4.0")
     implementation(project(":fragment:fragment-compose"))
-    implementation("androidx.compose.material:material:1.0.1")
-    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.0")
-    implementation("androidx.savedstate:savedstate-ktx:1.2.1")
+    implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
+    implementation("androidx.compose.runtime:runtime:1.5.4")
+    implementation("androidx.compose.ui:ui:1.5.4")
+    implementation("androidx.core:core-ktx:1.12.0")
 }
 
 androidx {
@@ -43,5 +43,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.fragment.compose.samples"
 }
diff --git a/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/FragmentRecreateTest.kt b/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/FragmentRecreateTest.kt
index 35b90a4..3ea3cc3 100644
--- a/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/FragmentRecreateTest.kt
+++ b/fragment/fragment-compose/src/androidTest/java/androidx/fragment/compose/FragmentRecreateTest.kt
@@ -104,6 +104,54 @@
             assertThat(recreatedEditText.text.toString()).isEqualTo("Updated")
         }
     }
+
+    @Test
+    fun testReplaceParentFragment() {
+        with(ActivityScenario.launch(ChildInflatedFragmentActivity::class.java)) {
+            val fragment = withActivity {
+                FragmentManager.findFragment<SimpleEditTextFragment>(
+                    findViewById(R.id.fragment_layout)
+                )
+            }
+
+            assertWithMessage("Fragment should be added as a child fragment")
+                .that(fragment)
+                .isNotNull()
+            assertThat(fragment.requireView().parent).isNotNull()
+            val editText: EditText = fragment.requireView().findViewById(R.id.edit_text)
+            assertThat(editText.text.toString()).isEqualTo("Default")
+
+            // Update the state to make sure it gets saved and restored properly
+            withActivity { editText.setText("Updated") }
+
+            val replaceFragment = MyFragment()
+            withActivity {
+                supportFragmentManager.commit {
+                    setReorderingAllowed(true)
+                    replace(PARENT_FRAGMENT_CONTAINER_ID, replaceFragment)
+                    addToBackStack(null)
+                }
+                supportFragmentManager.executePendingTransactions()
+            }
+
+            // Now pop the back stack and go back to the parent fragment
+            withActivity { supportFragmentManager.popBackStackImmediate() }
+
+            val recreatedFragment = withActivity {
+                FragmentManager.findFragment<SimpleEditTextFragment>(
+                    findViewById(R.id.fragment_layout)
+                )
+            }
+            assertWithMessage("Fragment should be re-added").that(recreatedFragment).isNotNull()
+            assertWithMessage("Fragment should be added as a child fragment")
+                .that(recreatedFragment.parentFragment)
+                .isNotNull()
+            assertThat(recreatedFragment.requireView().parent).isNotNull()
+            val recreatedEditText: EditText =
+                recreatedFragment.requireView().findViewById(R.id.edit_text)
+            assertThat(recreatedEditText.text.toString()).isEqualTo("Updated")
+        }
+    }
 }
 
 class ChildInflatedFragmentActivity : FragmentActivity() {
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index 5d166ded..2e32561 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -64,7 +64,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'fragment' artifact"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/fragment/fragment-lint/build.gradle b/fragment/fragment-lint/build.gradle
index 1a48d86..f1c6dfd 100644
--- a/fragment/fragment-lint/build.gradle
+++ b/fragment/fragment-lint/build.gradle
@@ -31,12 +31,17 @@
 dependencies {
     compileOnly(libs.androidLintMinApi)
     compileOnly(libs.kotlinStdlib)
+    compileOnly(libs.androidLayoutlibApi)
     compileOnly(libs.androidLintChecksMin)
+    compileOnly(libs.androidToolsCommon)
+    compileOnly(libs.intellijCore)
+    compileOnly(libs.uast)
+    compileOnly(libs.intellijKotlinCompiler)
 
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.kotlinReflect)
+    testRuntimeOnly(libs.kotlinReflect)
     testImplementation(libs.kotlinStdlibJdk8)
-    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintApi)
     testImplementation(libs.androidLintTests)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/fragment/fragment-testing-lint/build.gradle b/fragment/fragment-testing-lint/build.gradle
index b91fa08..b3db005 100644
--- a/fragment/fragment-testing-lint/build.gradle
+++ b/fragment/fragment-testing-lint/build.gradle
@@ -31,9 +31,10 @@
 dependencies {
     compileOnly(libs.androidLintMinApi)
     compileOnly(libs.kotlinStdlib)
+    compileOnly(libs.intellijCore)
 
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintApi)
     testImplementation(libs.androidLintTests)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/fragment/fragment-testing-manifest-lint/build.gradle b/fragment/fragment-testing-manifest-lint/build.gradle
index e82f5a8..3a94f2d 100644
--- a/fragment/fragment-testing-manifest-lint/build.gradle
+++ b/fragment/fragment-testing-manifest-lint/build.gradle
@@ -31,9 +31,10 @@
 dependencies {
     compileOnly(libs.androidLintMinApi)
     compileOnly(libs.kotlinStdlib)
+    compileOnly(libs.intellijCore)
 
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintApi)
     testImplementation(libs.androidLintTests)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/fragment/fragment-testing-manifest/build.gradle b/fragment/fragment-testing-manifest/build.gradle
index ee02713..94663cb 100644
--- a/fragment/fragment-testing-manifest/build.gradle
+++ b/fragment/fragment-testing-manifest/build.gradle
@@ -32,6 +32,9 @@
 
 dependencies {
     api(project(":fragment:fragment-ktx"))
+    api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
+    implementation("androidx.activity:activity-ktx:1.8.1")
+    implementation("androidx.activity:activity:1.8.1")
     api(libs.kotlinStdlib)
 
     lintPublish(project(":fragment:fragment-testing-manifest-lint"))
@@ -42,7 +45,6 @@
     type = LibraryType.PUBLISHED_TEST_LIBRARY
     inceptionYear = "2022"
     description = "Fragment testing library that should be added as a debugImplementation dependency to add properties to the debug manifest necessary for testing an application"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "No public API"
 }
diff --git a/fragment/fragment-testing/build.gradle b/fragment/fragment-testing/build.gradle
index 8247092..e782228 100644
--- a/fragment/fragment-testing/build.gradle
+++ b/fragment/fragment-testing/build.gradle
@@ -33,13 +33,18 @@
     api(project(":fragment:fragment-ktx"))
     api("androidx.test:core:1.5.0")
     api(libs.kotlinStdlib)
+    api("androidx.lifecycle:lifecycle-common:2.6.1")
     api(project(":fragment:fragment-testing-manifest"))
+    androidTestImplementation("androidx.annotation:annotation:1.8.1")
+    androidTestImplementation("androidx.core:core:1.9.0")
+    androidTestImplementation("androidx.lifecycle:lifecycle-livedata-core:2.6.1")
+    androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.hamcrestCore)
+    androidTestImplementation(libs.junit)
     androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testRules)
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
@@ -52,7 +57,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Extensions for testing 'fragment' artifact"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/fragment/fragment-truth/build.gradle b/fragment/fragment-truth/build.gradle
index 6bdf72d..ea47b74 100644
--- a/fragment/fragment-truth/build.gradle
+++ b/fragment/fragment-truth/build.gradle
@@ -33,9 +33,8 @@
     api(project(":fragment:fragment-ktx"))
     api(libs.truth)
     api(libs.kotlinStdlib)
-    androidTestImplementation(libs.junit)
-    androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.testCore)
+    androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
+    androidTestRuntimeOnly(libs.testCore)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRules)
     androidTestImplementation(libs.testRunner)
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index 6734ce4..ae9ed51 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -29,12 +29,13 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core-ktx:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
+    api("androidx.core:core-ktx:1.8.0")
     api("androidx.collection:collection:1.1.0")
     api("androidx.viewpager:viewpager:1.0.0")
     api("androidx.loader:loader:1.0.0")
     api("androidx.activity:activity:1.8.1")
+    api("androidx.lifecycle:lifecycle-common:2.6.1")
     api("androidx.lifecycle:lifecycle-runtime:2.6.1")
     api("androidx.lifecycle:lifecycle-livedata-core:2.6.1")
     api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
@@ -43,16 +44,19 @@
     api("androidx.savedstate:savedstate:1.2.1")
     api("androidx.annotation:annotation-experimental:1.4.1")
     api(libs.kotlinStdlib)
+    implementation("androidx.arch.core:core-common:2.2.0")
 
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0", {
         exclude group: "androidx.fragment", module: "fragment"
         exclude group: "androidx.activity", module: "activity"
     })
     androidTestImplementation(libs.kotlinStdlib)
-    androidTestImplementation(libs.leakcanary)
     androidTestImplementation(libs.leakcanaryInstrumentation)
+    androidTestImplementation(libs.hamcrestCore)
+    androidTestImplementation(libs.junit)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testMonitor)
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.testRules)
     androidTestImplementation(libs.truth)
@@ -65,9 +69,8 @@
 
     testImplementation(projectOrArtifact(":fragment:fragment"))
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.testExtJunit)
-    testImplementation(libs.testCore)
-    testImplementation(libs.testRules)
+    testImplementation(libs.junit)
+    testRuntimeOnly(libs.testCore)
     testImplementation(libs.truth)
     testImplementation(libs.robolectric)
 
@@ -79,6 +82,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2011"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimatorTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimatorTest.kt
index eec950b..f8562d5 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimatorTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimatorTest.kt
@@ -680,14 +680,12 @@
         activityRule.runOnUiThread {
             dispatcher.dispatchOnBackStarted(BackEventCompat(0.1F, 0.1F, 0.1F, BackEvent.EDGE_LEFT))
         }
-        activityRule.executePendingTransactions(fm1)
 
         activityRule.runOnUiThread {
             dispatcher.dispatchOnBackProgressed(
                 BackEventCompat(0.2F, 0.2F, 0.2F, BackEvent.EDGE_LEFT)
             )
         }
-        activityRule.executePendingTransactions(fm1)
 
         if (FragmentManager.USE_PREDICTIVE_BACK) {
             assertThat(fragment1.startLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
@@ -700,7 +698,6 @@
         }
 
         activityRule.runOnUiThread { dispatcher.onBackPressed() }
-        activityRule.executePendingTransactions(fm1)
 
         assertThat(fragment2.wasStarted).isTrue()
         // Now fragment2 should be animating away
@@ -749,7 +746,6 @@
         activityRule.runOnUiThread {
             dispatcher.dispatchOnBackStarted(BackEventCompat(0.1F, 0.1F, 0.1F, BackEvent.EDGE_LEFT))
         }
-        activityRule.executePendingTransactions(fm1)
 
         fragment2.resumeLatch = CountDownLatch(1)
 
@@ -758,7 +754,6 @@
                 BackEventCompat(0.2F, 0.2F, 0.2F, BackEvent.EDGE_LEFT)
             )
         }
-        activityRule.executePendingTransactions(fm1)
 
         if (FragmentManager.USE_PREDICTIVE_BACK) {
             assertThat(fragment1.startLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
@@ -771,7 +766,6 @@
         }
 
         activityRule.runOnUiThread { dispatcher.dispatchOnBackCancelled() }
-        activityRule.executePendingTransactions(fm1)
 
         assertThat(fragment2.wasStarted).isTrue()
         // Now fragment1 should be animating away
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
index 008873c..5e6d2c7 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
@@ -28,6 +28,8 @@
 import androidx.test.filters.SdkSuppress
 import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.Rule
 import org.junit.Test
@@ -550,12 +552,10 @@
 
             withActivity {
                 onBackPressedDispatcher.dispatchOnBackStarted(BackEventCompat(0f, 0f, 0f, 0))
-                executePendingTransactions()
             }
 
             withActivity {
                 onBackPressedDispatcher.dispatchOnBackProgressed(BackEventCompat(0f, 0f, 0.5f, 0))
-                executePendingTransactions()
             }
 
             if (FragmentManager.USE_PREDICTIVE_BACK) {
@@ -589,6 +589,10 @@
             var cancelledCount = 0
             var backStackChangedCount = 0
 
+            val startedLatch = CountDownLatch(1)
+            val cancelLatch = CountDownLatch(1)
+            val changedLatch = CountDownLatch(1)
+
             withActivity {
                 fragmentManager
                     .beginTransaction()
@@ -609,17 +613,16 @@
                 executePendingTransactions()
             }
 
-            var beforeOnBackStackChanged = false
-
             val listener =
                 object : OnBackStackChangedListener {
                     override fun onBackStackChanged() {
-                        beforeOnBackStackChanged = false
                         backStackChangedCount++
+                        changedLatch.countDown()
                     }
 
                     override fun onBackStackChangeStarted(fragment: Fragment, pop: Boolean) {
                         startedCount++
+                        startedLatch.countDown()
                     }
 
                     override fun onBackStackChangeCommitted(fragment: Fragment, pop: Boolean) {
@@ -627,8 +630,9 @@
                     }
 
                     override fun onBackStackChangeCancelled() {
-                        if (beforeOnBackStackChanged) {
+                        if (backStackChangedCount == 1) {
                             cancelledCount++
+                            cancelLatch.countDown()
                         }
                     }
                 }
@@ -636,30 +640,31 @@
 
             withActivity {
                 onBackPressedDispatcher.dispatchOnBackStarted(BackEventCompat(0f, 0f, 0f, 0))
-                executePendingTransactions()
             }
 
             if (FragmentManager.USE_PREDICTIVE_BACK) {
+                assertThat(startedLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
                 assertThat(startedCount).isEqualTo(1)
             } else {
                 assertThat(startedCount).isEqualTo(0)
             }
-            assertThat(backStackChangedCount).isEqualTo(1)
-            assertThat(committedCount).isEqualTo(0)
 
-            beforeOnBackStackChanged = true
+            assertThat(changedLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+            assertThat(backStackChangedCount).isEqualTo(1)
+
+            assertThat(committedCount).isEqualTo(0)
 
             withActivity { onBackPressedDispatcher.dispatchOnBackCancelled() }
 
             if (FragmentManager.USE_PREDICTIVE_BACK) {
                 assertThat(startedCount).isEqualTo(1)
+                assertThat(cancelLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
                 assertThat(cancelledCount).isEqualTo(1)
             } else {
                 assertThat(startedCount).isEqualTo(0)
             }
             assertThat(committedCount).isEqualTo(0)
             assertThat(backStackChangedCount).isEqualTo(2)
-            assertThat(beforeOnBackStackChanged).isFalse()
 
             assertThat(fragment2).isSameInstanceAs(fragmentManager.findFragmentById(R.id.content))
         }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
index 7723f08..c1f1c03 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
@@ -303,12 +303,12 @@
 
     @Override
     public int commit() {
-        return commitInternal(false);
+        return commitInternal(false, true);
     }
 
     @Override
     public int commitAllowingStateLoss() {
-        return commitInternal(true);
+        return commitInternal(true, true);
     }
 
     @Override
@@ -323,7 +323,7 @@
         mManager.execSingleAction(this, true);
     }
 
-    int commitInternal(boolean allowStateLoss) {
+    int commitInternal(boolean allowStateLoss, boolean commitAction) {
         if (mCommitted) throw new IllegalStateException("commit already called");
         if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
             Log.v(TAG, "Commit: " + this);
@@ -338,7 +338,9 @@
         } else {
             mIndex = -1;
         }
-        mManager.enqueueAction(this, allowStateLoss);
+        if (commitAction) {
+            mManager.enqueueAction(this, allowStateLoss);
+        }
         return mIndex;
     }
 
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
index 66aa154..b1923e8 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
@@ -27,7 +27,6 @@
 import android.view.ViewGroup
 import android.view.animation.Animation
 import androidx.activity.BackEventCompat
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.collection.ArrayMap
 import androidx.core.view.OneShotPreDrawListener
@@ -1210,7 +1209,6 @@
 
     @RequiresApi(24)
     internal object Api24Impl {
-        @DoNotInline
         fun totalDuration(animatorSet: AnimatorSet): Long {
             return animatorSet.totalDuration
         }
@@ -1218,12 +1216,10 @@
 
     @RequiresApi(26)
     internal object Api26Impl {
-        @DoNotInline
         fun reverse(animatorSet: AnimatorSet) {
             animatorSet.reverse()
         }
 
-        @DoNotInline
         fun setCurrentPlayTime(animatorSet: AnimatorSet, time: Long) {
             animatorSet.currentPlayTime = time
         }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
index 7e09f69..1986b9c 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -184,6 +184,10 @@
     // Set to true when the view has actually been inflated in its layout.
     boolean mInLayout;
 
+    // Set to true if the fragment has been added to a specific container
+    // that may not be available immediately.
+    boolean mInDynamicContainer;
+
     // True if this fragment has been restored from previously saved state.
     boolean mRestored;
 
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 7afd354..e3d35ae 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -516,7 +516,9 @@
 
     BackStackRecord mTransitioningOp = null;
 
-    boolean mBackStarted = false;
+    // signal to indicate whether execPendingAction is coming from handleOnBackPressed
+    // or cancelBackStackTransition that prevents the mTransitioningOp from being recommited.
+    boolean mHandlingTransitioningOp = false;
     private final OnBackPressedCallback mOnBackPressedCallback =
             new OnBackPressedCallback(false) {
 
@@ -580,7 +582,6 @@
                     }
                     if (USE_PREDICTIVE_BACK) {
                         cancelBackStackTransition();
-                        mTransitioningOp = null;
                     }
                 }
             };
@@ -859,7 +860,9 @@
         // up to date view of the world just in case anyone is queuing
         // up transactions that change the back stack then immediately
         // calling onBackPressed()
+        mHandlingTransitioningOp = true;
         execPendingActions(true);
+        mHandlingTransitioningOp = false;
         if (USE_PREDICTIVE_BACK && mTransitioningOp != null) {
             if (!mBackStackChangeListeners.isEmpty()) {
                 // Build a list of fragments based on the records
@@ -1053,6 +1056,9 @@
     }
 
     void cancelBackStackTransition() {
+        if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
+            Log.d(TAG, "cancelBackStackTransition for transition " + mTransitioningOp);
+        }
         if (mTransitioningOp != null) {
             mTransitioningOp.mCommitted = false;
             mTransitioningOp.runOnCommitInternal(true, () -> {
@@ -1061,7 +1067,10 @@
                 }
             });
             mTransitioningOp.commit();
+            mHandlingTransitioningOp = true;
             executePendingTransactions();
+            mHandlingTransitioningOp = false;
+            mTransitioningOp = null;
         }
     }
 
@@ -1343,6 +1352,7 @@
             ) {
                 fragment.mContainer = container;
                 fragmentStateManager.addViewToContainer();
+                fragmentStateManager.moveToExpectedState();
             }
         }
     }
@@ -1964,7 +1974,27 @@
             return;
         }
         ensureExecReady(allowStateLoss);
-        if (action.generateOps(mTmpRecords, mTmpIsPop)) {
+        boolean generateOpsResult = false;
+        // If we are interrupting a predictive back gesture with an incoming action,
+        // we cancel the gesture by recommitting the mTransitioningOp and adding the ops
+        // to the records about to be executed.
+        if (mTransitioningOp != null) {
+            mTransitioningOp.mCommitted = false;
+            if (isLoggingEnabled(Log.DEBUG)) {
+                Log.d(TAG, "Reversing mTransitioningOp " + mTransitioningOp
+                        + " as part of execSingleAction for action " + action);
+            }
+            mTransitioningOp.commitInternal(false, false);
+            generateOpsResult = mTransitioningOp.generateOps(mTmpRecords, mTmpIsPop);
+            for (FragmentTransaction.Op op : mTransitioningOp.mOps) {
+                if (op.mFragment != null) {
+                    op.mFragment.mTransitioning = false;
+                }
+            }
+            mTransitioningOp = null;
+        }
+        boolean actionOpsResult = action.generateOps(mTmpRecords, mTmpIsPop);
+        if (generateOpsResult || actionOpsResult) {
             mExecutingActions = true;
             try {
                 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
@@ -1995,6 +2025,24 @@
         ensureExecReady(allowStateLoss);
 
         boolean didSomething = false;
+        // If we are interrupting a predictive back gesture with an incoming action,
+        // we cancel the gesture by recommitting the mTransitioningOp and executing it
+        // as the first pending action.
+        if (!mHandlingTransitioningOp && mTransitioningOp != null) {
+            mTransitioningOp.mCommitted = false;
+            if (isLoggingEnabled(Log.DEBUG)) {
+                Log.d(TAG, "Reversing mTransitioningOp " + mTransitioningOp
+                        + " as part of execPendingActions for actions " + mPendingActions);
+            }
+            mTransitioningOp.commitInternal(false, false);
+            mPendingActions.add(0, mTransitioningOp);
+            for (FragmentTransaction.Op op : mTransitioningOp.mOps) {
+                if (op.mFragment != null) {
+                    op.mFragment.mTransitioning = false;
+                }
+            }
+            mTransitioningOp = null;
+        }
         while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
             mExecutingActions = true;
             try {
@@ -3797,7 +3845,6 @@
         public boolean generateOps(@NonNull ArrayList<BackStackRecord> records,
                 @NonNull ArrayList<Boolean> isRecordPop) {
             boolean result = prepareBackStackState(records, isRecordPop);
-            mBackStarted = true;
             // Dispatch started signal to onBackStackChangedListeners.
             if (!mBackStackChangeListeners.isEmpty()) {
                 if (records.size() > 0) {
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentState.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentState.java
index a3f476d..e8a5d12 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentState.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentState.java
@@ -28,6 +28,7 @@
     final String mClassName;
     final String mWho;
     final boolean mFromLayout;
+    final boolean mInDynamicContainer;
     final int mFragmentId;
     final int mContainerId;
     final String mTag;
@@ -44,6 +45,7 @@
         mClassName = frag.getClass().getName();
         mWho = frag.mWho;
         mFromLayout = frag.mFromLayout;
+        mInDynamicContainer = frag.mInDynamicContainer;
         mFragmentId = frag.mFragmentId;
         mContainerId = frag.mContainerId;
         mTag = frag.mTag;
@@ -61,6 +63,7 @@
         mClassName = in.readString();
         mWho = in.readString();
         mFromLayout = in.readInt() != 0;
+        mInDynamicContainer = in.readInt() != 0;
         mFragmentId = in.readInt();
         mContainerId = in.readInt();
         mTag = in.readString();
@@ -84,6 +87,7 @@
         Fragment fragment = fragmentFactory.instantiate(classLoader, mClassName);
         fragment.mWho = mWho;
         fragment.mFromLayout = mFromLayout;
+        fragment.mInDynamicContainer = mInDynamicContainer;
         fragment.mRestored = true;
         fragment.mFragmentId = mFragmentId;
         fragment.mContainerId = mContainerId;
@@ -111,6 +115,9 @@
         if (mFromLayout) {
             sb.append(" fromLayout");
         }
+        if (mInDynamicContainer) {
+            sb.append(" dynamicContainer");
+        }
         if (mContainerId != 0) {
             sb.append(" id=0x");
             sb.append(Integer.toHexString(mContainerId));
@@ -153,6 +160,7 @@
         dest.writeString(mClassName);
         dest.writeString(mWho);
         dest.writeInt(mFromLayout ? 1 : 0);
+        dest.writeInt(mInDynamicContainer ? 1 : 0);
         dest.writeInt(mFragmentId);
         dest.writeInt(mContainerId);
         dest.writeString(mTag);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
index 32d40bc..bbef906 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
@@ -206,6 +206,14 @@
                 }
             }
         }
+        // For fragments that are added via FragmentTransaction.add(ViewGroup)
+        if (mFragment.mInDynamicContainer) {
+            if (mFragment.mContainer == null) {
+                // If their container is not available yet (onContainerAvailable hasn't been
+                // called), don't allow the fragment to go beyond ACTIVITY_CREATED
+                maxState = Math.min(maxState, Fragment.ACTIVITY_CREATED);
+            }
+        }
         // Fragments that are not currently added will sit in the CREATED state.
         if (!mFragment.mAdded) {
             maxState = Math.min(maxState, Fragment.CREATED);
@@ -548,7 +556,7 @@
             FragmentContainer fragmentContainer = mFragment.mFragmentManager.getContainer();
             container = (ViewGroup) fragmentContainer.onFindViewById(mFragment.mContainerId);
             if (container == null) {
-                if (!mFragment.mRestored) {
+                if (!mFragment.mRestored && !mFragment.mInDynamicContainer) {
                     String resName;
                     try {
                         resName = mFragment.getResources().getResourceName(mFragment.mContainerId);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
index 841d354..0346112 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
@@ -308,6 +308,7 @@
     public final FragmentTransaction add(@NonNull ViewGroup container, @NonNull Fragment fragment,
             @Nullable String tag) {
         fragment.mContainer = container;
+        fragment.mInDynamicContainer = true;
         return add(container.getId(), fragment, tag);
     }
 
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt
index e2de00b..55efd95 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt
@@ -33,6 +33,7 @@
 internal abstract class SpecialEffectsController(val container: ViewGroup) {
     private val pendingOperations = mutableListOf<Operation>()
     private val runningOperations = mutableListOf<Operation>()
+    private var runningNonSeekableTransition = false
     private var operationDirectionIsPop = false
     private var isContainerPostponed = false
 
@@ -204,48 +205,48 @@
             return
         }
         synchronized(pendingOperations) {
-            if (pendingOperations.isEmpty()) {
-                val currentlyRunningOperations = runningOperations.toMutableList()
-                runningOperations.clear()
-                for (operation in currentlyRunningOperations) {
+            val currentlyRunningOperations = runningOperations.toMutableList()
+            runningOperations.clear()
+            for (operation in currentlyRunningOperations) {
+                // Another operation is about to run while we already have operations running
+                // There are 2 cases that need to be handled:
+                // 1. The previous running operations were transitioning, but not seeking. Here
+                // we were holding the animation until we the gesture was committed so we never
+                // started the effects and need to complete immediately.
+                // 2. The previous running operations were either transitioning and seeking, or
+                // not transitioning at all. In this case we are guaranteed to have starting the
+                // effect so we can just call cancel, passing in the transitioning status.
+                if (runningNonSeekableTransition) {
                     if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
                         Log.v(
                             FragmentManager.TAG,
-                            "SpecialEffectsController: Cancelling operation $operation " +
-                                "with no incoming pendingOperations"
+                            "SpecialEffectsController: Completing non-seekable " +
+                                "operation $operation"
                         )
                     }
-                    // Cancel the currently running operation immediately as this is the case
-                    // where we got an handleOnBackCanceled callback and we don't want to run
-                    // any effects back to start cause they will have already been seeked to
-                    // start
-                    operation.cancel(container, false)
-                    if (!operation.isComplete) {
-                        // Re-add any animations that didn't synchronously call complete()
-                        // to continue to track them as running operations
-                        runningOperations.add(operation)
-                    }
-                }
-            } else {
-                val currentlyRunningOperations = runningOperations.toMutableList()
-                runningOperations.clear()
-                for (operation in currentlyRunningOperations) {
+                    operation.complete()
+                } else {
                     if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
                         Log.v(
                             FragmentManager.TAG,
                             "SpecialEffectsController: Cancelling operation $operation"
                         )
                     }
-                    // Cancel with seeking if the fragment is transitioning as this is the case
-                    // where another operation is about to run while we are still seeking
-                    // so we should move our current effect back to the start.
-                    operation.cancel(container, operation.fragment.mTransitioning)
-                    if (!operation.isComplete) {
-                        // Re-add any animations that didn't synchronously call complete()
-                        // to continue to track them as running operations
-                        runningOperations.add(operation)
-                    }
+                    // If we have no pendingOperations, we should always cancel without seeking,
+                    // otherwise, we should check if the fragment has mTransitioning set.
+                    operation.cancel(
+                        container,
+                        pendingOperations.isNotEmpty() && operation.fragment.mTransitioning
+                    )
                 }
+                runningNonSeekableTransition = false
+                if (!operation.isComplete) {
+                    // Re-add any animations that didn't synchronously call complete()
+                    // to continue to track them as running operations
+                    runningOperations.add(operation)
+                }
+            }
+            if (pendingOperations.isNotEmpty()) {
                 updateFinalState()
                 val newPendingOperations = pendingOperations.toMutableList()
                 if (newPendingOperations.isEmpty()) {
@@ -260,17 +261,17 @@
                     )
                 }
                 collectEffects(newPendingOperations, operationDirectionIsPop)
-                var seekable = true
-                var transitioning = true
-                newPendingOperations.forEach { operation ->
-                    seekable =
-                        operation.effects.isNotEmpty() &&
-                            operation.effects.all { effect -> effect.isSeekingSupported }
-                    if (!operation.fragment.mTransitioning) {
-                        transitioning = false
-                    }
+                val seekable = isOperationSeekable(newPendingOperations)
+                val transitioning = isOperationTransitioning(newPendingOperations)
+                runningNonSeekableTransition = transitioning && !seekable
+
+                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                    Log.v(
+                        FragmentManager.TAG,
+                        "SpecialEffectsController: Operation seekable = $seekable \n" +
+                            "transition = $transitioning"
+                    )
                 }
-                seekable = seekable && newPendingOperations.flatMap { it.effects }.isNotEmpty()
 
                 if (!transitioning) {
                     processStart(newPendingOperations)
@@ -295,6 +296,27 @@
         }
     }
 
+    private fun isOperationTransitioning(newPendingOperations: MutableList<Operation>): Boolean {
+        var transitioning = true
+        newPendingOperations.forEach { operation ->
+            if (!operation.fragment.mTransitioning) {
+                transitioning = false
+            }
+        }
+        return transitioning
+    }
+
+    private fun isOperationSeekable(newPendingOperations: MutableList<Operation>): Boolean {
+        var seekable = true
+        newPendingOperations.forEach { operation ->
+            seekable =
+                operation.effects.isNotEmpty() &&
+                    operation.effects.all { effect -> effect.isSeekingSupported }
+        }
+        seekable = seekable && newPendingOperations.flatMap { it.effects }.isNotEmpty()
+        return seekable
+    }
+
     internal fun applyContainerChangesToOperation(operation: Operation) {
         if (operation.isAwaitingContainerChanges) {
             operation.finalState.applyState(operation.fragment.requireView(), container)
diff --git a/fragment/integration-tests/testapp/build.gradle b/fragment/integration-tests/testapp/build.gradle
index a5151d3..364175d 100644
--- a/fragment/integration-tests/testapp/build.gradle
+++ b/fragment/integration-tests/testapp/build.gradle
@@ -26,16 +26,21 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
+    implementation("androidx.annotation:annotation:1.8.1")
+    implementation("androidx.core:core-ktx:1.13.0")
     implementation(project(":fragment:fragment-ktx"))
+    implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.2")
     implementation(projectOrArtifact(":transition:transition"))
     implementation("androidx.recyclerview:recyclerview:1.1.0")
     debugImplementation(project(":fragment:fragment-testing-manifest"))
 
     androidTestImplementation(project(":fragment:fragment-testing"))
+    androidTestImplementation("androidx.lifecycle:lifecycle-common:2.6.2")
+    androidTestImplementation(project(":fragment:fragment-testing-manifest"))
+    androidTestImplementation(libs.junit)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.espressoCore)
 }
 
 tasks["check"].dependsOn(tasks["connectedCheck"])
diff --git a/glance/glance-appwidget-preview/build.gradle b/glance/glance-appwidget-preview/build.gradle
index fc28bd4..25f53bf 100644
--- a/glance/glance-appwidget-preview/build.gradle
+++ b/glance/glance-appwidget-preview/build.gradle
@@ -59,6 +59,5 @@
     inceptionYear = "2022"
     description = "Glance tooling library. This library provides the API required for the " +
             "GlanceAppWidget components and its Glance @Composable to be previewable in the IDE."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/glance/glance-appwidget-preview/lint-baseline.xml b/glance/glance-appwidget-preview/lint-baseline.xml
deleted file mode 100644
index 88ba6f5..0000000
--- a/glance/glance-appwidget-preview/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-alpha15" type="baseline" client="cli" dependencies="false" name="AGP (8.2.0-alpha15)" variant="all" version="8.2.0-alpha15">
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                .all { it }"
-        errorLine2="                 ~~~">
-        <location
-            file="src/main/java/androidx/glance/appwidget/preview/ComposableInvoker.kt"/>
-    </issue>
-
-</issues>
diff --git a/glance/glance-appwidget-testing/build.gradle b/glance/glance-appwidget-testing/build.gradle
index 3dc85de..4859305 100644
--- a/glance/glance-appwidget-testing/build.gradle
+++ b/glance/glance-appwidget-testing/build.gradle
@@ -58,6 +58,8 @@
         minSdkVersion 23
     }
     namespace "androidx.glance.appwidget.testing"
+    // TODO(b/313699418): need to update compose.runtime version to 1.6.0+ in glance-appwidget
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
diff --git a/glance/glance-appwidget-testing/src/test/kotlin/androidx/glance/appwidget/testing/unit/GlanceAppWidgetUnitTestEnvironmentRobolectricTest.kt b/glance/glance-appwidget-testing/src/test/kotlin/androidx/glance/appwidget/testing/unit/GlanceAppWidgetUnitTestEnvironmentRobolectricTest.kt
index ac8219d..6891c31 100644
--- a/glance/glance-appwidget-testing/src/test/kotlin/androidx/glance/appwidget/testing/unit/GlanceAppWidgetUnitTestEnvironmentRobolectricTest.kt
+++ b/glance/glance-appwidget-testing/src/test/kotlin/androidx/glance/appwidget/testing/unit/GlanceAppWidgetUnitTestEnvironmentRobolectricTest.kt
@@ -29,6 +29,7 @@
 import androidx.glance.text.Text
 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
@@ -45,6 +46,7 @@
         context = ApplicationProvider.getApplicationContext()
     }
 
+    @Ignore // b/355680002
     @Test
     fun runTest_localContextRead() = runGlanceAppWidgetUnitTest {
         setContext(context)
diff --git a/glance/glance-appwidget/api/current.txt b/glance/glance-appwidget/api/current.txt
index a1dcbd0..f212864 100644
--- a/glance/glance-appwidget/api/current.txt
+++ b/glance/glance-appwidget/api/current.txt
@@ -53,6 +53,7 @@
   }
 
   public abstract class GlanceAppWidget {
+    ctor public GlanceAppWidget();
     ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
     method public androidx.glance.appwidget.SizeMode getSizeMode();
     method public androidx.glance.state.GlanceStateDefinition<? extends java.lang.Object?>? getStateDefinition();
diff --git a/glance/glance-appwidget/api/restricted_current.txt b/glance/glance-appwidget/api/restricted_current.txt
index a1dcbd0..f212864 100644
--- a/glance/glance-appwidget/api/restricted_current.txt
+++ b/glance/glance-appwidget/api/restricted_current.txt
@@ -53,6 +53,7 @@
   }
 
   public abstract class GlanceAppWidget {
+    ctor public GlanceAppWidget();
     ctor public GlanceAppWidget(optional @LayoutRes int errorUiLayout);
     method public androidx.glance.appwidget.SizeMode getSizeMode();
     method public androidx.glance.state.GlanceStateDefinition<? extends java.lang.Object?>? getStateDefinition();
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index edd3e36..cdb829a 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -34,7 +34,7 @@
 
 dependencies {
     api(project(":glance:glance"))
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.runtime:runtime:1.1.1")
     api("androidx.compose.ui:ui-graphics:1.1.1")
     api("androidx.compose.ui:ui-unit:1.1.1")
@@ -102,6 +102,8 @@
     buildTypes.configureEach {
         consumerProguardFiles "proguard-rules.pro"
     }
+    // TODO(b/313699418): need to update compose.runtime version to 1.6.0+
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
@@ -111,6 +113,7 @@
     description = "Glance-appwidgets allows developers to build layouts for Android AppWidgets " +
             "using a Jetpack Compose-style API."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(projectOrArtifact(":glance:glance-appwidget:glance-appwidget-samples"))
 }
 
diff --git a/glance/glance-appwidget/integration-tests/demos/build.gradle b/glance/glance-appwidget/integration-tests/demos/build.gradle
index bf53b84..448265d8 100644
--- a/glance/glance-appwidget/integration-tests/demos/build.gradle
+++ b/glance/glance-appwidget/integration-tests/demos/build.gradle
@@ -42,4 +42,6 @@
 
 android {
     namespace "androidx.glance.appwidget.demos"
+    // TODO(b/313699418): need to update compose.runtime version to 1.6.0+ in glance-appwidget
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/glance/glance-appwidget/lint-baseline.xml b/glance/glance-appwidget/lint-baseline.xml
index c90172e..d5bde38 100644
--- a/glance/glance-appwidget/lint-baseline.xml
+++ b/glance/glance-appwidget/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
+<issues format="6" by="lint 8.6.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha07)" variant="all" version="8.6.0-alpha07">
 
     <issue
         id="BanThreadSleep"
@@ -507,42 +507,6 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="variable children with type Map&lt;SizeSelector, ? extends Integer>: replace with ObjectIntMap"
-        errorLine1="    val children ="
-        errorLine2="    ^">
-        <location
-            file="src/main/java/androidx/glance/appwidget/LayoutSelection.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable var6b5af7fa with type Map&lt;SizeSelector, ? extends Integer>: replace with ObjectIntMap"
-        errorLine1="        translationContext.parentContext.children[pos]"
-        errorLine2="        ^">
-        <location
-            file="src/main/java/androidx/glance/appwidget/LayoutSelection.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable childrenMapping with type Map&lt;Integer, ? extends Map&lt;SizeSelector, ? extends Integer>>: replace with IntObjectMap"
-        errorLine1="    val childrenMapping ="
-        errorLine2="    ^">
-        <location
-            file="src/main/java/androidx/glance/appwidget/LayoutSelection.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable var405cd718 with type Map&lt;Integer, ? extends Map&lt;SizeSelector, ? extends Integer>>: replace with IntObjectMap"
-        errorLine1="        generatedChildren[type]"
-        errorLine2="        ^">
-        <location
-            file="src/main/java/androidx/glance/appwidget/LayoutSelection.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
         message="field ids with type ArrayList&lt;Long>: replace with LongList"
         errorLine1="        private val ids = arrayListOf&lt;Long>()"
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/AppWidgetUtils.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/AppWidgetUtils.kt
index 3b35e96..9b04e96 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/AppWidgetUtils.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/AppWidgetUtils.kt
@@ -27,7 +27,6 @@
 import android.util.Log
 import android.util.SizeF
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.compose.runtime.Composable
@@ -194,13 +193,11 @@
 
 @RequiresApi(Build.VERSION_CODES.Q)
 internal object TracingApi29Impl {
-    @DoNotInline
     fun beginAsyncSection(
         methodName: String,
         cookie: Int,
     ) = Trace.beginAsyncSection(methodName, cookie)
 
-    @DoNotInline
     fun endAsyncSection(
         methodName: String,
         cookie: Int,
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/ApplyModifiers.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/ApplyModifiers.kt
index c074490..69e14b9 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/ApplyModifiers.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/ApplyModifiers.kt
@@ -25,7 +25,6 @@
 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
 import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.toArgb
 import androidx.core.widget.RemoteViewsCompat.setTextViewHeight
@@ -321,7 +320,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object ApplyModifiersApi31Impl {
-    @DoNotInline
     fun setViewWidth(rv: RemoteViews, viewId: Int, width: Dimension) {
         when (width) {
             is Dimension.Wrap -> {
@@ -336,7 +334,6 @@
         }.let {}
     }
 
-    @DoNotInline
     fun setViewHeight(rv: RemoteViews, viewId: Int, height: Dimension) {
         when (height) {
             is Dimension.Wrap -> {
@@ -351,7 +348,6 @@
         }.let {}
     }
 
-    @DoNotInline
     fun applyRoundedCorners(rv: RemoteViews, viewId: Int, radius: Dimension) {
         rv.setViewClipToOutline(viewId, true)
         when (radius) {
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidgetManager.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidgetManager.kt
index 3bbd8a6f2..a5ecba7 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidgetManager.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidgetManager.kt
@@ -24,7 +24,6 @@
 import android.content.Intent
 import android.os.Build
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.compose.ui.unit.DpSize
@@ -371,11 +370,9 @@
     @RequiresApi(Build.VERSION_CODES.O)
     private object AppWidgetManagerApi26Impl {
 
-        @DoNotInline
         fun isRequestPinAppWidgetSupported(manager: AppWidgetManager) =
             manager.isRequestPinAppWidgetSupported
 
-        @DoNotInline
         fun requestPinAppWidget(
             manager: AppWidgetManager,
             target: ComponentName,
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceRemoteViewsService.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceRemoteViewsService.kt
index c12f362..573b01a 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceRemoteViewsService.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceRemoteViewsService.kt
@@ -24,7 +24,6 @@
 import android.util.Log
 import android.widget.RemoteViews
 import android.widget.RemoteViewsService
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.glance.session.GlanceSessionManager
@@ -227,7 +226,6 @@
  * GlanceRemoteViewsService using an intent.
  */
 @Suppress("DEPRECATION")
-@DoNotInline
 internal fun RemoteViews.setRemoteAdapter(
     context: Context,
     appWidgetId: Int,
@@ -258,12 +256,10 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object CollectionItemsApi31Impl {
-    @DoNotInline
     fun setRemoteAdapter(remoteViews: RemoteViews, viewId: Int, items: RemoteCollectionItems) {
         remoteViews.setRemoteAdapter(viewId, toPlatformCollectionItems(items))
     }
 
-    @DoNotInline
     fun toPlatformCollectionItems(items: RemoteCollectionItems): RemoteViews.RemoteCollectionItems {
         return RemoteViews.RemoteCollectionItems.Builder()
             .setHasStableIds(items.hasStableIds())
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/LayoutSelection.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/LayoutSelection.kt
index 06118af..4a8be41 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/LayoutSelection.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/LayoutSelection.kt
@@ -21,7 +21,6 @@
 import android.view.View
 import android.view.ViewGroup
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.IdRes
 import androidx.annotation.LayoutRes
 import androidx.annotation.RequiresApi
@@ -361,6 +360,7 @@
     height: LayoutSize
 ): Int {
     val child = makeViewStubSelector(width, height)
+    @Suppress("PrimitiveInCollection")
     val children =
         translationContext.parentContext.children[pos]
             ?: throw IllegalStateException("Parent doesn't have child position $pos")
@@ -404,6 +404,7 @@
             ?: throw IllegalArgumentException(
                 "Cannot find container $type with $numChildren children"
             )
+    @Suppress("PrimitiveInCollection")
     val childrenMapping =
         generatedChildren[type]
             ?: throw IllegalArgumentException("Cannot find generated children for $type")
@@ -433,7 +434,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object LayoutSelectionApi31Impl {
-    @DoNotInline
     fun remoteViews(packageName: String, @LayoutRes layoutId: Int, viewId: Int) =
         RemoteViews(packageName, layoutId, viewId)
 }
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RemoteViewsTranslator.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RemoteViewsTranslator.kt
index 9de41bc..ed0f454 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RemoteViewsTranslator.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RemoteViewsTranslator.kt
@@ -24,7 +24,6 @@
 import android.view.Gravity
 import android.view.View
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.LayoutRes
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
@@ -93,7 +92,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object Api31Impl {
-    @DoNotInline
     fun createRemoteViews(sizeMap: Map<SizeF, RemoteViews>): RemoteViews = RemoteViews(sizeMap)
 }
 
@@ -469,12 +467,11 @@
 
 @RequiresApi(Build.VERSION_CODES.P)
 private object RemoteViewsTranslatorApi28Impl {
-    @DoNotInline fun copyRemoteViews(rv: RemoteViews) = RemoteViews(rv)
+    fun copyRemoteViews(rv: RemoteViews) = RemoteViews(rv)
 }
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object RemoteViewsTranslatorApi31Impl {
-    @DoNotInline
     fun addChildView(rv: RemoteViews, viewId: Int, childView: RemoteViews, stableId: Int) {
         rv.addStableView(viewId, childView, stableId)
     }
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/WidgetLayout.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/WidgetLayout.kt
index 493830a..d1595e4 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/WidgetLayout.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/WidgetLayout.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.os.Build
 import android.util.Log
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.datastore.core.CorruptionException
@@ -385,7 +384,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object WidgetLayoutImpl31 {
-    @DoNotInline
     fun toProto(dimension: Dimension) =
         if (dimension is Dimension.Expand) {
             LayoutProto.DimensionType.EXPAND
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ActionTrampoline.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ActionTrampoline.kt
index 5bc6daa..349855a 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ActionTrampoline.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ActionTrampoline.kt
@@ -24,7 +24,6 @@
 import android.os.Bundle
 import android.os.StrictMode
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.glance.appwidget.TranslationContext
 
@@ -156,7 +155,6 @@
 
 @RequiresApi(Build.VERSION_CODES.O)
 private object ListAdapterTrampolineApi26Impl {
-    @DoNotInline
     fun startForegroundService(context: Context, intent: Intent) {
         context.startForegroundService(intent)
     }
@@ -164,7 +162,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object StrictModeVmPolicyApi31Impl {
-    @DoNotInline
     fun permitUnsafeIntentLaunch(builder: StrictMode.VmPolicy.Builder) =
         builder.permitUnsafeIntentLaunch()
 }
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ApplyAction.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ApplyAction.kt
index fb82ee2..a248d03 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ApplyAction.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/action/ApplyAction.kt
@@ -22,7 +22,6 @@
 import android.os.Build
 import android.util.Log
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.IdRes
 import androidx.annotation.RequiresApi
 import androidx.core.os.bundleOf
@@ -353,22 +352,18 @@
 @RequiresApi(Build.VERSION_CODES.S)
 private object ApplyActionApi31Impl {
 
-    @DoNotInline
     fun setOnCheckedChangeResponse(rv: RemoteViews, viewId: Int, intent: PendingIntent) {
         rv.setOnCheckedChangeResponse(viewId, RemoteViews.RemoteResponse.fromPendingIntent(intent))
     }
 
-    @DoNotInline
     fun setOnCheckedChangeResponse(rv: RemoteViews, viewId: Int, intent: Intent) {
         rv.setOnCheckedChangeResponse(viewId, RemoteViews.RemoteResponse.fromFillInIntent(intent))
     }
 
-    @DoNotInline
     fun unsetOnCheckedChangeResponse(rv: RemoteViews, viewId: Int) {
         rv.setOnCheckedChangeResponse(viewId, RemoteViews.RemoteResponse())
     }
 
-    @DoNotInline
     fun unsetOnClickResponse(rv: RemoteViews, viewId: Int) {
         rv.setOnClickResponse(viewId, RemoteViews.RemoteResponse())
     }
@@ -376,14 +371,12 @@
 
 @RequiresApi(Build.VERSION_CODES.Q)
 private object ApplyActionApi29Impl {
-    @DoNotInline
     fun setIntentIdentifier(intent: Intent, viewId: Int): Intent =
         intent.apply { identifier = viewId.toString() }
 }
 
 @RequiresApi(Build.VERSION_CODES.O)
 private object ApplyActionApi26Impl {
-    @DoNotInline
     fun getForegroundServicePendingIntent(context: Context, intent: Intent): PendingIntent {
         return PendingIntent.getForegroundService(
             context,
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/components/Scaffold.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/components/Scaffold.kt
index 156db80..4f60438 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/components/Scaffold.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/components/Scaffold.kt
@@ -21,7 +21,6 @@
 import androidx.compose.ui.unit.dp
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceTheme
-import androidx.glance.LocalContext
 import androidx.glance.appwidget.appWidgetBackground
 import androidx.glance.appwidget.cornerRadius
 import androidx.glance.background
@@ -51,17 +50,15 @@
     horizontalPadding: Dp = 12.dp,
     content: @Composable () -> Unit,
 ) {
-    var theModifier = modifier.fillMaxSize().background(backgroundColor).appWidgetBackground()
 
-    val systemCornerRadiusDefined =
-        LocalContext.current.resources.getResourceName(
-            android.R.dimen.system_app_widget_background_radius
-        ) != null
-    if (android.os.Build.VERSION.SDK_INT >= 31 && systemCornerRadiusDefined) {
-        theModifier = theModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius)
-    }
-
-    Column(modifier = theModifier) {
+    Column(
+        modifier =
+            modifier
+                .fillMaxSize()
+                .background(backgroundColor)
+                .appWidgetBackground()
+                .widgetCornerRadius()
+    ) {
         titleBar?.invoke()
         Box(
             modifier = GlanceModifier.padding(horizontal = horizontalPadding).defaultWeight(),
@@ -69,3 +66,14 @@
         )
     }
 }
+
+private fun GlanceModifier.widgetCornerRadius(): GlanceModifier {
+    val cornerRadiusModifier =
+        if (android.os.Build.VERSION.SDK_INT >= 31) {
+            GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius)
+        } else {
+            GlanceModifier
+        }
+
+    return this.then(cornerRadiusModifier)
+}
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/CompoundButtonApi31Impl.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/CompoundButtonApi31Impl.kt
index a3573cf..72c01d3 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/CompoundButtonApi31Impl.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/CompoundButtonApi31Impl.kt
@@ -18,12 +18,10 @@
 
 import android.os.Build
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 @RequiresApi(Build.VERSION_CODES.S)
 internal object CompoundButtonApi31Impl {
-    @DoNotInline
     fun setCompoundButtonChecked(rv: RemoteViews, viewId: Int, checked: Boolean) {
         rv.setCompoundButtonChecked(viewId, checked)
     }
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/ImageTranslator.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/ImageTranslator.kt
index 7c8f05c..d015583 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/ImageTranslator.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/ImageTranslator.kt
@@ -20,7 +20,6 @@
 import android.os.Build
 import android.util.Log
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
@@ -160,7 +159,6 @@
 
 @RequiresApi(Build.VERSION_CODES.M)
 private object ImageTranslatorApi23Impl {
-    @DoNotInline
     fun setImageViewIcon(rv: RemoteViews, viewId: Int, icon: Icon) {
         rv.setImageViewIcon(viewId, icon)
     }
@@ -168,7 +166,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object ImageTranslatorApi31Impl {
-    @DoNotInline
     fun applyTintColorFilter(
         translationContext: TranslationContext,
         rv: RemoteViews,
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/TextTranslator.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/TextTranslator.kt
index fecd12d..bf1fbde 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/TextTranslator.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/translators/TextTranslator.kt
@@ -32,7 +32,6 @@
 import android.util.TypedValue
 import android.view.Gravity
 import android.widget.RemoteViews
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.toArgb
 import androidx.core.widget.RemoteViewsCompat.setTextViewGravity
@@ -184,7 +183,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object TextTranslatorApi31Impl {
-    @DoNotInline
     fun setTextViewGravity(rv: RemoteViews, viewId: Int, gravity: Int) {
         rv.setTextViewGravity(viewId, gravity)
     }
diff --git a/glance/glance-material/build.gradle b/glance/glance-material/build.gradle
index 1bea181..0d8fd1e 100644
--- a/glance/glance-material/build.gradle
+++ b/glance/glance-material/build.gradle
@@ -16,7 +16,7 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.4.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.runtime:runtime:1.1.1")
     api(project(":glance:glance"))
     implementation 'androidx.compose.material:material:1.3.0'
@@ -32,7 +32,6 @@
     inceptionYear = "2022"
     description = "Glance Material 2 integration library." +
             " This library provides interop APIs with Material 2."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/glance/glance-material3/build.gradle b/glance/glance-material3/build.gradle
index 88bc255..821e95e 100644
--- a/glance/glance-material3/build.gradle
+++ b/glance/glance-material3/build.gradle
@@ -16,7 +16,7 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.4.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.runtime:runtime:1.1.1")
     api(project(":glance:glance"))
     implementation("androidx.compose.material3:material3:1.0.0")
@@ -33,7 +33,6 @@
     inceptionYear = "2022"
     description = "Glance Material integration library." +
             " This library provides interop APIs with Material 3."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/glance/glance-preview/build.gradle b/glance/glance-preview/build.gradle
index 117210d..9126227 100644
--- a/glance/glance-preview/build.gradle
+++ b/glance/glance-preview/build.gradle
@@ -16,7 +16,7 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.4.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.runtime:runtime:1.1.1")
 }
 
@@ -30,6 +30,5 @@
     inceptionYear = "2022"
     description = "Glance preview library. This library provides the API required for marking the" +
             "glance @Composable components that should have preview in the Android Studio."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/glance/glance-template/api/current.txt b/glance/glance-template/api/current.txt
index 529b7b8..14e6361 100644
--- a/glance/glance-template/api/current.txt
+++ b/glance/glance-template/api/current.txt
@@ -2,6 +2,7 @@
 package androidx.glance.template {
 
   public final class ActionBlock {
+    ctor public ActionBlock();
     ctor public ActionBlock(optional java.util.List<? extends androidx.glance.template.TemplateButton> actionButtons, optional int type);
     method public java.util.List<androidx.glance.template.TemplateButton> getActionButtons();
     method public int getType();
@@ -103,6 +104,7 @@
   }
 
   public final class ImageBlock {
+    ctor public ImageBlock();
     ctor public ImageBlock(optional java.util.List<androidx.glance.template.TemplateImageWithDescription> images, optional int aspectRatio, optional int size, optional @IntRange(from=0L) int priority);
     method public int getAspectRatio();
     method public java.util.List<androidx.glance.template.TemplateImageWithDescription> getImages();
@@ -141,6 +143,7 @@
   }
 
   public final class ListTemplateData {
+    ctor public ListTemplateData();
     ctor public ListTemplateData(optional androidx.glance.template.HeaderBlock? headerBlock, optional java.util.List<androidx.glance.template.ListTemplateItem> listContent, optional int listStyle);
     method public androidx.glance.template.HeaderBlock? getHeaderBlock();
     method public java.util.List<androidx.glance.template.ListTemplateItem> getListContent();
@@ -165,6 +168,7 @@
   }
 
   public final class SingleEntityTemplateData {
+    ctor public SingleEntityTemplateData();
     ctor public SingleEntityTemplateData(optional androidx.glance.template.HeaderBlock? headerBlock, optional androidx.glance.template.TextBlock? textBlock, optional androidx.glance.template.ImageBlock? imageBlock, optional androidx.glance.template.ActionBlock? actionBlock);
     method public androidx.glance.template.ActionBlock? getActionBlock();
     method public androidx.glance.template.HeaderBlock? getHeaderBlock();
diff --git a/glance/glance-template/api/restricted_current.txt b/glance/glance-template/api/restricted_current.txt
index 529b7b8..14e6361 100644
--- a/glance/glance-template/api/restricted_current.txt
+++ b/glance/glance-template/api/restricted_current.txt
@@ -2,6 +2,7 @@
 package androidx.glance.template {
 
   public final class ActionBlock {
+    ctor public ActionBlock();
     ctor public ActionBlock(optional java.util.List<? extends androidx.glance.template.TemplateButton> actionButtons, optional int type);
     method public java.util.List<androidx.glance.template.TemplateButton> getActionButtons();
     method public int getType();
@@ -103,6 +104,7 @@
   }
 
   public final class ImageBlock {
+    ctor public ImageBlock();
     ctor public ImageBlock(optional java.util.List<androidx.glance.template.TemplateImageWithDescription> images, optional int aspectRatio, optional int size, optional @IntRange(from=0L) int priority);
     method public int getAspectRatio();
     method public java.util.List<androidx.glance.template.TemplateImageWithDescription> getImages();
@@ -141,6 +143,7 @@
   }
 
   public final class ListTemplateData {
+    ctor public ListTemplateData();
     ctor public ListTemplateData(optional androidx.glance.template.HeaderBlock? headerBlock, optional java.util.List<androidx.glance.template.ListTemplateItem> listContent, optional int listStyle);
     method public androidx.glance.template.HeaderBlock? getHeaderBlock();
     method public java.util.List<androidx.glance.template.ListTemplateItem> getListContent();
@@ -165,6 +168,7 @@
   }
 
   public final class SingleEntityTemplateData {
+    ctor public SingleEntityTemplateData();
     ctor public SingleEntityTemplateData(optional androidx.glance.template.HeaderBlock? headerBlock, optional androidx.glance.template.TextBlock? textBlock, optional androidx.glance.template.ImageBlock? imageBlock, optional androidx.glance.template.ActionBlock? actionBlock);
     method public androidx.glance.template.ActionBlock? getActionBlock();
     method public androidx.glance.template.HeaderBlock? getHeaderBlock();
diff --git a/glance/glance-template/build.gradle b/glance/glance-template/build.gradle
index 0db72c3..89b21c6 100644
--- a/glance/glance-template/build.gradle
+++ b/glance/glance-template/build.gradle
@@ -35,7 +35,7 @@
 dependencies {
     api(project(":glance:glance"))
     api(project(":glance:glance-appwidget"))
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.runtime:runtime:1.1.1")
     api("androidx.compose.ui:ui-graphics:1.1.1")
     api("androidx.compose.ui:ui-unit:1.1.1")
@@ -43,7 +43,6 @@
     api("androidx.datastore:datastore-preferences-core:1.0.0")
     api("androidx.datastore:datastore-preferences:1.0.0")
 
-    implementation("androidx.annotation:annotation:1.1.0")
     implementation("androidx.work:work-runtime:2.7.1")
     implementation("androidx.work:work-runtime-ktx:2.7.1")
     implementation(libs.kotlinStdlib)
@@ -85,4 +84,5 @@
     description = "Glance allows developers to build layouts for remote surfaces using a Jetpack " +
             "Compose-style API."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/glance/glance-template/lint-baseline.xml b/glance/glance-template/lint-baseline.xml
deleted file mode 100644
index 8a73870..0000000
--- a/glance/glance-template/lint-baseline.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-alpha15" type="baseline" client="cli" dependencies="false" name="AGP (8.2.0-alpha15)" variant="all" version="8.2.0-alpha15">
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        textList.forEachIndexed { index, item ->"
-        errorLine2="                 ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/template/GlanceAppWidgetTemplates.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            actionBlock.actionButtons.forEach { button ->"
-        errorLine2="                                      ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/template/GlanceAppWidgetTemplates.kt"/>
-    </issue>
-
-</issues>
diff --git a/glance/glance-testing/build.gradle b/glance/glance-testing/build.gradle
index c9eac8f..9fec2dd 100644
--- a/glance/glance-testing/build.gradle
+++ b/glance/glance-testing/build.gradle
@@ -62,6 +62,5 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2023"
     description = "This library provides base APIs to enable testing Glance"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/glance/glance-testing/lint-baseline.xml b/glance/glance-testing/lint-baseline.xml
deleted file mode 100644
index 42fb306..0000000
--- a/glance/glance-testing/lint-baseline.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="    nodes.forEachIndexed { index, glanceNode ->"
-        errorLine2="          ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/testing/AssertionErrorMessages.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        children.forEach { child ->"
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/testing/unit/GlanceMappedNode.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        return mappedNodes.toList()"
-        errorLine2="                           ~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/testing/unit/GlanceMappedNode.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        val violations = filteredNodes.filter { !matcher.matches(it) }"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/testing/GlanceNodeAssertionCollection.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            this.allNodes = allNodes.toList()"
-        errorLine2="                                     ~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/testing/TestContext.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        return node.children().any { checkIfSubtreeMatchesRecursive(matcher, it) }"
-        errorLine2="                               ~~~">
-        <location
-            file="src/main/java/androidx/glance/testing/unit/UnitTestFilters.kt"/>
-    </issue>
-
-</issues>
diff --git a/glance/glance-wear-tiles/api/current.txt b/glance/glance-wear-tiles/api/current.txt
index 1b62900..e063d86 100644
--- a/glance/glance-wear-tiles/api/current.txt
+++ b/glance/glance-wear-tiles/api/current.txt
@@ -19,6 +19,7 @@
   }
 
   public abstract class GlanceTileService extends androidx.wear.tiles.TileService {
+    ctor public GlanceTileService();
     ctor public GlanceTileService(optional androidx.wear.tiles.LayoutElementBuilders.LayoutElement? errorUiLayout);
     method @androidx.compose.runtime.Composable @androidx.glance.GlanceComposable public abstract void Content();
     method public androidx.glance.state.GlanceStateDefinition<? extends java.lang.Object?>? getStateDefinition();
@@ -37,6 +38,7 @@
   }
 
   public final class TimeInterval {
+    ctor public TimeInterval();
     ctor public TimeInterval(optional java.time.Instant start, optional java.time.Instant end);
     method public java.time.Instant component1();
     method public java.time.Instant component2();
@@ -124,6 +126,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class CurvedTextStyle {
+    ctor public CurvedTextStyle();
     ctor public CurvedTextStyle(optional androidx.glance.unit.ColorProvider? color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle);
     method public androidx.glance.unit.ColorProvider? getColor();
     method public androidx.compose.ui.unit.TextUnit? getFontSize();
diff --git a/glance/glance-wear-tiles/api/restricted_current.txt b/glance/glance-wear-tiles/api/restricted_current.txt
index 1b62900..e063d86 100644
--- a/glance/glance-wear-tiles/api/restricted_current.txt
+++ b/glance/glance-wear-tiles/api/restricted_current.txt
@@ -19,6 +19,7 @@
   }
 
   public abstract class GlanceTileService extends androidx.wear.tiles.TileService {
+    ctor public GlanceTileService();
     ctor public GlanceTileService(optional androidx.wear.tiles.LayoutElementBuilders.LayoutElement? errorUiLayout);
     method @androidx.compose.runtime.Composable @androidx.glance.GlanceComposable public abstract void Content();
     method public androidx.glance.state.GlanceStateDefinition<? extends java.lang.Object?>? getStateDefinition();
@@ -37,6 +38,7 @@
   }
 
   public final class TimeInterval {
+    ctor public TimeInterval();
     ctor public TimeInterval(optional java.time.Instant start, optional java.time.Instant end);
     method public java.time.Instant component1();
     method public java.time.Instant component2();
@@ -124,6 +126,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class CurvedTextStyle {
+    ctor public CurvedTextStyle();
     ctor public CurvedTextStyle(optional androidx.glance.unit.ColorProvider? color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle);
     method public androidx.glance.unit.ColorProvider? getColor();
     method public androidx.compose.ui.unit.TextUnit? getFontSize();
diff --git a/glance/glance-wear-tiles/build.gradle b/glance/glance-wear-tiles/build.gradle
index ed1d830..6a4bc14 100644
--- a/glance/glance-wear-tiles/build.gradle
+++ b/glance/glance-wear-tiles/build.gradle
@@ -99,4 +99,5 @@
     description = "Glance allows developers to build layouts for Wear Tiles using a Jetpack " +
             "Compose-style API."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/glance/glance-wear-tiles/lint-baseline.xml b/glance/glance-wear-tiles/lint-baseline.xml
index 72becec..6106ce775 100644
--- a/glance/glance-wear-tiles/lint-baseline.xml
+++ b/glance/glance-wear-tiles/lint-baseline.xml
@@ -1,131 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="    return { curvedChildList.forEach { composable -> object : CurvedChildScope {}.composable() } }"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/curved/CurvedRow.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            it.children.addAll(children.map { it.copy() })"
-        errorLine2="                                        ~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/curved/CurvedRow.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            it.children.addAll(children.map { it.copy() })"
-        errorLine2="                                        ~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/curved/CurvedRow.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="    children.mapIndexed { index, child ->"
-        errorLine2="             ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/NormalizeCompositionTree.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="    toDelete.forEach { children.removeAt(it) }"
-        errorLine2="             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/NormalizeCompositionTree.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            children.forEach { child ->"
-        errorLine2="                     ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/NormalizeCompositionTree.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="        textList.forEach { item ->"
-        errorLine2="                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/template/SingleEntityTemplateLayouts.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            .setContentDescription(it.joinToString())"
-        errorLine2="                                      ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/WearCompositionTranslator.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            .setContentDescription(it.joinToString())"
-        errorLine2="                                      ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/WearCompositionTranslator.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            element.children.forEach {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/WearCompositionTranslator.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                element.children.forEach {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/WearCompositionTranslator.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="                element.children.forEach {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/WearCompositionTranslator.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="    element.children.forEach { curvedChild ->"
-        errorLine2="                     ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/WearCompositionTranslator.kt"/>
-    </issue>
-
-    <issue
-        id="ListIterator"
-        message="Creating an unnecessary Iterator to iterate through a List"
-        errorLine1="            curvedChild.children.forEach {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/glance/wear/tiles/WearCompositionTranslator.kt"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
diff --git a/glance/glance/api/current.txt b/glance/glance/api/current.txt
index 66f1f53..638518e 100644
--- a/glance/glance/api/current.txt
+++ b/glance/glance/api/current.txt
@@ -545,6 +545,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
+    ctor public TextStyle();
     ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
     method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
     method public androidx.glance.unit.ColorProvider getColor();
diff --git a/glance/glance/api/restricted_current.txt b/glance/glance/api/restricted_current.txt
index 66f1f53..638518e 100644
--- a/glance/glance/api/restricted_current.txt
+++ b/glance/glance/api/restricted_current.txt
@@ -545,6 +545,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
+    ctor public TextStyle();
     ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
     method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
     method public androidx.glance.unit.ColorProvider getColor();
diff --git a/glance/glance/build.gradle b/glance/glance/build.gradle
index 04462a0..543e207 100644
--- a/glance/glance/build.gradle
+++ b/glance/glance/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.runtime:runtime:1.2.1")
     api("androidx.compose.ui:ui-graphics:1.1.1")
     api("androidx.compose.ui:ui-unit:1.1.1")
@@ -39,7 +39,6 @@
     api("androidx.datastore:datastore-preferences-core:1.0.0")
     api("androidx.datastore:datastore-preferences:1.0.0")
 
-    implementation("androidx.annotation:annotation:1.1.0")
     implementation("androidx.work:work-runtime:2.7.1")
     implementation("androidx.work:work-runtime-ktx:2.7.1")
     implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
@@ -88,6 +87,8 @@
     testOptions.unitTests.includeAndroidResources = true
     resourcePrefix "glance_"
     namespace "androidx.glance"
+    // TODO(b/313699418): need to update compose.runtime version to 1.6.0+
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
@@ -97,4 +98,5 @@
     description = "Glance allows developers to build layouts for remote surfaces using a Jetpack " +
             "Compose-style API."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/glance/glance/src/main/java/androidx/glance/session/IdleEventBroadcastReceiver.kt b/glance/glance/src/main/java/androidx/glance/session/IdleEventBroadcastReceiver.kt
index 7545543..c6f4aa3 100644
--- a/glance/glance/src/main/java/androidx/glance/session/IdleEventBroadcastReceiver.kt
+++ b/glance/glance/src/main/java/androidx/glance/session/IdleEventBroadcastReceiver.kt
@@ -22,7 +22,6 @@
 import android.content.IntentFilter
 import android.os.Build
 import android.os.PowerManager
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
@@ -57,7 +56,6 @@
 
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 private object Api33Impl {
-    @DoNotInline
     fun isLightIdleOrLowPowerStandby(pm: PowerManager): Boolean {
         return pm.isLowPowerStandbyEnabled || pm.isDeviceLightIdleMode
     }
@@ -65,7 +63,6 @@
 
 @RequiresApi(Build.VERSION_CODES.M)
 private object Api23Impl {
-    @DoNotInline
     fun isIdle(pm: PowerManager): Boolean {
         return pm.isDeviceIdleMode
     }
diff --git a/glance/glance/src/main/java/androidx/glance/unit/ColorProvider.kt b/glance/glance/src/main/java/androidx/glance/unit/ColorProvider.kt
index b0fb38d..2019a9f 100644
--- a/glance/glance/src/main/java/androidx/glance/unit/ColorProvider.kt
+++ b/glance/glance/src/main/java/androidx/glance/unit/ColorProvider.kt
@@ -20,7 +20,6 @@
 import android.os.Build
 import androidx.annotation.ColorInt
 import androidx.annotation.ColorRes
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.compose.ui.graphics.Color
@@ -68,7 +67,6 @@
 @RequiresApi(23)
 private object ColorProviderApi23Impl {
     @ColorInt
-    @DoNotInline
     fun getColor(context: Context, @ColorRes resId: Int): Int {
         return context.getColor(resId)
     }
diff --git a/gradle.properties b/gradle.properties
index e013251..2376c76 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,6 +20,8 @@
 android.javaCompile.suppressSourceTargetDeprecationWarning=true
 android.lint.baselineOmitLineNumbers=true
 android.lint.printStackTrace=true
+# b/271371556
+android.lint.useK2Uast=true
 android.builder.sdkDownload=false
 android.uniquePackageNames=true
 android.enableAdditionalTestOutput=true
@@ -30,14 +32,14 @@
 # Remove when AGP defaults to 2.1.0
 android.prefabVersion=2.1.0
 
-# Do generate versioned API files
+# Don't generate versioned API files
 androidx.writeVersionedApiFiles=true
 
-# Do run the CheckAarMetadata task
+# Don't run the CheckAarMetadata task
 android.experimental.disableCompileSdkChecks=false
 
 # Don't warn about needing to update AGP
-android.suppressUnsupportedCompileSdk=UpsideDownCake,VanillaIceCream,33,34
+android.suppressUnsupportedCompileSdk=35
 
 androidx.compileSdk=34
 androidx.targetSdkVersion=34
@@ -57,7 +59,7 @@
 android.experimental.dependency.excludeLibraryComponentsFromConstraints=true
 # Disallow resolving dependencies at configuration time, which is a slight performance problem
 android.dependencyResolutionAtConfigurationTime.disallow=true
-android.suppressUnsupportedOptionWarnings=android.suppressUnsupportedOptionWarnings,android.dependencyResolutionAtConfigurationTime.disallow,android.experimental.lint.missingBaselineIsEmptyBaseline,android.lint.printStackTrace,android.lint.baselineOmitLineNumbers,android.experimental.disableCompileSdkChecks,android.overrideVersionCheck,android.r8.maxWorkers,android.experimental.lint.reservedMemoryPerTask,android.experimental.dependency.excludeLibraryComponentsFromConstraints,android.prefabVersion,android.experimental.privacysandboxsdk.plugin.enable,android.experimental.privacysandboxsdk.requireServices
+android.suppressUnsupportedOptionWarnings=android.suppressUnsupportedOptionWarnings,android.dependencyResolutionAtConfigurationTime.disallow,android.experimental.lint.missingBaselineIsEmptyBaseline,android.lint.printStackTrace,android.lint.baselineOmitLineNumbers,android.experimental.disableCompileSdkChecks,android.overrideVersionCheck,android.r8.maxWorkers,android.experimental.lint.reservedMemoryPerTask,android.experimental.dependency.excludeLibraryComponentsFromConstraints,android.prefabVersion,android.experimental.privacysandboxsdk.plugin.enable,android.experimental.privacysandboxsdk.requireServices,android.lint.useK2Uast
 # Workaround for b/162074215
 android.includeDependencyInfoInApks=false
 # Allow multiple r8 tasks at once because otherwise they can make the critical path longer: b/256187923
@@ -97,3 +99,12 @@
 
 # Disable dependency analysis compatibility check
 dependency.analysis.compatibility=NONE
+
+# Preserve the test APKs after a test run
+android.injected.androidTest.leaveApksInstalledAfterRun=true
+
+# Fetch yarn packages from an offline mirror by default
+androidx.yarnOfflineMode=true
+
+# Remove when https://youtrack.jetbrains.com/issue/KT-70013 is fixed
+org.gradle.configuration-cache.inputs.unsafe.ignore.file-system-checks=**/out/.gradle/nodejs;**/out/.gradle/yarn
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index bec1d5d..963c6b9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,13 +2,13 @@
 # -----------------------------------------------------------------------------
 # All of the following should be updated in sync.
 # -----------------------------------------------------------------------------
-androidGradlePlugin = "8.6.0-alpha07"
+androidGradlePlugin = "8.6.0-beta01"
 # NOTE: When updating the lint version we also need to update the `api` version
 # supported by `IssueRegistry`'s.' For e.g. r.android.com/1331903
-androidLint = "31.6.0-alpha07"
+androidLint = "31.6.0-beta01"
 # Once you have a chosen version of AGP to upgrade to, go to
 # https://developer.android.com/studio/archive and find the matching version of Studio.
-androidStudio = "2024.1.2.7"
+androidStudio = "2024.1.2.9"
 # -----------------------------------------------------------------------------
 
 androidLintMin = "31.1.0"
@@ -19,7 +19,7 @@
 androidxTestExtJunit = "1.2.1"
 androidxTestExtTruth = "1.6.0"
 annotationVersion = "1.7.0"
-atomicFu = "0.17.0"
+atomicFu = "0.23.2"
 autoService = "1.0-rc6"
 autoValue = "1.6.3"
 binaryCompatibilityValidator = "0.15.0-Beta.2"
@@ -45,12 +45,12 @@
 kotlin18 = "1.8.22"
 kotlin19 = "1.9.24"
 kotlin = "1.9.24"
-kotlinBenchmark = "0.4.8"
+kotlinBenchmark = "0.4.11"
 kotlinGradlePluginAnnotations = "1.9.24"
 kotlinGradlePluginApi = "1.9.24"
 kotlinNative = "1.9.24"
 kotlinCompileTesting = "1.4.9"
-kotlinCoroutines = "1.7.3"
+kotlinCoroutines = "1.8.1"
 kotlinNativeUtils = "1.9.24"
 kotlinSerialization = "1.6.3"
 kotlinToolingCore = "1.9.24"
@@ -58,9 +58,10 @@
 ktfmt = "0.50"
 leakcanary = "2.13"
 media3 = "1.1.0"
-metalava = "1.0.0-alpha10"
+metalava = "1.0.0-alpha11"
 mockito = "2.25.0"
 moshi = "1.13.0"
+node = "16.20.2"
 protobuf = "3.22.3"
 shadow = "8.1.1"
 skiko = "0.7.7"
@@ -70,6 +71,7 @@
 wire = "4.9.7"
 core = "1.12.0"
 xmlApis = "1.4.01"
+yarn = "1.22.17"
 
 [libraries]
 agpTestingPlatformCoreProto =  { module = "com.google.testing.platform:core-proto", version = "0.0.8-alpha08" }
@@ -253,13 +255,14 @@
 moshiCodeGen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
 nullaway = { module = "com.uber.nullaway:nullaway", version = "0.10.18" }
 okhttpMockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version = "3.14.7" }
-okio = { module = "com.squareup.okio:okio", version = "3.4.0" }
+okio = { module = "com.squareup.okio:okio", version = "3.6.0" }
 opentest4j = { module = "org.opentest4j:opentest4j", version = "1.2.0" }
 playFeatureDelivery = { module = "com.google.android.play:feature-delivery", version = "2.1.0" }
 playCore = { module = "com.google.android.play:core", version = "1.10.3" }
 playServicesAuth = {module = "com.google.android.gms:play-services-auth", version = "21.1.1"}
 playServicesBase = { module = "com.google.android.gms:play-services-base", version = "17.0.0" }
 playServicesBasement = { module = "com.google.android.gms:play-services-basement", version = "17.0.0" }
+playServicesBlockstore = {module = "com.google.android.gms:play-services-auth-blockstore", version = "16.4.0"}
 playServicesDevicePerformance = { module = "com.google.android.gms:play-services-deviceperformance", version = "16.0.0" }
 playServicesFido = {module = "com.google.android.gms:play-services-fido", version = "21.0.0"}
 playServicesWearable = { module = "com.google.android.gms:play-services-wearable", version = "17.1.0" }
diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys
index 5840cbe..efdf962 100644
--- a/gradle/verification-keyring.keys
+++ b/gradle/verification-keyring.keys
@@ -5304,6 +5304,562 @@
 =8Tiq
 -----END PGP PUBLIC KEY BLOCK-----
 
+pub    1646B01B86E50310
+uid    Yarn Packaging <[email protected]>
+
+sub    4F77679369475BAA
+sub    23E7166788B63E1E
+sub    02820C39D50AF136
+sub    46C2130DFD2497F5
+sub    D101F7899D41F3C3
+sub    E074D16EB6FF4DE3
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.77.00
+
+mQINBFf0j5oBEADS6cItqCbf4lOLICohq2aHqM5I1jsz3DC4ddIU5ONbKXP1t0wk
+FEUPRzd6m80cTo7Q02Bw7enh4J6HvM5XVBSSGKENP6XAsiOZnY9nkXlcQAPFRnCn
+CjEfoOPZ0cBKjn2IpIXXcC+7xh4p1yruBpOsCbT6BuzA+Nm9j4cpRjdRdWSSmdID
+TyMZClmYm/NIfCPduYvNZxZXhW3QYeieP7HIonhZSHVu/jauEUyHLVsieUIvAOJI
+cXYpwLlrw0yy4flHe1ORJzuA7EZ4eOWCuKf1PgowEnVSS7Qp7lksCuljtfXgWelB
+XGJlAMD90mMbsNpQPF8ywQ2wjECM8Q6BGUcQuGMDBtFihobb+ufJxpUOm4uDt0y4
+zaw+MVSi+a56+zvY0VmMGVyJstldPAcUlFYBDsfC9+zpzyrAqRY+qFWOT2tj29R5
+ZNYvUUjEmA/kXPNIwmEr4oj7PVjSTUSpwoKamFFE6Bbha1bzIHpdPIRYc6cEulp3
+dTOWfp+Cniiblp9gwz3HeXOWu7npTTvJBnnyRSVtQgRnZrrtRt3oLZgmj2fpZFCE
+g8VcnQOb0iFcIM7VlWL0QR4SOz36/GFyezZkGsMlJwIGjXkqGhcEHYVDpg0nMoq1
+qUvizxv4nKLanZ5jKrV2J8V09PbL+BERIi6QSeXhXQIui/HfV5wHXC6DywARAQAB
+tBxZYXJuIFBhY2thZ2luZyA8eWFybkBkYW4uY3g+uQINBFw39W8BEACgQciTKMUO
+x+gLED9vao2PP6Xw4jZaEAx2rFF6Dsk31HrWpt62P1FghxazA9I7zhzSKhFST7El
+q8QWH/ouDTRArAko9/SR3VSzpJ+qSXEZzM5pY0vKIzsm2EVE0ynSpVfo2MuoagI0
+753/6c2TqzbHissQKP8RE6YTdlBASjmcFeY+WFelV6bbesZ9cRC9cM2oWa+qs/4F
+5+f/5oOmjz1AibewTwbQt+BxqMh1J+Asf40lO85UC1ddVck8rf02OvVoHNZNtaEt
+LZuOLsPA/uGyF2HsMTatLrbe+XO+SSuazygqSJGkoBN2FDwITH6aFMUoKPoa02qH
+C20Jt/3w30amIcPSFqzrli2BBCOTaktHratl/pzNqBF8psVDNeOXYnFL9uwdWFOS
+HIkWqANwcJJd7sry4ZMATYCRqQaEbIMlioZVKXTDkgBTx2oNAxzQimxUxsWTidfg
+aA7CLHCvOuAKHSNGL4TBQclhkcEDLM0KSv74PblHSDA2E8yNIp7wqzobeEfdc/9l
+WVx5b+A9/FGLFROT+ruzfE+k+EsxEQSkpLVoZyZjESSuRUEMDM1gp0o/UxjAufiW
+HjI0VdaVMKWcPIjpQ4NMfC3zEN122dP275T7Fzo4mJhwI6y2X6i9L0eYz8q31VMR
+f1xyQQQvUhL716xAyvhvlfTZjWr1Da4S5wARAQABiQRbBBgBCAAmAhsCFiEEcuz0
+ala0rTnJB7u3FkawG4blAxAFAmPPe44FCQ07IJ8CKcFdIAQZAQIABgUCXDf1bwAK
+CRBPd2eTaUdbqnR6D/4rYkm7VFtNIsk8VhCEtzFf7l3a6eS40sjLeQCu1m4H33YT
+YXDTSiQ8a2491LKTbKCHgN5ifCZpshYKGLssOn5iepwjgZ8zE0bpGwez4D/vussF
+JPcEHWx6NmmmYoJBkNILam9hpkGlLzRS9+0Jn1EV1j6RMLbYG2f/l6SJA58iqFQf
+0LIZZL9iUZyKiZUhWfWFI83yXgSkEIO1x5Fp6mG/ONAhlPcDE59KWnAm+JiMA6RJ
+fipbIiMLkp7mR8Q6V1S4E3UkYCbjm0qWvP8sP/5U5zYhaluQ4gBVKV1SClHKIglA
+BnzS2s3GskjudxogPyUBrukGm0mIvQqitGOTdzpjunzfQ3B7Qnl7n5bVHM3jCVHz
+7265HI/wxzDP8fPaPAyK/fxWTi0A57g9g1m4OemNnbzIKkXdRSC/ONqu5sUByDaN
+3xJq4JAWWY5+9P7WogxJ4uZYE52XMlNSklFxmqeQQTha2IxKXHJZUW9TA6YpIaLw
+IugZbUqyH22FD3MqG9VSJWgqfhnv0devilfquen6bB4nZ0Rm2WoPsfGNVOUIef1/
+MKW3823SHvbV8ltLnzsAL9kyCDkZX4Yq7oyKPSw8CjBQlUIjA324r8YcgJkQCgxK
+eA/zHMqeFTkUPfy/AG+ypsrFO0gKu/Y27CdDRYEFDnzDma/ZEIyvZbqJ6BIogAkQ
+FkawG4blAxDRIhAAwU3QWVRI/lOIqtMUyPv1NwIsN4C+Tz6Huc2P92WAOlGXiSmv
+5c79rRVPArxdWzM0DjcUO5PRY5E8bvctiWAmLmB0t5ahdoSdbV/5nJHa5zz9Q27y
+fC/Z08t58tuGtamRBtyuI63kUcvrVnwKphrru+bYuSvXIP7LdcUC4HaPeFsHWkXG
+ZzfC+Rj5kwDd8xv5lsCVX3KU2fgxbMrZnWHMLQbfdibrPxJuqWO9OfQAFLwEjSBw
+lo+1JXv6ur5r9g4IG51JjWoEsIca+mQHKKymoqOC5L/3n7fvNyoRtMJNf1LleZKW
+woVjTz7qurvK7ym0uRuD9G5cvMC+fqDnrMVCU95H4Eisp4JtZG72HHiRJLq43bYy
+BiC/uX2iE1PNHGe6Aeed6t3ISUOB7nQBB4VHjdt0cSG/K6Y8uNonZZtIe3FGQvAd
+xc6XHqUv+LYMl4sxoKyEhNXpN++5tVgbx2MxSiBaJcrRPviihw9KzTQcM2zs4/L1
+bjhkAWSca+hpM4RjE2BIjzjJR/bfTgiE/5T2lhxGcnAkzik7NxdnE+JutB/YTH1d
+ldGRv2OR9gy+zRcGcJNSdKXlZVWU0ejahQLb+bC5zfbUzq5x/u4PA3oV09BWUmHW
+pdUu9s2LJi6DSW/BezJ/C7ohf7CX6QA/k2tFi5fwf4vR5q5MatGyV3s5nceJBFsE
+GAEIACYCGwIWIQRy7PRqVrStOckHu7cWRrAbhuUDEAUCY897jgUJDTsgnwIpCRAW
+RrAbhuUDEMFdIAQZAQIABgUCXDf1bwAKCRBPd2eTaUdbqnR6D/4rYkm7VFtNIsk8
+VhCEtzFf7l3a6eS40sjLeQCu1m4H33YTYXDTSiQ8a2491LKTbKCHgN5ifCZpshYK
+GLssOn5iepwjgZ8zE0bpGwez4D/vussFJPcEHWx6NmmmYoJBkNILam9hpkGlLzRS
+9+0Jn1EV1j6RMLbYG2f/l6SJA58iqFQf0LIZZL9iUZyKiZUhWfWFI83yXgSkEIO1
+x5Fp6mG/ONAhlPcDE59KWnAm+JiMA6RJfipbIiMLkp7mR8Q6V1S4E3UkYCbjm0qW
+vP8sP/5U5zYhaluQ4gBVKV1SClHKIglABnzS2s3GskjudxogPyUBrukGm0mIvQqi
+tGOTdzpjunzfQ3B7Qnl7n5bVHM3jCVHz7265HI/wxzDP8fPaPAyK/fxWTi0A57g9
+g1m4OemNnbzIKkXdRSC/ONqu5sUByDaN3xJq4JAWWY5+9P7WogxJ4uZYE52XMlNS
+klFxmqeQQTha2IxKXHJZUW9TA6YpIaLwIugZbUqyH22FD3MqG9VSJWgqfhnv0dev
+ilfquen6bB4nZ0Rm2WoPsfGNVOUIef1/MKW3823SHvbV8ltLnzsAL9kyCDkZX4Yq
+7oyKPSw8CjBQlUIjA324r8YcgJkQCgxKeA/zHMqeFTkUPfy/AG+ypsrFO0gKu/Y2
+7CdDRYEFDnzDma/ZEIyvZbqJ6BIogNEiEADBTdBZVEj+U4iq0xTI+/U3Aiw3gL5P
+Poe5zY/3ZYA6UZeJKa/lzv2tFU8CvF1bMzQONxQ7k9FjkTxu9y2JYCYuYHS3lqF2
+hJ1tX/mckdrnPP1DbvJ8L9nTy3ny24a1qZEG3K4jreRRy+tWfAqmGuu75ti5K9cg
+/st1xQLgdo94WwdaRcZnN8L5GPmTAN3zG/mWwJVfcpTZ+DFsytmdYcwtBt92Jus/
+Em6pY7059AAUvASNIHCWj7Ule/q6vmv2DggbnUmNagSwhxr6ZAcorKaio4Lkv/ef
+t+83KhG0wk1/UuV5kpbChWNPPuq6u8rvKbS5G4P0bly8wL5+oOesxUJT3kfgSKyn
+gm1kbvYceJEkurjdtjIGIL+5faITU80cZ7oB553q3chJQ4HudAEHhUeN23RxIb8r
+pjy42idlm0h7cUZC8B3FzpcepS/4tgyXizGgrISE1ek377m1WBvHYzFKIFolytE+
++KKHD0rNNBwzbOzj8vVuOGQBZJxr6GkzhGMTYEiPOMlH9t9OCIT/lPaWHEZycCTO
+KTs3F2cT4m60H9hMfV2V0ZG/Y5H2DL7NFwZwk1J0peVlVZTR6NqFAtv5sLnN9tTO
+rnH+7g8DehXT0FZSYdal1S72zYsmLoNJb8F7Mn8LuiF/sJfpAD+Ta0WLl/B/i9Hm
+rkxq0bJXezmdx4kEWwQYAQgAJgIbAhYhBHLs9GpWtK05yQe7txZGsBuG5QMQBQJg
+DcVSBQkHmDbjAinBXSAEGQECAAYFAlw39W8ACgkQT3dnk2lHW6p0eg/+K2JJu1Rb
+TSLJPFYQhLcxX+5d2unkuNLIy3kArtZuB992E2Fw00okPGtuPdSyk2ygh4DeYnwm
+abIWChi7LDp+YnqcI4GfMxNG6RsHs+A/77rLBST3BB1sejZppmKCQZDSC2pvYaZB
+pS80UvftCZ9RFdY+kTC22Btn/5ekiQOfIqhUH9CyGWS/YlGciomVIVn1hSPN8l4E
+pBCDtceRaephvzjQIZT3AxOfSlpwJviYjAOkSX4qWyIjC5Ke5kfEOldUuBN1JGAm
+45tKlrz/LD/+VOc2IWpbkOIAVSldUgpRyiIJQAZ80trNxrJI7ncaID8lAa7pBptJ
+iL0KorRjk3c6Y7p830Nwe0J5e5+W1RzN4wlR8+9uuRyP8Mcwz/Hz2jwMiv38Vk4t
+AOe4PYNZuDnpjZ28yCpF3UUgvzjarubFAcg2jd8SauCQFlmOfvT+1qIMSeLmWBOd
+lzJTUpJRcZqnkEE4WtiMSlxyWVFvUwOmKSGi8CLoGW1Ksh9thQ9zKhvVUiVoKn4Z
+79HXr4pX6rnp+mweJ2dEZtlqD7HxjVTlCHn9fzClt/Nt0h721fJbS587AC/ZMgg5
+GV+GKu6Mij0sPAowUJVCIwN9uK/GHICZEAoMSngP8xzKnhU5FD38vwBvsqbKxTtI
+Crv2NuwnQ0WBBQ58w5mv2RCMr2W6iegSKIAJEBZGsBuG5QMQU8oQAMjiPEOFmgRc
+uhvhlzXT53d/1b8sfG4MV9c45xKE65L+kPoSGzvNWYumB2KwQzf8tWu+6PmOljj1
+Ofyilqm3bblOasHWgDGPTSOcBaVhl8nZrS3o2fzZy7aQKYE3gQBZ6+jzhHQzrnQU
+RpR+s/mdSO3+Gs+6kBmh9dkIQ8U1cfaAbZgy17BipPZkpwjrltTcDyJniQyEm7L6
+yV6MWt2TiFUA5IvyH+hTSKrLHnR7+lYDEo28wV8f8UcLrUpQjoiCOWZeNCubaIxH
+HoGtCE+zkhSsuW9lGSX0rzQlmx1vclrYwyMKhlpDOqy8kzdIWs7VF3vCXRi6fWSA
+7apRtQQ7PbuZOOyYTaEkEuJ5CfWhFGy3eikiXilPk05ECZd3/uMB1dmPFKT+MbUD
+CA/b8amfkNTLg+RFNX+5isMLkrJ+8k13ueTp/PToGMIkYsbR+HRm0HmrdqGFPl7o
++0xXUT4wGbQD8QfK81lzH1QQhsu+12OsFt+jQC3IDYiXOUBkzgkwMlt8C0vU0i/E
+Elpqx/0n19iHv7XvPn5q0MdNBS5pW+DOho0D+z+NM9MWpYUuymC/28jo8Olju+9D
+ZuZwEUEbptmltcA8UQ5r4FHx4m3sfCmCs1QUeb8TPNL0x8OAXnADXbxMgGYTNX7Y
+vdUw3a8M73stqnN9M8lUXln7ulOCee2ziQRbBBgBCAAmAhsCFiEEcuz0ala0rTnJ
+B7u3FkawG4blAxAFAmANxVIFCQeYNuMCKQkQFkawG4blAxDBXSAEGQECAAYFAlw3
+9W8ACgkQT3dnk2lHW6p0eg/+K2JJu1RbTSLJPFYQhLcxX+5d2unkuNLIy3kArtZu
+B992E2Fw00okPGtuPdSyk2ygh4DeYnwmabIWChi7LDp+YnqcI4GfMxNG6RsHs+A/
+77rLBST3BB1sejZppmKCQZDSC2pvYaZBpS80UvftCZ9RFdY+kTC22Btn/5ekiQOf
+IqhUH9CyGWS/YlGciomVIVn1hSPN8l4EpBCDtceRaephvzjQIZT3AxOfSlpwJviY
+jAOkSX4qWyIjC5Ke5kfEOldUuBN1JGAm45tKlrz/LD/+VOc2IWpbkOIAVSldUgpR
+yiIJQAZ80trNxrJI7ncaID8lAa7pBptJiL0KorRjk3c6Y7p830Nwe0J5e5+W1RzN
+4wlR8+9uuRyP8Mcwz/Hz2jwMiv38Vk4tAOe4PYNZuDnpjZ28yCpF3UUgvzjarubF
+Acg2jd8SauCQFlmOfvT+1qIMSeLmWBOdlzJTUpJRcZqnkEE4WtiMSlxyWVFvUwOm
+KSGi8CLoGW1Ksh9thQ9zKhvVUiVoKn4Z79HXr4pX6rnp+mweJ2dEZtlqD7HxjVTl
+CHn9fzClt/Nt0h721fJbS587AC/ZMgg5GV+GKu6Mij0sPAowUJVCIwN9uK/GHICZ
+EAoMSngP8xzKnhU5FD38vwBvsqbKxTtICrv2NuwnQ0WBBQ58w5mv2RCMr2W6iegS
+KIBTyhAAyOI8Q4WaBFy6G+GXNdPnd3/Vvyx8bgxX1zjnEoTrkv6Q+hIbO81Zi6YH
+YrBDN/y1a77o+Y6WOPU5/KKWqbdtuU5qwdaAMY9NI5wFpWGXydmtLejZ/NnLtpAp
+gTeBAFnr6POEdDOudBRGlH6z+Z1I7f4az7qQGaH12QhDxTVx9oBtmDLXsGKk9mSn
+COuW1NwPImeJDISbsvrJXoxa3ZOIVQDki/If6FNIqssedHv6VgMSjbzBXx/xRwut
+SlCOiII5Zl40K5tojEcega0IT7OSFKy5b2UZJfSvNCWbHW9yWtjDIwqGWkM6rLyT
+N0haztUXe8JdGLp9ZIDtqlG1BDs9u5k47JhNoSQS4nkJ9aEUbLd6KSJeKU+TTkQJ
+l3f+4wHV2Y8UpP4xtQMID9vxqZ+Q1MuD5EU1f7mKwwuSsn7yTXe55On89OgYwiRi
+xtH4dGbQeat2oYU+Xuj7TFdRPjAZtAPxB8rzWXMfVBCGy77XY6wW36NALcgNiJc5
+QGTOCTAyW3wLS9TSL8QSWmrH/SfX2Ie/te8+fmrQx00FLmlb4M6GjQP7P40z0xal
+hS7KYL/byOjw6WO770Nm5nARQRum2aW1wDxRDmvgUfHibex8KYKzVBR5vxM80vTH
+w4BecANdvEyAZhM1fti91TDdrwzvey2qc30zyVReWfu6U4J57bOJBFsEGAEIACYC
+GwIWIQRy7PRqVrStOckHu7cWRrAbhuUDEAUCXiUQMAUJA+IUwQIpwV0gBBkBAgAG
+BQJcN/VvAAoJEE93Z5NpR1uqdHoP/itiSbtUW00iyTxWEIS3MV/uXdrp5LjSyMt5
+AK7WbgffdhNhcNNKJDxrbj3UspNsoIeA3mJ8JmmyFgoYuyw6fmJ6nCOBnzMTRukb
+B7PgP++6ywUk9wQdbHo2aaZigkGQ0gtqb2GmQaUvNFL37QmfURXWPpEwttgbZ/+X
+pIkDnyKoVB/Qshlkv2JRnIqJlSFZ9YUjzfJeBKQQg7XHkWnqYb840CGU9wMTn0pa
+cCb4mIwDpEl+KlsiIwuSnuZHxDpXVLgTdSRgJuObSpa8/yw//lTnNiFqW5DiAFUp
+XVIKUcoiCUAGfNLazcaySO53GiA/JQGu6QabSYi9CqK0Y5N3OmO6fN9DcHtCeXuf
+ltUczeMJUfPvbrkcj/DHMM/x89o8DIr9/FZOLQDnuD2DWbg56Y2dvMgqRd1FIL84
+2q7mxQHINo3fEmrgkBZZjn70/taiDEni5lgTnZcyU1KSUXGap5BBOFrYjEpccllR
+b1MDpikhovAi6BltSrIfbYUPcyob1VIlaCp+Ge/R16+KV+q56fpsHidnRGbZag+x
+8Y1U5Qh5/X8wpbfzbdIe9tXyW0ufOwAv2TIIORlfhirujIo9LDwKMFCVQiMDfbiv
+xhyAmRAKDEp4D/Mcyp4VORQ9/L8Ab7KmysU7SAq79jbsJ0NFgQUOfMOZr9kQjK9l
+uonoEiiACRAWRrAbhuUDEK4uEACObAsZAO/OX5WtlHeHLYtlrv152O3UZe8kiFEX
+lWjabyS3s2Udkq0dg4DQfJ/ivP7m/+L7yic5jN+USThml+9grQzFnvm/Cbhsf51V
+o4VGcVbo4CXacJWivP/nHWkEBQ5BU6gERIG0AK3xBbXkIDn8rwdNlgghkY9RxrnJ
+HXt9Dmgmtyd+AcQt+wMLX4OrkYWIUj99pitVO1wKUXRnAy6nsmzfIZxTfK5a/tRo
+grty+T/0QQ5Yx9dEys8knMvElW/ICNbcDdYbqqqlLM++xrgZdjlyGOe9xsRl/Nwz
+zVIUgimj+LliOT1lBdVXNLoKy8NsLC+LhBK3ZyoE+QQr3wvPM3eLHJt85i6FqTA6
+jq57ixTDB0iXFYOGfkeeqOf7yXxboYQ2wbqIqGmZT5GDG2tm8v/HeIvwhR42ohla
+Z0yUU2jX78GLdcnMHZv/xLlo5ttcxEpJG3EzZ9H1WKcWLSFGwqIxs21+nh8bVzxq
+pFOOfhsJKpVuXJ8CB1PFzSsG9o0dM8hQjTA/1W9d+9UaU9Ckv9D8iUuuidD9/OgD
+UFhLJZYHVApBEo4+QE9SqK3TBwbe/F0eY7vU9AHC3yb2g46ANLEs+wYtoMHdJakb
+pGNm8IG6XdSv2jzeTY7eMEnRlQYWSPZf+vKMgZnnZdY7ykH6+5la0jRXjofLHGaw
+ZXWDdYkERAQYAQIADwUCXDf1bwIbAgUJAf40gAIpCRAWRrAbhuUDEMFdIAQZAQIA
+BgUCXDf1bwAKCRBPd2eTaUdbqnR6D/4rYkm7VFtNIsk8VhCEtzFf7l3a6eS40sjL
+eQCu1m4H33YTYXDTSiQ8a2491LKTbKCHgN5ifCZpshYKGLssOn5iepwjgZ8zE0bp
+Gwez4D/vussFJPcEHWx6NmmmYoJBkNILam9hpkGlLzRS9+0Jn1EV1j6RMLbYG2f/
+l6SJA58iqFQf0LIZZL9iUZyKiZUhWfWFI83yXgSkEIO1x5Fp6mG/ONAhlPcDE59K
+WnAm+JiMA6RJfipbIiMLkp7mR8Q6V1S4E3UkYCbjm0qWvP8sP/5U5zYhaluQ4gBV
+KV1SClHKIglABnzS2s3GskjudxogPyUBrukGm0mIvQqitGOTdzpjunzfQ3B7Qnl7
+n5bVHM3jCVHz7265HI/wxzDP8fPaPAyK/fxWTi0A57g9g1m4OemNnbzIKkXdRSC/
+ONqu5sUByDaN3xJq4JAWWY5+9P7WogxJ4uZYE52XMlNSklFxmqeQQTha2IxKXHJZ
+UW9TA6YpIaLwIugZbUqyH22FD3MqG9VSJWgqfhnv0devilfquen6bB4nZ0Rm2WoP
+sfGNVOUIef1/MKW3823SHvbV8ltLnzsAL9kyCDkZX4Yq7oyKPSw8CjBQlUIjA324
+r8YcgJkQCgxKeA/zHMqeFTkUPfy/AG+ypsrFO0gKu/Y27CdDRYEFDnzDma/ZEIyv
+ZbqJ6BIogOPDEACEOkLDR2VQ6h1jiiYkHX1Fr3Mhk7kgbFPb6iSySze5aRP+X4js
+VjT5g20hmcLTUZ5EnEm24PBBeKBgQqfVQQnvKYEqsB0IRjQ4LfjgB98vHzVo5hZt
+tbtdo7qtmfyfJm7SgL3l0aVBB1ZgkEtxPAA3HnSJBAaRHlr5cXzvJVyIWbrHkJ/v
+ynMnk4G83eosnM3hgF+08woiBujMabr/cjoMNPRIx4I6FqayGgF/sqyp3o35dSNE
+dmvtTCmtYMmWFIJcUXV7q2OV/bY3Bn1Qfb1WzAWR6lP3218EWmhuNvN8AE7RicCP
+vZZxHZko/uhgd/sSSEjayxZD09E9PB05JdkMQfSzhwmo75nNGgA8T7eFseUa6QCp
+mKIdGU2k68907pCaBUFGxRGzmL7i/KV9q3n0dS3NBZtMXVudlsyV5+21vkIdEk1v
+c9O7EdHnrZTOn3/9Jl8OEDHtJiDBEX4UA1Ymy9f4jsJs/iq87O628hhOw1yFsLGs
+6X7Q/EzFZphnNVtlbjmY7QsVQ0IWpyevdKi3l72fQLS+MoFX1eAgIcoqB4dozA4y
+KtvUwq+8yknf40gFhknp34vKEVcvTEldxxScReyaTaHhIEQRlgiECT2e1GbM5eFc
+UsA5LxqBaWa9jCHF55vvpxU5iWkvDW61dX2Fu6j6gzXT28yzveakXzyDiLkCDQRc
+LEQRARAApWOx2WPA8MY77jzzv0sNrV3sYS78mb0dErog0JCJKJL/SYeR4prq3gNG
+AJjYgP2UWJ6DHYSv6wiAvRyGzXpcmsU7bW6TD5uQF2bYdhQpKxQWav9uazwOSYAz
+r329FhVQDM715cb+DyVWsDOVvdFPt4Ydgp3XkNPkrFcNAH5P6ZYT2oPlEnATI+NJ
+T7haNgX5Gv2BtK4C3lTJJRv2NNHkdE2P3C1vhi618/2Nt+rLx6xBUb1H+GlYpvaV
+rG+fxBzZeP3eo0iQG39Igt/H1YMmXTA1Sa77NTCmILbCssVOA06mruD3BE+2DmaL
+wxcTFbBIXYOUPnjLE44C91UAIkUFcRyy1DdJaDyDGWMbx4sykOPCggFrHcNnCJhm
+OSMk97J1K6WHy8qgUEJCR/Y9wiZd06TYeo5XUvsVE4zp8rJ15IslvHXtI2NV1Cer
+U6zKpI7Rxq0AU2m0wcGw362IIjb2CkZ6Bqi6ZX1yc0Pr1pi/I8GC922bM8KGNKZz
+qvNbXJcIkObarPCb8FzAk1dbkHcRQ/FzSpJay1719SdrQbiX9Yy5ojNWS5OBd/X+
+v0d5BuO3EDO3EfR3RWB0cBY8ORRibO1xnix3Q9aRVrXypKs6/KEj3qALN28xEtfX
+ubWQ+8/k+U+6W+qLOMOzwFJyp5LMaBJttBWblPHjDa4Xj+fcUGcAEQEAAYkEWwQY
+AQgAJgIbAhYhBHLs9GpWtK05yQe7txZGsBuG5QMQBQJjz3uEBQkNRtHzAinBXSAE
+GQECAAYFAlwsRBEACgkQI+cWZ4i2Ph6B0g//cPis3v2M6XvAbVoM3GIMXnsVj1WA
+HuwA/ja7UfZJ9+kV/PiMLkAbW0fBj0/y0O3Ry12VVQGXhC+Vo4j6C8qwFP4OXa6E
+sxHXuvWMIztBaX1Kav613aXBtxp6tTrud0FFUh4sDc1RREb3tMr6y5cvFJgnrdWc
+X1gsl6ODcgWBGNc6ZX7H7j48hMR6KmNeZocW7p8W+BgDQJqXYwVNL15qOHzVAh0d
+WsFLE9gwBTmDCY03x9arxSNDGCXyxt6E77LbNVIoSRlEbkvi6j33nEbuERICYl6C
+ltXQCyiVKjheJcLMjbgv5+bLCv2zfeJ/WyOmOGKpHRu+lBV1GvliRxUblVlmjWPh
+YPBZXGyjII16Tqr+ilREcZFW+STccbrVct75JWLbxwlEmix+W1HwSRCR+KHx3Cur
+4ZPMOBlPsFilOOsNa7ROUB56t7zv21Ef3BeeaCd9c4kzNGN8d1icEqSXoWWPqgST
+0LZPtZyqWZVnWrHChVHfrioxhSnw8O3wY1A2GSahiCSvvjvOeEoJyU21ZMw6AVyH
+Ch6v42oYadBfGgFwNo5OCMhNxNy/CcUrBSDqyLVTM5QlNsT75Ys7kHHnc+Jk+xx4
+JpiyNCz5LzcPhlwpqnJQcjJdY1hDhK75Ormj/NfCMeZ8g1aVPX4xEq8AMyZYhZ5/
+lmM+13Rdv8ZW6FIJEBZGsBuG5QMQdLEP/0zmKX7qJkOwswVC+xkmNFaeq4/FFcF/
+IDdqIsleyYzBZ9J0SNLckqraGs4pVPcWCQ0IcHGE9EQg8SGUTsSwaZ1i+/Nx5h4z
+wu4rgNUwiOOmwiXHJL6HeJA1qaghNvC5nJweC/0UZU4SWu2e2o9joFlYF0rVlUBP
+dDiF/X6jZRnqjmBya7qtHnCWCyp0ZzlcJ69hVonupBe8uFTSDNbInWn/93gNBSqt
+db37VsWD+DA8Wf5kGg7jT8ii/Cgh2jXFfikqAfjtpcVEcHncuE2297jbZcMS5CcF
+PA25y/EAD3VOYW3UENXLVUITCUNooxLhO7DGsEQErCy4Gp6Uk8RwPPO37K9zeM+h
+Ws2sLnMukAXdU8JQKR/bW1upiugXL9R9CWw7bqImF8/GgsQa24Ji34OumdH/UO14
+qq16235nWcn0kRkDGTxCN1yl0xrmQG0LWF29v1/zJJLG/3VG3ObJ6EhztMGiDAr6
+bJ1YyyROY0CXh5wZ6l+CE98DkkNbQ1Sb9W/8MDKXN74FfNc8F4xcMCwc5x/gDxBW
+G32NflrTdCk9wVxdRgWO0i2uN4PLOVIGncrwH0QIzLb6R7RTSO6k1aUXHHNHXl76
+sSLjnSKUv/AqbtVrlWFC42VdOghkl1CD/d6faNv7RbJ7ZI0J0+MXzrrfP5XBpkNV
+oNeqdPvbAQm/iQRbBBgBCAAmAhsCFiEEcuz0ala0rTnJB7u3FkawG4blAxAFAmPP
+e4QFCQ1G0fMCKQkQFkawG4blAxDBXSAEGQECAAYFAlwsRBEACgkQI+cWZ4i2Ph6B
+0g//cPis3v2M6XvAbVoM3GIMXnsVj1WAHuwA/ja7UfZJ9+kV/PiMLkAbW0fBj0/y
+0O3Ry12VVQGXhC+Vo4j6C8qwFP4OXa6EsxHXuvWMIztBaX1Kav613aXBtxp6tTru
+d0FFUh4sDc1RREb3tMr6y5cvFJgnrdWcX1gsl6ODcgWBGNc6ZX7H7j48hMR6KmNe
+ZocW7p8W+BgDQJqXYwVNL15qOHzVAh0dWsFLE9gwBTmDCY03x9arxSNDGCXyxt6E
+77LbNVIoSRlEbkvi6j33nEbuERICYl6CltXQCyiVKjheJcLMjbgv5+bLCv2zfeJ/
+WyOmOGKpHRu+lBV1GvliRxUblVlmjWPhYPBZXGyjII16Tqr+ilREcZFW+STccbrV
+ct75JWLbxwlEmix+W1HwSRCR+KHx3Cur4ZPMOBlPsFilOOsNa7ROUB56t7zv21Ef
+3BeeaCd9c4kzNGN8d1icEqSXoWWPqgST0LZPtZyqWZVnWrHChVHfrioxhSnw8O3w
+Y1A2GSahiCSvvjvOeEoJyU21ZMw6AVyHCh6v42oYadBfGgFwNo5OCMhNxNy/CcUr
+BSDqyLVTM5QlNsT75Ys7kHHnc+Jk+xx4JpiyNCz5LzcPhlwpqnJQcjJdY1hDhK75
+Ormj/NfCMeZ8g1aVPX4xEq8AMyZYhZ5/lmM+13Rdv8ZW6FJ0sQ//TOYpfuomQ7Cz
+BUL7GSY0Vp6rj8UVwX8gN2oiyV7JjMFn0nRI0tySqtoazilU9xYJDQhwcYT0RCDx
+IZROxLBpnWL783HmHjPC7iuA1TCI46bCJcckvod4kDWpqCE28LmcnB4L/RRlThJa
+7Z7aj2OgWVgXStWVQE90OIX9fqNlGeqOYHJruq0ecJYLKnRnOVwnr2FWie6kF7y4
+VNIM1sidaf/3eA0FKq11vftWxYP4MDxZ/mQaDuNPyKL8KCHaNcV+KSoB+O2lxURw
+edy4Tbb3uNtlwxLkJwU8DbnL8QAPdU5hbdQQ1ctVQhMJQ2ijEuE7sMawRASsLLga
+npSTxHA887fsr3N4z6Fazawucy6QBd1TwlApH9tbW6mK6Bcv1H0JbDtuoiYXz8aC
+xBrbgmLfg66Z0f9Q7XiqrXrbfmdZyfSRGQMZPEI3XKXTGuZAbQtYXb2/X/Mkksb/
+dUbc5snoSHO0waIMCvpsnVjLJE5jQJeHnBnqX4IT3wOSQ1tDVJv1b/wwMpc3vgV8
+1zwXjFwwLBznH+APEFYbfY1+WtN0KT3BXF1GBY7SLa43g8s5UgadyvAfRAjMtvpH
+tFNI7qTVpRccc0deXvqxIuOdIpS/8Cpu1WuVYULjZV06CGSXUIP93p9o2/tFsntk
+jQnT4xfOut8/lcGmQ1Wg16p0+9sBCb+JBFsEGAEIACYCGwIWIQRy7PRqVrStOckH
+u7cWRrAbhuUDEAUCYA3FQQUJB6PoMAIpwV0gBBkBAgAGBQJcLEQRAAoJECPnFmeI
+tj4egdIP/3D4rN79jOl7wG1aDNxiDF57FY9VgB7sAP42u1H2SffpFfz4jC5AG1tH
+wY9P8tDt0ctdlVUBl4QvlaOI+gvKsBT+Dl2uhLMR17r1jCM7QWl9Smr+td2lwbca
+erU67ndBRVIeLA3NUURG97TK+suXLxSYJ63VnF9YLJejg3IFgRjXOmV+x+4+PITE
+eipjXmaHFu6fFvgYA0Cal2MFTS9eajh81QIdHVrBSxPYMAU5gwmNN8fWq8UjQxgl
+8sbehO+y2zVSKEkZRG5L4uo995xG7hESAmJegpbV0AsolSo4XiXCzI24L+fmywr9
+s33if1sjpjhiqR0bvpQVdRr5YkcVG5VZZo1j4WDwWVxsoyCNek6q/opURHGRVvkk
+3HG61XLe+SVi28cJRJosfltR8EkQkfih8dwrq+GTzDgZT7BYpTjrDWu0TlAeere8
+79tRH9wXnmgnfXOJMzRjfHdYnBKkl6Flj6oEk9C2T7WcqlmVZ1qxwoVR364qMYUp
+8PDt8GNQNhkmoYgkr747znhKCclNtWTMOgFchwoer+NqGGnQXxoBcDaOTgjITcTc
+vwnFKwUg6si1UzOUJTbE++WLO5Bx53PiZPsceCaYsjQs+S83D4ZcKapyUHIyXWNY
+Q4Su+Tq5o/zXwjHmfINWlT1+MRKvADMmWIWef5ZjPtd0Xb/GVuhSCRAWRrAbhuUD
+EMTLEACyFHe0SPm4rMMAE6dyadTJP8wRoI2epQciRqitIhANhmJ244WyqPWV3tDT
+gH/TaWPV7DerL6d2jOnwmdfT5JeXkWrGf5Gxwz619UFx/S4VpPOQf4eJb1Z9WaOd
+Q87A9+BwwO8d+2XROhMmiAetVo6jhvil0xR5t9HYg/uUSUu+tlHXlwPjdlYHUwUn
+t8HftoefWLXJj8ADHir1slw7jjFR/INE2dWqk6Lx2Ala+3yHN7/vpfOYvY4EyTvI
+eyLSoVn0fzUrsIv3HQSRWogO3MykjkiMjNbhdH8CXbEiQ1MiFKsugyi0kY6HOIe3
+//+cZ4xXlQLsLRnV3xm9e/xGOte4M8o05JaUCrcsCmubOnqUIaZmDF9bITHI7bhk
+xLkvXopoxx4UodiL4PPGOarAdRD2Y73eI7W6QhqZt8267tsLx4qe0q8/pCr7gX60
+E9hOSx2NszyS0FPME2CI4vxVR+GxS8gzp5hFQ8OUaSC9a6eb4YI66bDhkRog0GrM
+agX3JJI2172blRyp8Fe7DAEUOb/xCcaKdv6waT+pqtrOaxDArDVRPVVqDlr1fY0l
+Jis92ycBk4Gs8pAYiMEZlGUoh5MouBEPP7HtfZTMlsQm8J5hq3cJ+AxUPSbGTWUC
+ql7hGpT4S97mpyATuLnWqLZmBgDHhpHEmUQmONKSSpzSjjAS6IkEWwQYAQgAJgIb
+AhYhBHLs9GpWtK05yQe7txZGsBuG5QMQBQJgDcVBBQkHo+gwAikJEBZGsBuG5QMQ
+wV0gBBkBAgAGBQJcLEQRAAoJECPnFmeItj4egdIP/3D4rN79jOl7wG1aDNxiDF57
+FY9VgB7sAP42u1H2SffpFfz4jC5AG1tHwY9P8tDt0ctdlVUBl4QvlaOI+gvKsBT+
+Dl2uhLMR17r1jCM7QWl9Smr+td2lwbcaerU67ndBRVIeLA3NUURG97TK+suXLxSY
+J63VnF9YLJejg3IFgRjXOmV+x+4+PITEeipjXmaHFu6fFvgYA0Cal2MFTS9eajh8
+1QIdHVrBSxPYMAU5gwmNN8fWq8UjQxgl8sbehO+y2zVSKEkZRG5L4uo995xG7hES
+AmJegpbV0AsolSo4XiXCzI24L+fmywr9s33if1sjpjhiqR0bvpQVdRr5YkcVG5VZ
+Zo1j4WDwWVxsoyCNek6q/opURHGRVvkk3HG61XLe+SVi28cJRJosfltR8EkQkfih
+8dwrq+GTzDgZT7BYpTjrDWu0TlAeere879tRH9wXnmgnfXOJMzRjfHdYnBKkl6Fl
+j6oEk9C2T7WcqlmVZ1qxwoVR364qMYUp8PDt8GNQNhkmoYgkr747znhKCclNtWTM
+OgFchwoer+NqGGnQXxoBcDaOTgjITcTcvwnFKwUg6si1UzOUJTbE++WLO5Bx53Pi
+ZPsceCaYsjQs+S83D4ZcKapyUHIyXWNYQ4Su+Tq5o/zXwjHmfINWlT1+MRKvADMm
+WIWef5ZjPtd0Xb/GVuhSxMsQALIUd7RI+biswwATp3Jp1Mk/zBGgjZ6lByJGqK0i
+EA2GYnbjhbKo9ZXe0NOAf9NpY9XsN6svp3aM6fCZ19Pkl5eRasZ/kbHDPrX1QXH9
+LhWk85B/h4lvVn1Zo51DzsD34HDA7x37ZdE6EyaIB61WjqOG+KXTFHm30diD+5RJ
+S762UdeXA+N2VgdTBSe3wd+2h59YtcmPwAMeKvWyXDuOMVH8g0TZ1aqTovHYCVr7
+fIc3v++l85i9jgTJO8h7ItKhWfR/NSuwi/cdBJFaiA7czKSOSIyM1uF0fwJdsSJD
+UyIUqy6DKLSRjoc4h7f//5xnjFeVAuwtGdXfGb17/EY617gzyjTklpQKtywKa5s6
+epQhpmYMX1shMcjtuGTEuS9eimjHHhSh2Ivg88Y5qsB1EPZjvd4jtbpCGpm3zbru
+2wvHip7Srz+kKvuBfrQT2E5LHY2zPJLQU8wTYIji/FVH4bFLyDOnmEVDw5RpIL1r
+p5vhgjrpsOGRGiDQasxqBfckkjbXvZuVHKnwV7sMARQ5v/EJxop2/rBpP6mq2s5r
+EMCsNVE9VWoOWvV9jSUmKz3bJwGTgazykBiIwRmUZSiHkyi4EQ8/se19lMyWxCbw
+nmGrdwn4DFQ9JsZNZQKqXuEalPhL3uanIBO4udaotmYGAMeGkcSZRCY40pJKnNKO
+MBLoiQRbBBgBCAAmAhsCFiEEcuz0ala0rTnJB7u3FkawG4blAxAFAl4lEBIFCQPt
+xgECKcFdIAQZAQIABgUCXCxEEQAKCRAj5xZniLY+HoHSD/9w+Kze/Yzpe8BtWgzc
+YgxeexWPVYAe7AD+NrtR9kn36RX8+IwuQBtbR8GPT/LQ7dHLXZVVAZeEL5WjiPoL
+yrAU/g5droSzEde69YwjO0FpfUpq/rXdpcG3Gnq1Ou53QUVSHiwNzVFERve0yvrL
+ly8UmCet1ZxfWCyXo4NyBYEY1zplfsfuPjyExHoqY15mhxbunxb4GANAmpdjBU0v
+Xmo4fNUCHR1awUsT2DAFOYMJjTfH1qvFI0MYJfLG3oTvsts1UihJGURuS+LqPfec
+Ru4REgJiXoKW1dALKJUqOF4lwsyNuC/n5ssK/bN94n9bI6Y4YqkdG76UFXUa+WJH
+FRuVWWaNY+Fg8FlcbKMgjXpOqv6KVERxkVb5JNxxutVy3vklYtvHCUSaLH5bUfBJ
+EJH4ofHcK6vhk8w4GU+wWKU46w1rtE5QHnq3vO/bUR/cF55oJ31ziTM0Y3x3WJwS
+pJehZY+qBJPQtk+1nKpZlWdascKFUd+uKjGFKfDw7fBjUDYZJqGIJK++O854SgnJ
+TbVkzDoBXIcKHq/jahhp0F8aAXA2jk4IyE3E3L8JxSsFIOrItVMzlCU2xPvlizuQ
+cedz4mT7HHgmmLI0LPkvNw+GXCmqclByMl1jWEOErvk6uaP818Ix5nyDVpU9fjES
+rwAzJliFnn+WYz7XdF2/xlboUgkQFkawG4blAxDmfRAArNnI93fhZRi1YfmNjF4X
+1GmwkxEV+W/W5eKGENPoCx98/MtwXYXxpofTl6o8gk26exQZpFKQqfBSBgYBF91V
+W1v5cxH21mrQY/PcWlGP6Fewn1x4waremFQ1WRMzLybrNuzKy2KHMMfZARYUhDPl
+lBVxNEw9SXLZmWRSYrle8D4+/IMEX2N2Q2QSwob66DF9YxYyksyvqkK38W247ByD
+KuJSVp5V8E/LiIFfDt8yzwEJTgveOONsBYso7esOLiEngC464Hb7U+bwWmn/x/2C
+cUvJkqcx4wIId/1pUUGNKJSyfFnREG803x813ArecuumM4J/e8J400rYuZfvycZM
+CUSmU3ezqrSp+6F70UxEvdiyutwHSKnTtHOIFeE3RtQ0yQzA+NU4nJifm7FeAuVZ
+lCk2uyjriWEKGQ8d3zqcRreG1ckvMEih5w6amW6vgOTqjigU7DxZFThYHkSSIywp
+IPD4JzSKUfLbEms2l77E6FxWJ3KBS6Usa03d4FZQQGOEnXShRGTjZeZPYt311FHl
+9tCcVjTzpntRx4Rfyaz+4tqJDpgb1uTcY1tKK4QDxrrT4sONaDccHDtE47mSrQYE
+z0rlPQu32p5UcdCRsOpWh6sCnzNuFnYDcM5fkIq5D7c8VXya1fNroQpvWUxK8oOt
+PjFBXudjYJRx0coMDe1JR3OJBEQEGAECAA8FAlwsRBECGwIFCQIKEgACKQkQFkaw
+G4blAxDBXSAEGQECAAYFAlwsRBEACgkQI+cWZ4i2Ph6B0g//cPis3v2M6XvAbVoM
+3GIMXnsVj1WAHuwA/ja7UfZJ9+kV/PiMLkAbW0fBj0/y0O3Ry12VVQGXhC+Vo4j6
+C8qwFP4OXa6EsxHXuvWMIztBaX1Kav613aXBtxp6tTrud0FFUh4sDc1RREb3tMr6
+y5cvFJgnrdWcX1gsl6ODcgWBGNc6ZX7H7j48hMR6KmNeZocW7p8W+BgDQJqXYwVN
+L15qOHzVAh0dWsFLE9gwBTmDCY03x9arxSNDGCXyxt6E77LbNVIoSRlEbkvi6j33
+nEbuERICYl6CltXQCyiVKjheJcLMjbgv5+bLCv2zfeJ/WyOmOGKpHRu+lBV1Gvli
+RxUblVlmjWPhYPBZXGyjII16Tqr+ilREcZFW+STccbrVct75JWLbxwlEmix+W1Hw
+SRCR+KHx3Cur4ZPMOBlPsFilOOsNa7ROUB56t7zv21Ef3BeeaCd9c4kzNGN8d1ic
+EqSXoWWPqgST0LZPtZyqWZVnWrHChVHfrioxhSnw8O3wY1A2GSahiCSvvjvOeEoJ
+yU21ZMw6AVyHCh6v42oYadBfGgFwNo5OCMhNxNy/CcUrBSDqyLVTM5QlNsT75Ys7
+kHHnc+Jk+xx4JpiyNCz5LzcPhlwpqnJQcjJdY1hDhK75Ormj/NfCMeZ8g1aVPX4x
+Eq8AMyZYhZ5/lmM+13Rdv8ZW6FK7HQ/+IAKzntxOjw0MzCXkksKdmIOZ2bLeOVI8
+aSLaUmoT5CLuoia9g7iFHlYrSY+01riRrAaPtYx0x8onfyVxL9dlW/Fv5+qc1fF5
+FxdhyIgdqgzm82TnXHu/haUxYmUvNrbsmmNl5UTTOf+YQHMccKFdYfZ2rCBtbN2n
+iXG1tuz2+k83pozu4mJ1rOOLNAsQoY3yR6OODte1FyOgp7blwDhTIoQb8/UiJ7CM
+BI3OPrfoXFAnhYoxeRSAN4UFu9/HIkqfaQgRPCZS1gNerWF6r6yz9AZWUZqjSJss
+jBqXCtK9bGbTYBZk+pw3H9Nd0RJ2WJ9qPqmlmUr1wdqct0ChsJx1xAT86QrssicJ
+/HFFmF45hlnGkHUBWLaVJt8YkLb/DqOIbVbwyCLQtJ80VQLEeupfmu5QNsTpntRY
+NKf8cr00uc8vSYXYFRxa5H5oRT1eoFEEjDDvokNnHXfT+Hya44IjYpzaqvAgeDp6
+sYlOdtWIv/V3s+trxACwTkRN7zw3lLTbT8PK9szK0fYZ5KHG1/AKH+mbZ6qNc/25
+PNbAFRtttLGuEIC3HJ12IAp2JdjioeD2OnWLu4ZeCT2CKKFsleZPrSyCrn3gyZPm
+fYvv5h2JbQNO6uweOrZENWX5SU43OBoplbuKJZsMP6p6NahuGnIeJLlv509JYAf/
+HN4ARyvvOpO5Ag0EV/SPmgEQANDSEMBKp6ER86y+udfKdSLP9gOv6hPsAgCHhcvB
+sks+ixeX9U9KkK7vj/1q6wodKf9oEbbdykHgIIB1lzY1l7u7/biAtQhTjdEZPh/d
+t3vjogrJblUEC0rt+fZe325ociocS4Bt9I75TtkdnWgkE4uOBJsSllpUbqfLBfYR
+58zz2Rz1pkBqRTkmJFetVNYErYi2tWbeJ59GjUN7w1K3GhxqbMbgx4dF5+rjGs+K
+I9k6jkGeeQHqhDk+FU70oLVLuH2Dmi9IFjklKmGa3BU7VpNxvDwdoV7ttRYEBcBn
+POmL24Sn4Xhe2MDCqgJwwyohd9rk8neV7GtavVeaTv6bnzi1iJRgDld51HFWG8X+
+y55i5cYWaiXHdHOAG1+t35QUrczm9+sgkiKSk1IITlEFsfwRl16NTCMGzjP5kGCm
+/W+yyyvBMw7CkENQcd23fMsdaQ/2UNYJau2PoRH/m+IoRehIcmE0npKeLVTDeZNC
+zpmfY18T542ibK49kdjZiK6G/VyBhIbWEFVu5Ll9+8GbcO9ucYaaeWkFS8Hg0FZa
+fMk59VxKiICKLZ5he/C4f0UssXdyRYU6C5BH8UTCQLg0z8mSSL+Wb2iFVPrn39Do
+7Zm8ry6LBCmfCf3pI99Q/1VaLDauorooJV3rQ5kCJEiAeqQtLOvyoXIex1VbzlRU
+XmElABEBAAGJAh8EGAEIAAkFAlf0j5oCGwwACgkQFkawG4blAxAUUQ//afD0KLHj
+ClHsA/dFiW+5qVzI8kPMHwO1QcUjeXrB6I3SluOTrLSPhOsoS72yAaU9hFuq8g9e
+cmFrl3Skp/U4DHZXioEmozyZRp7eVsaHTewlfaOb6g7+v52ktYdomcp3BM5v/pPZ
+CnB5rLrH2KaUWbpY6V6tqtCHbF7zftDqcBENJDXfhiCqS19J08GZFjDEqGDrEj3Y
+EmEXZMN7PcXEISPIz6NYI6rw4yVH8AXfQW6vpPzmycHwI0QsVW2NQdcZ6zZt+phm
+6shNUbN2iDdg3BJICmIvQf8qhO3bOh0Bwc11FLHuMKuGVxnWN82HyIsuUB7WDLBH
+EOtg61Zf1nAF1PQK52YuQz3EWI4LL9OqVqfSTY1JjqIfj+u1PY2UHrxZfxlz1M8p
+Xb1grozjKQ5aNqBKRrcMZNx71itR5rv18qGjGR2iSciu/xah7zAroEQrx72IjYt0
+3tbk/007CvUlUqFIFB8kY1bbfX8JAA+TxelUniUR2CY8eom5HnaPpKE3kGXZ0jWk
+udbWb7uuWcW1FE/bO+VtexpBL3SoXmwbVMGnJIEiUvy8m6ez0kzLXzJ/4K4b8bDO
+4NjFX2ocKdzLA89Z95KcZUxEG0O7kaDCu0x3BEgeuArJLecD5je2/2HXAdvkOAOU
+i6Gc/LiJrtInc0vUFsdqWCUK5Ao/MKvdMFWJAjYEGAEIAAkFAlf0j5oCGwwAIQkQ
+FkawG4blAxAWIQRy7PRqVrStOckHu7cWRrAbhuUDEBRRD/9p8PQoseMKUewD90WJ
+b7mpXMjyQ8wfA7VBxSN5esHojdKW45OstI+E6yhLvbIBpT2EW6ryD15yYWuXdKSn
+9TgMdleKgSajPJlGnt5WxodN7CV9o5vqDv6/naS1h2iZyncEzm/+k9kKcHmsusfY
+ppRZuljpXq2q0IdsXvN+0OpwEQ0kNd+GIKpLX0nTwZkWMMSoYOsSPdgSYRdkw3s9
+xcQhI8jPo1gjqvDjJUfwBd9Bbq+k/ObJwfAjRCxVbY1B1xnrNm36mGbqyE1Rs3aI
+N2DcEkgKYi9B/yqE7ds6HQHBzXUUse4wq4ZXGdY3zYfIiy5QHtYMsEcQ62DrVl/W
+cAXU9ArnZi5DPcRYjgsv06pWp9JNjUmOoh+P67U9jZQevFl/GXPUzyldvWCujOMp
+Dlo2oEpGtwxk3HvWK1Hmu/XyoaMZHaJJyK7/FqHvMCugRCvHvYiNi3Te1uT/TTsK
+9SVSoUgUHyRjVtt9fwkAD5PF6VSeJRHYJjx6ibkedo+koTeQZdnSNaS51tZvu65Z
+xbUUT9s75W17GkEvdKhebBtUwackgSJS/Lybp7PSTMtfMn/grhvxsM7g2MVfahwp
+3MsDz1n3kpxlTEQbQ7uRoMK7THcESB64Cskt5wPmN7b/YdcB2+Q4A5SLoZz8uImu
+0idzS9QWx2pYJQrkCj8wq90wVbkCDQRYFTt3ARAAvWnAfimsUwUw1Xswk8DAS6DQ
+zF/c6xMG0IGcFg2+AUH5IXApAvINdS6O4ZC52OpA8LP/T0w4k/AYI6LS4DQFBJwn
+kkB86rfepeLWJcKP1qR4J9lcI5a0bcjNzeh9Lq557sp9ZQXWE3IDc9TClG0zamWX
+6xAQ7fL8Q3vFgZQ4zSaUJrCtga3FI0yVYGUyT/Lkcr26nP6IfBRoQng+NX4/hiYO
+rQ+c+9cTiqFMi/HHSQ7mhkXS0AkKhODUcTuj33/Gr1mt6MNHUkmPpu03DVgVCxEM
+vFFm/isv0d7XSWYuGJATxA3vM5bPIltE/761ZjZ3bMTIXO+w6wY9sGosM2Rtv13w
+F++n650bXewcAS1HMjZRZ7lIR1UehsCC7mX6bXRaWw1Mb8vSAl7V4PVvBx5aWdKt
+urD7L6lqb1gpap27XwRM3oJ4Bs2jjDuGidGRxF1nKgan3Nl/ejVsYEEyVrpFsu4k
+GhEM7LZCXsH5nzs3S13oqUcjpfx81tq1u2xdHuotQE/mhFvqddfj4OdMwNu8WkTb
+PGeSD4LfnhPDnKzIeEzppsHb7MJdxneGiV7LEL4FSs6AY/Isf+zTWaDbOa23ALVR
+uRoaRDpLAKhCgURWr0On3oHo+BnGETDQYyspHDncuhfitmJ/0TG/qpQWwZjxT11o
+R0M8XlQdh7u3+c+l9bcAEQEAAYkERAQYAQgADwIbAgUCWiRlIwUJBBaYowIpwV0g
+BBkBCAAGBQJYFTt3AAoJEEbCEw39JJf1cp0P/j91ZLLuH56NurwO5pfW81d5rv1/
+4M1NR6skqmL/WcM5NqOddmeEJKE//JUXqSBu7wBUUMz5+ej8/76TH4jA2n+rYcbP
+MKb+Mt5vq3ECECjuLDd8MBTz27zPMBqGOnxmryJCyiiF8tQac0RpbRnbn9HNvfsn
+L83HEKcrk62ZOGZe95C8gUvHwrfjGVnozDKJQ7riyYh213Iuvj3YtyJmm8krWzZy
+WJksq7jR3Cx5YCG8roE4gHHU8tAYv8/DH3mMI1QMBJF+G9jcLbsHmC87qQTk79Eq
+mApgXbmTm5EuSxzGfuWIfR363dOLjHTuXCIstALBnoU0Qt18mnmu0JPfd6zXBhM1
++yzQdRgdgV0qvRWD2/qtHsH/JPC6/RNFhtHbwzmiUpEhnnYHQNUM704KQAJFVrF5
+ZLEoVX+DVbJMTVBg0Wjy3kQfiFf+YzvpEbBbjoOU5ZJ+nILtfgZ+YCH79J8vSu03
+guve4EUCHCvHW9bfF4nYEim0ZrEmBGkQst3PccNvzewy1PybN4FfBVHe3GaWmDfp
+P3qzHcn6iwO6BXfhy2wOQ7v/e9chfP+gaH/M6YwffkD0SAEaXEThHEF89PXbZfMF
+vDNK9PcaZ2vtCZOicRfrLQ/e/oc4pEFyMVLQl49+abPVEksb9XLs9DGhXVhltdzr
+oLTeG0NgzYHpW0w1CRAWRrAbhuUDEKJVEACGr/UvliWyYi2ILoxE608/6V8BZGYx
+KSgi4C3ORX+iuo963BbONguACk69VcG5/0V0nm8DHUhcbeYPHqZBBaSvi32ifAJS
+5DuUTb2qa8IcHcWkpUk8qIYLrErfy3l0YAm1tQxkFBvW8O5Dud7Dx1UvMFIfVVnu
+DH0BP5YSm1bOdvVRLUBt6M7S5PyOxVz8xwrhHctCZXL36FtNhkXPXMIia6/1II1k
+9J0ukN7jVDFwx8qJbtdTiuyElQsp3gdgb3YqN4lTzTOALKmk6bzkQ2IzP3W09Pv4
+Dg7tcyU5okQfy3/ZJkYET92YeNIpxYiklishkpdOs2/utJAa/B40blqW8IC+essn
+ZxGlTVHOLDtq4RTViJKx76FDVkEjNaE8y6ch2eQv8Em6cFNUTuE8wwbtqU4vLlnS
+KQ3K1V7BqxmWb92Pm0Sx+8Xu2KxVNlZWNYK8QxM/l95w0wQDBtl0EPJAEA3TTLvG
+ng/Bz+Q8u5qoRLJNO3yMZ6T+l7v/7p+7QZCZHaBkoZqADF5kS9davFImn+rzVdJA
+Zw7pBgx9/tV7Fjedx2NJUuuj5ndm1i3ppUgYAQQDlQTfjXIIYh436evbsJcPh8cX
+bxo+H4AnNiH65WNCEdsvxqDBzIIwuBTzy7VTS1PlC2PCfD7D7dQ2FE6+fj3lESl4
+TAPrSJV6K4XkVYkEWwQYAQgADwIbAgUCWiRlIwUJBBaYowJACRAWRrAbhuUDEMFd
+IAQZAQgABgUCWBU7dwAKCRBGwhMN/SSX9XKdD/4/dWSy7h+ejbq8DuaX1vNXea79
+f+DNTUerJKpi/1nDOTajnXZnhCShP/yVF6kgbu8AVFDM+fno/P++kx+IwNp/q2HG
+zzCm/jLeb6txAhAo7iw3fDAU89u8zzAahjp8Zq8iQsoohfLUGnNEaW0Z25/Rzb37
+Jy/NxxCnK5OtmThmXveQvIFLx8K34xlZ6MwyiUO64smIdtdyLr492LciZpvJK1s2
+cliZLKu40dwseWAhvK6BOIBx1PLQGL/Pwx95jCNUDASRfhvY3C27B5gvO6kE5O/R
+KpgKYF25k5uRLkscxn7liH0d+t3Ti4x07lwiLLQCwZ6FNELdfJp5rtCT33es1wYT
+Nfss0HUYHYFdKr0Vg9v6rR7B/yTwuv0TRYbR28M5olKRIZ52B0DVDO9OCkACRVax
+eWSxKFV/g1WyTE1QYNFo8t5EH4hX/mM76RGwW46DlOWSfpyC7X4GfmAh+/SfL0rt
+N4Lr3uBFAhwrx1vW3xeJ2BIptGaxJgRpELLdz3HDb83sMtT8mzeBXwVR3txmlpg3
+6T96sx3J+osDugV34ctsDkO7/3vXIXz/oGh/zOmMH35A9EgBGlxE4RxBfPT122Xz
+BbwzSvT3Gmdr7QmTonEX6y0P3v6HOKRBcjFS0JePfmmz1RJLG/Vy7PQxoV1YZbXc
+66C03htDYM2B6VtMNRYhBHLs9GpWtK05yQe7txZGsBuG5QMQolUQAIav9S+WJbJi
+LYgujETrTz/pXwFkZjEpKCLgLc5Ff6K6j3rcFs42C4AKTr1Vwbn/RXSebwMdSFxt
+5g8epkEFpK+LfaJ8AlLkO5RNvaprwhwdxaSlSTyohgusSt/LeXRgCbW1DGQUG9bw
+7kO53sPHVS8wUh9VWe4MfQE/lhKbVs529VEtQG3oztLk/I7FXPzHCuEdy0Jlcvfo
+W02GRc9cwiJrr/UgjWT0nS6Q3uNUMXDHyolu11OK7ISVCyneB2Bvdio3iVPNM4As
+qaTpvORDYjM/dbT0+/gODu1zJTmiRB/Lf9kmRgRP3Zh40inFiKSWKyGSl06zb+60
+kBr8HjRuWpbwgL56yydnEaVNUc4sO2rhFNWIkrHvoUNWQSM1oTzLpyHZ5C/wSbpw
+U1RO4TzDBu2pTi8uWdIpDcrVXsGrGZZv3Y+bRLH7xe7YrFU2VlY1grxDEz+X3nDT
+BAMG2XQQ8kAQDdNMu8aeD8HP5Dy7mqhEsk07fIxnpP6Xu//un7tBkJkdoGShmoAM
+XmRL11q8Uiaf6vNV0kBnDukGDH3+1XsWN53HY0lS66Pmd2bWLemlSBgBBAOVBN+N
+cghiHjfp69uwlw+HxxdvGj4fgCc2IfrlY0IR2y/GoMHMgjC4FPPLtVNLU+ULY8J8
+PsPt1DYUTr5+PeURKXhMA+tIlXorheRViQQ+BBgBCAAJBQJYFTt3AhsCAikJEBZG
+sBuG5QMQwV0gBBkBCAAGBQJYFTt3AAoJEEbCEw39JJf1cp0P/j91ZLLuH56NurwO
+5pfW81d5rv1/4M1NR6skqmL/WcM5NqOddmeEJKE//JUXqSBu7wBUUMz5+ej8/76T
+H4jA2n+rYcbPMKb+Mt5vq3ECECjuLDd8MBTz27zPMBqGOnxmryJCyiiF8tQac0Rp
+bRnbn9HNvfsnL83HEKcrk62ZOGZe95C8gUvHwrfjGVnozDKJQ7riyYh213Iuvj3Y
+tyJmm8krWzZyWJksq7jR3Cx5YCG8roE4gHHU8tAYv8/DH3mMI1QMBJF+G9jcLbsH
+mC87qQTk79EqmApgXbmTm5EuSxzGfuWIfR363dOLjHTuXCIstALBnoU0Qt18mnmu
+0JPfd6zXBhM1+yzQdRgdgV0qvRWD2/qtHsH/JPC6/RNFhtHbwzmiUpEhnnYHQNUM
+704KQAJFVrF5ZLEoVX+DVbJMTVBg0Wjy3kQfiFf+YzvpEbBbjoOU5ZJ+nILtfgZ+
+YCH79J8vSu03guve4EUCHCvHW9bfF4nYEim0ZrEmBGkQst3PccNvzewy1PybN4Ff
+BVHe3GaWmDfpP3qzHcn6iwO6BXfhy2wOQ7v/e9chfP+gaH/M6YwffkD0SAEaXETh
+HEF89PXbZfMFvDNK9PcaZ2vtCZOicRfrLQ/e/oc4pEFyMVLQl49+abPVEksb9XLs
+9DGhXVhltdzroLTeG0NgzYHpW0w1BCoP/2E2wZfaBmEMR7/Uuar2VTb1dw7iNfBO
+VVuXXcqPyX1tuPk7pJYv2Yz0GJzDj/Op5yKqPyzxm+J1dbli+CoZjgmx+7eN/k5A
+MHcKMS42D+4jqkKfVUTxY6JlH77Zi4x6eK93FRLeUHiRGS4kgs0ijIzyqkTKOZzb
+NxLfvxclgjEWHUoIMIQX5GyiqOOB/aZi920y6soq+xCQHAK5Ws4yjMmakbCt4GX0
+IKAEjecQIXplVh553nFUgKqzluffRAZHOyn+pV401R7RzdNr12GEWdes6xI638G9
+ty0fpQMUn1sSw6LyiwYNkj26XbhL9RschBYBe7/X3KOGkG7of3zhrwPJ7l5AuZyO
+rpMOHOM8JiefkjULYRVGCli3eIUeKqwqCZDBFmZrAkbasY1Qif4vcoESRzhgFmmp
+u20vlO6XZqtpoBfqk8W9r1+pksyN6tzO3axlANxHWmaNYTwObpFou7TAhqj0JrVB
+UZ1Lxz8wg7ly2E/JUcYU8Q4t0+TF3SFJQFE3DacBvCJZdMZBnKTJYdA+aeOBhHTB
+XyYl8OgFBEx3Kgtqma9qz/ZKc71ZWqSK7D0YPw1PrO9GJ3HCN4jAlZuP1q4IRXit
+t9s5I4sEVTpviBFgZQxa5uDDI34LlHp3mGyvVoaiZ7LPZ3OEClYc7sp9xk7YNcP0
++gmmFVwY3BxZuQINBFf0j9gBEAC0XGL/4Yr9Z91WLiWxZxHzJBsJHQWCxqN4Yhym
+PMa7BVXj5fUpC/GqnQt9dClBTSONoBmP5c+eEVBzqnzLM03Wnxa5sFNfBAlhqOKB
+vahLrSChu35MXL5NjLYLDjGRSalQ0tWvSOEEyJL9XpZSwYVbm/Uu74Xv9Iw417PS
+MjgaWEK6QlwJuksqQn6apAyeKrf8hYXgE3Jt7PGVU7JNoYs9o526n76bXt4a91wK
+w42V695hgKd2GPSRU81QTocoU//dnurVEc/Gv/iKzCc4UsiA3pfS/trBx6PRPnwN
+/4d5YNpqOhGZkU7rxL/Rl3+cXj+ZUgg8EumAV3ryfDkj5aHAvEJvM4sMOncv9t10
+8T4OGE5HX+Nou1TqUqWuxLwRp3vGK5lM3268cgAKytOZaOemVm/UToE56jOtsH76
+wmxKkh4nTd1kEBITBze77V3gz4QHfL2g4WAZOAP8llyMICGzHM2H+z5e6GN215UB
+29Clv8uzdzmQq+Zj1veFSyx/7/k+UeA12qk1nlrQxly4oe7l6xvb3Zcq1CxFvhQ0
+qXsShVEHvN2vlZKesRl+xXTAvbTSmJz3kWKjxfVEBCYPjEm35B0gCYJMvm22nM/n
+qmnC1zDQhJNi1xVdQeurthL0pBmYh+/ugTTmq/OCN6ia1OywQ+05igQx7Z0a/SV6
+wMXntwARAQABiQREBBgBCAAPBQJX9I/YAhsCBQkB4TOAAikJEBZGsBuG5QMQwV0g
+BBkBCAAGBQJX9I/YAAoJENEB94mdQfPD2qAP/1bhnRebI8USDgDmKl8qTg/fyne5
+WMsZrQsJ7ynTO3/DuYg/KEdj5NUYWB46ZHzEVYzJGi38SEV3xkLFnyjdmbGuM5k7
+jcux+l9ul1t65BVqZnRAs9bIhkb3KS+jb8CzyD0Hu7Uk+9qtN7tR+23MixpNLE6a
+YDKATQ+8rmgUwWZADlos0ftd8exVfmNo+N9BbfwznvzU3PXQJaelpheW2jmQpVC3
+xcwk3/TnEtvm6OvKD4MFsJ3IiR0ki5ulkY7IKA8aOX98QSbNBCygq+IzMKUjk2Km
+w4BGDkE5hypF/GXkAO5qJHPYLgNFivg4hmYrPOZVPeZp3sidkbmHM+v1Q8L0/OKK
+HtbMFZAtqWpQ6gTMII2Uk823FFamUUZ0gwnvY7abxePFiuGUYqrRjugL84NWcDJl
+kq9Mx0Ky61DE6v+tlisS8o9qD8YKbUYOfPYSMF1cSUVkIxYo7yv8Uo0TC4IO92jU
+6ujo4ffIsigENx381uolwSy7hn78X5RXnZ1EnVqK69/DLKXBb4xQTpcvjQ0ozx2p
+jUNodfYhy62E+7eL3p1PT7wvjkb1xsUzfJkEfW7UnYnp0PGwblP5oP3ONe8Olm2O
+7ZJKh6lLkAvS6GlXQkBXhP0ihjXvsFjfxCotEIV0fflIFgONRnXYvNwZOXXrk8G1
+HmJDA3lJTqt8snpnBlcQAND0pLV5yYFI+t/nHV7u7rLVaszI1SckgikgbcCToHuW
+msqJwjcc+ysKDzt+Jhna/T+IiX3PZI2sDVe/WkOWlB/blNqhZW2TnTvKRzk48uBP
+idMGyW91bcwiXNRGmvN5GSxbSeORb9lkENjb9xaPsqgLZDpGgWb/WKtFE2g5EmBP
+TDMxw1RcddU09tTWQ8btkIiIqgXGX6ibSBRzeYzjY8DxtPMSwECn1DUTs3JWdYeF
+2FE/k1qLqKZqTXmsuSUjXKeS7AtkZ9I0hbmRjydawcAfb0hVQPxrzo/KhHX4Yzd1
+26jy1XTngoSroZStP7Di2WRvdUh5k+Uoz9szOkCiksDYB1+MWbeLroEmYmNd/l6p
+6zhjvc0R/LrpobEJiHhRHU9u37F0w05FjyNs5R05bUIx9Ynm6qjPYH2tilLeu9Vs
+qC9ci4Vq4Hj7wxPh3q9NNjrJb71umgCCAZItxZ7Tw6nSB/PRBAeFBBqogNXiGeoR
+mkH4pwu/yDpJJv5hGqnXqCOjUUudtf8n+4t+DTY9yGi/q2UBW1VnW0xMwD7QLPmz
+CTh0F0hmXlFBaxEPyWnpemjYO5pa+3GnJg8GVmC6An2OMPRluiR4uout56fBAN+A
+l3nH6K+3AF+F32qJAV48hJK531ejlmEu39NSSTnKmADYZ3cc9T4fG8aET3pX+avl
+iQRbBBgBCAAPBQJX9I/YAhsCBQkB4TOAAkAJEBZGsBuG5QMQwV0gBBkBCAAGBQJX
+9I/YAAoJENEB94mdQfPD2qAP/1bhnRebI8USDgDmKl8qTg/fyne5WMsZrQsJ7ynT
+O3/DuYg/KEdj5NUYWB46ZHzEVYzJGi38SEV3xkLFnyjdmbGuM5k7jcux+l9ul1t6
+5BVqZnRAs9bIhkb3KS+jb8CzyD0Hu7Uk+9qtN7tR+23MixpNLE6aYDKATQ+8rmgU
+wWZADlos0ftd8exVfmNo+N9BbfwznvzU3PXQJaelpheW2jmQpVC3xcwk3/TnEtvm
+6OvKD4MFsJ3IiR0ki5ulkY7IKA8aOX98QSbNBCygq+IzMKUjk2Kmw4BGDkE5hypF
+/GXkAO5qJHPYLgNFivg4hmYrPOZVPeZp3sidkbmHM+v1Q8L0/OKKHtbMFZAtqWpQ
+6gTMII2Uk823FFamUUZ0gwnvY7abxePFiuGUYqrRjugL84NWcDJlkq9Mx0Ky61DE
+6v+tlisS8o9qD8YKbUYOfPYSMF1cSUVkIxYo7yv8Uo0TC4IO92jU6ujo4ffIsigE
+Nx381uolwSy7hn78X5RXnZ1EnVqK69/DLKXBb4xQTpcvjQ0ozx2pjUNodfYhy62E
++7eL3p1PT7wvjkb1xsUzfJkEfW7UnYnp0PGwblP5oP3ONe8Olm2O7ZJKh6lLkAvS
+6GlXQkBXhP0ihjXvsFjfxCotEIV0fflIFgONRnXYvNwZOXXrk8G1HmJDA3lJTqt8
+snpnFiEEcuz0ala0rTnJB7u3FkawG4blAxAGVxAA0PSktXnJgUj63+cdXu7ustVq
+zMjVJySCKSBtwJOge5aayonCNxz7KwoPO34mGdr9P4iJfc9kjawNV79aQ5aUH9uU
+2qFlbZOdO8pHOTjy4E+J0wbJb3VtzCJc1Eaa83kZLFtJ45Fv2WQQ2Nv3Fo+yqAtk
+OkaBZv9Yq0UTaDkSYE9MMzHDVFx11TT21NZDxu2QiIiqBcZfqJtIFHN5jONjwPG0
+8xLAQKfUNROzclZ1h4XYUT+TWouopmpNeay5JSNcp5LsC2Rn0jSFuZGPJ1rBwB9v
+SFVA/GvOj8qEdfhjN3XbqPLVdOeChKuhlK0/sOLZZG91SHmT5SjP2zM6QKKSwNgH
+X4xZt4uugSZiY13+XqnrOGO9zRH8uumhsQmIeFEdT27fsXTDTkWPI2zlHTltQjH1
+iebqqM9gfa2KUt671WyoL1yLhWrgePvDE+Her002OslvvW6aAIIBki3FntPDqdIH
+89EEB4UEGqiA1eIZ6hGaQfinC7/IOkkm/mEaqdeoI6NRS521/yf7i34NNj3IaL+r
+ZQFbVWdbTEzAPtAs+bMJOHQXSGZeUUFrEQ/Jael6aNg7mlr7cacmDwZWYLoCfY4w
+9GW6JHi6i63np8EA34CXecfor7cAX4XfaokBXjyEkrnfV6OWYS7f01JJOcqYANhn
+dxz1Ph8bxoRPelf5q+W5Ag0EWbWWowEQALCiEk5Ic40W7/v5hqYNjrRlxTE/1axO
+hhzt8eCB7eOeNOMQKwabYxqBceNmol/guzlnFqLtbaA6yZQkzz/K3eNwWQg7CfXO
+3+p/dN0HtktPfdCk+kY/t7StKRjINW6S9xk9KshiukmdiDq8JKS0HgxqphBB3tDj
+mo6/RiaOEFMoUlXKSU+BYYpBpLKg53P8F/8nIsK2aZJyk8XuBd0UXKI+N1gfCfzo
+DWnYHs73LQKcjrTaZQauT81J7+TeWoLI28vkVxyjvTXAyjSBnhxTYfwUNGSoawEX
+yJ1uKCwhIpklxcCMI9Hykg7sKNsvmJ4uNcRJ7cSRfb0g5DR9dLhR+eEvFd+o4Pbl
+Kk16AI48N8Zg1dLlJuV2cAtl0oBPk+tnbZukvkS5n1IzTSmiiPIXvK2t506VtfFE
+w4iZrJWf2Q9//TszBM3r1FPATLH7EAeG5P8RV+ri7L7NvzP6ZQClRDUsxeimCSe8
+v/t0OpheCVMlM9TpVcKGMw8ig/WEodoLOP4iqBs4BKR7fuydjDqbU0k/sdJTltp7
+IIdK1e49POIQ7pt+SUrsq/HnPW4woLC1WjouBWyr2M7/a0SldPidZ2BUAK7O9oXo
+sidZMJT7dBp3eHrspY4bdkSxsd0nshj0ndtqNktxkrSFRkoFpMz0J/M3Q93CjdHu
+TLpTHQEWjm/7ABEBAAGJBEQEGAEIAA8FAlm1lqMCGwIFCQJ2LQACKQkQFkawG4bl
+AxDBXSAEGQEIAAYFAlm1lqMACgkQ4HTRbrb/TeMpDQ//eOIsCWY2gYOGACw42JzM
+VvuTDrgRT4hMhgHCGeKzn1wFL1EsbSQV4Z6pYvnNayuEakgIz14wf4UFs5u1ehfB
+watmakSQJn32ANcAvI0INAkLEoqqy81mROjMc9FFrOkdqjcN7yN0BzH9jNYL/gsv
+mOOwOu+dIH3C1Lgei844ZR1BZK1900mohuRwcji0sdROMcrKrGjqd4yb6f7yl0wb
+dAxA3IHT3TFGczC7Y41P2OEpaJeVIZZgxkgQsJ14qK/QGpdKvmZAQpjHBipeO/H+
+qxyOT5Y+f15VLWGOOVL090+ZdtF7h3m4X2+L7xWsFIgdOprfO60gq3e79YFfgNBY
+U5BGtJGFGlJ0sGtnpzx5QCRka0j/1E5lIu00sW3WfGItFd48hW6wHCloyoi7pBR7
+xqSEoU/U5o7+nC8wHFrDYyqcyO9Q3mZDw4LvlgnyMOM+qLv/fNgO9USE4T30eSvc
+0t/5p1hCKNvyxHFghdRSJqn70bm6MQY+kd6+B/k62Oy8eCwRt4PR+LQEIPnxN7xG
+uNpVO1oMyhhO41osYruMrodzw81icBRKYFlSuDOQ5jlcSajc6TvF22y+VXy7nx1q
+/CN4tzB/ryUASU+vXS8/QNM6qI/QbbgBy7VtHqDbs2KHp4cP0j9KYQzMrKwtRwfH
+qVrwFLkCp61EHwSlPsEFiglpMg/8DQ92O4beY0n7eSrilwEdJg89IeepTBm1QYiL
+M33qWLR9CABYAIiDG7qxviHozVfX6kUwbkntVpyHAXSbWrM3kD6jPs3u/dimLKVy
+d29AVrBSn9FC04EjtDWsj1KB7HrFN4oo9o0JLSnXeJb8FnPf3MitaKltvj/kZheg
+ozIs+zvpzuri0LvoB4fNA0T4eAmxkGkZBB+mjNCrUHIakyPZVzWGL0QGsfK1Q9jv
+w0OErqHJYX8A1wLre/HkBne+e5ezS6Mc7kFW33Y1arfbHFNAe12juPsOxqK76qNi
+lUbQpPtNvWP3FTpbkAdodMLq/gQ+M5yHwPe8SkpZ8wYCfcwEemz/P+4QhQB8tbYb
+pcPxJ+aQjVjcHpsLdrlSY3JL/gqockR7+97GrCzqXbgvsqiWr16Zyn6mxYWEHn9H
+XMh3b+2IYKFFXHffbIBq/mfibDnZtQBrZpn2uyh6F2ZuOsZh0LTD7RL53KV3fi90
+nS00Gs1kbMkPycL1JLqvYQDpllE2oZ1dKDYkwivGyDQhRNfERL6JkjyiSxfZ2c84
+r2HPgnJTi/WBplloQkM+2NfXrBo6kLHSC6aBndRKk2UmUhrUluGcQUyfzYRFH5kV
+ueIYfDaBPus9gb+sjnViFRpqVjefwlXSJEDHWP3Cl2cuo2mJjeDghj400U6pjSUW
+3bIC/PKJBFsEGAEIAA8FAlm1lqMCGwIFCQJ2LQACQAkQFkawG4blAxDBXSAEGQEI
+AAYFAlm1lqMACgkQ4HTRbrb/TeMpDQ//eOIsCWY2gYOGACw42JzMVvuTDrgRT4hM
+hgHCGeKzn1wFL1EsbSQV4Z6pYvnNayuEakgIz14wf4UFs5u1ehfBwatmakSQJn32
+ANcAvI0INAkLEoqqy81mROjMc9FFrOkdqjcN7yN0BzH9jNYL/gsvmOOwOu+dIH3C
+1Lgei844ZR1BZK1900mohuRwcji0sdROMcrKrGjqd4yb6f7yl0wbdAxA3IHT3TFG
+czC7Y41P2OEpaJeVIZZgxkgQsJ14qK/QGpdKvmZAQpjHBipeO/H+qxyOT5Y+f15V
+LWGOOVL090+ZdtF7h3m4X2+L7xWsFIgdOprfO60gq3e79YFfgNBYU5BGtJGFGlJ0
+sGtnpzx5QCRka0j/1E5lIu00sW3WfGItFd48hW6wHCloyoi7pBR7xqSEoU/U5o7+
+nC8wHFrDYyqcyO9Q3mZDw4LvlgnyMOM+qLv/fNgO9USE4T30eSvc0t/5p1hCKNvy
+xHFghdRSJqn70bm6MQY+kd6+B/k62Oy8eCwRt4PR+LQEIPnxN7xGuNpVO1oMyhhO
+41osYruMrodzw81icBRKYFlSuDOQ5jlcSajc6TvF22y+VXy7nx1q/CN4tzB/ryUA
+SU+vXS8/QNM6qI/QbbgBy7VtHqDbs2KHp4cP0j9KYQzMrKwtRwfHqVrwFLkCp61E
+HwSlPsEFigkWIQRy7PRqVrStOckHu7cWRrAbhuUDEGkyD/wND3Y7ht5jSft5KuKX
+AR0mDz0h56lMGbVBiIszfepYtH0IAFgAiIMburG+IejNV9fqRTBuSe1WnIcBdJta
+szeQPqM+ze792KYspXJ3b0BWsFKf0ULTgSO0NayPUoHsesU3iij2jQktKdd4lvwW
+c9/cyK1oqW2+P+RmF6CjMiz7O+nO6uLQu+gHh80DRPh4CbGQaRkEH6aM0KtQchqT
+I9lXNYYvRAax8rVD2O/DQ4SuoclhfwDXAut78eQGd757l7NLoxzuQVbfdjVqt9sc
+U0B7XaO4+w7Gorvqo2KVRtCk+029Y/cVOluQB2h0wur+BD4znIfA97xKSlnzBgJ9
+zAR6bP8/7hCFAHy1thulw/En5pCNWNwemwt2uVJjckv+CqhyRHv73sasLOpduC+y
+qJavXpnKfqbFhYQef0dcyHdv7YhgoUVcd99sgGr+Z+JsOdm1AGtmmfa7KHoXZm46
+xmHQtMPtEvncpXd+L3SdLTQazWRsyQ/JwvUkuq9hAOmWUTahnV0oNiTCK8bINCFE
+18REvomSPKJLF9nZzzivYc+CclOL9YGmWWhCQz7Y19esGjqQsdILpoGd1EqTZSZS
+GtSW4ZxBTJ/NhEUfmRW54hh8NoE+6z2Bv6yOdWIVGmpWN5/CVdIkQMdY/cKXZy6j
+aYmN4OCGPjTRTqmNJRbdsgL88g==
+=YmLn
+-----END PGP PUBLIC KEY BLOCK-----
+
 pub    164BD2247B936711
 uid    Marc Philipp (JUnit Development, 2014) <[email protected]>
 
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 16a5511..965a16a 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -10,6 +10,8 @@
          <key-server uri="https://keys.openpgp.org"/>
       </key-servers>
       <trusted-artifacts>
+         <trust file="apiLevels.json" reason="We do not sign this metadata"/>
+         <trust group="androidx[.]annotation" version="1\.[0-7]\..*" regex="true" reason="Old versions, before signing"/>
          <trust group="com.android.ndk.thirdparty" reason="b/215430394"/>
          <trust group="com.android.tools" name="desugar_jdk_libs" reason="b/215430394"/>
          <trust group="com.android.tools" name="desugar_jdk_libs_configuration" reason="b/215430394"/>
@@ -40,7 +42,7 @@
          <trust file=".*-javadoc[.]jar" regex="true"/>
          <trust file=".*-sources[.]jar" regex="true"/>
          <trust file=".*[.]asc" regex="true"/>
-         <trust group="^androidx(?!\.compose.compiler\b)\..*" regex="true" reason="not signed yet"/>
+         <trust group="^androidx(?!\.compose.compiler\b)(?!\.annotation\b)\..*" regex="true" reason="not signed yet"/>
          <trust group="^com[.]android($|([.].*))" version="30.0.0" regex="true" reason="old version, before signing"/>
          <trust group="^com[.]android($|([.].*))" version="30.0.4" regex="true" reason="old version, before signing"/>
          <trust group="^com[.]android($|([.].*))" version="30.3.0" regex="true" reason="old version, before signing"/>
@@ -277,6 +279,7 @@
             <trusting group="org.jvnet.staxex"/>
             <trusting group="^com[.]sun($|([.].*))" regex="true"/>
          </trusted-key>
+         <trusted-key id="6D98490C6F1ACDDD448E45954F77679369475BAA" group="com.yarnpkg"/>
          <trusted-key id="713DA88BE50911535FE716F5208B0AB1D63011C7" group="org.apache.tomcat" name="annotations-api"/>
          <trusted-key id="7186BBF993566D8C2F4F7ED7D945E643368FEF62" group="io.github.eisop"/>
          <trusted-key id="719F7C29985A8E95F58F47194D8159D6A1159B69" group="dev.zacsweers.moshix"/>
@@ -942,9 +945,17 @@
             <sha256 value="c6c38e1fb8d99d1d41728b0f055e490d5e463c1252f13e7bb6f7015c22c95ab6" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
-      <component group="org.jetbrains.kotlinx.benchmark" name="org.jetbrains.kotlinx.benchmark.gradle.plugin" version="0.4.8">
-         <artifact name="org.jetbrains.kotlinx.benchmark.gradle.plugin-0.4.8.pom">
-            <sha256 value="094140c425967f8ca98a2bdaf4bf293315b057e141c1dec85b1898d78b601536" origin="Generated by Gradle" reason="Artifact is not signed"/>
+      <component group="org.jetbrains.kotlinx.benchmark" name="org.jetbrains.kotlinx.benchmark.gradle.plugin" version="0.4.11">
+         <artifact name="org.jetbrains.kotlinx.benchmark.gradle.plugin-0.4.11.pom">
+            <sha256 value="35428beab195a9a9df2afd6614ff1e4e4feac1a2c689006b1d649abe4a9391e7" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
+      <component group="org.jetbrains.kotlinx" name="kotlinx-benchmark-plugin" version="0.4.11">
+         <artifact name="kotlinx-benchmark-plugin-0.4.11.jar">
+            <sha256 value="5c337e082137eb3cdf64b27b11e16b97e5dd0a7905aa9c2d7c8c323601e5c31b" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+         <artifact name="kotlinx-benchmark-plugin-0.4.11.module">
+            <sha256 value="af5e8d80674cd6f5bce92451296f3238c6524b880179d56f08187cef3de723ec" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
       <component group="org.jetbrains.skiko" name="skiko" version="0.7.7">
@@ -1056,5 +1067,16 @@
             <sha256 value="4e54622f5dc0f8b6c51e28650268f001e3b55d076c8e3a9d9731c050820c0a3d" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+      <component group="org.nodejs" name="node" version="16.20.2">
+         <artifact name="node-16.20.2-darwin-arm64.tar.gz">
+            <sha256 value="6a5c4108475871362d742b988566f3fe307f6a67ce14634eb3fbceb4f9eea88c" origin="Generated by Node" reason="Artifact is not signed. Remove when https://github.com/nodejs/node/issues/53917 is resolved"/>
+         </artifact>
+         <artifact name="node-16.20.2-darwin-x64.tar.gz">
+            <sha256 value="d7a46eaf2b57ffddeda16ece0d887feb2e31a91ad33f8774da553da0249dc4a6" origin="Generated by Node" reason="Artifact is not signed. Remove when https://github.com/nodejs/node/issues/53917 is resolved"/>
+         </artifact>
+         <artifact name="node-16.20.2-linux-x64.tar.gz">
+            <sha256 value="c9193e6c414891694759febe846f4f023bf48410a6924a8b1520c46565859665" origin="Generated by Node" reason="Artifact is not signed. Remove when https://github.com/nodejs/node/issues/53917 is resolved"/>
+         </artifact>
+      </component>
    </components>
 </verification-metadata>
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 952107d..3164a58 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-8.8-bin.zip
-distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
+distributionUrl=../../../../tools/external/gradle/gradle-8.9-bin.zip
+distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/graphics/filters/filters/build.gradle b/graphics/filters/filters/build.gradle
index b6f4fa6..a593ab0 100644
--- a/graphics/filters/filters/build.gradle
+++ b/graphics/filters/filters/build.gradle
@@ -35,7 +35,7 @@
     api(libs.kotlinStdlib)
 
     // Add dependencies here
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation('androidx.media3:media3-effect:' + media3Version)
     implementation('androidx.media3:media3-common:' + media3Version)
     implementation('androidx.media3:media3-ui:' + media3Version)
@@ -62,6 +62,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Apply visual filters to images, video or UI in real time."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/graphics/filters/filters/lint-baseline.xml b/graphics/filters/filters/lint-baseline.xml
index 7b4b9b9d..4ed9fd3 100644
--- a/graphics/filters/filters/lint-baseline.xml
+++ b/graphics/filters/filters/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="UnsafeOptInUsageError"
@@ -13,8 +13,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    Assertions.checkArgument("
-        errorLine2="               ~~~~~~~~~~~~~">
+        errorLine1="        Assertions.checkArgument("
+        errorLine2="                   ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/Vignette.kt"/>
     </issue>
@@ -22,8 +22,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    Assertions.checkArgument("
-        errorLine2="               ~~~~~~~~~~~~~">
+        errorLine1="        Assertions.checkArgument("
+        errorLine2="                   ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/Vignette.kt"/>
     </issue>
@@ -31,8 +31,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  @Throws(FrameProcessingException::class)"
-        errorLine2="          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    @Throws(FrameProcessingException::class)"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/Vignette.kt"/>
     </issue>
@@ -40,8 +40,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  override fun toGlTextureProcessor("
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    override fun toGlTextureProcessor("
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/Vignette.kt"/>
     </issue>
@@ -49,8 +49,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  ): SingleFrameGlTextureProcessor {"
-        errorLine2="     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    ): SingleFrameGlTextureProcessor {"
+        errorLine2="       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/Vignette.kt"/>
     </issue>
@@ -58,8 +58,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  SingleFrameGlTextureProcessor(useHdr) {"
-        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    SingleFrameGlTextureProcessor(useHdr) {"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -67,8 +67,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  SingleFrameGlTextureProcessor(useHdr) {"
-        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    SingleFrameGlTextureProcessor(useHdr) {"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -76,8 +76,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  private var glProgram: GlProgram? = null"
-        errorLine2="                         ~~~~~~~~~~">
+        errorLine1="    private var glProgram: GlProgram? = null"
+        errorLine2="                           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -85,8 +85,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="        GlProgram(context!!, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH)"
-        errorLine2="        ~~~~~~~~~">
+        errorLine1="                GlProgram(context!!, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH)"
+        errorLine2="                ~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -94,8 +94,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="        throw FrameProcessingException(e)"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                throw FrameProcessingException(e)"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -103,8 +103,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      } catch (e: GlUtil.GlException) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~">
+        errorLine1="            } catch (e: GlUtil.GlException) {"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -112,8 +112,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="        throw FrameProcessingException(e)"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                throw FrameProcessingException(e)"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -121,8 +121,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    glProgram!!.setBufferAttribute("
-        errorLine2="                ~~~~~~~~~~~~~~~~~~">
+        errorLine1="        glProgram!!.setBufferAttribute("
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -130,8 +130,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      GlUtil.getNormalizedCoordinateBounds(),"
-        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            GlUtil.getNormalizedCoordinateBounds(),"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -139,8 +139,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE"
-        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -148,8 +148,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    val identityMatrix = GlUtil.create4x4IdentityMatrix()"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="        val identityMatrix = GlUtil.create4x4IdentityMatrix()"
+        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -157,8 +157,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    glProgram!!.setFloatsUniform(&quot;uTransformationMatrix&quot;, identityMatrix)"
-        errorLine2="                ~~~~~~~~~~~~~~~~">
+        errorLine1="        glProgram!!.setFloatsUniform(&quot;uTransformationMatrix&quot;, identityMatrix)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -166,8 +166,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    glProgram!!.setFloatsUniform(&quot;uTexTransformationMatrix&quot;, identityMatrix)"
-        errorLine2="                ~~~~~~~~~~~~~~~~">
+        errorLine1="        glProgram!!.setFloatsUniform(&quot;uTexTransformationMatrix&quot;, identityMatrix)"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -175,8 +175,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  override fun configure(inputWidth: Int, inputHeight: Int): Pair&lt;Int, Int> {"
-        errorLine2="               ~~~~~~~~~">
+        errorLine1="    override fun configure(inputWidth: Int, inputHeight: Int): Pair&lt;Int, Int> {"
+        errorLine2="                 ~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -184,8 +184,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  @Throws(FrameProcessingException::class)"
-        errorLine2="          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    @Throws(FrameProcessingException::class)"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -193,8 +193,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  override fun drawFrame(inputTexId: Int, presentationTimeUs: Long) {"
-        errorLine2="               ~~~~~~~~~">
+        errorLine1="    override fun drawFrame(inputTexId: Int, presentationTimeUs: Long) {"
+        errorLine2="                 ~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -202,8 +202,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.use()"
-        errorLine2="                  ~~~">
+        errorLine1="            glProgram!!.use()"
+        errorLine2="                        ~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -211,8 +211,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.setFloatUniform(&quot;uAspectRatio&quot;, this.aspectRatio)"
-        errorLine2="                  ~~~~~~~~~~~~~~~">
+        errorLine1="            glProgram!!.setFloatUniform(&quot;uAspectRatio&quot;, this.aspectRatio)"
+        errorLine2="                        ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -220,8 +220,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.setFloatUniform(&quot;uInnerRadius&quot;, this.vignetteEffect.innerRadius)"
-        errorLine2="                  ~~~~~~~~~~~~~~~">
+        errorLine1="            glProgram!!.setFloatUniform(&quot;uInnerRadius&quot;, this.vignetteEffect.innerRadius)"
+        errorLine2="                        ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -229,8 +229,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.setFloatUniform(&quot;uOuterRadius&quot;, this.vignetteEffect.outerRadius)"
-        errorLine2="                  ~~~~~~~~~~~~~~~">
+        errorLine1="            glProgram!!.setFloatUniform(&quot;uOuterRadius&quot;, this.vignetteEffect.outerRadius)"
+        errorLine2="                        ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -238,8 +238,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.setIntUniform(&quot;uShouldVignetteAlpha&quot;,"
-        errorLine2="                  ~~~~~~~~~~~~~">
+        errorLine1="            glProgram!!.setIntUniform(&quot;uShouldVignetteAlpha&quot;, if (shouldVignetteAlpha) 1 else 0)"
+        errorLine2="                        ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -247,8 +247,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.setIntUniform(&quot;uShouldVignetteColor&quot;,"
-        errorLine2="                  ~~~~~~~~~~~~~">
+        errorLine1="            glProgram!!.setIntUniform(&quot;uShouldVignetteColor&quot;, if (shouldVignetteColor) 1 else 0)"
+        errorLine2="                        ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -256,8 +256,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.setSamplerTexIdUniform(&quot;uTexSampler&quot;, inputTexId, /* texUnitIndex= */ 0)"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            glProgram!!.setSamplerTexIdUniform(&quot;uTexSampler&quot;, inputTexId, /* texUnitIndex= */ 0)"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -265,8 +265,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.bindAttributesAndUniforms()"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            glProgram!!.bindAttributesAndUniforms()"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -274,8 +274,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    } catch (e: GlUtil.GlException) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~">
+        errorLine1="        } catch (e: GlUtil.GlException) {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -283,8 +283,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      throw FrameProcessingException(e, presentationTimeUs)"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            throw FrameProcessingException(e, presentationTimeUs)"
+        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -292,8 +292,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  @Throws(FrameProcessingException::class)"
-        errorLine2="          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    @Throws(FrameProcessingException::class)"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -301,8 +301,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="  override fun release() {"
-        errorLine2="               ~~~~~~~">
+        errorLine1="    override fun release() {"
+        errorLine2="                 ~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -310,8 +310,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    super.release()"
-        errorLine2="          ~~~~~~~">
+        errorLine1="        super.release()"
+        errorLine2="              ~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -319,8 +319,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      glProgram!!.delete()"
-        errorLine2="                  ~~~~~~">
+        errorLine1="            glProgram!!.delete()"
+        errorLine2="                        ~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -328,8 +328,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="    } catch (e: GlUtil.GlException) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~">
+        errorLine1="        } catch (e: GlUtil.GlException) {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
@@ -337,8 +337,8 @@
     <issue
         id="UnsafeOptInUsageError"
         message="This declaration is opt-in and its usage should be marked with `@androidx.media3.common.util.UnstableApi` or `@OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)`"
-        errorLine1="      throw FrameProcessingException(e)"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            throw FrameProcessingException(e)"
+        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/graphics/filters/VignetteProcessor.kt"/>
     </issue>
diff --git a/graphics/graphics-core/api/current.ignore b/graphics/graphics-core/api/current.ignore
index 8ef4c71..bc23c60 100644
--- a/graphics/graphics-core/api/current.ignore
+++ b/graphics/graphics-core/api/current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
+AddedMethod: androidx.graphics.opengl.GLRenderer#GLRenderer():
+    Added constructor androidx.graphics.opengl.GLRenderer()
 AddedMethod: androidx.graphics.opengl.egl.EGLConfigAttributes.Builder#Builder():
     Added constructor androidx.graphics.opengl.egl.EGLConfigAttributes.Builder()
+AddedMethod: androidx.graphics.opengl.egl.EGLManager#EGLManager():
+    Added constructor androidx.graphics.opengl.egl.EGLManager()
diff --git a/graphics/graphics-core/api/current.txt b/graphics/graphics-core/api/current.txt
index 2359210..09fe8bf 100644
--- a/graphics/graphics-core/api/current.txt
+++ b/graphics/graphics-core/api/current.txt
@@ -200,6 +200,7 @@
   }
 
   public final class GLRenderer {
+    ctor public GLRenderer();
     ctor public GLRenderer(optional kotlin.jvm.functions.Function0<? extends androidx.graphics.opengl.egl.EGLSpec> eglSpecFactory, optional kotlin.jvm.functions.Function1<? super androidx.graphics.opengl.egl.EGLManager,? extends android.opengl.EGLConfig> eglConfigFactory);
     method public androidx.graphics.opengl.GLRenderer.RenderTarget attach(android.view.Surface surface, int width, int height, androidx.graphics.opengl.GLRenderer.RenderCallback renderer);
     method public androidx.graphics.opengl.GLRenderer.RenderTarget attach(android.view.SurfaceView surfaceView, androidx.graphics.opengl.GLRenderer.RenderCallback renderer);
@@ -294,6 +295,7 @@
   }
 
   public final class EGLManager {
+    ctor public EGLManager();
     ctor public EGLManager(optional androidx.graphics.opengl.egl.EGLSpec eglSpec);
     method public android.opengl.EGLContext createContext(android.opengl.EGLConfig config);
     method public android.opengl.EGLSurface getCurrentDrawSurface();
diff --git a/graphics/graphics-core/api/restricted_current.ignore b/graphics/graphics-core/api/restricted_current.ignore
new file mode 100644
index 0000000..3c08e53
--- /dev/null
+++ b/graphics/graphics-core/api/restricted_current.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+AddedMethod: androidx.graphics.opengl.GLRenderer#GLRenderer():
+    Added constructor androidx.graphics.opengl.GLRenderer()
+AddedMethod: androidx.graphics.opengl.egl.EGLManager#EGLManager():
+    Added constructor androidx.graphics.opengl.egl.EGLManager()
diff --git a/graphics/graphics-core/api/restricted_current.txt b/graphics/graphics-core/api/restricted_current.txt
index 2359210..09fe8bf 100644
--- a/graphics/graphics-core/api/restricted_current.txt
+++ b/graphics/graphics-core/api/restricted_current.txt
@@ -200,6 +200,7 @@
   }
 
   public final class GLRenderer {
+    ctor public GLRenderer();
     ctor public GLRenderer(optional kotlin.jvm.functions.Function0<? extends androidx.graphics.opengl.egl.EGLSpec> eglSpecFactory, optional kotlin.jvm.functions.Function1<? super androidx.graphics.opengl.egl.EGLManager,? extends android.opengl.EGLConfig> eglConfigFactory);
     method public androidx.graphics.opengl.GLRenderer.RenderTarget attach(android.view.Surface surface, int width, int height, androidx.graphics.opengl.GLRenderer.RenderCallback renderer);
     method public androidx.graphics.opengl.GLRenderer.RenderTarget attach(android.view.SurfaceView surfaceView, androidx.graphics.opengl.GLRenderer.RenderCallback renderer);
@@ -294,6 +295,7 @@
   }
 
   public final class EGLManager {
+    ctor public EGLManager();
     ctor public EGLManager(optional androidx.graphics.opengl.egl.EGLSpec eglSpec);
     method public android.opengl.EGLContext createContext(android.opengl.EGLConfig config);
     method public android.opengl.EGLSurface getCurrentDrawSurface();
diff --git a/graphics/graphics-core/build.gradle b/graphics/graphics-core/build.gradle
index 4860a59..79c217f 100644
--- a/graphics/graphics-core/build.gradle
+++ b/graphics/graphics-core/build.gradle
@@ -39,12 +39,13 @@
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.testRules)
-    androidTestImplementation("androidx.lifecycle:lifecycle-common:2.5.0-alpha01")
+    androidTestImplementation("androidx.lifecycle:lifecycle-common:2.8.3")
     androidTestImplementation("androidx.test:core:1.4.0@aar")
-    androidTestImplementation(project(":core:core"))
 }
 
 android {
+    compileSdk 35
+
     namespace 'androidx.graphics.core'
 
     defaultConfig {
@@ -56,7 +57,8 @@
                         "-fomit-frame-pointer", "-fdata-sections", "-ffunction-sections", "-fvisibility=hidden"
                 arguments "-DCMAKE_VERBOSE_MAKEFILE=ON",
                         "-DCMAKE_SHARED_LINKER_FLAGS=-Wl,--gc-sections " +
-                                "-Wl,--version-script=${versionScript}"
+                                "-Wl,--version-script=${versionScript} " +
+                                "-Wl,--undefined-version"
             }
         }
         consumerProguardFiles 'proguard-rules.pro'
@@ -75,7 +77,6 @@
     mavenVersion = LibraryVersions.GRAPHICS_CORE
     inceptionYear = "2021"
     description = "Leverage graphics facilities across multiple Android platform releases"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":graphics:graphics-core:graphics-core-samples"))
 }
diff --git a/graphics/graphics-core/samples/build.gradle b/graphics/graphics-core/samples/build.gradle
index fadc1fb..c63cdc2 100644
--- a/graphics/graphics-core/samples/build.gradle
+++ b/graphics/graphics-core/samples/build.gradle
@@ -27,10 +27,11 @@
 
     compileOnly project(":annotation:annotation-sampled")
     implementation project(":graphics:graphics-core")
-    implementation "androidx.annotation:annotation:1.7.0"
+    implementation "androidx.annotation:annotation:1.8.1"
 }
 
 android {
+    compileSdk 35
     namespace "androidx.graphics.core.samples"
 }
 
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
index d67e8df..cbb00c6 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
@@ -55,6 +55,7 @@
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertSame
 import org.junit.Assert.assertThrows
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
@@ -2388,6 +2389,42 @@
         assertEquals(listOf("FIRST", "SECOND"), frontBufferDraws.toList())
     }
 
+    @Test
+    fun multiBufferedLayerDrawDoesNotAllocateEmptySegment() {
+        val callbacks =
+            object : GLFrontBufferedRenderer.Callback<String> {
+
+                override fun onDrawFrontBufferedLayer(
+                    eglManager: EGLManager,
+                    width: Int,
+                    height: Int,
+                    bufferInfo: BufferInfo,
+                    transform: FloatArray,
+                    param: String
+                ) {}
+
+                override fun onDrawMultiBufferedLayer(
+                    eglManager: EGLManager,
+                    width: Int,
+                    height: Int,
+                    bufferInfo: BufferInfo,
+                    transform: FloatArray,
+                    params: Collection<String>
+                ) {
+                    // emptyList always returns the same singleton instance, which we use both for
+                    // empty commits and any time a multi-layer draw is implcitly requested with no
+                    // active segment in the queue (e.g. on surface creation).
+                    assertSame(emptyList<String>(), params)
+                }
+            }
+        verifyGLFrontBufferedRenderer(
+            callbacks,
+        ) { _, renderer, _ ->
+            // commit with the current segment empty (no front draws).
+            renderer.commit()
+        }
+    }
+
     @RequiresApi(Build.VERSION_CODES.Q)
     private fun GLFrontBufferedRenderer<*>?.blockingRelease(timeoutMillis: Long = 3000) {
         if (this != null) {
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
index c9717db..966c457 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
@@ -32,6 +32,7 @@
 import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
 import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
 import org.junit.Assert
 
@@ -111,7 +112,48 @@
             }
         }
 
+        private fun flushSurfaceFlinger() {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                var commitLatch: CountDownLatch? = null
+                // Android S only requires 1 additional transaction
+                val maxTransactions =
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                        1
+                    } else {
+                        3
+                    }
+                for (i in 0 until maxTransactions) {
+                    val transaction = SurfaceControlCompat.Transaction()
+                    // CommittedListener only added on Android S
+                    if (
+                        i == maxTransactions - 1 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+                    ) {
+                        commitLatch = CountDownLatch(1)
+                        val executor = Executors.newSingleThreadExecutor()
+                        transaction.addTransactionCommittedListener(
+                            executor,
+                            object : SurfaceControlCompat.TransactionCommittedListener {
+                                override fun onTransactionCommitted() {
+                                    executor.shutdownNow()
+                                    commitLatch.countDown()
+                                }
+                            }
+                        )
+                    }
+                    transaction.commit()
+                }
+
+                if (commitLatch != null) {
+                    commitLatch.await(3000, TimeUnit.MILLISECONDS)
+                } else {
+                    // Wait for transactions to flush
+                    SystemClock.sleep(maxTransactions * 16L)
+                }
+            }
+        }
+
         fun validateOutput(block: (bitmap: Bitmap) -> Boolean) {
+            flushSurfaceFlinger()
             var sleepDurationMillis = 1000L
             var success = false
             for (i in 0..3) {
diff --git a/graphics/graphics-core/src/main/cpp/graphics-core.cpp b/graphics/graphics-core/src/main/cpp/graphics-core.cpp
index 632d647..bf4633b 100644
--- a/graphics/graphics-core/src/main/cpp/graphics-core.cpp
+++ b/graphics/graphics-core/src/main/cpp/graphics-core.cpp
@@ -309,7 +309,8 @@
     if (android_get_device_api_level() >= 29) {
         auto st = reinterpret_cast<ASurfaceTransaction *>(surfaceTransaction);
         auto sc = reinterpret_cast<ASurfaceControl *>(surfaceControl);
-        ASurfaceTransaction_setVisibility(st, sc, jVisibility);
+        auto stv = static_cast<ASurfaceTransactionVisibility>(jVisibility);
+        ASurfaceTransaction_setVisibility(st, sc, stv);
     }
 }
 
@@ -368,7 +369,7 @@
         ASurfaceTransaction_setBufferTransparency(
                 reinterpret_cast<ASurfaceTransaction *>(surfaceTransaction),
                 reinterpret_cast<ASurfaceControl *>(surfaceControl),
-                transparency);
+                static_cast<ASurfaceTransactionTransparency>(transparency));
     }
 }
 
@@ -694,4 +695,4 @@
     }
 
     return JNI_VERSION_1_6;
-}
\ No newline at end of file
+}
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt b/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt
index fb26f51..b13b6da 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt
@@ -393,11 +393,9 @@
     companion object {
 
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        @androidx.annotation.DoNotInline
         fun getFence(image: Image): SyncFenceCompat = SyncFenceCompat(image.fence)
 
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        @androidx.annotation.DoNotInline
         fun setFence(image: Image, fence: SyncFenceCompat?) {
             if (fence != null && fence.mImpl is SyncFenceV33) {
                 image.fence = fence.mImpl.mSyncFence
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/BufferTransformHintResolver.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/BufferTransformHintResolver.kt
index 8cfaf8f..b4f2ab5 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/BufferTransformHintResolver.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/BufferTransformHintResolver.kt
@@ -151,7 +151,6 @@
 
     companion object {
         @RequiresApi(Build.VERSION_CODES.S_V2)
-        @androidx.annotation.DoNotInline
         fun resolveBufferTransformHint(view: View): Int =
             view.rootSurfaceControl?.bufferTransformHint ?: 0
     }
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt
index 7e9b9d7..8d031b0 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrontBufferUtils.kt
@@ -85,7 +85,6 @@
         // developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_usageflags
         @SuppressLint("WrongConstant")
         @RequiresApi(Build.VERSION_CODES.Q)
-        @androidx.annotation.DoNotInline
         internal fun isSupported(flag: Long): Boolean =
             HardwareBuffer.isSupported(
                 1, // width
@@ -96,7 +95,6 @@
             )
 
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        @androidx.annotation.DoNotInline
         fun obtainUsageFlagsV33(): Long {
             // First verify if the front buffer usage flag is supported along with the
             // "usage composer overlay" flag that was introduced in API level 33
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
index f2e937b..2fd880c 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
@@ -37,7 +37,6 @@
 import androidx.hardware.SyncFenceCompat
 import androidx.opengl.EGLExt.Companion.EGL_ANDROID_NATIVE_FENCE_SYNC
 import androidx.opengl.EGLExt.Companion.EGL_KHR_FENCE_SYNC
-import java.util.Collections
 import java.util.concurrent.ConcurrentLinkedQueue
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.atomic.AtomicBoolean
@@ -210,7 +209,7 @@
                     height,
                     bufferInfo,
                     transform,
-                    mSegments.poll() ?: Collections.emptyList()
+                    mSegments.poll() ?: emptyList()
                 )
             }
 
@@ -617,7 +616,7 @@
     private fun commitInternal() {
         if (isValid()) {
             mPendingRenderCount.set(0)
-            mSegments.add(mActiveSegment.release())
+            mSegments.add(if (mActiveSegment.isEmpty()) emptyList() else mActiveSegment.release())
             mMultiBufferedRenderer?.render()
         } else {
             Log.w(
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt
index fb10a42..d56f4c3 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt
@@ -678,7 +678,6 @@
     companion object {
 
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        @androidx.annotation.DoNotInline
         fun getColorSpaceFromDataSpace(dataSpace: Int) =
             ColorSpace.getFromDataSpace(dataSpace)
                 // If wide color gamut is supported, then this should always return non-null
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
index 2bee850..b06c22c 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
@@ -685,19 +685,15 @@
 
     companion object {
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        @androidx.annotation.DoNotInline
         fun createBuilderV33(): SurfaceControlImpl.Builder = SurfaceControlV33.Builder()
 
         @RequiresApi(Build.VERSION_CODES.Q)
-        @androidx.annotation.DoNotInline
         fun createBuilderV29(): SurfaceControlImpl.Builder = SurfaceControlV29.Builder()
 
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        @androidx.annotation.DoNotInline
         fun createTransactionV33(): SurfaceControlImpl.Transaction = SurfaceControlV33.Transaction()
 
         @RequiresApi(Build.VERSION_CODES.Q)
-        @androidx.annotation.DoNotInline
         fun createTransactionV29(): SurfaceControlImpl.Transaction = SurfaceControlV29.Transaction()
     }
 }
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt
index 9cc7d50..bfab6d3 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlV33.kt
@@ -331,7 +331,6 @@
 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 private object SurfaceControlTransactionVerificationHelperV34 {
 
-    @androidx.annotation.DoNotInline
     fun setExtendedRangeBrightness(
         transaction: Transaction,
         surfaceControl: SurfaceControl,
@@ -345,7 +344,6 @@
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 private object SurfaceControlTransactionVerificationHelperV33 {
 
-    @androidx.annotation.DoNotInline
     fun setDataSpace(transaction: Transaction, surfaceControl: SurfaceControl, dataspace: Int) {
         transaction.setDataSpace(surfaceControl, dataspace)
     }
@@ -353,7 +351,6 @@
 
 @RequiresApi(Build.VERSION_CODES.S)
 private object SurfaceControlVerificationHelperV31 {
-    @androidx.annotation.DoNotInline
     fun setFrameRate(
         transaction: Transaction,
         surfaceControl: SurfaceControl,
@@ -367,7 +364,6 @@
 
 @RequiresApi(Build.VERSION_CODES.R)
 private object SurfaceControlVerificationHelperV30 {
-    @androidx.annotation.DoNotInline
     fun setFrameRate(
         transaction: Transaction,
         surfaceControl: SurfaceControl,
@@ -381,7 +377,6 @@
 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 private object SurfaceControlVerificationHelperV34 {
 
-    @androidx.annotation.DoNotInline
     fun clearFrameRate(transaction: Transaction, surfaceControl: SurfaceControl) {
         transaction.clearFrameRate(surfaceControl)
     }
diff --git a/graphics/graphics-core/src/main/java/androidx/hardware/FileDescriptorMonitor.kt b/graphics/graphics-core/src/main/java/androidx/hardware/FileDescriptorMonitor.kt
index 9ee7291..1fea0c7 100644
--- a/graphics/graphics-core/src/main/java/androidx/hardware/FileDescriptorMonitor.kt
+++ b/graphics/graphics-core/src/main/java/androidx/hardware/FileDescriptorMonitor.kt
@@ -22,6 +22,7 @@
 import java.lang.NumberFormatException
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.atomic.AtomicBoolean
+import kotlin.collections.removeLast as removeLastKt
 
 /**
  * Class to monitor open file descriptors and clean up those with fences that have already signalled
@@ -51,7 +52,7 @@
     private fun closePendingFileDescriptors() {
         pendingFileDescriptors.sortByDescending { fdSignalTimePair -> fdSignalTimePair.signalTime }
         while (pendingFileDescriptors.size > MAX_FD) {
-            val fdSignalPair = pendingFileDescriptors.removeLast()
+            val fdSignalPair = pendingFileDescriptors.removeLastKt()
             try {
                 val fd = fdSignalPair.fd
                 // Re-query the signal time in case the fd was re-used
diff --git a/graphics/graphics-core/src/main/java/androidx/hardware/SyncFenceCompat.kt b/graphics/graphics-core/src/main/java/androidx/hardware/SyncFenceCompat.kt
index 18b1b3d..4adb250 100644
--- a/graphics/graphics-core/src/main/java/androidx/hardware/SyncFenceCompat.kt
+++ b/graphics/graphics-core/src/main/java/androidx/hardware/SyncFenceCompat.kt
@@ -130,7 +130,6 @@
         private val mEmptyAttributes = longArrayOf(EGL14.EGL_NONE.toLong())
 
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-        @androidx.annotation.DoNotInline
         fun createSyncFenceCompatV33(): SyncFenceCompat {
             val display = EGL14.eglGetCurrentDisplay()
             if (display == EGL15.EGL_NO_DISPLAY) {
diff --git a/graphics/graphics-path/build.gradle b/graphics/graphics-path/build.gradle
index e0bd680..70f190e 100644
--- a/graphics/graphics-path/build.gradle
+++ b/graphics/graphics-path/build.gradle
@@ -34,7 +34,7 @@
 
     implementation('androidx.core:core:1.12.0')
 
-    androidTestImplementation("androidx.annotation:annotation:1.7.0")
+    androidTestImplementation("androidx.annotation:annotation:1.8.1")
     androidTestImplementation("androidx.core:core-ktx:1.12.0")
     androidTestImplementation("androidx.test:core:1.5.0@aar")
     androidTestImplementation(libs.testExtJunit)
@@ -88,6 +88,5 @@
     mavenVersion = LibraryVersions.GRAPHICS_PATH
     inceptionYear = "2022"
     description = "Query segment data for android.graphics.Path objects"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/graphics/graphics-shapes/api/current.ignore b/graphics/graphics-shapes/api/current.ignore
new file mode 100644
index 0000000..8d47252
--- /dev/null
+++ b/graphics/graphics-shapes/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AddedMethod: androidx.graphics.shapes.CornerRounding#CornerRounding():
+    Added constructor androidx.graphics.shapes.CornerRounding()
diff --git a/graphics/graphics-shapes/api/current.txt b/graphics/graphics-shapes/api/current.txt
index f9d62d6f..48e9617 100644
--- a/graphics/graphics-shapes/api/current.txt
+++ b/graphics/graphics-shapes/api/current.txt
@@ -2,6 +2,7 @@
 package androidx.graphics.shapes {
 
   public final class CornerRounding {
+    ctor public CornerRounding();
     ctor public CornerRounding(optional @FloatRange(from=0.0) float radius, optional @FloatRange(from=0.0, to=1.0) float smoothing);
     method public float getRadius();
     method public float getSmoothing();
diff --git a/graphics/graphics-shapes/api/restricted_current.ignore b/graphics/graphics-shapes/api/restricted_current.ignore
new file mode 100644
index 0000000..8d47252
--- /dev/null
+++ b/graphics/graphics-shapes/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AddedMethod: androidx.graphics.shapes.CornerRounding#CornerRounding():
+    Added constructor androidx.graphics.shapes.CornerRounding()
diff --git a/graphics/graphics-shapes/api/restricted_current.txt b/graphics/graphics-shapes/api/restricted_current.txt
index d691f29..795add9 100644
--- a/graphics/graphics-shapes/api/restricted_current.txt
+++ b/graphics/graphics-shapes/api/restricted_current.txt
@@ -2,6 +2,7 @@
 package androidx.graphics.shapes {
 
   public final class CornerRounding {
+    ctor public CornerRounding();
     ctor public CornerRounding(optional @FloatRange(from=0.0) float radius, optional @FloatRange(from=0.0, to=1.0) float smoothing);
     method public float getRadius();
     method public float getSmoothing();
diff --git a/graphics/graphics-shapes/bcv/native/current.txt b/graphics/graphics-shapes/bcv/native/current.txt
new file mode 100644
index 0000000..dd8ee15
--- /dev/null
+++ b/graphics/graphics-shapes/bcv/native/current.txt
@@ -0,0 +1,102 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.graphics:graphics-shapes>
+abstract fun interface androidx.graphics.shapes/PointTransformer { // androidx.graphics.shapes/PointTransformer|null[0]
+    abstract fun transform(kotlin/Float, kotlin/Float): androidx.collection/FloatFloatPair // androidx.graphics.shapes/PointTransformer.transform|transform(kotlin.Float;kotlin.Float){}[0]
+}
+abstract interface androidx.graphics.shapes/MutablePoint { // androidx.graphics.shapes/MutablePoint|null[0]
+    abstract var x // androidx.graphics.shapes/MutablePoint.x|{}x[0]
+        abstract fun <get-x>(): kotlin/Float // androidx.graphics.shapes/MutablePoint.x.<get-x>|<get-x>(){}[0]
+        abstract fun <set-x>(kotlin/Float) // androidx.graphics.shapes/MutablePoint.x.<set-x>|<set-x>(kotlin.Float){}[0]
+    abstract var y // androidx.graphics.shapes/MutablePoint.y|{}y[0]
+        abstract fun <get-y>(): kotlin/Float // androidx.graphics.shapes/MutablePoint.y.<get-y>|<get-y>(){}[0]
+        abstract fun <set-y>(kotlin/Float) // androidx.graphics.shapes/MutablePoint.y.<set-y>|<set-y>(kotlin.Float){}[0]
+}
+final class androidx.graphics.shapes/CornerRounding { // androidx.graphics.shapes/CornerRounding|null[0]
+    constructor <init>(kotlin/Float =..., kotlin/Float =...) // androidx.graphics.shapes/CornerRounding.<init>|<init>(kotlin.Float;kotlin.Float){}[0]
+    final object Companion { // androidx.graphics.shapes/CornerRounding.Companion|null[0]
+        final val Unrounded // androidx.graphics.shapes/CornerRounding.Companion.Unrounded|{}Unrounded[0]
+            final fun <get-Unrounded>(): androidx.graphics.shapes/CornerRounding // androidx.graphics.shapes/CornerRounding.Companion.Unrounded.<get-Unrounded>|<get-Unrounded>(){}[0]
+    }
+    final val radius // androidx.graphics.shapes/CornerRounding.radius|{}radius[0]
+        final fun <get-radius>(): kotlin/Float // androidx.graphics.shapes/CornerRounding.radius.<get-radius>|<get-radius>(){}[0]
+    final val smoothing // androidx.graphics.shapes/CornerRounding.smoothing|{}smoothing[0]
+        final fun <get-smoothing>(): kotlin/Float // androidx.graphics.shapes/CornerRounding.smoothing.<get-smoothing>|<get-smoothing>(){}[0]
+}
+final class androidx.graphics.shapes/Morph { // androidx.graphics.shapes/Morph|null[0]
+    constructor <init>(androidx.graphics.shapes/RoundedPolygon, androidx.graphics.shapes/RoundedPolygon) // androidx.graphics.shapes/Morph.<init>|<init>(androidx.graphics.shapes.RoundedPolygon;androidx.graphics.shapes.RoundedPolygon){}[0]
+    final fun asCubics(kotlin/Float): kotlin.collections/List<androidx.graphics.shapes/Cubic> // androidx.graphics.shapes/Morph.asCubics|asCubics(kotlin.Float){}[0]
+    final fun calculateBounds(kotlin/FloatArray =..., kotlin/Boolean =...): kotlin/FloatArray // androidx.graphics.shapes/Morph.calculateBounds|calculateBounds(kotlin.FloatArray;kotlin.Boolean){}[0]
+    final fun calculateMaxBounds(kotlin/FloatArray =...): kotlin/FloatArray // androidx.graphics.shapes/Morph.calculateMaxBounds|calculateMaxBounds(kotlin.FloatArray){}[0]
+    final inline fun forEachCubic(kotlin/Float, androidx.graphics.shapes/MutableCubic =..., kotlin/Function1<androidx.graphics.shapes/MutableCubic, kotlin/Unit>) // androidx.graphics.shapes/Morph.forEachCubic|forEachCubic(kotlin.Float;androidx.graphics.shapes.MutableCubic;kotlin.Function1<androidx.graphics.shapes.MutableCubic,kotlin.Unit>){}[0]
+    final val morphMatch // androidx.graphics.shapes/Morph.morphMatch|{}morphMatch[0]
+        final fun <get-morphMatch>(): kotlin.collections/List<kotlin/Pair<androidx.graphics.shapes/Cubic, androidx.graphics.shapes/Cubic>> // androidx.graphics.shapes/Morph.morphMatch.<get-morphMatch>|<get-morphMatch>(){}[0]
+}
+final class androidx.graphics.shapes/MutableCubic : androidx.graphics.shapes/Cubic { // androidx.graphics.shapes/MutableCubic|null[0]
+    constructor <init>() // androidx.graphics.shapes/MutableCubic.<init>|<init>(){}[0]
+    final fun interpolate(androidx.graphics.shapes/Cubic, androidx.graphics.shapes/Cubic, kotlin/Float) // androidx.graphics.shapes/MutableCubic.interpolate|interpolate(androidx.graphics.shapes.Cubic;androidx.graphics.shapes.Cubic;kotlin.Float){}[0]
+    final fun transform(androidx.graphics.shapes/PointTransformer) // androidx.graphics.shapes/MutableCubic.transform|transform(androidx.graphics.shapes.PointTransformer){}[0]
+}
+final class androidx.graphics.shapes/RoundedPolygon { // androidx.graphics.shapes/RoundedPolygon|null[0]
+    final fun calculateBounds(kotlin/FloatArray =..., kotlin/Boolean =...): kotlin/FloatArray // androidx.graphics.shapes/RoundedPolygon.calculateBounds|calculateBounds(kotlin.FloatArray;kotlin.Boolean){}[0]
+    final fun calculateMaxBounds(kotlin/FloatArray =...): kotlin/FloatArray // androidx.graphics.shapes/RoundedPolygon.calculateMaxBounds|calculateMaxBounds(kotlin.FloatArray){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // androidx.graphics.shapes/RoundedPolygon.equals|equals(kotlin.Any?){}[0]
+    final fun hashCode(): kotlin/Int // androidx.graphics.shapes/RoundedPolygon.hashCode|hashCode(){}[0]
+    final fun normalized(): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/RoundedPolygon.normalized|normalized(){}[0]
+    final fun toString(): kotlin/String // androidx.graphics.shapes/RoundedPolygon.toString|toString(){}[0]
+    final fun transformed(androidx.graphics.shapes/PointTransformer): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/RoundedPolygon.transformed|transformed(androidx.graphics.shapes.PointTransformer){}[0]
+    final object Companion // androidx.graphics.shapes/RoundedPolygon.Companion|null[0]
+    final val centerX // androidx.graphics.shapes/RoundedPolygon.centerX|{}centerX[0]
+        final fun <get-centerX>(): kotlin/Float // androidx.graphics.shapes/RoundedPolygon.centerX.<get-centerX>|<get-centerX>(){}[0]
+    final val centerY // androidx.graphics.shapes/RoundedPolygon.centerY|{}centerY[0]
+        final fun <get-centerY>(): kotlin/Float // androidx.graphics.shapes/RoundedPolygon.centerY.<get-centerY>|<get-centerY>(){}[0]
+    final val cubics // androidx.graphics.shapes/RoundedPolygon.cubics|{}cubics[0]
+        final fun <get-cubics>(): kotlin.collections/List<androidx.graphics.shapes/Cubic> // androidx.graphics.shapes/RoundedPolygon.cubics.<get-cubics>|<get-cubics>(){}[0]
+}
+final fun (androidx.graphics.shapes/RoundedPolygon.Companion).androidx.graphics.shapes/circle(kotlin/Int =..., kotlin/Float =..., kotlin/Float =..., kotlin/Float =...): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/circle|[email protected](kotlin.Int;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun (androidx.graphics.shapes/RoundedPolygon.Companion).androidx.graphics.shapes/pill(kotlin/Float =..., kotlin/Float =..., kotlin/Float =..., kotlin/Float =..., kotlin/Float =...): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/pill|[email protected](kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun (androidx.graphics.shapes/RoundedPolygon.Companion).androidx.graphics.shapes/pillStar(kotlin/Float =..., kotlin/Float =..., kotlin/Int =..., kotlin/Float =..., androidx.graphics.shapes/CornerRounding =..., androidx.graphics.shapes/CornerRounding? =..., kotlin.collections/List<androidx.graphics.shapes/CornerRounding>? =..., kotlin/Float =..., kotlin/Float =..., kotlin/Float =..., kotlin/Float =...): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/pillStar|[email protected](kotlin.Float;kotlin.Float;kotlin.Int;kotlin.Float;androidx.graphics.shapes.CornerRounding;androidx.graphics.shapes.CornerRounding?;kotlin.collections.List<androidx.graphics.shapes.CornerRounding>?;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun (androidx.graphics.shapes/RoundedPolygon.Companion).androidx.graphics.shapes/rectangle(kotlin/Float =..., kotlin/Float =..., androidx.graphics.shapes/CornerRounding =..., kotlin.collections/List<androidx.graphics.shapes/CornerRounding>? =..., kotlin/Float =..., kotlin/Float =...): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/rectangle|[email protected](kotlin.Float;kotlin.Float;androidx.graphics.shapes.CornerRounding;kotlin.collections.List<androidx.graphics.shapes.CornerRounding>?;kotlin.Float;kotlin.Float){}[0]
+final fun (androidx.graphics.shapes/RoundedPolygon.Companion).androidx.graphics.shapes/star(kotlin/Int, kotlin/Float =..., kotlin/Float =..., androidx.graphics.shapes/CornerRounding =..., androidx.graphics.shapes/CornerRounding? =..., kotlin.collections/List<androidx.graphics.shapes/CornerRounding>? =..., kotlin/Float =..., kotlin/Float =...): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/star|[email protected](kotlin.Int;kotlin.Float;kotlin.Float;androidx.graphics.shapes.CornerRounding;androidx.graphics.shapes.CornerRounding?;kotlin.collections.List<androidx.graphics.shapes.CornerRounding>?;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.graphics.shapes/Cubic(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic|Cubic(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.graphics.shapes/RoundedPolygon(androidx.graphics.shapes/RoundedPolygon): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/RoundedPolygon|RoundedPolygon(androidx.graphics.shapes.RoundedPolygon){}[0]
+final fun androidx.graphics.shapes/RoundedPolygon(kotlin/FloatArray, androidx.graphics.shapes/CornerRounding =..., kotlin.collections/List<androidx.graphics.shapes/CornerRounding>? =..., kotlin/Float =..., kotlin/Float =...): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/RoundedPolygon|RoundedPolygon(kotlin.FloatArray;androidx.graphics.shapes.CornerRounding;kotlin.collections.List<androidx.graphics.shapes.CornerRounding>?;kotlin.Float;kotlin.Float){}[0]
+final fun androidx.graphics.shapes/RoundedPolygon(kotlin/Int, kotlin/Float =..., kotlin/Float =..., kotlin/Float =..., androidx.graphics.shapes/CornerRounding =..., kotlin.collections/List<androidx.graphics.shapes/CornerRounding>? =...): androidx.graphics.shapes/RoundedPolygon // androidx.graphics.shapes/RoundedPolygon|RoundedPolygon(kotlin.Int;kotlin.Float;kotlin.Float;kotlin.Float;androidx.graphics.shapes.CornerRounding;kotlin.collections.List<androidx.graphics.shapes.CornerRounding>?){}[0]
+open class androidx.graphics.shapes/Cubic { // androidx.graphics.shapes/Cubic|null[0]
+    final fun div(kotlin/Float): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.div|div(kotlin.Float){}[0]
+    final fun div(kotlin/Int): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.div|div(kotlin.Int){}[0]
+    final fun plus(androidx.graphics.shapes/Cubic): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.plus|plus(androidx.graphics.shapes.Cubic){}[0]
+    final fun reverse(): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.reverse|reverse(){}[0]
+    final fun split(kotlin/Float): kotlin/Pair<androidx.graphics.shapes/Cubic, androidx.graphics.shapes/Cubic> // androidx.graphics.shapes/Cubic.split|split(kotlin.Float){}[0]
+    final fun times(kotlin/Float): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.times|times(kotlin.Float){}[0]
+    final fun times(kotlin/Int): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.times|times(kotlin.Int){}[0]
+    final fun transformed(androidx.graphics.shapes/PointTransformer): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.transformed|transformed(androidx.graphics.shapes.PointTransformer){}[0]
+    final object Companion { // androidx.graphics.shapes/Cubic.Companion|null[0]
+        final fun circularArc(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.Companion.circularArc|circularArc(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+        final fun straightLine(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float): androidx.graphics.shapes/Cubic // androidx.graphics.shapes/Cubic.Companion.straightLine|straightLine(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
+    }
+    final val anchor0X // androidx.graphics.shapes/Cubic.anchor0X|{}anchor0X[0]
+        final fun <get-anchor0X>(): kotlin/Float // androidx.graphics.shapes/Cubic.anchor0X.<get-anchor0X>|<get-anchor0X>(){}[0]
+    final val anchor0Y // androidx.graphics.shapes/Cubic.anchor0Y|{}anchor0Y[0]
+        final fun <get-anchor0Y>(): kotlin/Float // androidx.graphics.shapes/Cubic.anchor0Y.<get-anchor0Y>|<get-anchor0Y>(){}[0]
+    final val anchor1X // androidx.graphics.shapes/Cubic.anchor1X|{}anchor1X[0]
+        final fun <get-anchor1X>(): kotlin/Float // androidx.graphics.shapes/Cubic.anchor1X.<get-anchor1X>|<get-anchor1X>(){}[0]
+    final val anchor1Y // androidx.graphics.shapes/Cubic.anchor1Y|{}anchor1Y[0]
+        final fun <get-anchor1Y>(): kotlin/Float // androidx.graphics.shapes/Cubic.anchor1Y.<get-anchor1Y>|<get-anchor1Y>(){}[0]
+    final val control0X // androidx.graphics.shapes/Cubic.control0X|{}control0X[0]
+        final fun <get-control0X>(): kotlin/Float // androidx.graphics.shapes/Cubic.control0X.<get-control0X>|<get-control0X>(){}[0]
+    final val control0Y // androidx.graphics.shapes/Cubic.control0Y|{}control0Y[0]
+        final fun <get-control0Y>(): kotlin/Float // androidx.graphics.shapes/Cubic.control0Y.<get-control0Y>|<get-control0Y>(){}[0]
+    final val control1X // androidx.graphics.shapes/Cubic.control1X|{}control1X[0]
+        final fun <get-control1X>(): kotlin/Float // androidx.graphics.shapes/Cubic.control1X.<get-control1X>|<get-control1X>(){}[0]
+    final val control1Y // androidx.graphics.shapes/Cubic.control1Y|{}control1Y[0]
+        final fun <get-control1Y>(): kotlin/Float // androidx.graphics.shapes/Cubic.control1Y.<get-control1Y>|<get-control1Y>(){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.graphics.shapes/Cubic.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.graphics.shapes/Cubic.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.graphics.shapes/Cubic.toString|toString(){}[0]
+}
diff --git a/graphics/graphics-shapes/build.gradle b/graphics/graphics-shapes/build.gradle
index a253ff9..7212f00 100644
--- a/graphics/graphics-shapes/build.gradle
+++ b/graphics/graphics-shapes/build.gradle
@@ -33,20 +33,20 @@
 androidXMultiplatform {
     android()
     desktop()
+    linux()
+    ios()
+    watchos()
+    tvos()
+    mac()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
 
     sourceSets {
-        configureEach {
-            languageSettings.optIn("kotlin.RequiresOptIn")
-            languageSettings.optIn("kotlin.contracts.ExperimentalContracts")
-        }
-
         commonMain {
             dependencies {
-                implementation(libs.kotlinStdlibCommon)
-                implementation("androidx.collection:collection:1.4.0")
-                implementation("androidx.annotation:annotation:1.1.0")
+                implementation(libs.kotlinStdlib)
+                implementation("androidx.collection:collection:1.4.2")
+                implementation("androidx.annotation:annotation:1.8.1")
             }
         }
 
@@ -61,12 +61,6 @@
             }
         }
 
-        skikoMain {
-            dependsOn(commonMain)
-            dependencies {
-            }
-        }
-
         androidMain {
             dependsOn(jvmMain)
             dependencies {
@@ -76,31 +70,15 @@
         }
 
         desktopMain {
-            dependsOn(skikoMain)
             dependsOn(jvmMain)
-            dependencies {
-                implementation(libs.kotlinStdlib)
-            }
         }
 
-        jvmTest {
-            dependsOn(commonTest)
-            dependencies {
-            }
-        }
-
-        androidUnitTest {
-            dependsOn(jvmTest)
-            dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.truth)
-            }
+        nativeMain {
+            dependsOn(commonMain)
         }
 
         androidInstrumentedTest {
-            dependsOn(jvmTest)
+            dependsOn(commonTest)
             dependencies {
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
@@ -111,12 +89,11 @@
             }
         }
 
-        desktopTest {
-            dependsOn(jvmTest)
-            dependencies {
-                implementation(libs.truth)
-                implementation(libs.junit)
-                implementation(libs.skikoCurrentOs)
+        targets.configureEach { target ->
+            if (target.platformType == org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.native) {
+                target.compilations["main"].defaultSourceSet {
+                    dependsOn(nativeMain)
+                }
             }
         }
     }
@@ -133,4 +110,5 @@
     inceptionYear = "2022"
     description = "create and render rounded polygonal shapes"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/RoundedPolygonTest.kt b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/RoundedPolygonTest.kt
index bd82201..6109212 100644
--- a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/RoundedPolygonTest.kt
+++ b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/RoundedPolygonTest.kt
@@ -97,6 +97,14 @@
         assertInBounds(manualSquarePVRounded.cubics, min, max)
     }
 
+    @Test
+    fun computeCenterTest() {
+        val polygon = RoundedPolygon(floatArrayOf(0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f))
+
+        assertEquals(0.5f, polygon.centerX, 1e-4f)
+        assertEquals(0.5f, polygon.centerY, 1e-4f)
+    }
+
     private fun pointsToFloats(points: List<Point>): FloatArray {
         val result = FloatArray(points.size * 2)
         var index = 0
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/CornerRounding.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/CornerRounding.kt
index 09371bc..da70552 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/CornerRounding.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/CornerRounding.kt
@@ -17,6 +17,7 @@
 package androidx.graphics.shapes
 
 import androidx.annotation.FloatRange
+import kotlin.jvm.JvmField
 
 /**
  * CornerRounding defines the amount and quality around a given vertex of a shape. [radius] defines
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt
index 1cb62ca..b3253c6 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Cubic.kt
@@ -17,6 +17,7 @@
 package androidx.graphics.shapes
 
 import androidx.collection.FloatFloatPair
+import kotlin.jvm.JvmStatic
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FeatureMapping.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FeatureMapping.kt
index 92df080..3ffa13f 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FeatureMapping.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FeatureMapping.kt
@@ -67,9 +67,11 @@
         debugLog(LOG_TAG) {
             val N = 10
             "Map: " +
-                (0..N).joinToString { i -> "%.3f".format(dm.map(i.toFloat() / N)) } +
+                (0..N).joinToString { i -> (dm.map(i.toFloat() / N)).toStringWithLessPrecision() } +
                 "\nMb : " +
-                (0..N).joinToString { i -> "%.3f".format(dm.mapBack(i.toFloat() / N)) }
+                (0..N).joinToString { i ->
+                    (dm.mapBack(i.toFloat() / N)).toStringWithLessPrecision()
+                }
         }
     }
 }
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FloatMapping.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FloatMapping.kt
index a5f6af7..b79a12a 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FloatMapping.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/FloatMapping.kt
@@ -18,6 +18,7 @@
 
 import androidx.collection.FloatList
 import androidx.collection.MutableFloatList
+import kotlin.jvm.JvmField
 
 /**
  * Checks if the given progress is in the given progress range, since progress is in the [0..1)
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Format.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Format.kt
new file mode 100644
index 0000000..bc012848
--- /dev/null
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Format.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.graphics.shapes
+
+internal expect fun Float.toStringWithLessPrecision(): String
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt
index 3896690..f052bcd 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Morph.kt
@@ -16,6 +16,8 @@
 
 package androidx.graphics.shapes
 
+import kotlin.jvm.JvmOverloads
+import kotlin.jvm.JvmStatic
 import kotlin.math.max
 import kotlin.math.min
 
@@ -310,7 +312,9 @@
 
             if (DEBUG) {
                 // Export as SVG path.
-                val showPoint: (Point) -> String = { "%.3f %.3f".format(it.x * 100, it.y * 100) }
+                val showPoint: (Point) -> String = {
+                    "${(it.x * 100).toStringWithLessPrecision()} ${(it.y * 100).toStringWithLessPrecision()}"
+                }
                 repeat(2) { listIx ->
                     val points = ret.map { if (listIx == 0) it.first else it.second }
                     debugLog(LOG_TAG) {
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
index b0a7e59..b2fc8f2 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
@@ -18,6 +18,7 @@
 
 import androidx.annotation.IntRange
 import androidx.collection.MutableFloatList
+import kotlin.jvm.JvmOverloads
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
@@ -448,7 +449,7 @@
         cumulativeX += vertices[index++]
         cumulativeY += vertices[index++]
     }
-    return Point(cumulativeX / vertices.size / 2, cumulativeY / vertices.size / 2)
+    return Point(cumulativeX / (vertices.size / 2), cumulativeY / (vertices.size / 2))
 }
 
 /**
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Shapes.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Shapes.kt
index 2555d22..fa779d2 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Shapes.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Shapes.kt
@@ -18,6 +18,7 @@
 
 import androidx.annotation.FloatRange
 import androidx.annotation.IntRange
+import kotlin.jvm.JvmOverloads
 import kotlin.math.cos
 import kotlin.math.min
 
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Utils.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Utils.kt
index 7af1e8b..a3692d6 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Utils.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/Utils.kt
@@ -18,6 +18,8 @@
 
 package androidx.graphics.shapes
 
+import kotlin.jvm.JvmName
+import kotlin.math.PI
 import kotlin.math.atan2
 import kotlin.math.cos
 import kotlin.math.sin
@@ -54,9 +56,9 @@
 
 internal val Zero = Point(0f, 0f)
 
-internal val FloatPi = Math.PI.toFloat()
+internal val FloatPi = PI.toFloat()
 
-internal val TwoPi: Float = 2 * Math.PI.toFloat()
+internal val TwoPi: Float = 2 * PI.toFloat()
 
 internal fun square(x: Float) = x * x
 
diff --git a/graphics/graphics-shapes/src/jvmMain/kotlin/androidx/graphics/shapes/Format.jvm.kt b/graphics/graphics-shapes/src/jvmMain/kotlin/androidx/graphics/shapes/Format.jvm.kt
new file mode 100644
index 0000000..2a54bd2
--- /dev/null
+++ b/graphics/graphics-shapes/src/jvmMain/kotlin/androidx/graphics/shapes/Format.jvm.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.graphics.shapes
+
+internal actual fun Float.toStringWithLessPrecision(): String {
+    return "%.3f".format(this)
+}
diff --git a/graphics/graphics-shapes/src/nativeMain/kotlin/androidx/graphics/shapes/Format.native.kt b/graphics/graphics-shapes/src/nativeMain/kotlin/androidx/graphics/shapes/Format.native.kt
new file mode 100644
index 0000000..ada5108
--- /dev/null
+++ b/graphics/graphics-shapes/src/nativeMain/kotlin/androidx/graphics/shapes/Format.native.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.graphics.shapes
+
+internal actual fun Float.toStringWithLessPrecision(): String {
+    return this.toString()
+}
diff --git a/graphics/integration-tests/testapp-compose/build.gradle b/graphics/integration-tests/testapp-compose/build.gradle
index 4ed75df..ee94261 100644
--- a/graphics/integration-tests/testapp-compose/build.gradle
+++ b/graphics/integration-tests/testapp-compose/build.gradle
@@ -49,4 +49,6 @@
 
 android {
     namespace "androidx.graphics.shapes.testcompose"
+    // TODO(b/313699418): need to update compose.runtime version to 1.6.0+
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/gridlayout/gridlayout/build.gradle b/gridlayout/gridlayout/build.gradle
index 9db1edb..95ca125 100644
--- a/gridlayout/gridlayout/build.gradle
+++ b/gridlayout/gridlayout/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.3.0")
 
     androidTestImplementation(libs.testExtJunit)
@@ -29,7 +29,6 @@
     inceptionYear = "2013"
     description = "Android Support Grid Layout"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/health/connect/connect-client/api/current.txt b/health/connect/connect-client/api/current.txt
index 6f9a11e..1d86a0d 100644
--- a/health/connect/connect-client/api/current.txt
+++ b/health/connect/connect-client/api/current.txt
@@ -127,6 +127,7 @@
   }
 
   public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
+    ctor public HealthPermissionsRequestContract();
     ctor public HealthPermissionsRequestContract(optional String providerPackageName);
     method public android.content.Intent createIntent(android.content.Context context, java.util.Set<java.lang.String> input);
     method public java.util.Set<java.lang.String> parseResult(int resultCode, android.content.Intent? intent);
@@ -1114,6 +1115,42 @@
   public static final class SexualActivityRecord.Companion {
   }
 
+  public final class SkinTemperatureRecord implements androidx.health.connect.client.records.Record {
+    ctor public SkinTemperatureRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List<androidx.health.connect.client.records.SkinTemperatureRecord.Delta> deltas, optional androidx.health.connect.client.units.Temperature? baseline, optional int measurementLocation, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    method public androidx.health.connect.client.units.Temperature? getBaseline();
+    method public java.util.List<androidx.health.connect.client.records.SkinTemperatureRecord.Delta> getDeltas();
+    method public java.time.Instant getEndTime();
+    method public java.time.ZoneOffset? getEndZoneOffset();
+    method public int getMeasurementLocation();
+    method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
+    method public java.time.Instant getStartTime();
+    method public java.time.ZoneOffset? getStartZoneOffset();
+    property public final androidx.health.connect.client.units.Temperature? baseline;
+    property public final java.util.List<androidx.health.connect.client.records.SkinTemperatureRecord.Delta> deltas;
+    property public java.time.Instant endTime;
+    property public java.time.ZoneOffset? endZoneOffset;
+    property public final int measurementLocation;
+    property public androidx.health.connect.client.records.metadata.Metadata metadata;
+    property public java.time.Instant startTime;
+    property public java.time.ZoneOffset? startZoneOffset;
+    field public static final androidx.health.connect.client.records.SkinTemperatureRecord.Companion Companion;
+    field public static final int MEASUREMENT_LOCATION_FINGER = 1; // 0x1
+    field public static final int MEASUREMENT_LOCATION_TOE = 2; // 0x2
+    field public static final int MEASUREMENT_LOCATION_UNKNOWN = 0; // 0x0
+    field public static final int MEASUREMENT_LOCATION_WRIST = 3; // 0x3
+  }
+
+  public static final class SkinTemperatureRecord.Companion {
+  }
+
+  public static final class SkinTemperatureRecord.Delta {
+    ctor public SkinTemperatureRecord.Delta(java.time.Instant time, androidx.health.connect.client.units.TemperatureDelta delta);
+    method public androidx.health.connect.client.units.TemperatureDelta getDelta();
+    method public java.time.Instant getTime();
+    property public final androidx.health.connect.client.units.TemperatureDelta delta;
+    property public final java.time.Instant time;
+  }
+
   public final class SleepSessionRecord implements androidx.health.connect.client.records.Record {
     ctor public SleepSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional String? title, optional String? notes, optional java.util.List<androidx.health.connect.client.records.SleepSessionRecord.Stage> stages, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
@@ -1336,6 +1373,7 @@
   }
 
   public final class Device {
+    ctor public Device();
     ctor public Device(optional String? manufacturer, optional String? model, optional int type);
     method public String? getManufacturer();
     method public String? getModel();
@@ -1359,6 +1397,7 @@
   }
 
   public final class Metadata {
+    ctor public Metadata();
     ctor public Metadata(optional String id, optional androidx.health.connect.client.records.metadata.DataOrigin dataOrigin, optional java.time.Instant lastModifiedTime, optional String? clientRecordId, optional long clientRecordVersion, optional androidx.health.connect.client.records.metadata.Device? device, optional int recordingMethod);
     method public String? getClientRecordId();
     method public long getClientRecordVersion();
@@ -1449,6 +1488,7 @@
 package androidx.health.connect.client.time {
 
   public final class TimeRangeFilter {
+    ctor public TimeRangeFilter();
     method public static androidx.health.connect.client.time.TimeRangeFilter after(java.time.Instant startTime);
     method public static androidx.health.connect.client.time.TimeRangeFilter after(java.time.LocalDateTime startTime);
     method public static androidx.health.connect.client.time.TimeRangeFilter before(java.time.Instant endTime);
@@ -1622,6 +1662,22 @@
     method public androidx.health.connect.client.units.Temperature fahrenheit(double value);
   }
 
+  public final class TemperatureDelta implements java.lang.Comparable<androidx.health.connect.client.units.TemperatureDelta> {
+    method public static androidx.health.connect.client.units.TemperatureDelta celsius(double value);
+    method public int compareTo(androidx.health.connect.client.units.TemperatureDelta other);
+    method public static androidx.health.connect.client.units.TemperatureDelta fahrenheit(double value);
+    method public double getCelsius();
+    method public double getFahrenheit();
+    property public final double inCelsius;
+    property public final double inFahrenheit;
+    field public static final androidx.health.connect.client.units.TemperatureDelta.Companion Companion;
+  }
+
+  public static final class TemperatureDelta.Companion {
+    method public androidx.health.connect.client.units.TemperatureDelta celsius(double value);
+    method public androidx.health.connect.client.units.TemperatureDelta fahrenheit(double value);
+  }
+
   public final class Velocity implements java.lang.Comparable<androidx.health.connect.client.units.Velocity> {
     method public int compareTo(androidx.health.connect.client.units.Velocity other);
     method public double getKilometersPerHour();
diff --git a/health/connect/connect-client/api/restricted_current.txt b/health/connect/connect-client/api/restricted_current.txt
index 829b64ab..0942c03 100644
--- a/health/connect/connect-client/api/restricted_current.txt
+++ b/health/connect/connect-client/api/restricted_current.txt
@@ -127,6 +127,7 @@
   }
 
   public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
+    ctor public HealthPermissionsRequestContract();
     ctor public HealthPermissionsRequestContract(optional String providerPackageName);
     method public android.content.Intent createIntent(android.content.Context context, java.util.Set<java.lang.String> input);
     method public java.util.Set<java.lang.String> parseResult(int resultCode, android.content.Intent? intent);
@@ -1137,6 +1138,42 @@
   public static final class SexualActivityRecord.Companion {
   }
 
+  public final class SkinTemperatureRecord implements androidx.health.connect.client.records.IntervalRecord {
+    ctor public SkinTemperatureRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, java.util.List<androidx.health.connect.client.records.SkinTemperatureRecord.Delta> deltas, optional androidx.health.connect.client.units.Temperature? baseline, optional int measurementLocation, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    method public androidx.health.connect.client.units.Temperature? getBaseline();
+    method public java.util.List<androidx.health.connect.client.records.SkinTemperatureRecord.Delta> getDeltas();
+    method public java.time.Instant getEndTime();
+    method public java.time.ZoneOffset? getEndZoneOffset();
+    method public int getMeasurementLocation();
+    method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
+    method public java.time.Instant getStartTime();
+    method public java.time.ZoneOffset? getStartZoneOffset();
+    property public final androidx.health.connect.client.units.Temperature? baseline;
+    property public final java.util.List<androidx.health.connect.client.records.SkinTemperatureRecord.Delta> deltas;
+    property public java.time.Instant endTime;
+    property public java.time.ZoneOffset? endZoneOffset;
+    property public final int measurementLocation;
+    property public androidx.health.connect.client.records.metadata.Metadata metadata;
+    property public java.time.Instant startTime;
+    property public java.time.ZoneOffset? startZoneOffset;
+    field public static final androidx.health.connect.client.records.SkinTemperatureRecord.Companion Companion;
+    field public static final int MEASUREMENT_LOCATION_FINGER = 1; // 0x1
+    field public static final int MEASUREMENT_LOCATION_TOE = 2; // 0x2
+    field public static final int MEASUREMENT_LOCATION_UNKNOWN = 0; // 0x0
+    field public static final int MEASUREMENT_LOCATION_WRIST = 3; // 0x3
+  }
+
+  public static final class SkinTemperatureRecord.Companion {
+  }
+
+  public static final class SkinTemperatureRecord.Delta {
+    ctor public SkinTemperatureRecord.Delta(java.time.Instant time, androidx.health.connect.client.units.TemperatureDelta delta);
+    method public androidx.health.connect.client.units.TemperatureDelta getDelta();
+    method public java.time.Instant getTime();
+    property public final androidx.health.connect.client.units.TemperatureDelta delta;
+    property public final java.time.Instant time;
+  }
+
   public final class SleepSessionRecord implements androidx.health.connect.client.records.IntervalRecord {
     ctor public SleepSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, optional String? title, optional String? notes, optional java.util.List<androidx.health.connect.client.records.SleepSessionRecord.Stage> stages, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public java.time.Instant getEndTime();
@@ -1359,6 +1396,7 @@
   }
 
   public final class Device {
+    ctor public Device();
     ctor public Device(optional String? manufacturer, optional String? model, optional int type);
     method public String? getManufacturer();
     method public String? getModel();
@@ -1382,6 +1420,7 @@
   }
 
   public final class Metadata {
+    ctor public Metadata();
     ctor public Metadata(optional String id, optional androidx.health.connect.client.records.metadata.DataOrigin dataOrigin, optional java.time.Instant lastModifiedTime, optional String? clientRecordId, optional long clientRecordVersion, optional androidx.health.connect.client.records.metadata.Device? device, optional int recordingMethod);
     method public String? getClientRecordId();
     method public long getClientRecordVersion();
@@ -1472,6 +1511,7 @@
 package androidx.health.connect.client.time {
 
   public final class TimeRangeFilter {
+    ctor public TimeRangeFilter();
     method public static androidx.health.connect.client.time.TimeRangeFilter after(java.time.Instant startTime);
     method public static androidx.health.connect.client.time.TimeRangeFilter after(java.time.LocalDateTime startTime);
     method public static androidx.health.connect.client.time.TimeRangeFilter before(java.time.Instant endTime);
@@ -1645,6 +1685,22 @@
     method public androidx.health.connect.client.units.Temperature fahrenheit(double value);
   }
 
+  public final class TemperatureDelta implements java.lang.Comparable<androidx.health.connect.client.units.TemperatureDelta> {
+    method public static androidx.health.connect.client.units.TemperatureDelta celsius(double value);
+    method public int compareTo(androidx.health.connect.client.units.TemperatureDelta other);
+    method public static androidx.health.connect.client.units.TemperatureDelta fahrenheit(double value);
+    method public double getCelsius();
+    method public double getFahrenheit();
+    property public final double inCelsius;
+    property public final double inFahrenheit;
+    field public static final androidx.health.connect.client.units.TemperatureDelta.Companion Companion;
+  }
+
+  public static final class TemperatureDelta.Companion {
+    method public androidx.health.connect.client.units.TemperatureDelta celsius(double value);
+    method public androidx.health.connect.client.units.TemperatureDelta fahrenheit(double value);
+  }
+
   public final class Velocity implements java.lang.Comparable<androidx.health.connect.client.units.Velocity> {
     method public int compareTo(androidx.health.connect.client.units.Velocity other);
     method public double getKilometersPerHour();
diff --git a/health/connect/connect-client/build.gradle b/health/connect/connect-client/build.gradle
index 0f23892..9b1779f 100644
--- a/health/connect/connect-client/build.gradle
+++ b/health/connect/connect-client/build.gradle
@@ -35,7 +35,7 @@
     api(libs.kotlinStdlib)
     // Add dependencies here
     api("androidx.activity:activity:1.2.0")
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(project(":health:connect:connect-client-proto"))
     implementation(libs.guavaListenableFuture)
     implementation(libs.guavaAndroid)
@@ -79,6 +79,8 @@
     namespace "androidx.health.connect.client"
     compileSdk = 34
     compileSdkExtension = 10
+    // TODO(b/352609562): Typedef with `toLong()`
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
@@ -86,7 +88,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "read or write user's health and fitness records."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":health:connect:connect-client-samples"))
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
index 5ce83ec..062596f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
@@ -21,7 +21,6 @@
 import android.content.pm.PackageManager
 import android.os.Build
 import android.os.UserManager
-import androidx.annotation.DoNotInline
 import androidx.annotation.IntDef
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
@@ -485,7 +484,6 @@
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     private object Api34Impl {
         @JvmStatic
-        @DoNotInline
         @AvailabilityStatus
         fun getSdkStatus(context: Context): Int {
             return if (
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
index a63bb6d..68b8036 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
@@ -47,6 +47,7 @@
 import androidx.health.connect.client.records.RespiratoryRateRecord
 import androidx.health.connect.client.records.RestingHeartRateRecord
 import androidx.health.connect.client.records.SexualActivityRecord
+import androidx.health.connect.client.records.SkinTemperatureRecord
 import androidx.health.connect.client.records.SleepSessionRecord
 import androidx.health.connect.client.records.SpeedRecord
 import androidx.health.connect.client.records.StepsCadenceRecord
@@ -180,12 +181,8 @@
         internal const val READ_BONE_MASS = PERMISSION_PREFIX + "READ_BONE_MASS"
         internal const val READ_HEIGHT = PERMISSION_PREFIX + "READ_HEIGHT"
 
-        @RestrictTo(RestrictTo.Scope.LIBRARY)
-        internal const val READ_HIP_CIRCUMFERENCE = PERMISSION_PREFIX + "READ_HIP_CIRCUMFERENCE"
         internal const val READ_LEAN_BODY_MASS = PERMISSION_PREFIX + "READ_LEAN_BODY_MASS"
 
-        @RestrictTo(RestrictTo.Scope.LIBRARY)
-        internal const val READ_WAIST_CIRCUMFERENCE = PERMISSION_PREFIX + "READ_WAIST_CIRCUMFERENCE"
         internal const val READ_WEIGHT = PERMISSION_PREFIX + "READ_WEIGHT"
 
         // Read permissions for CYCLE_TRACKING.
@@ -217,6 +214,7 @@
         internal const val READ_OXYGEN_SATURATION = PERMISSION_PREFIX + "READ_OXYGEN_SATURATION"
         internal const val READ_RESPIRATORY_RATE = PERMISSION_PREFIX + "READ_RESPIRATORY_RATE"
         internal const val READ_RESTING_HEART_RATE = PERMISSION_PREFIX + "READ_RESTING_HEART_RATE"
+        internal const val READ_SKIN_TEMPERATURE = PERMISSION_PREFIX + "READ_SKIN_TEMPERATURE"
 
         // Write permissions for ACTIVITY.
         internal const val WRITE_ACTIVE_CALORIES_BURNED =
@@ -241,13 +239,8 @@
         internal const val WRITE_BONE_MASS = PERMISSION_PREFIX + "WRITE_BONE_MASS"
         internal const val WRITE_HEIGHT = PERMISSION_PREFIX + "WRITE_HEIGHT"
 
-        @RestrictTo(RestrictTo.Scope.LIBRARY)
-        internal const val WRITE_HIP_CIRCUMFERENCE = PERMISSION_PREFIX + "WRITE_HIP_CIRCUMFERENCE"
         internal const val WRITE_LEAN_BODY_MASS = PERMISSION_PREFIX + "WRITE_LEAN_BODY_MASS"
 
-        @RestrictTo(RestrictTo.Scope.LIBRARY)
-        internal const val WRITE_WAIST_CIRCUMFERENCE =
-            PERMISSION_PREFIX + "WRITE_WAIST_CIRCUMFERENCE"
         internal const val WRITE_WEIGHT = PERMISSION_PREFIX + "WRITE_WEIGHT"
 
         // Write permissions for CYCLE_TRACKING.
@@ -279,6 +272,7 @@
         internal const val WRITE_OXYGEN_SATURATION = PERMISSION_PREFIX + "WRITE_OXYGEN_SATURATION"
         internal const val WRITE_RESPIRATORY_RATE = PERMISSION_PREFIX + "WRITE_RESPIRATORY_RATE"
         internal const val WRITE_RESTING_HEART_RATE = PERMISSION_PREFIX + "WRITE_RESTING_HEART_RATE"
+        internal const val WRITE_SKIN_TEMPERATURE = PERMISSION_PREFIX + "WRITE_SKIN_TEMPERATURE"
 
         internal const val READ_PERMISSION_PREFIX = PERMISSION_PREFIX + "READ_"
         internal const val WRITE_PERMISSION_PREFIX = PERMISSION_PREFIX + "WRITE_"
@@ -339,6 +333,8 @@
                     READ_SEXUAL_ACTIVITY.substringAfter(READ_PERMISSION_PREFIX),
                 SleepSessionRecord::class to READ_SLEEP.substringAfter(READ_PERMISSION_PREFIX),
                 SpeedRecord::class to READ_SPEED.substringAfter(READ_PERMISSION_PREFIX),
+                SkinTemperatureRecord::class to
+                    READ_SKIN_TEMPERATURE.substringAfter(READ_PERMISSION_PREFIX),
                 StepsCadenceRecord::class to READ_STEPS.substringAfter(READ_PERMISSION_PREFIX),
                 StepsRecord::class to READ_STEPS.substringAfter(READ_PERMISSION_PREFIX),
                 TotalCaloriesBurnedRecord::class to
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecord.kt
index db64ddf..00edeed 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecord.kt
@@ -73,6 +73,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "ActiveCaloriesBurnedRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, energy=$energy, metadata=$metadata)"
+    }
+
     companion object {
         private const val TYPE_NAME = "ActiveCaloriesBurned"
         private const val ENERGY_FIELD_NAME = "energy"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalBodyTemperatureRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalBodyTemperatureRecord.kt
index 57e308b..4f68f4a 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalBodyTemperatureRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalBodyTemperatureRecord.kt
@@ -76,6 +76,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "BasalBodyTemperatureRecord(time=$time, zoneOffset=$zoneOffset, temperature=$temperature, measurementLocation=$measurementLocation, metadata=$metadata)"
+    }
+
     private companion object {
         private val MIN_TEMPERATURE = 0.celsius
         private val MAX_TEMPERATURE = 100.celsius
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
index 5ffcb09..55a1231 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BasalMetabolicRateRecord.kt
@@ -60,6 +60,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "BasalMetabolicRateRecord(time=$time, zoneOffset=$zoneOffset, basalMetabolicRate=$basalMetabolicRate, metadata=$metadata)"
+    }
+
     companion object {
         private const val BASAL_CALORIES_TYPE_NAME = "BasalCaloriesBurned"
         private const val ENERGY_FIELD_NAME = "energy"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodGlucoseRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodGlucoseRecord.kt
index 20549e5..4f7f37d 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodGlucoseRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodGlucoseRecord.kt
@@ -197,4 +197,8 @@
         result = 31 * result + metadata.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "BloodGlucoseRecord(time=$time, zoneOffset=$zoneOffset, level=$level, specimenSource=$specimenSource, mealType=$mealType, relationToMeal=$relationToMeal, metadata=$metadata)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt
index dcb895a..38cb5b4 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BloodPressureRecord.kt
@@ -99,6 +99,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "BloodPressureRecord(time=$time, zoneOffset=$zoneOffset, systolic=$systolic, diastolic=$diastolic, bodyPosition=$bodyPosition, measurementLocation=$measurementLocation, metadata=$metadata)"
+    }
+
     /** The arm and part of the arm where a blood pressure measurement was taken. */
     internal object MeasurementLocation {
         const val LEFT_WRIST = "left_wrist"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt
index 69bd426..fa033bc 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt
@@ -64,6 +64,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "BodyFatRecord(time=$time, zoneOffset=$zoneOffset, percentage=$percentage, metadata=$metadata)"
+    }
+
     private companion object {
         private val MAX_PERCENTAGE = 100.percent
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureRecord.kt
index a939b11..3157e46 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyTemperatureRecord.kt
@@ -68,4 +68,8 @@
         result = 31 * result + metadata.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "BodyTemperatureRecord(time=$time, zoneOffset=$zoneOffset, temperature=$temperature, measurementLocation=$measurementLocation, metadata=$metadata)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyWaterMassRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyWaterMassRecord.kt
index 787b426..eb5915c 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyWaterMassRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BodyWaterMassRecord.kt
@@ -63,6 +63,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "BodyWaterMassRecord(time=$time, zoneOffset=$zoneOffset, mass=$mass, metadata=$metadata)"
+    }
+
     private companion object {
         private val MAX_BODY_WATER_MASS = 1000.kilograms
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BoneMassRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BoneMassRecord.kt
index 3d33b8e..48e655a 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BoneMassRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/BoneMassRecord.kt
@@ -61,6 +61,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "BoneMassRecord(time=$time, zoneOffset=$zoneOffset, mass=$mass, metadata=$metadata)"
+    }
+
     private companion object {
         private val MAX_BONE_MASS = 1000.kilograms
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CervicalMucusRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CervicalMucusRecord.kt
index 4c744c9..b576cea 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CervicalMucusRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CervicalMucusRecord.kt
@@ -150,4 +150,8 @@
         result = 31 * result + metadata.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "CervicalMucusRecord(time=$time, zoneOffset=$zoneOffset, appearance=$appearance, sensation=$sensation, metadata=$metadata)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
index 6fd3e7d..461d874 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecord.kt
@@ -71,6 +71,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "CyclingPedalingCadenceRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, samples=$samples, metadata=$metadata)"
+    }
+
     companion object {
         private const val TYPE = "CyclingPedalingCadenceSeries"
         private const val RPM_FIELD = "rpm"
@@ -133,5 +137,9 @@
             result = 31 * result + revolutionsPerMinute.hashCode()
             return result
         }
+
+        override fun toString(): String {
+            return "Sample(time=$time, revolutionsPerMinute=$revolutionsPerMinute)"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/DistanceRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/DistanceRecord.kt
index 9776e47..9d8dedf 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/DistanceRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/DistanceRecord.kt
@@ -79,6 +79,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "DistanceRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, distance=$distance, metadata=$metadata)"
+    }
+
     companion object {
         private val MAX_DISTANCE = 1000_000.meters
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ElevationGainedRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ElevationGainedRecord.kt
index 112e704..f91ae28 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ElevationGainedRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ElevationGainedRecord.kt
@@ -69,6 +69,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "ElevationGainedRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, elevation=$elevation, metadata=$metadata)"
+    }
+
     companion object {
         private val MAX_ELEVATION_GAIN = (1000_000).meters
         private val MIN_ELEVATION_GAIN = (-1000_000).meters
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseLap.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseLap.kt
index 290feb0..1289635 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseLap.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseLap.kt
@@ -60,4 +60,8 @@
         result = 31 * result + length.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "ExerciseLap(startTime=$startTime, endTime=$endTime, length=$length)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
index 6b8d7bd..98f5ef0 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
@@ -46,6 +46,10 @@
         return route.hashCode()
     }
 
+    override fun toString(): String {
+        return "ExerciseRoute(route=$route)"
+    }
+
     /**
      * Represents a single location point recorded during an exercise.
      *
@@ -112,5 +116,9 @@
             result = 31 * result + (altitude?.hashCode() ?: 0)
             return result
         }
+
+        override fun toString(): String {
+            return "Location(time=$time, latitude=$latitude, longitude=$longitude, horizontalAccuracy=$horizontalAccuracy, verticalAccuracy=$verticalAccuracy, altitude=$altitude)"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
index 3978f5c..80098e1 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
@@ -32,6 +32,10 @@
         override fun hashCode(): Int {
             return 0
         }
+
+        override fun toString(): String {
+            return "Data(exerciseRoute=$exerciseRoute)"
+        }
     }
 
     /** Class indicating that a permission hasn't been granted and a value couldn't be returned. */
@@ -43,6 +47,10 @@
         override fun hashCode(): Int {
             return 0
         }
+
+        override fun toString(): String {
+            return "ConsentRequired"
+        }
     }
 
     /** Class indicating that there's no data to request permissions for. */
@@ -54,5 +62,9 @@
         override fun hashCode(): Int {
             return 0
         }
+
+        override fun toString(): String {
+            return "NoData"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSegment.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSegment.kt
index b299ca6..e6d5a6f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSegment.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSegment.kt
@@ -72,6 +72,10 @@
         return SESSION_TO_SEGMENTS_MAPPING[sessionType]?.contains(segmentType) ?: false
     }
 
+    override fun toString(): String {
+        return "ExerciseSegment(startTime=$startTime, endTime=$endTime, segmentType=$segmentType, repetitions=$repetitions)"
+    }
+
     companion object {
         /** Next Id: 68. */
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
index 187d9f3..8d7a3cf 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
@@ -178,6 +178,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "ExerciseSessionRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, exerciseType=$exerciseType, title=$title, notes=$notes, metadata=$metadata, segments=$segments, laps=$laps, exerciseRouteResult=$exerciseRouteResult)"
+    }
+
     companion object {
         /**
          * Metric identifier to retrieve the total exercise time from
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/FloorsClimbedRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/FloorsClimbedRecord.kt
index b398b5e..cc582d0 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/FloorsClimbedRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/FloorsClimbedRecord.kt
@@ -60,6 +60,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "FloorsClimbedRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, floors=$floors, metadata=$metadata)"
+    }
+
     companion object {
         /**
          * Metric identifier to retrieve the total floors climbed from
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
index 3572b0d..4c7afe1 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateRecord.kt
@@ -65,6 +65,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "HeartRateRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, samples=$samples, metadata=$metadata)"
+    }
+
     companion object {
         private const val HEART_RATE_TYPE_NAME = "HeartRateSeries"
         private const val BPM_FIELD_NAME = "bpm"
@@ -143,5 +147,9 @@
             result = 31 * result + beatsPerMinute.hashCode()
             return result
         }
+
+        override fun toString(): String {
+            return "Sample(time=$time, beatsPerMinute=$beatsPerMinute)"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateVariabilityRmssdRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateVariabilityRmssdRecord.kt
index a61b4ec..f9e8306a3 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateVariabilityRmssdRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeartRateVariabilityRmssdRecord.kt
@@ -64,4 +64,8 @@
         result = 31 * result + metadata.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "HeartRateVariabilityRmssdRecord(time=$time, zoneOffset=$zoneOffset, heartRateVariabilityMillis=$heartRateVariabilityMillis, metadata=$metadata)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeightRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeightRecord.kt
index 8da6503..3bc62fa 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeightRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HeightRecord.kt
@@ -62,6 +62,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "HeightRecord(time=$time, zoneOffset=$zoneOffset, height=$height, metadata=$metadata)"
+    }
+
     companion object {
         private const val HEIGHT_NAME = "Height"
         private const val HEIGHT_FIELD_NAME = "height"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HydrationRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HydrationRecord.kt
index 464629f..adac860 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HydrationRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/HydrationRecord.kt
@@ -63,6 +63,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "HydrationRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, volume=$volume, metadata=$metadata)"
+    }
+
     companion object {
         private val MAX_VOLUME = 100.liters
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/IntermenstrualBleedingRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/IntermenstrualBleedingRecord.kt
index 9f292cc..741738f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/IntermenstrualBleedingRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/IntermenstrualBleedingRecord.kt
@@ -50,4 +50,8 @@
         result = 31 * result + metadata.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "IntermenstrualBleedingRecord(time=$time, zoneOffset=$zoneOffset, metadata=$metadata)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/LeanBodyMassRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/LeanBodyMassRecord.kt
index be9e795..fc2997c 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/LeanBodyMassRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/LeanBodyMassRecord.kt
@@ -63,6 +63,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "LeanBodyMassRecord(time=$time, zoneOffset=$zoneOffset, mass=$mass, metadata=$metadata)"
+    }
+
     private companion object {
         private val MAX_MASS = 1000.kilograms
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationFlowRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationFlowRecord.kt
index 2f3db96..1e3f7a5 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationFlowRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationFlowRecord.kt
@@ -52,6 +52,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "MenstruationFlowRecord(time=$time, zoneOffset=$zoneOffset, flow=$flow, metadata=$metadata)"
+    }
+
     companion object {
         const val FLOW_UNKNOWN = 0
         const val FLOW_LIGHT = 1
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationPeriodRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationPeriodRecord.kt
index fb565b7..4239fec 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationPeriodRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MenstruationPeriodRecord.kt
@@ -66,6 +66,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "MenstruationPeriodRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, metadata=$metadata)"
+    }
+
     private companion object {
         private val MAX_DURATION = Duration.ofDays(31)
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/NutritionRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/NutritionRecord.kt
index ea51584..fbe2c02 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/NutritionRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/NutritionRecord.kt
@@ -291,6 +291,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "NutritionRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, biotin=$biotin, caffeine=$caffeine, calcium=$calcium, energy=$energy, energyFromFat=$energyFromFat, chloride=$chloride, cholesterol=$cholesterol, chromium=$chromium, copper=$copper, dietaryFiber=$dietaryFiber, folate=$folate, folicAcid=$folicAcid, iodine=$iodine, iron=$iron, magnesium=$magnesium, manganese=$manganese, molybdenum=$molybdenum, monounsaturatedFat=$monounsaturatedFat, niacin=$niacin, pantothenicAcid=$pantothenicAcid, phosphorus=$phosphorus, polyunsaturatedFat=$polyunsaturatedFat, potassium=$potassium, protein=$protein, riboflavin=$riboflavin, saturatedFat=$saturatedFat, selenium=$selenium, sodium=$sodium, sugar=$sugar, thiamin=$thiamin, totalCarbohydrate=$totalCarbohydrate, totalFat=$totalFat, transFat=$transFat, unsaturatedFat=$unsaturatedFat, vitaminA=$vitaminA, vitaminB12=$vitaminB12, vitaminB6=$vitaminB6, vitaminC=$vitaminC, vitaminD=$vitaminD, vitaminE=$vitaminE, vitaminK=$vitaminK, zinc=$zinc, name=$name, mealType=$mealType, metadata=$metadata)"
+    }
+
     companion object {
         private const val TYPE_NAME = "Nutrition"
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OvulationTestRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OvulationTestRecord.kt
index a71e4a3..dd7d508f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OvulationTestRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OvulationTestRecord.kt
@@ -52,6 +52,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "OvulationTestRecord(time=$time, zoneOffset=$zoneOffset, result=$result, metadata=$metadata)"
+    }
+
     /** The result of a user's ovulation test. */
     internal object Result {
         const val POSITIVE = "positive"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt
index 902dc78..f3a51b6 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt
@@ -63,4 +63,8 @@
         result = 31 * result + metadata.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "OxygenSaturationRecord(time=$time, zoneOffset=$zoneOffset, percentage=$percentage, metadata=$metadata)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/PowerRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/PowerRecord.kt
index c52e42d..0d01b97 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/PowerRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/PowerRecord.kt
@@ -73,6 +73,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "PowerRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, samples=$samples, metadata=$metadata)"
+    }
+
     companion object {
         private const val TYPE = "PowerSeries"
         private const val POWER_FIELD = "power"
@@ -157,5 +161,9 @@
             result = 31 * result + power.hashCode()
             return result
         }
+
+        override fun toString(): String {
+            return "Sample(time=$time, power=$power)"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RespiratoryRateRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RespiratoryRateRecord.kt
index ae9324d..bd0c65e 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RespiratoryRateRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RespiratoryRateRecord.kt
@@ -54,4 +54,8 @@
         result = 31 * result + metadata.hashCode()
         return result
     }
+
+    override fun toString(): String {
+        return "RespiratoryRateRecord(time=$time, zoneOffset=$zoneOffset, rate=$rate, metadata=$metadata)"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RestingHeartRateRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RestingHeartRateRecord.kt
index 848e5696..8151bf0 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RestingHeartRateRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/RestingHeartRateRecord.kt
@@ -58,6 +58,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "RestingHeartRateRecord(time=$time, zoneOffset=$zoneOffset, beatsPerMinute=$beatsPerMinute, metadata=$metadata)"
+    }
+
     companion object {
         private const val REST_HEART_RATE_TYPE_NAME = "RestingHeartRate"
         private const val BPM_FIELD_NAME = "bpm"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SexualActivityRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SexualActivityRecord.kt
index f904aa4..5be02b6 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SexualActivityRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SexualActivityRecord.kt
@@ -57,6 +57,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "SexualActivityRecord(time=$time, zoneOffset=$zoneOffset, protectionUsed=$protectionUsed, metadata=$metadata)"
+    }
+
     companion object {
         const val PROTECTION_USED_UNKNOWN = 0
         const val PROTECTION_USED_PROTECTED = 1
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SkinTemperatureRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SkinTemperatureRecord.kt
new file mode 100644
index 0000000..9be96d0
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SkinTemperatureRecord.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.health.connect.client.records
+
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.Temperature
+import androidx.health.connect.client.units.TemperatureDelta
+import androidx.health.connect.client.units.celsius
+import java.time.Instant
+import java.time.ZoneOffset
+
+/**
+ * Captures the skin temperature of a user. Each record can represent a series of measurements of
+ * temperature differences.
+ *
+ * @param startTime Start time of the record.
+ * @param startZoneOffset User experienced zone offset at [startTime], or null if unknown. Providing
+ *   these will help
+ *     * history aggregations results stay consistent should user travel. Queries with user
+ *     * experienced time filters will assume system current zone offset if the information is
+ *       absent.
+ *
+ * @param endTime End time of the record.
+ * @param endZoneOffset User experienced zone offset at [endTime], or null if unknown. Providing
+ *   these will help
+ *     * history aggregations results stay consistent should user travel. Queries with user
+ *     * experienced time filters will assume system current zone offset if the information is
+ *       absent.
+ *
+ * @param deltas a list of skin temperature [Delta] values.
+ * @param baseline Temperature in [Temperature] unit. Optional field. Valid range: 0-100 Celsius
+ *   degrees.
+ * @param measurementLocation location of the skin temperature. Optional field. Allowed values:
+ *   [SkinTemperatureMeasurementLocation].
+ * @param metadata set of common metadata associated with the written record.
+ */
+class SkinTemperatureRecord(
+    override val startTime: Instant,
+    override val startZoneOffset: ZoneOffset?,
+    override val endTime: Instant,
+    override val endZoneOffset: ZoneOffset?,
+    val deltas: List<Delta>,
+    val baseline: Temperature? = null,
+    @SkinTemperatureMeasurementLocation val measurementLocation: Int = MEASUREMENT_LOCATION_UNKNOWN,
+    override val metadata: Metadata = Metadata.EMPTY,
+) : IntervalRecord {
+
+    init {
+        require(startTime.isBefore(endTime)) { "startTime must be before endTime." }
+        if (baseline != null) {
+            baseline.requireNotLess(other = MIN_TEMPERATURE, "temperature")
+            baseline.requireNotMore(other = MAX_TEMPERATURE, "temperature")
+        }
+
+        if (deltas.isNotEmpty()) {
+            // check all deltas are within parent record duration
+            require(!deltas.minBy { it.time }.time.isBefore(startTime)) {
+                "segments can not be out of parent time range."
+            }
+            require(deltas.maxBy { it.time }.time.isBefore(endTime)) {
+                "segments can not be out of parent time range."
+            }
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is SkinTemperatureRecord) return false
+
+        if (startTime != other.startTime) return false
+        if (endTime != other.endTime) return false
+        if (startZoneOffset != other.startZoneOffset) return false
+        if (endZoneOffset != other.endZoneOffset) return false
+        if (baseline != other.baseline) return false
+        if (measurementLocation != other.measurementLocation) return false
+        if (deltas != other.deltas) return false
+        if (metadata != other.metadata) return false
+
+        return true
+    }
+
+    /*
+     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+     */
+    override fun hashCode(): Int {
+        var result = startTime.hashCode()
+        result = 31 * result + endTime.hashCode()
+        result = 31 * result + (startZoneOffset?.hashCode() ?: 0)
+        result = 31 * result + (endZoneOffset?.hashCode() ?: 0)
+        result = 31 * result + (baseline?.hashCode() ?: 0)
+        result = 31 * result + measurementLocation.hashCode()
+        result = 31 * result + deltas.hashCode()
+        result = 31 * result + metadata.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "SkinTemperatureRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, deltas=$deltas, baseline=$baseline, measurementLocation=$measurementLocation, metadata=$metadata)"
+    }
+
+    companion object {
+        private val MIN_TEMPERATURE = 0.celsius
+        private val MAX_TEMPERATURE = 100.celsius
+
+        /** Use this if the location is unknown. */
+        const val MEASUREMENT_LOCATION_UNKNOWN: Int = 0
+        /** Skin temperature measurement was finger. */
+        const val MEASUREMENT_LOCATION_FINGER: Int = 1
+        /** Skin temperature measurement was toe. */
+        const val MEASUREMENT_LOCATION_TOE: Int = 2
+        /** Skin temperature measurement was wrist. */
+        const val MEASUREMENT_LOCATION_WRIST: Int = 3
+
+        /** Internal mappings useful for interoperability between integers and strings. */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @JvmField
+        val MEASUREMENT_LOCATION_STRING_TO_INT_MAP: Map<String, Int> =
+            mapOf(
+                "finger" to MEASUREMENT_LOCATION_FINGER,
+                "toe" to MEASUREMENT_LOCATION_TOE,
+                "wrist" to MEASUREMENT_LOCATION_WRIST,
+            )
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @JvmField
+        val MEASUREMENT_LOCATION_INT_TO_STRING_MAP =
+            MEASUREMENT_LOCATION_STRING_TO_INT_MAP.reverse()
+
+        /** Measurement location of the skin temperature. */
+        @Retention(AnnotationRetention.SOURCE)
+        @IntDef(
+            value =
+                [
+                    MEASUREMENT_LOCATION_UNKNOWN,
+                    MEASUREMENT_LOCATION_FINGER,
+                    MEASUREMENT_LOCATION_TOE,
+                    MEASUREMENT_LOCATION_WRIST,
+                ]
+        )
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        annotation class SkinTemperatureMeasurementLocation
+    }
+
+    /**
+     * Represents a skin temperature delta entry of [SkinTemperatureRecord].
+     *
+     * @param time The point in time when the measurement was taken.
+     * @param delta delta temperature difference. Valid range: -30 to 30 Celsius degrees.
+     * @see SkinTemperatureRecord
+     * @see TemperatureDelta
+     */
+    public class Delta(val time: Instant, val delta: TemperatureDelta) {
+
+        init {
+            delta.requireNotLess(other = MIN_DELTA_TEMPERATURE, "delta")
+            delta.requireNotMore(other = MAX_DELTA_TEMPERATURE, "delta")
+        }
+
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Delta
+
+            if (time != other.time) return false
+            if (delta != other.delta) return false
+
+            return true
+        }
+
+        /*
+         * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+         */
+        override fun hashCode(): Int {
+            var result = time.hashCode()
+            result = 31 * result + delta.hashCode()
+            return result
+        }
+
+        override fun toString(): String {
+            return "Delta(time=$time, delta=$delta)"
+        }
+
+        private companion object {
+            private val MIN_DELTA_TEMPERATURE = TemperatureDelta.celsius(-30.0)
+            private val MAX_DELTA_TEMPERATURE = TemperatureDelta.celsius(30.0)
+        }
+    }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SleepSessionRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SleepSessionRecord.kt
index 916fefc..3243d57 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SleepSessionRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SleepSessionRecord.kt
@@ -88,6 +88,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "SleepSessionRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, title=$title, notes=$notes, stages=$stages, metadata=$metadata)"
+    }
+
     companion object {
         /**
          * Metric identifier to retrieve the total sleep session duration from
@@ -193,5 +197,9 @@
             result = 31 * result + endTime.hashCode()
             return result
         }
+
+        override fun toString(): String {
+            return "Stage(startTime=$startTime, endTime=$endTime, stage=$stage)"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SpeedRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SpeedRecord.kt
index db2d1d6..5bad4a9 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SpeedRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/SpeedRecord.kt
@@ -69,6 +69,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "SpeedRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, samples=$samples, metadata=$metadata)"
+    }
+
     companion object {
         private const val SPEED_TYPE_NAME = "SpeedSeries"
         private const val SPEED_FIELD_NAME = "speed"
@@ -146,5 +150,9 @@
             result = 31 * result + speed.hashCode()
             return result
         }
+
+        override fun toString(): String {
+            return "Sample(time=$time, speed=$speed)"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
index 4471594..28888a0 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsCadenceRecord.kt
@@ -69,6 +69,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "StepsCadenceRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, samples=$samples, metadata=$metadata)"
+    }
+
     companion object {
         private const val TYPE = "StepsCadenceSeries"
         private const val RATE_FIELD = "rate"
@@ -129,5 +133,9 @@
             result = 31 * result + rate.hashCode()
             return result
         }
+
+        override fun toString(): String {
+            return "Sample(time=$time, rate=$rate)"
+        }
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt
index b5ca55d..f5fcf49 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/StepsRecord.kt
@@ -69,6 +69,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "StepsRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, count=$count, metadata=$metadata)"
+    }
+
     companion object {
         /**
          * Metric identifier to retrieve the total steps count from
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecord.kt
index 7b03508..0462b63 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecord.kt
@@ -72,6 +72,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "TotalCaloriesBurnedRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, energy=$energy, metadata=$metadata)"
+    }
+
     companion object {
         private val MAX_ENERGY = 1000_000.kilocalories
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt
index d6b5be6..c074f58 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt
@@ -69,6 +69,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "Vo2MaxRecord(time=$time, zoneOffset=$zoneOffset, vo2MillilitersPerMinuteKilogram=$vo2MillilitersPerMinuteKilogram, measurementMethod=$measurementMethod, metadata=$metadata)"
+    }
+
     companion object {
         const val MEASUREMENT_METHOD_OTHER = 0
         const val MEASUREMENT_METHOD_METABOLIC_CART = 1
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WeightRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WeightRecord.kt
index f460fca..bf0dd0b 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WeightRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WeightRecord.kt
@@ -63,6 +63,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "WeightRecord(time=$time, zoneOffset=$zoneOffset, weight=$weight, metadata=$metadata)"
+    }
+
     /*
      * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
      */
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WheelchairPushesRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WheelchairPushesRecord.kt
index 4aa943d..b639907 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WheelchairPushesRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/WheelchairPushesRecord.kt
@@ -65,6 +65,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "WheelchairPushesRecord(startTime=$startTime, startZoneOffset=$startZoneOffset, endTime=$endTime, endZoneOffset=$endZoneOffset, count=$count, metadata=$metadata)"
+    }
+
     companion object {
         /**
          * Metric identifier to retrieve the total wheelchair push count from
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/DataOrigin.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/DataOrigin.kt
index 80db93b..d457aa25 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/DataOrigin.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/DataOrigin.kt
@@ -37,4 +37,8 @@
     override fun hashCode(): Int {
         return packageName.hashCode()
     }
+
+    override fun toString(): String {
+        return "DataOrigin(packageName='$packageName')"
+    }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Device.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Device.kt
index 3dba95b..9746b14 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Device.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Device.kt
@@ -54,6 +54,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "Device(manufacturer=$manufacturer, model=$model, type=$type)"
+    }
+
     companion object {
         const val TYPE_UNKNOWN = 0
         const val TYPE_WATCH = 1
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Metadata.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Metadata.kt
index bef884f..83915da 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Metadata.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/metadata/Metadata.kt
@@ -108,6 +108,10 @@
         return result
     }
 
+    override fun toString(): String {
+        return "Metadata(id='$id', dataOrigin=$dataOrigin, lastModifiedTime=$lastModifiedTime, clientRecordId=$clientRecordId, clientRecordVersion=$clientRecordVersion, device=$device, recordingMethod=$recordingMethod)"
+    }
+
     companion object {
         internal const val EMPTY_ID: String = ""
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/TemperatureDelta.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/TemperatureDelta.kt
new file mode 100644
index 0000000..0d5f9ef
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/TemperatureDelta.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+/**
+ * Represents a unit of TemperatureDelta difference. Supported units:
+ * - Celsius - see [TemperatureDelta.celsius], [Double.celsius]
+ * - Fahrenheit - see [TemperatureDelta.fahrenheit], [Double.fahrenheit]
+ */
+class TemperatureDelta
+private constructor(
+    private val value: Double,
+    private val type: Type,
+) : Comparable<TemperatureDelta> {
+
+    /** Returns the TemperatureDelta in Celsius degrees. */
+    @get:JvmName("getCelsius")
+    val inCelsius: Double
+        get() =
+            when (type) {
+                Type.CELSIUS -> value
+                Type.FAHRENHEIT -> value / 1.8
+            }
+
+    /** Returns the TemperatureDelta in Fahrenheit degrees. */
+    @get:JvmName("getFahrenheit")
+    val inFahrenheit: Double
+        get() =
+            when (type) {
+                Type.CELSIUS -> value * 1.8
+                Type.FAHRENHEIT -> value
+            }
+
+    override fun compareTo(other: TemperatureDelta): Int =
+        if (type == other.type) {
+            value.compareTo(other.value)
+        } else {
+            inCelsius.compareTo(other.inCelsius)
+        }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is TemperatureDelta) return false
+
+        if (type == other.type) {
+            return value == other.value
+        }
+
+        return inCelsius == other.inCelsius
+    }
+
+    override fun hashCode(): Int = inCelsius.hashCode()
+
+    override fun toString(): String = "$value ${type.title}"
+
+    companion object {
+        /** Creates [TemperatureDelta] with the specified value in Celsius degrees. */
+        @JvmStatic
+        fun celsius(value: Double): TemperatureDelta = TemperatureDelta(value, Type.CELSIUS)
+
+        /** Creates [TemperatureDelta] with the specified value in Fahrenheit degrees. */
+        @JvmStatic
+        fun fahrenheit(value: Double): TemperatureDelta = TemperatureDelta(value, Type.FAHRENHEIT)
+    }
+
+    private enum class Type {
+        CELSIUS {
+            override val title: String = "Celsius"
+        },
+        FAHRENHEIT {
+            override val title: String = "Fahrenheit"
+        };
+
+        abstract val title: String
+    }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/platform/client/impl/data/SharedMemory27Impl.kt b/health/connect/connect-client/src/main/java/androidx/health/platform/client/impl/data/SharedMemory27Impl.kt
index 5168b79..0a8d7d24 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/platform/client/impl/data/SharedMemory27Impl.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/platform/client/impl/data/SharedMemory27Impl.kt
@@ -20,14 +20,12 @@
 import android.os.Parcel
 import android.os.SharedMemory
 import android.system.OsConstants
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 
 /** Internal class to ensure calls to shared memory are guarded, so that */
 @RequiresApi(api = Build.VERSION_CODES.O_MR1)
 internal object SharedMemory27Impl {
     /** Flattens `bytes` into `dest` using [SharedMemory]. */
-    @DoNotInline
     fun writeToParcelUsingSharedMemory(name: String, bytes: ByteArray, dest: Parcel, flags: Int) {
         SharedMemory.create(name, bytes.size).use { memory ->
             memory.setProtect(OsConstants.PROT_READ or OsConstants.PROT_WRITE)
@@ -37,7 +35,6 @@
         }
     }
 
-    @DoNotInline
     fun <U : Any> parseParcelUsingSharedMemory(source: Parcel, parser: (ByteArray) -> U): U =
         SharedMemory.CREATOR.createFromParcel(source).use { memory ->
             val buffer = memory.mapReadOnly()
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecordTest.kt
index 5beb3fa..3210907 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ActiveCaloriesBurnedRecordTest.kt
@@ -61,4 +61,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                ActiveCaloriesBurnedRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        energy = 10.calories,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "ActiveCaloriesBurnedRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, energy=10.0 cal, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt
index 8ab87c1..dbc4728 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt
@@ -16,8 +16,11 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.BloodGlucose
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import java.time.Instant
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -43,4 +46,23 @@
         assertThat(BloodGlucoseRecord.SPECIMEN_SOURCE_INT_TO_STRING_MAP.keys)
             .containsExactlyElementsIn(allEnums)
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                BloodGlucoseRecord(
+                        time = Instant.ofEpochMilli(1234L),
+                        zoneOffset = null,
+                        level = BloodGlucose.millimolesPerLiter(2.4),
+                        specimenSource = BloodGlucoseRecord.SPECIMEN_SOURCE_SERUM,
+                        mealType = MealType.MEAL_TYPE_LUNCH,
+                        relationToMeal = BloodGlucoseRecord.RELATION_TO_MEAL_FASTING,
+                        metadata = Metadata.EMPTY,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "BloodGlucoseRecord(time=1970-01-01T00:00:01.234Z, zoneOffset=null, level=2.4 mmol/L, specimenSource=4, mealType=2, relationToMeal=2, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
index f62bcac..4dc9b41 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
@@ -16,8 +16,11 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.Pressure
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import java.time.Instant
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -43,4 +46,23 @@
         assertThat(BloodPressureRecord.MEASUREMENT_LOCATION_INT_TO_STRING_MAP.keys)
             .containsExactlyElementsIn(allEnums)
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                BloodPressureRecord(
+                        time = Instant.ofEpochMilli(1234L),
+                        zoneOffset = null,
+                        systolic = Pressure.millimetersOfMercury(120.0),
+                        diastolic = Pressure.millimetersOfMercury(112.0),
+                        bodyPosition = BloodPressureRecord.BODY_POSITION_RECLINING,
+                        measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST,
+                        metadata = Metadata.EMPTY,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "BloodPressureRecord(time=1970-01-01T00:00:01.234Z, zoneOffset=null, systolic=120.0 mmHg, diastolic=112.0 mmHg, bodyPosition=4, measurementLocation=1, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt
index a9620fbd..254d817 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt
@@ -16,8 +16,10 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.records.metadata.Metadata
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import java.time.Instant
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -43,4 +45,21 @@
         assertThat(CervicalMucusRecord.SENSATION_INT_TO_STRING_MAP.keys)
             .containsExactlyElementsIn(allEnums)
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                CervicalMucusRecord(
+                        time = Instant.ofEpochMilli(1234L),
+                        zoneOffset = null,
+                        appearance = CervicalMucusRecord.APPEARANCE_UNUSUAL,
+                        sensation = CervicalMucusRecord.SENSATION_MEDIUM,
+                        metadata = Metadata.EMPTY,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "CervicalMucusRecord(time=1970-01-01T00:00:01.234Z, zoneOffset=null, appearance=6, sensation=2, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecordTest.kt
index f54b524..32fafd6 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CyclingPedalingCadenceRecordTest.kt
@@ -82,4 +82,27 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                CyclingPedalingCadenceRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        samples =
+                            listOf(
+                                CyclingPedalingCadenceRecord.Sample(
+                                    Instant.ofEpochMilli(1234L),
+                                    80.0
+                                )
+                            )
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "CyclingPedalingCadenceRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, samples=[Sample(time=1970-01-01T00:00:01.234Z, revolutionsPerMinute=80.0)], metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/DistanceRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/DistanceRecordTest.kt
index 201061c..9877841 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/DistanceRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/DistanceRecordTest.kt
@@ -61,4 +61,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                DistanceRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        distance = 10.meters
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "DistanceRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, distance=10.0 meters, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ElevationGainedRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ElevationGainedRecordTest.kt
index b0cabe7..a759da8 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ElevationGainedRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ElevationGainedRecordTest.kt
@@ -61,4 +61,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                ElevationGainedRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        elevation = 100.0.meters,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "ElevationGainedRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, elevation=100.0 meters, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseLapTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseLapTest.kt
index 3957e43..06f00d4 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseLapTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseLapTest.kt
@@ -77,4 +77,19 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                ExerciseLap(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        endTime = Instant.ofEpochMilli(1236L),
+                        length = 100.0.meters
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "ExerciseLap(startTime=1970-01-01T00:00:01.234Z, endTime=1970-01-01T00:00:01.236Z, length=100.0 meters)"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
index 35275bf..a579b13 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
@@ -17,6 +17,7 @@
 package androidx.health.connect.client.records
 
 import androidx.health.connect.client.units.Length
+import androidx.health.connect.client.units.meters
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
 import kotlin.test.assertFailsWith
@@ -114,4 +115,26 @@
             )
         assertFailsWith<IllegalArgumentException> { ExerciseRoute(listOf(location1, location2)) }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                ExerciseRoute(
+                        listOf(
+                            ExerciseRoute.Location(
+                                time = Instant.ofEpochMilli(1234L),
+                                latitude = 34.8,
+                                longitude = -34.8,
+                                horizontalAccuracy = 2.0.meters,
+                                verticalAccuracy = 3.0.meters,
+                                altitude = 120.0.meters
+                            )
+                        )
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "ExerciseRoute(route=[Location(time=1970-01-01T00:00:01.234Z, latitude=34.8, longitude=-34.8, horizontalAccuracy=2.0 meters, verticalAccuracy=3.0 meters, altitude=120.0 meters)])"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
index f4b9255..49709f7 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
@@ -506,4 +506,53 @@
             )
             .isEqualTo(ExerciseRouteResult.NoData())
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                ExerciseSessionRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
+                        title = "title",
+                        notes = "notes",
+                        segments =
+                            listOf(
+                                ExerciseSegment(
+                                    startTime = Instant.ofEpochMilli(1234L),
+                                    endTime = Instant.ofEpochMilli(1235L),
+                                    segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
+                                )
+                            ),
+                        laps =
+                            listOf(
+                                ExerciseLap(
+                                    startTime = Instant.ofEpochMilli(1235L),
+                                    endTime = Instant.ofEpochMilli(1236L),
+                                    length = 10.meters,
+                                )
+                            ),
+                        exerciseRoute =
+                            ExerciseRoute(
+                                route =
+                                    listOf(
+                                        ExerciseRoute.Location(
+                                            time = Instant.ofEpochMilli(1234L),
+                                            latitude = 34.5,
+                                            longitude = -34.5,
+                                            horizontalAccuracy = 0.4.meters,
+                                            verticalAccuracy = 1.3.meters,
+                                            altitude = 23.4.meters
+                                        )
+                                    )
+                            ),
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "ExerciseSessionRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, exerciseType=8, title=title, notes=notes, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0), segments=[ExerciseSegment(startTime=1970-01-01T00:00:01.234Z, endTime=1970-01-01T00:00:01.235Z, segmentType=7, repetitions=0)], laps=[ExerciseLap(startTime=1970-01-01T00:00:01.235Z, endTime=1970-01-01T00:00:01.236Z, length=10.0 meters)], exerciseRouteResult=Data(exerciseRoute=ExerciseRoute(route=[Location(time=1970-01-01T00:00:01.234Z, latitude=34.5, longitude=-34.5, horizontalAccuracy=0.4 meters, verticalAccuracy=1.3 meters, altitude=23.4 meters)])))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/FloorsClimbedRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/FloorsClimbedRecordTest.kt
index 4077757..acff80f 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/FloorsClimbedRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/FloorsClimbedRecordTest.kt
@@ -60,4 +60,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                FloorsClimbedRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        floors = 32.5,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "FloorsClimbedRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, floors=32.5, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt
index 3152da4..149c059 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HeartRateRecordTest.kt
@@ -102,4 +102,27 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                HeartRateRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        samples =
+                            listOf(
+                                HeartRateRecord.Sample(
+                                    time = Instant.ofEpochMilli(1234L),
+                                    beatsPerMinute = 64
+                                )
+                            )
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "HeartRateRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, samples=[Sample(time=1970-01-01T00:00:01.234Z, beatsPerMinute=64)], metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HydrationRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HydrationRecordTest.kt
index 14b976f..91431c91 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HydrationRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/HydrationRecordTest.kt
@@ -61,4 +61,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                HydrationRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        volume = 2.4.liters
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "HydrationRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, volume=2.4 L, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/IntermenstrualBleedingRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/IntermenstrualBleedingRecordTest.kt
index 2a2e4b9..db8719bd 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/IntermenstrualBleedingRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/IntermenstrualBleedingRecordTest.kt
@@ -40,4 +40,18 @@
                 )
             )
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                IntermenstrualBleedingRecord(
+                        time = Instant.ofEpochMilli(1234L),
+                        zoneOffset = null,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "IntermenstrualBleedingRecord(time=1970-01-01T00:00:01.234Z, zoneOffset=null, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MenstruationPeriodRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MenstruationPeriodRecordTest.kt
index a031398..f52ea57 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MenstruationPeriodRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MenstruationPeriodRecordTest.kt
@@ -91,4 +91,20 @@
             endZoneOffset = null,
         )
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                MenstruationPeriodRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "MenstruationPeriodRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/NutritionRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/NutritionRecordTest.kt
index d2ffee1..9c1e231 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/NutritionRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/NutritionRecordTest.kt
@@ -17,6 +17,7 @@
 package androidx.health.connect.client.records
 
 import androidx.health.connect.client.units.Energy
+import androidx.health.connect.client.units.calories
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
@@ -82,4 +83,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                NutritionRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        energy = 240.calories
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "NutritionRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, biotin=null, caffeine=null, calcium=null, energy=240.0 cal, energyFromFat=null, chloride=null, cholesterol=null, chromium=null, copper=null, dietaryFiber=null, folate=null, folicAcid=null, iodine=null, iron=null, magnesium=null, manganese=null, molybdenum=null, monounsaturatedFat=null, niacin=null, pantothenicAcid=null, phosphorus=null, polyunsaturatedFat=null, potassium=null, protein=null, riboflavin=null, saturatedFat=null, selenium=null, sodium=null, sugar=null, thiamin=null, totalCarbohydrate=null, totalFat=null, transFat=null, unsaturatedFat=null, vitaminA=null, vitaminB12=null, vitaminB6=null, vitaminC=null, vitaminD=null, vitaminE=null, vitaminK=null, zinc=null, name=null, mealType=0, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt
index 7e64ee6..f28e447 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt
@@ -16,8 +16,10 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.records.metadata.Metadata
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import java.time.Instant
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -33,4 +35,20 @@
         assertThat(OvulationTestRecord.RESULT_INT_TO_STRING_MAP.keys)
             .containsExactlyElementsIn(allEnums)
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                OvulationTestRecord(
+                        time = Instant.ofEpochMilli(1234L),
+                        zoneOffset = null,
+                        result = OvulationTestRecord.RESULT_INCONCLUSIVE,
+                        metadata = Metadata.EMPTY,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "OvulationTestRecord(time=1970-01-01T00:00:01.234Z, zoneOffset=null, result=0, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/PowerRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/PowerRecordTest.kt
index bfbe52f..2369979 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/PowerRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/PowerRecordTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.units.watts
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
@@ -82,4 +83,22 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                PowerRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        samples =
+                            listOf(PowerRecord.Sample(Instant.ofEpochMilli(1234L), 240.0.watts))
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "PowerRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, samples=[Sample(time=1970-01-01T00:00:01.234Z, power=240.0 Watts)], metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt
index 78f9bd6..604bb60 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt
@@ -16,8 +16,11 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.records.metadata.Metadata
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -33,4 +36,20 @@
         Truth.assertThat(SexualActivityRecord.PROTECTION_USED_INT_TO_STRING_MAP.keys)
             .containsExactlyElementsIn(allEnums)
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                SexualActivityRecord(
+                        time = Instant.ofEpochMilli(1234L),
+                        zoneOffset = null,
+                        protectionUsed = SexualActivityRecord.PROTECTION_USED_PROTECTED,
+                        metadata = Metadata.EMPTY,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "SexualActivityRecord(time=1970-01-01T00:00:01.234Z, zoneOffset=null, protectionUsed=1, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SkinTemperatureRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SkinTemperatureRecordTest.kt
new file mode 100644
index 0000000..b2df436
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SkinTemperatureRecordTest.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.records
+
+import androidx.health.connect.client.units.Temperature
+import androidx.health.connect.client.units.TemperatureDelta
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SkinTemperatureRecordTest {
+
+    @Test
+    fun invalidTimes_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            SkinTemperatureRecord(
+                startTime = Instant.ofEpochMilli(1235L),
+                startZoneOffset = null,
+                endTime = Instant.ofEpochMilli(1234L),
+                endZoneOffset = null,
+                deltas = emptyList()
+            )
+        }
+    }
+
+    @Test
+    fun invalidBaseline_lessThanLimit_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            SkinTemperatureRecord(
+                startTime = Instant.ofEpochMilli(1234L),
+                startZoneOffset = null,
+                endTime = Instant.ofEpochMilli(1236L),
+                endZoneOffset = null,
+                baseline = Temperature.celsius(-0.1),
+                deltas = emptyList()
+            )
+        }
+    }
+
+    @Test
+    fun invalidBaseline_moreThanLimit_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            SkinTemperatureRecord(
+                startTime = Instant.ofEpochMilli(1234L),
+                startZoneOffset = null,
+                endTime = Instant.ofEpochMilli(1236L),
+                endZoneOffset = null,
+                baseline = Temperature.celsius(100.1),
+                deltas = emptyList()
+            )
+        }
+    }
+
+    @Test
+    fun invalidDeltaTime_beforeStartTime_throws() {
+        val delta =
+            SkinTemperatureRecord.Delta(
+                time = Instant.ofEpochMilli(1231L),
+                delta = TemperatureDelta.celsius(2.0)
+            )
+
+        assertFailsWith<IllegalArgumentException> {
+            SkinTemperatureRecord(
+                startTime = Instant.ofEpochMilli(1234L),
+                startZoneOffset = null,
+                endTime = Instant.ofEpochMilli(1236L),
+                endZoneOffset = null,
+                deltas = listOf(delta)
+            )
+        }
+    }
+
+    @Test
+    fun invalidDeltaTime_afterEndTime_throws() {
+        val delta =
+            SkinTemperatureRecord.Delta(
+                time = Instant.ofEpochMilli(1237L),
+                delta = TemperatureDelta.celsius(2.0)
+            )
+
+        assertFailsWith<IllegalArgumentException> {
+            SkinTemperatureRecord(
+                startTime = Instant.ofEpochMilli(1234L),
+                startZoneOffset = null,
+                endTime = Instant.ofEpochMilli(1236L),
+                endZoneOffset = null,
+                deltas = listOf(delta)
+            )
+        }
+    }
+
+    @Test
+    fun invalidDelta_lessThanLimit_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            SkinTemperatureRecord.Delta(
+                time = Instant.ofEpochMilli(1237L),
+                delta = TemperatureDelta.celsius(-30.1)
+            )
+        }
+    }
+
+    @Test
+    fun invalidDelta_moreThanLimit_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            SkinTemperatureRecord.Delta(
+                time = Instant.ofEpochMilli(1237L),
+                delta = TemperatureDelta.celsius(30.1)
+            )
+        }
+    }
+
+    @Test
+    fun validRecords_equals() {
+        assertThat(
+                SkinTemperatureRecord(
+                    startTime = Instant.ofEpochMilli(1234L),
+                    startZoneOffset = null,
+                    endTime = Instant.ofEpochMilli(1236L),
+                    endZoneOffset = null,
+                    deltas =
+                        listOf(
+                            SkinTemperatureRecord.Delta(
+                                time = Instant.ofEpochMilli(1234L),
+                                delta = TemperatureDelta.celsius(2.0)
+                            )
+                        )
+                )
+            )
+            .isEqualTo(
+                SkinTemperatureRecord(
+                    startTime = Instant.ofEpochMilli(1234L),
+                    startZoneOffset = null,
+                    endTime = Instant.ofEpochMilli(1236L),
+                    endZoneOffset = null,
+                    deltas =
+                        listOf(
+                            SkinTemperatureRecord.Delta(
+                                time = Instant.ofEpochMilli(1234L),
+                                delta = TemperatureDelta.celsius(2.0)
+                            )
+                        )
+                )
+            )
+    }
+
+    @Test
+    fun validRecords_hash_equals() {
+        assertThat(
+                SkinTemperatureRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        deltas =
+                            listOf(
+                                SkinTemperatureRecord.Delta(
+                                    time = Instant.ofEpochMilli(1234L),
+                                    delta = TemperatureDelta.celsius(2.0)
+                                )
+                            )
+                    )
+                    .hashCode()
+            )
+            .isEqualTo(
+                SkinTemperatureRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        deltas =
+                            listOf(
+                                SkinTemperatureRecord.Delta(
+                                    time = Instant.ofEpochMilli(1234L),
+                                    delta = TemperatureDelta.celsius(2.0)
+                                )
+                            )
+                    )
+                    .hashCode()
+            )
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SleepSessionRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SleepSessionRecordTest.kt
index dea278d..8890862 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SleepSessionRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SleepSessionRecordTest.kt
@@ -176,4 +176,30 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                SleepSessionRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        title = "title",
+                        notes = "note",
+                        stages =
+                            listOf(
+                                SleepSessionRecord.Stage(
+                                    startTime = Instant.ofEpochMilli(1234),
+                                    endTime = Instant.ofEpochMilli(1236),
+                                    stage = SleepSessionRecord.STAGE_TYPE_DEEP,
+                                ),
+                            ),
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "SleepSessionRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, title=title, notes=note, stages=[Stage(startTime=1970-01-01T00:00:01.234Z, endTime=1970-01-01T00:00:01.236Z, stage=5)], metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SpeedRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SpeedRecordTest.kt
index a398458..82bcf12 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SpeedRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SpeedRecordTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.units.milesPerHour
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
@@ -82,4 +83,22 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                SpeedRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        samples =
+                            listOf(SpeedRecord.Sample(Instant.ofEpochMilli(1234L), 24.milesPerHour))
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "SpeedRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, samples=[Sample(time=1970-01-01T00:00:01.234Z, speed=24.0 miles/h)], metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsCadenceRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsCadenceRecordTest.kt
index ecf828a..4d1bd55 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsCadenceRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsCadenceRecordTest.kt
@@ -82,4 +82,22 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                StepsCadenceRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        samples =
+                            listOf(StepsCadenceRecord.Sample(Instant.ofEpochMilli(1234L), 85.0))
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "StepsCadenceRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, samples=[Sample(time=1970-01-01T00:00:01.234Z, rate=85.0)], metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt
index 8e7d4a2..47159ffd 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/StepsRecordTest.kt
@@ -86,4 +86,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                StepsRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        count = 42,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "StepsRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, count=42, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecordTest.kt
index 393f670..4bfe2fc 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/TotalCaloriesBurnedRecordTest.kt
@@ -61,4 +61,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                TotalCaloriesBurnedRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        energy = 400.calories
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "TotalCaloriesBurnedRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, energy=400.0 cal, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt
index 3f531bd..b3e64f4 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt
@@ -16,8 +16,11 @@
 
 package androidx.health.connect.client.records
 
+import androidx.health.connect.client.records.metadata.Metadata
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -32,4 +35,21 @@
         Truth.assertThat(Vo2MaxRecord.MEASUREMENT_METHOD_INT_TO_STRING_MAP.keys)
             .containsExactlyElementsIn(allEnums)
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                Vo2MaxRecord(
+                        time = Instant.ofEpochMilli(1234L),
+                        zoneOffset = null,
+                        vo2MillilitersPerMinuteKilogram = 95.0,
+                        measurementMethod = Vo2MaxRecord.MEASUREMENT_METHOD_ROCKPORT_FITNESS_TEST,
+                        metadata = Metadata.EMPTY,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "Vo2MaxRecord(time=1970-01-01T00:00:01.234Z, zoneOffset=null, vo2MillilitersPerMinuteKilogram=95.0, measurementMethod=5, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/WheelchairPushesRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/WheelchairPushesRecordTest.kt
index f50dd03..10046d1 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/WheelchairPushesRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/WheelchairPushesRecordTest.kt
@@ -60,4 +60,21 @@
             )
         }
     }
+
+    @Test
+    fun toString_containsMembers() {
+        assertThat(
+                WheelchairPushesRecord(
+                        startTime = Instant.ofEpochMilli(1234L),
+                        startZoneOffset = null,
+                        endTime = Instant.ofEpochMilli(1236L),
+                        endZoneOffset = null,
+                        count = 42,
+                    )
+                    .toString()
+            )
+            .isEqualTo(
+                "WheelchairPushesRecord(startTime=1970-01-01T00:00:01.234Z, startZoneOffset=null, endTime=1970-01-01T00:00:01.236Z, endZoneOffset=null, count=42, metadata=Metadata(id='', dataOrigin=DataOrigin(packageName=''), lastModifiedTime=1970-01-01T00:00:00Z, clientRecordId=null, clientRecordVersion=0, device=null, recordingMethod=0))"
+            )
+    }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/TemperatureDeltaTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/TemperatureDeltaTest.kt
new file mode 100644
index 0000000..a80aa8c
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/TemperatureDeltaTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TemperatureDeltaTest {
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(TemperatureDelta.celsius(-1.0)).isEqualTo(TemperatureDelta.celsius(-1.0))
+        expect.that(TemperatureDelta.celsius(-1.0)).isEqualTo(TemperatureDelta.fahrenheit(-1.8))
+    }
+
+    @Test
+    fun equals_differentValues_areEqual() {
+        expect.that(TemperatureDelta.celsius(-1.0)).isNotEqualTo(TemperatureDelta.celsius(-1.1))
+        expect.that(TemperatureDelta.celsius(-1.0)).isNotEqualTo(TemperatureDelta.fahrenheit(-1.9))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(TemperatureDelta.celsius(1.0).hashCode())
+            .isEqualTo(TemperatureDelta.fahrenheit(1.8).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(TemperatureDelta.celsius(1.0).hashCode())
+            .isNotEqualTo(TemperatureDelta.fahrenheit(1.9).hashCode())
+    }
+
+    @Test
+    fun inCelsius_valueInFahrenheit_convertsValue() {
+        expect.that(TemperatureDelta.fahrenheit(1.8).inCelsius).isEqualTo(1.0)
+    }
+
+    @Test
+    fun inCelsius_valueInCelsius_returnsValue() {
+        expect.that(TemperatureDelta.celsius(1.0).inCelsius).isEqualTo(1.0)
+    }
+
+    @Test
+    fun inFahrenheit_valueInFahrenheit_returnsValue() {
+        expect.that(TemperatureDelta.fahrenheit(1.8).inFahrenheit).isEqualTo(1.8)
+    }
+
+    @Test
+    fun inFahrenheit_valueInCelsius_convertsValue() {
+        expect.that(TemperatureDelta.celsius(1.0).inFahrenheit).isEqualTo(1.8)
+    }
+}
diff --git a/health/connect/connect-testing/api/current.txt b/health/connect/connect-testing/api/current.txt
index 0183341..cc14015 100644
--- a/health/connect/connect-testing/api/current.txt
+++ b/health/connect/connect-testing/api/current.txt
@@ -2,10 +2,11 @@
 package androidx.health.connect.client.testing {
 
   @SuppressCompatibility @androidx.health.connect.client.ExperimentalHealthConnectApi public final class FakeHealthConnectClient implements androidx.health.connect.client.HealthConnectClient {
+    ctor public FakeHealthConnectClient();
     ctor public FakeHealthConnectClient(optional String packageName, optional java.time.Clock clock, optional androidx.health.connect.client.PermissionController permissionController);
     method public suspend Object? aggregate(androidx.health.connect.client.request.AggregateRequest request, kotlin.coroutines.Continuation<? super androidx.health.connect.client.aggregate.AggregationResult>);
-    method public suspend Object? aggregateGroupByDuration(androidx.health.connect.client.request.AggregateGroupByDurationRequest request, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration>>);
-    method public suspend Object? aggregateGroupByPeriod(androidx.health.connect.client.request.AggregateGroupByPeriodRequest request, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod>>);
+    method public suspend Object? aggregateGroupByDuration(androidx.health.connect.client.request.AggregateGroupByDurationRequest request, kotlin.coroutines.Continuation<? super java.util.List<androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration>>);
+    method public suspend Object? aggregateGroupByPeriod(androidx.health.connect.client.request.AggregateGroupByPeriodRequest request, kotlin.coroutines.Continuation<? super java.util.List<androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod>>);
     method public suspend Object? deleteRecords(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType, androidx.health.connect.client.time.TimeRangeFilter timeRangeFilter, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public suspend Object? deleteRecords(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType, java.util.List<java.lang.String> recordIdsList, java.util.List<java.lang.String> clientRecordIdsList, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public void expireToken(String token);
@@ -28,8 +29,9 @@
   }
 
   @SuppressCompatibility @androidx.health.connect.client.ExperimentalHealthConnectApi public final class FakePermissionController implements androidx.health.connect.client.PermissionController {
+    ctor public FakePermissionController();
     ctor public FakePermissionController(optional boolean grantAll);
-    method public suspend Object? getGrantedPermissions(kotlin.coroutines.Continuation<? super java.util.Set<? extends java.lang.String>>);
+    method public suspend Object? getGrantedPermissions(kotlin.coroutines.Continuation<? super java.util.Set<java.lang.String>>);
     method public void grantPermission(String permission);
     method public void grantPermissions(java.util.Set<java.lang.String> permission);
     method public void replaceGrantedPermissions(java.util.Set<java.lang.String> permissions);
diff --git a/health/connect/connect-testing/api/restricted_current.txt b/health/connect/connect-testing/api/restricted_current.txt
index 0183341..cc14015 100644
--- a/health/connect/connect-testing/api/restricted_current.txt
+++ b/health/connect/connect-testing/api/restricted_current.txt
@@ -2,10 +2,11 @@
 package androidx.health.connect.client.testing {
 
   @SuppressCompatibility @androidx.health.connect.client.ExperimentalHealthConnectApi public final class FakeHealthConnectClient implements androidx.health.connect.client.HealthConnectClient {
+    ctor public FakeHealthConnectClient();
     ctor public FakeHealthConnectClient(optional String packageName, optional java.time.Clock clock, optional androidx.health.connect.client.PermissionController permissionController);
     method public suspend Object? aggregate(androidx.health.connect.client.request.AggregateRequest request, kotlin.coroutines.Continuation<? super androidx.health.connect.client.aggregate.AggregationResult>);
-    method public suspend Object? aggregateGroupByDuration(androidx.health.connect.client.request.AggregateGroupByDurationRequest request, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration>>);
-    method public suspend Object? aggregateGroupByPeriod(androidx.health.connect.client.request.AggregateGroupByPeriodRequest request, kotlin.coroutines.Continuation<? super java.util.List<? extends androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod>>);
+    method public suspend Object? aggregateGroupByDuration(androidx.health.connect.client.request.AggregateGroupByDurationRequest request, kotlin.coroutines.Continuation<? super java.util.List<androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration>>);
+    method public suspend Object? aggregateGroupByPeriod(androidx.health.connect.client.request.AggregateGroupByPeriodRequest request, kotlin.coroutines.Continuation<? super java.util.List<androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod>>);
     method public suspend Object? deleteRecords(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType, androidx.health.connect.client.time.TimeRangeFilter timeRangeFilter, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public suspend Object? deleteRecords(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType, java.util.List<java.lang.String> recordIdsList, java.util.List<java.lang.String> clientRecordIdsList, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public void expireToken(String token);
@@ -28,8 +29,9 @@
   }
 
   @SuppressCompatibility @androidx.health.connect.client.ExperimentalHealthConnectApi public final class FakePermissionController implements androidx.health.connect.client.PermissionController {
+    ctor public FakePermissionController();
     ctor public FakePermissionController(optional boolean grantAll);
-    method public suspend Object? getGrantedPermissions(kotlin.coroutines.Continuation<? super java.util.Set<? extends java.lang.String>>);
+    method public suspend Object? getGrantedPermissions(kotlin.coroutines.Continuation<? super java.util.Set<java.lang.String>>);
     method public void grantPermission(String permission);
     method public void grantPermissions(java.util.Set<java.lang.String> permission);
     method public void replaceGrantedPermissions(java.util.Set<java.lang.String> permissions);
diff --git a/health/health-services-client/api/current.txt b/health/health-services-client/api/current.txt
index 2fd8af7..944d55a 100644
--- a/health/health-services-client/api/current.txt
+++ b/health/health-services-client/api/current.txt
@@ -721,6 +721,7 @@
   }
 
   public final class GolfExerciseTypeConfig extends androidx.health.services.client.data.ExerciseTypeConfig {
+    ctor public GolfExerciseTypeConfig();
     ctor public GolfExerciseTypeConfig(optional androidx.health.services.client.data.GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo golfShotTrackingPlaceInfo);
     method public androidx.health.services.client.data.GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo getGolfShotTrackingPlaceInfo();
     property public final androidx.health.services.client.data.GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo golfShotTrackingPlaceInfo;
diff --git a/health/health-services-client/api/restricted_current.txt b/health/health-services-client/api/restricted_current.txt
index 6b19869..24ce359 100644
--- a/health/health-services-client/api/restricted_current.txt
+++ b/health/health-services-client/api/restricted_current.txt
@@ -723,6 +723,7 @@
   }
 
   public final class GolfExerciseTypeConfig extends androidx.health.services.client.data.ExerciseTypeConfig {
+    ctor public GolfExerciseTypeConfig();
     ctor public GolfExerciseTypeConfig(optional androidx.health.services.client.data.GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo golfShotTrackingPlaceInfo);
     method public androidx.health.services.client.data.GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo getGolfShotTrackingPlaceInfo();
     property public final androidx.health.services.client.data.GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo golfShotTrackingPlaceInfo;
diff --git a/health/health-services-client/build.gradle b/health/health-services-client/build.gradle
index 038845e..99f4198 100644
--- a/health/health-services-client/build.gradle
+++ b/health/health-services-client/build.gradle
@@ -34,7 +34,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesCore)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(libs.guavaListenableFuture)
     implementation(libs.guavaAndroid)
     implementation("androidx.core:core-ktx:1.7.0")
@@ -87,6 +87,5 @@
     mavenVersion = LibraryVersions.HEALTH_SERVICES_CLIENT
     inceptionYear = "2021"
     description = "This library helps developers create performant health applications in a platform agnostic way"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/heifwriter/heifwriter/build.gradle b/heifwriter/heifwriter/build.gradle
index 856a0a0..530763e 100644
--- a/heifwriter/heifwriter/build.gradle
+++ b/heifwriter/heifwriter/build.gradle
@@ -20,7 +20,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
@@ -34,5 +34,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Support HeifWriter for writing HEIF still images"
-    metalavaK2UastEnabled = true
 }
diff --git a/hilt/hilt-common/build.gradle b/hilt/hilt-common/build.gradle
index f3b9057..a8db29a 100644
--- a/hilt/hilt-common/build.gradle
+++ b/hilt/hilt-common/build.gradle
@@ -42,5 +42,4 @@
     // TODO(danysantiago): Remove once @ViewModelInject is deleted.
     // This suppresses the the warning in the generated code by Hilt's GeneratesRootInput processor.
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/hilt/hilt-navigation-compose/build.gradle b/hilt/hilt-navigation-compose/build.gradle
index cba9404..0d2dbc8 100644
--- a/hilt/hilt-navigation-compose/build.gradle
+++ b/hilt/hilt-navigation-compose/build.gradle
@@ -33,6 +33,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         testInstrumentationRunner "androidx.hilt.navigation.compose.TestRunner"
     }
@@ -76,6 +77,5 @@
     inceptionYear = "2021"
     description = "Navigation Compose Hilt Integration"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     samples(projectOrArtifact(":hilt:hilt-navigation-compose-samples"))
 }
diff --git a/hilt/hilt-navigation-compose/samples/build.gradle b/hilt/hilt-navigation-compose/samples/build.gradle
index 95f9a44..cb0491f 100644
--- a/hilt/hilt-navigation-compose/samples/build.gradle
+++ b/hilt/hilt-navigation-compose/samples/build.gradle
@@ -47,5 +47,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.hilt.navigation.compose.samples"
 }
diff --git a/hilt/hilt-navigation-fragment/build.gradle b/hilt/hilt-navigation-fragment/build.gradle
index 326f2e4..5dc55cc1 100644
--- a/hilt/hilt-navigation-fragment/build.gradle
+++ b/hilt/hilt-navigation-fragment/build.gradle
@@ -68,6 +68,5 @@
     mavenVersion = LibraryVersions.HILT_NAVIGATION
     inceptionYear = "2021"
     description = "Android Navigation Fragment Hilt Extension"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/hilt/hilt-navigation/build.gradle b/hilt/hilt-navigation/build.gradle
index 7ad2aa4..2e4f65b 100644
--- a/hilt/hilt-navigation/build.gradle
+++ b/hilt/hilt-navigation/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.navigation:navigation-runtime:2.5.1")
     api(libs.hiltAndroid)
     ksp(libs.hiltCompiler)
@@ -44,7 +44,6 @@
     mavenVersion = LibraryVersions.HILT_NAVIGATION
     inceptionYear = "2021"
     description = "Android Navigation Hilt Extension"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/hilt/hilt-work/build.gradle b/hilt/hilt-work/build.gradle
index d38477c..26056f9 100644
--- a/hilt/hilt-work/build.gradle
+++ b/hilt/hilt-work/build.gradle
@@ -36,7 +36,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":hilt:hilt-common"))
     api("androidx.work:work-runtime:2.3.4")
     api(libs.hiltAndroid)
@@ -49,5 +49,4 @@
     mavenVersion = LibraryVersions.HILT
     inceptionYear = "2020"
     description = "Android Lifecycle WorkManager Hilt Extension"
-    metalavaK2UastEnabled = true
 }
diff --git a/hilt/integration-tests/viewmodelapp/build.gradle b/hilt/integration-tests/viewmodelapp/build.gradle
index 0db4203..12eec0a 100644
--- a/hilt/integration-tests/viewmodelapp/build.gradle
+++ b/hilt/integration-tests/viewmodelapp/build.gradle
@@ -23,6 +23,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         testInstrumentationRunner "androidx.hilt.integration.viewmodelapp.TestRunner"
     }
diff --git a/hilt/integration-tests/workerapp/build.gradle b/hilt/integration-tests/workerapp/build.gradle
index 391f756..b277151 100644
--- a/hilt/integration-tests/workerapp/build.gradle
+++ b/hilt/integration-tests/workerapp/build.gradle
@@ -23,6 +23,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         testInstrumentationRunner "androidx.hilt.integration.workerapp.TestRunner"
     }
diff --git a/ink/ink-brush/build.gradle b/ink/ink-brush/build.gradle
index 8083aae..8f0e941 100644
--- a/ink/ink-brush/build.gradle
+++ b/ink/ink-brush/build.gradle
@@ -71,7 +71,8 @@
 }
 
 android {
-  namespace = "androidx.ink.brush"
+    compileSdk 35
+    namespace = "androidx.ink.brush"
 }
 
 androidx {
@@ -79,4 +80,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2024"
     description = "Define brushes for freehand input."
+    metalavaK2UastEnabled = false
 }
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/ink/ink-geometry/api/current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to ink/ink-geometry/api/current.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/ink/ink-geometry/api/res-current.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to ink/ink-geometry/api/res-current.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/ink/ink-geometry/api/restricted_current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to ink/ink-geometry/api/restricted_current.txt
diff --git a/ink/ink-geometry/build.gradle b/ink/ink-geometry/build.gradle
new file mode 100644
index 0000000..ef4460c
--- /dev/null
+++ b/ink/ink-geometry/build.gradle
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+import androidx.build.PlatformIdentifier
+
+plugins {
+  id("AndroidXPlugin")
+  id("com.android.library")
+}
+
+androidXMultiplatform {
+  jvm()
+  android()
+
+  defaultPlatform(PlatformIdentifier.JVM)
+
+  sourceSets {
+    commonMain {
+      dependencies {
+        api(libs.kotlinStdlib)
+
+        implementation("androidx.annotation:annotation:1.8.0")
+      }
+    }
+
+    jvmAndroidMain {
+      dependsOn(commonMain)
+      dependencies {
+        implementation(project(":ink:ink-nativeloader"))
+      }
+    }
+
+    jvmAndroidTest {
+      dependencies {
+        implementation(libs.junit)
+        implementation(libs.truth)
+      }
+    }
+
+    jvmMain {
+      dependsOn(jvmAndroidMain)
+    }
+
+    jvmTest {
+      dependsOn(jvmAndroidTest)
+    }
+
+    androidMain {
+      dependsOn(jvmAndroidMain)
+    }
+
+    androidInstrumentedTest {
+      dependsOn(jvmAndroidTest)
+      dependencies {
+        implementation(libs.testRunner)
+      }
+    }
+  }
+}
+
+android {
+  namespace = "androidx.ink.geometry"
+}
+
+androidx {
+    name = "Ink Geometry"
+    type = LibraryType.PUBLISHED_LIBRARY
+    inceptionYear = "2024"
+    description = "Create beautiful strokes"
+}
diff --git a/ink/ink-geometry/src/jvmAndroidMain/kotlin/androidx/ink/geometry/Angle.kt b/ink/ink-geometry/src/jvmAndroidMain/kotlin/androidx/ink/geometry/Angle.kt
new file mode 100644
index 0000000..ad3c2f2
--- /dev/null
+++ b/ink/ink-geometry/src/jvmAndroidMain/kotlin/androidx/ink/geometry/Angle.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ink.geometry
+
+import androidx.annotation.RestrictTo
+import androidx.ink.nativeloader.NativeLoader
+
+/**
+ * A utility for working with a signed angle. A positive value represents rotation from the positive
+ * x-axis to the positive y-axis. Angle functions manage the conversion of angle values in degrees
+ * and radians. Most of Strokes API requires angle values in radians.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public object Angle {
+
+    init {
+        NativeLoader.load()
+    }
+
+    @JvmStatic
+    @AngleRadiansFloat
+    public fun degreesToRadians(degrees: Float): Float = degrees * RADIANS_PER_DEGREE
+
+    @JvmStatic
+    @AngleRadiansFloat
+    public fun degreesToRadians(degrees: Int): Float = degrees.toFloat() * RADIANS_PER_DEGREE
+
+    @JvmStatic
+    @AngleRadiansFloat
+    public fun degreesToRadians(degrees: Double): Float = degrees.toFloat() * RADIANS_PER_DEGREE
+
+    @JvmStatic
+    public fun radiansToDegrees(@AngleRadiansFloat radians: Float): Float =
+        radians * DEGREES_PER_RADIAN
+
+    @JvmStatic
+    public fun normalized(@AngleRadiansFloat radians: Float): Float = nativeNormalized(radians)
+
+    @JvmStatic
+    public fun normalizedAboutZero(@AngleRadiansFloat radians: Float): Float =
+        nativeNormalizedAboutZero(radians)
+
+    private const val PI = Math.PI.toFloat()
+    private const val HALF_PI = (Math.PI / 2.0).toFloat()
+    private const val TWO_PI = (2 * Math.PI).toFloat()
+    private const val DEGREES_PER_RADIAN = 180.0f / Math.PI.toFloat()
+    private const val RADIANS_PER_DEGREE = Math.PI.toFloat() / 180.0f
+
+    /** Angle of zero radians. */
+    @JvmField @AngleRadiansFloat public val ZERO: Float = 0.0f
+    /** Angle of PI radians. */
+    @JvmField @AngleRadiansFloat public val PI_RADIANS: Float = PI
+    /** Angle of PI/2 radians. */
+    @JvmField @AngleRadiansFloat public val HALF_PI_RADIANS: Float = HALF_PI
+    /** Angle of 2*PI radians. */
+    @JvmField @AngleRadiansFloat public val TWO_PI_RADIANS: Float = TWO_PI
+
+    private external fun nativeNormalized(radians: Float): Float
+
+    private external fun nativeNormalizedAboutZero(radians: Float): Float
+}
diff --git a/ink/ink-geometry/src/jvmAndroidMain/kotlin/androidx/ink/geometry/AngleRadiansFloat.kt b/ink/ink-geometry/src/jvmAndroidMain/kotlin/androidx/ink/geometry/AngleRadiansFloat.kt
new file mode 100644
index 0000000..368f6dd
--- /dev/null
+++ b/ink/ink-geometry/src/jvmAndroidMain/kotlin/androidx/ink/geometry/AngleRadiansFloat.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ink.geometry
+
+import androidx.annotation.RestrictTo
+import kotlin.annotation.AnnotationRetention
+import kotlin.annotation.MustBeDocumented
+import kotlin.annotation.Retention
+import kotlin.annotation.Target
+
+/**
+ * A signed angle in radians. A positive value represents rotation from the positive x-axis to the
+ * positive y-axis. [Angle] class manages the conversion of angle values in degrees and radians with
+ * [Angle.radiansToDegrees] and [Angle.degreesToRadians]. Most of Strokes API requires angle values
+ * in radians.
+ */
+@MustBeDocumented
+@Retention(AnnotationRetention.SOURCE)
+@Target(
+    AnnotationTarget.VALUE_PARAMETER,
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY_GETTER,
+    AnnotationTarget.PROPERTY_SETTER,
+    AnnotationTarget.LOCAL_VARIABLE,
+    AnnotationTarget.FIELD,
+)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public annotation class AngleRadiansFloat
diff --git a/ink/ink-geometry/src/jvmAndroidTest/kotlin/androidx/ink/geometry/AngleTest.kt b/ink/ink-geometry/src/jvmAndroidTest/kotlin/androidx/ink/geometry/AngleTest.kt
new file mode 100644
index 0000000..88381a2
--- /dev/null
+++ b/ink/ink-geometry/src/jvmAndroidTest/kotlin/androidx/ink/geometry/AngleTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ink.geometry
+
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.PI
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class AngleTest {
+
+    @Test
+    fun degreesToRadians() {
+        assertThat(Angle.degreesToRadians(180f)).isEqualTo(Math.PI.toFloat())
+        assertThat(Angle.degreesToRadians(180.toDouble())).isEqualTo(Math.PI.toFloat())
+        assertThat(Angle.degreesToRadians(180.toInt())).isEqualTo(Math.PI.toFloat())
+    }
+
+    @Test
+    fun radiansToDegrees() {
+        assertThat(Angle.radiansToDegrees(Math.PI.toFloat())).isEqualTo(180f)
+    }
+
+    @Test
+    fun constants_areCorrect() {
+        assertThat(Angle.ZERO).isEqualTo(0f)
+        assertThat(Angle.PI_RADIANS).isEqualTo(Math.PI.toFloat())
+        assertThat(Angle.TWO_PI_RADIANS).isEqualTo((Math.PI * 2).toFloat())
+        assertThat(Angle.HALF_PI_RADIANS).isEqualTo((Math.PI / 2).toFloat())
+    }
+
+    @Test
+    fun normalized_returnsValueFromJni() {
+        assertThat(Angle.normalized(Angle.ZERO)).isEqualTo(0f)
+        assertThat(Angle.normalized(-Angle.PI_RADIANS)).isWithin(1e-6F).of(Math.PI.toFloat())
+    }
+
+    @Test
+    fun normalizedAboutZero_returnsValueFromJni() {
+        assertThat(Angle.normalizedAboutZero(Angle.ZERO)).isEqualTo(0f)
+        assertThat(Angle.normalizedAboutZero(Angle.TWO_PI_RADIANS - Angle.HALF_PI_RADIANS))
+            .isWithin(1e-6F)
+            .of(-Math.PI.toFloat() / 2F)
+    }
+}
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/ink/ink-nativeloader/api/current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to ink/ink-nativeloader/api/current.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/ink/ink-nativeloader/api/res-current.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to ink/ink-nativeloader/api/res-current.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/ink/ink-nativeloader/api/restricted_current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to ink/ink-nativeloader/api/restricted_current.txt
diff --git a/ink/ink-nativeloader/build.gradle b/ink/ink-nativeloader/build.gradle
new file mode 100644
index 0000000..103dc2d
--- /dev/null
+++ b/ink/ink-nativeloader/build.gradle
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+import androidx.build.PlatformIdentifier
+import androidx.build.AndroidXConfig
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+androidXMultiplatform {
+  jvm()
+  android()
+
+  defaultPlatform(PlatformIdentifier.JVM)
+
+  sourceSets {
+    commonMain {
+      dependencies {
+        api(libs.kotlinStdlib)
+
+        implementation("androidx.annotation:annotation:1.8.0")
+      }
+    }
+
+    jvmMain {
+      dependsOn(commonMain)
+      resources.srcDir(new File(
+          AndroidXConfig.getPrebuiltsRoot(project),
+          "androidx/ink/jvm"))
+    }
+
+    androidMain {
+      dependsOn(commonMain)
+    }
+  }
+}
+
+android {
+  namespace = "androidx.ink.nativeloader"
+  sourceSets {
+    main.jniLibs.srcDirs += new File(
+        AndroidXConfig.getPrebuiltsRoot(project),
+        "androidx/ink/android")
+  }
+}
+
+androidx {
+    name = "Ink Native Loader"
+    type = LibraryType.PUBLISHED_LIBRARY
+    inceptionYear = "2024"
+    description = "Native dependencies for Ink"
+}
diff --git a/ink/ink-nativeloader/src/androidMain/kotlin/androidx/ink/nativeloader/Nativeloader.android.kt b/ink/ink-nativeloader/src/androidMain/kotlin/androidx/ink/nativeloader/Nativeloader.android.kt
new file mode 100644
index 0000000..da6d80d
--- /dev/null
+++ b/ink/ink-nativeloader/src/androidMain/kotlin/androidx/ink/nativeloader/Nativeloader.android.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ink.nativeloader
+
+import androidx.annotation.RestrictTo
+
+/** Native code loader for Android. */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+actual public object NativeLoader {
+    actual public fun load() {
+        // If we're on Android, this should automatically pull in the correct architecture variant
+        // of libink.so.
+        System.loadLibrary("ink")
+    }
+}
diff --git a/ink/ink-nativeloader/src/commonMain/kotlin/androidx/ink/nativeloader/NativeLoader.kt b/ink/ink-nativeloader/src/commonMain/kotlin/androidx/ink/nativeloader/NativeLoader.kt
new file mode 100644
index 0000000..203a018
--- /dev/null
+++ b/ink/ink-nativeloader/src/commonMain/kotlin/androidx/ink/nativeloader/NativeLoader.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ink.nativeloader
+
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+expect public object NativeLoader {
+    public fun load()
+}
diff --git a/ink/ink-nativeloader/src/jvmMain/kotlin/androidx/ink/nativeloader/Nativeloader.jvm.kt b/ink/ink-nativeloader/src/jvmMain/kotlin/androidx/ink/nativeloader/Nativeloader.jvm.kt
new file mode 100644
index 0000000..c3bacd3
--- /dev/null
+++ b/ink/ink-nativeloader/src/jvmMain/kotlin/androidx/ink/nativeloader/Nativeloader.jvm.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.ink.nativeloader
+
+import androidx.annotation.RestrictTo
+import java.nio.file.Files
+import java.nio.file.StandardCopyOption
+
+/** Native code loader for JVM. */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+actual public object NativeLoader {
+    actual public fun load() {
+        // On the JVM we need to find the correct libink library file in the JAR resources, copy it
+        // out to a tempfile, and load it directly.
+        //
+        // See
+        // https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt;l=24;drc=a70f25f13b00141438854309a0de47d537904522
+        // for a similar system.
+        val tempFile = Files.createTempFile("libink.so", null).apply { toFile().deleteOnExit() }
+        NativeLoader::class
+            .java
+            .classLoader!!
+            // If additional architectures need to be supported, construct the correct resource
+            // prefix for that platform.
+            .getResourceAsStream("linux-x86_64/libink.so")
+            .use { resourceStream ->
+                Files.copy(resourceStream, tempFile, StandardCopyOption.REPLACE_EXISTING)
+            }
+        System.load(tempFile.toFile().canonicalPath)
+    }
+}
diff --git a/input/input-motionprediction/build.gradle b/input/input-motionprediction/build.gradle
index 581e0a8..b15a852 100644
--- a/input/input-motionprediction/build.gradle
+++ b/input/input-motionprediction/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("androidx.core:core:1.10.1")
 
@@ -59,6 +59,5 @@
     mavenVersion = LibraryVersions.INPUT_MOTIONPREDICTION
     inceptionYear = "2022"
     description = "reduce the latency of input interactions by predicting future MotionEvents"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/PredictionEstimator.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/PredictionEstimator.java
index 6bbd88c..f23b7d3d 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/PredictionEstimator.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/PredictionEstimator.java
@@ -25,7 +25,6 @@
 import android.view.MotionEvent;
 import android.view.WindowManager;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -91,7 +90,6 @@
             // Not instantiable
         }
 
-        @DoNotInline
         static float getFastestFrameTimeMs(Display display) {
             float[] refreshRates = display.getSupportedRefreshRates();
             float largestRefreshRate = refreshRates[0];
@@ -112,7 +110,6 @@
             // Not instantiable
         }
 
-        @DoNotInline
         static float getFastestFrameTimeMs(Display display) {
             Display.Mode[] displayModes = display.getSupportedModes();
             float largestRefreshRate = displayModes[0].getRefreshRate();
@@ -134,7 +131,6 @@
             // Not instantiable
         }
 
-        @DoNotInline
         static Display getDisplayForContext(Context context) {
             return context.getDisplay();
         }
diff --git a/inspection/inspection-gradle-plugin/lint-baseline.xml b/inspection/inspection-gradle-plugin/lint-baseline.xml
index cae3d85..603e7bd 100644
--- a/inspection/inspection-gradle-plugin/lint-baseline.xml
+++ b/inspection/inspection-gradle-plugin/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="EagerGradleConfiguration"
         message="Avoid using method get"
-        errorLine1="        it.jars.from(jar.get().archiveFile)"
-        errorLine2="                         ~~~">
+        errorLine1="            it.jars.from(jar.get().archiveFile)"
+        errorLine2="                             ~~~">
         <location
             file="src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt"/>
     </issue>
@@ -40,8 +40,8 @@
     <issue
         id="WithPluginClasspathUsage"
         message="Avoid usage of GradleRunner#withPluginClasspath, which is broken. Instead use something like https://github.com/autonomousapps/dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
-        errorLine1="            .withPluginClasspath()"
-        errorLine2="             ~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            GradleRunner.create().withProjectDir(projectSetup.rootDir).withPluginClasspath()"
+        errorLine2="                                                                       ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/test/kotlin/androidx/inspection/gradle/InspectionPluginTest.kt"/>
     </issue>
diff --git a/inspection/inspection-testing/build.gradle b/inspection/inspection-testing/build.gradle
index 700c1a3..4aebd0f 100644
--- a/inspection/inspection-testing/build.gradle
+++ b/inspection/inspection-testing/build.gradle
@@ -32,7 +32,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":inspection:inspection"))
     implementation(libs.kotlinStdlib)
     implementation(libs.kotlinCoroutinesAndroid)
diff --git a/inspection/inspection/build.gradle b/inspection/inspection/build.gradle
index 8642940..204a479e 100644
--- a/inspection/inspection/build.gradle
+++ b/inspection/inspection/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
@@ -48,7 +48,6 @@
     runApiTasks =  new RunApiTasks.Yes(
             "Interfaces provided in this artifact should be binary compatible to guarantee " +
                     "that old inspectors are compatible with newer Android Studio versions")
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "Not shipped externally"
 }
diff --git a/interpolator/interpolator/build.gradle b/interpolator/interpolator/build.gradle
index ee1097b..ce31af1 100644
--- a/interpolator/interpolator/build.gradle
+++ b/interpolator/interpolator/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
@@ -21,7 +21,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/javascriptengine/javascriptengine/build.gradle b/javascriptengine/javascriptengine/build.gradle
index 734a6b2..29bac07 100644
--- a/javascriptengine/javascriptengine/build.gradle
+++ b/javascriptengine/javascriptengine/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.3.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.concurrent:concurrent-futures:1.0.0")
     api("androidx.core:core:1.1.0")
     api(libs.guavaAndroid)
@@ -68,5 +68,4 @@
     inceptionYear = "2022"
     description = "Javascript Engine is a static library you can add to your Android application in order to evaluate JavaScript."
     additionalDeviceTestApkKeys.add("chrome")
-    metalavaK2UastEnabled = true
 }
diff --git a/kruth/kruth/bcv/native/current.txt b/kruth/kruth/bcv/native/current.txt
new file mode 100644
index 0000000..8e53cb7
--- /dev/null
+++ b/kruth/kruth/bcv/native/current.txt
@@ -0,0 +1,322 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.kruth:kruth>
+abstract fun interface androidx.kruth/FailureStrategy { // androidx.kruth/FailureStrategy|null[0]
+    abstract fun fail(kotlin/AssertionError) // androidx.kruth/FailureStrategy.fail|fail(kotlin.AssertionError){}[0]
+}
+abstract interface androidx.kruth/Ordered { // androidx.kruth/Ordered|null[0]
+    abstract fun inOrder() // androidx.kruth/Ordered.inOrder|inOrder(){}[0]
+}
+final class <#A: kotlin/Any?> androidx.kruth/ObjectArraySubject : androidx.kruth/Subject<kotlin/Array<out #A>> { // androidx.kruth/ObjectArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<*> // androidx.kruth/ObjectArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/ObjectArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/ObjectArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/ObjectArraySubject.isNotEmpty|isNotEmpty(){}[0]
+}
+final class <#A: kotlin/AssertionError> androidx.kruth/TruthFailureSubject : androidx.kruth/ThrowableSubject<#A> { // androidx.kruth/TruthFailureSubject|null[0]
+    final fun factKeys(): androidx.kruth/IterableSubject<kotlin/String> // androidx.kruth/TruthFailureSubject.factKeys|factKeys(){}[0]
+    final fun factValue(kotlin/String): androidx.kruth/StringSubject // androidx.kruth/TruthFailureSubject.factValue|factValue(kotlin.String){}[0]
+    final fun factValue(kotlin/String, kotlin/Int): androidx.kruth/StringSubject // androidx.kruth/TruthFailureSubject.factValue|factValue(kotlin.String;kotlin.Int){}[0]
+    final object Companion { // androidx.kruth/TruthFailureSubject.Companion|null[0]
+        final fun <#A2: kotlin/AssertionError> truthFailures(): androidx.kruth/Subject.Factory<androidx.kruth/TruthFailureSubject<#A2>, #A2> // androidx.kruth/TruthFailureSubject.Companion.truthFailures|truthFailures(){0§<kotlin.AssertionError>}[0]
+    }
+}
+final class <#A: out androidx.kruth/Subject<#B>, #B: kotlin/Any?> androidx.kruth/SimpleSubjectBuilder { // androidx.kruth/SimpleSubjectBuilder|null[0]
+    final fun that(#B?): #A // androidx.kruth/SimpleSubjectBuilder.that|that(1:1?){}[0]
+}
+final class androidx.kruth/BooleanSubject : androidx.kruth/Subject<kotlin/Boolean> { // androidx.kruth/BooleanSubject|null[0]
+    final fun isFalse() // androidx.kruth/BooleanSubject.isFalse|isFalse(){}[0]
+    final fun isTrue() // androidx.kruth/BooleanSubject.isTrue|isTrue(){}[0]
+}
+final class androidx.kruth/DoubleSubject : androidx.kruth/ComparableSubject<kotlin/Double> { // androidx.kruth/DoubleSubject|null[0]
+    abstract class TolerantDoubleComparison { // androidx.kruth/DoubleSubject.TolerantDoubleComparison|null[0]
+        abstract fun of(kotlin/Double) // androidx.kruth/DoubleSubject.TolerantDoubleComparison.of|of(kotlin.Double){}[0]
+        open fun equals(kotlin/Any?): kotlin/Boolean // androidx.kruth/DoubleSubject.TolerantDoubleComparison.equals|equals(kotlin.Any?){}[0]
+        open fun hashCode(): kotlin/Int // androidx.kruth/DoubleSubject.TolerantDoubleComparison.hashCode|hashCode(){}[0]
+    }
+    final fun isAtLeast(kotlin/Int) // androidx.kruth/DoubleSubject.isAtLeast|isAtLeast(kotlin.Int){}[0]
+    final fun isAtMost(kotlin/Int) // androidx.kruth/DoubleSubject.isAtMost|isAtMost(kotlin.Int){}[0]
+    final fun isFinite() // androidx.kruth/DoubleSubject.isFinite|isFinite(){}[0]
+    final fun isGreaterThan(kotlin/Int) // androidx.kruth/DoubleSubject.isGreaterThan|isGreaterThan(kotlin.Int){}[0]
+    final fun isLessThan(kotlin/Int) // androidx.kruth/DoubleSubject.isLessThan|isLessThan(kotlin.Int){}[0]
+    final fun isNaN() // androidx.kruth/DoubleSubject.isNaN|isNaN(){}[0]
+    final fun isNegativeInfinity() // androidx.kruth/DoubleSubject.isNegativeInfinity|isNegativeInfinity(){}[0]
+    final fun isNonZero() // androidx.kruth/DoubleSubject.isNonZero|isNonZero(){}[0]
+    final fun isNotNaN() // androidx.kruth/DoubleSubject.isNotNaN|isNotNaN(){}[0]
+    final fun isNotWithin(kotlin/Double): androidx.kruth/DoubleSubject.TolerantDoubleComparison // androidx.kruth/DoubleSubject.isNotWithin|isNotWithin(kotlin.Double){}[0]
+    final fun isPositiveInfinity() // androidx.kruth/DoubleSubject.isPositiveInfinity|isPositiveInfinity(){}[0]
+    final fun isWithin(kotlin/Double): androidx.kruth/DoubleSubject.TolerantDoubleComparison // androidx.kruth/DoubleSubject.isWithin|isWithin(kotlin.Double){}[0]
+    final fun isZero() // androidx.kruth/DoubleSubject.isZero|isZero(){}[0]
+}
+final class androidx.kruth/Fact { // androidx.kruth/Fact|null[0]
+    final fun toString(): kotlin/String // androidx.kruth/Fact.toString|toString(){}[0]
+    final object Companion { // androidx.kruth/Fact.Companion|null[0]
+        final fun fact(kotlin/String, kotlin/Any?): androidx.kruth/Fact // androidx.kruth/Fact.Companion.fact|fact(kotlin.String;kotlin.Any?){}[0]
+        final fun simpleFact(kotlin/String): androidx.kruth/Fact // androidx.kruth/Fact.Companion.simpleFact|simpleFact(kotlin.String){}[0]
+    }
+}
+final class androidx.kruth/FailureMetadata // androidx.kruth/FailureMetadata|null[0]
+final class androidx.kruth/FloatSubject : androidx.kruth/ComparableSubject<kotlin/Float> { // androidx.kruth/FloatSubject|null[0]
+    abstract class TolerantFloatComparison { // androidx.kruth/FloatSubject.TolerantFloatComparison|null[0]
+        abstract fun of(kotlin/Float) // androidx.kruth/FloatSubject.TolerantFloatComparison.of|of(kotlin.Float){}[0]
+        open fun equals(kotlin/Any?): kotlin/Boolean // androidx.kruth/FloatSubject.TolerantFloatComparison.equals|equals(kotlin.Any?){}[0]
+        open fun hashCode(): kotlin/Int // androidx.kruth/FloatSubject.TolerantFloatComparison.hashCode|hashCode(){}[0]
+    }
+    final fun isAtLeast(kotlin/Int) // androidx.kruth/FloatSubject.isAtLeast|isAtLeast(kotlin.Int){}[0]
+    final fun isAtMost(kotlin/Int) // androidx.kruth/FloatSubject.isAtMost|isAtMost(kotlin.Int){}[0]
+    final fun isEqualTo(kotlin/Any?) // androidx.kruth/FloatSubject.isEqualTo|isEqualTo(kotlin.Any?){}[0]
+    final fun isEquivalentAccordingToCompareTo(kotlin/Float?) // androidx.kruth/FloatSubject.isEquivalentAccordingToCompareTo|isEquivalentAccordingToCompareTo(kotlin.Float?){}[0]
+    final fun isFinite() // androidx.kruth/FloatSubject.isFinite|isFinite(){}[0]
+    final fun isGreaterThan(kotlin/Int) // androidx.kruth/FloatSubject.isGreaterThan|isGreaterThan(kotlin.Int){}[0]
+    final fun isLessThan(kotlin/Int) // androidx.kruth/FloatSubject.isLessThan|isLessThan(kotlin.Int){}[0]
+    final fun isNaN() // androidx.kruth/FloatSubject.isNaN|isNaN(){}[0]
+    final fun isNegativeInfinity() // androidx.kruth/FloatSubject.isNegativeInfinity|isNegativeInfinity(){}[0]
+    final fun isNonZero() // androidx.kruth/FloatSubject.isNonZero|isNonZero(){}[0]
+    final fun isNotEqualTo(kotlin/Any?) // androidx.kruth/FloatSubject.isNotEqualTo|isNotEqualTo(kotlin.Any?){}[0]
+    final fun isNotNaN() // androidx.kruth/FloatSubject.isNotNaN|isNotNaN(){}[0]
+    final fun isNotWithin(kotlin/Float): androidx.kruth/FloatSubject.TolerantFloatComparison // androidx.kruth/FloatSubject.isNotWithin|isNotWithin(kotlin.Float){}[0]
+    final fun isPositiveInfinity() // androidx.kruth/FloatSubject.isPositiveInfinity|isPositiveInfinity(){}[0]
+    final fun isWithin(kotlin/Float): androidx.kruth/FloatSubject.TolerantFloatComparison // androidx.kruth/FloatSubject.isWithin|isWithin(kotlin.Float){}[0]
+    final fun isZero() // androidx.kruth/FloatSubject.isZero|isZero(){}[0]
+}
+final class androidx.kruth/PrimitiveBooleanArraySubject : androidx.kruth/Subject<kotlin/BooleanArray?> { // androidx.kruth/PrimitiveBooleanArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Boolean> // androidx.kruth/PrimitiveBooleanArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveBooleanArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveBooleanArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveBooleanArraySubject.isNotEmpty|isNotEmpty(){}[0]
+}
+final class androidx.kruth/PrimitiveByteArraySubject : androidx.kruth/Subject<kotlin/ByteArray?> { // androidx.kruth/PrimitiveByteArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Byte> // androidx.kruth/PrimitiveByteArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveByteArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveByteArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveByteArraySubject.isNotEmpty|isNotEmpty(){}[0]
+}
+final class androidx.kruth/PrimitiveCharArraySubject : androidx.kruth/Subject<kotlin/CharArray?> { // androidx.kruth/PrimitiveCharArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Char> // androidx.kruth/PrimitiveCharArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveCharArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveCharArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveCharArraySubject.isNotEmpty|isNotEmpty(){}[0]
+}
+final class androidx.kruth/PrimitiveDoubleArraySubject : androidx.kruth/Subject<kotlin/DoubleArray?> { // androidx.kruth/PrimitiveDoubleArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Double> // androidx.kruth/PrimitiveDoubleArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveDoubleArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveDoubleArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isEqualTo(kotlin/Any?) // androidx.kruth/PrimitiveDoubleArraySubject.isEqualTo|isEqualTo(kotlin.Any?){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveDoubleArraySubject.isNotEmpty|isNotEmpty(){}[0]
+    final fun isNotEqualTo(kotlin/Any?) // androidx.kruth/PrimitiveDoubleArraySubject.isNotEqualTo|isNotEqualTo(kotlin.Any?){}[0]
+}
+final class androidx.kruth/PrimitiveFloatArraySubject : androidx.kruth/Subject<kotlin/FloatArray?> { // androidx.kruth/PrimitiveFloatArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Float> // androidx.kruth/PrimitiveFloatArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveFloatArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveFloatArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isEqualTo(kotlin/Any?) // androidx.kruth/PrimitiveFloatArraySubject.isEqualTo|isEqualTo(kotlin.Any?){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveFloatArraySubject.isNotEmpty|isNotEmpty(){}[0]
+    final fun isNotEqualTo(kotlin/Any?) // androidx.kruth/PrimitiveFloatArraySubject.isNotEqualTo|isNotEqualTo(kotlin.Any?){}[0]
+}
+final class androidx.kruth/PrimitiveIntArraySubject : androidx.kruth/Subject<kotlin/IntArray?> { // androidx.kruth/PrimitiveIntArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Int> // androidx.kruth/PrimitiveIntArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveIntArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveIntArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveIntArraySubject.isNotEmpty|isNotEmpty(){}[0]
+}
+final class androidx.kruth/PrimitiveLongArraySubject : androidx.kruth/Subject<kotlin/LongArray?> { // androidx.kruth/PrimitiveLongArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Long> // androidx.kruth/PrimitiveLongArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveLongArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveLongArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveLongArraySubject.isNotEmpty|isNotEmpty(){}[0]
+}
+final class androidx.kruth/PrimitiveShortArraySubject : androidx.kruth/Subject<kotlin/ShortArray?> { // androidx.kruth/PrimitiveShortArraySubject|null[0]
+    final fun asList(): androidx.kruth/IterableSubject<kotlin/Short> // androidx.kruth/PrimitiveShortArraySubject.asList|asList(){}[0]
+    final fun hasLength(kotlin/Int) // androidx.kruth/PrimitiveShortArraySubject.hasLength|hasLength(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/PrimitiveShortArraySubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/PrimitiveShortArraySubject.isNotEmpty|isNotEmpty(){}[0]
+}
+final fun <#A: androidx.kruth/Subject<#B>, #B: kotlin/Any?> androidx.kruth/assertAbout(androidx.kruth/Subject.Factory<#A, #B>): androidx.kruth/SimpleSubjectBuilder<#A, #B> // androidx.kruth/assertAbout|assertAbout(androidx.kruth.Subject.Factory<0:0,0:1>){0§<androidx.kruth.Subject<0:1>>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> androidx.kruth/assertThat(kotlin.collections/Map<#A, #B>?): androidx.kruth/MapSubject<#A, #B> // androidx.kruth/assertThat|assertThat(kotlin.collections.Map<0:0,0:1>?){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.kruth/assertThat(#A?): androidx.kruth/Subject<#A> // androidx.kruth/assertThat|assertThat(0:0?){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.kruth/assertThat(kotlin.collections/Iterable<#A>?): androidx.kruth/IterableSubject<#A> // androidx.kruth/assertThat|assertThat(kotlin.collections.Iterable<0:0>?){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.kruth/assertThat(kotlin/Array<out #A>?): androidx.kruth/ObjectArraySubject<#A> // androidx.kruth/assertThat|assertThat(kotlin.Array<out|0:0>?){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Comparable<#A>> androidx.kruth/assertThat(#A?): androidx.kruth/ComparableSubject<#A> // androidx.kruth/assertThat|assertThat(0:0?){0§<kotlin.Comparable<0:0>>}[0]
+final fun <#A: kotlin/Int?> androidx.kruth/assertThat(#A): androidx.kruth/IntegerSubject // androidx.kruth/assertThat|assertThat(0:0){0§<kotlin.Int?>}[0]
+final fun <#A: kotlin/Long?> androidx.kruth/assertThat(#A): androidx.kruth/LongSubject // androidx.kruth/assertThat|assertThat(0:0){0§<kotlin.Long?>}[0]
+final fun <#A: kotlin/Throwable> androidx.kruth/assertThat(#A?): androidx.kruth/ThrowableSubject<#A> // androidx.kruth/assertThat|assertThat(0:0?){0§<kotlin.Throwable>}[0]
+final fun androidx.kruth/assertThat(kotlin/Boolean?): androidx.kruth/BooleanSubject // androidx.kruth/assertThat|assertThat(kotlin.Boolean?){}[0]
+final fun androidx.kruth/assertThat(kotlin/BooleanArray?): androidx.kruth/PrimitiveBooleanArraySubject // androidx.kruth/assertThat|assertThat(kotlin.BooleanArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/ByteArray?): androidx.kruth/PrimitiveByteArraySubject // androidx.kruth/assertThat|assertThat(kotlin.ByteArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/CharArray?): androidx.kruth/PrimitiveCharArraySubject // androidx.kruth/assertThat|assertThat(kotlin.CharArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/Double?): androidx.kruth/DoubleSubject // androidx.kruth/assertThat|assertThat(kotlin.Double?){}[0]
+final fun androidx.kruth/assertThat(kotlin/DoubleArray?): androidx.kruth/PrimitiveDoubleArraySubject // androidx.kruth/assertThat|assertThat(kotlin.DoubleArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/Float?): androidx.kruth/FloatSubject // androidx.kruth/assertThat|assertThat(kotlin.Float?){}[0]
+final fun androidx.kruth/assertThat(kotlin/FloatArray?): androidx.kruth/PrimitiveFloatArraySubject // androidx.kruth/assertThat|assertThat(kotlin.FloatArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/Int): androidx.kruth/IntegerSubject // androidx.kruth/assertThat|assertThat(kotlin.Int){}[0]
+final fun androidx.kruth/assertThat(kotlin/IntArray?): androidx.kruth/PrimitiveIntArraySubject // androidx.kruth/assertThat|assertThat(kotlin.IntArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/Long): androidx.kruth/LongSubject // androidx.kruth/assertThat|assertThat(kotlin.Long){}[0]
+final fun androidx.kruth/assertThat(kotlin/LongArray?): androidx.kruth/PrimitiveLongArraySubject // androidx.kruth/assertThat|assertThat(kotlin.LongArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/ShortArray?): androidx.kruth/PrimitiveShortArraySubject // androidx.kruth/assertThat|assertThat(kotlin.ShortArray?){}[0]
+final fun androidx.kruth/assertThat(kotlin/String?): androidx.kruth/StringSubject // androidx.kruth/assertThat|assertThat(kotlin.String?){}[0]
+final fun androidx.kruth/assertWithMessage(kotlin/String): androidx.kruth/StandardSubjectBuilder // androidx.kruth/assertWithMessage|assertWithMessage(kotlin.String){}[0]
+final fun androidx.kruth/assert_(): androidx.kruth/StandardSubjectBuilder // androidx.kruth/assert_|assert_(){}[0]
+final inline fun <#A: kotlin/Throwable> androidx.kruth/assertThrows(kotlin.reflect/KClass<#A>, kotlin/Function0<kotlin/Unit>): androidx.kruth/ThrowableSubject<#A> // androidx.kruth/assertThrows|assertThrows(kotlin.reflect.KClass<0:0>;kotlin.Function0<kotlin.Unit>){0§<kotlin.Throwable>}[0]
+final inline fun <#A: reified kotlin/Throwable> androidx.kruth/assertThrows(kotlin/Function0<kotlin/Unit>): androidx.kruth/ThrowableSubject<#A> // androidx.kruth/assertThrows|assertThrows(kotlin.Function0<kotlin.Unit>){0§<kotlin.Throwable>}[0]
+open class <#A: kotlin/Any?, #B: kotlin/Any?> androidx.kruth/MapSubject : androidx.kruth/Subject<kotlin.collections/Map<#A, #B>> { // androidx.kruth/MapSubject|null[0]
+    constructor <init>(androidx.kruth/FailureMetadata, kotlin.collections/Map<#A, #B>?) // androidx.kruth/MapSubject.<init>|<init>(androidx.kruth.FailureMetadata;kotlin.collections.Map<1:0,1:1>?){}[0]
+    final fun containsAtLeast(kotlin/Array<out kotlin/Pair<#A, #B>>...): androidx.kruth/Ordered // androidx.kruth/MapSubject.containsAtLeast|containsAtLeast(kotlin.Array<out|kotlin.Pair<1:0,1:1>>...){}[0]
+    final fun containsAtLeastEntriesIn(kotlin.collections/Map<#A, #B>): androidx.kruth/Ordered // androidx.kruth/MapSubject.containsAtLeastEntriesIn|containsAtLeastEntriesIn(kotlin.collections.Map<1:0,1:1>){}[0]
+    final fun containsEntry(#A, #B) // androidx.kruth/MapSubject.containsEntry|containsEntry(1:0;1:1){}[0]
+    final fun containsEntry(kotlin/Pair<#A, #B>) // androidx.kruth/MapSubject.containsEntry|containsEntry(kotlin.Pair<1:0,1:1>){}[0]
+    final fun containsExactly(kotlin/Array<out kotlin/Pair<#A, #B>>...): androidx.kruth/Ordered // androidx.kruth/MapSubject.containsExactly|containsExactly(kotlin.Array<out|kotlin.Pair<1:0,1:1>>...){}[0]
+    final fun containsExactlyEntriesIn(kotlin.collections/Map<#A, #B>): androidx.kruth/Ordered // androidx.kruth/MapSubject.containsExactlyEntriesIn|containsExactlyEntriesIn(kotlin.collections.Map<1:0,1:1>){}[0]
+    final fun containsKey(kotlin/Any?) // androidx.kruth/MapSubject.containsKey|containsKey(kotlin.Any?){}[0]
+    final fun doesNotContainEntry(#A, #B) // androidx.kruth/MapSubject.doesNotContainEntry|doesNotContainEntry(1:0;1:1){}[0]
+    final fun doesNotContainEntry(kotlin/Pair<#A, #B>) // androidx.kruth/MapSubject.doesNotContainEntry|doesNotContainEntry(kotlin.Pair<1:0,1:1>){}[0]
+    final fun doesNotContainKey(kotlin/Any?) // androidx.kruth/MapSubject.doesNotContainKey|doesNotContainKey(kotlin.Any?){}[0]
+    final fun hasSize(kotlin/Int) // androidx.kruth/MapSubject.hasSize|hasSize(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/MapSubject.isEmpty|isEmpty(){}[0]
+    final fun isNotEmpty() // androidx.kruth/MapSubject.isNotEmpty|isNotEmpty(){}[0]
+}
+open class <#A: kotlin/Any?> androidx.kruth/IterableSubject : androidx.kruth/Subject<kotlin.collections/Iterable<#A>> { // androidx.kruth/IterableSubject|null[0]
+    constructor <init>(androidx.kruth/FailureMetadata, kotlin.collections/Iterable<#A>?) // androidx.kruth/IterableSubject.<init>|<init>(androidx.kruth.FailureMetadata;kotlin.collections.Iterable<1:0>?){}[0]
+    final fun contains(kotlin/Any?) // androidx.kruth/IterableSubject.contains|contains(kotlin.Any?){}[0]
+    final fun containsAnyIn(kotlin.collections/Iterable<*>?) // androidx.kruth/IterableSubject.containsAnyIn|containsAnyIn(kotlin.collections.Iterable<*>?){}[0]
+    final fun containsAnyIn(kotlin/Array<out kotlin/Any?>?) // androidx.kruth/IterableSubject.containsAnyIn|containsAnyIn(kotlin.Array<out|kotlin.Any?>?){}[0]
+    final fun containsAnyOf(kotlin/Any?, kotlin/Any?, kotlin/Array<out kotlin/Any?>...) // androidx.kruth/IterableSubject.containsAnyOf|containsAnyOf(kotlin.Any?;kotlin.Any?;kotlin.Array<out|kotlin.Any?>...){}[0]
+    final fun containsAtLeast(kotlin/Any?, kotlin/Any?, kotlin/Array<out kotlin/Any?>...): androidx.kruth/Ordered // androidx.kruth/IterableSubject.containsAtLeast|containsAtLeast(kotlin.Any?;kotlin.Any?;kotlin.Array<out|kotlin.Any?>...){}[0]
+    final fun containsAtLeastElementsIn(kotlin.collections/Iterable<*>?): androidx.kruth/Ordered // androidx.kruth/IterableSubject.containsAtLeastElementsIn|containsAtLeastElementsIn(kotlin.collections.Iterable<*>?){}[0]
+    final fun containsAtLeastElementsIn(kotlin/Array<out kotlin/Any?>?): androidx.kruth/Ordered // androidx.kruth/IterableSubject.containsAtLeastElementsIn|containsAtLeastElementsIn(kotlin.Array<out|kotlin.Any?>?){}[0]
+    final fun containsExactly(kotlin/Array<out kotlin/Any?>...): androidx.kruth/Ordered // androidx.kruth/IterableSubject.containsExactly|containsExactly(kotlin.Array<out|kotlin.Any?>...){}[0]
+    final fun containsExactlyElementsIn(kotlin.collections/Iterable<*>?): androidx.kruth/Ordered // androidx.kruth/IterableSubject.containsExactlyElementsIn|containsExactlyElementsIn(kotlin.collections.Iterable<*>?){}[0]
+    final fun containsExactlyElementsIn(kotlin/Array<out kotlin/Any?>?): androidx.kruth/Ordered // androidx.kruth/IterableSubject.containsExactlyElementsIn|containsExactlyElementsIn(kotlin.Array<out|kotlin.Any?>?){}[0]
+    final fun containsNoDuplicates() // androidx.kruth/IterableSubject.containsNoDuplicates|containsNoDuplicates(){}[0]
+    final fun containsNoneIn(kotlin.collections/Iterable<*>?) // androidx.kruth/IterableSubject.containsNoneIn|containsNoneIn(kotlin.collections.Iterable<*>?){}[0]
+    final fun containsNoneIn(kotlin/Array<kotlin/Any?>?) // androidx.kruth/IterableSubject.containsNoneIn|containsNoneIn(kotlin.Array<kotlin.Any?>?){}[0]
+    final fun containsNoneOf(kotlin/Any?, kotlin/Any?, kotlin/Array<out kotlin/Any?>...) // androidx.kruth/IterableSubject.containsNoneOf|containsNoneOf(kotlin.Any?;kotlin.Any?;kotlin.Array<out|kotlin.Any?>...){}[0]
+    final fun doesNotContain(kotlin/Any?) // androidx.kruth/IterableSubject.doesNotContain|doesNotContain(kotlin.Any?){}[0]
+    final fun hasSize(kotlin/Int) // androidx.kruth/IterableSubject.hasSize|hasSize(kotlin.Int){}[0]
+    final fun isEmpty() // androidx.kruth/IterableSubject.isEmpty|isEmpty(){}[0]
+    final fun isInOrder(kotlin/Comparator<*>?) // androidx.kruth/IterableSubject.isInOrder|isInOrder(kotlin.Comparator<*>?){}[0]
+    final fun isInStrictOrder(kotlin/Comparator<*>?) // androidx.kruth/IterableSubject.isInStrictOrder|isInStrictOrder(kotlin.Comparator<*>?){}[0]
+    final fun isNotEmpty() // androidx.kruth/IterableSubject.isNotEmpty|isNotEmpty(){}[0]
+    open fun isEqualTo(kotlin/Any?) // androidx.kruth/IterableSubject.isEqualTo|isEqualTo(kotlin.Any?){}[0]
+    open fun isInOrder() // androidx.kruth/IterableSubject.isInOrder|isInOrder(){}[0]
+    open fun isInStrictOrder() // androidx.kruth/IterableSubject.isInStrictOrder|isInStrictOrder(){}[0]
+    open fun isNoneOf(kotlin/Any?, kotlin/Any?, kotlin/Array<out kotlin/Any?>...) // androidx.kruth/IterableSubject.isNoneOf|isNoneOf(kotlin.Any?;kotlin.Any?;kotlin.Array<out|kotlin.Any?>...){}[0]
+    open fun isNotIn(kotlin.collections/Iterable<*>?) // androidx.kruth/IterableSubject.isNotIn|isNotIn(kotlin.collections.Iterable<*>?){}[0]
+}
+open class <#A: kotlin/Comparable<#A>> androidx.kruth/ComparableSubject : androidx.kruth/PlatformComparableSubject<#A>, androidx.kruth/Subject<#A> { // androidx.kruth/ComparableSubject|null[0]
+    constructor <init>(androidx.kruth/FailureMetadata, #A?) // androidx.kruth/ComparableSubject.<init>|<init>(androidx.kruth.FailureMetadata;1:0?){}[0]
+    final fun isAtLeast(#A?) // androidx.kruth/ComparableSubject.isAtLeast|isAtLeast(1:0?){}[0]
+    final fun isAtMost(#A?) // androidx.kruth/ComparableSubject.isAtMost|isAtMost(1:0?){}[0]
+    final fun isGreaterThan(#A?) // androidx.kruth/ComparableSubject.isGreaterThan|isGreaterThan(1:0?){}[0]
+    final fun isLessThan(#A?) // androidx.kruth/ComparableSubject.isLessThan|isLessThan(1:0?){}[0]
+    open fun isEquivalentAccordingToCompareTo(#A?) // androidx.kruth/ComparableSubject.isEquivalentAccordingToCompareTo|isEquivalentAccordingToCompareTo(1:0?){}[0]
+}
+open class <#A: out kotlin/Any?> androidx.kruth/Subject { // androidx.kruth/Subject|null[0]
+    abstract fun interface <#A1: out androidx.kruth/Subject<#B1>, #B1: kotlin/Any?> Factory { // androidx.kruth/Subject.Factory|null[0]
+        abstract fun createSubject(androidx.kruth/FailureMetadata, #B1?): #A1 // androidx.kruth/Subject.Factory.createSubject|createSubject(androidx.kruth.FailureMetadata;1:1?){}[0]
+    }
+    constructor <init>(androidx.kruth/FailureMetadata, #A?) // androidx.kruth/Subject.<init>|<init>(androidx.kruth.FailureMetadata;1:0?){}[0]
+    final fun check(): androidx.kruth/StandardSubjectBuilder // androidx.kruth/Subject.check|check(){}[0]
+    final fun check(kotlin/String, kotlin/Array<out kotlin/Any>...): androidx.kruth/StandardSubjectBuilder // androidx.kruth/Subject.check|check(kotlin.String;kotlin.Array<out|kotlin.Any>...){}[0]
+    final fun doFail(kotlin/Array<out androidx.kruth/Fact>...) // androidx.kruth/Subject.doFail|doFail(kotlin.Array<out|androidx.kruth.Fact>...){}[0]
+    final fun failWithActual(androidx.kruth/Fact, kotlin/Array<out androidx.kruth/Fact>...) // androidx.kruth/Subject.failWithActual|failWithActual(androidx.kruth.Fact;kotlin.Array<out|androidx.kruth.Fact>...){}[0]
+    final fun failWithActual(kotlin/String, kotlin/Any?) // androidx.kruth/Subject.failWithActual|failWithActual(kotlin.String;kotlin.Any?){}[0]
+    final fun failWithoutActual(androidx.kruth/Fact, kotlin/Array<out androidx.kruth/Fact>...) // androidx.kruth/Subject.failWithoutActual|failWithoutActual(androidx.kruth.Fact;kotlin.Array<out|androidx.kruth.Fact>...){}[0]
+    final fun ignoreCheck(): androidx.kruth/StandardSubjectBuilder // androidx.kruth/Subject.ignoreCheck|ignoreCheck(){}[0]
+    final inline fun <#A1: reified kotlin/Any?> isInstanceOf() // androidx.kruth/Subject.isInstanceOf|isInstanceOf(){0§<kotlin.Any?>}[0]
+    final inline fun <#A1: reified kotlin/Any?> isNotInstanceOf() // androidx.kruth/Subject.isNotInstanceOf|isNotInstanceOf(){0§<kotlin.Any?>}[0]
+    final val actual // androidx.kruth/Subject.actual|{}actual[0]
+        final fun <get-actual>(): #A? // androidx.kruth/Subject.actual.<get-actual>|<get-actual>(){}[0]
+    final val metadata // androidx.kruth/Subject.metadata|{}metadata[0]
+        final fun <get-metadata>(): androidx.kruth/FailureMetadata // androidx.kruth/Subject.metadata.<get-metadata>|<get-metadata>(){}[0]
+    open fun actualCustomStringRepresentation(): kotlin/String // androidx.kruth/Subject.actualCustomStringRepresentation|actualCustomStringRepresentation(){}[0]
+    open fun isAnyOf(kotlin/Any?, kotlin/Any?, kotlin/Array<out kotlin/Any?>...) // androidx.kruth/Subject.isAnyOf|isAnyOf(kotlin.Any?;kotlin.Any?;kotlin.Array<out|kotlin.Any?>...){}[0]
+    open fun isEqualTo(kotlin/Any?) // androidx.kruth/Subject.isEqualTo|isEqualTo(kotlin.Any?){}[0]
+    open fun isIn(kotlin.collections/Iterable<*>?) // androidx.kruth/Subject.isIn|isIn(kotlin.collections.Iterable<*>?){}[0]
+    open fun isNoneOf(kotlin/Any?, kotlin/Any?, kotlin/Array<out kotlin/Any?>...) // androidx.kruth/Subject.isNoneOf|isNoneOf(kotlin.Any?;kotlin.Any?;kotlin.Array<out|kotlin.Any?>...){}[0]
+    open fun isNotEqualTo(kotlin/Any?) // androidx.kruth/Subject.isNotEqualTo|isNotEqualTo(kotlin.Any?){}[0]
+    open fun isNotIn(kotlin.collections/Iterable<*>?) // androidx.kruth/Subject.isNotIn|isNotIn(kotlin.collections.Iterable<*>?){}[0]
+    open fun isNotNull() // androidx.kruth/Subject.isNotNull|isNotNull(){}[0]
+    open fun isNotSameInstanceAs(kotlin/Any?) // androidx.kruth/Subject.isNotSameInstanceAs|isNotSameInstanceAs(kotlin.Any?){}[0]
+    open fun isNull() // androidx.kruth/Subject.isNull|isNull(){}[0]
+    open fun isSameInstanceAs(kotlin/Any?) // androidx.kruth/Subject.isSameInstanceAs|isSameInstanceAs(kotlin.Any?){}[0]
+}
+open class <#A: out kotlin/Throwable> androidx.kruth/ThrowableSubject : androidx.kruth/Subject<#A> { // androidx.kruth/ThrowableSubject|null[0]
+    constructor <init>(androidx.kruth/FailureMetadata, #A?) // androidx.kruth/ThrowableSubject.<init>|<init>(androidx.kruth.FailureMetadata;1:0?){}[0]
+    final fun hasCauseThat(): androidx.kruth/ThrowableSubject<kotlin/Throwable> // androidx.kruth/ThrowableSubject.hasCauseThat|hasCauseThat(){}[0]
+    final fun hasMessageThat(): androidx.kruth/StringSubject // androidx.kruth/ThrowableSubject.hasMessageThat|hasMessageThat(){}[0]
+}
+open class androidx.kruth/IntegerSubject : androidx.kruth/ComparableSubject<kotlin/Int> { // androidx.kruth/IntegerSubject|null[0]
+    constructor <init>(androidx.kruth/FailureMetadata, kotlin/Int?) // androidx.kruth/IntegerSubject.<init>|<init>(androidx.kruth.FailureMetadata;kotlin.Int?){}[0]
+    open fun isEquivalentAccordingToCompareTo(kotlin/Int?) // androidx.kruth/IntegerSubject.isEquivalentAccordingToCompareTo|isEquivalentAccordingToCompareTo(kotlin.Int?){}[0]
+}
+open class androidx.kruth/LongSubject : androidx.kruth/ComparableSubject<kotlin/Long> { // androidx.kruth/LongSubject|null[0]
+    abstract class TolerantLongComparison { // androidx.kruth/LongSubject.TolerantLongComparison|null[0]
+        abstract fun of(kotlin/Long) // androidx.kruth/LongSubject.TolerantLongComparison.of|of(kotlin.Long){}[0]
+        open fun equals(kotlin/Any?): kotlin/Boolean // androidx.kruth/LongSubject.TolerantLongComparison.equals|equals(kotlin.Any?){}[0]
+        open fun hashCode(): kotlin/Int // androidx.kruth/LongSubject.TolerantLongComparison.hashCode|hashCode(){}[0]
+    }
+    final fun isNotWithin(kotlin/Long): androidx.kruth/LongSubject.TolerantLongComparison // androidx.kruth/LongSubject.isNotWithin|isNotWithin(kotlin.Long){}[0]
+    final fun isWithin(kotlin/Long): androidx.kruth/LongSubject.TolerantLongComparison // androidx.kruth/LongSubject.isWithin|isWithin(kotlin.Long){}[0]
+    open fun isEquivalentAccordingToCompareTo(kotlin/Long?) // androidx.kruth/LongSubject.isEquivalentAccordingToCompareTo|isEquivalentAccordingToCompareTo(kotlin.Long?){}[0]
+}
+open class androidx.kruth/StandardSubjectBuilder : androidx.kruth/PlatformStandardSubjectBuilder { // androidx.kruth/StandardSubjectBuilder|null[0]
+    final fun <#A1: kotlin/Any?, #B1: androidx.kruth/Subject<#A1>> about(androidx.kruth/Subject.Factory<#B1, #A1>): androidx.kruth/SimpleSubjectBuilder<#B1, #A1> // androidx.kruth/StandardSubjectBuilder.about|about(androidx.kruth.Subject.Factory<0:1,0:0>){0§<kotlin.Any?>;1§<androidx.kruth.Subject<0:0>>}[0]
+    final fun <#A1: kotlin/Any?, #B1: kotlin/Any?> that(kotlin.collections/Map<#A1, #B1>?): androidx.kruth/MapSubject<#A1, #B1> // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.collections.Map<0:0,0:1>?){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> that(#A1?): androidx.kruth/Subject<#A1> // androidx.kruth/StandardSubjectBuilder.that|that(0:0?){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> that(kotlin.collections/Iterable<#A1>?): androidx.kruth/IterableSubject<#A1> // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.collections.Iterable<0:0>?){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> that(kotlin/Array<out #A1>?): androidx.kruth/ObjectArraySubject<#A1> // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.Array<out|0:0>?){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Comparable<#A1>> that(#A1?): androidx.kruth/ComparableSubject<#A1> // androidx.kruth/StandardSubjectBuilder.that|that(0:0?){0§<kotlin.Comparable<0:0>>}[0]
+    final fun <#A1: kotlin/Int?> that(#A1): androidx.kruth/IntegerSubject // androidx.kruth/StandardSubjectBuilder.that|that(0:0){0§<kotlin.Int?>}[0]
+    final fun <#A1: kotlin/Long?> that(#A1): androidx.kruth/LongSubject // androidx.kruth/StandardSubjectBuilder.that|that(0:0){0§<kotlin.Long?>}[0]
+    final fun <#A1: kotlin/Throwable> that(#A1?): androidx.kruth/ThrowableSubject<#A1> // androidx.kruth/StandardSubjectBuilder.that|that(0:0?){0§<kotlin.Throwable>}[0]
+    final fun fail() // androidx.kruth/StandardSubjectBuilder.fail|fail(){}[0]
+    final fun that(kotlin/Boolean?): androidx.kruth/BooleanSubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.Boolean?){}[0]
+    final fun that(kotlin/BooleanArray?): androidx.kruth/PrimitiveBooleanArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.BooleanArray?){}[0]
+    final fun that(kotlin/ByteArray?): androidx.kruth/PrimitiveByteArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.ByteArray?){}[0]
+    final fun that(kotlin/Char): androidx.kruth/Subject<kotlin/Char> // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.Char){}[0]
+    final fun that(kotlin/CharArray?): androidx.kruth/PrimitiveCharArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.CharArray?){}[0]
+    final fun that(kotlin/Double?): androidx.kruth/DoubleSubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.Double?){}[0]
+    final fun that(kotlin/DoubleArray?): androidx.kruth/PrimitiveDoubleArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.DoubleArray?){}[0]
+    final fun that(kotlin/Float?): androidx.kruth/FloatSubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.Float?){}[0]
+    final fun that(kotlin/FloatArray?): androidx.kruth/PrimitiveFloatArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.FloatArray?){}[0]
+    final fun that(kotlin/Int): androidx.kruth/IntegerSubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.Int){}[0]
+    final fun that(kotlin/IntArray?): androidx.kruth/PrimitiveIntArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.IntArray?){}[0]
+    final fun that(kotlin/Long): androidx.kruth/LongSubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.Long){}[0]
+    final fun that(kotlin/LongArray?): androidx.kruth/PrimitiveLongArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.LongArray?){}[0]
+    final fun that(kotlin/ShortArray?): androidx.kruth/PrimitiveShortArraySubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.ShortArray?){}[0]
+    final fun that(kotlin/String?): androidx.kruth/StringSubject // androidx.kruth/StandardSubjectBuilder.that|that(kotlin.String?){}[0]
+    final fun withMessage(kotlin/String): androidx.kruth/StandardSubjectBuilder // androidx.kruth/StandardSubjectBuilder.withMessage|withMessage(kotlin.String){}[0]
+    final object Companion { // androidx.kruth/StandardSubjectBuilder.Companion|null[0]
+        final fun forCustomFailureStrategy(androidx.kruth/FailureStrategy): androidx.kruth/StandardSubjectBuilder // androidx.kruth/StandardSubjectBuilder.Companion.forCustomFailureStrategy|forCustomFailureStrategy(androidx.kruth.FailureStrategy){}[0]
+    }
+}
+open class androidx.kruth/StringSubject : androidx.kruth/ComparableSubject<kotlin/String>, androidx.kruth/PlatformStringSubject { // androidx.kruth/StringSubject|null[0]
+    constructor <init>(androidx.kruth/FailureMetadata, kotlin/String?) // androidx.kruth/StringSubject.<init>|<init>(androidx.kruth.FailureMetadata;kotlin.String?){}[0]
+    final fun containsMatch(kotlin.text/Regex) // androidx.kruth/StringSubject.containsMatch|containsMatch(kotlin.text.Regex){}[0]
+    final fun doesNotContainMatch(kotlin.text/Regex) // androidx.kruth/StringSubject.doesNotContainMatch|doesNotContainMatch(kotlin.text.Regex){}[0]
+    final fun doesNotMatch(kotlin.text/Regex) // androidx.kruth/StringSubject.doesNotMatch|doesNotMatch(kotlin.text.Regex){}[0]
+    final fun matches(kotlin.text/Regex) // androidx.kruth/StringSubject.matches|matches(kotlin.text.Regex){}[0]
+    final inner class CaseInsensitiveStringComparison { // androidx.kruth/StringSubject.CaseInsensitiveStringComparison|null[0]
+        final fun contains(kotlin/CharSequence?) // androidx.kruth/StringSubject.CaseInsensitiveStringComparison.contains|contains(kotlin.CharSequence?){}[0]
+        final fun doesNotContain(kotlin/CharSequence) // androidx.kruth/StringSubject.CaseInsensitiveStringComparison.doesNotContain|doesNotContain(kotlin.CharSequence){}[0]
+        final fun isEqualTo(kotlin/String?) // androidx.kruth/StringSubject.CaseInsensitiveStringComparison.isEqualTo|isEqualTo(kotlin.String?){}[0]
+        final fun isNotEqualTo(kotlin/String?) // androidx.kruth/StringSubject.CaseInsensitiveStringComparison.isNotEqualTo|isNotEqualTo(kotlin.String?){}[0]
+    }
+    open fun contains(kotlin/CharSequence) // androidx.kruth/StringSubject.contains|contains(kotlin.CharSequence){}[0]
+    open fun containsMatch(kotlin/String) // androidx.kruth/StringSubject.containsMatch|containsMatch(kotlin.String){}[0]
+    open fun doesNotContain(kotlin/CharSequence) // androidx.kruth/StringSubject.doesNotContain|doesNotContain(kotlin.CharSequence){}[0]
+    open fun doesNotContainMatch(kotlin/String) // androidx.kruth/StringSubject.doesNotContainMatch|doesNotContainMatch(kotlin.String){}[0]
+    open fun doesNotMatch(kotlin/String) // androidx.kruth/StringSubject.doesNotMatch|doesNotMatch(kotlin.String){}[0]
+    open fun endsWith(kotlin/String) // androidx.kruth/StringSubject.endsWith|endsWith(kotlin.String){}[0]
+    open fun hasLength(kotlin/Int) // androidx.kruth/StringSubject.hasLength|hasLength(kotlin.Int){}[0]
+    open fun ignoringCase(): androidx.kruth/StringSubject.CaseInsensitiveStringComparison // androidx.kruth/StringSubject.ignoringCase|ignoringCase(){}[0]
+    open fun isEmpty() // androidx.kruth/StringSubject.isEmpty|isEmpty(){}[0]
+    open fun isNotEmpty() // androidx.kruth/StringSubject.isNotEmpty|isNotEmpty(){}[0]
+    open fun matches(kotlin/String) // androidx.kruth/StringSubject.matches|matches(kotlin.String){}[0]
+    open fun startsWith(kotlin/String) // androidx.kruth/StringSubject.startsWith|startsWith(kotlin.String){}[0]
+}
diff --git a/kruth/kruth/build.gradle b/kruth/kruth/build.gradle
index b62f9f8..12da8f8 100644
--- a/kruth/kruth/build.gradle
+++ b/kruth/kruth/build.gradle
@@ -106,4 +106,5 @@
     runApiTasks = new RunApiTasks.Yes() // Used to diff against Google Truth
     type = LibraryType.INTERNAL_TEST_LIBRARY
     doNotDocumentReason = "Not shipped externally"
+    metalavaK2UastEnabled = false
 }
diff --git a/leanback/leanback-grid/build.gradle b/leanback/leanback-grid/build.gradle
index e8fcba4..cb2e0bf 100644
--- a/leanback/leanback-grid/build.gradle
+++ b/leanback/leanback-grid/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
     api("androidx.recyclerview:recyclerview:1.3.2")
     implementation("androidx.collection:collection:1.0.0")
@@ -52,6 +52,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.leanback.widget"
 }
 
@@ -61,5 +62,4 @@
     mavenVersion = LibraryVersions.LEANBACK_GRID
     inceptionYear = "2021"
     description = "This library adds grid view components to be used in TV"
-    metalavaK2UastEnabled = true
 }
diff --git a/leanback/leanback-paging/build.gradle b/leanback/leanback-paging/build.gradle
index 66941c4..9eade4e 100644
--- a/leanback/leanback-paging/build.gradle
+++ b/leanback/leanback-paging/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.leanback:leanback:1.1.0-beta01")
     api("androidx.paging:paging-runtime:3.1.0")
 
@@ -47,6 +47,7 @@
 }
 
 android {
+    compileSdk 35
     lintOptions {
         // Bug in Android Lint 7.0.0-alpha15 b/187419330
         disable("MissingLeanbackLauncher", "ImpliedTouchscreenHardware", "MissingLeanbackSupport")
@@ -62,5 +63,4 @@
     description = "AndroidX Leanback Paging"
     failOnDeprecationWarnings = false
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
 }
diff --git a/leanback/leanback-preference/build.gradle b/leanback/leanback-preference/build.gradle
index c387e06..1f96cc24 100644
--- a/leanback/leanback-preference/build.gradle
+++ b/leanback/leanback-preference/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.appcompat:appcompat:1.0.0")
     api("androidx.recyclerview:recyclerview:1.3.2")
@@ -22,6 +22,7 @@
 }
 
 android {
+    compileSdk 35
     sourceSets {
         main.java.srcDirs += [
                 "api21"
@@ -37,5 +38,4 @@
     inceptionYear = "2015"
     description = "AndroidX Leanback Preference"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/leanback/leanback-tab/build.gradle b/leanback/leanback-tab/build.gradle
index cd1640a..78d8b76 100644
--- a/leanback/leanback-tab/build.gradle
+++ b/leanback/leanback-tab/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("com.google.android.material:material:1.0.0")
     api("androidx.viewpager:viewpager:1.0.0")
 
@@ -37,6 +37,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.leanback.tab"
 }
 
@@ -46,5 +47,4 @@
     mavenVersion = LibraryVersions.LEANBACK_TAB
     inceptionYear = "2020"
     description = "This library adds top tab navigation component to be used in TV"
-    metalavaK2UastEnabled = true
 }
diff --git a/leanback/leanback/build.gradle b/leanback/leanback/build.gradle
index 4790443..5f18f6e 100644
--- a/leanback/leanback/build.gradle
+++ b/leanback/leanback/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.interpolator:interpolator:1.0.0")
     api("androidx.core:core:1.1.0")
     api(project(":leanback:leanback-grid"))
@@ -39,6 +39,7 @@
 }
 
 android {
+    compileSdk 35
     sourceSets {
         main.java.srcDirs += [
                 "common",
@@ -55,6 +56,5 @@
     mavenVersion = LibraryVersions.LEANBACK
     inceptionYear = "2014"
     description = "Android Support Leanback v17"
-    metalavaK2UastEnabled = true
     failOnDeprecationWarnings = false
 }
diff --git a/leanback/leanback/lint-baseline.xml b/leanback/leanback/lint-baseline.xml
index 32fe49d..26ee15a 100644
--- a/leanback/leanback/lint-baseline.xml
+++ b/leanback/leanback/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -1325,33 +1325,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.leanback.widget.ForegroundHelper is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return view.getForeground();"
-        errorLine2="                        ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/ForegroundHelper.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.leanback.widget.ForegroundHelper is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            view.setForeground(drawable);"
-        errorLine2="                 ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/widget/ForegroundHelper.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.leanback.app.PermissionHelper is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            fragment.requestPermissions(permissions, requestCode);"
-        errorLine2="                     ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/leanback/app/PermissionHelper.java"/>
-    </issue>
-
-    <issue
         id="PrivateConstructorForUtilityClass"
         message="Utility class is missing private constructor"
         errorLine1="public class FocusHighlightHelper {"
diff --git a/leanback/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java b/leanback/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
index 761cf21..d9d72df 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.RequiresApi;
 
 class FragmentUtil {
@@ -39,7 +38,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static Context getContext(Fragment fragment) {
             return fragment.getContext();
         }
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionsStylist.java b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionsStylist.java
index 7ecbd55..469d0fd 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionsStylist.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionsStylist.java
@@ -1582,12 +1582,10 @@
             // This class is not instantiable.
         }
 
-        @androidx.annotation.DoNotInline
         static void setAutofillHints(View view, String... autofillHints) {
             view.setAutofillHints(autofillHints);
         }
 
-        @androidx.annotation.DoNotInline
         static void setImportantForAutofill(
                 View view,
                 @SuppressWarnings("SameParameterValue") int mode
diff --git a/libraryversions.toml b/libraryversions.toml
index 65e1e5b..98e9304 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -1,5 +1,5 @@
 [versions]
-ACTIVITY = "1.10.0-alpha01"
+ACTIVITY = "1.10.0-beta01"
 ANNOTATION = "1.9.0-alpha01"
 ANNOTATION_EXPERIMENTAL = "1.4.0-rc01"
 APPCOMPAT = "1.8.0-alpha01"
@@ -7,12 +7,12 @@
 ARCH_CORE = "2.3.0-alpha01"
 ASYNCLAYOUTINFLATER = "1.1.0-alpha02"
 AUTOFILL = "1.3.0-alpha02"
-BENCHMARK = "1.3.0-beta02"
-BIOMETRIC = "1.2.0-alpha06"
+BENCHMARK = "1.3.0-rc01"
+BIOMETRIC = "1.4.0-alpha01"
 BLUETOOTH = "1.0.0-alpha02"
 BROWSER = "1.9.0-alpha01"
 BUILDSRC_TESTS = "1.0.0-alpha01"
-CAMERA = "1.4.0-beta03"
+CAMERA = "1.4.0-rc01"
 CAMERA_PIPE = "1.0.0-alpha01"
 CAMERA_TESTING = "1.0.0-alpha01"
 CAMERA_VIEWFINDER = "1.4.0-alpha08"
@@ -25,12 +25,12 @@
 COMPOSE_MATERIAL3_ADAPTIVE = "1.1.0-alpha01"
 COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
 COMPOSE_RUNTIME_TRACING = "1.0.0-beta01"
-CONSTRAINTLAYOUT = "2.2.0-beta01"
-CONSTRAINTLAYOUT_COMPOSE = "1.1.0-beta01"
-CONSTRAINTLAYOUT_CORE = "1.1.0-beta01"
+CONSTRAINTLAYOUT = "2.2.0-alpha14"
+CONSTRAINTLAYOUT_COMPOSE = "1.1.0-alpha14"
+CONSTRAINTLAYOUT_CORE = "1.1.0-alpha14"
 CONTENTPAGER = "1.1.0-alpha01"
 COORDINATORLAYOUT = "1.3.0-alpha02"
-CORE = "1.14.0-alpha01"
+CORE = "1.15.0-alpha01"
 CORE_ANIMATION = "1.0.0"
 CORE_ANIMATION_TESTING = "1.0.0"
 CORE_APPDIGEST = "1.0.0-alpha01"
@@ -42,9 +42,9 @@
 CORE_REMOTEVIEWS = "1.1.0-rc01"
 CORE_ROLE = "1.2.0-alpha01"
 CORE_SPLASHSCREEN = "1.2.0-alpha01"
-CORE_TELECOM = "1.0.0-alpha08"
+CORE_TELECOM = "1.0.0-alpha11"
 CORE_UWB = "1.0.0-alpha08"
-CREDENTIALS = "1.3.0-rc01"
+CREDENTIALS = "1.5.0-alpha03"
 CREDENTIALS_E2EE_QUARANTINE = "1.0.0-alpha02"
 CREDENTIALS_FIDO_QUARANTINE = "1.0.0-alpha02"
 CURSORADAPTER = "1.1.0-alpha01"
@@ -97,7 +97,7 @@
 MEDIA = "1.7.0-rc01"
 MEDIAROUTER = "1.8.0-alpha01"
 METRICS = "1.0.0-beta02"
-NAVIGATION = "2.8.0-beta05"
+NAVIGATION = "2.8.0-beta06"
 PAGING = "3.4.0-alpha01"
 PALETTE = "1.1.0-alpha01"
 PDF = "1.0.0-alpha01"
@@ -107,12 +107,12 @@
 PRIVACYSANDBOX_ACTIVITY = "1.0.0-alpha01"
 PRIVACYSANDBOX_ADS = "1.1.0-beta09"
 PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha03"
-PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha13"
+PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha14"
 PRIVACYSANDBOX_TOOLS = "1.0.0-alpha09"
 PRIVACYSANDBOX_UI = "1.0.0-alpha09"
-PROFILEINSTALLER = "1.4.0-alpha01"
+PROFILEINSTALLER = "1.4.0-alpha02"
 RECOMMENDATION = "1.1.0-alpha01"
-RECYCLERVIEW = "1.4.0-alpha01"
+RECYCLERVIEW = "1.4.0-alpha02"
 RECYCLERVIEW_SELECTION = "1.2.0-alpha02"
 REMOTECALLBACK = "1.0.0-alpha02"
 RESOURCEINSPECTION = "1.1.0-alpha01"
@@ -125,7 +125,7 @@
 SECURITY_BIOMETRIC = "1.0.0-alpha01"
 SECURITY_IDENTITY_CREDENTIAL = "1.0.0-alpha04"
 SECURITY_MLS = "1.0.0-alpha01"
-SECURITY_STATE = "1.0.0-alpha03"
+SECURITY_STATE = "1.0.0-alpha04"
 SHARETARGET = "1.3.0-alpha01"
 SLICE = "1.1.0-alpha03"
 SLICE_BENCHMARK = "1.1.0-alpha03"
@@ -161,16 +161,16 @@
 WEAR_INPUT_TESTING = "1.2.0-alpha03"
 WEAR_ONGOING = "1.1.0-alpha02"
 WEAR_PHONE_INTERACTIONS = "1.1.0-alpha04"
-WEAR_PROTOLAYOUT = "1.2.0-beta01"
+WEAR_PROTOLAYOUT = "1.3.0-alpha01"
 WEAR_PROTOLAYOUT_MATERIAL3 = "1.0.0-alpha01"
-WEAR_REMOTE_INTERACTIONS = "1.1.0-alpha02"
-WEAR_TILES = "1.4.0-beta01"
+WEAR_REMOTE_INTERACTIONS = "1.1.0-beta01"
+WEAR_TILES = "1.5.0-alpha01"
 WEAR_TOOLING_PREVIEW = "1.0.0-rc01"
 WEAR_WATCHFACE = "1.3.0-alpha03"
 WEBKIT = "1.12.0-alpha02"
 # Adding a comment to prevent merge conflicts for Window artifact
-WINDOW = "1.4.0-alpha01"
-WINDOW_EXTENSIONS = "1.4.0-alpha01"
+WINDOW = "1.6.0-alpha01"
+WINDOW_EXTENSIONS = "1.6.0-alpha01"
 WINDOW_EXTENSIONS_CORE = "1.1.0-alpha01"
 WINDOW_SIDECAR = "1.0.0-rc01"
 WORK = "2.10.0-alpha02"
@@ -202,7 +202,7 @@
 COMPOSE_MATERIAL = { group = "androidx.compose.material" }
 COMPOSE_MATERIAL3 = { group = "androidx.compose.material3", atomicGroupVersion = "versions.COMPOSE_MATERIAL3" }
 COMPOSE_MATERIAL3_ADAPTIVE = { group = "androidx.compose.material3.adaptive", atomicGroupVersion = "versions.COMPOSE_MATERIAL3_ADAPTIVE" }
-COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE= { group = "androidx.compose.material3", atomicGroupVersion = "versions.COMPOSE_MATERIAL3", overrideInclude = [ ":compose:material3:material3-adaptive-navigation-suite" ] }
+COMPOSE_MATERIAL3_ADAPTIVE_NAVIGATION_SUITE = { group = "androidx.compose.material3", atomicGroupVersion = "versions.COMPOSE_MATERIAL3", overrideInclude = [ ":compose:material3:material3-adaptive-navigation-suite" ] }
 COMPOSE_RUNTIME = { group = "androidx.compose.runtime", atomicGroupVersion = "versions.COMPOSE" }
 COMPOSE_RUNTIME_TRACING = { group = "androidx.compose.runtime", atomicGroupVersion = "versions.COMPOSE_RUNTIME_TRACING", overrideInclude = [ ":compose:runtime:runtime-tracing" ] }
 COMPOSE_UI = { group = "androidx.compose.ui", atomicGroupVersion = "versions.COMPOSE" }
diff --git a/lifecycle/lifecycle-common-java8/build.gradle b/lifecycle/lifecycle-common-java8/build.gradle
index 830b8c2..fbe390f 100644
--- a/lifecycle/lifecycle-common-java8/build.gradle
+++ b/lifecycle/lifecycle-common-java8/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     api(project(":lifecycle:lifecycle-common"))
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
@@ -41,5 +41,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Lifecycle-Common for Java 8 Language"
-    metalavaK2UastEnabled = true
 }
diff --git a/lifecycle/lifecycle-common/bcv/native/current.txt b/lifecycle/lifecycle-common/bcv/native/current.txt
index 9bd0719..6e2e5b1 100644
--- a/lifecycle/lifecycle-common/bcv/native/current.txt
+++ b/lifecycle/lifecycle-common/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/lifecycle/lifecycle-common/build.gradle b/lifecycle/lifecycle-common/build.gradle
index 3cf55d1..30cdeb4 100644
--- a/lifecycle/lifecycle-common/build.gradle
+++ b/lifecycle/lifecycle-common/build.gradle
@@ -31,8 +31,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm {
         withJava()
     }
@@ -51,7 +49,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
-                api("androidx.annotation:annotation:1.8.0")
+                api(project(":annotation:annotation"))
             }
         }
 
@@ -65,7 +63,7 @@
             }
         }
 
-        nonJvmMain {
+        nonJvmCommonMain {
             dependsOn(commonMain)
             dependencies {
                 implementation(libs.atomicFu)
@@ -75,7 +73,7 @@
         targets.configureEach { target ->
             if (target.platformType !in [KotlinPlatformType.jvm, KotlinPlatformType.common]) {
                 target.compilations["main"].defaultSourceSet {
-                    dependsOn(nonJvmMain)
+                    dependsOn(nonJvmCommonMain)
                 }
             }
         }
@@ -87,4 +85,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Lifecycle-Common"
+    metalavaK2UastEnabled = false
 }
diff --git a/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/DefaultLifecycleObserver.kt b/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/DefaultLifecycleObserver.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/DefaultLifecycleObserver.kt
rename to lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/DefaultLifecycleObserver.kt
diff --git a/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/DefaultLifecycleObserverAdapter.kt b/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/DefaultLifecycleObserverAdapter.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/DefaultLifecycleObserverAdapter.kt
rename to lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/DefaultLifecycleObserverAdapter.kt
diff --git a/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/Lifecycle.kt b/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/Lifecycle.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/Lifecycle.kt
rename to lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/Lifecycle.kt
diff --git a/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/LifecycleEventObserver.kt b/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/LifecycleEventObserver.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/LifecycleEventObserver.kt
rename to lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/LifecycleEventObserver.kt
diff --git a/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/LifecycleObserver.kt b/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/LifecycleObserver.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/LifecycleObserver.kt
rename to lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/LifecycleObserver.kt
diff --git a/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/LifecycleOwner.kt b/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/LifecycleOwner.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/LifecycleOwner.kt
rename to lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/LifecycleOwner.kt
diff --git a/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/Lifecycling.kt b/lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/Lifecycling.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/commonMain/kotlin/androidx.lifecycle/Lifecycling.kt
rename to lifecycle/lifecycle-common/src/commonMain/kotlin/androidx/lifecycle/Lifecycling.kt
diff --git a/lifecycle/lifecycle-common/src/nonJvmMain/kotlin/androidx/lifecycle/Lifecycle.nonJvm.kt b/lifecycle/lifecycle-common/src/nonJvmCommonMain/kotlin/androidx/lifecycle/Lifecycle.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/nonJvmMain/kotlin/androidx/lifecycle/Lifecycle.nonJvm.kt
rename to lifecycle/lifecycle-common/src/nonJvmCommonMain/kotlin/androidx/lifecycle/Lifecycle.nonJvm.kt
diff --git a/lifecycle/lifecycle-common/src/nonJvmMain/kotlin/androidx/lifecycle/Lifecycling.nonJvm.kt b/lifecycle/lifecycle-common/src/nonJvmCommonMain/kotlin/androidx/lifecycle/Lifecycling.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-common/src/nonJvmMain/kotlin/androidx/lifecycle/Lifecycling.nonJvm.kt
rename to lifecycle/lifecycle-common/src/nonJvmCommonMain/kotlin/androidx/lifecycle/Lifecycling.nonJvm.kt
diff --git a/lifecycle/lifecycle-extensions/build.gradle b/lifecycle/lifecycle-extensions/build.gradle
index 6a602d2..a65176a 100644
--- a/lifecycle/lifecycle-extensions/build.gradle
+++ b/lifecycle/lifecycle-extensions/build.gradle
@@ -63,7 +63,6 @@
     inceptionYear = "2017"
     description = "Android Lifecycle Extensions"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/lifecycle/lifecycle-livedata-core-ktx/build.gradle b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
index 17d4c9a..108896c 100644
--- a/lifecycle/lifecycle-livedata-core-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
@@ -39,7 +39,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'livedata-core' artifact"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/lifecycle/lifecycle-livedata-core/build.gradle b/lifecycle/lifecycle-livedata-core/build.gradle
index c858cee..2c83c90 100644
--- a/lifecycle/lifecycle-livedata-core/build.gradle
+++ b/lifecycle/lifecycle-livedata-core/build.gradle
@@ -51,7 +51,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Lifecycle LiveData Core"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/lifecycle/lifecycle-livedata-ktx/build.gradle b/lifecycle/lifecycle-livedata-ktx/build.gradle
index d059f30..2b8f628 100644
--- a/lifecycle/lifecycle-livedata-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-ktx/build.gradle
@@ -53,7 +53,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'livedata' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/lifecycle/lifecycle-livedata/build.gradle b/lifecycle/lifecycle-livedata/build.gradle
index 0ef6ced..31819a6 100644
--- a/lifecycle/lifecycle-livedata/build.gradle
+++ b/lifecycle/lifecycle-livedata/build.gradle
@@ -59,7 +59,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Lifecycle LiveData"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/FlowLiveData.kt b/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/FlowLiveData.kt
index a0dc7aa..ba172dc 100644
--- a/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/FlowLiveData.kt
+++ b/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/FlowLiveData.kt
@@ -101,9 +101,8 @@
 public fun <T> LiveData<T>.asFlow(): Flow<T> =
     callbackFlow {
             val observer = Observer<T> { trySend(it) }
-            withContext(Dispatchers.Main.immediate) { observeForever(observer) }
-
             try {
+                withContext(Dispatchers.Main.immediate) { observeForever(observer) }
                 awaitCancellation()
             } finally {
                 withContext(Dispatchers.Main.immediate + NonCancellable) {
diff --git a/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/LiveDataAsFlowTest.kt b/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/LiveDataAsFlowTest.kt
index 8b01a15..a1c69ec 100644
--- a/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/LiveDataAsFlowTest.kt
+++ b/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/LiveDataAsFlowTest.kt
@@ -54,6 +54,45 @@
     }
 
     @Test
+    fun checkCancellationFromInitialValue() {
+        val ld = MutableLiveData<Int>()
+        ld.value = 1
+        val flow = ld.asFlow()
+        // check that flow creation didn't make livedata active
+        assertThat(ld.hasActiveObservers()).isFalse()
+        // Collect only a single value to get the initial value and cancel immediately
+        val job = testScope.launch { assertThat(flow.take(1).toList()).isEqualTo(listOf(1)) }
+        scopes.triggerAllActions()
+        mainScope.launch {
+            // This should never be received by the take(1)
+            ld.value = 2
+        }
+        scopes.triggerAllActions()
+        // Verify that the job completing removes the observer
+        assertThat(job.isCompleted).isTrue()
+        assertThat(ld.hasActiveObservers()).isFalse()
+    }
+
+    @Test
+    fun checkCancellationAfterJobCompletes() {
+        val ld = MutableLiveData<Int>()
+        ld.value = 1
+        val flow = ld.asFlow()
+        // check that flow creation didn't make livedata active
+        assertThat(ld.hasActiveObservers()).isFalse()
+        val job = testScope.launch { assertThat(flow.take(2).toList()).isEqualTo(listOf(1, 2)) }
+        scopes.triggerAllActions()
+        mainScope.launch {
+            // Receiving this should complete the job and remove the observer
+            ld.value = 2
+        }
+        scopes.triggerAllActions()
+        // Verify that the job completing removes the observer
+        assertThat(job.isCompleted).isTrue()
+        assertThat(ld.hasActiveObservers()).isFalse()
+    }
+
+    @Test
     fun dispatchMultiple() {
         val ld = MutableLiveData<Int>()
         val collected = mutableListOf<Int>()
diff --git a/lifecycle/lifecycle-process/build.gradle b/lifecycle/lifecycle-process/build.gradle
index 334c39c..669a5d9 100644
--- a/lifecycle/lifecycle-process/build.gradle
+++ b/lifecycle/lifecycle-process/build.gradle
@@ -40,7 +40,7 @@
     api(libs.kotlinStdlib)
     api(project(":lifecycle:lifecycle-runtime"))
     api("androidx.startup:startup-runtime:1.1.1")
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
@@ -51,6 +51,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Lifecycle Process"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/lifecycle/lifecycle-process/src/main/java/androidx/lifecycle/ProcessLifecycleOwner.kt b/lifecycle/lifecycle-process/src/main/java/androidx/lifecycle/ProcessLifecycleOwner.kt
index b5165de..5193aa8 100644
--- a/lifecycle/lifecycle-process/src/main/java/androidx/lifecycle/ProcessLifecycleOwner.kt
+++ b/lifecycle/lifecycle-process/src/main/java/androidx/lifecycle/ProcessLifecycleOwner.kt
@@ -21,7 +21,6 @@
 import android.os.Build
 import android.os.Bundle
 import android.os.Handler
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.ReportFragment.Companion.reportFragment
@@ -189,7 +188,6 @@
 
     @RequiresApi(29)
     internal object Api29Impl {
-        @DoNotInline
         @JvmStatic
         fun registerActivityLifecycleCallbacks(
             activity: Activity,
diff --git a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
index e1ae335..b054abb 100644
--- a/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams-ktx/build.gradle
@@ -46,7 +46,6 @@
   type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
   inceptionYear = "2018"
   description = "Kotlin extensions for Lifecycle ReactiveStreams"
-  metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/lifecycle/lifecycle-reactivestreams/build.gradle b/lifecycle/lifecycle-reactivestreams/build.gradle
index 55605b8d..71eaf3b 100644
--- a/lifecycle/lifecycle-reactivestreams/build.gradle
+++ b/lifecycle/lifecycle-reactivestreams/build.gradle
@@ -35,7 +35,7 @@
     api(project(":lifecycle:lifecycle-common"))
     api(project(":lifecycle:lifecycle-livedata"))
     api(project(":lifecycle:lifecycle-runtime"))
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.reactiveStreams)
 
     annotationProcessor(libs.nullaway)
@@ -53,7 +53,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Lifecycle Reactivestreams"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/lifecycle/lifecycle-runtime-compose/build.gradle b/lifecycle/lifecycle-runtime-compose/build.gradle
index 5fef01f..be2d776 100644
--- a/lifecycle/lifecycle-runtime-compose/build.gradle
+++ b/lifecycle/lifecycle-runtime-compose/build.gradle
@@ -31,8 +31,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
     desktop()
 
@@ -42,7 +40,7 @@
         commonMain {
             dependencies {
                 api(projectOrArtifact(":lifecycle:lifecycle-runtime"))
-                api("androidx.annotation:annotation:1.8.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.compose.runtime:runtime:1.6.5")
             }
         }
@@ -78,10 +76,13 @@
     inceptionYear = "2021"
     description = "Compose integration with Lifecycle"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":lifecycle:lifecycle-runtime-compose:lifecycle-runtime-compose-samples"))
 }
 
 android {
+    compileSdk 35
+    
     buildTypes.configureEach {
         consumerProguardFiles "proguard-rules.pro"
     }
diff --git a/lifecycle/lifecycle-runtime-compose/samples/build.gradle b/lifecycle/lifecycle-runtime-compose/samples/build.gradle
index 020a892..40a0c51 100644
--- a/lifecycle/lifecycle-runtime-compose/samples/build.gradle
+++ b/lifecycle/lifecycle-runtime-compose/samples/build.gradle
@@ -47,5 +47,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.lifecycle.runtime.compose.samples"
 }
\ No newline at end of file
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/compose/LifecycleEffectTest.kt b/lifecycle/lifecycle-runtime-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/compose/LifecycleEffectTest.kt
index 43cb7d4..bd2c77e 100644
--- a/lifecycle/lifecycle-runtime-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/compose/LifecycleEffectTest.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/compose/LifecycleEffectTest.kt
@@ -18,8 +18,10 @@
 
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.runComposeUiTest
 import androidx.kruth.assertThat
@@ -88,6 +90,75 @@
     }
 
     @Test
+    fun lifecycleEventEffectTest_onPause_beforeOnDispose_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            if (visible) {
+                LifecycleEventEffect(Lifecycle.Event.ON_PAUSE, lifecycleOwner) { stopCount++ }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+            visible = false
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
+    fun lifecycleEventEffectTest_onDestroy_beforeOnDispose_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            if (visible) {
+                LifecycleEventEffect(Lifecycle.Event.ON_PAUSE, lifecycleOwner) { stopCount++ }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+            visible = false
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
+    fun lifecycleEventEffectTest_onDispose_beforeOnDestroy_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            if (visible) {
+                LifecycleEventEffect(Lifecycle.Event.ON_PAUSE, lifecycleOwner) { stopCount++ }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            visible = false
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
     fun lifecycleStartEffectTest() = runComposeUiTest {
         lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
         var startCount = 0
@@ -318,6 +389,81 @@
     }
 
     @Test
+    fun lifecycleStartEffect_onStop_beforeOnDispose_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                if (visible) {
+                    LifecycleStartEffect(key1 = "key1") { onStopOrDispose { stopCount++ } }
+                }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
+            visible = false
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
+    fun lifecycleStartEffect_onDestroy_beforeOnDispose_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                if (visible) {
+                    LifecycleStartEffect(key1 = "key1") { onStopOrDispose { stopCount++ } }
+                }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+            visible = false
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
+    fun lifecycleStartEffect_onDispose_beforeOnDestroy_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                if (visible) {
+                    LifecycleStartEffect(key1 = "key1") { onStopOrDispose { stopCount++ } }
+                }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+            visible = false
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
     fun lifecycleResumeEffectTest() = runComposeUiTest {
         var resumeCount = 0
         var pauseCount = 0
@@ -548,4 +694,104 @@
             assertThat(state.value).isEqualTo("changed resumed disposed")
         }
     }
+
+    @Test
+    fun lifecycleResumeEffect_onPause_beforeOnDispose_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                if (visible) {
+                    LifecycleResumeEffect(key1 = "key1") { onPauseOrDispose { stopCount++ } }
+                }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+            visible = false
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
+    fun lifecycleResumeEffect_onDispose_beforeOnPause_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                if (visible) {
+                    LifecycleResumeEffect(key1 = "key1") { onPauseOrDispose { stopCount++ } }
+                }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            visible = false
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
+    fun lifecycleResumeEffect_onDestroy_beforeOnDispose_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                if (visible) {
+                    LifecycleResumeEffect(key1 = "key1") { onPauseOrDispose { stopCount++ } }
+                }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+            visible = false
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
+
+    @Test
+    fun lifecycleResumeEffect_onDispose_beforeOnDestroy_isIdempotent() = runComposeUiTest {
+        lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED)
+
+        var visible by mutableStateOf(true)
+        var stopCount = 0
+
+        waitForIdle()
+        setContent {
+            CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
+                if (visible) {
+                    LifecycleResumeEffect(key1 = "key1") { onPauseOrDispose { stopCount++ } }
+                }
+            }
+        }
+
+        runOnIdle {
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+            visible = false
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+        }
+
+        runOnIdle { assertThat(stopCount).isEqualTo(1) }
+    }
 }
diff --git a/lifecycle/lifecycle-runtime-compose/src/commonMain/kotlin/androidx/lifecycle/compose/LifecycleEffect.kt b/lifecycle/lifecycle-runtime-compose/src/commonMain/kotlin/androidx/lifecycle/compose/LifecycleEffect.kt
index 6bd95af..ca52fb5 100644
--- a/lifecycle/lifecycle-runtime-compose/src/commonMain/kotlin/androidx/lifecycle/compose/LifecycleEffect.kt
+++ b/lifecycle/lifecycle-runtime-compose/src/commonMain/kotlin/androidx/lifecycle/compose/LifecycleEffect.kt
@@ -344,7 +344,10 @@
         val observer = LifecycleEventObserver { _, event ->
             when (event) {
                 Lifecycle.Event.ON_START -> with(scope) { effectResult = effects() }
-                Lifecycle.Event.ON_STOP -> effectResult?.runStopOrDisposeEffect()
+                Lifecycle.Event.ON_STOP -> {
+                    effectResult?.runStopOrDisposeEffect()
+                    effectResult = null
+                }
                 else -> {}
             }
         }
@@ -664,7 +667,10 @@
         val observer = LifecycleEventObserver { _, event ->
             when (event) {
                 Lifecycle.Event.ON_RESUME -> with(scope) { effectResult = effects() }
-                Lifecycle.Event.ON_PAUSE -> effectResult?.runPauseOrOnDisposeEffect()
+                Lifecycle.Event.ON_PAUSE -> {
+                    effectResult?.runPauseOrOnDisposeEffect()
+                    effectResult = null
+                }
                 else -> {}
             }
         }
diff --git a/lifecycle/lifecycle-runtime-ktx/build.gradle b/lifecycle/lifecycle-runtime-ktx/build.gradle
index 097623f..e660299 100644
--- a/lifecycle/lifecycle-runtime-ktx/build.gradle
+++ b/lifecycle/lifecycle-runtime-ktx/build.gradle
@@ -31,8 +31,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
 
     defaultPlatform(PlatformIdentifier.ANDROID)
@@ -42,7 +40,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(project(":lifecycle:lifecycle-runtime"))
-                api("androidx.annotation:annotation:1.8.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
@@ -60,7 +58,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2019"
     description = "Kotlin extensions for 'lifecycle' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/lifecycle/lifecycle-runtime-testing/bcv/native/current.txt b/lifecycle/lifecycle-runtime-testing/bcv/native/current.txt
new file mode 100644
index 0000000..85afe97
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-testing/bcv/native/current.txt
@@ -0,0 +1,20 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.lifecycle:lifecycle-runtime-testing>
+final class androidx.lifecycle.testing/TestLifecycleOwner : androidx.lifecycle/LifecycleOwner { // androidx.lifecycle.testing/TestLifecycleOwner|null[0]
+    constructor <init>(androidx.lifecycle/Lifecycle.State =..., kotlinx.coroutines/CoroutineDispatcher =...) // androidx.lifecycle.testing/TestLifecycleOwner.<init>|<init>(androidx.lifecycle.Lifecycle.State;kotlinx.coroutines.CoroutineDispatcher){}[0]
+    final fun handleLifecycleEvent(androidx.lifecycle/Lifecycle.Event) // androidx.lifecycle.testing/TestLifecycleOwner.handleLifecycleEvent|handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event){}[0]
+    final suspend fun setCurrentState(androidx.lifecycle/Lifecycle.State) // androidx.lifecycle.testing/TestLifecycleOwner.setCurrentState|setCurrentState(androidx.lifecycle.Lifecycle.State){}[0]
+    final val lifecycle // androidx.lifecycle.testing/TestLifecycleOwner.lifecycle|{}lifecycle[0]
+        final fun <get-lifecycle>(): androidx.lifecycle/LifecycleRegistry // androidx.lifecycle.testing/TestLifecycleOwner.lifecycle.<get-lifecycle>|<get-lifecycle>(){}[0]
+    final val observerCount // androidx.lifecycle.testing/TestLifecycleOwner.observerCount|{}observerCount[0]
+        final fun <get-observerCount>(): kotlin/Int // androidx.lifecycle.testing/TestLifecycleOwner.observerCount.<get-observerCount>|<get-observerCount>(){}[0]
+    final var currentState // androidx.lifecycle.testing/TestLifecycleOwner.currentState|{}currentState[0]
+        final fun <get-currentState>(): androidx.lifecycle/Lifecycle.State // androidx.lifecycle.testing/TestLifecycleOwner.currentState.<get-currentState>|<get-currentState>(){}[0]
+        final fun <set-currentState>(androidx.lifecycle/Lifecycle.State) // androidx.lifecycle.testing/TestLifecycleOwner.currentState.<set-currentState>|<set-currentState>(androidx.lifecycle.Lifecycle.State){}[0]
+}
diff --git a/lifecycle/lifecycle-runtime-testing/build.gradle b/lifecycle/lifecycle-runtime-testing/build.gradle
index 1025722..430b902 100644
--- a/lifecycle/lifecycle-runtime-testing/build.gradle
+++ b/lifecycle/lifecycle-runtime-testing/build.gradle
@@ -21,29 +21,53 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
+
+import androidx.build.PlatformIdentifier
 import androidx.build.LibraryType
 
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
-    id("org.jetbrains.kotlin.android")
+}
+
+androidXMultiplatform {
+    android()
+    desktop()
+    mac()
+    linux()
+    ios()
+
+    defaultPlatform(PlatformIdentifier.ANDROID)
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(libs.kotlinStdlib)
+                api(project(":lifecycle:lifecycle-runtime"))
+            }
+        }
+
+        commonTest {
+            dependencies {
+                implementation(libs.kotlinCoroutinesTest)
+                implementation(libs.kotlinTest)
+                implementation(project(":kruth:kruth"))
+            }
+        }
+
+        androidInstrumentedTest {
+            dependencies {
+                implementation(libs.truth)
+                implementation(libs.testExtJunit)
+                implementation(libs.testCore)
+                implementation(libs.testRunner)
+                implementation(libs.kotlinCoroutinesTest)
+            }
+        }
+    }
 }
 
 dependencies {
-    api(project(":lifecycle:lifecycle-runtime-ktx"))
-    api(libs.kotlinStdlib)
-    api(libs.kotlinCoroutinesAndroid)
-
-    testImplementation(libs.truth)
-    testImplementation(libs.junit)
-    testImplementation(libs.kotlinCoroutinesTest)
-
-    androidTestImplementation(libs.kotlinCoroutinesTest)
-    androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-
     lintPublish(project(":lifecycle:lifecycle-runtime-testing-lint"))
 }
 
@@ -52,8 +76,8 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2019"
     description = "Testing utilities for 'lifecycle' artifact"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 android {
diff --git a/lifecycle/lifecycle-runtime-testing/src/androidTest/java/androidx/lifecycle/testing/TestLifecycleOwnerAndroidTest.kt b/lifecycle/lifecycle-runtime-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/testing/TestLifecycleOwnerAndroidTest.kt
similarity index 100%
rename from lifecycle/lifecycle-runtime-testing/src/androidTest/java/androidx/lifecycle/testing/TestLifecycleOwnerAndroidTest.kt
rename to lifecycle/lifecycle-runtime-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/testing/TestLifecycleOwnerAndroidTest.kt
diff --git a/lifecycle/lifecycle-runtime-testing/src/main/AndroidManifest.xml b/lifecycle/lifecycle-runtime-testing/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from lifecycle/lifecycle-runtime-testing/src/main/AndroidManifest.xml
rename to lifecycle/lifecycle-runtime-testing/src/androidMain/AndroidManifest.xml
diff --git a/lifecycle/lifecycle-runtime-testing/src/commonMain/kotlin/androidx/lifecycle/testing/TestLifecycleOwner.kt b/lifecycle/lifecycle-runtime-testing/src/commonMain/kotlin/androidx/lifecycle/testing/TestLifecycleOwner.kt
new file mode 100644
index 0000000..1a7b5f2
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-testing/src/commonMain/kotlin/androidx/lifecycle/testing/TestLifecycleOwner.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.lifecycle.testing
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import kotlin.jvm.JvmOverloads
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+
+/**
+ * Create a [LifecycleOwner] that allows changing the state via the [handleLifecycleEvent] method or
+ * [currentState] property.
+ *
+ * Under the hood, this uses a [LifecycleRegistry]. However, it uses
+ * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate] as the default
+ * [coroutineDispatcher] to ensure that all mutations to the [current state][currentState] are run
+ * on that dispatcher, no matter what thread you mutate the state from.
+ *
+ * @param initialState The initial [Lifecycle.State].
+ * @param coroutineDispatcher A [CoroutineDispatcher] to use when dispatching work from this class.
+ */
+public class TestLifecycleOwner
+@JvmOverloads
+constructor(
+    initialState: Lifecycle.State = Lifecycle.State.STARTED,
+    private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.Main.immediate
+) : LifecycleOwner {
+    // it is in test artifact
+    private val lifecycleRegistry =
+        LifecycleRegistry.createUnsafe(this).apply { currentState = initialState }
+
+    override val lifecycle: LifecycleRegistry
+        get() = lifecycleRegistry
+
+    /**
+     * Update the [currentState] by moving it to the state directly after the given [event]. This is
+     * safe to mutate on any thread, but will block that thread during execution.
+     */
+    public fun handleLifecycleEvent(event: Lifecycle.Event) {
+        runBlocking(coroutineDispatcher) { lifecycleRegistry.handleLifecycleEvent(event) }
+    }
+
+    /**
+     * The current [Lifecycle.State] of this owner. This is safe to call on any thread but is
+     * thread-blocking and should not be called from within a coroutine (use [setCurrentState]
+     * instead).
+     */
+    public var currentState: Lifecycle.State
+        get() = runBlocking(coroutineDispatcher) { lifecycleRegistry.currentState }
+        set(value) {
+            runBlocking(coroutineDispatcher) { lifecycleRegistry.currentState = value }
+        }
+
+    /**
+     * Updates the [currentState]. This suspending function is safe to call on any thread and will
+     * not block that thread. If the state should be updated from outside of a suspending function,
+     * use [currentState] property syntax instead.
+     */
+    suspend fun setCurrentState(state: Lifecycle.State) {
+        withContext(coroutineDispatcher) { lifecycleRegistry.currentState = state }
+    }
+
+    /** Get the number of observers. */
+    public val observerCount: Int
+        get() = lifecycleRegistry.observerCount
+}
diff --git a/lifecycle/lifecycle-runtime-testing/src/commonTest/kotlin/androidx/lifecycle/testing/LifecycleEventFlowTest.kt b/lifecycle/lifecycle-runtime-testing/src/commonTest/kotlin/androidx/lifecycle/testing/LifecycleEventFlowTest.kt
new file mode 100644
index 0000000..c69fffe
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-testing/src/commonTest/kotlin/androidx/lifecycle/testing/LifecycleEventFlowTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.lifecycle.testing
+
+import androidx.kruth.assertThat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.eventFlow
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class LifecycleEventFlowTest {
+
+    private val dispatcher = UnconfinedTestDispatcher()
+    private val owner = TestLifecycleOwner(coroutineDispatcher = dispatcher)
+    private val testScope = TestScope(dispatcher)
+
+    @BeforeTest
+    fun setMainDispatcher() {
+        Dispatchers.setMain(dispatcher)
+    }
+
+    @Test
+    fun testLifecycleEventFlow() =
+        testScope.runTest {
+            val collectedEvents = mutableListOf<Lifecycle.Event>()
+            val lifecycleEventFlow = owner.lifecycle.eventFlow
+            backgroundScope.launch { lifecycleEventFlow.collect { collectedEvents.add(it) } }
+            owner.currentState = Lifecycle.State.CREATED
+            assertThat(collectedEvents)
+                .containsExactly(
+                    Lifecycle.Event.ON_CREATE,
+                    Lifecycle.Event.ON_START,
+                    Lifecycle.Event.ON_STOP
+                )
+                .inOrder()
+        }
+
+    @Test
+    fun testEventFlowStopsCollectingAfterDestroyed() =
+        testScope.runTest {
+            val collectedEvents = mutableListOf<Lifecycle.Event>()
+            val lifecycleEventFlow = owner.lifecycle.eventFlow
+            backgroundScope.launch { lifecycleEventFlow.collect { collectedEvents.add(it) } }
+            owner.currentState = Lifecycle.State.CREATED
+            owner.currentState = Lifecycle.State.DESTROYED
+            owner.currentState = Lifecycle.State.RESUMED
+            assertThat(collectedEvents)
+                .containsExactly(
+                    Lifecycle.Event.ON_CREATE,
+                    Lifecycle.Event.ON_START,
+                    Lifecycle.Event.ON_STOP,
+                    Lifecycle.Event.ON_DESTROY
+                )
+                .inOrder()
+        }
+
+    @Test
+    fun testEventFlowStopsCollectingAfterJobCancelled() =
+        testScope.runTest {
+            val collectedEvents = mutableListOf<Lifecycle.Event>()
+            val lifecycleEventFlow = owner.lifecycle.eventFlow
+            val job =
+                backgroundScope.launch { lifecycleEventFlow.collect { collectedEvents.add(it) } }
+            owner.currentState = Lifecycle.State.CREATED
+            assertThat(collectedEvents)
+                .containsExactly(
+                    Lifecycle.Event.ON_CREATE,
+                    Lifecycle.Event.ON_START,
+                    Lifecycle.Event.ON_STOP,
+                )
+                .inOrder()
+            collectedEvents.clear()
+            job.cancel()
+            owner.currentState = Lifecycle.State.RESUMED
+            assertThat(collectedEvents).isEmpty()
+        }
+}
diff --git a/lifecycle/lifecycle-runtime-testing/src/commonTest/kotlin/androidx/lifecycle/testing/LifecycleRegistryTest.kt b/lifecycle/lifecycle-runtime-testing/src/commonTest/kotlin/androidx/lifecycle/testing/LifecycleRegistryTest.kt
new file mode 100644
index 0000000..8ae88a8
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-testing/src/commonTest/kotlin/androidx/lifecycle/testing/LifecycleRegistryTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.lifecycle.testing
+
+import androidx.kruth.assertThat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleRegistry
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class LifecycleRegistryTest {
+
+    private val dispatcher = UnconfinedTestDispatcher()
+    private val lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED, dispatcher)
+    private val testScope = TestScope(dispatcher)
+
+    @BeforeTest
+    fun setMainDispatcher() {
+        Dispatchers.setMain(dispatcher)
+    }
+
+    @Test
+    fun getCurrentState() {
+        lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+        assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.RESUMED)
+
+        lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+        assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.DESTROYED)
+    }
+
+    @Test
+    fun getCurrentStateFlow() {
+        val lifecycle = lifecycleOwner.lifecycle
+        assertThat(lifecycle.currentStateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
+
+        lifecycleOwner.currentState = Lifecycle.State.CREATED
+        assertThat(lifecycle.currentStateFlow.value).isEqualTo(Lifecycle.State.CREATED)
+
+        lifecycleOwner.currentState = Lifecycle.State.DESTROYED
+        assertThat(lifecycle.currentStateFlow.value).isEqualTo(Lifecycle.State.DESTROYED)
+    }
+
+    @Test
+    fun getCurrentStateFlowWithReentranceNoObservers() =
+        testScope.runTest {
+            val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
+            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
+
+            backgroundScope.launch {
+                stateFlow.collect { lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) }
+            }
+
+            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.CREATED)
+            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.CREATED)
+        }
+
+    @Test
+    fun getCurrentStateFlowWithObserverReentrance() =
+        testScope.runTest {
+            val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
+            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
+
+            lifecycleOwner.lifecycle.addObserver(
+                LifecycleEventObserver { owner, _ ->
+                    (owner.lifecycle as LifecycleRegistry).handleLifecycleEvent(
+                        Lifecycle.Event.ON_RESUME
+                    )
+                }
+            )
+
+            backgroundScope.launch { stateFlow.collect {} }
+
+            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.RESUMED)
+            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.RESUMED)
+        }
+
+    @Test
+    fun getCurrentStateFlowWithObserverWithFlowReentrance() =
+        testScope.runTest {
+            val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
+            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
+
+            lateinit var event: Lifecycle.Event
+            lifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, e -> event = e })
+
+            backgroundScope.launch {
+                stateFlow.collect { lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) }
+            }
+
+            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.CREATED)
+            assertThat(event).isEqualTo(Lifecycle.Event.ON_CREATE)
+        }
+
+    @Test
+    fun observerCount() {
+        lifecycleOwner.currentState = Lifecycle.State.STARTED
+        assertThat(lifecycleOwner.observerCount).isEqualTo(0)
+        lifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, _ -> })
+        assertThat(lifecycleOwner.observerCount).isEqualTo(1)
+    }
+}
diff --git a/lifecycle/lifecycle-runtime-testing/src/main/java/androidx/lifecycle/testing/TestLifecycleOwner.kt b/lifecycle/lifecycle-runtime-testing/src/main/java/androidx/lifecycle/testing/TestLifecycleOwner.kt
deleted file mode 100644
index 5a23389..0000000
--- a/lifecycle/lifecycle-runtime-testing/src/main/java/androidx/lifecycle/testing/TestLifecycleOwner.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.lifecycle.testing
-
-import android.annotation.SuppressLint
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LifecycleRegistry
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
-
-/**
- * Create a [LifecycleOwner] that allows changing the state via the [handleLifecycleEvent] method or
- * [currentState] property.
- *
- * Under the hood, this uses a [LifecycleRegistry]. However, it uses
- * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate] as the default
- * [coroutineDispatcher] to ensure that all mutations to the [current state][currentState] are run
- * on that dispatcher, no matter what thread you mutate the state from.
- *
- * @param initialState The initial [Lifecycle.State].
- * @param coroutineDispatcher A [CoroutineDispatcher] to use when dispatching work from this class.
- */
-public class TestLifecycleOwner
-@JvmOverloads
-constructor(
-    initialState: Lifecycle.State = Lifecycle.State.STARTED,
-    private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.Main.immediate
-) : LifecycleOwner {
-    // it is in test artifact
-    @SuppressLint("VisibleForTests")
-    private val lifecycleRegistry =
-        LifecycleRegistry.createUnsafe(this).apply { currentState = initialState }
-
-    override val lifecycle: LifecycleRegistry
-        get() = lifecycleRegistry
-
-    /**
-     * Update the [currentState] by moving it to the state directly after the given [event]. This is
-     * safe to mutate on any thread, but will block that thread during execution.
-     */
-    public fun handleLifecycleEvent(event: Lifecycle.Event) {
-        runBlocking(coroutineDispatcher) { lifecycleRegistry.handleLifecycleEvent(event) }
-    }
-
-    /**
-     * The current [Lifecycle.State] of this owner. This is safe to call on any thread but is
-     * thread-blocking and should not be called from within a coroutine (use [setCurrentState]
-     * instead).
-     */
-    public var currentState: Lifecycle.State
-        get() = runBlocking(coroutineDispatcher) { lifecycleRegistry.currentState }
-        set(value) {
-            runBlocking(coroutineDispatcher) { lifecycleRegistry.currentState = value }
-        }
-
-    /**
-     * Updates the [currentState]. This suspending function is safe to call on any thread and will
-     * not block that thread. If the state should be updated from outside of a suspending function,
-     * use [currentState] property syntax instead.
-     */
-    suspend fun setCurrentState(state: Lifecycle.State) {
-        withContext(coroutineDispatcher) { lifecycleRegistry.currentState = state }
-    }
-
-    /** Get the number of observers. */
-    public val observerCount: Int
-        get() = lifecycleRegistry.observerCount
-}
diff --git a/lifecycle/lifecycle-runtime-testing/src/test/java/androidx/lifecycle/testing/LifecycleEventFlowTest.kt b/lifecycle/lifecycle-runtime-testing/src/test/java/androidx/lifecycle/testing/LifecycleEventFlowTest.kt
deleted file mode 100644
index dddbbab..0000000
--- a/lifecycle/lifecycle-runtime-testing/src/test/java/androidx/lifecycle/testing/LifecycleEventFlowTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.lifecycle.testing
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.eventFlow
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(JUnit4::class)
-class LifecycleEventFlowTest {
-
-    private val dispatcher = UnconfinedTestDispatcher()
-    private val owner = TestLifecycleOwner(coroutineDispatcher = dispatcher)
-    private val testScope = TestScope(dispatcher)
-
-    @Before
-    fun setMainDispatcher() {
-        Dispatchers.setMain(dispatcher)
-    }
-
-    @Test
-    fun testLifecycleEventFlow() =
-        testScope.runTest {
-            val collectedEvents = mutableListOf<Lifecycle.Event>()
-            val lifecycleEventFlow = owner.lifecycle.eventFlow
-            backgroundScope.launch { lifecycleEventFlow.collect { collectedEvents.add(it) } }
-            owner.currentState = Lifecycle.State.CREATED
-            assertThat(collectedEvents)
-                .containsExactly(
-                    Lifecycle.Event.ON_CREATE,
-                    Lifecycle.Event.ON_START,
-                    Lifecycle.Event.ON_STOP
-                )
-                .inOrder()
-        }
-
-    @Test
-    fun testEventFlowStopsCollectingAfterDestroyed() =
-        testScope.runTest {
-            val collectedEvents = mutableListOf<Lifecycle.Event>()
-            val lifecycleEventFlow = owner.lifecycle.eventFlow
-            backgroundScope.launch { lifecycleEventFlow.collect { collectedEvents.add(it) } }
-            owner.currentState = Lifecycle.State.CREATED
-            owner.currentState = Lifecycle.State.DESTROYED
-            owner.currentState = Lifecycle.State.RESUMED
-            assertThat(collectedEvents)
-                .containsExactly(
-                    Lifecycle.Event.ON_CREATE,
-                    Lifecycle.Event.ON_START,
-                    Lifecycle.Event.ON_STOP,
-                    Lifecycle.Event.ON_DESTROY
-                )
-                .inOrder()
-        }
-
-    @Test
-    fun testEventFlowStopsCollectingAfterJobCancelled() =
-        testScope.runTest {
-            val collectedEvents = mutableListOf<Lifecycle.Event>()
-            val lifecycleEventFlow = owner.lifecycle.eventFlow
-            val job =
-                backgroundScope.launch { lifecycleEventFlow.collect { collectedEvents.add(it) } }
-            owner.currentState = Lifecycle.State.CREATED
-            assertThat(collectedEvents)
-                .containsExactly(
-                    Lifecycle.Event.ON_CREATE,
-                    Lifecycle.Event.ON_START,
-                    Lifecycle.Event.ON_STOP,
-                )
-                .inOrder()
-            collectedEvents.clear()
-            job.cancel()
-            owner.currentState = Lifecycle.State.RESUMED
-            assertThat(collectedEvents).isEmpty()
-        }
-}
diff --git a/lifecycle/lifecycle-runtime-testing/src/test/java/androidx/lifecycle/testing/LifecycleRegistryTest.kt b/lifecycle/lifecycle-runtime-testing/src/test/java/androidx/lifecycle/testing/LifecycleRegistryTest.kt
deleted file mode 100644
index 6b3e405..0000000
--- a/lifecycle/lifecycle-runtime-testing/src/test/java/androidx/lifecycle/testing/LifecycleRegistryTest.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.lifecycle.testing
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
-import androidx.lifecycle.LifecycleRegistry
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
-@RunWith(JUnit4::class)
-class LifecycleRegistryTest {
-
-    private val dispatcher = UnconfinedTestDispatcher()
-    private val lifecycleOwner = TestLifecycleOwner(Lifecycle.State.INITIALIZED, dispatcher)
-    private val testScope = TestScope(dispatcher)
-
-    @Before
-    fun setMainDispatcher() {
-        Dispatchers.setMain(dispatcher)
-    }
-
-    @Test
-    fun getCurrentState() {
-        lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
-        assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.RESUMED)
-
-        lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
-        assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.DESTROYED)
-    }
-
-    @Test
-    fun getCurrentStateFlow() {
-        val lifecycle = lifecycleOwner.lifecycle
-        assertThat(lifecycle.currentStateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
-
-        lifecycleOwner.currentState = Lifecycle.State.CREATED
-        assertThat(lifecycle.currentStateFlow.value).isEqualTo(Lifecycle.State.CREATED)
-
-        lifecycleOwner.currentState = Lifecycle.State.DESTROYED
-        assertThat(lifecycle.currentStateFlow.value).isEqualTo(Lifecycle.State.DESTROYED)
-    }
-
-    @Test
-    fun getCurrentStateFlowWithReentranceNoObservers() =
-        testScope.runTest {
-            val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
-            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
-            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
-
-            backgroundScope.launch {
-                stateFlow.collect { lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) }
-            }
-
-            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.CREATED)
-            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.CREATED)
-        }
-
-    @Test
-    fun getCurrentStateFlowWithObserverReentrance() =
-        testScope.runTest {
-            val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
-            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
-            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
-
-            lifecycleOwner.lifecycle.addObserver(
-                LifecycleEventObserver { owner, _ ->
-                    (owner.lifecycle as LifecycleRegistry).handleLifecycleEvent(
-                        Lifecycle.Event.ON_RESUME
-                    )
-                }
-            )
-
-            backgroundScope.launch { stateFlow.collect {} }
-
-            lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
-            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.RESUMED)
-            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.RESUMED)
-        }
-
-    @Test
-    fun getCurrentStateFlowWithObserverWithFlowReentrance() =
-        testScope.runTest {
-            val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
-            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
-            assertThat(stateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
-
-            lateinit var event: Lifecycle.Event
-            lifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, e -> event = e })
-
-            backgroundScope.launch {
-                stateFlow.collect { lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) }
-            }
-
-            assertThat(lifecycleOwner.currentState).isEqualTo(Lifecycle.State.CREATED)
-            assertThat(event).isEqualTo(Lifecycle.Event.ON_CREATE)
-        }
-
-    @Test
-    fun observerCount() {
-        lifecycleOwner.currentState = Lifecycle.State.STARTED
-        assertThat(lifecycleOwner.observerCount).isEqualTo(0)
-        lifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, _ -> })
-        assertThat(lifecycleOwner.observerCount).isEqualTo(1)
-    }
-}
diff --git a/lifecycle/lifecycle-runtime/bcv/native/current.txt b/lifecycle/lifecycle-runtime/bcv/native/current.txt
index be71f45..0ddfa64 100644
--- a/lifecycle/lifecycle-runtime/bcv/native/current.txt
+++ b/lifecycle/lifecycle-runtime/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/lifecycle/lifecycle-runtime/build.gradle b/lifecycle/lifecycle-runtime/build.gradle
index cd82e0c..7f3a53e 100644
--- a/lifecycle/lifecycle-runtime/build.gradle
+++ b/lifecycle/lifecycle-runtime/build.gradle
@@ -17,8 +17,6 @@
     id("androidx.baselineprofile")
 }
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
     desktop()
     mac()
@@ -36,7 +34,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(project(":lifecycle:lifecycle-common"))
-                api("androidx.annotation:annotation:1.8.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
@@ -50,7 +48,7 @@
             }
         }
 
-        jvmMain {
+        jvmCommonMain {
             dependsOn(commonMain)
             dependencies {
                 api("androidx.arch.core:core-common:2.2.0")
@@ -58,7 +56,7 @@
         }
 
         desktopMain {
-            dependsOn(jvmMain)
+            dependsOn(jvmCommonMain)
         }
 
         desktopTest {
@@ -69,7 +67,7 @@
         }
 
         androidMain {
-            dependsOn(jvmMain)
+            dependsOn(jvmCommonMain)
             dependencies {
                 api(libs.kotlinCoroutinesAndroid)
                 implementation("androidx.arch.core:core-runtime:2.2.0")
@@ -97,12 +95,12 @@
             }
         }
 
-        nonJvmMain {
+        nonJvmCommonMain {
             dependsOn(commonMain)
         }
 
         nativeMain {
-            dependsOn(nonJvmMain)
+            dependsOn(nonJvmCommonMain)
 
             // Required for WeakReference usage
             languageSettings.optIn("kotlin.experimental.ExperimentalNativeApi")
@@ -141,6 +139,8 @@
     sourceSets["main"].java.srcDir("src/androidMain/java")
     sourceSets["test"].java.srcDir("src/androidUnitTest/kotlin")
     namespace "androidx.lifecycle.runtime"
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidx {
@@ -148,4 +148,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear "2017"
     description "Android Lifecycle Runtime"
+    metalavaK2UastEnabled = false
 }
diff --git a/lifecycle/lifecycle-runtime/src/jvmMain/kotlin/androidx/lifecycle/LifecycleRegistry.jvm.kt b/lifecycle/lifecycle-runtime/src/jvmCommonMain/kotlin/androidx/lifecycle/LifecycleRegistry.jvm.kt
similarity index 100%
rename from lifecycle/lifecycle-runtime/src/jvmMain/kotlin/androidx/lifecycle/LifecycleRegistry.jvm.kt
rename to lifecycle/lifecycle-runtime/src/jvmCommonMain/kotlin/androidx/lifecycle/LifecycleRegistry.jvm.kt
diff --git a/lifecycle/lifecycle-runtime/src/nonJvmMain/kotlin/androidx/lifecycle/LifecycleRegistry.nonJvm.kt b/lifecycle/lifecycle-runtime/src/nonJvmCommonMain/kotlin/androidx/lifecycle/LifecycleRegistry.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-runtime/src/nonJvmMain/kotlin/androidx/lifecycle/LifecycleRegistry.nonJvm.kt
rename to lifecycle/lifecycle-runtime/src/nonJvmCommonMain/kotlin/androidx/lifecycle/LifecycleRegistry.nonJvm.kt
diff --git a/lifecycle/lifecycle-runtime/src/nonJvmMain/kotlin/androidx/lifecycle/WeakReference.nonJvm.kt b/lifecycle/lifecycle-runtime/src/nonJvmCommonMain/kotlin/androidx/lifecycle/WeakReference.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-runtime/src/nonJvmMain/kotlin/androidx/lifecycle/WeakReference.nonJvm.kt
rename to lifecycle/lifecycle-runtime/src/nonJvmCommonMain/kotlin/androidx/lifecycle/WeakReference.nonJvm.kt
diff --git a/lifecycle/lifecycle-service/build.gradle b/lifecycle/lifecycle-service/build.gradle
index c9a026b..a1f1195 100644
--- a/lifecycle/lifecycle-service/build.gradle
+++ b/lifecycle/lifecycle-service/build.gradle
@@ -44,7 +44,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Lifecycle Service"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/lifecycle/lifecycle-viewmodel-compose-lint/build.gradle b/lifecycle/lifecycle-viewmodel-compose-lint/build.gradle
new file mode 100644
index 0000000..ab1344d
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose-lint/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+
+import androidx.build.BundleInsideHelper
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+BundleInsideHelper.forInsideLintJar(project)
+
+dependencies {
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.kotlinStdlib)
+    bundleInside(projectOrArtifact(":compose:lint:common"))
+
+    testImplementation(projectOrArtifact(":compose:lint:common-test"))
+    testImplementation(libs.kotlinStdlib)
+    testImplementation(libs.kotlinReflect)
+    testImplementation(libs.kotlinStdlibJdk8)
+    testImplementation(libs.androidLint)
+    testImplementation(libs.androidLintTests)
+    testImplementation(libs.junit)
+}
+
+androidx {
+    name = "Lifecycle ViewModel Compose Lint Checks"
+    type = LibraryType.LINT
+    inceptionYear = "2024"
+    description = "Android Lifecycles Lint Checks"
+}
diff --git a/lifecycle/lifecycle-viewmodel-compose-lint/src/main/java/androidx/lifecycle/lint/LifecycleViewModelComposeIssueRegistry.kt b/lifecycle/lifecycle-viewmodel-compose-lint/src/main/java/androidx/lifecycle/lint/LifecycleViewModelComposeIssueRegistry.kt
new file mode 100644
index 0000000..2e1099f
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose-lint/src/main/java/androidx/lifecycle/lint/LifecycleViewModelComposeIssueRegistry.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+
+@Suppress("UnstableApiUsage")
+class LifecycleViewModelComposeIssueRegistry : IssueRegistry() {
+    // tests are run with this version. We ensure that with ApiLintVersionsTest
+    override val api = 14
+    override val minApi = CURRENT_API
+    override val issues
+        get() = listOf(ViewModelConstructorInComposableDetector.ISSUE)
+
+    override val vendor =
+        Vendor(
+            feedbackUrl = "https://issuetracker.google.com/issues/new?component=413132",
+            identifier = "androidx.lifecycle",
+            vendorName = "Android Open Source Project",
+        )
+}
diff --git a/lifecycle/lifecycle-viewmodel-compose-lint/src/main/java/androidx/lifecycle/lint/ViewModelConstructorInComposableDetector.kt b/lifecycle/lifecycle-viewmodel-compose-lint/src/main/java/androidx/lifecycle/lint/ViewModelConstructorInComposableDetector.kt
new file mode 100644
index 0000000..0114cd8
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose-lint/src/main/java/androidx/lifecycle/lint/ViewModelConstructorInComposableDetector.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.lint
+
+import androidx.compose.lint.Name
+import androidx.compose.lint.Package
+import androidx.compose.lint.inheritsFrom
+import androidx.compose.lint.isInvokedWithinComposable
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.util.isConstructorCall
+
+/** [Detector] that checks if a view model is being constructed directly in a composable. */
+class ViewModelConstructorInComposableDetector : Detector(), SourceCodeScanner {
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UCallExpression::class.java)
+    }
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitCallExpression(node: UCallExpression) {
+                if (!node.isInvokedWithinComposable()) return
+                if (!node.isConstructorCall()) return
+
+                val containingClass = node.resolve()?.containingClass ?: return
+                if (containingClass.inheritsFrom(FqViewModelName)) {
+                    context.report(
+                        ISSUE,
+                        node,
+                        context.getNameLocation(node),
+                        "Constructing a view model in a composable"
+                    )
+                }
+            }
+        }
+    }
+
+    companion object {
+        private val FqViewModelName = Name(Package("androidx.lifecycle"), "ViewModel")
+
+        val ISSUE =
+            Issue.create(
+                "ViewModelConstructorInComposable",
+                "Constructing a view model in a composable",
+                "View models should not be constructed directly inside composable" +
+                    " functions. Instead you should use the lifecycle viewmodel extension" +
+                    "functions e.g. viewModel<MyViewModel>()",
+                Category.CORRECTNESS,
+                3,
+                Severity.ERROR,
+                Implementation(
+                    ViewModelConstructorInComposableDetector::class.java,
+                    EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+                )
+            )
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel-compose-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/lifecycle/lifecycle-viewmodel-compose-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
new file mode 100644
index 0000000..3dc32ba
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
@@ -0,0 +1 @@
+androidx.lifecycle.lint.LifecycleViewModelComposeIssueRegistry
\ No newline at end of file
diff --git a/lifecycle/lifecycle-viewmodel-compose-lint/src/test/java/androidx/lifecycle/viewmodel/compose/lint/Stubs.kt b/lifecycle/lifecycle-viewmodel-compose-lint/src/test/java/androidx/lifecycle/viewmodel/compose/lint/Stubs.kt
new file mode 100644
index 0000000..997e846
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose-lint/src/test/java/androidx/lifecycle/viewmodel/compose/lint/Stubs.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.compose.lint.lint
+
+import androidx.compose.lint.test.bytecodeStub
+
+internal val VIEWMODEL =
+    bytecodeStub(
+        filename = "ViewModel.kt",
+        filepath = "androidx/lifecycle",
+        checksum = 0xacdef33f,
+        source =
+            """
+                package androidx.lifecycle
+                public open class ViewModel
+
+                public class ViewModelStoreOwner
+                public class ViewModelProvider {
+                   public class Factory {
+                        public fun <T : ViewModel> create(): T {
+                            return MockStore<T>().vm
+                        }
+
+                        private class MockStore<T : ViewModel> {
+                            lateinit var vm: T
+                        }
+                   }
+                }
+                public class CreationExtras
+            """
+                .trimIndent(),
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuZSScxLKcrPTKnQy8lMS02uTM5J
+                1SvLTC3PzU9JzdFLzs8tyC9OFeIOAwr5goS8S7hkuLiB4nqpFYm5BTmpQry+
+                lUiySgxaDABrUUXUawAAAA==
+                """,
+        """
+                androidx/lifecycle/CreationExtras.class:
+                H4sIAAAAAAAA/4VRTS8DQRh+3qluWcXWZ30m4oKDLXEjEoSkSZEgvThNdwej
+                29lkd0rd+lv8AyeJgzSOfpR4d7m7PHk+3pk878zX9/sHgF0sE1alCZNYhz0/
+                0rcqeA4i5R8nSlodm5OeTWRaAhG8B/ko/UiaO/+i9aACW0KB4Oxro+0BobC+
+                0SyjCMfFEEqEIXuvU8Ja49/b9wiVRju2kTb+mbIylFayJzqPBa5IGYxkAAK1
+                2e/pTNWYhduElUHfdUVVuMJjNuhXB/0dUaOj4ueLIzyRTe1Qdrbc1OrpLA5V
+                tNW23O+YKWGioY0673ZaKrmWrYidyUYcyKgpE53pP9O9irtJoE51JuYvu8bq
+                jmrqVHN6aExs831SbEPw+n9ls9dgrLLycw0UN98w/MpEYJ7RyU0PC4zl3wGM
+                wM3zxRznsJT/E2GUs/INCnWM1TFexwQ8pqjUMYmpG1CKacxwnsJNMZvC+QGU
+                yd0P5AEAAA==
+                """,
+        """
+                androidx/lifecycle/ViewModel.class:
+                H4sIAAAAAAAA/31RTS8DQRh+3qlua5W2Pqo+ww0Hi7gRCRJJk5YE6cVpujuY
+                djubdKfFrb/FP3CSOEjj6EeJd5c4ujx5Pt6ZeWbm8+vtHcA+VgjL0gS9SAeP
+                Xqhvlf/kh8pravXQiAIV5kCEUlsOpBdKc+ddtNrKtzlkCM6hNtoeETIbm80C
+                snBcjCFHGLP3Oias1v/b+IBQrnciG2rjNZSVgbSSPdEdZLgYJTCeAAjUYf9R
+                J2qHWbBLWBsNXVdUhStKzEbDfKU6Gu6JHTrJfjw7oiSSuT1KVhf+jtzuWC53
+                ypRQrGujzvvdlupdy1bIznQ98mXYlD2d6F/TvYr6PV+d6UQsXPaN1V3V1LHm
+                9NiYyEqrIxNjHYLv/ls3eQrGKisv1UB26xX5FyYCC4xOagosMhZ+BjAON/WW
+                UpzHcvo/hAnOCjfI1DBZw1QNRZSYolzDNGZuQDFmMcd5DDdGJYbzDcrOclbc
+                AQAA
+                """,
+        """
+                androidx/lifecycle/ViewModelProvider$Factory$MockStore.class:
+                H4sIAAAAAAAA/5VUW28TRxT+Zn3ZtXHK2hSahJAGSIvtQDakKU1JGggpVJac
+                FGFjWuVpsp46E693o92xSfrkp/6Q/gIqtSrqQ2Xx0If+KNQz9ioNNwsk68y5
+                fOc7Z86c9b8v//obwAruM9zifjMMZPPI8eRPwj12PeE0pHi6HTSF9zAMerIp
+                wvkH3FVBeDy/HbjtGmnCBGNYWa/fro7LX9uoHvAedzzut5zv9w6Eq9YY7Nd9
+                JpIM6XXpS7XBkCiWGjmkYWaRgsWQVPsyYlgdW+ndnVLBVEuoRodhrlga3y5B
+                i6V6nc6r1SBsOQdC7YVc+pHDfT9QXMmA9J1A7XQ9jTZ6xDo7njMHG/kMDBQs
+                2IRuB8qTvnPQ6zjSVyL0uedUfBVSFelGJs4zlNR+GDx97OuBSO7Jn0WT7nco
+                QnW86boiiu4fueJQN8NwvnhqxDVN01rT8/sEk1lcwBTDzLj2TFykS0ej+Vwu
+                jr9KqUHPVKT5aMVcp6wbdzYszFEX7r5w2/FgHvKQdwRdjeFa8c0FeHu/V3BV
+                9ztP7z98gHw1HtS2ULzJFdfj7vQStLdMi4wWYGBt8h9JbS2R1rzJUB/0C1lj
+                0sgO+sPDsLVimZYxOeiXLWvQt1nZWDKWjaXEvdSLX9OGnXw0Y6emjdVB/4cX
+                vyySy6as7HTSStvmlaRl2RnNvUzl6kxXvf4hu2hihWH+fTJM3KLBxmkMmZMl
+                ZsidgBfbikI12fK56upQcovcDGer0hc73c6eCOt8zyNPoRq43GvwUGo7dk7U
+                FHfb2/wwti8+6vpKdkTF78lIkmvz/1Wntl+PnjzuK7BcxfdFuOXxKBJkZmtB
+                N3TFA6kLTMUUjTfosUSfRWr0jPorIblGloHPkSCd/gJIrpPHGSKAVPlPZH4b
+                Qr4hmR46z2CDZG4EQJZsEFUOE0Sik78jtI7ly7/j3I//IPnsSeHj55hmz8iZ
+                wJ0RUcXEzCnS3CnSfEw66u0j3I1RZ4exS5iNC92lqKGzFgqXn+Oz8sIfOPdq
+                s+mY98IIF/Nqbe7U7T/FJp0mi0skcY9kgQJf4iusYmqofY3pGJ7A1vC8jW/p
+                rFLWNZpDcReJCkoVlCtYwHVScaOCRTi7YHrwN3dxJtK/5Qj5CF9EMCPYEXIR
+                JiJcijBL/v8ARg7ZkCkGAAA=
+                """,
+        """
+                androidx/lifecycle/ViewModelProvider$Factory.class:
+                H4sIAAAAAAAA/5VSXU8TQRQ9M9tu222VBUH5UESo0iKyQNQHJCTahFhT0EjT
+                xPA03Y516HY32Z1WeOtv8RfoiySamMZHf5Tx7tKg0URlHu7ce+bcc2fu3G/f
+                P30BcB/rDKvCb4WBah07nnot3RPXk05Dybd7QUt6L8Kgr1oyLO4KVwfhSQaM
+                wT4SfeF4wm87z5tH0tUZGAzmtvKV3mEwSuVGAWmYFlLIMKT0GxUxrNUuUugR
+                CbqhFFoyLJTKf80l7vJ2fevvnJ1SuV4n5lItCNvOkdTNUCg/coTvB1poFZC/
+                H+j9nhfrPbzIXYt7gds5IE9mMGbBjh+dbkvd6BYwgUKMXGEYr3UC7Snf2ZNa
+                tIQWVIZ3+wb9A4tNLjZgYB3Cj1Uc0e/w1gZDZTiYtPg0t7g9HFg8a5wFWT49
+                HGzydbbFMk/SX9+Z3ObPpmxjlj9NLWazw4GdWuHrZwdmLLXJkgJ1hvl/NTR3
+                /iaG4v80I4MiQ2bUEYbCOWGto2kGKuQyjNWUL/d73aYM66LpETJRC1zhNUSo
+                4ngE5g5U2xe6Fxefe9nzterKqt9XkaLjxz8/jMpUfV+GFU9EkaTQOgh6oSt3
+                VawyM8ps/JGHDXCazXhxagiNKtkSRU7cHtrTK6fIfkiOy2TNBDSxQrZwRkAO
+                Fu3jyBNiJMkPEjEg/xn2q1OMf8Tk+98ksr9I5EcSdxPOJayOWJdpN3CP7ATh
+                HLdxBzM0QhxLmMVawl6mmwIVYk/RVa4ewqjiWhXTVWLOkou5Kq7jxiFYhHnc
+                PEQ2ghVhIYIZIR/hVoTFCIUISz8ALZc+bgoEAAA=
+                """,
+        """
+                androidx/lifecycle/ViewModelProvider.class:
+                H4sIAAAAAAAA/41QTWsUQRB91bM7k0wmZhM/svnwkyAq4iRBEGIQNBAY2KgY
+                2cueemfapLOz3TDduya3/S3+A0+CB1k8+qOCNXHxnMur916/oqvqz+XPXwBe
+                4iFhS5qisro4T0v9ReUXeanSrlZfj2yhyo+VHetCVRGI0DqTY5mW0pykH/pn
+                KvcRAkK4r432bwjBk6fdBE2EMRqICA1/qh3hcec6H7wmLHcG1pfapEfKy0J6
+                yZ4YjgMelGqYrwEEGrB/rmu1zazYITyaTpJYtEUsWtNJLOZEezrZFdu0R8G7
+                5u9voWiJOrlLdX90KHNvqwvC8+tMtjWLR2gTkv/PLwaeVzxgSljqaKPej4Z9
+                VX2W/ZKdlY7NZdmVla71zEwyY1R1UErnFB8mPrajKleHun5b+zQyXg9VVzvN
+                4bfGWC+9tsZhB4IPOtu9vi/jBqv0SgPNZz8w952JwCZj+M/EXcZkxucRcw1w
+                jzFmb42zq4z3r7rW8YDrK/YXOJv0EGRYzHAjwxJaTLGcYQU3eyCHW7jdQ8Mh
+                drjjEDqs/gWgamwlTAIAAA==
+                """,
+        """
+                androidx/lifecycle/ViewModelStoreOwner.class:
+                H4sIAAAAAAAA/41Ry0rDQBQ9d9qmNlZb3/W5lOrCqLhTBBWEQqug0o2raTLq
+                tOkEkqmPXb/FP3AluJDi0o8Sb6K4dnM4jzvcw53Pr7d3AHtYJaxLE8SRDh69
+                UN8o/8kPldfW6qEVBSq8tFGszh+MiosgQrUr76UXSnPrnXe6yrdF5AjOgTba
+                HhJy9Y12GQU4LvIoEvL2TieEevN/K/YJU81eZENtvJayMpBWsif69zkuSymU
+                UgCBeuw/6lRtMwt2CGujoeuKmnBFldloWBsNd8U2HRc+nh1RFenULqVvy3+r
+                t3qWS54wJVSa2qizQb+j4ivZCdmZbka+DNsy1qn+Nd3LaBD76lSnYvFiYKzu
+                q7ZONKdHxkRWWh2ZBDsQfIPfsulJGGusvEwDhc1XjL0wEVhkdDIzjyXG8s8A
+                SnCzfDnDBaxkP0YY56x8jVwDEw1MNlBBlSmmGpjGzDUowSzmOE/gJphP4HwD
+                iZB0Wu4BAAA=
+                """
+    )
+
+internal val VIEWMODEL_COMPOSE =
+    bytecodeStub(
+        filename = "ViewModel.kt",
+        filepath = "androidx/lifecycle/viewmodel/compose",
+        checksum = 0xa5fbc03d,
+        source =
+            """
+            package androidx.lifecycle.viewmodel.compose
+
+            import androidx.compose.runtime.*
+            import androidx.lifecycle.*
+
+            inline fun <reified VM : ViewModel> viewModel(
+                viewModelStoreOwner: ViewModelStoreOwner = ViewModelStoreOwner(),
+                key: String? = null,
+                factory: ViewModelProvider.Factory? = null,
+                extras: CreationExtras = CreationExtras()
+            ): VM {
+                return factory!!.create()
+            }
+
+            @Composable
+            inline fun <reified VM : ViewModel> viewModel(
+                viewModelStoreOwner: ViewModelStoreOwner = ViewModelStoreOwner(),
+                key: String? = null,
+                noinline initializer: CreationExtras.() -> VM
+            ): VM { return CreationExtras().run { initializer() } }
+        """
+                .trimIndent(),
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuZSScxLKcrPTKnQy8lMS02uTM5J
+                1SvLTC3PzU9JzdFLzs8tyC9OFeIOAwr5goS8S7hkuLiB4nqpFYm5BTmpQry+
+                lUiySgxaDABrUUXUawAAAA==
+                """,
+        """
+                androidx/lifecycle/viewmodel/compose/ViewModelKt.class:
+                H4sIAAAAAAAA/9VWW28bRRT+Zr2215s02bhx67i3pHFzcZP4QgmQGy1pTUzs
+                NNTBUAKUjb1JN7bXlXftNn1AEQ/wxBNPfUXiCQnxgKCAVEXhAQl+Bv8DcWZ9
+                iUOsXECVila758zsmTPfd87Mmfn9r5+fAbiGNYaIauTKJT33KFzQ17XsVrag
+                hau69rBYymmFcLZUfFAytXCGelK8Z9FygzEom2pVDRdUYyN8e21Ty1Kvg8FT
+                bZgxfDOSbOO56Sdtlcra7YeGVp5O7vlKW2Xd2Jg+dORyuVTVc1o5GFez5GSr
+                rfV8WVMtvWTcemSVVXN69FCP0wzfz2RSU4cbzb1QhFYyKYJ9unpwegkSw8V8
+                ySroRnizWgzrhqWVDbUQThgcjalnTTdkBl/2vpbNL5WspUqhsKyW1aJGhgzD
+                I8l/ZrcNpdFMJzpxSkYHuhhcmo1LgsLQ2eqXpmnjjg/24jQf3MswdpLouHGG
+                psvycGgM/SNHpLYTfvTJOIsAQ1dQD64HW9YoSzAcO6sMjry2xeA9GAsG93oN
+                HcPEyXLNMHiMdDP0NGEHc9q6WilYDM9epBWZaJPlo3bd0PHguzFEKZ/RDd2a
+                oyyM8NUzglEZwwgxDByJzY0xGePc9vF/iVjLllqvGFnu3wzH61r0aLbfPuca
+                czi+mfFjJPEqLytzjepyZW9E4xwoVwxLL9I4u62uFTSy6+CZ0dWC/phXn9j+
+                6tMGihvXKJ+6US3laQsPtasPB7s6MYlXOvAyXmU4f1iQ3JiiRUFbXQ2OE9zx
+                lqNrb+8Ho4QyaN3Xzb2+e5XYtYJaXMupXItQcTkq5QxfPMcF9W92VE/DaUqz
+                1JxqqdQnFKsOOu0Z/3j4B1T58lwR6OcjnWvEVshRTP7c2Z6QBUmQBb8g72w3
+                hdLVaDVfqSkDS8rOdkBYYJdlaWdbYX4WEiJCTJIExREQImJMJs0ZEP0s4or1
+                SZLiDpz2il4h4uHfBRaRdr9yCZK8sPu59OtTtrPNm0pHIH4Ct1zvtPVTtkUX
+                WXgWdj+zXSvdu58Kbtkp7T6JRRinSotUyKRoDRwVUPCCX49pa+IvNjpp52h0
+                ppaMxt+VrQd8UwSPU2DdeJvOjnjj7OhsGkzkqb6Hku1O8XSpUs5qN7W1ykZz
+                bprPWVULFdpOX6dTN5blVkfyou1FDqX7G1pcvtof7d9ndNJrIHmI9a+reY0P
+                boczTv/ILJSUo4PRsVhkKkpKbCoWlUO36PYhzpMfhu6kbmhLleKaVl7h5YSH
+                u5RVCxm1rPN2vdOT1jcM1aqUST+VttRsPqU+qP87d6dWlBJGVTd16rphGCXL
+                LmsmxTRh0PabL6imqVFTroUvrvORve1iydBXd5g54A5RCBD5FoKo9MEJF7Xf
+                o9YmSSdJf8jreYrucW8PfR2T4tjdp/D9gHPf8d2Gu/R10YrqppvT+/YF3Em6
+                B+dxwfbqh4SLtnc/LqGfLLk2gMs0lmsKBuHAqu3LrXgQxBWy4fP/QT0ukgmf
+                KH7yBB2/YPjuj7i66BOd1HSypE9020rKJypSzWKcLCaWDgHswAc2YLFbkrpt
+                8CHIdo+bnm6SZwiwn+QlIjFAcpT+R0lebyF4vYVgokkw0SSYaBJM1AjagQ3T
+                7JzYl3ViwzWcY96XOM65Ovop0THpHOsTf8JrAnYwjb04nyEnXeilm58PAZJX
+                6B2il8OaI5cBunvOECwnWXRhliYWSfbZUHlFHG5CHW5CHUaMxgr1DETwYT2f
+                UcDOxuv1bPzG80Ny9sK+ZFyo56I9Fadj0jXW59xHpZYBEUKXuyX+Ck3aQ2AV
+                ouMlogpN3UsXHB9B6qWRvURsj2ikTtRFFjWiTrLfIzrbJDrbJDpbJ+rER9SS
+                qe8s7iBNo+7Z1FfwMcn/R6mBSlBXicJ1onNjFY4E3khgPoGbuJVAHG8msIAE
+                GZh4C4urUEw4TSRNpExIJi6auGRiyUTMxG0TgyaWTYRNDJi4bHJjl70MumiC
+                d+jN2I7e/Rt/dMvZYg8AAA==
+                """
+    )
diff --git a/lifecycle/lifecycle-viewmodel-compose-lint/src/test/java/androidx/lifecycle/viewmodel/compose/lint/ViewModelConstructorInComposableTest.kt b/lifecycle/lifecycle-viewmodel-compose-lint/src/test/java/androidx/lifecycle/viewmodel/compose/lint/ViewModelConstructorInComposableTest.kt
new file mode 100644
index 0000000..5afca98
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose-lint/src/test/java/androidx/lifecycle/viewmodel/compose/lint/ViewModelConstructorInComposableTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.compose.lint.lint
+
+import androidx.compose.lint.test.Stubs
+import androidx.lifecycle.lint.ViewModelConstructorInComposableDetector
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ViewModelConstructorInComposableDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = ViewModelConstructorInComposableDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(ViewModelConstructorInComposableDetector.ISSUE)
+
+    @Test
+    fun constructInComposableShouldFail() {
+        lint()
+            .files(
+                kotlin(
+                    """
+                    package com.example
+
+                    import androidx.compose.runtime.*
+                    import androidx.lifecycle.ViewModel
+
+                    class MyViewModel: ViewModel()
+                    @Composable
+                    fun Test() {
+                        val viewModel = MyViewModel()
+                        val composableLambda = @Composable {
+                            val vm = MyViewModel()
+                        }
+                    }
+                """
+                ),
+                Stubs.Composable,
+                VIEWMODEL
+            )
+            .run()
+            .expect(
+                """
+src/com/example/MyViewModel.kt:10: Error: Constructing a view model in a composable [ViewModelConstructorInComposable]
+                        val viewModel = MyViewModel()
+                                        ~~~~~~~~~~~
+src/com/example/MyViewModel.kt:12: Error: Constructing a view model in a composable [ViewModelConstructorInComposable]
+                            val vm = MyViewModel()
+                                     ~~~~~~~~~~~
+2 errors, 0 warnings
+            """
+                    .trimIndent()
+            )
+    }
+
+    @Test
+    fun useExtensionMethodShouldPass() {
+        lint()
+            .files(
+                kotlin(
+                    """
+                    package com.example
+
+                    import androidx.compose.runtime.*
+                    import androidx.lifecycle.ViewModel
+                    import androidx.lifecycle.viewmodel.compose.viewModel
+
+                    class MyViewModel: ViewModel()
+                    @Composable
+                    fun Test() {
+                        val viewModel = viewModel<MyViewModel>()
+                        val viewModel2 = viewModel { MyViewModel() }
+                    }
+
+                    fun Test2(viewModel: MyViewModel = viewModel<MyViewModel>()) {
+
+                    }
+                """
+                ),
+                Stubs.Composable,
+                VIEWMODEL,
+                VIEWMODEL_COMPOSE
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun constructedOutsideComposableShouldPass() {
+        lint()
+            .files(
+                kotlin(
+                    """
+                    package com.example
+
+                    import androidx.compose.runtime.*
+                    import androidx.lifecycle.ViewModel
+
+                    class MyViewModel: ViewModel()
+                    fun Test() {
+                        val myViewModel = MyViewModel()
+                        val viewModel = ViewModel()
+                    }
+                """
+                ),
+                Stubs.Composable,
+                VIEWMODEL,
+            )
+            .run()
+            .expectClean()
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel-compose/build.gradle b/lifecycle/lifecycle-viewmodel-compose/build.gradle
index b62fe94..13aebe3 100644
--- a/lifecycle/lifecycle-viewmodel-compose/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/build.gradle
@@ -31,8 +31,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
     desktop()
 
@@ -43,7 +41,7 @@
             dependencies {
                 api(projectOrArtifact(":lifecycle:lifecycle-common"))
                 api(projectOrArtifact(":lifecycle:lifecycle-viewmodel"))
-                api("androidx.annotation:annotation:1.8.0")
+                api("androidx.annotation:annotation:1.8.1")
                 api("androidx.compose.runtime:runtime:1.6.0")
                 implementation(libs.kotlinStdlib)
             }
@@ -91,15 +89,21 @@
     }
 }
 
+dependencies {
+    lintPublish(project(":lifecycle:lifecycle-viewmodel-compose-lint"))
+}
+
 androidx {
     name = "Lifecycle ViewModel Compose"
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2021"
     description = "Compose integration with Lifecycle ViewModel"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(projectOrArtifact(":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.lifecycle.viewmodel.compose"
 }
diff --git a/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle b/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
index cd2f40e..aecf4ad 100644
--- a/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/samples/build.gradle
@@ -47,5 +47,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.lifecycle.viewmodel.compose.samples"
 }
\ No newline at end of file
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/ViewModelTest.android.kt b/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/ViewModelTest.android.kt
index e671681..a0c7268 100644
--- a/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/ViewModelTest.android.kt
+++ b/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/ViewModelTest.android.kt
@@ -112,7 +112,7 @@
     public fun viewModelCreatedViaDefaultFactoryWithKeyAndCreationExtras() {
         val owner = FakeViewModelStoreOwner()
         var createdInComposition: Any? = null
-        val extrasKey = object : CreationExtras.Key<String> {}
+        val extrasKey = CreationExtras.Key<String>()
         val extras = MutableCreationExtras().apply { set(extrasKey, "value") }
         rule.setContent {
             CompositionLocalProvider(LocalViewModelStoreOwner provides owner) {
@@ -130,7 +130,7 @@
     public fun viewModelCreatedCreationExtrasInitializer() {
         val owner = FakeViewModelStoreOwner()
         var createdInComposition: Any? = null
-        val extrasKey = object : CreationExtras.Key<String> {}
+        val extrasKey = CreationExtras.Key<String>()
         rule.setContent {
             CompositionLocalProvider(LocalViewModelStoreOwner provides owner) {
                 createdInComposition =
diff --git a/lifecycle/lifecycle-viewmodel-ktx/build.gradle b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
index c488ac6..b1d0a28 100644
--- a/lifecycle/lifecycle-viewmodel-ktx/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
@@ -40,7 +40,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'viewmodel' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
index 5d918ab..6f417dd 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
@@ -37,7 +37,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.0.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core-ktx:1.2.0")
     api("androidx.savedstate:savedstate:1.2.1")
     api(projectOrArtifact(":lifecycle:lifecycle-livedata-core"))
@@ -62,6 +62,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Lifecycle ViewModel"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/lint-baseline.xml b/lifecycle/lifecycle-viewmodel-savedstate/lint-baseline.xml
index d1cab9b..89aacd2 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/lint-baseline.xml
+++ b/lifecycle/lifecycle-viewmodel-savedstate/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) Size::class.java"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/lifecycle/SavedStateHandle.kt"/>
     </issue>
@@ -13,8 +13,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) SizeF::class.java"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/lifecycle/SavedStateHandle.kt"/>
     </issue>
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandleSupport.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandleSupport.kt
index e4e00a9..08a09c6 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandleSupport.kt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateHandleSupport.kt
@@ -217,11 +217,10 @@
  * A key for [SavedStateRegistryOwner] that corresponds to [ViewModelStoreOwner] of a [ViewModel]
  * that is being created.
  */
-@JvmField
-val SAVED_STATE_REGISTRY_OWNER_KEY = object : CreationExtras.Key<SavedStateRegistryOwner> {}
+@JvmField val SAVED_STATE_REGISTRY_OWNER_KEY = CreationExtras.Key<SavedStateRegistryOwner>()
 
 /** A key for [ViewModelStoreOwner] that is an owner of a [ViewModel] that is being created. */
-@JvmField val VIEW_MODEL_STORE_OWNER_KEY = object : CreationExtras.Key<ViewModelStoreOwner> {}
+@JvmField val VIEW_MODEL_STORE_OWNER_KEY = CreationExtras.Key<ViewModelStoreOwner>()
 
 /** A key for default arguments that should be passed to [SavedStateHandle] if needed. */
-@JvmField val DEFAULT_ARGS_KEY = object : CreationExtras.Key<Bundle> {}
+@JvmField val DEFAULT_ARGS_KEY = CreationExtras.Key<Bundle>()
diff --git a/lifecycle/lifecycle-viewmodel-testing/api/current.txt b/lifecycle/lifecycle-viewmodel-testing/api/current.txt
new file mode 100644
index 0000000..567246e
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/api/current.txt
@@ -0,0 +1,21 @@
+// Signature format: 4.0
+package androidx.lifecycle.viewmodel.testing {
+
+  public final class DefaultCreationExtrasKt {
+    method public static androidx.lifecycle.viewmodel.CreationExtras DefaultCreationExtras();
+    method public static androidx.lifecycle.viewmodel.CreationExtras DefaultCreationExtras(android.os.Bundle defaultArgs);
+  }
+
+  public final class ViewModelScenario<VM extends androidx.lifecycle.ViewModel> implements java.lang.AutoCloseable {
+    method public void close();
+    method public VM getViewModel();
+    property public final VM viewModel;
+  }
+
+  public final class ViewModelScenarioKt {
+    method public static inline <reified VM extends androidx.lifecycle.ViewModel> androidx.lifecycle.viewmodel.testing.ViewModelScenario<VM> viewModelScenario(optional androidx.lifecycle.viewmodel.CreationExtras creationExtras, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+    method public static inline <reified VM extends androidx.lifecycle.ViewModel> androidx.lifecycle.viewmodel.testing.ViewModelScenario<VM> viewModelScenario(androidx.lifecycle.ViewModelProvider.Factory factory, optional androidx.lifecycle.viewmodel.CreationExtras creationExtras);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/lifecycle/lifecycle-viewmodel-testing/api/res-current.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to lifecycle/lifecycle-viewmodel-testing/api/res-current.txt
diff --git a/lifecycle/lifecycle-viewmodel-testing/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel-testing/api/restricted_current.txt
new file mode 100644
index 0000000..f90ed9f
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/api/restricted_current.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.lifecycle.viewmodel.testing {
+
+  public final class DefaultCreationExtrasKt {
+    method public static androidx.lifecycle.viewmodel.CreationExtras DefaultCreationExtras();
+    method public static androidx.lifecycle.viewmodel.CreationExtras DefaultCreationExtras(android.os.Bundle defaultArgs);
+  }
+
+  public final class ViewModelScenario<VM extends androidx.lifecycle.ViewModel> implements java.lang.AutoCloseable {
+    ctor @kotlin.PublishedApi internal ViewModelScenario(kotlin.reflect.KClass<VM> modelClass, androidx.lifecycle.ViewModelStore modelStore, androidx.lifecycle.ViewModelProvider.Factory modelFactory, optional androidx.lifecycle.viewmodel.CreationExtras modelExtras);
+    method public void close();
+    method public VM getViewModel();
+    property public final VM viewModel;
+  }
+
+  public final class ViewModelScenarioKt {
+    method public static inline <reified VM extends androidx.lifecycle.ViewModel> androidx.lifecycle.viewmodel.testing.ViewModelScenario<VM> viewModelScenario(optional androidx.lifecycle.viewmodel.CreationExtras creationExtras, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends VM> initializer);
+    method public static inline <reified VM extends androidx.lifecycle.ViewModel> androidx.lifecycle.viewmodel.testing.ViewModelScenario<VM> viewModelScenario(androidx.lifecycle.ViewModelProvider.Factory factory, optional androidx.lifecycle.viewmodel.CreationExtras creationExtras);
+  }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-testing/bcv/native/current.txt b/lifecycle/lifecycle-viewmodel-testing/bcv/native/current.txt
new file mode 100644
index 0000000..d7cbc40
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/bcv/native/current.txt
@@ -0,0 +1,17 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.lifecycle:lifecycle-viewmodel-testing>
+final class <#A: androidx.lifecycle/ViewModel> androidx.lifecycle.viewmodel.testing/ViewModelScenario : kotlin/AutoCloseable { // androidx.lifecycle.viewmodel.testing/ViewModelScenario|null[0]
+    constructor <init>(kotlin.reflect/KClass<#A>, androidx.lifecycle/ViewModelStore, androidx.lifecycle/ViewModelProvider.Factory, androidx.lifecycle.viewmodel/CreationExtras =...) // androidx.lifecycle.viewmodel.testing/ViewModelScenario.<init>|<init>(kotlin.reflect.KClass<1:0>;androidx.lifecycle.ViewModelStore;androidx.lifecycle.ViewModelProvider.Factory;androidx.lifecycle.viewmodel.CreationExtras){}[0]
+    final fun close() // androidx.lifecycle.viewmodel.testing/ViewModelScenario.close|close(){}[0]
+    final val viewModel // androidx.lifecycle.viewmodel.testing/ViewModelScenario.viewModel|{}viewModel[0]
+        final fun <get-viewModel>(): #A // androidx.lifecycle.viewmodel.testing/ViewModelScenario.viewModel.<get-viewModel>|<get-viewModel>(){}[0]
+}
+final fun androidx.lifecycle.viewmodel.testing/DefaultCreationExtras(): androidx.lifecycle.viewmodel/CreationExtras // androidx.lifecycle.viewmodel.testing/DefaultCreationExtras|DefaultCreationExtras(){}[0]
+final inline fun <#A: reified androidx.lifecycle/ViewModel> androidx.lifecycle.viewmodel.testing/viewModelScenario(androidx.lifecycle.viewmodel/CreationExtras =..., noinline kotlin/Function1<androidx.lifecycle.viewmodel/CreationExtras, #A>): androidx.lifecycle.viewmodel.testing/ViewModelScenario<#A> // androidx.lifecycle.viewmodel.testing/viewModelScenario|viewModelScenario(androidx.lifecycle.viewmodel.CreationExtras;kotlin.Function1<androidx.lifecycle.viewmodel.CreationExtras,0:0>){0§<androidx.lifecycle.ViewModel>}[0]
+final inline fun <#A: reified androidx.lifecycle/ViewModel> androidx.lifecycle.viewmodel.testing/viewModelScenario(androidx.lifecycle/ViewModelProvider.Factory, androidx.lifecycle.viewmodel/CreationExtras =...): androidx.lifecycle.viewmodel.testing/ViewModelScenario<#A> // androidx.lifecycle.viewmodel.testing/viewModelScenario|viewModelScenario(androidx.lifecycle.ViewModelProvider.Factory;androidx.lifecycle.viewmodel.CreationExtras){0§<androidx.lifecycle.ViewModel>}[0]
diff --git a/lifecycle/lifecycle-viewmodel-testing/build.gradle b/lifecycle/lifecycle-viewmodel-testing/build.gradle
new file mode 100644
index 0000000..958dcae
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/build.gradle
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+
+import androidx.build.LibraryType
+import androidx.build.PlatformIdentifier
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.konan.target.Family
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+}
+
+androidXMultiplatform {
+    android()
+    desktop()
+    mac()
+    linux()
+    ios()
+
+    defaultPlatform(PlatformIdentifier.ANDROID)
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(project(":lifecycle:lifecycle-viewmodel"))
+                api(libs.kotlinStdlib)
+                api(libs.kotlinCoroutinesCore)
+            }
+        }
+
+        commonTest {
+            dependencies {
+                implementation(project(":kruth:kruth"))
+                implementation(libs.kotlinTest)
+                implementation(libs.kotlinCoroutinesTest)
+            }
+        }
+
+        androidMain {
+            dependsOn(commonMain)
+            dependencies {
+                implementation(project(":lifecycle:lifecycle-runtime"))
+                implementation(project(":lifecycle:lifecycle-runtime-testing"))
+                implementation(project(":lifecycle:lifecycle-viewmodel-savedstate"))
+            }
+        }
+
+        androidInstrumentedTest {
+            dependsOn(commonTest)
+            dependencies {
+                implementation("androidx.core:core-ktx:1.2.0")
+                implementation(libs.testExtJunit)
+                implementation(libs.testCore)
+                implementation(libs.testRunner)
+            }
+        }
+
+        desktopMain.dependsOn(jvmCommonMain)
+        nonJvmCommonMain.dependsOn(commonMain)
+        nativeMain.dependsOn(nonJvmCommonMain)
+        darwinMain.dependsOn(nativeMain)
+        linuxMain.dependsOn(nativeMain)
+
+        targets.configureEach { target ->
+            if (target.platformType == KotlinPlatformType.native) {
+                target.compilations["main"].defaultSourceSet {
+                    def konanTargetFamily = target.konanTarget.family
+                    if (konanTargetFamily == Family.OSX || konanTargetFamily == Family.IOS) {
+                        dependsOn(darwinMain)
+                    } else if (konanTargetFamily == Family.LINUX) {
+                        dependsOn(linuxMain)
+                    } else {
+                        throw new GradleException("unknown native target ${target}")
+                    }
+                }
+            }
+        }
+    }
+}
+
+androidx {
+    name = "Lifecycle ViewModel Testing"
+    type = LibraryType.PUBLISHED_TEST_LIBRARY
+    inceptionYear = "2024"
+    description = "Testing utilities for 'lifecycle-viewmodel' artifact"
+    // TODO(b/337268135): Temporarily disabled until issue fixed.
+    metalavaK2UastEnabled = false
+}
+
+android {
+    namespace "androidx.lifecycle.viewmodel.testing"
+}
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtrasInstrumentedTest.kt b/lifecycle/lifecycle-viewmodel-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtrasInstrumentedTest.kt
new file mode 100644
index 0000000..a3065bf
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtrasInstrumentedTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.testing
+
+import android.os.Bundle
+import androidx.kruth.assertThat
+import androidx.lifecycle.DEFAULT_ARGS_KEY
+import androidx.lifecycle.SAVED_STATE_REGISTRY_OWNER_KEY
+import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
+import androidx.lifecycle.ViewModelProvider.Companion.VIEW_MODEL_KEY
+import androidx.lifecycle.createSavedStateHandle
+import androidx.lifecycle.viewmodel.MutableCreationExtras
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+internal class DefaultCreationExtrasInstrumentedTest {
+
+    @Test
+    fun creationExtras_hasAllExtras() {
+        val creationExtras = DefaultCreationExtras()
+
+        assertThat(creationExtras[SAVED_STATE_REGISTRY_OWNER_KEY]).isNotNull()
+        assertThat(creationExtras[VIEW_MODEL_STORE_OWNER_KEY]).isNotNull()
+        assertThat(creationExtras[DEFAULT_ARGS_KEY]).isNotNull()
+        assertThat(creationExtras[DEFAULT_ARGS_KEY]!!.isEmpty()).isTrue()
+    }
+
+    @Test
+    fun creationExtras_withCustomDefaultArgs() {
+        val defaultArgs = Bundle().apply { putString("key", "value") }
+        val creationExtras = DefaultCreationExtras(defaultArgs)
+
+        assertThat(creationExtras[DEFAULT_ARGS_KEY]).isEqualTo(defaultArgs)
+    }
+
+    @Test
+    fun creationExtras_savedStateHandle_isEnabled() {
+        val creationExtras = DefaultCreationExtras() as MutableCreationExtras
+        creationExtras[VIEW_MODEL_KEY] = "modelKey"
+
+        assertThat(creationExtras.createSavedStateHandle()).isNotNull()
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenarioInstrumentedTest.kt b/lifecycle/lifecycle-viewmodel-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenarioInstrumentedTest.kt
new file mode 100644
index 0000000..47d4f5f
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenarioInstrumentedTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.testing
+
+import android.os.Bundle
+import androidx.kruth.assertThat
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.createSavedStateHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+internal class ViewModelScenarioInstrumentedTest {
+
+    @Test
+    fun createSavedStateHandle_withDefaultExtras() {
+        val scenario = viewModelScenario { TestViewModel(createSavedStateHandle()) }
+
+        // assert `.viewModel` does not throw.
+        assertThat(scenario.viewModel.handle).isNotNull()
+    }
+
+    @Test
+    fun createSavedStateHandle_withInitialExtras() {
+        val defaultArgs = Bundle().apply { putString("key", "value") }
+        val creationExtras = DefaultCreationExtras(defaultArgs)
+        val scenario = viewModelScenario(creationExtras) { TestViewModel(createSavedStateHandle()) }
+
+        assertThat(scenario.viewModel.handle.get<String>("key")).isEqualTo("value")
+    }
+
+    private class TestViewModel(val handle: SavedStateHandle) : ViewModel()
+}
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/androidMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.android.kt b/lifecycle/lifecycle-viewmodel-testing/src/androidMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.android.kt
new file mode 100644
index 0000000..6e5a48c
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/androidMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.android.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DefaultCreationExtrasKt")
+
+package androidx.lifecycle.viewmodel.testing
+
+import android.os.Bundle
+import androidx.lifecycle.DEFAULT_ARGS_KEY
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.SAVED_STATE_REGISTRY_OWNER_KEY
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
+import androidx.lifecycle.ViewModelStore
+import androidx.lifecycle.ViewModelStoreOwner
+import androidx.lifecycle.enableSavedStateHandles
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.MutableCreationExtras
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+
+/**
+ * Creates a default instance of [CreationExtras] pre-configured with all keys required to use
+ * [SavedStateHandle].
+ *
+ * This function sets up the instance with:
+ * - A fake [SavedStateRegistryOwner] assigned to [SAVED_STATE_REGISTRY_OWNER_KEY], delegating the
+ *   [LifecycleOwner] to a [TestLifecycleOwner].
+ * - A fake [ViewModelStoreOwner] assigned to [VIEW_MODEL_STORE_OWNER_KEY], containing an empty
+ *   [ViewModelStore].
+ */
+@Suppress("FunctionName")
+public actual fun DefaultCreationExtras(): CreationExtras {
+    return DefaultCreationExtras(defaultArgs = Bundle())
+}
+
+/**
+ * Creates a default instance of [CreationExtras] pre-configured with all keys required to use
+ * [SavedStateHandle], with the specified [defaultArgs] as the [DEFAULT_ARGS_KEY].
+ *
+ * This function sets up the instance with:
+ * - A fake [SavedStateRegistryOwner] assigned to [SAVED_STATE_REGISTRY_OWNER_KEY], delegating the
+ *   [LifecycleOwner] to a [TestLifecycleOwner].
+ * - A fake [ViewModelStoreOwner] assigned to [VIEW_MODEL_STORE_OWNER_KEY], containing an empty
+ *   [ViewModelStore].
+ */
+@Suppress("FunctionName")
+public fun DefaultCreationExtras(defaultArgs: Bundle): CreationExtras {
+    val owner =
+        object : ViewModelStoreOwner, LifecycleOwner, SavedStateRegistryOwner {
+            override val viewModelStore = ViewModelStore()
+
+            val lifecycleRegistry = LifecycleRegistry.createUnsafe(owner = this)
+            override val lifecycle: Lifecycle = lifecycleRegistry
+
+            val savedStateRegistryController = SavedStateRegistryController.create(owner = this)
+            override val savedStateRegistry = savedStateRegistryController.savedStateRegistry
+        }
+
+    owner.savedStateRegistryController.performAttach()
+    owner.savedStateRegistryController.performRestore(savedState = null)
+    owner.enableSavedStateHandles()
+
+    owner.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+
+    return MutableCreationExtras().apply {
+        this[SAVED_STATE_REGISTRY_OWNER_KEY] = owner
+        this[VIEW_MODEL_STORE_OWNER_KEY] = owner
+        this[DEFAULT_ARGS_KEY] = defaultArgs
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/commonMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.kt b/lifecycle/lifecycle-viewmodel-testing/src/commonMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.kt
new file mode 100644
index 0000000..5ca4b56
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/commonMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.testing
+
+import androidx.lifecycle.viewmodel.CreationExtras
+
+/** Creates a default instance of [CreationExtras]. */
+@Suppress("FunctionName") public expect fun DefaultCreationExtras(): CreationExtras
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/commonMain/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenario.kt b/lifecycle/lifecycle-viewmodel-testing/src/commonMain/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenario.kt
new file mode 100644
index 0000000..c60f7c8
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/commonMain/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenario.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.testing
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.Factory
+import androidx.lifecycle.ViewModelStore
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.CreationExtras.Empty
+import androidx.lifecycle.viewmodel.viewModelFactory
+import kotlin.reflect.KClass
+
+/**
+ * [ViewModelScenario] provides API to start and drive a [ViewModel]'s lifecycle state for testing.
+ */
+@OptIn(ExperimentalStdlibApi::class)
+public class ViewModelScenario<VM : ViewModel>
+@PublishedApi
+internal constructor(
+    private val modelClass: KClass<VM>,
+    private val modelStore: ViewModelStore,
+    modelFactory: Factory,
+    modelExtras: CreationExtras = Empty,
+) : AutoCloseable {
+
+    private val modelProvider = ViewModelProvider.create(modelStore, modelFactory, modelExtras)
+
+    /** The current [ViewModel]. The instance might update if the [ViewModelStore] is cleared. */
+    public val viewModel: VM
+        get() = modelProvider[modelClass]
+
+    /** Finishes the managed [ViewModel] and clear the [ViewModelStore]. */
+    override fun close() {
+        modelStore.clear()
+    }
+}
+
+/**
+ * Creates a [ViewModelScenario] using a given [VM] class as key, an [initializer] as a
+ * [ViewModelProvider.Factory] and a [creationExtras] as default extras.
+ *
+ * You should access the [ViewModel] instance using [ViewModelScenario.viewModel], and clear the
+ * [ViewModelStore] using [ViewModelScenario.close].
+ *
+ * Example usage:
+ * ```
+ * viewModelScenario { MyViewModel(parameters) }
+ *   .use { scenario ->
+ *     val vm = scenario.viewModel
+ *     // Use the ViewModel
+ *   }
+ * ```
+ *
+ * @param VM The reified [ViewModel] class to be created.
+ * @param creationExtras Additional data passed to the [Factory] during a [ViewModel] creation.
+ * @param initializer A [Factory] function to create a [ViewModel].
+ */
+public inline fun <reified VM : ViewModel> viewModelScenario(
+    creationExtras: CreationExtras = DefaultCreationExtras(),
+    noinline initializer: CreationExtras.() -> VM,
+): ViewModelScenario<VM> {
+    return viewModelScenario(
+        creationExtras = creationExtras,
+        factory = viewModelFactory { addInitializer(VM::class, initializer) },
+    )
+}
+
+/**
+ * Creates a [ViewModelScenario] using a given [VM] class as key, an [factory] and a
+ * [creationExtras] as default extras.
+ *
+ * You should access the [ViewModel] instance using [ViewModelScenario.viewModel], and clear the
+ * [ViewModelStore] using [ViewModelScenario.close].
+ *
+ * Example usage:
+ * ```
+ * val myFactory: ViewModelProvider.Factory = MyViewModelFactory()
+ * viewModelScenario<MyViewModel>(myFactory)
+ *   .use { scenario ->
+ *     val vm = scenario.viewModel
+ *     // Use the ViewModel
+ *   }
+ * ```
+ *
+ * @param VM The reified [ViewModel] class to be created.
+ * @param creationExtras Additional data passed to the [Factory] during a [ViewModel] creation.
+ * @param factory A [Factory] to create a [ViewModel].
+ */
+public inline fun <reified VM : ViewModel> viewModelScenario(
+    factory: Factory,
+    creationExtras: CreationExtras = DefaultCreationExtras(),
+): ViewModelScenario<VM> {
+    return ViewModelScenario(
+        modelClass = VM::class,
+        modelStore = ViewModelStore(),
+        modelFactory = factory,
+        modelExtras = creationExtras,
+    )
+}
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/commonTest/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenarioTest.kt b/lifecycle/lifecycle-viewmodel-testing/src/commonTest/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenarioTest.kt
new file mode 100644
index 0000000..df656c0
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/commonTest/kotlin/androidx/lifecycle/viewmodel/testing/ViewModelScenarioTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.testing
+
+import androidx.kruth.assertThat
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.MutableCreationExtras
+import kotlin.test.Test
+
+class ViewModelScenarioTest {
+
+    @Test
+    fun viewModel_createsInstance() {
+        val scenario = viewModelScenario { TestViewModel() }
+
+        val actualModel = scenario.viewModel
+
+        assertThat(actualModel).isNotNull()
+        assertThat(actualModel).isInstanceOf<TestViewModel>()
+    }
+
+    @Test
+    fun viewModel_whenNotCleared_returnsSameInstance() {
+        val scenario = viewModelScenario { TestViewModel() }
+
+        val actualModel1 = scenario.viewModel
+        val actualModel2 = scenario.viewModel
+
+        assertThat(actualModel1).isEqualTo(actualModel2)
+    }
+
+    @Test
+    fun viewModel_whenCleared_returnsNewInstance() {
+        val scenario = viewModelScenario { TestViewModel() }
+
+        val actualModel1 = scenario.viewModel
+        scenario.close()
+        val actualModel2 = scenario.viewModel
+
+        assertThat(actualModel1).isNotEqualTo(actualModel2)
+    }
+
+    @Test
+    fun viewModel_whenNotCleared_usesCustomCreationExtras() {
+        val expectedExtras = MutableCreationExtras().apply { this[CREATION_EXTRAS_KEY] = "value" }
+        val scenario = viewModelScenario(expectedExtras) { TestViewModel(creationExtras = this) }
+
+        val actualExtras = scenario.viewModel.creationExtras
+
+        assertThat(actualExtras[CREATION_EXTRAS_KEY]).isEqualTo(expectedExtras[CREATION_EXTRAS_KEY])
+    }
+
+    @Test
+    fun viewModel_whenCleared_reusesCustomCreationExtras() {
+        val expectedExtras = MutableCreationExtras().apply { this[CREATION_EXTRAS_KEY] = "value" }
+        val scenario = viewModelScenario(expectedExtras) { TestViewModel(creationExtras = this) }
+
+        val actualExtras1 = scenario.viewModel.creationExtras
+        scenario.close()
+        val actualExtras2 = scenario.viewModel.creationExtras
+
+        assertThat(actualExtras1[CREATION_EXTRAS_KEY]).isEqualTo(actualExtras2[CREATION_EXTRAS_KEY])
+    }
+
+    @Test
+    fun viewModel_whenCleared_clearsViewModel() {
+        val scenario = viewModelScenario { TestViewModel() }
+
+        val actualModel1 = scenario.viewModel
+        scenario.close()
+        val actualModel2 = scenario.viewModel
+
+        assertThat(actualModel1.onClearedCount).isEqualTo(expected = 1)
+        assertThat(actualModel2.onClearedCount).isEqualTo(expected = 0)
+    }
+}
+
+private val CREATION_EXTRAS_KEY = CreationExtras.Key<String>()
+
+private class TestViewModel(
+    val creationExtras: CreationExtras = CreationExtras.Empty,
+) : ViewModel() {
+
+    var onClearedCount = 0
+        private set
+
+    override fun onCleared() {
+        onClearedCount++
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/desktopMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.desktop.kt b/lifecycle/lifecycle-viewmodel-testing/src/desktopMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.desktop.kt
new file mode 100644
index 0000000..43f80b4
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/desktopMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.desktop.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DefaultCreationExtrasKt")
+
+package androidx.lifecycle.viewmodel.testing
+
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.CreationExtras.Empty
+
+@Suppress("FunctionName") public actual fun DefaultCreationExtras(): CreationExtras = Empty
diff --git a/lifecycle/lifecycle-viewmodel-testing/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.nonJvm.kt b/lifecycle/lifecycle-viewmodel-testing/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.nonJvm.kt
new file mode 100644
index 0000000..e220c67
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-testing/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/testing/DefaultCreationExtras.nonJvm.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle.viewmodel.testing
+
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.CreationExtras.Empty
+
+@Suppress("FunctionName") public actual fun DefaultCreationExtras(): CreationExtras = Empty
diff --git a/lifecycle/lifecycle-viewmodel/api/current.txt b/lifecycle/lifecycle-viewmodel/api/current.txt
index f4d093f..7181574 100644
--- a/lifecycle/lifecycle-viewmodel/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/current.txt
@@ -124,6 +124,11 @@
 
   public abstract class CreationExtras {
     method public abstract operator <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+    field public static final androidx.lifecycle.viewmodel.CreationExtras.Companion Companion;
+  }
+
+  public static final class CreationExtras.Companion {
+    method public inline <reified T> androidx.lifecycle.viewmodel.CreationExtras.Key<T> Key();
   }
 
   public static final class CreationExtras.Empty extends androidx.lifecycle.viewmodel.CreationExtras {
@@ -134,6 +139,12 @@
   public static interface CreationExtras.Key<T> {
   }
 
+  public final class CreationExtrasKt {
+    method public static operator boolean contains(androidx.lifecycle.viewmodel.CreationExtras, androidx.lifecycle.viewmodel.CreationExtras.Key<? extends java.lang.Object?> key);
+    method public static operator androidx.lifecycle.viewmodel.MutableCreationExtras plus(androidx.lifecycle.viewmodel.CreationExtras, androidx.lifecycle.viewmodel.CreationExtras creationExtras);
+    method public static operator void plusAssign(androidx.lifecycle.viewmodel.MutableCreationExtras, androidx.lifecycle.viewmodel.CreationExtras creationExtras);
+  }
+
   @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
     ctor public InitializerViewModelFactoryBuilder();
     method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
@@ -146,6 +157,7 @@
   }
 
   public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
+    ctor public MutableCreationExtras();
     ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
     method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
     method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T t);
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
index f4d093f..7181574 100644
--- a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
@@ -124,6 +124,11 @@
 
   public abstract class CreationExtras {
     method public abstract operator <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
+    field public static final androidx.lifecycle.viewmodel.CreationExtras.Companion Companion;
+  }
+
+  public static final class CreationExtras.Companion {
+    method public inline <reified T> androidx.lifecycle.viewmodel.CreationExtras.Key<T> Key();
   }
 
   public static final class CreationExtras.Empty extends androidx.lifecycle.viewmodel.CreationExtras {
@@ -134,6 +139,12 @@
   public static interface CreationExtras.Key<T> {
   }
 
+  public final class CreationExtrasKt {
+    method public static operator boolean contains(androidx.lifecycle.viewmodel.CreationExtras, androidx.lifecycle.viewmodel.CreationExtras.Key<? extends java.lang.Object?> key);
+    method public static operator androidx.lifecycle.viewmodel.MutableCreationExtras plus(androidx.lifecycle.viewmodel.CreationExtras, androidx.lifecycle.viewmodel.CreationExtras creationExtras);
+    method public static operator void plusAssign(androidx.lifecycle.viewmodel.MutableCreationExtras, androidx.lifecycle.viewmodel.CreationExtras creationExtras);
+  }
+
   @androidx.lifecycle.viewmodel.ViewModelFactoryDsl public final class InitializerViewModelFactoryBuilder {
     ctor public InitializerViewModelFactoryBuilder();
     method public <T extends androidx.lifecycle.ViewModel> void addInitializer(kotlin.reflect.KClass<T> clazz, kotlin.jvm.functions.Function1<? super androidx.lifecycle.viewmodel.CreationExtras,? extends T> initializer);
@@ -146,6 +157,7 @@
   }
 
   public final class MutableCreationExtras extends androidx.lifecycle.viewmodel.CreationExtras {
+    ctor public MutableCreationExtras();
     ctor public MutableCreationExtras(optional androidx.lifecycle.viewmodel.CreationExtras initialExtras);
     method public <T> T? get(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key);
     method public operator <T> void set(androidx.lifecycle.viewmodel.CreationExtras.Key<T> key, T t);
diff --git a/lifecycle/lifecycle-viewmodel/bcv/native/current.txt b/lifecycle/lifecycle-viewmodel/bcv/native/current.txt
index e126e46..c22e7d4 100644
--- a/lifecycle/lifecycle-viewmodel/bcv/native/current.txt
+++ b/lifecycle/lifecycle-viewmodel/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
@@ -9,9 +9,15 @@
 abstract class androidx.lifecycle.viewmodel/CreationExtras { // androidx.lifecycle.viewmodel/CreationExtras|null[0]
     abstract fun <#A1: kotlin/Any?> get(androidx.lifecycle.viewmodel/CreationExtras.Key<#A1>): #A1? // androidx.lifecycle.viewmodel/CreationExtras.get|get(androidx.lifecycle.viewmodel.CreationExtras.Key<0:0>){0§<kotlin.Any?>}[0]
     abstract interface <#A1: kotlin/Any?> Key // androidx.lifecycle.viewmodel/CreationExtras.Key|null[0]
+    final object Companion { // androidx.lifecycle.viewmodel/CreationExtras.Companion|null[0]
+        final inline fun <#A2: reified kotlin/Any?> Key(): androidx.lifecycle.viewmodel/CreationExtras.Key<#A2> // androidx.lifecycle.viewmodel/CreationExtras.Companion.Key|Key(){0§<kotlin.Any?>}[0]
+    }
     final object Empty : androidx.lifecycle.viewmodel/CreationExtras { // androidx.lifecycle.viewmodel/CreationExtras.Empty|null[0]
         final fun <#A2: kotlin/Any?> get(androidx.lifecycle.viewmodel/CreationExtras.Key<#A2>): #A2? // androidx.lifecycle.viewmodel/CreationExtras.Empty.get|get(androidx.lifecycle.viewmodel.CreationExtras.Key<0:0>){0§<kotlin.Any?>}[0]
     }
+    open fun equals(kotlin/Any?): kotlin/Boolean // androidx.lifecycle.viewmodel/CreationExtras.equals|equals(kotlin.Any?){}[0]
+    open fun hashCode(): kotlin/Int // androidx.lifecycle.viewmodel/CreationExtras.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // androidx.lifecycle.viewmodel/CreationExtras.toString|toString(){}[0]
 }
 abstract class androidx.lifecycle/ViewModel { // androidx.lifecycle/ViewModel|null[0]
     constructor <init>() // androidx.lifecycle/ViewModel.<init>|<init>(){}[0]
@@ -69,6 +75,9 @@
         open fun onRequery(androidx.lifecycle/ViewModel) // androidx.lifecycle/ViewModelProvider.OnRequeryFactory.onRequery|onRequery(androidx.lifecycle.ViewModel){}[0]
     }
 }
+final fun (androidx.lifecycle.viewmodel/CreationExtras).androidx.lifecycle.viewmodel/contains(androidx.lifecycle.viewmodel/CreationExtras.Key<*>): kotlin/Boolean // androidx.lifecycle.viewmodel/contains|[email protected](androidx.lifecycle.viewmodel.CreationExtras.Key<*>){}[0]
+final fun (androidx.lifecycle.viewmodel/CreationExtras).androidx.lifecycle.viewmodel/plus(androidx.lifecycle.viewmodel/CreationExtras): androidx.lifecycle.viewmodel/MutableCreationExtras // androidx.lifecycle.viewmodel/plus|[email protected](androidx.lifecycle.viewmodel.CreationExtras){}[0]
+final fun (androidx.lifecycle.viewmodel/MutableCreationExtras).androidx.lifecycle.viewmodel/plusAssign(androidx.lifecycle.viewmodel/CreationExtras) // androidx.lifecycle.viewmodel/plusAssign|[email protected](androidx.lifecycle.viewmodel.CreationExtras){}[0]
 final inline fun <#A: reified androidx.lifecycle/ViewModel> (androidx.lifecycle.viewmodel/InitializerViewModelFactoryBuilder).androidx.lifecycle.viewmodel/initializer(noinline kotlin/Function1<androidx.lifecycle.viewmodel/CreationExtras, #A>) // androidx.lifecycle.viewmodel/initializer|initializer@androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder(kotlin.Function1<androidx.lifecycle.viewmodel.CreationExtras,0:0>){0§<androidx.lifecycle.ViewModel>}[0]
 final inline fun <#A: reified androidx.lifecycle/ViewModel> (androidx.lifecycle/ViewModelProvider).androidx.lifecycle/get(): #A // androidx.lifecycle/get|[email protected](){0§<androidx.lifecycle.ViewModel>}[0]
 final inline fun androidx.lifecycle.viewmodel/viewModelFactory(kotlin/Function1<androidx.lifecycle.viewmodel/InitializerViewModelFactoryBuilder, kotlin/Unit>): androidx.lifecycle/ViewModelProvider.Factory // androidx.lifecycle.viewmodel/viewModelFactory|viewModelFactory(kotlin.Function1<androidx.lifecycle.viewmodel.InitializerViewModelFactoryBuilder,kotlin.Unit>){}[0]
diff --git a/lifecycle/lifecycle-viewmodel/build.gradle b/lifecycle/lifecycle-viewmodel/build.gradle
index c787e6a..1b2efff 100644
--- a/lifecycle/lifecycle-viewmodel/build.gradle
+++ b/lifecycle/lifecycle-viewmodel/build.gradle
@@ -34,8 +34,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
     desktop()
     mac()
@@ -55,7 +53,7 @@
 
         commonMain {
             dependencies {
-                api("androidx.annotation:annotation:1.8.0")
+                api(project(":annotation:annotation"))
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
             }
@@ -69,11 +67,11 @@
             }
         }
 
-        jvmMain {
+        jvmCommonMain {
             dependsOn(commonMain)
         }
 
-        jvmTest {
+        jvmCommonTest {
             dependsOn(commonTest)
             dependencies {
                 implementation(libs.junit)
@@ -81,14 +79,14 @@
         }
 
         androidMain {
-            dependsOn(jvmMain)
+            dependsOn(jvmCommonMain)
             dependencies {
                 api(libs.kotlinCoroutinesAndroid)
             }
         }
 
         androidUnitTest {
-            dependsOn(jvmTest)
+            dependsOn(jvmCommonTest)
             dependencies {
                 implementation(libs.mockitoCore4)
             }
@@ -104,9 +102,9 @@
             }
         }
 
-        desktopMain.dependsOn(jvmMain)
-        nonJvmMain.dependsOn(commonMain)
-        nativeMain.dependsOn(nonJvmMain)
+        desktopMain.dependsOn(jvmCommonMain)
+        nonJvmCommonMain.dependsOn(commonMain)
+        nativeMain.dependsOn(nonJvmCommonMain)
         darwinMain.dependsOn(nativeMain)
         linuxMain.dependsOn(nativeMain)
 
@@ -139,4 +137,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Lifecycle ViewModel"
+    metalavaK2UastEnabled = false
 }
diff --git a/lifecycle/lifecycle-viewmodel/src/androidInstrumentedTest/kotlin/androidx/lifecycle/CreationExtrasTest.kt b/lifecycle/lifecycle-viewmodel/src/androidInstrumentedTest/kotlin/androidx/lifecycle/CreationExtrasTest.kt
index ef58257..f64b119 100644
--- a/lifecycle/lifecycle-viewmodel/src/androidInstrumentedTest/kotlin/androidx/lifecycle/CreationExtrasTest.kt
+++ b/lifecycle/lifecycle-viewmodel/src/androidInstrumentedTest/kotlin/androidx/lifecycle/CreationExtrasTest.kt
@@ -16,26 +16,115 @@
 
 package androidx.lifecycle
 
-import android.os.Bundle
-import androidx.core.os.bundleOf
 import androidx.kruth.assertThat
 import androidx.lifecycle.viewmodel.CreationExtras
 import androidx.lifecycle.viewmodel.MutableCreationExtras
+import androidx.lifecycle.viewmodel.contains
+import androidx.lifecycle.viewmodel.plus
+import androidx.lifecycle.viewmodel.plusAssign
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
+private val STRING_KEY_1 = CreationExtras.Key<String>()
+private val STRING_KEY_2 = CreationExtras.Key<String>()
+
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class CreationExtrasTest {
+
     @Test
-    fun testInitialCreationExtras() {
-        val initial = MutableCreationExtras()
-        val key = object : CreationExtras.Key<Bundle> {}
-        initial[key] = bundleOf("value" to "initial")
-        val mutable = MutableCreationExtras(initial)
-        initial[key] = bundleOf("value" to "overridden")
-        assertThat(mutable[key]?.getString("value")).isEqualTo("initial")
+    fun keyFactory_returnsDistinctInstances() {
+        val key1 = CreationExtras.Key<String>()
+        val key2 = CreationExtras.Key<String>()
+
+        assertThat(key1).isNotEqualTo(key2)
+    }
+
+    @Test
+    fun initialExtras_originalModifiedAfterCopy_copyRemainsUnchanged() {
+        val otherExtras = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+        val underTest = MutableCreationExtras(initialExtras = otherExtras)
+        otherExtras[STRING_KEY_1] = "value2"
+
+        assertThat(otherExtras[STRING_KEY_1]).isEqualTo("value2")
+        assertThat(underTest[STRING_KEY_1]).isEqualTo("value1")
+    }
+
+    @Test
+    fun equals_sameValues_isEqual() {
+        val underTest = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+        val otherExtras = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+
+        assertThat(underTest).isEqualTo(otherExtras)
+    }
+
+    @Test
+    fun equals_differentValues_isNotEqual() {
+        val underTest = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+        val otherExtras = MutableCreationExtras().apply { this[STRING_KEY_1] = "value2" }
+
+        assertThat(underTest).isNotEqualTo(otherExtras)
+    }
+
+    @Test
+    fun contains_returnsTrueForExistingKey() {
+        val underTest = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+
+        val result = STRING_KEY_1 in underTest
+
+        assertThat(result).isTrue()
+    }
+
+    @Test
+    fun contains_returnsFalseForNonExistingKey() {
+        val underTest = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+
+        val result = STRING_KEY_2 in underTest
+
+        assertThat(result).isFalse()
+    }
+
+    @Test
+    fun plus_addedTogetherWithUniqueKeys_combinesValues() {
+        val extras1 = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+        val extras2 = MutableCreationExtras().apply { this[STRING_KEY_2] = "value2" }
+
+        val underTest = extras1 + extras2
+
+        assertThat(underTest[STRING_KEY_1]).isEqualTo(extras1[STRING_KEY_1])
+        assertThat(underTest[STRING_KEY_2]).isEqualTo(extras2[STRING_KEY_2])
+    }
+
+    @Test
+    fun plus_addedTogetherWithConflictingKeys_overridesFirstValue() {
+        val extras1 = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+        val extras2 = MutableCreationExtras().apply { this[STRING_KEY_1] = "value2" }
+
+        val underTest = extras1 + extras2
+
+        assertThat(underTest[STRING_KEY_1]).isEqualTo(extras2[STRING_KEY_1])
+    }
+
+    @Test
+    fun plusAssign_addedTogetherWithUniqueKeys_combinesValues() {
+        val underTest = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+        val otherExtras = MutableCreationExtras().apply { this[STRING_KEY_2] = "value2" }
+
+        underTest += otherExtras
+
+        assertThat(underTest[STRING_KEY_1]).isEqualTo(underTest[STRING_KEY_1])
+        assertThat(underTest[STRING_KEY_2]).isEqualTo(otherExtras[STRING_KEY_2])
+    }
+
+    @Test
+    fun plusAssign_addedTogetherWithConflictingKeys_overridesFirstValue() {
+        val underTest = MutableCreationExtras().apply { this[STRING_KEY_1] = "value1" }
+        val otherExtras = MutableCreationExtras().apply { this[STRING_KEY_1] = "value2" }
+
+        underTest += otherExtras
+
+        assertThat(underTest[STRING_KEY_1]).isEqualTo(otherExtras[STRING_KEY_1])
     }
 }
diff --git a/lifecycle/lifecycle-viewmodel/src/androidMain/kotlin/androidx/lifecycle/ViewModelProvider.android.kt b/lifecycle/lifecycle-viewmodel/src/androidMain/kotlin/androidx/lifecycle/ViewModelProvider.android.kt
index a39fcd5..268e1ab 100644
--- a/lifecycle/lifecycle-viewmodel/src/androidMain/kotlin/androidx/lifecycle/ViewModelProvider.android.kt
+++ b/lifecycle/lifecycle-viewmodel/src/androidMain/kotlin/androidx/lifecycle/ViewModelProvider.android.kt
@@ -221,7 +221,7 @@
              *
              * @see ViewModelProvider.VIEW_MODEL_KEY
              */
-            @JvmField public val VIEW_MODEL_KEY: Key<String> = ViewModelProviders.ViewModelKey
+            @JvmField public val VIEW_MODEL_KEY: Key<String> = ViewModelProvider.VIEW_MODEL_KEY
         }
     }
 
@@ -326,7 +326,7 @@
             /**
              * A [CreationExtras.Key] to query an application in which ViewModel is being created.
              */
-            @JvmField public val APPLICATION_KEY: Key<Application> = object : Key<Application> {}
+            @JvmField public val APPLICATION_KEY: Key<Application> = CreationExtras.Companion.Key()
         }
     }
 
@@ -347,6 +347,6 @@
             extras: CreationExtras
         ): ViewModelProvider = ViewModelProvider(store, factory, extras)
 
-        @JvmField public actual val VIEW_MODEL_KEY: Key<String> = ViewModelProviders.ViewModelKey
+        @JvmField public actual val VIEW_MODEL_KEY: Key<String> = CreationExtras.Companion.Key()
     }
 }
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelLazy.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelLazy.kt
index 8a2086f..578de4a 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelLazy.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelLazy.kt
@@ -20,8 +20,8 @@
 import kotlin.reflect.KClass
 
 /**
- * An implementation of [Lazy] used by [androidx.fragment.app.Fragment.viewModels] and
- * [androidx.activity.ComponentActivity.viewmodels].
+ * An implementation of [Lazy] used by [androidx.fragment.app.viewModels] and
+ * [androidx.activity.viewModels].
  *
  * [storeProducer] is a lambda that will be called during initialization, [VM] will be created in
  * the scope of returned [ViewModelStore].
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStore.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStore.kt
index 5ea8b4b..ae57354 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStore.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStore.kt
@@ -35,7 +35,6 @@
 
     private val map = mutableMapOf<String, ViewModel>()
 
-    /**  */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun put(key: String, viewModel: ViewModel) {
         val oldViewModel = map.put(key, viewModel)
@@ -43,13 +42,11 @@
     }
 
     /** Returns the `ViewModel` mapped to the given `key` or null if none exists. */
-    /**  */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public operator fun get(key: String): ViewModel? {
         return map[key]
     }
 
-    /**  */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public fun keys(): Set<String> {
         return HashSet(map.keys)
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStoreOwner.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStoreOwner.kt
index 91f5d45..30c2702 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStoreOwner.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModelStoreOwner.kt
@@ -21,8 +21,6 @@
  * A responsibility of an implementation of this interface is to retain owned ViewModelStore during
  * the configuration changes and call [ViewModelStore.clear], when this scope is going to be
  * destroyed.
- *
- * @see ViewTreeViewModelStoreOwner
  */
 public interface ViewModelStoreOwner {
 
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/CreationExtras.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/CreationExtras.kt
index 3592b35..f7dfc9e 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/CreationExtras.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/CreationExtras.kt
@@ -15,45 +15,120 @@
  */
 package androidx.lifecycle.viewmodel
 
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.Factory
+import androidx.lifecycle.viewmodel.CreationExtras.Key
+import kotlin.jvm.JvmOverloads
+import kotlin.jvm.JvmStatic
+
 /**
- * Simple map-like object that passed in [ViewModelProvider.Factory.create] to provide an additional
- * information to a factory.
+ * A map-like object holding pairs of [CreationExtras.Key] and [Any], enabling efficient value
+ * retrieval for each key. Each key in [CreationExtras] is unique, storing only one value per key.
  *
- * It allows making `Factory` implementations stateless, which makes an injection of factories
- * easier because don't require all information be available at construction time.
+ * [CreationExtras] is used in [ViewModelProvider.Factory.create] to provide extra information to
+ * the [Factory]. This makes [Factory] implementations stateless, simplifying factory injection by
+ * not requiring all information at construction time.
+ *
+ * This abstract class supports read-only access; use [MutableCreationExtras] for read-write access.
  */
 public abstract class CreationExtras internal constructor() {
-    internal val map: MutableMap<Key<*>, Any?> = mutableMapOf()
+    internal val extras: MutableMap<Key<*>, Any?> = mutableMapOf()
 
-    /** Key for the elements of [CreationExtras]. [T] is a type of an element with this key. */
+    /**
+     * Key for the elements of [CreationExtras]. [T] represents the type of element associated with
+     * this key.
+     */
     public interface Key<T>
 
-    /** Returns an element associated with the given [key] */
+    /**
+     * Returns the value to which the specified [key] is associated, or null if this
+     * [CreationExtras] contains no mapping for the key.
+     */
     public abstract operator fun <T> get(key: Key<T>): T?
 
-    /** Empty [CreationExtras] */
+    /** Compares the specified object with this [CreationExtras] for equality. */
+    override fun equals(other: Any?): Boolean = other is CreationExtras && extras == other.extras
+
+    /** Returns the hash code value for this [CreationExtras]. */
+    override fun hashCode(): Int = extras.hashCode()
+
+    /**
+     * Returns a string representation of this [CreationExtras]. The string representation consists
+     * of a list of key-value mappings in the order returned by the [CreationExtras]'s iterator.
+     */
+    override fun toString(): String = "CreationExtras(extras=$extras)"
+
+    /** An empty read-only [CreationExtras]. */
     public object Empty : CreationExtras() {
         override fun <T> get(key: Key<T>): T? = null
     }
+
+    public companion object {
+        /** Returns an unique [Key] to be associated with an extra. */
+        @JvmStatic public inline fun <reified T> Key(): Key<T> = object : Key<T> {}
+    }
 }
 
 /**
- * Mutable implementation of [CreationExtras]
+ * A modifiable [CreationExtras] that holds pairs of [CreationExtras.Key] and [Any], allowing
+ * efficient value retrieval for each key.
  *
- * @param initialExtras extras that will be filled into the resulting MutableCreationExtras
+ * Each key in [CreationExtras] is unique, storing only one value per key.
+ *
+ * @see [CreationExtras]
  */
-public class MutableCreationExtras(initialExtras: CreationExtras = Empty) : CreationExtras() {
+public class MutableCreationExtras
+/**
+ * Constructs a [MutableCreationExtras] containing the elements of the specified `initialExtras`, in
+ * the order they are returned by the [Map]'s iterator.
+ */
+internal constructor(initialExtras: Map<Key<*>, Any?>) : CreationExtras() {
+
+    /**
+     * Constructs a [MutableCreationExtras] containing the elements of the specified
+     * [initialExtras], in the order they are returned by the [CreationExtras]'s iterator.
+     */
+    @JvmOverloads
+    public constructor(initialExtras: CreationExtras = Empty) : this(initialExtras.extras)
 
     init {
-        map.putAll(initialExtras.map)
+        extras += initialExtras
     }
 
-    /** Associates the given [key] with [t] */
+    /** Associates the specified [t] with the specified [key] in this [CreationExtras]. */
     public operator fun <T> set(key: Key<T>, t: T) {
-        map[key] = t
+        extras[key] = t
     }
 
-    public override fun <T> get(key: Key<T>): T? {
-        @Suppress("UNCHECKED_CAST") return map[key] as T?
-    }
+    /**
+     * Returns the value to which the specified [key] is associated, or null if this
+     * [CreationExtras] contains no mapping for the key.
+     */
+    @Suppress("UNCHECKED_CAST") public override fun <T> get(key: Key<T>): T? = extras[key] as T?
+}
+
+/**
+ * Checks if the [CreationExtras] contains the given [key].
+ *
+ * This method allows to use the `key in creationExtras` syntax for checking whether an [key] is
+ * contained in the [CreationExtras].
+ */
+public operator fun CreationExtras.contains(key: Key<*>): Boolean = key in extras
+
+/**
+ * Creates a new read-only [CreationExtras] by replacing or adding entries to [this] extras from
+ * another [creationExtras].
+ *
+ * The returned [CreationExtras] preserves the entry iteration order of the original
+ * [CreationExtras].
+ *
+ * Those entries of another [creationExtras] that are missing in [this] extras are iterated in the
+ * end in the order of that [creationExtras].
+ */
+public operator fun CreationExtras.plus(creationExtras: CreationExtras): MutableCreationExtras =
+    MutableCreationExtras(initialExtras = extras + creationExtras.extras)
+
+/** Appends or replaces all entries from the given [creationExtras] in [this] mutable extras. */
+public operator fun MutableCreationExtras.plusAssign(creationExtras: CreationExtras) {
+    extras += creationExtras.extras
 }
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.kt
index 1ffff28..6966d2a 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.kt
@@ -55,7 +55,7 @@
             }
 
             val modelExtras = MutableCreationExtras(defaultExtras)
-            modelExtras[ViewModelProviders.ViewModelKey] = key
+            modelExtras[ViewModelProvider.VIEW_MODEL_KEY] = key
 
             return@synchronized createViewModel(factory, modelClass, modelExtras).also { vm ->
                 store.put(key, vm)
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.kt
index 6968da0..b3b7f20 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.kt
@@ -46,8 +46,6 @@
         return "$VIEW_MODEL_PROVIDER_DEFAULT_KEY:$canonicalName"
     }
 
-    internal object ViewModelKey : CreationExtras.Key<String>
-
     internal fun <VM : ViewModel> unsupportedCreateViewModel(): VM =
         throw UnsupportedOperationException(
             "`Factory.create(String, CreationExtras)` is not implemented. You may need to " +
diff --git a/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelProviderTest.kt b/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelProviderTest.kt
index 12229b5..9e2cd30 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelProviderTest.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelProviderTest.kt
@@ -19,7 +19,6 @@
 import androidx.kruth.assertThat
 import androidx.lifecycle.viewmodel.CreationExtras
 import androidx.lifecycle.viewmodel.MutableCreationExtras
-import androidx.lifecycle.viewmodel.internal.ViewModelProviders
 import kotlin.reflect.KClass
 import kotlin.test.Test
 import kotlin.test.fail
@@ -92,7 +91,7 @@
                     modelClass: KClass<T>,
                     extras: CreationExtras
                 ): T {
-                    val key = extras[ViewModelProviders.ViewModelKey]
+                    val key = extras[ViewModelProvider.VIEW_MODEL_KEY]
                     assertThat(key).isEqualTo("customKey")
                     @Suppress("UNCHECKED_CAST") return TestViewModel1() as T
                 }
@@ -105,7 +104,7 @@
                     modelClass: KClass<T>,
                     extras: CreationExtras
                 ): T {
-                    val key = extras[ViewModelProviders.ViewModelKey]
+                    val key = extras[ViewModelProvider.VIEW_MODEL_KEY]
                     assertThat(key).isNotNull()
                     @Suppress("UNCHECKED_CAST") return TestViewModel1() as T
                 }
@@ -123,11 +122,11 @@
                     modelClass: KClass<T>,
                     extras: CreationExtras
                 ): T {
-                    val mutableKey = object : CreationExtras.Key<String> {}
+                    val mutableKey = CreationExtras.Key<String>()
                     val mutableValue = "value"
                     val mutableExtras = MutableCreationExtras(extras)
                     mutableExtras[mutableKey] = mutableValue
-                    val key = mutableExtras[ViewModelProviders.ViewModelKey]
+                    val key = mutableExtras[ViewModelProvider.VIEW_MODEL_KEY]
                     assertThat(key).isEqualTo("customKey")
                     assertThat(mutableExtras[TEST_KEY]).isEqualTo(TEST_VALUE)
                     assertThat(mutableExtras[mutableKey]).isEqualTo(mutableValue)
@@ -156,7 +155,7 @@
                     modelClass: KClass<T>,
                     extras: CreationExtras
                 ): T {
-                    val key = extras[ViewModelProviders.ViewModelKey]
+                    val key = extras[ViewModelProvider.VIEW_MODEL_KEY]
                     assertThat(key).isEqualTo("customKey")
                     assertThat(extras[TEST_KEY]).isEqualTo(TEST_VALUE)
                     wasCalled[0] = true
@@ -233,5 +232,5 @@
     }
 }
 
-private val TEST_KEY = object : CreationExtras.Key<String> {}
+private val TEST_KEY = CreationExtras.Key<String>()
 private const val TEST_VALUE = "test_value"
diff --git a/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/viewmodel/ViewModelInitializerTest.kt b/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/viewmodel/ViewModelInitializerTest.kt
index 72e535b..63cdb55 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/viewmodel/ViewModelInitializerTest.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/viewmodel/ViewModelInitializerTest.kt
@@ -25,11 +25,11 @@
 
     @Test
     fun viewModelFactory_withUniqueInitializers_withCreationExtras_returnsViewModels() {
-        val key1 = object : CreationExtras.Key<String> {}
+        val key1 = CreationExtras.Key<String>()
         val value1 = "test_value1"
         val extras1 = MutableCreationExtras().apply { set(key1, value1) }
 
-        val key2 = object : CreationExtras.Key<String> {}
+        val key2 = CreationExtras.Key<String>()
         val value2 = "test_value2"
         val extras2 = MutableCreationExtras().apply { set(key2, value2) }
 
diff --git a/lifecycle/lifecycle-viewmodel/src/desktopMain/kotlin/androidx/lifecycle/ViewModelProvider.desktop.kt b/lifecycle/lifecycle-viewmodel/src/desktopMain/kotlin/androidx/lifecycle/ViewModelProvider.desktop.kt
index f2833cc..d63199f 100644
--- a/lifecycle/lifecycle-viewmodel/src/desktopMain/kotlin/androidx/lifecycle/ViewModelProvider.desktop.kt
+++ b/lifecycle/lifecycle-viewmodel/src/desktopMain/kotlin/androidx/lifecycle/ViewModelProvider.desktop.kt
@@ -103,8 +103,6 @@
             extras: CreationExtras
         ): ViewModelProvider = ViewModelProvider(ViewModelProviderImpl(store, factory, extras))
 
-        @JvmField
-        public actual val VIEW_MODEL_KEY: CreationExtras.Key<String> =
-            ViewModelProviders.ViewModelKey
+        @JvmField public actual val VIEW_MODEL_KEY: Key<String> = CreationExtras.Companion.Key()
     }
 }
diff --git a/lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/ViewModel.jvm.kt b/lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/ViewModel.jvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/ViewModel.jvm.kt
rename to lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/ViewModel.jvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.jvm.kt b/lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.jvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.jvm.kt
rename to lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.jvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/JvmViewModelProviders.kt b/lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/JvmViewModelProviders.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/JvmViewModelProviders.kt
rename to lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/JvmViewModelProviders.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/SynchronizedObject.jvm.kt b/lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/SynchronizedObject.jvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/SynchronizedObject.jvm.kt
rename to lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/SynchronizedObject.jvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.jvm.kt b/lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.jvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/jvmMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.jvm.kt
rename to lifecycle/lifecycle-viewmodel/src/jvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviders.jvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/jvmTest/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactoryTest.kt b/lifecycle/lifecycle-viewmodel/src/jvmCommonTest/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactoryTest.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/jvmTest/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactoryTest.kt
rename to lifecycle/lifecycle-viewmodel/src/jvmCommonTest/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactoryTest.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/ViewModel.nonJvm.kt b/lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/ViewModel.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/ViewModel.nonJvm.kt
rename to lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/ViewModel.nonJvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/ViewModelProvider.nonJvm.kt b/lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/ViewModelProvider.nonJvm.kt
new file mode 100644
index 0000000..96b7768
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/ViewModelProvider.nonJvm.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.lifecycle
+
+import androidx.annotation.MainThread
+import androidx.annotation.RestrictTo
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.CreationExtras.Key
+import androidx.lifecycle.viewmodel.ViewModelProviderImpl
+import androidx.lifecycle.viewmodel.internal.ViewModelProviders
+import kotlin.reflect.KClass
+
+public actual class ViewModelProvider
+private constructor(
+    private val impl: ViewModelProviderImpl,
+) {
+
+    @MainThread
+    public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T =
+        impl.getViewModel(modelClass)
+
+    @MainThread
+    public actual operator fun <T : ViewModel> get(
+        key: String,
+        modelClass: KClass<T>,
+    ): T = impl.getViewModel(modelClass, key)
+
+    public actual interface Factory {
+        public actual fun <T : ViewModel> create(
+            modelClass: KClass<T>,
+            extras: CreationExtras,
+        ): T = ViewModelProviders.unsupportedCreateViewModel()
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public actual open class OnRequeryFactory {
+        public actual open fun onRequery(viewModel: ViewModel) {}
+    }
+
+    public actual companion object {
+        public actual fun create(
+            owner: ViewModelStoreOwner,
+            factory: Factory,
+            extras: CreationExtras,
+        ): ViewModelProvider =
+            ViewModelProvider(ViewModelProviderImpl(owner.viewModelStore, factory, extras))
+
+        public actual fun create(
+            store: ViewModelStore,
+            factory: Factory,
+            extras: CreationExtras
+        ): ViewModelProvider = ViewModelProvider(ViewModelProviderImpl(store, factory, extras))
+
+        public actual val VIEW_MODEL_KEY: Key<String> = CreationExtras.Companion.Key()
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/viewmodel/InitializerViewModelFactory.nonJvm.kt b/lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/InitializerViewModelFactory.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/viewmodel/InitializerViewModelFactory.nonJvm.kt
rename to lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/InitializerViewModelFactory.nonJvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.nonJvm.kt b/lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.nonJvm.kt
rename to lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/DefaultViewModelProviderFactory.nonJvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.nonJvm.kt b/lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.nonJvm.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.nonJvm.kt
rename to lifecycle/lifecycle-viewmodel/src/nonJvmCommonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelProviderImpl.nonJvm.kt
diff --git a/lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/ViewModelProvider.nonJvm.kt b/lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/ViewModelProvider.nonJvm.kt
deleted file mode 100644
index 6990ed4..0000000
--- a/lifecycle/lifecycle-viewmodel/src/nonJvmMain/kotlin/androidx/lifecycle/ViewModelProvider.nonJvm.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.lifecycle
-
-import androidx.annotation.MainThread
-import androidx.annotation.RestrictTo
-import androidx.lifecycle.viewmodel.CreationExtras
-import androidx.lifecycle.viewmodel.ViewModelProviderImpl
-import androidx.lifecycle.viewmodel.internal.ViewModelProviders
-import kotlin.reflect.KClass
-
-public actual class ViewModelProvider
-private constructor(
-    private val impl: ViewModelProviderImpl,
-) {
-
-    @MainThread
-    public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T =
-        impl.getViewModel(modelClass)
-
-    @MainThread
-    public actual operator fun <T : ViewModel> get(
-        key: String,
-        modelClass: KClass<T>,
-    ): T = impl.getViewModel(modelClass, key)
-
-    public actual interface Factory {
-        public actual fun <T : ViewModel> create(
-            modelClass: KClass<T>,
-            extras: CreationExtras,
-        ): T = ViewModelProviders.unsupportedCreateViewModel()
-    }
-
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public actual open class OnRequeryFactory {
-        public actual open fun onRequery(viewModel: ViewModel) {}
-    }
-
-    public actual companion object {
-        public actual fun create(
-            owner: ViewModelStoreOwner,
-            factory: Factory,
-            extras: CreationExtras,
-        ): ViewModelProvider =
-            ViewModelProvider(ViewModelProviderImpl(owner.viewModelStore, factory, extras))
-
-        public actual fun create(
-            store: ViewModelStore,
-            factory: Factory,
-            extras: CreationExtras
-        ): ViewModelProvider = ViewModelProvider(ViewModelProviderImpl(store, factory, extras))
-
-        public actual val VIEW_MODEL_KEY: CreationExtras.Key<String> =
-            ViewModelProviders.ViewModelKey
-    }
-}
diff --git a/lint-checks/integration-tests/build.gradle b/lint-checks/integration-tests/build.gradle
index 4966bb6..8060a19 100644
--- a/lint-checks/integration-tests/build.gradle
+++ b/lint-checks/integration-tests/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.8.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.annotation:annotation-experimental:1.4.1")
     implementation(libs.kotlinStdlib)
 }
diff --git a/lint-checks/integration-tests/lint-baseline.xml b/lint-checks/integration-tests/lint-baseline.xml
index f0e1d46..d566222 100644
--- a/lint-checks/integration-tests/lint-baseline.xml
+++ b/lint-checks/integration-tests/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="MissingClass"
@@ -31,7 +31,7 @@
     <issue
         id="BanKeepAnnotation"
         message="Uses @Keep annotation"
-        errorLine1="@Keep"
+        errorLine1="@Keep class KeepAnnotationUsageKotlin"
         errorLine2="~~~~~">
         <location
             file="src/main/java/androidx/KeepAnnotationUsageKotlin.kt"/>
@@ -85,7 +85,7 @@
     <issue
         id="BanTargetApiAnnotation"
         message="Use `@RequiresApi` instead of `@TargetApi`"
-        errorLine1="    @TargetApi(30)"
+        errorLine1="    @TargetApi(30) fun someMethod() {}"
         errorLine2="    ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/TargetApiUsageKotlin.kt"/>
@@ -139,157 +139,13 @@
     <issue
         id="BanUncheckedReflection"
         message="Method.invoke requires both an upper and lower SDK bounds checks to be safe, and the upper bound must be below SdkVersionInfo.HIGHEST_KNOWN_API."
-        errorLine1="                        performStopActivity2ParamsMethod!!.invoke("
-        errorLine2="                        ^">
+        errorLine1="                        performStopActivity2ParamsMethod!!.invoke(activityThread, token, false)"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/sample/core/app/ActivityRecreatorKt.kt"/>
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        adapter.setAutofillOptions();"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        adapter.setAutofillOptions(vararg);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        adapter.setAutofillOptions(vararg1, vararg2, vararg3);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 28; however, the containing class androidx.AutofixUnsafeCallOnCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            ((DisplayCutout) secretDisplayCutout).getSafeInsetTop();"
-        errorLine2="                                                  ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallOnCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return new AdaptiveIconDrawable(null, null);"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitReturnCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return new AdaptiveIconDrawable(null, null);"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitReturnCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return Icon.createWithAdaptiveBitmap(null);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitReturnCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return Icon.createWithAdaptiveBitmap(null);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitReturnCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        useStyle(new Notification.DecoratedCustomViewStyle());"
-        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitReturnCast.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.AutofixUnsafeConstructorQualifiedClass is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return new Notification.DecoratedCustomViewStyle();"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeConstructorQualifiedClass.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.AutofixUnsafeConstructorReferenceJava is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            AccessibilityNodeInfo node = new AccessibilityNodeInfo(new View(context), 1);"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeConstructorReferenceJava.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.AutofixUnsafeGenericMethodReferenceJava is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return context.getSystemService(serviceClass);"
-        errorLine2="                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeGenericMethodReferenceJava.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class androidx.ClassVerificationFailureFromJava is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return view.getAccessibilityClassName();"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/ClassVerificationFailureFromJava.java"/>
-    </issue>
-
-    <issue
-        id="ImplicitCastClassVerificationFailure"
-        message="This expression has type android.app.Notification.CarExtender (introduced in API level 23) but it used as type android.app.Notification.Extender (introduced in API level 20). Run-time class verification will not be able to validate this implicit cast on devices between these API levels."
-        errorLine1="        builder.extend(extender);"
-        errorLine2="                       ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitParamCast.java"/>
-    </issue>
-
-    <issue
-        id="ImplicitCastClassVerificationFailure"
-        message="This expression has type android.graphics.drawable.AdaptiveIconDrawable (introduced in API level 26) but it used as type android.graphics.drawable.Drawable (introduced in API level 1). Run-time class verification will not be able to validate this implicit cast on devices between these API levels."
-        errorLine1="        return new AdaptiveIconDrawable(null, null);"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitReturnCast.java"/>
-    </issue>
-
-    <issue
-        id="ImplicitCastClassVerificationFailure"
-        message="This expression has type android.app.Notification.DecoratedCustomViewStyle (introduced in API level 24) but it used as type android.app.Notification.Style (introduced in API level 16). Run-time class verification will not be able to validate this implicit cast on devices between these API levels."
-        errorLine1="        useStyle(new Notification.DecoratedCustomViewStyle());"
-        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/AutofixUnsafeCallWithImplicitReturnCast.java"/>
-    </issue>
-
-    <issue
         id="LongLogTag"
         message="The logging tag can be at most 23 characters, was 24 (ActivityRecreatorChecked)"
         errorLine1="                    Log.e(LOG_TAG, &quot;Exception while invoking performStopActivity&quot;, t);"
@@ -310,8 +166,8 @@
     <issue
         id="LongLogTag"
         message="The logging tag can be at most 23 characters, was 24 (ActivityRecreatorChecked)"
-        errorLine1="                        LOG_TAG,"
-        errorLine2="                        ~~~~~~~">
+        errorLine1="                    Log.e(LOG_TAG, &quot;Exception while invoking performStopActivity&quot;, t)"
+        errorLine2="                          ~~~~~~~">
         <location
             file="src/main/java/androidx/sample/core/app/ActivityRecreatorKtChecked.kt"/>
     </issue>
@@ -382,7 +238,7 @@
     <issue
         id="UsesNonDefaultVisibleForTesting"
         message="Found non-default `otherwise` value for @VisibleForTesting"
-        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)"
+        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) fun testMethodPrivate() {}"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/VisibleForTestingUsageKotlin.kt"/>
@@ -400,7 +256,7 @@
     <issue
         id="UsesNonDefaultVisibleForTesting"
         message="Found non-default `otherwise` value for @VisibleForTesting"
-        errorLine1="    @VisibleForTesting(VisibleForTesting.PRIVATE)"
+        errorLine1="    @VisibleForTesting(VisibleForTesting.PRIVATE) fun testMethodValuePrivate() {}"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/VisibleForTestingUsageKotlin.kt"/>
@@ -418,7 +274,7 @@
     <issue
         id="UsesNonDefaultVisibleForTesting"
         message="Found non-default `otherwise` value for @VisibleForTesting"
-        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)"
+        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) fun testMethodProtected() {}"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/VisibleForTestingUsageKotlin.kt"/>
@@ -427,7 +283,7 @@
     <issue
         id="UsesNonDefaultVisibleForTesting"
         message="Found non-default `otherwise` value for @VisibleForTesting"
-        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.NONE)"
+        errorLine1="    @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun testMethodPackageNone() {}"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/VisibleForTestingUsageKotlin.kt"/>
@@ -436,7 +292,7 @@
     <issue
         id="UsesNonDefaultVisibleForTesting"
         message="Found non-default `otherwise` value for @VisibleForTesting"
-        errorLine1="    @get:VisibleForTesting(NONE)"
+        errorLine1="    @get:VisibleForTesting(NONE) val testPropertyGet = &quot;test&quot;"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/VisibleForTestingUsageKotlin.kt"/>
@@ -490,7 +346,7 @@
     <issue
         id="UsesRestrictToTestsScope"
         message="Replace `@RestrictTo(TESTS)` with `@VisibleForTesting`"
-        errorLine1="    @RestrictTo(RestrictTo.Scope.TESTS)"
+        errorLine1="    @RestrictTo(RestrictTo.Scope.TESTS) fun testMethod() {}"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt"/>
@@ -499,7 +355,7 @@
     <issue
         id="UsesRestrictToTestsScope"
         message="Replace `@RestrictTo(TESTS)` with `@VisibleForTesting`"
-        errorLine1="    @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY)"
+        errorLine1="    @RestrictTo(RestrictTo.Scope.TESTS, RestrictTo.Scope.LIBRARY) fun testMethodVarArg() {}"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt"/>
@@ -508,7 +364,7 @@
     <issue
         id="UsesRestrictToTestsScope"
         message="Replace `@RestrictTo(TESTS)` with `@VisibleForTesting`"
-        errorLine1="    @get:RestrictTo(RestrictTo.Scope.TESTS)"
+        errorLine1="    @get:RestrictTo(RestrictTo.Scope.TESTS) val testPropertyGet = &quot;test&quot;"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/RestrictToTestsAnnotationUsageKotlin.kt"/>
@@ -679,8 +535,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 19"
-        errorLine1="     @RequiresApi(19)"
-        errorLine2="     ~~~~~~~~~~~~~~~~">
+        errorLine1="    @RequiresApi(19)"
+        errorLine2="    ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/RequiresApiKotlin.kt"/>
     </issue>
diff --git a/loader/loader-ktx/build.gradle b/loader/loader-ktx/build.gradle
index c700ba7..ffb4403 100644
--- a/loader/loader-ktx/build.gradle
+++ b/loader/loader-ktx/build.gradle
@@ -52,9 +52,9 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2019"
     description = "Kotlin extensions for 'loader' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.loader.ktx"
 }
diff --git a/loader/loader/build.gradle b/loader/loader/build.gradle
index 5e08078..fb67f53 100644
--- a/loader/loader/build.gradle
+++ b/loader/loader/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.0.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.lifecycle:lifecycle-viewmodel:2.0.0")
     implementation(projectOrArtifact(":core:core"))
     implementation("androidx.collection:collection:1.0.0")
@@ -34,9 +34,9 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2011"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.loader"
 }
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 5e86297..9d86e93 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     api("androidx.core:core:1.6.0")
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.collection:collection:1.1.0")
     implementation("androidx.core:core:1.9.0")
 
@@ -65,5 +65,4 @@
     inceptionYear = "2011"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java b/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
index b3ce8b3..450f034 100644
--- a/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
@@ -76,7 +76,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -2383,12 +2382,10 @@
     private static class Api21Impl {
         private Api21Impl() {}
 
-        @DoNotInline
         static MediaDescription getDescription(MediaBrowser.MediaItem item) {
             return item.getDescription();
         }
 
-        @DoNotInline
         static int getFlags(MediaBrowser.MediaItem item) {
             return item.getFlags();
         }
diff --git a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
index 1a4f88e..ffa9638 100644
--- a/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
@@ -28,7 +28,6 @@
 import android.support.v4.media.session.MediaSessionCompat;
 import android.text.TextUtils;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -573,95 +572,79 @@
     private static class Api21Impl {
         private Api21Impl() {}
 
-        @DoNotInline
         static MediaDescription.Builder createBuilder() {
             return new MediaDescription.Builder();
         }
 
-        @DoNotInline
         static void setMediaId(MediaDescription.Builder builder,
                 @Nullable String mediaId) {
             builder.setMediaId(mediaId);
         }
 
-        @DoNotInline
         static void setTitle(MediaDescription.Builder builder,
                 @Nullable CharSequence title) {
             builder.setTitle(title);
         }
 
-        @DoNotInline
         static void setSubtitle(MediaDescription.Builder builder,
                 @Nullable CharSequence subtitle) {
             builder.setSubtitle(subtitle);
         }
 
-        @DoNotInline
         static void setDescription(MediaDescription.Builder builder,
                 @Nullable CharSequence description) {
             builder.setDescription(description);
         }
 
-        @DoNotInline
         static void setIconBitmap(MediaDescription.Builder builder,
                 @Nullable Bitmap icon) {
             builder.setIconBitmap(icon);
         }
 
-        @DoNotInline
         static void setIconUri(MediaDescription.Builder builder,
                 @Nullable Uri iconUri) {
             builder.setIconUri(iconUri);
         }
 
-        @DoNotInline
         static void setExtras(MediaDescription.Builder builder,
                 @Nullable Bundle extras) {
             builder.setExtras(extras);
         }
 
-        @DoNotInline
         static MediaDescription build(MediaDescription.Builder builder) {
             return builder.build();
         }
 
-        @DoNotInline
         @Nullable
         static String getMediaId(MediaDescription description) {
             return description.getMediaId();
         }
 
-        @DoNotInline
         @Nullable
         static CharSequence getTitle(MediaDescription description) {
             return description.getTitle();
         }
 
-        @DoNotInline
         @Nullable
         static CharSequence getSubtitle(MediaDescription description) {
             return description.getSubtitle();
         }
 
-        @DoNotInline
         @Nullable
         static CharSequence getDescription(MediaDescription description) {
             return description.getDescription();
         }
 
-        @DoNotInline
         @Nullable
         static Bitmap getIconBitmap(MediaDescription description) {
             return description.getIconBitmap();
         }
 
-        @DoNotInline
         @Nullable
         static Uri getIconUri(MediaDescription description) {
             return description.getIconUri();
         }
 
-        @DoNotInline
         @Nullable
         static Bundle getExtras(MediaDescription description) {
             return description.getExtras();
@@ -672,13 +655,11 @@
     private static class Api23Impl {
         private Api23Impl() {}
 
-        @DoNotInline
         static void setMediaUri(MediaDescription.Builder builder,
                 @Nullable Uri mediaUri) {
             builder.setMediaUri(mediaUri);
         }
 
-        @DoNotInline
         @Nullable
         static Uri getMediaUri(MediaDescription description) {
             return description.getMediaUri();
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
index 7dd3fbe..3030cdf 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -65,7 +65,6 @@
 import android.view.KeyEvent;
 import android.view.ViewConfiguration;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.GuardedBy;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -2281,17 +2280,14 @@
         private static class Api21Impl {
             private Api21Impl() {}
 
-            @DoNotInline
             static MediaSession.QueueItem createQueueItem(MediaDescription description, long id) {
                 return new MediaSession.QueueItem(description, id);
             }
 
-            @DoNotInline
             static MediaDescription getDescription(MediaSession.QueueItem queueItem) {
                 return queueItem.getDescription();
             }
 
-            @DoNotInline
             static long getQueueId(MediaSession.QueueItem queueItem) {
                 return queueItem.getQueueId();
             }
diff --git a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
index a4c0f17..4a84c9a0 100644
--- a/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -28,7 +28,6 @@
 import android.text.TextUtils;
 import android.view.KeyEvent;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.LongDef;
 import androidx.annotation.Nullable;
@@ -1379,125 +1378,101 @@
     private static class Api21Impl {
         private Api21Impl() {}
 
-        @DoNotInline
         static PlaybackState.Builder createBuilder() {
             return new PlaybackState.Builder();
         }
 
-        @DoNotInline
         static void setState(PlaybackState.Builder builder, int state, long position,
                 float playbackSpeed, long updateTime) {
             builder.setState(state, position, playbackSpeed, updateTime);
         }
 
-        @DoNotInline
         static void setBufferedPosition(PlaybackState.Builder builder, long bufferedPosition) {
             builder.setBufferedPosition(bufferedPosition);
         }
 
-        @DoNotInline
         static void setActions(PlaybackState.Builder builder, long actions) {
             builder.setActions(actions);
         }
 
-        @DoNotInline
         static void setErrorMessage(PlaybackState.Builder builder, CharSequence error) {
             builder.setErrorMessage(error);
         }
 
-        @DoNotInline
         static void addCustomAction(PlaybackState.Builder builder,
                 PlaybackState.CustomAction customAction) {
             builder.addCustomAction(customAction);
         }
 
-        @DoNotInline
         static void setActiveQueueItemId(PlaybackState.Builder builder, long id) {
             builder.setActiveQueueItemId(id);
         }
 
-        @DoNotInline
         static List<PlaybackState.CustomAction> getCustomActions(PlaybackState state) {
             return state.getCustomActions();
         }
 
-        @DoNotInline
         static PlaybackState build(PlaybackState.Builder builder) {
             return builder.build();
         }
 
-        @DoNotInline
         static int getState(PlaybackState state) {
             return state.getState();
         }
 
-        @DoNotInline
         static long getPosition(PlaybackState state) {
             return state.getPosition();
         }
 
-        @DoNotInline
         static long getBufferedPosition(PlaybackState state) {
             return state.getBufferedPosition();
         }
 
-        @DoNotInline
         static float getPlaybackSpeed(PlaybackState state) {
             return state.getPlaybackSpeed();
         }
 
-        @DoNotInline
         static long getActions(PlaybackState state) {
             return state.getActions();
         }
 
-        @DoNotInline
         static CharSequence getErrorMessage(PlaybackState state) {
             return state.getErrorMessage();
         }
 
-        @DoNotInline
         static long getLastPositionUpdateTime(PlaybackState state) {
             return state.getLastPositionUpdateTime();
         }
 
-        @DoNotInline
         static long getActiveQueueItemId(PlaybackState state) {
             return state.getActiveQueueItemId();
         }
 
-        @DoNotInline
         static PlaybackState.CustomAction.Builder createCustomActionBuilder(String action,
                 CharSequence name, int icon) {
             return new PlaybackState.CustomAction.Builder(action, name, icon);
         }
 
-        @DoNotInline
         static void setExtras(PlaybackState.CustomAction.Builder builder, Bundle extras) {
             builder.setExtras(extras);
         }
 
-        @DoNotInline
         static PlaybackState.CustomAction build(PlaybackState.CustomAction.Builder builder) {
             return builder.build();
         }
 
-        @DoNotInline
         static Bundle getExtras(PlaybackState.CustomAction customAction) {
             return customAction.getExtras();
         }
 
-        @DoNotInline
         static String getAction(PlaybackState.CustomAction customAction) {
             return customAction.getAction();
         }
 
-        @DoNotInline
         static CharSequence getName(PlaybackState.CustomAction customAction) {
             return customAction.getName();
         }
 
-        @DoNotInline
         static int getIcon(PlaybackState.CustomAction customAction) {
             return customAction.getIcon();
         }
@@ -1507,12 +1482,10 @@
     private static class Api22Impl {
         private Api22Impl() {}
 
-        @DoNotInline
         static void setExtras(PlaybackState.Builder builder, Bundle extras) {
             builder.setExtras(extras);
         }
 
-        @DoNotInline
         static Bundle getExtras(PlaybackState state) {
             return state.getExtras();
         }
diff --git a/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java b/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
index a126a53..249172d 100644
--- a/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
+++ b/media/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
@@ -28,7 +28,6 @@
 import android.os.Looper;
 import android.os.Message;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -430,7 +429,6 @@
     private static class Api26Impl {
         private Api26Impl() {}
 
-        @DoNotInline
         static AudioFocusRequest createInstance(
                 int focusGain,
                 AudioAttributes audioAttributes,
diff --git a/media/media/src/main/java/androidx/media/AudioManagerCompat.java b/media/media/src/main/java/androidx/media/AudioManagerCompat.java
index d65d023..e962042 100644
--- a/media/media/src/main/java/androidx/media/AudioManagerCompat.java
+++ b/media/media/src/main/java/androidx/media/AudioManagerCompat.java
@@ -20,7 +20,6 @@
 import android.media.AudioManager;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -178,7 +177,6 @@
     @RequiresApi(21)
     private static class Api21Impl {
 
-        @DoNotInline
         static boolean isVolumeFixed(AudioManager audioManager) {
             return audioManager.isVolumeFixed();
         }
@@ -189,13 +187,11 @@
     @RequiresApi(26)
     private static class Api26Impl {
 
-        @DoNotInline
         static int abandonAudioFocusRequest(AudioManager audioManager,
                 AudioFocusRequest focusRequest) {
             return audioManager.abandonAudioFocusRequest(focusRequest);
         }
 
-        @DoNotInline
         static int requestAudioFocus(AudioManager audioManager, AudioFocusRequest focusRequest) {
             return audioManager.requestAudioFocus(focusRequest);
         }
@@ -206,7 +202,6 @@
     @RequiresApi(28)
     private static class Api28Impl {
 
-        @DoNotInline
         static int getStreamMinVolume(AudioManager audioManager, int streamType) {
             return audioManager.getStreamMinVolume(streamType);
         }
diff --git a/media/media/src/main/java/androidx/media/VolumeProviderCompat.java b/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
index 1d098f6..d83e42a 100644
--- a/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
+++ b/media/media/src/main/java/androidx/media/VolumeProviderCompat.java
@@ -22,7 +22,6 @@
 import android.os.Build;
 import android.support.v4.media.session.MediaSessionCompat;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -239,7 +238,6 @@
     private static class Api21Impl {
         private Api21Impl() {}
 
-        @DoNotInline
         static void setCurrentVolume(VolumeProvider volumeProvider, int currentVolume) {
             volumeProvider.setCurrentVolume(currentVolume);
         }
diff --git a/media/media/src/main/java/androidx/media/app/NotificationCompat.java b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
index bde7799..93ff351 100644
--- a/media/media/src/main/java/androidx/media/app/NotificationCompat.java
+++ b/media/media/src/main/java/androidx/media/app/NotificationCompat.java
@@ -33,7 +33,6 @@
 import android.view.View;
 import android.widget.RemoteViews;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -542,17 +541,14 @@
     private static class Api21Impl {
         private Api21Impl() {}
 
-        @DoNotInline
         static void setMediaStyle(Notification.Builder builder, Notification.MediaStyle style) {
             builder.setStyle(style);
         }
 
-        @DoNotInline
         static Notification.MediaStyle createMediaStyle() {
             return new Notification.MediaStyle();
         }
 
-        @DoNotInline
         static Notification.MediaStyle fillInMediaStyle(Notification.MediaStyle style,
                 int[] actionsToShowInCompact, MediaSessionCompat.Token token) {
             if (actionsToShowInCompact != null) {
@@ -564,12 +560,10 @@
             return style;
         }
 
-        @DoNotInline
         static void setShowActionsInCompactView(Notification.MediaStyle style, int... actions) {
             style.setShowActionsInCompactView(actions);
         }
 
-        @DoNotInline
         static void setMediaSession(Notification.MediaStyle style, MediaSession.Token token) {
             style.setMediaSession(token);
         }
@@ -579,7 +573,6 @@
     private static class Api24Impl {
         private Api24Impl() {}
 
-        @DoNotInline
         static Notification.MediaStyle createDecoratedMediaCustomViewStyle() {
             return new Notification.DecoratedMediaCustomViewStyle();
         }
@@ -591,7 +584,6 @@
         private Api34Impl() {}
 
         @SuppressLint({"MissingPermission"})
-        @DoNotInline
         static Notification.MediaStyle setRemotePlaybackInfo(Notification.MediaStyle style,
                 @NonNull CharSequence deviceName, @DrawableRes int iconResource,
                 @Nullable PendingIntent chipIntent, Boolean showRemotePlaybackInfo) {
diff --git a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
index e24f55e..c2dedad 100644
--- a/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
+++ b/media/media/src/main/java/androidx/media/session/MediaButtonReceiver.java
@@ -36,7 +36,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
@@ -366,7 +365,6 @@
          * Returns true if the passed exception is a
          * {@link ForegroundServiceStartNotAllowedException}.
          */
-        @DoNotInline
         public static boolean instanceOfForegroundServiceStartNotAllowedException(
                 IllegalStateException e) {
             return e instanceof ForegroundServiceStartNotAllowedException;
@@ -377,7 +375,6 @@
          * {@link ForegroundServiceStartNotAllowedException} and throws an exception if the cast
          * fails.
          */
-        @DoNotInline
         public static ForegroundServiceStartNotAllowedException
                 castToForegroundServiceStartNotAllowedException(IllegalStateException e) {
             return (ForegroundServiceStartNotAllowedException) e;
diff --git a/media/version-compat-tests/lib/build.gradle b/media/version-compat-tests/lib/build.gradle
index 879c393..bca738f 100644
--- a/media/version-compat-tests/lib/build.gradle
+++ b/media/version-compat-tests/lib/build.gradle
@@ -20,7 +20,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.versionedparcelable:versionedparcelable:1.1.1")
     implementation(libs.junit)
 }
diff --git a/mediarouter/mediarouter-testing/build.gradle b/mediarouter/mediarouter-testing/build.gradle
index df62521..993da5e 100644
--- a/mediarouter/mediarouter-testing/build.gradle
+++ b/mediarouter/mediarouter-testing/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation(project(":mediarouter:mediarouter"))
 }
 
@@ -38,7 +38,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Test utilities for AndroidX MediaRouter"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/mediarouter/mediarouter/api/current.txt b/mediarouter/mediarouter/api/current.txt
index 453631b..dc395c4 100644
--- a/mediarouter/mediarouter/api/current.txt
+++ b/mediarouter/mediarouter/api/current.txt
@@ -324,12 +324,23 @@
 
   public abstract class MediaRouteProviderService extends android.app.Service {
     ctor public MediaRouteProviderService();
+    method public void addClientInfoListener(java.util.concurrent.Executor, androidx.core.util.Consumer<java.util.List<androidx.mediarouter.media.MediaRouteProviderService.ClientInfo!>!>);
     method public androidx.mediarouter.media.MediaRouteProvider? getMediaRouteProvider();
     method public android.os.IBinder? onBind(android.content.Intent);
     method public abstract androidx.mediarouter.media.MediaRouteProvider? onCreateMediaRouteProvider();
+    method public void removeClientInfoListener(androidx.core.util.Consumer<java.util.List<androidx.mediarouter.media.MediaRouteProviderService.ClientInfo!>!>);
     field public static final String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
   }
 
+  public static final class MediaRouteProviderService.ClientInfo {
+    method public String getPackageName();
+  }
+
+  public static final class MediaRouteProviderService.ClientInfo.Builder {
+    ctor public MediaRouteProviderService.ClientInfo.Builder(String);
+    method public androidx.mediarouter.media.MediaRouteProviderService.ClientInfo build();
+  }
+
   public final class MediaRouteSelector {
     method public android.os.Bundle asBundle();
     method public boolean contains(androidx.mediarouter.media.MediaRouteSelector);
diff --git a/mediarouter/mediarouter/api/restricted_current.txt b/mediarouter/mediarouter/api/restricted_current.txt
index 453631b..dc395c4 100644
--- a/mediarouter/mediarouter/api/restricted_current.txt
+++ b/mediarouter/mediarouter/api/restricted_current.txt
@@ -324,12 +324,23 @@
 
   public abstract class MediaRouteProviderService extends android.app.Service {
     ctor public MediaRouteProviderService();
+    method public void addClientInfoListener(java.util.concurrent.Executor, androidx.core.util.Consumer<java.util.List<androidx.mediarouter.media.MediaRouteProviderService.ClientInfo!>!>);
     method public androidx.mediarouter.media.MediaRouteProvider? getMediaRouteProvider();
     method public android.os.IBinder? onBind(android.content.Intent);
     method public abstract androidx.mediarouter.media.MediaRouteProvider? onCreateMediaRouteProvider();
+    method public void removeClientInfoListener(androidx.core.util.Consumer<java.util.List<androidx.mediarouter.media.MediaRouteProviderService.ClientInfo!>!>);
     field public static final String SERVICE_INTERFACE = "android.media.MediaRouteProviderService";
   }
 
+  public static final class MediaRouteProviderService.ClientInfo {
+    method public String getPackageName();
+  }
+
+  public static final class MediaRouteProviderService.ClientInfo.Builder {
+    ctor public MediaRouteProviderService.ClientInfo.Builder(String);
+    method public androidx.mediarouter.media.MediaRouteProviderService.ClientInfo build();
+  }
+
   public final class MediaRouteSelector {
     method public android.os.Bundle asBundle();
     method public boolean contains(androidx.mediarouter.media.MediaRouteSelector);
diff --git a/mediarouter/mediarouter/build.gradle b/mediarouter/mediarouter/build.gradle
index 51397fd..d30aa8d 100644
--- a/mediarouter/mediarouter/build.gradle
+++ b/mediarouter/mediarouter/build.gradle
@@ -72,5 +72,4 @@
     inceptionYear = "2013"
     description = "Android MediaRouter Support Library"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/mediarouter/mediarouter/lint-baseline.xml b/mediarouter/mediarouter/lint-baseline.xml
index 315dbc1..44dd466 100644
--- a/mediarouter/mediarouter/lint-baseline.xml
+++ b/mediarouter/mediarouter/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.5.0-alpha06" type="baseline" client="gradle" dependencies="false" name="AGP (8.5.0-alpha06)" variant="all" version="8.5.0-alpha06">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -29,24 +29,6 @@
     </issue>
 
     <issue
-        id="IllegalExperimentalApiUsage"
-        message="`Experimental` and `RequiresOptIn` APIs may only be used within the same-version group where they were defined."
-        errorLine1="    @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java"/>
-    </issue>
-
-    <issue
-        id="IllegalExperimentalApiUsage"
-        message="`Experimental` and `RequiresOptIn` APIs may only be used within the same-version group where they were defined."
-        errorLine1="    @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java"/>
-    </issue>
-
-    <issue
         id="MissingTestSizeAnnotation"
         message="Missing test size annotation"
         errorLine1="    public void testReset() {"
@@ -73,13 +55,4 @@
             file="src/main/java/androidx/mediarouter/app/DeviceUtils.java"/>
     </issue>
 
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="                if (what == CLIENT_MSG_REGISTER &amp;&amp; Build.VERSION.SDK_INT"
-        errorLine2="                                                   ^">
-        <location
-            file="src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java"/>
-    </issue>
-
 </issues>
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteProviderServiceTest.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteProviderServiceTest.java
index 02aea2f..9fd3cc8 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteProviderServiceTest.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouteProviderServiceTest.java
@@ -31,7 +31,9 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.text.TextUtils;
 
+import androidx.mediarouter.media.MediaRouteProviderService.ClientInfo;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
@@ -44,8 +46,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -75,11 +79,16 @@
 
     private static CountDownLatch sActiveScanCountDownLatch;
     private static CountDownLatch sPassiveScanCountDownLatch;
+    private static CountDownLatch sClientInfoListenerAdditionCountDownLatch;
+    private static CountDownLatch sClientInfoListenerRemovalCountDownLatch;
     private static MediaRouteDiscoveryRequest sLastDiscoveryRequest;
+    private static List<ClientInfo> sLatestClientInfo = new ArrayList<>();
 
     @Before
     public void setUp() throws Exception {
         resetActiveAndPassiveScanCountDownLatches();
+        resetClientInfoListenerAdditionCountDownLatch(2);
+        resetClientInfoListenerRemovalCountDownLatch(1);
         Context context = ApplicationProvider.getApplicationContext();
         Intent intent =
                 new Intent(context, MediaRouteProviderServiceImpl.class)
@@ -92,6 +101,8 @@
                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO).build();
         registerClient(mReceiveMessenger1);
         registerClient(mReceiveMessenger2);
+        assertTrue(sClientInfoListenerAdditionCountDownLatch.await(
+                TIME_OUT_MS, TimeUnit.MILLISECONDS));
     }
 
     @After
@@ -261,6 +272,31 @@
         assertTrue(sPassiveScanCountDownLatch.await(1000 + TIME_OUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    @LargeTest
+    @Test
+    public void testRegisterClient_clientRecordListenerCalled() throws Exception {
+        resetClientInfoListenerAdditionCountDownLatch(1);
+        resetClientInfoListenerRemovalCountDownLatch(1);
+        int initialCount = sLatestClientInfo.size();
+        Messenger messenger = new Messenger(new Handler(Looper.getMainLooper()));
+        registerClient(messenger);
+
+        assertTrue(
+                sClientInfoListenerAdditionCountDownLatch.await(
+                        TIME_OUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(1, sLatestClientInfo.size() - initialCount);
+        Context context = ApplicationProvider.getApplicationContext();
+        for (ClientInfo clientInfo : sLatestClientInfo) {
+            assertTrue(TextUtils.equals(
+                    context.getPackageName(), clientInfo.getPackageName()));
+        }
+
+        unregisterClient(messenger);
+        assertTrue(
+                sClientInfoListenerRemovalCountDownLatch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(initialCount, sLatestClientInfo.size());
+    }
+
     private void registerClient(Messenger receiveMessenger) throws Exception {
         Message msg = Message.obtain();
         msg.what = MediaRouteProviderProtocol.CLIENT_MSG_REGISTER;
@@ -292,6 +328,23 @@
 
     /** Fake {@link MediaRouteProviderService} implementation. */
     public static final class MediaRouteProviderServiceImpl extends MediaRouteProviderService {
+
+        @Override
+        public void onCreate() {
+            super.onCreate();
+            sLatestClientInfo.clear();
+            addClientInfoListener(Executors.newSingleThreadExecutor(), clients -> {
+                int previousSize = sLatestClientInfo.size();
+                int newSize = clients.size();
+                sLatestClientInfo = clients;
+                if (newSize > previousSize) {
+                    sClientInfoListenerAdditionCountDownLatch.countDown();
+                } else if (previousSize > newSize) {
+                    sClientInfoListenerRemovalCountDownLatch.countDown();
+                }
+            });
+        }
+
         @Override
         public MediaRouteProvider onCreateMediaRouteProvider() {
             return new MediaRouteProviderImpl(this);
@@ -325,4 +378,12 @@
         sActiveScanCountDownLatch = new CountDownLatch(1);
         sPassiveScanCountDownLatch = new CountDownLatch(1);
     }
+
+    private void resetClientInfoListenerAdditionCountDownLatch(int count) {
+        sClientInfoListenerAdditionCountDownLatch = new CountDownLatch(count);
+    }
+
+    private void resetClientInfoListenerRemovalCountDownLatch(int count) {
+        sClientInfoListenerRemovalCountDownLatch = new CountDownLatch(count);
+    }
 }
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java
index ec6a61b..1ab6667 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteChooserDialog.java
@@ -596,4 +596,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java
index 6cee3d0..e88cc01 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/SystemOutputSwitcherDialogController.java
@@ -28,7 +28,6 @@
 import android.os.Build;
 import android.provider.Settings;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
@@ -163,6 +162,7 @@
             ApplicationInfo appInfo = activityInfo.applicationInfo;
             if (((ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
                     & appInfo.flags) != 0) {
+                intent.setPackage(appInfo.packageName);
                 context.startActivity(intent);
                 return true;
             }
@@ -192,6 +192,7 @@
             ApplicationInfo appInfo = activityInfo.applicationInfo;
             if (((ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
                     & appInfo.flags) != 0) {
+                intent.setPackage(appInfo.packageName);
                 context.startActivity(intent);
                 return true;
             }
@@ -218,7 +219,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static MediaRouter2 getInstance(Context context) {
             return MediaRouter2.getInstance(context);
         }
@@ -230,7 +230,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static boolean showSystemOutputSwitcher(MediaRouter2 mediaRouter2) {
             return mediaRouter2.showSystemOutputSwitcher();
         }
@@ -241,7 +240,6 @@
         private Api23Impl() {
         }
 
-        @DoNotInline
         public static boolean isSuitableDeviceAlreadyConnectedAsAudioOutput(Context context) {
             AudioManager audioManager = context.getSystemService(AudioManager.class);
             AudioDeviceInfo[] audioDeviceInfos = audioManager.getDevices(
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
index d5593e6..201dd205 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
@@ -97,8 +97,8 @@
             new RemoteControlClientCompat.PlaybackInfo();
     private final ProviderCallback mProviderCallback = new ProviderCallback();
     private final boolean mLowRam;
+    private final boolean mTransferReceiverDeclared;
 
-    private boolean mTransferReceiverDeclared;
     private boolean mUseMediaRouter2ForSystemRouting;
     private MediaRoute2Provider mMr2Provider;
     private PlatformMediaRouter1RouteProvider mPlatformMediaRouter1RouteProvider;
@@ -404,6 +404,13 @@
 
     /* package */ void selectRoute(
             @NonNull MediaRouter.RouteInfo route, @MediaRouter.UnselectReason int unselectReason) {
+        selectRoute(route, unselectReason, /* syncMediaRoute1Provider= */ true);
+    }
+
+    /* package */ void selectRoute(
+            @NonNull MediaRouter.RouteInfo route,
+            @MediaRouter.UnselectReason int unselectReason,
+            boolean syncMediaRoute1Provider) {
         if (!mRoutes.contains(route)) {
             Log.w(TAG, "Ignoring attempt to select removed route: " + route);
             return;
@@ -420,7 +427,7 @@
                 && mSelectedRoute != route) {
             mMr2Provider.transferTo(route.getDescriptorId());
         } else {
-            selectRouteInternal(route, unselectReason);
+            selectRouteInternal(route, unselectReason, syncMediaRoute1Provider);
         }
     }
 
@@ -762,7 +769,12 @@
                 }
             }
         } else {
-            Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
+            String message =
+                    providerDescriptor != null
+                            ? "Ignoring invalid provider descriptor: " + providerDescriptor
+                            : "Ignoring null provider descriptor from "
+                                    + provider.getComponentName();
+            Log.w(TAG, message);
         }
 
         // Dispose all remaining routes that do not have matching descriptors.
@@ -917,7 +929,15 @@
                     "Unselecting the current route because it "
                             + "is no longer selectable: "
                             + mSelectedRoute);
-            selectRouteInternal(chooseFallbackRoute(), UNSELECT_REASON_UNKNOWN);
+            // TODO: b/294968421 - Consider passing a false syncMediaRoute1Provider. This could help
+            // with the prevention of setBluetoothA2dpOn(false) bugs, but it could also leave the
+            // platform MediaRouter in an inconsistent state. In order to change
+            // syncMediaRoute1Provider to false, we need to assess the impact of not calling
+            // android.media.MediaRouter.selectRoute as a result of this method call.
+            selectRouteInternal(
+                    chooseFallbackRoute(),
+                    UNSELECT_REASON_UNKNOWN,
+                    /* syncMediaRoute1Provider= */ true);
         } else if (selectedRouteDescriptorChanged) {
             // In case the selected route is a route group, select/unselect route controllers
             // for the added/removed route members.
@@ -953,7 +973,9 @@
     }
 
     /* package */ void selectRouteInternal(
-            @NonNull MediaRouter.RouteInfo route, @MediaRouter.UnselectReason int unselectReason) {
+            @NonNull MediaRouter.RouteInfo route,
+            @MediaRouter.UnselectReason int unselectReason,
+            boolean syncMediaRoute1Provider) {
         if (mSelectedRoute == route) {
             return;
         }
@@ -1044,16 +1066,18 @@
         if (mSelectedRoute == null) {
             mSelectedRoute = route;
             mSelectedRouteController = routeController;
-            mCallbackHandler.post(
-                    GlobalMediaRouter.CallbackHandler.MSG_ROUTE_SELECTED,
-                    new Pair<>(null, route),
-                    unselectReason);
+            mCallbackHandler.postRouteSelectedMessage(
+                    /* fromRoute= */ null,
+                    /* targetRoute= */ route,
+                    unselectReason,
+                    syncMediaRoute1Provider);
         } else {
             notifyTransfer(
                     this,
                     route,
                     routeController,
                     unselectReason,
+                    syncMediaRoute1Provider,
                     /* requestedRoute= */ null,
                     /* memberRoutes= */ null);
         }
@@ -1101,19 +1125,26 @@
             MediaRouter.RouteInfo route,
             @Nullable MediaRouteProvider.RouteController routeController,
             @MediaRouter.UnselectReason int reason,
+            boolean syncMediaRoute1Provider,
             @Nullable MediaRouter.RouteInfo requestedRoute,
             @Nullable
-            Collection<
-                    MediaRouteProvider.DynamicGroupRouteController
-                            .DynamicRouteDescriptor>
-                    memberRoutes) {
+                    Collection<
+                                    MediaRouteProvider.DynamicGroupRouteController
+                                            .DynamicRouteDescriptor>
+                            memberRoutes) {
         if (mTransferNotifier != null) {
             mTransferNotifier.cancel();
             mTransferNotifier = null;
         }
         mTransferNotifier =
                 new MediaRouter.PrepareTransferNotifier(
-                        router, route, routeController, reason, requestedRoute, memberRoutes);
+                        router,
+                        route,
+                        routeController,
+                        reason,
+                        syncMediaRoute1Provider,
+                        requestedRoute,
+                        memberRoutes);
 
         if (mTransferNotifier.mReason != UNSELECT_REASON_ROUTE_CHANGED
                 || mOnPrepareTransferListener == null) {
@@ -1132,51 +1163,52 @@
 
     /* package */ MediaRouteProvider.DynamicGroupRouteController.OnDynamicRoutesChangedListener
             mDynamicRoutesListener =
-            new MediaRouteProvider.DynamicGroupRouteController
-                    .OnDynamicRoutesChangedListener() {
-                @Override
-                public void onRoutesChanged(
-                        @NonNull MediaRouteProvider.DynamicGroupRouteController controller,
-                        @Nullable MediaRouteDescriptor groupRouteDescriptor,
-                        @NonNull
-                        Collection<
-                                MediaRouteProvider
-                                        .DynamicGroupRouteController
-                                        .DynamicRouteDescriptor>
-                                routes) {
-                    if (controller == mRequestedRouteController
-                            && groupRouteDescriptor != null) {
-                        MediaRouter.ProviderInfo provider = mRequestedRoute.getProvider();
-                        String groupId = groupRouteDescriptor.getId();
+                    new MediaRouteProvider.DynamicGroupRouteController
+                            .OnDynamicRoutesChangedListener() {
+                        @Override
+                        public void onRoutesChanged(
+                                @NonNull MediaRouteProvider.DynamicGroupRouteController controller,
+                                @Nullable MediaRouteDescriptor groupRouteDescriptor,
+                                @NonNull
+                                        Collection<
+                                                        MediaRouteProvider
+                                                                .DynamicGroupRouteController
+                                                                .DynamicRouteDescriptor>
+                                                routes) {
+                            if (controller == mRequestedRouteController
+                                    && groupRouteDescriptor != null) {
+                                MediaRouter.ProviderInfo provider = mRequestedRoute.getProvider();
+                                String groupId = groupRouteDescriptor.getId();
 
-                        String uniqueId = assignRouteUniqueId(provider, groupId);
-                        MediaRouter.RouteInfo route =
-                                new MediaRouter.RouteInfo(provider, groupId, uniqueId);
-                        route.maybeUpdateDescriptor(groupRouteDescriptor);
+                                String uniqueId = assignRouteUniqueId(provider, groupId);
+                                MediaRouter.RouteInfo route =
+                                        new MediaRouter.RouteInfo(provider, groupId, uniqueId);
+                                route.maybeUpdateDescriptor(groupRouteDescriptor);
 
-                        if (mSelectedRoute == route) {
-                            return;
+                                if (mSelectedRoute == route) {
+                                    return;
+                                }
+
+                                notifyTransfer(
+                                        GlobalMediaRouter.this,
+                                        route,
+                                        mRequestedRouteController,
+                                        UNSELECT_REASON_ROUTE_CHANGED,
+                                        /* syncMediaRoute1Provider= */ true,
+                                        mRequestedRoute,
+                                        routes);
+
+                                mRequestedRoute = null;
+                                mRequestedRouteController = null;
+                            } else if (controller == mSelectedRouteController) {
+                                if (groupRouteDescriptor != null) {
+                                    updateRouteDescriptorAndNotify(
+                                            mSelectedRoute, groupRouteDescriptor);
+                                }
+                                mSelectedRoute.updateDynamicDescriptors(routes);
+                            }
                         }
-
-                        notifyTransfer(
-                                GlobalMediaRouter.this,
-                                route,
-                                mRequestedRouteController,
-                                UNSELECT_REASON_ROUTE_CHANGED,
-                                mRequestedRoute,
-                                routes);
-
-                        mRequestedRoute = null;
-                        mRequestedRouteController = null;
-                    } else if (controller == mSelectedRouteController) {
-                        if (groupRouteDescriptor != null) {
-                            updateRouteDescriptorAndNotify(
-                                    mSelectedRoute, groupRouteDescriptor);
-                        }
-                        mSelectedRoute.updateDynamicDescriptors(routes);
-                    }
-                }
-            };
+                    };
 
     @Override
     public void onPlatformRouteSelectedByDescriptorId(@NonNull String id) {
@@ -1324,7 +1356,12 @@
                 return;
             }
 
-            selectRouteInternal(routeToSelect, reason);
+            // TODO: b/294968421 - Consider passing a false syncMediaRoute1Provider. This could help
+            // with the prevention of setBluetoothA2dpOn(false) bugs, but it could also leave the
+            // platform MediaRouter in an inconsistent state. In order to change
+            // syncMediaRoute1Provider to false, we need to assess the impact of not calling
+            // android.media.MediaRouter.selectRoute as a result of this method call.
+            selectRouteInternal(routeToSelect, reason, /* syncMediaRoute1Provider */ true);
         }
 
         @Override
@@ -1351,7 +1388,7 @@
         /* package */ void selectRouteToFallbackRoute(@MediaRouter.UnselectReason int reason) {
             MediaRouter.RouteInfo fallbackRoute = chooseFallbackRoute();
             if (getSelectedRoute() != fallbackRoute) {
-                selectRouteInternal(fallbackRoute, reason);
+                selectRouteInternal(fallbackRoute, reason, /* syncMediaRoute1Provider */ true);
             }
             // Does nothing when the selected route is same with fallback route.
             // This is the difference between this and unselect().
@@ -1492,7 +1529,29 @@
 
         public static final int MSG_ROUTER_PARAMS_CHANGED = MSG_TYPE_ROUTER | 1;
 
-        CallbackHandler() {
+        /* package */ void postRouteSelectedMessage(
+                @Nullable MediaRouter.RouteInfo fromRoute,
+                @NonNull MediaRouter.RouteInfo targetRoute,
+                int reason,
+                boolean syncMediaRoute1Provider) {
+            RouteSelectedMessageParams params =
+                    new RouteSelectedMessageParams(fromRoute, targetRoute, syncMediaRoute1Provider);
+            Message message = obtainMessage(MSG_ROUTE_SELECTED, params);
+            message.arg1 = reason;
+            message.sendToTarget();
+        }
+
+        /* package */ void postAnotherRouteSelectedMessage(
+                @Nullable MediaRouter.RouteInfo requestedRoute,
+                @NonNull MediaRouter.RouteInfo targetRoute,
+                int reason,
+                boolean syncMediaRoute1Provider) {
+            RouteSelectedMessageParams params =
+                    new RouteSelectedMessageParams(
+                            requestedRoute, targetRoute, syncMediaRoute1Provider);
+            Message message = obtainMessage(MSG_ROUTE_ANOTHER_SELECTED, params);
+            message.arg1 = reason;
+            message.sendToTarget();
         }
 
         /* package */ void post(int msg, Object obj) {
@@ -1557,9 +1616,11 @@
                             (MediaRouter.RouteInfo) obj);
                     break;
                 case MSG_ROUTE_SELECTED: {
-                    MediaRouter.RouteInfo selectedRoute =
-                            ((Pair<MediaRouter.RouteInfo, MediaRouter.RouteInfo>) obj).second;
-                    mPlatformMediaRouter1RouteProvider.onSyncRouteSelected(selectedRoute);
+                    RouteSelectedMessageParams params = (RouteSelectedMessageParams) obj;
+                    MediaRouter.RouteInfo selectedRoute = params.mTargetRoute;
+                    if (params.mSyncMediaRoute1Provider) {
+                        mPlatformMediaRouter1RouteProvider.onSyncRouteSelected(selectedRoute);
+                    }
                     // TODO(b/166794092): Remove this nullness check
                     if (mDefaultRoute != null && selectedRoute.isDefaultOrBluetooth()) {
                         for (MediaRouter.RouteInfo prevGroupRoute : mDynamicGroupRoutes) {
@@ -1570,11 +1631,13 @@
                     break;
                 }
                 case MSG_ROUTE_ANOTHER_SELECTED: {
-                    MediaRouter.RouteInfo groupRoute =
-                            ((Pair<MediaRouter.RouteInfo, MediaRouter.RouteInfo>) obj).second;
+                    RouteSelectedMessageParams params = (RouteSelectedMessageParams) obj;
+                    MediaRouter.RouteInfo groupRoute = params.mTargetRoute;
                     mDynamicGroupRoutes.add(groupRoute);
                     mPlatformMediaRouter1RouteProvider.onSyncRouteAdded(groupRoute);
-                    mPlatformMediaRouter1RouteProvider.onSyncRouteSelected(groupRoute);
+                    if (params.mSyncMediaRoute1Provider) {
+                        mPlatformMediaRouter1RouteProvider.onSyncRouteSelected(groupRoute);
+                    }
                     break;
                 }
             }
@@ -1587,16 +1650,18 @@
             final MediaRouter.Callback callback = record.mCallback;
             switch (what & MSG_TYPE_MASK) {
                 case MSG_TYPE_ROUTE: {
+                    RouteSelectedMessageParams selectedMessageParams =
+                                what == MSG_ROUTE_ANOTHER_SELECTED || what == MSG_ROUTE_SELECTED
+                                        ? ((RouteSelectedMessageParams) obj)
+                                        : null;
                     final MediaRouter.RouteInfo route =
-                            (what == MSG_ROUTE_ANOTHER_SELECTED || what == MSG_ROUTE_SELECTED)
-                                    ? ((Pair<MediaRouter.RouteInfo, MediaRouter.RouteInfo>) obj)
-                                    .second
-                                    : (MediaRouter.RouteInfo) obj;
+                                selectedMessageParams != null
+                                        ? selectedMessageParams.mTargetRoute
+                                        : (MediaRouter.RouteInfo) obj;
                     final MediaRouter.RouteInfo optionalRoute =
-                            (what == MSG_ROUTE_ANOTHER_SELECTED || what == MSG_ROUTE_SELECTED)
-                                    ? ((Pair<MediaRouter.RouteInfo, MediaRouter.RouteInfo>) obj)
-                                    .first
-                                    : null;
+                                selectedMessageParams != null
+                                        ? selectedMessageParams.mFromOrRequestedRoute
+                                        : null;
                     if (route == null
                             || !record.filterRouteEvent(route, what, optionalRoute, arg)) {
                         break;
@@ -1656,4 +1721,29 @@
             }
         }
     }
+
+    /**
+     * Holds the parameters of {@link CallbackHandler#MSG_ROUTE_SELECTED} and {@link
+     * CallbackHandler#MSG_ROUTE_ANOTHER_SELECTED}.
+     */
+    private static final class RouteSelectedMessageParams {
+        /**
+         * Holds the origin route for {@link CallbackHandler#MSG_ROUTE_SELECTED}, or the originally
+         * requested route for {@link CallbackHandler#MSG_ROUTE_ANOTHER_SELECTED}.
+         */
+        @Nullable public final MediaRouter.RouteInfo mFromOrRequestedRoute;
+
+        @NonNull public final MediaRouter.RouteInfo mTargetRoute;
+
+        public final boolean mSyncMediaRoute1Provider;
+
+        private RouteSelectedMessageParams(
+                @Nullable MediaRouter.RouteInfo fromOrRequestedRoute,
+                @NonNull MediaRouter.RouteInfo targetRoute,
+                boolean syncMediaRoute1Provider) {
+            mFromOrRequestedRoute = fromOrRequestedRoute;
+            mTargetRoute = targetRoute;
+            mSyncMediaRoute1Provider = syncMediaRoute1Provider;
+        }
+    }
 }
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java
index cf0b40a..6220936 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2Provider.java
@@ -44,7 +44,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -406,7 +405,6 @@
     }
 
     private class TransferCallback extends MediaRouter2.TransferCallback {
-        TransferCallback() {}
 
         @Override
         public void onTransfer(@NonNull MediaRouter2.RoutingController oldController,
@@ -727,7 +725,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static void setPlatformRouteListingPreference(
                 @NonNull MediaRouter2 mediaRouter2,
                 @Nullable android.media.RouteListingPreference routeListingPreference) {
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java
index 545cbf9..9520134 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderService.java
@@ -16,6 +16,7 @@
 
 package androidx.mediarouter.media;
 
+import static androidx.mediarouter.media.MediaRouter.UNSELECT_REASON_UNKNOWN;
 import static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_MEMBER_ROUTE_ID;
 import static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_MEMBER_ROUTE_IDS;
 import static androidx.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID;
@@ -54,7 +55,6 @@
 import static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED;
 import static androidx.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_CURRENT;
 import static androidx.mediarouter.media.MediaRouteProviderProtocol.isValidRemoteMessenger;
-import static androidx.mediarouter.media.MediaRouter.UNSELECT_REASON_UNKNOWN;
 
 import android.app.Service;
 import android.content.Context;
@@ -79,6 +79,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.collection.ArrayMap;
 import androidx.core.content.ContextCompat;
+import androidx.core.util.Consumer;
 import androidx.core.util.ObjectsCompat;
 import androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController;
 import androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor;
@@ -89,8 +90,10 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * Base class for media route provider services.
@@ -174,6 +177,9 @@
         boolean onSetDiscoveryRequest(Messenger messenger, int requestId,
                 MediaRouteDiscoveryRequest request);
         MediaRouteProvider.Callback getProviderCallback();
+        void addClientInfoListener(Executor listenerExecutor, Consumer<List<ClientInfo>> listener);
+        void removeClientInfoListener(Consumer<List<ClientInfo>> listener);
+        void removeAllClientInfoListeners();
     }
 
     /**
@@ -193,6 +199,31 @@
     }
 
     /**
+     * Adds a {@link Consumer} that will be used to provide updates when the list of
+     * bound clients changes.
+     *
+     * <p>The {@link Consumer} will be called with the current list of bound clients.</p>
+     *
+     * @param listenerExecutor an {@link Executor} that will be used to dispatch the callback
+     * @param listener a @code{@link Consumer} that takes a list of {@link ClientInfo}
+     */
+    public void addClientInfoListener(
+            @NonNull /* @CallbackExecutor */ Executor listenerExecutor,
+            @NonNull Consumer<List<ClientInfo>> listener) {
+        mImpl.addClientInfoListener(listenerExecutor, listener);
+    }
+
+    /**
+     * Removes the given {@link Consumer} if it was previously set.
+     *
+     * @param listener the {@link Consumer} to remove.
+     * @see #addClientInfoListener(Executor, Consumer)
+     */
+    public void removeClientInfoListener(@NonNull Consumer<List<ClientInfo>> listener) {
+        mImpl.removeClientInfoListener(listener);
+    }
+
+    /**
      * Called by the system when it is time to create the media route provider.
      *
      * @return The media route provider offered by this service, or null if
@@ -250,6 +281,7 @@
         if (mProvider != null) {
             mProvider.setCallback(null);
         }
+        mImpl.removeAllClientInfoListeners();
         super.onDestroy();
     }
 
@@ -309,6 +341,42 @@
         return "Client connection " + messenger.getBinder().toString();
     }
 
+    /**
+     * Contains information about a client that is bound to this service.
+     */
+    public static final class ClientInfo {
+        private final String packageName;
+
+        private ClientInfo(@NonNull String packageName) {
+            this.packageName = packageName;
+        }
+
+        /**
+         * Returns the package name of the client.
+         *
+         * @return The package name of the client
+         */
+        @NonNull
+        public String getPackageName() {
+            return packageName;
+        }
+
+        /** Builder for {@link ClientInfo}. */
+        public static final class Builder {
+            private final String packageName;
+
+            public Builder(@NonNull String packageName) {
+                this.packageName = packageName;
+            }
+
+            /** Builds and returns the {@link ClientInfo} object. */
+            @NonNull
+            public ClientInfo build() {
+                return new ClientInfo(packageName);
+            }
+        }
+    }
+
     private final class PrivateHandler extends Handler {
         PrivateHandler() {
         }
@@ -490,10 +558,14 @@
 
     static class MediaRouteProviderServiceImplBase implements MediaRouteProviderServiceImpl {
         final MediaRouteProviderService mService;
-        final ArrayList<ClientRecord> mClients = new ArrayList<ClientRecord>();
+        final ArrayList<ClientRecord> mClients = new ArrayList<>();
         MediaRouteDiscoveryRequest mCompositeDiscoveryRequest;
         MediaRouteDiscoveryRequest mBaseDiscoveryRequest;
         long mBaseDiscoveryRequestTimestamp;
+        @Nullable
+        private final Map<Consumer<List<ClientInfo>>, Executor> mClientInfoListeners =
+                new HashMap<>();
+        private final Object mClientInfoListenersLock = new Object();
         private final MediaRouterActiveScanThrottlingHelper mActiveScanThrottlingHelper =
                 new MediaRouterActiveScanThrottlingHelper(new Runnable() {
                     @Override
@@ -537,7 +609,7 @@
                 if (index < 0) {
                     ClientRecord client = createClientRecord(messenger, version, packageName);
                     if (client.register()) {
-                        mClients.add(client);
+                        addClient(client);
                         if (DEBUG) {
                             Log.d(TAG, client + ": Registered, version=" + version);
                         }
@@ -560,7 +632,7 @@
         public boolean onUnregisterClient(Messenger messenger, int requestId) {
             int index = findClient(messenger);
             if (index >= 0) {
-                ClientRecord client = mClients.remove(index);
+                ClientRecord client = removeClient(index);
                 if (DEBUG) {
                     Log.d(TAG, client + ": Unregistered");
                 }
@@ -575,7 +647,7 @@
         public void onBinderDied(Messenger messenger) {
             int index = findClient(messenger);
             if (index >= 0) {
-                ClientRecord client = mClients.remove(index);
+                ClientRecord client = removeClient(index);
                 if (DEBUG) {
                     Log.d(TAG, client + ": Binder died");
                 }
@@ -1100,12 +1172,72 @@
                 sendMessage(mMessenger, SERVICE_MSG_DYNAMIC_ROUTE_DESCRIPTORS_CHANGED,
                         0, controllerId, bundle, null);
             }
+
+            private ClientInfo getClientInfo() {
+                return new ClientInfo.Builder(mPackageName).build();
+            }
         }
 
         ClientRecord createClientRecord(Messenger messenger, int version, String packageName) {
             return new ClientRecord(messenger, version, packageName);
         }
 
+        private void addClient(ClientRecord client) {
+            mClients.add(client);
+            notifyClientRecordsChanged();
+        }
+
+        private ClientRecord removeClient(int index) {
+            ClientRecord removedClient = mClients.remove(index);
+            notifyClientRecordsChanged();
+            return removedClient;
+        }
+
+        @Override
+        public void addClientInfoListener (
+                @NonNull /* @CallbackExecutor */ Executor listenerExecutor,
+                @NonNull Consumer<List<ClientInfo>> listener) {
+            synchronized (mClientInfoListenersLock) {
+                mClientInfoListeners.put(listener, listenerExecutor);
+                // Immediately provide the current list of bound clients.
+                notifyClientRecordsChanged();
+            }
+        }
+
+        @Override
+        public void removeClientInfoListener(Consumer<List<ClientInfo>> listener) {
+            synchronized (mClientInfoListenersLock) {
+                mClientInfoListeners.remove(listener);
+            }
+        }
+
+        @Override
+        public void removeAllClientInfoListeners() {
+            synchronized (mClientInfoListenersLock) {
+                mClientInfoListeners.clear();
+            }
+        }
+
+        private void notifyClientRecordsChanged() {
+            if (!mClientInfoListeners.isEmpty()) {
+                ArrayList<ClientInfo> clientInfos = new ArrayList<>();
+                for (ClientRecord client : new ArrayList<>(mClients)) {
+                    clientInfos.add(client.getClientInfo());
+                }
+
+                synchronized (mClientInfoListenersLock) {
+                    for (Map.Entry<Consumer<List<ClientInfo>>, Executor> entry :
+                            mClientInfoListeners.entrySet()) {
+                        Consumer<List<ClientInfo>> listener = entry.getKey();
+                        Executor executor = entry.getValue();
+                        executor.execute(() -> {
+                            listener.accept(clientInfos);
+                        });
+                    }
+                }
+            }
+        }
+
         class ProviderCallbackBase extends MediaRouteProvider.Callback {
             @Override
             public void onDescriptorChanged(@NonNull MediaRouteProvider provider,
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
index 1a6bad4..faabbdc 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -43,7 +43,6 @@
 import androidx.annotation.RestrictTo;
 import androidx.collection.ArrayMap;
 import androidx.core.util.ObjectsCompat;
-import androidx.core.util.Pair;
 import androidx.mediarouter.app.MediaRouteDiscoveryFragment;
 import androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController;
 import androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor;
@@ -1979,11 +1978,29 @@
          */
         @MainThread
         public void select() {
-            checkCallingThread();
-            getGlobalRouter().selectRoute(this, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED);
+            select(/* syncMediaRoute1Provider= */ true);
         }
 
         /**
+         * Selects this media route.
+         *
+         * @param syncMediaRoute1Provider Whether this selection should be passed through to {@link
+         *     PlatformMediaRouter1RouteProvider}. Should be false when this call is the result of a
+         *     {@link MediaRouter.Callback#onRouteSelected} call.
+         */
+        @RestrictTo(LIBRARY)
+        @MainThread
+        public void select(boolean syncMediaRoute1Provider) {
+            checkCallingThread();
+            getGlobalRouter()
+                    .selectRoute(
+                            this,
+                            MediaRouter.UNSELECT_REASON_ROUTE_CHANGED,
+                            syncMediaRoute1Provider);
+        }
+
+
+        /**
          * Returns true if the route has one or more members
          */
         @RestrictTo(LIBRARY)
@@ -2691,6 +2708,7 @@
 
         final RouteController mToRouteController;
         final @UnselectReason int mReason;
+        private final boolean mSyncMediaRoute1Provider;
         private final RouteInfo mFromRoute;
         final RouteInfo mToRoute;
         private final RouteInfo mRequestedRoute;
@@ -2702,8 +2720,12 @@
         private boolean mFinished = false;
         private boolean mCanceled = false;
 
-        PrepareTransferNotifier(GlobalMediaRouter router, RouteInfo route,
-                @Nullable RouteController routeController, @UnselectReason int reason,
+        PrepareTransferNotifier(
+                GlobalMediaRouter router,
+                RouteInfo route,
+                @Nullable RouteController routeController,
+                @UnselectReason int reason,
+                boolean syncMediaRoute1Provider,
                 @Nullable RouteInfo requestedRoute,
                 @Nullable Collection<DynamicRouteDescriptor> memberRoutes) {
             mRouter = new WeakReference<>(router);
@@ -2711,6 +2733,7 @@
             mToRoute = route;
             mToRouteController = routeController;
             mReason = reason;
+            mSyncMediaRoute1Provider = syncMediaRoute1Provider;
             mFromRoute = router.mSelectedRoute;
             mRequestedRoute = requestedRoute;
             mMemberRoutes = (memberRoutes == null) ? null : new ArrayList<>(memberRoutes);
@@ -2807,12 +2830,11 @@
             router.mSelectedRouteController = mToRouteController;
 
             if (mRequestedRoute == null) {
-                router.mCallbackHandler.post(GlobalMediaRouter.CallbackHandler.MSG_ROUTE_SELECTED,
-                        new Pair<>(mFromRoute, mToRoute), mReason);
+                router.mCallbackHandler.postRouteSelectedMessage(
+                        mFromRoute, mToRoute, mReason, mSyncMediaRoute1Provider);
             } else {
-                router.mCallbackHandler.post(
-                        GlobalMediaRouter.CallbackHandler.MSG_ROUTE_ANOTHER_SELECTED,
-                        new Pair<>(mRequestedRoute, mToRoute), mReason);
+                router.mCallbackHandler.postAnotherRouteSelectedMessage(
+                        mRequestedRoute, mToRoute, mReason, mSyncMediaRoute1Provider);
             }
 
             router.mRouteControllerMap.clear();
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java
index 0b8ccf5..40c5b79 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter2Utils.java
@@ -73,7 +73,6 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
@@ -470,18 +469,15 @@
     @RequiresApi(api = 34)
     private static final class Api34Impl {
 
-        @DoNotInline
         public static void setDeduplicationIds(
                 MediaRoute2Info.Builder builder, Set<String> deduplicationIds) {
             builder.setDeduplicationIds(deduplicationIds);
         }
 
-        @DoNotInline
         public static Set<String> getDeduplicationIds(MediaRoute2Info fwkMediaRoute2Info) {
             return fwkMediaRoute2Info.getDeduplicationIds();
         }
 
-        @DoNotInline
         public static void copyDescriptorVisibilityToBuilder(MediaRoute2Info.Builder builder,
                 MediaRouteDescriptor descriptor) {
             if (descriptor.isVisibilityPublic()) {
@@ -491,12 +487,10 @@
             }
         }
 
-        @DoNotInline
         public static void setDeviceType(MediaRoute2Info.Builder builder, int deviceType) {
             builder.setType(deviceType);
         }
 
-        @DoNotInline
         public static int getType(MediaRoute2Info fwkMediaRoute2Info) {
             return fwkMediaRoute2Info.getType();
         }
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterUtils.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterUtils.java
index daa8fbd..095aa65 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterUtils.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterUtils.java
@@ -16,7 +16,6 @@
 
 package androidx.mediarouter.media;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 
 /** Utils for usage with platform {@link android.media.MediaRouter} */
@@ -28,7 +27,6 @@
         return new CallbackProxy<>(callback);
     }
 
-    @DoNotInline
     public static android.media.MediaRouter.VolumeCallback createVolumeCallback(
             VolumeCallback callback) {
         return new VolumeCallbackProxy<>(callback);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/PlatformMediaRouter1RouteProvider.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/PlatformMediaRouter1RouteProvider.java
index ea8dc73..322240f 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/PlatformMediaRouter1RouteProvider.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/PlatformMediaRouter1RouteProvider.java
@@ -16,16 +16,21 @@
 
 package androidx.mediarouter.media;
 
+import static android.media.MediaRouter.RouteInfo.DEVICE_TYPE_BLUETOOTH;
+import static android.media.MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER;
+import static android.media.MediaRouter.RouteInfo.DEVICE_TYPE_TV;
+import static android.media.MediaRouter.RouteInfo.DEVICE_TYPE_UNKNOWN;
+
 import android.annotation.SuppressLint;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.os.Build;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.mediarouter.R;
@@ -307,7 +312,7 @@
 
             UserRouteRecord userRouteRecord = getUserRouteRecord(route);
             if (userRouteRecord != null) {
-                userRouteRecord.mRoute.select();
+                userRouteRecord.mRoute.select(/* syncMediaRoute1Provider= */ false);
             } else {
                 // Select the route if it already exists in the compat media router.
                 // If not, we will select it instead when the route is added.
@@ -492,15 +497,41 @@
         }
 
         protected String getRouteName(android.media.MediaRouter.RouteInfo route) {
-            // Routes should not have null names but it may happen for badly configured
-            // user routes.  We tolerate this by using an empty name string here but
-            // such unnamed routes will be discarded by the media router upstream
-            // (with a log message so we can track down the problem).
-            CharSequence name = route.getName(getContext());
-            return name != null ? name.toString() : "";
+            // Routes with null or empty names are discarded by MediaRouter as not valid. This may
+            // happen for badly configured user routes, or for system routes (which are not
+            // guaranteed to have a non-empty name). For system routes, we replace the empty name
+            // with a placeholder one so that the route is not swallowed. Otherwise, that can mean
+            // that media router thinks no bluetooth route is available, and selects the default
+            // route instead. See b/294968421.
+            CharSequence routeInfoName = route.getName(getContext());
+            if (!TextUtils.isEmpty(routeInfoName)) {
+                return routeInfoName.toString();
+            } else if ((route.getSupportedTypes() & ROUTE_TYPE_USER) == 0) {
+                int fallbackRouteNameResourceId =
+                        getStringResourceIdForType(
+                                Build.VERSION.SDK_INT >= 24
+                                        ? route.getDeviceType()
+                                        : DEVICE_TYPE_UNKNOWN);
+                return getContext().getString(fallbackRouteNameResourceId);
+            } else {
+                return "";
+            }
         }
 
-        @DoNotInline
+        private static int getStringResourceIdForType(int deviceType) {
+            switch (deviceType) {
+                case DEVICE_TYPE_BLUETOOTH:
+                    return R.string.mr_route_name_bluetooth;
+                case DEVICE_TYPE_TV:
+                    return R.string.mr_route_name_tv;
+                case DEVICE_TYPE_SPEAKER:
+                    return R.string.mr_route_name_speaker;
+                case DEVICE_TYPE_UNKNOWN:
+                default:
+                    return R.string.mr_route_name_unknown;
+            }
+        }
+
         protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
                 MediaRouteDescriptor.Builder builder) {
             int supportedTypes = record.mRoute.getSupportedTypes();
@@ -557,18 +588,15 @@
             }
         }
 
-        @DoNotInline
         protected void selectRoute(android.media.MediaRouter.RouteInfo route) {
             mRouter.selectRoute(ALL_ROUTE_TYPES, route);
         }
 
-        @DoNotInline
         protected android.media.MediaRouter.RouteInfo getDefaultRoute() {
             return mRouter.getDefaultRoute();
         }
 
         @SuppressLint("WrongConstant") // False positive. See b/310913043.
-        @DoNotInline
         protected void updateUserRouteProperties(UserRouteRecord record) {
             android.media.MediaRouter.UserRouteInfo userRoute = record.mUserRoute;
             MediaRouter.RouteInfo routeInfo = record.mRoute;
@@ -581,7 +609,6 @@
             userRoute.setDescription(routeInfo.getDescription());
         }
 
-        @DoNotInline
         protected void updateCallback() {
             if (mCallbackRegistered) {
                 mRouter.removeCallback(mCallback);
@@ -593,7 +620,6 @@
             mRouter.addCallback(mRouteTypes, mCallback, flags);
         }
 
-        @DoNotInline
         protected boolean isConnecting(SystemRouteRecord record) {
             return record.mRoute.isConnecting();
         }
@@ -658,7 +684,6 @@
 
         @SuppressLint("WrongConstant") // False positive. See b/283059575.
         @Override
-        @DoNotInline
         protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
                 MediaRouteDescriptor.Builder builder) {
             super.onBuildSystemRouteDescriptor(record, builder);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java
index 19045af..937a1de 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemotePlaybackClient.java
@@ -25,7 +25,6 @@
 import android.os.Bundle;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -1070,7 +1069,6 @@
 
     @RequiresApi(33)
     private static class Api33 {
-        @DoNotInline
         static void registerReceiver(@NonNull Context context, @NonNull BroadcastReceiver receiver,
                 @NonNull IntentFilter filter, int flags) {
             context.registerReceiver(receiver, filter, flags);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RouteListingPreference.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RouteListingPreference.java
index 5ea089f..a6827e4 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RouteListingPreference.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RouteListingPreference.java
@@ -20,7 +20,6 @@
 import android.content.Intent;
 import android.text.TextUtils;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -562,7 +561,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         @NonNull
         public static android.media.RouteListingPreference toPlatformRouteListingPreference(
                 RouteListingPreference routeListingPreference) {
@@ -579,7 +577,6 @@
                     .build();
         }
 
-        @DoNotInline
         @NonNull
         public static android.media.RouteListingPreference.Item toPlatformItem(Item item) {
             return new android.media.RouteListingPreference.Item.Builder(item.getRouteId())
diff --git a/mediarouter/mediarouter/src/main/res/values-af/strings.xml b/mediarouter/mediarouter/src/main/res/values-af/strings.xml
index e3bcd52..fb320bd 100644
--- a/mediarouter/mediarouter/src/main/res/values-af/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-af/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Maak seker dat die ander toestel op dieselfde wi-fi-netwerk as hierdie toestel is"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Kom meer te wete"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Leer hoe om uit te saai"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Onbekend"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Luidspreker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-am/strings.xml b/mediarouter/mediarouter/src/main/res/values-am/strings.xml
index 037294c..ce40c5b 100644
--- a/mediarouter/mediarouter/src/main/res/values-am/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-am/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ሌላኛው መሣሪያ ከዚህ መሣሪያ ጋር አንድ ዓይነት የWi-Fi አውታረ መረብ ላይ መሆኑን ያረጋግጡ"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"የበለጠ ለመረዳት"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"እንዴት cast እንደሚያደርጉ ይወቁ"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"ያልታወቀ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ብሉቱዝ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ቲቪ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ድምፅ ማውጫ"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ar/strings.xml b/mediarouter/mediarouter/src/main/res/values-ar/strings.xml
index 76d00dc..e4bb37f 100644
--- a/mediarouter/mediarouter/src/main/res/values-ar/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ar/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"‏تأكَّد من أنّ الجهاز الآخر وهذا الجهاز متّصلان بشبكة Wi-Fi نفسها."</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"مزيد من المعلومات"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"التعرُّف على كيفية البثّ"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"غير معروف"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"بلوتوث"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"تلفزيون"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"مكبّر صوت"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-as/strings.xml b/mediarouter/mediarouter/src/main/res/values-as/strings.xml
index 863456a..17b1047 100644
--- a/mediarouter/mediarouter/src/main/res/values-as/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-as/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"আনটো ডিভাইচ এই ডিভাইচটোৰ সৈতে একেটা ৱাই-ফাই নেটৱৰ্কতে থকাটো নিশ্চিত কৰক"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"অধিক জানক"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"কেনেকৈ কাষ্ট কৰিব লাগে জানক"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"অজ্ঞাত"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ব্লুটুথ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"টিভি"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"স্পীকাৰ"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-az/strings.xml b/mediarouter/mediarouter/src/main/res/values-az/strings.xml
index 31b98ac..6e7401b 100644
--- a/mediarouter/mediarouter/src/main/res/values-az/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-az/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Digər cihaz bu cihaz ilə eyni Wi-Fi şəbəkəsində olmalıdır"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Ətraflı məlumat"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Yayım qaydasını öyrənin"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Naməlum"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Dinamik"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-b+sr+Latn/strings.xml b/mediarouter/mediarouter/src/main/res/values-b+sr+Latn/strings.xml
index 1101e83..780c450 100644
--- a/mediarouter/mediarouter/src/main/res/values-b+sr+Latn/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-b+sr+Latn/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Uverite se da je drugi uređaj povezan na istu WiFi mrežu kao ovaj uređaj"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Saznajte više"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Saznajte kako da prebacujete"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Nepoznato"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Zvučnik"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-be/strings.xml b/mediarouter/mediarouter/src/main/res/values-be/strings.xml
index d25f79c..2145991 100644
--- a/mediarouter/mediarouter/src/main/res/values-be/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-be/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Упэўніцеся, што другая прылада падключана да той жа сеткі Wi-Fi, што і гэта"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Даведацца больш"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Як пачаць трансляцыю"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Невядома"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Тэлевізар"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Дынамік"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-bg/strings.xml b/mediarouter/mediarouter/src/main/res/values-bg/strings.xml
index fdcf70c..486e2cc 100644
--- a/mediarouter/mediarouter/src/main/res/values-bg/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-bg/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Уверете се, че това и другото устройство са свързани с една и съща Wi-Fi мрежа"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Научете повече"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Научете как да предавате"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Неизвестно"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Телевизор"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Високоговорител"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-bn/strings.xml b/mediarouter/mediarouter/src/main/res/values-bn/strings.xml
index ed69a3c..28de1bd 100644
--- a/mediarouter/mediarouter/src/main/res/values-bn/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-bn/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"এই ডিভাইস ও অন্য ডিভাইস একই ওয়াই-ফাই নেটওয়ার্কের সাথে কানেক্ট করা আছে কিনা তা ভালভাবে দেখে নিন"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"আরও জানুন"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"কীভাবে কাস্ট করবেন তা জানুন"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"অজানা"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ব্লুটুথ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"টিভি"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"স্পিকার"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-bs/strings.xml b/mediarouter/mediarouter/src/main/res/values-bs/strings.xml
index a466315..88f0f7b 100644
--- a/mediarouter/mediarouter/src/main/res/values-bs/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-bs/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Provjerite je li drugi uređaj na istoj WiFi mreži na kojoj je i ovaj uređaj"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Saznajte više"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Saznajte kako emitirati"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Nepoznato"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Zvučnik"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ca/strings.xml b/mediarouter/mediarouter/src/main/res/values-ca/strings.xml
index a02305f..1a2f409 100644
--- a/mediarouter/mediarouter/src/main/res/values-ca/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ca/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Assegura\'t que l\'altre dispositiu estigui connectat a la mateixa xarxa Wi‑Fi que aquest dispositiu"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Més informació"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Informació sobre com pots emetre contingut"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Desconegut"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Altaveu"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-cs/strings.xml b/mediarouter/mediarouter/src/main/res/values-cs/strings.xml
index c49e396..044516d 100644
--- a/mediarouter/mediarouter/src/main/res/values-cs/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-cs/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Připojte druhé zařízení ke stejné síti Wi-Fi, jako toto zařízení"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Další informace"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Podívejte se, jak na to"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Neznámé"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Televize"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Reproduktor"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-da/strings.xml b/mediarouter/mediarouter/src/main/res/values-da/strings.xml
index 7bcda06..dc09990 100644
--- a/mediarouter/mediarouter/src/main/res/values-da/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-da/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Sørg for, at den anden enhed har forbindelse til det samme Wi-Fi-netværk som denne enhed"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Få flere oplysninger"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Sådan caster du indhold"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Ukendt"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Fjernsyn"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Højttaler"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-de/strings.xml b/mediarouter/mediarouter/src/main/res/values-de/strings.xml
index f3c8246..fb80e5b 100644
--- a/mediarouter/mediarouter/src/main/res/values-de/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-de/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Achte darauf, dass sich das andere Gerät im selben WLAN wie dieses Gerät befindet"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Weitere Informationen"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Informationen zum Streamen"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Unbekannt"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Fernseher"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Lautsprecher"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-el/strings.xml b/mediarouter/mediarouter/src/main/res/values-el/strings.xml
index 8836241..7286460 100644
--- a/mediarouter/mediarouter/src/main/res/values-el/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-el/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Βεβαιωθείτε ότι η άλλη συσκευή είναι συνδεδεμένη στο ίδιο δίκτυο Wi-Fi με αυτήν τη συσκευή"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Μάθετε περισσότερα"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Μάθετε πώς μπορείτε να κάνετε μετάδοση"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Άγνωστο"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Τηλεόραση"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Ηχείο"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-en-rAU/strings.xml b/mediarouter/mediarouter/src/main/res/values-en-rAU/strings.xml
index c402d8c..d6c25b2 100644
--- a/mediarouter/mediarouter/src/main/res/values-en-rAU/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-en-rAU/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Make sure that the other device is on the same Wi-Fi network as this device"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Learn more"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Learn how to cast"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Unknown"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-en-rCA/strings.xml b/mediarouter/mediarouter/src/main/res/values-en-rCA/strings.xml
index c303499..bea3c98 100644
--- a/mediarouter/mediarouter/src/main/res/values-en-rCA/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-en-rCA/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Make sure the other device is on the same Wi-Fi network as this device"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Learn more"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Learn how to cast"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Unknown"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-en-rGB/strings.xml b/mediarouter/mediarouter/src/main/res/values-en-rGB/strings.xml
index c402d8c..d6c25b2 100644
--- a/mediarouter/mediarouter/src/main/res/values-en-rGB/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-en-rGB/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Make sure that the other device is on the same Wi-Fi network as this device"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Learn more"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Learn how to cast"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Unknown"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-en-rIN/strings.xml b/mediarouter/mediarouter/src/main/res/values-en-rIN/strings.xml
index c402d8c..d6c25b2 100644
--- a/mediarouter/mediarouter/src/main/res/values-en-rIN/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-en-rIN/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Make sure that the other device is on the same Wi-Fi network as this device"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Learn more"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Learn how to cast"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Unknown"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-en-rXC/strings.xml b/mediarouter/mediarouter/src/main/res/values-en-rXC/strings.xml
index d155ad0..e40581a 100644
--- a/mediarouter/mediarouter/src/main/res/values-en-rXC/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-en-rXC/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎Make sure the other device is on the same Wi-Fi network as this device‎‏‎‎‏‎"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎"<a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"‎‏‎‎‏‏‏‎Learn more‎‏‎‎‏‏‎"</a>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎Learn how to cast‎‏‎‎‏‎"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎Unknown‎‏‎‎‏‎"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎Bluetooth‎‏‎‎‏‎"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎TV‎‏‎‎‏‎"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎Speaker‎‏‎‎‏‎"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-es-rUS/strings.xml b/mediarouter/mediarouter/src/main/res/values-es-rUS/strings.xml
index 71a2e79..377d0fd 100644
--- a/mediarouter/mediarouter/src/main/res/values-es-rUS/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-es-rUS/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Asegúrate de que el dispositivo esté conectado a la misma red Wi-Fi que este dispositivo"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Más información"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Descubre cómo transmitir contenido"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Desconocida"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Bocina"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-es/strings.xml b/mediarouter/mediarouter/src/main/res/values-es/strings.xml
index 75c921a..3711b9d 100644
--- a/mediarouter/mediarouter/src/main/res/values-es/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-es/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Asegúrate de que el otro dispositivo esté conectado a la misma red Wi-Fi que este dispositivo"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Más información"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Consulta cómo enviar contenido"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Desconocido"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Televisión"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Altavoz"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-et/strings.xml b/mediarouter/mediarouter/src/main/res/values-et/strings.xml
index 9ee1a87..cef8a6e 100644
--- a/mediarouter/mediarouter/src/main/res/values-et/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-et/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Veenduge, et teine seade oleks samas WiFi-võrgus kui see seade"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Lisateave"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Lisateave ülekandmise kohta"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Teadmata"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Teler"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Kõlar"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-eu/strings.xml b/mediarouter/mediarouter/src/main/res/values-eu/strings.xml
index c11b400..1eae7a0 100644
--- a/mediarouter/mediarouter/src/main/res/values-eu/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-eu/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Ziurtatu beste gailua gailu honen wifi-sare berera konektatuta dagoela"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Lortu informazio gehiago"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Lortu edukia igortzeko argibideak"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Ezezaguna"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetootha"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Telebista"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Bozgorailua"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-fa/strings.xml b/mediarouter/mediarouter/src/main/res/values-fa/strings.xml
index d13e45f..1e7e967 100644
--- a/mediarouter/mediarouter/src/main/res/values-fa/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-fa/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"‏مطمئن شوید این دستگاه و دستگاه دیگر به شبکه Wi-Fi یکسانی متصل باشند"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"بیشتر بدانید"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"آشنایی با نحوه پخش محتوا"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"نامشخص"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"بلوتوث"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"تلویزیون"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"بلندگو"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-fi/strings.xml b/mediarouter/mediarouter/src/main/res/values-fi/strings.xml
index 6b53293..eb28beb 100644
--- a/mediarouter/mediarouter/src/main/res/values-fi/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-fi/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Varmista, että tämä ja toinen laite ovat yhdistettynä samaan Wi-Fi-verkkoon"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Lue lisää"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Näin striimaaminen onnistuu"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Tuntematon"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Kaiutin"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-fr-rCA/strings.xml b/mediarouter/mediarouter/src/main/res/values-fr-rCA/strings.xml
index 22b8438..36328b1 100644
--- a/mediarouter/mediarouter/src/main/res/values-fr-rCA/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-fr-rCA/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Assurez-vous que l\'autre appareil est connecté au même réseau Wi-Fi que cet appareil"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"En savoir plus"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Apprendre à diffuser du contenu"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Inconnu"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Téléviseur"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Haut-parleur"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-fr/strings.xml b/mediarouter/mediarouter/src/main/res/values-fr/strings.xml
index dc37209..e7d1d6a 100644
--- a/mediarouter/mediarouter/src/main/res/values-fr/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-fr/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Assurez-vous que l\'autre appareil est connecté au même réseau Wi-Fi que cet appareil"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"En savoir plus"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Découvrez comment caster"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Inconnu"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Enceinte"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-gl/strings.xml b/mediarouter/mediarouter/src/main/res/values-gl/strings.xml
index 6455657..61d38e4 100644
--- a/mediarouter/mediarouter/src/main/res/values-gl/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-gl/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Asegúrate de que o outro dispositivo estea na mesma rede wifi que este dispositivo"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Máis información"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Aprende a emitir contido"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Dispositivo descoñecido"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Televisión"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Altofalante"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-gu/strings.xml b/mediarouter/mediarouter/src/main/res/values-gu/strings.xml
index 850d436..10fa701 100644
--- a/mediarouter/mediarouter/src/main/res/values-gu/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-gu/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ખાતરી કરો કે અન્ય ડિવાઇસ એ જ વાઇ-ફાઇ નેટવર્ક પર હોય જેના પર આ ડિવાઇસ છે"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"વધુ જાણો"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"કાસ્ટ કરવાની રીત જાણો"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"અજાણ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"બ્લૂટૂથ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ટીવી"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"સ્પીકર"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-hi/strings.xml b/mediarouter/mediarouter/src/main/res/values-hi/strings.xml
index 1028a38..56a7d08 100644
--- a/mediarouter/mediarouter/src/main/res/values-hi/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-hi/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"पक्का करें कि दूसरा डिवाइस और यह डिवाइस, दोनों एक ही वाई-फ़ाई नेटवर्क से जुड़े हों"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ज़्यादा जानें"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"कास्ट करने का तरीका जानें"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"कोई जानकारी नहीं है"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ब्लूटूथ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"टीवी"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"स्पीकर"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-hr/strings.xml b/mediarouter/mediarouter/src/main/res/values-hr/strings.xml
index d0b716a..8c32666 100644
--- a/mediarouter/mediarouter/src/main/res/values-hr/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-hr/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Povežite drugi uređaj s istom Wi-Fi mrežom s kojom je povezan ovaj uređaj"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Saznajte više"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Saznajte kako emitirati"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Nepoznato"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Zvučnik"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-hu/strings.xml b/mediarouter/mediarouter/src/main/res/values-hu/strings.xml
index f03b68b..43bdc3a 100644
--- a/mediarouter/mediarouter/src/main/res/values-hu/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-hu/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Gondoskodjon arról, hogy a másik eszköz ugyanahhoz a Wi-Fi-hálózathoz csatlakozzon, mint ez az eszköz."</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"További információ"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"A tartalomátküldés elsajátítása"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Ismeretlen"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Tévé"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Hangszóró"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-hy/strings.xml b/mediarouter/mediarouter/src/main/res/values-hy/strings.xml
index 5e9a3c2..a9968ba 100644
--- a/mediarouter/mediarouter/src/main/res/values-hy/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-hy/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Համոզվեք, որ մյուս սարքը միացած է նույն Wi-Fi ցանցին, ինչ այս սարքը"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Իմանալ ավելին"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Ինչպես հեռարձակել"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Անհայտ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Հեռուստացույց"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Բարձրախոս"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-in/strings.xml b/mediarouter/mediarouter/src/main/res/values-in/strings.xml
index d8d59a2..a646d3d 100644
--- a/mediarouter/mediarouter/src/main/res/values-in/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-in/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Pastikan perangkat lainnya berada di jaringan Wi-Fi yang sama dengan perangkat ini"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Pelajari lebih lanjut"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Pelajari cara melakukan transmisi"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Tidak diketahui"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-is/strings.xml b/mediarouter/mediarouter/src/main/res/values-is/strings.xml
index aa7794c..ca21530 100644
--- a/mediarouter/mediarouter/src/main/res/values-is/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-is/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Passaðu að hitt tækið sé tengt sama WiFi-neti og þetta tæki"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Nánar"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Kynntu þér hvernig þú varpar"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Óþekkt"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Sjónvarp"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Hátalari"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-it/strings.xml b/mediarouter/mediarouter/src/main/res/values-it/strings.xml
index 9b3b874..141abba 100644
--- a/mediarouter/mediarouter/src/main/res/values-it/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-it/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Assicurati che l\'altro dispositivo sia connesso alla stessa rete Wi-Fi di questo dispositivo"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Scopri di più"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Scopri come trasmettere contenuti"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Sconosciuto"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-iw/strings.xml b/mediarouter/mediarouter/src/main/res/values-iw/strings.xml
index 7bafb1a..acc104f 100644
--- a/mediarouter/mediarouter/src/main/res/values-iw/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-iw/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"‏מוודאים ששני המכשירים מחוברים לאותה רשת Wi-Fi"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"מידע נוסף"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"‏איך להפעיל Cast"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"לא ידוע"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"טלוויזיה"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"רמקול"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ja/strings.xml b/mediarouter/mediarouter/src/main/res/values-ja/strings.xml
index b9ffc6a1..e50c76e 100644
--- a/mediarouter/mediarouter/src/main/res/values-ja/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ja/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"他のデバイスがこのデバイスと同じ Wi-Fi ネットワークに接続していることを確認してください"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"詳細"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"キャスト方法"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"不明"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"テレビ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"スピーカー"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ka/strings.xml b/mediarouter/mediarouter/src/main/res/values-ka/strings.xml
index c631572..472367a 100644
--- a/mediarouter/mediarouter/src/main/res/values-ka/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ka/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"დარწმუნდით, რომ სხვა მოწყობილობა იმავე Wi-Fi ქსელთან არის დაკავშირებული, რომელთანაც ეს მოწყობილობა"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"შეიტყვეთ მეტი"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ტრანსლირების სწავლა"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"უცნობი"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ტელევიზორი"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"დინამიკი"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-kk/strings.xml b/mediarouter/mediarouter/src/main/res/values-kk/strings.xml
index 60e465c..5413c59 100644
--- a/mediarouter/mediarouter/src/main/res/values-kk/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-kk/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Басқа құрылғы осы құрылғымен бірдей Wi-Fi желісінде екеніне көз жеткізіңіз."</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Толық ақпарат"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Трансляциялау жолы"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Белгісіз"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Теледидар"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Динамик"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-km/strings.xml b/mediarouter/mediarouter/src/main/res/values-km/strings.xml
index a85983a..e8ae21e 100644
--- a/mediarouter/mediarouter/src/main/res/values-km/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-km/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"សូមប្រាកដថា ឧបករណ៍ផ្សេងទៀតស្ថិតនៅលើបណ្ដាញ Wi-Fi តែមួយជាមួយនឹងឧបករណ៍នេះ"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ស្វែងយល់បន្ថែម"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ស្វែងយល់ពីរបៀបខាស"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"មិនស្គាល់"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ប៊្លូធូស"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ទូរទស្សន៍"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ឧបករណ៍​បំពង​សំឡេង"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-kn/strings.xml b/mediarouter/mediarouter/src/main/res/values-kn/strings.xml
index 8c945f6..0987a03 100644
--- a/mediarouter/mediarouter/src/main/res/values-kn/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-kn/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ಇನ್ನೊಂದು ಸಾಧನವು ಈ ಸಾಧನದಲ್ಲಿ ಇರುವಂತಹ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ನಲ್ಲೇ ಇದೆ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ಬಿತ್ತರಿಸುವುದು ಹೇಗೆಂದು ತಿಳಿಯಿರಿ"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"ಅಪರಿಚಿತ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ಬ್ಲೂಟೂತ್"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ಟಿವಿ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ಸ್ಪೀಕರ್"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ko/strings.xml b/mediarouter/mediarouter/src/main/res/values-ko/strings.xml
index 4ddc419..d87e564 100644
--- a/mediarouter/mediarouter/src/main/res/values-ko/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ko/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"다른 기기가 이 기기와 동일한 Wi-Fi 네트워크에 연결되어 있어야 합니다."</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"자세히 알아보기"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"전송 방법 알아보기"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"알 수 없음"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"블루투스"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"스피커"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ky/strings.xml b/mediarouter/mediarouter/src/main/res/values-ky/strings.xml
index f1eadfb..b6e27b2 100644
--- a/mediarouter/mediarouter/src/main/res/values-ky/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ky/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Башка түзмөк бул түзмөк менен бир Wi-Fi тармагына туташып турушу керек"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Кеңири маалымат"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Тышкы экранга чыгарууну үйрөнүү"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Белгисиз"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Сыналгы"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Динамик"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-lo/strings.xml b/mediarouter/mediarouter/src/main/res/values-lo/strings.xml
index bfb2968..fa7bf30 100644
--- a/mediarouter/mediarouter/src/main/res/values-lo/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-lo/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ກວດສອບວ່າອຸປະກອນອື່ນໃຊ້ເຄືອຂ່າຍ Wi-Fi ດຽວກັນກັບອຸປະກອນນີ້"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ສຶກສາເພີ່ມເຕີມ"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ສຶກສາວິທີສົ່ງສັນຍານ"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"ບໍ່ຮູ້ຈັກ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ໂທລະທັດ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ລຳໂພງ"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-lt/strings.xml b/mediarouter/mediarouter/src/main/res/values-lt/strings.xml
index fc8e784..04de836 100644
--- a/mediarouter/mediarouter/src/main/res/values-lt/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-lt/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Patikrinkite, ar kitas įrenginys prijungtas prie to paties „Wi-Fi“ tinklo kaip šis įrenginys"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Sužinokite daugiau"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Sužinokite, kaip perduoti"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Nežinoma"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Garsiakalbis"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-lv/strings.xml b/mediarouter/mediarouter/src/main/res/values-lv/strings.xml
index 6fc17cf..b4803a1 100644
--- a/mediarouter/mediarouter/src/main/res/values-lv/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-lv/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Otrai ierīcei ir jābūt savienotai ar to pašu Wi-Fi tīklu, ar kuru ir izveidots savienojums šajā ierīcē."</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Uzzināt vairāk"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Uzziniet, kā veikt apraidi"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Nav zināms"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Skaļrunis"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-mk/strings.xml b/mediarouter/mediarouter/src/main/res/values-mk/strings.xml
index 3c38d72..e777c8f 100644
--- a/mediarouter/mediarouter/src/main/res/values-mk/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-mk/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Погрижете се другиот уред да биде поврзан на истата Wi-Fi мрежа како овој уред"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Дознајте повеќе"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Дознајте како да емитувате"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Непознато"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ТВ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Звучник"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ml/strings.xml b/mediarouter/mediarouter/src/main/res/values-ml/strings.xml
index 5b9076e..afc78b2 100644
--- a/mediarouter/mediarouter/src/main/res/values-ml/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ml/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ഈ ഉപകരണം കണക്‌റ്റ് ചെയ്‌തിരിക്കുന്ന അതേ വൈഫൈ നെറ്റ്‌വർക്കിലാണ് മറ്റേ ഉപകരണവും കണക്‌റ്റ് ചെയ്‌തിട്ടുള്ളതെന്ന് ഉറപ്പാക്കുക"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"കൂടുതലറിയുക"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"എങ്ങനെ കാസ്‌റ്റുചെയ്യണമെന്ന് അറിയുക"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"അജ്ഞാതം"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ടിവി"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"സ്പീക്കർ"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-mn/strings.xml b/mediarouter/mediarouter/src/main/res/values-mn/strings.xml
index f678337..278185b 100644
--- a/mediarouter/mediarouter/src/main/res/values-mn/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-mn/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Нөгөө төхөөрөмж энэ төхөөрөмжтэй ижил Wi-Fi сүлжээнд байгаа болохыг баталгаажуулна уу"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Нэмэлт мэдээлэл авах"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Хэрхэн дамжуулахыг мэдэж авах"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Тодорхойгүй"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ТВ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Чанга яригч"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-mr/strings.xml b/mediarouter/mediarouter/src/main/res/values-mr/strings.xml
index 3e7e22d..47ac20a 100644
--- a/mediarouter/mediarouter/src/main/res/values-mr/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-mr/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"दुसरे डिव्हाइस हे डिव्हाइस आहे त्याच वाय-फाय नेटवर्कवर असल्याची खात्री करा"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"अधिक जाणून घ्या"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"कास्ट कसे करावे हे जाणून घ्या"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"अज्ञात"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ब्लूटूथ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"टीव्ही"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"स्पीकर"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ms/strings.xml b/mediarouter/mediarouter/src/main/res/values-ms/strings.xml
index b21e481..dbc360a 100644
--- a/mediarouter/mediarouter/src/main/res/values-ms/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ms/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Pastikan peranti lain disambungkan kepada rangkaian Wi-Fi yang sama dengan peranti ini"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Ketahui lebih lanjut"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Ketahui cara menghantar"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Tidak diketahui"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Pembesar suara"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-my/strings.xml b/mediarouter/mediarouter/src/main/res/values-my/strings.xml
index 8688caf..429a2a7 100644
--- a/mediarouter/mediarouter/src/main/res/values-my/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-my/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"အခြားစက်သည် ဤစက်နှင့် Wi-Fi ကွန်ရက်တစ်ခုတည်းတွင် ရှိနေကြောင်း သေချာပါစေ"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ပိုမိုလေ့လာရန်"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ကာစ်လုပ်ပုံကို လေ့လာရန်"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"မသိ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ဘလူးတုသ်"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"စပီကာ"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-nb/strings.xml b/mediarouter/mediarouter/src/main/res/values-nb/strings.xml
index 5d8725b..5616c07 100644
--- a/mediarouter/mediarouter/src/main/res/values-nb/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-nb/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Sørg for at den andre enheten er på det samme wifi-nettverket som denne enheten"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Finn ut mer"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Finn ut hvordan du caster"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Ukjent"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Høyttaler"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ne/strings.xml b/mediarouter/mediarouter/src/main/res/values-ne/strings.xml
index 7cdafb4..55dd9e1 100644
--- a/mediarouter/mediarouter/src/main/res/values-ne/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ne/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"यो डिभाइस जुन Wi-Fi नेटवर्कमा कनेक्ट गरिएको छ अर्को डिभाइस पनि उही नेटवर्कमा कनेक्ट गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"थप जान्नुहोस्"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"कास्ट गर्ने तरिका सिक्नुहोस्"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"अज्ञात"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ब्लुटुथ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"टिभी"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"स्पिकर"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-nl/strings.xml b/mediarouter/mediarouter/src/main/res/values-nl/strings.xml
index 73ddbd1..310fb82 100644
--- a/mediarouter/mediarouter/src/main/res/values-nl/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-nl/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Zorg dat het andere apparaat is verbonden met hetzelfde wifi-netwerk als dit apparaat"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Meer informatie"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Meer informatie over casten"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Onbekend"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Tv"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-or/strings.xml b/mediarouter/mediarouter/src/main/res/values-or/strings.xml
index 322adc9..37a9112 100644
--- a/mediarouter/mediarouter/src/main/res/values-or/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-or/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ଅନ୍ୟ ଡିଭାଇସ ଏହି ଡିଭାଇସ ପରି ସମାନ ୱାଇ-ଫାଇ ନେଟୱାର୍କରେ ଅଛି ବୋଲି ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ଅଧିକ ଜାଣନ୍ତୁ"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"କିପରି କାଷ୍ଟ କରିବେ ତାହା ଜାଣନ୍ତୁ"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"ଅଜଣା"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ବ୍ଲୁଟୁଥ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ଟିଭି"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ସ୍ପିକର"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-pa/strings.xml b/mediarouter/mediarouter/src/main/res/values-pa/strings.xml
index fe164cb..2149dac 100644
--- a/mediarouter/mediarouter/src/main/res/values-pa/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-pa/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ਪੱਕਾ ਕਰੋ ਕਿ ਦੂਜਾ ਡੀਵਾਈਸ ਉਸੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਹੈ, ਜਿਸ \'ਤੇ ਇਹ ਡੀਵਾਈਸ ਹੈ"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ਹੋਰ ਜਾਣੋ"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ਕਾਸਟ ਕਰਨ ਦਾ ਤਰੀਕਾ ਜਾਣੋ"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"ਅਗਿਆਤ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"ਬਲੂਟੁੱਥ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ਟੀਵੀ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ਸਪੀਕਰ"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-pl/strings.xml b/mediarouter/mediarouter/src/main/res/values-pl/strings.xml
index e967185..ced7b9e 100644
--- a/mediarouter/mediarouter/src/main/res/values-pl/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-pl/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Sprawdź, czy drugie urządzenie jest połączone z tą samą siecią Wi-Fi co to urządzenie"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Więcej informacji"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Dowiedz się, jak przesyłać treści"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Nieznane"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Telewizor"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Głośnik"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-pt-rBR/strings.xml b/mediarouter/mediarouter/src/main/res/values-pt-rBR/strings.xml
index b0b3b87..800afa1 100644
--- a/mediarouter/mediarouter/src/main/res/values-pt-rBR/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-pt-rBR/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Conecte o outro dispositivo à mesma rede Wi-Fi que este dispositivo"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Saiba mais"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Aprenda a transmitir conteúdo"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Desconhecido"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Alto-falante"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-pt-rPT/strings.xml b/mediarouter/mediarouter/src/main/res/values-pt-rPT/strings.xml
index 132dc35..04432e5 100644
--- a/mediarouter/mediarouter/src/main/res/values-pt-rPT/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-pt-rPT/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Certifique-se de que o outro dispositivo se encontra na mesma rede Wi-Fi que este dispositivo"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Saiba mais"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Saiba como transmitir"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Desconhecido"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Altifalante"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-pt/strings.xml b/mediarouter/mediarouter/src/main/res/values-pt/strings.xml
index b0b3b87..800afa1 100644
--- a/mediarouter/mediarouter/src/main/res/values-pt/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-pt/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Conecte o outro dispositivo à mesma rede Wi-Fi que este dispositivo"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Saiba mais"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Aprenda a transmitir conteúdo"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Desconhecido"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Alto-falante"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ro/strings.xml b/mediarouter/mediarouter/src/main/res/values-ro/strings.xml
index ee059b9..6c48c30 100644
--- a/mediarouter/mediarouter/src/main/res/values-ro/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ro/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Verifică dacă celălalt dispozitiv este în aceeași rețea Wi-Fi ca acest dispozitiv"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Află mai multe"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Află cum să proiectezi"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Necunoscut"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Televizor"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Difuzor"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ru/strings.xml b/mediarouter/mediarouter/src/main/res/values-ru/strings.xml
index c7ed5a3..43844f8 100644
--- a/mediarouter/mediarouter/src/main/res/values-ru/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ru/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Убедитесь, что другое устройство подключено к той же сети Wi-Fi, что и это устройство"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Подробнее…"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Как начать трансляцию"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Неизвестно"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Телевизор"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Динамик"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-si/strings.xml b/mediarouter/mediarouter/src/main/res/values-si/strings.xml
index cd04cafb..b5f13e1 100644
--- a/mediarouter/mediarouter/src/main/res/values-si/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-si/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"අනෙක් උපාංගය මෙම උපාංගය මෙන් එකම Wi-Fi ජාලයෙහි ඇති බවට වග බලා ගන්න"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"තව දැන ගන්න"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"විකාශය කරන්නේ කෙසේ දැයි දැන ගන්න"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"නොදනී"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"බ්ලූටූත්"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"රූපවාහිනිය"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ස්පීකරය"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-sk/strings.xml b/mediarouter/mediarouter/src/main/res/values-sk/strings.xml
index e747250..8a7e009 100644
--- a/mediarouter/mediarouter/src/main/res/values-sk/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-sk/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Skontrolujte, či je vaše druhé zariadenie pripojené k rovnakej sieti Wi‑Fi ako toto zariadenie"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Ďalšie informácie"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Pokyny na prenos"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Neznáme"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Televízor"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Reproduktor"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-sl/strings.xml b/mediarouter/mediarouter/src/main/res/values-sl/strings.xml
index df9efe1..8b9634e 100644
--- a/mediarouter/mediarouter/src/main/res/values-sl/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-sl/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Prepričajte se, da je druga naprava povezana v isto omrežje Wi-Fi kot ta naprava"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Več o tem"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Več o tem, kako predvajati"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Neznano"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Televizor"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Zvočnik"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-sq/strings.xml b/mediarouter/mediarouter/src/main/res/values-sq/strings.xml
index 2eb6522..512e009 100644
--- a/mediarouter/mediarouter/src/main/res/values-sq/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-sq/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Sigurohu që pajisja tjetër të jetë në të njëjtin rrjet Wi-Fi me këtë pajisje"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Mëso më shumë"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Mëso si të transmetosh"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"I panjohur"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Televizori"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Altoparlanti"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-sr/strings.xml b/mediarouter/mediarouter/src/main/res/values-sr/strings.xml
index 9a14bac..e6b2a25 100644
--- a/mediarouter/mediarouter/src/main/res/values-sr/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-sr/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Уверите се да је други уређај повезан на исту WiFi мрежу као овај уређај"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Сазнајте више"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Сазнајте како да пребацујете"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Непознато"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ТВ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Звучник"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-sv/strings.xml b/mediarouter/mediarouter/src/main/res/values-sv/strings.xml
index 01737c5..5dbf580 100644
--- a/mediarouter/mediarouter/src/main/res/values-sv/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-sv/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Kontrollera att den andra enheten är ansluten till samma wifi-nätverk som denna enhet"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Läs mer"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Så castar du"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Okänd"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Tv"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Högtalare"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-sw/strings.xml b/mediarouter/mediarouter/src/main/res/values-sw/strings.xml
index 7e564d6..c0c47f6 100644
--- a/mediarouter/mediarouter/src/main/res/values-sw/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-sw/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Hakikisha kifaa hicho kingine kimeunganishwa kwenye mtandao wa Wi-Fi unaotumiwa na kifaa hiki"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Pata maelezo zaidi"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Jifunze jinsi ta kutuma"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Haijulikani"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Spika"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ta/strings.xml b/mediarouter/mediarouter/src/main/res/values-ta/strings.xml
index 6894b6e..fb2c60e 100644
--- a/mediarouter/mediarouter/src/main/res/values-ta/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ta/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"இந்தச் சாதனம் இணைக்கப்பட்டுள்ள அதே வைஃபை நெட்வொர்க்கில் மற்றொரு சாதனமும் இணைக்கப்பட்டுள்ளதை உறுதிசெய்துகொள்ளுங்கள்"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"மேலும் அறிக"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"அலைபரப்புவதற்கான வழிமுறையை அறிக"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"தெரியாதவை"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"புளூடூத்"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"டிவி"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ஸ்பீக்கர்"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-te/strings.xml b/mediarouter/mediarouter/src/main/res/values-te/strings.xml
index 85ae80a..cc2df86 100644
--- a/mediarouter/mediarouter/src/main/res/values-te/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-te/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"మరొక పరికరం ఈ పరికరం లాగా అదే Wi-Fi నెట్‌వర్క్‌లో ఉందని నిర్ధారించుకోండి"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"మరింత తెలుసుకోండి"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ఎలా ప్రసారం చేయాలో తెలుసుకోండి"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"తెలియదు"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"బ్లూటూత్"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"టీవీ"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"స్పీకర్"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-th/strings.xml b/mediarouter/mediarouter/src/main/res/values-th/strings.xml
index 359653b..0993259 100644
--- a/mediarouter/mediarouter/src/main/res/values-th/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-th/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"ตรวจสอบว่าอุปกรณ์อื่นๆ อยู่ในเครือข่าย Wi-Fi เดียวกันกับอุปกรณ์เครื่องนี้"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"ดูข้อมูลเพิ่มเติม"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"ดูวิธีแคสต์"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"ไม่ทราบ"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"บลูทูธ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"ทีวี"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"ลำโพง"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-tl/strings.xml b/mediarouter/mediarouter/src/main/res/values-tl/strings.xml
index 5ae0c47..0638f34 100644
--- a/mediarouter/mediarouter/src/main/res/values-tl/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-tl/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Tiyaking nasa iisang Wi-Fi network ang ibang device at ang device na ito"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Matuto pa"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Matutunan kung paano mag-cast"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Hindi alam"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Speaker"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-tr/strings.xml b/mediarouter/mediarouter/src/main/res/values-tr/strings.xml
index de21a62..b4dd51e 100644
--- a/mediarouter/mediarouter/src/main/res/values-tr/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-tr/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Diğer cihazın bu cihaz ile aynı kablosuz ağa bağlandığından emin olun"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Daha fazla bilgi"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Nasıl yayınlayacağınızı öğrenin"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Bilinmiyor"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Hoparlör"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-uk/strings.xml b/mediarouter/mediarouter/src/main/res/values-uk/strings.xml
index c917313..2dc1d85 100644
--- a/mediarouter/mediarouter/src/main/res/values-uk/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-uk/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Переконайтеся, що інший пристрій підключено до тієї самої мережі Wi-Fi, що й цей пристрій"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Докладніше"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Дізнайтеся, як транслювати контент"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Невідомо"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"Телевізор"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Колонка"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-ur/strings.xml b/mediarouter/mediarouter/src/main/res/values-ur/strings.xml
index 473a54a..dfda68b 100644
--- a/mediarouter/mediarouter/src/main/res/values-ur/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-ur/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"‏یقینی بنائیں کہ دوسرا آلہ اسی Wi-Fi نیٹ ورک پر ہے جس پر یہ آلہ ہے"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"مزید جانیں"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"کاسٹ کرنے کا طریقہ جانیں"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"نامعلوم"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"بلوٹوتھ"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"اسپیکر"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-uz/strings.xml b/mediarouter/mediarouter/src/main/res/values-uz/strings.xml
index 2e11e09..4cd57ba 100644
--- a/mediarouter/mediarouter/src/main/res/values-uz/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-uz/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Boshqa qurilma bu qurilma bilan bir xil Wi-Fi tarmoqda ekaniga ishonch hosil qiling"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Batafsil"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Qanday translatsiya qilinadi"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Noaniq"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Karnay"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-vi/strings.xml b/mediarouter/mediarouter/src/main/res/values-vi/strings.xml
index 68322745..9293fe2 100644
--- a/mediarouter/mediarouter/src/main/res/values-vi/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-vi/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Đảm bảo thiết bị kia và thiết bị này kết nối với cùng một mạng Wi-Fi"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Tìm hiểu thêm"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Tìm hiểu cách truyền"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Không xác định"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"Bluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Loa"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml b/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml
index d6f9784..4d5ffc5 100644
--- a/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-zh-rCN/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"确保另一设备与此设备连接到同一 WLAN 网络"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"了解详情"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"了解如何投放"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"未知"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"蓝牙"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"电视"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"音箱"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-zh-rHK/strings.xml b/mediarouter/mediarouter/src/main/res/values-zh-rHK/strings.xml
index fc7761b..8ad20b9 100644
--- a/mediarouter/mediarouter/src/main/res/values-zh-rHK/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-zh-rHK/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"請確保另一部裝置與此裝置連接同一 Wi-Fi 網絡"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"瞭解詳情"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"瞭解投放方式"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"不明"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"藍牙"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"電視"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"喇叭"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-zh-rTW/strings.xml b/mediarouter/mediarouter/src/main/res/values-zh-rTW/strings.xml
index f945a62..f400c48 100644
--- a/mediarouter/mediarouter/src/main/res/values-zh-rTW/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-zh-rTW/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"確認其他裝置與這部裝置連上同一個 Wi-Fi 網路"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"瞭解詳情"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"瞭解如何投放"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"不明"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"藍牙"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"電視"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"喇叭"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values-zu/strings.xml b/mediarouter/mediarouter/src/main/res/values-zu/strings.xml
index b4bf614..8fa5369 100644
--- a/mediarouter/mediarouter/src/main/res/values-zu/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values-zu/strings.xml
@@ -51,4 +51,8 @@
     <string name="mr_chooser_wifi_warning_description_unknown" msgid="3459891599800041449">"Qinisekisa ukuthi enye idivayisi ikunethiwekhi efanayo ye-Wi-Fi nje ngale divayisi"</string>
     <string name="mr_chooser_wifi_learn_more" msgid="3799500840179081429"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">"Funda kabanzi"</a></string>
     <string name="ic_media_route_learn_more_accessibility" msgid="9119039724000326934">"Funda indlela yokusakaza"</string>
+    <string name="mr_route_name_unknown" msgid="5538521943939635302">"Akwaziwa"</string>
+    <string name="mr_route_name_bluetooth" msgid="5056346328175584455">"IBluetooth"</string>
+    <string name="mr_route_name_tv" msgid="8041420425123528188">"I-TV"</string>
+    <string name="mr_route_name_speaker" msgid="708574147123374685">"Isipikha"</string>
 </resources>
diff --git a/mediarouter/mediarouter/src/main/res/values/strings.xml b/mediarouter/mediarouter/src/main/res/values/strings.xml
index 7cb83b0a6..3b7cb26 100644
--- a/mediarouter/mediarouter/src/main/res/values/strings.xml
+++ b/mediarouter/mediarouter/src/main/res/values/strings.xml
@@ -112,4 +112,17 @@
     <!-- Text displayed to link the user to the support article. [CHAR_LIMIT=NONE] -->
     <string name="mr_chooser_wifi_learn_more"><a href="https://support.google.com/chromecast/?p=trouble-finding-devices">Learn more</a></string>
     <string name="ic_media_route_learn_more_accessibility">Learn how to cast</string>
+
+    <!-- Name of a route whose type is unknown [CHAR LIMIT=25] -->
+    <string name="mr_route_name_unknown">Unknown</string>
+
+    <!-- Name of a route whose type is bluetooth [CHAR LIMIT=25] -->
+    <string name="mr_route_name_bluetooth">Bluetooth</string>
+
+    <!-- Name of a route whose type is TV [CHAR LIMIT=25] -->
+    <string name="mr_route_name_tv">TV</string>
+
+    <!-- Name of a route whose type is speaker [CHAR LIMIT=25] -->
+    <string name="mr_route_name_speaker">Speaker</string>
+
 </resources>
diff --git a/metrics/integration-tests/janktest/build.gradle b/metrics/integration-tests/janktest/build.gradle
index da13b35..90a1980 100644
--- a/metrics/integration-tests/janktest/build.gradle
+++ b/metrics/integration-tests/janktest/build.gradle
@@ -24,6 +24,7 @@
     buildFeatures {
         viewBinding true
     }
+    compileSdk 35
     namespace "androidx.metrics.performance.janktest"
 }
 
diff --git a/metrics/metrics-benchmark/build.gradle b/metrics/metrics-benchmark/build.gradle
index 7a1d47b..9246ef6 100644
--- a/metrics/metrics-benchmark/build.gradle
+++ b/metrics/metrics-benchmark/build.gradle
@@ -33,6 +33,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.metrics.performance.benchmark"
     defaultConfig {
         // Enable measuring on an emulator, devices with low battery, and eng builds
diff --git a/metrics/metrics-performance/build.gradle b/metrics/metrics-performance/build.gradle
index 5a7c352..d7925e7 100644
--- a/metrics/metrics-performance/build.gradle
+++ b/metrics/metrics-performance/build.gradle
@@ -56,6 +56,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.metrics.performance"
 }
 
@@ -64,6 +65,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Library for tracking and reporting various runtime metrics for applications"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/navigation/integration-tests/testapp/lint-baseline.xml b/navigation/integration-tests/testapp/lint-baseline.xml
index b1b8eb9..ef3607d 100644
--- a/navigation/integration-tests/testapp/lint-baseline.xml
+++ b/navigation/integration-tests/testapp/lint-baseline.xml
@@ -1,23 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.navigation.testapp.AndroidFragment is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                notificationManager.createNotificationChannel("
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/navigation/testapp/AndroidFragment.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.navigation.testapp.AndroidFragment is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                    NotificationChannel("
-        errorLine2="                    ^">
-        <location
-            file="src/main/java/androidx/navigation/testapp/AndroidFragment.kt"/>
-    </issue>
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ObsoleteSdkInt"
diff --git a/navigation/navigation-benchmark/lint-baseline.xml b/navigation/navigation-benchmark/lint-baseline.xml
index a59f98e..bb41c27 100644
--- a/navigation/navigation-benchmark/lint-baseline.xml
+++ b/navigation/navigation-benchmark/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-alpha05)" variant="all" version="8.0.0-alpha05">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="MissingTestSizeAnnotation"
         message="Missing test size annotation"
-        errorLine1="    fun navGraphDestinations_withRoutes() = inflateNavGraph_withRoutes(1)"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    @Test fun navGraphDestinations_withRoutes() = inflateNavGraph_withRoutes(1)"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidTest/java/androidx/navigation/NavDeepLinkBenchmark.kt"/>
     </issue>
@@ -13,8 +13,8 @@
     <issue
         id="MissingTestSizeAnnotation"
         message="Missing test size annotation"
-        errorLine1="    fun navGraphDestinations_withRoutes10() = inflateNavGraph_withRoutes(10)"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    @Test fun navGraphDestinations_withRoutes10() = inflateNavGraph_withRoutes(10)"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidTest/java/androidx/navigation/NavDeepLinkBenchmark.kt"/>
     </issue>
@@ -22,8 +22,8 @@
     <issue
         id="MissingTestSizeAnnotation"
         message="Missing test size annotation"
-        errorLine1="    fun navGraphDestinations_withRoutes50() = inflateNavGraph_withRoutes(50)"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    @Test fun navGraphDestinations_withRoutes50() = inflateNavGraph_withRoutes(50)"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidTest/java/androidx/navigation/NavDeepLinkBenchmark.kt"/>
     </issue>
@@ -31,8 +31,8 @@
     <issue
         id="MissingTestSizeAnnotation"
         message="Missing test size annotation"
-        errorLine1="    fun navGraphDestinations_withRoutes100() = inflateNavGraph_withRoutes(100)"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="    @Test fun navGraphDestinations_withRoutes100() = inflateNavGraph_withRoutes(100)"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidTest/java/androidx/navigation/NavDeepLinkBenchmark.kt"/>
     </issue>
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-common-ktx/api/2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-common-ktx/api/2.8.0-beta06.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-common-ktx/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-common-ktx/api/res-2.8.0-beta06.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-common-ktx/api/restricted_2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-common-ktx/api/restricted_2.8.0-beta06.txt
diff --git a/navigation/navigation-common-ktx/build.gradle b/navigation/navigation-common-ktx/build.gradle
index 142ea22..50189b1 100644
--- a/navigation/navigation-common-ktx/build.gradle
+++ b/navigation/navigation-common-ktx/build.gradle
@@ -43,7 +43,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Android Navigation-Common-Ktx"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/navigation/navigation-common-lint/build.gradle b/navigation/navigation-common-lint/build.gradle
index 8521b45..d1f2801 100644
--- a/navigation/navigation-common-lint/build.gradle
+++ b/navigation/navigation-common-lint/build.gradle
@@ -21,6 +21,8 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
+
+import androidx.build.BundleInsideHelper
 import androidx.build.LibraryType
 
 plugins {
@@ -28,9 +30,12 @@
     id("kotlin")
 }
 
+BundleInsideHelper.forInsideLintJar(project)
+
 dependencies {
     compileOnly(libs.kotlinStdlib)
     compileOnly(libs.androidLintApi)
+    bundleInside(projectOrArtifact(":navigation:navigation-lint-common"))
 
     testImplementation(libs.kotlinStdlib)
     testImplementation(libs.androidLint)
diff --git a/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/BaseWrongStartDestinationTypeDetector.kt b/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/BaseWrongStartDestinationTypeDetector.kt
deleted file mode 100644
index ffa3358..0000000
--- a/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/BaseWrongStartDestinationTypeDetector.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.navigation.common.lint
-
-import com.android.tools.lint.detector.api.Category
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Implementation
-import com.android.tools.lint.detector.api.Issue
-import com.android.tools.lint.detector.api.JavaContext
-import com.android.tools.lint.detector.api.Scope
-import com.android.tools.lint.detector.api.Severity
-import com.android.tools.lint.detector.api.SourceCodeScanner
-import com.intellij.psi.PsiMethod
-import org.jetbrains.uast.UCallExpression
-import org.jetbrains.uast.UElement
-import org.jetbrains.uast.getParameterForArgument
-
-abstract class BaseWrongStartDestinationTypeDetector(
-    private val methodNames: List<String>,
-    private val parameterNames: List<String>
-) : Detector(), SourceCodeScanner {
-
-    final override fun getApplicableMethodNames(): List<String> = methodNames
-
-    final override fun visitMethodCall(
-        context: JavaContext,
-        node: UCallExpression,
-        method: PsiMethod
-    ) {
-        val startNode =
-            node.valueArguments.find {
-                parameterNames.contains(node.getParameterForArgument(it)?.name)
-            } ?: return
-
-        val (isClassType, name) = startNode.isClassReference()
-        if (isClassType) {
-            context.report(
-                getIssue(),
-                startNode,
-                context.getNameLocation(startNode as UElement),
-                """
-                        StartDestination should not be a simple class name reference.
-                        Did you mean to call its constructor $name(...)?
-                        If the class $name does not contain arguments,
-                        you can also pass in its KClass reference $name::class
-                    """
-                    .trimIndent()
-            )
-        }
-    }
-
-    abstract fun getIssue(): Issue
-}
-
-fun createWrongStartDestinationTypeIssue(
-    detectorClass: Class<out BaseWrongStartDestinationTypeDetector>
-) =
-    Issue.create(
-        id = "WrongStartDestinationType",
-        briefDescription =
-            "If the startDestination points to a Class with arguments, the " +
-                "startDestination must be an instance of that class. If it points to a " +
-                "Class without arguments, startDestination can be a KClass literal, such as" +
-                " StartClass::class.",
-        explanation =
-            "If the startDestination contains arguments, the arguments must be " +
-                "provided to navigation via a fully formed route (a class instance with arguments" +
-                "filled in), or else it will be treated as a case of missing arguments.",
-        category = Category.CORRECTNESS,
-        severity = Severity.ERROR,
-        implementation = Implementation(detectorClass, Scope.JAVA_FILE_SCOPE)
-    )
diff --git a/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/LintUtil.kt b/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/LintUtil.kt
deleted file mode 100644
index d393265..0000000
--- a/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/LintUtil.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.navigation.common.lint
-
-import org.jetbrains.kotlin.analysis.api.analyze
-import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
-import org.jetbrains.kotlin.idea.references.mainReference
-import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
-import org.jetbrains.kotlin.psi.KtExpression
-import org.jetbrains.kotlin.psi.KtReferenceExpression
-import org.jetbrains.uast.UExpression
-import org.jetbrains.uast.UQualifiedReferenceExpression
-import org.jetbrains.uast.USimpleNameReferenceExpression
-
-/** Catches simple class/interface name reference */
-fun UExpression.isClassReference(): Pair<Boolean, String?> {
-    /**
-     * True if:
-     * 1. reference to object (i.e. val myStart = TestStart(), startDest = myStart)
-     * 2. object declaration (i.e. object MyStart, startDest = MyStart)
-     * 3. class reference (i.e. class MyStart, startDest = MyStart)
-     *
-     *    We only want to catch case 3., so we need more filters to eliminate case 1 & 2.
-     */
-    val isSimpleRefExpression = this is USimpleNameReferenceExpression
-
-    /** True if nested class i.e. OuterClass.InnerClass */
-    val isQualifiedRefExpression = this is UQualifiedReferenceExpression
-
-    if (!(isSimpleRefExpression || isQualifiedRefExpression)) return false to null
-
-    val sourcePsi = sourcePsi as? KtExpression ?: return false to null
-    return analyze(sourcePsi) {
-        val symbol =
-            when (sourcePsi) {
-                is KtDotQualifiedExpression -> {
-                    val lastChild = sourcePsi.lastChild
-                    if (lastChild is KtReferenceExpression) {
-                        lastChild.mainReference.resolveToSymbol()
-                    } else {
-                        null
-                    }
-                }
-                is KtReferenceExpression -> sourcePsi.mainReference.resolveToSymbol()
-                else -> null
-            }
-                as? KtClassOrObjectSymbol ?: return false to null
-
-        (symbol.classKind.isClass || symbol.classKind.name == "INTERFACE") to
-            symbol.name?.asString()
-    }
-}
diff --git a/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetector.kt b/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetector.kt
index 3b0403c..fec9b9f 100644
--- a/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetector.kt
+++ b/navigation/navigation-common-lint/src/main/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetector.kt
@@ -16,6 +16,9 @@
 
 package androidx.navigation.common.lint
 
+import androidx.navigation.lint.common.BaseWrongStartDestinationTypeDetector
+import androidx.navigation.lint.common.createWrongStartDestinationTypeIssue
+
 class WrongStartDestinationTypeDetector :
     BaseWrongStartDestinationTypeDetector(
         methodNames = listOf("navigation", "setStartDestination"),
diff --git a/navigation/navigation-common-lint/src/test/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetectorTest.kt b/navigation/navigation-common-lint/src/test/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetectorTest.kt
index 647068d..5e1ca05 100644
--- a/navigation/navigation-common-lint/src/test/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetectorTest.kt
+++ b/navigation/navigation-common-lint/src/test/java/androidx/navigation/common/lint/WrongStartDestinationTypeDetectorTest.kt
@@ -17,12 +17,22 @@
 package androidx.navigation.common.lint
 
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.compiled
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
+import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.checks.infrastructure.TestMode
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 
-class WrongStartDestinationTypeDetectorTest : LintDetectorTest() {
+@RunWith(Parameterized::class)
+class WrongStartDestinationTypeDetectorTest(private val testFile: TestFile) : LintDetectorTest() {
+
+    private companion object {
+        @JvmStatic @Parameterized.Parameters public fun data() = listOf(SOURCECODE, BYTECODE)
+    }
 
     @Test
     fun testEmptyConstructorNoError() {
@@ -42,11 +52,14 @@
                     builder.navigation<TestGraph>(startDestination = TestClass())
                     provider.navigation(startDestination = TestClass()) {}
                     navGraph.setStartDestination(startDestRoute = TestClass())
+                    builder.navigation<TestGraph>(startDestination = TestClassComp())
+                    provider.navigation(startDestination = TestClassComp()) {}
+                    navGraph.setStartDestination(startDestRoute = TestClassComp())
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .skipTestModes(TestMode.FULLY_QUALIFIED)
             .run()
@@ -74,11 +87,17 @@
                     builder.navigation<TestGraph>(startDestination = Outer.InnerClass(15))
                     builder.navigation<TestGraph>(startDestination = InterfaceChildClass(true))
                     builder.navigation<TestGraph>(startDestination = AbstractChildClass(true))
+                    builder.navigation<TestGraph>(startDestination = TestClassWithArgComp(15))
+                    builder.navigation<TestGraph>(startDestination = TestClassComp::class)
+                    builder.navigation<TestGraph>(startDestination = OuterComp.InnerClassComp::class)
+                    builder.navigation<TestGraph>(startDestination = OuterComp.InnerClassComp(15))
+                    builder.navigation<TestGraph>(startDestination = InterfaceChildClassComp(true))
+                    builder.navigation<TestGraph>(startDestination = AbstractChildClassComp(true))
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -104,11 +123,13 @@
                     builder.navigation<TestGraph>(startDestination = Outer.InnerObject::class)
                     builder.navigation<TestGraph>(startDestination = InterfaceChildObject)
                     builder.navigation<TestGraph>(startDestination = AbstractChildObject)
+                    builder.navigation<TestGraph>(startDestination = OuterComp.InnerObject::class)
+                    builder.navigation<TestGraph>(startDestination = AbstractChildObjectComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -134,11 +155,18 @@
                     builder.navigation<TestGraph>(startDestination = AbstractChildClass)
                     builder.navigation<TestGraph>(startDestination = TestInterface)
                     builder.navigation<TestGraph>(startDestination = TestAbstract)
+                    // classes with companion object to simulate marked with @Serializable
+                    builder.navigation<TestGraph>(startDestination = TestClassComp)
+                    builder.navigation<TestGraph>(startDestination = TestClassWithArgComp)
+                    builder.navigation<TestGraph>(startDestination = OuterComp.InnerClassComp)
+                    builder.navigation<TestGraph>(startDestination = InterfaceChildClassComp)
+                    builder.navigation<TestGraph>(startDestination = AbstractChildClassComp)
+                    builder.navigation<TestGraph>(startDestination = TestAbstractComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expect(
@@ -185,7 +213,43 @@
 you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
     builder.navigation<TestGraph>(startDestination = TestAbstract)
                                                      ~~~~~~~~~~~~
-7 errors, 0 warnings
+src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    builder.navigation<TestGraph>(startDestination = TestClassComp)
+                                                     ~~~~~~~~~~~~~
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    builder.navigation<TestGraph>(startDestination = TestClassWithArgComp)
+                                                     ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    builder.navigation<TestGraph>(startDestination = OuterComp.InnerClassComp)
+                                                     ~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    builder.navigation<TestGraph>(startDestination = InterfaceChildClassComp)
+                                                     ~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:20: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    builder.navigation<TestGraph>(startDestination = AbstractChildClassComp)
+                                                     ~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:21: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    builder.navigation<TestGraph>(startDestination = TestAbstractComp)
+                                                     ~~~~~~~~~~~~~~~~
+13 errors, 0 warnings
             """
             )
     }
@@ -210,11 +274,17 @@
                     provider.navigation(startDestination = Outer.InnerClass(15)) {}
                     provider.navigation(startDestination = InterfaceChildClass(true)) {}
                     provider.navigation(startDestination = AbstractChildClass(true)) {}
+                    provider.navigation(startDestination = TestClassWithArgComp(15)) {}
+                    provider.navigation(startDestination = TestClassComp::class) {}
+                    provider.navigation(startDestination = OuterComp.InnerClassComp::class) {}
+                    provider.navigation(startDestination = OuterComp.InnerClassComp(15)) {}
+                    provider.navigation(startDestination = InterfaceChildClassComp(true)) {}
+                    provider.navigation(startDestination = AbstractChildClassComp(true)) {}
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -239,11 +309,14 @@
                     provider.navigation(startDestination = Outer.InnerObject::class) {}
                     provider.navigation(startDestination = InterfaceChildObject) {}
                     provider.navigation(startDestination = AbstractChildObject) {}
+                    provider.navigation(startDestination = OuterComp) {}
+                    provider.navigation(startDestination = OuterComp.InnerObject) {}
+                    provider.navigation(startDestination = OuterComp.InnerObject::class) {}
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -268,11 +341,17 @@
                     provider.navigation(startDestination = AbstractChildClass) {}
                     provider.navigation(startDestination = TestInterface)
                     provider.navigation(startDestination = TestAbstract)
+                    provider.navigation(startDestination = TestClassComp) {}
+                    provider.navigation(startDestination = TestClassWithArgComp) {}
+                    provider.navigation(startDestination = OuterComp.InnerClassComp) {}
+                    provider.navigation(startDestination = InterfaceChildClassComp) {}
+                    provider.navigation(startDestination = AbstractChildClassComp) {}
+                    provider.navigation(startDestination = TestAbstractComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expect(
@@ -319,7 +398,43 @@
 you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
     provider.navigation(startDestination = TestAbstract)
                                            ~~~~~~~~~~~~
-7 errors, 0 warnings
+src/com/example/test.kt:14: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    provider.navigation(startDestination = TestClassComp) {}
+                                           ~~~~~~~~~~~~~
+src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    provider.navigation(startDestination = TestClassWithArgComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    provider.navigation(startDestination = OuterComp.InnerClassComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    provider.navigation(startDestination = InterfaceChildClassComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    provider.navigation(startDestination = AbstractChildClassComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    provider.navigation(startDestination = TestAbstractComp)
+                                           ~~~~~~~~~~~~~~~~
+13 errors, 0 warnings
             """
             )
     }
@@ -344,11 +459,17 @@
                     navGraph.setStartDestination(startDestRoute = Outer.InnerClass(15))
                     navGraph.setStartDestination(startDestRoute = InterfaceChildClass(true))
                     navGraph.setStartDestination(startDestRoute = AbstractChildClass(true))
+                    navGraph.setStartDestination(startDestRoute = TestClassWithArgComp(15))
+                    navGraph.setStartDestination(startDestRoute = TestClassComp::class)
+                    navGraph.setStartDestination(startDestRoute = OuterComp.InnerClassComp::class)
+                    navGraph.setStartDestination(startDestRoute = OuterComp.InnerClassComp(15))
+                    navGraph.setStartDestination(startDestRoute = InterfaceChildClassComp(true))
+                    navGraph.setStartDestination(startDestRoute = AbstractChildClassComp(true))
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -373,11 +494,13 @@
                     navGraph.setStartDestination(startDestRoute = Outer.InnerObject::class)
                     navGraph.setStartDestination(startDestRoute = InterfaceChildObject)
                     navGraph.setStartDestination(startDestRoute = AbstractChildObject)
+                    navGraph.setStartDestination(startDestRoute = OuterComp.InnerObject::class)
+                    navGraph.setStartDestination(startDestRoute = AbstractChildObjectComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -401,12 +524,19 @@
                     navGraph.setStartDestination(startDestRoute = InterfaceChildClass)
                     navGraph.setStartDestination(startDestRoute = AbstractChildClass)
                     navGraph.setStartDestination(startDestRoute = TestInterface)
-                    navGraph.setStartDestination(startDestRoute = InterfaceChildClass)
+                    navGraph.setStartDestination(startDestRoute = TestAbstract)
+                    // classes with companion object to simulate marked with @Serializable
+                    navGraph.setStartDestination(startDestRoute = TestClassComp)
+                    navGraph.setStartDestination(startDestRoute = TestClassWithArgComp)
+                    navGraph.setStartDestination(startDestRoute = OuterComp.InnerClassComp)
+                    navGraph.setStartDestination(startDestRoute = InterfaceChildClassComp)
+                    navGraph.setStartDestination(startDestRoute = AbstractChildClassComp)
+                    navGraph.setStartDestination(startDestRoute = TestAbstractComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expect(
@@ -448,18 +578,61 @@
     navGraph.setStartDestination(startDestRoute = TestInterface)
                                                   ~~~~~~~~~~~~~
 src/com/example/test.kt:13: Error: StartDestination should not be a simple class name reference.
-Did you mean to call its constructor InterfaceChildClass(...)?
-If the class InterfaceChildClass does not contain arguments,
-you can also pass in its KClass reference InterfaceChildClass::class [WrongStartDestinationType]
-    navGraph.setStartDestination(startDestRoute = InterfaceChildClass)
-                                                  ~~~~~~~~~~~~~~~~~~~
-7 errors, 0 warnings
+Did you mean to call its constructor TestAbstract(...)?
+If the class TestAbstract does not contain arguments,
+you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
+    navGraph.setStartDestination(startDestRoute = TestAbstract)
+                                                  ~~~~~~~~~~~~
+src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navGraph.setStartDestination(startDestRoute = TestClassComp)
+                                                  ~~~~~~~~~~~~~
+src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navGraph.setStartDestination(startDestRoute = TestClassWithArgComp)
+                                                  ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navGraph.setStartDestination(startDestRoute = OuterComp.InnerClassComp)
+                                                  ~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navGraph.setStartDestination(startDestRoute = InterfaceChildClassComp)
+                                                  ~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navGraph.setStartDestination(startDestRoute = AbstractChildClassComp)
+                                                  ~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:20: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navGraph.setStartDestination(startDestRoute = TestAbstractComp)
+                                                  ~~~~~~~~~~~~~~~~
+13 errors, 0 warnings
             """
             )
     }
 
-    private val sourceCode =
-        """
+    override fun getDetector(): Detector = WrongStartDestinationTypeDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(WrongStartDestinationTypeDetector.WrongStartDestinationType)
+}
+
+private val SOURCECODE =
+    kotlin(
+            """
 package androidx.navigation
 
 import kotlin.reflect.KClass
@@ -496,6 +669,7 @@
     builder: NavGraphBuilder.() -> Unit
 ): NavGraph = NavGraph()
 
+// source code for classes
 val classInstanceRef = TestClass()
 
 val classInstanceWithArgRef = TestClassWithArg(15)
@@ -526,79 +700,180 @@
 class AbstractChildClass(val arg: Boolean): TestAbstract()
 object AbstractChildObject: TestAbstract()
 
-"""
+// classes with companion object to simulate classes marked with @Serializable
+class TestClassComp { companion object }
 
-    // Stub
-    private val byteCode =
-        compiled(
-            "libs/StartDestinationLint.jar",
-            kotlin(sourceCode).indented(),
-            0x8f4dea5e,
-            """
+class TestClassWithArgComp(val arg: Int) { companion object }
+
+object OuterComp {
+    data object InnerObject
+
+    data class InnerClassComp (
+        val innerArg: Int,
+    ) { companion object }
+}
+
+class InterfaceChildClassComp(val arg: Boolean): TestInterface { companion object }
+
+abstract class TestAbstractComp { companion object }
+class AbstractChildClassComp(val arg: Boolean): TestAbstractComp() { companion object }
+object AbstractChildObjectComp: TestAbstractComp()
+"""
+        )
+        .indented()
+
+// Stub
+private val BYTECODE =
+    compiled(
+        "libs/StartDestinationLint.jar",
+        SOURCECODE,
+        0xbfec49d0,
+        """
                 META-INF/main.kotlin_module:
                 H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgUuQSTsxLKcrPTKnQy0ssy0xPLMnM
                 zxPi8ksscy9KLMjwLuES5eJOzs/VS61IzC3ISRViC0ktLvEuUWLQYgAAL/WN
                 klIAAAA=
                 """,
-            """
+        """
                 androidx/navigation/AbstractChildClass.class:
-                H4sIAAAAAAAA/41Qz28SQRh9M7sssIAs+ItStbW2hnIQ2njTNFISDQn2UBsO
-                cBrYDUyAXbMzkB75Wzx7MdGYeDDEo3+U8ZulGg8cPMyb7828vO/73s9f374D
-                eI4jhqci9ONI+teNUCzlWGgZhY3WUOlYjHR7Imd+eyaUSoMx7G/TXgVK/9Gn
-                YTE4L2Uo9RmDXesf9xis2nEvjxTSLmxkiIt4zMD6ebjIZcGRJ6meSMVQ6/7f
-                NC+oyzjQLWNE9n2GUnca6ZkMG28DLXyhBUn4fGnRmsxA1gCo7ZTer6VhTar8
-                E4bz9ars8gpPznrlci/n8oxVWa9OeZOdF8qOx6u8af344HDPviz9ZRlSV+1M
-                ynOM0ynDwdbx/w2IpqIhchdi+SYW7yfPppp2b0d+wFDsyjC4WMyHQXwlhjN6
-                KXejkZj1RCwNv3l030WLeBS8lobsXC5CLedBTypJv60wjHTSVOGEgrWTlcsm
-                Z6o41Sk4hPvEzohzut36V2Tru19Q+JRoHhMaDfAKB4T3NircQtEkSJVxo8Dh
-                0dl4NUywdKfqn1H4uNUmvxHc2HA8SXAPh4nCDJnC7QGsDu50cLdDbe9TiUoH
-                O6gOwBR28WCAtEJR4aGCq/BIwVHwFEq/ASCFXj3QAgAA
+                H4sIAAAAAAAA/41Qz28SQRT+ZnZZYAFZ8BelamttDeUgtPGmaaQkGhLsoTYc
+                4DSwG5gAu2ZnID3yt3j2YqIx8WCIR/8o45ulGg8cPMx773vz5XvvfT9/ffsO
+                4DmOGJ6K0I8j6V83QrGUY6FlFDZaQ6VjMdLtiZz57ZlQKg3GsL+NexUo/Yef
+                hsXgvJSh1GcMdq1/3GOwase9PFJIu7CRISziMQPr5+EilwVHnqh6IhVDrft/
+                27ygKeNAt4wQyfcZSt1ppGcybLwNtPCFFkTh86VFZzITsiaAxk6pfy0NalLl
+                nzCcr1dll1d48tYrl3s5l2esynp1ypvsvFB2PF7lTevHB4d79mXpL8oQu2pn
+                Up5jlE4ZDrau/69BtBUtkbsQyzexeD95NtV0ezvyA4ZiV4bBxWI+DOIrMZxR
+                p9yNRmLWE7E0+KbpvosW8Sh4LQ3YuVyEWs6DnlSSflthGOlkqMIJGWsnJ5eN
+                z1RxqlNwKO4TOiPMKbv1r8jWd7+g8CnhPKZoOEALBxTvbVi4haJxkCqjRobD
+                o7fRahhjKafqn1H4uFUmvyHcyHA8SeIeDim/SpZM4fYAVgd3OrjbobH3qUSl
+                gx1UB2AKu3gwQFqhqPBQwVV4pOAoeAql3yqWmbDQAgAA
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51Sy24TQRCsmXX82BhwEh4274eRkkhk4yjiEoQUjECWnCCF
+                yJcc0Hh3iMfenY1mx1aOPvEh/EFOSByQlSMfhehZG7gCl57uqq7unZr9/uPr
+                NwC7eMqwK3RkUhWdB1pM1KmwKtXBfj+zRoS2PVBx1I5FlrXT5KzpgtDUUAJj
+                qA3FRASx0KfBu/5QhrYEj6H4QmllXzJ46xu9KpZQ9FFAiaFgBypjeN79n4V7
+                DK317ii1sdLBcJIESltptIiD1/KjGMe2nWqaMA5tag6EGUmzt9Hzwd3itWb4
+                h/yQ5CzD1r9NY1j5JTiQVkTCCsJ4MvHISOZCxQUwsBHh58pV25RFLYbmbOr7
+                vM59XqNsNi1ffvLqs+kO32avSmV++bnIa9z17jA3YfPvHSrhDkPlt00My4di
+                8taIs8HWyJLn7TSSDNe6SsvDcdKX5lj0Y0JWu2ko4p4wytULsNrRWpp8tqSX
+                8t+nYxPKN8pxjaOxtiqRPZUpat7XOrX5h2Vokc0Fd3c6uXtwusIDqgJnBp1L
+                m19QvsjphxSLOXiMRxSr8wZU4AM1RtnyQvyMTr4QVy9yY53g5hycC/LsCq4S
+                5+ExVX4uuot7aOBJvvA+mvmPTh5Qb+0EXgcrHax2sIbrlOJGh2beOgHLUEeD
+                +Ax+htsZij8BRr5n4CUDAAA=
+                """,
+        """
+                androidx/navigation/AbstractChildClassComp.class:
+                H4sIAAAAAAAA/5VSS08TURT+7vQ1HYqUiljABwpiqcgUQlwIIcEaSZPSBZIm
+                wuq2Hcul0ztk7m3Dkt/i2g1RQ6KJIS79UcZzpxUSZYGLOeeeM9/5zvPnr6/f
+                AaxhlaHIZSsMROvElbwv2lyLQLpbDaVD3tTlQ+G3yj5Xqhx0j1NgDPPX4fc8
+                pS9jImSMIbkhpNCbDPHC/mKdIVZYrGeQQMpBHDbZPGwzsP0MHIykYSFDUH0o
+                FMNS9eZVrVOmtqe3DBml2GewN5r+MPXazXnmjeCSACncZlgpVDuBJh73qN91
+                hdReKLnvvvbe855PTUri6DV1EO7wsOOF64Pe7jiYwCRD+pKM4cV/NHNVxHoG
+                eUyZsUwzzFWDsO0eeboRciGVy6UMdMSj3Fqgaz3fpzGM/6l4x9O8xTUnn9Xt
+                x2jVzIi0EaCRd8h/IoxVoldrhWH74jTnWHkr+i5OHSs74lh2PH9xOptatUrs
+                JUu9Gs0ls9a0VYr9+JC0svHd8UvLppDpuJ3IJg0dHdXCtS3/fSVUHlUzUuP9
+                7ZAfHy53NMPMbk9q0fUqsi+UaPje1lWrdB7loOUxjFWF9Gq9bsML9zhhGHLV
+                oMn9Og+FsYfOTEVKL4xm61Gw8zbohU3vjTD/poZ56v9kwQrNPE6zsTBlVkAl
+                PiMrSfoe6Zy5VtIxshORd4msTUJbpJ3iOdLFmS8YPYsYng8jgT0sk5wcoHAL
+                Y2YX9DJstDpk6RtwuWZFpBPFzxj9eC1NZgAY0thUVGoYnEe0ZGS+YeIdO8fd
+                T5g5izwxIjYJGV2oFTVWiriL1DBQJv99YnxwgFgFDyuYreARHtMTcxXM48kB
+                mMICnh7AVhhTKCg4CosKSYWswrhC/jfYorpPVwQAAA==
+                """,
+        """
                 androidx/navigation/AbstractChildObject.class:
-                H4sIAAAAAAAA/41Sy27TQBQ9M0kcxwn0waMJ5VFapFIWuK3YUSGFCJAlYyQa
-                RUJdjR9qpnFs5JlEXWbFh/AHFYtKIKEIdnwU4o4JD4ku8Mj3zrlz5tyZY3/7
-                /vEzgEe4x7AtsrjIZXzqZmIqj4WWeeZ2Q6ULEeneUKbxq/AkiXQdjGHjInI/
-                UfrXhjoqDNaBzKR+wlC5vzNooQbLQRV1hqoeSsWw4/9nz8cM9kGUlmoOuJGw
-                veCw3w16z1q4BKdBxcsMW35eHLsniQ4LITPliizLdamq3CDXwSRNSWrFH+Wa
-                xNyXiRax0IJqfDytkBPMhIYJYGAjqp9Kg3ZpFu9Rg/nMcXibl+98Zn99x9vz
-                2T7fZU/rNv/y3uLL3FD3GTYvvNzfHlHbZiCmLwrxdvhwpBnWX08yLceJl02l
-                kmGadP+cn0zr5XHCsOTLLAkm4zAp+oI4DKt+Hol0IApp8KLoHOaTIkqeSwM6
-                C+HBP7LYI+eq5XU7xkjKtwlZlJcpcxq1Et0h5BpTKNcenMM+K5c3FmSgi7sU
-                Wz8JaJAUYKP5e/Masc3T/AT+5hytD1g6Kwscm2W8ha3yX6QPRAKrR6h4uOLh
-                qodruE5TrHloo3MEpnAD67Su4CjcVLB+AMKW4JLIAgAA
+                H4sIAAAAAAAA/41Sy27TQBQ9M0kcxwm0lEcTCrS0SFAWuK3YUSGlESBLxkg0
+                ioS6Gj/UTOPYyDOJusyKD+EPKhaVQEIR7PgoxB0THhJd4JHvnXPnzLkzx/72
+                /eNnAI9xj+G+yOIil/Gpm4mpPBZa5pnbDZUuRKR7Q5nGr8KTJNJ1MIaNi8j9
+                ROlfG+qoMFj7MpP6KUPlwfaghRosB1XUGap6KBXDtv+fPZ8w2PtRWqo54EbC
+                9oLDfjfoPWvhEpwGFS8zbPl5ceyeJDoshMyUK7Is16WqcoNcB5M0Jakr/ijX
+                JOa+TLSIhRZU4+NphZxgJjRMAAMbUf1UGrRDs3iXGsxnjsPbvHznM/vrO96e
+                z/b4Djuo2/zLe4svc0PdY9i88HJ/e0Rtm4GYvijE2+GjkWZYez3JtBwnXjaV
+                SoZp0v1zfjKtl8cJw5IvsySYjMOk6AviMKz4eSTSgSikwYuic5hPiih5Lg3o
+                LIQH/8hil5yrltftGCMp3yFkUV6mzGnUSrROyDWmUK49PId9Vi5vLMjAAe5S
+                bP0koEFSgI3m782rxDZP8xP4m3O0PmDprCxwbJbxNrbKf5E+EAmsHKHi4aqH
+                ax6u4wZNseqhjc4RmMJNrNG6gqNwS8H6AX2Z6w7IAgAA
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildObjectComp.class:
+                H4sIAAAAAAAA/5VSW2sTQRT+ZnLbbKKt9dLEem8RL+i2xTeLEIPKwrqCjQHp
+                02x2aabZzJSdSehjnvwh/oPiQ0FBgr75o8Sza6igfXGXPbf5znc43+yPn5+/
+                AniCDYaHQsWZlvGRp8RU7gsrtfI6kbGZGNjuUKbxm+ggoVCPD2tgDBtnNfQS
+                Y0+bCmSJobojlbTPGEr37vebqKDqoowaQ9kOpWF4FPzH7KcMzs4gLRhd8JzG
+                8cPdXifsvmjiHNw6Fc8zrAc62/cOEhtlQirjCaW0LZiNF2obTtKUqC4EI22J
+                zHudWBELK6jGx9MSqcJyU88NGNiI6kcyzzYpirdowHzmurzFi28+c75/4K35
+                bJtvsuc1h3/7WOXLPIduM9w9c8G/taLRjVBMX2XicPh4ZBnW3k6UlePEV1Np
+                ZJQmnT87kHhdHScMS4FUSTgZR0nWE4RhWAn0QKR9kck8XxTdXT3JBslLmSft
+                BXH/H1pskXrlYuV2Lib5G5RVyS+T5/RWiuwmZV4uDPnKgxM4x8XxrQUYeIfb
+                ZJu/AagTFeCgcdq8Suj8aXwBf3+C5icsHRcFjjuFvY714t+kSyKClT2UfFz0
+                ccnHZVyhEKs+WmjvgRlcxRqdG7gG1wyqvwCOLhDu2AIAAA==
+                """,
+        """
                 androidx/navigation/InterfaceChildClass.class:
-                H4sIAAAAAAAA/41Qz28SQRT+ZhZYWKgsVCtt/VWrtnBwaePNprEl0ZBgTWrD
-                AU4Du8KUZdfsDKRH/hbPXkw0Jh4M8egfZXxDSRMTDh7mvfe99833fvz+8+Mn
-                gBfYY9gTkZ/E0r/yIjGVA6FlHHnNSAfJB9EPGkMZ+o1QKGWDMbiXYiq8UEQD
-                713vMuhrGxbDziqJi0DpGxkbaYbMkYykPmZI7XeqbQZrv9ouwEbOQQoOYZEM
-                GFingALWcuC4RVQ9lIqh2vrPKV9Sm0GgT4wS6XcYSq1RrEMZeW8DLXyhBVH4
-                eGrR/syYnDGgviPKX0mD6hT5Bwyn81nZ4RW+ePOZw928w7NWZT475HV2ulbO
-                uHyL161fnzLcTZ2XblCW2FupbNrNGKVDht2V8/9zIhqLpsifiembRHwcPh9p
-                2r4R+wFDsSWj4Gwy7gXJheiFlCm34r4I2yKRBi+Tzvt4kvSD19KAzfNJpOU4
-                aEslqXoSRbFedFU4oNOmqFeGXtncmlbmFNvIkn1M6JgwJ+/UviNf2/6G4pcF
-                Z5es+QUc4QnZjWsWXJTMESkyanRz0l1fannmtuTTta8ofl4pU7gmLGU4ni7s
-                Dp6Rf0W121S704XVxEYTd5uoYJNCbDWxjXtdMIX7eNCFrVBSeKhQUHikkFUo
-                K6z/BWy7OjDsAgAA
+                H4sIAAAAAAAA/41Qz28SQRT+ZhZYWKgsVCul/qpVWzi4tPGmNrZNNCRYk9pw
+                gNPArjBl2TU7A+mRv8WzFxONiQdDPPpHGd9Q0sSEg4d5733vffO9H7///PgJ
+                4Bl2GXZF5Cex9C+9SEzlQGgZR14z0kHyQfSDk6EM/ZNQKGWDMbgXYiq8UEQD
+                713vIuhrGxbD9iqJ80DpaxkbaYbMCxlJfciQ2uvU2gzWXq1dgI2cgxQcwiIZ
+                MLBOAQWs5cBxg6h6KBVDrfWfUz6nNoNAHxkl0u8wlFqjWIcy8t4GWvhCC6Lw
+                8dSi/ZkxOWNAfUeUv5QGNSjy9xmO57Oywyt88eYzh7t5h2etynx2wBvseK2c
+                cXmVN6xfnzLcTZ2VrlGW2NVUNu1mjNIBw87K+f85EY1FU+RPxfRNIj4On440
+                bX8S+wFDsSWj4HQy7gXJueiFlCm34r4I2yKRBi+Tzvt4kvSD19KAzbNJpOU4
+                aEslqXoURbFedFXYp9OmqFeGXtncmlbmFNvIkn1I6JAwJ+/UvyNf3/qG4pcF
+                Z4es+QW8xCOyG1csuCiZI1Jk1OjmpLu+1PLMbcmn619R/LxSpnBFWMpwPF7Y
+                bTwh/4pqN6l2qwuriY0mbjdRwSaFqDaxhTtdMIW7uNeFrVBSuK9QUHigkFUo
+                K6z/BWao/b3sAgAA
                 """,
-            """
+        """
+                androidx/navigation/InterfaceChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51Sy27TQBQ9M07zcAO4LY+E9yNILYi6qUAsipAgCGQpbcVD
+                2XSBJva0mcQeV/Yk6jIrPoQ/6AqJBYq65KMQd5wA67K5c+8599zrOeOfv77/
+                APAUDxmeCR1lqYpOfC0m6kgYlWo/0EZmhyKUnYGKo04s8ryTJsctG4SmjgoY
+                gzcUE+HHQh/5+/2hDE0FDkP5hdLKvGRw1jd6dSyh7KKECkPJDFTO8Lz7Xxt3
+                GNrr3VFqYqX94STxlVVoEftv5KEYx6aT6txk49Ck2a7IRjLb2ei54HbzWiv8
+                R35OCpZh83zTGFb+CHalEZEwgjCeTByyktlQswEMbET4ibLVFmVRm6E1m7ou
+                b3CXe5TNptWzL05jNt3mW+x1pcrPvpa5x23vNrMTHp/DogpuMNT++sSwvCcm
+                7zJxPNgcGXK9k0aS4VJXabk3Tvoy+yT6MSGr3TQUcU9kytYLsB5oLbNitqS3
+                cj+m4yyUb5Xlmh/G2qhE9lSuqPmV1qkpvixHm3wu2cvTye2T0x3uUOVbN+hc
+                evQN1dOCvkuxXIDvcY9ifd6AGlzAY5QtL8RP6OQLcf20cNYKrs7BuaDILuAi
+                cQ7uU+UWopu4hSYeFAtvo1X86+QB9XoHcAKsBFgNsIbLlOJKQDOvHYDlaKBJ
+                fA43x/Uc5d8GoYhiKAMAAA==
+                """,
+        """
+                androidx/navigation/InterfaceChildClassComp.class:
+                H4sIAAAAAAAA/5VS308TQRD+9lp67VGkLYql+AMEtRThADEmQkiwRtKk1Iik
+                ifC0bZey7XWP3G0bHvlbfPaFqCHRxBAf/aOMs6XCgyaGh5vZmZv55psfP399
+                /Q5gFasM81w1Al82jl3Fe7LJtfSVW1JaBAe8LoqH0msUPR6GRb9zZIMxpFq8
+                x12Pq6b7ptYSdW0jwjD9L5hdEepLKBtDDLF1qaTeYIjm9+aqDJH8XDUJGwkH
+                UThk86DJwPaSSGIkAQs3KFQfypBhoXwNpmtUqin0pkGjGnsM8fW6N6j97BpA
+                s0ZwRRE2bjEs58ttXxOQ2+p1XGlyFPfcV+KAdz1d9FWog25d+8E2D9oiWLvo
+                7raDcWQZEpdgDM+v084Vi7Ukcpg0k7nDMFP2g6bbEroWcKlClyvl6z5Q6FZ8
+                Xel6Hg0i/YfyttC8wTUnn9XpRegCmBEJI0BTb5P/WBpriV6NZYat85OMY2Wt
+                /nd+4lipYceKR7PnJ1P2irXEXjD75UgmlrJy1lLkx4eYlYrupC+tOKXkovGh
+                VMzArRi+/70S4kZUhiu8txXwo8PFtmaY3OkqLTuipHoylDVPbF71SedR9BuC
+                YbQslah0OzUR7HKKYciU/Tr3qjyQxh44kyWlRNAfrKBk553fDeritTT/JgZ1
+                qn9VwTINPErEYqQnzAbovUCDipG+Rzpj7pV0hGwbcZKLZG1QtEXaKZxhuDD5
+                BaOnZFlwB5nAWyyRHL+IQgppswp6GTTaHOGODbBcsyHSQ4XPGP34T5jkRcAA
+                Jo6bSAySs+jvGMlvGH/PzjDxCXdP+54ItWYKsj6JHDW30sd+gqeki+S/T4hT
+                +4iUMF3CgxJmMEtPPCzhER7vg4XIY24f8RDpEIUQyRDzoTEzIcZC5H4Dj/nZ
+                kW0EAAA=
+                """,
+        """
                 androidx/navigation/InterfaceChildObject.class:
-                H4sIAAAAAAAA/41STW/TQBB9u0kTxwk0LV8J5asUUOkBtxU3ClKJAFkKRmqj
-                SKinjb0kmzhrZG+iHnPih/APKg6VQEIR3PhRiFkTygEksLUz897OvN0Z+9v3
-                j58BPMQ9hk2hozRR0bGnxVT1hVGJ9nxtZPpGhLI1UHH0qjeUoSmDMdSHYiq8
-                WOi+94stMKz/TaMjM3OmU8YSQ2lPaWWeMBQ273drKMNxUUSFoWgGKmPYav/v
-                XR4xOHthnMu54FbD8YPDzn7QelbDMmoVIusMG+0k7XtDaXqpUDrzhNaJyWUz
-                L0hMMIljklppjxJDYt5LaUQkjCCOj6cFGhGzpmINGNiI+GNl0TZF0Q4dMJ+5
-                Lm/wfM1nztd3vDGf7fJt9rTs8C/vS7zObequvcs/p0TnVgMxfZGKt4MHI8Ow
-                djDRRo2lr6cqU71Y7v9ugMbWSiLJsNxWWgaTcU+mHUE5DKvtJBRxV6TK4gXp
-                HiaTNJTPlQXNhXD3D1ns0OiK1G2JVtPOkvwtatniVfKcXvp0hNYJeXYu5Je2
-                TuGe5Nu3F8nAY2yQrf1MQJUiUOG5s+IrlG2f6ifw16c4/wErJznBcSe3N3E3
-                /08ZLpDAxSMUfFzycdmn0gaFaPq4irUjsAzXcJ32M9Qy3Mjg/AA8NOf95AIA
+                H4sIAAAAAAAA/41STW/TQBB9u0kTxwk0LV8JBUopoNIDbituVJVKBMhSMBKN
+                IqGeNvaSbOKskb2JesyJH8I/qDhUAglFcONHIWZNKAeQwNbOzHs783Zn7G/f
+                P34G8Aj3GbaEjtJERSeeFlPVF0Yl2vO1kekbEcrWQMXRy95QhqYMxlAfiqnw
+                YqH73i+2wLDxN42OzMy5ThlLDKV9pZU5YChsPejWUIbjoogKQ9EMVMaw3f7f
+                uzxmcPbDOJdzwa2G4wdHncOg9bSGZdQqRNYZNttJ2veG0vRSoXTmCa0Tk8tm
+                XpCYYBLHJLXSHiWGxLwX0ohIGEEcH08LNCJmTcUaMLAR8SfKoh2Kol06YD5z
+                Xd7g+ZrPnK/veGM+2+M77EnZ4V/el3id29Q9e5d/TonOrQZi+jwVbwcPR4Zh
+                7dVEGzWWvp6qTPViefi7ARpbK4kkw3JbaRlMxj2ZdgTlMKy2k1DEXZEqixek
+                e5RM0lA+UxY0F8LdP2SxS6MrUrclWk07S/K3qWWLV8lzeunTEdog5Nm5kF/a
+                PoN7mm/fWSQDB9gkW/uZgCpFoMIL58XXKNs+1U/gr89w8QNWTnOC425u13Ev
+                /08ZLpHA5WMUfFzxcdWn0gaFaPq4jrVjsAw3cJP2M9Qy3Mrg/ACDO+xh5AIA
                 AA==
                 """,
-            """
+        """
                 androidx/navigation/NavDestination.class:
                 H4sIAAAAAAAA/4VRy07CQBQ9M0CBivJQEXwkalyoC4vEncbERzQkiIkaNq4G
                 2sBAmZp2aFjyLf6BKxMXhrj0o4y3lb2bk/O4d+bOne+fj08AJ9hi2BXK9j1p
@@ -610,7 +885,7 @@
                 vwJkYcb5Roxr2Iy/isanLPeMRAOLDSw1kEeBKIoN6l9+BguwglXKA5gBygGM
                 X6tGzkfnAQAA
                 """,
-            """
+        """
                 androidx/navigation/NavDestinationBuilder.class:
                 H4sIAAAAAAAA/41QTW/TQBSct86nE4hToKR8tZV6oEHCoeoprSpRIlAkUySK
                 cslpE1vpNs4aeddRj/kt/ANOSBxQxJEfhXg2OXGhl3kzs7P79r1fv7//AHCM
@@ -623,7 +898,7 @@
                 ZSLwhLFSmG08ZWz+DcBFg6uDZ0XKwW5RH2GPa58zTc7cGcMZ4u4QrSE8tJli
                 a4h7uD8GGTzA9hhlg4bBQ4OOwY5B9Q/QTbpQiwIAAA==
                 """,
-            """
+        """
                 androidx/navigation/NavGraph.class:
                 H4sIAAAAAAAA/31SW28SQRT+ZinLQi/Q1tYW26pttbTVLjY+SdPES6oYRC2E
                 lz4NMKEDy6zZHUgf+S3+A580Phjioz/KeGahiql1kzmX79y+2TM/fn79BuAx
@@ -640,7 +915,7 @@
                 bFS+PEoZlxsrjZsU34uyZ7FvMENiLgIeRPI+HpI+IXSFJq+eIVZEtohbRaxh
                 nUxsFHEbd87AzNU2z5AMkQqxFcIOMR1iO8S9SKZDzPwCFg2MhPgDAAA=
                 """,
-            """
+        """
                 androidx/navigation/NavGraphBuilder.class:
                 H4sIAAAAAAAA/61TW08TQRT+Zre0S6nSLgKlIoKAXGUL8lZCIhhNQ0ViTRPD
                 g5m2a5my3SUz04ZHfov/wCeND4b46I8ynl2WS6IlPPiw5zbf+c6ZM2d//f7+
@@ -659,41 +934,41 @@
                 g1H1C5kmeQkxsRHpVTwnXabTKer18SHMMqbLmCnjCWbJxFwZ83h6CKawgMVD
                 pBUmFJYUlhWGFAoKIworCpMKjxRSfwCslzbYagUAAA==
                 """,
-            """
+        """
                 androidx/navigation/NavGraphKt.class:
-                H4sIAAAAAAAA/71W31MTVxT+bn6QzRrIEoxAEBCJMaCYGPEnSIsomgpIBbHW
-                tnZJlrAQdp29m4x96TB96Eunf4AvPvQv0PqgU2Y6DH3rH9X23GVDQgKEmc74
-                kHvPPfd853zn3HPv5u9//vgTwCgKDH2qkbdMPf8qZahlvaDaummk5tTyfUt9
-                ufrQDoAxKGtqWU0VVaOQerS8puVI62WQqwCGW8mZoxzdKenFvGaNzdR7Ghta
-                YpgfX7zVuDPxf1y2jNurOp+QIFGK66Zd1I3UWnkjpRu2ZhlqMZU1bEs3uJ7j
-                AcgM0dyqllufM+25UrE4r1rqhkaGDOeTjf5rNAvCSYEihhBCq4wTaKNycVu1
-                7Lsat3XD4SxBYQjH9fhKvLZmLEu2ccFzn/rcsdJmiDQyY/jl0KIJ0bTmLbOs
-                H1y2xrRmagq3UjJywhVPTbvS5bGhI5kSm+1PyGZ85Hjt4np5YujUYs1TGJwx
-                rUJqTbOXLZX6JaUahmmru7HddiGr+FFWZKIuFzUyCyzvspDQw3D6qMgB9Iom
-                1onlBIM3KTqsH2dk9GGAuvKYZWXwW2bJ1vY3i1tPhjPNDphwVf/xvLailoo2
-                w5tP2WTZAy54s0Prqb9VL0qZ0T36HQXNniqqnGcNuqlGTnusrTAMJA92u0j3
-                2LEmv0quAdbfBBTCRYwE4cElhlh94Ke6vTppFRxHyWbxXWOi0Zk7zEnieC5C
-                yOCKIDXK0EWksoahWY0lOYzSI2opK14FEaWofrCLgwk1OAjhBm4KQrcYpPFc
-                0e373iOzCeC2jAlxH+LHyTqAzxl8yezuVbojYxJTh0Dr+QVwT8a0MG+vtOus
-                Zqt51VYpd89G2UufUyaGoBhAb/u6EDy0+UoXUpqk/GWGf7c378jbm7KnyyN7
-                JG/jrHicbcW1qZi6otJWo4kNKKGYJ916Vpa2N5W2LjbsSYfpp2RalHbaCD/Y
-                +VmK3XaMIqTvqOgzsuRRTsZ8XSwdzXQqp2IdEV+EcM7Ymu7c+a3FI3UJ9F8f
-                2fbm2XZiQzhGeonix3wE95LWR0p/VdmiBEgpkTJYVcrKiZ2fPAHZL+28zqSZ
-                KEOGORVapC9zs4scqZS79k3qqyjvvbI1+oSbRmV38YeX4pntPvRMA3hGf1yq
-                B8twohLt0jo9DT2PS4atb2hZo6xznR7tyepDTs0zZebpLQ3P6IY2V9pY1qxF
-                8bALmmZOLS6pli7WrjK4oBfo+1+ySI7X+937i7EvQOuCrebWZ9WXrotQlalG
-                2/KCWbJy2rQu9rpdl0sNRHGZLpKPms6LmHh9qNjf0qqF5m6aY+LyN+jo/tXp
-                Ako3/LTy4DtazdIsGjk8HAl+RPhCpJ1G7+13osvxwoW1IYjvST5Dpm20jqCD
-                dgmEk4iKi0GSglO0ozq4AESITiIrQpik8dPcWxtiJHJaBLrm20Lfsw84+3Zf
-                wCgGnYCjBIxCcgKKzHsp4KATsNcNKKQ4zjl0etGDBFHcJdG1L+tl+nX73EVl
-                jFVkJYjzSJIs+P5K4BaaE1Gf/8fX8LPZZsS9yDkjCzoZRByu/VSGASpcP33b
-                q9n01WSTwJCbTWIvm8ReNgk3m24M44J7WqcdG8D3O1JvHfKVioFQtfl2I+30
-                Sx3qaj3qfB3qGq43osbqUUP7UBLGqTV2izfldBMwuIUJKs9n75HawuQzJfwB
-                d9/j6hamHfn+e4y923Pa5oDOQSY6p8i5F3lay7Q7ja/xnIJozpF+gxWay6R/
-                QKXMPoc3iy+yeJjFDGazmMOjLObx5XMwjsdYICDHRY4RjmGOCxwZjiscabpJ
-                HDc4bnJc47jO4edY5HjijFFOf/SxxDHIEXc0PRxPOb76Dzb5xaFeDQAA
+                H4sIAAAAAAAA/71W31MTVxT+bn6QzRrIEoyQICASY0QxMeJPkBZRNBWQCmKt
+                be2SLGEh7Dp7Nxn70mH60JdO/4C+9KF/gdYHnTLTYehb/6i25y4bEhIgzHTG
+                h9x77rnnO+c75557N3//88efAEZRZOhXjYJl6oXXaUOt6EXV1k0jPadWHljq
+                q9VHdgCMQVlTK2q6pBrF9OPlNS1PWi+DXAMw3E7NHOXoblkvFTRrbKbR09iF
+                JYb58cXbzTsT/8dl27i9qvMJCRKluG7aJd1Ir1U20rpha5ahltI5w7Z0g+t5
+                HoDMEM2vavn1OdOeK5dK86qlbmhkyHA+1ey/TrMgnBQpYgghtMs4gQ4qF7dV
+                y76ncVs3HM4SFIZwQk+sJOprxnJkmxA896nPHStthkgzM4afDi2aEE1r3jIr
+                +sFla05rpq5wK2UjL1zx9LQrXRm7cCRTYrP9EdmMjxyvXVwvTw2dWqx1CkMz
+                plVMr2n2sqVSv6RVwzBtdTe22y5klTjKikzU5ZJGZoHlXRYSehlOHxU5gD7R
+                xDqxnGDwpkSHDeCMjH4MUlces6wMfsss29r+ZnHryXCm1QETruY/UdBW1HLJ
+                Zvj1YzZZ7oAL3urQehtv1ctydnSPfldRs6dKKuc5g26qkdeeaCsMg6mD3S7S
+                PXasya+Sb4INtACFcAkjQXhwmSHeGPiZbq9OWkXHUapVfNeYaHTnD3OSPJ6L
+                ELK4KkiNMvQQqZxhaFZzSQ6j9JhaykrUQEQpqh/s4mBCTQ5CuIlbgtBtBmk8
+                X3L7vu/IbAK4I2NC3IfEcbIO4FMGXyq3e5XuypjE1CHQRn4B3JcxLcw7q+06
+                q9lqQbVVyt2zUfHS55SJISgG0Nu+LgQPbb7WhZQhqXCF4d/tzbvy9qbs6fHI
+                HsnbPCseZ1txbaqmrqh01Gnig0oo7sm0n5Wl7U2lo4cNezJh+inZNqWTNsIP
+                d36U4nccowjpu6r6rCx5lJNxXw/LRLPdyql4V8QXIZwztme6d35r80g9Av3X
+                B7a9ebaT2BCOkV6i+HEfwb2k9ZHSX1O2KQFSSqQM1pSycmLnB09A9ks7v2Qz
+                TJQhy5wKLdKXudVFjlTLXf8m9VeV91/bGn3CTaO6u/jdK/HMxg490wCe0x+X
+                2sEynKhGu7xOT0Pvk7Jh6xtazqjoXKdHe7L2kFPzTJkFekvDM7qhzZU3ljVr
+                UTzsgqaZV0tLqqWLtasMLuhF+v6XLZITjX73/mLsC9C+YKv59Vn1lesiVGOq
+                0ba8YJatvDati72Y63KpiSiu0EXyUdN5ERevDxX7a1q10RyjOS4uf5OO7l+D
+                LqDE4KeVB9/QapZm0cjh4UjwA8IXI500eu+8FV2Oly6sA0F8S/IZMu2gdQRd
+                tEsgnERUXAySFJyiHdXBBSBCdBNZEcIkjZ/mvvoQI5HTItB13xb6n7/H2Tf7
+                AkYx5AQcJWAUkhNQZN5HAYecgH1uQCElcM6h04deJIniLomefVkv0y/mcxfV
+                MV6VlSDOI0Wy4PszgdtoTkZ9/u9/gZ/NtiLuRd4ZWdDJIOJwHaAyDFLhBujb
+                Xsumvy6bJC642ST3sknuZZN0s4lhGBfd0zrt2AC+35F+45CvVkzY1+cbQ8bp
+                lwbUtUZUqgF1HTeaUWONqOF9KAnj1Bq7xZtyugkY2sIEleeTd0hvYfK5En6P
+                e+9wbQvTjvzgHcbe7jntcEBJyETnFDn3okBrmXan8SVeUBDNOdKvsEJzhfQP
+                qZS5F/Dm8FkOj3KYwWwOc3icwzw+fwHG8QQLBOS4xDHCMcxxkSPLcZUjQzeJ
+                4ybHLY7rHDc4/ByLHE+dMcrpjz6WOIY4Eo6ml+MZxxf/AV3xdy9eDQAA
                 """,
-            """
+        """
                 androidx/navigation/NavigatorProvider.class:
                 H4sIAAAAAAAA/41Ry0rDQBQ9M2nTGqt9+Gp9gC7Ex8LU4k4RVFAC9YFKN66m
                 Tahj0xlJpqXLfot/4EpwIcWlHyXeRD/AzeE8Zrhn7nx9v38AOMAaw6ZQfqSl
@@ -705,9 +980,9 @@
                 mbDwewBTcNJ8JcUlrKYfRvUpKzzA8jDjYdZDESWiKHuoYO4BLMY8FiiP4cRY
                 jGH/AKZPGqDtAQAA
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerClass.class:
-                H4sIAAAAAAAA/41U32/bVBT+rp0fjpu2Tttt/RE2oKEk6Ta3ZaNj7QZtoatL
+                H4sIAAAAAAAA/41U32/bVBT+rp0fjpu2Tttt/RE2oKEk6Ta3ZWNl7QZtoatL
                 mo4WVYzycpOYxG1qB9upxgvq0/6ESfCChBBPfRgStIhJqGxv/E0IcW7sJSMd
                 VSX7nnOPz/nOd88513/98/sfAG5gkyHD7YrrWJWHus33rSr3LcfW15u+6WYM
                 2zbdpTr3vDgYg7bD97le53ZVXy/tmGU/DpkhNm/Zln+XIZI1clsMcja3lUQU
@@ -724,53 +999,134 @@
                 XN6oXd+lexIJ+tZfsGyz2Nwrme6norSiok6Z17e4a4l9aBzbaNq+tWca9r7l
                 WWRa6LSFoXfT5+XdNd4IvTPd3ve5y/dMYvWfsGSHnUlbddNpumVz2RIQIyHE
                 1ql0NEcS/cbE6QfEr4s0hXT6IdC6Srtl+i6RVPPHSOTHfkXvz7ST8DGtfRDN
-                fpfiZ5EgWaDdxcCbvvWLsSBNoNIUQaM3wNTFtJCM5n9B72EbLtYyzrZgkoFD
-                CJMici+Cx7uD2SsD6KdCsCJgmlgKTomnkB6MHePSk3ZQQDbRJpsIya6FbC4A
-                WgLDGAlzT4TFSqUj33wLRTCYz48dYSyALNIqgwkEutZh+tskBbX0U1x5cIzX
-                B948woSIPEJOyx3h6hGuP+k6Rjpk9BIPWvV2DSbCGrQY/IYb3WVQwniGm9SW
-                gMcXJEW7MvnJnxCNHE7+Cek7ROXDyRNIawLoKr3fC0sk6Emx1T45rvyNVJz2
-                nYpl2hXL4BbeozzrpMcFqdlWDe63Qulm4R5WqHyftAANbJD8jOy3qVNz25AN
-                zBu4Y+Au3icVHxhYwOI2mIclfLiNfk88H3lQW2vMg+Yh5WHAw6CHmy3jLQ+6
-                hzTp/wKll0bx9QcAAA==
+                vkXxs0iQLNDuYuBN3/rFWJAmUGmKoNEbYOpiWkhG87+g97ANF2sZZ1swycAh
+                hEkRuRfB493B7JUB9FMhWBEwTSwFp8RTSA/GjnHpSTsoIJtok02EZNdCNhcA
+                LYFhjIS5J8JipdKRb76FIhjM58eOMBZAFmmVwQQCXesw/W2Sglr6Ka48OMbr
+                A28eYUJEHiGn5Y5w9QjXn3QdIx0yeokHrXq7BhNhDVoMfsON7jIoYTzDTbwb
+                8viCpGhXJj/5E6KRw8k/IX2HqHw4eQJpTQBdpfd7YYkEPSm22ifHlb+RitO+
+                U7FMu2IZ6td7lGed9LggdatVg/utULpZuIcVKt8nLUADGyQ/I/tt6tTcNmQD
+                8wbuGLiL90nFBwYWsLgN5mEJH26j3xPPRx7U1hrzoHlIeRjwMOjhZss460H3
+                kCb9X5rxH871BwAA
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerObject.class:
-                H4sIAAAAAAAA/41TS08TURT+7p0+plMe5SFPxQcgLShTEFcQEyQ+hpRiLMEo
-                q0s70oEygzO3DUtW7ty6cOnCFQuJCxJNDErc+KOI5w6jIARj0t7znTPnnO/M
-                d+78PPr8FcAU7jIMC7fie05l23RFw1kT0vFcc7EubX/Icl3bX1xdt8syCcaQ
-                WRcNYdaEu2b+jmoMiRnHdeQ9Bi2bW25CHAkDMSQZYrLqBAwjhf9imGbQpVeS
-                vuOuMXRmc4UTtuMoZQwWPH/NXLflqi8cNzCF63oybBiYRU8W67UaZaVPtdXR
-                Qo2rIqjOeRU7HNLSfkwdvaHB7Vd1UaMJL2ULZ99sOveCYehfbEQlVms20cU9
-                WbV9hvbzXYh6plwL9THAlSi6VSwtzRbnHjShD0aKgv0MbYUNT1KauWBLURFS
-                UCHfbGi0I6aOlDrAwDYovu0oL0+oMsHw8mBnwOA93OCZgx2D6wqkI6sbKpRp
-                0Q9fGz0HO5M8z+4ndf79fYJn+HxHRuvj+diknon3xXpYnj0+fKvNpzIJiiYJ
-                M8I64ZTCim2SqRl6L9xmEjnSvigaj3yxVR3fkAz9T+uudDZty204gUNyzZ5I
-                SBfkeCWtBce1i/XNVdtfUpIqJb2yqC0L31F+FGwuSVHeWBBbkT90tvcT4YtN
-                mwb5i6QpvAxzNREENrlGyav7Zfuho1r0Ri2Wzw2HCdpMLBS9Vy2K7C3yEmSb
-                ycbpaTz0bpNnqtWo6Og+9D0CHONRsvrITDqbjhOQolaqaZoiPCy+HhVr7a0f
-                w0cn6VqUfpqZvkK0Rbwnpe27F5QydKAzYrLIcrLdo2MfEI/tjn0Df4e4tjt2
-                AP4sthsOnqczBp7Uw2ZdxwVRM4W66M9IHajLTJ8OAR09f6ToDguA9Bfw5/vo
-                /YTLe2FAwySdSkeOUbSQqndCvjESSI3GcIXkGViBZuGqhWsWvd0Nghi0MITh
-                FbAANzGyAiNQv2yARICOEHQFyIQgTecvRvQ+5dsEAAA=
+                H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQcgBZQpCCuICRIfQ0oxlmCU
+                1W070oEygzO3DUtW7ty6cOnCFQuJCxJNDErc+KOI5w6jIERj0t77nXPPOd+Z
+                79yZH8efvgCYxgzDsHArvudUdkxXNJx1IR3PNZfr0vaHLNe1/eXShl2WSTCG
+                zIZoCLMm3HXzl1djSMw5riPvMmjZ0dUmxJEwEEOSISarTsAwkv8vhlkGXXpF
+                6TvuOkNndjR/ynbipYjBvOevmxu2LPnCcQNTuK4nw4KBWfBkoV6rUVT6TFkd
+                LVS4KoLqglexwyYt7fv08Wtq3H5ZFzXq8FI2f/7JZkefMwz9i42oRKlmE13c
+                k1XbZ2i/WIWo58q1UB8DXImiW4Xiynxh4X4T+mCkyNnP0Jbf9CSFmUu2FBUh
+                BSXyrYZGM2JqSakFDGyT/DuOsnKEKpMMLw53Bwzeww2eOdw1uK5AOtp1Q7ky
+                LfrRK6PncHeK59i9pM6/vUvwDF/syGh9PBeb0jPxvlgPy7FHR2+0xVQmQd4k
+                YUZYJ5xSWLFNMdVD71+nmcQoaV8QjYe+2K5ObEqG/id1VzpbtuU2nMAhueZP
+                JaQLcjKS1rzj2oX6Vsn2V5SkSkmvLGqrwneUHTmbi1KUN5fEdmQPna/9WPhi
+                y6ZG/iBpCi/DQk0EgU2mUfTqftl+4KgSvVGJ1QvNYZImEwtF71WDov0WWQna
+                m2mP02k8tG6TZarRKO/YAfR9AhwTUTAwQ8dA00kAUlRKFU2Th4fJ16Nkrb31
+                Q3h0Gq5F4WeZ6S1EW8R7mtq+95dUhg50RkwW7Zz27rHx94jH9sa/gr9FXNsb
+                PwR/GtsLG8/RGgNP6mGxrpOEqJhCXfRnpA7UZaZXh4COnt9SdIcJQPoz+LMD
+                9H7E5f3QoWGKVqUjxxhaSNU7Id84fYVUawxXSJ6BNWgWrlq4ZtHT3SCIQQtD
+                GF4DC3ATI2swAvXLBkgE6AhBV4BMCNK0/gRLa5P62wQAAA==
                 """,
-            """
+        """
                 androidx/navigation/Outer.class:
-                H4sIAAAAAAAA/4VRy27TQBQ9M87DcQxNw6MJoeXRFJoi4bawKhVSiQBZSlOJ
-                VpFQVpNklE7ijJHtRF1mxYfwBxWLSiChCHZ8FOLaDWSBKjzyPXMf59yZOz9/
-                ffkG4DmeMJSF7gW+6p05WkxUX0TK187ROJJBFoyhMBAT4XhC952jzkB2oywM
-                hsy+0ip6yWBs1lo20shYSCHLkIpOVchQaVyp+oLB3O96Cd8Cj0mm2zw+OWjW
-                X9u4BitHwesM6w0/6DsDGXUCoXToCK39KNEJnaYfNceeR1LLjaEfkZhzKCPR
-                E5GgGB9NDLodi00uNmBgQ4qfqdjbpl1vh6E2m9oWL3GLF2ZTi5uG+eMjL82m
-                u3yb7XEj9Spr8u+fMrzAY8Iui2UsV2sZ1D0R0iXziXM5FYbqlTeuLkhZ3GPY
-                +E/lnzk/oBZNMXkbiA+nT4fUovJurCM1kq6eqFB1PHmwmAmNvu73JMNSQ2nZ
-                HI86MjgRVMNQbPhd4bVEoGJ/HrQXh5JEto79cdCVb1ScK8/7tP7pgh16nFQy
-                0XL8VoRV8jKEBUJOK514G+Q58dwJ01sXMM+T9KN5MfAMj8nalwXIkRRgIv+X
-                vELV8Zf/Cv7+AvZnLJ0nAQObZIuUvk//Kp3jIeEaYS1psY4twj2SWSbhYhuG
-                ixsubrq4hdu0xYqLEsptsBB3UGkjHcIKcTdEJsRqiLXfdJuyTB0DAAA=
+                H4sIAAAAAAAA/4VSy27TQBQ9M87DcQxNw6MJoeXRFJoi4bZiVSqkEgGyFFyJ
+                VpFQVpPESqdxxsh2oi6z4kP4g4pFJZBQBDs+CnHHDWSBKmz5nrmPc+7MHf/8
+                9eUbgGd4wlAVqh+Fsn/mKDGRA5HIUDmH48SP8mAMpVMxEU4g1MA57J76vSQP
+                gyG3L5VMXjAYm422jSxyFjLIM2SSExkz1FpXqj5nMPd7Qcq3wDXJdL2j4wOv
+                +crGNVgFCl5nWG+F0cA59ZNuJKSKHaFUmKQ6seOFiTcOApJabg3DhMSct34i
+                +iIRFOOjiUGnY9oUtAEDG1L8TGpvm1b9HYbGbGpbvMItXppNLW4a5o+PvDKb
+                7vJttseNzMu8yb9/yvES14RdpmUsVyk/agYipkMWU+dyKgz1K09cX5DyuMew
+                8Z/KP3N+QC08MXkTiQ8nT4fUovZurBI58l01kbHsBv7BYiY0+mbY9xmWWlL5
+                3njU9aNjQTUM5VbYE0FbRFL786C92JRPZOsoHEc9/7XUueq8T/ufLtihy8mk
+                E63quyKsk5cjLBFyerOpt0Geo+dOmN26gHmeph/Ni/W/95isfVmAAkkBJop/
+                yStUrZ/iV/D3F7A/Y+k8DRjYJFum9H36VmkfDwnXCBtpi3VsEe6RzDIJlzsw
+                XNxwcdPFLdymJVZcVFDtgMW4g1oH2RhWjLsxcjFWY6z9BuhHh3wdAwAA
                 """,
-            """
+        """
+                androidx/navigation/OuterComp$InnerClassComp$Companion.class:
+                H4sIAAAAAAAA/51Ty27TUBA9Y6dx4oaStjwSoJRHgBRB3VQIIRUhQRAoUppK
+                gLLpAt0kpr2JfV3Z11GXWfEh/EFXSCxQ1CUfhZjrBCo2SGUzc2bOnBl7xv7x
+                89t3AE9QJzwVahBHcnDsKTGWB0LLSHl7qfbjZhQe1VpKMQpEkmShMUJxiQMi
+                lIdiLLxAqANvrzf0+9qBTcg/l0rqFwS7vtEtYQF5Fzk4hJw+lAnhWfv/Ru4Q
+                GvX2KNKBVN5wHHpSsUSJwHvtfxJpoJuRSnSc9nUU74p45Mc7G10Xlhm9Wuuf
+                kR/DjCVsnq8bYfm3YNfXYiC04JwVjm1eJhlTNAYEGnH+WJpoi9GgQahNJ65r
+                VSzXKjOaTgqnn+3KdLJtbdErp2CdfslbZcvUbpPp8Og8O3JwnbD2T4WDNcLS
+                3zJC8c9yCYsdMX4bi6PDzZHmWzWjgU+42JbK76Rhz48/iF7AmZV21BdBV8TS
+                xPNk6ayxzxd230dp3PffSMNV36VKy9DvykRy8UulIp09XIIGHydnNsbeMh8K
+                v/gdjjyzQvYLD7+icJLRd9nms2QHNbalWQGKcIEyMVqcix+zt+bi0kl2DiO4
+                MkvOBBm6gCXmbNzjaIXZG7iJdVQzdIv9/WzwbTzIfhXeBWvK+7BbWG5hpYVV
+                XGKIyy3ufXUflKCCKvMJ3ATXEuR/Ae6K/YtnAwAA
+                """,
+        """
+                androidx/navigation/OuterComp$InnerClassComp.class:
+                H4sIAAAAAAAA/5VUW08bVxD+zvq2XgysIRcCpEmLm5pLWKBJSsFpuKSEpTak
+                kNIQ2oeDvTULZpfurlH6UuUpPyFS+1KpD33iIVFbqIpU0eStv6mqOmd3MWAi
+                JEv2OTOzM998Z2bO+ee/P/8CcAtfMwxwq+TYZumpZvEds8w907a0hapnONP2
+                1nZGtyySKtx1hZoAY1A3+A7XKtwqawtrG0bRSyDCEM+Zlul9whDN6r3LDJFs
+                73IKMSQURCEzyKZAmnTKDExPQUFTEhJS5O+tmy7DYL4RIuMMTWXD02uYlE5n
+                UIr0zbYMyxsm4KK9/R3DMPFpFLsnbztlbcPw1hxuWq7GLcv2/ChXm7e9+Wql
+                Mi4OF1foDJcYUiJVpmR8w6sVj8HJNpZQ1/P1NR1vkHMK7bgg2HRSqT17yXNM
+                i8pyIdt7Ajqw0vku19umqmalZDgJvKPgmmhXx2n87FH37sp4l5rNt7cNq8Rw
+                M3sW/mzGEJ1I9iAjErzP0C3acp7jB8IxKxynz3fsE479KXTjqpBuUgHWubs+
+                bZcMhvRxpG55RlmccSgYUppCDSMKhvEhncj4tsorNIcXs2/pxROGzHkjQfPA
+                1yoGVTZme+uGw9B2FoV45YqV8JbcaaS7GbFwi1wSGBcTnd+0PULSNna2NJOO
+                5Vi8ot0Pxm+aGHlOtejZToE7m1Sk4CLeVZADZU7WwBhGGxqyYxpU9wlMigs8
+                RSU+YlMwPF7iHieK0tZOhF4YJpakWEDXfpPsT02hUQekEl3R3cNn1xWpQ1Ik
+                9fCZQj9JlRVJjtPeRHuE9hYyy6+fyx3k2joiDbEx1jrV3BZXpU5pKPL657ik
+                RueSakJos2+eR+baVZnkw2cjsiwFTmRmZE6SrIzIalNntIMNsdk3LyIUmAo8
+                XjCSm0luEfJiugYvU/7OqBxT44LzCBMnuXpu1RJ4yNByunT0XM3znQcO314f
+                3KQXomuxannmlqFbO6Zr0uhMHo8TTWcwu6150zLmq1trhvNIjJeYKrvIK8vc
+                MYUeGpuXPF7cLPDtUM/UYz/kDt8yiN2pJKljhgapypJddYrGjCkgroQQy2fI
+                0W2R6EEHrVfEDFA1HpEWp/0i7W3iYRc9Jz3mW78gbYa8JdqVvn0k+7p+R/Mr
+                H2GZ1haIgcgTZoGi8viStEuBN31rFaNDkkClSYNK/wBTExNFe6zvNzTv1uDi
+                vrHgw6QChxAmTeSOgnvqg9lbA+hJJVgRMEwsBafkAaSVrn1cflkLCsgma2ST
+                IdkTZVGT6KByBblvhAVMd0e//wGyYJDr69pDVwD5mNYImECgBy1MP0a7oNZ9
+                gGsr+7je9t4ebojIPfSqvXsY2MPgy7pjdIeMTraHUdnSNR5BDXwGf+BWfRnk
+                MJ7hNu6EPL6iXbQr09f/C2LR3f6/If2IWGS3/xBSQQAN0P8nYYkGPXnsty+S
+                kP9FOkH6ccUytYplMIqPKc8KyQlB6iM//RgSIdUOPykRO0Buhe3j3q+YfuVb
+                InjiT52Yr8+xSEXOkTRB+6qffokog2SG+9TXT1cR0TGj44GOWegkYk7HZ8iT
+                g0tDM78K1UWriwUXir/GXWFJu2hz0e7itm8cdaG56Pblif8BqzOQX0wJAAA=
+                """,
+        """
+                androidx/navigation/OuterComp$InnerObject.class:
+                H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQdVKVWmoK4gJkh8DKnFCMEo
+                q0s70oF2BmduG5as/AkuXLrQDQuJCxJNTJWdP8p47nQUhEhM2nu/c+455zvz
+                nTvz4+fnrwBu4w5DTrgV33MqO6Yrms6GkI7nmksNafsLXn07a7mu7S+tb9pl
+                mQRjyGyKpjBrwt0wf3s1hsSc4zryLoM2kVvtQBwJAzEkGWKy6gQM+eJ/s8wy
+                6NJblr7jbjD0T+SKR4xtL0WMFz1/w9y05bovHDcwhet6MiwamCVPlhq1GkWl
+                j5XV0UWFqyKoLngVO2zU0twPhTlq3n7VEDXq8txE8eTTzeZeMGTPYiMqsV6z
+                iS7uyartM/SerkLUc+VaqJEBroTRrdLyynxp4X4HRmCkyDnK0FPc8iSFmY9t
+                KSpCCkrk9aZGs2JqSakFDGyL/DuOsgqEKtMML1u7YwYf4gbPtHYNriuQjnbd
+                UK5Ml3742hhq7c7wAruX1Pn3dwme4Yt9GW2EF2IzeiY+EhtiBfbo8I22mMok
+                yJskzAjrhFMKK7YZpnq4cOZEk8iR/iXRfOiL7erUlmQYfdpwpVO3LbfpBA5J
+                Nn8kI12U9li6i45rlxr1ddtfUbIqNb2yqK0K31F25OxclqK89VhsR3b2ZO0n
+                whd1m5r5i6QjvBALNREENpnGstfwy/YDR5UYjkqsnmoO0zSdWCj8sBoW7TfI
+                StDeSXucTuOhdZMsU41HeScPoO8T4JiKgoFFOgY62gFIUSlVNE0eHiZfjpK1
+                3u6P4dFRuBaFH2emtxE9Ee9Rau/eP1IZ+tAfMVm0c9oHJ/PvEY/t5b+Bv0Vc
+                28u3wJ/F9sLGC7TGwJN6WGygnRAVU2iA/ozUgbrQ9PoQ0DH0R4rBMAFIfwF/
+                foDhTzi/Hzo0zNCqdOSYRBepeivky9MXSbVGl4vkGVuDZuGihUsWPd0Vghi3
+                kMXVNbAA13B9DUagfhMBEgH6QjAQIBOCNK2/ALLTKT3nBAAA
+                """,
+        """
+                androidx/navigation/OuterComp.class:
+                H4sIAAAAAAAA/41RW2sTQRT+ZjaXzWZt03hpYm2rtmpTxW2LT7UINagsxC3Y
+                EpA8TZIlnWQzW3Y2oY958of4D4oPBQUJ+uaPEs9uo0WE4g57vjm378w558fP
+                z18BPMNjhmWhulEou6eOEmPZE7EMlXMwiv2oHg5P8mAMpb4YCycQqucctPt+
+                J87DYMjtSSXjFwzGRq1pI4uchQzyDJn4WGqG1caVzM8ZzL1OkHJY4Emi6XqH
+                R/te/ZWNa7AKZJxjWGuEUc/p+3E7ElJpRygVximXdrww9kZBQFQLjUEYE5nz
+                1o9FV8SCbHw4NqhLlohCIsDABmQ/lYm2RbfuNkNtOrEtXuEWL00nFjcN8/sH
+                XplOdvgW2+VG5mXe5N8+5niJJwk7LKGZc5WiNgKhddILQzE1XEyH4cmVna//
+                nZzHKj3iPzJ+z/4elfPE+E0kTo6fDqjc0ruRiuXQd9VYatkO/P3LGdE66mHX
+                Z5hvSOV7o2Hbj44ExTCUG2FHBE0RyUSfGe3Lx/mUbB2Go6jjv5aJrzqr0/yn
+                CrZpWZl0wtVkd4TrpOUIS4ScTjbVHpDmJHsgzG6ewzxL3Q9nwYCLRyTtiwAU
+                iAowUfyTvEjRyVf8Av7+HPYnzJ+lBgMbJMvkvkv/Mr3jPuEKYS0tsYZNwl2i
+                WSDicguGi+subri4iVt0xaKLCqotMI3bWGohq2Fp3NHIaSxrrPwCZxVYTTUD
+                AAA=
+                """,
+        """
                 androidx/navigation/TestAbstract.class:
-                H4sIAAAAAAAA/4VRy04CMRQ9LTDKiAo+wUeiLoy6cJS402jQREOCmChhw6ow
+                H4sIAAAAAAAA/4VRy04CMRQ9LTDKiAo+wUeiLoy6cJS40xjRREOCmChhw6ow
                 Ey2PjpmWiUu+xT9wZeLCEJd+lPF2ZO/m5Dxum3Pb75+PTwAn2GTYEsqPQum/
                 eErE8lEYGSqvEWhTaWsTiY6ZAmPId0UsvL5Qj95duxtYN8XgnEklzTlDam+/
                 mUMGjos0phjS5klqhp3af5efMhRqvdD0pfJuAyN8YQR5fBCnqCCzkLUABtYj
@@ -778,37 +1134,132 @@
                 /HTYM9TvKvQDhvmaVEF9OGgHUUO0++Qs1MKO6DdFJK2emO5DOIw6wbW0onQ/
                 VEYOgqbUktKKUqFJ9tLpbXBaf9LWvgZhkZSXaCBz8I7pNyIcJUInMS+wRpj7
                 G0AWbpKvJ7iKjeSbqD5luRZSVcxWMVfFPPJEUahiAYstMI0lLFOu4WqsaDi/
-                +rnGr+MBAAA=
+                +liLRuMBAAA=
                 """,
-            """
+        """
+                androidx/navigation/TestAbstractComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ99qR5TAOkLY+EV3kEqUWi01TsipBKEChSWiRa
+                ZdMFciamdTLjqWwn6jIrPoQ/6AqJBYq65KMQ15MAW7q5r3PPvfaxf/76/gPA
+                Szxj2BJ6YDI1OI+0mKgT4VSmoyNp3V7fOiNi187Ss6Y3QhNUAmOoDcVERInQ
+                J9GH/lDGroSAofhKaeVeMwQbm70qllAMUUCJoeBOlWXY7l5t1S5Da6M7ylyi
+                dDScpJHSThotkuit/CzGCbVr4o1jl5l9YUbS7G72QnC/cq0Z/wM/pTlKd73a
+                NIaVP4R96cRAOEE1nk4CEo95U/EGDGxE9XPls22KBi2G5mwahrzOQ16jaDYt
+                X34J6rPpDt9mb0plfvm1yGvc9+4wP6H5P9qUcI+h8lcghuUDMXlvxNnp1siR
+                zu1sIBludJWWB+O0L82R6CdUWe1msUh6wiifL4rVjtbStBNhraTXCQ+zsYnl
+                O+WxxsexdiqVPWUVNe9pnbn8SBYtErjgb02e+0emw69TFnkZyC89/4byRQ4/
+                IlvMi4d4TLY6b0AFIVBjFC0vyC/I8wW5epFL6gm358U5IY+u4TphAZ5QFuak
+                +3iABp7mCx+imX9r0oB6a8cIOljpYLWDNdykELc6NPPOMZhFHQ3CLUKLuxbF
+                3++scPYTAwAA
+                """,
+        """
+                androidx/navigation/TestAbstractComp.class:
+                H4sIAAAAAAAA/41RW08TQRg9s9vrspUWb0W8gFQEHlggJiZCTBCjaVJKIoTE
+                8DRtxzLtdpbMzDY89rf4D4gPJJqYxkd/lPHbpcKDL335znyXc77L/P7z/SeA
+                V1hnqHHV0ZHsXASKD2WXWxmp4FgYu9cyVvO23Y8G53kwhnKPD3kQctUNDls9
+                0bZ5uAy5Xamkfcvgrq6d+Mgi5yGDPEPGnknDsNKYpsEOQ2G3HU6kNqah1BLD
+                FaXy8Bm2Vhv9yJJC0BsOAqms0IqHwXvxhcchERQx47aN9AHXfaF3roe946GE
+                WYbijRjD5lQT37bf8VHBXBEO7jIsNyLdDXrCtjSXygRcqcimCiZoRrYZhyHt
+                Wvk364GwvMMtp5gzGLr0KSwxxcSAgfUpfiETb5NenS2653jke07V8ZzyeOQ5
+                BaewUh2PFt1tZ5O9Ye677K+vOafsJNXbLNGYafLhR83Pzzb6lmHhU6ysHIi6
+                GkojW6HYux2Q/mw/6giG2YZUohkPWkIfc6phmGtEbR6ecC0TfxL060oJvR9y
+                YwSRvaMo1m3xQSa5+Umfk/+6ZJboUpl0vfnkcITL5OUI7xM6hNnUq5EXJEcg
+                zK5foXCZpl9MioEjrJD1rwtQhEdYwMwNuYr0jPB/oPSZXaH8Dfcu04iLl2Q9
+                qiuRYoUGWU21n2ON8DXFH5Diw1O4dVTrmK/jERboicd1PMHTUzCDZ1g8RcbA
+                M1gyyBlU/gL0PzRmVwMAAA==
+                """,
+        """
                 androidx/navigation/TestClass.class:
-                H4sIAAAAAAAA/31Ru07DMBQ9dmgKoUBaXuW9AgMBxAZCAiRQpAASoC5MbmMV
-                t6mDYrfq2G/hD5iQGFDFyEchbgIzy9F5XFvn2l/f7x8AjrDBsCF0nKUqHgZa
-                DFRbWJXq4EEae5EIY8pgDH5HDESQCN0Obpsd2bJlOAzuidLKnjI42zuNCkpw
-                PUygzDBhn5Rh2Ir+vfmYoRp1U5soHVxLK2JhBXm8N3CoGsthKgcwsC75Q5Wr
-                fWLxAcPmeOR5vM497hMbj+rj0SHfZ+elzxeX+zyfOmT52ekbMbjKxPPTXtdS
-                tYs0lgxzkdLypt9ryuxBNBNyalHaEklDZCrXf6Z3n/azlrxUuVi562urerKh
-                jKL0TOvUFisZHIDT5n9d84cgrJMKCg2Udt8w+UqEY4XQLcwAq4SV3wFMwSvy
-                tQKXsV58D9WnrPIIJ8RMiNkQc/CJohqihvlHMIMFLFJu4BksGbg/2IItFdsB
+                H4sIAAAAAAAA/31Ru07DMBQ9dmhKQ4GUZ3mvwEDaig2EBEigSAEkQF2Y3CYC
+                09RBsVsx9lv4AyYkBlQx8lGI68DMcnQe19a59tf3+weAfWwwbAgV55mMnwMl
+                hvJeGJmp4DbR5jQVWpfBGPxHMRRBKtR9cNV5TLqmDIfBPZRKmiMGZ3unXUUJ
+                rocJlBkmzIPUDFvRvzcfMNSiXmZSqYKLxIhYGEEe7w8dqsYsVCyAgfXIf5ZW
+                NYjFTYbN8cjzeJ173Cc2HtXHoxZvsJPS54vLfW6nWsyenboUw/NcPD3s9QxV
+                O83ihGE2kiq5HPQ7SX4rOik5c1HWFWlb5NLqP9O7yQZ5NzmTVqxcD5SR/aQt
+                taT0WKnMFCtpNMFp87+u9iEI66SCQgOl3TdMvhLhWCF0C7OBVcLq7wAq8Ip8
+                rcBlrBffQ/Upq97BCTEdYibELHyiqIWYw/wdmMYCFinX8DSWNNwfvyv/UtsB
                 AAA=
                 """,
-            """
-                androidx/navigation/TestClassWithArg.class:
-                H4sIAAAAAAAA/41QTW/TQBScXSeO4ybECV9pChRoVbU54LTiBqoIkUCWQpFK
-                FQ45bWIr2caxkXcT9ZjfwpkLEgiJA4o48qMQb52KEwcke96b59E8v/n1+/sP
-                AE+xz7AvkjBLZXjlJ2IpJ0LLNPEvIqV7sVDqvdTTbjYpgTF4l2Ip/FgkE//t
-                6DIa6xIsBvu5TKQ+ZSgcBkcDBuvwaFBBESUXBTjERTZhYEEFLrbK4KiQVE+l
-                Yjjo/8/uZ7RjEumusSHzgKHen6U6lon/JtIiFFqQhM+XFp3EDJQNgJbOaH4l
-                DetQFx4z9Narhsub3OXeeuXSwz3H5Y7VXK9OeIe9rDZsj7d4x/r50eZe4bz+
-                lzmkbhWcomcbqxNmFmydieXrTHyYPplpuqqXhhFDrS+T6GwxH0XZhRjFNGn0
-                07GIByKThl8P3XfpIhtHr6Qh2+eLRMt5NJBK0tdukqQ6T0PhmCIr5Oc0TILU
-                ceqLsAl3iZ0S51Td9jeU2ztfUf2cax4SGg3I4RHhnY0KN1Az6VBn3ChMePRu
-                vHwTGtVi+wuqn/5pU9kIrm04Huf4AHtUX+Q/WcTNIawAtwLcDmjtXWrRDLCN
-                1hBMYQf3higp1BTuK7g52gqeQv0Pkn5Ve5gCAAA=
+        """
+                androidx/navigation/TestClassComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ99qR5TAOkLY+Ed9sgtaB2mopdERKEhyKlrQRV
+                Nl0gJzGtkxlPZTtRl1nxIfxBV0gsUNQlH4W4ngRYom6u7z3nnns9x/Pz1/cf
+                AJ7jCcMzofsmVf3zSIuxOhFOpTo6ktY1Y2FtM03O6j4ITXgBjKEyEGMRxUKf
+                RIfdgey5AgKG/AullXvJEGxsdspYQD5EDgWGnDtVlmGrfYU9ewyNjfYwdbHS
+                0WCcREo7abSIozfysxjFrplq68yo51KzL8xQmr3NTgju963Ue//IT0nGMmxf
+                bRrD0h/BvnSiL5wgjCfjgGxjPpR8AAMbEn6ufLVDWb/BUJ9OwpBXecgrlE0n
+                xcsvQXU62eU77HWhyC+/5nmF+95d5ies/teYAu4xlP66w7B4IMbvjTg73R46
+                criZ9iXDjbbS8mCUdKU5Et2YkOV22hNxRxjl6zlYbmktTTZb0ruEH9OR6cl3
+                ynO1DyPtVCI7yipqfqV16rL7WDTI3Zz/ZDq5f166+SOqIu8BnQtPv6F4kdGP
+                KeYz8C1WKZZnDSghBCqMssW5eItOPheXLzI/veD2DJwJsuwarhMXYI2qMBPd
+                xwPUsJ4tfIh69jeTB9RbOUbQwlILyy2s4CaluNWimXeOwSyqqBFvEVrctcj/
+                Bj3L7PkKAwAA
                 """,
-            """
+        """
+                androidx/navigation/TestClassComp.class:
+                H4sIAAAAAAAA/4VRXU8TQRQ9s9vPZZEWv4r4AYJaMLqUmJgIMdH6kU1KTZQ0
+                MX2atiNMu50lM9MNj/wW/wHxgUQTQ3z0RxnvLhUefODlnrlnzj33zp3ff77/
+                BPAM6wzLXA10LAeHgeKJ3ONWxirYFcY2I25MMx4fFMEYKkOe8CDiai/40BuK
+                vi3CZShsSyXtSwa3vtbxkUfBQw5Fhpzdl4ZhpXWp+xZDabsfTX0eX6pfTQNX
+                xBfhMzTqrVFsqTwYJuNAKiu04lHwRnzhk8g2Y2WsnvRtrHe4Hgm9dTbmFQ+z
+                mGMon5sxPLl81oveWz6qmC/DwdX0lbHeC4bC9jSXygRcqdhm5SZox7Y9iSJ6
+                ZfXfoDvC8gG3nDhnnLj0ESwN5TSAgY2IP5RptkGnQYNh9fTI95ya4zmV0yPP
+                KTm106Mld9PZYC+Y+zr/62vBqTipdpOlDjNtnrzX/GD/6cgyLH6cKCvHIlSJ
+                NLIXiVcX49FHNeOBYJhrSSXak3FP6F1OGob5VtznUYdrmeZT0g+VEjrbh6Bi
+                71M80X3xTqZ3C9M+nf+6oEF7ymWPW0jXRrhCWYHwOqFDmM+yVcqCdAWE+fUT
+                lI6z6wdTMfAWDyn6ZwKU4RGWMHNeXEO2RPg/MPuZnaDyDdeOM8bFI4oe6WbJ
+                sUqD1DPv+1gjfE78DXK82YUbohZiIcQtLNIRt0Pcwd0umME9LHWRM/AMlg0K
+                BtW/kzCVQUkDAAA=
+                """,
+        """
+                androidx/navigation/TestClassWithArg.class:
+                H4sIAAAAAAAA/41QTY/SUBQ977WU0gEp+MUw6vgxMTMsLEPcaSYiiaYJjsk4
+                wQWrBzTlDdCavgeZJb/FtRsTjYkLQ1z6o4z3lYkrFybtuffcnpzbe379/v4D
+                wFMcMByIZJKlcnIZJGIlY6FlmgTnkdK9uVDqvdTTbhYXwRj8C7ESwVwkcfB2
+                dBGNdREWg/NcJlKfMNiH4dGAwTo8GpRRQNGDDZe4yGIGFpbhYacEjjJJ9VQq
+                hsf9/9n9jHbEke4aGzIPGWr9WarnMgneRFpMhBYk4YuVRScxAyUDoKUzml9K
+                w9rUTY4Zept13eMN7nF/s/bo4b7rcddqbNYd3mYvK3XH503etn5+dLhvn9X+
+                MpfUTdst+I6x6jCzYOdUrF5n4sP0yUzTVb10EjFU+zKJTpeLUZSdi9GcJvV+
+                Ohbzgcik4VdD7126zMbRK2nI7tky0XIRDaSS9LWbJKnO01A4psjs/Jy6SZA6
+                Tn0BDuE+sRPinKrX+oZSa+8rKp9zzX1CowE6eEB4a6vCNVRNOtQZNwoTPr1b
+                r8CERrXQ+oLKp3/alLeCKxuOhznewyOqL/KfLOD6EFaIGyFuhrT2NrVohNhF
+                cwimsIc7QxQVqgp3FbwcHQVfofYHzU1tNpgCAAA=
+                """,
+        """
+                androidx/navigation/TestClassWithArgComp$Companion.class:
+                H4sIAAAAAAAA/5VSTW8TMRB93k3zsQ2QtnwkfBeClCLRbaLeipBKEFWktEi0
+                CoceKicxiZNdb2U7UY858UP4Bz0hcUBRj/woxHgT4Eov45n35s2sn/fnr+8/
+                AOziBUODq75OZP8iVHwqB9zKRIUnwthmxI35JO1wXw+aSXxedYEronNgDKUR
+                n/Iw4moQfuiORM/m4DNkX0sl7RsGv7bVKWIF2QAZ5BgydigNw277+uv2GOq1
+                9jixkVThaBqHUlmhFY/Cd+Izn0S2mShj9aRnE33I9Vjova1OAM+t3aj2/pFn
+                ccoybF9vGsPaH8GhsLzPLSfMi6c+mchcKLgABjYm/EK6aoeyfp2hOp8FgVf2
+                Aq9E2XyWv/ril+ezhrfD3uby3tXXrFfyXG+DuQm1//UnhwcMhb8mMawe8emB
+                5ufD7bElv5tJXzDcaksljiZxV+gT3o0IWW8nPR51uJauXoLFllJCpysEvVJw
+                nEx0T7yXjqt8nCgrY9GRRlLzvlKJTT/LoE4mZ9zN6fTcY9MFnlAVOivoXHn5
+                DfnLlH5KMZuCB9ikWFw0oIAAKDHKVpfiV3R6S3HxMrXVCe4uwIUgzW7gJnE+
+                nlEVpKKHeIQKnqcLH6Oa/uLkAfWWTuG3sNbCegsbuE0p7rRo5r1TMIMyKsQb
+                BAb3DbK/Ae9ImxYfAwAA
+                """,
+        """
+                androidx/navigation/TestClassWithArgComp.class:
+                H4sIAAAAAAAA/41SW08TQRT+ZnvZ7VJkqYgFvKCglqpsITwJIcEadZNSEyQ1
+                hqdpO5Zpt7Nkd9rwyG/x2ReihkQTQ3z0RxnPbis86APJ7jlzzpzzfecyv35/
+                +wFgA2sMJa7aYSDbx67iQ9nhWgbK3ReRrvo8it5JfbgTdqpB/8gEY3C6fMhd
+                n6uO+6bZFS1tIsWQ3ZJK6m2GdMlbaTCkSiuNPDIwbaRhkc3DDgPz8rAxkYOB
+                PIXqQxkxlGtX5d8kno7QOzEUEXgM1lbLHxOvXxVlORZc0bWJ6wxrpVov0ITi
+                dod9VyotQsV994X4wAe+rgYq0uGgpYNwl4c9EW6O+rphYwazDLkLMIaNKzdy
+                WcJmHkXMxQOZZ1iqBWHH7QrdDLlUkcuVCnSCErn1QNcHvk8jmP5b767QvM01
+                J5/RH6ZonSwWuViAht0j/7GMrQqd2rTp1+cnBdsoGrbhnJ/Y9BmOZRtWunh+
+                smiuGxX2jJnPJwtZx5g3KqmfH7OGk96bvrAsSplPWxknG+Ots5hlos6Hr0J+
+                dLja0wwLewOlZV94aigj2fTFzmULtPBq0BYMUzWpRH3Qb4pwn1MMQ6EWtLjf
+                4KGM7bEz7yklwmR0gpLtt8EgbImXMr6bG/M0/mHBGs0yTT0bmItHSyWWycqS
+                vkW6EL8/0imyM4n3MVnbFG2QtstnyJUXvmLyNEF4Ms4EXuEpydlRFK5hKp4x
+                nWI0Wgkc+kdYbjx60pnyF0x++i9MfhQwhrGoKHOcXESyPOS/Y+Y9O8PNz1g4
+                TTwprCaEjN6dkTTmJtgrqJCukv82Id45QMrDXQ+LHu7hPh2x5GEZDw7AIjzE
+                owNYEaYilCLYicxGcCJMRyj+AVa+iyYTBAAA
+                """,
+        """
                 androidx/navigation/TestGraph.class:
-                H4sIAAAAAAAA/32S32oTQRTGv5kkm80m2rT+aWKtVduLquC2xTuLUIvKwrqC
+                H4sIAAAAAAAA/32S32oTQRTGv5kkm80m2rT+aWKtVduLKuK2xTuLUIvKwrqC
                 DQHp1SQ7pJNsZmV3svQyVz6Ib1C8KChI0DsfSjyzBr0Q3IVzzvfNmR97Dvvj
                 5+evAJ5gh2FT6DhLVXzua1GokTAq1X5P5uZVJt6f1cEY2mNRCD8ReuS/GYzl
                 0NRRYXAOlVbmGUNl90G/hRocD1XUGarmTOUMW+F/yU8Z3MNhUjI8cHvRDaKT
@@ -818,9 +1269,9 @@
                 kmElVFpGs+lAZj1BPQxrYToUSV9kyuql6Z2ks2woXyoruktw/x8s9mkx1XKa
                 rt0T5TukHMptypzeWqm2SPl2Zsq1h5dwL8rju8tm4BHuUWz9bkCDUICL5p/L
                 69Rtn+YX8HeXaH3CykVpcNwv4ya2y9+I9k+AtVNUAlwLcD3ADdykEusBOuie
-                guW4hQ06z+HluJ3D+QVOlj18gwIAAA==
+                guW4hQ06z+HluJ3D+QXkdVzrgwIAAA==
                 """,
-            """
+        """
                 androidx/navigation/TestInterface.class:
                 H4sIAAAAAAAA/4WOz07CQBDGv9kqheKfopLg0Xi3QLx58qJpgpqo8dLT0C64
                 tGxNd2k48lweDGcfyrjFB3Am+eabmeQ38/3z+QXgGn3CBeusKlW2jjTXas5W
@@ -829,24 +1280,18 @@
                 95Hr+4o/3q9ySwheylWVyjtVSML580pbtZRvyqhpIW+1Lu2OY1ruCvbwFwKn
                 Oz3Bmasjx9x32UrgxfBjtGN0EDiLbowDHCYggyMcJxAGoUHvFzaSAvhMAQAA
                 """,
-            """
+        """
                 androidx/navigation/TestObject.class:
-                H4sIAAAAAAAA/32Sz27TQBDGv90kjuMEGsqfJhTaQnsAJHBbcaNCKhUgS8ZI
-                NIqEetrEq3QTZ43sjdVjTjwIb1BxqAQSiuDGQyFmTYADErY0M9+3sz95Rv7+
-                49MXAI+xw7AhdJylKj7ztSjUSBiVar8nc/N6MJZDUwdjaI9FIfxE6JH/260w
-                OAdKK/OUoXLvfr+FGhwPVdQZquZU5Qxb4f/RTxjcg2FSQjxwe9MNouPeYXT0
-                vIVL8BpkXmbYDtNs5I+lGWRC6dwXWqemhOV+lJpoliSEuhJOUkMw/5U0IhZG
-                kMenRYXmZDY0bAADm5B/pqzapSreY9hZzD2Pd7jH21Qt5u6397yzmO/zXfas
-                7vKvHxze5rZ3n1lCMxLFy0y8O300MQzrb2baqKkMdKFyNUjk4d/Po1UcpbFk
-                WAmVltFsOpBZT1APw2qYDkXSF5myeml6x+ksG8oXyoruEtz/B4s9Wky1nKZr
-                90R5g5RDuU2Z01sr1SYp385MufbgAu55eby1bAYe4g7F1q8GNAgFuGj+ubxG
-                3fZpfgZ/e4HWR6yclwbH3TLexnb5I9H+CbB6gkqAqwGuBbiOG1RiLUAH3ROw
-                HDexTuc5vBy3cjg/AW9Vq0qFAgAA
+                H4sIAAAAAAAA/32Sz27TQBDGv90kjuMEGsqfJhRKoT0Ah7qtuFEhlQqQJWMk
+                GkWqetrEq3QTZ43sjdVjTjwIb1BxqAQSiuDGQyFmTYADErY0M9+3sz95Rv7+
+                49MXAE+wzbAhdJylKj73tSjUSBiVar8nc/NmMJZDUwdjaI9FIfxE6JH/260w
+                OAdKK/OMofLwUb+FGhwPVdQZquZM5Qyb4f/RTxncg2FSQjxwe9MNouPeYXT0
+                ooUr8BpkXmXYCtNs5I+lGWRC6dwXWqemhOV+lJpoliSEuhZOUkMw/7U0IhZG
+                kMenRYXmZDY0bAADm5B/rqzapSreY9hezD2Pd7jH21Qt5u6397yzmO/zXfa8
+                7vKvHxze5rZ3n1lCMxLFq0y8O9uZGIb1tzNt1FQGulC5GiTy8O/n0SqO0lgy
+                rIRKy2g2HcisJ6iHYTVMhyLpi0xZvTS943SWDeVLZUV3Ce7/g8UeLaZaTtO1
+                e6K8Qcqh3KbM6a2V6h4p385Mufb4Eu5Feby5bAZ2cJ9i61cDGoQCXDT/XF6j
+                bvs0P4OfXKL1ESsXpcHxoIx3sVX+SLR/AqyeohLgeoAbAW7iFpVYC9BB9xQs
+                x22s03kOL8edHM5P0Fqg1oUCAAA=
                 """
-        )
-
-    override fun getDetector(): Detector = WrongStartDestinationTypeDetector()
-
-    override fun getIssues(): MutableList<Issue> =
-        mutableListOf(WrongStartDestinationTypeDetector.WrongStartDestinationType)
-}
+    )
diff --git a/navigation/navigation-common/api/2.8.0-beta06.txt b/navigation/navigation-common/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..bb6ad19
--- /dev/null
+++ b/navigation/navigation-common/api/2.8.0-beta06.txt
@@ -0,0 +1,594 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+    ctor public ActionOnlyNavDirections(int actionId);
+    method public int component1();
+    method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+    method public int getActionId();
+    method public android.os.Bundle getArguments();
+    property public int actionId;
+    property public android.os.Bundle arguments;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+    ctor public AnimBuilder();
+    method public int getEnter();
+    method public int getExit();
+    method public int getPopEnter();
+    method public int getPopExit();
+    method public void setEnter(int);
+    method public void setExit(int);
+    method public void setPopEnter(int);
+    method public void setPopExit(int);
+    property public final int enter;
+    property public final int exit;
+    property public final int popEnter;
+    property public final int popExit;
+  }
+
+  public abstract class CollectionNavType<T> extends androidx.navigation.NavType<T> {
+    ctor public CollectionNavType(boolean isNullableAllowed);
+    method public abstract T emptyCollection();
+    method public abstract java.util.List<java.lang.String> serializeAsValues(T value);
+  }
+
+  public interface FloatingWindow {
+  }
+
+  public final class NamedNavArgument {
+    method public operator String component1();
+    method public operator androidx.navigation.NavArgument component2();
+    method public androidx.navigation.NavArgument getArgument();
+    method public String getName();
+    property public final androidx.navigation.NavArgument argument;
+    property public final String name;
+  }
+
+  public final class NamedNavArgumentKt {
+    method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavAction {
+    ctor public NavAction(@IdRes int destinationId);
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+    method public android.os.Bundle? getDefaultArguments();
+    method public int getDestinationId();
+    method public androidx.navigation.NavOptions? getNavOptions();
+    method public void setDefaultArguments(android.os.Bundle?);
+    method public void setNavOptions(androidx.navigation.NavOptions?);
+    property public final android.os.Bundle? defaultArguments;
+    property public final int destinationId;
+    property public final androidx.navigation.NavOptions? navOptions;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+    ctor public NavActionBuilder();
+    method public java.util.Map<java.lang.String,java.lang.Object?> getDefaultArguments();
+    method public int getDestinationId();
+    method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+    method public void setDestinationId(int);
+    property public final java.util.Map<java.lang.String,java.lang.Object?> defaultArguments;
+    property public final int destinationId;
+  }
+
+  public interface NavArgs {
+  }
+
+  public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+    ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+    method public Args getValue();
+    method public boolean isInitialized();
+    property public Args value;
+  }
+
+  public final class NavArgument {
+    method public Object? getDefaultValue();
+    method public androidx.navigation.NavType<java.lang.Object?> getType();
+    method public boolean isDefaultValuePresent();
+    method public boolean isNullable();
+    property public final Object? defaultValue;
+    property public final boolean isDefaultValuePresent;
+    property public final boolean isNullable;
+    property public final androidx.navigation.NavType<java.lang.Object?> type;
+  }
+
+  public static final class NavArgument.Builder {
+    ctor public NavArgument.Builder();
+    method public androidx.navigation.NavArgument build();
+    method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+    method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+    method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+    ctor public NavArgumentBuilder();
+    method public androidx.navigation.NavArgument build();
+    method public Object? getDefaultValue();
+    method public boolean getNullable();
+    method public androidx.navigation.NavType<? extends java.lang.Object?> getType();
+    method public void setDefaultValue(Object?);
+    method public void setNullable(boolean);
+    method public void setType(androidx.navigation.NavType<? extends java.lang.Object?>);
+    property public final Object? defaultValue;
+    property public final boolean nullable;
+    property public final androidx.navigation.NavType<? extends java.lang.Object?> type;
+  }
+
+  public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+    method public android.os.Bundle? getArguments();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public androidx.navigation.NavDestination getDestination();
+    method public String getId();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method @MainThread public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    property public final android.os.Bundle? arguments;
+    property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+    property public final androidx.navigation.NavDestination destination;
+    property public final String id;
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property @MainThread public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+    property public androidx.lifecycle.ViewModelStore viewModelStore;
+    field public static final androidx.navigation.NavBackStackEntry.Companion Companion;
+  }
+
+  public static final class NavBackStackEntry.Companion {
+  }
+
+  public final class NavBackStackEntryKt {
+    method public static inline <reified T> T toRoute(androidx.navigation.NavBackStackEntry);
+  }
+
+  public final class NavDeepLink {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public static final class NavDeepLink.Builder {
+    method public androidx.navigation.NavDeepLink build();
+    method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+    method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+    method public inline <reified T> androidx.navigation.NavDeepLink.Builder setUriPattern(String basePath, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+  }
+
+  @kotlin.DslMarker public @interface NavDeepLinkDsl {
+  }
+
+  @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+    ctor public NavDeepLinkDslBuilder();
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    method public void setAction(String?);
+    method public void setMimeType(String?);
+    method public void setUriPattern(String?);
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public final class NavDeepLinkDslBuilderKt {
+    method public static inline <reified T> androidx.navigation.NavDeepLink navDeepLink(String basePath, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+    method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+  }
+
+  public class NavDeepLinkRequest {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public android.net.Uri? getUri();
+    property public String? action;
+    property public String? mimeType;
+    property public android.net.Uri? uri;
+  }
+
+  public static final class NavDeepLinkRequest.Builder {
+    method public androidx.navigation.NavDeepLinkRequest build();
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+    field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+  }
+
+  public static final class NavDeepLinkRequest.Builder.Companion {
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+  }
+
+  public class NavDestination {
+    ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    ctor public NavDestination(String navigatorName);
+    method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+    method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+    method public final void addDeepLink(String uriPattern);
+    method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+    method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+    method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+    method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method @IdRes public final int getId();
+    method public final CharSequence? getLabel();
+    method public final String getNavigatorName();
+    method public final androidx.navigation.NavGraph? getParent();
+    method public final String? getRoute();
+    method public boolean hasDeepLink(android.net.Uri deepLink);
+    method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+    method public static final <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
+    method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+    method protected static final <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
+    method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+    method public final void putAction(@IdRes int actionId, @IdRes int destId);
+    method public final void removeAction(@IdRes int actionId);
+    method public final void removeArgument(String argumentName);
+    method public final void setId(@IdRes int);
+    method public final void setLabel(CharSequence?);
+    method public final void setRoute(String?);
+    property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+    property @IdRes public final int id;
+    property public final CharSequence? label;
+    property public final String navigatorName;
+    property public final androidx.navigation.NavGraph? parent;
+    property public final String? route;
+    field public static final androidx.navigation.NavDestination.Companion Companion;
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+    method public abstract Class<? extends java.lang.Object?> value();
+    property public abstract Class<? extends java.lang.Object?> value;
+  }
+
+  public static final class NavDestination.Companion {
+    method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method public inline <reified T> boolean hasRoute(androidx.navigation.NavDestination);
+    method public <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
+    method protected <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+    method public final void argument(String name, androidx.navigation.NavArgument argument);
+    method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+    method public D build();
+    method public final void deepLink(androidx.navigation.NavDeepLink navDeepLink);
+    method public final void deepLink(String uriPattern);
+    method public inline <reified T> void deepLink(String basePath, kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+    method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+    method public inline <reified T> void deepLinkSafeArgs(String basePath);
+    method public final int getId();
+    method public final CharSequence? getLabel();
+    method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+    method public final String? getRoute();
+    method protected D instantiateDestination();
+    method public final void setLabel(CharSequence?);
+    property public final int id;
+    property public final CharSequence? label;
+    property protected final androidx.navigation.Navigator<? extends D> navigator;
+    property public final String? route;
+  }
+
+  @kotlin.DslMarker public @interface NavDestinationDsl {
+  }
+
+  public interface NavDirections {
+    method @IdRes public int getActionId();
+    method public android.os.Bundle getArguments();
+    property @IdRes public abstract int actionId;
+    property public abstract android.os.Bundle arguments;
+  }
+
+  public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+    method public final void addAll(androidx.navigation.NavGraph other);
+    method public final void addDestination(androidx.navigation.NavDestination node);
+    method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+    method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination?> nodes);
+    method public final void clear();
+    method public inline <reified T> androidx.navigation.NavDestination? findNode();
+    method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+    method public final androidx.navigation.NavDestination? findNode(String? route);
+    method public final <T> androidx.navigation.NavDestination? findNode(T? route);
+    method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+    method @Deprecated @IdRes public final int getStartDestination();
+    method @IdRes public final int getStartDestinationId();
+    method public final String? getStartDestinationRoute();
+    method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+    method public final void remove(androidx.navigation.NavDestination node);
+    method public inline <reified T> void setStartDestination();
+    method public final void setStartDestination(int startDestId);
+    method public final void setStartDestination(String startDestRoute);
+    method public final <T> void setStartDestination(T startDestRoute);
+    property @IdRes public final int startDestinationId;
+    property public final String? startDestinationRoute;
+    field public static final androidx.navigation.NavGraph.Companion Companion;
+  }
+
+  public static final class NavGraph.Companion {
+    method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, Object startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public final void addDestination(androidx.navigation.NavDestination destination);
+    method public androidx.navigation.NavGraph build();
+    method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+    method public final androidx.navigation.NavigatorProvider getProvider();
+    method public final operator void unaryPlus(androidx.navigation.NavDestination);
+    property public final androidx.navigation.NavigatorProvider provider;
+  }
+
+  public final class NavGraphBuilderKt {
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, Object startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavGraphKt {
+    method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+    method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+    method public static inline operator <reified T> boolean contains(androidx.navigation.NavGraph, kotlin.reflect.KClass<T> route);
+    method public static operator <T> boolean contains(androidx.navigation.NavGraph, T route);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+    method public static inline operator <reified T> androidx.navigation.NavDestination get(androidx.navigation.NavGraph, kotlin.reflect.KClass<T> route);
+    method public static inline operator <T> androidx.navigation.NavDestination get(androidx.navigation.NavGraph, T route);
+    method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+    ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph createDestination();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  public final class NavOptions {
+    method @AnimRes @AnimatorRes public int getEnterAnim();
+    method @AnimRes @AnimatorRes public int getExitAnim();
+    method @AnimRes @AnimatorRes public int getPopEnterAnim();
+    method @AnimRes @AnimatorRes public int getPopExitAnim();
+    method @Deprecated @IdRes public int getPopUpTo();
+    method @IdRes public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public kotlin.reflect.KClass<? extends java.lang.Object?>? getPopUpToRouteClass();
+    method public Object? getPopUpToRouteObject();
+    method public boolean isPopUpToInclusive();
+    method public boolean shouldLaunchSingleTop();
+    method public boolean shouldPopUpToSaveState();
+    method public boolean shouldRestoreState();
+    property @AnimRes @AnimatorRes public final int enterAnim;
+    property @AnimRes @AnimatorRes public final int exitAnim;
+    property @AnimRes @AnimatorRes public final int popEnterAnim;
+    property @AnimRes @AnimatorRes public final int popExitAnim;
+    property @IdRes public final int popUpToId;
+    property public final String? popUpToRoute;
+    property public final kotlin.reflect.KClass<? extends java.lang.Object?>? popUpToRouteClass;
+    property public final Object? popUpToRouteObject;
+  }
+
+  public static final class NavOptions.Builder {
+    ctor public NavOptions.Builder();
+    method public androidx.navigation.NavOptions build();
+    method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+    method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+    method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+    method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+    method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+    method public inline <reified T> androidx.navigation.NavOptions.Builder setPopUpTo(boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+    method public <T> androidx.navigation.NavOptions.Builder setPopUpTo(T route, boolean inclusive);
+    method public <T> androidx.navigation.NavOptions.Builder setPopUpTo(T route, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+    ctor public NavOptionsBuilder();
+    method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+    method public boolean getLaunchSingleTop();
+    method @Deprecated public int getPopUpTo();
+    method public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public kotlin.reflect.KClass<? extends java.lang.Object?>? getPopUpToRouteClass();
+    method public Object? getPopUpToRouteObject();
+    method public boolean getRestoreState();
+    method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public inline <reified T> void popUpTo(optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public <T> void popUpTo(T route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void setLaunchSingleTop(boolean);
+    method @Deprecated public void setPopUpTo(int);
+    method public void setRestoreState(boolean);
+    property public final boolean launchSingleTop;
+    property @Deprecated public final int popUpTo;
+    property public final int popUpToId;
+    property public final String? popUpToRoute;
+    property public final kotlin.reflect.KClass<? extends java.lang.Object?>? popUpToRouteClass;
+    property public final Object? popUpToRouteObject;
+    property public final boolean restoreState;
+  }
+
+  public final class NavOptionsBuilderKt {
+    method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+  }
+
+  @kotlin.DslMarker public @interface NavOptionsDsl {
+  }
+
+  public abstract class NavType<T> {
+    ctor public NavType(boolean isNullableAllowed);
+    method public static androidx.navigation.NavType<? extends java.lang.Object?> fromArgType(String? type, String? packageName);
+    method public abstract operator T? get(android.os.Bundle bundle, String key);
+    method public String getName();
+    method public boolean isNullableAllowed();
+    method public abstract T parseValue(String value);
+    method public T parseValue(String value, T previousValue);
+    method public abstract void put(android.os.Bundle bundle, String key, T value);
+    method public String serializeAsValue(T value);
+    method public boolean valueEquals(T value, T other);
+    property public boolean isNullableAllowed;
+    property public String name;
+    field public static final androidx.navigation.NavType<boolean[]?> BoolArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Boolean>?> BoolListType;
+    field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+    field public static final androidx.navigation.NavType.Companion Companion;
+    field public static final androidx.navigation.NavType<float[]?> FloatArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Float>?> FloatListType;
+    field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+    field public static final androidx.navigation.NavType<int[]?> IntArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Integer>?> IntListType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+    field public static final androidx.navigation.NavType<long[]?> LongArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Long>?> LongListType;
+    field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+    field public static final androidx.navigation.NavType<java.lang.String[]?> StringArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.String>?> StringListType;
+    field public static final androidx.navigation.NavType<java.lang.String?> StringType;
+  }
+
+  public static final class NavType.Companion {
+    method public androidx.navigation.NavType<? extends java.lang.Object?> fromArgType(String? type, String? packageName);
+  }
+
+  public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+    ctor public NavType.EnumType(Class<D> type);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]?> {
+    ctor public NavType.ParcelableArrayType(Class<D> type);
+    method public D[]? get(android.os.Bundle bundle, String key);
+    method public D[] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D[]? value);
+    method public boolean valueEquals(D[]? value, D[]? other);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+    ctor public NavType.ParcelableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]?> {
+    ctor public NavType.SerializableArrayType(Class<D> type);
+    method public D[]? get(android.os.Bundle bundle, String key);
+    method public D[] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D[]? value);
+    method public boolean valueEquals(D[]? value, D[]? other);
+    property public String name;
+  }
+
+  public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+    ctor public NavType.SerializableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+    ctor public Navigator();
+    method public abstract D createDestination();
+    method protected final androidx.navigation.NavigatorState getState();
+    method public final boolean isAttached();
+    method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+    method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void onRestoreState(android.os.Bundle savedState);
+    method public android.os.Bundle? onSaveState();
+    method public boolean popBackStack();
+    method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+    property public final boolean isAttached;
+    property protected final androidx.navigation.NavigatorState state;
+  }
+
+  public static interface Navigator.Extras {
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+    method public abstract String value();
+    property public abstract String value;
+  }
+
+  public class NavigatorProvider {
+    ctor public NavigatorProvider();
+    method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+    method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+  }
+
+  public final class NavigatorProviderKt {
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+    method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+  }
+
+  public abstract class NavigatorState {
+    ctor public NavigatorState();
+    method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+    method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+    ctor public PopUpToBuilder();
+    method public boolean getInclusive();
+    method public boolean getSaveState();
+    method public void setInclusive(boolean);
+    method public void setSaveState(boolean);
+    property public final boolean inclusive;
+    property public final boolean saveState;
+  }
+
+  public final class SavedStateHandleKt {
+    method public static inline <reified T> T toRoute(androidx.lifecycle.SavedStateHandle, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-common/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-common/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-common/api/restricted_2.8.0-beta06.txt b/navigation/navigation-common/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..bb6ad19
--- /dev/null
+++ b/navigation/navigation-common/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,594 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActionOnlyNavDirections implements androidx.navigation.NavDirections {
+    ctor public ActionOnlyNavDirections(int actionId);
+    method public int component1();
+    method public androidx.navigation.ActionOnlyNavDirections copy(int actionId);
+    method public int getActionId();
+    method public android.os.Bundle getArguments();
+    property public int actionId;
+    property public android.os.Bundle arguments;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class AnimBuilder {
+    ctor public AnimBuilder();
+    method public int getEnter();
+    method public int getExit();
+    method public int getPopEnter();
+    method public int getPopExit();
+    method public void setEnter(int);
+    method public void setExit(int);
+    method public void setPopEnter(int);
+    method public void setPopExit(int);
+    property public final int enter;
+    property public final int exit;
+    property public final int popEnter;
+    property public final int popExit;
+  }
+
+  public abstract class CollectionNavType<T> extends androidx.navigation.NavType<T> {
+    ctor public CollectionNavType(boolean isNullableAllowed);
+    method public abstract T emptyCollection();
+    method public abstract java.util.List<java.lang.String> serializeAsValues(T value);
+  }
+
+  public interface FloatingWindow {
+  }
+
+  public final class NamedNavArgument {
+    method public operator String component1();
+    method public operator androidx.navigation.NavArgument component2();
+    method public androidx.navigation.NavArgument getArgument();
+    method public String getName();
+    property public final androidx.navigation.NavArgument argument;
+    property public final String name;
+  }
+
+  public final class NamedNavArgumentKt {
+    method public static androidx.navigation.NamedNavArgument navArgument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavAction {
+    ctor public NavAction(@IdRes int destinationId);
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions);
+    ctor public NavAction(@IdRes int destinationId, optional androidx.navigation.NavOptions? navOptions, optional android.os.Bundle? defaultArguments);
+    method public android.os.Bundle? getDefaultArguments();
+    method public int getDestinationId();
+    method public androidx.navigation.NavOptions? getNavOptions();
+    method public void setDefaultArguments(android.os.Bundle?);
+    method public void setNavOptions(androidx.navigation.NavOptions?);
+    property public final android.os.Bundle? defaultArguments;
+    property public final int destinationId;
+    property public final androidx.navigation.NavOptions? navOptions;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavActionBuilder {
+    ctor public NavActionBuilder();
+    method public java.util.Map<java.lang.String,java.lang.Object?> getDefaultArguments();
+    method public int getDestinationId();
+    method public void navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+    method public void setDestinationId(int);
+    property public final java.util.Map<java.lang.String,java.lang.Object?> defaultArguments;
+    property public final int destinationId;
+  }
+
+  public interface NavArgs {
+  }
+
+  public final class NavArgsLazy<Args extends androidx.navigation.NavArgs> implements kotlin.Lazy<Args> {
+    ctor public NavArgsLazy(kotlin.reflect.KClass<Args> navArgsClass, kotlin.jvm.functions.Function0<android.os.Bundle> argumentProducer);
+    method public Args getValue();
+    method public boolean isInitialized();
+    property public Args value;
+  }
+
+  public final class NavArgument {
+    method public Object? getDefaultValue();
+    method public androidx.navigation.NavType<java.lang.Object?> getType();
+    method public boolean isDefaultValuePresent();
+    method public boolean isNullable();
+    property public final Object? defaultValue;
+    property public final boolean isDefaultValuePresent;
+    property public final boolean isNullable;
+    property public final androidx.navigation.NavType<java.lang.Object?> type;
+  }
+
+  public static final class NavArgument.Builder {
+    ctor public NavArgument.Builder();
+    method public androidx.navigation.NavArgument build();
+    method public androidx.navigation.NavArgument.Builder setDefaultValue(Object? defaultValue);
+    method public androidx.navigation.NavArgument.Builder setIsNullable(boolean isNullable);
+    method public <T> androidx.navigation.NavArgument.Builder setType(androidx.navigation.NavType<T> type);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class NavArgumentBuilder {
+    ctor public NavArgumentBuilder();
+    method public androidx.navigation.NavArgument build();
+    method public Object? getDefaultValue();
+    method public boolean getNullable();
+    method public androidx.navigation.NavType<? extends java.lang.Object?> getType();
+    method public void setDefaultValue(Object?);
+    method public void setNullable(boolean);
+    method public void setType(androidx.navigation.NavType<? extends java.lang.Object?>);
+    property public final Object? defaultValue;
+    property public final boolean nullable;
+    property public final androidx.navigation.NavType<? extends java.lang.Object?> type;
+  }
+
+  public final class NavBackStackEntry implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+    method public android.os.Bundle? getArguments();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+    method public androidx.navigation.NavDestination getDestination();
+    method public String getId();
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method @MainThread public androidx.lifecycle.SavedStateHandle getSavedStateHandle();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method public androidx.lifecycle.ViewModelStore getViewModelStore();
+    property public final android.os.Bundle? arguments;
+    property public androidx.lifecycle.viewmodel.CreationExtras defaultViewModelCreationExtras;
+    property public androidx.lifecycle.ViewModelProvider.Factory defaultViewModelProviderFactory;
+    property public final androidx.navigation.NavDestination destination;
+    property public final String id;
+    property public androidx.lifecycle.Lifecycle lifecycle;
+    property @MainThread public final androidx.lifecycle.SavedStateHandle savedStateHandle;
+    property public androidx.savedstate.SavedStateRegistry savedStateRegistry;
+    property public androidx.lifecycle.ViewModelStore viewModelStore;
+    field public static final androidx.navigation.NavBackStackEntry.Companion Companion;
+  }
+
+  public static final class NavBackStackEntry.Companion {
+  }
+
+  public final class NavBackStackEntryKt {
+    method public static inline <reified T> T toRoute(androidx.navigation.NavBackStackEntry);
+  }
+
+  public final class NavDeepLink {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public static final class NavDeepLink.Builder {
+    method public androidx.navigation.NavDeepLink build();
+    method public static androidx.navigation.NavDeepLink.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLink.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLink.Builder fromUriPattern(String uriPattern);
+    method public androidx.navigation.NavDeepLink.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
+    method public inline <reified T> androidx.navigation.NavDeepLink.Builder setUriPattern(String basePath, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+  }
+
+  @kotlin.DslMarker public @interface NavDeepLinkDsl {
+  }
+
+  @androidx.navigation.NavDeepLinkDsl public final class NavDeepLinkDslBuilder {
+    ctor public NavDeepLinkDslBuilder();
+    method public String? getAction();
+    method public String? getMimeType();
+    method public String? getUriPattern();
+    method public void setAction(String?);
+    method public void setMimeType(String?);
+    method public void setUriPattern(String?);
+    property public final String? action;
+    property public final String? mimeType;
+    property public final String? uriPattern;
+  }
+
+  public final class NavDeepLinkDslBuilderKt {
+    method public static inline <reified T> androidx.navigation.NavDeepLink navDeepLink(String basePath, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+    method public static androidx.navigation.NavDeepLink navDeepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> deepLinkBuilder);
+  }
+
+  public class NavDeepLinkRequest {
+    method public String? getAction();
+    method public String? getMimeType();
+    method public android.net.Uri? getUri();
+    property public String? action;
+    property public String? mimeType;
+    property public android.net.Uri? uri;
+  }
+
+  public static final class NavDeepLinkRequest.Builder {
+    method public androidx.navigation.NavDeepLinkRequest build();
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public static androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder setUri(android.net.Uri uri);
+    field public static final androidx.navigation.NavDeepLinkRequest.Builder.Companion Companion;
+  }
+
+  public static final class NavDeepLinkRequest.Builder.Companion {
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromAction(String action);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromMimeType(String mimeType);
+    method public androidx.navigation.NavDeepLinkRequest.Builder fromUri(android.net.Uri uri);
+  }
+
+  public class NavDestination {
+    ctor public NavDestination(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    ctor public NavDestination(String navigatorName);
+    method public final void addArgument(String argumentName, androidx.navigation.NavArgument argument);
+    method public final void addDeepLink(androidx.navigation.NavDeepLink navDeepLink);
+    method public final void addDeepLink(String uriPattern);
+    method public final String? fillInLabel(android.content.Context context, android.os.Bundle? bundle);
+    method public final androidx.navigation.NavAction? getAction(@IdRes int id);
+    method public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> getArguments();
+    method public static final kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method @IdRes public final int getId();
+    method public final CharSequence? getLabel();
+    method public final String getNavigatorName();
+    method public final androidx.navigation.NavGraph? getParent();
+    method public final String? getRoute();
+    method public boolean hasDeepLink(android.net.Uri deepLink);
+    method public boolean hasDeepLink(androidx.navigation.NavDeepLinkRequest deepLinkRequest);
+    method public static final <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
+    method @CallSuper public void onInflate(android.content.Context context, android.util.AttributeSet attrs);
+    method protected static final <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
+    method public final void putAction(@IdRes int actionId, androidx.navigation.NavAction action);
+    method public final void putAction(@IdRes int actionId, @IdRes int destId);
+    method public final void removeAction(@IdRes int actionId);
+    method public final void removeArgument(String argumentName);
+    method public final void setId(@IdRes int);
+    method public final void setLabel(CharSequence?);
+    method public final void setRoute(String?);
+    property public final java.util.Map<java.lang.String,androidx.navigation.NavArgument> arguments;
+    property @IdRes public final int id;
+    property public final CharSequence? label;
+    property public final String navigatorName;
+    property public final androidx.navigation.NavGraph? parent;
+    property public final String? route;
+    field public static final androidx.navigation.NavDestination.Companion Companion;
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface NavDestination.ClassType {
+    method public abstract Class<? extends java.lang.Object?> value();
+    property public abstract Class<? extends java.lang.Object?> value;
+  }
+
+  public static final class NavDestination.Companion {
+    method public kotlin.sequences.Sequence<androidx.navigation.NavDestination> getHierarchy(androidx.navigation.NavDestination);
+    method public inline <reified T> boolean hasRoute(androidx.navigation.NavDestination);
+    method public <T> boolean hasRoute(androidx.navigation.NavDestination, kotlin.reflect.KClass<T> route);
+    method protected <C> Class<? extends C?> parseClassFromName(android.content.Context context, String name, Class<? extends C?> expectedClassType);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavDestinationBuilder<D extends androidx.navigation.NavDestination> {
+    ctor @Deprecated public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, @IdRes int id);
+    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, String? route);
+    ctor public NavDestinationBuilder(androidx.navigation.Navigator<? extends D> navigator, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method @Deprecated public final void action(int actionId, kotlin.jvm.functions.Function1<? super androidx.navigation.NavActionBuilder,kotlin.Unit> actionBuilder);
+    method public final void argument(String name, androidx.navigation.NavArgument argument);
+    method public final void argument(String name, kotlin.jvm.functions.Function1<? super androidx.navigation.NavArgumentBuilder,kotlin.Unit> argumentBuilder);
+    method public D build();
+    method public final void deepLink(androidx.navigation.NavDeepLink navDeepLink);
+    method public final void deepLink(String uriPattern);
+    method public inline <reified T> void deepLink(String basePath, kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+    method public final void deepLink(kotlin.jvm.functions.Function1<? super androidx.navigation.NavDeepLinkDslBuilder,kotlin.Unit> navDeepLink);
+    method public inline <reified T> void deepLinkSafeArgs(String basePath);
+    method public final int getId();
+    method public final CharSequence? getLabel();
+    method protected final androidx.navigation.Navigator<? extends D> getNavigator();
+    method public final String? getRoute();
+    method protected D instantiateDestination();
+    method public final void setLabel(CharSequence?);
+    property public final int id;
+    property public final CharSequence? label;
+    property protected final androidx.navigation.Navigator<? extends D> navigator;
+    property public final String? route;
+  }
+
+  @kotlin.DslMarker public @interface NavDestinationDsl {
+  }
+
+  public interface NavDirections {
+    method @IdRes public int getActionId();
+    method public android.os.Bundle getArguments();
+    property @IdRes public abstract int actionId;
+    property public abstract android.os.Bundle arguments;
+  }
+
+  public class NavGraph extends androidx.navigation.NavDestination implements java.lang.Iterable<androidx.navigation.NavDestination> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public NavGraph(androidx.navigation.Navigator<? extends androidx.navigation.NavGraph> navGraphNavigator);
+    method public final void addAll(androidx.navigation.NavGraph other);
+    method public final void addDestination(androidx.navigation.NavDestination node);
+    method public final void addDestinations(androidx.navigation.NavDestination... nodes);
+    method public final void addDestinations(java.util.Collection<? extends androidx.navigation.NavDestination?> nodes);
+    method public final void clear();
+    method public inline <reified T> androidx.navigation.NavDestination? findNode();
+    method public final androidx.navigation.NavDestination? findNode(@IdRes int resId);
+    method public final androidx.navigation.NavDestination? findNode(String? route);
+    method public final <T> androidx.navigation.NavDestination? findNode(T? route);
+    method public static final androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+    method @Deprecated @IdRes public final int getStartDestination();
+    method @IdRes public final int getStartDestinationId();
+    method public final String? getStartDestinationRoute();
+    method public final java.util.Iterator<androidx.navigation.NavDestination> iterator();
+    method public final void remove(androidx.navigation.NavDestination node);
+    method public inline <reified T> void setStartDestination();
+    method public final void setStartDestination(int startDestId);
+    method public final void setStartDestination(String startDestRoute);
+    method public final <T> void setStartDestination(T startDestRoute);
+    property @IdRes public final int startDestinationId;
+    property public final String? startDestinationRoute;
+    field public static final androidx.navigation.NavGraph.Companion Companion;
+  }
+
+  public static final class NavGraph.Companion {
+    method public androidx.navigation.NavDestination findStartDestination(androidx.navigation.NavGraph);
+  }
+
+  @androidx.navigation.NavDestinationDsl public class NavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.NavGraph> {
+    ctor @Deprecated public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, Object startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, String? route);
+    ctor public NavGraphBuilder(androidx.navigation.NavigatorProvider provider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public final void addDestination(androidx.navigation.NavDestination destination);
+    method public androidx.navigation.NavGraph build();
+    method public final <D extends androidx.navigation.NavDestination> void destination(androidx.navigation.NavDestinationBuilder<? extends D> navDestination);
+    method public final androidx.navigation.NavigatorProvider getProvider();
+    method public final operator void unaryPlus(androidx.navigation.NavDestination);
+    property public final androidx.navigation.NavigatorProvider provider;
+  }
+
+  public final class NavGraphBuilderKt {
+    method @Deprecated public static inline void navigation(androidx.navigation.NavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, Object startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavGraphKt {
+    method public static operator boolean contains(androidx.navigation.NavGraph, @IdRes int id);
+    method public static operator boolean contains(androidx.navigation.NavGraph, String route);
+    method public static inline operator <reified T> boolean contains(androidx.navigation.NavGraph, kotlin.reflect.KClass<T> route);
+    method public static operator <T> boolean contains(androidx.navigation.NavGraph, T route);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);
+    method public static inline operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, String route);
+    method public static inline operator <reified T> androidx.navigation.NavDestination get(androidx.navigation.NavGraph, kotlin.reflect.KClass<T> route);
+    method public static inline operator <T> androidx.navigation.NavDestination get(androidx.navigation.NavGraph, T route);
+    method public static inline operator void minusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavDestination node);
+    method public static inline operator void plusAssign(androidx.navigation.NavGraph, androidx.navigation.NavGraph other);
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public class NavGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.NavGraph> {
+    ctor public NavGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph createDestination();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  public final class NavOptions {
+    method @AnimRes @AnimatorRes public int getEnterAnim();
+    method @AnimRes @AnimatorRes public int getExitAnim();
+    method @AnimRes @AnimatorRes public int getPopEnterAnim();
+    method @AnimRes @AnimatorRes public int getPopExitAnim();
+    method @Deprecated @IdRes public int getPopUpTo();
+    method @IdRes public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public kotlin.reflect.KClass<? extends java.lang.Object?>? getPopUpToRouteClass();
+    method public Object? getPopUpToRouteObject();
+    method public boolean isPopUpToInclusive();
+    method public boolean shouldLaunchSingleTop();
+    method public boolean shouldPopUpToSaveState();
+    method public boolean shouldRestoreState();
+    property @AnimRes @AnimatorRes public final int enterAnim;
+    property @AnimRes @AnimatorRes public final int exitAnim;
+    property @AnimRes @AnimatorRes public final int popEnterAnim;
+    property @AnimRes @AnimatorRes public final int popExitAnim;
+    property @IdRes public final int popUpToId;
+    property public final String? popUpToRoute;
+    property public final kotlin.reflect.KClass<? extends java.lang.Object?>? popUpToRouteClass;
+    property public final Object? popUpToRouteObject;
+  }
+
+  public static final class NavOptions.Builder {
+    ctor public NavOptions.Builder();
+    method public androidx.navigation.NavOptions build();
+    method public androidx.navigation.NavOptions.Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim);
+    method public androidx.navigation.NavOptions.Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim);
+    method public androidx.navigation.NavOptions.Builder setLaunchSingleTop(boolean singleTop);
+    method public androidx.navigation.NavOptions.Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim);
+    method public androidx.navigation.NavOptions.Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim);
+    method public inline <reified T> androidx.navigation.NavOptions.Builder setPopUpTo(boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(@IdRes int destinationId, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive);
+    method public androidx.navigation.NavOptions.Builder setPopUpTo(String? route, boolean inclusive, optional boolean saveState);
+    method public <T> androidx.navigation.NavOptions.Builder setPopUpTo(T route, boolean inclusive);
+    method public <T> androidx.navigation.NavOptions.Builder setPopUpTo(T route, boolean inclusive, optional boolean saveState);
+    method public androidx.navigation.NavOptions.Builder setRestoreState(boolean restoreState);
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class NavOptionsBuilder {
+    ctor public NavOptionsBuilder();
+    method public void anim(kotlin.jvm.functions.Function1<? super androidx.navigation.AnimBuilder,kotlin.Unit> animBuilder);
+    method public boolean getLaunchSingleTop();
+    method @Deprecated public int getPopUpTo();
+    method public int getPopUpToId();
+    method public String? getPopUpToRoute();
+    method public kotlin.reflect.KClass<? extends java.lang.Object?>? getPopUpToRouteClass();
+    method public Object? getPopUpToRouteObject();
+    method public boolean getRestoreState();
+    method public void popUpTo(@IdRes int id, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void popUpTo(String route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public inline <reified T> void popUpTo(optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public <T> void popUpTo(T route, optional kotlin.jvm.functions.Function1<? super androidx.navigation.PopUpToBuilder,kotlin.Unit> popUpToBuilder);
+    method public void setLaunchSingleTop(boolean);
+    method @Deprecated public void setPopUpTo(int);
+    method public void setRestoreState(boolean);
+    property public final boolean launchSingleTop;
+    property @Deprecated public final int popUpTo;
+    property public final int popUpToId;
+    property public final String? popUpToRoute;
+    property public final kotlin.reflect.KClass<? extends java.lang.Object?>? popUpToRouteClass;
+    property public final Object? popUpToRouteObject;
+    property public final boolean restoreState;
+  }
+
+  public final class NavOptionsBuilderKt {
+    method public static androidx.navigation.NavOptions navOptions(kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> optionsBuilder);
+  }
+
+  @kotlin.DslMarker public @interface NavOptionsDsl {
+  }
+
+  public abstract class NavType<T> {
+    ctor public NavType(boolean isNullableAllowed);
+    method public static androidx.navigation.NavType<? extends java.lang.Object?> fromArgType(String? type, String? packageName);
+    method public abstract operator T? get(android.os.Bundle bundle, String key);
+    method public String getName();
+    method public boolean isNullableAllowed();
+    method public abstract T parseValue(String value);
+    method public T parseValue(String value, T previousValue);
+    method public abstract void put(android.os.Bundle bundle, String key, T value);
+    method public String serializeAsValue(T value);
+    method public boolean valueEquals(T value, T other);
+    property public boolean isNullableAllowed;
+    property public String name;
+    field public static final androidx.navigation.NavType<boolean[]?> BoolArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Boolean>?> BoolListType;
+    field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
+    field public static final androidx.navigation.NavType.Companion Companion;
+    field public static final androidx.navigation.NavType<float[]?> FloatArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Float>?> FloatListType;
+    field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
+    field public static final androidx.navigation.NavType<int[]?> IntArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Integer>?> IntListType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
+    field public static final androidx.navigation.NavType<long[]?> LongArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.Long>?> LongListType;
+    field public static final androidx.navigation.NavType<java.lang.Long> LongType;
+    field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
+    field public static final androidx.navigation.NavType<java.lang.String[]?> StringArrayType;
+    field public static final androidx.navigation.NavType<java.util.List<java.lang.String>?> StringListType;
+    field public static final androidx.navigation.NavType<java.lang.String?> StringType;
+  }
+
+  public static final class NavType.Companion {
+    method public androidx.navigation.NavType<? extends java.lang.Object?> fromArgType(String? type, String? packageName);
+  }
+
+  public static final class NavType.EnumType<D extends java.lang.Enum<?>> extends androidx.navigation.NavType.SerializableType<D> {
+    ctor public NavType.EnumType(Class<D> type);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableArrayType<D extends android.os.Parcelable> extends androidx.navigation.NavType<D[]?> {
+    ctor public NavType.ParcelableArrayType(Class<D> type);
+    method public D[]? get(android.os.Bundle bundle, String key);
+    method public D[] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D[]? value);
+    method public boolean valueEquals(D[]? value, D[]? other);
+    property public String name;
+  }
+
+  public static final class NavType.ParcelableType<D> extends androidx.navigation.NavType<D> {
+    ctor public NavType.ParcelableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public static final class NavType.SerializableArrayType<D extends java.io.Serializable> extends androidx.navigation.NavType<D[]?> {
+    ctor public NavType.SerializableArrayType(Class<D> type);
+    method public D[]? get(android.os.Bundle bundle, String key);
+    method public D[] parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D[]? value);
+    method public boolean valueEquals(D[]? value, D[]? other);
+    property public String name;
+  }
+
+  public static class NavType.SerializableType<D extends java.io.Serializable> extends androidx.navigation.NavType<D> {
+    ctor public NavType.SerializableType(Class<D> type);
+    method public D? get(android.os.Bundle bundle, String key);
+    method public D parseValue(String value);
+    method public void put(android.os.Bundle bundle, String key, D value);
+    property public String name;
+  }
+
+  public abstract class Navigator<D extends androidx.navigation.NavDestination> {
+    ctor public Navigator();
+    method public abstract D createDestination();
+    method protected final androidx.navigation.NavigatorState getState();
+    method public final boolean isAttached();
+    method public androidx.navigation.NavDestination? navigate(D destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method public void navigate(java.util.List<androidx.navigation.NavBackStackEntry> entries, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @CallSuper public void onAttach(androidx.navigation.NavigatorState state);
+    method public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void onRestoreState(android.os.Bundle savedState);
+    method public android.os.Bundle? onSaveState();
+    method public boolean popBackStack();
+    method public void popBackStack(androidx.navigation.NavBackStackEntry popUpTo, boolean savedState);
+    property public final boolean isAttached;
+    property protected final androidx.navigation.NavigatorState state;
+  }
+
+  public static interface Navigator.Extras {
+  }
+
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.RUNTIME) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS, kotlin.annotation.AnnotationTarget.CLASS}) public static @interface Navigator.Name {
+    method public abstract String value();
+    property public abstract String value;
+  }
+
+  public class NavigatorProvider {
+    ctor public NavigatorProvider();
+    method public final androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method @CallSuper public androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? addNavigator(String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public final <T extends androidx.navigation.Navigator<?>> T getNavigator(Class<T> navigatorClass);
+    method @CallSuper public <T extends androidx.navigation.Navigator<?>> T getNavigator(String name);
+  }
+
+  public final class NavigatorProviderKt {
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, String name);
+    method public static inline operator <T extends androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);
+    method public static inline operator void plusAssign(androidx.navigation.NavigatorProvider, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+    method public static inline operator androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>? set(androidx.navigation.NavigatorProvider, String name, androidx.navigation.Navigator<? extends androidx.navigation.NavDestination> navigator);
+  }
+
+  public abstract class NavigatorState {
+    ctor public NavigatorState();
+    method public abstract androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> getTransitionsInProgress();
+    method public void markTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    method @CallSuper public void onLaunchSingleTop(androidx.navigation.NavBackStackEntry backStackEntry);
+    method @CallSuper public void onLaunchSingleTopWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pop(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method public void popWithTransition(androidx.navigation.NavBackStackEntry popUpTo, boolean saveState);
+    method @CallSuper public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+    method public void push(androidx.navigation.NavBackStackEntry backStackEntry);
+    method public void pushWithTransition(androidx.navigation.NavBackStackEntry backStackEntry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.Set<androidx.navigation.NavBackStackEntry>> transitionsInProgress;
+  }
+
+  @androidx.navigation.NavOptionsDsl public final class PopUpToBuilder {
+    ctor public PopUpToBuilder();
+    method public boolean getInclusive();
+    method public boolean getSaveState();
+    method public void setInclusive(boolean);
+    method public void setSaveState(boolean);
+    property public final boolean inclusive;
+    property public final boolean saveState;
+  }
+
+  public final class SavedStateHandleKt {
+    method public static inline <reified T> T toRoute(androidx.lifecycle.SavedStateHandle, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+  }
+
+}
+
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index 3f71e16..6560a1e 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -38,7 +38,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.lifecycle:lifecycle-common:2.6.2")
     api("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
     api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
@@ -77,6 +77,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Navigation-Common"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
index 37556ae..775993d 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
@@ -346,6 +346,40 @@
     }
 
     @Test
+    fun enumType() {
+        @Serializable
+        @SerialName(PATH_SERIAL_NAME)
+        class TestClass(val arg: TestEnum, val arg2: TestEnum)
+
+        val clazz = TestClass(TestEnum.ONE, TestEnum.TWO)
+        assertThatRouteFilledFrom(
+                clazz,
+                listOf(
+                    enumArgument("arg", TestEnum::class.java),
+                    enumArgument("arg2", TestEnum::class.java)
+                )
+            )
+            .isEqualTo("$PATH_SERIAL_NAME/ONE/TWO")
+    }
+
+    @Test
+    fun enumNullableType() {
+        @Serializable
+        @SerialName(PATH_SERIAL_NAME)
+        class TestClass(val arg: TestEnum?, val arg2: TestEnum?)
+
+        val clazz = TestClass(TestEnum.ONE, null)
+        assertThatRouteFilledFrom(
+                clazz,
+                listOf(
+                    enumArgument("arg", TestEnum::class.java),
+                    enumArgument("arg2", TestEnum::class.java)
+                )
+            )
+            .isEqualTo("$PATH_SERIAL_NAME/ONE/null")
+    }
+
+    @Test
     fun customParamType() {
         @Serializable class CustomType
 
@@ -779,7 +813,7 @@
 
 private fun nullableIntArgument(name: String, hasDefaultValue: Boolean = false) =
     navArgument(name) {
-        type = NullableIntType
+        type = InternalNavType.IntNullableType
         nullable = true
         unknownDefaultValuePresent = hasDefaultValue
     }
@@ -791,30 +825,8 @@
         unknownDefaultValuePresent = hasDefaultValue
     }
 
-private val NullableIntType: NavType<Int?> =
-    object : NavType<Int?>(true) {
-        override val name: String
-            get() = "nullable_integer"
-
-        override fun put(bundle: Bundle, key: String, value: Int?) {
-            value?.let { bundle.putInt(key, value) }
-        }
-
-        @Suppress("DEPRECATION")
-        override fun get(bundle: Bundle, key: String): Int? {
-            val value = bundle[key]
-            return value?.let { it as Int }
-        }
-
-        override fun parseValue(value: String): Int? {
-            return if (value == "null") {
-                null
-            } else if (value.startsWith("0x")) {
-                value.substring(2).toInt(16)
-            } else {
-                value.toInt()
-            }
-        }
-
-        override fun serializeAsValue(value: Int?): String = value?.toString() ?: "null"
-    }
+@Serializable
+private enum class TestEnum {
+    ONE,
+    TWO
+}
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/TestUtil.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/TestUtil.kt
index 62a5cb5..6fe7e20 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/TestUtil.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/TestUtil.kt
@@ -73,5 +73,16 @@
         unknownDefaultValuePresent = hasDefaultValue
     }
 
+internal fun <D : Enum<*>> enumArgument(
+    name: String,
+    clazz: Class<D>,
+    hasDefaultValue: Boolean = false
+) =
+    navArgument(name) {
+        type = NavType.EnumType(clazz)
+        nullable = false
+        unknownDefaultValuePresent = hasDefaultValue
+    }
+
 @OptIn(InternalSerializationApi::class)
 internal fun <T> KSerializer<T>.expectedSafeArgsId(): Int = generateHashCode()
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphNavigator.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphNavigator.kt
index 0dda829..79035974 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphNavigator.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavGraphNavigator.kt
@@ -83,7 +83,7 @@
                 "navigation destination $dest is not a direct child of this NavGraph"
             )
         }
-        if (startRoute != null) {
+        if (startRoute != null && startRoute != startDestination.route) {
             val matchingArgs = startDestination.matchDeepLink(startRoute)?.matchingArgs
             if (matchingArgs != null && !matchingArgs.isEmpty) {
                 val bundle = Bundle()
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
index 5496a6f..1d4158a 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
@@ -203,9 +203,8 @@
                             }
                         val isArray = type.endsWith("[]")
                         if (isArray) className = className.substring(0, className.length - 2)
-                        return requireNotNull(
-                            parseSerializableOrParcelableType(className, isArray)
-                        ) {
+                        val clazz = Class.forName(className)
+                        return requireNotNull(parseSerializableOrParcelableType(clazz, isArray)) {
                             "$className is not Serializable or Parcelable."
                         }
                     } catch (e: ClassNotFoundException) {
@@ -218,11 +217,10 @@
 
         @Suppress("UNCHECKED_CAST")
         internal fun parseSerializableOrParcelableType(
-            className: String,
+            clazz: Class<*>,
             isArray: Boolean
-        ): NavType<*>? {
-            val clazz = Class.forName(className)
-            return when {
+        ): NavType<*>? =
+            when {
                 Parcelable::class.java.isAssignableFrom(clazz) -> {
                     if (isArray) {
                         ParcelableArrayType(clazz as Class<Parcelable>)
@@ -236,12 +234,11 @@
                     if (isArray) {
                         SerializableArrayType(clazz as Class<Serializable>)
                     } else {
-                        return SerializableType(clazz as Class<Serializable>)
+                        SerializableType(clazz as Class<Serializable>)
                     }
                 }
                 else -> null
             }
-        }
 
         @Suppress("UNCHECKED_CAST") // needed for cast to NavType<Any>
         @JvmStatic
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt b/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
index 8c0d4c4..130deb0 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
@@ -20,17 +20,23 @@
 
 import android.os.Bundle
 import androidx.navigation.NavType
+import java.io.Serializable
 import kotlin.reflect.KType
 import kotlinx.serialization.ExperimentalSerializationApi
 import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.descriptors.SerialKind
 import kotlinx.serialization.serializerOrNull
 
 /** Marker for Native Kotlin types with either full or partial built-in NavType support */
 private enum class InternalType {
     INT,
+    INT_NULLABLE,
     BOOL,
+    BOOL_NULLABLE,
     FLOAT,
+    FLOAT_NULLABLE,
     LONG,
+    LONG_NULLABLE,
     STRING,
     INT_ARRAY,
     BOOL_ARRAY,
@@ -38,6 +44,8 @@
     LONG_ARRAY,
     ARRAY,
     LIST,
+    ENUM,
+    ENUM_NULLABLE,
     UNKNOWN
 }
 
@@ -53,9 +61,13 @@
     val type =
         when (this.toInternalType()) {
             InternalType.INT -> NavType.IntType
+            InternalType.INT_NULLABLE -> InternalNavType.IntNullableType
             InternalType.BOOL -> NavType.BoolType
+            InternalType.BOOL_NULLABLE -> InternalNavType.BoolNullableType
             InternalType.FLOAT -> NavType.FloatType
+            InternalType.FLOAT_NULLABLE -> InternalNavType.FloatNullableType
             InternalType.LONG -> NavType.LongType
+            InternalType.LONG_NULLABLE -> InternalNavType.LongNullableType
             InternalType.STRING -> NavType.StringType
             InternalType.INT_ARRAY -> NavType.IntArrayType
             InternalType.BOOL_ARRAY -> NavType.BoolArrayType
@@ -76,6 +88,15 @@
                     else -> UNKNOWN
                 }
             }
+            InternalType.ENUM ->
+                NavType.parseSerializableOrParcelableType(getClass(), false) ?: UNKNOWN
+            InternalType.ENUM_NULLABLE -> {
+                val clazz = getClass()
+                if (Enum::class.java.isAssignableFrom(clazz)) {
+                    @Suppress("UNCHECKED_CAST")
+                    InternalNavType.EnumNullableType(clazz as Class<Enum<*>?>)
+                } else UNKNOWN
+            }
             else -> UNKNOWN
         }
     return type
@@ -90,10 +111,15 @@
 private fun SerialDescriptor.toInternalType(): InternalType {
     val serialName = serialName.replace("?", "")
     return when {
-        serialName == "kotlin.Int" -> InternalType.INT
-        serialName == "kotlin.Boolean" -> InternalType.BOOL
-        serialName == "kotlin.Float" -> InternalType.FLOAT
-        serialName == "kotlin.Long" -> InternalType.LONG
+        kind == SerialKind.ENUM -> if (isNullable) InternalType.ENUM_NULLABLE else InternalType.ENUM
+        serialName == "kotlin.Int" ->
+            if (isNullable) InternalType.INT_NULLABLE else InternalType.INT
+        serialName == "kotlin.Boolean" ->
+            if (isNullable) InternalType.BOOL_NULLABLE else InternalType.BOOL
+        serialName == "kotlin.Float" ->
+            if (isNullable) InternalType.FLOAT_NULLABLE else InternalType.FLOAT
+        serialName == "kotlin.Long" ->
+            if (isNullable) InternalType.LONG_NULLABLE else InternalType.LONG
         serialName == "kotlin.String" -> InternalType.STRING
         serialName == "kotlin.IntArray" -> InternalType.INT_ARRAY
         serialName == "kotlin.BooleanArray" -> InternalType.BOOL_ARRAY
@@ -107,10 +133,26 @@
     }
 }
 
+private fun SerialDescriptor.getClass(): Class<*> {
+    var className = serialName.replace("?", "")
+    while (className.contains(".")) {
+        // Support nested Class by incrementally replacing last `.` with `$`
+        // until we find the correct enum class name.
+        className = Regex("(\\.+)(?!.*\\.)").replace(className, "\\$")
+        try {
+            return Class.forName(className)
+        } catch (_: ClassNotFoundException) {}
+    }
+    throw IllegalArgumentException(
+        "Cannot find class with name \"$serialName\". Ensure that the " +
+            "serialName for this argument is the default fully qualified name"
+    )
+}
+
 /**
  * Match the [SerialDescriptor] of a type to a KType
  *
- * Returns true match, false otherwise.
+ * Returns true if match, false otherwise.
  */
 internal fun SerialDescriptor.matchKType(kType: KType): Boolean {
     if (this.isNullable != kType.isMarkedNullable) return false
@@ -121,8 +163,7 @@
             "types. Please use @Serializable or @Serializable(with = ...) on the " +
             "class or object declaration."
     }
-    if (this.hashCode() != kTypeSerializer.descriptor.hashCode()) return false
-    return true
+    return this == kTypeSerializer.descriptor
 }
 
 internal object UNKNOWN : NavType<String>(false) {
@@ -135,3 +176,153 @@
 
     override fun parseValue(value: String): String = "null"
 }
+
+internal object InternalNavType {
+    val IntNullableType =
+        object : NavType<Int?>(true) {
+            override val name: String
+                get() = "integer_nullable"
+
+            override fun put(bundle: Bundle, key: String, value: Int?) {
+                // store null as serializable inside bundle, so that decoder will use the null
+                // instead of default value
+                if (value == null) bundle.putSerializable(key, null)
+                else IntType.put(bundle, key, value)
+            }
+
+            @Suppress("DEPRECATION")
+            override fun get(bundle: Bundle, key: String): Int? {
+                return bundle[key] as? Int
+            }
+
+            override fun parseValue(value: String): Int? {
+                return if (value == "null") null else IntType.parseValue(value)
+            }
+        }
+
+    val BoolNullableType =
+        object : NavType<Boolean?>(true) {
+            override val name: String
+                get() = "boolean_nullable"
+
+            override fun put(bundle: Bundle, key: String, value: Boolean?) {
+                if (value == null) bundle.putSerializable(key, null)
+                else BoolType.put(bundle, key, value)
+            }
+
+            @Suppress("DEPRECATION")
+            override fun get(bundle: Bundle, key: String): Boolean? {
+                return bundle[key] as? Boolean
+            }
+
+            override fun parseValue(value: String): Boolean? {
+                return if (value == "null") null else BoolType.parseValue(value)
+            }
+        }
+
+    val FloatNullableType =
+        object : NavType<Float?>(true) {
+            override val name: String
+                get() = "float_nullable"
+
+            override fun put(bundle: Bundle, key: String, value: Float?) {
+                if (value == null) bundle.putSerializable(key, null)
+                else FloatType.put(bundle, key, value)
+            }
+
+            @Suppress("DEPRECATION")
+            override fun get(bundle: Bundle, key: String): Float? {
+                return bundle[key] as? Float
+            }
+
+            override fun parseValue(value: String): Float? {
+                return if (value == "null") null else FloatType.parseValue(value)
+            }
+        }
+
+    val LongNullableType =
+        object : NavType<Long?>(true) {
+            override val name: String
+                get() = "long_nullable"
+
+            override fun put(bundle: Bundle, key: String, value: Long?) {
+                if (value == null) bundle.putSerializable(key, null)
+                else LongType.put(bundle, key, value)
+            }
+
+            @Suppress("DEPRECATION")
+            override fun get(bundle: Bundle, key: String): Long? {
+                return bundle[key] as? Long
+            }
+
+            override fun parseValue(value: String): Long? {
+                return if (value == "null") null else LongType.parseValue(value)
+            }
+        }
+
+    class EnumNullableType<D : Enum<*>?>(type: Class<D?>) : SerializableNullableType<D?>(type) {
+        private val type: Class<D?>
+
+        /** Constructs a NavType that supports a given Enum type. */
+        init {
+            require(type.isEnum) { "$type is not an Enum type." }
+            this.type = type
+        }
+
+        override val name: String
+            get() = type.name
+
+        override fun parseValue(value: String): D? {
+            return if (value == "null") {
+                null
+            } else {
+                type.enumConstants!!.firstOrNull { constant ->
+                    constant!!.name.equals(value, ignoreCase = true)
+                }
+                    ?: throw IllegalArgumentException(
+                        "Enum value $value not found for type ${type.name}."
+                    )
+            }
+        }
+    }
+
+    // Base Serializable class to support nullable EnumNullableType
+    open class SerializableNullableType<D : Serializable?>(private val type: Class<D?>) :
+        NavType<D?>(true) {
+
+        override val name: String
+            get() = type.name
+
+        init {
+            require(Serializable::class.java.isAssignableFrom(type)) {
+                "$type does not implement Serializable."
+            }
+        }
+
+        override fun put(bundle: Bundle, key: String, value: D?) {
+            bundle.putSerializable(key, type.cast(value))
+        }
+
+        @Suppress("UNCHECKED_CAST", "DEPRECATION")
+        override fun get(bundle: Bundle, key: String): D? {
+            return bundle[key] as? D
+        }
+
+        /**
+         * @throws UnsupportedOperationException since Serializables do not support default values
+         */
+        public override fun parseValue(value: String): D? {
+            throw UnsupportedOperationException("Serializables don't support default values.")
+        }
+
+        public override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is SerializableNullableType<*>) return false
+            return type == other.type
+        }
+
+        public override fun hashCode(): Int {
+            return type.hashCode()
+        }
+    }
+}
diff --git a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
index 4d1190c..ea6f634 100644
--- a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
+++ b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
@@ -53,14 +53,17 @@
     }
 
     @Test
-    fun convertToIntNullableIllegal() {
+    fun convertToIntNullable() {
         @Serializable class TestClass(val arg: Int?)
 
-        val exception =
-            assertFailsWith<IllegalArgumentException> {
-                serializer<TestClass>().generateNavArguments()
+        val converted = serializer<TestClass>().generateNavArguments()
+        val expected =
+            navArgument("arg") {
+                type = InternalNavType.IntNullableType
+                nullable = true
             }
-        assertThat(exception.message).isEqualTo("integer does not allow nullable values")
+        assertThat(converted).containsExactlyInOrder(expected)
+        assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
     }
 
     @Test
@@ -106,14 +109,17 @@
     }
 
     @Test
-    fun convertToBooleanNullableIllegal() {
+    fun convertToBooleanNullable() {
         @Serializable class TestClass(val arg: Boolean?)
 
-        val exception =
-            assertFailsWith<IllegalArgumentException> {
-                serializer<TestClass>().generateNavArguments()
+        val converted = serializer<TestClass>().generateNavArguments()
+        val expected =
+            navArgument("arg") {
+                type = InternalNavType.BoolNullableType
+                nullable = true
             }
-        assertThat(exception.message).isEqualTo("boolean does not allow nullable values")
+        assertThat(converted).containsExactlyInOrder(expected)
+        assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
     }
 
     @Test
@@ -131,14 +137,17 @@
     }
 
     @Test
-    fun convertToFloatNullableIllegal() {
+    fun convertToFloatNullable() {
         @Serializable class TestClass(val arg: Float?)
 
-        val exception =
-            assertFailsWith<IllegalArgumentException> {
-                serializer<TestClass>().generateNavArguments()
+        val converted = serializer<TestClass>().generateNavArguments()
+        val expected =
+            navArgument("arg") {
+                type = InternalNavType.FloatNullableType
+                nullable = true
             }
-        assertThat(exception.message).isEqualTo("float does not allow nullable values")
+        assertThat(converted).containsExactlyInOrder(expected)
+        assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
     }
 
     @Test
@@ -156,14 +165,17 @@
     }
 
     @Test
-    fun convertToLongNullableIllegal() {
+    fun convertToLongNullable() {
         @Serializable class TestClass(val arg: Long?)
 
-        val exception =
-            assertFailsWith<IllegalArgumentException> {
-                serializer<TestClass>().generateNavArguments()
+        val converted = serializer<TestClass>().generateNavArguments()
+        val expected =
+            navArgument("arg") {
+                type = InternalNavType.LongNullableType
+                nullable = true
             }
-        assertThat(exception.message).isEqualTo("long does not allow nullable values")
+        assertThat(converted).containsExactlyInOrder(expected)
+        assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
     }
 
     @Test
@@ -749,21 +761,12 @@
     fun convertToEnum() {
         @Serializable class TestClass(val arg: TestEnum)
 
-        val navType =
-            object : NavType<TestEnum>(false) {
-                override fun put(bundle: Bundle, key: String, value: TestEnum) {}
-
-                override fun get(bundle: Bundle, key: String) = null
-
-                override fun parseValue(value: String) = TestEnum.TEST
-            }
         val expected =
             navArgument("arg") {
-                type = navType
+                type = NavType.EnumType(TestEnum::class.java)
                 nullable = false
             }
-        val converted =
-            serializer<TestClass>().generateNavArguments(mapOf(typeOf<TestEnum>() to navType))
+        val converted = serializer<TestClass>().generateNavArguments()
         assertThat(converted).containsExactlyInOrder(expected)
         assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
     }
@@ -772,29 +775,47 @@
     fun convertToEnumNullable() {
         @Serializable class TestClass(val arg: TestEnum?)
 
-        val navType =
-            object : NavType<TestEnum?>(true) {
-                override val name: String
-                    get() = "TestEnum"
-
-                override fun put(bundle: Bundle, key: String, value: TestEnum?) {}
-
-                override fun get(bundle: Bundle, key: String) = null
-
-                override fun parseValue(value: String) = TestEnum.TEST
-            }
-        val converted =
-            serializer<TestClass>().generateNavArguments(mapOf(typeOf<TestEnum?>() to navType))
+        @Suppress("UNCHECKED_CAST")
         val expected =
             navArgument("arg") {
-                type = navType
+                type = InternalNavType.EnumNullableType(TestEnum::class.java as Class<Enum<*>?>)
                 nullable = true
             }
+        val converted = serializer<TestClass>().generateNavArguments()
         assertThat(converted).containsExactlyInOrder(expected)
         assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
     }
 
     @Test
+    fun convertToNestedEnum() {
+        @Serializable class TestClass(val arg: EnumWrapper.NestedEnum)
+
+        val expected =
+            navArgument("arg") {
+                type = NavType.EnumType(EnumWrapper.NestedEnum::class.java)
+                nullable = false
+            }
+        val converted = serializer<TestClass>().generateNavArguments()
+        assertThat(converted).containsExactlyInOrder(expected)
+        assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+    }
+
+    @Test
+    fun convertToEnumOverriddenSerialNameIllegal() {
+        @Serializable class TestClass(val arg: TestEnumCustomSerialName)
+
+        val exception =
+            assertFailsWith<IllegalArgumentException> {
+                serializer<TestClass>().generateNavArguments()
+            }
+        assertThat(exception.message)
+            .isEqualTo(
+                "Cannot find class with name \"MyCustomSerialName\". Ensure that the " +
+                    "serialName for this argument is the default fully qualified name"
+            )
+    }
+
+    @Test
     fun convertToEnumArray() {
         @Serializable class TestClass(val arg: Array<TestEnum>)
         val navType =
@@ -1313,8 +1334,20 @@
         } else true
     }
 
-    @Serializable
     enum class TestEnum {
         TEST
     }
+
+    @SerialName("MyCustomSerialName")
+    enum class TestEnumCustomSerialName {
+        TEST
+    }
+
+    @Serializable
+    private class EnumWrapper {
+        enum class NestedEnum {
+            ONE,
+            TWO
+        }
+    }
 }
diff --git a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavTypeConverterTest.kt b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavTypeConverterTest.kt
index 258467b..5ebbcf0 100644
--- a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavTypeConverterTest.kt
+++ b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavTypeConverterTest.kt
@@ -694,7 +694,7 @@
     @Test
     fun getNavTypeEnumSerializable() {
         val type = serializer<TestEnum>().descriptor.getNavType()
-        assertThat(type).isEqualTo(UNKNOWN)
+        assertThat(type).isEqualTo(NavType.EnumType(TestEnum::class.java))
     }
 
     @Test
diff --git a/navigation/navigation-compose-lint/build.gradle b/navigation/navigation-compose-lint/build.gradle
index ba09d9a..4548102 100644
--- a/navigation/navigation-compose-lint/build.gradle
+++ b/navigation/navigation-compose-lint/build.gradle
@@ -35,7 +35,7 @@
     compileOnly(libs.androidLintMinApi)
     compileOnly(libs.kotlinStdlib)
     bundleInside(projectOrArtifact(":compose:lint:common"))
-    bundleInside(projectOrArtifact(":navigation:navigation-common-lint"))
+    bundleInside(projectOrArtifact(":navigation:navigation-lint-common"))
 
     testImplementation(projectOrArtifact(":compose:lint:common-test"))
     testImplementation(libs.kotlinStdlib)
diff --git a/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetector.kt b/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetector.kt
index e20baec..ca68f28 100644
--- a/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetector.kt
+++ b/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetector.kt
@@ -16,8 +16,8 @@
 
 package androidx.navigation.compose.lint
 
-import androidx.navigation.common.lint.BaseWrongStartDestinationTypeDetector
-import androidx.navigation.common.lint.createWrongStartDestinationTypeIssue
+import androidx.navigation.lint.common.BaseWrongStartDestinationTypeDetector
+import androidx.navigation.lint.common.createWrongStartDestinationTypeIssue
 
 class WrongStartDestinationTypeDetector :
     BaseWrongStartDestinationTypeDetector(
diff --git a/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetectorTest.kt b/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetectorTest.kt
index 5892633..aa425c4 100644
--- a/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetectorTest.kt
+++ b/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/WrongStartDestinationTypeDetectorTest.kt
@@ -18,12 +18,22 @@
 
 import androidx.compose.lint.test.Stubs
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.bytecode
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
+import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.checks.infrastructure.TestMode
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 
-class WrongStartDestinationTypeDetectorTest : LintDetectorTest() {
+@RunWith(Parameterized::class)
+class WrongStartDestinationTypeDetectorTest(private val testFile: TestFile) : LintDetectorTest() {
+
+    private companion object {
+        @JvmStatic @Parameterized.Parameters public fun data() = listOf(SOURCECODE, BYTECODE)
+    }
 
     @Test
     fun testEmptyConstructorNoError() {
@@ -40,11 +50,12 @@
                 fun createGraph() {
                     val controller = NavHostController()
                     NavHost(navController = controller, startDestination = TestClass())
+                    NavHost(navController = controller, startDestination = TestClassComp())
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
                 Stubs.Composable
             )
             .skipTestModes(TestMode.FULLY_QUALIFIED)
@@ -74,11 +85,17 @@
                     NavHost(navController = controller, startDestination = Outer.InnerClass(15)) {}
                     NavHost(navController = controller, startDestination = InterfaceChildClass(true)) {}
                     NavHost(navController = controller, startDestination = AbstractChildClass(true)) {}
+                    NavHost(navController = controller, startDestination = TestClassWithArgComp(15)) {}
+                    NavHost(navController = controller, startDestination = TestClassComp::class) {}
+                    NavHost(navController = controller, startDestination = OuterComp.InnerClassComp::class) {}
+                    NavHost(navController = controller, startDestination = OuterComp.InnerClassComp(15)) {}
+                    NavHost(navController = controller, startDestination = InterfaceChildClassComp(true)) {}
+                    NavHost(navController = controller, startDestination = AbstractChildClassComp(true)) {}
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
                 Stubs.Composable
             )
             .run()
@@ -106,11 +123,13 @@
                     NavHost(navController = controller, startDestination = Outer.InnerObject::class) {}
                     NavHost(navController = controller, startDestination = InterfaceChildObject) {}
                     NavHost(navController = controller, startDestination = AbstractChildObject) {}
+                    NavHost(navController = controller, startDestination = OuterComp.InnerObject::class) {}
+                    NavHost(navController = controller, startDestination = AbstractChildObjectComp) {}
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
                 Stubs.Composable
             )
             .run()
@@ -136,11 +155,20 @@
                     NavHost(navController = controller, startDestination = Outer.InnerClass) {}
                     NavHost(navController = controller, startDestination = InterfaceChildClass) {}
                     NavHost(navController = controller, startDestination = AbstractChildClass) {}
+                    NavHost(navController = controller, startDestination = TestInterface)
+                    NavHost(navController = controller, startDestination = TestAbstract)
+                    // classes with companion object to simulate marked with @Serializable
+                    NavHost(navController = controller, startDestination = TestClassComp)
+                    NavHost(navController = controller, startDestination = TestClassWithArgComp)
+                    NavHost(navController = controller, startDestination = OuterComp.InnerClassComp)
+                    NavHost(navController = controller, startDestination = InterfaceChildClassComp)
+                    NavHost(navController = controller, startDestination = AbstractChildClassComp)
+                    NavHost(navController = controller, startDestination = TestAbstractComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
                 Stubs.Composable
             )
             .run()
@@ -176,14 +204,69 @@
 you can also pass in its KClass reference AbstractChildClass::class [WrongStartDestinationType]
     NavHost(navController = controller, startDestination = AbstractChildClass) {}
                                                            ~~~~~~~~~~~~~~~~~~
-5 errors, 0 warnings
+src/com/example/test.kt:14: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor TestInterface(...)?
+If the class TestInterface does not contain arguments,
+you can also pass in its KClass reference TestInterface::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = TestInterface)
+                                                           ~~~~~~~~~~~~~
+src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor TestAbstract(...)?
+If the class TestAbstract does not contain arguments,
+you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = TestAbstract)
+                                                           ~~~~~~~~~~~~
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = TestClassComp)
+                                                           ~~~~~~~~~~~~~
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = TestClassWithArgComp)
+                                                           ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = OuterComp.InnerClassComp)
+                                                           ~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:20: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = InterfaceChildClassComp)
+                                                           ~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:21: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = AbstractChildClassComp)
+                                                           ~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:22: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    NavHost(navController = controller, startDestination = TestAbstractComp)
+                                                           ~~~~~~~~~~~~~~~~
+13 errors, 0 warnings
             """
             )
     }
 
-    // Stub
-    private val sourceCode =
-        """
+    override fun getDetector(): Detector = WrongStartDestinationTypeDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(WrongStartDestinationTypeDetector.WrongStartDestinationType)
+}
+
+// Stub
+private val SOURCECODE =
+    kotlin(
+            """
 package androidx.navigation
 
 import kotlin.reflect.KClass
@@ -229,22 +312,42 @@
 class AbstractChildClass(val arg: Boolean): TestAbstract()
 object AbstractChildObject: TestAbstract()
 
-"""
+// classes with companion object to simulate classes marked with @Serializable
+class TestClassComp { companion object }
 
-    private val byteCode =
-        bytecode(
-            "libs/StartDestinationLint.jar",
-            kotlin(sourceCode),
-            0x5341e714,
-            """
+class TestClassWithArgComp(val arg: Int) { companion object }
+
+object OuterComp {
+    data object InnerObject
+
+    data class InnerClassComp (
+        val innerArg: Int,
+    ) { companion object }
+}
+
+class InterfaceChildClassComp(val arg: Boolean): TestInterface { companion object }
+
+abstract class TestAbstractComp { companion object }
+class AbstractChildClassComp(val arg: Boolean): TestAbstractComp() { companion object }
+object AbstractChildObjectComp: TestAbstractComp()
+"""
+        )
+        .indented()
+
+private val BYTECODE =
+    bytecode(
+        "libs/StartDestinationLint.jar",
+        SOURCECODE,
+        0x90a33e02,
+        """
                 META-INF/main.kotlin_module:
                 H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijg0uISTsxLKcrPTKnQy0ssy0xPLMnM
                 zxMS9kss88gvLnHOzyspys/JSS3yLuES5eJOzs/VS61IzC3ISRViC0ktLvEu
                 UWLQYgAArkTi/VsAAAA=
                 """,
-            """
+        """
                 androidx/navigation/AbstractChildClass.class:
-                H4sIAAAAAAAA/41Qz28SQRh9M7sssAVZsCql/qi1NZQYoY03TSMlMZJgD7Xh
+                H4sIAAAAAAAA/41Qz28SQRh9M7sssAVZsCql/qi1NRQToY03TSMlMZJgD7Xh
                 AKeB3dAJy26yM5Ae+Vs8ezHRmHgwxKN/lPGbpRoPHDzMm+998/K+b97PX9++
                 A3iBQ4anIvKTWPrXzUgs5ERoGUfN9kjpRIx150qGficUSmXBGPY2aS8Dpf/o
                 s7AYnFcykvqUwa4PjvoMVv2oX0AGWRc2csRFMmFggwJcbOXBUSCpvpKKod77
@@ -254,11 +357,44 @@
                 NgqSSzEKqVPpxWMR9kUiDb9puu/jeTIO3khDdi7mkZazoC+VpNd2FMU6na5w
                 TAnb6d8rJnCqONUZOIR7xE6Jc7rdxlfkG7tfUPyUah4TGg3wDPuEd9cq3ELJ
                 REmVcaPk4dFZezVNwnRnGp9R/LjRprAW3NhwPEnxEQ7ofp0umcHtIawutru4
-                06Wx96hEtYsd1IZgCru4P0RWoaTwQMFVeKjgKHgK5d/NuJIe2QIAAA==
+                06Wx96hEtYsd1IZgCru4P0RWoaTwQMFVeKjgKHgK5d95zSUJ2QIAAA==
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51Sy27TQBQ9Y6d5mADuA0h4P4LUVqJuqopNUaUShIiUFglQ
+                Nl2giT20k9gzaDyJusyKD+EPukJigaIu+SjEHSfAFtjcufece+71nPH3H1+/
+                AdjFY4ZdrhKjZXIWKT6RJ9xKraKDQW4Nj23nVKZJJ+V53tHZx5YLXFFDBYwh
+                HPIJj1KuTqLXg6GIbQU+Q/mZVNLuM/jrG/06llAOUEKFoWRPZc7wtPc/C/cY
+                2uu9kbapVNFwkkVSWWEUT6MX4gMfp7ajFU0Yx1abQ25Gwuxt9AN4bvFqK/5D
+                vs8KlmHr36YxLP8SHArLE245YV428clI5kLNBTCwEeFn0lXblCVthtZsGgRe
+                wwu8kLLZtHrxyW/MpjveNnteqXoXn8te6LneHeYmbP69QxXcYqj9tolh7YhP
+                Xunc3cAanabCbI0smd/RiWC42pNKHI2zgTDv+CAlZKWnY572uZGuXoD1rlLC
+                FEsEPVnwVo9NLF5KxzXfjJWVmejLXFLzgVLaFl+Yo01+l5wJdHru5eku96iK
+                nCt0Lm1+QfW8oO9TLBfgPh5QrM8bUEMAhIyySwvxEzq9hbh+XjjsBNfn4FxQ
+                ZJdxhTgfD6kKCtFt3EETj4qFd9Eq/njygHrDY/hdLHex0sUq1ijFtS7NvHEM
+                lqOBJvE5ghw3c5R/AgRzNM0uAwAA
+                """,
+        """
+                androidx/navigation/AbstractChildClassComp.class:
+                H4sIAAAAAAAA/5VSzU8TQRT/zfZ7KVIqYgE/UBBLRbYQ4kEICdaoTUoPSJoI
+                p2k7lqHbWbMzbTjyt3j2QtSQaGKIR/8o45ulQqIc9LDvzXv7e7/3+ePnl28A
+                1rDKUOKqHQayfeQpPpAdbmSgvK2mNiFvmcqB9NsVn2tdCXrvUmAM81fhd4U2
+                FzERMsaQ3JBKmk2GeHFvscEQKy42skgg5SKONNk87DCwvSxcjGTgIEtQcyA1
+                w1Lt36tap0wdYbYsGaXYY0hvtPxh6rV/55m3gisCpHCdYaVY6waGeLzDQc+T
+                yohQcd97Lt7yvk9NKuLot0wQbvOwK8L1895uuJjAJEPmgozhyX80c1nEehYF
+                TNmxTDPM1YKw4x0K0wy5VNrjSgUm4tFePTD1vu/TGMZ/V7wtDG9zw8nn9AYx
+                WjWzImMFaORd8h9Ja5Xp1V5heHl2nHedghN9Z8eukxtxnXS8cHY8m1p1yuwp
+                Sz0bzSdzzrRTjn1/n3Ry8Z3xCytNIdPxdCKXtHR0VAtXtvznlVB5VM1EnQ9e
+                BdqO1ISB74twuWsYZnb6ysieqKqB1LLpi63LnulOKkFbMIzVpBL1fq8pwl1O
+                GIZ8LWhxv8FDae2hM1tVSoTRkAUFu6+DftgSL6T9NzXM0/grC1Zo+HEakoMp
+                uwuq9RFZSdK3SOft2ZKOkZ2IvEtkbRLaIe2WTpEpzXzG6EnE8HgYaRHLJCfP
+                UbiGMbsUelk22iFy9J1zeXZXpBOlTxj9cCVN9hwwpElTUalhcAHRtpH9iok3
+                7BQ3P2LmJPLEiNgmpNETo22sHHGXqGGgQv7bxHhnH7Eq7lYxW8U93Kcn5qqY
+                x4N9MI0FPNxHWmNMo6jhaixqJDVyGuMahV9kX+XHYAQAAA==
+                """,
+        """
                 androidx/navigation/AbstractChildObject.class:
-                H4sIAAAAAAAA/41SXWsTQRQ9M/nabKOttdrEqq2toBV02+KbRYhBcSGuYENA
+                H4sIAAAAAAAA/41SXWsTQRQ9M/nabKOttdrEqq2toBVx2+KbRYhBcSGuYENA
                 +jSbHdppNjOwMwl9zJM/xH9QfCgoSNA3f5R4d40fYB/cZe+dc+fMudwz++37
                 x88AHuMuwz2hk8yo5DTQYqKOhFNGB+3YukwMXOdYpcnr+EQOXA2MYeMick9a
                 9+tADSWG6r7Syj1lKN3f7jdQQdVHGTWGsjtWlmG7+589nzB4+4O0UPPBcwkv
@@ -268,14 +404,28 @@
                 kQz1RFkVp7L9ZxByr2MSybDYVVpG41Ess54gDsNy1wxE2heZyvG86B+YcTaQ
                 L1QOWnPh/j+y2CULy8XcrdxRyrcJVSkvUeb0Vgq0TijI3aFceXAO76zY3piT
                 gYe4Q7Hxk4A6SQEeFn4fXiV2/ix8An97jsYHLJ4VBY7NIt7CVvFT0k2RwPIh
-                SiGuhlgJcQ3XaYnVEE20DsEsbmCN9i18i5sW1R9Ulqy20QIAAA==
+                SiGuhlgJcQ3XaYnVEE20DsEsbmCN9i18i5sW1R/BbKrC0QIAAA==
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildObjectComp.class:
+                H4sIAAAAAAAA/5VSXWsTQRQ9M/nabKOttdrE+t0ifqDbFt8sSgyKC3EFGwLS
+                p9ns0E6zmZGdSehjnvwh/oPiQ0FBgr75o8S7a6igfXGXvfeeO/eeyz2zP35+
+                /grgMTYYHgidZEYlR4EWE7UvnDI6aMfWZWLgOgcqTd7Eh5JCM3pfA2PYOKuh
+                J607bSoqSwzVHaWVe8pQunuv30AFVR9l1BjK7kBZhofd/5j9hMHbGaQFow+e
+                03hhtNtrR50XDZyDX6fkeYb1rsn2g0Pp4kwobQOhtXEFsw0i46JxmhLVhe7Q
+                OCILXksnEuEE5fhoUiJVWG7quQEDG1L+SOVok6JkiwbMpr7Pm7z4ZlPv+wfe
+                nE23+SZ7XvP4t49VvsTz0m2GO2cu+LdWNHolEpNXxhLULjNpKrNHQ8ew9nas
+                nRrJUE+UVXEq23+WIRU7JpEMi12lZTQexTLrCaphWO6agUj7IlM5nif9XTPO
+                BvKlykFrTtz/hxZbJGO52L2Vq0r+OqEq+SXynN5KgW4QCnKFyFfun8A7Lo5v
+                zouBZ7hFtvG7AHWiAjwsnDavUnX+LHwBf3eCxicsHhcJjtuFvYb14iel2yKC
+                5T2UQlwMsRLiEi5TiNUQTbT2wCyuYI3OLXyLqxbVX5Ga3ibhAgAA
+                """,
+        """
                 androidx/navigation/InterfaceChildClass.class:
-                H4sIAAAAAAAA/41QTW9SQRQ9M3w8eKXyoFop9atWLbDw0cadprElMZJgTWrD
+                H4sIAAAAAAAA/41QTW9SQRQ9M3w8eKXyoFop9atWpbDw0cadprElMZJgTWrD
                 AlYDb6RTHu8lbwbSJb/FtRsTjYkLQ1z6o4x3KGli0oWLufeeO2fOvXN+//nx
-                E8AL7DHsiShIYhVc+pGYqZEwKo78dmRk8lEMZetchUErFFo7YAzehZgJPxTR
-                yH8/uJBD4yDFsHOTxJnU5lrGQYYh+0pFyhwypGu9epchVat3C3CQd5GGS1gk
+                E8AL1BhqIgqSWAWXfiRmaiSMiiO/HRmZfBRD2TpXYdAKhdYOGIN3IWbCD0U0
+                8t8PLuTQOEgx7NwkcSa1uZZxkGHIvlKRMocM6b1evcuQ2qt3C3CQd5GGS1gk
                 IwbWK6CA9Tw4bhHVnCvNUO/855YvacxImiOrRPo9hlJnHJtQRf47aUQgjCAK
                 n8xS9H9mQ94G0Nwx9S+VRU2qgn2G48W87PIKX57F3OXemstzqcpifsCb7Hi9
                 nPV4lTdTvz5luZc+LV2jHLGr6VzGy1qlA4bdG/f/xyJai7a4fSJmb2NtWnFk
@@ -283,151 +433,329 @@
                 YOt0Ghk1kV2lFd0eRVFsluM19snjNA3N0ilb0+nvnGoHOYqPCR0S5pTdxnes
                 Nba/ofhlydmlaF8BNTyhuHnFgoeSdZMqq0bmk+7GSsu3JlPONL6i+PlGmcIV
                 YSXD8XQZd/CM8mvrDt3d6SPVxmYbd9uoYItKVNvYxr0+mMZ9POjD0ShpPNQo
-                aDzSyGmUNTb+AhJd9mP1AgAA
+                aDzSyGmUNTb+AnClX8H1AgAA
                 """,
-            """
+        """
+                androidx/navigation/InterfaceChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51Sy27TQBQ9Y6d5mADpA0h4P4LUgqibCsQiCAmCEJbSVgKU
+                TRdoYk/bSewZNJ5EXWbFh/AHXSGxQFGXfBTijlNgXTZ37j3nnns9Z/zz1/cf
+                AJ7iIcMzrhKjZXIcKj6Vh9xKrcJIWWEOeCx6RzJNeinP857OPrdd4Io6KmAM
+                jRGf8jDl6jDcG45EbCvwGcovpJL2JYO/vjGoYwnlACVUGEr2SOYMz/v/tbHL
+                0Fnvj7VNpQpH0yyUTqF4Gr4RB3yS2p5WuTWT2Gqzw81YmO7GIIDnNq+243/k
+                p6xgGTbPN41h+Y9gR1iecMsJ87KpT1YyF2ougIGNCT+WrtqiLOkwtOezIPCa
+                XuA1KJvPqqdf/OZ8tu1tsdeVqnf6tew1PNe7zdyEx+ewqIIbDLW/PjGs7fLp
+                O527K1ij01SYzbEl+3s6EQyX+1KJ3Uk2FOYjH6aErPR1zNMBN9LVZ2A9UkqY
+                YomgRws+6ImJxVvpuNb7ibIyEwOZS2p+pZS2xSfm6JDhJecCnZ57e7rMHapC
+                ZwudS4++oXpS0Hcplguwi3sU64sG1BAADUbZhTPxEzq9M3H9pLDYCa4uwIWg
+                yC7iEnE+7lMVFKKbuIUWHhQLb6Nd/PTkAfU29uFHWI6wEmEVa5TiSkQzr+2D
+                5WiiRXyOIMf1HOXf1GOmWzEDAAA=
+                """,
+        """
+                androidx/navigation/InterfaceChildClassComp.class:
+                H4sIAAAAAAAA/5VSW08TQRT+Zlt6Y5G2IJbiBQS1FGELYkwsIcEatUmpCZIm
+                wtO0Hcq221mzM2145Lf47AtRQ6KJIT76o4xnSoUHSQwPe858Z8/5zvXX728/
+                AKxjnWGJy2bgu80jR/K+2+La9aVTlloEB7whSoeu1yx5XKmS3/0QBWNItnmf
+                Ox6XLedtvS0aOooQw9xVNLtC6QuqKEYYIhuudPUmQzi3t1hjCOUWazaiiCcQ
+                RoIwD1oMbM+GjbE4LNwgV33oKoblyjUqLVKqltBbho1y7DHENhreMPfTaxAt
+                GMEleURxk2E1V+n4moicdr/ruCZGcs95KQ54z9MlXyod9BraD7Z50BFB8by7
+                WwlMIcMQvyBjeHaddi6rKNrIYsZM5jbDfMUPWk5b6HrAXakcLqWvB0TKqfq6
+                2vM8GkTqb8nbQvMm15xsVrcfogtgRsSNAE29Q/Yj16ACvZqrDK/PjtMJK2MN
+                vrPjhJUcTVixcObseDa6ZhXYcxZ9MZaOJK2sVQj9/BixkuGd1AWKUUg2HBtJ
+                Rgzdmqn3v1dCtVEpk1Xef+MrM1Ad+J4ngpWOZpjZ6UntdkVZ9l3l1j2xddkw
+                3UnJbwqG8YorRbXXrYtgl5MPQ7riN7hX44Fr8NBol6UUwWDCgoIT7/xe0BCv
+                XPNvepin9k8WrNLkw1RhhPS0WQW9l2liEdJ3SafN4ZIOEY4iRnKF0CZ5W6QT
+                +VOM5me+YvyEkAVnGAkUUSA5de6FJFJmJ/QybLRC4p0YcjlmVaRH8l8w/ulK
+                GvvcYUgTwyTiw+AMBsuG/R1T79kppj/jzsnAEqLWTEI2KCJLza0NuB/jCekS
+                2e8R4+w+QmXMlXG/jHks0BMPyniIR/tgCjks7iOmkFLIK9gKS8rAtMKEQvYP
+                Y7gHcXYEAAA=
+                """,
+        """
                 androidx/navigation/InterfaceChildObject.class:
-                H4sIAAAAAAAA/41SwW7TQBB9u0kTxw00LQUSCpRSQG0PuK24USGVCISlYCQa
-                RUI9beIl3cTZlexN1GNOfAh/UHGoBBKK4MZHIWZNKAeQwNbOzHs783Zn7G/f
-                P34G8AgPGLaEjlOj4tNAi4nqC6uMDkJtZfpW9GTzRCXxq+5A9mwZjKE2EBMR
-                JEL3g19sgWHjbxptmdkLnTIWGEoHSiv7hKGwtd2pogzPRxEVhqI9URnDTut/
-                7/KYwTvoJbmcD+40vDA6ah9GzWdVLKFaIbLGsNkyaT8YSNtNhdJZILQ2NpfN
-                gsjYaJwkJLXcGhpLYsFLaUUsrCCOjyYFGhFzpuIMGNiQ+FPl0C5F8R4dMJv6
-                Pq/zfM2m3td3vD6b7vNd9rTs8S/vS7zGXeq+u8s/p0TnrkZi8sJktmm0TU2S
-                yPTh0DKsvR5rq0Yy1BOVqW4iD393QvNrmlgyLLWUltF41JVpW1AOw0rL9ETS
-                EalyeE76R2ac9uRz5UBjLtz5QxZ7NMMitV2i1XBDJX+Hend4hTynl74hoQ1C
-                gRsQ+YWdc/hn+fbdeTKwjU2y1Z8JWKQIVHjpovg6Zbtn8RP4m3Nc/oDls5zg
-                uJfbddzPf1iGKySweoxCiKshroVUWqcQjRA3sHYMluEmbtF+hmqG2xm8H3Jw
-                I2btAgAA
+                H4sIAAAAAAAA/41STW/TQBB9u0kTxw00LQUSylcpoNADbituVEglAmEpGIlG
+                kVBPm3hJN3F2JXsT9ZgTP4R/UHGoBBKK4MaPQsyaUA4gga2dmfd25u3O2N++
+                f/wM4BHuMzSFjlOj4pNAi6kaCKuMDkJtZfpW9GXrWCXxq95Q9m0ZjKE2FFMR
+                JEIPgl9sgWHzbxodmdlznTKWGEr7Siv7hKHQfNCtogzPRxEVhqI9VhnDdvt/
+                7/KYwdvvJ7mcD+40vDA67BxErWdVrKBaIbLGsNU26SAYSttLhdJZILQ2NpfN
+                gsjYaJIkJLXaHhlLYsFLaUUsrCCOj6cFGhFzpuIMGNiI+BPl0A5F8S4dMJ/5
+                Pq/zfM1n3td3vD6f7fEd9rTs8S/vS7zGXeqeu8s/p0Tnrkdi+sJktmW0TU2S
+                yPThyDJsvJ5oq8Yy1FOVqV4iD353QvNrmVgyrLSVltFk3JNpR1AOw1rb9EXS
+                FalyeEH6h2aS9uVz5UBjIdz9Qxa7NMMitV2i1XBDJX+bend4jTynl74hoU1C
+                gRsQ+aXtM/in+fadRTLQxBbZ6s8ELFMEKrxwXnyVst2z/An8zRkufsDqaU5w
+                3M3tLdzLf1iGSySwfoRCiMshroRUWqcQjRDXsHEEluE6btB+hmqGmxm8H+eK
+                JRLtAgAA
                 """,
-            """
+        """
                 androidx/navigation/NavHostController.class:
-                H4sIAAAAAAAA/41Ru0oDQRQ9d2I2yRpN1KjxBVqIj8JVsVMEFcRAVFBJYzXJ
-                LnGSzQzsTkLKfIt/YCVYSLD0o8S7q52NzeE87nAf8/n19g7gCGuETan9yCh/
-                6Gk5UG1pldHejRxcmdheGG0jE4ZBlAMRyh05kF4oddu7bXaCls0hQ3BOlFb2
-                lJDZ3mkUkYXjYgI5woR9UjFhq/6vDseEmXrX2FBp7zqw0pdWsid6gwyPSgkU
-                EgCBuuwPVaL2mfkHhPXxyHVFVbiizGw8yi9Ux6NDsU/n2Y9nR5RFUndIyevK
-                n9Z7XcvTXhg/IJTqSgc3/V4ziB5kM2Rntm5aMmzISCX613TvTT9qBZcqEUt3
-                fW1VL2ioWHF6prWx6ZYxNiD4GL9jJ7dhrLLyUg1kd1+Rf2EisMTopGYBy4zF
-                nwJWbpqvpLiI1fTnCJOcFR+RqWGqhukaSigzxUwNs5h7BMWoYJ7zGG6MhRjO
-                N+LuMN72AQAA
+                H4sIAAAAAAAA/41Ru0oDQRQ9d2I2cY2axFfiA7QQH4WrYqcIKoiBGEEljdUk
+                u+gkmxnYnYSU+Rb/wEqwkGDpR4l3Vzsbm8N53OE+5vPr7R3AEdYIm1L7kVH+
+                0NNyoB6lVUZ7DTm4MrG9MNpGJgyDKAciFDtyIL1Q6kfvptUJ2jaHDME5UVrZ
+                U0Jme6dZQBaOiwnkCBP2ScWErfq/OhwTSvWusaHS3nVgpS+tZE/0BhkelRKY
+                TAAE6rI/VInaZ+YfENbHI9cVFeGKIrPxKL9YGY8OxT6dZz+eHVEUSd0hJa/n
+                /7Te61qe9sL4AWG2rnTQ6PdaQXQvWyE75bppy7ApI5XoX9O9M/2oHVyqRFRv
+                +9qqXtBUseL0TGtj0y1jbEDwMX7HTm7DWGHlpRrI7r4i/8JEoMropGYey4yF
+                nwJMwk3zlRSXsJr+HGGKs8IDMjVM1zBTwyyKTFGqoYy5B1CMeSxwHsONsRjD
+                +QaW0s1z9gEAAA==
                 """,
-            """
+        """
                 androidx/navigation/NavHostControllerKt.class:
-                H4sIAAAAAAAA/41UXVPbRhQ9Kxt/KAZkE4NtAgngEANtRCj9hKalTlPUOKaT
-                ZOhkeFrkrSOQpY5W9uSR9/6L/oKmPKRTZjpMH/ujOr0rnJhY0OAZ6969e8+5
-                R1e7959///wLwDp2GO5wrxX4Tuul6fGe0+ah43tmk/e2fRnWfS8MfNcVwaMw
-                DcZgHPAeN13utc2d/QNhUzTBkO6nM2zWGlei22gME20s7TLcHqBtv/OzL4UZ
-                dL3Q6QizHq35vis2GBYaftA2D0S4H3DHkyb3PD+MSkmz6YfNrutS1igpGJTM
-                IMswe+iHruOZB72O6XihCDzumhalEItjyzSuMRTtF8I+7NP8wAPeEZRIjarF
-                RZ+LPFUkbXqNHEYxpiOHceqXDHkQPhAydLxIXwZ5YrpikxgK8ZIME20R1l0u
-                peURvWeLJ+Inhrna0oW0z6h4lE1Iw47Bbr4HlMMUSlloKDNUhgv/6IQvtoJ2
-                RFR7X/1+MsmYsi8jWbwaRQ4zmFWibjKUSJTleSKIt+QySTtd+qDVAYgkFZ2L
-                KS4WFCPIYQFVJeg2Q2bTpiPmhPcZEjV1qmf+953SWGJIbUaIHFZQ07GMDxiq
-                V+lEGncZkjVLnboVrOowce8S6LDmND7SaQJQer7RvxWPRchbPOTUD63TS9CE
-                YOqRVQ8wsEPlaLT50lHeKnktwv9yerSsnx7pWknTtUwibg3tzbYyxulRpWTk
-                Ktrq6FrKGCM7TtYgm99Oz+fVtrbK/v41laH0SjKjGQmKJik4MgimDJWaoWB2
-                ENSNa0rSGlNqy5c2IY06gz7oBMP12N27e0jTbPrJ2fCxvJ4jHRo9W4M5Q22v
-                +y3BMN5wPNHsdvZF8EyNJ3VpfZu7uzxw1LofrA5zvZ0s75DmBrIELfWnfjew
-                xUNHUZT7FLsxMbhHRy9JnySBirqv9Prf0SpFNkO2oq5LLEYndig2gjL9U8Sy
-                Tast4tTIji4X9D9grBQK9HylTgCsPohmHL4nf/IsEVlMqKNCXh7Xz5GnyT5S
-                ca1f6exZRjGCqmo3KKZ+yd9R+S3KeFMERHZeZRnTlB1D3RpGTQyh5jAfRy0O
-                o4rvoDK4gxr5ClVHdA+wcILl56/x4TEqJzCfG+OvsXaMWydYj/yPj7H46i3p
-                WATKQyc5k0SeQIPWOu2u4wG+JVmPo44+RJMsp/gn9Ak+3UPCwmcWPrfwBTYs
-                bOJLC/fx1R6YxNfY2kNBYkqiJFGUmJSYkZiVmJa4IbEgUZWYk5iXGJH4RiIr
-                MSGRJ/8/37G4ovoHAAA=
+                H4sIAAAAAAAA/41UXVPbRhQ9Kxt/KAZk82WbQAI4xEAbEUo/oWmp0xQljskk
+                GToZnhZ56whkqaOVPXnkvf+iv6ApD+mUmQ7Tx/6oTu8KJw4WNHjGunfv3nPu
+                0dXu/effP/8CsI4dhtvcawa+03xlerzrtHjo+J7Z4N1tX4Y13wsD33VF8ChM
+                gzEYB7zLTZd7LXNn/0DYFE0wpHvpDJvV+pXoNuqDRBtLuwy3+mjbb//sS2EG
+                HS902sKsRWu+74oNhoW6H7TMAxHuB9zxpMk9zw+jUtJs+GGj47qUNUwK+iUz
+                yDLMHvqh63jmQbdtOl4oAo+7pkUpxOLYMo1rDBP2S2Ef9mie8IC3BSVSo6px
+                0e9FnimSFr1GDsMY0ZHDKPVLhjwI7wsZOl6kL4M8MV2xSQyFeEmGsZYIay6X
+                0vKI3rPFU/ETw1x16ULa51Q8yiakYcdgNz4AymEKxSw0lBjKg4V/dMKXW0Er
+                Iqp+qH4vmWRM2ZeRLF6NIocZzCpRNxiKJMryPBHEW3KZpJ0OfdBKH0SSJpyL
+                KS4WFCPIYQEVJegWQ2bTpiPmhPcYElV1qmf+953SWGJIbUaIHFZQ1bGMjxgq
+                V+lEGncYklVLnboVrOowcfcS6KDmND7RaQJQer7euxWPRcibPOTUD63dTdCE
+                YOqRVQ8wsEPlaLT5ylHeKnlNwv9yerSsnx7pWlHTtUwibg3t7bYyxulRuWjk
+                ytrq8FrKGCE7StYgm99Oz+fVtrbK/v41laH0cjKjGQmKJik41A+mDJWaoWC2
+                H9SNa0rSGlNqS5c2IY0ag97vBMN47O7dOaRpNv30bPhYXteRDo2erf6cobbX
+                /KZgGK07nmh02vsieK7Gk7q0vs3dXR44at0LVga53k2Wc6S5vixBS/2Z3wls
+                8cBRFKUexW5MDO7S0UvSJ0mgrO4rvf4PtEqRzZAtq+sSi9GJHYgNoUT/FLFs
+                02qLODWyw8sF/Q8YK4UCPV+rEwCrB8phBA/JnzxLRBZj6qiQl8f4e+Rpso9U
+                XOtVOnuWMBFBVbXrFFO/5O8o/xZlvC0CGOdUljBN2THUzUFUYQA1h/k4anEQ
+                NX4OlcFtVMlXqBqie4CFEyy/eIOPj1E+gfnCGH2DtWPcPMF65H96jMXX70hH
+                IpABneRMEnkCdVrrtLuO+/ieZD2OOvoADbKc4p/RJ/h8DwkLX1j40sJX2LCw
+                ia8t3MM3e2AS32JrDwWJKYmixITEpMSMxKzEtMR1iQWJisScxLzEkMR3ElmJ
+                MYk8+f8Bprsff/oHAAA=
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerClass.class:
-                H4sIAAAAAAAA/41U308cVRT+7uyv2WGBWaAtP9ZWy4q7S9sBbLUWWgUUGVyW
-                CoZY8eWyOy4DwwzO3CX1xfDUP6GJvpgY41Mf2kTB2MRg++bfZIzn7kx361IJ
-                ycw955455zvfPefc+euf3/8AcB1rDHnu1nzPrt03XL5v17mwPddYaQjLz5uu
-                a/nzDg+CFBiDvs33ueFwt26sbG5bVZFCjCE5Y7u2uMMQL5jFdYZYobieQQIp
-                DXGoDKotUWb9OgMzM9DQlYaCDPmLLTtgGCufhcA0Q1fdEmYLi9KYDFrV293z
-                XMsVkwRY9fa+YSgSj7NijpY9v25sW2LT57YbGNx1PdH0DoyKJyoNx5mWh0lq
-                xPk8Q0amyNesr3jDEQybhbMlMs1yZ+2mz8gxg34MyOzDVErhrQnfdun4A4Xi
-                S5Chlc5zodM217CdmuWncFHDJdmOgTZ24UVnbqt4gxrJ9/Yst8ZwtXAS+mS2
-                CJkIjiIvwd9kyMnSn+b4lnQsSMf50x1L0nE8gxxek9pVOvwWD7bmvZrFkG1H
-                mq6w6vJ8E+EA0oQZmNIwibfpRNbXDe7QjJ0rvKL+X9Dsn9Z+6j3fdCyqasIT
-                W5bP0HcShciUdzzh2K6xbAle44KTTdndj9H9YnJJywU0/Dtkv2/LHXFVajSw
-                Px0fXNSUQUVT9OMDjR5FVzVFTZLsIhkj2aM+e6AOHh9MKRNsrrsvqSvDykTs
-                2Y9JRY8vpfWU3C0+fxBb6tdV0slRVZXQicyMzGnStSlV7xqOD7IJtvj8YYwC
-                M6HHQ0Z6N+k9Ul/NtuBVojMcVxN6UnKdYvIEQ/87sCks0F1sTxaNWYXvL3qB
-                mPdc4XuOY/nXdujCxMMG9pZt16o0djct/zNZY1lar8qdde7bch8ZR1YbrrB3
-                LdPdtwObTLPt/jB0rwle3Vnme5F3vtP7Lvf5rkX0/hOWadO0aKuteQ2/ai3Y
-                EmIoglg/kY4GSqH/mSxDn/yHkaaSTn8GWpdot0DfFZJa6Qjp0siv6H5COwWf
-                0NoD2fXLFD+KNMky7c6H3vStV84HaRKVxgk6vSGmIceGZKL0C7ofteCSTeNo
-                EyYTOkQwWSL3Ini0M5i9MoD+LgQrAyaJpeSUfgrl3sgRLjxuBYVk0y2y6Yjs
-                csTmHKCnMYihKPdYVKxsLv7td1Alg5nSyCFGQsgKrTEwiUD3O0p/i6SklnuK
-                S/eO8Hrf5UOMychDFPXiIa4c4trjjmPkIkYv8aDVaNVgLKpBk8FvuN5ZBjWK
-                Z7iBdyIeX5KU7cqXxn9GIv5o/E8o3yMRezR+DGVZAl2h9wdpiYc9qTTbF0up
-                fyObon27YvlWxfK4ifcozwrpKUnq3WYN7jZD6YrhYyxS+T5tAppYJfk52W9R
-                p6Y3EDMxY+K2iTt4n1R8YGIWcxtgAebx4QZ6A/l8FEBrrskAeoBsgL4A/QFu
-                NI03AxgBcqT/CyhrMdv+BwAA
+                H4sIAAAAAAAA/41U31PcVBT+brI/smEXskBbfqythRV3l7YBbLUWWgUUCS5L
+                BYex4stlNy6BkGCSZeqLw1P/hM7oizOO4xMPdUbBsTMOtm/+TY7juZt0ty6V
+                YSa559yTc77z3XPOzV///P4HgJtYY8hzp+a5Vu2h7vB9q84Dy3X0lUZgennD
+                cUxv3ua+nwRj0Lb5Ptdt7tT1lc1tsxokITMkZizHCu4xxApGcZ1BLhTX04gj
+                qSIGhUGxBMqsV2dgRhoqulKQkCb/YMvyGcbK5yEwzdBVNwOjhUVpDAa16u7u
+                uY7pBJMEWHX3vmYoEo/zYo6WXa+ub5vBpsctx9e547hB09vXK25Qadj2tDhM
+                QiXOFxnSIkW+Zn7JG3bAsFk4XyLDKHfWbvqcHNPoQ7/IPkSlDNy1wLMcOn5/
+                ofgSZGil81zqtM01LLtmeklcVnFFtKO/jV140Zm7Cq5SI/nenunUGK4XTkOf
+                zhYhE8FR5AX4Gww5UfqzHN8UjgXhOH+2Y0k4jqeRw2tCu06H3+L+1rxbMxmy
+                7UjDCcy6ON9EOIA0YTqmVEziLTqR+VWD2zRjFwqvqP/nNPtntZ96zzdtk6oa
+                d4Mt02PoPY1CZMo7bmBbjr5sBrzGA042aXdfpvvFxJISC2j4d8j+0BI74irV
+                aGB/PDm4rEoDkippJwcqPZKmqJKSINlFUibZrTx7pAycHExJE2wu05vQpCFp
+                Qn72Q0LSYkspLSl2i88fyUt9mkI6OSqKFDqRmZE5Rbo6pWhdQ7EBNsEWnz+W
+                KTAdejxmpGdI7xb6arYFrxCdoZgS1xKC6xQTJxj834FNYoHuYnuyaMwqfH/R
+                9YN51wk817ZN78YOXZhY2MCesuWYlcbupul9KmosSutWub3OPUvsI+PwasMJ
+                rF3TcPYt3yLTbLs/DJm1gFd3lvle5J3v9L7PPb5rEr3/hKXbNE3aqmtuw6ua
+                C5aAGIwg1k+lo4GS6H8mytAr/mGkKaTTn4HWJdot0HeJpFo6Rqo0/CsyP9NO
+                wse0dkN0/SrFjyBFsky7i6E3fesR80GaQKVxgkZviKmLsSEZL/2CzGELLtE0
+                jjRh0qFDBJMlci+CRzuD2SsD6O9CsCJgklgKTqmnkB4MH+PSk1ZQSDbVIpuK
+                yC5HbC4AWgoDGIxyj0XFyuZi33wLRTCYKQ0fYTiErNAqgwkEut9R+jskBbXc
+                U1x5cIzXe0eOMCYij1DUike4doQbTzqOkYsYvcSDVr1Vg7GoBk0Gv+FmZxmU
+                KJ7hFt6OeHxBUrQrXxr/CfHY4fifkL5DXD4cP4G0LICu0fu9sMTCnlSa7ZOT
+                yt/IJmnfrli+VbE8buNdyrNCelKQeqdZg/vNULpi+AiLVL5PmoAGVkl+RvY7
+                1KnpDcgGZgzcNXAP75GK9w3MYm4DzMc8PthAjy+eD32ozTXhQ/OR9dHro8/H
+                rabxtg/dR470fwEaMaS//gcAAA==
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerObject.class:
-                H4sIAAAAAAAA/41US08UQRD+umcfs7M8loc8FVEWeSmzIJ4gJkg0DFkWIwSj
-                nHp3RxgYZnSmd8ORkzevHjx68MRB4oFEE4MSL/4oYvUwCkIwJrvdX1VX1Vfz
-                Vc/8PP78FcAU7jEMCq8a+E51x/RE3VkX0vE9c6km7SBveZ4dLJU37YpMgzHk
-                NkVdmK7w1s3fXo0hNeN4jrzPoA2PrDYgiZSBBNIMCbnhhAxDxf9imGbQpb8s
-                A8dbZ2gfHimesp14KWKg6Afr5qYty4FwvNAUnufLqGBolnxZqrkuRWXPlNXR
-                RIU3RLgx51ftqElL+zF1/IYat1/VhEsdXhkunn+y6ZHnDPl/sRGVKLs20SV9
+                H4sIAAAAAAAA/41US08UQRD+umcfs7M8loc8FVRAFlBmQTxBTJBoGLIsRghG
+                OfXujjAwzOhM74YjJ29ePXj04ImDxAOJJgYlXvxRxOphFIRgTHa7v6quqq/m
+                q575efz5K4Ap3GMYEl418J3qjumJurMupON75lJN2sGg5Xl2sFTetCsyDcaQ
+                2xR1YbrCWzd/ezWG1IzjOfI+g5YfWW1AEikDCaQZEnLDCRmGi//FMM2gS39Z
+                Bo63ztCeHymesp14KWKg6Afr5qYty4FwvNAUnufLqGBolnxZqrkuRWXPlNXR
+                RIU3RLgx51ftqElL+zF1/IYat1/VhEsdXskXzz/Z9MhzhsF/sRGVKLs20SV9
                 uWEHDK0XqxD1TMWN9DHAlSi6VVpemS3NPWxAD4wMOXsZWopbvqQwc9GWoiqk
                 oES+XddoRkwtGbWAgW2Rf8dRVoFQdYLhxeFun8G7uMFzh7sG1xXIxrtuKFeu
                 ST96bXQd7k7yAnuQ1vn39yme4wttOa2HFxKTei7Zk+hiBTZ/9FZbyORS5E0T
                 ZoR1whmFFdskUz10XzrNNEZocCVRn/dDOed7MvBd1w7GtyRD75OaJ51t2/Lq
-                TuiQbrOnWtJNOZlNc9Hx7FJtu2wHK0pbJalfEe6qCBxlx87GZSkqW4viZWzn
-                z9d+LAKxbVNHf5E0RLdizhVhaJNpLPu1oGI/clSJ7rjE6oXmMEEjSkTqd6uJ
-                0X6brBTtjbQn6TQZWXfIMtWMlHf0APo+AY7xOBjop2Og4SQAGSqlimbJw6Pk
-                G3Gy1tr8MTo6Ddfi8LPM9DqiJeY9TW3duySVoQ3tMZNFO6e9c3TsA5KJvbFv
-                4O+Q1PbGDsGfJvaixgu0JsDTelSs4yQhLqZQB/0ZqQN1q+kdIqCj648UnVEC
-                kP0C/uwA3Z9wdT9yaJikVenIMYomUvVuxDdGnyPVGsM1kqdvDZqF6xb6LXq6
-                mwQxYCGPwTWwELcwtAYjVL/hEKkQbRHoCJGLQJbWX0Nhj3HkBAAA
+                TuiQbrOnWtJNOZlNc9Hx7FJtu2wHK0pbJalfEe6qCBxlx87GZSkqW4viZWwP
+                nq/9WARi26aO/iJpiG7FnCvC0CbTWPZrQcV+5KgS3XGJ1QvNYYJGlIjU71YT
+                o/02WSnaG2lP0mkysu6QZaoZKe/oAfR9AhzjcTDQT8dAw0kAMlRKFc2Sh0fJ
+                N+JkrbX5Y3R0Gq7F4WeZ6XVES8x7mtq6d0kqQxvaYyaLdk575+jYByQTe2Pf
+                wN8hqe2NHYI/TexFjRdoTYCn9ahYx0lCXEyhDvozUgfqVtM7REBH1x8pOqME
+                IPsF/NkBuj/h6n7k0DBJq9KRYxRNpOrdiG+MPkeqNYZrJE/fGjQL/RauW/R0
+                NwliwMIghtbAQtzC8BqMUP3yIVIh2iLQESIXgSytvwC7X1qw5AQAAA==
                 """,
-            """
+        """
                 androidx/navigation/Outer.class:
-                H4sIAAAAAAAA/4VRXWsTQRQ9M5uPzSbaNFabGNuqTbWp4LbFp1qEGhQXYgq2
-                BCRPk2SIk2xnYXcS+pgnf4j/oPhQUJCgb/4o8e42mgcp7rD3zP06d+69P399
-                +QbgGZ4wVITuh4Hqn7taTNRAGBVo93hsZJgFYygOxUS4vtAD97g7lD2ThcWQ
-                OVRamRcM1na9XUAaGQcpZBlS5oOKGKrNa1mfM9iHPT/Jd8DjJNtrnZwetRqv
-                CrgBJ0fGmwybzSAcuENpuqFQOnKF1oFJeCK3FZjW2PeJark5CgyRuW+lEX1h
-                BNn42cSi7lgscrEAAxuR/VzF2i7d+nsM9dm04PAyd3hxNnW4bdk/PvLybLrP
-                d9kBt1Ivszb//inDizxO2GcxjeNpLcOGLyJqMp8oV1NhqF3bcW2RlMUGw9Z/
-                Iv/M+QHDSktM3gSRaQTahIHvy/DpiGpV3421UWfS0xMVqa4vjxbDoR00gr5k
-                WGoqLVvjs64MTwXFMJSaQU/4bRGqWJ8bC4vXSUp2ToJx2JOvVeyrzOu0/6mC
-                PdpSKhltJV4aYY20DGGRkNNJJ9oWaW68AML0ziXsi8T9aB4MbOAxycJVAHJE
-                BdjI/01epej4y38Ff3+JwmcsXSQGC9skS+S+T/8aveMh4TphPSmxiR3CA6JZ
-                JuJSB5aHWx5WPNzGHbpi1UMZlQ5YhLuodpCO4ES4FyETYS3C+m+Jq/qyJgMA
+                H4sIAAAAAAAA/4VRXU8TQRQ9M9uP7bZKqSitlaICCpi4QHxCYoKNxk3qkggh
+                MTxN20kduswku9OGxz75Q/wHxAcSTQzRN3+U8e5S5cEQd7L3zP06d+69P399
+                +QbgGZ4wNITux0b1T30txmogrDLa3xtZGRfBGKrHYiz8SOiBv9c9lj1bhMNQ
+                2FFa2RcMzuraYQV5FDzkUGTI2Q8qYWh2rmV9zuDu9KIs3wNPk9wg3D/YDduv
+                KrgBr0TGmwxLHRMP/GNpu7FQOvGF1sZmPIkfGhuOooioZjtDY4nMfyut6Asr
+                yMZPxg51x1JRSgUY2JDspyrVNujW32RYu5hUPF7nHq9eTDzuOu6Pj7x+Mdni
+                G2ybO7mXRZd//1TgVZ4mbLGUxgu0lnE7Egk1Wc6Uy6kwLF/b8fJVUhGLDCv/
+                ifwz5wcMc6EYvzGJbRttYxNFMn46pFrNdyNt1YkM9FglqhvJ3avh0A7api8Z
+                ZjpKy3B00pXxgaAYhlrH9ER0KGKV6lNj5ep1kpK9fTOKe/K1Sn2NaZ3Df6pg
+                k7aUy0bbSJdGuExagbBKyOnkM22FND9dAGF+/RzuWeZ+NA0GWnhMsnIZgBJR
+                AS7Kf5PnKTr9yl/B35+j8hkzZ5nBwSrJGrnv079A73hI2CJcy0osYZ1wm2hm
+                ibh2BCfArQBzAW7jDl0xH6COxhFYgrtoHiGfwEtwL0EhwUKC1m8Ngp25JgMA
                 AA==
                 """,
-            """
+        """
+                androidx/navigation/OuterComp$InnerClassComp$Companion.class:
+                H4sIAAAAAAAA/51TTW/TQBB9a6dxYkJJ0wIJUD4DpAjqpqpQpSIkCEJESlsJ
+                UC49oE2ylE3sXWSvox5z4ofwD3pC4lBFPfKjELNOoOKCVC4zb+bNm7Fn7B8/
+                v58A2EKD4SlXg1jLwVGg+FgeciO1CvZTI+KWjj7X20oRCnmSZKE1XFGJB8ZQ
+                HvIxD0KuDoP93lD0jQeXIf9MKmmeM7iNtW4JC8j7yMFjyJlPMmHY7vzfyB2G
+                ZqMz0iaUKhiOo0AqkigeBq/ER56GpqVVYuK0b3S8y+ORiHfWuj4cO3q53j8j
+                P0QZy7B+vm4MS78Fu8LwATecck40dmmZzJqiNWBgI8ofSRttEBo0GerTie87
+                Vcd3yoSmk8LpF7c6nWw6G+ylV3BOv+adsmNrN5nt8Pg8O/JwnWH1nwoPqwyL
+                f8sYin+Wy7Cyx8dvdGLf28Q6DEW8PjJ0tJYeCIZLHanEXhr1RPye90LKVDq6
+                z8Muj6WN58nS2QRBp/bf6TTui9fScrW3qTIyEl2ZSCp+oZQ22VMmaNKVcnZ1
+                5B37xdAG7lIU2F2SX3j0DYXjjL5HNp8lt1EnW5oVoAgfKDNCF+biJ+Sdubh0
+                nN3FCq7MkjNBhi5ikTgX9ymqEHsDN3ELtQzdJv8gG3wHD7N/hnZBmvIB3DaW
+                2qi0sYwVgrjcpt5XD8ASVFEjPoGf4FqC/C+Tnk+BcAMAAA==
+                """,
+        """
+                androidx/navigation/OuterComp$InnerClassComp.class:
+                H4sIAAAAAAAA/5VUW08bVxD+zvq2XgysIRcCpEmLm5pLWKAJUHAaLillKZcU
+                UhpC+3Cwt2ZhvUt311b6UuUpPyFS+1KpD33iIVFbqIpU0eStv6mqOmfXmFuE
+                ZMk+Z2Z25pvvzMw5//z3518A7uBrhj5uF1zHLDzVbF4xi9w3HVtbKvuGO+2U
+                djK6bZNkcc8TagKMQd3iFa5Z3C5qSxtbRt5PIMIQz5m26X/MEM3q3asMkWz3
+                agoxJBREITPIpkCadIsMTE9BQUMSElLk72+aHkP/fD1Exhkaioav1zApnc6g
+                5OmbYxu2P0jAeWfnO4ZB4lMvdte84xa1LcPfcLlpexq3bccPojxt0fEXy5Y1
+                Lg4XV+gMVxhSIlWmYHzDy5bP4GbrS6jr82drOl4n5xRacUmwaadS+86K75o2
+                leVStvsEdGil8109a5sqm1bBcBN4R8EN0a620/jZo+7dk/EuNZvv7Bh2geF2
+                9jz8+YxVdCLZhYxI8D5Dp2jLRY4fCMescJy+2LFHOPam0InrQrpNBdjk3ua0
+                UzAY0seRuu0bRXHGgXBIaQo1DCkYxId0IuPbMrdoDi9n39KLJwyZi0aC5oFv
+                WAZVNub4m4bL0HIehXjl8lb1lgzX092MWLhNLgmMi4me33Z8QtK2KiXNpGO5
+                Nre0B+H4TRMj3y3nfcdd4O42FSm8iPcU5ECZkzUwhtG6huyYBtV9ApPiAk9R
+                iY/YLBg+L3CfE0WpVInQC8PEkhQL6Npvk/2pKTTqgFSgK7p7+OymIrVJiqQe
+                PlPoJ6myIslx2htoj9DeRGb59XO5jVybh6QBNsaapxpb4qrULg1EXv8cl9To
+                XFJNCG32zfPIXKsqk3z4bEiWpdCJzIzMSZKVIVltaI+2sQE2++ZFhAJToccL
+                RnIjyU1CXk7X4GXK3x6VY2pccB5i4iTXL6xaAg8Zmk6Xjm7iIq/MOp5oj+86
+                lmW4/dv0VHQsl23fLBm6XTE9k2Zo8niuaEzDIW6eN21jsVzaMNxHYs7EeDl5
+                bq1y1xR61di44vP89gLfqeqZs9gPuctLBtE8lSR1TNUgVVlxym7emDEFxLUq
+                xOo5cnRtJHrZQes1MQxUlkekxWm/THuLeOFF80mPBdYvSJshb4l2pWcfyZ6O
+                39H4KkBYpbUJYjKGCXOEoobxJWlXQm/61ixmiCSBSiMHlf4hpiZGi/ZYz29o
+                3K3BxQPjSACTCh2qMGkidxTcdTaYvTWA3laCFQGDxFJwSh5AWuvYx9WXtaCQ
+                bLJGNlkle6IsahJtVK4w961qAdOd0e9/gCwY5Ho69tARQj6mNQImEOhlq6Yf
+                o11Q6zzAjbV93Gx5bw+3ROQeutXuPfTtof/lmWN0VhmdbA+jsqVrPMIaBAz+
+                wJ2zZZCr8Qx3qS0hj69oF+3K9PT+glh0t/dvSD8iFtntPYS0IID66P+TsETD
+                njwO2hdJyP8inSD9uGKZWsUyGMVHlGeN5IQgNRKkH0OiSrUtSErEDpBbY/u4
+                /yumXwWWCJ4EUyfm63MsU5FzJE3Qvh6kXyHKIJnhAfX1k3VEdMzo+FTHLHQS
+                MafjM8yTg4cFLK5D9dDsYcmDEqxxT1jSHlo8tHq4GxhHPWgeOgN54n/YBk2n
+                VQkAAA==
+                """,
+        """
+                androidx/navigation/OuterComp$InnerObject.class:
+                H4sIAAAAAAAA/41TS2/TQBCeXTuJ46St+6BPKI8GSBqo0wCnVkglAtVVmiJS
+                FUFPm8Qkbhy72Juox574CRw4coBLDlQcKoGEAr3xoxCzjiHQigop2flmPDPf
+                +Jv19x+fvgDAXbhHIMOcmudatQPdYR2rzrjlOvpWm5tewW3tpwzHMb2typ5Z
+                5TEgBLQ91mG6zZy6/isqEYiuWo7F7xOQ0pmdJEQgqoIMMQIyb1g+gWzxv1lW
+                CCjcLXPPcuoEJtKZ4oCxH8WMhaLr1fU9k1c8Zjm+zhzH5UFTXy+5vNS2bcxK
+                /NFWgWFs3GB+o+DWzGBQQ3Le51ZxePNlm9k45YV08fTbrWSeE0idx4ZUrGKb
+                SBdxecP0CIyd7YLUq1U70EgFKoRRjFJ5e61UeJiEWVDjGJwjMFpsuhzT9E2T
+                sxrjDAtpqyPhrog44uIAAqSJ8QNLeDlEtWUCL3qH8yqdpirVeocqVQRIhFZR
+                RUgbVk5eqdO9wzzNkQcxhX57G6Ua3RjXpFmak/OKFpmVp0mOrJ+8ljbiWhSj
+                McQEsYI4LrBgyxMxw6VzNxqDDC6vxDrrrs8LrsM917ZNb6nJCcw9aTvcapmG
+                07F8C7VbG+iJN6a/n5Gi5ZildqtiettCXyGrW2X2DvMs4YfBoTJn1eYm2w/9
+                1Onej5nHWiZO9RdJMrgZBZv5vomuWnbbXtV8ZIkWM2GLnTPDwTKuSQ42MCO2
+                hvYWelG0Q2gj+DQSeLfR08WeRHTxGJQjBBSWwmTx5el4JvsJEMdWomkCIzQo
+                vhoWS2MjH4JHg3QpTP+TGT9LGA15B6Vj3X+UEhiHiZDJQEvRTi1m30FE7ma/
+                An0DEamb7QF9KneDwXN4ykBjStBssl8QNhNoEv8E1QFxs/E7QqDA9G8ppoIC
+                gMRnoM+OYeYjXDwKAhLk8RQ6UliEYVT1TsCXRYHEaHjLUJ75XZAMuGzAFQPf
+                7hpCWDAgBdd3gfhwA27uguqLX9qHqA/jAZj0QQtAAs+fAKoQrfAEAAA=
+                """,
+        """
+                androidx/navigation/OuterComp.class:
+                H4sIAAAAAAAA/41RXU8TQRQ9M9uPZVmhVBQqAiqoFI0L6BMSE2w0blKXREgT
+                06dpu8Gh2xmzM2145Mkf4j8gPpBoYoi++aOMdxeUGBPiTvaeuV/nzr33x8/P
+                XwE8wQOGeaF6qZa9w0CJkdwXVmoV7AxtnDb04H0ZjKFyIEYiSITaD3Y6B3HX
+                luEwlLakkvYZg7NSb/koouShgDJDwb6ThmGxeSnzUwZ3q5vkHB54luiG0e7e
+                dtR44eMKvDEyTjAsNXW6HxzEtpMKqUwglNI25zJBpG00TBKimmr2tSWy4HVs
+                RU9YQTY+GDnUJcvEWCbAwPpkP5SZtka33jpD/fTI9/gs93jl9MjjruN+/8Bn
+                T482+Brb5E7hednl3z6WeIVnCRsso5kIlaI2EmFM1gvDeG44mw7Dw0s7X/47
+                uYxFesR/ZPye/W2G6UiMXmljG1rZVCdJnD7qU925N0Nl5SAO1Uga2Uni7Yth
+                0V4auhczTDaliqPhoBOne4JiGKpN3RVJS6Qy08+N/sUrY0r2dvUw7cYvZear
+                nddp/VMF67S1Qj7qWrZEwmXSSoQVQk6nmGt3SQuyhRAWV0/gHufue+fBwGPc
+                J+mfBWCMqAAX43+SZyg6+8a/gL89gf8Jk8e5wcEKySq5b9E/T++4Q7hAWM9L
+                LGGVcJNopoi42oYT4mqI6RDXcJ2umAkxi1obzOAG5tooGngGNw1KBvMGC78A
+                XHUOHD4DAAA=
+                """,
+        """
                 androidx/navigation/TestAbstract.class:
-                H4sIAAAAAAAA/4VRy0oDMRQ9SduxjtXW+qovUBfiAxwVd4qgglioCirduEo7
-                QWOnCUzS4rLf4h+4ElxIcelHiTejezeH87gJJzdf3+8fAA6wzLAidJwaFT9H
-                WvTVg3DK6OhOWnfSsi4VbTcCxlB5En0RJUI/RNetJ+ndHENwpLRyxwy5jc1m
-                CQUEIfIYYci7R2UZ1hr/XX7IMNnoGJcoHV1KJ2LhBHm8289RQeZh1AMYWIf8
-                Z+XVLrF4j7oPB2HIazzkFWLDQXG9Nhzs8112Wvh8CXiF+7l95k9PX4n+hbHu
-                zGiXmiSR6U7HUdEzE0uGckNpedXrtmR6J1oJOdWGaYukKVLl9Z8Z3ppe2pbn
-                yov5m552qiubyipKT7Q2Lnugza+C0x7+avu1ENZIRZkGCltvKL4S4ZgnDDJz
-                GwuEpd8BjCLM8sUM57CU/RfDGGWle+TqGK9joo4yKkQxWUcVU/dgFtOYodwi
-                tJi1CH4AV4wNbewBAAA=
+                H4sIAAAAAAAA/4VRzS5DQRg9M21vuYqiqL8EC8HCReyIpCSiSZEg3VhNeydM
+                ezuT3Jk2ln0Wb2AlsZDG0kOJby57m5Pz883kzDdf3+8fAI6wxrAudJwaFT9H
+                WgzUo3DK6OheWldrWZeKtiuCMZQ7YiCiROjH6KbVkd7NMQQnSit3ypDb3mmW
+                UEAQIo8iQ949Kcuw2fjv8mOGmUbXuETp6Eo6EQsnyOO9QY4KMg/jHsDAuuQ/
+                K6/2icUH1H00DENe5SEvExsNx7aqo+Eh32dnhc+XgJe5nztk/nTlWgwujXXn
+                RrvUJIlM97qOip6bWDJMN5SW1/1eS6b3opWQM9swbZE0Raq8/jPDO9NP2/JC
+                ebF029dO9WRTWUVpTWvjsgfa/AY47eGvtl8LYZVUlGmgsPuGsVciHEuEQWbu
+                Ypmw9DuAcYRZvpLhIlaz/2KYoKz0gFwdk3VM1TGNMlHM1DGLuQcwiwrmKbcI
+                LRYsgh8jsPDA7AEAAA==
                 """,
-            """
+        """
+                androidx/navigation/TestAbstractComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSB5DwfgSpRaKTVOwKSCUIESktUqmy
+                6QI5E1OczHiQ7URdZsWH8AddIbFAUZd8FOJ6EmBLN/d17rnXPvbPX99/AHiG
+                xwzbQg9NpoankRZTdSKcynR0JK3bG1hnROw6Wfq56Y3QBJXAGGojMRVRIvRJ
+                9G4wkrErIWAoPldauZcMweZWv4oVFEMUUGIouE/KMrR6F1u1y9De7I0zlygd
+                jaZppLSTRoskei0/iklC7Zp4k9hlZl+YsTS7W/0Q3K9cb8b/wA9pjtJdLzaN
+                YfUPYV86MRROUI2n04DEY95UvAEDG1P9VPmsRdGwzdCcz8KQ13nIaxTNZ+Xz
+                L0F9PtvhLfaqVObnX4u8xn3vDvMTmv+jTQm3GCp/BWLYOBDTt5n1Z3cmSxJp
+                tseOBO9kQ8lwtae0PJikA2mOxCChylovi0XSF0b5fFmsdrWWppMIayU9U/g+
+                m5hYvlEeaxxOtFOp7CurqHlP68zlZ7Nok9IFf33y3L823eIeZZHXg/zKk28o
+                n+XwfbLFvPgCD8hWFw2oIARqjKJLS/JT8nxJrp7l2nrC9UVxQcijy7hCWICH
+                lIU56TbuoIFH+cK7aOb/mzSg3toxgi5Wu1jrYh0bFOJal2beOAazqKNBuEVo
+                cdOi+Bu0qQF5HAMAAA==
+                """,
+        """
+                androidx/navigation/TestAbstractComp.class:
+                H4sIAAAAAAAA/41RXU8TQRQ9s9vPdSstohZRAakIPLBATEwETbDG2KTUREkT
+                06dpO+K02xkzM2145Lf4D4gPJJoY4qM/ynh3qfDgCy/3zP06994zv/98/wng
+                KTYYalz1jZb940jxiTziTmoVHQrr9rvWGd5zdT36kgdjKA/4hEcxV0fRu+5A
+                9FwePkNuTyrpXjL4a+vtEFnkAmSQZ8i4z9IyrDavM2CXobDXi6dUm9dpqSWG
+                K0rlETJsrzWH2hFDNJiMIqmcMIrH0WvxiY9jalDUOe45bQ64GQqze7HszQAl
+                zDAUL8kYtq618dX43RAVzBbh4RbDSlObo2ggXNdwqWzEldIuZbBRS7vWOI7p
+                1sq/XQ+E433uOMW80cSnT2GJKSYGDGxI8WOZeFv06m+TnucnYeBVvcArn58E
+                XsErrFbPT5b8HW+LPWf+q+yvrzmv7CXVOyzhmGvxyVttEwmc0XEszObQMSy8
+                HysnR6KhJtLKbiz2rzalz6vrvmCYaUolWuNRV5hDTjUMs03d43GbG5n402DY
+                UEqYesytFdQcfNBj0xNvZJKbn85p/zcls0ySZdI75xMFCVfIyxHeJvQIs6lX
+                Iy9K1CDMbpyhcJqmH0+LgRdYJRteFKCIgLCAG5fNVaR6IvyB0kd2hvI3zJ2m
+                ER9PyAZUVyLGCi2ylnI/wjrhM4rfIca7HfgNVBuYb+AeFuiJ+w08wMMOmMUi
+                ljrIWAQWyxY5i8pfEw28C2ADAAA=
+                """,
+        """
                 androidx/navigation/TestClass.class:
-                H4sIAAAAAAAA/31Ry0oDMRQ9N7VTHavW+qrvrbpwrLhTBBXEQlVQ6cZV2gka
-                O01gkhaX/Rb/wJXgQopLP0q8M7p2cziPm5ubm6/v9w8AB1gnrEsTp1bHz5GR
-                A/0gvbYmulPOnyXSuRKIUHmSAxkl0jxE1+0n1fElFAjBkTbaHxMKW9utMooI
-                QoyhRBjzj9oRNpv/dj4kzDa71ifaRJfKy1h6yZ7oDQo8GmUwkQEI1GX/WWdq
-                j1lcJ2yMhmEoaiIUFWajYW003Bd7dFr8fAlERWRV+5Sdnb+SgwvLd1rjU5sk
-                Kt3tep7xzMaKMNPURl31e22V3sl2wk61aTsyaclUZ/rPDG9tP+2oc52J5Zu+
-                8bqnWtppTk+MsT5/m0MdglfwN3S2EcYaqyjXQHHnDeOvTASWGYPcXMUKY/m3
-                ABMI83w1xyWs5f9EmOSsfI9CA1MNTDcwgwpTzDZQxdw9yGEeC5w7hA6LDsEP
-                tqQsxuQBAAA=
+                H4sIAAAAAAAA/31Ry0oDMRQ9N7VTHau2Put7qy4cFXeKoIJYqAoq3bhKO0HT
+                ThOYpMVlv8U/cCW4kOLSjxLvjK4lcDiPm+Tm5uv7/QPAIdYJ69LEqdXxc2Tk
+                QD9Kr62J7pXz54l0rgQiVDpyIKNEmsfoptVRbV9CgRAca6P9CaGwtd0so4gg
+                xBhKhDH/pB1hs/HvyUeEaqNrfaJNdKW8jKWX7IneoMCtUQYTGYBAXfafdab2
+                mMX7hI3RMAxFTYSiwmw0rI2GB2KPzoqfL4GoiKzqgLK989dycGn5Tmt8apNE
+                pbtdzz2e21gRZhraqOt+r6XSe9lK2Jlt2LZMmjLVmf4zwzvbT9vqQmdi+bZv
+                vO6ppnaa01NjrM/f5rAPwSP4azqbCGONVZRroLjzhvFXJgLLjEFurvACyr8F
+                mECY56s5LmEt/yfCJGflBxTqmKpjuo4ZVJiiWscs5h5ADvNY4NwhdFh0CH4A
+                wpjRa+QBAAA=
                 """,
-            """
+        """
+                androidx/navigation/TestClassComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSB5DwLgSpBbXTVOyKkCAIESktElTZ
+                dIGcxBQnMzaynajLrPgQ/qArJBYo6pKPQlxPCixRN/dx7j332sf++ev7DwBP
+                8YjhidBDa9TwJNFiqo6FV0Ynh9L5diqca5vsczMYoQkvgTHURmIqklTo4+Rt
+                fyQHvoSIofhMaeWfM0Qbm70qllCMUUCJoeA/Kcew1b3Anj2G1kZ3bHyqdDKa
+                ZonSXlot0uSV/CgmqW8b7bydDLyx+8KOpd3b7MXgYd9qc/Cv+CHLqwzbF5vG
+                sPyHsC+9GAovCOPZNCLZWDCVYMDAxoSfqJDtUDRsMTTnszjmdR7zGkXzWfns
+                S1Sfz3b5DntZKvOzr0Ve46F3l4UJ6/8VpoRbDJW/6jCsHYjpG+PCwb01aSrt
+                9tiT1G0zlAxXu0rLg0nWl/ZQ9FNCVrpmINKesCrk52C1o7W0+RJJDxS/NxM7
+                kK9VqDXeTbRXmewpp6j5hdbG5wdzaJHMhXB38jy8M13hHmVJEIP80uNvKJ/m
+                5ftkizmYYJ1sddGACmKgxii6dE7eIs/PydXTXNhAuL4AF4Q8uowrVIvwgLI4
+                J93GHTTwMF94F838W5MG1Fs7QtTBcgcrHaxijUJc69DMG0dgDnU0qO4QO9x0
+                KP4Ga0TtSRMDAAA=
+                """,
+        """
+                androidx/navigation/TestClassComp.class:
+                H4sIAAAAAAAA/4VRXU8TQRQ9s9vPdZEWUYuogKACRheIiYkQE60xNik1UUJi
+                eJq2I067nTEz04ZHfov/gPhAoolpfPRHGe8sFR584OWeuefee+7H/P7z/SeA
+                p1hnWOKqa7TsHiWKj+Qhd1KrZE9YV0+5tXU9+FIEY6j0+IgnKVeHybt2T3Rc
+                ESFDYUcq6V4whKtr+zHyKETIociQc5+lZVhuXqq+zVDa6aQTnUeX5q94wxXx
+                RcQMm6vNvnZUnvRGg0QqJ4ziafJafOLD1NW1ss4MO06bXW76wmyfjXk1whSm
+                GcrnYgyPL5/1ovd2jCpmyghwzW+pzWHSE65tuFQ24Uppl5XbpKVda5imtGX1
+                36C7wvEud5y4YDAK6SOYN2VvwMD6xB9J723Qq7vJsDI+jqOgFkRBZXwcBaWg
+                Nj5eDLeCDfacha/yv74Wgkrgc7eYV5ht8dFbbf32zug0FeZJ3zHMvx8qJwei
+                oUbSynYqXl7MST9W113BMN2USrSGg7Ywe5xyGGaausPTfW6k9ydk3FBKmOww
+                goqjD3poOuKN9LG5SZ/9/7pgkw6Wy7ac8/cjXCavQHidMCDMZ94KeYm/BWF+
+                /RSlkyx8f5Lsgw/IxmcJKCMiLOHKeXEN2TUR/8DUR3aKyjfMnmRMiIdkI8qb
+                IsUqDbKaad/DGuEz4m+Q4s0DhA3UGphr4Bbm6YnbDdzB3QMwiwUsHiBnEVks
+                WRQsqn8BnQupylIDAAA=
+                """,
+        """
                 androidx/navigation/TestClassWithArg.class:
-                H4sIAAAAAAAA/41Qz2sTQRh9M5tskjUxm1g1Ta31R5E2Bzct3pRiDIgLsUIt
+                H4sIAAAAAAAA/41Qz2sTQRh9M5tskjUxm1g1Tav1R5E2Bzct3pRiDIgLsUIt
                 8ZDTJLuk02xmYWcSeszf4tmLoAgeJHj0jxK/2RRPHoTd931v5vG++d6v399/
                 AHiGfYZ9oaIsldFVoMRSToWRqQrOY236idD6gzQXvWxaAmPwL8VSBIlQ0+Dd
                 +DKemBIcBveFVNKcMBQOwsMhg3NwOKyiiJKHAsrERTZlYGEVHm5UwFElqbmQ
@@ -437,23 +765,56 @@
                 nYtxQifNQToRyVBk0vLrQ+99usgm8WtpyfbZQhk5j4dSS7rtKZWaPBaNI8qu
                 kO/VtFFSx6kvwiXcI3ZCnFP1Ot9Q6ex8Re1zrnlAaDXALh4S3tmocBN1GxN1
                 1o1ShU//xiuw6VEtdr6g9umfNtWN4NqG41GO9/GY6sv8kUXcGsEJsRXidkhj
-                71KLVohttEdgGju4N0JJo66xq+Hl6Gr4Go0/oUSxVaECAAA=
+                71KLVohttEdgGjvYHaGkUde4p+Hl6Gr4Go0/q1d22KECAAA=
                 """,
-            """
+        """
+                androidx/navigation/TestClassWithArgComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ9nknzGAKkDyDh/QhSikSniborQipBiEhpkWgV
+                Fl0gJzGtkxkb2U7UZVZ8CH/QFRILFHXJRyGuJwG2dHN97zn33Os5np+/vv8A
+                sIOnDC2uhkbL4Vms+FSecCe1io+Ede2EW/tButM9c9LW6ee6D1wRXQBjqIz4
+                lMcJVyfxu/5IDFwBIUP+hVTSvWQIG5u9MlaQj5BDgSHnTqVl2Oleft0uQ7PR
+                HWuXSBWPpmkslRNG8SR+LT7xSeLaWllnJgOnzT43Y2F2N3sRAr92vT74R35M
+                M5Zh63LTGFb/CPaF40PuOGFBOg3JROZDyQcwsDHhZ9JX25QNmwz1+SyKgmoQ
+                BRXK5rPixZewOp+1gm32qlAMLr7mg0rge1vMT2j8rz8F3GEo/TWJYeOAT99q
+                6+/vjE4SYbbGjoxv66FguN6VShxM0r4wR7yfELLW1QOe9LiRvl6C5Y5SwmS7
+                BD1XdKgnZiDeSM/V3k+Uk6noSSupeU8p7bL7WTTJ7Zy3gM7Avzp9yQOqYu8J
+                nSvPvqF4ntEPKeYzsIlHFMuLBpQQARVG2ZWl+DmdwVJcPs/89YKbC3AhyLKr
+                uEZciMdURZnoLu6hhifZwvuoZ/86eUC9lWOEHax2sNbBOjYoxY0Ozbx1DGZR
+                RY14i8jitkX+N9lemKQoAwAA
+                """,
+        """
+                androidx/navigation/TestClassWithArgComp.class:
+                H4sIAAAAAAAA/41S308TQRD+9vrrehY5KmIBf6CglqpcITwJIcEawyWlJkhq
+                DE/bdi3bXvfM3bbhkb/FZ1+IGhJNDPHRP8o4e1R40AeSu5mduZnv2/nmfv3+
+                9gPAOlYZylx1olB2jjzFR7LLtQyVty9iXQt4HL+V+nA76tbCwYccGIPb4yPu
+                BVx1vdetnmjrHFIM2U2ppN5iSJf95SZDqrzcLCCDnIM0bIp51GVgfgEOruVh
+                oUCl+lDGDJX6Vfk3iKcr9LaBIgKfwd5sB2PitauiLBnDFX3O4QbDarneDzWh
+                eL3RwJNKi0jxwHsp3vNhoGuhinU0bOsw2uVRX0Qb53PddDCNGYb8BRjD+pUH
+                ubzCRgElzBpB5hgW62HU9XpCtyIuVexxpUKdoMReI9SNYRCQBFN/77srNO9w
+                zSlnDUYpWiczJm8MSOw+5Y+kiap06tCmd86Oi45VshzLPTt26LFc27HsdOns
+                eCG3ZlXZc5Z7MVHMutacVU39/Ji13PTe1EVkU8tc2s64WYO3xgzLdIOPdsLY
+                CKWjMAhEtNLXDPN7Q6XlQPhqJGPZCsT25Sy0+VrYEQyTdalEYzhoiWifUw1D
+                sR62edDkkTTxOFnwlRJRoqGgZudNOIza4pU032bHPM1/WLBKoqZpeAuzRmO6
+                a4WiLPnb5IvmRySfojiTZJ9QtEXVFnmncop8Zf4rJk4ShKfjThDqM7Iz51W4
+                jkkjNp0MGu0GLr3nWJ7ZAflM5QsmPv0XpnBeMIax6VK5cXMJyRZR+I7pd+wU
+                tz5j/iTJpLCSEJL0hGgG8xLsZVTJ1yh/hxDvHiDl456PBR/38YCOWPSxhIcH
+                YDEe4fEB7BiTMcoxnMRmY7gxpmKU/gBa3QlZHAQAAA==
+                """,
+        """
                 androidx/navigation/TestGraph.class:
-                H4sIAAAAAAAA/31SXWsTQRQ9M/nabKONtZrEWqu2D60Pblt8swg1+LEQV7Ah
-                UPo0yQ7pJJsZ2Z2EPubJH+I/KD4UFCTomz9KvLMGfRDcgXvvOXPmMPfu/Pj5
-                +SuAJ9hh2BQ6To2KLwItZmoorDI66MrMvkrF+/MKGEN9JGYiSIQeBm/7Izmw
-                FRQYykdKK/uMobC716uhhLKPIioMRXuuMoatzn+dnzJ4R4Mk9/DB3UEvjE66
-                x1H7RQ3X4FeJvM6w3THpMBhJ20+F0lkgtDY298qCyNhomiRkdaMzNpbMgjfS
-                ilhYQRyfzArUJXOh6gIY2Jj4C+XQPlXxAcPOYu77vMl9XqdqMfe+f+DNxfyQ
-                77PnFY9/+1jmde60h8w5rEdi9tpktm20TU2SyPTx2DJsvJtqqyYy1DOVqX4i
-                j//ek0bSNrFkWO0oLaPppC/TriANw1rHDETSE6lyeEn6J2aaDuRL5UBradz7
-                xxYHNKFi3lbLDYzyPUJlynXKnFYpR1uEAtc85dKjK3iX+fb9pRho4AHF2m8B
-                qmQFeFj5c7hBavetfAE/vULtE1Yvc4LjYR43sZ2/J/oRZLB2hkKImyHWQ9zC
-                bSrRCNFE6wwswx1s0H4GP8PdDOVfjjOBnYwCAAA=
+                H4sIAAAAAAAA/31SXWsTQRQ9M/nabKONtbaJtVZtH9QHty2+WYQa/FhYV7Ah
+                IH2aZId0ks2M7E5CH/PkD/EfFB8KChL0zR8l3lmDPgjuwL33nDlzmHt3fvz8
+                /BXAY+wxbAudZEYl54EWMzUUVhkddGVuX2bi/VkNjKE5EjMRpEIPgzf9kRzY
+                GkoM1SOllX3KULr/oNdABVUfZdQYyvZM5Qw70X+dnzB4R4O08PDB3UEvjE+6
+                x3HneQNX4NeJvMqwG5lsGIyk7WdC6TwQWhtbeOVBbGw8TVOyuhaNjSWz4LW0
+                IhFWEMcnsxJ1yVyouwAGNib+XDm0T1VywLC3mPs+b3GfN6lazL3vH3hrMT/k
+                ++xZzePfPlZ5kzvtIXMO67GYvTK57RhtM5OmMns0tgxbb6faqokM9Uzlqp/K
+                47/3pJF0TCIZViOlZTyd9GXWFaRhWIvMQKQ9kSmHl6R/YqbZQL5QDrSXxr1/
+                bHFAEyoXbbXdwCjfJlSl3KTMaVUKtEMocM1Trjy8hHdRbN9ZioEN3KXY+C1A
+                nawADyt/Dm+S2n0rX8DfXaLxCasXBcFxr4jb2C3eE/0IMlg7RSnE9RDrIW5g
+                g0pshmihfQqW4ya2aD+Hn+NWjuovG8mH6YwCAAA=
                 """,
-            """
+        """
                 androidx/navigation/TestInterface.class:
                 H4sIAAAAAAAA/4WOz0rDQBDGv9lo08Z/qbZQj+LdtKU3TyKIgaqg4iWnbbIt
                 22x3IbsNPfa5PEjPPpS4qQ/gDHzzzQz8Zr5/Pr8ATNAnXHFdVEYWm0TzWi64
@@ -463,24 +824,18 @@
                 czjAXzBc7PUcPV9HHn7os5UhSBGmaKfoIPIWRymOcZKBLE5xloFZxBbdX1Yg
                 D5FVAQAA
                 """,
-            """
+        """
                 androidx/navigation/TestObject.class:
                 H4sIAAAAAAAA/31SQWsTQRT+ZpJsNtto01ptYrVW20P14LbFm0WoQXEhrmBD
-                oPQ0yQ5xks0M7E5Cjzn5Q/wHxUNBQYLe/FHimzXqQXAX3nvfN9/72Pd2vv/4
-                9AXAE+wxbAudZEYlF6EWMzUUVhkddmVu3/RHcmCrYAyNkZiJMBV6GP5mSwze
-                sdLKPmMo7T/s1VGBF6CMKkPZvlM5w07n/9ZPGfzjQVqYBOCu04/i0+5J3H5R
-                xzUENSKvM+x2TDYMR9L2M6F0HgqtjS3M8jA2Np6mKVmtdcbGkln4WlqRCCuI
-                45NZieZkLtRcAAMbE3+hHDqgKjlk2FvMg4A3ecAbVC3m/rf3vLmYH/ED9rzq
-                868fPN7gTnvEnMNGLGavTG7bRtvMpKnMHo8tw9bbqbZqIiM9U7nqp/Lk73fS
-                TtomkQyrHaVlPJ30ZdYVpGFY75iBSHsiUw4vyeDUTLOBfKkcaC2Ne//Y4pA2
-                VC7GarmFUd4m5FFuUOb0Vgp0j1DohqdceXQF/7I43lmKQe33KdZ/CVAjK8DH
-                yp/mTVK7Z+Uz+NkV6h+xelkQHA+KeBe7xY2iH0EG6+coRbgRYSPCTdyiEpsR
-                mmidg+W4jS06zxHkuJPD+wldvRYbjgIAAA==
+                oPQ0yQ5xks0M7E5Cjzn5Q/wHxUNBQYLe/FHimzXqQXAH3nvfN998s+/tfv/x
+                6QuAJ9hj2BY6yYxKLkItZmoorDI67MrcvumP5MBWwRgaIzETYSr0MPzNlhi8
+                Y6WVfcZQ2n/Yq6MCL0AZVYayfadyhp3O/62fMvjHg7QwCcDdST+KT7sncftF
+                HdcQ1Ii8zrDbMdkwHEnbz4TSeSi0NrYwy8PY2HiapmS11hkbS2bha2lFIqwg
+                jk9mJeqTuVBzAQxsTPyFcuiAquSQYW8xDwLe5AFvULWY+9/e8+ZifsQP2POq
+                z79+8HiDO+0Rcw4bsZi9MrltG20zk6Yyezy2DFtvp9qqiYz0TOWqn8qTv+9J
+                M2mbRDKsdpSW8XTSl1lXkIZhvWMGIu2JTDm8JINTM80G8qVyoLU07v1ji0Oa
+                ULloq+UGRnmbkEe5QZnTqhToHqHQNU+58ugK/mWxvbMUA03cp1j/JUCNrAAf
+                K38Ob5LaPSufwc+uUP+I1cuC4HhQxLvYLf4o+hBksH6OUoQbETYi3MQtKrEZ
+                0SWtc7Act7FF+zmCHHdyeD8ByEcQb44CAAA=
                 """
-        )
-
-    override fun getDetector(): Detector = WrongStartDestinationTypeDetector()
-
-    override fun getIssues(): MutableList<Issue> =
-        mutableListOf(WrongStartDestinationTypeDetector.WrongStartDestinationType)
-}
+    )
diff --git a/navigation/navigation-compose/api/2.8.0-beta06.txt b/navigation/navigation-compose/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..f4411f7
--- /dev/null
+++ b/navigation/navigation-compose/api/2.8.0-beta06.txt
@@ -0,0 +1,94 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+  @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+    ctor public ComposeNavigator();
+    method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+    method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    method public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor @Deprecated public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class ComposeNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.compose.ComposeNavigator.Destination> {
+    ctor public ComposeNavigatorDestinationBuilder(androidx.navigation.compose.ComposeNavigator navigator, String route, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    ctor public ComposeNavigatorDestinationBuilder(androidx.navigation.compose.ComposeNavigator navigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public androidx.navigation.compose.ComposeNavigator.Destination build();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? getEnterTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? getExitTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? getPopEnterTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? getPopExitTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? getSizeTransform();
+    method protected androidx.navigation.compose.ComposeNavigator.Destination instantiateDestination();
+    method public void setEnterTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>?);
+    method public void setExitTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>?);
+    method public void setPopEnterTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>?);
+    method public void setPopExitTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>?);
+    method public void setSizeTransform(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>?);
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform;
+  }
+
+  public final class DialogHostKt {
+    method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+    ctor public DialogNavigator();
+    method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DialogNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.compose.DialogNavigator.Destination> {
+    ctor public DialogNavigatorDestinationBuilder(androidx.navigation.compose.DialogNavigator navigator, String route, androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    ctor public DialogNavigatorDestinationBuilder(androidx.navigation.compose.DialogNavigator navigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method protected androidx.navigation.compose.DialogNavigator.Destination instantiateDestination();
+  }
+
+  public final class NavBackStackEntryProviderKt {
+    method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class NavGraphBuilderKt {
+    method @Deprecated public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method @Deprecated public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static inline <reified T> void composable(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static inline <reified T> void dialog(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, Object startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition?>? popExitTransition, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostControllerKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry?> currentBackStackEntryAsState(androidx.navigation.NavController);
+    method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> popExitTransition);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, Object startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional String? route, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> popExitTransition, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional String? route, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+}
+
diff --git a/constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt b/navigation/navigation-compose/api/res-2.8.0-beta06.txt
similarity index 100%
copy from constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt
copy to navigation/navigation-compose/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-compose/api/restricted_2.8.0-beta06.txt b/navigation/navigation-compose/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..f4411f7
--- /dev/null
+++ b/navigation/navigation-compose/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,94 @@
+// Signature format: 4.0
+package androidx.navigation.compose {
+
+  @androidx.navigation.Navigator.Name("composable") public final class ComposeNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.ComposeNavigator.Destination> {
+    ctor public ComposeNavigator();
+    method public androidx.navigation.compose.ComposeNavigator.Destination createDestination();
+    method public kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getBackStack();
+    method public void onTransitionComplete(androidx.navigation.NavBackStackEntry entry);
+    method public void prepareForTransition(androidx.navigation.NavBackStackEntry entry);
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> backStack;
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class ComposeNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor @Deprecated public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class ComposeNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.compose.ComposeNavigator.Destination> {
+    ctor public ComposeNavigatorDestinationBuilder(androidx.navigation.compose.ComposeNavigator navigator, String route, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    ctor public ComposeNavigatorDestinationBuilder(androidx.navigation.compose.ComposeNavigator navigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public androidx.navigation.compose.ComposeNavigator.Destination build();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? getEnterTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? getExitTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? getPopEnterTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? getPopExitTransition();
+    method public kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? getSizeTransform();
+    method protected androidx.navigation.compose.ComposeNavigator.Destination instantiateDestination();
+    method public void setEnterTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>?);
+    method public void setExitTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>?);
+    method public void setPopEnterTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>?);
+    method public void setPopExitTransition(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>?);
+    method public void setSizeTransform(kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>?);
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition;
+    property public final kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform;
+  }
+
+  public final class DialogHostKt {
+    method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator);
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator<androidx.navigation.compose.DialogNavigator.Destination> {
+    ctor public DialogNavigator();
+    method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Composable::class) public static final class DialogNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogNavigator.Destination(androidx.navigation.compose.DialogNavigator navigator, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DialogNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.compose.DialogNavigator.Destination> {
+    ctor public DialogNavigatorDestinationBuilder(androidx.navigation.compose.DialogNavigator navigator, String route, androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    ctor public DialogNavigatorDestinationBuilder(androidx.navigation.compose.DialogNavigator navigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method protected androidx.navigation.compose.DialogNavigator.Destination instantiateDestination();
+  }
+
+  public final class NavBackStackEntryProviderKt {
+    method @androidx.compose.runtime.Composable public static void LocalOwnersProvider(androidx.navigation.NavBackStackEntry, androidx.compose.runtime.saveable.SaveableStateHolder saveableStateHolder, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class NavGraphBuilderKt {
+    method @Deprecated public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method @Deprecated public static void composable(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static inline <reified T> void composable(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function2<? super androidx.compose.animation.AnimatedContentScope,? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static void dialog(androidx.navigation.NavGraphBuilder, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static inline <reified T> void dialog(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional androidx.compose.ui.window.DialogProperties dialogProperties, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> content);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, Object startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition?>? popExitTransition, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static void navigation(androidx.navigation.NavGraphBuilder, String startDestination, String route, optional java.util.List<androidx.navigation.NamedNavArgument> arguments, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.NavGraphBuilder, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional java.util.List<androidx.navigation.NavDeepLink> deepLinks, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition?>? popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition?>? popExitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostControllerKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.navigation.NavBackStackEntry?> currentBackStackEntryAsState(androidx.navigation.NavController);
+    method @androidx.compose.runtime.Composable public static androidx.navigation.NavHostController rememberNavController(androidx.navigation.Navigator<? extends androidx.navigation.NavDestination>... navigators);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier);
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> popExitTransition);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, androidx.navigation.NavGraph graph, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, Object startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional String? route, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<? super androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,? extends androidx.compose.animation.ExitTransition> popExitTransition, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional String? route, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, String startDestination, optional androidx.compose.ui.Modifier modifier, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method @androidx.compose.runtime.Composable public static void NavHost(androidx.navigation.NavHostController navController, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.Alignment contentAlignment, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> enterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> exitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.EnterTransition> popEnterTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.ExitTransition> popExitTransition, optional kotlin.jvm.functions.Function1<androidx.compose.animation.AnimatedContentTransitionScope<androidx.navigation.NavBackStackEntry>,androidx.compose.animation.SizeTransform?>? sizeTransform, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+}
+
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index 2e7e9c4..0337b95 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -28,11 +28,11 @@
 
     implementation(libs.kotlinStdlib)
     api("androidx.activity:activity-compose:1.8.0")
-    api("androidx.compose.animation:animation:1.7.0-beta04")
-    implementation("androidx.compose.foundation:foundation-layout:1.7.0-beta04")
-    api("androidx.compose.runtime:runtime:1.7.0-beta04")
-    api("androidx.compose.runtime:runtime-saveable:1.7.0-beta04")
-    api("androidx.compose.ui:ui:1.7.0-beta04")
+    api("androidx.compose.animation:animation:1.7.0-beta06")
+    implementation("androidx.compose.foundation:foundation-layout:1.7.0-beta06")
+    api("androidx.compose.runtime:runtime:1.7.0-beta06")
+    api("androidx.compose.runtime:runtime-saveable:1.7.0-beta06")
+    api("androidx.compose.ui:ui:1.7.0-beta06")
     api("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
     api(projectOrArtifact(":navigation:navigation-runtime-ktx"))
     implementation(libs.kotlinSerializationCore)
@@ -67,11 +67,11 @@
     description = "Compose integration with Navigation"
     legacyDisableKotlinStrictApiMode = true
     samples(projectOrArtifact(":navigation:navigation-compose:navigation-compose-samples"))
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     sourceSets.androidTest.assets.srcDirs +=
             project.rootDir.absolutePath + "/../../golden/navigation/navigation-compose"
 
diff --git a/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle b/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
index 03a6aca..41d1a1a 100644
--- a/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
+++ b/navigation/navigation-compose/integration-tests/navigation-demos/build.gradle
@@ -38,6 +38,7 @@
     implementation(project(":compose:integration-tests:demos:common"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:material:material"))
+    implementation("androidx.compose.material:material-icons-core:1.6.7")
     implementation(project(":navigation:navigation-compose"))
     implementation(project(":navigation:navigation-compose:navigation-compose-samples"))
 }
@@ -50,5 +51,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.navigation.compose.demos"
 }
diff --git a/navigation/navigation-compose/samples/build.gradle b/navigation/navigation-compose/samples/build.gradle
index b7b3ffb..19e494b 100644
--- a/navigation/navigation-compose/samples/build.gradle
+++ b/navigation/navigation-compose/samples/build.gradle
@@ -59,5 +59,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.navigation.compose.samples"
 }
diff --git a/navigation/navigation-dynamic-features-fragment/api/2.8.0-beta06.txt b/navigation/navigation-dynamic-features-fragment/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..575de0b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/2.8.0-beta06.txt
@@ -0,0 +1,73 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+  @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+    ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, String fragmentClassName);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  public final class DynamicFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String fragmentClassName, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+    ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+  }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+  public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractProgressFragment();
+    ctor public AbstractProgressFragment(int contentLayoutId);
+    method protected abstract void onCancelled();
+    method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onInstalled();
+    method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+  }
+
+  public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+    ctor public DefaultProgressFragment();
+    method protected void onCancelled();
+    method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-dynamic-features-fragment/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-dynamic-features-fragment/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-dynamic-features-fragment/api/restricted_2.8.0-beta06.txt b/navigation/navigation-dynamic-features-fragment/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..575de0b
--- /dev/null
+++ b/navigation/navigation-dynamic-features-fragment/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,73 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures.fragment {
+
+  @androidx.navigation.Navigator.Name("fragment") public final class DynamicFragmentNavigator extends androidx.navigation.fragment.FragmentNavigator {
+    ctor public DynamicFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager manager, int containerId, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicFragmentNavigator.Destination extends androidx.navigation.fragment.FragmentNavigator.Destination {
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    ctor public DynamicFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, @IdRes int id, String fragmentClassName);
+    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, String route, String fragmentClassName);
+    ctor public DynamicFragmentNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator navigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, String fragmentClassName);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigator.Destination build();
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  public final class DynamicFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route);
+    method public static inline void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String fragmentClassName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String fragmentClassName, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.fragment.DynamicFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public class DynamicNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+    ctor public DynamicNavHostFragment();
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+    method public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method protected com.google.android.play.core.splitinstall.SplitInstallManager createSplitInstallManager();
+    field public static final androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment.Companion Companion;
+  }
+
+  public static final class DynamicNavHostFragment.Companion {
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+  }
+
+}
+
+package androidx.navigation.dynamicfeatures.fragment.ui {
+
+  public abstract class AbstractProgressFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractProgressFragment();
+    ctor public AbstractProgressFragment(int contentLayoutId);
+    method protected abstract void onCancelled();
+    method protected abstract void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onInstalled();
+    method protected abstract void onProgress(@com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus int status, long bytesDownloaded, long bytesTotal);
+  }
+
+  public final class DefaultProgressFragment extends androidx.navigation.dynamicfeatures.fragment.ui.AbstractProgressFragment {
+    ctor public DefaultProgressFragment();
+    method protected void onCancelled();
+    method protected void onFailed(@com.google.android.play.core.splitinstall.model.SplitInstallErrorCode int errorCode);
+    method protected void onProgress(int status, long bytesDownloaded, long bytesTotal);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-fragment/build.gradle b/navigation/navigation-dynamic-features-fragment/build.gradle
index 63a98a1..a1527d203 100644
--- a/navigation/navigation-dynamic-features-fragment/build.gradle
+++ b/navigation/navigation-dynamic-features-fragment/build.gradle
@@ -67,6 +67,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2019"
     description = "Android Dynamic Feature Navigation Fragment"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/navigation/navigation-dynamic-features-runtime/api/2.8.0-beta06.txt b/navigation/navigation-dynamic-features-runtime/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..2761db9
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/2.8.0-beta06.txt
@@ -0,0 +1,169 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+  @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+    ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+    method public String? getAction();
+    method public String? getActivityClassName();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getModuleName();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClassName(String?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setModuleName(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final String? activityClassName;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? moduleName;
+    property public final String? targetPackage;
+  }
+
+  public final class DynamicActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+    ctor public DynamicExtras();
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+    method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+    method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+    property public final androidx.navigation.Navigator.Extras? destinationExtras;
+    property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+    ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+    method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+  }
+
+  public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+    ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    property public final String? moduleName;
+    property public final int progressDestination;
+  }
+
+  @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+  }
+
+  public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+    method public String? getGraphPackage();
+    method public String? getGraphResourceName();
+    method public String? getModuleName();
+    method public void setGraphPackage(String?);
+    method public void setGraphResourceName(String?);
+    method public void setModuleName(String?);
+    property public final String? graphPackage;
+    property public final String? graphResourceName;
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, String moduleName, String graphResourceName);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+    method public String? getGraphPackage();
+    method public void setGraphPackage(String?);
+    property public final String? graphPackage;
+  }
+
+  public final class DynamicIncludeNavGraphBuilderKt {
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String moduleName, String graphResourceName, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified T> void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String moduleName, String graphResourceName, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public class DynamicInstallManager {
+    ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+  }
+
+  public final class DynamicInstallMonitor {
+    ctor public DynamicInstallMonitor();
+    method public void cancelInstall();
+    method public Exception? getException();
+    method public int getSessionId();
+    method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+    method public boolean isInstallRequired();
+    property public final Exception? exception;
+    property public final boolean isInstallRequired;
+    property public final int sessionId;
+    property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, Object startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public String? getProgressDestinationRoute();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    method public void setProgressDestinationRoute(String?);
+    property public final String? moduleName;
+    property public final int progressDestination;
+    property public final String? progressDestinationRoute;
+  }
+
+  public final class DynamicNavGraphBuilderKt {
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, Object startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-dynamic-features-runtime/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-dynamic-features-runtime/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-dynamic-features-runtime/api/restricted_2.8.0-beta06.txt b/navigation/navigation-dynamic-features-runtime/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..2761db9
--- /dev/null
+++ b/navigation/navigation-dynamic-features-runtime/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,169 @@
+// Signature format: 4.0
+package androidx.navigation.dynamicfeatures {
+
+  @androidx.navigation.Navigator.Name("activity") public final class DynamicActivityNavigator extends androidx.navigation.ActivityNavigator {
+    ctor public DynamicActivityNavigator(android.content.Context context, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination createDestination();
+  }
+
+  public static final class DynamicActivityNavigator.Destination extends androidx.navigation.ActivityNavigator.Destination {
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    ctor public DynamicActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public void setModuleName(String?);
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, @IdRes int id);
+    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, String route);
+    ctor public DynamicActivityNavigatorDestinationBuilder(androidx.navigation.dynamicfeatures.DynamicActivityNavigator activityNavigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public androidx.navigation.dynamicfeatures.DynamicActivityNavigator.Destination build();
+    method public String? getAction();
+    method public String? getActivityClassName();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getModuleName();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClassName(String?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setModuleName(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final String? activityClassName;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? moduleName;
+    property public final String? targetPackage;
+  }
+
+  public final class DynamicActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void activity(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class DynamicExtras implements androidx.navigation.Navigator.Extras {
+    ctor public DynamicExtras();
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor);
+    ctor public DynamicExtras(optional androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor, optional androidx.navigation.Navigator.Extras? destinationExtras);
+    method public androidx.navigation.Navigator.Extras? getDestinationExtras();
+    method public androidx.navigation.dynamicfeatures.DynamicInstallMonitor? getInstallMonitor();
+    property public final androidx.navigation.Navigator.Extras? destinationExtras;
+    property public final androidx.navigation.dynamicfeatures.DynamicInstallMonitor? installMonitor;
+  }
+
+  @androidx.navigation.Navigator.Name("navigation") public final class DynamicGraphNavigator extends androidx.navigation.NavGraphNavigator {
+    ctor public DynamicGraphNavigator(androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicGraphNavigator.DynamicNavGraph createDestination();
+    method public void installDefaultProgressDestination(kotlin.jvm.functions.Function0<? extends androidx.navigation.NavDestination> progressDestinationSupplier);
+  }
+
+  public static final class DynamicGraphNavigator.DynamicNavGraph extends androidx.navigation.NavGraph {
+    ctor public DynamicGraphNavigator.DynamicNavGraph(androidx.navigation.dynamicfeatures.DynamicGraphNavigator navGraphNavigator, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    property public final String? moduleName;
+    property public final int progressDestination;
+  }
+
+  @androidx.navigation.Navigator.Name("include-dynamic") public final class DynamicIncludeGraphNavigator extends androidx.navigation.Navigator<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor public DynamicIncludeGraphNavigator(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider, androidx.navigation.NavInflater navInflater, androidx.navigation.dynamicfeatures.DynamicInstallManager installManager);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph createDestination();
+  }
+
+  public static final class DynamicIncludeGraphNavigator.DynamicIncludeNavGraph extends androidx.navigation.NavDestination {
+    method public String? getGraphPackage();
+    method public String? getGraphResourceName();
+    method public String? getModuleName();
+    method public void setGraphPackage(String?);
+    method public void setGraphResourceName(String?);
+    method public void setModuleName(String?);
+    property public final String? graphPackage;
+    property public final String? graphResourceName;
+    property public final String? moduleName;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicIncludeNavGraphBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph> {
+    ctor @Deprecated public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, @IdRes int id, String moduleName, String graphResourceName);
+    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, String route, String moduleName, String graphResourceName);
+    ctor public DynamicIncludeNavGraphBuilder(androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator dynamicIncludeGraphNavigator, kotlin.reflect.KClass<? extends java.lang.Object?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, String moduleName, String graphResourceName);
+    method public androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.DynamicIncludeNavGraph build();
+    method public String? getGraphPackage();
+    method public void setGraphPackage(String?);
+    property public final String? graphPackage;
+  }
+
+  public final class DynamicIncludeNavGraphBuilderKt {
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName);
+    method @Deprecated public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName);
+    method public static inline void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String route, String moduleName, String graphResourceName, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String moduleName, String graphResourceName, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified T> void includeDynamic(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String moduleName, String graphResourceName, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicIncludeNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public class DynamicInstallManager {
+    ctor public DynamicInstallManager(android.content.Context context, com.google.android.play.core.splitinstall.SplitInstallManager splitInstallManager);
+  }
+
+  public final class DynamicInstallMonitor {
+    ctor public DynamicInstallMonitor();
+    method public void cancelInstall();
+    method public Exception? getException();
+    method public int getSessionId();
+    method public androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> getStatus();
+    method public boolean isInstallRequired();
+    property public final Exception? exception;
+    property public final boolean isInstallRequired;
+    property public final int sessionId;
+    property public final androidx.lifecycle.LiveData<com.google.android.play.core.splitinstall.SplitInstallSessionState> status;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DynamicNavGraphBuilder extends androidx.navigation.NavGraphBuilder {
+    ctor @Deprecated public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, @IdRes int id, @IdRes int startDestination);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, Object startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, String startDestination, optional String? route);
+    ctor public DynamicNavGraphBuilder(androidx.navigation.NavigatorProvider provider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, kotlin.reflect.KClass<? extends java.lang.Object?>? route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public String? getModuleName();
+    method public int getProgressDestination();
+    method public String? getProgressDestinationRoute();
+    method public void setModuleName(String?);
+    method public void setProgressDestination(int);
+    method public void setProgressDestinationRoute(String?);
+    property public final String? moduleName;
+    property public final int progressDestination;
+    property public final String? progressDestinationRoute;
+  }
+
+  public final class DynamicNavGraphBuilderKt {
+    method @Deprecated public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, Object startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, String startDestination, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void navigation(androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method @Deprecated public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph navigation(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.dynamicfeatures.DynamicNavGraphBuilder,kotlin.Unit> builder);
+  }
+
+}
+
diff --git a/navigation/navigation-dynamic-features-runtime/build.gradle b/navigation/navigation-dynamic-features-runtime/build.gradle
index e3f17e2..8693621 100644
--- a/navigation/navigation-dynamic-features-runtime/build.gradle
+++ b/navigation/navigation-dynamic-features-runtime/build.gradle
@@ -68,6 +68,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2019"
     description = "Android Dynamic Feature Navigation Runtime"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/navigation/navigation-fragment-compose/api/2.8.0-beta06.txt b/navigation/navigation-fragment-compose/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..d598ace
--- /dev/null
+++ b/navigation/navigation-fragment-compose/api/2.8.0-beta06.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.navigation.fragment.compose {
+
+  public final class ComposableFragment extends androidx.fragment.app.Fragment {
+    method public static androidx.navigation.fragment.compose.ComposableFragment ComposableFragment(String fullyQualifiedName);
+    field public static final androidx.navigation.fragment.compose.ComposableFragment.Companion Companion;
+  }
+
+  public static final class ComposableFragment.Companion {
+    method public androidx.navigation.fragment.compose.ComposableFragment ComposableFragment(String fullyQualifiedName);
+  }
+
+  @androidx.navigation.Navigator.Name("composable") public final class ComposableFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor public ComposableFragmentNavigator(androidx.navigation.fragment.FragmentNavigator fragmentNavigator);
+    ctor public ComposableFragmentNavigator(androidx.navigation.NavigatorProvider provider);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+  }
+
+  public class ComposableNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+    ctor public ComposableNavHostFragment();
+    method public static final androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId);
+    method public static final androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    field public static final androidx.navigation.fragment.compose.ComposableNavHostFragment.Companion Companion;
+  }
+
+  public static final class ComposableNavHostFragment.Companion {
+    method public androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+  }
+
+  public final class LocalFragmentKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.fragment.app.Fragment> getLocalFragment();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.fragment.app.Fragment> LocalFragment;
+  }
+
+}
+
diff --git a/constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt b/navigation/navigation-fragment-compose/api/res-2.8.0-beta06.txt
similarity index 100%
copy from constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt
copy to navigation/navigation-fragment-compose/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-fragment-compose/api/restricted_2.8.0-beta06.txt b/navigation/navigation-fragment-compose/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..d598ace
--- /dev/null
+++ b/navigation/navigation-fragment-compose/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,37 @@
+// Signature format: 4.0
+package androidx.navigation.fragment.compose {
+
+  public final class ComposableFragment extends androidx.fragment.app.Fragment {
+    method public static androidx.navigation.fragment.compose.ComposableFragment ComposableFragment(String fullyQualifiedName);
+    field public static final androidx.navigation.fragment.compose.ComposableFragment.Companion Companion;
+  }
+
+  public static final class ComposableFragment.Companion {
+    method public androidx.navigation.fragment.compose.ComposableFragment ComposableFragment(String fullyQualifiedName);
+  }
+
+  @androidx.navigation.Navigator.Name("composable") public final class ComposableFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor public ComposableFragmentNavigator(androidx.navigation.fragment.FragmentNavigator fragmentNavigator);
+    ctor public ComposableFragmentNavigator(androidx.navigation.NavigatorProvider provider);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+  }
+
+  public class ComposableNavHostFragment extends androidx.navigation.fragment.NavHostFragment {
+    ctor public ComposableNavHostFragment();
+    method public static final androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId);
+    method public static final androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    field public static final androidx.navigation.fragment.compose.ComposableNavHostFragment.Companion Companion;
+  }
+
+  public static final class ComposableNavHostFragment.Companion {
+    method public androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.fragment.compose.ComposableNavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+  }
+
+  public final class LocalFragmentKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.fragment.app.Fragment> getLocalFragment();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.fragment.app.Fragment> LocalFragment;
+  }
+
+}
+
diff --git a/navigation/navigation-fragment-compose/build.gradle b/navigation/navigation-fragment-compose/build.gradle
index f7956f5..9b1cc7b 100644
--- a/navigation/navigation-fragment-compose/build.gradle
+++ b/navigation/navigation-fragment-compose/build.gradle
@@ -46,6 +46,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.navigation.fragment.compose"
 }
 
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-fragment-ktx/api/2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-fragment-ktx/api/2.8.0-beta06.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-fragment-ktx/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-fragment-ktx/api/res-2.8.0-beta06.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-fragment-ktx/api/restricted_2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-fragment-ktx/api/restricted_2.8.0-beta06.txt
diff --git a/navigation/navigation-fragment-ktx/build.gradle b/navigation/navigation-fragment-ktx/build.gradle
index f38f638..b1199e7 100644
--- a/navigation/navigation-fragment-ktx/build.gradle
+++ b/navigation/navigation-fragment-ktx/build.gradle
@@ -41,7 +41,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Android Navigation-Fragment-Ktx"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/navigation/navigation-fragment/api/2.8.0-beta06.txt b/navigation/navigation-fragment/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..0db42b8
--- /dev/null
+++ b/navigation/navigation-fragment/api/2.8.0-beta06.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class NavGraphViewModelLazyKt {
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+}
+
+package androidx.navigation.fragment {
+
+  public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractListDetailFragment();
+    method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+    method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+    method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+    method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+    property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, kotlin.reflect.KClass<?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+  }
+
+  public final class DialogFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment, reified T> void dialog(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment, reified T> void dialog(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class FragmentKt {
+    method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+  }
+
+  public final class FragmentNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+  }
+
+  @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+    method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+    property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+  }
+
+  public static final class FragmentNavigator.Extras.Builder {
+    ctor public FragmentNavigator.Extras.Builder();
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, kotlin.reflect.KClass<?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+  }
+
+  public final class FragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class FragmentNavigatorExtrasKt {
+    method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+  }
+
+  public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+    ctor public NavHostFragment();
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+    method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+    method public final androidx.navigation.NavController getNavController();
+    method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+    method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+    property public final androidx.navigation.NavController navController;
+    field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+  }
+
+  public static final class NavHostFragment.Companion {
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-fragment/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-fragment/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-fragment/api/restricted_2.8.0-beta06.txt b/navigation/navigation-fragment/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..0db42b8
--- /dev/null
+++ b/navigation/navigation-fragment/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class NavGraphViewModelLazyKt {
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, @IdRes int navGraphId, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.viewmodel.CreationExtras>? extrasProducer, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+    method @Deprecated @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> navGraphViewModels(androidx.fragment.app.Fragment, String navGraphRoute, optional kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer);
+  }
+
+}
+
+package androidx.navigation.fragment {
+
+  public abstract class AbstractListDetailFragment extends androidx.fragment.app.Fragment {
+    ctor public AbstractListDetailFragment();
+    method public final androidx.navigation.fragment.NavHostFragment getDetailPaneNavHostFragment();
+    method public final androidx.slidingpanelayout.widget.SlidingPaneLayout getSlidingPaneLayout();
+    method public androidx.navigation.fragment.NavHostFragment onCreateDetailPaneNavHostFragment();
+    method public abstract android.view.View onCreateListPaneView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup? container, android.os.Bundle? savedInstanceState);
+    method public void onListPaneViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    method @CallSuper public final void onViewCreated(android.view.View view, android.os.Bundle? savedInstanceState);
+    property public final androidx.navigation.fragment.NavHostFragment detailPaneNavHostFragment;
+    property public final androidx.slidingpanelayout.widget.SlidingPaneLayout slidingPaneLayout;
+  }
+
+  @androidx.navigation.Navigator.Name("dialog") public final class DialogFragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor public DialogFragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination createDestination();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(DialogFragment::class) public static class DialogFragmentNavigator.Destination extends androidx.navigation.NavDestination implements androidx.navigation.FloatingWindow {
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.DialogFragmentNavigator.Destination> fragmentNavigator);
+    ctor public DialogFragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.DialogFragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class DialogFragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.DialogFragmentNavigator.Destination> {
+    ctor @Deprecated public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    ctor public DialogFragmentNavigatorDestinationBuilder(androidx.navigation.fragment.DialogFragmentNavigator navigator, kotlin.reflect.KClass<?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.reflect.KClass<? extends androidx.fragment.app.DialogFragment> fragmentClass);
+    method public androidx.navigation.fragment.DialogFragmentNavigator.Destination build();
+  }
+
+  public final class DialogFragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment> void dialog(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment, reified T> void dialog(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified F extends androidx.fragment.app.DialogFragment, reified T> void dialog(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.DialogFragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class FragmentKt {
+    method public static androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment);
+  }
+
+  public final class FragmentNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(androidx.fragment.app.Fragment);
+  }
+
+  @androidx.navigation.Navigator.Name("fragment") public class FragmentNavigator extends androidx.navigation.Navigator<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor public FragmentNavigator(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, int containerId);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination createDestination();
+    method @Deprecated public androidx.fragment.app.Fragment instantiateFragment(android.content.Context context, androidx.fragment.app.FragmentManager fragmentManager, String className, android.os.Bundle? args);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Fragment::class) public static class FragmentNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public FragmentNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> fragmentNavigator);
+    ctor public FragmentNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String getClassName();
+    method public final androidx.navigation.fragment.FragmentNavigator.Destination setClassName(String className);
+    property public final String className;
+  }
+
+  public static final class FragmentNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public java.util.Map<android.view.View,java.lang.String> getSharedElements();
+    property public final java.util.Map<android.view.View,java.lang.String> sharedElements;
+  }
+
+  public static final class FragmentNavigator.Extras.Builder {
+    ctor public FragmentNavigator.Extras.Builder();
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElement(android.view.View sharedElement, String name);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras.Builder addSharedElements(java.util.Map<android.view.View,java.lang.String> sharedElements);
+    method public androidx.navigation.fragment.FragmentNavigator.Extras build();
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class FragmentNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.fragment.FragmentNavigator.Destination> {
+    ctor @Deprecated public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, @IdRes int id, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, String route, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    ctor public FragmentNavigatorDestinationBuilder(androidx.navigation.fragment.FragmentNavigator navigator, kotlin.reflect.KClass<?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.reflect.KClass<? extends androidx.fragment.app.Fragment> fragmentClass);
+    method public androidx.navigation.fragment.FragmentNavigator.Destination build();
+  }
+
+  public final class FragmentNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id);
+    method @Deprecated public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route);
+    method public static inline <reified F extends androidx.fragment.app.Fragment> void fragment(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public static inline <reified F extends androidx.fragment.app.Fragment, reified T> void fragment(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.fragment.FragmentNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class FragmentNavigatorExtrasKt {
+    method public static androidx.navigation.fragment.FragmentNavigator.Extras FragmentNavigatorExtras(kotlin.Pair<? extends android.view.View,java.lang.String>... sharedElements);
+  }
+
+  public class NavHostFragment extends androidx.fragment.app.Fragment implements androidx.navigation.NavHost {
+    ctor public NavHostFragment();
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method public static final androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method @Deprecated protected androidx.navigation.Navigator<? extends androidx.navigation.fragment.FragmentNavigator.Destination> createFragmentNavigator();
+    method public static final androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+    method public final androidx.navigation.NavController getNavController();
+    method @Deprecated @CallSuper protected void onCreateNavController(androidx.navigation.NavController navController);
+    method @CallSuper protected void onCreateNavHostController(androidx.navigation.NavHostController navHostController);
+    property public final androidx.navigation.NavController navController;
+    field public static final androidx.navigation.fragment.NavHostFragment.Companion Companion;
+  }
+
+  public static final class NavHostFragment.Companion {
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId);
+    method public androidx.navigation.fragment.NavHostFragment create(@NavigationRes int graphResId, optional android.os.Bundle? startDestinationArgs);
+    method public androidx.navigation.NavController findNavController(androidx.fragment.app.Fragment fragment);
+  }
+
+}
+
diff --git a/navigation/navigation-fragment/build.gradle b/navigation/navigation-fragment/build.gradle
index 7bf3df6..b20d87a 100644
--- a/navigation/navigation-fragment/build.gradle
+++ b/navigation/navigation-fragment/build.gradle
@@ -56,7 +56,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Navigation-Fragment"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/navigation/navigation-lint-common/build.gradle b/navigation/navigation-lint-common/build.gradle
new file mode 100644
index 0000000..4ed49c5
--- /dev/null
+++ b/navigation/navigation-lint-common/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    compileOnly(libs.androidLintMinApi)
+    compileOnly(libs.kotlinStdlib)
+}
+
+androidx {
+    name = "Navigation Lint Utils"
+    type = LibraryType.LINT
+    inceptionYear = "2024"
+    description = "Lint utils for Navigation lint checks"
+}
diff --git a/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/BaseWrongStartDestinationTypeDetector.kt b/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/BaseWrongStartDestinationTypeDetector.kt
new file mode 100644
index 0000000..12fd2ba
--- /dev/null
+++ b/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/BaseWrongStartDestinationTypeDetector.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.navigation.lint.common
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.getParameterForArgument
+
+abstract class BaseWrongStartDestinationTypeDetector(
+    private val methodNames: List<String>,
+    private val parameterNames: List<String>
+) : Detector(), SourceCodeScanner {
+
+    final override fun getApplicableMethodNames(): List<String> = methodNames
+
+    final override fun visitMethodCall(
+        context: JavaContext,
+        node: UCallExpression,
+        method: PsiMethod
+    ) {
+        val startNode =
+            node.valueArguments.find {
+                parameterNames.contains(node.getParameterForArgument(it)?.name)
+            } ?: return
+
+        val (isClassType, name) = startNode.isClassReference()
+        if (isClassType) {
+            context.report(
+                getIssue(),
+                startNode,
+                context.getNameLocation(startNode as UElement),
+                """
+                        StartDestination should not be a simple class name reference.
+                        Did you mean to call its constructor $name(...)?
+                        If the class $name does not contain arguments,
+                        you can also pass in its KClass reference $name::class
+                    """
+                    .trimIndent()
+            )
+        }
+    }
+
+    abstract fun getIssue(): Issue
+}
+
+fun createWrongStartDestinationTypeIssue(
+    detectorClass: Class<out BaseWrongStartDestinationTypeDetector>
+) =
+    Issue.create(
+        id = "WrongStartDestinationType",
+        briefDescription =
+            "If the startDestination points to a Class with arguments, the " +
+                "startDestination must be an instance of that class. If it points to a " +
+                "Class without arguments, startDestination can be a KClass literal, such as" +
+                " StartClass::class.",
+        explanation =
+            "If the startDestination contains arguments, the arguments must be " +
+                "provided to navigation via a fully formed route (a class instance with arguments" +
+                "filled in), or else it will be treated as a case of missing arguments.",
+        category = Category.CORRECTNESS,
+        severity = Severity.ERROR,
+        implementation = Implementation(detectorClass, Scope.JAVA_FILE_SCOPE)
+    )
diff --git a/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/LintUtil.kt b/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/LintUtil.kt
new file mode 100644
index 0000000..611f180
--- /dev/null
+++ b/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/LintUtil.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.navigation.lint.common
+
+import org.jetbrains.kotlin.analysis.api.analyze
+import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
+import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
+import org.jetbrains.kotlin.idea.references.mainReference
+import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.uast.UExpression
+import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+
+/** Catches simple class/interface name reference */
+fun UExpression.isClassReference(): Pair<Boolean, String?> {
+    /**
+     * True if:
+     * 1. reference to object (i.e. val myStart = TestStart(), startDest = myStart)
+     * 2. object declaration (i.e. object MyStart, startDest = MyStart)
+     * 3. class reference (i.e. class MyStart, startDest = MyStart)
+     *
+     *    We only want to catch case 3., so we need more filters to eliminate case 1 & 2.
+     */
+    val isSimpleRefExpression = this is USimpleNameReferenceExpression
+
+    /** True if nested class i.e. OuterClass.InnerClass */
+    val isQualifiedRefExpression = this is UQualifiedReferenceExpression
+
+    if (!(isSimpleRefExpression || isQualifiedRefExpression)) return false to null
+
+    val sourcePsi = sourcePsi as? KtExpression ?: return false to null
+    return analyze(sourcePsi) {
+        val symbol =
+            when (sourcePsi) {
+                is KtDotQualifiedExpression -> {
+                    val lastChild = sourcePsi.lastChild
+                    if (lastChild is KtReferenceExpression) {
+                        lastChild.mainReference.resolveToSymbol()
+                    } else {
+                        null
+                    }
+                }
+                is KtReferenceExpression -> sourcePsi.mainReference.resolveToSymbol()
+                else -> null
+            }
+                as? KtClassOrObjectSymbol ?: return false to null
+
+        (symbol.classKind.isClass ||
+            symbol.classKind == KtClassKind.INTERFACE ||
+            symbol.classKind == KtClassKind.COMPANION_OBJECT) to symbol.name?.asString()
+    }
+}
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-runtime-ktx/api/2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-runtime-ktx/api/2.8.0-beta06.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-runtime-ktx/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-runtime-ktx/api/res-2.8.0-beta06.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-runtime-ktx/api/restricted_2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-runtime-ktx/api/restricted_2.8.0-beta06.txt
diff --git a/navigation/navigation-runtime-ktx/build.gradle b/navigation/navigation-runtime-ktx/build.gradle
index 36897df..1d67a1c6 100644
--- a/navigation/navigation-runtime-ktx/build.gradle
+++ b/navigation/navigation-runtime-ktx/build.gradle
@@ -41,7 +41,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Android Navigation-Runtime-Ktx"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/navigation/navigation-runtime-lint/build.gradle b/navigation/navigation-runtime-lint/build.gradle
index 5983aed0..91991d3 100644
--- a/navigation/navigation-runtime-lint/build.gradle
+++ b/navigation/navigation-runtime-lint/build.gradle
@@ -35,7 +35,7 @@
 dependencies {
     compileOnly(libs.androidLintMinApi)
     compileOnly(libs.kotlinStdlib)
-    bundleInside(projectOrArtifact(":navigation:navigation-common-lint"))
+    bundleInside(projectOrArtifact(":navigation:navigation-lint-common"))
 
     testImplementation(libs.kotlinStdlib)
     testImplementation(libs.androidLint)
diff --git a/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetector.kt b/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetector.kt
index ee38a50..8d4fdbc 100644
--- a/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetector.kt
+++ b/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetector.kt
@@ -16,7 +16,7 @@
 
 package androidx.navigation.runtime.lint
 
-import androidx.navigation.common.lint.isClassReference
+import androidx.navigation.lint.common.isClassReference
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Implementation
diff --git a/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetector.kt b/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetector.kt
index eae2ed1..8ac38db 100644
--- a/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetector.kt
+++ b/navigation/navigation-runtime-lint/src/main/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetector.kt
@@ -16,8 +16,8 @@
 
 package androidx.navigation.runtime.lint
 
-import androidx.navigation.common.lint.BaseWrongStartDestinationTypeDetector
-import androidx.navigation.common.lint.createWrongStartDestinationTypeIssue
+import androidx.navigation.lint.common.BaseWrongStartDestinationTypeDetector
+import androidx.navigation.lint.common.createWrongStartDestinationTypeIssue
 
 class WrongStartDestinationTypeDetector :
     BaseWrongStartDestinationTypeDetector(
diff --git a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/Stub.kt b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/Stub.kt
new file mode 100644
index 0000000..4b568a9
--- /dev/null
+++ b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/Stub.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.navigation.runtime.lint
+
+val TEST_CLASS =
+    """
+val classInstanceRef = TestClass()
+
+val classInstanceWithArgRef = TestClassWithArg(15)
+
+val innerClassInstanceRef = Outer.InnerClass(15)
+
+object TestGraph
+
+object TestObject
+
+class TestClass
+
+class TestClassWithArg(val arg: Int)
+
+object Outer {
+    data object InnerObject
+
+    data class InnerClass (
+        val innerArg: Int,
+    )
+}
+
+interface TestInterface
+class InterfaceChildClass(val arg: Boolean): TestInterface
+object InterfaceChildObject: TestInterface
+
+abstract class TestAbstract
+class AbstractChildClass(val arg: Boolean): TestAbstract()
+object AbstractChildObject: TestAbstract()
+
+// classes with companion object to simulate classes marked with @Serializable
+class TestClassComp { companion object }
+
+class TestClassWithArgComp(val arg: Int) { companion object }
+
+object OuterComp {
+    data object InnerObject
+
+    data class InnerClassComp (
+        val innerArg: Int,
+    ) { companion object }
+}
+
+class InterfaceChildClassComp(val arg: Boolean): TestInterface { companion object }
+
+abstract class TestAbstractComp { companion object }
+class AbstractChildClassComp(val arg: Boolean): TestAbstractComp() { companion object }
+object AbstractChildObjectComp: TestAbstractComp()
+"""
diff --git a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt
index 3931fb0..4af2ebc 100644
--- a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt
+++ b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt
@@ -17,18 +17,28 @@
 package androidx.navigation.runtime.lint
 
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.compiled
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
+import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.checks.infrastructure.TestMode
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 
-class WrongNavigateRouteDetectorTest : LintDetectorTest() {
+@RunWith(Parameterized::class)
+class WrongNavigateRouteDetectorTest(private val testFile: TestFile) : LintDetectorTest() {
 
     override fun getDetector(): Detector = WrongNavigateRouteDetector()
 
     override fun getIssues(): MutableList<Issue> =
         mutableListOf(WrongNavigateRouteDetector.WrongNavigateRouteType)
 
+    private companion object {
+        @JvmStatic @Parameterized.Parameters public fun data() = listOf(SOURCECODE, BYTECODE)
+    }
+
     @Test
     fun testEmptyConstructorNoError() {
         lint()
@@ -42,11 +52,12 @@
                 fun createGraph() {
                     val navController = NavController()
                     navController.navigate(route = TestClass())
+                    navController.navigate(route = TestClassComp())
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .skipTestModes(TestMode.FULLY_QUALIFIED)
             .run()
@@ -78,11 +89,16 @@
                     navController.navigate(route = InterfaceChildObject)
                     navController.navigate(route = AbstractChildClass(true))
                     navController.navigate(route = AbstractChildObject)
+                    //classes with companion object to simulate marked with @Serializable
+                    navController.navigate(route = TestClassWithArgComp(15))
+                    navController.navigate(route = OuterComp.InnerClassComp(15))
+                    navController.navigate(route = InterfaceChildClassComp(true))
+                    navController.navigate(route = AbstractChildClassComp(true))
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .skipTestModes(TestMode.FULLY_QUALIFIED)
             .run()
@@ -116,11 +132,25 @@
                     navController.navigate(route = InterfaceChildClass::class)
                     navController.navigate(route = Outer.InnerClass)
                     navController.navigate(route = Outer.InnerClass::class)
+
+                    //classes with companion object to simulate marked with @Serializable
+                    navController.navigate(route = TestClassComp)
+                    navController.navigate(route = TestClassComp::class)
+                    navController.navigate(route = TestClassWithArgComp)
+                    navController.navigate(route = TestClassWithArgComp::class)
+                    navController.navigate(route = OuterComp.InnerClassComp)
+                    navController.navigate(route = OuterComp.InnerClassComp::class)
+                    navController.navigate(route = InterfaceChildClassComp)
+                    navController.navigate(route = InterfaceChildClassComp::class)
+                    navController.navigate(route = AbstractChildClassComp)
+                    navController.navigate(route = AbstractChildClassComp::class)
+                    navController.navigate(route = TestAbstractComp)
+                    navController.navigate(route = TestAbstractComp::class)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .skipTestModes(TestMode.FULLY_QUALIFIED)
             .run()
@@ -171,13 +201,51 @@
 src/com/example/test.kt:21: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
     navController.navigate(route = Outer.InnerClass::class)
                                    ~~~~~~~~~~~~~~~~~~~~~~~
-15 errors, 0 warnings
+src/com/example/test.kt:24: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = TestClassComp)
+                                   ~~~~~~~~~~~~~
+src/com/example/test.kt:25: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = TestClassComp::class)
+                                   ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:26: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = TestClassWithArgComp)
+                                   ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:27: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = TestClassWithArgComp::class)
+                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:28: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = OuterComp.InnerClassComp)
+                                   ~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:29: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = OuterComp.InnerClassComp::class)
+                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:30: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = InterfaceChildClassComp)
+                                   ~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:31: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = InterfaceChildClassComp::class)
+                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:32: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = AbstractChildClassComp)
+                                   ~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:33: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = AbstractChildClassComp::class)
+                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:34: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = TestAbstractComp)
+                                   ~~~~~~~~~~~~~~~~
+src/com/example/test.kt:35: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+    navController.navigate(route = TestAbstractComp::class)
+                                   ~~~~~~~~~~~~~~~~~~~~~~~
+27 errors, 0 warnings
                 """
             )
     }
+}
 
-    private val sourceCode =
-        """
+private val SOURCECODE =
+    kotlin(
+            """
 
 package androidx.navigation
 
@@ -189,46 +257,23 @@
 
     public fun <T : Any> navigate(route: T) {}
 }
+""" +
+                TEST_CLASS
+        )
+        .indented()
 
-object TestObject
-
-class TestClass
-
-class TestClassWithArg(val arg: Int)
-
-val classInstanceRef = TestClass()
-
-val classInstanceWithArgRef = TestClassWithArg(15)
-
-object Outer {
-    data object InnerObject
-
-    data class InnerClass (
-        val innerArg: Int,
-    )
-}
-
-interface TestInterface
-class InterfaceChildClass(val arg: Boolean): TestInterface
-object InterfaceChildObject: TestInterface
-
-abstract class TestAbstract
-class AbstractChildClass(val arg: Boolean): TestAbstract()
-object AbstractChildObject: TestAbstract()
-"""
-
-    private val byteCode =
-        compiled(
-            "libs/StartDestinationLint.jar",
-            kotlin(sourceCode).indented(),
-            0xa3960069,
-            """
+private val BYTECODE =
+    compiled(
+        "libs/StartDestinationLint.jar",
+        SOURCECODE,
+        0xb1569275,
+        """
                 META-INF/main.kotlin_module:
                 H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgUuMSTsxLKcrPTKnQy0ssy0xPLMnM
                 zxPi90ssc87PKynKz8lJLfIu4RLl4k7Oz9VLrUjMLchJFWILSS0u8S5RYtBi
                 AADcysPxVwAAAA==
                 """,
-            """
+        """
                 androidx/navigation/AbstractChildClass.class:
                 H4sIAAAAAAAA/41QTW9SQRQ9M+8DeAV54Belamv9CGUhtHGnaaQkJiTYRW1Y
                 wGrgvdAJj/eSNwPpkt/i2o2JxsSFIS79UcY7j2pcsHAxZ+65c3LunfPz17fv
@@ -238,55 +283,136 @@
                 4JjhbL2qerzGs7Needzf8Xjeqq1XJ7zNzkpV1+d13rZ+fHC5b19U/rI8qet2
                 3vFd43TCcLh1/X8Doq1oCf9cLLtJrNMkisL0xUxTAN0kCBnKfRmH54v5OEwv
                 xTiiTrWfTEQ0EKk0/KbpvU8W6SR8Kw3ZvVjEWs7DgVSSXjtxnOhsssIxpWtn
-                /66asKniVDtwCQ+InRLndHvNryg0976g9CnTPCY0GuApDgnvbVS4hbKJkSrj
-                RqnDp7Pxapl06Xaan1H6uNWmuBHc2HA8yXCfXoE32ZIObo9g9XCnh7s9Gnuf
-                StR62EV9BKawhwcj5BTKCg8VPIVHCq6Cr1D5DV3dCrfVAgAA
+                /66asKniVDtwCQ+InRLndHvNryg0976g9CnTPCY0GqCBQ8J7GxVuoWxipMq4
+                Uerw6Wy8WiZdup3mZ5Q+brUpbgQ3NhxPMtzHU7rfZEs6uD2C1cOdHu72aOx9
+                KlHrYRf1EZjCHh6MkFMoKzxU8BQeKbgKvkLlN2AWm1jVAgAA
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51Sy27TQBQ9Y6dxYgKkLY+E9yNIbSXqpqrYBCGVIKRIaZEo
+                yqYLNLGHdhJ7Bo0nUZdZ8SH8QVdILFDUJR+FuOME2AKb+zr33Ds+199/fP0G
+                YA9PGPa4SoyWyVmk+FSecCu1ivaHuTU8tt1TmSbdlOd5V2cfW85wRQ0BGEN9
+                xKc8Srk6id4MRyK2AXyG8nOppH3B4G9sDmpYQTlECQFDyZ7KnOFZ/38Wdhja
+                G/2xtqlU0WiaRVJZYRRPo1fiA5+ktqsVTZjEVpsDbsbCdDYHITy3eL0V/wHf
+                ZwXKsP1v0xhWfxEOhOUJt5xqXjb1SUjmTNUZMLAx1c+ky3YoStoMrfksDL2G
+                F3p1iuazysUnvzGf7Xo77GVQ8S4+l72653p3mZuw9fcKBbjNUP0tEx3lkE/p
+                9dboNBVme2xJ+K5OBMPVvlTicJINhXnHhylV1vo65umAG+nyZbHWU0qYYoGg
+                c4VHemJi8Vo6rPl2oqzMxEDmkpr3ldK2eF2ONmldcgKQ99zV6TvuUxY5Rciv
+                bH1B5byAH5AtF8UOHpKtLRpQRQjUGUWXluSn5L0luXZeqOsINxbFBaGILuMK
+                YT4eURYWpDu4iyYeFwvvoVX87aQB9daP4few2sNaD+u4RiGu92jmzWOwHA00
+                Cc8R5riVo/wTjWwvPCoDAAA=
+                """,
+        """
+                androidx/navigation/AbstractChildClassComp.class:
+                H4sIAAAAAAAA/5VSS08TURT+7vQ1HYqUiljABwpiqcgUQlxYQoI1mialCyRN
+                hNVtey2XTu+YubcNS36LazdEDYkmhrj0RxnPTCskygIXc849Z77znefPX1+/
+                A9jAOkORq3bgy/axq/hAdriRvnK3m9oEvGUqh9JrVzyudcXvvU+BMSxehd8T
+                2lzERMgYQ3JTKmm2GOKF/eUGQ6yw3MgggZSDOGyyedBhYPsZOBhLw0KGoOZQ
+                aoaV2vWrKlOmjjDbIRml2GewN1veKPXG9XkWQ8EVAVK4ybBWqHV9Qzzu0aDn
+                SmVEoLjnvhTveN+jJhVx9FvGD3Z40BVBedjbLQdTmGZIX5AxPPuPZi6LKGeQ
+                x0w4llmGhZofdNwjYZoBl0q7XCnfRDzarfum3vc8GsPkn4p3hOFtbjj5rN4g
+                RqtmoUiHAjTyLvmPZWiV6NVeY3h9fpJzrLwVfecnjpUdcyw7nj8/mU+tWyX2
+                nKVejOeSWWvWKsV+fEha2fju5IVlU8hs3E5kkyEdHdXSlS3/fSVUHlWTrfMB
+                jdMEvueJYLVrGOZ2+8rInqiqgdSy6Ynty37pRip+WzBM1KQS9X6vKYI9ThiG
+                XM1vca/BAxnaI2emqpQIogELCnbe+P2gJV7J8N/MKE/jnyxYo8HHaUAWZsI9
+                UJ1PyEqSvkM6F54s6RjZici7QtYWoS3STvEM6eLcF4yfRgxPR5FAGaskp4co
+                3MBEuBB6hWy0P2TpG3K54Z5IJ4qfMf7xSprMEDCisamo1Cg4j2jTyHzD1Ft2
+                htufMHcaeWJEHCZkdKZW1Fgp4i5Sw0CF/HeJ8d4BYlXcr2K+igd4SE8sVLGI
+                RwdgGkt4fABbY0KjoOFoLGskNbIakxr53/A96KpcBAAA
+                """,
+        """
                 androidx/navigation/AbstractChildObject.class:
-                H4sIAAAAAAAA/41STW/TQBB9u0kcxw00lI8mFGhpQVAOuK24USGFCCRLwUg0
-                ioR6WturdhtnV7I3UY858UP4BxWHSiChCG78KMTYhA+JHrDlmX2zb99o3vrb
-                94+fATzBPYYHQieZUcmpr8VUHQmrjPa7UW4zEdvesUqT19GJjG0djGHjIvJA
-                5vbXgToqDM6+0so+Y6g83B42UYPjoYo6Q9Ueq5xhu/+fPZ8yuPtxWqp54IWE
-                G4QHg27Ye9HEJXgNKl5m2Oqb7Mg/kTbKhNK5L7Q2tlTN/dDYcJKmJHWlPzKW
-                xPxX0opEWEE1Pp5WyAlWhEYRwMBGVD9VBdqhVbJLDeYzz+NtXn7zmfv1HW/P
-                Z3t8hz2vu/zLe4e3eEHdY9i8cLi/PaK2rVBMe0bbzKSpzB6PLMPam4m2aiwD
-                PVW5ilLZ/TMEOdcziWRY7istw8k4ktlAEIdhpW9ikQ5Fpgq8KHoHZpLF8qUq
-                QGchPPxHFrtkX7WcuVO4SfkOIYdyizKnt1aidUJ+4Qzl2qNzuGfl9saCDNzH
-                XYrNnwQ0SApwsfT78Cqxi2fpE/jbczQ/YPmsLHBslvE2tsofkm6JBFYOUQlw
-                NcC1ANdxg5ZYDdBG5xAsx02s0X4OL8etHM4PGz/wlc0CAAA=
+                H4sIAAAAAAAA/41Sy27TQBQ9M3k5bqChPJpQHqVFgrDAbcWOCilEIFkyRqJR
+                JNTV2B610zgzkj2JusyKD+EPKhaVQEIR7PgoxLUJD4kusOV759w5c67uGX/7
+                /vEzgCe4z/BA6CQzKjn1tJipI2GV0V4/ym0mYjs4VmnyOjqRsW2AMWxeRB7K
+                3P460ECFob6vtLLPGCoPe6MWaqi7qKLBULXHKmfoBf/Z8ymDsx+npZoLXkg4
+                fngw7IeDFy1cgtuk4mWG7cBkR96JtFEmlM49obWxpWruhcaG0zQlqSvB2FgS
+                815JKxJhBdX4ZFYhJ1gRmkUAAxtT/VQVaIdWyS41WMxdl3d4+S3mztd3vLOY
+                7/Ed9rzh8C/v67zNC+oew9aFw/3tEbVth2I2MNpmJk1l9nhsGTbeTLVVE+nr
+                mcpVlMr+nyHIuYFJJMNqoLQMp5NIZkNBHIa1wMQiHYlMFXhZdA/MNIvlS1WA
+                7lJ49I8sdsm+ajlzt3CT8h1CdcptypzeWonuEvIKZyjXHp3DOSu3N5dkoId7
+                FFs/CWiSFOBg5ffhdWIXz8on8LfnaH3A6llZ4Ngq421slz8k3RIJrB2i4uOq
+                j2s+ruMGLbHuo4PuIViOm9ig/Rxujls56j8AJAtPiM0CAAA=
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildObjectComp.class:
+                H4sIAAAAAAAA/5VSW2sTQRT+ZnLbbKNN66WJVau2FC/otsU3gxCDwsK6gg0B
+                6dNsdmin2czI7iT0MU/+EP9B8aGgIEHf/FHi2TVU0L64y57bfOc7nG/2x8/P
+                XwE8xRbDI6Hj1Kj4xNNiqg6FVUZ73SizqRja3pFK4jfRsaTQjN/XwBi2Lmro
+                y8yeNxXIEkO1o7SyzxlK9x8MGqig6qKMGkPZHqmM4XHwH7OfMTidYVIwuuA5
+                jeOH+/1u2HvZwCW4dSpeZtgMTHroHUsbpULpzBNaG1swZ15obDhJEqJaCUbG
+                Epn3WloRCyuoxsfTEqnCclPPDRjYiOonKs92KIp3acB85rq8xYtvPnO+f+Ct
+                +WyP77AXNYd/+1jlTZ5D9xi2L1zwb61odDMU057RNjVJItMnI8uw/nairRpL
+                X09VpqJEdv8sQgr2TCwZlgOlZTgZRzLtC8IwrAZmKJKBSFWeL4ruvpmkQ/lK
+                5Ul7QTz4hxa7JGG52LudK0r+NmVV8k3ynN5KkW1Q5uXqkK88PINzWhzfWYCB
+                Du6SbfwGoE5UgIOl8+Y1QufP0hfwd2dofMLyaVHguFfYW9gsflC6KSJYPUDJ
+                xxUfV31cw3UKseajhfYBWIYbWKfzDG6GmxmqvwBXhfge3QIAAA==
+                """,
+        """
                 androidx/navigation/InterfaceChildClass.class:
-                H4sIAAAAAAAA/41Qz28SQRT+ZhZY2FJZqFZK/dFatYWD0MabprElMSHBmtSG
-                A5wGdqVTltlkZyA98rd49mKiMfFgiEf/KOMbSpqYcPAw771v3jffe/P9/vPj
-                J4CX2GfYFypIYhlc15WYyqEwMlb1ljJh8lEMwualjIJmJLR2wRj8KzEV9Uio
-                Yf19/yocGBcOw+4qiYtQm1sZF2mGzGuppDlmSB10qx0G56DaycNFzkMKHmGR
-                DBlYN4881nPguENUcyk1Q7X9n1u+ojHD0JxYJdLvMhTbo9hEUtXfhUYEwgii
-                8PHUof8zG3I2gOaO6P5aWtSgKjhkOJ3PSh4v88WZzzzur3k865TnsyPeYKfr
-                pYzPK7zh/PqU4X7qvHiLssSupLJpP2OVjhj2Vu7/j0W0Fm3hn4lpM1YmiaMo
-                TF6MDFnQjIOQodCWKjybjPthciH6Ed2U2vFARB2RSIuXl96HeJIMwrfSgq3z
-                iTJyHHakltQ9USo2i9Eah+RvigZm6JSs4fRvTrWLLMUnhI4Jc8pe7TvWatvf
-                UPiy4OxRtK+AHTyluHnDgo+idZIqq0bGk+7GUqtuDaacrn1F4fNKmfwNYSnD
-                8WwRd/Gc8hvq3aXevR6cFjZbuN9CGVtUotLCNh70wDQe4lEPrkZR47FGXmNH
-                I6tR0tj4C6G2ycbxAgAA
+                H4sIAAAAAAAA/41Qz2sTQRT+ZvJjk21qNqnWNPVXrdomBzct3pRiGxACsUJb
+                ckhOk+yaTrOZhZ1J6DF/i2cvgiJ4kODRP0p8k4aCkIMs89775n37vTff7z8/
+                fgJ4hT2GPaGCJJbBta/EVA6FkbHyW8qEyUcxCJuXMgqakdDaAWPwrsRU+JFQ
+                Q/9D/yocGAcphp1VEhehNrcyDjIM2TdSSXPEkN7v1joMqf1apwAHeRdpuIRF
+                MmRg3QIKWM+D4w5RzaXUDLX2f275msYMQ3NslUi/y1Bqj2ITSeW/D40IhBFE
+                4eNpit7PbMjbAJo7ovtraVGDquCA4WQ+K7u8whdnPnO5t+byXKoynx3yBjtZ
+                L2c9XuWN1K9PWe6lz0q3KEfsajqX8bJW6ZBhd+X+/1hEa9EW3qmYNmNlkjiK
+                wuTlyJAFzTgIGYptqcLTybgfJheiH9FNuR0PRNQRibR4eemex5NkEL6TFmyd
+                TZSR47AjtaTusVKxWYzWOCB/0zQwS6dsDad3c6od5Cg+JXREmFN269+xVt/+
+                huKXBWeXov0LeEYfsHnDgoeSdZIqq0bGk+7GUsu3BlPO1L+i+HmlTOGGsJTh
+                eL6IO3hB+S317lLvXg+pFjZbuN9CBVtUotrCNh70wDQe4lEPjkZJ47FGQeOJ
+                Rk6jrLHxF0rwRpzxAgAA
                 """,
-            """
+        """
+                androidx/navigation/InterfaceChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51Sy27TQBQ9Y6d5mABpyyPhXQhSC6JuKhBIRUgQhGQpLRJU
+                2XSBJva0ncSeQeNJ1GVWfAh/0BUSCxR1yUch7jgB1mVzX+eee8fn+uev7z8A
+                PMVDhmdcJUbL5CRUfCKPuJVahZGywhzyWHSPZZp0U57nXZ19bjvDFXVUwBga
+                Qz7hYcrVUfh+MBSxrcBnKL+UStpXDP76Rr+OJZQDlFBhKNljmTM87/3Xxh2G
+                znpvpG0qVTicZKF0DMXT8K045OPUdrXKrRnHVptdbkbC7Gz0A3hu82o7/gd+
+                ygqUYfN80xiW/xB2heUJt5xqXjbxSUrmTM0ZMLAR1U+ky7YoSjoM7dk0CLym
+                F3gNimbT6tkXvzmbbntb7E2l6p19LXsNz/VuMzfh8TkkquAmQ+2vTnSWPT6h
+                51uj01SYzZEl6bs6EQyXe1KJvXE2EGafD1KqrPR0zNM+N9Lli2I9UkqYYoGg
+                gwUf9djE4p10WOvDWFmZib7MJTW/Vkrb4nk5OiR2ySlA3nN3pw+5S1noJCG/
+                9OgbqqcFfI9suSi+wBrZ+rwBNQRAg1F0YUF+Qt5bkOunhbyOcG1enBOK6CIu
+                EebjPmVBQbqF22jhQbHwDtrFD08aUG/jAH6E5QgrEVZxhUJcjWjm9QOwHE20
+                CM8R5LiRo/wbyZhkAy0DAAA=
+                """,
+        """
+                androidx/navigation/InterfaceChildClassComp.class:
+                H4sIAAAAAAAA/5VSXU8TQRQ9sy3dtizSFsVS/ABBLUXYghiNEBKs0TQpNUHS
+                RHiatkPZdjtrdqYNj/wWn30hakg0McRHf5TxTqnwoInhYe+dc/fecz9//vr6
+                HcAa1hgWuWyGgdc8ciXvey2uvUC6ZalFeMAbonTo+c2Sz5UqBd33NhhDqs37
+                3PW5bLlv6m3R0DYiDLP/otkVSl9Q2RhhiG140tObDNH83kKNIZJfqDmwkUgi
+                iiRhHrYY2J4DB2MJWLhGrvrQUwxLlStUuk6pWkJvGTbKsccQ32j4w9xPrkA0
+                bwSX5GHjBsNKvtIJNBG57X7X9UyM5L77Uhzwnq9LgVQ67DV0EG7zsCPC9fPu
+                biYxiSxD4oKM4elV2rmsYt1BDtNmMrcY5ipB2HLbQtdD7knlcikDPSBSbjXQ
+                1Z7v0yDSf0reFpo3ueZks7r9CF0AMyJhBGjqHbIfeQYV6dVcYXh9dpxJWllr
+                8J0dJ63UaNKKR7NnxzP2qlVkz5n9YiwTS1k5qxj58SFmpaI76QsUp5BcND6S
+                ihm6VVPvf6+EaqNSUlXep2HqMPB9ES53NMP0Tk9qryvKsu8pr+6Lrctm6UZK
+                QVMwjFc8Kaq9bl2Eu5x8GDKVoMH9Gg89g4dGpyylCAfTFRScfBv0woZ45Zl/
+                U8M8tb+yYIWmHqXqYqSnzBrovUTTipG+QzpjjpZ0hLCNOMllQpvkbZFOFk4x
+                Wpj+gvETQhbcYSTwDEWSk+deSCFt9kEvw0brI96JIZdr1kR6pPAZ4x//SeOc
+                Owxp4riOxDA4i8Gi4XzD5Dt2iqlPuH0ysESoNZOQDYrIUXOrA+5HeEy6RPa7
+                xDizj0gZs2XcK2MO8/TE/TIe4OE+mEIeC/uIK6QVCgqOwqIyMKMwoZD7DYsK
+                p/5yBAAA
+                """,
+        """
                 androidx/navigation/InterfaceChildObject.class:
-                H4sIAAAAAAAA/41SXWsTQRQ9M0mTzTbatH4l1q9aldoHty2+WYQaFBbiCjYE
-                pE+T3TGdZDMDu5PQxzz5Q/wHxYeCggR980eJd9ZYHxR0l7kfZ849M/fufvv+
-                8TOAx3jAsCV0khmVnARaTNVAWGV0EGors7cilu1jlSav+kMZ2yoYQ2MopiJI
-                hR4Ev9ASw8bfNLoyt+c6VSwxVPaVVvYpQ2nrYa+OKjwfZdQYyvZY5Qzbnf+9
-                yxMGbz9OCzkf3Gl4YXTYPYjaz+tYQb1GYINhs2OyQTCUtp8JpfNAaG1sIZsH
-                kbHRJE1JarUzMpbEgpfSikRYQRgfT0s0IuZMzRkwsBHhJ8plOxQlu3TAfOb7
-                vMmLNZ95X9/x5ny2x3fYs6rHv7yv8AZ31D13l39Oic5tRGLaNtpmJk1l9mhk
-                GdZfT7RVYxnqqcpVP5UHv7ug2bVNIhlWOkrLaDLuy6wriMOw1jGxSHsiUy5f
-                gP6hmWSxfKFc0loI9/6QxS7Nr0wtV2i13EDJ36G+Xb5GntNL34+yDcoCNxzy
-                S9tn8E+L7bsLMoiwSbb+k4BlikCFF86LrxHbPcufwN+c4eIHrJ4WAMe9wt7G
-                /eJnZbhEApePUApxJcTVkEqbFKIV4jrWj8By3MBN2s9Rz3Erh/cDKimwMukC
-                AAA=
+                H4sIAAAAAAAA/41STW/TQBB9u0kTxw00LV8J5asUUOkBtxU3KqQSgWQpGIlG
+                kVBPG3tJN3F2JXsT9ZgTP4R/UHGoBBKK4MaPQsyaUA4gga2dmfd25u3O2N++
+                f/wM4DEeMGwJnWRGJSeBFlM1EFYZHYTayuytiGX7WKXJq/5QxrYKxtAYiqkI
+                UqEHwS+2xLDxN42uzO25ThVLDJV9pZV9ylDaetirowrPRxk1hrI9VjnDdud/
+                7/KEwduP00LOB3caXhgddg+i9vM6VlCvEdlg2OyYbBAMpe1nQuk8EFobW8jm
+                QWRsNElTklrtjIwlseCltCIRVhDHx9MSjYg5U3MGDGxE/IlyaIeiZJcOmM98
+                nzd5seYz7+s73pzP9vgOe1b1+Jf3Fd7gLnXP3eWfU6JzG5GYto22mUlTmT0a
+                WYb11xNt1ViGeqpy1U/lwe8uaHZtk0iGlY7SMpqM+zLrCsphWOuYWKQ9kSmH
+                F6R/aCZZLF8oB1oL4d4fstil+ZWp5Qqtlhso+TvUt8Nr5Dm99P0IbRAK3HDI
+                L22fwT8ttu8ukoH72CRb/5mAZYpAhRfOi69RtnuWP4G/OcPFD1g9LQiOe4W9
+                TRLuZ2W4RAKXj1AKcSXE1ZBKmxSiFeI61o/ActzATdrPUc9xK4f3Az/F2jnp
+                AgAA
                 """,
-            """
+        """
                 androidx/navigation/NavController.class:
                 H4sIAAAAAAAA/41SW08TQRT+Zttut8utrYJQQeWiFFC2EH2xhERJjEtqNdL0
                 hadpuynTbmeT3WnDY3+L/8AnjQ+G+OiPMp7ZNlBAo5vsuX/fnDNzfv769h3A
@@ -305,125 +431,318 @@
                 XcmDUyRcPHTxyKUbXiMT6y428PgULMITbJ5iKoIdoRjBjLRNxlaEXIRChJnY
                 Lf4G70EG2roEAAA=
                 """,
-            """
+        """
                 androidx/navigation/NavControllerKt.class:
-                H4sIAAAAAAAA/41SXU8TQRQ9s1va7VLoFkRoAZEPsfWDBfVNYkKamGysNUGC
-                ITxN27EuLLPJzrThkd/iL1B5IJHEEB/9UcY7ayMBVNhs5s69c865987cHz+/
-                fgPwDKsMi1x2kjjsHPqS98Mu12Es/Sbv12OpkziKRPJK58AYvD3e537EZdd/
-                09oTbYraDONdoesRVyqQSnPZFpviPcN8tdb4m+6WUL/RzylxI066/p7QrYSH
-                UvlcylinMOU3Y93sRRGhvPYV8blrpAtwkM/DgstQuVzeu1B/2Ei6qVD1uioH
-                YCpjsv0vkeWbSRQwiqIpymNw1ttRKEP9gsGu1rYZZv8rkcMthux6yijgNsZd
-                TGCSYekmiXMoM2SqQW3bUKddVDDDUGrsx5pq8F8LzTtcc2rROujbNBTMLHmz
-                gIHtm41Fh4eh2dG8WJ01ho2zozH37Mi1pizXcuyBtRZK3tlRxVpl3z9mHTqv
-                ZBzLsymaoeDQeTDr5YzQE3reC5O2sq8Zpjd7UocHIpD9UIWtSGycDwa1Uo87
-                gqHYCKVo9g5aItnihGFw38a9pC1ehsYpDzS2ryhgjd4gY5qim6A5oSYfkpcl
-                myNbMc90KWajjCHyLDwib4ai5st8wfCn9JIeD7DA+AVeGQWMXGWVLrMmLrAc
-                jJEOS1lPkb4DJk8xsXOCqWMMn6Ky4xVPMHuM0uc/Qi6lMenNZFhYSdt7AJ9s
-                nRB3qPy5XdgB7gaYD7CAxQBLuBdgGfd3wRSqqO3CUebPKwwpZBVGFYoKBYWR
-                X85LYc0vBAAA
+                H4sIAAAAAAAA/41UW08TQRg9s1va7VJoy70F5Y6tFxbwLmhCmphsrCVBgiEk
+                Jtt2rAvLbLIzbXjkt/gLVB5IJDHER3+U8duV2NCC0IedmTPnnD3z9Zv99fv7
+                DwCP8JJh1hH1wHfrh5ZwWm7DUa4vrIrTKvlCBb7n8eCNSoAxZPaclmN5jmhY
+                G9U9XiNUZxhscFXyHCltIZUjanyTf2SYLhTLl/lucfmXvUovLvtBw9rjqho4
+                rpCWI4SvIpq0Kr6qND2PWJlal/nkNdYpGEgmocFkyHfGe++qT+tBIzIqXJfy
+                nEwxRmtXmSzczCKFfqTDUBmGMQplC8GD7sJdFWmjqXgw1xZRpGH3covLA3UZ
+                pDCE4TDQCIOxVvNc4apXDHqhuM1w679nSiDPEF+LFClMIGdiHLcY5m5SiQQm
+                GWIFu7gdSqdNTGHmCmln5gTmTMyH9Gx531cU2XrLlVN3lEP10A5aOjU1Cx/J
+                8AEGth9ONNo8dMPZEs3qywwfzo7y5tmRqY1ppmboHaM2k80QQVtiPz/HDeLl
+                Y4aW0QmNEdjTBuOZBIEGgck2aGZ6w7esMOSuPFQCDxnM9smozy9cucV9xTC+
+                2RTKPeC2aLnSrXp8vX1DqIQlv84Z0mVX8ErzoMqDLYc4DKm2LSee+c5vBjX+
+                2g33cueW212GWKZWiFGJdOTD+0PFe0qrOI0JGvNh+3Zh1EEdWAw59NBKwzNa
+                TRAa/mLf0Psl+kOen3OBvgu6HFKEdKmynap0h2oAg92q0U5V9oLKwBgpWaQq
+                IeoUzJ5ifOcEt4/Re4qpnUz6BLPHyJ5iPpovHGP06z/T/kjUB5PijJC5jhe0
+                Nml3nr6pj8l8New6PMEajRuE36GiFHah2yjauGvjHu7beIBFGxaWdsHC8q/s
+                IiVhSCQleiTiEv0SaRmCfRJDEsMSAxKDfwAPYTX9vQUAAA==
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerClass.class:
-                H4sIAAAAAAAA/41U31MbVRT+7m5+bJYAG6AtkNiqRExC2wVstRZaBRRZDKGC
-                w1jx5ZKsYWHZxd0NU18cnvondEZfnHEcn3ioMxocO+Ng++bf5Diem90mNVSG
+                H4sIAAAAAAAA/41U31MbVRT+7m5+bJYENkBbfkRQiZiEtgvYai20CiiyGEIF
+                h7HiyyVZw8Kyi7sbpr44PPVP6Iy+OOM4PvFQZxQcO+Ng++bf5Diem90mNVSG
                 meSec8+e853vnnPu/euf3/8AcAPrDHnu1DzXqj3QHX5g1XlguY6+2ghML284
                 jukt2Nz3k2AM2g4/4LrNnbq+urVjVoMkZIbErOVYwV2GWMEobjDIheJGGnEk
-                VcSgMCiWQJnz6gzMSENFTwoS0uQfbFs+w3j5PARmGHrqZmC0sSiNwaBW3b19
-                1zGdYIoAq+7+1wxF4nFezLGy69X1HTPY8rjl+Dp3HDdoeft6xQ0qDdueEYdJ
-                qMT5IkNapMjXzC95ww4YtgrnS2QY5e7azZyTYxqDGBLZR6mUgbseeJZDxx8q
-                FF+ADK10nkvdtvmGZddML4nLKq6Idgx1sAvPO3NHwWvUSL6/bzo1hmuF09Cn
-                s0XIRHAMeQH+BkNOlP4sxzeFY0E4LpztWBKOE2nk8IrQrtHht7m/veDWTIZM
-                J9JwArMuzjcZDiBNmI5pFVN4i05kftXgNs3YhcJL6v85zf5Z7afe8y3bpKrG
-                3WDb9BgGTqMQmfKuG9iWo6+YAa/xgJNN2juQ6X4xsaTEAhr+XbI/sMSOuEo1
-                GtgfTw4vq9KwpErayaFKP0lTVElJkOwhKZPsU54+VIZPDqelSTbfO5DQpFFp
-                Un76Q0LSYsspLSl2S88eysuDmkI6OSqKFDqRmZE5Rbo6rWg9o7FhNsmWnj2S
-                KTAdejxipPeS3if0tUwbXiE6ozElriUE12kmTjDyvwObxCLdxc5k0VtR4QcL
-                rhN4rm2b3vVduiyxsHn9ZcsxK429LdP7VNRXlNWtcnuDe5bYR8bsWsMJrD3T
-                cA4s3yLTXKc3DL3rAa/urvD9yDvf7X2Pe3zPJGr/CUt3KJq0Vdfdhlc1Fy0B
-                MRJBbJxKR8Mk0VsmSjAg3i/SFNLpVaB1mXaL9F0iqZaOkSplf0Xvz7ST8DGt
-                fRAdH6X4LFIky7S7GHrTt34xG6QJVBolaPQPMXUxMiTjpV/Qe9SGS7SM2RZM
-                OnSIYDJE7nnwWHcwe2kAvSwEKwKmiKXglHoC6X72GJcet4NCsqk22VREdiVi
-                cwHQUhjGSJR7PCpWJhf75lsogsFsKdtENoSs0CqDCQS621H62yQFtdwTXLl/
-                jFcHXm9iXEQ2UdSKTVxt4vrjrmPkIkYv8KBVb9dgPKpBi8FvuNFdBiWKZ7iJ
-                tyMeX5AU7cqXJn5CPHY08Sek7xCXjyZOIK0IoKv0/15YYmFPKq32yUnlb2SS
-                tO9ULN+uWB638C7lWSU9KUi906rBvVYoXS98hCUq3yctQANrJD8j+23q1Mwm
-                ZAOzBu4YuIv3SMX7BuYwvwnmYwEfbKLfF78PfaitNeFD85HxMeBj0MfNlvGW
-                D91HjvR/AXBODlL6BwAA
+                VcSgMCiWQJnz6gzMSENFVwoS0uQfbFs+w3j5IgRmGLrqZmC0sCiNwaBW3b19
+                1zGdYIoAq+7+1wxF4nFRzLGy69X1HTPY8rjl+Dp3HDdoevt6xQ0qDdueEYdJ
+                qMT5MkNapMjXzC95ww4YtgoXS2QY5c7azVyQYxp96BfZh6iUgbseeJZDx+8v
+                FF+ADK10niudtvmGZddML4kRFaOiHf1t7MLzztxR8Bo1ku/vm06N4VrhLPTZ
+                bBEyERxDXoC/wZATpT/P8U3hWBCOC+c7loTjRBo5vCK0a3T4be5vL7g1kyHb
+                jjScwKyL802GA0gTpmNaxRTeohOZXzW4TTN2qfCS+n9Os39e+6n3fMs2qapx
+                N9g2PYbesyhEprzrBrbl6CtmwGs84GST9g5kul9MLCmxgIZ/l+wPLLEjrlKN
+                BvbH08MRVRqQVEk7PVTpJ2mKKikJkl0kZZLdytOHysDp4bQ0yeYzvQlNGpIm
+                5ac/JCQttpzSkmK39OyhvNynKaSTo6JIoROZGZlTpKvTitY1FBtgk2zp2SOZ
+                AtOhxyNGeob0bqGvZVvwCtEZiilxLSG4TjNxgsH/HdgkFukutieL3ooKP1hw
+                ncBzbdv0ru/SZYmFzespW45Zaextmd6nor6irG6V2xvcs8Q+Mg6vNZzA2jMN
+                58DyLTLNtXvDkFkPeHV3he9H3vlO73vc43smUftPWLpN0aStuu42vKq5aAmI
+                wQhi40w6GiaJ3jJRgl7xfpGmkE6vAq3LtFuk7xJJtXSCVGn4V2R+pp2Ej2nt
+                huj4CMWPIkWyTLvLoTd96xGzQZpApVGCRv8QUxcjQzJe+gWZoxZcomkcbcKk
+                Q4cIJkvkngePdQazlwbQy0KwImCKWApOqSeQ7g+f4MrjVlBINtUim4rIrkRs
+                LgFaCgMYjHKPR8XK5mLffAtFMJgtDR9jOISs0CqDCQS621H62yQFtdwTjN4/
+                wau9rx9jXEQeo6gVj3H1GNcfdxwjFzF6gQeteqsG41ENmgx+w43OMihRPMNN
+                vB3x+IKkaFe+NPET4rGjiT8hfYe4fDRxCmlFAF2l//fCEgt7Umm2T04qfyOb
+                pH27YvlWxfK4hXcpzyrpSUHqnWYN7jVD6XrhIyxR+T5pAhpYI/kZ2W9Tp2Y2
+                IRuYNXDHwF28RyreNzCH+U0wHwv4YBM9vvh96ENtrgkfmo+sj14ffT5uNo23
+                fOg+cqT/CwA97z/6BwAA
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerObject.class:
-                H4sIAAAAAAAA/41US08UQRD+umcfs7M8loc8FR8s8lJmQTxBTJBoHLIsxiUY
-                5dTsjjAwzOhM74YjJ29ePXj04ImDxAOJJgYlXvxRxOrZISAEY7Lb/VV1VX3V
-                X/Xu7+Ov3wFM4z7DkPCqge9Ud0xP1J11IR3fM5dq0g7ylufZwdLapl2RaTCG
-                3KaoC9MV3rp54tUYUrOO58gHDNrI6EoTkkgZSCDNkJAbTsgwXPwvhhkGXfpl
-                GTjeOkPnyGjxlK3hpYjBoh+sm5u2XAuE44Wm8DxfRgVDs+TLUs11KSp7pqyO
-                Fiq8IcKNeb9qR01a2q/p43fUuP2mJlzq8MpI8fzNZkZfMuT/xUZUYs21iS7p
-                yw07YGi/WIWoZytupI8BrkTRrVJ5ea40/6gJfTAy5OxnaCtu+ZLCzEVbiqqQ
-                ghL5dl2jGTG1ZNQCBrZF/h1HWQVC1UmGV4e7Awbv4QbPHe4aXFcgG++6oVy5
-                Fv3ordFzuDvFC+xhWuc/P6Z4ji905LQ+XkhM6blkX6KHFdiTo/faQiaXIm+a
-                MCOsE84orNimmOqh99JppjFKb6Qk6vO+JwPfde1gYksy9D+redLZti2v7oQO
-                aTZ3qiO9ksZcWouOZ5dq22t2sKx0VXL6FeGuiMBRduxsLktR2VoUr2M7f772
-                UxGIbZu6+YukKXoR864IQ5tMo+zXgor92FEleuMSKxeawySNJxEp36umRfsd
-                slK0N9OepNNkZN0ly1TzUd6xA+j7BDgm4mCgh46BpkYAMlRKFc2Sh0fJN+Nk
-                rb31c3R0Gq7F4WeZSWa0xbynqe17l6QydKAzZrJo57R3j41/QjKxN/4D/AOS
-                2t74IfjzxF7UeIHWBHhaj4p1NRLiYgp10ZeROlAvmn4/BHS644kU3VECkP0G
-                /uIAvV9wdT9yaJiiVenIMYYWUvVexDdOf0WqNYZrJM/AKjQL1y3csOh2twhi
-                0EIeQ6tgIW5jeBVGqD4jIVIhOiLQFSIXgSytfwCP7WED4AQAAA==
+                H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFvnxR5KVMQVxATJBqHlGKEYJTV
+                bTvCwDCjM7cNS1bu3Lpw6cIVC4kLEk0MStz4o4jnTkdBCMakvec7Z84535nv
+                3Pbn8eevAKZxl2FIeNXAd6o7pifqzrqQju+ZSzVpB3nL8+xgqbxpV2QajCG3
+                KerCdIW3bv6OagypWcdz5D0GbWR0tQlJpAwkkGZIyA0nZBgu/hfDDIMu/WUZ
+                ON46Q+fIaPGErRGljMGiH6ybm7YsB8LxQlN4ni+jhqFZ8mWp5rqUlT3VVkcL
+                Nd4Q4ca8X7WjIS3tx/TxGxrcflUTLk14aaR49s1mRp8z5P/FRlSi7NpEl/Tl
+                hh0wtJ/vQtSzFTfSxwBXouhWaXllrjT/oAl9MDIU7GdoK275ktLMRVuKqpCC
+                Cvl2XaMdMXVk1AEGtkXxHUd5BULVSYYXh7tXDN7DDZ473DW4rkA2trqhQrkW
+                /ei10XO4O8UL7H5a59/fp3iOL3TktD5eSEzpuWRfoocV2KOjt9pCJpeiaJow
+                I6wTziis2KaYmqH3wm2mMUp3pCTq874nA9917WBiSzL0P6l50tm2La/uhA5p
+                NneiI92Sxl5ai45nl2rbZTtYUboqOf2KcFdF4Cg/DjYvS1HZWhQvYz9/tvdj
+                EYhtm6b5i6QpuhHzrghDm1xj2a8FFfuho1r0xi1Wzw2HSVpPIlK+V22L7C3y
+                UmSbySbpaTLybpNnqv2o6NgB9H0CHBNxMjBAj4GmRgIy1Eo1zVKER8XX42Kt
+                vfVj9OgkXYvTTzOTzGiLeU9K2/cuKGXoQGfMZJHlZLvHxj8gmdgb/wb+Dklt
+                b/wQ/GliLxq8QGcCPK1HzboaBXEzhbroy0gdqBtNvx8COnr+SNEdFQDZL+DP
+                DtD7CQP7UUDDFJ1KR44xtJCqdyK+cforUqMxXCZ5rqxBs3DVwjWL3u4GQQxa
+                yGNoDSzETQyvwQjVZyREKkRHBLpC5CKQpfMXu5HXfeAEAAA=
                 """,
-            """
+        """
                 androidx/navigation/Outer.class:
-                H4sIAAAAAAAA/4VRW2sTQRg9M5vLZhNtGi9JrK2XptpUcNviUy1CDQoLMYW2
+                H4sIAAAAAAAA/4VRW2sTQRg9M5vLZhNtGi9NjK2XptpUcNviUy1CDQoLMYW2
                 BCRPk2SIk2xmYXcS+pgnf4j/oPhQUJCgb/4o8dttNA9S3GG/M9/tfDNnfv76
-                8g3ACzxjqArdDwPVP3e1mKqBMCrQ7vHEyDALxlAciqlwfaEH7nF3KHsmC4sh
-                c6i0Mq8YrO16u4A0Mg5SyDKkzAcVMaw1r2V9yWAf9vyk3wGPm2yvdXp21Gq8
-                KeAGnBwFbzJsNoNw4A6l6YZC6cgVWgcm4YncVmBaE98nqtXmKDBE5r6TRvSF
-                ERTj46lFt2OxycUGDGxE8XMVe7u06+8x1OezgsMr3OHF+czhtmX/+Mgr89k+
-                32UH3Eq9ztr8+6cML/K4YZ/FNI6ntQwbvojokvnEuVKFoXbtjWvLpiweMGz9
-                p/KPzo9I/ZaYNgJtwsD3Zfh8RHPWTibaqLH09FRFquvLo6UwpH8j6EuGlabS
-                sjUZd2V4JqiGodQMesJvi1DF/iJYWJ5MUrNzGkzCnnyr4lx1Maf9zxTs0Qul
-                Elmr8YMR1sjLEBYJOa104m2R58biE6Z3LmFfJOkni2KgjKdkC1cFyBEVYCP/
-                t7lM1fGX/wr+/hKFz1i5SAIWtsmWKP2Q/nU6x2PCDcJ6MmITO4QHRLNKxKUO
-                LA+3PNz2cAd3aYuyhwqqHbAI97DWQTqCE+F+hEyE9QgbvwFBj+0jIgMAAA==
+                8g3ACzxjqAjdDwPVP3e1mKqBMCrQ7vHEyDALxlAciqlwfaEH7nF3KHsmC4sh
+                c6i0Mq8YrO16u4A0Mg5SyDKkzAcVMVSb17K+ZLAPe37S74DHTbbXOj07ajXe
+                FHADTo6CNxk2m0E4cIfSdEOhdOQKrQOT8ERuKzCtie8T1WpzFBgic99JI/rC
+                CIrx8dSi27HY5GIDBjai+LmKvV3a9fcY6vNZweFl7vDifOZw27J/fOTl+Wyf
+                77IDbqVeZ23+/VOGF3ncsM9iGsfTWoYNX0R0yXziXKnCULv2xrVlUxYPGLb+
+                U/lH50ekfktMG4E2YeD7Mnw+ojnVk4k2aiw9PVWR6vryaCkM6d8I+pJhpam0
+                bE3GXRmeCaphKDWDnvDbIlSxvwgWlieT1OycBpOwJ9+qOFdZzGn/MwV79EKp
+                RNZK/GCENfIyhEVCTiudeFvkubH4hOmdS9gXSfrJohio4inZwlUBckQF2Mj/
+                bV6j6vjLfwV/f4nCZ6xcJAEL22RLlH5I/zqd4zHhBmE9GbGJHcIDolkl4lIH
+                lodbHm57uIO7tMWahzIqHbAI91DtIB3BiXA/QibCeoSN33FlSFUiAwAA
                 """,
-            """
+        """
+                androidx/navigation/OuterComp$InnerClassComp$Companion.class:
+                H4sIAAAAAAAA/51TTW/TQBB9a6d2YkJJUz4SoHwGSBHUTQUIqQgJgpAipa0E
+                KJce0CZZyib2Gq3XUY858UP4Bz0hcUBRj/woxKwTqLgglcvMm3nzZr0z3h8/
+                v30H8AhNhidcDXUih4eh4hN5wI1MVLiXGaHbSfyp0VGKUMTTNA+t4YpKfDCG
+                yohPeBhxdRDu9UdiYHy4DN4zqaR5zuA213tlLMELUIDPUDAfZcrwtPt/R24z
+                tJrdcWIiqcLRJA6lIoniUfhKfOBZZNqJSo3OBibRO1yPhd5e7wVw7NGrjcEJ
+                +T7OWYaN03VjWPkt2BGGD7nhlHPiiUvDZNaUrAEDG1P+UNpok9CwxdCYTYPA
+                qTmBUyE0mxaPP7u12XTL2WQv/aJz/MVzKo6t3WK2w4PTzMjHFYa1fyp8rDEs
+                /y1jKP0ZLi1zl0/ozkYnUST0xtjQwtrJUDCc60oldrO4L/Q73o8oU+0mAx71
+                uJY2XiTLJ90FrTl4m2R6IF5Ly9XfZMrIWPRkKqn4hVKJyb8wRYs2VLBjI+/Y
+                v4Vuf4ui0M6R/NL9ryge5fRtsl6efIwG2fK8ACUEQIUROrMQPyTvLMTlo3wn
+                VnBxnpwLcnQWy8S5uENRldiruIbrqOfoBvm7+cE3cS9/LzQL0lT24Xaw0kG1
+                g1WcJ4gLHep9aR8sRQ114lMEKS6n8H4BDr3L2mwDAAA=
+                """,
+        """
+                androidx/navigation/OuterComp$InnerClassComp.class:
+                H4sIAAAAAAAA/5VUW0/cVhD+jvfmNQt4IRcCmyYt23S5BHNLSoE0XFKK6QIp
+                pDSE9uGw64LB2NT2ovSlylN+QqT2pVIf+sRDorZQFamiyVt/U1V1jm2WW4S0
+                0u45M+OZb74zM+f889+ffwEYxNcM3dwuu45ZfqrZfMdc477p2Np8xTfcSWdr
+                O6/bNkkW9zyhpsAY1A2+wzWL22va/OqGUfJTiDEkR03b9D9miBf0jiWGWKFj
+                KYMEUgrikBlkUyCNu2sMTM9AQV0aEjLk76+bHkNPsRYiIwx1a4avVzEpnc6g
+                lOibYxu230fAJWf7O4Y+4lMrdnvRcde0DcNfdblpexq3bccPojxtzvHnKpY1
+                Ig6XVOgMVxgyIlW+bHzDK5bP4BZqS6jrxbM1HamRcwbNuCTYtFKpfWfRd02b
+                ynKp0HECOrTS+a6etU1UTKtsuCm8o+CGaFfLafzCUffuyXiXms23tw27zHC7
+                cB7+fMYInUi2Iy8SvM+QE225yPED4VgQjpMXO3YKx64McrgupNtUgHXurU86
+                ZYMhexyp276xJs7YGw4pTaGGfgV9GKATGd9WuEVzeLnwll48YchfNBI0D3zV
+                MqiyCcdfN1yGpvMoxGu0ZEW35G4t3c2LhdvkksKImOjipuMTkraxs6WZdCzX
+                5pb2IBy/SWLku5WS77iz3N2kIoUX8Z6CUVDmdBWMYaimITumQXUfw7i4wBNU
+                4iM2s4bPy9znRFHa2onRC8PEkhYL6Npvkv2pKTTqgFSmK7p7+OymIrVIiqQe
+                PlPoJ6myIslJ2utoj9HeQGb59XO5hVwb+6VeNswaJ+qbkqrUKvXGXv+clNT4
+                TFpNCW36zfPYTLMqk3z4rF+WpdCJzIzMaZKVflmta423sF42/eZFjAIzoccL
+                RnI9yQ1CXshW4WXK3xqXE2pScO5n4iTXL6xaCg8ZGk6Xjl7NOb5DrfFdx7IM
+                t2eTnom2hYrtm1uGbu+YnknzM348UzSi4QA3Fk3bmKtsrRruIzFjYrScEreW
+                uGsKPTLWL/q8tDnLtyM9fxb7IXf5lkEUTyXJHNM0SFUWnYpbMqZMAXEtglg6
+                R46ujESvOmi9JgaBSvKItCTtl2lvEq+7aDzpicD6BWlT5C3RrnTuI93Z9jvq
+                XwUIS7Q2QEzFAGEOUtQAviTtSuhN3xrF/JAkUGncoNI/xNTEWNGe6PwN9btV
+                uGRgHAxgMqFDBJMlckfB7WeD2VsD6F0lWBHQRywFp/QBpOW2fVx9WQ0Kyaar
+                ZNMR2RNlUdNooXKFuW9FBczm4t//AFkwGO1s20NbCPmY1hiYQKBXLUo/TLug
+                ljvAjeV93Gx6bw+3ROQeOtSOPXTvoeflmWPkIkYn28OobNkqj7AGAYM/MHi2
+                DHIUz3AHdyMeX9Eu2pXv7PoFifhu19+QfkQittt1CGlWAHXT/ydhiYc9eRy0
+                L5aS/0U2RfpxxfLViuUxhI8ozzLJKUHqwyD9MFIR1ZYgKRE7wOgy28f9XzH5
+                KrDE8CSYOjFfn2OBijxK0hjtK0H6RaIMkhkeUF8/WUFMx5SOT3VMQycRMzo+
+                Q5EcPMxibgWqh0YP8x6UYE16wpL10OSh2cOdwDjkQfOQC+Sx/wHugknKUQkA
+                AA==
+                """,
+        """
+                androidx/navigation/OuterComp$InnerObject.class:
+                H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQeolCpTUFcQE2w0DinFCMEo
+                q0s7wsB0BmduG5as/AkuXLrQDQuJCxJNTJWdP8p47jAKQiQm7T2POef7znzn
+                tj9+fv4K4C7uMeSFVwt8p7ZjeqLprAvp+J652JB2UPLr22OW59nB4tqmXZVp
+                MIbcpmgK0xXeuvk7qzGkZh3PkfcZtPH8ShuSSBlIIM2QkBtOyFAo/zfLDIMu
+                /SUZON46Q+94vnzMeJSlitGyH6ybm7ZcC4TjhabwPF9GoKFZ8WWl4bpUlT0B
+                q6ODgDdEuFHya3Y0qKV5H4qzNLz9qiFcmvLCePn0283kXzCMncdGVGLNtYku
+                6csNO2DoPotC1LNVN9LIAFfC6FZlaXmuUnrYhiEYGUoOM3SVt3xJZeaCLUVN
+                SEGNvN7UaFdMHRl1gIFtUX7HUVGRvNoUw8vW7ojBB7jBc61dg+vKycZWN1Qq
+                16EfvjYGWrvTvMgepHX+/V2K5/h8T04b4sXEtJ5LDiUGWJE9PnyjzWdyKcqm
+                yWfk6+RnlK/Yppma4dK5G00jT3elIpol35OB77p2MLklGYafNjzp1G3Lazqh
+                Q7rNHWtJt+VoN51lx7MrjfqaHSwrbZWkflW4KyJwVBwn25ekqG4tiO04HjuN
+                /UQEom7TRH+RtEW3ouSKMLQpNJb8RlC1HzkKYjCGWDkzHKZoRYlI/UG1MbK3
+                KEqRbSebpKfJKLpNkal2pLITB9D3yeGYjItBQCadbUcFyBCUAs1ShkfNV+Nm
+                rbvzY/TouFyLy08yk8zoinmPW7v3/tHK0IPemMkiy8n2TxTeI5nYK3wDf4uk
+                tldogT9L7EWDF+lMgKf1CKzvqCEGU14ffRm9FNStpt8QOToG/kjRHzUA2S/g
+                zw8w+AkX96OEhmk6lY4cE+ggVe9EfAX6W1Kj0Q0jeUZWoVm4bOGKRW93jVyM
+                WhjD9VWwEDdwcxVGqD7jIVIheiKnL0QucrJ0/gKE9w8Q7AQAAA==
+                """,
+        """
+                androidx/navigation/OuterComp.class:
+                H4sIAAAAAAAA/41RW2sTQRT+ZjaXzWZt03hpYk2rtmpTxW2LT7UINSgsxC20
+                JSB5miRLnGQzK7uT0Mc8+UP8B8WHgoIEffNHiWe30SJCcYc935zbd+ac8+Pn
+                568AnuExQ02oXhTK3qmjxET2hZahcg7H2o8a4eh9HoyhNBAT4QRC9Z3DzsDv
+                6jwMhty+VFK/YDA26y0bWeQsZJBnyOh3MmZYa17J/JzB3O8GKYcFniSarnd8
+                cuA1Xtm4BqtAxgWG9WYY9Z2BrzuRkCp2hFKhTrlixwu1Nw4ColpqDkNNZM4b
+                X4ue0IJsfDQxqEuWiEIiwMCGZD+VibZNt94OQ302tS1e4RYvzaYWNw3z+wde
+                mU13+Tbb40bmZd7k3z7meIknCbssoVlwlaI2AhHHSS8MxdRwMR2GJ1d2vvF3
+                ch5r9Ij/yPg9+3u0EU9MGqHSURgEfvR0SDVXjsZKy5HvqomMZSfwDy4HRTtp
+                hD2fYbEple+NRx0/OhEUw1Buhl0RtEQkE31utC9f6FOydRyOo67/Wia+6rxO
+                658q2KGNZdIxV5MFEm6QliMsEXI62VR7QJqTLIMwu3UO8yx1P5wH02rwiKR9
+                EYACUQEmin+Slyk6+YpfwN+ew/6ExbPUYGCTZJncd+mv0TvuE64S1tMS69gi
+                3COaJSIut2G4uO7ihoubuEVXLLuooNoGi3EbK21kY1gx7sTIxajFWP0Fraxj
+                CzoDAAA=
+                """,
+        """
                 androidx/navigation/TestAbstract.class:
-                H4sIAAAAAAAA/4VRy0oDMRQ9SduxjlWnPusLfICoC0fFnSKoIBSqgko3rtJO
-                0NhpApO0uOy3+AeuBBdSXPpR4s3o3s3hPG7Cyc3X9/sHgEOsMKwKnWRGJc+x
-                Fn31IJwyOr6T1p22rMtE242AMURPoi/iVOiH+Lr1JL1bYAiOlVbuhKGwtd2s
-                oIQgRBEjDEX3qCzDeuO/y48Yqo2OcanS8aV0IhFOkMe7/QIVZB5GPYCBdch/
-                Vl7tEUv2qftwEIa8xkMeERsOypu14eCA77Gz0udLwCPu5w6YPx1dif650S4z
-                aSqz3Y6jkucmkQyTDaXlVa/bktmdaKXkTDVMW6RNkSmv/8zw1vSytrxQXizc
-                9LRTXdlUVlF6qrVx+eNscQ2cdvBX2a+EsEYqzjVQ2nlD+ZUIxwJhkJsbWCSs
-                /A5gFGGeL+U4j+X8rxjGKKvco1DHeB0TdUwiIopqHVOYvgezmMEs5RahxZxF
-                8AMTgVUI6AEAAA==
+                H4sIAAAAAAAA/4VRy0oDMRQ9SduxjlWnPusL1IWvhaPiThFUEApVQaUbV2kn
+                aOw0gUlaXPZb/ANXggspLv0o8WZ07+ZwHjfh5Obr+/0DwCFWGFaFTjKjkudY
+                i756EE4ZHd9J605b1mWi7UbAGKIn0RdxKvRDfN16kt4tMATHSit3wlDY2m5W
+                UEIQoogRhqJ7VJZhvfHf5UcM1UbHuFTp+FI6kQgnyOPdfoEKMg+jHsDAOuQ/
+                K6/2iCX71H04CENe4yGPiA0H5Y3acHDA99hZ6fMl4BH3cwfMn46uRP/caJeZ
+                NJXZbsdRyXOTSIbJhtLyqtdtyexOtFJyphqmLdKmyJTXf2Z4a3pZW14oLxZu
+                etqprmwqqyg91dq4/HG2uAZOO/ir7FdCWCMV5xoo7byh/EqEY4EwyM1NLBJW
+                fgcwijDPl3Kcx3L+VwxjlFXuUahjvI6JOiYREUW1jilM34NZzGCWcovQYs4i
+                +AEAejLS6AEAAA==
                 """,
-            """
+        """
+                androidx/navigation/TestAbstractComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSlkfCuyVILRKdpGJFEVIJQoqUFolW
+                2XSBnIkpTmY8yHaiLrPiQ/iDrpBYoKhLPgpxPQmwpZv7Ovfcax/756/vPwA8
+                xxOGHaGHJlPDs0iLqToVTmU6OpbW7Q+sMyJ2nSz93PRGaIJKYAy1kZiKKBH6
+                NHo3GMnYlRAwFF8qrdwrhmBru1/FCoohCigxFNwnZRlavcut2mNob/XGmUuU
+                jkbTNFLaSaNFEr2RH8UkoXZNvEnsMnMgzFiave1+CO5Xrjfjf+CHNEfprpeb
+                xrD6h3AgnRgKJ6jG02lA4jFvKt6AgY2pfqZ81qJo2GZozmdhyOs85DWK5rPy
+                xZegPp/t8hZ7XSrzi69FXuO+d5f5Cc3/0aaEuwyVvwLRQxyKKZ3bmSxJpNkZ
+                OxK7kw0lw/We0vJwkg6kORaDhCprvSwWSV8Y5fNlsdrVWppOIqyV9EThUTYx
+                sXyrPNZ4P9FOpbKvrKLmfa0zl5/Lok0qF/zVyXP/0nSDh5RFXgvyK0+/oXye
+                w4/IFvPiC2yQrS4aUEEI1BhFV5bkZ+T5klw9z3X1hFuL4oKQR1dxjbAAm5SF
+                Oeke7qOBx/nCB2jmf5s0oN7aCYIuVrtY62IdNyjEzS7NvH0CZlFHg3CL0OKO
+                RfE3/dAjtxgDAAA=
+                """,
+        """
+                androidx/navigation/TestAbstractComp.class:
+                H4sIAAAAAAAA/41RW2sTQRg9s5vrurFJvSXWS2trTPvQbYsgNEWoESGQpqAl
+                IHmaJGOdZDMrM5PQx/4W/0HxoaAgwUd/lPjtNrYPvuTlO/Ndzvku8/vP958A
+                XmKLYYOrgY7k4CxQfCpPuZWRCk6EsYc9YzXv20Y0/pIFYygO+ZQHIVenwXFv
+                KPo2C5chcyCVtK8Z3Npmx0caGQ8pZBlS9rM0DNXWIg3qDLmDfjiX2l6EshEb
+                riiVhc+wW2uNIksKwXA6DqSyQiseBm/FJz4JiaCIOenbSB9xPRK6fjXsbQ8F
+                LDHkr8UYdhaa+KZ93UcJy3k4uMOw3or0aTAUtqe5VCbgSkU2UTBBO7LtSRjS
+                rqV/sx4Jywfccoo546lLn8Jik48NGNiI4mcy9nboNdile87Ofc8pO55TnJ17
+                Ts7JVcuz81V3z9lh+8x9k/71NeMUnbh6j8UaxTaf0vpWR2Eo9PbIMqy8nygr
+                x6KpptLIXigOb6akj2tEA8Gw1JJKtCfjntAnnGoYlltRn4cdrmXsz4N+Uymh
+                GyE3RhDZ+xBNdF+8k3GuMu/T+a9Lao3OlUp2rMTXI1wnL0N4j9AhTCfeBnlB
+                fAnC9NYlchdJ+vm8GNhHlax/VYA8PMIcbl2Ty0huCf8HCh/ZJYrfcPciibh4
+                QdajugIplmiQWqL9DJuEryh+nxQfdOE2UW6i0sRDrNATj5p4jCddMIOnWO0i
+                ZeAZrBlkDEp/AVeTkqlcAwAA
+                """,
+        """
                 androidx/navigation/TestClass.class:
-                H4sIAAAAAAAA/31RO0sDQRD+ZmMuekY93/HdqoWnYqcIGhACUUEljdUmt+ia
-                yy7cboJlfov/wEqwkGDpjxLnTmubj+8xM8zsfn2/fwA4wgZhQ5okszp5jo0c
-                6AfptTXxnXK+nkrnKiBC9CQHMk6leYiv20+q4ysoEYITbbQ/JZS2d1pVlBGE
-                GEOFMOYftSNsNf+dfEyYbXatT7WJL5WXifSSPdEblHg1ymEiBxCoy/6zztU+
-                s+SAsDkahqGoiVBEzEbD2mh4KPbpvPz5EohI5FWHlPdGV3JQt8ZnNk1Vttf1
-                vF/dJoow09RGXfV7bZXdyXbKzlzTdmTakpnO9Z8Z3tp+1lEXOhcrN33jdU+1
-                tNOcnhljfXGXwwEEn/+3cP4ajDVWcaGB8u4bxl+ZCKwwBoU5g1XG6m8BJhAW
-                +VqBy1gv/ogwyVn1HqUGphqYbnBXxBSzDcxh/h7ksIBFzh1ChyWH4AcFuY/X
+                H4sIAAAAAAAA/31Ry0oDMRQ9N7VTHauO7/reqgtHxZ0iaEEoVAWVblylnaCx
+                0wQmaXHZb/EPXAkupLj0o8Q7o2s3h/O4CecmX9/vHwCOsEHYkCbJrE6eYyMH
+                +kF6bU18p5yvp9K5CogQPcmBjFNpHuLr9pPq+ApKhOBEG+1PCaXtnVYVZQQh
+                xlAhjPlH7QhbzX9vPibMNrvWp9rEl8rLRHrJnugNSlyNcpjIAQTqsv+sc7XP
+                LDkgbI6GYShqIhQRs9GwNhoein06L3++BCIS+dQh5WejKzmoW+Mzm6Yq2+t6
+                7le3iSLMNLVRV/1eW2V3sp2yM9e0HZm2ZKZz/WeGt7afddSFzsXKTd943VMt
+                7TSnZ8ZYX+zlcADB6/8Vzl+DscYqLjRQ3n3D+CsTgRXGoDCXscpY/R3ABMIi
+                XytwGevFHxEmOaveo9TAVAPTDcwgYorZBuYwfw9yWMAi5w6hw5JD8AOsqUxn
                 4AEAAA==
                 """,
-            """
-                androidx/navigation/TestClassWithArg.class:
-                H4sIAAAAAAAA/41QTYvTUBQ97yVN09jatH51On47yEwXpjO4UwZrQQjUEcah
-                Lrp6bUPnTdMXyHsts+xvce1GUAQXUlz6o8T70sGVCyE5956bw7m559fv7z8A
-                PMcew55Q0zyT08tIiZWcCSMzFZ0l2vRTofUHac57+awMxhBeiJWIUqFm0bvx
-                RTIxZTgM3kuppDlmcPfjgyGDs38wrKKEcgAXPnGRzxhYXEWAaxVwVElqzqVm
-                eDr4n90vaMcsMT1rQ+YxQ2Mwz0wqVfQ2MWIqjCAJX6wcOolZqFgALZ3T/FJa
-                1qVuesjQ36ybAW/xgIebdUAPD/2A+05rsz7iXfa61vRC3uZd5+dHj4fuaeMv
-                80nddv1S6FmrI2YXhCdi1c+UybM0TfJnc0On9bNpwlAfSJWcLBfjJD8T45Qm
-                zUE2EelQ5NLyq2HwPlvmk+SNtGTndKmMXCRDqSV97SmVmSISjUPKzS1uatoY
-                qePUl+ARPiB2TJxTDTrfUOnsfkXtc6F5SGg1QAOPCG9vVbiOuo2IOutGiSKk
-                d+sV2eSoljpfUPv0T5vqVnBlw/G4wPt4QvVV8ZMl3BjBiXEzxq2Y1t6hFq0Y
-                O2iPwDR2cXeEskZd455GUKCnEWo0/gB/P3a9nQIAAA==
+        """
+                androidx/navigation/TestClassComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSlkfCuxCkFminqdgVIUEQUqS0SKXK
+                pgvkJKY4mbGR7URdZsWH8AddIbFAUZd8FOJ6UmCJurmPc++5987x/Pz1/QeA
+                53jM8FTooTVqeJJoMVXHwiujk0PpfDsVzrVN9rkZjNCEl8AYaiMxFUkq9HHy
+                rj+SA19CxFB8obTyLxmi9Y1eFUsoxiigxFDwn5Rj2OxeYM8uQ2u9OzY+VToZ
+                TbNEaS+tFmnyRn4Uk9S3jXbeTgbe2D1hx9LubvRi8LBvtTn4V/yQ5VWGrYtN
+                Y1j+Q9iTXgyFF4TxbBqRbCyYSjBgYGPCT1TItikathia81kc8zqPeY2i+ax8
+                9iWqz2c7fJu9LpX52dcir/HQu8PChLX/ClPCbYbKX3XoCfbFlI721qSptFtj
+                TzK3zVAyXO0qLfcnWV/aQ9FPCVnpmoFIe8KqkJ+D1Y7W0uYLJD1O/N5M7EC+
+                VaHWOJhorzLZU05R8yutjc+PcmiRxIXw3eR5eGM6/z5lSRCC/NKTbyif5uUH
+                ZIs5+AxrZKuLBlQQAzVG0aVz8iZ5fk6unuaiBsKNBbgg5NFlXKFahIeUxTnp
+                Du6igUf5wnto5r80aUC9tSNEHSx3sNLBKq5RiOsdmnnzCMyhjgbVHWKHWw7F
+                3/peHcMPAwAA
                 """,
-            """
+        """
+                androidx/navigation/TestClassComp.class:
+                H4sIAAAAAAAA/4VRXWsTQRQ9s5vPdWOT+pVYP1obta0f2xRBsEXQiBBII2gp
+                SJ8myVgn2czIzCT0sb/Ff1B8KChI8NEfJd7dxvbBh7zcM/fMuWfuvfP7z/ef
+                AJ5hg2GFq77Rsn8UKT6Rh9xJraI9YV0z5tY29ehLHoyhPOATHsVcHUbvugPR
+                c3n4DLkdqaR7yeCvre+HyCIXIIM8Q8Z9lpZhtT3XfZuhsNOLZz6P5urrSeCK
+                +DxChsZae6gdlUeDySiSygmjeBy9EZ/4OHZNrawz457TZpeboTDbZ21eDlDC
+                AkPx3IzhyfxeL97eDlHBYhEeriRTanMYDYTrGi6VjbhS2qXlNupo1xnHMU1Z
+                +dfornC8zx0nzhtNfPoIloRiEsDAhsQfySTbpFO/wVCfHoeBV/UCrzw9DryC
+                V50eL/tb3iZ7wfzX2V9fc17ZS7RbLHEod/iEJndGx7EwT4eOYen9WDk5Ei01
+                kVZ2Y/Hqokf6rabuC4aFtlSiMx51hdnjpGFYbOsej/e5kUk+I8OWUsKkSxFU
+                HHzQY9MTb2VyV5u9s//fK2jQsjLphLVkd4SrlOUIrxF6hNk0q1MWJXsgzG6c
+                onCSXt+fiYHHeEAxPBOgiICwgEvnxVWkm0T4A6WP7BTlb7h6kjI+HlIMSFci
+                xwo1spZ638M64XPir5PjjQP4LVRbqLVwE0t0xK0WbuPOAZjFXSwfIGMRWKxY
+                5CwqfwEoeIqnTgMAAA==
+                """,
+        """
+                androidx/navigation/TestClassWithArg.class:
+                H4sIAAAAAAAA/41QTW/TQBScXSdOYhLihK805ZsKtTngtOIGqgiRkCyFIpUq
+                HHLaxJa7jbOWvJuox/wWzlyQQEgcUMSRH4V461ScOCDZ897sjuf5za/f338A
+                eI49hj2hojyT0WWgxEomwshMBWexNsNUaP1BmvNBnlTAGPwLsRJBKlQSvJte
+                xDNTgcPgvpRKmmOG0n54MGZw9g/GdZRR8VBClbjIEwYW1uHhWg0cdZKac6kZ
+                no7+Z/YLmpHEZmBtyDxkaI3mmUmlCt7GRkTCCJLwxcqhlZiFmgXQ0DmdX0rL
+                +tRFhwzDzbrt8Q73uL9Ze/Rwv+rxqtPZrI94n71utF2fd3nf+fnR5X7ptPWX
+                VUndLVXLvmutjpgd4J+I1TBTJs/SNM6fzQ2tNsyimKE5kio+WS6mcX4mpimd
+                tEfZTKRjkUvLrw6999kyn8VvpCU7p0tl5CIeSy3pdqBUZopINA4pt1KxU9vG
+                SB2nvgyX8AGxY+Kcqtf7hlpv9ysanwvNQ0KrAXbwiPD2VoXraNqIqLNulCh8
+                erdegU2Oarn3BY1P/7SpbwVXNhyPC7yPJ1RfFT9Zxo0JnBA3Q9wKaewdatEJ
+                6fvuBExjF3cnqGg0Ne5peAW6Gr5G6w+psmgInQIAAA==
+                """,
+        """
+                androidx/navigation/TestClassWithArgComp$Companion.class:
+                H4sIAAAAAAAA/5VSS29SQRT+Zi6FckWlrQ/wVR+YUBO5hXRXY1IxJiS0Jtrg
+                ogszwEgH7p0xcwfSJSt/iP+gKxMXhnTpjzKeuaBu7ea8vvOdM/PN/Pz1/QeA
+                PTxlaAk9tEYNzyItZmoknDI6Opapa8ciTT8od3pgR22TfK55IzTBBTCG8ljM
+                RBQLPYre9sdy4AoIGPIvlFbuJUNQ3+mVsIZ8iBwKDDl3qlKGve7l1+0zNOvd
+                iXGx0tF4lkRKO2m1iKPX8pOYxq5tdOrsdOCMPRR2Iu3+Ti8E92u3aoN/4Mck
+                Qxkal5vGsPGHcCidGAonqMaTWUAiMm+K3oCBTah+pny2S9GwyVBbzMOQV3jI
+                yxQt5usXX4LKYt7iu+xVYZ1ffM3zMve9LeYn1P9XnwLuMhT/ikQPciRmdHZn
+                TRxL25g4Er1thpLheldpeTRN+tIei35Mlc2uGYi4J6zy+apY6mgtbbZH0lOF
+                783UDuQb5bHqu6l2KpE9lSpqPtDauOxsKZqkdM5fnzz3L0632KYs8nqQX3v2
+                DevnGfyQbD4rNvCIbGnZgCJCoMwourIiPyfPV+TSeaatJ9xaFpeELLqKa4QF
+                eExZmJHu4T6qeJItfIBa9s9JA+otnyDoYKODzQ62cINC3OzQzNsnYCkqqBKe
+                IkxxJ0X+N9Gij4okAwAA
+                """,
+        """
+                androidx/navigation/TestClassWithArgComp.class:
+                H4sIAAAAAAAA/41S308TQRD+9vrrehY5KmIBf6CglqpcITwJIcEa4yWlJkhq
+                DE/bdi3bXvfM3rbhkb/FZ1+IGhJNDPHRP8o4d634oA8kdzM7szPfzHyzP399
+                /Q5gE+sMZa46OpSdY0/xkexyI0PlHYjI1AIeRW+kOdrV3Vo4eJ8DY3B7fMS9
+                gKuu96rVE22TQ4ohuy2VNDsM6bK/2mRIlVebBWSQc5CGTTbXXQbmF+DgSh4W
+                ChRqjmTEUKlftv4W1ekKsxtDUQGfwd5uB5PCG5dFWYkFV3SdwzWG9XK9HxpC
+                8XqjgSeVEVrxwHsu3vFhYGqhiowetk2o97juC701nuu6g1nMMeQvwBg2Lz3I
+                3xa2CihhPiZkgWG5Huqu1xOmpblUkceVCk2CEnmN0DSGQUAUzPzpd08Y3uGG
+                k88ajFK0ThaLfCxAZPfJfyxjq0qnDm365flJ0bFKlmO55ycOfZZrO5adLp2f
+                LOU2rCp7ynLPpopZ11qwqqkfH7KWm96fubBsSllI2xk3G+NtsLiK2+AjIsno
+                MAiEXusbhsX9oTJyIHw1kpFsBWL37xy09VrYEQzTdalEYzhoCX3AKYahWA/b
+                PGhyLWN74iz4Sgmd8Cco2XkdDnVbvJDx3fykTvOfKlgnQtM0uIX5mF/qs0JW
+                lvRN0sX4EZJOkZ1JvI/I2qFoi7RTOUO+svgFU6cJwuNJJrCGJyTnxlG4iumY
+                aDrFaLQXuPSPsbyYf9KZymdMffwvTGEcMIGxqancJLmEZIMofMPsW3aGG5+w
+                eJp4UpQbF2T0+KxkMC/BXkWVdI38twjx9iFSPu74WPJxF/foiGUfK7h/CBbh
+                AR4ewo4wHaEcwUlkNoIbYSZC6Tedt709GAQAAA==
+                """,
+        """
+                androidx/navigation/TestGraph.class:
+                H4sIAAAAAAAA/31SQWsTQRT+ZpJsNttoY6s2sdaq7UE9uG3xZhFqUFmIK9gQ
+                kJ4m2SGdZDMju5PQY07+EP9B8VBQKKHe/FHimzXoQXAW3nvfN998zHuzP35+
+                vQTwDLsMW0InmVHJWajFTA2FVUaHXZnbN5n4eFoFY2iMxEyEqdDD8F1/JAe2
+                ihKDd6i0si8YSo8e9+qowAtQRpWhbE9VzrDd+a/zcwb/cJAWHgG4O+hH8XH3
+                KG6/quMaghqR1xl2OiYbhiNp+5lQOg+F1sYWXnkYGxtP05SsbnTGxpJZ+FZa
+                kQgriOOTWYm6ZC7UXAADGxN/phzaoyrZZ9hdzIOAN3nAG1Qt5v73T7y5mB/w
+                Pfay6vOrzx5vcKc9YM6hEYtZ22ibmTSV2dOxZdh8P9VWTWSkZypX/VQe/b0j
+                jaNtEsmw2lFaxtNJX2ZdQRqGtY4ZiLQnMuXwkgyOzTQbyNfKgdbSuPePLfZp
+                OuWipZYbFuV7hDx3QcqcvkqBtgmFrnHKlScX8M+L7ftLMbCOBxTrvwWokRXg
+                Y+XP4Q1Su7XyDfzDBepfsHpeEBwPi7iFneJfokcgg7UTlCKsR7gZ4RZuU4mN
+                CE20TsBy3MEm7ecIctzN4f0CEKx6togCAAA=
+                """,
+        """
                 androidx/navigation/TestInterface.class:
                 H4sIAAAAAAAA/4WOz0rDQBDGv9lo08Z/qVqoR/Fu2tKbJykIgaqg4iWnbbIt
                 22x3IbsNPfa5PEjPPpR0Ux/AGfjmmxn4zfz8fn0DGKNHuOW6qIwsNonmtVxw
@@ -433,19 +752,18 @@
                 XzBcHfQS174OPfjYZytDkCJM0U7RQeQtTlKc4iwDWZzjIgOziC26e5qGvyhR
                 AQAA
                 """,
-            """
+        """
                 androidx/navigation/TestObject.class:
-                H4sIAAAAAAAA/31STW/TQBB9u0kcxw00lI8mFEqhPQAH3FbcqJBKBJKlYCQa
-                Rap62sSrsImzK9kbq8ec+CH8g4pDJZBQBDd+FGLWBDggYUsz896+edoZ+/uP
-                T18APMUew7bQSWZUch5qUaixsMrosC9z+2Y4kSNbB2NoTUQhwlTocfibrTB4
-                R0or+5yh8vDRoIkavABV1Bmq9p3KGXZ6/7d+xuAfjdLSJAB3nX4Un/SP4+7L
-                Jq4gaBB5lWG3Z7JxOJF2mAml81BobWxploexsfE8TcnqWm9qLJmFr6UVibCC
-                OD4rKjQnc6HhAhjYlPhz5dA+VckBw95yEQS8zQPeomq58L+95+3l4pDvsxd1
-                n3/94PEWd9pD5hxasSi6RtvMpKnMnkwtw9bbubZqJiNdqFwNU3n89460j65J
-                JMN6T2kZz2dDmfUFaRg2emYk0oHIlMMrMjgx82wkXykHOivjwT+2OKDtVMuR
-                Om5ZlLcJee6ClDm9tRLdIxS6wSnXHl/CvyiPd1ZiUPN9is1fAjTICvCx9qd5
-                k9TuWfsMfnqJ5kesX5QEx4My3sVu+TfRRyCDjTNUIlyPcCPCTdyiEpsR2uic
-                geW4jS06zxHkuJPD+wnyhROwigIAAA==
+                H4sIAAAAAAAA/32Sz2sTQRTHvzNJNptttLH+aGK1VtuDenDb4s0i1KCwEFew
+                IVB6mmSHOMlmBnYnS485+Yf4HxQPBQUJevOPEt+sUQ+Cu/De+37nzYeZt/v9
+                x6cvAJ5ij2Fb6CQzKjkPtSjUWFhldNiXuX0znMiRrYMxtCaiEGEq9Dj87VYY
+                vCOllX3OUHn4aNBEDV6AKuoMVftO5Qw7vf+jnzH4R6O0hATgbqcfxSf947j7
+                sokrCBpkXmXY7ZlsHE6kHWZC6TwUWhtbwvIwNjaepymhrvWmxhIsfC2tSIQV
+                5PFZUaF7MhcaLoCBTck/V07tU5UcMOwtF0HA2zzgLaqWC//be95eLg75PntR
+                9/nXDx5vcdd7yByhFYuia7TNTJrK7MnUMmy9nWurZjLShcrVMJXHf89I8+ia
+                RDKs95SW8Xw2lFlfUA/DRs+MRDoQmXJ6ZQYnZp6N5CvlRGcFHvyDxQFNp1pe
+                qeOGRXmblOcOSJnTWyvVPVKhuzjl2uNL+Bfl8s6qGbiJ+xSbvxrQIBTgY+3P
+                5k3qds/aZ/DTSzQ/Yv2iNDgelPEudsu/iT4CATbOUIlwPcKNiNC3qMRmhDY6
+                Z2A5bmOL1nMEOe7k8H4CjO1ti4oCAAA=
                 """
-        )
-}
+    )
diff --git a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt
index c530ccd..eaaf466 100644
--- a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt
+++ b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt
@@ -17,12 +17,22 @@
 package androidx.navigation.runtime.lint
 
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.compiled
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
+import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.checks.infrastructure.TestMode
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 
-class WrongStartDestinationTypeDetectorTest : LintDetectorTest() {
+@RunWith(Parameterized::class)
+class WrongStartDestinationTypeDetectorTest(private val testFile: TestFile) : LintDetectorTest() {
+
+    private companion object {
+        @JvmStatic @Parameterized.Parameters public fun data() = listOf(SOURCECODE, BYTECODE)
+    }
 
     @Test
     fun testEmptyConstructorNoError() {
@@ -37,14 +47,16 @@
                 fun createGraph() {
                     val navController = NavController()
                     navController.createGraph(startDestination = TestClass())
+                    navController.createGraph(startDestination = TestClassComp())
 
                     val navHost = TestNavHost()
                     navHost.createGraph(startDestination = TestClass())
+                    navHost.createGraph(startDestination = TestClassComp())
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .skipTestModes(TestMode.FULLY_QUALIFIED)
             .run()
@@ -71,11 +83,17 @@
                     navController.createGraph(startDestination = Outer.InnerClass(15)) {}
                     navController.createGraph(startDestination = InterfaceChildClass(true)) {}
                     navController.createGraph(startDestination = AbstractChildClass(true)) {}
+                    navController.createGraph(startDestination = TestClassWithArgComp(15))
+                    navController.createGraph(startDestination = TestClassComp::class)
+                    navController.createGraph(startDestination = OuterComp.InnerClassComp::class)
+                    navController.createGraph(startDestination = OuterComp.InnerClassComp(15))
+                    navController.createGraph(startDestination = InterfaceChildClassComp(true))
+                    navController.createGraph(startDestination = AbstractChildClassComp(true))
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -100,11 +118,13 @@
                     navController.createGraph(startDestination = Outer.InnerObject::class) {}
                     navController.createGraph(startDestination = InterfaceChildObject) {}
                     navController.createGraph(startDestination = AbstractChildObject) {}
+                    navController.createGraph(startDestination = OuterComp.InnerObject::class)
+                    navController.createGraph(startDestination = AbstractChildObjectComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -127,11 +147,20 @@
                     navController.createGraph(startDestination = Outer.InnerClass) {}
                     navController.createGraph(startDestination = InterfaceChildClass) {}
                     navController.createGraph(startDestination = AbstractChildClass) {}
+                    navController.createGraph(startDestination = TestInterface)
+                    navController.createGraph(startDestination = TestAbstract)
+                    //classes with companion object to simulate marked with @Serializable
+                    navController.createGraph(startDestination = TestClassComp)
+                    navController.createGraph(startDestination = TestClassWithArgComp)
+                    navController.createGraph(startDestination = OuterComp.InnerClassComp)
+                    navController.createGraph(startDestination = InterfaceChildClassComp)
+                    navController.createGraph(startDestination = AbstractChildClassComp)
+                    navController.createGraph(startDestination = TestAbstractComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expect(
@@ -166,7 +195,55 @@
 you can also pass in its KClass reference AbstractChildClass::class [WrongStartDestinationType]
     navController.createGraph(startDestination = AbstractChildClass) {}
                                                  ~~~~~~~~~~~~~~~~~~
-5 errors, 0 warnings
+src/com/example/test.kt:12: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor TestInterface(...)?
+If the class TestInterface does not contain arguments,
+you can also pass in its KClass reference TestInterface::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = TestInterface)
+                                                 ~~~~~~~~~~~~~
+src/com/example/test.kt:13: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor TestAbstract(...)?
+If the class TestAbstract does not contain arguments,
+you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = TestAbstract)
+                                                 ~~~~~~~~~~~~
+src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = TestClassComp)
+                                                 ~~~~~~~~~~~~~
+src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = TestClassWithArgComp)
+                                                 ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = OuterComp.InnerClassComp)
+                                                 ~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = InterfaceChildClassComp)
+                                                 ~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = AbstractChildClassComp)
+                                                 ~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:20: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navController.createGraph(startDestination = TestAbstractComp)
+                                                 ~~~~~~~~~~~~~~~~
+13 errors, 0 warnings
             """
             )
     }
@@ -191,11 +268,17 @@
                     navHost.createGraph(startDestination = Outer.InnerClass(15)) {}
                     navHost.createGraph(startDestination = InterfaceChildClass(true)) {}
                     navHost.createGraph(startDestination = AbstractChildClass(true)) {}
+                    navHost.createGraph(startDestination = TestClassWithArgComp(15)) {}
+                    navHost.createGraph(startDestination = TestClassComp::class) {}
+                    navHost.createGraph(startDestination = OuterComp.InnerClassComp::class) {}
+                    navHost.createGraph(startDestination = OuterComp.InnerClassComp(15)) {}
+                    navHost.createGraph(startDestination = InterfaceChildClassComp(true)) {}
+                    navHost.createGraph(startDestination = AbstractChildClassComp(true)) {}
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -220,11 +303,14 @@
                     navHost.createGraph(startDestination = Outer.InnerObject::class) {}
                     navHost.createGraph(startDestination = InterfaceChildObject) {}
                     navHost.createGraph(startDestination = AbstractChildObject) {}
+                    navHost.createGraph(startDestination = OuterComp) {}
+                    navHost.createGraph(startDestination = OuterComp.InnerObject) {}
+                    navHost.createGraph(startDestination = OuterComp.InnerObject::class) {}
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expectClean()
@@ -247,11 +333,19 @@
                     navHost.createGraph(startDestination = Outer.InnerClass) {}
                     navHost.createGraph(startDestination = InterfaceChildClass) {}
                     navHost.createGraph(startDestination = AbstractChildClass) {}
+                    navHost.createGraph(startDestination = TestInterface)
+                    navHost.createGraph(startDestination = TestAbstract)
+                    navHost.createGraph(startDestination = TestClassComp) {}
+                    navHost.createGraph(startDestination = TestClassWithArgComp) {}
+                    navHost.createGraph(startDestination = OuterComp.InnerClassComp) {}
+                    navHost.createGraph(startDestination = InterfaceChildClassComp) {}
+                    navHost.createGraph(startDestination = AbstractChildClassComp) {}
+                    navHost.createGraph(startDestination = TestAbstractComp)
                 }
                 """
                     )
                     .indented(),
-                byteCode,
+                testFile,
             )
             .run()
             .expect(
@@ -286,13 +380,68 @@
 you can also pass in its KClass reference AbstractChildClass::class [WrongStartDestinationType]
     navHost.createGraph(startDestination = AbstractChildClass) {}
                                            ~~~~~~~~~~~~~~~~~~
-5 errors, 0 warnings
+src/com/example/test.kt:12: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor TestInterface(...)?
+If the class TestInterface does not contain arguments,
+you can also pass in its KClass reference TestInterface::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = TestInterface)
+                                           ~~~~~~~~~~~~~
+src/com/example/test.kt:13: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor TestAbstract(...)?
+If the class TestAbstract does not contain arguments,
+you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = TestAbstract)
+                                           ~~~~~~~~~~~~
+src/com/example/test.kt:14: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = TestClassComp) {}
+                                           ~~~~~~~~~~~~~
+src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = TestClassWithArgComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = OuterComp.InnerClassComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = InterfaceChildClassComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = AbstractChildClassComp) {}
+                                           ~~~~~~~~~~~~~~~~~~~~~~
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+Did you mean to call its constructor Companion(...)?
+If the class Companion does not contain arguments,
+you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
+    navHost.createGraph(startDestination = TestAbstractComp)
+                                           ~~~~~~~~~~~~~~~~
+13 errors, 0 warnings
             """
             )
     }
 
-    private val sourceCode =
-        """
+    override fun getDetector(): Detector = WrongStartDestinationTypeDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(WrongStartDestinationTypeDetector.WrongStartDestinationType)
+}
+
+private val SOURCECODE =
+    kotlin(
+            """
 package androidx.navigation
 
 import kotlin.reflect.KClass
@@ -321,52 +470,24 @@
     startDestination: Any,
     route: KClass<*>? = null,
 ): NavGraph { return NavGraph() }
+""" +
+                TEST_CLASS
+        )
+        .indented()
 
-val classInstanceRef = TestClass()
-
-val classInstanceWithArgRef = TestClassWithArg(15)
-
-val innerClassInstanceRef = Outer.InnerClass(15)
-
-object TestGraph
-
-object TestObject
-
-class TestClass
-
-class TestClassWithArg(val arg: Int)
-
-object Outer {
-    data object InnerObject
-
-    data class InnerClass (
-        val innerArg: Int,
-    )
-}
-
-interface TestInterface
-class InterfaceChildClass(val arg: Boolean): TestInterface
-object InterfaceChildObject: TestInterface
-
-abstract class TestAbstract
-class AbstractChildClass(val arg: Boolean): TestAbstract()
-object AbstractChildObject: TestAbstract()
-
-"""
-
-    // Stub
-    private val byteCode =
-        compiled(
-            "libs/StartDestinationLint.jar",
-            kotlin(sourceCode).indented(),
-            0x88777768,
-            """
+// Stub
+private val BYTECODE =
+    compiled(
+        "libs/StartDestinationLint.jar",
+        SOURCECODE,
+        0x8e62b385,
+        """
                 META-INF/main.kotlin_module:
                 H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgUucSTsxLKcrPTKnQy0ssy0xPLMnM
                 zxMS8Essc0ktLsnMA/O9S7hEubiT83P1UisScwtyUoXYQoCy3iVKDFoMAGXO
                 +shYAAAA
                 """,
-            """
+        """
                 androidx/navigation/AbstractChildClass.class:
                 H4sIAAAAAAAA/41QTW8SURQ9780wwBRkwC9K/ajVNpSF0MaN0TRSjAkJdtE2
                 LGD1YCb0hWEmmfcgXfJbXLsx0Zi4MMSlP8p431CNCxYu3rn33Hdy7sfPX9++
@@ -380,7 +501,40 @@
                 CTx6a6+mOS/FTOMzih832hTWghsbjqcpPsYzim/SITO4PYTVxZ0u7nap7X1K
                 Ue1iG7UhmMIOHgyRVSgpPFRwFR4pOAqeQvk3MHA2w9YCAAA=
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51Sy24TMRQ99qR5DAHSlkfC+xGkthKdpqrYFCGVVEiR0iIB
+                yqYL5MyY1smMB42dqMuu+BD+oCskFijqko9CXDsBtsDm+t5z7rnXczzff3z9
+                BmAHTxh2hE6KXCWnkRZTdSysynW0NzS2ELHtnqg06abCmG6efWy7IDQ1VMAY
+                GiMxFVEq9HH0ejiSsa0gYCg/V1rZFwzB2vqgjiWUQ5RQYSjZE2UYnvX/Z+Eu
+                Q2etP85tqnQ0mmaR0lYWWqTRvvwgJqnt5pomTGKbFweiGMtid30QgrvFq+34
+                D/k+8yzD5r9NY1j+JTiQViTCCsJ4Ng3ISOZCzQUwsDHhp8pVW5QlHYb27CwM
+                eZOHvEHZ7Kx68Slozs62+RZ7Wanyi89l3uCud5u5CRt/71AFtxlqv22iWx6K
+                6b40Vmkv2xxbcr6bJ5Lhal9peTjJhrJ4J4YpISv9PBbpQBTK1Quw3tNaFn6D
+                pPcK3+aTIpavlONabybaqkwOlFHUvKd1bv0egw6ZXXIO0Mnds9OH3KcqcpbQ
+                ubTxBdVzTz+gWPZgHw8p1ucNqCEEGoyySwvxUzr5Qlw/9/Y6wY05OBf47DKu
+                EBfgEVWhF93BXbTw2C+8h7b/3ckD6m0cIehhuYeVHlZxjVJc79HMm0dgBk20
+                iDcIDW4ZlH8CofOnlCsDAAA=
+                """,
+        """
+                androidx/navigation/AbstractChildClassComp.class:
+                H4sIAAAAAAAA/5VSzU8TQRT/zW4/lyJtRSzgBwpiqcgWQjwIIcESTZPSA5Im
+                wmnarmXodtbsTBuO/C2evRA1JJoY4tE/yvhmWyFRDnqYefPe/N7vff74+eUb
+                gHWsMZS4bIeBaJ+4kg9Eh2sRSHe7qXTIW7pyJPx2xedKVYLeuyQYw8J1+H1P
+                6UufCGkzJDaFFHqLIVY8WGow2MWlRgZxJB3EkCKdhx0GdpCBg7E0LGQIqo+E
+                Yliu/XtWGxSp4+ltQ0YhDhhSmy1/FHr933kWzMUlAZK4ybBarHUDTTzu8aDn
+                Cqm9UHLf3fHe8r5PRUri6Ld0EO7ysOuFG8PabjmYxBRD+pKM4dl/FHOVxEYG
+                BUybtswwzNeCsOMee7oZciGVy6UMdMSj3Hqg633fpzbkfme862ne5pqTzeoN
+                bBo1M1faXKCWd8l+IoxWpld7leHVxWnesQpWdC5OHSs75lipWOHidC65ZpXZ
+                c5Z8MZ5PZK0Zq2x/f5+wsrG93KWWIpeZWCqeTRg6WqrFa0v+c0soPcomV+eD
+                HfoRMkKtdDXD7F5fatHzqnIglGj63vZVwbQklaDtMUzUhPTq/V7TC/c5YRjy
+                taDF/QYPhdFHxkxVSi+MOuyRs/M66Ict76Uwf9OjOI2/omCVOh+jDlmYNoOg
+                RJ+QliB5h2Te7CxJm/R4ZF0mbYvQFkmndI50afYzxs8ihqcjT6CGFbqnhijc
+                wISZCL0MG7UCWTpDLtcMimS89AnjH66lyQwBI5oUJZUcORcQjRqZr5h8w85x
+                +yNmzyKLTcQmIKM9taLCyhF3iQoGKmS/S4z3DmFXcb+KuSoe4CE9MV/FAh4d
+                giks4vEhUgoTCkUFR2FJIaGQVcgpFH4BxvFHnF0EAAA=
+                """,
+        """
                 androidx/navigation/AbstractChildObject.class:
                 H4sIAAAAAAAA/41Sy2oUQRQ9VfPq6Yzm4SMzxkdMhBgXdhJcaRDGUaGhbcEM
                 A5JV9YOkMj3V0F0zZDkrP8Q/CC4CCjLozo8Sb5XjA8zCbvreuueeOtX3UN++
@@ -394,7 +548,21 @@
                 rZ8ENEkKcLDwe/Mqsc2z8An87TlaH7B4ZgGODRtvYdPeSPKGBFYOUfFxxcdV
                 H9dwnZZY9dFG5xCsxA2sUb+EW+JmifoP1gZuws4CAAA=
                 """,
-            """
+        """
+                androidx/navigation/AbstractChildObjectComp.class:
+                H4sIAAAAAAAA/5VSW2sTQRg9M0k2m220tV6aWO8t4gXdtvhmEWJUWNiu0IaA
+                9Gk2u7TTbGZldxL6mCd/iP+g+FBQkKBv/ijxmzFU0L64y36XM+c7s3OYHz8/
+                fwXwDOsMj4VKilwmx74SE3kgtMyV34lLXYiB7h7KLHkbH6VU5qP3dTCG9fMG
+                emmpz4Yss8LgbEsl9QuGyoOH/SZqcDxUUWeo6kNZMjwJ/2Pv5wzu9iCzih64
+                kXGDaK/Xibqvm7gAr0HgRYa1MC8O/KNUx4WQqvSFUrm2yqUf5ToaZxlJXQqH
+                uSYxfyfVIhFaEMZHkwq5wkxomAAGNiT8WJpug6pkkzaYTT2Pt7j9ZlP3+wfe
+                mk23+AZ7WXf5t48OX+KGusVw/9wD/u2V+Z1ITF4RLJWlPB1qhtXdsdJylAZq
+                IksZZ2nnz0nIwm6epAyLoVRpNB7FadETxGFYDvOByPqikKafg95ePi4G6Rtp
+                mvZcuP+PLDbJw6o9eNtYSvkWdQ7lJcqc3prtblPnG3so1x6dwj2xy3fmZGAH
+                dyk2fxPQICnAxcLZ8AqxzbPwBfzdKZqfsHhiAY57Nt7Emr2h5A0JLO+jEuBy
+                gCsBruIalVgJ0EJ7H6zEdazSegmvxI0Szi+zsivd3gIAAA==
+                """,
+        """
                 androidx/navigation/InterfaceChildClass.class:
                 H4sIAAAAAAAA/41Qz28SQRT+ZhZY2FJZqFZK/VWrtnBwaaMnTWNbY0KCbVIb
                 DnAaYKVTltlkZyA98rd49mKiMfFgiEf/KOMbSpqYcPAw773vvW++9+P3nx8/
@@ -409,7 +577,41 @@
                 brfwjPwbqt2m2p0OnAbWG7jbQBkbFKLSwCbudcA07uNBB65GUeOhRl7jkUZW
                 o6Sx9hcxw6Dy8gIAAA==
                 """,
-            """
+        """
+                androidx/navigation/InterfaceChildClassComp$Companion.class:
+                H4sIAAAAAAAA/51STW/TQBB9u07zYQJNWz4SvgtBakHUTQXiUIQEqZAspUUC
+                lEsPaGNv203sNfJuoh5z4ofwD3pC4oCiHvlRiFknwLlcZmfemzezfuufv77/
+                APAMjxieCx3nmYpPAy0m6lhYlekg1FbmRyKS3ROVxN1EGNPN0s9tF4SmjgoY
+                Q2MoJiJIhD4O3g2GMrIVeAzll0or+4rB29js17GEso8SKgwle6IMw4vef23c
+                Zehs9EaZTZQOhpM0UE6hRRLsySMxTmw308bm48hm+b7IRzLf3ez74G7zWjv6
+                R35KC5Zh62LTGFb+CPalFbGwgjCeTjyykrlQcwEMbET4qXLVNmVxh6E9m/o+
+                b3KfNyibTavnX7zmbLrDt9mbSpWffy3zBne9O8xNeHIBiyq4xVD76xNd80BM
+                9qSxShe6rZEl77tZLBmWe0rLg3E6kPlHMUgIWe1lkUj6IleuXoD1UGuZFxsk
+                vZj/IRvnkXyrHNd6P9ZWpbKvjKLm11pntthj0CG3S84COrl7ePqSe1QFzhM6
+                lx5/Q/WsoO9TLBdgiHWK9XkDavCBBqPs0kL8lE6+ENfPCn+d4PocnAuK7DKu
+                EOfhAVV+IbqNO2jhYbHwLtrFH08eUG/jEF6IlRCrIdZwlVJcC2nmjUMwgyZa
+                xBv4BjcNyr8BSdfj0S4DAAA=
+                """,
+        """
+                androidx/navigation/InterfaceChildClassComp.class:
+                H4sIAAAAAAAA/5VSW08TURD+zrb0RpG2KJbiBQS1FGELYkyEkGCNZpNSEyRN
+                hKfT9lBOuz1rdk8bHvktPvtC1JBoYoiP/ijjnKXCgySGh52Zb3bmm8uZX7+/
+                /QCwhjWGRa5avidbR7biA9nmWnrKdpQW/gFvisqhdFsVlwdBxet9iIMxZDp8
+                wG2Xq7b9ttERTR1HhGH2KppdEegLqjhGGGIbUkm9yRAt7i3UGSLFhXoacSRT
+                iCJFmPttBraXRhpjSVi4QaH6UAYMS9VrdLpOpdpCbxk2qrHHkNhousPaz65B
+                NG8EVxQRxy2GlWK162kisjuDni1NjuKu/Uoc8L6rK54KtN9vas/f5n5X+Ovn
+                091OYRJ5huQFGcPz64xz2cV6GgVMm83cYZiren7b7gjd8LlUgc2V8nRIFNg1
+                T9f6rkuLyP5teVto3uKak8/qDSJ0AcyIpBGgrXfJfyQNKpPVWmF4c3acS1l5
+                K/zOjlNWZjRlJaL5s+OZ+KpVZi9Y/OVYLpaxClY58vNjzMpEd7IXKEEphWhi
+                JBMzdKum3/9eCfVGrWRrfPCK3FKFIctdzTC901da9oSjBjKQDVdsXU5LR1Lx
+                WoJhvCqVqPV7DeHvcophyFW9Jnfr3JcGD51pRynhh+sVlJx65/X9pngtzb+p
+                YZ36P1WwQmuPUnsx0lPmHcheonXFSN8jnTNXSzpCOI4EyWVCmxRtkU6VTjFa
+                mv6K8RNCFuxhJuCgTHLyPAoZZM2DkGXYaBnEOzHkss07kR4pfcH4pytp0ucB
+                Q5oEbiI5TM4jfGmkv2PyPTvF1GfcPQk9ERrNFGRhEwUabjXkfoKnpCvkv0+M
+                M/uIOJh18MDBHObJxEMHj/B4HyxAEQv7SATIBigFSAdYDAzMBZgIUPgDTG80
+                x3MEAAA=
+                """,
+        """
                 androidx/navigation/InterfaceChildObject.class:
                 H4sIAAAAAAAA/41SS2/TQBD+dpMmjmtoWl4J5VXaotIDbivEhQqpBJAsBSPR
                 KBLqaRMv6SbOWrI3Vo858UP4BxWHSiChCG78KMSsCeUAEtjaeXwz83lm1t++
@@ -424,7 +626,7 @@
                 Fn8rwyUiuHyEUoArAa4GVNogE80A17F6BJbhBm5SPIOX4VYG5wfApo4N6gIA
                 AA==
                 """,
-            """
+        """
                 androidx/navigation/NavController.class:
                 H4sIAAAAAAAA/4VRu0oDQRQ9d2I2ukZNfMYXGGzUwlWxUwSNCIGooJLGapId
                 4pjNDOxOgmW+xT+wEiwkWPpR4t01vc3hPGbuHO58/3x8AjjGJqEqTRhbHb4E
@@ -436,7 +638,7 @@
                 fwcwBT/L1zNcwUb2XYRpzoqPyNUxU8dsHXMoMUW5jnksPIISLGKJ8wR+guUE
                 3i+86bUs6wEAAA==
                 """,
-            """
+        """
                 androidx/navigation/NavDestination.class:
                 H4sIAAAAAAAA/4VRO08CQRD+ZoEDTpSHiuAjUWOhFh4SO42Jj5iQICZqaKwW
                 7oLLYy/hFkLJb/EfWJlYGGLpjzLOnTRWNl++x+zMZPbr+/0DwAm2CLtSu0Nf
@@ -448,7 +650,7 @@
                 HeUbEa5hM/owwgJnmSfEalisYamGLHJMka+hgOUnUIAVrHIewA5QDGD9AKYj
                 0APtAQAA
                 """,
-            """
+        """
                 androidx/navigation/NavDestinationKt.class:
                 H4sIAAAAAAAA/61WbVPbRhB+zsbYCGOECW8GDAGHGGgQEJI0hZJSaILKS1Kg
                 pJS26WGEEQgpo5OZdDrT5lP/Q7/2FyTlQzplppPJt/ZHdbqnCgw2BibDB5/2
@@ -479,7 +681,7 @@
                 N0/6h7Qfj9YR1jGrQ9fxOeZ0zGNBxyIer4MJPMEX67gmMCAwKJAV6BcYppMt
                 MCSgCdwVuCcwJnBHICKwJNApkBRYFugS6Bbo+w//HMZ8gQ0AAA==
                 """,
-            """
+        """
                 androidx/navigation/NavGraph.class:
                 H4sIAAAAAAAA/31SXU8TQRQ9s6XbbUFaQBAq+AEoBZStxCdLSPwIWlOr0qYv
                 PE3bSZl+zJqdacNjf4v/wCeND6bx0R9lvLOtCkHcZO6de+65d87OnR8/v34D
@@ -496,7 +698,7 @@
                 3CW6LV8aUybldpfGDcrvROxr2LWYFTEbAQ8iex8PyR8Rukwnr5wgVkS2iJtF
                 rGKNtrhVxG3cOQGzv7Z+gqRGSmNDw9WY1tjUuBfZtMbML5qRw8f+AwAA
                 """,
-            """
+        """
                 androidx/navigation/NavHost.class:
                 H4sIAAAAAAAA/31OTUvDQBB9s9F+xK9ELVTEv2Da4s2TUMRAVVDwktO2Wcs2
                 6S50t6HH/i4P0nN/lDiJd2fgzZt5w5vZ/3x9A7hDj3AtTb6yOt8kRlZ6Lr22
@@ -505,7 +707,7 @@
                 m0a8LTwhfLfr1Uw96lIRrt7Wxuul+tBOT0v1YIz1zapr8RUc4C8ELho8xyXX
                 ITsfcrYyBCnaKTopugiZ4ijFMU4ykMMpzjIIh8gh/gUZbPE0RgEAAA==
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerClass.class:
                 H4sIAAAAAAAA/41U30/bVhT+rp0fjgngAG35kbXdyFgS2jqwdusK7QZ0DLMQ
                 OpjQOvZySbxgCDazHdS9TDz1T6i0vUyapj3x0EobTKtUsfZtf9M07VzbTbrQ
@@ -533,7 +735,7 @@
                 NjBt4LaBO/iQVHxkYAaz62Ae5nB3Hb2eeD72oAZrwoPmIeOhz0O/hxuB8aYH
                 3UOW9H8BSGQIivsHAAA=
                 """,
-            """
+        """
                 androidx/navigation/Outer$InnerObject.class:
                 H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQdFXsoUxBXEBFHjkFKMJRhl
                 dWlHGGhndOa2YcnKnVsXLl24YiFxQaKJqRI3/ijiudNREKIxac/5zrnnNd+5
@@ -553,7 +755,7 @@
                 0f8RFw9Ch4YZkopHjgm0Eau3wn6T9C1SozFcInqG1qFZuGzhikVPd40ghi1k
                 MbIOFuA6RtdhBOo3FiARoCsEPQEyIUiT/AmvDPuL4QQAAA==
                 """,
-            """
+        """
                 androidx/navigation/Outer.class:
                 H4sIAAAAAAAA/4VRW2sTQRT+ZjaXzSbaNF6aWFsvTbWp4rbFp1qEGhUW0hRs
                 CUieJskQJ9nMwu4k9DFP/hD/QfGhoCBB3/xR4tltNA9S3GHPN+f2nTnn/Pz1
@@ -568,7 +770,88 @@
                 ySsUHX/5r+DvL1D4jKXzxGBhi2SJ3PfpX6N3PCRcJ6wlJTawTbhPNMtEXGrD
                 8nDDw00Pt3CbrljxUEalDRbhDlbbSEdwItyNkImwFmH9NzJGDiwjAwAA
                 """,
-            """
+        """
+                androidx/navigation/OuterComp$InnerClassComp$Companion.class:
+                H4sIAAAAAAAA/51TTW/TQBB9a6dxYkJJUz4SoJRCgBRB3VQIIRUhQapKkdJW
+                giqXHtAmWcom9hp511GPOfFD+Ac9IXFAUY/8KMSsE6i4IJXLzJt582a0M/aP
+                n9++A3iGBsNzrgZJLAcngeJjecyNjFVwkBqRtOLoU72tFKGQa52F1nBFJR4Y
+                Q3nIxzwIuToODnpD0TceXIb8S6mkecXgNta7JSwg7yMHjyFnPkrN8KLzfyO3
+                GZqNzig2oVTBcBwFUpFE8TDYER94GppWrLRJ0r6Jkz2ejESyvd714djRy/X+
+                Ofk+yliGjYt1Y1j6LdgThg+44ZRzorFLy2TWFK0BAxtR/kTaaJPQoMlQn058
+                36k6vlMmNJ0Uzj671elky9lkb7yCc/Yl75QdW7vFbIcnF9mRh1sMK/9UeFhh
+                WPxbxlD8s1x62z4f7whtpMqkGyNDF2vFA8FwpSOV2E+jnkgOeS+kTKUT93nY
+                5Ym08TxZOm8v6M7+uzhN+mJXWq72NlVGRqIrtaTi10rFJpuj0aQT5ezeyDv2
+                c6Hn36MosIskv/D4KwqnGX2fbD5L7qJOtjQrQBE+UGaELs3FT8k7c3HpNDuK
+                FVyfJWeCDF3GInEuHlBUIfY27mAVtQzdJf8wG7yGR9kPQ7sgTfkIbhtLbVTa
+                WMZVgrjWpt43jsA0qqgRr+Fr3NTI/wIu2QAobQMAAA==
+                """,
+        """
+                androidx/navigation/OuterComp$InnerClassComp.class:
+                H4sIAAAAAAAA/5VUW08bVxD+zvq2XgysIRcHSJMWNzWGsECTlAJpuJelXFJI
+                aQjtw8HemgV7l+6urfSlylN+QqT2pVIf+sRDorZQFamiyVt/U1V1zu5ibhES
+                kn3OzOzMN9+ZmXP++e/PvwDcwdcMPdwqOrZZfKpZvGaWuGfalrZY9Qxnwq5s
+                Z3XLIqnMXVeoCTAGdZPXuFbmVklbXN80Cl4CEYb4iGmZ3icM0ZzetcIQyXWt
+                pBBDQkEUMoNsCqQxp8TA9BQUNCQhIUX+3obpMvTOXYTIMENDyfD0Oial0xmU
+                An2zLcPy+gm4YG9/x9BPfC6K3TlnOyVt0/DWHW5arsYty/b8KFdbsL2Fark8
+                LA4XV+gMVxhSIlW2aHzDq2WPwcldLKGuz52u6fAFOafQikuCTRuV2rOXPce0
+                qCyXcl3HoAMrne/qadt41SwXDSeBdxTcEO3KnMTPHXbvvox3qdl8e9uwigy3
+                c2fhz2YM0YlkJ7IiwfsMHaIt5zl+IBxzwnHifMe8cOxOoQPXhXSbCrDB3Y0J
+                u2gwpI8idcszSuKMfcGQ0hRqGFDQjw/pRMa3VV6mObyce0svnjBkzxsJmge+
+                XjaosjHb2zAchpazKMRrpFAOb8m9i3Q3KxZukUsCw2Ki57Zsj5C0zVpFM+lY
+                jsXL2mQwfhPEyHOqBc925rmzRUUKLuJ9BSOgzMk6GMPghYbsiAbVfRRj4gKP
+                U4kP2cwbHi9yjxNFqVKL0AvDxJIUC+jab5H9qSk06oBUpCu6c/DspiJlJEVS
+                D54p9JNUWZHkOO0NtEdobyKz/Pq5nCHX5gGpjw2x5vHGlrgqtUl9kdc/xyU1
+                OptUE0KbefM8MtuqyiQfPBuQZSlwIjMjc5JkZUBWG9qiGdbHZt68iFBgKvB4
+                wUhuJLlJyEvpOrxM+duickyNC84DTJzk+rlVS+AhQ9PJ0lGVFnht0nA90/Ld
+                e7fonWhfqlqeWTF0q2a6Jg3Q2NFQ0YwGE9w8Z1rGQrWybjiPxJCJ2bILvLzC
+                HVPoobFx2eOFrXm+HerZ09gPucMrBnE8kSR1xNMgVVm2q07BmDYFxLUQYuUM
+                ObozEj3roPWamASqySPS4rRfpr1FPO+i86THfOsXpE2Tt0S7kt9DMt/+Oxpf
+                +QgrtDZBjMUkYU5R1CS+JO1K4E3fmsUAkSRQqZJQ6R9gamKuaI/lf0PjTh0u
+                7hunfJhU4BDCpIncYXDn6WD21gB6WAlWBPQTS8EpuQ9ptX0PV1/WgwKyyTrZ
+                ZEj2WFnUJDJUriD3rbCA6Y7o9z9AFgxG8u27aA8gH9MaARMI9KyF6YdoF9Q6
+                9nFjdQ83W97bxS0RuYsutWsXPbvofXnqGB0ho+PtYVS2dJ1HUAOfwR+4c7oM
+                chjPcBf3Qh5f0S7alc13/4JYdKf7b0g/IhbZ6T6ANC+Aeuj/k7BEg5489tsX
+                Scj/Ip0g/ahi2XrFshjEx5RnleSEIPWRn34IiZBqxk9KxPYxssr28OBXTLzy
+                LRE88adOzNfnWKIij5A0Svuan36ZKINkRpMVw9QaIjqmdXyqYwY6iZjV8Rnm
+                yMHFPBbWoLpodrHoQvHXuCssaRctLlpd3PWNgy40Fx2+PPo/FBJ91lIJAAA=
+                """,
+        """
+                androidx/navigation/OuterComp$InnerObject.class:
+                H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQeoLVWmoK4gJoAah5RihGCU
+                1aUdy0A7gzO3DUtW/gQXLl3ohoXEBYkmpsrOH2U8dxgFIRKT9t7vnHvO+c58
+                5878+Pn5K4C7uMeQE27F95zKjumKplMV0vFcc6khbX/eq2+PWa5r+0vrm3ZZ
+                JsEYMpuiKcyacKvmb6/GkJhxXEfeZ9CyudU2xJEwEEOSISY3nIAhX/xvlmkG
+                XXrL0nfcKkNvNlc8ZjzyUsRo0fOr5qYt133huIEpXNeTYdHALHmy1KjVKCp9
+                oqyODiq8IYKNea9ih41amvuhMEPN268aokZdXsgWTz/ddO4Fw9h5bEQl1ms2
+                0cU9uWH7DN1nqxD1TLkWamSAK2F0q7S8Mluaf9iGIRgpcg4zdBW3PElh5qIt
+                RUVIQYm83tRoVkwtKbWAgW2Rf8dRVoFQZZLhZWt3xOAD3OCZ1q7BdQXS0a4b
+                ypXp0A9fGwOt3SleYHNJnX9/l+AZvtCT0YZ4ITalZ+JDsQFWYI8P32gLqUyC
+                vEnCjLBOOKWwYptiqodL5040iRw9Tkk0H9iBdNzwdGJLMgw/bbjSqduW23QC
+                h4SbPRaTrsvRcDqLjmuXGvV1219R4ipNvbKorQrfUXbkbF+Wory1KLYje+x0
+                7SfCF3WbWvqLpC28FvM1EQQ2mcay1/DL9iNHlRiMSqyeaQ6TNKNYKP+gGhnt
+                t8hK0N5Oe5xO46F1myxTDUl5xw+g7xPgmIiCgTk6BtqOApCiUqpomjw8TL4a
+                JWvdnR/Do+NwLQo/yUzvJLoi3uPU7r1/pDL0oDdismjntPeP598jHtvLfwN/
+                i7i2l2+BP4vthY0XaI2BJ/WwWN9RQlRMoT76M1IH6lrTS0RAx8AfKfrDBCD9
+                Bfz5AQY/4eJ+6NAwRavSkWMcHaTqnZAvT98l1RpdMZJnZA2ahcsWrlj0dNcI
+                YtTCGK6vgQW4gZtrMAL1ywZIBOgJQV+ATAjStP4Cqe39ku0EAAA=
+                """,
+        """
+                androidx/navigation/OuterComp.class:
+                H4sIAAAAAAAA/41RW2sTQRT+ZjaXzSa2abw0sbZVW7Wp4rbFp1qEGBUWYgq2
+                BCRPk2SJk2xmZWcS+pgnf4j/oPhQUJCgb/4o8ew2WkQo7rDnm3P7zpxzfvz8
+                /BXAEzxkWBWqF4Wyd+IqMZF9YWSo3MOx8aN6OHqfBWMoDsREuIFQffewM/C7
+                JguLIXMglTTPGKytaquANDIOUsgypMw7qRnWG5cyP2WwD7pBwuGAx4m21zw6
+                rjXrLwu4AidHxgWGjUYY9d2BbzqRkEq7QqnQJFzabYamOQ4ColpqDENDZO5r
+                34ieMIJsfDSxqEsWi1wswMCGZD+RsbZDt94uQ3U2LTi8zB1enE0dblv29w+8
+                PJvu8R22z63U86zNv33M8CKPE/ZYTLPgKUVtBELruBeGfGI4nw7Do0s73/w7
+                OYt1esR/ZPye/R1qtykmL3xtpEoiHw+p6MqbsTJy5HtqIrXsBH7tYlK0lHrY
+                8xkWG1L5zfGo40fHgmIYSo2wK4KWiGSsz42Fiyf6lOwcheOo67+Ssa8yr9P6
+                pwp2aWWpZM6VeIOEm6RlCIuEnE460e6R5sbbIExvn8E+Tdz358FADQ9IFs4D
+                kCMqwEb+T/IyRcdf/gv42zMUPmHxNDFY2CJZIvdt+lfpHXcJ1wirSYkNbBPu
+                E80SEZfasDxc9XDNw3XcoCuWPZRRaYNp3MRKG2kNR+OWRkZjVWPtF1d0yO47
+                AwAA
+                """,
+        """
                 androidx/navigation/TestAbstract.class:
                 H4sIAAAAAAAA/4VRy0oDMRQ9SduxHau29dX6AHUh6sLR4kJQhKoIA7WCSjeu
                 0s6gsdMMTNList/iH7gSXEhx6UeJN2P3bg7nkdwcbr5/Pj4BHGGdYUOoIIll
@@ -580,7 +863,38 @@
                 CIt/B1CAm+arKS5jLf0shmnKig/I+JjxMetjDiWiKPuoYP4BTGMBi5RruBpL
                 Gs4vELgJXekBAAA=
                 """,
-            """
+        """
+                androidx/navigation/TestAbstractComp$Companion.class:
+                H4sIAAAAAAAA/5VSy27TQBQ9M07zMAHclkfCmzZILRJ1UrErQiqpkCLSIkGV
+                TRdo4gxlEnuMPBOry6z4EP6gKyQWKOqSj0LccQJs6ea+zj33+p7xz1/ffwB4
+                jicMO0KPslSNzkItcnUqrEp1eCyN3R8am4nIdtPkc8sZoQmqgDEEY5GLMBb6
+                NHw7HMvIVuAxlF8orexLBm9re1DHCso+SqgwlOwnZRja/cut2mPobPUnqY2V
+                Dsd5EiptZaZFHB7Ij2IaU7sm3jSyaXYosonM9rYHPrhbud6K/oEfkgKlWy83
+                jWH1D+FQWjESVlCNJ7lH4jFnas6AgU2ofqZc1qZo1GFozWe+zxvc5wFF81n1
+                4ovXmM92eZu9qlT5xdcyD7jr3WVuQut/tKngLkPtr0D0fUciP6AmpQvCzsSS
+                2t10JBmu95WWR9NkKLNjMYypstZPIxEPRKZcvizWe1rLrBsLYyS9kf8+nWaR
+                fK0c1nw31VYlcqCMouZ9rVNb7DHokMwldzt57p6aTnhIWejEIL/y9Buq5wX8
+                iGy5KL7BY7L1RQNq8IGAUXRlSX5Gni/J9fNCWEe4tSguCEV0FdcI87BBmV+Q
+                7uE+mtgsFj5Aq/i5SQPqDU7g9bDaw1oP67hBIW72aObtEzCDBpqEG/gGdwzK
+                vwGgwqXcGQMAAA==
+                """,
+        """
+                androidx/navigation/TestAbstractComp.class:
+                H4sIAAAAAAAA/41RW28SQRT+ZpfrulioN7BeWotI+9CljYmJNCaVxoRIMdGG
+                xPA0wIgDy6zZGUgf+S3+g8aHJpoY4qM/ynh2i+2DL7ycb87tO9858/vP958A
+                nmOXoczVIAzk4MxTfCaH3MhAeadCm6OeNiHvm0Yw+ZIGY8iP+Ix7PldD711v
+                JPomDZshdSiVNK8Y7OpOx0USKQcJpBkS5rPUDJXWKgPqDJnDvr+k2lulpRwZ
+                riiVhsuwX22NA0MM3mg28aQyIlTc947FJz71qUFR57RvgvCEh2MR1i/F3nSQ
+                wxpD9oqMobaS4uvxdRcFrGdh4RbDdisIh95ImF7IpdIeVyowMYP22oFpT32f
+                di3803oiDB9wwylmTWY2fQqLTDYyYGBjip/JyKvRa7BP91zMXccqWo6VX8wd
+                K2NlKsXFfNM+sGrsJbNfJ399TVl5K6o+YBFHoc1nxyReqljG3tgwbLyfKiMn
+                oqlmUsueL46uZdLPNYKBYFhrSSXa00lPhKecahjWW0Gf+x0eyshfBt2mUiJs
+                +FxrQc3Oh2Aa9sUbGeVKyzmd/6YktuheiXjJUnQ+wm3yUoR3CC3CZOyVyfOi
+                UxAmdy+QOY/TT5fFwFtUyLqXBcjCIczgxlVzEfEx4f5A7iO7QP4bbp/HERvP
+                yDpUlyPGAgmpxtxPsEP4guJ3ifFeF3YTxSZKTdzHBj3xoImHeNQF03iMzS4S
+                Go7GlkZKo/AX8BZ2A10DAAA=
+                """,
+        """
                 androidx/navigation/TestClass.class:
                 H4sIAAAAAAAA/31Ru04CQRQ9d4BFVlTAF/hs1cJFYqcxUYwJCWKihsZqYDc4
                 sMwmzEAs+Rb/wMrEwhBLP8p4d6W2OTmPO/femfn++fgEcIpdwq7U/ihS/oun
@@ -592,7 +906,38 @@
                 ybcT3MRO8kmERc7yT0g1sNTAcgMrKDBFsYESVp9ABmtY59zANdgwcH4BpKME
                 iuEBAAA=
                 """,
-            """
+        """
+                androidx/navigation/TestClassComp$Companion.class:
+                H4sIAAAAAAAA/5VSTW8TMRB99qb5WAJsWz4SvtsGqQW121TcCkiQCilSWiSo
+                cukBOYkpTna9aO1EPebED+Ef9ITEAUU98qMQYyfAEfUynnnz3oz3eX/++v4D
+                wDM8Zngq9CDP1OAs1mKiToVVmY6PpbGtRBjTytLPDReEJrwExhANxUTEidCn
+                8dveUPZtCQFD8bnSyr5kCDa3ulUsoRiigBJDwX5ShmG7c4k9+wzNzc4os4nS
+                8XCSxkpbmWuRxAfyoxgntpVpY/Nx32b5ochHMt/f6obgbt9qo/+v+SH1XYad
+                y01jWP4jOJRWDIQVhPF0EpBtzIWKC2BgI8LPlKt2KRs0GRqzaRjyGg95RNls
+                Wr74EtRm0z2+y16Xyvzia5FH3HH3mJuw9l9jSrjLUPnrDl3uSEwOiKG0Z++M
+                LPncygaS4XpHaXk0TnsyPxa9hJCVTtYXSVfkytULsNrWWuZ+g6TXCd9n47wv
+                3yjXq78ba6tS2VVGEfmV1pn1ewya5HHBfTid3D0y3f8hVbFzgs6lJ99QPvft
+                RxSLHnyBNYrVOQEVhEDEKLuyEG/TyRfi6rl31QluzcG5wGdXcY16AdapCr3o
+                Hu6jjg2/8AEa/p8mD4gbnSBoY7mNlTZWcYNS3GzTzNsnYAY11KlvEBrcMSj+
+                BuBNy2UQAwAA
+                """,
+        """
+                androidx/navigation/TestClassComp.class:
+                H4sIAAAAAAAA/4VRXWsTQRQ9s5vPdWOT+pVYta2NmlZ0myIIpgqaIiykEbQE
+                JE+TZIyTbGZlZxL6mN/iPyg+FBQk+OiPEu9uY/vgQ1/umXvn3HPP3Pn95/tP
+                AM+ww7DJ1SAK5eDYU3wmh9zIUHlHQptmwLVuhpMvWTCG4ojPuBdwNfTe9Uai
+                b7KwGTL7UknzisGubXdcpJFxkEKWIWU+S82w1bpUvcGQ2+8HS53Hl/KrceCK
+                6lm4DPVaaxwaavdGs4knlRGR4oF3ID7xaWCaodImmvZNGB3yaCyixpnNqw4K
+                WGHIn4sxPLnc68XshosSVvOwcC1+ZRgNvZEwvYhLpT2uVGiSdu21Q9OeBgG9
+                svTP6KEwfMANp5o1mdn0ESwO+TiAgY2pfizjbJdOgzpDdTF3HatsOVZxMXes
+                nFVezDfsPWuXvWD2m/SvrxmraMXcPRYrlNp8dkC+pUpMPB0bhrX3U2XkRPhq
+                JrXsBeL1hUn6rmY4EAwrLalEezrpieiIE4dhtRX2edDhkYzzZdH1lRJRshVB
+                zc6HcBr1xVsZ31WWczr/TUGdtpVKnliJl0e4RVmG8AahRZhOsiplXrwIwvTO
+                KXInyfWDJRl4iYcU3TMC8nAIc7hy3lxGskq4P1D4yE5R/IbrJ0nFxiOKDvEK
+                pFgiI7VE+z62CZ9T/SYp3urC9lH2UfFxG2t0xB0fd3GvC6axjo0uUhqOxqZG
+                RqP0F+8+XexPAwAA
+                """,
+        """
                 androidx/navigation/TestClassWithArg.class:
                 H4sIAAAAAAAA/41QTWsTURQ9781kkoyJmcSvNFWrtpQ2Cyct7pRijAgDsUIt
                 cZHVSzKkr5m8gXkvocv8FtduBEVwIcGlP0q8b1JcuRBmzr3nvsO5H79+f/8B
@@ -606,7 +951,40 @@
                 b7xCezqKhfYXVD/906ayEVzbcDzJ8SF2Kb7Mhyzg1hBOhNsR7kTU9h6laEbY
                 QmsIprGN+0MUNWoaDzT8HD2NQKP+B9uGEJmeAgAA
                 """,
-            """
+        """
+                androidx/navigation/TestClassWithArgComp$Companion.class:
+                H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSlkfC+xGkFIlOE3VXBCqpkCKlRaJV
+                WHSBnMS0TmY8yHaiLrPiQ/iDrpBYoKhLPgpxPQmwpZvre8+5597x8fz89f0H
+                gG08Y2gJPTSpGp5FWkzViXAq1dGRtK4dC2s/KHe6a07aafK57oPQRBfAGCoj
+                MRVRLPRJ9K4/kgNXQMCQf6m0cq8YgsZGr4wV5EPkUGDIuVNlGba7l1+3w9Bs
+                dMepi5WORtMkUtpJo0Uc7clPYhK7dqqtM5OBS82+MGNpdjZ6Ibhfu14f/CM/
+                JhnLsHm5aQyrfwT70omhcIIwnkwDMpH5UPIBDGxM+Jny1RZlwyZDfT4LQ17l
+                Ia9QNp8VL74E1fmsxbfYm0KRX3zN8wr3vS3mJzT+158C7jKU/ppE33ggpnvU
+                qHQm2hw7cr2dDiXD9a7S8mCS9KU5Ev2YkLVuOhBxTxjl6yVY7mgtTbZI0luF
+                h+nEDORb5bna+4l2KpE9ZRU172qdumyPRZOszvn708n9k9M1HlIVeUPoXHn+
+                DcXzjH5EMZ+Br/GYYnnRgBJCoMIou7IUv6CTL8Xl88xcL7i1ABeCLLuKa8QF
+                eEJVmInu4T5qeJotfIB69qOTB9RbOUbQwWoHax2s4waluNmhmbePwSyqqBFv
+                EVrcscj/BkiAeOUlAwAA
+                """,
+        """
+                androidx/navigation/TestClassWithArgComp.class:
+                H4sIAAAAAAAA/41SW08TQRT+ZnvZ7VpkWxELeEFBLVXZQngSgsEa4yalJkhq
+                DE/TdizTbmfN7rThkd/isy9EDYkmhvjojzKe3VZ40AeS3XPmnDnn+85lfv3+
+                9gPABtYYylx1wkB2jlzFR7LLtQyUuy8iXfN5FL2V+nAn7NaCwQcTjMHp8RF3
+                fa667utWT7S1iRRDdksqqbcZ0mVvpcmQKq8088jAtJGGRTYPuwzMy8PGlRwM
+                5ClUH8qIoVK/LP8m8XSF3omhiMBjsLba/oR4/bIoy7Hgiq5NXGNYK9f7gSYU
+                tzcauFJpESruuy/Eez70dS1QkQ6HbR2Euzzsi3Bz3Nd1GzOYZcidgzFsXLqR
+                ixI28yhhLh7IPMNSPQi7bk/oVsililyuVKATlMhtBLox9H0aQeFvvbtC8w7X
+                nHzGYJSidbJY5GIBGnaf/Ecytqp06tCmX50dF22jZNiGc3Zs02c4lm1Y6dLZ
+                8aK5blTZU2Y+nypmHWPeqKZ+fswaTnqvcG5ZlDKftjJONsZbZzFLocFHL6hF
+                qZJCV/uaYWFvqLQcCE+NZCRbvti5aITWXgs6gmG6LpVoDActEe5zimEo1oM2
+                95s8lLE9ceY9pUSYDFBQsv0mGIZt8VLGd3MTnuY/LFijiaapcwNz8YCp0ApZ
+                WdI3SRfjV0g6RXYm8T4ia5uiDdJ25RS5ysJXTJ0kCI8nmcAzPCE5O47CVUzH
+                k6ZTjEajgEP/GMuNF0A6U/mCqU//hcmPAyYwFhVlTpJLSFaI/HfMvGOnuPEZ
+                CyeJJ4XVhJDR6zOSxtwEewVV0jXy3yLE2wdIebjjYdHDXdyjI5Y8LOP+AViE
+                B3h4ACvCdIRyBDuR2QhOhEKE0h8DM7acGQQAAA==
+                """,
+        """
                 androidx/navigation/TestGraph.class:
                 H4sIAAAAAAAA/31SXWsTQRQ9M0k2m220af1oYq1Vmwf1wW2Lbxah1g8W1hVs
                 CJQ+TbJDOslmVnYnSx/z5A/xHxQfCgoS9M0fJd5Zgz4IzsC995w5c5h7mR8/
@@ -620,7 +998,7 @@
                 YuXP5Q1S27XyBfzkEs1PWL0oCY77ZdzCTvmZaDZksH6KSoBrAa4HuIGbVGIj
                 QBudU7Act7BJ5zm8HLdzOL8ATyx+j4kCAAA=
                 """,
-            """
+        """
                 androidx/navigation/TestInterface.class:
                 H4sIAAAAAAAA/4VOTUvDQBB9s9GmjV+JWqhH8W7a0psnQYRAVVDxktM22ZZt
                 0g10t6HH/i4P0rM/SpzEH+AMvHkz83gz3z+fXwAm6BOupcnXlc63sZG1Xkin
@@ -630,7 +1008,7 @@
                 Llo8xyXXETsfcnZSeAn8BN0EPQRMcZTgGCcpyOIUZymERWgR/QKEKxfgUgEA
                 AA==
                 """,
-            """
+        """
                 androidx/navigation/TestNavHost.class:
                 H4sIAAAAAAAA/4VRu04CQRQ9d3koKyrgA/BF7NTCBWKnMfERIwlqooaGamA3
                 OLLMJsxAKPkW/8DKxMIQSz/KeHcxxsLEYk7O487MnTsfn69vAA5QIpSEcvuB
@@ -642,7 +1020,7 @@
                 mVhYZ0xGZh4bjOlpAeaYhflmhEVsRf9MmOdsoYlYDYs1ZGrIIscUSzUsY6UJ
                 0lhFnnONtEZBY/YLEThWOyQCAAA=
                 """,
-            """
+        """
                 androidx/navigation/TestObject.class:
                 H4sIAAAAAAAA/31S0WoTQRQ9M0k2m220aW1tYrVWW0R96LbFN4tQq8LCuoIN
                 AenTJDvESTazsDtZ+pgnP8Q/KH0oKEjQNz9KvLNGfRCcgXvvOXPmMPcy3398
@@ -656,10 +1034,4 @@
                 ILVdS5/B312heYnli5LguF/GO9gpvxPNhgxWz1AJcCPAWoB13KQSGwHa6JyB
                 5biFTTrP4eW4ncP5CW9bciiLAgAA
                 """
-        )
-
-    override fun getDetector(): Detector = WrongStartDestinationTypeDetector()
-
-    override fun getIssues(): MutableList<Issue> =
-        mutableListOf(WrongStartDestinationTypeDetector.WrongStartDestinationType)
-}
+    )
diff --git a/navigation/navigation-runtime/api/2.8.0-beta06.txt b/navigation/navigation-runtime/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..b801964
--- /dev/null
+++ b/navigation/navigation-runtime/api/2.8.0-beta06.txt
@@ -0,0 +1,246 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActivityKt {
+    method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+  }
+
+  public final class ActivityNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+  }
+
+  @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+    ctor public ActivityNavigator(android.content.Context context);
+    method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+    method public androidx.navigation.ActivityNavigator.Destination createDestination();
+    method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+  }
+
+  public static final class ActivityNavigator.Companion {
+    method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String? getAction();
+    method public final android.content.ComponentName? getComponent();
+    method public final android.net.Uri? getData();
+    method public final String? getDataPattern();
+    method public final android.content.Intent? getIntent();
+    method public final String? getTargetPackage();
+    method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+    method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+    method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+    method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+    method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+    method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+    property public final String? action;
+    property public final android.content.ComponentName? component;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final android.content.Intent? intent;
+    property public final String? targetPackage;
+  }
+
+  public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+    method public int getFlags();
+    property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+    property public final int flags;
+  }
+
+  public static final class ActivityNavigator.Extras.Builder {
+    ctor public ActivityNavigator.Extras.Builder();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+    method public androidx.navigation.ActivityNavigator.Extras build();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, kotlin.reflect.KClass<?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public androidx.navigation.ActivityNavigator.Destination build();
+    method public String? getAction();
+    method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? targetPackage;
+  }
+
+  public final class ActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void activity(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class ActivityNavigatorExtrasKt {
+    method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+  }
+
+  public class NavController {
+    ctor public NavController(android.content.Context context);
+    method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @MainThread public inline <reified T> boolean clearBackStack();
+    method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+    method @MainThread public final boolean clearBackStack(String route);
+    method @MainThread public final <T> boolean clearBackStack(T route);
+    method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+    method @SuppressCompatibility @androidx.navigation.NavDeepLinkSaveStateControl public static final void enableDeepLinkSaveState(boolean saveState);
+    method public inline <reified T> androidx.navigation.NavBackStackEntry getBackStackEntry();
+    method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+    method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+    method public final <T> androidx.navigation.NavBackStackEntry getBackStackEntry(T route);
+    method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+    method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+    method public androidx.navigation.NavDestination? getCurrentDestination();
+    method @MainThread public androidx.navigation.NavGraph getGraph();
+    method public androidx.navigation.NavInflater getNavInflater();
+    method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+    method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+    method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+    method @MainThread public void navigate(android.net.Uri deepLink);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(@IdRes int resId);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final void navigate(String route);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+    method @MainThread public final <T> void navigate(T route);
+    method @MainThread public final <T> void navigate(T route, optional androidx.navigation.NavOptions? navOptions);
+    method @MainThread public final <T> void navigate(T route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final <T> void navigate(T route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+    method @MainThread public boolean navigateUp();
+    method @MainThread public boolean popBackStack();
+    method @MainThread public inline <reified T> boolean popBackStack(boolean inclusive, optional boolean saveState);
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+    method @MainThread public final <T> boolean popBackStack(T route, boolean inclusive);
+    method @MainThread public final <T> boolean popBackStack(T route, boolean inclusive, optional boolean saveState);
+    method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @CallSuper public void restoreState(android.os.Bundle? navState);
+    method @CallSuper public android.os.Bundle? saveState();
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+    property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+    property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+    property public androidx.navigation.NavDestination? currentDestination;
+    property @MainThread public androidx.navigation.NavGraph graph;
+    property public androidx.navigation.NavInflater navInflater;
+    property public androidx.navigation.NavigatorProvider navigatorProvider;
+    property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+    field public static final androidx.navigation.NavController.Companion Companion;
+    field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+  }
+
+  public static final class NavController.Companion {
+    method @SuppressCompatibility @androidx.navigation.NavDeepLinkSaveStateControl public void enableDeepLinkSaveState(boolean saveState);
+  }
+
+  public static fun interface NavController.OnDestinationChangedListener {
+    method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavDeepLinkBuilder {
+    ctor public NavDeepLinkBuilder(android.content.Context context);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+    method public android.app.PendingIntent createPendingIntent();
+    method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+    method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity?> activityClass);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavDeepLinkSaveStateControl {
+  }
+
+  public interface NavHost {
+    method public androidx.navigation.NavController getNavController();
+    property public abstract androidx.navigation.NavController navController;
+  }
+
+  public class NavHostController extends androidx.navigation.NavController {
+    ctor public NavHostController(android.content.Context context);
+    method public final void enableOnBackPressed(boolean enabled);
+    method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+    method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+    method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavInflater {
+    ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+    field public static final androidx.navigation.NavInflater.Companion Companion;
+  }
+
+  public static final class NavInflater.Companion {
+  }
+
+  public final class Navigation {
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+    method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+    method public static androidx.navigation.NavController findNavController(android.view.View view);
+    method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+    field public static final androidx.navigation.Navigation INSTANCE;
+  }
+
+  public final class ViewKt {
+    method public static androidx.navigation.NavController findNavController(android.view.View);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-runtime/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-runtime/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-runtime/api/restricted_2.8.0-beta06.txt b/navigation/navigation-runtime/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..b801964
--- /dev/null
+++ b/navigation/navigation-runtime/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,246 @@
+// Signature format: 4.0
+package androidx.navigation {
+
+  public final class ActivityKt {
+    method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int viewId);
+  }
+
+  public final class ActivityNavArgsLazyKt {
+    method @MainThread public static inline <reified Args extends androidx.navigation.NavArgs> androidx.navigation.NavArgsLazy<Args> navArgs(android.app.Activity);
+  }
+
+  @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+    ctor public ActivityNavigator(android.content.Context context);
+    method public static final void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+    method public androidx.navigation.ActivityNavigator.Destination createDestination();
+    method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination destination, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    field public static final androidx.navigation.ActivityNavigator.Companion Companion;
+  }
+
+  public static final class ActivityNavigator.Companion {
+    method public void applyPopAnimationsToPendingTransition(android.app.Activity activity);
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Activity::class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination> activityNavigator);
+    ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider navigatorProvider);
+    method public final String? getAction();
+    method public final android.content.ComponentName? getComponent();
+    method public final android.net.Uri? getData();
+    method public final String? getDataPattern();
+    method public final android.content.Intent? getIntent();
+    method public final String? getTargetPackage();
+    method public final androidx.navigation.ActivityNavigator.Destination setAction(String? action);
+    method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName? name);
+    method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri? data);
+    method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String? dataPattern);
+    method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent? intent);
+    method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String? packageName);
+    property public final String? action;
+    property public final android.content.ComponentName? component;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final android.content.Intent? intent;
+    property public final String? targetPackage;
+  }
+
+  public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+    method public int getFlags();
+    property public final androidx.core.app.ActivityOptionsCompat? activityOptions;
+    property public final int flags;
+  }
+
+  public static final class ActivityNavigator.Extras.Builder {
+    ctor public ActivityNavigator.Extras.Builder();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int flags);
+    method public androidx.navigation.ActivityNavigator.Extras build();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat activityOptions);
+  }
+
+  @androidx.navigation.NavDestinationDsl public final class ActivityNavigatorDestinationBuilder extends androidx.navigation.NavDestinationBuilder<androidx.navigation.ActivityNavigator.Destination> {
+    ctor @Deprecated public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, @IdRes int id);
+    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, String route);
+    ctor public ActivityNavigatorDestinationBuilder(androidx.navigation.ActivityNavigator navigator, kotlin.reflect.KClass<?> route, java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+    method public androidx.navigation.ActivityNavigator.Destination build();
+    method public String? getAction();
+    method public kotlin.reflect.KClass<? extends android.app.Activity>? getActivityClass();
+    method public android.net.Uri? getData();
+    method public String? getDataPattern();
+    method public String? getTargetPackage();
+    method public void setAction(String?);
+    method public void setActivityClass(kotlin.reflect.KClass<? extends android.app.Activity>?);
+    method public void setData(android.net.Uri?);
+    method public void setDataPattern(String?);
+    method public void setTargetPackage(String?);
+    property public final String? action;
+    property public final kotlin.reflect.KClass<? extends android.app.Activity>? activityClass;
+    property public final android.net.Uri? data;
+    property public final String? dataPattern;
+    property public final String? targetPackage;
+  }
+
+  public final class ActivityNavigatorDestinationBuilderKt {
+    method @Deprecated public static inline void activity(androidx.navigation.NavGraphBuilder, @IdRes int id, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline void activity(androidx.navigation.NavGraphBuilder, String route, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+    method public static inline <reified T> void activity(androidx.navigation.NavGraphBuilder, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.ActivityNavigatorDestinationBuilder,kotlin.Unit> builder);
+  }
+
+  public final class ActivityNavigatorExtrasKt {
+    method public static androidx.navigation.ActivityNavigator.Extras ActivityNavigatorExtras(optional androidx.core.app.ActivityOptionsCompat? activityOptions, optional int flags);
+  }
+
+  public class NavController {
+    ctor public NavController(android.content.Context context);
+    method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @MainThread public inline <reified T> boolean clearBackStack();
+    method @MainThread public final boolean clearBackStack(@IdRes int destinationId);
+    method @MainThread public final boolean clearBackStack(String route);
+    method @MainThread public final <T> boolean clearBackStack(T route);
+    method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+    method @SuppressCompatibility @androidx.navigation.NavDeepLinkSaveStateControl public static final void enableDeepLinkSaveState(boolean saveState);
+    method public inline <reified T> androidx.navigation.NavBackStackEntry getBackStackEntry();
+    method public androidx.navigation.NavBackStackEntry getBackStackEntry(@IdRes int destinationId);
+    method public final androidx.navigation.NavBackStackEntry getBackStackEntry(String route);
+    method public final <T> androidx.navigation.NavBackStackEntry getBackStackEntry(T route);
+    method public androidx.navigation.NavBackStackEntry? getCurrentBackStackEntry();
+    method public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> getCurrentBackStackEntryFlow();
+    method public androidx.navigation.NavDestination? getCurrentDestination();
+    method @MainThread public androidx.navigation.NavGraph getGraph();
+    method public androidx.navigation.NavInflater getNavInflater();
+    method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+    method public androidx.navigation.NavBackStackEntry? getPreviousBackStackEntry();
+    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int navGraphId);
+    method public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> getVisibleEntries();
+    method @MainThread public boolean handleDeepLink(android.content.Intent? intent);
+    method @MainThread public void navigate(android.net.Uri deepLink);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(android.net.Uri deepLink, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(androidx.navigation.NavDeepLinkRequest request, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.Navigator.Extras navigatorExtras);
+    method @MainThread public void navigate(androidx.navigation.NavDirections directions, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(@IdRes int resId);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions);
+    method @MainThread public void navigate(@IdRes int resId, android.os.Bundle? args, androidx.navigation.NavOptions? navOptions, androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final void navigate(String route);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions);
+    method @MainThread public final void navigate(String route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final void navigate(String route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+    method @MainThread public final <T> void navigate(T route);
+    method @MainThread public final <T> void navigate(T route, optional androidx.navigation.NavOptions? navOptions);
+    method @MainThread public final <T> void navigate(T route, optional androidx.navigation.NavOptions? navOptions, optional androidx.navigation.Navigator.Extras? navigatorExtras);
+    method @MainThread public final <T> void navigate(T route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavOptionsBuilder,kotlin.Unit> builder);
+    method @MainThread public boolean navigateUp();
+    method @MainThread public boolean popBackStack();
+    method @MainThread public inline <reified T> boolean popBackStack(boolean inclusive, optional boolean saveState);
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive);
+    method @MainThread public boolean popBackStack(@IdRes int destinationId, boolean inclusive, boolean saveState);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive);
+    method @MainThread public final boolean popBackStack(String route, boolean inclusive, optional boolean saveState);
+    method @MainThread public final <T> boolean popBackStack(T route, boolean inclusive);
+    method @MainThread public final <T> boolean popBackStack(T route, boolean inclusive, optional boolean saveState);
+    method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener listener);
+    method @CallSuper public void restoreState(android.os.Bundle? navState);
+    method @CallSuper public android.os.Bundle? saveState();
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph);
+    method @CallSuper @MainThread public void setGraph(androidx.navigation.NavGraph graph, android.os.Bundle? startDestinationArgs);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId);
+    method @CallSuper @MainThread public void setGraph(@NavigationRes int graphResId, android.os.Bundle? startDestinationArgs);
+    property public androidx.navigation.NavBackStackEntry? currentBackStackEntry;
+    property public final kotlinx.coroutines.flow.Flow<androidx.navigation.NavBackStackEntry> currentBackStackEntryFlow;
+    property public androidx.navigation.NavDestination? currentDestination;
+    property @MainThread public androidx.navigation.NavGraph graph;
+    property public androidx.navigation.NavInflater navInflater;
+    property public androidx.navigation.NavigatorProvider navigatorProvider;
+    property public androidx.navigation.NavBackStackEntry? previousBackStackEntry;
+    property public final kotlinx.coroutines.flow.StateFlow<java.util.List<androidx.navigation.NavBackStackEntry>> visibleEntries;
+    field public static final androidx.navigation.NavController.Companion Companion;
+    field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+  }
+
+  public static final class NavController.Companion {
+    method @SuppressCompatibility @androidx.navigation.NavDeepLinkSaveStateControl public void enableDeepLinkSaveState(boolean saveState);
+  }
+
+  public static fun interface NavController.OnDestinationChangedListener {
+    method public void onDestinationChanged(androidx.navigation.NavController controller, androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+  }
+
+  public final class NavControllerKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavController, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavDeepLinkBuilder {
+    ctor public NavDeepLinkBuilder(android.content.Context context);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route);
+    method public androidx.navigation.NavDeepLinkBuilder addDestination(String route, optional android.os.Bundle? args);
+    method public android.app.PendingIntent createPendingIntent();
+    method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+    method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName componentName);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity?> activityClass);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int destId, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(String destRoute, optional android.os.Bundle? args);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph navGraph);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId);
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavDeepLinkSaveStateControl {
+  }
+
+  public interface NavHost {
+    method public androidx.navigation.NavController getNavController();
+    property public abstract androidx.navigation.NavController navController;
+  }
+
+  public class NavHostController extends androidx.navigation.NavController {
+    ctor public NavHostController(android.content.Context context);
+    method public final void enableOnBackPressed(boolean enabled);
+    method public final void setLifecycleOwner(androidx.lifecycle.LifecycleOwner owner);
+    method public final void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher dispatcher);
+    method public final void setViewModelStore(androidx.lifecycle.ViewModelStore viewModelStore);
+  }
+
+  public final class NavHostKt {
+    method @Deprecated public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, optional @IdRes int id, @IdRes int startDestination, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, Object startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, String startDestination, optional String? route, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+    method public static inline androidx.navigation.NavGraph createGraph(androidx.navigation.NavHost, kotlin.reflect.KClass<? extends java.lang.Object?> startDestination, optional kotlin.reflect.KClass<? extends java.lang.Object?>? route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap, kotlin.jvm.functions.Function1<? super androidx.navigation.NavGraphBuilder,kotlin.Unit> builder);
+  }
+
+  public final class NavInflater {
+    ctor public NavInflater(android.content.Context context, androidx.navigation.NavigatorProvider navigatorProvider);
+    method public androidx.navigation.NavGraph inflate(@NavigationRes int graphResId);
+    field public static final androidx.navigation.NavInflater.Companion Companion;
+  }
+
+  public static final class NavInflater.Companion {
+  }
+
+  public final class Navigation {
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections directions);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int resId, optional android.os.Bundle? args);
+    method public static androidx.navigation.NavController findNavController(android.app.Activity activity, @IdRes int viewId);
+    method public static androidx.navigation.NavController findNavController(android.view.View view);
+    method public static void setViewNavController(android.view.View view, androidx.navigation.NavController? controller);
+    field public static final androidx.navigation.Navigation INSTANCE;
+  }
+
+  public final class ViewKt {
+    method public static androidx.navigation.NavController findNavController(android.view.View);
+  }
+
+}
+
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index 64200c4..87e1c42 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -68,6 +68,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Navigation-Runtime"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
index a408809..b4ae718 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
@@ -30,6 +30,7 @@
 import androidx.lifecycle.ViewModelStore
 import androidx.lifecycle.testing.TestLifecycleOwner
 import androidx.navigation.NavDestination.Companion.createRoute
+import androidx.navigation.NavDestination.Companion.hasRoute
 import androidx.navigation.serialization.generateHashCode
 import androidx.navigation.test.R
 import androidx.test.annotation.UiThreadTest
@@ -209,6 +210,24 @@
 
     @Serializable class TestGraph
 
+    private enum class TestEnum {
+        ONE,
+        TWO
+    }
+
+    private enum class TestEnumWithArg(val number: Int) {
+        ONE(1),
+        TWO(2)
+    }
+
+    @Serializable
+    private class EnumWrapper {
+        enum class NestedEnum(val number: Int) {
+            ONE(1),
+            TWO(2)
+        }
+    }
+
     companion object {
         private const val UNKNOWN_DESTINATION_ID = -1
         private const val TEST_ARG = "test"
@@ -552,6 +571,149 @@
 
     @UiThreadTest
     @Test
+    fun testStartDestinationUseDefaultPathArg() {
+        val navController = createNavController()
+        val graph =
+            navController.createGraph(route = "graph", startDestination = "start/{arg}") {
+                test("start/{arg}") {
+                    argument("arg") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg"
+                    }
+                }
+            }
+        navController.setGraph(graph, null)
+        assertThat(navController.currentDestination?.route).isEqualTo("start/{arg}")
+        val entry = navController.currentBackStackEntry!!
+        val actual = entry.arguments!!.getString("arg")
+        val expected = "defaultArg"
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testStartDestinationUseDefaultQueryArg() {
+        val navController = createNavController()
+        val graph =
+            navController.createGraph(route = "graph", startDestination = "start?arg={arg}") {
+                test("start?arg={arg}") {
+                    argument("arg") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg"
+                    }
+                }
+            }
+        navController.setGraph(graph, null)
+        assertThat(navController.currentDestination?.route).isEqualTo("start?arg={arg}")
+        val entry = navController.currentBackStackEntry!!
+        val actual = entry.arguments!!.getString("arg")
+        val expected = "defaultArg"
+        assertThat(actual).isEqualTo(expected)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testStartDestinationUseMultipleDefaultArgs() {
+        val navController = createNavController()
+        val graph =
+            navController.createGraph(
+                route = "graph",
+                startDestination = "start/{arg}?arg2={arg2}"
+            ) {
+                test("start/{arg}?arg2={arg2}") {
+                    argument("arg") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg"
+                    }
+                    argument("arg2") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg2"
+                    }
+                }
+            }
+        navController.setGraph(graph, null)
+        assertThat(navController.currentDestination?.route).isEqualTo("start/{arg}?arg2={arg2}")
+        val entry = navController.currentBackStackEntry!!
+        val actual = entry.arguments!!.getString("arg")
+        val expected = "defaultArg"
+        assertThat(actual).isEqualTo(expected)
+        val actual2 = entry.arguments!!.getString("arg2")
+        val expected2 = "defaultArg2"
+        assertThat(actual2).isEqualTo(expected2)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testStartDestinationQueryPlaceholderArg() {
+        val navController = createNavController()
+        val graph =
+            navController.createGraph(
+                route = "graph",
+                startDestination = "start/myArg?arg2={arg2}"
+            ) {
+                test("start/{arg}?arg2={arg2}") {
+                    argument("arg") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg"
+                    }
+                    argument("arg2") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg2"
+                    }
+                }
+            }
+        navController.setGraph(graph, null)
+        assertThat(navController.currentDestination?.route).isEqualTo("start/{arg}?arg2={arg2}")
+        val entry = navController.currentBackStackEntry!!
+        val actual = entry.arguments!!.getString("arg")
+        val expected = "myArg"
+        assertThat(actual).isEqualTo(expected)
+        val actual2 = entry.arguments!!.getString("arg2")
+        val expected2 = "{arg2}"
+        assertThat(actual2).isEqualTo(expected2)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testStartDestinationPathPlaceholderArg() {
+        val navController = createNavController()
+        val graph =
+            navController.createGraph(
+                route = "graph",
+                startDestination = "start/{arg}?arg2=myArg2"
+            ) {
+                test("start/{arg}?arg2={arg2}") {
+                    argument("arg") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg"
+                    }
+                    argument("arg2") {
+                        type = NavType.StringType
+                        nullable = false
+                        defaultValue = "defaultArg2"
+                    }
+                }
+            }
+        navController.setGraph(graph, null)
+        assertThat(navController.currentDestination?.route).isEqualTo("start/{arg}?arg2={arg2}")
+        val entry = navController.currentBackStackEntry!!
+        val actual = entry.arguments!!.getString("arg")
+        val expected = "{arg}"
+        assertThat(actual).isEqualTo(expected)
+        val actual2 = entry.arguments!!.getString("arg2")
+        val expected2 = "myArg2"
+        assertThat(actual2).isEqualTo(expected2)
+    }
+
+    @UiThreadTest
+    @Test
     fun testNestedStartDestination() {
         val navController = createNavController()
         navController.graph = nav_nested_start_destination_route_graph
@@ -1010,6 +1172,370 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateWithObjectNullableInt() {
+        @Serializable class TestClass(val arg: Int? = 10)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(15))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(15)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+
+        // use default
+        navController.navigate(TestClass())
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(10)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(5)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNullableIntNoDefault() {
+        @Serializable class TestClass(val arg: Int?)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(15))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(15)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNullableBoolean() {
+        @Serializable class TestClass(val arg: Boolean? = false)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(true))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isTrue()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+
+        // use default
+        navController.navigate(TestClass())
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isFalse()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(5)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNullableBooleanNoDefault() {
+        @Serializable class TestClass(val arg: Boolean?)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(true))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isTrue()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNullableFloat() {
+        @Serializable class TestClass(val arg: Float? = 1.0F)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(2.0F))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(2.0F)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+
+        // use default
+        navController.navigate(TestClass())
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(1.0F)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(5)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNullableFloatNoDefault() {
+        @Serializable class TestClass(val arg: Float?)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(2.0F))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(2.0F)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNullableLong() {
+        @Serializable class TestClass(val arg: Long? = 1L)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(2L))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(2L)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+
+        // use default
+        navController.navigate(TestClass())
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(1L)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(5)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNullableLongNoDefault() {
+        @Serializable class TestClass(val arg: Long?)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(2L))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isEqualTo(2L)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectEnum() {
+        @Serializable class TestClass(val arg: TestEnum = TestEnum.ONE)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(TestEnum.TWO))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg)
+            .isEqualTo(TestEnum.TWO)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // use default
+        navController.navigate(TestClass())
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg)
+            .isEqualTo(TestEnum.ONE)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectEnumNullable() {
+        @Serializable class TestClass(val arg: TestEnum? = TestEnum.ONE)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(TestEnum.TWO))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg)
+            .isEqualTo(TestEnum.TWO)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in null
+        navController.navigate(TestClass(null))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg).isNull()
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+
+        // use default
+        navController.navigate(TestClass())
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg)
+            .isEqualTo(TestEnum.ONE)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(5)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectEnumNoDefault() {
+        @Serializable class TestClass(val arg: TestEnum)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        // passed in arg
+        navController.navigate(TestClass(TestEnum.ONE))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg)
+            .isEqualTo(TestEnum.ONE)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+
+        // passed in arg
+        navController.navigate(TestClass(TestEnum.TWO))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+        assertThat(navController.currentBackStackEntry?.toRoute<TestClass>()?.arg)
+            .isEqualTo(TestEnum.TWO)
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectEnumWithArg() {
+        @Serializable class TestClass(val arg: TestEnumWithArg, val arg2: TestEnumWithArg)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        navController.navigate(TestClass(TestEnumWithArg.ONE, TestEnumWithArg.TWO))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+
+        val arg = navController.currentBackStackEntry?.toRoute<TestClass>()?.arg
+        assertThat(arg).isEqualTo(TestEnumWithArg.ONE)
+        assertThat(arg?.number).isEqualTo(1)
+
+        val arg2 = navController.currentBackStackEntry?.toRoute<TestClass>()?.arg2
+        assertThat(arg2).isEqualTo(TestEnumWithArg.TWO)
+        assertThat(arg2?.number).isEqualTo(2)
+
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateWithObjectNestedEnum() {
+        @Serializable
+        class TestClass(val arg: EnumWrapper.NestedEnum, val arg2: EnumWrapper.NestedEnum)
+        val navController = createNavController()
+        navController.graph =
+            navController.createGraph(startDestination = "start") {
+                test("start")
+                test<TestClass>()
+            }
+        assertThat(navController.currentDestination?.route).isEqualTo("start")
+
+        navController.navigate(TestClass(EnumWrapper.NestedEnum.ONE, EnumWrapper.NestedEnum.TWO))
+        assertThat(navController.currentDestination?.hasRoute(TestClass::class)).isTrue()
+
+        val arg = navController.currentBackStackEntry?.toRoute<TestClass>()?.arg
+        assertThat(arg).isEqualTo(EnumWrapper.NestedEnum.ONE)
+        assertThat(arg?.number).isEqualTo(1)
+
+        val arg2 = navController.currentBackStackEntry?.toRoute<TestClass>()?.arg2
+        assertThat(arg2).isEqualTo(EnumWrapper.NestedEnum.TWO)
+        assertThat(arg2?.number).isEqualTo(2)
+
+        assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+    }
+
+    @UiThreadTest
+    @Test
     fun testNavigateWithPopUpToFurthestRoute() {
         val navController = createNavController()
         navController.graph = nav_singleArg_graph
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt
index fec16b1..3e65714 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt
@@ -19,6 +19,7 @@
 import android.app.PendingIntent
 import android.content.ComponentName
 import android.content.Context
+import android.content.ContextWrapper
 import android.content.Intent
 import android.os.Bundle
 import androidx.annotation.IdRes
@@ -51,9 +52,13 @@
 public class NavDeepLinkBuilder(private val context: Context) {
     private class DeepLinkDestination constructor(val destinationId: Int, val arguments: Bundle?)
 
+    private val activity: Activity? =
+        generateSequence(context) { (it as? ContextWrapper)?.baseContext }
+            .mapNotNull { it as? Activity }
+            .firstOrNull()
     private val intent: Intent =
-        if (context is Activity) {
-                Intent(context, context.javaClass)
+        if (activity != null) {
+                Intent(context, activity.javaClass)
             } else {
                 val launchIntent =
                     context.packageManager.getLaunchIntentForPackage(context.packageName)
diff --git a/navigation/navigation-safe-args-gradle-plugin/lint-baseline.xml b/navigation/navigation-safe-args-gradle-plugin/lint-baseline.xml
index ac0c4e5..530d8a9 100644
--- a/navigation/navigation-safe-args-gradle-plugin/lint-baseline.xml
+++ b/navigation/navigation-safe-args-gradle-plugin/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -58,8 +58,8 @@
     <issue
         id="GradleProjectIsolation"
         message="Use providers.gradleProperty instead of findProperty"
-        errorLine1="                    (project.findProperty(&quot;android.useAndroidX&quot;) == &quot;true&quot;).also {"
-        errorLine2="                             ~~~~~~~~~~~~">
+        errorLine1="                        (project.findProperty(&quot;android.useAndroidX&quot;) == &quot;true&quot;).also {"
+        errorLine2="                                 ~~~~~~~~~~~~">
         <location
             file="src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt"/>
     </issue>
@@ -67,8 +67,8 @@
     <issue
         id="WithPluginClasspathUsage"
         message="Avoid usage of GradleRunner#withPluginClasspath, which is broken. Instead use something like https://github.com/autonomousapps/dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
-        errorLine1="        .withProjectDir(projectRoot()).withPluginClasspath()"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            .withPluginClasspath()"
+        errorLine2="             ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt"/>
     </issue>
diff --git a/navigation/navigation-testing/api/2.8.0-beta06.txt b/navigation/navigation-testing/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..ebd90df
--- /dev/null
+++ b/navigation/navigation-testing/api/2.8.0-beta06.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+  public final class SavedStateHandleFactoryKt {
+    method public static operator androidx.lifecycle.SavedStateHandle invoke(androidx.lifecycle.SavedStateHandle.Companion, Object route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+  }
+
+  public final class TestNavHostController extends androidx.navigation.NavHostController {
+    ctor public TestNavHostController(android.content.Context context);
+    method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+    method public void setCurrentDestination(@IdRes int destId);
+    method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+    method public void setCurrentDestination(String destRoute);
+    method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+    property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+  }
+
+  public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+    ctor public TestNavigatorState();
+    ctor public TestNavigatorState(optional android.content.Context? context);
+    ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+    method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-testing/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-testing/api/res-2.8.0-beta06.txt
diff --git a/navigation/navigation-testing/api/restricted_2.8.0-beta06.txt b/navigation/navigation-testing/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..ebd90df
--- /dev/null
+++ b/navigation/navigation-testing/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.navigation.testing {
+
+  public final class SavedStateHandleFactoryKt {
+    method public static operator androidx.lifecycle.SavedStateHandle invoke(androidx.lifecycle.SavedStateHandle.Companion, Object route, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<? extends java.lang.Object?>> typeMap);
+  }
+
+  public final class TestNavHostController extends androidx.navigation.NavHostController {
+    ctor public TestNavHostController(android.content.Context context);
+    method public java.util.List<androidx.navigation.NavBackStackEntry> getBackStack();
+    method public void setCurrentDestination(@IdRes int destId);
+    method public void setCurrentDestination(@IdRes int destId, optional android.os.Bundle args);
+    method public void setCurrentDestination(String destRoute);
+    method public void setCurrentDestination(String destRoute, optional android.os.Bundle args);
+    property public final java.util.List<androidx.navigation.NavBackStackEntry> backStack;
+  }
+
+  public final class TestNavigatorState extends androidx.navigation.NavigatorState {
+    ctor public TestNavigatorState();
+    ctor public TestNavigatorState(optional android.content.Context? context);
+    ctor public TestNavigatorState(optional android.content.Context? context, optional kotlinx.coroutines.CoroutineDispatcher coroutineDispatcher);
+    method public androidx.navigation.NavBackStackEntry createBackStackEntry(androidx.navigation.NavDestination destination, android.os.Bundle? arguments);
+    method public androidx.navigation.NavBackStackEntry restoreBackStackEntry(androidx.navigation.NavBackStackEntry previouslySavedEntry);
+  }
+
+}
+
diff --git a/navigation/navigation-testing/build.gradle b/navigation/navigation-testing/build.gradle
index 40e272e..5c80ca7 100644
--- a/navigation/navigation-testing/build.gradle
+++ b/navigation/navigation-testing/build.gradle
@@ -61,7 +61,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Navigation-Testing"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-ui-ktx/api/2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-ui-ktx/api/2.8.0-beta06.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/navigation/navigation-ui-ktx/api/res-2.8.0-beta06.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to navigation/navigation-ui-ktx/api/res-2.8.0-beta06.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/navigation/navigation-ui-ktx/api/restricted_2.8.0-beta06.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to navigation/navigation-ui-ktx/api/restricted_2.8.0-beta06.txt
diff --git a/navigation/navigation-ui-ktx/build.gradle b/navigation/navigation-ui-ktx/build.gradle
index 0a1ede1..77f8cdf 100644
--- a/navigation/navigation-ui-ktx/build.gradle
+++ b/navigation/navigation-ui-ktx/build.gradle
@@ -41,7 +41,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Android Navigation-UI-Ktx"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/navigation/navigation-ui/api/2.8.0-beta06.txt b/navigation/navigation-ui/api/2.8.0-beta06.txt
new file mode 100644
index 0000000..326d323
--- /dev/null
+++ b/navigation/navigation-ui/api/2.8.0-beta06.txt
@@ -0,0 +1,94 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+  public final class ActivityKt {
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class AppBarConfiguration {
+    method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+    method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+    method public androidx.customview.widget.Openable? getOpenableLayout();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+  }
+
+  public static final class AppBarConfiguration.Builder {
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+    method public androidx.navigation.ui.AppBarConfiguration build();
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+  }
+
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
+    method public boolean onNavigateUp();
+  }
+
+  public final class AppBarConfigurationKt {
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+  }
+
+  public final class BottomNavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+  }
+
+  public final class CollapsingToolbarLayoutKt {
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class MenuItemKt {
+    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+  }
+
+  public final class NavControllerKt {
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+  }
+
+  public final class NavigationUI {
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method @SuppressCompatibility @androidx.navigation.ui.NavigationUiSaveStateControl public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController, boolean saveState);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+    method @SuppressCompatibility @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController, boolean saveState);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method @SuppressCompatibility @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController, boolean saveState);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavigationUiSaveStateControl {
+  }
+
+  public final class NavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+  }
+
+  public final class ToolbarKt {
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+}
+
diff --git a/navigation/navigation-ui/api/res-2.8.0-beta06.txt b/navigation/navigation-ui/api/res-2.8.0-beta06.txt
new file mode 100644
index 0000000..e65fdbe
--- /dev/null
+++ b/navigation/navigation-ui/api/res-2.8.0-beta06.txt
@@ -0,0 +1,8 @@
+anim nav_default_enter_anim
+anim nav_default_exit_anim
+anim nav_default_pop_enter_anim
+anim nav_default_pop_exit_anim
+animator nav_default_enter_anim
+animator nav_default_exit_anim
+animator nav_default_pop_enter_anim
+animator nav_default_pop_exit_anim
diff --git a/navigation/navigation-ui/api/restricted_2.8.0-beta06.txt b/navigation/navigation-ui/api/restricted_2.8.0-beta06.txt
new file mode 100644
index 0000000..326d323
--- /dev/null
+++ b/navigation/navigation-ui/api/restricted_2.8.0-beta06.txt
@@ -0,0 +1,94 @@
+// Signature format: 4.0
+package androidx.navigation.ui {
+
+  public final class ActivityKt {
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class AppBarConfiguration {
+    method @Deprecated public androidx.drawerlayout.widget.DrawerLayout? getDrawerLayout();
+    method public androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? getFallbackOnNavigateUpListener();
+    method public androidx.customview.widget.Openable? getOpenableLayout();
+    method public java.util.Set<java.lang.Integer> getTopLevelDestinations();
+    method public boolean isTopLevelDestination(androidx.navigation.NavDestination destination);
+    property @Deprecated public final androidx.drawerlayout.widget.DrawerLayout? drawerLayout;
+    property public final androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener;
+    property public final androidx.customview.widget.Openable? openableLayout;
+    property public final java.util.Set<java.lang.Integer> topLevelDestinations;
+  }
+
+  public static final class AppBarConfiguration.Builder {
+    ctor public AppBarConfiguration.Builder(android.view.Menu topLevelMenu);
+    ctor public AppBarConfiguration.Builder(androidx.navigation.NavGraph navGraph);
+    ctor public AppBarConfiguration.Builder(int... topLevelDestinationIds);
+    ctor public AppBarConfiguration.Builder(java.util.Set<java.lang.Integer> topLevelDestinationIds);
+    method public androidx.navigation.ui.AppBarConfiguration build();
+    method @Deprecated public androidx.navigation.ui.AppBarConfiguration.Builder setDrawerLayout(androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setFallbackOnNavigateUpListener(androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener? fallbackOnNavigateUpListener);
+    method public androidx.navigation.ui.AppBarConfiguration.Builder setOpenableLayout(androidx.customview.widget.Openable? openableLayout);
+  }
+
+  public static fun interface AppBarConfiguration.OnNavigateUpListener {
+    method public boolean onNavigateUp();
+  }
+
+  public final class AppBarConfigurationKt {
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(android.view.Menu topLevelMenu, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(androidx.navigation.NavGraph navGraph, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+    method public static inline androidx.navigation.ui.AppBarConfiguration AppBarConfiguration(java.util.Set<java.lang.Integer> topLevelDestinationIds, optional androidx.customview.widget.Openable? drawerLayout, optional kotlin.jvm.functions.Function0<java.lang.Boolean> fallbackOnNavigateUpListener);
+  }
+
+  public final class BottomNavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView, androidx.navigation.NavController navController);
+  }
+
+  public final class CollapsingToolbarLayoutKt {
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+  public final class MenuItemKt {
+    method public static boolean onNavDestinationSelected(android.view.MenuItem, androidx.navigation.NavController navController);
+  }
+
+  public final class NavControllerKt {
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.customview.widget.Openable? drawerLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController, androidx.navigation.ui.AppBarConfiguration appBarConfiguration);
+  }
+
+  public final class NavigationUI {
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static boolean navigateUp(androidx.navigation.NavController navController, androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController);
+    method @SuppressCompatibility @androidx.navigation.ui.NavigationUiSaveStateControl public static boolean onNavDestinationSelected(android.view.MenuItem item, androidx.navigation.NavController navController, boolean saveState);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity activity, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, androidx.customview.widget.Openable? openableLayout);
+    method public static void setupWithNavController(com.google.android.material.appbar.CollapsingToolbarLayout collapsingToolbarLayout, androidx.appcompat.widget.Toolbar toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController);
+    method @SuppressCompatibility @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationBarView navigationBarView, androidx.navigation.NavController navController, boolean saveState);
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController);
+    method @SuppressCompatibility @androidx.navigation.ui.NavigationUiSaveStateControl public static void setupWithNavController(com.google.android.material.navigation.NavigationView navigationView, androidx.navigation.NavController navController, boolean saveState);
+    field public static final androidx.navigation.ui.NavigationUI INSTANCE;
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface NavigationUiSaveStateControl {
+  }
+
+  public final class NavigationViewKt {
+    method public static void setupWithNavController(com.google.android.material.navigation.NavigationView, androidx.navigation.NavController navController);
+  }
+
+  public final class ToolbarKt {
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, androidx.drawerlayout.widget.DrawerLayout? drawerLayout);
+    method public static void setupWithNavController(androidx.appcompat.widget.Toolbar, androidx.navigation.NavController navController, optional androidx.navigation.ui.AppBarConfiguration configuration);
+  }
+
+}
+
diff --git a/navigation/navigation-ui/build.gradle b/navigation/navigation-ui/build.gradle
index 58e46d0..ab3978b 100644
--- a/navigation/navigation-ui/build.gradle
+++ b/navigation/navigation-ui/build.gradle
@@ -60,6 +60,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Navigation-UI"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index 0988655..cbd8d34 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -55,6 +55,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.paging.integration.testapp"
 }
 
diff --git a/paging/paging-common-ktx/build.gradle b/paging/paging-common-ktx/build.gradle
index 0f607d3..23bbad8 100644
--- a/paging/paging-common-ktx/build.gradle
+++ b/paging/paging-common-ktx/build.gradle
@@ -37,5 +37,4 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'paging-common' artifact"
-    metalavaK2UastEnabled = true
 }
diff --git a/paging/paging-common/api/current.txt b/paging/paging-common/api/current.txt
index 776b8df..d7e4e71 100644
--- a/paging/paging-common/api/current.txt
+++ b/paging/paging-common/api/current.txt
@@ -380,6 +380,7 @@
   }
 
   public abstract class PagingDataPresenter<T> {
+    ctor public PagingDataPresenter();
     ctor public PagingDataPresenter(optional kotlin.coroutines.CoroutineContext mainContext, optional androidx.paging.PagingData<T>? cachedPagingData);
     method public final void addLoadStateListener(kotlin.jvm.functions.Function1<androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public final void addOnPagesUpdatedListener(kotlin.jvm.functions.Function0<kotlin.Unit> listener);
diff --git a/paging/paging-common/api/restricted_current.txt b/paging/paging-common/api/restricted_current.txt
index 776b8df..d7e4e71 100644
--- a/paging/paging-common/api/restricted_current.txt
+++ b/paging/paging-common/api/restricted_current.txt
@@ -380,6 +380,7 @@
   }
 
   public abstract class PagingDataPresenter<T> {
+    ctor public PagingDataPresenter();
     ctor public PagingDataPresenter(optional kotlin.coroutines.CoroutineContext mainContext, optional androidx.paging.PagingData<T>? cachedPagingData);
     method public final void addLoadStateListener(kotlin.jvm.functions.Function1<androidx.paging.CombinedLoadStates,kotlin.Unit> listener);
     method public final void addOnPagesUpdatedListener(kotlin.jvm.functions.Function0<kotlin.Unit> listener);
diff --git a/paging/paging-common/bcv/native/current.txt b/paging/paging-common/bcv/native/current.txt
index 5fbbeb5..8a8c037 100644
--- a/paging/paging-common/bcv/native/current.txt
+++ b/paging/paging-common/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index d47fe82..9d75111 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -33,8 +33,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm()
     mac()
     linux()
@@ -48,7 +46,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
-                api("androidx.annotation:annotation:1.8.0")
+                api(project(":annotation:annotation"))
             }
         }
 
@@ -144,6 +142,7 @@
     inceptionYear = "2017"
     description = "Android Paging-Common"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":paging:paging-samples"))
 }
 
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index 2c80b5a..8d938df 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -30,8 +30,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
 
     sourceSets {
@@ -84,9 +82,13 @@
     inceptionYear = "2020"
     description = "Compose integration with Paging"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":paging:paging-compose:paging-compose-samples"))
 }
 
 android {
+    compileSdk 35
     namespace "androidx.paging.compose"
+    // TODO(b/313699418): need to update compose.runtime version to 1.6.0+
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
diff --git a/paging/paging-compose/integration-tests/paging-demos/build.gradle b/paging/paging-compose/integration-tests/paging-demos/build.gradle
index e9cb7bb..2541828 100644
--- a/paging/paging-compose/integration-tests/paging-demos/build.gradle
+++ b/paging/paging-compose/integration-tests/paging-demos/build.gradle
@@ -56,5 +56,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.paging.compose.demos"
 }
diff --git a/paging/paging-compose/samples/build.gradle b/paging/paging-compose/samples/build.gradle
index 1df0616..2980ac1 100644
--- a/paging/paging-compose/samples/build.gradle
+++ b/paging/paging-compose/samples/build.gradle
@@ -48,5 +48,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.paging.compose.samples"
 }
diff --git a/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt b/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
index 4055fc4..dc64222 100644
--- a/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
+++ b/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
@@ -31,6 +31,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
@@ -38,7 +39,9 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
 import androidx.paging.CombinedLoadStates
 import androidx.paging.LoadState
 import androidx.paging.LoadState.Loading
@@ -260,6 +263,8 @@
             }
         }
 
+        val idMinus1 = rule.onNodeWithTag("-1").semanticsId()
+        val id0 = rule.onNodeWithTag("0").semanticsId()
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2)
@@ -267,8 +272,8 @@
             }
         }
 
-        rule.onNodeWithTag("-1").assertIsDeactivated()
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idMinus1)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
 
         rule.runOnIdle {
             runBlocking {
@@ -277,7 +282,9 @@
             }
         }
 
-        rule.onNodeWithTag("-1").assertIsDeactivated()
+        // Assert -1 is deactivated still
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idMinus1)
+
         // node reused
         rule.onNodeWithTag("0").assertDoesNotExist()
         rule.onNodeWithTag("7").assertIsDisplayed()
@@ -320,6 +327,9 @@
 
         rule.waitUntil { loadedItem6 }
 
+        val idMinus1 = rule.onNodeWithTag("-1").semanticsId()
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2)
@@ -327,8 +337,8 @@
             }
         }
 
-        rule.onNodeWithTag("-1").assertIsDeactivated()
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idMinus1)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
 
         rule.runOnIdle {
             runBlocking {
@@ -337,7 +347,8 @@
             }
         }
 
-        rule.onNodeWithTag("-1").assertIsDeactivated()
+        // Assert -1 is deactivated still
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idMinus1)
         // node reused
         rule.onNodeWithTag("0").assertDoesNotExist()
     }
@@ -370,6 +381,9 @@
             }
         }
 
+        val idMinus1 = rule.onNodeWithTag("-1").semanticsId()
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2)
@@ -377,8 +391,8 @@
             }
         }
 
-        rule.onNodeWithTag("-1").assertIsDeactivated()
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idMinus1)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
 
         rule.runOnIdle {
             runBlocking {
@@ -387,7 +401,8 @@
             }
         }
 
-        rule.onNodeWithTag("-1").assertIsDeactivated()
+        // Assert -1 is deactivated still
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(idMinus1)
         // node reused
         rule.onNodeWithTag("0").assertDoesNotExist()
         rule.onNodeWithTag("4").assertExists().assertIsDisplayed()
@@ -1107,4 +1122,12 @@
     private fun Content(tag: String) {
         Spacer(Modifier.height(itemsSizeDp).width(10.dp).testTag(tag))
     }
+
+    private fun SemanticsNode.assertLayoutDeactivatedById(id: Int) {
+        children.fastForEach {
+            if (it.id == id) {
+                assert(it.layoutInfo.isDeactivated)
+            }
+        }
+    }
 }
diff --git a/paging/paging-guava/build.gradle b/paging/paging-guava/build.gradle
index 9a985cb..8cd1581 100644
--- a/paging/paging-guava/build.gradle
+++ b/paging/paging-guava/build.gradle
@@ -48,7 +48,6 @@
     inceptionYear = "2019"
     description = "Android Paging Guava"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":paging:paging-samples"))
 }
diff --git a/paging/paging-runtime-ktx/build.gradle b/paging/paging-runtime-ktx/build.gradle
index dc1b808..3693aac 100644
--- a/paging/paging-runtime-ktx/build.gradle
+++ b/paging/paging-runtime-ktx/build.gradle
@@ -40,7 +40,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'paging-runtime' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/paging/paging-runtime/build.gradle b/paging/paging-runtime/build.gradle
index 3561986..e11cacf 100644
--- a/paging/paging-runtime/build.gradle
+++ b/paging/paging-runtime/build.gradle
@@ -38,9 +38,9 @@
     // Ensure that the -ktx dependency graph mirrors the Java dependency graph
     api(project(":paging:paging-common-ktx"))
 
-    api("androidx.lifecycle:lifecycle-livedata-ktx:2.4.0")
-    api("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0")
-    api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0")
+    api("androidx.lifecycle:lifecycle-livedata-ktx:2.8.3")
+    api("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3")
+    api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3")
     api("androidx.recyclerview:recyclerview:1.2.1")
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesAndroid)
@@ -67,6 +67,5 @@
     inceptionYear = "2017"
     description = "Android Paging-Runtime"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     samples(project(":paging:paging-samples"))
 }
diff --git a/paging/paging-runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt b/paging/paging-runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt
index bfc1e11..4df9cb0 100644
--- a/paging/paging-runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt
+++ b/paging/paging-runtime/src/androidTest/java/androidx/paging/AsyncPagingDataDifferTest.kt
@@ -29,6 +29,7 @@
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.testutils.MainDispatcherRule
+import androidx.testutils.TestDispatcher
 import com.google.common.truth.Truth.assertThat
 import kotlin.coroutines.ContinuationInterceptor
 import kotlin.coroutines.CoroutineContext
@@ -47,6 +48,7 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.resetMain
 import kotlinx.coroutines.test.runTest
@@ -276,10 +278,6 @@
                 currentPagedSource!!.invalidate()
                 advanceUntilIdle()
 
-                // UI access refreshed items. Load PREPEND [50] to fulfill prefetch distance
-                differ.getItem(51)
-                advanceUntilIdle()
-
                 assertEvents(
                     // TODO(b/182510751): Every change event here should have payload.
                     listOf(
@@ -480,7 +478,10 @@
             // Connect pager2, which should override pager1
             val job2 = launch { pager2.flow.collectLatest(differ::submitData) }
             advanceUntilIdle()
-            assertEquals(19, differ.itemCount)
+            // This prepends an extra page due to transformedAnchorPosition re-sending an Access at
+            // the
+            // first position, we therefore load 19 + 7 items.
+            assertEquals(26, differ.itemCount)
 
             // now if pager1 gets an invalidation, it overrides pager2
             source1.invalidate()
@@ -1471,6 +1472,275 @@
     }
 
     @Test
+    fun recoverFromInterruptedPrefetch() =
+        testScope.runTest {
+            val pagingSources = mutableListOf<TestPagingSource>()
+            val pager =
+                Pager(
+                    config =
+                        PagingConfig(pageSize = 10, prefetchDistance = 3, initialLoadSize = 10),
+                ) {
+                    TestPagingSource().also {
+                        it.getRefreshKeyResult = 0
+                        pagingSources.add(it)
+                    }
+                }
+
+            val collectPager = launch { pager.flow.collectLatest { differ.submitData(it) } }
+
+            // wait refresh
+            advanceUntilIdle()
+            assertThat(differ.snapshot().items.size).isEqualTo(10)
+
+            // sent hint to the first gen hint receiver and trigger a prefetch load
+            differ.getItem(9)
+            // Interrupt prefetch. We set getRefreshKeyResult = 0 so that after refresh, the last
+            // loaded item matches the lastAccessedIndex
+            differ.refresh()
+            advanceUntilIdle()
+
+            assertThat(pagingSources.size).isEqualTo(2)
+            // even though the prefetching hint had been discarded, make sure that after refresh,
+            // the prefetch is still respected even if it was interrupted by an invalidation
+            assertThat(differ.snapshot().items.size).isEqualTo(20)
+            collectPager.cancelAndJoin()
+        }
+
+    @Test
+    fun useTempPresenterOnDiffCalculation() = runTest {
+        val workerDispatcher = TestDispatcher()
+        val pager =
+            Pager(
+                config = PagingConfig(pageSize = 3, prefetchDistance = 1, initialLoadSize = 5),
+            ) {
+                TestPagingSource(loadDelay = 500)
+            }
+
+        val differ =
+            AsyncPagingDataDiffer(
+                diffCallback =
+                    object : DiffUtil.ItemCallback<Int>() {
+                        override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+
+                        override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+                    },
+                updateCallback = listUpdateCapture,
+                workerDispatcher = workerDispatcher
+            )
+        val collectPager = launch { pager.flow.collectLatest { differ.submitData(it) } }
+
+        // wait refresh
+        advanceUntilIdle()
+        assertThat(differ.snapshot().items.size).isEqualTo(5)
+
+        // append
+        differ.getItem(4)
+        advanceUntilIdle()
+        assertThat(differ.snapshot().items.size).isEqualTo(8)
+
+        // refresh with 5 items loaded
+        differ.refresh()
+
+        // present new list
+        advanceUntilIdle()
+        // check that presenter has been updated
+        assertThat(differ.presenter.snapshot().items.size).isEqualTo(5)
+        // but then differ should have switched to old presenter
+        assertThat(differ.snapshot().items.size).isEqualTo(8)
+
+        // run compute diff and dispatch
+        workerDispatcher.executeAll()
+        // let initial refresh complete
+        advanceTimeBy(1)
+
+        // diff should switch back to new presenter
+        assertThat(differ.snapshot().items.size).isEqualTo(5)
+        collectPager.cancelAndJoin()
+    }
+
+    @Test
+    fun tempPresenterSnapshotPlaceholders() = runTest {
+        val workerDispatcher = TestDispatcher()
+        val pager =
+            Pager(
+                config =
+                    PagingConfig(
+                        pageSize = 3,
+                        prefetchDistance = 1,
+                        initialLoadSize = 5,
+                        enablePlaceholders = true
+                    ),
+                initialKey = 20
+            ) {
+                TestPagingSource(loadDelay = 0)
+            }
+
+        val differ =
+            AsyncPagingDataDiffer(
+                diffCallback =
+                    object : DiffUtil.ItemCallback<Int>() {
+                        override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+
+                        override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+                    },
+                updateCallback = listUpdateCapture,
+                workerDispatcher = workerDispatcher
+            )
+        val collectPager = launch { pager.flow.collectLatest { differ.submitData(it) } }
+
+        advanceUntilIdle()
+
+        differ.getItem(25)
+        advanceUntilIdle()
+
+        val snapshot1 = differ.snapshot()
+        assertThat(snapshot1.size).isEqualTo(100)
+        assertThat(snapshot1.placeholdersBefore).isEqualTo(20)
+        assertThat(snapshot1.placeholdersAfter).isEqualTo(72)
+
+        // refresh with 5 items loaded
+        differ.refresh()
+        advanceUntilIdle()
+
+        // assert old presenter snapshot()
+        val snapshot2 = differ.snapshot()
+        assertThat(snapshot2.size).isEqualTo(100)
+        assertThat(snapshot2.placeholdersBefore).isEqualTo(20)
+        assertThat(snapshot2.placeholdersAfter).isEqualTo(72)
+
+        // run compute diff and dispatch
+        workerDispatcher.executeAll()
+        advanceUntilIdle()
+
+        // assert new presenter snapshot()
+        val snapshot3 = differ.snapshot()
+        assertThat(snapshot3.size).isEqualTo(100)
+        // load refresh + prefetch from transformedIndex = 8 items loaded
+        assertThat(snapshot3.placeholdersBefore).isEqualTo(22)
+        assertThat(snapshot3.placeholdersAfter).isEqualTo(70)
+        collectPager.cancelAndJoin()
+    }
+
+    @Test
+    fun tempPresenterGetOutOfBounds() = runTest {
+        val workerDispatcher = TestDispatcher()
+        val pager =
+            Pager(
+                config = PagingConfig(pageSize = 3, prefetchDistance = 1, initialLoadSize = 5),
+            ) {
+                TestPagingSource(loadDelay = 0)
+            }
+
+        val differ =
+            AsyncPagingDataDiffer(
+                diffCallback =
+                    object : DiffUtil.ItemCallback<Int>() {
+                        override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+
+                        override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+                    },
+                updateCallback = listUpdateCapture,
+                workerDispatcher = workerDispatcher
+            )
+        val collectPager = launch { pager.flow.collectLatest { differ.submitData(it) } }
+
+        // wait refresh
+        advanceUntilIdle()
+        assertThat(differ.snapshot().items.size).isEqualTo(5)
+
+        // append
+        differ.getItem(4)
+        advanceUntilIdle()
+
+        // refresh with 5 items loaded
+        differ.refresh()
+        advanceUntilIdle()
+
+        // make sure differ is on old list
+        assertThat(differ.snapshot().items.size).isEqualTo(8)
+
+        var exception: IndexOutOfBoundsException? = null
+        try {
+            differ.getItem(200)
+        } catch (e: IndexOutOfBoundsException) {
+            exception = e
+        }
+        assertThat(exception).isNotNull()
+
+        workerDispatcher.executeAll()
+        advanceUntilIdle()
+        collectPager.cancelAndJoin()
+    }
+
+    @Test
+    fun tempPresenterGetPlaceholder() = runTest {
+        val workerDispatcher = TestDispatcher()
+        val pager =
+            Pager(
+                config =
+                    PagingConfig(
+                        pageSize = 3,
+                        prefetchDistance = 1,
+                        initialLoadSize = 5,
+                        enablePlaceholders = true
+                    ),
+            ) {
+                TestPagingSource(loadDelay = 0)
+            }
+
+        val differ =
+            AsyncPagingDataDiffer(
+                diffCallback =
+                    object : DiffUtil.ItemCallback<Int>() {
+                        override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+
+                        override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
+                            return oldItem == newItem
+                        }
+                    },
+                updateCallback = listUpdateCapture,
+                workerDispatcher = workerDispatcher
+            )
+        val collectPager = launch { pager.flow.collectLatest { differ.submitData(it) } }
+
+        // wait refresh
+        advanceUntilIdle()
+        assertThat(differ.snapshot().items.size).isEqualTo(5)
+
+        // append
+        differ.getItem(4)
+        advanceUntilIdle()
+
+        // refresh with 5 items loaded
+        differ.refresh()
+        advanceUntilIdle()
+
+        // make sure differ is on old list
+        assertThat(differ.snapshot().placeholdersAfter).isEqualTo(92)
+
+        val placeholder = differ.getItem(70)
+        assertThat(placeholder).isNull()
+
+        workerDispatcher.executeAll()
+        advanceUntilIdle()
+        collectPager.cancelAndJoin()
+    }
+
+    @Test
     fun insertPageEmpty() =
         verifyPrependAppendCallback(
             initialItems = 2,
diff --git a/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt b/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
index 724cf94..4349ca67 100644
--- a/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
+++ b/paging/paging-runtime/src/main/java/androidx/paging/AsyncPagingDataDiffer.kt
@@ -146,6 +146,16 @@
 
     /** True if we're currently executing [getItem] */
     internal val inGetItem = MutableStateFlow(false)
+    private var lastAccessedIndex = 0
+
+    /**
+     * When presenter presents a new Refresh load, temporarily stores the previous generation of
+     * loaded data while doing [computeDiff] to ensure that RV has access to the previous list (the
+     * list actually still presented on the UI) for diffing.
+     *
+     * This presenter should be null when not computing diff.
+     */
+    private val previousPresenter: AtomicReference<PlaceholderPaddedList<T>> = AtomicReference(null)
 
     internal val presenter =
         object : PagingDataPresenter<T>(mainDispatcher) {
@@ -167,11 +177,29 @@
                                     }
                                 }
                                 else -> {
+                                    previousPresenter.set(previousList)
                                     val diffResult =
                                         withContext(workerDispatcher) {
                                             previousList.computeDiff(newList, diffCallback)
                                         }
+                                    previousPresenter.set(null)
                                     previousList.dispatchDiff(updateCallback, newList, diffResult)
+                                    val transformedIndex =
+                                        previousList.transformAnchorIndex(
+                                            diffResult = diffResult,
+                                            newList = newList,
+                                            oldPosition = lastAccessedIndex
+                                        )
+                                    // Transform the last loadAround index from the old list to the
+                                    // new list by passing it through the DiffResult, and pass
+                                    // it forward as a ViewportHint within the new list to the
+                                    // next generation of Pager.
+                                    // This ensures prefetch distance for the last ViewportHint from
+                                    // the old list is respected in the new list, even if
+                                    // invalidation interrupts the prepend / append load that
+                                    // would have fulfilled it in the old list.
+                                    lastAccessedIndex = transformedIndex
+                                    get(transformedIndex)
                                 }
                             }
                         }
@@ -428,7 +456,9 @@
     fun getItem(@IntRange(from = 0) index: Int): T? {
         try {
             inGetItem.update { true }
-            return presenter[index]
+            lastAccessedIndex = index
+            val tempList = previousPresenter.get()
+            return if (tempList != null) tempList.get(index) else presenter[index]
         } finally {
             inGetItem.update { false }
         }
@@ -443,14 +473,16 @@
      */
     @MainThread
     fun peek(@IntRange(from = 0) index: Int): T? {
-        return presenter.peek(index)
+        val tempList = previousPresenter.get()
+        return if (tempList != null) tempList.peek(index) else presenter.peek(index)
     }
 
     /**
      * Returns a new [ItemSnapshotList] representing the currently presented items, including any
      * placeholders if they are enabled.
      */
-    fun snapshot(): ItemSnapshotList<T> = presenter.snapshot()
+    fun snapshot(): ItemSnapshotList<T> =
+        previousPresenter.get()?.snapshot() ?: presenter.snapshot()
 
     /**
      * Get the number of items currently presented by this Differ. This value can be directly
@@ -459,7 +491,7 @@
      * @return Number of items being presented, including placeholders.
      */
     val itemCount: Int
-        get() = presenter.size
+        get() = previousPresenter.get()?.size ?: presenter.size
 
     /**
      * A hot [Flow] of [CombinedLoadStates] that emits a snapshot whenever the loading state of the
@@ -607,3 +639,23 @@
             }
         }
 }
+
+private fun <T : Any> PlaceholderPaddedList<T>.get(@IntRange(from = 0) index: Int): T? {
+    if (index < 0 || index >= size) {
+        throw IndexOutOfBoundsException("Index: $index, Size: $size")
+    }
+    val localIndex = index - placeholdersBefore
+    if (localIndex < 0 || localIndex >= dataCount) return null
+    return getItem(localIndex)
+}
+
+private fun <T : Any> PlaceholderPaddedList<T>.peek(@IntRange(from = 0) index: Int): T? = get(index)
+
+private fun <T : Any> PlaceholderPaddedList<T>.snapshot(): ItemSnapshotList<T> {
+    val itemEndIndex = dataCount - 1
+    val items = mutableListOf<T>()
+    for (i in 0..itemEndIndex) {
+        items.add(getItem(i))
+    }
+    return ItemSnapshotList(placeholdersBefore, placeholdersAfter, items)
+}
diff --git a/paging/paging-rxjava2-ktx/build.gradle b/paging/paging-rxjava2-ktx/build.gradle
index 9bd8dab..7b50469 100644
--- a/paging/paging-rxjava2-ktx/build.gradle
+++ b/paging/paging-rxjava2-ktx/build.gradle
@@ -47,7 +47,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'paging-rxjava2' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/paging/paging-rxjava2/build.gradle b/paging/paging-rxjava2/build.gradle
index 3903b2e..88681ad 100644
--- a/paging/paging-rxjava2/build.gradle
+++ b/paging/paging-rxjava2/build.gradle
@@ -56,7 +56,6 @@
     inceptionYear = "2018"
     description = "Android Paging-RXJava2"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
     samples(project(":paging:paging-samples"))
 }
 
diff --git a/paging/paging-rxjava3/build.gradle b/paging/paging-rxjava3/build.gradle
index e046913..ff402ad 100644
--- a/paging/paging-rxjava3/build.gradle
+++ b/paging/paging-rxjava3/build.gradle
@@ -57,7 +57,6 @@
     inceptionYear = "2020"
     description = "Android Paging-RxJava3"
     legacyDisableKotlinStrictApiMode = true
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/paging/paging-testing/bcv/native/current.txt b/paging/paging-testing/bcv/native/current.txt
index bfa820a..692f951 100644
--- a/paging/paging-testing/bcv/native/current.txt
+++ b/paging/paging-testing/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/paging/paging-testing/build.gradle b/paging/paging-testing/build.gradle
index 434fac4..c30a878 100644
--- a/paging/paging-testing/build.gradle
+++ b/paging/paging-testing/build.gradle
@@ -33,8 +33,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm()
     mac()
     linux()
@@ -125,6 +123,7 @@
     type = LibraryType.PUBLISHED_TEST_LIBRARY
     inceptionYear = "2022"
     description = "Test artifact for Paging implementation"
+    metalavaK2UastEnabled = false
 }
 
 android {
diff --git a/palette/palette-ktx/build.gradle b/palette/palette-ktx/build.gradle
index 206b05c..e32c1cf 100644
--- a/palette/palette-ktx/build.gradle
+++ b/palette/palette-ktx/build.gradle
@@ -43,7 +43,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for 'palette' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/palette/palette/build.gradle b/palette/palette/build.gradle
index 04dc2e7..e66fa0e 100644
--- a/palette/palette/build.gradle
+++ b/palette/palette/build.gradle
@@ -28,7 +28,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2014"
     description = "Android Support Palette"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/pdf/OWNERS b/pdf/OWNERS
index 45b25c4..144951d 100644
--- a/pdf/OWNERS
+++ b/pdf/OWNERS
@@ -1,7 +1,7 @@
 # Bug component: 325029
 [email protected]
 [email protected]
[email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
diff --git a/pdf/integration-tests/testapp/build.gradle b/pdf/integration-tests/testapp/build.gradle
index 1b1a2e5..f6170f4 100644
--- a/pdf/integration-tests/testapp/build.gradle
+++ b/pdf/integration-tests/testapp/build.gradle
@@ -6,17 +6,18 @@
 
 android {
     namespace 'androidx.pdf.testapp'
+    buildToolsVersion "35.0.0-rc1"
 
     defaultConfig {
         applicationId "androidx.pdf.testapp"
-        minSdkVersion 31
+        minSdk 35
+        compileSdk 35
+        targetSdk 35
     }
 }
 
 dependencies {
-
     api("com.google.android.material:material:1.11.0")
-    implementation(project(":pdf:pdf-viewer"))
-
+    implementation(project(":pdf:pdf-viewer-fragment"))
     implementation(libs.constraintLayout)
 }
\ No newline at end of file
diff --git a/pdf/integration-tests/testapp/src/main/AndroidManifest.xml b/pdf/integration-tests/testapp/src/main/AndroidManifest.xml
index 4a33224..2c55785 100644
--- a/pdf/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/pdf/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -28,9 +28,7 @@
         tools:replace="android:label">
         <activity
             android:name=".MainActivity"
-            android:exported="true"
-            android:windowSoftInputMode="adjustResize"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout">
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
diff --git a/pdf/integration-tests/testapp/src/main/java/androidx/pdf/testapp/LegacyMainActivity.java b/pdf/integration-tests/testapp/src/main/java/androidx/pdf/testapp/LegacyMainActivity.java
new file mode 100644
index 0000000..01344f4
--- /dev/null
+++ b/pdf/integration-tests/testapp/src/main/java/androidx/pdf/testapp/LegacyMainActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.testapp;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.pdf.viewer.PdfViewer;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressWarnings({"deprecation", "RestrictedApiAndroidX"})
+public class LegacyMainActivity extends AppCompatActivity {
+
+    private PdfViewer mPdfViewer;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        Button getContentButton = findViewById(R.id.launch_button);
+        assert getContentButton != null;
+        getContentButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+                intent.addCategory(Intent.CATEGORY_OPENABLE);
+                intent.setType("application/pdf");
+                startActivityForResult(intent, 2);
+            }
+        });
+    }
+
+    @Override
+    protected void onActivityResult(int requestcode, int resultcode, @Nullable Intent data) {
+        super.onActivityResult(requestcode, resultcode, data);
+        assert data != null;
+        Uri uri = data.getData();
+        if (uri != null) {
+            setPdfViewer();
+            mPdfViewer.loadFile(uri);
+        }
+    }
+
+    void setPdfViewer() {
+
+        FragmentManager fragmentManager = getSupportFragmentManager();
+        FragmentTransaction transaction = fragmentManager.beginTransaction();
+
+        mPdfViewer = new PdfViewer();
+        transaction.replace(R.id.fragment_container_view, mPdfViewer, null);
+        transaction.commitAllowingStateLoss();
+        fragmentManager.executePendingTransactions();
+    }
+}
diff --git a/pdf/integration-tests/testapp/src/main/kotlin/androidx/pdf/testapp/MainActivity.kt b/pdf/integration-tests/testapp/src/main/kotlin/androidx/pdf/testapp/MainActivity.kt
index 8b8669c..b75ec64 100644
--- a/pdf/integration-tests/testapp/src/main/kotlin/androidx/pdf/testapp/MainActivity.kt
+++ b/pdf/integration-tests/testapp/src/main/kotlin/androidx/pdf/testapp/MainActivity.kt
@@ -16,38 +16,72 @@
 
 package androidx.pdf.testapp
 
+import android.annotation.SuppressLint
 import android.net.Uri
 import android.os.Bundle
 import androidx.activity.result.contract.ActivityResultContracts.GetContent
 import androidx.annotation.RestrictTo
 import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentTransaction
 import com.google.android.material.button.MaterialButton
 
+@SuppressLint("RestrictedApiAndroidX")
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 class MainActivity : AppCompatActivity() {
 
-    companion object {
-        private const val MIME_TYPE_PDF = "application/pdf"
-    }
+    private var pdfViewerFragment: androidx.pdf.PdfViewerFragment? = null
 
     private val filePicker =
         registerForActivityResult(GetContent()) { uri: Uri? ->
-            uri?.let {
-                setPdfView()
-                // TODO: Implement loading PDF from URI using latest APIs
-            }
+            uri?.let { pdfViewerFragment?.documentUri = uri }
         }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
 
+        if (pdfViewerFragment == null) {
+            pdfViewerFragment =
+                supportFragmentManager.findFragmentByTag(PDF_VIEWER_FRAGMENT_TAG)
+                    as androidx.pdf.PdfViewerFragment?
+        }
+
         val getContentButton: MaterialButton = findViewById(R.id.launch_button)
+        val searchButton: MaterialButton = findViewById(R.id.search_button)
 
         getContentButton.setOnClickListener { filePicker.launch(MIME_TYPE_PDF) }
+        searchButton.setOnClickListener { setFindInFileViewVisible() }
+        if (savedInstanceState == null) {
+            setPdfView()
+        }
     }
 
     private fun setPdfView() {
-        // TODO: Implement this based on new APIs
+        val fragmentManager: FragmentManager = supportFragmentManager
+
+        // Fragment initialization
+        pdfViewerFragment = androidx.pdf.PdfViewerFragment()
+        val transaction: FragmentTransaction = fragmentManager.beginTransaction()
+
+        // Replace an existing fragment in a container with an instance of a new fragment
+        transaction.replace(
+            R.id.fragment_container_view,
+            pdfViewerFragment!!,
+            PDF_VIEWER_FRAGMENT_TAG
+        )
+        transaction.commitAllowingStateLoss()
+        fragmentManager.executePendingTransactions()
+    }
+
+    private fun setFindInFileViewVisible() {
+        if (pdfViewerFragment != null) {
+            pdfViewerFragment!!.isTextSearchActive = true
+        }
+    }
+
+    companion object {
+        private const val MIME_TYPE_PDF = "application/pdf"
+        private const val PDF_VIEWER_FRAGMENT_TAG = "pdf_viewer_fragment_tag"
     }
 }
diff --git a/pdf/integration-tests/testapp/src/main/res/layout/activity_main.xml b/pdf/integration-tests/testapp/src/main/res/layout/activity_main.xml
index 7820e52..76f5b6cf 100644
--- a/pdf/integration-tests/testapp/src/main/res/layout/activity_main.xml
+++ b/pdf/integration-tests/testapp/src/main/res/layout/activity_main.xml
@@ -17,19 +17,38 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/fragment_container_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_gravity="center"
-    android:fitsSystemWindows="true"
     android:orientation="vertical"
     tools:context=".MainActivity">
 
-    <com.google.android.material.button.MaterialButton
-        android:id="@+id/launch_button"
-        android:layout_width="wrap_content"
+    <FrameLayout
+        android:id="@+id/fragment_container_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/launch_string"
-        android:textSize="16sp" />
+        android:orientation="horizontal">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/launch_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/launch_string"
+            android:textSize="16sp" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/search_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/search_string"
+            android:textSize="16sp" />
+    </LinearLayout>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/pdf/integration-tests/testapp/src/main/res/values-night/strings.xml b/pdf/integration-tests/testapp/src/main/res/values-night/strings.xml
new file mode 100644
index 0000000..5b89e97
--- /dev/null
+++ b/pdf/integration-tests/testapp/src/main/res/values-night/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="launch_string">Launch AOSP PDF Viewer</string>
+</resources>
\ No newline at end of file
diff --git a/pdf/integration-tests/testapp/src/main/res/values-night/themes.xml b/pdf/integration-tests/testapp/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..df40dfd
--- /dev/null
+++ b/pdf/integration-tests/testapp/src/main/res/values-night/themes.xml
@@ -0,0 +1,23 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Base.Theme.Androidx" parent="Theme.Material3.DayNight.NoActionBar">
+        <!-- Customize your dark theme here. -->
+        <!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
+    </style>
+</resources>
\ No newline at end of file
diff --git a/pdf/integration-tests/testapp/src/main/res/values/colors.xml b/pdf/integration-tests/testapp/src/main/res/values/colors.xml
new file mode 100644
index 0000000..8175d9c
--- /dev/null
+++ b/pdf/integration-tests/testapp/src/main/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/pdf/integration-tests/testapp/src/main/res/values/strings.xml b/pdf/integration-tests/testapp/src/main/res/values/strings.xml
index 91a7c87..ee8e986 100644
--- a/pdf/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/pdf/integration-tests/testapp/src/main/res/values/strings.xml
@@ -1,4 +1,5 @@
 <resources>
     <string name="app_name">PDF Viewer Integration Tests</string>
     <string name="launch_string">Launch AOSP PDF Viewer</string>
+    <string name="search_string">Search</string>
 </resources>
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/pdf/pdf-viewer-fragment/api/current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to pdf/pdf-viewer-fragment/api/current.txt
diff --git a/benchmark/benchmark-common/api/res-1.3.0-beta01.txt b/pdf/pdf-viewer-fragment/api/res-current.txt
similarity index 100%
copy from benchmark/benchmark-common/api/res-1.3.0-beta01.txt
copy to pdf/pdf-viewer-fragment/api/res-current.txt
diff --git a/credentials/credentials-play-services-auth/api/1.3.0-beta01.txt b/pdf/pdf-viewer-fragment/api/restricted_current.txt
similarity index 100%
copy from credentials/credentials-play-services-auth/api/1.3.0-beta01.txt
copy to pdf/pdf-viewer-fragment/api/restricted_current.txt
diff --git a/pdf/pdf-viewer-fragment/build.gradle b/pdf/pdf-viewer-fragment/build.gradle
new file mode 100644
index 0000000..62e1c47
--- /dev/null
+++ b/pdf/pdf-viewer-fragment/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    api(project(":pdf:pdf-viewer"))
+    api("androidx.core:core:1.13.1")
+}
+
+android {
+    namespace "androidx.pdf.viewer.fragment"
+
+    defaultConfig {
+        minSdk 31
+        buildToolsVersion "35.0.0-rc1"
+        compileSdk 35
+    }
+}
+
+androidx {
+    name = "androidx.pdf:pdf-viewer-fragment"
+    type = LibraryType.PUBLISHED_LIBRARY
+    inceptionYear = "2024"
+    description = "This library can be used in your Android application to embed a fragment that renders a PDF document and provides functionalities to interact with it."
+}
diff --git a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/PdfViewerFragment.kt b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/PdfViewerFragment.kt
new file mode 100644
index 0000000..b6b4574
--- /dev/null
+++ b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/PdfViewerFragment.kt
@@ -0,0 +1,777 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf
+
+import android.content.ContentResolver
+import android.content.res.Configuration
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.annotation.RestrictTo
+import androidx.core.os.BundleCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.updateLayoutParams
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.pdf.data.DisplayData
+import androidx.pdf.data.FutureValue
+import androidx.pdf.data.Openable
+import androidx.pdf.fetcher.Fetcher
+import androidx.pdf.find.FindInFileView
+import androidx.pdf.models.PageSelection
+import androidx.pdf.select.SelectionActionMode
+import androidx.pdf.util.AnnotationUtils
+import androidx.pdf.util.ObservableValue.ValueObserver
+import androidx.pdf.util.Observables
+import androidx.pdf.util.Observables.ExposedValue
+import androidx.pdf.util.Preconditions
+import androidx.pdf.util.Uris
+import androidx.pdf.viewer.LayoutHandler
+import androidx.pdf.viewer.LoadingView
+import androidx.pdf.viewer.PageSelectionValueObserver
+import androidx.pdf.viewer.PageViewFactory
+import androidx.pdf.viewer.PaginatedView
+import androidx.pdf.viewer.PaginationModel
+import androidx.pdf.viewer.PdfSelectionHandles
+import androidx.pdf.viewer.PdfSelectionModel
+import androidx.pdf.viewer.SearchQueryObserver
+import androidx.pdf.viewer.SelectedMatch
+import androidx.pdf.viewer.SelectedMatchValueObserver
+import androidx.pdf.viewer.SingleTapHandler
+import androidx.pdf.viewer.ZoomScrollValueObserver
+import androidx.pdf.viewer.loader.PdfLoader
+import androidx.pdf.viewer.loader.PdfLoaderCallbacksImpl
+import androidx.pdf.viewmodel.PdfLoaderViewModel
+import androidx.pdf.widget.FastScrollView
+import androidx.pdf.widget.ZoomView
+import androidx.pdf.widget.ZoomView.ZoomScroll
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import kotlinx.coroutines.launch
+
+/**
+ * A Fragment that renders a PDF document.
+ *
+ * <p>A [PdfViewerFragment] that can display paginated PDFs. The viewer includes a FAB for
+ * annotation support and a search menu. Each page is rendered in its own View. Upon creation, this
+ * fragment displays a loading spinner.
+ *
+ * <p>Rendering is done in 2 passes:
+ * <ol>
+ * <li>Layout: Request the page data, get the dimensions and set them as measure for the image view.
+ * <li>Render: Create bitmap(s) at adequate dimensions and attach them to the page view.
+ * </ol>
+ *
+ * <p>The layout pass is progressive: starts with a few first pages of the document, then reach
+ * further as the user scrolls down (and ultimately spans the whole document). The rendering pass is
+ * tightly limited to the currently visible pages. Pages that are scrolled past (become not visible)
+ * have their bitmaps released to free up memory.
+ *
+ * @see documentUri
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public open class PdfViewerFragment : Fragment() {
+
+    // ViewModel to manage PdfLoader state
+    private val viewModel: PdfLoaderViewModel by viewModels()
+
+    /** Single access to the PDF document: loads contents asynchronously (bitmaps, text,...) */
+    private var pdfLoader: PdfLoader? = null
+
+    /** True when this Fragment's life-cycle is between [.onStart] and [.onStop]. */
+    private var started = false
+
+    /**
+     * True when this Viewer is on-screen (but independent on whether it is actually started, so it
+     * could be invisible, because obscured by another app). This value is controlled by [postEnter]
+     * and [.exit].
+     */
+    private var onScreen = false
+
+    /** Marks that [onEnter] must be run after [onCreateView]. */
+    private var delayedEnter = false
+    private var hasContents = false
+
+    private var container: ViewGroup? = null
+    private var viewState: ExposedValue<ViewState> =
+        Observables.newExposedValueWithInitialValue(ViewState.NO_VIEW)
+    private var zoomView: ZoomView? = null
+    private var paginatedView: PaginatedView? = null
+    private var fastScrollView: FastScrollView? = null
+    private var selectionObserver: ValueObserver<PageSelection>? = null
+    private var selectionActionMode: SelectionActionMode? = null
+    private var localUri: Uri? = null
+
+    private var fetcher: Fetcher? = null
+    private var zoomScrollObserver: ValueObserver<ZoomScroll>? = null
+    private var searchQueryObserver: ValueObserver<String>? = null
+    private var selectedMatchObserver: ValueObserver<SelectedMatch>? = null
+    private var pdfViewer: FrameLayout? = null
+    private var findInFileView: FindInFileView? = null
+    private var singleTapHandler: SingleTapHandler? = null
+
+    /** Callbacks of PDF loading asynchronous tasks. */
+    private var pdfLoaderCallbacks: PdfLoaderCallbacksImpl? = null
+
+    /** A saved [.onContentsAvailable] runnable to be run after [.onCreateView]. */
+    private var delayedContentsAvailable: Runnable? = null
+    private var loadingView: LoadingView? = null
+    private var paginationModel: PaginationModel? = null
+    private var layoutHandler: LayoutHandler? = null
+    private var pageViewFactory: PageViewFactory? = null
+    private var selectionHandles: PdfSelectionHandles? = null
+    private var annotationButton: FloatingActionButton? = null
+    private var fileData: DisplayData? = null
+
+    internal var shouldRedrawOnDocumentLoaded = false
+    internal var isAnnotationIntentResolvable = false
+    internal var documentLoaded = false
+
+    /**
+     * The URI of the PDF document to display defaulting to `null`.
+     *
+     * When this property is set, the fragment begins loading the PDF. A loading spinner is
+     * displayed until the document is fully loaded. If an error occurs during loading, an error
+     * message is displayed, and the detailed exception can be captured by overriding
+     * [onLoadDocumentError].
+     */
+    public var documentUri: Uri? = null
+        set(value) {
+            field = value
+            if (value != null) {
+                loadFile(value)
+            }
+        }
+
+    /**
+     * Controls the visibility of the "find in file" menu. Defaults to `false`.
+     *
+     * Set to `true` to display the menu, or `false` to hide it.
+     */
+    public var isTextSearchActive: Boolean = false
+        set(value) {
+            field = value
+            findInFileView!!.setFindInFileView(value)
+        }
+
+    /**
+     * Callback invoked when an error occurs while loading the PDF document.
+     *
+     * Override this method to handle document loading errors. The default implementation displays a
+     * generic error message in the loading view.
+     *
+     * @param throwable [Throwable] that occurred during document loading.
+     */
+    @Suppress("UNUSED_PARAMETER") public fun onLoadDocumentError(throwable: Throwable) {}
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        fetcher = Fetcher.build(requireContext(), 1)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        super.onCreateView(inflater, container, savedInstanceState)
+        this.container = container
+
+        if (!hasContents && delayedContentsAvailable == null) {
+            if (savedInstanceState != null) {
+                restoreContents(savedInstanceState)
+            }
+        }
+
+        pdfViewer = inflater.inflate(R.layout.pdf_viewer_container, container, false) as FrameLayout
+        findInFileView = pdfViewer?.findViewById(R.id.search)
+        fastScrollView = pdfViewer?.findViewById(R.id.fast_scroll_view)
+        loadingView = pdfViewer?.findViewById(R.id.loadingView)
+        paginatedView = fastScrollView?.findViewById(R.id.pdf_view)
+        paginationModel = paginatedView!!.paginationModel
+        zoomView = pdfViewer?.findViewById(R.id.zoom_view)
+
+        pdfViewer?.isScrollContainer = true
+
+        annotationButton = pdfViewer?.findViewById(R.id.edit_fab)
+
+        // All views are inflated, update the view state.
+        if (viewState.get() == ViewState.NO_VIEW || viewState.get() == ViewState.ERROR) {
+            viewState.set(ViewState.VIEW_CREATED)
+            // View Inflated, show loading view
+            loadingView?.showLoadingView()
+        }
+
+        adjustInsetsForSearchMenu(findInFileView!!)
+
+        pdfLoaderCallbacks =
+            PdfLoaderCallbacksImpl(
+                requireContext(),
+                requireActivity().supportFragmentManager,
+                fastScrollView!!,
+                zoomView!!,
+                paginatedView!!,
+                loadingView!!,
+                viewState,
+                view,
+                onRequestPassword = { onScreen ->
+                    if (!(isResumed && onScreen)) {
+                        // This would happen if the service decides to start while we're in
+                        // the background. The dialog code below would then crash. We can't just
+                        // bypass it because then we'd have a started service with no loaded PDF
+                        // and no means to load it. The best way is to just kill the service which
+                        // will restart on the next onStart.
+                        pdfLoader?.disconnect()
+                    }
+                },
+                onDocumentLoaded = {
+                    documentLoaded = true
+                    if (shouldRedrawOnDocumentLoaded) {
+                        shouldRedrawOnDocumentLoaded = false
+                    }
+
+                    if (annotationButton != null && isAnnotationIntentResolvable) {
+                        annotationButton?.visibility = View.VISIBLE
+                    }
+                },
+                onDocumentLoadFailure = { thrown -> onLoadDocumentError(thrown) }
+            )
+
+        setUpEditFab()
+
+        viewModel.pdfLoaderStateFlow.value?.let { loader ->
+            pdfLoader = loader
+            refreshContentAndModels(loader)
+        }
+
+        return pdfViewer
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        // Using lifecycleScope to collect the flow
+        viewLifecycleOwner.lifecycleScope.launch {
+            viewModel.pdfLoaderStateFlow.collect { loader ->
+                loader?.let {
+                    pdfLoader = loader
+                    setContents(savedInstanceState)
+                }
+            }
+        }
+    }
+
+    override fun onStart() {
+        delayedContentsAvailable?.run()
+        super.onStart()
+        started = true
+        if (delayedEnter || onScreen) {
+            onEnter()
+            delayedEnter = false
+        }
+    }
+
+    override fun onStop() {
+        if (onScreen) {
+            onExit()
+        }
+        onScreen = false
+        started = false
+        super.onStop()
+    }
+
+    /**
+     * Adjusts the [FindInFileView] in portrait to be displayed on top of the keyboard in portrait
+     * mode but this is not required in landscape mode as the keyboard is detached from the bottom
+     * of the view.
+     */
+    private fun adjustInsetsForSearchMenu(view: FindInFileView) {
+        if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+            WindowCompat.setDecorFitsSystemWindows(
+                requireActivity().window,
+                /* decorFitsSystemWindows= */ false
+            )
+
+            // Set the listener to handle window insets
+            ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
+                val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime())
+                v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
+                    bottomMargin = imeInsets.bottom
+                }
+
+                // Consume only the IME insets
+                insets.inset(imeInsets)
+            }
+        }
+    }
+
+    /** Called after this viewer enters the screen and becomes visible. */
+    private fun onEnter() {
+        participateInAccessibility(true)
+
+        // This is necessary for password protected PDF documents. If the user failed to produce the
+        // correct password, we want to prompt for the correct password every time the film strip
+        // comes back to this viewer.
+        if (!documentLoaded) {
+            pdfLoader?.reconnect()
+        }
+
+        if (paginatedView != null && paginatedView?.childCount!! > 0) {
+            pdfLoaderCallbacks?.loadPageAssets(zoomView?.zoomScroll()?.get()!!)
+        }
+    }
+
+    /** Called after this viewer exits the screen and becomes invisible to the user. */
+    private fun onExit() {
+        participateInAccessibility(false)
+        if (!documentLoaded) {
+            // e.g. a password-protected pdf that wasn't loaded.
+            pdfLoader?.disconnect()
+        }
+    }
+
+    /**
+     * Notifies this Viewer goes on-screen. Guarantees that [.onEnter] will be called now or when
+     * the Viewer is started.
+     */
+    private fun postEnter() {
+        pdfLoaderCallbacks?.onScreen = true
+        onScreen = true
+        if (started) {
+            onEnter()
+        } else {
+            delayedEnter = true
+        }
+    }
+
+    private fun isStarted(): Boolean {
+        return started
+    }
+
+    /**
+     * Posts a [.onContentsAvailable] method to be run as soon as permitted (when this Viewer has
+     * its view hierarchy built up and [.onCreateView] has finished). It might run right now if the
+     * Viewer is currently started.
+     */
+    private fun postContentsAvailable(contents: DisplayData) {
+        Preconditions.checkState(delayedContentsAvailable == null, "Already waits for contents")
+
+        if (isStarted()) {
+            onContentsAvailable(contents)
+            hasContents = true
+        } else {
+            delayedContentsAvailable = Runnable {
+                Preconditions.checkState(
+                    !hasContents,
+                    "Received contents while restoring another copy"
+                )
+                onContentsAvailable(contents)
+                delayedContentsAvailable = null
+                hasContents = true
+            }
+        }
+    }
+
+    private fun onContentsAvailable(contents: DisplayData) {
+        fileData = contents
+
+        // Update the PdfLoader in the ViewModel with the new DisplayData
+        viewModel.updatePdfLoader(
+            requireActivity().applicationContext,
+            contents,
+            pdfLoaderCallbacks!!
+        )
+    }
+
+    /**
+     * Sets PDF viewer content. Initializes/configures components based on provided data and saved
+     * state.
+     *
+     * @param savedState Saved state (e.g., layout) or null.
+     */
+    private fun setContents(savedState: Bundle?) {
+        savedState?.let { state ->
+            val showAnnotationButton = state.getBoolean(KEY_SHOW_ANNOTATION)
+            isAnnotationIntentResolvable =
+                showAnnotationButton && findInFileView!!.visibility != View.VISIBLE
+        }
+
+        refreshContentAndModels(pdfLoader!!)
+
+        savedState?.let { state ->
+            state.containsKey(KEY_LAYOUT_REACH).let {
+                val layoutReach = state.getInt(KEY_LAYOUT_REACH)
+                layoutHandler?.setInitialPageLayoutReachWithMax(layoutReach)
+            }
+
+            // Restore page selection from saved state if it exists
+            val savedSelection =
+                BundleCompat.getParcelable(state, KEY_PAGE_SELECTION, PageSelection::class.java)
+            savedSelection?.let { pdfLoaderCallbacks?.selectionModel?.setSelection(it) }
+
+            savedState.containsKey(KEY_TEXT_SEARCH_ACTIVE).let {
+                val textSearchActive = savedState.getBoolean(KEY_TEXT_SEARCH_ACTIVE)
+                if (textSearchActive) {
+                    findInFileView!!.setFindInFileView(true)
+                }
+            }
+        }
+    }
+
+    private fun updateSelectionModel(updatedSelectionModel: PdfSelectionModel) {
+        pdfLoaderCallbacks?.selectionModel = updatedSelectionModel
+        zoomView?.setPdfSelectionModel(updatedSelectionModel)
+        paginatedView?.selectionModel = updatedSelectionModel
+
+        selectionActionMode =
+            SelectionActionMode(requireActivity(), paginatedView!!, updatedSelectionModel)
+        selectionHandles =
+            PdfSelectionHandles(
+                updatedSelectionModel,
+                zoomView!!,
+                paginatedView!!,
+                selectionActionMode!!
+            )
+        paginatedView?.selectionHandles = selectionHandles!!
+    }
+
+    private fun updatePageViewFactory(updatedPageViewFactory: PageViewFactory) {
+        pageViewFactory = updatedPageViewFactory
+        pdfLoaderCallbacks?.pageViewFactory = updatedPageViewFactory
+        paginatedView?.pageViewFactory = updatedPageViewFactory
+
+        selectionObserver =
+            PageSelectionValueObserver(
+                paginatedView!!,
+                paginationModel!!,
+                pageViewFactory!!,
+                requireContext()
+            )
+        pdfLoaderCallbacks?.selectionModel?.selection()?.addObserver(selectionObserver)
+    }
+
+    private fun updateSearchModel() {
+        findInFileView?.searchModel?.let { model ->
+            pdfLoaderCallbacks?.searchModel = model
+            paginatedView?.searchModel = model
+            searchQueryObserver = SearchQueryObserver(paginatedView!!)
+            model.query().addObserver(searchQueryObserver)
+        }
+    }
+
+    private fun updateSingleTapHandler(pdfLoader: PdfLoader, selectionModel: PdfSelectionModel) {
+        singleTapHandler =
+            SingleTapHandler(
+                requireContext(),
+                annotationButton!!,
+                findInFileView!!,
+                zoomView!!,
+                selectionModel,
+                paginationModel!!,
+                layoutHandler!!
+            )
+        singleTapHandler!!.setAnnotationIntentResolvable(isAnnotationIntentResolvable)
+
+        pageViewFactory =
+            PageViewFactory(
+                requireContext(),
+                pdfLoader,
+                paginatedView!!,
+                zoomView!!,
+                singleTapHandler!!
+            )
+        updatePageViewFactory(pageViewFactory!!)
+    }
+
+    private fun refreshContentAndModels(pdfLoader: PdfLoader) {
+        paginationModel = paginatedView!!.initPaginationModelAndPageRangeHandler(requireActivity())
+
+        paginatedView?.setPdfLoader(pdfLoader)
+        findInFileView?.setPdfLoader(pdfLoader)
+        pdfLoaderCallbacks?.pdfLoader = pdfLoader
+
+        layoutHandler = LayoutHandler(pdfLoader)
+
+        val updatedSelectionModel = PdfSelectionModel(pdfLoader)
+        updateSelectionModel(updatedSelectionModel)
+        updateSingleTapHandler(pdfLoader, updatedSelectionModel)
+        updateSearchModel()
+
+        pdfLoaderCallbacks!!.layoutHandler = layoutHandler
+        zoomScrollObserver =
+            ZoomScrollValueObserver(
+                zoomView!!,
+                paginatedView!!,
+                layoutHandler!!,
+                annotationButton!!,
+                findInFileView!!,
+                isAnnotationIntentResolvable,
+                selectionActionMode!!,
+                viewState
+            )
+        zoomView?.zoomScroll()?.addObserver(zoomScrollObserver)
+
+        selectedMatchObserver =
+            SelectedMatchValueObserver(
+                paginatedView!!,
+                paginationModel!!,
+                pageViewFactory!!,
+                zoomView!!,
+                layoutHandler!!,
+                requireContext()
+            )
+        findInFileView!!.searchModel.selectedMatch().addObserver(selectedMatchObserver)
+
+        annotationButton?.let { findInFileView!!.setAnnotationButton(it) }
+    }
+
+    /** Restores the contents of this Viewer when it is automatically restored by android. */
+    private fun restoreContents(savedState: Bundle?) {
+        val dataBundle = savedState?.getBundle(KEY_DATA)
+        if (dataBundle != null) {
+            try {
+                fileData = DisplayData.fromBundle(dataBundle)
+                fileData?.let {
+                    localUri = it.uri
+                    postContentsAvailable(it)
+                }
+            } catch (e: Exception) {
+                // This can happen if the data is an instance of StreamOpenable, and the client
+                // app that owns it has been killed by the system. We will still recover,
+                // but log this.
+                viewState.set(ViewState.ERROR)
+                onLoadDocumentError(e)
+            }
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        if (!documentLoaded) {
+            return
+        }
+        if (annotationButton?.visibility != View.VISIBLE) {
+            annotationButton?.post {
+                annotationButton?.visibility = View.VISIBLE
+                annotationButton?.alpha = 0f
+                annotationButton?.animate()?.alpha(1f)?.setDuration(200)?.start()
+            }
+        }
+    }
+
+    private fun destroyContentModel() {
+        pdfLoader?.cancelAll()
+
+        paginationModel = null
+
+        selectionHandles?.destroy()
+        selectionHandles = null
+
+        pdfLoaderCallbacks?.selectionModel = null
+        selectionActionMode?.destroy()
+
+        findInFileView?.searchModel?.let {
+            it.selectedMatch().removeObserver(selectedMatchObserver!!)
+            it.query().removeObserver(searchQueryObserver!!)
+        }
+
+        pdfLoaderCallbacks?.searchModel = null
+
+        pdfLoader = null
+        documentLoaded = false
+    }
+
+    private fun destroyView() {
+        zoomScrollObserver?.let { zoomView?.zoomScroll()?.removeObserver(it) }
+        paginatedView?.let { view ->
+            view.removeAllViews()
+            paginationModel?.removeObserver(view)
+        }
+
+        zoomView = null
+        paginatedView = null
+
+        pdfLoader?.cancelAll()
+        documentLoaded = false
+        if (viewState.get() !== ViewState.NO_VIEW) {
+            viewState.set(ViewState.NO_VIEW)
+        }
+        if (container != null && view != null && container === requireView().parent) {
+            // Some viewers add extra views to their container, e.g. toasts. Remove them all.
+            // Do not remove what's under it though.
+            val count = container?.childCount
+            var child: View
+            if (count != null) {
+                for (i in count - 1 downTo 1) {
+                    child = container!!.getChildAt(i)
+                    container?.removeView(child)
+                    if (child === view) {
+                        break
+                    }
+                }
+            }
+        }
+    }
+
+    override fun onDestroyView() {
+        destroyView()
+        container = null
+        pdfLoaderCallbacks = null
+        super.onDestroyView()
+        (zoomScrollObserver as? ZoomScrollValueObserver)?.clearAnnotationHandler()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        if (pdfLoader != null) {
+            destroyContentModel()
+        }
+    }
+
+    override fun onSaveInstanceState(outState: Bundle) {
+        super.onSaveInstanceState(outState)
+        outState.putBundle(KEY_DATA, fileData?.asBundle())
+        layoutHandler?.let { outState.putInt(KEY_LAYOUT_REACH, it.pageLayoutReach) }
+        outState.putBoolean(KEY_SHOW_ANNOTATION, isAnnotationIntentResolvable)
+        pdfLoaderCallbacks?.selectionModel?.let {
+            outState.putParcelable(KEY_PAGE_SELECTION, it.selection().get())
+        }
+        findInFileView?.let {
+            outState.putBoolean(KEY_TEXT_SEARCH_ACTIVE, it.visibility == View.VISIBLE)
+        }
+    }
+
+    private fun loadFile(fileUri: Uri) {
+        Preconditions.checkNotNull(fileUri)
+        if (pdfLoader != null) {
+            destroyContentModel()
+        }
+        if (paginatedView?.childCount!! > 0) {
+            paginatedView?.removeAllViews()
+        }
+        try {
+            validateFileUri(fileUri)
+            fetchFile(fileUri)
+        } catch (e: SecurityException) {
+            onLoadDocumentError(e)
+        }
+        if (localUri != null && localUri != fileUri) {
+            annotationButton?.visibility = View.GONE
+        }
+        localUri = fileUri
+        isAnnotationIntentResolvable =
+            AnnotationUtils.resolveAnnotationIntent(requireContext(), localUri!!)
+        singleTapHandler?.setAnnotationIntentResolvable(isAnnotationIntentResolvable)
+        findInFileView!!.setAnnotationIntentResolvable(isAnnotationIntentResolvable)
+        (zoomScrollObserver as? ZoomScrollValueObserver)?.setAnnotationIntentResolvable(
+            isAnnotationIntentResolvable
+        )
+    }
+
+    private fun validateFileUri(fileUri: Uri) {
+        if (!Uris.isContentUri(fileUri) && !Uris.isFileUri(fileUri)) {
+            throw IllegalArgumentException("Only content and file uri is supported")
+        }
+    }
+
+    private fun fetchFile(fileUri: Uri) {
+        Preconditions.checkNotNull(fileUri)
+        val fileName: String = getFileName(fileUri)
+        val openable: FutureValue<Openable> = fetcher?.loadLocal(fileUri)!!
+
+        openable[
+            object : FutureValue.Callback<Openable> {
+                override fun available(value: Openable) {
+                    viewerAvailable(fileUri, fileName, value)
+                }
+
+                override fun failed(thrown: Throwable) {
+                    onLoadDocumentError(thrown)
+                }
+
+                override fun progress(progress: Float) {}
+            }]
+    }
+
+    private fun getFileName(fileUri: Uri): String {
+        val resolver: ContentResolver? = getResolver()
+        return if (resolver != null) Uris.extractName(fileUri, resolver)
+        else Uris.extractFileName(fileUri)
+    }
+
+    private fun getResolver(): ContentResolver? {
+        if (activity != null) {
+            return requireActivity().contentResolver
+        }
+        return null
+    }
+
+    private fun viewerAvailable(fileUri: Uri, fileName: String, openable: Openable) {
+        val contents = DisplayData(fileUri, fileName, openable)
+
+        startViewer(contents)
+    }
+
+    private fun startViewer(contents: DisplayData) {
+        Preconditions.checkNotNull(contents)
+        try {
+            feed(contents)
+            postEnter()
+        } catch (exception: Exception) {
+            onLoadDocumentError(exception)
+        }
+    }
+
+    /** Feed this Viewer with contents to be displayed. */
+    private fun feed(contents: DisplayData?): PdfViewerFragment {
+        if (contents != null) {
+            postContentsAvailable(contents)
+        }
+        return this
+    }
+
+    /** Makes the views of this Viewer visible to TalkBack (in the swipe gesture circus) or not. */
+    private fun participateInAccessibility(participate: Boolean) {
+        view?.importantForAccessibility =
+            if (participate) View.IMPORTANT_FOR_ACCESSIBILITY_YES
+            else View.IMPORTANT_FOR_ACCESSIBILITY_NO
+    }
+
+    private fun setUpEditFab() {
+        annotationButton?.setOnClickListener(View.OnClickListener { performEdit() })
+    }
+
+    private fun performEdit() {
+        val intent = AnnotationUtils.getAnnotationIntent(localUri!!)
+        intent.setData(localUri)
+        startActivity(intent)
+    }
+
+    private companion object {
+        /** Key for saving page layout reach in bundles. */
+        private const val KEY_LAYOUT_REACH: String = "plr"
+        private const val KEY_DATA: String = "data"
+        private const val KEY_TEXT_SEARCH_ACTIVE: String = "isTextSearchActive"
+        private const val KEY_SHOW_ANNOTATION: String = "showEditFab"
+        private const val KEY_PAGE_SELECTION: String = "currentPageSelection"
+    }
+}
diff --git a/pdf/pdf-viewer/build.gradle b/pdf/pdf-viewer/build.gradle
index 787fa02..45aca39 100644
--- a/pdf/pdf-viewer/build.gradle
+++ b/pdf/pdf-viewer/build.gradle
@@ -26,11 +26,14 @@
 dependencies {
     api(libs.rxjava2)
     api(libs.guavaAndroid)
+    api(libs.kotlinCoroutinesCore)
+    api("androidx.fragment:fragment-ktx:1.8.1")
     api("com.google.android.material:material:1.6.0")
 
     implementation(libs.kotlinStdlib)
     implementation("androidx.exifinterface:exifinterface:1.3.2")
 
+    testImplementation(project(":pdf:pdf-viewer-fragment"))
     testImplementation(libs.junit)
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
@@ -42,6 +45,7 @@
     testImplementation(libs.testExtTruth)
     testImplementation(libs.testExtJunitKtx)
     testImplementation("androidx.fragment:fragment-testing:1.7.1")
+    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.junit)
@@ -59,7 +63,9 @@
     namespace "androidx.pdf"
 
     defaultConfig {
-        minSdkVersion 30
+        minSdk 31
+        buildToolsVersion "35.0.0-rc1"
+        compileSdk 35
     }
 
     buildFeatures {
@@ -72,12 +78,6 @@
         }
     }
 
-    externalNativeBuild {
-        cmake {
-            path file('src/main/native/CMakeLists.txt')
-            version libs.versions.cmake.get()
-        }
-    }
     sourceSets {
         test {
             assets {
diff --git a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java
index 3e2c6ca..f240ae0 100644
--- a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java
+++ b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfLoaderTest.java
@@ -269,17 +269,6 @@
 
     @Test
     @UiThreadTest
-    public void testCloneWithoutSecurity() throws InterruptedException, RemoteException {
-        CountDownLatch latch = new CountDownLatch(1);
-        mWeakPdfLoaderCallbacks.setClonedLatch(latch);
-        mPdfLoader.cloneWithoutSecurity(mFileOutputStream);
-        /** Wait for {@link TestCallbacks#documentCloned(boolean)} to be called. */
-        latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        verify(mPdfDocument).cloneWithoutSecurity(any(ParcelFileDescriptor.class));
-    }
-
-    @Test
-    @UiThreadTest
     public void testGotoLinksTask() throws RemoteException, InterruptedException {
         getGotoLinks(mPdfLoader);
         verify(mPdfDocument).getPageGotoLinks(PAGE);
@@ -290,6 +279,8 @@
     public void testLoadDocumentTask() throws InterruptedException, RemoteException {
         CountDownLatch latch = new CountDownLatch(1);
         mWeakPdfLoaderCallbacks.setDocumentLoadedLatch(latch);
+        // ensure document is not already loaded
+        when(mConnection.isLoaded()).thenReturn(false);
         mPdfLoader.applyPassword(TEST_PW);
         /** Wait for {@link TestCallbacks#documentLoaded(int)} ()} to be called. */
         latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
@@ -365,14 +356,6 @@
         }
 
         @Override
-        public void documentCloned(boolean result) {
-            super.documentCloned(result);
-            if (mClonedLatch != null) {
-                mClonedLatch.countDown();
-            }
-        }
-
-        @Override
         public void documentLoaded(int numPages) {
             super.documentLoaded(numPages);
             if (mDocumentLoadedLatch != null) {
diff --git a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java
index 1466502..fe945ea 100644
--- a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java
+++ b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/viewer/loader/PdfTaskExecutorTest.java
@@ -25,7 +25,7 @@
 import android.os.RemoteException;
 
 import androidx.pdf.models.PdfDocumentRemote;
-import androidx.pdf.pdflib.PdfDocumentRemoteProto;
+import androidx.pdf.service.PdfDocumentRemoteProto;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 
diff --git a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/widget/FastScrollViewIntegrationTest.kt b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/widget/FastScrollViewIntegrationTest.kt
new file mode 100644
index 0000000..f15fb9f
--- /dev/null
+++ b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/widget/FastScrollViewIntegrationTest.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.widget
+
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.pdf.TestActivity
+import androidx.pdf.models.Dimensions
+import androidx.pdf.viewer.PaginatedView
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Integration tests for [FastScrollView] */
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class FastScrollViewIntegrationTest {
+    @get:Rule
+    val activityScenario: ActivityScenarioRule<TestActivity> =
+        ActivityScenarioRule(TestActivity::class.java)
+
+    private lateinit var fastScrollView: FastScrollView
+    private lateinit var zoomView: ZoomView
+    private lateinit var paginatedView: PaginatedView
+
+    private val dragHandle: View
+        get() = fastScrollView.dragHandle
+
+    private val pageIndicator: TextView
+        get() = fastScrollView.pageIndicator
+
+    private fun configureViews() {
+        activityScenario.scenario.onActivity { activity ->
+            // Build layout from the bottom up
+            // Start by adding a PaginatedView with 10 50x50 pages
+            paginatedView =
+                PaginatedView(activity).apply {
+                    initPaginationModelAndPageRangeHandler(activity).apply {
+                        initialize(10)
+                        for (i in 0..9) {
+                            addPage(i, Dimensions(50, 50))
+                        }
+                    }
+                    model = paginationModel
+                }
+            // Add a ZoomView to host the PaginatedView
+            zoomView =
+                ZoomView(activity).apply {
+                    layoutParams = ViewGroup.LayoutParams(100, 400)
+                    addView(paginatedView)
+                }
+            // Add a FastScrollView to host the ZoomView
+            fastScrollView =
+                FastScrollView(activity).apply {
+                    layoutParams = ViewGroup.LayoutParams(100, 400)
+                    setPaginationModel(paginatedView.paginationModel)
+                    addView(zoomView)
+                }
+            activity.setContentView(fastScrollView)
+            // FastScrollView expects to be inflated from XML, so simulate this
+            fastScrollView.onFinishInflate()
+        }
+    }
+
+    @Test
+    fun pageIndicatorUpdatesOnScroll() {
+        configureViews()
+        activityScenario.scenario.onActivity {
+            // Indicator is hidden
+            assertThat(pageIndicator.visibility).isEqualTo(View.GONE)
+
+            // Overscroll the bottom
+            zoomView.scrollTo(0, 2000, true)
+            assertThat(pageIndicator.visibility).isEqualTo(View.VISIBLE)
+            assertThat(pageIndicator.text).isEqualTo("9-10 / 10")
+
+            // Overscroll the top
+            zoomView.scrollTo(0, -50, true)
+            assertThat(pageIndicator.visibility).isEqualTo(View.VISIBLE)
+            assertThat(pageIndicator.text).isEqualTo("1-3 / 10")
+        }
+    }
+
+    @Test
+    fun contentScrollsWithDragHandle() {
+        configureViews()
+        activityScenario.scenario.onActivity {
+            // Setup: reveal drag handle by scrolling up 50 pixels
+            zoomView.scrollTo(0, 50, true)
+            assertThat(dragHandle.alpha).isEqualTo(1.0f)
+            val contentPosInit = zoomView.scrollY
+
+            // Drag the handle to the top
+            fastScrollView.onTouchEvent(
+                downEvent(dragHandle.x + dragHandle.width / 2, dragHandle.y + dragHandle.height / 2)
+            )
+            fastScrollView.onTouchEvent(moveEvent(dragHandle.x + dragHandle.height / 2, 0f))
+
+            assertThat(zoomView.scrollY).isLessThan(contentPosInit)
+        }
+    }
+
+    @Test
+    fun dragHandleScrollsWithContent() {
+        configureViews()
+        activityScenario.scenario.onActivity {
+            val handlePosInit = dragHandle.translationY
+            // Drag handle is initially hidden
+            assertThat(dragHandle.alpha).isEqualTo(0F)
+
+            zoomView.scrollTo(0, 50, true)
+
+            // Drag handle is revealed and has translated downwards
+            assertThat(dragHandle.alpha).isEqualTo(1.0f)
+            assertThat(dragHandle.translationY).isGreaterThan(handlePosInit)
+        }
+    }
+
+    private fun motionEvent(
+        action: Int,
+        x: Float,
+        y: Float,
+    ) = MotionEvent.obtain(0L, 0L, action, x, y, 0)
+
+    private fun downEvent(x: Float, y: Float) = motionEvent(MotionEvent.ACTION_DOWN, x, y)
+
+    private fun moveEvent(x: Float, y: Float) = motionEvent(MotionEvent.ACTION_MOVE, x, y)
+}
diff --git a/pdf/pdf-viewer/src/main/AndroidManifest.xml b/pdf/pdf-viewer/src/main/AndroidManifest.xml
index cd24525..eadf1dd 100644
--- a/pdf/pdf-viewer/src/main/AndroidManifest.xml
+++ b/pdf/pdf-viewer/src/main/AndroidManifest.xml
@@ -19,7 +19,7 @@
     <application android:label="PdfViewer">
 
         <service
-            android:name="androidx.pdf.pdflib.PdfDocumentService"
+            android:name="androidx.pdf.service.PdfDocumentService"
             android:isolatedProcess="true"
             tools:ignore="MissingServiceExportedEqualsTrue" />
 
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/PdfViewerFragment.kt b/pdf/pdf-viewer/src/main/java/androidx/pdf/PdfViewerFragment.kt
deleted file mode 100644
index 385ab21..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/PdfViewerFragment.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf
-
-import android.net.Uri
-import androidx.annotation.RestrictTo
-import androidx.fragment.app.Fragment
-
-/**
- * A Fragment that renders a PDF document.
- *
- * <p>A [PdfViewerFragment] that can display paginated PDFs. The viewer includes a FAB for
- * annotation support and a search menu. Each page is rendered in its own View. Upon creation, this
- * fragment displays a loading spinner.
- *
- * <p>Rendering is done in 2 passes:
- * <ol>
- * <li>Layout: Request the page data, get the dimensions and set them as measure for the image view.
- * <li>Render: Create bitmap(s) at adequate dimensions and attach them to the page view.
- * </ol>
- *
- * <p>The layout pass is progressive: starts with a few first pages of the document, then reach
- * further as the user scrolls down (and ultimately spans the whole document). The rendering pass is
- * tightly limited to the currently visible pages. Pages that are scrolled past (become not visible)
- * have their bitmaps released to free up memory.
- *
- * @see documentUri
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-open class PdfViewerFragment : Fragment() {
-
-    /**
-     * The URI of the PDF document to display defaulting to `null`.
-     *
-     * When this property is set, the fragment begins loading the PDF. A loading spinner is
-     * displayed until the document is fully loaded. If an error occurs during loading, an error
-     * message is displayed, and the detailed exception can be captured by overriding
-     * [onLoadDocumentError].
-     */
-    var documentUri: Uri? = null
-
-    /**
-     * Controls the visibility of the "find in file" menu. Defaults to `false`.
-     *
-     * Set to `true` to display the menu, or `false` to hide it.
-     */
-    var isTextSearchActive: Boolean = false
-
-    /**
-     * Callback invoked when an error occurs while loading the PDF document.
-     *
-     * Override this method to handle document loading errors. The default implementation displays a
-     * generic error message in the loading view.
-     *
-     * @param throwable [Throwable] that occurred during document loading.
-     */
-    @Suppress("UNUSED_PARAMETER") fun onLoadDocumentError(throwable: Throwable) {}
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/ViewState.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/ViewState.java
new file mode 100644
index 0000000..35df007
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/ViewState.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * The state of the view hierarchy.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public enum ViewState {
+    /**
+     * The view hierarchy does not exist yet or anymore.
+     */
+    NO_VIEW,
+
+    /**
+     * The view hierarchy exists but may be showing no or partial contents.
+     */
+    VIEW_CREATED,
+
+    /**
+     * The view hierarchy is ready for prime time: all Views are populated and responding.
+     */
+    VIEW_READY,
+
+    /**
+     * There is no view because this Viewer failed to start up (e.g. broken file).
+     */
+    ERROR
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/data/DisplayData.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/data/DisplayData.java
index 3024526..66a723e 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/data/DisplayData.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/data/DisplayData.java
@@ -28,6 +28,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Objects;
 
 /**
  * File data that can be displayed in a Viewer. This class contains meta-data specific to Projector
@@ -143,4 +144,25 @@
                 "Display Data [%s] +%s, uri: %s",
                 mName, mOpenable.getClass().getSimpleName(), mUri);
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (!(obj instanceof DisplayData)) {
+            return false;
+        }
+
+        DisplayData other = (DisplayData) obj;
+        return mUri.equals(other.mUri)
+                && mName.equals(other.mName)
+                && mOpenable.equals(other.mOpenable);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUri, mName, mOpenable);
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java
index 624ba88..75e81ca 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java
@@ -34,8 +34,14 @@
 import androidx.annotation.RestrictTo;
 import androidx.pdf.R;
 import androidx.pdf.util.Accessibility;
+import androidx.pdf.util.CycleRange;
 import androidx.pdf.util.ObservableValue;
 import androidx.pdf.util.ObservableValue.ValueObserver;
+import androidx.pdf.viewer.PaginatedView;
+import androidx.pdf.viewer.SearchModel;
+import androidx.pdf.viewer.loader.PdfLoader;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
 
 import javax.annotation.Nullable;
 
@@ -52,10 +58,14 @@
     private ImageView mNextButton;
     private TextView mMatchStatus;
     private View mCloseButton;
+    private FloatingActionButton mAnnotationButton;
     private FindInFileListener mFindInFileListener;
     private ObservableValue<MatchCount> mMatchCount;
+    private SearchModel mSearchModel;
+    private PaginatedView mPaginatedView;
+    private boolean mIsAnnotationIntentResolvable;
     private static final char MATCH_STATUS_COUNTING = '\u2026';
-
+    private static final String TAG = FindInFileView.class.getSimpleName();
     private final OnClickListener mOnClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -154,13 +164,115 @@
     }
 
     /**
-     * registers the {@link FindInFileListener}
-     * @param findInFileListener
+     * Sets the pdfLoader and create a new {@link SearchModel} instance with the given pdfLoader.
      */
-    public void setFindInFileListener(@Nullable FindInFileListener findInFileListener) {
+    public void setPdfLoader(@NonNull PdfLoader pdfLoader) {
+        mSearchModel = new SearchModel(pdfLoader);
+    }
+
+    public void setPaginatedView(@NonNull PaginatedView paginatedView) {
+        mPaginatedView = paginatedView;
+    }
+
+    @NonNull
+    public SearchModel getSearchModel() {
+        return mSearchModel;
+    }
+
+    public void setAnnotationButton(
+            @NonNull FloatingActionButton annotationButton) {
+        mAnnotationButton = annotationButton;
+    }
+
+    public void setAnnotationIntentResolvable(
+            boolean isAnnotationIntentResolvable) {
+        mIsAnnotationIntentResolvable = isAnnotationIntentResolvable;
+    }
+
+    /**
+     * Sets the visibility of the find-in-file view and configures its behavior.
+     *
+     * @param visibility true to show the find-in-file view, false to hide it.
+     */
+    public void setFindInFileView(boolean visibility) {
+        if (mSearchModel == null) {
+            return; // Ignore call. Models not initialized yet
+        }
+        if (visibility) {
+            this.setVisibility(VISIBLE);
+            if (mAnnotationButton != null && mAnnotationButton.getVisibility() == VISIBLE) {
+                mAnnotationButton.setVisibility(GONE);
+            }
+            setupFindInFileBtn();
+        } else {
+            this.setVisibility(GONE);
+        }
+    }
+
+    private void setupFindInFileBtn() {
+        setFindInFileListener(this.makeFindInFileListener());
+        queryBoxRequestFocus();
+
+        mCloseButton.setOnClickListener(view -> {
+            View parentLayout = (View) mCloseButton.getParent();
+            mQueryBox.clearFocus();
+            mQueryBox.setText("");
+            parentLayout.setVisibility(GONE);
+            if (mIsAnnotationIntentResolvable) {
+                mAnnotationButton.setVisibility(VISIBLE);
+            }
+        });
+    }
+
+    private FindInFileListener makeFindInFileListener() {
+        return new FindInFileListener() {
+            @Override
+            public boolean onQueryTextChange(@androidx.annotation.Nullable String query) {
+                if (mSearchModel != null && mPaginatedView != null) {
+                    mSearchModel.setQuery(query,
+                            mPaginatedView.getPageRangeHandler().getVisiblePage());
+                    return true;
+                }
+                return false;
+            }
+
+            @Override
+            public boolean onFindNextMatch(String query, boolean backwards) {
+                if (mSearchModel != null) {
+                    CycleRange.Direction direction;
+                    if (backwards) {
+                        direction = CycleRange.Direction.BACKWARDS;
+                    } else {
+                        direction = CycleRange.Direction.FORWARDS;
+                    }
+                    mSearchModel.selectNextMatch(direction,
+                            mPaginatedView.getPageRangeHandler().getVisiblePage());
+                    return true;
+                }
+                return false;
+            }
+
+            @androidx.annotation.Nullable
+            @Override
+            public ObservableValue<MatchCount> matchCount() {
+                return mSearchModel != null ? mSearchModel.matchCount() : null;
+            }
+        };
+    }
+
+    /**
+     * registers the {@link FindInFileListener}
+     */
+    private void setFindInFileListener(@Nullable FindInFileListener findInFileListener) {
         this.mFindInFileListener = findInFileListener;
         setObservableMatchCount(
                 (findInFileListener != null) ? findInFileListener.matchCount() : null);
+        if (!mQueryBox.getText().toString().isEmpty()) {
+            if (mFindInFileListener != null) {
+                mFindInFileListener.onQueryTextChange(mQueryBox.getText().toString());
+            }
+            mMatchStatus.setVisibility(VISIBLE);
+        }
     }
 
     private void setObservableMatchCount(@Nullable ObservableValue<MatchCount> matchCount) {
@@ -176,7 +288,7 @@
     /**
      * Shows the keyboard when find in file view is inflated.
      */
-    public void queryBoxRequestFocus() {
+    private void queryBoxRequestFocus() {
         mQueryBox.requestFocus();
     }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLink.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLink.java
index b029958..44ac3b1 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLink.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLink.java
@@ -18,14 +18,19 @@
 
 import android.annotation.SuppressLint;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.pdf.content.PdfPageGotoLinkContent;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ext.SdkExtensions;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 
 import com.google.common.base.Preconditions;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
@@ -77,6 +82,25 @@
     }
 
     /**
+     * Converts android.graphics.pdf.content.PdfPageGotoLinkContent object to its
+     * androidx.pdf.aidl.GotoLink representation.
+     */
+    @NonNull
+    public static GotoLink convert(@NonNull PdfPageGotoLinkContent pdfPageGotoLinkContent) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            List<Rect> rectBounds = new ArrayList<>();
+            List<RectF> rectFBounds = pdfPageGotoLinkContent.getBounds();
+            for (RectF rectF : rectFBounds) {
+                rectBounds.add(new Rect((int) rectF.left, (int) rectF.top, (int) rectF.right,
+                        (int) rectF.bottom));
+            }
+            return new GotoLink(rectBounds,
+                    GotoLinkDestination.convert(pdfPageGotoLinkContent.getDestination()));
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    /**
      * Gets the bounds of a {@link GotoLink} represented as a list of {@link Rect}.
      * Links which are spread across multiple lines will be surrounded by multiple {@link Rect}
      * in order of viewing.
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLinkDestination.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLinkDestination.java
index 11c2137..e7eae7f 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLinkDestination.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/GotoLinkDestination.java
@@ -17,8 +17,11 @@
 package androidx.pdf.models;
 
 import android.annotation.SuppressLint;
+import android.graphics.pdf.content.PdfPageGotoLinkContent;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ext.SdkExtensions;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -140,4 +143,20 @@
         parcel.writeFloat(mYCoordinate);
         parcel.writeFloat(mZoom);
     }
+
+    /**
+     * Converts android.graphics.pdf.content.PdfPageGotoLinkContent.Destination object to its
+     * androidx.pdf.aidl.GotoLinkDestination representation.
+     */
+    @NonNull
+    public static GotoLinkDestination convert(
+            @NonNull PdfPageGotoLinkContent.Destination pdfPageGotoLinkContentDest) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            return new GotoLinkDestination(pdfPageGotoLinkContentDest.getPageNumber(),
+                    pdfPageGotoLinkContentDest.getXCoordinate(),
+                    pdfPageGotoLinkContentDest.getYCoordinate(),
+                    pdfPageGotoLinkContentDest.getZoom());
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/LinkRects.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/LinkRects.java
index 9d51435..640ef5c 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/LinkRects.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/LinkRects.java
@@ -18,8 +18,12 @@
 
 import android.annotation.SuppressLint;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.pdf.content.PdfPageLinkContent;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ext.SdkExtensions;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -27,6 +31,7 @@
 import androidx.pdf.data.ListOfList;
 import androidx.pdf.util.Preconditions;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -114,4 +119,68 @@
         parcel.writeList(mLinkToRect);
         parcel.writeList(mUrls);
     }
+
+    /**
+     * Flattens the list of PdfPageLinkContent objects and converts to a LinkRects objects.
+     * <p>As an example, in case there are 2 weblinks on the page of the document with the 1st link
+     * overflowing to the next line, {@code List<PdfPageLinkContent>} would have the following
+     * values -
+     * <pre>
+     * List(
+     *      PdfPageLinkContent(
+     *          bounds = [RectF(l1, t1, r1, b1), RectF(l2, t2, r2, b2)],
+     *          url = url1
+     *      ),
+     *      PdfPageLinkContent(
+     *          bounds = [RectF(l3, t3, r3, b3)],
+     *          url = url2
+     *      ),
+     * )
+     *
+     * Using the method below, we can flatten the {@code List<PdfPageLinkContent>} to the following
+     * representation -
+     * LinkRects(
+     *      mRects=[Rect(l1, t1, r1, b1), Rect(l2, t2, r2, b2), Rect(l3, t3, r3, b3)],
+     *      mLinkToRect=[0,2],
+     *      mUrls=[url1, url2]
+     * )
+     * </pre>
+     */
+    @NonNull
+    public static LinkRects flattenList(@NonNull List<PdfPageLinkContent> pdfPageLinkContentList) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            List<Rect> rects = new ArrayList<>();
+            List<Integer> linkToRect = new ArrayList<>();
+            List<String> urls = new ArrayList<>();
+            int numRects = 0;
+            for (PdfPageLinkContent pdfPageLinkContent : pdfPageLinkContentList) {
+                List<RectF> rectFBounds = pdfPageLinkContent.getBounds();
+                for (RectF rectF : rectFBounds) {
+                    rects.add(new Rect((int) rectF.left, (int) rectF.top, (int) rectF.right,
+                            (int) rectF.bottom));
+                }
+                urls.add(pdfPageLinkContent.getUri().toString());
+                linkToRect.add(numRects);
+                numRects += pdfPageLinkContent.getBounds().size();
+            }
+
+            return new LinkRects(rects, linkToRect, urls);
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    @NonNull
+    public List<Rect> getRects() {
+        return mRects;
+    }
+
+    @NonNull
+    public List<Integer> getLinkToRect() {
+        return mLinkToRect;
+    }
+
+    @NonNull
+    public List<String> getUrls() {
+        return mUrls;
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/MatchRects.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/MatchRects.java
index 4dade10..e12080e 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/MatchRects.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/MatchRects.java
@@ -18,8 +18,12 @@
 
 import android.annotation.SuppressLint;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.pdf.models.PageMatchBounds;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ext.SdkExtensions;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
@@ -27,6 +31,7 @@
 import androidx.pdf.util.Preconditions;
 
 import java.util.AbstractList;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -161,4 +166,66 @@
         parcel.writeList(mMatchToRect);
         parcel.writeList(mCharIndexes);
     }
+
+    /**
+     * Flattens the list of PageMatchBounds objects and converts it to a MatchRects objects.
+     * <p>As an example, in case there are 2 matches on the page of the document with the 1st match
+     * overflowing to the next line, {@code List<PageMatchBounds>} would have the following values -
+     * <pre>
+     * List(
+     *      PageMatchBounds(
+     *          bounds = [RectF(l1, t1, r1, b1), RectF(l2, t2, r2, b2)],
+     *          mTextStartIndex = 1
+     *      ),
+     *      PageMatchBounds(
+     *          bounds = [RectF(l3, t3, r3, b3)],
+     *          mTextStartIndex = 3
+     *      ),
+     * )
+     *
+     * Using the method below, we can flatten the {@code List<PageMatchBounds>} to the following
+     * representation -
+     * MatchRects(
+     *      mRects=[Rect(l1, t1, r1, b1), Rect(l2, t2, r2, b2), Rect(l3, t3, r3, b3)],
+     *      mMatchToRect=[0,2],
+     *      mCharIndexes=[1, 3]
+     * )
+     * </pre>
+     */
+    @NonNull
+    public static MatchRects flattenList(@NonNull List<PageMatchBounds> pageMatchBoundsList) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            List<Rect> rects = new ArrayList<>();
+            List<Integer> matchToRect = new ArrayList<>();
+            List<Integer> charIndexes = new ArrayList<>();
+            int numRects = 0;
+            for (PageMatchBounds pageMatchBound : pageMatchBoundsList) {
+                List<RectF> rectFBounds = pageMatchBound.getBounds();
+                for (RectF rectF : rectFBounds) {
+                    rects.add(new Rect((int) rectF.left, (int) rectF.top, (int) rectF.right,
+                            (int) rectF.bottom));
+                }
+                matchToRect.add(numRects);
+                numRects += pageMatchBound.getBounds().size();
+                charIndexes.add(pageMatchBound.getTextStartIndex());
+            }
+            return new MatchRects(rects, matchToRect, charIndexes);
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    @NonNull
+    public List<Rect> getRects() {
+        return mRects;
+    }
+
+    @NonNull
+    public List<Integer> getMatchToRect() {
+        return mMatchToRect;
+    }
+
+    @NonNull
+    public List<Integer> getCharIndexes() {
+        return mCharIndexes;
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/PageSelection.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/PageSelection.java
index 0ba0168..0e9d985 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/PageSelection.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/PageSelection.java
@@ -17,12 +17,17 @@
 package androidx.pdf.models;
 
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.pdf.content.PdfPageTextContent;
+import android.os.Build;
 import android.os.Parcel;
+import android.os.ext.SdkExtensions;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.pdf.data.TextSelection;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /** Represents text selection on a particular page of a PDF. Immutable. */
@@ -82,13 +87,6 @@
     }
 
     @Override
-    public String toString() {
-        return String.format("PageSelection(page=%d, start=%s, stop=%s, %d rects)", mPage,
-                getStart(),
-                getStop(), mRects.size());
-    }
-
-    @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeInt(mPage);
         parcel.writeParcelable(getStart(), 0);
@@ -96,4 +94,35 @@
         parcel.writeList(mRects);
         parcel.writeString(mText);
     }
+
+    /**
+     * Converts android.graphics.pdf.models.selection.PageSelection object to its
+     * androidx.pdf.aidl.PageSelection representation.
+     */
+    @NonNull
+    public static PageSelection convert(
+            @NonNull android.graphics.pdf.models.selection.PageSelection pageSelection) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            List<PdfPageTextContent> textSelections = pageSelection.getSelectedTextContents();
+
+            // TODO: Add list handling instead of taking its first element
+            String selectedText = textSelections.get(0).getText();
+
+            List<Rect> rectBounds = new ArrayList<Rect>();
+            // TODO: Add list handling instead of taking its first element
+            List<RectF> rectFBounds = textSelections.get(0).getBounds();
+            for (RectF rectF : rectFBounds) {
+                rectBounds.add(new Rect((int) rectF.left, (int) rectF.top, (int) rectF.right,
+                        (int) rectF.bottom));
+            }
+
+            return new PageSelection(pageSelection.getPage(),
+                    SelectionBoundary.convert(pageSelection.getStart(),
+                            pageSelection.getStart().getIsRtl()),
+                    SelectionBoundary.convert(pageSelection.getStop(),
+                            pageSelection.getStop().getIsRtl()),
+                    rectBounds, selectedText);
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/SelectionBoundary.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/SelectionBoundary.java
index 01294ab..e8c5b98 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/models/SelectionBoundary.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/models/SelectionBoundary.java
@@ -18,8 +18,10 @@
 
 import android.annotation.SuppressLint;
 import android.graphics.Point;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ext.SdkExtensions;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
@@ -101,12 +103,6 @@
     }
 
     @Override
-    public String toString() {
-        String indexStr = (mIndex == Integer.MAX_VALUE) ? "MAX" : Integer.toString(mIndex);
-        return String.format("@%s (%d,%d)", indexStr, mX, mY);
-    }
-
-    @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeIntArray(new int[]{mIndex, mX, mY, mIsRtl ? 1 : 0});
     }
@@ -135,4 +131,41 @@
         result = 31 * result + (mIsRtl ? 1231 : 1237);
         return result;
     }
+
+    /**
+     * Converts android.graphics.pdf.models.selection.SelectionBoundary object to its
+     * androidx.pdf.aidl.SelectionBoundary representation.
+     */
+    @NonNull
+    public static SelectionBoundary convert(
+            @NonNull android.graphics.pdf.models.selection.SelectionBoundary selectionBoundary,
+            boolean isRtl) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            if (selectionBoundary.getPoint() == null) {
+                return new SelectionBoundary(selectionBoundary.getIndex(), -1, -1, isRtl);
+            }
+            return new SelectionBoundary(selectionBoundary.getIndex(),
+                    selectionBoundary.getPoint().x,
+                    selectionBoundary.getPoint().y, isRtl);
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    /**
+     * Converts androidx.pdf.aidl.SelectionBoundary object to its
+     * android.graphics.pdf.models.selection.SelectionBoundary representation.
+     */
+    @NonNull
+    public static android.graphics.pdf.models.selection.SelectionBoundary convert(
+            @NonNull SelectionBoundary selectionBoundary) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            if (selectionBoundary.getIndex() == -1) {
+                return new android.graphics.pdf.models.selection.SelectionBoundary(
+                        new Point(selectionBoundary.getX(), selectionBoundary.getY()));
+            }
+            return new android.graphics.pdf.models.selection.SelectionBoundary(
+                    selectionBoundary.getIndex());
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/LoadPdfResult.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/LoadPdfResult.java
deleted file mode 100644
index 5ce75a3..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/LoadPdfResult.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.pdflib;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.pdf.data.PdfStatus;
-import androidx.pdf.util.Preconditions;
-
-/**
- * A struct that holds either a successfully loaded PdfDocument, or the reason why it failed.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class LoadPdfResult {
-
-    private final PdfStatus mStatus;
-    @Nullable
-    private final PdfDocument mPdfDocument;
-
-    public LoadPdfResult(int status, @Nullable PdfDocument pdfDocument) {
-        if (status == PdfStatus.LOADED.getNumber()) {
-            Preconditions.checkArgument(pdfDocument != null, "Missing pdfDocument");
-        } else {
-            Preconditions.checkArgument(pdfDocument == null,
-                    "Shouldn't construct " + "broken pdfDocument");
-        }
-        this.mStatus = PdfStatus.values()[status];
-        this.mPdfDocument = pdfDocument;
-    }
-
-    @NonNull
-    public PdfStatus getStatus() {
-        return mStatus;
-    }
-
-    @Nullable
-    public PdfDocument getPdfDocument() {
-        return mPdfDocument;
-    }
-
-    public boolean isLoaded() {
-        return mStatus == PdfStatus.LOADED;
-    }
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocument.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocument.java
deleted file mode 100644
index 66fef2ba..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocument.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.pdflib;
-
-import android.graphics.Bitmap;
-import android.os.ParcelFileDescriptor;
-
-import androidx.annotation.RestrictTo;
-import androidx.pdf.models.Dimensions;
-import androidx.pdf.models.GotoLink;
-import androidx.pdf.models.LinkRects;
-import androidx.pdf.models.MatchRects;
-import androidx.pdf.models.PageSelection;
-import androidx.pdf.models.SelectionBoundary;
-import androidx.pdf.util.StrictModeUtils;
-
-import java.util.List;
-
-// TODO: Delete this class once framework API calls are added
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class PdfDocument {
-    private static final String LIB_NAME = "pdfclient";
-
-    private final long mPdfDocPtr;
-
-    private final int mNumPages;
-
-    protected PdfDocument(long pdfDocPtr, int numPages) {
-        this.mPdfDocPtr = pdfDocPtr;
-        this.mNumPages = numPages;
-    }
-
-    public static LoadPdfResult createFromFd(int fd, String password) {
-        return null;
-    }
-
-    static void loadLibPdf() {
-        StrictModeUtils.bypass(() -> System.loadLibrary(LIB_NAME));
-    }
-
-    public void destroy() {
-    }
-
-    public boolean saveAs(ParcelFileDescriptor destination) {
-        return false;
-    }
-
-    public int numPages() {
-        return mNumPages;
-    }
-
-    public Dimensions getPageDimensions(int pageNum) {
-        return null;
-    }
-
-    public Bitmap renderPageFd(int pageNum, int pageWidth, int pageHeight, boolean hideTextAnnots) {
-        return null;
-    }
-
-    public Bitmap renderTileFd(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
-            int scaledPageHeight, int left, int top, boolean hideTextAnnots) {
-        return null;
-    }
-
-    public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) {
-        return false;
-    }
-
-    public String getPageText(int pageNum) {
-        return null;
-    }
-
-    public List<String> getPageAltText(int pageNum) {
-        return null;
-    }
-
-    public MatchRects searchPageText(int pageNum, String query) {
-        return null;
-    }
-
-    public PageSelection selectPageText(int pageNum, SelectionBoundary start,
-            SelectionBoundary stop) {
-        return null;
-    }
-
-    public LinkRects getPageLinks(int pageNum) {
-        return null;
-    }
-
-    public List<GotoLink> getPageGotoLinks(int pageNum) {
-        return null;
-    }
-
-    public boolean isPdfLinearized() {
-        return false;
-    }
-
-    public int getFormType() {
-        return -1;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("PdfDocument(%x, %d pages)", mPdfDocPtr, mNumPages);
-    }
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentRemoteProto.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentRemoteProto.java
deleted file mode 100644
index 5138d2d..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentRemoteProto.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.pdflib;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.pdf.models.PdfDocumentRemote;
-
-/**
- *
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class PdfDocumentRemoteProto {
-    private PdfDocumentRemote mRemote;
-
-    public PdfDocumentRemoteProto(@NonNull PdfDocumentRemote remote) {
-        this.mRemote = remote;
-    }
-
-    @NonNull
-    public PdfDocumentRemote getPdfDocumentRemote() {
-        return mRemote;
-    }
-
-    // TODO: Add goto links methods from the original kotlin file
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentService.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentService.java
deleted file mode 100644
index e7f6c28..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/pdflib/PdfDocumentService.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.pdflib;
-
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.pdf.data.FutureValues;
-import androidx.pdf.models.Dimensions;
-import androidx.pdf.models.GotoLink;
-import androidx.pdf.models.LinkRects;
-import androidx.pdf.models.MatchRects;
-import androidx.pdf.models.PageSelection;
-import androidx.pdf.models.PdfDocumentRemote;
-import androidx.pdf.models.SelectionBoundary;
-
-import java.util.List;
-
-/** Isolated Service wrapper around the PdfClient native lib, for security purposes. */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class PdfDocumentService extends Service {
-
-    private static final String TAG = "PdfDocumentService";
-
-    @NonNull
-    @Override
-    public IBinder onBind(Intent intent) {
-        return new PdfDocumentRemoteImpl();
-    }
-
-    @Override
-    public boolean onUnbind(Intent intent) {
-        return super.onUnbind(intent);
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-    }
-
-    private static class PdfDocumentRemoteImpl extends PdfDocumentRemote.Stub {
-
-        private final FutureValues.BlockingCallback<Boolean> mLoaderCallback =
-                new FutureValues.BlockingCallback<>();
-
-        private PdfDocument mPdfDocument;
-
-        PdfDocumentRemoteImpl() {
-        }
-
-        @Override
-        public int create(ParcelFileDescriptor pfd, String password) throws RemoteException {
-            mLoaderCallback.getBlocking();
-            ensurePdfDestroyed();
-            int fd = pfd.detachFd();
-            LoadPdfResult result = PdfDocument.createFromFd(fd, password);
-            if (result.isLoaded()) {
-                mPdfDocument = result.getPdfDocument();
-            }
-            return result.getStatus().getNumber();
-        }
-
-        @Override
-        public int numPages() {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.numPages();
-        }
-
-        @Override
-        public Dimensions getPageDimensions(int pageNum) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.getPageDimensions(pageNum);
-        }
-
-        @Override
-        public String getPageText(int pageNum) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.getPageText(pageNum);
-        }
-
-        @Override
-        public List<String> getPageAltText(int pageNum) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.getPageAltText(pageNum);
-        }
-
-        @Override
-        public Bitmap renderPage(int pageNum, int pageWidth, int pageHeight,
-                boolean hideTextAnnots) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.renderPageFd(pageNum, pageWidth, pageHeight, hideTextAnnots);
-        }
-
-        @Override
-        public Bitmap renderTile(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
-                int scaledPageHeight, int left, int top, boolean hideTextAnnots) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.renderTileFd(pageNum, tileWidth, tileHeight, scaledPageWidth,
-                    scaledPageHeight, left, top, hideTextAnnots);
-        }
-
-        @Override
-        public MatchRects searchPageText(int pageNum, String query) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.searchPageText(pageNum, query);
-        }
-
-        @Override
-        public PageSelection selectPageText(int pageNum, SelectionBoundary start,
-                SelectionBoundary stop) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.selectPageText(pageNum, start, stop);
-        }
-
-        @Override
-        public LinkRects getPageLinks(int pageNum) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.getPageLinks(pageNum);
-        }
-
-        @Override
-        public List<GotoLink> getPageGotoLinks(int pageNum) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.getPageGotoLinks(pageNum);
-        }
-
-        @Override
-        public boolean isPdfLinearized() {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.isPdfLinearized();
-        }
-
-        @Override
-        public int getFormType() {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.getFormType();
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            mLoaderCallback.getBlocking();
-            ensurePdfDestroyed();
-            super.finalize();
-        }
-
-        private void ensurePdfDestroyed() {
-            if (mPdfDocument != null) {
-                try {
-                    mPdfDocument.destroy();
-                } catch (Throwable ignored) {
-                }
-            }
-            mPdfDocument = null;
-        }
-
-        @Override
-        public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.cloneWithoutSecurity(destination);
-        }
-
-        @Override
-        public boolean saveAs(ParcelFileDescriptor destination) {
-            mLoaderCallback.getBlocking();
-            return mPdfDocument.saveAs(destination);
-        }
-    }
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionActionMode.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionActionMode.java
new file mode 100644
index 0000000..d0d4a6e
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionActionMode.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.select;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.R;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
+import androidx.pdf.util.ObservableValue;
+import androidx.pdf.util.Preconditions;
+import androidx.pdf.viewer.PageMosaicView;
+import androidx.pdf.viewer.PageViewFactory;
+import androidx.pdf.viewer.PaginatedView;
+
+import java.util.Objects;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SelectionActionMode {
+    private static final String TAG = "SelectionActionMode";
+    private final Context mContext;
+    private final PaginatedView mPaginatedView;
+    private final SelectionModel<PageSelection> mSelectionModel;
+    private final Object mSelectionObserverKey;
+    private final ActionMode.Callback2 mCallback;
+    private ActionMode mActionMode;
+    private PageSelection mCurrentSelection;
+
+    private final String mKeyCopiedText = "PdfCopiedText";
+
+    public SelectionActionMode(@NonNull Context context, @NonNull PaginatedView paginatedView,
+            @NonNull SelectionModel<PageSelection> selectionModel) {
+        Preconditions.checkNotNull(context, "Context should not be null");
+        Preconditions.checkNotNull(paginatedView, "paginatedView should not be null");
+        Preconditions.checkNotNull(paginatedView, "selectionModel should not be null");
+        Preconditions.checkNotNull(paginatedView, "callback should not be null");
+        this.mContext = context;
+        this.mPaginatedView = paginatedView;
+        this.mSelectionModel = selectionModel;
+        this.mCallback = new SelectionCallback();
+
+        mSelectionObserverKey = selectionModel.selection().addObserver(
+                new ObservableValue.ValueObserver<PageSelection>() {
+                    @Override
+                    public void onChange(PageSelection oldValue, PageSelection newValue) {
+                        mCurrentSelection = newValue;
+                        if (newValue == null) {
+                            stopActionMode();
+                        } else if (oldValue == null) {
+                            startActionMode();
+                        }
+                    }
+                });
+    }
+
+    /**
+     *
+     */
+    public void destroy() {
+        mSelectionModel.selection().removeObserver(mSelectionObserverKey);
+    }
+
+    /** Start this action mode - updates the menu, ensures it is visible. */
+    private void startActionMode() {
+        PageViewFactory.PageView pageView = mPaginatedView.getViewAt(
+                Objects.requireNonNull(mSelectionModel.mSelection.get()).getPage());
+        if (pageView != null) {
+            PageMosaicView pageMosaicView = pageView.getPageView();
+            pageMosaicView.startActionMode(mCallback, ActionMode.TYPE_FLOATING);
+        }
+    }
+
+    /** Resumes the context menu **/
+    public void resume() {
+        if (mCurrentSelection != null) {
+            startActionMode();
+        }
+    }
+
+    /** Stop this action mode. */
+    public void stopActionMode() {
+        if (mActionMode != null) {
+            mActionMode.finish();
+            mActionMode = null;
+        }
+    }
+
+    private void copyToClipboard(String text) {
+        try {
+            ClipboardManager clipboard = mContext.getSystemService(ClipboardManager.class);
+            ClipData clip = ClipData.newPlainText(mKeyCopiedText, text);
+            clipboard.setPrimaryClip(clip);
+        } catch (Exception e) {
+            Log.e(TAG, e.toString());
+        }
+    }
+
+    private class SelectionCallback extends ActionMode.Callback2 {
+        /** Called when the action mode is created. */
+        @Override
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            mActionMode = mode;
+
+            // Inflate the menu resource providing context menu items.
+            MenuInflater inflater = mode.getMenuInflater();
+            inflater.inflate(R.menu.context_menu, menu);
+            return true;
+
+        }
+
+        /**
+         * Called each time the action mode is shown. Always called after onCreateActionMode(),
+         * and might be called multiple times if the mode is invalidated.
+         */
+        @Override
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return false; // Return false as nothing needs to be done.
+        }
+
+        /** Called when the user selects a contextual menu item. */
+        @Override
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            if (item.getItemId() == R.id.action_selectAll) {
+                mSelectionModel.updateSelectionAsync(SelectionBoundary.PAGE_START,
+                        SelectionBoundary.PAGE_END);
+            } else if (item.getItemId() == R.id.action_copy) {
+                copyToClipboard(mSelectionModel.getText());
+                mSelectionModel.setSelection(null);
+            }
+            return true;
+
+        }
+
+        /** Called when the user exits the action mode. */
+        @Override
+        public void onDestroyActionMode(ActionMode mode) {
+            mode = null;
+
+        }
+
+        /**
+         * Called when an ActionMode needs to be positioned on screen, potentially occluding
+         * view content.
+         */
+        @Override
+        public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+            PageSelection pageSelection = mSelectionModel.selection().get();
+            Rect bounds = pageSelection.getRects().get(0);
+            outRect.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        }
+    }
+}
+
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/LoadPdfResult.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/LoadPdfResult.java
new file mode 100644
index 0000000..fcd7c7a
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/LoadPdfResult.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.service;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.data.PdfStatus;
+import androidx.pdf.util.Preconditions;
+
+/**
+ * A struct that holds either a successfully loaded PdfDocument, or the reason why it failed.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class LoadPdfResult {
+
+    private final PdfStatus mStatus;
+    @Nullable
+    private final PdfDocument mPdfDocument;
+
+    public LoadPdfResult(int status, @Nullable PdfDocument pdfDocument) {
+        if (status == PdfStatus.LOADED.getNumber()) {
+            Preconditions.checkArgument(pdfDocument != null, "Missing pdfDocument");
+        } else {
+            Preconditions.checkArgument(pdfDocument == null,
+                    "Shouldn't construct " + "broken pdfDocument");
+        }
+        this.mStatus = PdfStatus.values()[status];
+        this.mPdfDocument = pdfDocument;
+    }
+
+    @NonNull
+    public PdfStatus getStatus() {
+        return mStatus;
+    }
+
+    @Nullable
+    public PdfDocument getPdfDocument() {
+        return mPdfDocument;
+    }
+
+    public boolean isLoaded() {
+        return mStatus == PdfStatus.LOADED;
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocument.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocument.java
new file mode 100644
index 0000000..0818d63
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocument.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.service;
+
+import android.graphics.Bitmap;
+import android.os.ParcelFileDescriptor;
+
+import androidx.annotation.RestrictTo;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.GotoLink;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
+import androidx.pdf.util.StrictModeUtils;
+
+import java.util.List;
+
+// TODO: Delete this class once framework API calls are added
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class PdfDocument {
+    private static final String LIB_NAME = "pdfclient";
+
+    private final long mPdfDocPtr;
+
+    private final int mNumPages;
+
+    protected PdfDocument(long pdfDocPtr, int numPages) {
+        this.mPdfDocPtr = pdfDocPtr;
+        this.mNumPages = numPages;
+    }
+
+    public static LoadPdfResult createFromFd(int fd, String password) {
+        return null;
+    }
+
+    static void loadLibPdf() {
+        StrictModeUtils.bypass(() -> System.loadLibrary(LIB_NAME));
+    }
+
+    public void destroy() {
+    }
+
+    public boolean saveAs(ParcelFileDescriptor destination) {
+        return false;
+    }
+
+    public int numPages() {
+        return mNumPages;
+    }
+
+    public Dimensions getPageDimensions(int pageNum) {
+        return null;
+    }
+
+    public Bitmap renderPageFd(int pageNum, int pageWidth, int pageHeight, boolean hideTextAnnots) {
+        return null;
+    }
+
+    public Bitmap renderTileFd(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
+            int scaledPageHeight, int left, int top, boolean hideTextAnnots) {
+        return null;
+    }
+
+    public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) {
+        return false;
+    }
+
+    public String getPageText(int pageNum) {
+        return null;
+    }
+
+    public List<String> getPageAltText(int pageNum) {
+        return null;
+    }
+
+    public MatchRects searchPageText(int pageNum, String query) {
+        return null;
+    }
+
+    public PageSelection selectPageText(int pageNum, SelectionBoundary start,
+            SelectionBoundary stop) {
+        return null;
+    }
+
+    public LinkRects getPageLinks(int pageNum) {
+        return null;
+    }
+
+    public List<GotoLink> getPageGotoLinks(int pageNum) {
+        return null;
+    }
+
+    public boolean isPdfLinearized() {
+        return false;
+    }
+
+    public int getFormType() {
+        return -1;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PdfDocument(%x, %d pages)", mPdfDocPtr, mNumPages);
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentRemoteProto.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentRemoteProto.java
new file mode 100644
index 0000000..d9e62a5
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentRemoteProto.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.service;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.models.PdfDocumentRemote;
+
+/**
+ *
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PdfDocumentRemoteProto {
+    private PdfDocumentRemote mRemote;
+
+    public PdfDocumentRemoteProto(@NonNull PdfDocumentRemote remote) {
+        this.mRemote = remote;
+    }
+
+    @NonNull
+    public PdfDocumentRemote getPdfDocumentRemote() {
+        return mRemote;
+    }
+
+    // TODO: Add goto links methods from the original kotlin file
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentService.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentService.java
new file mode 100644
index 0000000..c6e1956
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentService.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.pdf.PdfRendererPreV;
+import android.graphics.pdf.content.PdfPageGotoLinkContent;
+import android.graphics.pdf.content.PdfPageImageContent;
+import android.graphics.pdf.content.PdfPageLinkContent;
+import android.graphics.pdf.content.PdfPageTextContent;
+import android.graphics.pdf.models.PageMatchBounds;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.ext.SdkExtensions;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.data.PdfStatus;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.GotoLink;
+import androidx.pdf.models.LinkRects;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.PdfDocumentRemote;
+import androidx.pdf.models.SelectionBoundary;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Isolated Service wrapper around the PdfClient native lib, for security purposes. */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PdfDocumentService extends Service {
+
+    @NonNull
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new PdfDocumentRemoteImpl();
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        return super.onUnbind(intent);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    private static class PdfDocumentRemoteImpl extends PdfDocumentRemote.Stub {
+        private PdfRendererAdapter mAdapter;
+
+        PdfDocumentRemoteImpl() {
+        }
+
+        @Override
+        public int create(ParcelFileDescriptor pfd, String password) {
+            try {
+                mAdapter = new PdfRendererAdapter(pfd, password);
+                return PdfStatus.LOADED.getNumber();
+            } catch (SecurityException e) {
+                return PdfStatus.REQUIRES_PASSWORD.getNumber();
+            } catch (Exception e) {
+                return PdfStatus.PDF_ERROR.getNumber();
+            }
+        }
+
+        @Override
+        public int numPages() {
+            return mAdapter.getPageCount();
+        }
+
+        @Override
+        public Dimensions getPageDimensions(int pageNum) {
+            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                return new Dimensions(pageAdapter.getWidth(),
+                        pageAdapter.getHeight());
+            }
+        }
+
+        @Override
+        public Bitmap renderPage(int pageNum, int pageWidth, int pageHeight,
+                boolean hideTextAnnots) {
+            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                Bitmap output = Bitmap.createBitmap(pageWidth, pageHeight, Bitmap.Config.ARGB_8888);
+                output.eraseColor(Color.WHITE);
+                pageAdapter.render(output);
+                return output;
+            }
+        }
+
+        @Override
+        public Bitmap renderTile(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
+                int scaledPageHeight, int left, int top, boolean hideTextAnnots) {
+            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                Bitmap output = Bitmap.createBitmap(tileWidth, tileHeight, Bitmap.Config.ARGB_8888);
+                output.eraseColor(Color.WHITE);
+                pageAdapter.renderTile(output, left, top, scaledPageWidth, scaledPageHeight);
+                return output;
+            }
+        }
+
+
+        @Override
+        public String getPageText(int pageNum) {
+            if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+                try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                    List<PdfPageTextContent> textPdfContentList = pageAdapter.getPageTextContents();
+                    // TODO: Add list handling instead of taking its first element
+                    return textPdfContentList.get(0).getText();
+                }
+            }
+            throw new UnsupportedOperationException("Operation support above S");
+        }
+
+        @Override
+        public List<String> getPageAltText(int pageNum) {
+            if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+                try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                    List<PdfPageImageContent> text = pageAdapter.getPageImageContents();
+                    return text.stream().map(PdfPageImageContent::getAltText).collect(
+                            Collectors.toList());
+                }
+            }
+            throw new UnsupportedOperationException("Operation support above S");
+        }
+
+        @Override
+        public MatchRects searchPageText(int pageNum, String query) {
+            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                List<PageMatchBounds> searchResultList = pageAdapter.searchPageText(query);
+                return MatchRects.flattenList(searchResultList);
+            }
+        }
+
+        @Override
+        public PageSelection selectPageText(int pageNum, SelectionBoundary start,
+                SelectionBoundary stop) {
+            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                android.graphics.pdf.models.selection.PageSelection pageSelection =
+                        pageAdapter.selectPageText(SelectionBoundary.convert(start),
+                                SelectionBoundary.convert(stop));
+                if (pageSelection != null) {
+                    return PageSelection.convert(pageSelection);
+                }
+                return null;
+            }
+        }
+
+        @Override
+        public LinkRects getPageLinks(int pageNum) {
+            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                List<PdfPageLinkContent> pageLinks = pageAdapter.getPageLinks();
+                return LinkRects.flattenList(pageLinks);
+            }
+        }
+
+        @Override
+        public List<GotoLink> getPageGotoLinks(int pageNum) {
+            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                List<PdfPageGotoLinkContent> gotoLinks = pageAdapter.getPageGotoLinks();
+                if (!gotoLinks.isEmpty()) {
+                    List<GotoLink> list = new ArrayList<>();
+                    for (PdfPageGotoLinkContent link : gotoLinks) {
+                        GotoLink convertedLink = GotoLink.convert(link);
+                        list.add(convertedLink);
+                    }
+                    return list;
+                }
+                return null;
+            }
+        }
+
+        @Override
+        public boolean isPdfLinearized() {
+            return mAdapter.getDocumentLinearizationType()
+                    == PdfRendererPreV.DOCUMENT_LINEARIZED_TYPE_LINEARIZED;
+        }
+
+        @Override
+        public int getFormType() {
+            return mAdapter.getDocumentFormType();
+
+        }
+
+        @Override
+        public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) {
+            // TODO: Implementation pending as use-case undiscovered.
+            return true;
+        }
+
+        @Override
+        public boolean saveAs(ParcelFileDescriptor destination) {
+            // TODO: Implementation pending as use-case undiscovered.
+            return true;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            mAdapter.close();
+            mAdapter = null;
+            super.finalize();
+        }
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfPageAdapter.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfPageAdapter.java
new file mode 100644
index 0000000..e6e638c
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfPageAdapter.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.service;
+
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.pdf.PdfRenderer;
+import android.graphics.pdf.PdfRendererPreV;
+import android.graphics.pdf.RenderParams;
+import android.graphics.pdf.content.PdfPageGotoLinkContent;
+import android.graphics.pdf.content.PdfPageImageContent;
+import android.graphics.pdf.content.PdfPageLinkContent;
+import android.graphics.pdf.content.PdfPageTextContent;
+import android.graphics.pdf.models.PageMatchBounds;
+import android.graphics.pdf.models.selection.PageSelection;
+import android.graphics.pdf.models.selection.SelectionBoundary;
+import android.os.Build;
+import android.os.ext.SdkExtensions;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.core.util.Supplier;
+
+import java.util.List;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class PdfPageAdapter implements AutoCloseable {
+    private int mPageNum;
+    private int mHeight;
+    private int mWidth;
+
+    private PdfRenderer.Page mPdfRendererPage;
+    private PdfRendererPreV.Page mPdfRendererPreVPage;
+
+    PdfPageAdapter(@NonNull PdfRenderer pdfRenderer, int pageNum) {
+        mPageNum = pageNum;
+        mPdfRendererPage = pdfRenderer.openPage(pageNum);
+        mHeight = mPdfRendererPage.getHeight();
+        mWidth = mPdfRendererPage.getWidth();
+    }
+
+    PdfPageAdapter(@NonNull PdfRendererPreV pdfRendererPreV, int pageNum) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            mPageNum = pageNum;
+            mPdfRendererPreVPage = pdfRendererPreV.openPage(pageNum);
+            mHeight = mPdfRendererPreVPage.getHeight();
+            mWidth = mPdfRendererPreVPage.getWidth();
+        }
+    }
+
+    public int getPageNum() {
+        return mPageNum;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public void render(@NonNull Bitmap bitmap) {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            mPdfRendererPage.render(bitmap, null, null, getRenderParams());
+        } else {
+            checkAndExecute(
+                    () -> mPdfRendererPreVPage.render(bitmap, null, null, getRenderParams()));
+        }
+    }
+
+    public void renderTile(@NonNull Bitmap bitmap,
+            int left, int top, int scaledPageWidth, int scaledPageHeight) {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            int pageWidth = mPdfRendererPage.getWidth();
+            int pageHeight = mPdfRendererPage.getHeight();
+            Matrix transform = getTransformationMatrix(left, top, (float) scaledPageWidth,
+                    (float) scaledPageHeight, pageWidth,
+                    pageHeight);
+            RenderParams renderParams = getRenderParams();
+            mPdfRendererPage.render(bitmap, null, transform, renderParams);
+        } else {
+            checkAndExecute(() -> {
+                {
+                    int pageWidth = mPdfRendererPreVPage.getWidth();
+                    int pageHeight = mPdfRendererPreVPage.getHeight();
+                    Matrix transform = getTransformationMatrix(left, top, (float) scaledPageWidth,
+                            (float) scaledPageHeight, pageWidth,
+                            pageHeight);
+                    RenderParams renderParams = getRenderParams();
+                    mPdfRendererPreVPage.render(bitmap, null, transform, renderParams);
+                }
+            });
+        }
+    }
+
+    @NonNull
+    public List<PdfPageTextContent> getPageTextContents() {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRendererPage.getTextContents();
+        }
+        return checkAndExecute(() -> mPdfRendererPreVPage.getTextContents());
+    }
+
+    @NonNull
+    public List<PdfPageImageContent> getPageImageContents() {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRendererPage.getImageContents();
+        }
+        return checkAndExecute(() -> mPdfRendererPreVPage.getImageContents());
+    }
+
+    @Nullable
+    public PageSelection selectPageText(@NonNull SelectionBoundary start,
+            @NonNull SelectionBoundary stop) {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRendererPage.selectContent(start, stop);
+        }
+        return checkAndExecute(() -> mPdfRendererPreVPage.selectContent(start, stop));
+    }
+
+    @NonNull
+    public List<PageMatchBounds> searchPageText(@NonNull String query) {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRendererPage.searchText(query);
+        }
+        return checkAndExecute(() -> mPdfRendererPreVPage.searchText(query));
+    }
+
+    @NonNull
+    public List<PdfPageLinkContent> getPageLinks() {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRendererPage.getLinkContents();
+        }
+        return checkAndExecute(() -> mPdfRendererPreVPage.getLinkContents());
+    }
+
+    @NonNull
+    public List<PdfPageGotoLinkContent> getPageGotoLinks() {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRendererPage.getGotoLinks();
+        }
+        return checkAndExecute(() -> mPdfRendererPreVPage.getGotoLinks());
+
+    }
+
+    private Matrix getTransformationMatrix(int left, int top, float scaledPageWidth,
+            float scaledPageHeight,
+            int pageWidth, int pageHeight) {
+        Matrix matrix = new Matrix();
+        matrix.setScale(scaledPageWidth / pageWidth,
+                scaledPageHeight / pageHeight);
+        matrix.postTranslate(-left, -top);
+        return matrix;
+    }
+
+    private RenderParams getRenderParams() {
+        return checkAndExecute(() -> {
+            RenderParams.Builder renderParamsBuilder = new RenderParams.Builder(
+                    RenderParams.RENDER_MODE_FOR_DISPLAY);
+            return renderParamsBuilder.setRenderFlags(0).build();
+        });
+    }
+
+    private static void checkAndExecute(@NonNull Runnable block) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            block.run();
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    private static <T> T checkAndExecute(@NonNull Supplier<T> block) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            return block.get();
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    @Override
+    public void close() {
+        if (mPdfRendererPage != null && Build.VERSION.SDK_INT >= 35) {
+            mPdfRendererPage.close();
+            mPdfRendererPage = null;
+        } else {
+            checkAndExecute(() -> {
+                mPdfRendererPreVPage.close();
+                mPdfRendererPreVPage = null;
+            });
+        }
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfRendererAdapter.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfRendererAdapter.java
new file mode 100644
index 0000000..f54ed0b
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfRendererAdapter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.service;
+
+import android.graphics.pdf.LoadParams;
+import android.graphics.pdf.PdfRenderer;
+import android.graphics.pdf.PdfRendererPreV;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.os.ext.SdkExtensions;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.core.util.Supplier;
+
+import java.io.IOException;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class PdfRendererAdapter implements AutoCloseable {
+    private PdfRenderer mPdfRenderer;
+    private PdfRendererPreV mPdfRendererPreV;
+
+    PdfRendererAdapter(@NonNull ParcelFileDescriptor parcelFileDescriptor,
+            @NonNull String password)
+            throws IOException, SecurityException {
+        if (Build.VERSION.SDK_INT >= 35) {
+            LoadParams params = new LoadParams.Builder().setPassword(password).build();
+            mPdfRenderer = new PdfRenderer(parcelFileDescriptor, params);
+        } else {
+            if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+                LoadParams params = new LoadParams.Builder().setPassword(password).build();
+                mPdfRendererPreV = new PdfRendererPreV(parcelFileDescriptor, params);
+            }
+        }
+    }
+
+    /**  */
+    @NonNull
+    PdfPageAdapter openPage(int pageNum) {
+        if (mPdfRenderer != null) {
+            return new PdfPageAdapter(mPdfRenderer, pageNum);
+        }
+        return new PdfPageAdapter(mPdfRendererPreV, pageNum);
+    }
+
+    public int getPageCount() {
+        if (mPdfRenderer != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRenderer.getPageCount();
+        }
+        return checkAndExecute(() -> mPdfRendererPreV.getPageCount());
+    }
+
+    public int getDocumentLinearizationType() {
+        if (mPdfRenderer != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRenderer.getDocumentLinearizationType();
+        }
+        return checkAndExecute(() -> mPdfRendererPreV.getDocumentLinearizationType());
+    }
+
+    public int getDocumentFormType() {
+        if (mPdfRenderer != null && Build.VERSION.SDK_INT >= 35) {
+            return mPdfRenderer.getPdfFormType();
+        }
+        return checkAndExecute(() -> mPdfRendererPreV.getPdfFormType());
+    }
+
+    private static void checkAndExecute(@NonNull Runnable block) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            block.run();
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    private static <T> T checkAndExecute(@NonNull Supplier<T> block) {
+        if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
+            return block.get();
+        }
+        throw new UnsupportedOperationException("Operation support above S");
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (mPdfRenderer != null) {
+            mPdfRenderer.close();
+            mPdfRenderer = null;
+        } else {
+            checkAndExecute(() -> {
+                mPdfRendererPreV.close();
+                mPdfRendererPreV = null;
+            });
+        }
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/AnnotationUtils.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/AnnotationUtils.java
index a33af08..0133a59 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/AnnotationUtils.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/AnnotationUtils.java
@@ -39,8 +39,8 @@
 
     private AnnotationUtils() {}
 
-    /** Launches the annotation intent for a given Uri */
-    public static boolean launchAnnotationIntent(@NonNull Context context, @NonNull Uri uri) {
+    /** Returns true if there is an activity that can resolve the annotation intent else false. */
+    public static boolean resolveAnnotationIntent(@NonNull Context context, @NonNull Uri uri) {
         Objects.requireNonNull(context);
         Objects.requireNonNull(uri);
         Intent intent = getAnnotationIntent(uri);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/BitmapParcel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/BitmapParcel.java
deleted file mode 100644
index aa6979e..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/BitmapParcel.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.util;
-
-import android.graphics.Bitmap;
-import android.os.ParcelFileDescriptor;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility to share {@link Bitmap}s across processes using a {@link android.os.Parcelable} reference
- * that can fit safely in an Intent.
- *
- * <p>A {@link BitmapParcel} wraps a {@link Bitmap} instance and exposes an output file descriptor
- * that can be used to fill in the bytes of the wrapped bitmap from any process.
- *
- * <p>Uses a piped file descriptor, and natively reads and copies bytes from the source end into the
- * {@link Bitmap}'s byte buffer. Runs on a new Thread.
- *
- * <p>Note: Only one-way transfers are implemented (write into a bitmap from any source).
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class BitmapParcel {
-
-    private static final String TAG = BitmapParcel.class.getSimpleName();
-    private final Bitmap mBitmap;
-    private final Timer mTimer = Timer.start();
-    private CountDownLatch mCountDownLatch;
-
-    /**
-     * Creates a BitmapParcel that allows writing bytes into the given {@link Bitmap}.
-     *
-     * @param bitmap the destination bitmap: its contents will be replaced by what is sent on the
-     *               fd.
-     */
-    public BitmapParcel(@NonNull Bitmap bitmap) {
-        this.mBitmap = bitmap;
-    }
-
-    /** Opens a file descriptor that will write into the wrapped bitmap. */
-    @Nullable
-    public ParcelFileDescriptor openOutputFd() {
-        ParcelFileDescriptor[] pipe;
-        try {
-            // TODO: StrictMode - close() is not explicitly called.
-            pipe = ParcelFileDescriptor.createPipe();
-        } catch (IOException e) {
-            return null;
-        }
-        ParcelFileDescriptor source = pipe[0];
-        ParcelFileDescriptor sink = pipe[1];
-        receiveAsync(source);
-        return sink;
-    }
-
-    /** Terminates any running copy and close all resources. */
-    public void close() {
-        if (mCountDownLatch != null) {
-            boolean timedOut = false;
-            try {
-                timedOut = !mCountDownLatch.await(5, TimeUnit.SECONDS);
-            } catch (InterruptedException ignored) {
-            }
-        }
-    }
-
-    /** Receives the bitmap's bytes from a file descriptor. Runs on a new thread. */
-    private void receiveAsync(final ParcelFileDescriptor source) {
-        mCountDownLatch = new CountDownLatch(1);
-        new Thread(
-                () -> {
-                    Timer t = Timer.start();
-                    receiveBitmap(mBitmap, source);
-                    mCountDownLatch.countDown();
-                },
-                "Pico-AsyncPipedFdNative.receiveAsync")
-                .start();
-    }
-
-    /**
-     * Reads bytes from a file descriptor into a {@link Bitmap}, using a native memcpy.
-     *
-     * @param bitmap   A bitmap whose pixels to populate.
-     * @param sourceFd The source file descriptor.
-     */
-    protected void receiveBitmap(@NonNull Bitmap bitmap, @NonNull ParcelFileDescriptor sourceFd) {
-        readIntoBitmap(bitmap, sourceFd.detachFd());
-    }
-
-    /**
-     * Reads bytes from the given file descriptor and fill in the Bitmap instance.
-     *
-     * @param bitmap   A bitmap whose pixels to populate.
-     * @param sourceFd The source file descriptor. Will be closed after transfer.
-     */
-    private static native boolean readIntoBitmap(Bitmap bitmap, int sourceFd);
-
-    /**
-     *
-     */
-    public static void loadNdkLib() {
-        System.loadLibrary("bitmapParcel");
-    }
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/GestureTrackingView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/GestureTrackingView.java
index 92e2b0c..8e5d8cd 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/GestureTrackingView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/GestureTrackingView.java
@@ -23,6 +23,7 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.pdf.util.GestureTracker.Gesture;
 
@@ -46,16 +47,16 @@
         super(context);
     }
 
-    public GestureTrackingView(@NonNull Context context, @NonNull AttributeSet attrs) {
+    public GestureTrackingView(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public GestureTrackingView(@NonNull Context context, @NonNull AttributeSet attrs,
+    public GestureTrackingView(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
-    public GestureTrackingView(@NonNull Context ctx, @NonNull AttributeSet attrs, int defStyleAttr,
+    public GestureTrackingView(@NonNull Context ctx, @Nullable AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(ctx, attrs, defStyleAttr, defStyleRes);
     }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/PaginationUtils.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/PaginationUtils.java
new file mode 100644
index 0000000..9dee5d8b
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/PaginationUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.util;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+/**
+ * Utils class for [PaginatedView]
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PaginationUtils {
+    private PaginationUtils() {
+    }
+
+    /** {@link View#setElevation(float)} value for PDF Pages (API 21+). */
+    public static final int PAGE_ELEVATION_DP = 2;
+
+    /** The spacing added before and after each page, in dip. */
+    private static final int PAGE_SPACING_DP = 4;
+
+    /** Converts a value given in dp to pixels, based on the screen density. */
+    public static int getPageElevationInPixels(@NonNull Context context) {
+        float density = context.getResources().getDisplayMetrics().density;
+        return (int) (PAGE_ELEVATION_DP * density);
+    }
+
+    /** Converts a value given in dp to pixels, based on the screen density. */
+    public static int getPageSpacingInPixels(@NonNull Context context) {
+        float density = context.getResources().getDisplayMetrics().density;
+        return (int) (PAGE_SPACING_DP * density);
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/ZoomUtils.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/ZoomUtils.java
index 6711671..fd9ce63 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/util/ZoomUtils.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/util/ZoomUtils.java
@@ -38,4 +38,91 @@
             return outerHeight / innerHeight;
         }
     }
+
+    /**
+     * Used to convert the zoom view coordinates to the content coordinates using the current
+     * zoom and scroll values.
+     *
+     * @param zoomViewCoordinate coordinate for either the x or y-axis in the [ZoomView] viewport.
+     * @param zoom               current zoom factor.
+     * @param scroll             current scroll position.
+     * @return content coordinates converted from the viewport coordinates.
+     */
+    public static float toContentCoordinate(float zoomViewCoordinate, float zoom, int scroll) {
+        Preconditions.checkArgument(zoom > 0, "Zoom factor must be positive.");
+        return (zoomViewCoordinate + scroll) / zoom;
+    }
+
+    /**
+     * Used to convert the content coordinates to the view coordinates using the current
+     * zoom and scroll values.
+     *
+     * @param contentCoordinate coordinate for either the x or y-axis in the [ZoomView] viewport.
+     * @param zoom              current zoom factor.
+     * @param scroll            current scroll position.
+     * @return view coordinates converted from the content coordinates.
+     */
+    public static float toZoomViewCoordinate(float contentCoordinate, float zoom, int scroll) {
+        Preconditions.checkArgument(zoom > 0, "Zoom factor must be positive.");
+        return (contentCoordinate * zoom) - scroll;
+    }
+
+    /**
+     * Used to find the delta between the view port pivot and the pivot after the zoom in/out is
+     * done. Delta is positive in case of zooming in, negative in case it has been zoomed out and
+     * 0 if no change.
+     *
+     * @param oldZoom   previous zoom factor.
+     * @param newZoom   current zoom factor.
+     * @param zoomPivot pivot point from zoom.
+     * @param scroll    scroll position.
+     * @return delta between the view port and zoomed in/out pivot.
+     */
+    public static int scrollDeltaNeededForZoomChange(
+            float oldZoom, float newZoom, float zoomPivot, int scroll) {
+        // Find where the given pivot point would move to when we change the zoom, and return the
+        // delta.
+        float contentPivot = ZoomUtils.toContentCoordinate(zoomPivot, oldZoom, scroll);
+        float movedZoomViewPivot = ZoomUtils.toZoomViewCoordinate(contentPivot, newZoom,
+                scroll);
+        return (int) (movedZoomViewPivot - zoomPivot);
+    }
+
+    /**
+     * Used to constrain the coordinate using the current zoom factor and the scroll position
+     * based on the content raw size and the view port size. In case of adjusting the x
+     * coordinate, the content and view port dimension will be the width while in case of
+     * y-coordinate it will be the width. Lower and upper bound is the left and right of the
+     * content for x-axis and is top and bottom for y-axis.
+     *
+     * @param zoom                current zoom factor.
+     * @param scroll              current scroll position.
+     * @param contentRawDimension raw dimension (height/width) for the content
+     * @param viewportDimension   viewport dimension
+     * @return scaled coordinate or 0 if no adjustment is needed.
+     */
+    public static int constrainCoordinate(float zoom, int scroll, int contentRawDimension,
+            int viewportDimension) {
+        float lowerBound = ZoomUtils.toZoomViewCoordinate(0, zoom, scroll);
+        float upperBound = ZoomUtils.toZoomViewCoordinate(contentRawDimension, zoom, scroll);
+
+        if (lowerBound <= 0 && upperBound >= viewportDimension) {
+            // Content too large for viewport and no dead margins: no adjustment needed.
+            return 0;
+        }
+
+        float scaledContentSize = upperBound - lowerBound;
+        if (scaledContentSize <= viewportDimension) {
+            // Content fits in viewport: keep in the center.
+            return (int) ((upperBound + lowerBound - viewportDimension) / 2);
+        } else {
+            // Content doesn't fit in viewport: eliminate dead margins.
+            if (lowerBound > 0) { // Dead margin on the left.
+                return (int) lowerBound;
+            } else if (upperBound < viewportDimension) { // Dead margin on the right.
+                return (int) (upperBound - viewportDimension);
+            }
+        }
+        return 0;
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AbstractPaginatedView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AbstractPaginatedView.java
index 11eb417..6575393 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AbstractPaginatedView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AbstractPaginatedView.java
@@ -52,7 +52,7 @@
         super(context, attrs);
     }
 
-    public AbstractPaginatedView(@NonNull Context context, @NonNull AttributeSet attrs,
+    public AbstractPaginatedView(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/LayoutHandler.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/LayoutHandler.java
new file mode 100644
index 0000000..d3acfe9
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/LayoutHandler.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.ViewState;
+import androidx.pdf.util.ThreadUtils;
+import androidx.pdf.viewer.loader.PdfLoader;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class LayoutHandler {
+    /** Only interact with Queue on the main thread. */
+    private final List<OnDimensCallback> mDimensCallbackQueue;
+    private final PdfLoader mPdfLoader;
+
+    /** The number of pages that have been laid out in the document. */
+    private int mPageLayoutReach = 0;
+    private int mInitialPageLayoutReach = 4;
+
+    public LayoutHandler(@NonNull PdfLoader pdfLoader) {
+        mDimensCallbackQueue = new ArrayList<>();
+        mPdfLoader = pdfLoader;
+    }
+
+    public int getPageLayoutReach() {
+        return mPageLayoutReach;
+    }
+
+    public void setPageLayoutReach(int pageLayoutReach) {
+        mPageLayoutReach = pageLayoutReach;
+    }
+
+    public int getInitialPageLayoutReach() {
+        return mInitialPageLayoutReach;
+    }
+
+    public void setInitialPageLayoutReach(int initialPageLayoutReach) {
+        mInitialPageLayoutReach = initialPageLayoutReach;
+    }
+
+    public void setInitialPageLayoutReachWithMax(int layoutReach) {
+        mInitialPageLayoutReach = Math.max(mInitialPageLayoutReach, layoutReach);
+    }
+
+    /**
+     * Lay out some pages up to some distant page. Not guaranteed to lay out any pages: maybe all
+     * pages, or at least enough pages, are already laid out.
+     */
+    public void maybeLayoutPages(int current) {
+        int peekAhead = Math.min(current + 2, 100);
+        int distantPage = Math.max(current + peekAhead, mInitialPageLayoutReach);
+        layoutPages(distantPage);
+    }
+
+    /**
+     * Lays out all the pages until {@code untilPage}, or equivalently so that {@code untilPage}s
+     * are laid out. So calling with {@code untilPage = 10} will ensure pages 0-9 are laid out.
+     *
+     * @param untilPage The upper limit of the range of pages to be laid out. Cropped to the
+     *                  number of pages of the document if this number was larger.
+     */
+    public void layoutPages(int untilPage) {
+        if (mPdfLoader == null) {
+            return;
+        }
+        int lastPage = Math.min(untilPage, mPdfLoader.getNumPages());
+        int requestLayoutPage = mPageLayoutReach;
+        while (requestLayoutPage < lastPage) {
+            mPdfLoader.loadPageDimensions(requestLayoutPage);
+            requestLayoutPage++;
+        }
+    }
+
+    /** */
+    public void add(@NonNull OnDimensCallback callback) {
+        mDimensCallbackQueue.add(callback);
+    }
+
+    /** */
+    public void processCallbacksInQueue(@NonNull ViewState viewState,
+            int pageNum) {
+        ThreadUtils.postOnUiThread(
+                () -> {
+                    if (mDimensCallbackQueue.isEmpty()
+                            || viewState == ViewState.NO_VIEW) {
+                        return;
+                    }
+
+                    Iterator<OnDimensCallback> iterator =
+                            mDimensCallbackQueue.iterator();
+                    while (iterator.hasNext()) {
+                        OnDimensCallback callback = iterator.next();
+                        boolean shouldKeep = callback.onDimensLoaded(pageNum);
+                        if (!shouldKeep) {
+                            iterator.remove();
+                        }
+                    }
+                });
+    }
+
+    /** Callback is called everytime dimensions for a page have loaded. */
+    public interface OnDimensCallback {
+        /** Return true to continue receiving callbacks, else false. */
+        boolean onDimensLoaded(int pageNum);
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageIndicator.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageIndicator.java
deleted file mode 100644
index 210a15e..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageIndicator.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.viewer;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Resources;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-import androidx.pdf.R;
-import androidx.pdf.data.Range;
-import androidx.pdf.util.Accessibility;
-import androidx.pdf.widget.ReusableToast;
-
-import java.util.Objects;
-
-/**
- * A Toast-like overlay that surfaces details about the current view (mostly page number, but also
- * zoom factor) to the user. Input is accepted in raw format (e.g. pages in 0-based indexing), but
- * presented to the user in human dialect, i.e. page numbers in 1-based indexing, zoom factor in
- * percent.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class PageIndicator extends ReusableToast {
-    private static final int AUTO_HIDE_DELAY_MS = 1300;
-
-    private final Context mContext;
-    private final TextView mPageNumberView;
-    private final Accessibility mAccessibility;
-
-    private int mNumPages;
-
-    private Range mCurrentRange;
-
-    private float mCurrentZoom;
-
-    PageIndicator(Activity activity, ViewGroup container) {
-        this(activity, inflateView(activity, container), Accessibility.get());
-    }
-
-    @VisibleForTesting
-    PageIndicator(Context context, TextView pageNumberView, Accessibility accessibility) {
-        super(pageNumberView);
-        this.mContext = context;
-        this.mAccessibility = accessibility;
-        setAutoHideDelayMs(AUTO_HIDE_DELAY_MS);
-        hide();
-        this.mPageNumberView = pageNumberView;
-    }
-
-    public void setNumPages(int numPages) {
-        this.mNumPages = numPages;
-    }
-
-    /**
-     * Sets details about the current view: page range and zoom. If TalkBack is on, those details
-     * will be announced.
-     *
-     * @param range  the page range, in usual 0-based indexing.
-     * @param zoom   the zoom factor, as a number between 0 and 1.
-     * @param stable indicates whether the position in the document is stable.
-     * @return whether this method resulted in the pageIndicator being shown.
-     */
-    public boolean setRangeAndZoom(Range range, float zoom, boolean stable) {
-        boolean shown = false;
-
-        String announceStr = null;
-        if (!Objects.equals(mCurrentRange, range)) {
-            String desc = getDescription(range);
-            announceStr = desc;
-            mPageNumberView.setText(getLabel(range));
-            mPageNumberView.setContentDescription(desc);
-            if (mCurrentRange != null) {
-                // Do not show on the first time, only when updating
-                show();
-                shown = true;
-            }
-
-            mCurrentRange = range;
-        }
-
-        if (zoom != mCurrentZoom && stable) {
-            // Override announcement with zoom info.
-            announceStr = getDescription(range) + "\n" + getZoomDescription(zoom);
-            mCurrentZoom = zoom;
-        }
-        if (announceStr != null && mAccessibility.isAccessibilityEnabled(mContext)) {
-            mAccessibility.announce(mContext, mPageNumberView, announceStr);
-        }
-
-        return shown;
-    }
-
-    private static TextView inflateView(Activity activity, ViewGroup container) {
-        activity.getLayoutInflater().inflate(R.layout.page_indicator, container);
-        return (TextView) container.findViewById(R.id.pdf_page_num);
-    }
-
-    private String getLabel(Range range) {
-        Resources res = mContext.getResources();
-        switch (range.length()) {
-            case 0:
-                return res.getString(R.string.label_page_single, range.getLast() + 1, mNumPages);
-            case 1:
-                return res.getString(R.string.label_page_single, range.getFirst() + 1, mNumPages);
-            default:
-                return res.getString(R.string.label_page_range, range.getFirst() + 1,
-                        range.getLast() + 1,
-                        mNumPages);
-        }
-    }
-
-    private String getDescription(Range range) {
-        Resources res = mContext.getResources();
-        switch (range.length()) {
-            case 0:
-                return res.getString(R.string.desc_page_single, range.getLast() + 1, mNumPages);
-            case 1:
-                return res.getString(R.string.desc_page_single, range.getFirst() + 1, mNumPages);
-            default:
-                return res.getString(R.string.desc_page_range, range.getFirst() + 1,
-                        range.getLast() + 1,
-                        mNumPages);
-        }
-    }
-
-    private String getZoomDescription(float zoom) {
-        Resources res = mContext.getResources();
-        return res.getString(R.string.desc_zoom, Math.round(zoom * 100));
-    }
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
index b0f95d3..3a8b773 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageMosaicView.java
@@ -32,6 +32,7 @@
 import androidx.pdf.models.LinkRects;
 import androidx.pdf.util.Accessibility;
 import androidx.pdf.util.BitmapRecycler;
+import androidx.pdf.viewer.loader.PdfLoader;
 import androidx.pdf.widget.MosaicView;
 
 import java.util.List;
@@ -40,7 +41,6 @@
  * Renders one Page.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-@SuppressWarnings("UnusedVariable")
 public class PageMosaicView extends MosaicView implements PageViewFactory.PageView {
 
     @VisibleForTesting
@@ -50,19 +50,31 @@
     private String mPageText;
     private LinkRects mUrlLinks;
     private List<GotoLink> mGotoLinks;
+    private final PdfLoader mPdfLoader;
+    private final PdfSelectionModel mSelectionModel;
+    private final SearchModel mSearchModel;
+    private final PdfSelectionHandles mSelectionHandles;
 
     public PageMosaicView(
             @NonNull Context context,
             int pageNum,
             @NonNull Dimensions pageSize,
             @NonNull BitmapSource bitmapSource,
-            @Nullable BitmapRecycler bitmapRecycler) {
+            @Nullable BitmapRecycler bitmapRecycler,
+            @NonNull PdfLoader pdfLoader,
+            @NonNull PdfSelectionModel selectionModel,
+            @NonNull SearchModel searchModel,
+            @NonNull PdfSelectionHandles selectionHandles) {
         super(context);
         this.mPageNum = pageNum;
         init(pageSize, bitmapRecycler, bitmapSource);
         setId(pageNum);
         setPageText(null);
         setFocusableInTouchMode(true);
+        this.mPdfLoader = pdfLoader;
+        this.mSelectionModel = selectionModel;
+        this.mSearchModel = searchModel;
+        this.mSelectionHandles = selectionHandles;
     }
 
     /** Set the given overlay. */
@@ -169,4 +181,39 @@
     public View asView() {
         return this;
     }
+
+    /**
+     * Loads the page content like page text, external urls and goto links and also resets the
+     * overlays from selection and search
+     */
+    public void refreshPageContentAndOverlays() {
+        loadPageComponents();
+        resetOverlays();
+    }
+
+    /** Loads the page text, external links and the goto links for the page */
+    private void loadPageComponents() {
+        if (needsPageText()) {
+            mPdfLoader.loadPageText(mPageNum);
+        }
+        if (!hasPageUrlLinks()) {
+            mPdfLoader.loadPageUrlLinks(mPageNum);
+        }
+        if (!hasPageGotoLinks()) {
+            mPdfLoader.loadPageGotoLinks(mPageNum);
+        }
+    }
+
+    private void resetOverlays() {
+        if (getPageNum() == mSelectionModel.getPage()) {
+            setOverlay(new PdfHighlightOverlay(mSelectionModel.selection().get()));
+            mSelectionHandles.updateHandles();
+        } else if (mSearchModel.query().get() != null) {
+            if (!hasOverlay()) {
+                mPdfLoader.searchPageText(getPageNum(), mSearchModel.query().get());
+            }
+        } else {
+            setOverlay(null);
+        }
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageRangeHandler.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageRangeHandler.java
new file mode 100644
index 0000000..8936f4e
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageRangeHandler.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.data.Range;
+import androidx.pdf.util.Preconditions;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PageRangeHandler {
+    private static final int PAGE_PREFETCH_RADIUS = 1;
+
+    private final PaginationModel mPaginationModel;
+
+    /** The range of currently visible pages. */
+    private Range mVisiblePages = null;
+
+    /** The highest number page reached. */
+    private int mMaxPage = -1;
+
+    PageRangeHandler(PaginationModel paginationModel) {
+        this.mPaginationModel = paginationModel;
+        this.mVisiblePages = new Range();
+    }
+
+    @Nullable
+    public Range getVisiblePages() {
+        return mVisiblePages;
+    }
+
+    public void setVisiblePages(@Nullable Range visiblePages) {
+        mVisiblePages = visiblePages;
+    }
+
+    public int getMaxPage() {
+        return mMaxPage;
+    }
+
+    public void setMaxPage(int maxPage) {
+        mMaxPage = maxPage;
+    }
+
+    @NonNull
+    public PaginationModel getPaginationModel() {
+        return mPaginationModel;
+    }
+
+    /**
+     * Returns the page currently roughly centered in the view.
+     */
+    public int getVisiblePage() {
+        return (mVisiblePages != null) ? (mVisiblePages.getFirst() + mVisiblePages.getLast()) / 2
+                : 0;
+    }
+
+    /**
+     * Updates the max page to the upper bound of the visible page range if the upper bound is
+     * greater than the current max page
+     */
+    public void adjustMaxPageToUpperVisibleRange() {
+        if (mVisiblePages != null) {
+            mMaxPage = Math.max(mVisiblePages.getLast(), mMaxPage);
+        }
+    }
+
+    /** Updates the visible page range based on the y-scroll, current zoom and the view height */
+    public void refreshVisiblePageRange(int scrollY, float zoom, int viewHeight) {
+        mVisiblePages = computeVisibleRange(scrollY, zoom, viewHeight, true);
+    }
+
+    /** Computes the range of visible pages in the given position. */
+    @NonNull
+    public Range computeVisibleRange(int scrollY, float zoom, int viewHeight,
+            boolean includePartial) {
+        Preconditions.checkArgument(zoom > 0, "Zoom factor must be positive!");
+
+        int top = Math.round(scrollY / zoom);
+        int bottom = Math.round((scrollY + viewHeight) / zoom);
+        Range window = new Range(top, bottom);
+        return mPaginationModel.getPagesInWindow(window, includePartial);
+    }
+
+    /** Returns the range of pages within the prefetch radius of the visible pages. */
+    @NonNull
+    public Range getNearPagesToVisibleRange() {
+        Range allPages = new Range(0, mPaginationModel.getSize() - 1);
+        return mVisiblePages.expand(PAGE_PREFETCH_RADIUS, allPages);
+    }
+
+    /** Returns the pages that are out of view and prefetch radius */
+    @NonNull
+    public Range[] getGonePageRanges(@NonNull Range nearPages) {
+        Range allPages = new Range(0, mPaginationModel.getSize() - 1);
+        return allPages.minus(nearPages);
+    }
+
+    /** Returns the range of pages near the visible pages that are invisible to the view port */
+    @NonNull
+    public Range[] getInvisibleNearPageRanges(@NonNull Range nearPages) {
+        return nearPages.minus(mVisiblePages);
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageSelectionValueObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageSelectionValueObserver.java
new file mode 100644
index 0000000..5f3c02b
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageSelectionValueObserver.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.data.Range;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.util.ObservableValue;
+import androidx.pdf.util.PaginationUtils;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PageSelectionValueObserver implements ObservableValue.ValueObserver<PageSelection> {
+    private final PaginatedView mPaginatedView;
+    private final PaginationModel mPaginationModel;
+    private final PageViewFactory mPageViewFactory;
+    private Context mContext;
+
+    public PageSelectionValueObserver(@NonNull PaginatedView paginatedView,
+            @NonNull PaginationModel paginationModel,
+            @NonNull PageViewFactory pageViewFactory, @NonNull Context context) {
+        mPaginatedView = paginatedView;
+        mPaginationModel = paginationModel;
+        mPageViewFactory = pageViewFactory;
+        mContext = context;
+    }
+
+    @Override
+    public void onChange(@Nullable PageSelection oldSelection,
+            @Nullable PageSelection newSelection) {
+        if (oldSelection != null && isPageCreated(oldSelection.getPage())) {
+            getPage(oldSelection.getPage()).getPageView().setOverlay(null);
+        }
+
+        Range visiblePageRange =
+                mPaginatedView.getPageRangeHandler().getVisiblePages();
+        if (newSelection != null && visiblePageRange.contains(
+                newSelection.getPage())) {
+            ((PageMosaicView) mPageViewFactory.getOrCreatePageView(
+                    newSelection.getPage(),
+                    PaginationUtils.getPageElevationInPixels(mContext),
+                    mPaginationModel.getPageSize(newSelection.getPage())))
+                    .setOverlay(new PdfHighlightOverlay(newSelection));
+        }
+    }
+
+    private boolean isPageCreated(int pageNum) {
+        return pageNum < mPaginationModel.getSize() && mPaginatedView.getViewAt(pageNum) != null;
+    }
+
+    private PageViewFactory.PageView getPage(int pageNum) {
+        return mPaginatedView.getViewAt(pageNum);
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageTouchListener.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageTouchListener.java
index 0d0730f..6a802cd 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageTouchListener.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageTouchListener.java
@@ -20,25 +20,28 @@
 import android.view.MotionEvent;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.pdf.models.SelectionBoundary;
 import androidx.pdf.util.GestureTracker;
 import androidx.pdf.viewer.loader.PdfLoader;
 
 /** Gesture listener for PageView's handling of tap and long press. */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
 class PageTouchListener extends GestureTracker.GestureHandler {
 
     private final PageViewFactory.PageView mPageView;
 
     private final PdfLoader mPdfLoader;
 
-    private final PdfViewer.PageTouchHandler mPageTouchHandler;
+
+    private final SingleTapHandler mSingleTapHandler;
 
     PageTouchListener(@NonNull PageViewFactory.PageView pageView,
             @NonNull PdfLoader pdfLoader,
-            @NonNull PdfViewer.PageTouchHandler pageTouchHandler) {
+            @NonNull SingleTapHandler singleTapHandler) {
         this.mPageView = pageView;
         this.mPdfLoader = pdfLoader;
-        this.mPageTouchHandler = pageTouchHandler;
+        this.mSingleTapHandler = singleTapHandler;
     }
 
     @Override
@@ -48,7 +51,8 @@
 
     @Override
     public boolean onSingleTapConfirmed(@NonNull MotionEvent e) {
-        return mPageTouchHandler.handleSingleTapNoFormFilling(e, mPageView.getPageView());
+        mSingleTapHandler.handleSingleTapConfirmedEventOnPage(e, mPageView.getPageView());
+        return true;
     }
 
     @Override
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java
index efd550f..4d1014f 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageViewFactory.java
@@ -29,7 +29,6 @@
 import androidx.pdf.models.LinkRects;
 import androidx.pdf.util.Accessibility;
 import androidx.pdf.util.GestureTracker;
-import androidx.pdf.util.ObservableValue;
 import androidx.pdf.util.TileBoard;
 import androidx.pdf.viewer.loader.PdfLoader;
 import androidx.pdf.widget.MosaicView;
@@ -53,16 +52,19 @@
     private final Context mContext;
     private final PdfLoader mPdfLoader;
     private final PaginatedView mPaginatedView;
-    private final ObservableValue<ZoomView.ZoomScroll> mZoomScroll;
+    private final ZoomView mZoomView;
+    private final SingleTapHandler mSingleTapHandler;
 
     public PageViewFactory(@NonNull Context context,
             @NonNull PdfLoader pdfLoader,
             @NonNull PaginatedView paginatedView,
-            @NonNull ZoomView zoomView) {
+            @NonNull ZoomView zoomView,
+            @NonNull SingleTapHandler singleTapHandler) {
         this.mContext = context;
         this.mPdfLoader = pdfLoader;
         this.mPaginatedView = paginatedView;
-        this.mZoomScroll = zoomView.zoomScroll();
+        this.mZoomView = zoomView;
+        this.mSingleTapHandler = singleTapHandler;
     }
 
     /**
@@ -98,20 +100,21 @@
         void clearAll();
     }
 
-    /** Returns an instance of {@link PageView}. If the view is already created and added to the
-     *  {@link PaginatedView} then it will be returned from that list else a new instance will be
-     *  created. */
+    /**
+     * Returns an instance of {@link PageView}. If the view is already created and added to the
+     * {@link PaginatedView} then it will be returned from that list else a new instance will be
+     * created.
+     */
     @NonNull
     public PageView getOrCreatePageView(int pageNum,
             int pageElevationInPixels,
-            @NonNull Dimensions pageDimensions,
-            @NonNull PdfViewer.PageTouchHandler handler) {
+            @NonNull Dimensions pageDimensions) {
         PageView pageView = mPaginatedView.getViewAt(pageNum);
         if (pageView != null) {
             return pageView;
         }
 
-        return createAndSetupPageView(pageNum, pageElevationInPixels, pageDimensions, handler);
+        return createAndSetupPageView(pageNum, pageElevationInPixels, pageDimensions);
     }
 
     /**
@@ -126,9 +129,10 @@
         final MosaicView.BitmapSource bitmapSource = createBitmapSource(pageNum);
         final PageMosaicView pageMosaicView =
                 new PageMosaicView(mContext, pageNum, pageSize, bitmapSource,
-                        TileBoard.DEFAULT_RECYCLER);
+                        TileBoard.DEFAULT_RECYCLER, mPdfLoader, mPaginatedView.getSelectionModel(),
+                        mPaginatedView.getSearchModel(), mPaginatedView.getSelectionHandles());
         if (isTouchExplorationEnabled(mContext)) {
-            final PageLinksView pageLinksView = new PageLinksView(mContext, mZoomScroll);
+            final PageLinksView pageLinksView = new PageLinksView(mContext, mZoomView.zoomScroll());
 
             return new AccessibilityPageWrapper(
                     mContext, pageNum, pageMosaicView, pageLinksView);
@@ -168,8 +172,7 @@
     @NonNull
     protected PageView createAndSetupPageView(int pageNum,
             int pageElevationInPixels,
-            @NonNull Dimensions pageDimensions,
-            @NonNull PdfViewer.PageTouchHandler handler) {
+            @NonNull Dimensions pageDimensions) {
         PageView pageView =
                 createPageView(
                         pageNum,
@@ -178,8 +181,9 @@
         mPaginatedView.addView(pageView);
 
         GestureTracker gestureTracker = new GestureTracker(mContext);
+        gestureTracker.setDelegateHandler(new PageTouchListener(pageView, mPdfLoader,
+                mSingleTapHandler));
         pageView.asView().setOnTouchListener(gestureTracker);
-        gestureTracker.setDelegateHandler(new PageTouchListener(pageView, mPdfLoader, handler));
 
         PageMosaicView pageMosaicView = pageView.getPageView();
         // Setting Elevation only works if there is a background color.
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
index 35bd5b8..f3a4289 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
@@ -25,8 +25,14 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.pdf.ViewState;
+import androidx.pdf.data.Range;
+import androidx.pdf.util.PaginationUtils;
 import androidx.pdf.util.Preconditions;
+import androidx.pdf.util.ThreadUtils;
 import androidx.pdf.viewer.PageViewFactory.PageView;
+import androidx.pdf.viewer.loader.PdfLoader;
+import androidx.pdf.widget.ZoomView;
 
 import java.util.AbstractList;
 import java.util.List;
@@ -46,16 +52,90 @@
     /** Maps the current child views to pages. */
     private final SparseArray<PageView> mPageViews = new SparseArray<>();
 
+    private PaginationModel mPaginationModel;
+
+    private PageRangeHandler mPageRangeHandler;
+
+    private PdfSelectionModel mSelectionModel;
+
+    private PdfSelectionHandles mSelectionHandles;
+
+    private SearchModel mSearchModel;
+
+    private PdfLoader mPdfLoader;
+
+    private PageViewFactory mPageViewFactory;
+
     public PaginatedView(@NonNull Context context) {
-        super(context);
+        this(context, null);
     }
 
-    public PaginatedView(@NonNull Context context, @NonNull AttributeSet attrs) {
-        super(context, attrs);
+    public PaginatedView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
     }
 
-    public PaginatedView(@NonNull Context context, @NonNull AttributeSet attrs, int defStyle) {
+    public PaginatedView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+
+    }
+
+    /** Instantiate PaginationModel and PageRangeHandler */
+    @NonNull
+    public PaginationModel initPaginationModelAndPageRangeHandler(@NonNull Context context) {
+        mPaginationModel = new PaginationModel(context);
+        mPageRangeHandler = new PageRangeHandler(mPaginationModel);
+        return mPaginationModel;
+    }
+
+    @NonNull
+    public PaginationModel getPaginationModel() {
+        return mPaginationModel;
+    }
+
+    @NonNull
+    public PageRangeHandler getPageRangeHandler() {
+        return mPageRangeHandler;
+    }
+
+    @NonNull
+    public PdfSelectionModel getSelectionModel() {
+        return mSelectionModel;
+    }
+
+    public void setSelectionModel(
+            @NonNull PdfSelectionModel selectionModel) {
+        mSelectionModel = selectionModel;
+    }
+
+    @NonNull
+    public SearchModel getSearchModel() {
+        return mSearchModel;
+    }
+
+    public void setSearchModel(@NonNull SearchModel searchModel) {
+        mSearchModel = searchModel;
+    }
+
+    @NonNull
+    public PdfSelectionHandles getSelectionHandles() {
+        return  mSelectionHandles;
+    }
+
+    public void setSelectionHandles(@NonNull PdfSelectionHandles selectionHandles) {
+        mSelectionHandles = selectionHandles;
+    }
+
+    public void setPdfLoader(@NonNull PdfLoader pdfLoader) {
+        mPdfLoader = pdfLoader;
+    }
+
+    @NonNull
+    public PageViewFactory getPageViewFactory() {
+        return mPageViewFactory;
+    }
+
+    public void setPageViewFactory(@NonNull PageViewFactory pageViewFactory) {
+        mPageViewFactory = pageViewFactory;
     }
 
     /** Instantiate a page of this pageView into a child pageView. */
@@ -200,4 +280,167 @@
         // laid out already for this viewArea.
         onLayout(false, getLeft(), getTop(), getRight(), getBottom());
     }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        super.onWindowFocusChanged(hasWindowFocus);
+        if (getVisibility() == View.VISIBLE && mPageRangeHandler != null) {
+            mPageRangeHandler.adjustMaxPageToUpperVisibleRange();
+            if (getChildCount() > 0) {
+                for (PageMosaicView page : getChildViews()) {
+                    page.clearTiles();
+                    if (mPdfLoader != null) {
+                        mPdfLoader.cancelAllTileBitmaps(page.getPageNum());
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mPageRangeHandler != null) {
+            mPageRangeHandler.setVisiblePages(null);
+        }
+    }
+
+    /**
+     * Refreshes the page range for the visible area.
+     */
+    public void refreshPageRangeInVisibleArea(@NonNull ZoomView.ZoomScroll zoomScroll,
+            int parentViewHeight) {
+        mPageRangeHandler.refreshVisiblePageRange(zoomScroll.scrollY, zoomScroll.zoom,
+                parentViewHeight);
+
+        mPageRangeHandler.adjustMaxPageToUpperVisibleRange();
+    }
+
+    /** Cancels the background jobs for the disappeared pages and optionally clears the views */
+    public void handleGonePages(boolean clearViews) {
+        Range nearPages = mPageRangeHandler.getNearPagesToVisibleRange();
+        Range[] gonePages = mPageRangeHandler.getGonePageRanges(nearPages);
+        for (Range pages : gonePages) {
+            // Keep Views around for now, we'll clear them in step (4) if applicable.
+            clearPages(pages, clearViews);
+        }
+    }
+
+    /** Computes the invisible page range and loads them */
+    public void loadInvisibleNearPageRange(
+            float stableZoom) {
+        Range nearPages = mPageRangeHandler.getNearPagesToVisibleRange();
+        Range[] invisibleNearPages = mPageRangeHandler.getInvisibleNearPageRanges(nearPages);
+
+        for (Range pages : invisibleNearPages) {
+            loadPageRange(pages, stableZoom);
+        }
+    }
+
+    /**
+     * Creates the page views for the visible page range.
+     *
+     * @return true if any new page was created else false
+     */
+    public boolean createPageViewsForVisiblePageRange() {
+        boolean requiresLayoutPass = false;
+        for (int pageNum : mPageRangeHandler.getVisiblePages()) {
+            if (getViewAt(pageNum) == null) {
+                mPageViewFactory.getOrCreatePageView(pageNum,
+                        PaginationUtils.getPageElevationInPixels(getContext()),
+                        mPaginationModel.getPageSize(pageNum));
+                requiresLayoutPass = true;
+            }
+        }
+        return requiresLayoutPass;
+    }
+
+    /**  */
+    public void refreshVisiblePages(boolean requiresLayoutPass,
+            @NonNull ViewState viewState,
+            float stableZoom) {
+        if (requiresLayoutPass) {
+            refreshPagesAfterLayout(viewState, mPageRangeHandler.getVisiblePages(),
+                    stableZoom);
+        } else {
+            refreshPages(mPageRangeHandler.getVisiblePages(), stableZoom);
+        }
+        handleGonePages(/* clearViews= */ true);
+    }
+
+    /**  */
+    public void refreshVisibleTiles(boolean requiresLayoutPass,
+            @NonNull ViewState viewState) {
+        if (requiresLayoutPass) {
+            refreshTilesAfterLayout(viewState, mPageRangeHandler.getVisiblePages());
+        } else {
+            refreshTiles(mPageRangeHandler.getVisiblePages());
+        }
+    }
+
+    private void clearPages(Range pages, boolean clearViews) {
+        for (int page : pages) {
+            // Don't cancel search - search results for the current search are always useful,
+            // even for pages we can't see right now. Form filling operations should always
+            // be executed against the document, even if the user has scrolled away from the page.
+            mPdfLoader.cancelExceptSearchAndFormFilling(page);
+            if (clearViews) {
+                removeViewAt(page);
+            }
+        }
+    }
+
+    private void loadPageRange(Range pages,
+            float stableZoom) {
+        for (int page : pages) {
+            mPdfLoader.cancelAllTileBitmaps(page);
+            PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
+                    page,
+                    PaginationUtils.getPageElevationInPixels(getContext()),
+                    mPaginationModel.getPageSize(page));
+            pageView.clearTiles();
+            pageView.requestFastDrawAtZoom(stableZoom);
+            pageView.refreshPageContentAndOverlays();
+        }
+    }
+
+    private void refreshPages(Range pages, float stableZoom) {
+        for (int page : pages) {
+            PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
+                    page,
+                    PaginationUtils.getPageElevationInPixels(getContext()),
+                    mPaginationModel.getPageSize(page));
+            pageView.requestDrawAtZoom(stableZoom);
+            pageView.refreshPageContentAndOverlays();
+        }
+    }
+
+    private void refreshPagesAfterLayout(ViewState viewState, Range pages,
+            float stableZoom) {
+        ThreadUtils.postOnUiThread(
+                () -> {
+                    if (viewState != ViewState.NO_VIEW) {
+                        refreshPages(pages, stableZoom);
+                    }
+                });
+    }
+
+    private void refreshTiles(Range pages) {
+        for (int page : pages) {
+            PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
+                    page,
+                    PaginationUtils.getPageElevationInPixels(getContext()),
+                    mPaginationModel.getPageSize(page));
+            pageView.requestTiles();
+        }
+    }
+
+    private void refreshTilesAfterLayout(ViewState viewState, Range pages) {
+        ThreadUtils.postOnUiThread(
+                () -> {
+                    if (viewState != ViewState.NO_VIEW) {
+                        refreshTiles(pages);
+                    }
+                });
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
index 06e7505..33c794f 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
@@ -16,14 +16,15 @@
 
 package androidx.pdf.viewer;
 
+import android.content.Context;
 import android.graphics.Rect;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.pdf.data.Range;
 import androidx.pdf.models.Dimensions;
+import androidx.pdf.util.PaginationUtils;
 import androidx.pdf.util.Preconditions;
-import androidx.pdf.util.Screen;
 
 import java.util.AbstractList;
 import java.util.ArrayList;
@@ -62,9 +63,6 @@
 
     private static final String TAG = PaginationModel.class.getSimpleName();
 
-    /** The spacing added before and after each page, in dip. */
-    private static final int PAGE_SPACING_DP = 4;
-
     /**
      * The spacing added before and after each page (the actual space between 2 consecutive pages is
      * twice this distance), in pixels.
@@ -106,9 +104,8 @@
 
     private final Set<PaginationModelObserver> mObservers = new HashSet<>();
 
-    public PaginationModel() {
-        Screen screen = PdfViewer.getScreen();
-        mPageSpacingPx = screen.pxFromDp(PAGE_SPACING_DP);
+    public PaginationModel(@NonNull Context context) {
+        mPageSpacingPx = PaginationUtils.getPageSpacingInPixels(context);
     }
 
     /**
@@ -361,6 +358,16 @@
     }
 
     /**
+     * Returns the number of pages in the document.
+     *
+     * @throws IllegalStateException if this is called before the model is initialized
+     */
+    public int getNumPages() {
+        Preconditions.checkState(mMaxPages != -1, "Model is not initialized");
+        return mMaxPages;
+    }
+
+    /**
      * Returns the intersection of this model and the last viewArea that was reported to this model
      * via {@link #setViewArea(Rect)}.
      */
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java
index 2cbeb56..9619bfe 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java
@@ -34,7 +34,7 @@
      * <p>The {@link PaginationModel} does not enforce any implementation expectations.
      * Implementations are free to use this information as desired.
      */
-    void onPageAdded();
+    default void onPageAdded() {}
 
     /**
      * Notifies the implementation that the {@code viewArea} of the {@link PaginationModel} has
@@ -43,5 +43,5 @@
      * <p>The {@link PaginationModel} does not enforce any implementation expectations.
      * Implementations are free to use this information as desired.
      */
-    void onViewAreaChanged();
+    default void onViewAreaChanged() {}
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfPasswordDialog.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfPasswordDialog.java
index c086c89..4a31aa9 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfPasswordDialog.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfPasswordDialog.java
@@ -19,6 +19,7 @@
 import android.widget.EditText;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.pdf.viewer.password.PasswordDialog;
 
@@ -30,13 +31,36 @@
 @SuppressWarnings("deprecation")
 public class PdfPasswordDialog extends PasswordDialog {
 
+    @Nullable
+    public PasswordDialogEventsListener mListener;
+
+    public void setListener(@NonNull PasswordDialogEventsListener listener) {
+        mListener = listener;
+    }
+
     @Override
     public void sendPassword(@NonNull EditText textField) {
-        ((PdfViewer) getTargetFragment()).setPassword(textField.getText().toString());
+        if (mListener != null) {
+            mListener.onPasswordTextChange(textField.getText().toString());
+        }
     }
 
     @Override
     public void showErrorOnDialogCancel() {
-        ((PdfViewer) getTargetFragment()).setPasswordCancelError();
+        if (mListener != null) {
+            mListener.onDialogCancelled();
+        }
     }
-}
+
+    public interface PasswordDialogEventsListener {
+        /**
+         * Callback to pass the password to the fragment.
+         */
+        void onPasswordTextChange(@NonNull String password);
+
+        /**
+         * Callback to handle the cancellation of this dialog.
+         */
+        void onDialogCancelled();
+    }
+}
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
index 2ba8fcee..77ca69e 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfSelectionHandles.java
@@ -25,6 +25,7 @@
 import androidx.pdf.R;
 import androidx.pdf.models.PageSelection;
 import androidx.pdf.models.SelectionBoundary;
+import androidx.pdf.select.SelectionActionMode;
 import androidx.pdf.select.SelectionModel;
 import androidx.pdf.util.Preconditions;
 import androidx.pdf.widget.ZoomView;
@@ -42,14 +43,18 @@
     private SelectionBoundary mFixed;
     private SelectionBoundary mDragging;
 
+    private SelectionActionMode mSelectionActionMode;
+
     public PdfSelectionHandles(
             @NonNull PdfSelectionModel selectionModel, @NonNull ZoomView zoomView,
-            @NonNull PaginatedView pdfView) {
+            @NonNull PaginatedView pdfView,
+            @NonNull SelectionActionMode selectionActionMode) {
         super(
                 zoomView, (ViewGroup) zoomView.findViewById(R.id.zoomed_view),
                 selectionModel.selection());
         this.mSelectionModel = Preconditions.checkNotNull(selectionModel);
         this.mPdfView = Preconditions.checkNotNull(pdfView);
+        this.mSelectionActionMode = selectionActionMode;
     }
 
     @Override
@@ -86,6 +91,7 @@
 
     @Override
     protected void onDragHandleUp() {
+        mSelectionActionMode.resume();
         // Nothing required.
     }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
index 7d8e888..58f6a6b 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
@@ -16,7 +16,6 @@
 
 package androidx.pdf.viewer;
 
-import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
 import android.annotation.SuppressLint;
@@ -24,21 +23,15 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.graphics.Bitmap;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnScrollChangedListener;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 import android.widget.ProgressBar;
-import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -52,52 +45,37 @@
 import androidx.pdf.data.DisplayData;
 import androidx.pdf.data.ErrorType;
 import androidx.pdf.data.FutureValue;
-import androidx.pdf.data.FutureValues.SettableFutureValue;
 import androidx.pdf.data.Openable;
 import androidx.pdf.data.PdfStatus;
 import androidx.pdf.data.Range;
 import androidx.pdf.fetcher.Fetcher;
-import androidx.pdf.find.FindInFileListener;
 import androidx.pdf.find.FindInFileView;
-import androidx.pdf.find.MatchCount;
 import androidx.pdf.models.Dimensions;
 import androidx.pdf.models.GotoLink;
-import androidx.pdf.models.GotoLinkDestination;
 import androidx.pdf.models.LinkRects;
 import androidx.pdf.models.MatchRects;
 import androidx.pdf.models.PageSelection;
+import androidx.pdf.select.SelectionActionMode;
 import androidx.pdf.util.AnnotationUtils;
-import androidx.pdf.util.CycleRange;
-import androidx.pdf.util.ExternalLinks;
-import androidx.pdf.util.ObservableValue;
 import androidx.pdf.util.ObservableValue.ValueObserver;
 import androidx.pdf.util.Preconditions;
 import androidx.pdf.util.Screen;
-import androidx.pdf.util.StrictModeUtils;
 import androidx.pdf.util.ThreadUtils;
 import androidx.pdf.util.TileBoard;
 import androidx.pdf.util.TileBoard.TileInfo;
 import androidx.pdf.util.Toaster;
 import androidx.pdf.util.Uris;
-import androidx.pdf.util.ZoomUtils;
 import androidx.pdf.viewer.PageViewFactory.PageView;
 import androidx.pdf.viewer.loader.PdfLoader;
 import androidx.pdf.viewer.loader.PdfLoaderCallbacks;
-import androidx.pdf.widget.FastScrollContentModel;
 import androidx.pdf.widget.FastScrollView;
 import androidx.pdf.widget.ZoomView;
-import androidx.pdf.widget.ZoomView.ContentResizedMode;
-import androidx.pdf.widget.ZoomView.FitMode;
-import androidx.pdf.widget.ZoomView.InitialZoomMode;
-import androidx.pdf.widget.ZoomView.RotateMode;
 import androidx.pdf.widget.ZoomView.ZoomScroll;
 
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.android.material.snackbar.Snackbar;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 
-import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -125,34 +103,20 @@
  *       connected.
  * </ol>
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @SuppressWarnings({"UnusedMethod", "UnusedVariable"})
-public class PdfViewer extends LoadingViewer implements FastScrollContentModel {
+public class PdfViewer extends LoadingViewer {
 
     private static final String TAG = "PdfViewer";
 
-    @NonNull
-    @Override
-    protected String getLogTag() {
-        return TAG;
-    }
-
     /** {@link View#setElevation(float)} value for PDF Pages (API 21+). */
     private static final int PAGE_ELEVATION_DP = 2;
 
-    /** Key for saving {@link #mPageLayoutReach} in bundles. */
+    /** Key for saving page layout reach in bundles. */
     private static final String KEY_LAYOUT_REACH = "plr";
-
-    private static final String KEY_SPACE_LEFT = "leftSpace";
-    private static final String KEY_SPACE_TOP = "topSpace";
-    private static final String KEY_SPACE_BOTTOM = "bottomSpace";
-    private static final String KEY_SPACE_RIGHT = "rightSpace";
     private static final String KEY_QUIT_ON_ERROR = "quitOnError";
     private static final String KEY_EXIT_ON_CANCEL = "exitOnCancel";
 
-    /** Key to save/retrieve {@link #mEditingAuthorized} from Bundle. */
-    private static final String KEY_EDITING_AUTHORIZED = "editingAuthorized";
-
     private static Screen sScreen;
 
     /** Single access to the PDF document: loads contents asynchronously (bitmaps, text,...) */
@@ -166,7 +130,7 @@
     public final PdfLoaderCallbacks mPdfLoaderCallbacks;
 
     /** Observer of the page position that controls loading of relevant PDF assets. */
-    private final ValueObserver<ZoomScroll> mZoomScrollObserver;
+    private ValueObserver<ZoomScroll> mZoomScrollObserver;
 
     /** Observer to be set when the view is created. */
     @Nullable
@@ -174,65 +138,34 @@
 
     private Object mScrollPositionObserverKey;
 
-    /** The number of pages of this PDF, set to -1 when not available. */
-    private int mNumPages = -1;
-
-    /** The range of currently visible pages. */
-    private Range mVisiblePages;
-
-    /** The highest number page reached. */
-    private int mMaxPage = -1;
-
-    private int mInitialPageLayoutReach = 4;
-
-    /** The number of pages that have been laid out in the document. */
-    private int mPageLayoutReach;
-
-    /** The last stable zoom: we only re-draw bitmaps at stable zoom (not during a gesture). */
-    private float mStableZoom;
-
     private ZoomView mZoomView;
 
     private PaginatedView mPaginatedView;
     private PaginationModel mPaginationModel;
 
-    private PageIndicator mPageIndicator;
-
     private SearchModel mSearchModel;
     private PdfSelectionModel mSelectionModel;
     private PdfSelectionHandles mSelectionHandles;
-    private final ValueObserver<String> mSearchQueryObserver;
-    private final ValueObserver<SelectedMatch> mSelectedMatchObserver;
-    private final ValueObserver<PageSelection> mSelectionObserver;
-    private final ValueObserver<Integer> mFastscrollerPositionObserver;
-    private Object mFastscrollerPositionObserverKey;
+
+    private ValueObserver<String> mSearchQueryObserver;
+    private ValueObserver<SelectedMatch> mSelectedMatchObserver;
+    private ValueObserver<PageSelection> mSelectionObserver;
+
     private FastScrollView mFastScrollView;
     private ProgressBar mLoadingSpinner;
 
     private boolean mDocumentLoaded = false;
+    private boolean mIsAnnotationIntentResolvable = false;
+
     /**
      * After the document content is saved over the original in InkActivity, we set this bit to true
-     * so we know to callwhen the new document content is loaded.
+     * so we know to call when the new document content is loaded.
      */
     private boolean mShouldRedrawOnDocumentLoaded = false;
-
-    @Nullable
-    private SettableFutureValue<Boolean> mPrintableVersionCallback;
-
-    // Non-null when a save-as operation is in progress. Cleared when operation is complete and
-    // value has been set with success/failure result.
-    @Nullable
-    private SettableFutureValue<Boolean> mSaveAsCallback;
-
-    // Base padding for ZoomView in px as set in saveZoomViewBasePadding().
-    private Rect mZoomViewBasePadding = new Rect();
-    private boolean mZoomViewBasePaddingSaved;
     private Snackbar mSnackbar;
-    private boolean mWaitingOnSelectionToCreateInlineComment;
-    private boolean mEditingAuthorized;
 
-    /** Only interact with Queue on the main thread. */
-    private final List<OnDimensCallback> mDimensCallbackQueue = new ArrayList<>();
+    private LayoutHandler mLayoutHandler;
+
     private Uri mLocalUri;
     private FrameLayout mPdfViewer;
 
@@ -242,11 +175,9 @@
 
     private PageViewFactory mPageViewFactory;
 
-    /** Callback is called everytime dimensions for a page have loaded. */
-    private interface OnDimensCallback {
-        /** Return true to continue receiving callbacks, else false. */
-        boolean onDimensLoaded(int pageNum);
-    }
+    private SingleTapHandler mSingleTapHandler;
+
+    private SelectionActionMode mSelectionActionMode;
 
     public PdfViewer() {
         super(SELF_MANAGED_CONTENTS);
@@ -289,50 +220,18 @@
     @NonNull
     @SuppressLint("InflateParams")
     @Override
-    public View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container,
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
             @Nullable Bundle savedState) {
         super.onCreateView(inflater, container, savedState);
-        mPaginationModel = new PaginationModel();
 
         mPdfViewer = (FrameLayout) inflater.inflate(R.layout.pdf_viewer_container, container,
                 false);
         mFindInFileView = mPdfViewer.findViewById(R.id.search);
         mFastScrollView = mPdfViewer.findViewById(R.id.fast_scroll_view);
-
-        mZoomView = mFastScrollView.findViewById(R.id.zoom_view);
-        mZoomView.setStraightenVerticalScroll(true);
-
-        mZoomView
-                .setFitMode(FitMode.FIT_TO_WIDTH)
-                .setInitialZoomMode(InitialZoomMode.ZOOM_TO_FIT)
-                .setRotateMode(RotateMode.KEEP_SAME_VIEWPORT_WIDTH)
-                .setContentResizedModeX(ContentResizedMode.KEEP_SAME_RELATIVE);
-
-        // Setting an id so that the View can restore itself. The Id has to be unique and
-        // predictable. An alternative that doesn't require id is to rely on this Fragment's
-        // onSaveInstanceState().
-        mZoomView.setId(getId() * 100);
-        mPaginatedView = mFastScrollView.findViewById(R.id.pdf_view);
-
-        mVisiblePages = new Range();
-        mPageLayoutReach = 0;
-
-        mPageIndicator = new PageIndicator(getActivity(), mFastScrollView);
-        applyReservedSpace();
-        adjustZoomViewMargins();
-        mFastscrollerPositionObserver.onChange(null, mFastScrollView.getScrollerPositionY().get());
-        mFastscrollerPositionObserverKey =
-                mFastScrollView.getScrollerPositionY().addObserver(mFastscrollerPositionObserver);
-
-        // The view system requires the document loaded in order to be properly initialized, so
-        // we delay anything view-related until ViewState.VIEW_READY.
-        mZoomView.setVisibility(View.GONE);
-
-        mFastScrollView.setScrollable(this);
-        mFastScrollView.setId(getId() * 10);
-
-        mLoadingSpinner = mFastScrollView.findViewById(R.id.progress_indicator);
-
+        mPaginatedView = mPdfViewer.findViewById(R.id.pdf_view);
+        mPaginationModel = mPaginatedView.getPaginationModel();
+        mZoomView = mPdfViewer.findViewById(R.id.zoom_view);
+        mLoadingSpinner = mPdfViewer.findViewById(R.id.progress_indicator);
         setUpEditFab();
 
         return mPdfViewer;
@@ -348,85 +247,9 @@
         sScreen = new Screen(context);
     }
 
-    private void applyReservedSpace() {
-        if (getArguments().containsKey(KEY_SPACE_TOP)) {
-            saveZoomViewBasePadding();
-            int left = getArguments().getInt(KEY_SPACE_LEFT, 0);
-            int top = getArguments().getInt(KEY_SPACE_TOP, 0);
-            int right = getArguments().getInt(KEY_SPACE_RIGHT, 0);
-            int bottom = getArguments().getInt(KEY_SPACE_BOTTOM, 0);
-
-            mPageIndicator.getView().setTranslationX(-right);
-
-            mZoomView.setPadding(
-                    mZoomViewBasePadding.left + left,
-                    mZoomViewBasePadding.top + top,
-                    mZoomViewBasePadding.right + right,
-                    mZoomViewBasePadding.bottom + bottom);
-
-            // Adjust the scroll bar to also include the same padding.
-            mFastScrollView.setScrollbarMarginTop(mZoomView.getPaddingTop());
-            mFastScrollView.setScrollbarMarginRight(right);
-            mFastScrollView.setScrollbarMarginBottom(mZoomView.getPaddingBottom());
-        }
-    }
-
-    /**
-     * Saves the padding set on {@link ZoomView} following initial inflation from XML.
-     *
-     * <p>This does not have to be called immediately following inflation but <i>must</i> be called
-     * before any methods change the padding on {@link ZoomView}.
-     *
-     * <p>This can be used by methods that need to set padding to (base padding + some other
-     * dimension). If these values were obtained directly from {@link ZoomView} or this method was
-     * allowed to execute multiple times it could result in padding expanding continually.
-     */
-    private void saveZoomViewBasePadding() {
-        if (mZoomView == null || mZoomViewBasePaddingSaved) {
-            return;
-        }
-
-        mZoomViewBasePadding =
-                new Rect(
-                        mZoomView.getPaddingLeft(),
-                        mZoomView.getPaddingTop(),
-                        mZoomView.getPaddingRight(),
-                        mZoomView.getPaddingBottom());
-
-        mZoomViewBasePadding.top +=
-                getResources().getDimensionPixelSize(R.dimen.viewer_doc_additional_top_offset);
-
-        mZoomViewBasePaddingSaved = true;
-    }
-
-    /**
-     * Adjusts the horizontal margins (left and right padding) of the ZoomView based on the
-     * screen width to optimize the display of PDF content.
-     *
-     * This method applies different margin values depending on the screen size:
-     * - For screens with a screen width of 840dp or greater, a larger margin is applied
-     * to enhance readability on larger displays.
-     * - For screens with a screen width < 840dp, no margin is used to
-     * maximize the use of available space.
-     *
-     * This dynamic adjustment is achieved through the use of resource qualifiers (values-w840dp)
-     * that define different margin values for different screen sizes.
-     *
-     * Note: This method does not affect the top or bottom padding of the ZoomView.
-     */
-    private void adjustZoomViewMargins() {
-        int margin = getResources().getDimensionPixelSize(R.dimen.viewer_doc_padding_x);
-
-        mZoomView.setPadding(margin,
-                mZoomView.getPaddingTop(),
-                margin,
-                mZoomView.getPaddingBottom());
-    }
-
     @Override
     public void onActivityCreated(@Nullable Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
-        mZoomView.zoomScroll().addObserver(mZoomScrollObserver);
         if (mPendingScrollPositionObserver != null) {
             mScrollPositionObserverKey = mZoomView.zoomScroll().addObserver(
                     mPendingScrollPositionObserver);
@@ -437,9 +260,8 @@
     @Override
     protected void onContentsAvailable(@NonNull DisplayData contents, @Nullable Bundle savedState) {
         mFileData = contents;
+        mLocalUri = contents.getUri();
 
-        // TODO: StrictMode- disk read 58ms.
-        int lengthMb = StrictModeUtils.bypassAndReturn(() -> (int) (contents.length() >> 20));
         createContentModel(
                 PdfLoader.create(
                         getActivity().getApplicationContext(),
@@ -447,11 +269,38 @@
                         TileBoard.DEFAULT_RECYCLER,
                         mPdfLoaderCallbacks,
                         false));
+        mLayoutHandler = new LayoutHandler(mPdfLoader);
+        mZoomView.setPdfSelectionModel(mSelectionModel);
+        mPaginatedView.setSelectionModel(mSelectionModel);
+        mPaginatedView.setSearchModel(mSearchModel);
+        mPaginatedView.setPdfLoader(mPdfLoader);
+
+        mSearchQueryObserver =
+                new SearchQueryObserver(mPaginatedView);
+        mSearchModel.query().addObserver(mSearchQueryObserver);
+
+        mSingleTapHandler = new SingleTapHandler(getContext(), mAnnotationButton,
+                mFindInFileView, mZoomView, mSelectionModel, mPaginationModel, mLayoutHandler);
+        mPageViewFactory = new PageViewFactory(requireContext(), mPdfLoader,
+                mPaginatedView, mZoomView, mSingleTapHandler);
+        mPaginatedView.setPageViewFactory(mPageViewFactory);
+
+        mSelectionObserver =
+                new PageSelectionValueObserver(mPaginatedView, mPaginationModel, mPageViewFactory,
+                        requireContext());
+        mSelectionModel.selection().addObserver(mSelectionObserver);
+
+        mSelectedMatchObserver =
+                new SelectedMatchValueObserver(mPaginatedView, mPaginationModel, mPageViewFactory,
+                        mZoomView, mLayoutHandler, requireContext());
+        mSearchModel.selectedMatch().addObserver(mSelectedMatchObserver);
+
+        mFindInFileView.setPaginatedView(mPaginatedView);
+        mFindInFileView.setAnnotationIntentResolvable(mIsAnnotationIntentResolvable);
 
         if (savedState != null) {
             int layoutReach = savedState.getInt(KEY_LAYOUT_REACH);
-            mEditingAuthorized = savedState.getBoolean(KEY_EDITING_AUTHORIZED);
-            mInitialPageLayoutReach = Math.max(mInitialPageLayoutReach, layoutReach);
+            mLayoutHandler.setInitialPageLayoutReachWithMax(layoutReach);
         }
     }
 
@@ -465,9 +314,6 @@
             mPdfLoader.reconnect();
         }
 
-        mPageViewFactory = new PageViewFactory(requireContext(), mPdfLoader,
-                mPaginatedView, mZoomView);
-
         if (mPaginatedView != null && mPaginatedView.getChildCount() > 0) {
             loadPageAssets(mZoomView.zoomScroll().get());
         }
@@ -475,44 +321,27 @@
 
     @Override
     public void onExit() {
-        if (mVisiblePages != null && mVisiblePages.getLast() > mMaxPage) {
-            mMaxPage = mVisiblePages.getLast();
-        }
-
         super.onExit();
         if (!mDocumentLoaded && mPdfLoader != null) {
             // e.g. a password-protected pdf that wasn't loaded.
             mPdfLoader.disconnect();
         }
-
-        if (mPaginatedView != null && mPaginatedView.getChildCount() > 0) {
-            for (PageMosaicView page : mPaginatedView.getChildViews()) {
-                page.clearTiles();
-                if (mPdfLoader != null) {
-                    mPdfLoader.cancelAllTileBitmaps(page.getPageNum());
-                }
-            }
-        }
     }
 
     private void createContentModel(PdfLoader pdfLoader) {
         this.mPdfLoader = pdfLoader;
+        mFindInFileView.setPdfLoader(pdfLoader);
 
-        mSearchModel = new SearchModel(pdfLoader);
-        mSearchModel.query().addObserver(mSearchQueryObserver);
-        mSearchModel.selectedMatch().addObserver(mSelectedMatchObserver);
+        mSearchModel = mFindInFileView.getSearchModel();
 
         mSelectionModel = new PdfSelectionModel(pdfLoader);
-        mSelectionModel.selection().addObserver(mSelectionObserver);
 
-        mSelectionHandles = new PdfSelectionHandles(mSelectionModel, mZoomView, mPaginatedView);
+        mSelectionHandles = new PdfSelectionHandles(mSelectionModel, mZoomView, mPaginatedView,
+                mSelectionActionMode);
 
     }
 
     private void destroyContentModel() {
-
-        mPageIndicator = null;
-
         mSelectionHandles.destroy();
         mSelectionHandles = null;
 
@@ -552,17 +381,12 @@
             mPaginationModel.removeObserver(mPaginatedView);
             mPaginatedView = null;
         }
-        // Clears the model so we can start fresh if we rebuild views.
-        mPaginationModel = new PaginationModel();
-        mVisiblePages = null;
 
         if (mPdfLoader != null) {
             mPdfLoader.cancelAll();
             mPdfLoader.disconnect();
             mDocumentLoaded = false;
         }
-        mZoomViewBasePadding = new Rect();
-        mZoomViewBasePaddingSaved = false;
         super.destroyView();
     }
 
@@ -572,9 +396,6 @@
         if (mSnackbar != null) {
             mSnackbar.dismiss();
         }
-        if (mFastscrollerPositionObserverKey != null && mFastScrollView != null) {
-            mFastScrollView.getScrollerPositionY().removeObserver(mFastscrollerPositionObserverKey);
-        }
     }
 
     @Override
@@ -583,33 +404,11 @@
         if (mPdfLoader != null) {
             destroyContentModel();
         }
-        mPrintableVersionCallback = null;
     }
 
     @Override
     public void onSaveInstanceState(@NonNull Bundle outState) {
-        outState.putInt(KEY_LAYOUT_REACH, mPageLayoutReach);
-
-        outState.putBoolean(KEY_EDITING_AUTHORIZED, mEditingAuthorized);
-    }
-
-    @Override
-    public void onConfigurationChanged(@NonNull Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        adjustZoomViewMargins();
-    }
-
-    @Override
-    public long getContentLength() {
-        return getPageCount();
-    }
-
-    @Override
-    public int getViewProgress() {
-        if (mMaxPage > 0) {
-            return (int) (((double) mMaxPage / getPageCount() * 100) * PROGRESS_SCALER);
-        }
-        return -1;
+        outState.putInt(KEY_LAYOUT_REACH, mLayoutHandler.getPageLayoutReach());
     }
 
     /**
@@ -629,6 +428,9 @@
         showSpinner();
         fetchFile(fileUri);
         mLocalUri = fileUri;
+        mIsAnnotationIntentResolvable = AnnotationUtils.resolveAnnotationIntent(requireContext(),
+                mLocalUri);
+        mSingleTapHandler.setAnnotationIntentResolvable(mIsAnnotationIntentResolvable);
     }
 
     private void validateFileUri(Uri fileUri) {
@@ -708,45 +510,6 @@
         postEnter();
     }
 
-    private int getPageCount() {
-        return mNumPages;
-    }
-
-    /** Returns the page currently roughly centered in the view. */
-    public int getViewingPage() {
-        return (mVisiblePages != null) ? (mVisiblePages.getFirst() + mVisiblePages.getLast()) / 2
-                : 0;
-    }
-
-    /**
-     * Lay out some pages up to some distant page. Not guaranteed to lay out any pages: maybe all
-     * pages, or at least enough pages, are already laid out.
-     */
-    private void maybeLayoutPages(int current) {
-        int peekAhead = Math.min(current + 2, 100);
-        int distantPage = Math.max(current + peekAhead, mInitialPageLayoutReach);
-        layoutPages(distantPage);
-    }
-
-    /**
-     * Lays out all the pages until {@code untilPage}, or equivalently so that {@code untilPage}s
-     * are laid out. So calling with {@code untilPage = 10} will ensure pages 0-9 are laid out.
-     *
-     * @param untilPage The upper limit of the range of pages to be laid out. Cropped to the
-     *                  number of pages of the document if this number was larger.
-     */
-    private void layoutPages(int untilPage) {
-        if (mPdfLoader == null) {
-            return;
-        }
-        int lastPage = Math.min(untilPage, getPageCount());
-        int requestLayoutPage = mPageLayoutReach;
-        while (requestLayoutPage < lastPage) {
-            mPdfLoader.loadPageDimensions(requestLayoutPage);
-            requestLayoutPage++;
-        }
-    }
-
     private boolean isPageCreated(int pageNum) {
         return pageNum < mPaginationModel.getSize() && mPaginatedView.getViewAt(pageNum) != null;
     }
@@ -755,16 +518,12 @@
         return mPaginatedView.getViewAt(pageNum);
     }
 
-    private Range allPages() {
-        return new Range(0, mPaginationModel.getSize() - 1);
-    }
-
     private void lookAtSelection(SelectedMatch selection) {
         if (selection == null || selection.isEmpty()) {
             return;
         }
         if (selection.getPage() >= mPaginationModel.getSize()) {
-            layoutPages(selection.getPage() + 1);
+            mLayoutHandler.layoutPages(selection.getPage() + 1);
             return;
         }
         Rect rect = selection.getPageMatches().getFirstRect(selection.getSelected());
@@ -775,148 +534,33 @@
         PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
                 selection.getPage(),
                 sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                mPaginationModel.getPageSize(selection.getPage()),
-                new PageTouchHandler());
+                mPaginationModel.getPageSize(selection.getPage()));
         pageView.setOverlay(selection.getOverlay());
     }
 
     private void loadPageAssets(ZoomScroll position) {
-        Range oldVisiblePages = mVisiblePages;
-
-        // 1. Refresh visible pages and view area.
-        mVisiblePages = computeVisibleRange(position);
-
-        if (mVisiblePages.getLast() > mMaxPage) {
-            mMaxPage = mVisiblePages.getLast();
-        }
-
         // Change the resolution of the bitmaps only when a gesture is not in progress.
-        if (position.stable || mStableZoom == 0) {
-            mStableZoom = position.zoom;
+        if (position.stable || mZoomView.getStableZoom() == 0) {
+            mZoomView.setStableZoom(position.zoom);
         }
 
         mPaginationModel.setViewArea(mZoomView.getVisibleAreaInContentCoords());
-
-        Range allPages = allPages();
-        int prefetchRadius = 1; // Note: could make this variable depending on resolution.
-        Range nearPages = expandRange(mVisiblePages, prefetchRadius, allPages);
-
-        // 2. Release pages that we don't need anymore.
-        Range[] gonePages = allPages.minus(nearPages);
-        for (Range pages : gonePages) {
-            // Keep Views around for now, we'll clear them in step (4) if applicable.
-            clearPages(pages, false);
-        }
-
-        // 3. Bring minimal service to pages that we might need very soon
-        Range[] invisibleNearPages = nearPages.minus(mVisiblePages);
-        for (Range pages : invisibleNearPages) {
-            loadPageOnly(pages);
-        }
+        mPaginatedView.refreshPageRangeInVisibleArea(position, mZoomView.getHeight());
+        mPaginatedView.handleGonePages(/* clearViews= */ false);
+        mPaginatedView.loadInvisibleNearPageRange(mZoomView.getStableZoom());
 
         // The step (4) below requires page Views to be created and laid out. So we create them here
         // and set this flag if that operation needs to wait for a layout pass.
-        boolean requiresLayoutPass = false;
-        for (int pageNum : mVisiblePages) {
-            if (mPaginatedView.getViewAt(pageNum) == null) {
-                mPageViewFactory.getOrCreatePageView(pageNum,
-                        sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                        mPaginationModel.getPageSize(pageNum),
-                        new PageTouchHandler());
-                requiresLayoutPass = true;
-            }
-        }
+        boolean requiresLayoutPass = mPaginatedView.createPageViewsForVisiblePageRange();
 
         // 4. Refresh tiles and/or full pages.
         if (position.stable) {
             // Perform a full refresh on all visible pages
-            if (requiresLayoutPass) {
-                refreshPagesAfterLayout(mVisiblePages);
-            } else {
-                refreshPages(mVisiblePages);
-            }
-            for (Range pages : gonePages) {
-                clearPages(pages, true);
-            }
-        } else if (mStableZoom == position.zoom) {
-            // Just load a few more tiles in case of tile-scroll
-            if (requiresLayoutPass) {
-                refreshTilesAfterLayout(mVisiblePages);
-            } else {
-                refreshTiles(mVisiblePages);
-            }
+            mPaginatedView.handleGonePages(/* clearViews= */ true);
         }
 
-        maybeLayoutPages(mVisiblePages.getLast());
-    }
-
-    private void refreshTiles(Range pages) {
-        for (int page : pages) {
-            PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
-                    page,
-                    sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                    mPaginationModel.getPageSize(page),
-                    new PageTouchHandler());
-            pageView.requestTiles();
-        }
-    }
-
-    private void refreshTilesAfterLayout(final Range pages) {
-        ThreadUtils.postOnUiThread(
-                () -> {
-                    if (viewState().get() != ViewState.NO_VIEW) {
-                        refreshTiles(pages);
-                    }
-                });
-    }
-
-    private void loadPageOnly(Range pages) {
-        for (int page : pages) {
-            mPdfLoader.cancelAllTileBitmaps(page);
-            PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
-                    page,
-                    sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                    mPaginationModel.getPageSize(page),
-                    new PageTouchHandler());
-            pageView.clearTiles();
-            pageView.requestFastDrawAtZoom(mStableZoom);
-            loadVisiblePageText(page);
-            maybeLoadFormAccessibilityInfo(page);
-        }
-    }
-
-    private void refreshPages(Range pages) {
-        for (int page : pages) {
-            PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
-                    page,
-                    sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                    mPaginationModel.getPageSize(page),
-                    new PageTouchHandler());
-            pageView.requestDrawAtZoom(mStableZoom);
-            loadVisiblePageText(page);
-            maybeLoadFormAccessibilityInfo(page);
-        }
-    }
-
-    private void refreshPagesAfterLayout(final Range pages) {
-        ThreadUtils.postOnUiThread(
-                () -> {
-                    if (viewState().get() != ViewState.NO_VIEW) {
-                        refreshPages(pages);
-                    }
-                });
-    }
-
-    private void clearPages(Range pages, boolean clearViews) {
-        for (int page : pages) {
-            // Don't cancel search - search results for the current search are always useful,
-            // even for pages we can't see right now. Form filling operations should always
-            // be executed against the document, even if the user has scrolled away from the page.
-            mPdfLoader.cancelExceptSearchAndFormFilling(page);
-            if (clearViews) {
-                mPaginatedView.removeViewAt(page);
-            }
-        }
+        mLayoutHandler.maybeLayoutPages(
+                mPaginatedView.getPageRangeHandler().getVisiblePages().getLast());
     }
 
     /** Show the loading spinner. */
@@ -935,334 +579,6 @@
         }
     }
 
-    private void loadVisiblePageText(int page) {
-        PageView pageView = mPageViewFactory.getOrCreatePageView(
-                page,
-                sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                mPaginationModel.getPageSize(page),
-                new PageTouchHandler());
-        PageMosaicView pageMosaicView = pageView.getPageView();
-        if (pageMosaicView.needsPageText()) {
-            mPdfLoader.loadPageText(page);
-        }
-        if (!pageMosaicView.hasPageUrlLinks()) {
-            mPdfLoader.loadPageUrlLinks(page);
-        }
-        if (!pageMosaicView.hasPageGotoLinks()) {
-            mPdfLoader.loadPageGotoLinks(page);
-        }
-        if (page == mSelectionModel.getPage()) {
-            pageMosaicView.setOverlay(new PdfHighlightOverlay(mSelectionModel.selection().get()));
-        } else if (mSearchModel.query().get() != null) {
-            if (!pageMosaicView.hasOverlay()) {
-                mPdfLoader.searchPageText(page, mSearchModel.query().get());
-            }
-        } else {
-            pageMosaicView.setOverlay(null);
-        }
-    }
-
-    /**
-     * Load accessibility information for the form if document can be edited and accessibility is
-     * required.
-     */
-    private void maybeLoadFormAccessibilityInfo(int pageNum) {
-        mPageViewFactory.getOrCreatePageView(
-                pageNum,
-                sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                mPaginationModel.getPageSize(pageNum),
-                new PageTouchHandler());
-    }
-
-    /** Computes the range of visible pages in the given position. */
-    private Range computeVisibleRange(ZoomScroll position) {
-        int top = Math.round(position.scrollY / position.zoom);
-        int bottom = Math.round((position.scrollY + mZoomView.getHeight()) / position.zoom);
-        Range window = new Range(top, bottom);
-        return mPaginationModel.getPagesInWindow(window, true);
-    }
-
-    /** Expand the range to include more page(s) in the each direction. */
-    private static Range expandRange(Range range, int margin, Range allPages) {
-        return range.expand(margin, allPages);
-    }
-
-    /**
-     * Computes the range of pages that are entirely visible, or if no page is entirely visible,
-     * returns the most visible page.
-     */
-    private Range computeImportantRange(ZoomScroll position) {
-        int top = Math.round(position.scrollY / position.zoom);
-        int bottom = Math.round((position.scrollY + mZoomView.getHeight()) / position.zoom);
-        Range window = new Range(top, bottom);
-        return mPaginationModel.getPagesInWindow(window, false);
-    }
-
-    { // Listen to ZoomView.
-        mZoomScrollObserver =
-                new ValueObserver<ZoomScroll>() {
-                    @Override
-                    public void onChange(ZoomScroll oldPosition, ZoomScroll position) {
-                        loadPageAssets(position);
-                        if (mPageIndicator.setRangeAndZoom(
-                                computeImportantRange(position), position.zoom, position.stable)) {
-                            showFastScrollView();
-                        }
-
-                        if (AnnotationUtils.launchAnnotationIntent(requireContext(), mLocalUri)) {
-                            if (position.scrollY > 0) {
-                                mAnnotationButton.setVisibility(View.GONE);
-                            } else if (position.scrollY == 0
-                                    && mAnnotationButton.getVisibility() == View.GONE
-                                    && mFindInFileView.getVisibility() == View.GONE) {
-                                mAnnotationButton.setVisibility(View.VISIBLE);
-                            }
-                        }
-                    }
-
-                    @NonNull
-                    @Override
-                    public String toString() {
-                        return TAG + "#zoomScrollObserver";
-                    }
-                };
-    }
-
-    { // Listen to searchModel.
-        mSearchQueryObserver =
-                new ValueObserver<String>() {
-                    @Override
-                    public void onChange(String oldQuery, String newQuery) {
-                        mPaginatedView.clearAllOverlays();
-                    }
-
-                    @NonNull
-                    @Override
-                    public String toString() {
-                        return TAG + "#searchQueryObserver";
-                    }
-                };
-
-        mSelectedMatchObserver =
-                new ValueObserver<SelectedMatch>() {
-                    @Override
-                    public void onChange(
-                            @Nullable SelectedMatch oldSelection,
-                            @Nullable SelectedMatch newSelection) {
-                        if (newSelection == null) {
-                            mPaginatedView.clearAllOverlays();
-                            return;
-                        }
-                        if (oldSelection != null && isPageCreated(oldSelection.getPage())) {
-                            // Selected match has moved onto a new page - update the overlay on
-                            // the old page.
-                            getPage(oldSelection.getPage())
-                                    .getPageView()
-                                    .setOverlay(
-                                            new PdfHighlightOverlay(oldSelection.getPageMatches()));
-                        }
-                        lookAtSelection(newSelection);
-                    }
-
-                    @NonNull
-                    @Override
-                    public String toString() {
-                        return TAG + "#selectedMatchObserver";
-                    }
-                };
-    }
-
-    { // Listen to selectionModel.
-        mSelectionObserver =
-                new ValueObserver<PageSelection>() {
-                    @Override
-                    public void onChange(PageSelection oldSelection, PageSelection newSelection) {
-                        if (oldSelection != null && isPageCreated(oldSelection.getPage())) {
-                            getPage(oldSelection.getPage()).getPageView().setOverlay(null);
-                        }
-                        if (newSelection != null && mVisiblePages.contains(
-                                newSelection.getPage())) {
-                            ((PageMosaicView) mPageViewFactory.getOrCreatePageView(
-                                    newSelection.getPage(),
-                                    sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                                    mPaginationModel.getPageSize(newSelection.getPage()),
-                                    new PageTouchHandler()))
-                                    .setOverlay(new PdfHighlightOverlay(newSelection));
-                        }
-                    }
-
-                    @NonNull
-                    @Override
-                    public String toString() {
-                        return TAG + "#selectionObserver";
-                    }
-                };
-    }
-
-    {
-        mFastscrollerPositionObserver =
-                new ValueObserver<Integer>() {
-                    @Override
-                    public void onChange(@Nullable Integer oldValue, @Nullable Integer newValue) {
-                        if (mPageIndicator != null && newValue != null) {
-                            mPageIndicator.getView().setY(
-                                    newValue - (mPageIndicator.getView().getHeight() / 2));
-                            mPageIndicator.show();
-                            showFastScrollView();
-                        }
-                    }
-
-                    @NonNull
-                    @Override
-                    public String toString() {
-                        return TAG + "#fastscrollerPositionObserver";
-                    }
-                };
-    }
-
-    private FindInFileListener makeFindInFileListener() {
-        return new FindInFileListener() {
-            @Override
-            public boolean onQueryTextChange(@Nullable String query) {
-                if (mSearchModel != null) {
-                    mSearchModel.setQuery(query, getViewingPage());
-                    return true;
-                }
-                return false;
-            }
-
-            @Override
-            public boolean onFindNextMatch(String query, boolean backwards) {
-                if (mSearchModel != null) {
-                    CycleRange.Direction direction;
-                    if (backwards) {
-                        direction = CycleRange.Direction.BACKWARDS;
-                        // TODO: Track "find previous" action event.
-                    } else {
-                        direction = CycleRange.Direction.FORWARDS;
-                        // TODO: Track "find next" action event.
-                    }
-                    mSearchModel.selectNextMatch(direction, getViewingPage());
-                    return true;
-                }
-                return false;
-            }
-
-            @Nullable
-            @Override
-            public ObservableValue<MatchCount> matchCount() {
-                return mSearchModel != null ? mSearchModel.matchCount() : null;
-            }
-        };
-    }
-
-    public class PageTouchHandler {
-        /**
-         * Handles a tap event for non-formfilling actions.
-         *
-         * <p>This is includes comments, links and full screen toggle. Separate from form filling as
-         * form filling involves asynchronous evaluations that must be completed outside normal
-         * branch
-         * statements.
-         */
-        public boolean handleSingleTapNoFormFilling(@NonNull MotionEvent event,
-                @NonNull PageMosaicView pageMosaicView) {
-            if (AnnotationUtils.launchAnnotationIntent(requireContext(), mLocalUri)) {
-                if (mAnnotationButton.getVisibility() == View.GONE
-                        && mFindInFileView.getVisibility() == GONE) {
-                    mAnnotationButton.setVisibility(View.VISIBLE);
-                } else {
-                    mAnnotationButton.setVisibility(View.GONE);
-                }
-            }
-            boolean hadSelection =
-                    mSelectionModel != null && mSelectionModel.selection().get() != null;
-            if (hadSelection) {
-                mSelectionModel.setSelection(null);
-            }
-
-            Point point = new Point((int) event.getX(), (int) event.getY());
-            String linkUrl = pageMosaicView.getLinkUrl(point);
-            if (linkUrl != null) {
-                ExternalLinks.open(linkUrl, requireActivity());
-            }
-
-            GotoLinkDestination gotoDest = pageMosaicView.getGotoDestination(point);
-            if (gotoDest != null) {
-                gotoPageDest(gotoDest);
-            }
-
-            return true;
-        }
-
-        /** */
-        public void gotoPageDest(@NonNull GotoLinkDestination destination) {
-
-            if (destination.getPageNumber() >= mPaginationModel.getSize()) {
-                // We have not yet loaded our destination.
-                layoutPages(destination.getPageNumber() + 1);
-                mDimensCallbackQueue.add(
-                        pageNum -> {
-                            if (pageNum == destination.getPageNumber()) {
-                                gotoPageDest(destination);
-                                return false;
-                            }
-                            return true;
-                        });
-                return;
-            }
-
-            if (destination.getYCoordinate() != null) {
-                int pageY = (int) destination.getYCoordinate().floatValue();
-
-                Rect pageRect = mPaginationModel.getPageLocation(destination.getPageNumber());
-                int x = pageRect.left + (pageRect.width() / 2);
-                int y = mPaginationModel.getLookAtY(destination.getPageNumber(), pageY);
-                // Zoom should match the width of the page.
-                float zoom =
-                        ZoomUtils.calculateZoomToFit(
-                                mZoomView.getViewportWidth(), mZoomView.getViewportHeight(),
-                                pageRect.width(), 1);
-
-                mZoomView.setZoom(zoom);
-                mZoomView.centerAt(x, y);
-            } else {
-                gotoPage(destination.getPageNumber());
-            }
-        }
-
-        /** Goes to the {@code pageNum} and fits the page to the current viewport. */
-        private void gotoPage(int pageNum) {
-            if (pageNum >= mPaginationModel.getSize()) {
-                // We have not yet loaded our destination.
-                layoutPages(pageNum + 1);
-                mDimensCallbackQueue.add(
-                        loadedPageNum -> {
-                            if (pageNum == loadedPageNum) {
-                                gotoPage(pageNum);
-                                return false;
-                            }
-                            return true;
-                        });
-                return;
-            }
-
-            Rect pageRect = mPaginationModel.getPageLocation(pageNum);
-
-            int x = pageRect.left + (pageRect.width() / 2);
-            int y = pageRect.top + (pageRect.height() / 2);
-            float zoom =
-                    ZoomUtils.calculateZoomToFit(
-                            mZoomView.getViewportWidth(),
-                            mZoomView.getViewportHeight(),
-                            pageRect.width(),
-                            pageRect.height());
-
-            mZoomView.setZoom(zoom);
-            mZoomView.centerAt(x, y);
-        }
-    }
-
     // TODO: Revisit this method for its usage. Currently redundant
 
     { // Init pdfLoaderCallbacks
@@ -1284,6 +600,7 @@
                     // Callbacks should exit early if viewState == NO_VIEW (typically a Destroy
                     // is in progress).
                     @Override
+                    @SuppressWarnings("deprecation")
                     public void requestPassword(boolean incorrect) {
                         mIsPasswordProtected = true;
 
@@ -1329,20 +646,18 @@
                         }
 
                         mDocumentLoaded = true;
-                        PdfViewer.this.mNumPages = numPages;
-
                         hideSpinner();
 
                         // Assume we see at least the first page
-                        mMaxPage = 1;
+                        mPaginatedView.getPageRangeHandler().setMaxPage(1);
                         if (viewState().get() != ViewState.NO_VIEW) {
                             mPaginationModel.initialize(numPages);
                             mPaginatedView.setModel(mPaginationModel);
                             mPaginationModel.addObserver(mPaginatedView);
+                            mFastScrollView.setPaginationModel(mPaginationModel);
 
                             dismissPasswordDialog();
-                            maybeLayoutPages(1);
-                            mPageIndicator.setNumPages(numPages);
+                            mLayoutHandler.maybeLayoutPages(1);
                             mSearchModel.setNumPages(numPages);
                         }
 
@@ -1350,7 +665,7 @@
                             mShouldRedrawOnDocumentLoaded = false;
                         }
 
-                        if (AnnotationUtils.launchAnnotationIntent(requireContext(), mLocalUri)) {
+                        if (mIsAnnotationIntentResolvable) {
                             mAnnotationButton.setVisibility(VISIBLE);
                         }
                     }
@@ -1392,14 +707,14 @@
                             ((PageMosaicView) mPageViewFactory.getOrCreatePageView(
                                     page,
                                     sScreen.pxFromDp(PAGE_ELEVATION_DP),
-                                    mPaginationModel.getPageSize(page),
-                                    new PageTouchHandler()))
+                                    mPaginationModel.getPageSize(page)))
                                     .setFailure(getString(R.string.error_on_page, page + 1));
                             Toaster.LONG.popToast(getActivity(), R.string.error_on_page, page + 1);
                             // TODO: Track render error.
                         }
                     }
 
+                    @SuppressWarnings("deprecation")
                     private void dismissPasswordDialog() {
                         DialogFragment passwordDialog = currentPasswordDialog(
                                 requireActivity().getSupportFragmentManager());
@@ -1413,7 +728,7 @@
                     public void setPageDimensions(int pageNum, @NonNull Dimensions dimensions) {
                         if (viewState().get() != ViewState.NO_VIEW) {
                             mPaginationModel.addPage(pageNum, dimensions);
-                            mPageLayoutReach = mPaginationModel.getSize();
+                            mLayoutHandler.setPageLayoutReach(mPaginationModel.getSize());
 
                             if (mSearchModel.query().get() != null
                                     && mSearchModel.selectedMatch().get() != null
@@ -1427,35 +742,15 @@
                                         });
                             }
 
-                            ThreadUtils.postOnUiThread(
-                                    () -> {
-                                        if (mDimensCallbackQueue.isEmpty()
-                                                || viewState().get() == ViewState.NO_VIEW) {
-                                            return;
-                                        }
-
-                                        Iterator<OnDimensCallback> iterator =
-                                                mDimensCallbackQueue.iterator();
-                                        while (iterator.hasNext()) {
-                                            OnDimensCallback callback = iterator.next();
-                                            boolean shouldKeep = callback.onDimensLoaded(pageNum);
-                                            if (!shouldKeep) {
-                                                iterator.remove();
-                                            }
-                                        }
-                                    });
-
                             // The new page might actually be visible on the screen, so we need
                             // to fetch assets:
-                            Range newRange = computeVisibleRange(mZoomView.zoomScroll().get());
+                            ZoomScroll position = mZoomView.zoomScroll().get();
+                            Range newRange =
+                                    mPaginatedView.getPageRangeHandler().computeVisibleRange(
+                                            position.scrollY, position.zoom, mZoomView.getHeight(),
+                                            true);
                             if (newRange.isEmpty()) {
-                                // During fast-scroll, we mostly don't need to fetch assets, but
-                                // make sure we keep pushing layout bounds far enough, and update
-                                // page numbers as we "scroll" down.
-                                if (mPageIndicator.setRangeAndZoom(newRange, mStableZoom, false)) {
-                                    showFastScrollView();
-                                }
-                                maybeLayoutPages(newRange.getLast());
+                                mLayoutHandler.maybeLayoutPages(newRange.getLast());
                             } else if (newRange.contains(pageNum)) {
                                 // The new page is visible, fetch its assets.
                                 loadPageAssets(mZoomView.zoomScroll().get());
@@ -1512,10 +807,6 @@
                             return;
                         }
                         if (selection != null) {
-                            if (mWaitingOnSelectionToCreateInlineComment) {
-                                mSelectionModel.setSelection(selection);
-                                return;
-                            }
                             // Clear searchModel - we hide the search and show the selection
                             // instead.
                             mSearchModel.setQuery(null, -1);
@@ -1538,22 +829,6 @@
                         }
                     }
 
-                    @Override
-                    public void documentCloned(boolean result) {
-                        if (mPrintableVersionCallback != null) {
-                            mPrintableVersionCallback.set(result);
-                        }
-                        mPrintableVersionCallback = null;
-                    }
-
-                    @Override
-                    public void documentSavedAs(boolean result) {
-                        if (mSaveAsCallback != null) {
-                            mSaveAsCallback.set(result);
-                        }
-                        mSaveAsCallback = null;
-                    }
-
                     /**
                      * Receives areas of a page that have been invalidated by an editing action
                      * and asks the
@@ -1572,37 +847,6 @@
                 };
     }
 
-    @Override
-    public float estimateFullContentHeight() {
-        return mPaginationModel.getEstimatedFullHeight();
-    }
-
-    @Override
-    public float visibleHeight() {
-        return mZoomView.getViewportHeight() / mZoomView.getZoom();
-    }
-
-    @Override
-    public void fastScrollTo(float position, boolean stable) {
-        mZoomView.scrollTo(mZoomView.getScrollX(), (int) (position * mZoomView.getZoom()), stable);
-    }
-
-    @Override
-    public void setFastScrollListener(final @NonNull FastScrollListener listener) {
-        mZoomView
-                .getViewTreeObserver()
-                .addOnScrollChangedListener(
-                        new OnScrollChangedListener() {
-                            @Override
-                            public void onScrollChanged() {
-                                if (mZoomView != null) {
-                                    listener.updateFastScrollbar(
-                                            mZoomView.getScrollY() / mZoomView.getZoom());
-                                }
-                            }
-                        });
-    }
-
     protected void handleError() {
         mViewState.set(ViewState.ERROR);
     }
@@ -1636,41 +880,6 @@
         mSnackbar.show();
     }
 
-    private void showFastScrollView() {
-        if (mFastScrollView != null) {
-            mFastScrollView.setVisible();
-        }
-    }
-
-    /**
-     * Set up the find in file menu.
-     */
-    public void setFindInFileView(boolean visibility) {
-        if (visibility) {
-            mFindInFileView.setVisibility(VISIBLE);
-            setupFindInFileBtn();
-        } else {
-            mFindInFileView.setVisibility(GONE);
-        }
-    }
-
-    private void setupFindInFileBtn() {
-        mFindInFileView.setFindInFileListener(this.makeFindInFileListener());
-        mFindInFileView.queryBoxRequestFocus();
-
-        TextView queryBox = mFindInFileView.findViewById(R.id.find_query_box);
-        ImageView close_button = mFindInFileView.findViewById(R.id.close_btn);
-        close_button.setOnClickListener(view -> {
-            View parentLayout = (View) close_button.getParent();
-            queryBox.clearFocus();
-            queryBox.setText("");
-            parentLayout.setVisibility(GONE);
-            if (AnnotationUtils.launchAnnotationIntent(requireContext(), mLocalUri)) {
-                mAnnotationButton.setVisibility(VISIBLE);
-            }
-        });
-    }
-
     private void setUpEditFab() {
         mAnnotationButton = mPdfViewer.findViewById(R.id.edit_fab);
         mAnnotationButton.setOnClickListener(new View.OnClickListener() {
@@ -1687,4 +896,5 @@
         intent.setData(mLocalUri);
         startActivity(intent);
     }
+
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SearchQueryObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SearchQueryObserver.java
new file mode 100644
index 0000000..724e948
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SearchQueryObserver.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.util.ObservableValue;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SearchQueryObserver implements ObservableValue.ValueObserver<String> {
+    private final PaginatedView mPaginatedView;
+
+    public SearchQueryObserver(@NonNull PaginatedView paginatedView) {
+        mPaginatedView = paginatedView;
+    }
+
+    @Override
+    public void onChange(@Nullable String oldQuery, @Nullable String newQuery) {
+        mPaginatedView.clearAllOverlays();
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java
index 3639a61..9979a5d 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatch.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.pdf.models.MatchRects;
@@ -34,7 +35,7 @@
  * <p>Immutable.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-class SelectedMatch {
+public class SelectedMatch {
     private final String mQuery;
     private final int mPage;
     private final MatchRects mPageMatches;
@@ -58,14 +59,11 @@
         this.mSelected = selected;
     }
 
-    public String getQuery() {
-        return mQuery;
-    }
-
     public int getPage() {
         return mPage;
     }
 
+    @NonNull
     public MatchRects getPageMatches() {
         return mPageMatches;
     }
@@ -107,7 +105,7 @@
     }
 
     @Nullable
-    public SelectedMatch selectNextMatchOnPage(Direction direction) {
+    public SelectedMatch selectNextMatchOnPage(@NonNull Direction direction) {
         if (direction == Direction.BACKWARDS && mSelected > 0) {
             return withSelected(mSelected - 1);
         } else if (direction == Direction.FORWARDS && mSelected < mPageMatches.size() - 1) {
@@ -124,7 +122,8 @@
      * Given a new set of matches, selects the one that is closest to the old selected match (if
      * any).
      */
-    public SelectedMatch nearestMatch(String newQuery, MatchRects newMatches) {
+    @NonNull
+    public SelectedMatch nearestMatch(@NonNull String newQuery, @NonNull MatchRects newMatches) {
         if (newMatches.isEmpty()) {
             return noMatches(newQuery, mPage);
         }
@@ -138,12 +137,15 @@
     }
 
     /** Returns a SelectedMatch that contains no matches and so nothing is selected. */
-    public static SelectedMatch noMatches(String query, int page) {
+    @NonNull
+    public static SelectedMatch noMatches(@NonNull String query, int page) {
         return new SelectedMatch(query, page, MatchRects.NO_MATCHES, -1);
     }
 
     /** Selects the first match from the given matches. */
-    public static SelectedMatch firstMatch(String query, int page, MatchRects matches) {
+    @NonNull
+    public static SelectedMatch firstMatch(@NonNull String query, int page,
+            @NonNull MatchRects matches) {
         return matches.isEmpty() ? noMatches(query, page) : new SelectedMatch(query, page, matches,
                 0);
     }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatchValueObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatchValueObserver.java
new file mode 100644
index 0000000..1d51122
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SelectedMatchValueObserver.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import android.content.Context;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.util.ObservableValue;
+import androidx.pdf.util.PaginationUtils;
+import androidx.pdf.widget.ZoomView;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SelectedMatchValueObserver implements ObservableValue.ValueObserver<SelectedMatch> {
+    private final PaginatedView mPaginatedView;
+    private final PaginationModel mPaginationModel;
+    private final PageViewFactory mPageViewFactory;
+    private final LayoutHandler mLayoutHandler;
+    private final ZoomView mZoomView;
+    private final Context mContext;
+
+    public SelectedMatchValueObserver(@NonNull PaginatedView paginatedView,
+            @NonNull PaginationModel paginationModel, @NonNull PageViewFactory pageViewFactory,
+            @NonNull ZoomView zoomView, @NonNull LayoutHandler layoutHandler,
+            @NonNull Context context) {
+        mPaginatedView = paginatedView;
+        mPaginationModel = paginationModel;
+        mPageViewFactory = pageViewFactory;
+        mZoomView = zoomView;
+        mLayoutHandler = layoutHandler;
+        mContext = context;
+    }
+
+    @Override
+    public void onChange(
+            @Nullable SelectedMatch oldSelection,
+            @Nullable SelectedMatch newSelection) {
+        if (newSelection == null) {
+            mPaginatedView.clearAllOverlays();
+            return;
+        }
+        if (oldSelection != null && isPageCreated(oldSelection.getPage())) {
+            // Selected match has moved onto a new page - update the overlay on
+            // the old page.
+            mPaginatedView.getViewAt(oldSelection.getPage())
+                    .getPageView()
+                    .setOverlay(
+                            new PdfHighlightOverlay(oldSelection.getPageMatches()));
+        }
+        lookAtSelection(newSelection);
+    }
+
+    private boolean isPageCreated(int pageNum) {
+        return pageNum < mPaginationModel.getSize() && mPaginatedView.getViewAt(pageNum) != null;
+    }
+
+    private void lookAtSelection(SelectedMatch selection) {
+        if (selection == null || selection.isEmpty()) {
+            return;
+        }
+        if (selection.getPage() >= mPaginationModel.getSize()) {
+            mLayoutHandler.layoutPages(selection.getPage() + 1);
+            return;
+        }
+        Rect rect = selection.getPageMatches().getFirstRect(selection.getSelected());
+        int x = mPaginationModel.getLookAtX(selection.getPage(), rect.centerX());
+        int y = mPaginationModel.getLookAtY(selection.getPage(), rect.centerY());
+        mZoomView.centerAt(x, y);
+
+        PageMosaicView pageView = (PageMosaicView) mPageViewFactory.getOrCreatePageView(
+                selection.getPage(),
+                PaginationUtils.getPageElevationInPixels(mContext),
+                mPaginationModel.getPageSize(selection.getPage()));
+        pageView.setOverlay(selection.getOverlay());
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SingleTapHandler.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SingleTapHandler.java
new file mode 100644
index 0000000..74f02a6
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SingleTapHandler.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import static android.view.View.GONE;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.find.FindInFileView;
+import androidx.pdf.models.GotoLinkDestination;
+import androidx.pdf.util.ExternalLinks;
+import androidx.pdf.util.ZoomUtils;
+import androidx.pdf.widget.ZoomView;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SingleTapHandler {
+    private final Context mContext;
+    private final FloatingActionButton mFloatingActionButton;
+    private final FindInFileView mFindInFileView;
+    private final ZoomView mZoomView;
+    private final PdfSelectionModel mPdfSelectionModel;
+    private final PaginationModel mPaginationModel;
+    private final LayoutHandler mLayoutHandler;
+    private boolean mIsAnnotationIntentResolvable;
+
+    public SingleTapHandler(@NonNull Context context,
+            @NonNull FloatingActionButton floatingActionButton,
+            @NonNull FindInFileView findInFileView,
+            @NonNull ZoomView zoomView,
+            @NonNull PdfSelectionModel pdfSelectionModel,
+            @NonNull PaginationModel paginationModel,
+            @NonNull LayoutHandler layoutHandler) {
+        mContext = context;
+        mFloatingActionButton = floatingActionButton;
+        mFindInFileView = findInFileView;
+        mZoomView = zoomView;
+        mPdfSelectionModel = pdfSelectionModel;
+        mPaginationModel = paginationModel;
+        mLayoutHandler = layoutHandler;
+    }
+
+    public void setAnnotationIntentResolvable(boolean annotationIntentResolvable) {
+        mIsAnnotationIntentResolvable = annotationIntentResolvable;
+    }
+
+    /** */
+    public void handleSingleTapConfirmedEventOnPage(@NonNull MotionEvent event,
+            @NonNull PageMosaicView pageMosaicView) {
+        if (mIsAnnotationIntentResolvable) {
+            if (mFloatingActionButton.getVisibility() == View.GONE
+                    && mFindInFileView.getVisibility() == GONE) {
+                mFloatingActionButton.setVisibility(View.VISIBLE);
+            } else {
+                mFloatingActionButton.setVisibility(View.GONE);
+            }
+        }
+
+        handleSelection();
+
+        Point point = new Point((int) event.getX(), (int) event.getY());
+        handleExternalLink(point, pageMosaicView);
+
+        GotoLinkDestination gotoDest = pageMosaicView.getGotoDestination(point);
+        if (gotoDest != null) {
+            gotoPageDest(gotoDest);
+        }
+    }
+
+    private void handleSelection() {
+        boolean hadSelection =
+                mPdfSelectionModel != null && mPdfSelectionModel.selection().get() != null;
+        if (hadSelection) {
+            mPdfSelectionModel.setSelection(null);
+        }
+    }
+
+    private void handleExternalLink(Point point, PageMosaicView pageMosaicView) {
+        String linkUrl = pageMosaicView.getLinkUrl(point);
+        if (linkUrl != null) {
+            ExternalLinks.open(linkUrl, mContext);
+        }
+    }
+
+    /**  */
+    public void gotoPageDest(@NonNull GotoLinkDestination destination) {
+
+        if (destination.getPageNumber() >= mPaginationModel.getSize()) {
+            // We have not yet loaded our destination.
+            mLayoutHandler.layoutPages(destination.getPageNumber() + 1);
+            mLayoutHandler.add(
+                    pageNum -> {
+                        if (pageNum == destination.getPageNumber()) {
+                            gotoPageDest(destination);
+                            return false;
+                        }
+                        return true;
+                    });
+            return;
+        }
+
+        if (destination.getYCoordinate() != null) {
+            int pageY = (int) destination.getYCoordinate().floatValue();
+
+            Rect pageRect = mPaginationModel.getPageLocation(destination.getPageNumber());
+            int x = pageRect.left + (pageRect.width() / 2);
+            int y = mPaginationModel.getLookAtY(destination.getPageNumber(), pageY);
+            // Zoom should match the width of the page.
+            float zoom =
+                    ZoomUtils.calculateZoomToFit(
+                            mZoomView.getViewportWidth(), mZoomView.getViewportHeight(),
+                            pageRect.width(), 1);
+
+            mZoomView.setZoom(zoom);
+            mZoomView.centerAt(x, y);
+        } else {
+            gotoPage(destination.getPageNumber());
+        }
+    }
+
+    /** Goes to the {@code pageNum} and fits the page to the current viewport. */
+    private void gotoPage(int pageNum) {
+        if (pageNum >= mPaginationModel.getSize()) {
+            // We have not yet loaded our destination.
+            mLayoutHandler.layoutPages(pageNum + 1);
+            mLayoutHandler.add(
+                    loadedPageNum -> {
+                        if (pageNum == loadedPageNum) {
+                            gotoPage(pageNum);
+                            return false;
+                        }
+                        return true;
+                    });
+            return;
+        }
+
+        Rect pageRect = mPaginationModel.getPageLocation(pageNum);
+
+        int x = pageRect.left + (pageRect.width() / 2);
+        int y = pageRect.top + (pageRect.height() / 2);
+        float zoom =
+                ZoomUtils.calculateZoomToFit(
+                        mZoomView.getViewportWidth(),
+                        mZoomView.getViewportHeight(),
+                        pageRect.width(),
+                        pageRect.height());
+
+        mZoomView.setZoom(zoom);
+        mZoomView.centerAt(x, y);
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/Viewer.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/Viewer.java
index 64a949d..b81b180 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/Viewer.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/Viewer.java
@@ -56,14 +56,8 @@
 @SuppressWarnings("deprecation")
 public abstract class Viewer extends Fragment {
 
-    @NonNull
-    protected abstract String getLogTag();
-
     protected static final String KEY_DATA = "data";
 
-    /** Scale for the progress metric. */
-    protected static final int PROGRESS_SCALER = 100;
-
     /**
      * The state of the view hierarchy for this {@link Fragment}, as exposed by {@link #mViewState}.
      */
@@ -121,9 +115,6 @@
     protected ExposedValue<ViewState> mViewState = Observables.newExposedValueWithInitialValue(
             ViewState.NO_VIEW);
 
-    // Debug log of lifecycle events that happened on this viewer, helps investigating.
-    private final StringBuilder mEventlog = new StringBuilder();
-
     {
         // We can call getArguments() from setters and know that it will not be null.
         setArguments(new Bundle());
@@ -203,13 +194,6 @@
         }
     }
 
-    /** Notifies this Viewer goes off-screen. {@link #onExit()} will be called immediately. */
-    public void exit() {
-        mDelayedEnter = false; // in case we never started.
-        onExit();
-        mOnScreen = false;
-    }
-
     /** Called after this viewer enters the screen and becomes visible. */
     @CallSuper
     protected void onEnter() {
@@ -314,18 +298,6 @@
         getArguments().putBundle(KEY_DATA, data.asBundle());
     }
 
-    /** Returns a compact event log for this Viewer that helps investigating lifecycle issues. */
-    @NonNull
-    protected String getEventlog() {
-        return mEventlog.toString();
-    }
-
-    /** Returns the length of the current file. The meaning of the length is type dependent. */
-    public abstract long getContentLength();
-
-    /** Returns the user's current progress in the file in percentage. */
-    public abstract int getViewProgress();
-
     @Override
     protected void finalize() throws Throwable {
         super.finalize();
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
new file mode 100644
index 0000000..2f8b034
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import android.animation.ValueAnimator;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.pdf.ViewState;
+import androidx.pdf.find.FindInFileView;
+import androidx.pdf.select.SelectionActionMode;
+import androidx.pdf.util.ObservableValue;
+import androidx.pdf.widget.ZoomView;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class ZoomScrollValueObserver implements ObservableValue.ValueObserver<ZoomView.ZoomScroll> {
+    private final PaginatedView mPaginatedView;
+    private final ZoomView mZoomView;
+    private final LayoutHandler mLayoutHandler;
+    private final FloatingActionButton mAnnotationButton;
+    private final Handler mAnnotationButtonHandler;
+    private final FindInFileView mFindInFileView;
+    private boolean mIsAnnotationIntentResolvable;
+    private final SelectionActionMode mSelectionActionMode;
+    private final ObservableValue<ViewState> mViewState;
+
+    private static final int FAB_ANIMATION_DURATION = 200;
+    private boolean mIsPageScrollingUp;
+
+    public ZoomScrollValueObserver(@Nullable ZoomView zoomView,
+            @Nullable PaginatedView paginatedView,
+            @NonNull LayoutHandler layoutHandler, @NonNull FloatingActionButton annotationButton,
+            @NonNull FindInFileView findInFileView, boolean isAnnotationIntentResolvable,
+            @NonNull SelectionActionMode selectionActionMode,
+            @NonNull ObservableValue<ViewState> viewState) {
+        mZoomView = zoomView;
+        mPaginatedView = paginatedView;
+        mLayoutHandler = layoutHandler;
+        mAnnotationButton = annotationButton;
+        mFindInFileView = findInFileView;
+        mIsAnnotationIntentResolvable = isAnnotationIntentResolvable;
+        mSelectionActionMode = selectionActionMode;
+        mViewState = viewState;
+        mAnnotationButtonHandler = new Handler(Looper.getMainLooper());
+        mIsPageScrollingUp = false;
+    }
+
+    @Override
+    public void onChange(@Nullable ZoomView.ZoomScroll oldPosition,
+            @Nullable ZoomView.ZoomScroll position) {
+        if (mPaginatedView == null || !mPaginatedView.getPaginationModel().isInitialized()
+                || position == null || mPaginatedView.getPaginationModel().getSize() == 0) {
+            return;
+        }
+        loadPageAssets(position);
+        if (mIsAnnotationIntentResolvable) {
+
+            if (!isAnnotationButtonVisible() && position.scrollY == 0
+                    && mFindInFileView.getVisibility() == View.GONE) {
+                editFabExpandAnimation();
+            } else if (isAnnotationButtonVisible() && mIsPageScrollingUp) {
+                clearAnnotationHandler();
+                return;
+            }
+            if (position.scrollY == oldPosition.scrollY) {
+                mAnnotationButtonHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (position.scrollY != 0) {
+                            mAnnotationButton.animate()
+                                    .alpha(0.0f)
+                                    .setDuration(FAB_ANIMATION_DURATION)
+                                    .withEndAction(new Runnable() {
+                                        @Override
+                                        public void run() {
+                                            mAnnotationButton.setVisibility(View.GONE);
+                                            mAnnotationButton.setAlpha(1.0f);
+                                        }
+                                    });
+                        }
+                    }
+                });
+            }
+        }
+
+        if (position.scrollY > 0) {
+            mSelectionActionMode.stopActionMode();
+        }
+        if (position.scrollY == oldPosition.scrollY) {
+            mSelectionActionMode.resume();
+        }
+    }
+
+    private boolean isAnnotationButtonVisible() {
+        return mAnnotationButton.getVisibility() == View.VISIBLE;
+    }
+
+    private void editFabExpandAnimation() {
+        mAnnotationButton.setScaleX(0.0f);
+        mAnnotationButton.setScaleY(0.0f);
+        ValueAnimator scaleAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+        scaleAnimator.setDuration(FAB_ANIMATION_DURATION);
+        scaleAnimator.addUpdateListener(
+                new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+                        float scale = (float) animation.getAnimatedValue();
+                        mAnnotationButton.setScaleX(scale);
+                        mAnnotationButton.setScaleY(scale);
+                        mAnnotationButton.setAlpha(scale);
+                    }
+                });
+        scaleAnimator.start();
+        mAnnotationButton.setVisibility(View.VISIBLE);
+    }
+
+    private void loadPageAssets(ZoomView.ZoomScroll position) {
+        if (!mPaginatedView.getPaginationModel().isInitialized()) {
+            return;
+        }
+        // Change the resolution of the bitmaps only when a gesture is not in progress.
+        if (position.stable || mZoomView.getStableZoom() == 0) {
+            mZoomView.setStableZoom(position.zoom);
+        }
+
+        mPaginatedView.getPaginationModel().setViewArea(mZoomView.getVisibleAreaInContentCoords());
+        mPaginatedView.refreshPageRangeInVisibleArea(position, mZoomView.getHeight());
+        mPaginatedView.handleGonePages(/* clearViews= */ false);
+        mPaginatedView.loadInvisibleNearPageRange(mZoomView.getStableZoom());
+
+        // The step (4) below requires page Views to be created and laid out. So we create them here
+        // and set this flag if that operation needs to wait for a layout pass.
+        boolean requiresLayoutPass = mPaginatedView.createPageViewsForVisiblePageRange();
+
+        // 4. Refresh tiles and/or full pages.
+        if (position.stable) {
+            // Perform a full refresh on all visible pages
+            mPaginatedView.refreshVisiblePages(requiresLayoutPass, mViewState.get(),
+                    mZoomView.getStableZoom());
+            mPaginatedView.handleGonePages(/* clearViews= */ true);
+        } else if (mZoomView.getStableZoom() == position.zoom) {
+            // Just load a few more tiles in case of tile-scroll
+            mPaginatedView.refreshVisibleTiles(requiresLayoutPass, mViewState.get());
+        }
+
+        if (mPaginatedView.getPageRangeHandler().getVisiblePages() != null) {
+            mLayoutHandler.maybeLayoutPages(
+                    mPaginatedView.getPageRangeHandler().getVisiblePages().getLast());
+        }
+    }
+
+
+    /** Exposing a function to clear the handler when PDFViewer Fragment is destroyed. */
+    public void clearAnnotationHandler() {
+        mAnnotationButtonHandler.removeCallbacksAndMessages(null);
+    }
+
+    public void setAnnotationIntentResolvable(boolean annotationIntentResolvable) {
+        mIsAnnotationIntentResolvable = annotationIntentResolvable;
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java
index fafea44..7da2a94 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractPdfTask.java
@@ -21,7 +21,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.pdf.models.PdfDocumentRemote;
-import androidx.pdf.pdflib.PdfDocumentRemoteProto;
+import androidx.pdf.service.PdfDocumentRemoteProto;
 import androidx.pdf.util.ThreadUtils;
 
 /**
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractWriteTask.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractWriteTask.java
index ce69c0cb..7c1e5d7 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractWriteTask.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/AbstractWriteTask.java
@@ -20,7 +20,7 @@
 import android.os.RemoteException;
 
 import androidx.annotation.RestrictTo;
-import androidx.pdf.pdflib.PdfDocumentRemoteProto;
+import androidx.pdf.service.PdfDocumentRemoteProto;
 
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
index 5758c6a..df72681 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfConnection.java
@@ -26,7 +26,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.pdf.models.PdfDocumentRemote;
-import androidx.pdf.pdflib.PdfDocumentService;
+import androidx.pdf.service.PdfDocumentService;
 import androidx.pdf.util.Preconditions;
 
 import java.util.concurrent.locks.Condition;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
index 2884cd6..77727c5 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.print.PrintManager;
 import android.text.TextUtils;
 import android.util.SparseArray;
 
@@ -32,11 +31,10 @@
 import androidx.pdf.models.Dimensions;
 import androidx.pdf.models.PdfDocumentRemote;
 import androidx.pdf.models.SelectionBoundary;
-import androidx.pdf.pdflib.PdfDocumentRemoteProto;
+import androidx.pdf.service.PdfDocumentRemoteProto;
 import androidx.pdf.util.BitmapRecycler;
 import androidx.pdf.util.TileBoard.TileInfo;
 
-import java.io.FileOutputStream;
 import java.lang.ref.WeakReference;
 
 /**
@@ -63,10 +61,11 @@
     private final DisplayData mData;
     private final boolean mHideTextAnnotations;
 
-    private final WeakPdfLoaderCallbacks mCallbacks;
+    private WeakPdfLoaderCallbacks mCallbacks;
 
     private final SparseArray<PdfPageLoader> mPageLoaders;
     private String mLoadedPassword;
+    private int mNumPages;
 
     /** Creates a new {@link PdfLoader} and loads the document from the given data. */
     @NonNull
@@ -126,6 +125,18 @@
         this.mPageLoaders = new SparseArray<>();
     }
 
+    public int getNumPages() {
+        return mNumPages;
+    }
+
+    public void setNumPages(int numPages) {
+        mNumPages = numPages;
+    }
+
+    public void setCallbacks(@NonNull WeakPdfLoaderCallbacks callbacks) {
+        mCallbacks = callbacks;
+    }
+
     /** Schedule task to load a PdfDocument. */
     public void reloadDocument() {
         mExecutor.schedule(new LoadDocumentTask(mLoadedPassword));
@@ -149,70 +160,6 @@
         mExecutor.schedule(new LoadDocumentTask(password));
     }
 
-    /**
-     * Creates a copy of the current document without security, if it is password protected. This
-     * maybe necessary for the {@link PrintManager} which can't handle password protected files.
-     *
-     * @param fileOutputStream points to where pdfClient should make a copy of the pdf without
-     *                         security.
-     */
-    public void cloneWithoutSecurity(@NonNull FileOutputStream fileOutputStream) {
-        mExecutor.schedule(new CloneDocumentWithoutSecurityTask(fileOutputStream));
-    }
-
-    class CloneDocumentWithoutSecurityTask extends AbstractWriteTask {
-        CloneDocumentWithoutSecurityTask(FileOutputStream fileOutputStream) {
-            super(PdfLoader.this, fileOutputStream, Priority.CLONE_PDF);
-        }
-
-        @Override
-        protected String getLogTag() {
-            return "CloneDocNoSecurityTask";
-        }
-
-        @Override
-        boolean execute(PdfDocumentRemoteProto pdfDocument, ParcelFileDescriptor pfd)
-                throws RemoteException {
-            return pdfDocument.getPdfDocumentRemote().cloneWithoutSecurity(pfd);
-        }
-
-        @Override
-        protected void doCallback(PdfLoaderCallbacks callbacks, Boolean result) {
-            callbacks.documentCloned(result.booleanValue());
-        }
-    }
-
-    /**
-     * Saves the current document to the given {@link FileOutputStream}.
-     *
-     * @param fileOutputStream where the currently open PDF should be written.
-     */
-    public void saveAs(@NonNull FileOutputStream fileOutputStream) {
-        mExecutor.schedule(new SaveAsTask(fileOutputStream));
-    }
-
-    class SaveAsTask extends AbstractWriteTask {
-        SaveAsTask(FileOutputStream fileOutputStream) {
-            super(PdfLoader.this, fileOutputStream, Priority.CLONE_PDF);
-        }
-
-        @Override
-        protected String getLogTag() {
-            return "SaveAsTask";
-        }
-
-        @Override
-        boolean execute(PdfDocumentRemoteProto pdfDocument, ParcelFileDescriptor pfd)
-                throws RemoteException {
-            return pdfDocument.getPdfDocumentRemote().saveAs(pfd);
-        }
-
-        @Override
-        protected void doCallback(PdfLoaderCallbacks callbacks, Boolean result) {
-            callbacks.documentSavedAs(result.booleanValue());
-        }
-    }
-
     /** Cancels all requests related to one page (bitmaps, texts,...). */
     public void cancel(int pageNum) {
         getPageLoader(pageNum).cancel();
@@ -331,7 +278,7 @@
     // PdfViewer, so it can be garbage collected if no longer in use, in which case the callbacks
     // all become no-ops.
     @NonNull
-    protected WeakPdfLoaderCallbacks getCallbacks() {
+    public WeakPdfLoaderCallbacks getCallbacks() {
         return mCallbacks;
     }
 
@@ -347,7 +294,6 @@
     /** AsyncTask for loading a PdfDocument. */
     class LoadDocumentTask extends AbstractPdfTask<PdfStatus> {
         private final String mPassword;
-        private int mNumPages;
 //        private boolean mIsLinearized;
 
         LoadDocumentTask() {
@@ -373,20 +319,26 @@
         @Override
         protected PdfStatus doInBackground(PdfDocumentRemoteProto pdfDocument)
                 throws RemoteException {
-            if (mData == null) {
-                return PdfStatus.FILE_ERROR;
-            }
+            PdfStatus result;
+            if (mConnection.isLoaded()) {
+                // Already loaded, skip the loading process (e.g., during screen rotation).
+                result = PdfStatus.LOADED;
+            } else {
+                if (mData == null) {
+                    return PdfStatus.FILE_ERROR;
+                }
 
-            // NOTE: This filedescriptor is not closed since it continues to be used by Pdfium.
-            // TODO: StrictMode- Look into filedescriptors more and document
-            // exactly when they should be opened and closed, making sure they are not leaked.
-            ParcelFileDescriptor fd = mData.openFd(mOpener);
+                // NOTE: This filedescriptor is not closed since it continues to be used by Pdfium.
+                // TODO: StrictMode- Look into filedescriptors more and document
+                // exactly when they should be opened and closed, making sure they are not leaked.
+                ParcelFileDescriptor fd = mData.openFd(mOpener);
 
-            if (fd == null || fd.getFd() == -1) {
-                return PdfStatus.FILE_ERROR;
+                if (fd == null || fd.getFd() == -1) {
+                    return PdfStatus.FILE_ERROR;
+                }
+                int statusIndex = pdfDocument.getPdfDocumentRemote().create(fd, mPassword);
+                result = PdfStatus.values()[statusIndex];
             }
-            int statusIndex = pdfDocument.getPdfDocumentRemote().create(fd, mPassword);
-            PdfStatus result = PdfStatus.values()[statusIndex];
 
             if (result == PdfStatus.LOADED) {
                 mNumPages = pdfDocument.getPdfDocumentRemote().numPages();
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java
index b295305..23cbe20 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacks.java
@@ -29,7 +29,6 @@
 import androidx.pdf.models.PageSelection;
 import androidx.pdf.util.TileBoard.TileInfo;
 
-import java.io.FileOutputStream;
 import java.util.List;
 
 /**
@@ -81,20 +80,6 @@
     void setPageGotoLinks(int pageNum, @NonNull List<GotoLink> links);
 
     /**
-     * This is called in response to a call to {@link PdfLoader#cloneWithoutSecurity}.
-     *
-     * @param result is true if the document was successfully cloned.
-     */
-    void documentCloned(boolean result);
-
-    /**
-     * This is called in response to a call to {@link PdfLoader#saveAs(FileOutputStream)}.
-     *
-     * @param result is true if the document was successfully saved.
-     */
-    void documentSavedAs(boolean result);
-
-    /**
      * Called in response to form editing operations in {@link PdfLoader} to inform implementations
      * that portions of the page bitmap that have been invalidated.
      */
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
new file mode 100644
index 0000000..078a405
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer.loader
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.view.View
+import androidx.annotation.RestrictTo
+import androidx.annotation.UiThread
+import androidx.fragment.app.FragmentManager
+import androidx.pdf.R
+import androidx.pdf.ViewState
+import androidx.pdf.data.PdfStatus
+import androidx.pdf.data.Range
+import androidx.pdf.models.Dimensions
+import androidx.pdf.models.GotoLink
+import androidx.pdf.models.LinkRects
+import androidx.pdf.models.MatchRects
+import androidx.pdf.models.PageSelection
+import androidx.pdf.util.Observables.ExposedValue
+import androidx.pdf.util.PaginationUtils
+import androidx.pdf.util.Preconditions
+import androidx.pdf.util.ThreadUtils
+import androidx.pdf.util.TileBoard
+import androidx.pdf.util.Toaster
+import androidx.pdf.viewer.LayoutHandler
+import androidx.pdf.viewer.LoadingView
+import androidx.pdf.viewer.PageMosaicView
+import androidx.pdf.viewer.PageViewFactory
+import androidx.pdf.viewer.PaginatedView
+import androidx.pdf.viewer.PdfPasswordDialog
+import androidx.pdf.viewer.PdfSelectionModel
+import androidx.pdf.viewer.SearchModel
+import androidx.pdf.viewer.SelectedMatch
+import androidx.pdf.widget.FastScrollView
+import androidx.pdf.widget.ZoomView
+import androidx.pdf.widget.ZoomView.ZoomScroll
+import com.google.android.material.snackbar.Snackbar
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class PdfLoaderCallbacksImpl(
+    private val context: Context,
+    private val fragmentManager: FragmentManager,
+    private var fastScrollView: FastScrollView,
+    private var zoomView: ZoomView,
+    private var paginatedView: PaginatedView,
+    private var loadingView: LoadingView,
+    private var viewState: ExposedValue<ViewState>,
+    private val fragmentContainerView: View?,
+    private val onRequestPassword: (Boolean) -> Unit,
+    private val onDocumentLoaded: () -> Unit,
+    private val onDocumentLoadFailure: (Throwable) -> Unit
+) : PdfLoaderCallbacks {
+    private val pageElevationInPixels: Int = PaginationUtils.getPageElevationInPixels(context)
+
+    var selectionModel: PdfSelectionModel? = null
+    var searchModel: SearchModel? = null
+    var layoutHandler: LayoutHandler? = null
+    var fileName: String? = null
+    var pageViewFactory: PageViewFactory? = null
+    var pdfLoader: PdfLoader? = null
+    var onScreen = false
+
+    private fun currentPasswordDialog(fm: FragmentManager): PdfPasswordDialog? {
+        val passwordDialog = fm.findFragmentByTag(PASSWORD_DIALOG_TAG)
+        return passwordDialog as PdfPasswordDialog?
+    }
+
+    private fun dismissPasswordDialog() {
+        currentPasswordDialog(fragmentManager)?.dismiss()
+    }
+
+    private fun handleError(status: PdfStatus) {
+        viewState.set(ViewState.ERROR)
+
+        val thrown =
+            when (status) {
+                PdfStatus.FILE_ERROR ->
+                    RuntimeException(context.resources.getString(R.string.file_error))
+                PdfStatus.PAGE_BROKEN ->
+                    RuntimeException(context.resources.getString(R.string.page_broken))
+                PdfStatus.NEED_MORE_DATA ->
+                    RuntimeException(context.resources.getString(R.string.needs_more_data))
+                else -> RuntimeException(context.resources.getString(R.string.pdf_error))
+            }
+
+        onDocumentLoadFailure(thrown)
+    }
+
+    @UiThread
+    fun hideSpinner() {
+        loadingView.visibility = View.GONE
+    }
+
+    private fun lookAtSelection(selection: SelectedMatch?) {
+        if (selection == null || selection.isEmpty) {
+            return
+        }
+
+        if (selection.page >= paginatedView.paginationModel.size) {
+            layoutHandler!!.layoutPages(selection.page + 1)
+            return
+        }
+
+        val rect = selection.pageMatches.getFirstRect(selection.selected)
+        val x: Int = paginatedView.paginationModel.getLookAtX(selection.page, rect.centerX())
+        val y: Int = paginatedView.paginationModel.getLookAtY(selection.page, rect.centerY())
+        zoomView.centerAt(x.toFloat(), y.toFloat())
+
+        (pageViewFactory!!.getOrCreatePageView(
+                selection.page,
+                pageElevationInPixels,
+                paginatedView.paginationModel.getPageSize(selection.page)
+            ) as PageMosaicView)
+            .setOverlay(selection.overlay)
+    }
+
+    fun loadPageAssets(position: ZoomScroll) {
+        // Change the resolution of the bitmaps only when a gesture is not in progress.
+        if (position.stable || zoomView.stableZoom == 0f) {
+            zoomView.stableZoom = position.zoom
+        }
+
+        zoomView.let {
+            paginatedView.paginationModel.setViewArea(it.visibleAreaInContentCoords)
+            paginatedView.refreshPageRangeInVisibleArea(position, it.height)
+            paginatedView.handleGonePages(/* clearViews= */ false)
+            paginatedView.loadInvisibleNearPageRange(it.stableZoom)
+        }
+
+        // The step (4) below requires page Views to be created and laid out. So we create them here
+        // and set this flag if that operation needs to wait for a layout pass.
+        val requiresLayoutPass: Boolean = paginatedView.createPageViewsForVisiblePageRange()
+
+        // 4. Refresh tiles and/or full pages.
+        if (position.stable) {
+            // Perform a full refresh on all visible pages
+            viewState.get()?.let {
+                zoomView.let { it1 ->
+                    paginatedView.refreshVisiblePages(requiresLayoutPass, it, it1.stableZoom)
+                }
+            }
+            paginatedView.handleGonePages(/* clearViews= */ true)
+        } else if (zoomView.stableZoom == position.zoom) {
+            // Just load a few more tiles in case of tile-scroll
+            viewState.get()?.let { paginatedView.refreshVisibleTiles(requiresLayoutPass, it) }
+        }
+
+        paginatedView.pageRangeHandler.visiblePages?.let {
+            layoutHandler!!.maybeLayoutPages(it.last)
+        }
+    }
+
+    private fun isPageCreated(pageNum: Int): Boolean {
+        return pageNum < paginatedView.paginationModel.size &&
+            paginatedView.getViewAt(pageNum) != null
+    }
+
+    private fun getPage(pageNum: Int): PageViewFactory.PageView? {
+        return paginatedView.getViewAt(pageNum)
+    }
+
+    override fun requestPassword(incorrect: Boolean) {
+        onRequestPassword(onScreen)
+
+        if (viewState.get() != ViewState.NO_VIEW) {
+            var passwordDialog = currentPasswordDialog(fragmentManager)
+            if (passwordDialog == null) {
+                passwordDialog = PdfPasswordDialog()
+                passwordDialog.setListener(
+                    object : PdfPasswordDialog.PasswordDialogEventsListener {
+                        override fun onPasswordTextChange(password: String) {
+                            pdfLoader?.applyPassword(password)
+                        }
+
+                        override fun onDialogCancelled() {
+                            val retryCallback = Runnable { requestPassword(false) }
+                            val snackbar =
+                                fragmentContainerView?.let {
+                                    Snackbar.make(
+                                        it,
+                                        R.string.password_not_entered,
+                                        Snackbar.LENGTH_INDEFINITE
+                                    )
+                                }
+                            val mResolveClickListener =
+                                View.OnClickListener { _: View? -> retryCallback.run() }
+                            snackbar?.setAction(R.string.retry_button_text, mResolveClickListener)
+                            snackbar?.show()
+                        }
+                    }
+                )
+
+                passwordDialog.show(fragmentManager, PASSWORD_DIALOG_TAG)
+            }
+
+            if (incorrect) {
+                passwordDialog.retry()
+            }
+        }
+    }
+
+    override fun documentLoaded(numPages: Int) {
+        if (numPages <= 0) {
+            documentNotLoaded(PdfStatus.PDF_ERROR)
+            return
+        }
+
+        onDocumentLoaded()
+        hideSpinner()
+
+        // Assume we see at least the first page
+        paginatedView.pageRangeHandler.maxPage = 1
+        if (viewState.get() != ViewState.NO_VIEW) {
+
+            paginatedView.paginationModel.initialize(numPages)
+
+            // Add pagination model to the view
+            paginatedView.model = paginatedView.paginationModel
+            paginatedView.let { paginatedView.paginationModel.addObserver(it) }
+
+            fastScrollView.setPaginationModel(paginatedView.paginationModel)
+
+            dismissPasswordDialog()
+
+            layoutHandler!!.maybeLayoutPages(1)
+            searchModel?.setNumPages(numPages)
+        }
+    }
+
+    override fun documentNotLoaded(status: PdfStatus) {
+        if (viewState.get() != ViewState.NO_VIEW) {
+            dismissPasswordDialog()
+            when (status) {
+                PdfStatus.NONE,
+                PdfStatus.LOADED,
+                PdfStatus.REQUIRES_PASSWORD ->
+                    Preconditions.checkArgument(
+                        false,
+                        "Document not loaded but status " + status.number
+                    )
+                PdfStatus.PDF_ERROR -> {
+                    Toaster.LONG.popToast(context, R.string.error_file_format_pdf, fileName)
+                    handleError(status)
+                }
+                PdfStatus.FILE_ERROR,
+                PdfStatus.PAGE_BROKEN,
+                PdfStatus.NEED_MORE_DATA -> {
+                    handleError(status)
+                }
+            }
+        }
+    }
+
+    override fun pageBroken(page: Int) {
+        if (viewState.get() != ViewState.NO_VIEW) {
+            (pageViewFactory!!.getOrCreatePageView(
+                    page,
+                    pageElevationInPixels,
+                    paginatedView.paginationModel.getPageSize(page)
+                ) as PageMosaicView)
+                .setFailure(context.resources.getString(R.string.error_on_page, page + 1))
+            // TODO: Track render error.
+        }
+    }
+
+    override fun setPageDimensions(pageNum: Int, dimensions: Dimensions) {
+        if (viewState.get() != ViewState.NO_VIEW) {
+
+            paginatedView.paginationModel.addPage(pageNum, dimensions)
+
+            layoutHandler!!.pageLayoutReach = paginatedView.paginationModel.size
+
+            if (
+                searchModel!!.query().get() != null &&
+                    searchModel!!.selectedMatch().get() != null &&
+                    searchModel!!.selectedMatch().get()!!.page == pageNum &&
+                    pageViewFactory != null
+            ) {
+                // lookAtSelection is posted to run once layout has finished:
+                ThreadUtils.postOnUiThread {
+                    if (viewState.get() != ViewState.NO_VIEW) {
+                        lookAtSelection(searchModel!!.selectedMatch().get())
+                    }
+                }
+            }
+
+            viewState.get()?.let { layoutHandler!!.processCallbacksInQueue(it, pageNum) }
+
+            // The new page might actually be visible on the screen, so we need
+            // to fetch assets:
+            val position: ZoomScroll = zoomView.zoomScroll().get()!!
+            val newRange: Range =
+                paginatedView.pageRangeHandler.computeVisibleRange(
+                    position.scrollY,
+                    position.zoom,
+                    zoomView.height,
+                    true
+                )
+            if (newRange.isEmpty) {
+                layoutHandler!!.maybeLayoutPages(newRange.last)
+            } else if (newRange.contains(pageNum)) {
+                // The new page is visible, fetch its assets.
+                loadPageAssets(zoomView.zoomScroll().get()!!)
+            }
+        }
+    }
+
+    override fun setPageBitmap(pageNum: Int, bitmap: Bitmap) {
+        // We announce that the viewer is ready as soon as a bitmap is loaded
+        // (not before).
+        if (viewState.get() == ViewState.VIEW_CREATED) {
+            zoomView.visibility = View.VISIBLE
+            viewState.set(ViewState.VIEW_READY)
+        }
+        if (viewState.get() != ViewState.NO_VIEW && isPageCreated(pageNum)) {
+            getPage(pageNum)?.getPageView()?.setPageBitmap(bitmap)
+        }
+    }
+
+    override fun setTileBitmap(pageNum: Int, tileInfo: TileBoard.TileInfo, bitmap: Bitmap) {
+        if (viewState.get() != ViewState.NO_VIEW && isPageCreated(pageNum)) {
+            getPage(pageNum)?.getPageView()?.setTileBitmap(tileInfo, bitmap)
+        }
+    }
+
+    override fun setPageText(pageNum: Int, text: String) {
+        if (viewState.get() != ViewState.NO_VIEW && isPageCreated(pageNum)) {
+            getPage(pageNum)?.getPageView()?.setPageText(text)
+        }
+    }
+
+    override fun setSearchResults(query: String, pageNum: Int, matches: MatchRects) {
+        if (viewState.get() != ViewState.NO_VIEW && query == searchModel!!.query().get()) {
+
+            searchModel!!.updateMatches(query, pageNum, matches)
+            if (isPageCreated(pageNum)) {
+                getPage(pageNum)
+                    ?.getPageView()
+                    ?.setOverlay(searchModel!!.getOverlay(query, pageNum, matches))
+            }
+        }
+    }
+
+    override fun setSelection(pageNum: Int, selection: PageSelection) {
+        if (viewState.get() == ViewState.NO_VIEW) {
+            return
+        }
+        searchModel!!.setQuery(null, -1)
+        selectionModel?.setSelection(selection)
+    }
+
+    override fun setPageUrlLinks(pageNum: Int, links: LinkRects) {
+        if (viewState.get() != ViewState.NO_VIEW && isPageCreated(pageNum)) {
+            getPage(pageNum)?.setPageUrlLinks(links)
+        }
+    }
+
+    override fun setPageGotoLinks(pageNum: Int, links: MutableList<GotoLink>) {
+        if (viewState.get() != ViewState.NO_VIEW && isPageCreated(pageNum)) {
+            getPage(pageNum)?.setPageGotoLinks(links)
+        }
+    }
+
+    override fun setInvalidRects(pageNum: Int, invalidRects: MutableList<Rect>) {
+        if (viewState.get() != ViewState.NO_VIEW && isPageCreated(pageNum)) {
+            if (invalidRects.isEmpty()) {
+                return
+            }
+            paginatedView.getViewAt(pageNum)?.getPageView()?.requestRedrawAreas(invalidRects)
+        }
+    }
+
+    companion object {
+        private const val PASSWORD_DIALOG_TAG = "password-dialog"
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
index 61cddc6..d446bf0 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
@@ -30,9 +30,7 @@
 import androidx.pdf.models.MatchRects;
 import androidx.pdf.models.PageSelection;
 import androidx.pdf.models.SelectionBoundary;
-import androidx.pdf.pdflib.PdfDocumentRemoteProto;
-import androidx.pdf.util.BitmapParcel;
-import androidx.pdf.util.StrictModeUtils;
+import androidx.pdf.service.PdfDocumentRemoteProto;
 import androidx.pdf.util.TileBoard.TileInfo;
 
 import com.google.common.collect.ImmutableList;
@@ -58,14 +56,6 @@
     /** Arbitrary dimensions used for pages that are broken. */
     private static final Dimensions DEFAULT_PAGE = new Dimensions(400, 400);
 
-    static {
-        // TODO: StrictMode- disk read 14ms.
-        // NOTE: this line can break when running with --noforge, such as when debugging with
-        // Android Studio. You may need to comment it out locally if you see errors like
-        // `java.lang.UnsatisfiedLinkError: no bitmap_parcel in java.library.path`.
-        StrictModeUtils.bypass(() -> BitmapParcel.loadNdkLib());
-    }
-
     private final PdfLoader mParent;
     private final int mPageNum;
     private final boolean mHideTextAnnotations;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java
index 1553092..e1e80a8 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/WeakPdfLoaderCallbacks.java
@@ -156,22 +156,6 @@
     }
 
     @Override
-    public void documentCloned(boolean result) {
-        PdfLoaderCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.documentCloned(result);
-        }
-    }
-
-    @Override
-    public void documentSavedAs(boolean result) {
-        PdfLoaderCallbacks callbacks = getCallbacks();
-        if (callbacks != null) {
-            callbacks.documentSavedAs(result);
-        }
-    }
-
-    @Override
     public void setInvalidRects(int pageNum, @NonNull List<Rect> invalidRects) {
         PdfLoaderCallbacks callbacks = getCallbacks();
         if (callbacks != null) {
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
index 5867dc0..3a6e022 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
@@ -256,7 +256,7 @@
     private void swapBackground(EditText textField, boolean reverse) {
         if (!reverse) {
             textField.setBackground(
-                    getResources().getDrawable(R.drawable.textfield_default_mtrl_alpha));
+                    getResources().getDrawable(R.drawable.drag_indicator));
         } else {
             EditText sample = new EditText(getActivity());
             textField.setBackground(sample.getBackground());
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/FastScrollContentModel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/FastScrollContentModel.java
deleted file mode 100644
index ce6521b..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/FastScrollContentModel.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.widget;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Model for a {@link FastScrollView} to update the scrollbar when the content view is scrolled
- * and update the content scroll position when the scroll thumb is dragged.
- * <p>
- * All units used are arbitrary but must be consistent. For example they could be pixels or page
- * numbers.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface FastScrollContentModel {
-
-    /**
-     * Estimate the full content height.
-     */
-    float estimateFullContentHeight();
-
-    /**
-     * The currently visible height in the {@link FastScrollView} in the same units as
-     * {@link #estimateFullContentHeight}.
-     */
-    float visibleHeight();
-
-    /**
-     * Scroll the content view to the position indicated.
-     *
-     * @param position the latest/current scroll position
-     * @param stable   whether the scroll gesture is finished (stable is false while the gesture
-     *                 is in
-     *                 progress, and is true when the gesture finishes).
-     */
-    void fastScrollTo(float position, boolean stable);
-
-    /**
-     * Allow the {@link FastScrollView} to update the scroll thumb when the content scrolls.
-     */
-    void setFastScrollListener(@NonNull FastScrollListener listener);
-
-    /**
-     * Listener for content scroll events in units consistent with the other methods in this class.
-     */
-    interface FastScrollListener {
-
-        /** Update scrollbar based on the given position. */
-        void updateFastScrollbar(float position);
-    }
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/FastScrollView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/FastScrollView.java
index cf1de9a..5809a6f 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/FastScrollView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/FastScrollView.java
@@ -19,30 +19,34 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.WindowInsets;
 import android.widget.FrameLayout;
+import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.Insets;
+import androidx.core.view.WindowInsetsCompat;
 import androidx.pdf.R;
+import androidx.pdf.data.Range;
 import androidx.pdf.util.MathUtils;
-import androidx.pdf.util.ObservableValue;
 import androidx.pdf.util.ObservableValue.ValueObserver;
-import androidx.pdf.util.Observables;
-import androidx.pdf.widget.FastScrollContentModel.FastScrollListener;
+import androidx.pdf.viewer.PaginationModel;
+import androidx.pdf.viewer.PaginationModelObserver;
 
 /**
- * A {@link FrameLayout} that draws a draggable scrollbar over its child views. It uses a
- * {@link FastScrollContentModel} as its model to listen for scroll events on the content view to
- * control the scrollbar and conversely to scroll the content view when the scrollbar thumb is
- * dragged.
+ * A {@link FrameLayout} that draws a draggable scrollbar over its child views. It is tightly
+ * integrated with {@link ZoomView} as its scrolling content view.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-public class FastScrollView extends FrameLayout implements FastScrollListener {
+public class FastScrollView extends FrameLayout implements PaginationModelObserver {
 
     private enum State {
         NONE,
@@ -56,13 +60,9 @@
     private final View mDragHandle;
     private final float mOriginalTranslateX;
 
-    private FastScrollContentModel mScrollable;
-    private Observables.ExposedValue<Integer> mThumbY = Observables.newExposedValueWithInitialValue(
-            0);
+    private int mThumbY = 0;
     private float mCurrentPosition;
     private State mState = State.NONE;
-    private boolean mShowScrollThumb = false;
-    private final int mScrollbarMarginDefault;
 
     // The track's top and bottom margin include space for the scroll-thumb, but
     // this isn't included in the scrollBar margin as specified by callers.
@@ -73,23 +73,38 @@
     /** Has the thumb been dragged during the display of the scrollbar */
     private boolean mDragged;
 
-    private final ValueObserver<Integer> mYObserver =
-            new ValueObserver<Integer>() {
+    private ZoomView mZoomView;
+    private Rect mZoomViewBasePadding;
+
+    private final PageIndicator mPageIndicator;
+    private PaginationModel mPaginationModel;
+
+    private final ValueObserver<ZoomView.ZoomScroll> mZoomScrollObserver =
+            new ValueObserver<ZoomView.ZoomScroll>() {
                 @Override
-                public void onChange(@Nullable Integer oldValue, @Nullable Integer newValue) {
-                    View view = mDragHandle;
-                    if (view != null && newValue != null) {
-                        int transY = newValue - (view.getMeasuredHeight() / 2);
-                        view.setTranslationY(transY);
+                public void onChange(@Nullable ZoomView.ZoomScroll oldValue,
+                        @Nullable ZoomView.ZoomScroll newValue) {
+                    if (mPaginationModel == null || !mPaginationModel.isInitialized()
+                            || newValue == null || mPaginationModel.getSize() == 0) {
+                        return;
                     }
+                    if (mPageIndicator.setRangeAndZoom(
+                            computeImportantRange(newValue), newValue.zoom, newValue.stable)) {
+                        setVisible();
+                    }
+                    updateFastScrollbar(newValue.scrollY / newValue.zoom);
                 }
             };
 
-    public FastScrollView(@NonNull Context context, @NonNull AttributeSet attrs) {
+    public FastScrollView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public FastScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public FastScrollView(@NonNull Context context, @NonNull AttributeSet attrs, int defStyle) {
+    public FastScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         setWillNotDraw(false);
@@ -99,16 +114,18 @@
         mOriginalTranslateX = mDragHandle.getTranslationX();
 
         Resources res = getContext().getResources();
-        mScrollbarMarginDefault = res.getDimensionPixelOffset(
+        int scrollbarMarginDefault = res.getDimensionPixelOffset(
                 R.dimen.viewer_fastscroll_edge_offset);
 
         TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.FastScrollView, 0,
                 0);
         setScrollbarMarginTop(ta.getDimensionPixelOffset(
-                R.styleable.FastScrollView_scrollbarMarginTop, mScrollbarMarginDefault));
+                R.styleable.FastScrollView_scrollbarMarginTop, scrollbarMarginDefault));
         setScrollbarMarginBottom(ta.getDimensionPixelOffset(
-                R.styleable.FastScrollView_scrollbarMarginBottom, mScrollbarMarginDefault));
+                R.styleable.FastScrollView_scrollbarMarginBottom, scrollbarMarginDefault));
         ta.recycle();
+
+        mPageIndicator = new PageIndicator(getContext(), this);
     }
 
     @Override
@@ -118,19 +135,74 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        Integer y = mThumbY.get();
-        if (y != null) {
-            mYObserver.onChange(null, y);
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        if (child instanceof ZoomView && mZoomView != child) {
+            mZoomView = (ZoomView) child;
+            configureZoomView();
         }
-        mThumbY.addObserver(mYObserver);
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        // Prevent leaks if ZoomView is removed from this ViewGroup.
+        if (child instanceof ZoomView && child == mZoomView) {
+            mZoomView.zoomScroll().removeObserver(mZoomScrollObserver);
+            mZoomView = null;
+        }
+    }
+
+    private void configureZoomView() {
+        mZoomView
+                .setFitMode(ZoomView.FitMode.FIT_TO_WIDTH)
+                .setInitialZoomMode(ZoomView.InitialZoomMode.ZOOM_TO_FIT)
+                .setRotateMode(ZoomView.RotateMode.KEEP_SAME_VIEWPORT_WIDTH)
+                .setContentResizedModeX(ZoomView.ContentResizedMode.KEEP_SAME_RELATIVE);
+        mZoomView.setStraightenVerticalScroll(true);
+        mZoomView.zoomScroll().addObserver(mZoomScrollObserver);
+        mZoomViewBasePadding =
+                new Rect(
+                        mZoomView.getPaddingLeft(),
+                        mZoomView.getPaddingTop()
+                                + getResources().getDimensionPixelSize(
+                                R.dimen.viewer_doc_additional_top_offset),
+                        mZoomView.getPaddingRight(),
+                        mZoomView.getPaddingBottom());
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        Insets insetsCompat =
+                WindowInsetsCompat.toWindowInsetsCompat(insets)
+                        .getInsetsIgnoringVisibility(
+                                WindowInsetsCompat.Type.systemBars()
+                                        | WindowInsetsCompat.Type.displayCutout());
+        if (mZoomView != null) {
+            mZoomView.setPadding(
+                    0,
+                    mZoomViewBasePadding.top + insetsCompat.top,
+                    0,
+                    mZoomViewBasePadding.bottom + insetsCompat.bottom);
+            setScrollbarMarginTop(mZoomView.getPaddingTop());
+            // Ignore ZoomView's intrinsic padding on the right side as we want it to be
+            // right-anchored
+            setScrollbarMarginRight(insetsCompat.right);
+            setScrollbarMarginBottom(mZoomView.getPaddingBottom());
+        }
+        mPageIndicator.getView().setTranslationX(-insetsCompat.right);
+        return super.onApplyWindowInsets(insets);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mThumbY.removeObserver(mYObserver);
+        if (mZoomView != null) {
+            mZoomView.zoomScroll().removeObserver(mZoomScrollObserver);
+        }
+        if (mPaginationModel != null) {
+            mPaginationModel.removeObserver(this);
+        }
     }
 
     public void setScrollbarMarginTop(int scrollbarMarginTop) {
@@ -146,6 +218,18 @@
         this.mTrackBottomMargin = scrollbarMarginBottom + mDragHandle.getMeasuredHeight() / 2;
     }
 
+    @VisibleForTesting
+    @NonNull
+    public View getDragHandle() {
+        return mDragHandle;
+    }
+
+    @VisibleForTesting
+    @NonNull
+    public TextView getPageIndicator() {
+        return mPageIndicator.getTextView();
+    }
+
     private void updateDragHandleX() {
         // This has to be calculated because the amount of reserve space on the right can change
         // mattering on display configuration. The FastScrollView also holds other views, as it is a
@@ -155,18 +239,6 @@
                         - (mDragHandle.getMeasuredWidth() + mTrackRightMargin));
     }
 
-    /** Set listener on scrollable. */
-    public void setScrollable(@NonNull FastScrollContentModel scrollable) {
-        this.mScrollable = scrollable;
-        scrollable.setFastScrollListener(this);
-    }
-
-    /** Return the Y coordinate of center of the Scroller thumb, in pixels. */
-    @NonNull
-    public ObservableValue<Integer> getScrollerPositionY() {
-        return mThumbY;
-    }
-
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (mState != State.NONE && ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -246,7 +318,6 @@
                 scrollTo((int) me.getY(), true);
 
                 this.requestDisallowInterceptTouchEvent(false);
-
                 return true;
             }
         } else if (action == MotionEvent.ACTION_MOVE) {
@@ -259,17 +330,23 @@
     }
 
     private boolean scrollTo(int newThumbY, boolean stable) {
+        requireZoomViewAndPaginationModel();
         int top = mTrackTopMargin;
         int bottom = getHeight() - mTrackBottomMargin;
         newThumbY = MathUtils.clamp(newThumbY, top, bottom);
-        if (!stable && Math.abs(mThumbY.get() - newThumbY) < 2) {
+        if (!stable && Math.abs(mThumbY - newThumbY) < 2) {
             return false;
         }
-        mThumbY.set(newThumbY);
+        mThumbY = newThumbY;
+        updateDragHandleAndIndicator(newThumbY);
         int scrollbarLength = bottom - top;
-        float fraction = (mThumbY.get() - mTrackTopMargin) / (float) scrollbarLength;
-        float scrollRange = mScrollable.estimateFullContentHeight() - mScrollable.visibleHeight();
-        mScrollable.fastScrollTo(scrollRange * fraction, stable);
+        float fraction = (mThumbY - mTrackTopMargin) / (float) scrollbarLength;
+        float scrollRange =
+                mPaginationModel.getEstimatedFullHeight()
+                        - mZoomView.getViewportHeight() / mZoomView.getZoom();
+        mZoomView.scrollTo(
+                mZoomView.getScrollX(), (int) (scrollRange * fraction * mZoomView.getZoom()),
+                stable);
         return true;
     }
 
@@ -277,22 +354,20 @@
         return x > mDragHandle.getX()
                 // Deliberately ignore (x < getWidth() - scrollbarMarginRight) to make it easier
                 // to grab it.
-                && y >= mThumbY.get() - mDragHandle.getMeasuredHeight() / 2
-                && y <= mThumbY.get() + mDragHandle.getMeasuredHeight() / 2;
+                && y >= mThumbY - (float) mDragHandle.getMeasuredHeight() / 2
+                && y <= mThumbY + (float) mDragHandle.getMeasuredHeight() / 2;
     }
 
-
-    @Override
-    public void updateFastScrollbar(float position) {
+    private void updateFastScrollbar(float position) {
         if (position == mCurrentPosition) {
             return;
         }
+        requireZoomViewAndPaginationModel();
         mCurrentPosition = position;
 
-        mShowScrollThumb =
-                mScrollable.estimateFullContentHeight()
-                        > mScrollable.visibleHeight() * MIN_SCREENS_TO_SHOW;
-        if (!mShowScrollThumb) {
+        boolean showScrollThumb = mPaginationModel.getEstimatedFullHeight()
+                > mZoomView.getViewportHeight() / mZoomView.getZoom() * MIN_SCREENS_TO_SHOW;
+        if (!showScrollThumb) {
             if (mState != State.NONE) {
                 setState(State.NONE);
             }
@@ -303,13 +378,68 @@
             int scrollbarBottom = getHeight() - mTrackBottomMargin;
             int scrollbarLength = scrollbarBottom - mTrackTopMargin;
             float scrollRange =
-                    mScrollable.estimateFullContentHeight() - mScrollable.visibleHeight();
+                    mPaginationModel.getEstimatedFullHeight()
+                            - mZoomView.getViewportHeight() / mZoomView.getZoom();
             int tempThumbY = mTrackTopMargin + (int) (scrollbarLength * position / scrollRange);
-            mThumbY.set(
-                    MathUtils.clamp(tempThumbY, mTrackTopMargin, getHeight() - mTrackBottomMargin));
+            mThumbY = MathUtils.clamp(tempThumbY, mTrackTopMargin,
+                    getHeight() - mTrackBottomMargin);
+            updateDragHandleAndIndicator(mThumbY);
             if (mState != State.VISIBLE) {
                 setState(State.VISIBLE);
             }
         }
     }
+
+    private void updateDragHandleAndIndicator(int newPosition) {
+        if (mDragHandle == null) {
+            return;
+        }
+        View view = mDragHandle;
+        int transY = newPosition - (view.getMeasuredHeight() / 2);
+        view.setTranslationY(transY);
+        View indicatorView = mPageIndicator.getView();
+        indicatorView.setY(newPosition - ((float) indicatorView.getHeight() / 2));
+        mPageIndicator.show();
+        setVisible();
+    }
+
+    /**
+     * Sets the {@link PaginationModel} to inform this view about relationships between the viewport
+     * and document.
+     */
+    public void setPaginationModel(@NonNull PaginationModel paginationModel) {
+        mPaginationModel = paginationModel;
+        mPageIndicator.setNumPages(mPaginationModel.getNumPages());
+        mPaginationModel.addObserver(this);
+    }
+
+    /**
+     * Computes the range of pages that are entirely visible, or if no page is entirely visible,
+     * returns the most visible page.
+     */
+    private Range computeImportantRange(ZoomView.ZoomScroll position) {
+        requireZoomViewAndPaginationModel();
+        int top = Math.round(position.scrollY / position.zoom);
+        int bottom = Math.round((position.scrollY + mZoomView.getHeight()) / position.zoom);
+        Range window = new Range(top, bottom);
+        return mPaginationModel.getPagesInWindow(window, false);
+    }
+
+    @Override
+    public void onPageAdded() {
+        // Update PageIndicator as page dimensions become known
+        requireZoomViewAndPaginationModel();
+        ZoomView.ZoomScroll position = mZoomView.zoomScroll().get();
+        mPageIndicator.setRangeAndZoom(computeImportantRange(position), position.zoom,
+                position.stable);
+    }
+
+    private void requireZoomViewAndPaginationModel() {
+        if (mZoomView == null) {
+            throw new IllegalStateException("ZoomView must be a direct child of FastScrollView");
+        }
+        if (mPaginationModel == null || !mPaginationModel.isInitialized()) {
+            throw new IllegalStateException("PaginationModel not initialized!");
+        }
+    }
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java
index 7696a56..6825225 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/MosaicView.java
@@ -161,9 +161,6 @@
      */
     private final Rect mScaledViewArea = new Rect();
 
-    /** Whether to request a page bitmap even if tiling. */
-    protected boolean mAlwaysRequestPageBitmap = true;
-
     public MosaicView(@NonNull Context context) {
         super(context);
     }
@@ -360,13 +357,10 @@
     public void requestDrawAtZoom(float zoom) {
         mRequestedWidth = (int) (zoom * mBounds.width());
         boolean needTiling = needTiling(mRequestedWidth);
-
-        if (shouldDrawBitmap(needTiling)) {
-            int cappedWidth = getCappedWidth(needTiling);
-            if (mBitmap == null || mBitmap.getWidth() != cappedWidth) {
-                Dimensions pageSize = getPageDimensionsAtWidth(cappedWidth);
-                mBitmapSource.requestPageBitmap(pageSize, /* alsoRequestingTiles= */ needTiling);
-            }
+        int cappedWidth = getCappedWidth(needTiling);
+        if (mBitmap == null || mBitmap.getWidth() != cappedWidth) {
+            Dimensions pageSize = getPageDimensionsAtWidth(cappedWidth);
+            mBitmapSource.requestPageBitmap(pageSize, /* alsoRequestingTiles= */ needTiling);
         }
 
         if (needTiling) {
@@ -404,7 +398,7 @@
 
         boolean needTiling = needTiling(mRequestedWidth);
 
-        if (shouldDrawBitmap(needTiling) && mBitmap != null) {
+        if (mBitmap != null) {
             int cappedWidth = getCappedWidth(needTiling);
             if (cappedWidth > 0) {
                 Dimensions pageSize = getPageDimensionsAtWidth(cappedWidth);
@@ -437,14 +431,6 @@
         return cappedWidth;
     }
 
-    /**
-     * True if debug flag is not set and either {@link #mAlwaysRequestPageBitmap} is true or
-     * we are not tiling.
-     */
-    private boolean shouldDrawBitmap(boolean needTiling) {
-        return (mAlwaysRequestPageBitmap || !needTiling);
-    }
-
     /** Determines the current zoom of this view and scales {@code unscaled} accordingly. */
     private List<Rect> scaleRects(List<Rect> unscaled) {
         float zoom = ((float) mRequestedWidth) / ((float) mBounds.width());
@@ -640,19 +626,17 @@
     @Override
     protected void onDraw(@NonNull Canvas canvas) {
         // No tiling: draw the page bitmap or a white page.
-        if (mTileBoard == null) {
-            if (mBitmap != null) {
-                canvas.save();
-                float scale = (float) getWidth() / mBitmap.getWidth();
-                canvas.scale(scale, scale);
-                canvas.drawBitmap(mBitmap, IDENTITY, DITHER_BITMAP_PAINT);
-                canvas.restore();
-            } else if (mFailure != null) {
-                canvas.drawText(mFailure, getWidth() / 2, getHeight() / 2 - 10,
-                        MosaicView.MESSAGE_PAINT);
-            } else {
-                canvas.drawRect(mBounds, WHITE_PAINT);
-            }
+        if (mBitmap != null) {
+            canvas.save();
+            float scale = (float) getWidth() / mBitmap.getWidth();
+            canvas.scale(scale, scale);
+            canvas.drawBitmap(mBitmap, IDENTITY, DITHER_BITMAP_PAINT);
+            canvas.restore();
+        } else if (mFailure != null) {
+            canvas.drawText(mFailure, getWidth() / 2, getHeight() / 2 - 10,
+                    MosaicView.MESSAGE_PAINT);
+        } else {
+            canvas.drawRect(mBounds, WHITE_PAINT);
         }
     }
 
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/PageIndicator.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/PageIndicator.java
new file mode 100644
index 0000000..479bddd
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/PageIndicator.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
+import androidx.pdf.R;
+import androidx.pdf.data.Range;
+import androidx.pdf.util.Accessibility;
+
+import java.util.Objects;
+
+/**
+ * A Toast-like overlay that surfaces details about the current view (mostly page number, but also
+ * zoom factor) to the user. Input is accepted in raw format (e.g. pages in 0-based indexing), but
+ * presented to the user in human dialect, i.e. page numbers in 1-based indexing, zoom factor in
+ * percent.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class PageIndicator extends ReusableToast {
+    private static final int AUTO_HIDE_DELAY_MS = 1300;
+
+    private final Context mContext;
+    private final TextView mPageNumberView;
+    private final Accessibility mAccessibility;
+
+    private int mNumPages;
+
+    private Range mCurrentRange;
+
+    private float mCurrentZoom;
+
+    PageIndicator(Context context, ViewGroup container) {
+        this(context, inflateView(context, container), Accessibility.get());
+    }
+
+    @VisibleForTesting
+    PageIndicator(Context context, TextView pageNumberView, Accessibility accessibility) {
+        super(pageNumberView);
+        this.mContext = context;
+        this.mAccessibility = accessibility;
+        setAutoHideDelayMs(AUTO_HIDE_DELAY_MS);
+        hide();
+        this.mPageNumberView = pageNumberView;
+    }
+
+    public void setNumPages(int numPages) {
+        this.mNumPages = numPages;
+    }
+
+    /**
+     * Sets details about the current view: page range and zoom. If TalkBack is on, those details
+     * will be announced.
+     *
+     * @param range  the page range, in usual 0-based indexing.
+     * @param zoom   the zoom factor, as a number between 0 and 1.
+     * @param stable indicates whether the position in the document is stable.
+     * @return whether this method resulted in the pageIndicator being shown.
+     */
+    public boolean setRangeAndZoom(@NonNull Range range, float zoom, boolean stable) {
+        boolean shown = false;
+
+        String announceStr = null;
+        if (!Objects.equals(mCurrentRange, range)) {
+            String desc = getDescription(range);
+            announceStr = desc;
+            mPageNumberView.setText(getLabel(range));
+            mPageNumberView.setContentDescription(desc);
+            if (mCurrentRange != null) {
+                // Do not show on the first time, only when updating
+                show();
+                shown = true;
+            }
+
+            mCurrentRange = range;
+        }
+
+        if (zoom != mCurrentZoom && stable) {
+            // Override announcement with zoom info.
+            announceStr = getDescription(range) + "\n" + getZoomDescription(zoom);
+            mCurrentZoom = zoom;
+        }
+        if (announceStr != null && mAccessibility.isAccessibilityEnabled(mContext)) {
+            mAccessibility.announce(mContext, mPageNumberView, announceStr);
+        }
+
+        return shown;
+    }
+
+    @NonNull
+    public TextView getTextView() {
+        return mPageNumberView;
+    }
+
+    private static TextView inflateView(Context context, ViewGroup container) {
+        LayoutInflater.from(context).inflate(R.layout.page_indicator, container);
+        return (TextView) container.findViewById(R.id.pdf_page_num);
+    }
+
+    private String getLabel(Range range) {
+        Resources res = mContext.getResources();
+        switch (range.length()) {
+            case 0:
+                return res.getString(R.string.label_page_single, range.getLast() + 1, mNumPages);
+            case 1:
+                return res.getString(R.string.label_page_single, range.getFirst() + 1, mNumPages);
+            default:
+                return res.getString(R.string.label_page_range, range.getFirst() + 1,
+                        range.getLast() + 1,
+                        mNumPages);
+        }
+    }
+
+    private String getDescription(Range range) {
+        Resources res = mContext.getResources();
+        switch (range.length()) {
+            case 0:
+                return res.getString(R.string.desc_page_single, range.getLast() + 1, mNumPages);
+            case 1:
+                return res.getString(R.string.desc_page_single, range.getFirst() + 1, mNumPages);
+            default:
+                return res.getString(R.string.desc_page_range, range.getFirst() + 1,
+                        range.getLast() + 1,
+                        mNumPages);
+        }
+    }
+
+    private String getZoomDescription(float zoom) {
+        Resources res = mContext.getResources();
+        return res.getString(R.string.desc_zoom, Math.round(zoom * 100));
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
index faa86b3..dc048db 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
@@ -54,6 +54,8 @@
 import androidx.pdf.util.Screen;
 import androidx.pdf.util.ThreadUtils;
 import androidx.pdf.util.ZoomScrollRestorer;
+import androidx.pdf.util.ZoomUtils;
+import androidx.pdf.viewer.PdfSelectionModel;
 
 import com.google.android.material.motion.MotionUtils;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -133,6 +135,12 @@
     private static final boolean UNSTABLE = false;
     private static final String KEY_SUPER = "s";
     private static final String KEY_POSITION = "p";
+    private static final String KEY_VIEWPORT_INITIALIZED = "vi";
+    private static final String KEY_VIEWPORT = "v";
+    private static final String KEY_CONTENT_ZOOM = "z";
+    private static final String KEY_RAW_BOUNDS = "b";
+    private static final String KEY_PADDING = "pa";
+    private static final String KEY_LOOKAT_POINT = "l";
     private static final int OVERSCROLL_THRESHOLD = 25;
     /** Fallback duration for the zoom animation, when material attributes are unavailable. */
     private static final int FALLBACK_ZOOM_ANIMATION_DURATION_MS = 250;
@@ -196,6 +204,9 @@
     private int mContentResizedModeZoom = ContentResizedMode.KEEP_SAME_ABSOLUTE;
     private boolean mOverrideMinZoomToFit = false;
     private boolean mOverrideMaxZoomToFit = false;
+
+    /** The last stable zoom: we only re-draw bitmaps at stable zoom (not during a gesture). */
+    private float mStableZoom;
     /**
      * If set to true, suppresses {@link ZoomGestureHandler#onScale(ScaleGestureDetector)} behavior.
      */
@@ -210,6 +221,8 @@
      * last time we set the {@link #mViewport} and it should be updated.
      */
     private Rect mPaddingOnLastViewportUpdate;
+    private PdfSelectionModel mPdfSelectionModel;
+    private PointF mRestoreLookAtPoint;
 
     {
         mScroller = new RelativeScroller(getContext());
@@ -217,11 +230,15 @@
         mGestureTracker.setDelegateHandler(mGestureHandler);
     }
 
-    public ZoomView(@NonNull Context context, @NonNull AttributeSet attrs) {
+    public ZoomView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public ZoomView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public ZoomView(@NonNull Context context, @NonNull AttributeSet attrs, int defStyle) {
+    public ZoomView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ZoomView, defStyle,
@@ -234,51 +251,6 @@
         ViewCompat.setLayoutDirection(this, ViewCompat.LAYOUT_DIRECTION_LTR);
     }
 
-    private static int scrollDeltaNeededForZoomChange(
-            float oldZoom, float newZoom, float zoomviewPivot, int scroll) {
-        // Find where the given pivot point would move to when we change the zoom, and return the
-        // delta.
-        float contentPivot = toContentCoord(zoomviewPivot, oldZoom, scroll);
-        float movedZoomViewPivot = toZoomViewCoord(contentPivot, newZoom, scroll);
-        return (int) (movedZoomViewPivot - zoomviewPivot);
-    }
-
-    private static float toContentCoord(float zoomViewCoord, float zoom, int scroll) {
-        return (zoomViewCoord + scroll) / zoom;
-    }
-
-    private static float toZoomViewCoord(float contentCoord, float zoom, int scroll) {
-        return (contentCoord * zoom) - scroll;
-    }
-
-    private static int constrain(float zoom, int scroll, int contentRawSize, int viewportSize) {
-        // The variables in this method are named left and right, which is accurate when this
-        // method is used to constrain X position - when constraining Y, left means top and right
-        // means bottom.
-
-        // Find the left and right bounds of the content in the zoomview's co-ordinates.
-        float leftBound = toZoomViewCoord(0, zoom, scroll);
-        float rightBound = toZoomViewCoord(contentRawSize, zoom, scroll);
-
-        if (leftBound <= 0 && rightBound >= viewportSize) {
-            // Content too large for viewport and no dead margins: no adjustment needed.
-            return 0;
-        }
-        float scaledContentSize = rightBound - leftBound;
-        if (scaledContentSize <= viewportSize) {
-            // Content fits in viewport: keep in the center.
-            return (int) ((rightBound + leftBound - viewportSize) / 2);
-        } else {
-            // Content doesn't fit in viewport: eliminate dead margins.
-            if (leftBound > 0) { // Dead margin on the left.
-                return (int) leftBound;
-            } else if (rightBound < viewportSize) { // Dead margin on the right.
-                return (int) (rightBound - viewportSize);
-            }
-        }
-        return 0;
-    }
-
     /**
      * Set values of shareScrollToLeft, shareScrollToRight, shareScrollToTop and
      * shareScrollToBottom.
@@ -412,6 +384,15 @@
         return this;
     }
 
+    @Nullable
+    public PdfSelectionModel getPdfSelectionModel() {
+        return mPdfSelectionModel;
+    }
+
+    public void setPdfSelectionModel(@NonNull PdfSelectionModel pdfSelectionModel) {
+        mPdfSelectionModel = pdfSelectionModel;
+    }
+
     /** Exposes this view's position as an observable value. */
     @NonNull
     public ObservableValue<ZoomScroll> zoomScroll() {
@@ -491,6 +472,8 @@
         super.onLayout(changed, left, top, right, bottom);
         boolean hasContents = mContentView != null && mContentView.getWidth() > 0;
 
+        boolean zoomChanged = false;
+
         // Need to flip this to true if we think onLayout changed the position of anything.
         boolean shouldConstrainPosition = false;
 
@@ -575,13 +558,22 @@
                     // Nothing required: keep same zoom as before.
                 }
 
-                centerAt(lookAtPoint.x, lookAtPoint.y);
+                zoomChanged = true;
+                if (mSaveState && mRestoreLookAtPoint != null) {
+                    centerAt(mRestoreLookAtPoint.x, mRestoreLookAtPoint.y);
+                } else {
+                    centerAt(lookAtPoint.x, lookAtPoint.y);
+                }
                 shouldConstrainPosition = true;
+
+
+                mPositionToRestore = new ZoomScroll(getZoom(), getScrollX(), getScrollY(),
+                        true);
             }
         }
 
         // 3) Apply the initial position: restore or fit-to-screen.
-        if (hasContents) {
+        if (hasContents && !zoomChanged) {
             if (mPositionToRestore == null) {
                 // Initially fit the content to width and/or height.
                 if (!mInitialZoomDone) {
@@ -658,6 +650,13 @@
         this.mMaxZoom = maxZoom;
     }
 
+    public float getStableZoom() {
+        return mStableZoom;
+    }
+
+    public void setStableZoom(float stableZoom) {
+        this.mStableZoom = stableZoom;
+    }
     private float getConstrainedZoomToFit() {
         return constrainZoom(getUnconstrainedZoomToFit());
     }
@@ -764,8 +763,10 @@
         }
         float left = x * newZoom - mViewport.width() / 2f;
         float top = y * newZoom - mViewport.height() / 2f;
-        left += constrain(newZoom, (int) left, mContentRawBounds.width(), mViewport.width());
-        top += constrain(newZoom, (int) top, mContentRawBounds.height(), mViewport.height());
+        left += ZoomUtils.constrainCoordinate(newZoom, (int) left, mContentRawBounds.width(),
+                mViewport.width());
+        top += ZoomUtils.constrainCoordinate(newZoom, (int) top, mContentRawBounds.height(),
+                mViewport.height());
         zoomScrollAnimated(left, top, newZoom, updateListener);
     }
 
@@ -853,8 +854,10 @@
     public void setZoom(float zoom, float pivotX, float pivotY) {
         zoom = Float.isNaN(zoom) ? ZOOM_RESET : zoom;
         mInitialZoomDone = true;
-        int deltaX = scrollDeltaNeededForZoomChange(getZoom(), zoom, pivotX, getScrollX());
-        int deltaY = scrollDeltaNeededForZoomChange(getZoom(), zoom, pivotY, getScrollY());
+        int deltaX = ZoomUtils.scrollDeltaNeededForZoomChange(getZoom(), zoom, pivotX,
+                getScrollX());
+        int deltaY = ZoomUtils.scrollDeltaNeededForZoomChange(getZoom(), zoom, pivotY,
+                getScrollY());
 
         mContentView.setScaleX(zoom);
         mContentView.setScaleY(zoom);
@@ -866,11 +869,11 @@
      * using the current zoom and scroll position of the zoomview.
      */
     protected float toContentX(float zoomViewX) {
-        return toContentCoord(zoomViewX, getZoom(), getScrollX());
+        return ZoomUtils.toContentCoordinate(zoomViewX, getZoom(), getScrollX());
     }
 
     protected float toContentY(float zoomViewY) {
-        return toContentCoord(zoomViewY, getZoom(), getScrollY());
+        return ZoomUtils.toContentCoordinate(zoomViewY, getZoom(), getScrollY());
     }
 
     /**
@@ -878,11 +881,11 @@
      * using the current zoom and scroll position of the zoomview.
      */
     protected float toZoomViewX(float contentX) {
-        return toZoomViewCoord(contentX, getZoom(), getScrollX());
+        return ZoomUtils.toZoomViewCoordinate(contentX, getZoom(), getScrollX());
     }
 
     protected float toZoomViewY(float contentY) {
-        return toZoomViewCoord(contentY, getZoom(), getScrollY());
+        return ZoomUtils.toZoomViewCoordinate(contentY, getZoom(), getScrollY());
     }
 
     /**
@@ -915,11 +918,13 @@
     }
 
     private int constrainX() {
-        return constrain(getZoom(), getScrollX(), mContentRawBounds.width(), mViewport.width());
+        return ZoomUtils.constrainCoordinate(getZoom(), getScrollX(), mContentRawBounds.width(),
+                mViewport.width());
     }
 
     private int constrainY() {
-        return constrain(getZoom(), getScrollY(), mContentRawBounds.height(), mViewport.height());
+        return ZoomUtils.constrainCoordinate(getZoom(), getScrollY(), mContentRawBounds.height(),
+                mViewport.height());
     }
 
     /**
@@ -955,6 +960,12 @@
         bundle.putParcelable(KEY_SUPER, super.onSaveInstanceState());
         if (mSaveState) {
             bundle.putBundle(KEY_POSITION, mPosition.get().asBundle());
+            bundle.putBoolean(KEY_VIEWPORT_INITIALIZED, mViewportInitialized);
+            bundle.putParcelable(KEY_VIEWPORT, mViewport);
+            bundle.putFloat(KEY_CONTENT_ZOOM, mContentView.getScaleX());
+            bundle.putParcelable(KEY_RAW_BOUNDS, mContentRawBounds);
+            bundle.putParcelable(KEY_PADDING, mPaddingOnLastViewportUpdate);
+            bundle.putParcelable(KEY_LOOKAT_POINT, computeLookAtPoint());
         }
         return bundle;
     }
@@ -965,7 +976,14 @@
         super.onRestoreInstanceState(bundle.getParcelable(KEY_SUPER));
         if (mSaveState) {
             Bundle positionBundle = bundle.getBundle(KEY_POSITION);
+            assert positionBundle != null;
             mPositionToRestore = ZoomScroll.fromBundle(positionBundle);
+            mViewportInitialized = bundle.getBoolean(KEY_VIEWPORT_INITIALIZED);
+            mViewport.set(Objects.requireNonNull(bundle.getParcelable(KEY_VIEWPORT)));
+            mContentView.setScaleX(bundle.getFloat(KEY_CONTENT_ZOOM));
+            mContentRawBounds.set(Objects.requireNonNull(bundle.getParcelable(KEY_RAW_BOUNDS)));
+            mPaddingOnLastViewportUpdate = bundle.getParcelable(KEY_PADDING);
+            mRestoreLookAtPoint = bundle.getParcelable(KEY_LOOKAT_POINT);
         }
     }
 
@@ -1394,15 +1412,19 @@
                 int newScrollX = oldScrollX;
                 int newScrollY = oldScrollY;
                 // Adjust new scroll positions due to changed zoom.
-                newScrollX += scrollDeltaNeededForZoomChange(currentZoom, newZoom, e1.getX(),
+                newScrollX += ZoomUtils.scrollDeltaNeededForZoomChange(currentZoom, newZoom,
+                        e1.getX(),
                         oldScrollX);
-                newScrollY += scrollDeltaNeededForZoomChange(currentZoom, newZoom, e1.getY(),
+                newScrollY += ZoomUtils.scrollDeltaNeededForZoomChange(currentZoom, newZoom,
+                        e1.getY(),
                         oldScrollY);
 
                 // Constrain new scroll positions.
-                newScrollX += constrain(newZoom, newScrollX, mContentRawBounds.width(),
+                newScrollX += ZoomUtils.constrainCoordinate(newZoom, newScrollX,
+                        mContentRawBounds.width(),
                         mViewport.width());
-                newScrollY += constrain(newZoom, newScrollY, mContentRawBounds.height(),
+                newScrollY += ZoomUtils.constrainCoordinate(newZoom, newScrollY,
+                        mContentRawBounds.height(),
                         mViewport.height());
 
                 zoomScrollAnimated(
@@ -1478,21 +1500,24 @@
             int newScrollX = oldScrollX;
             int newScrollY = oldScrollY;
             // Adjust new scroll positions due to changed zoom.
-            newScrollX += scrollDeltaNeededForZoomChange(currentZoom, newZoom, e.getX(),
+            newScrollX += ZoomUtils.scrollDeltaNeededForZoomChange(currentZoom, newZoom, e.getX(),
                     oldScrollX);
-            newScrollY += scrollDeltaNeededForZoomChange(currentZoom, newZoom, e.getY(),
+            newScrollY += ZoomUtils.scrollDeltaNeededForZoomChange(currentZoom, newZoom, e.getY(),
                     oldScrollY);
 
             // Constrain new scroll positions.
-            newScrollX += constrain(newZoom, newScrollX, mContentRawBounds.width(),
+            newScrollX += ZoomUtils.constrainCoordinate(newZoom, newScrollX,
+                    mContentRawBounds.width(),
                     mViewport.width());
-            newScrollY += constrain(newZoom, newScrollY, mContentRawBounds.height(),
+            newScrollY += ZoomUtils.constrainCoordinate(newZoom, newScrollY,
+                    mContentRawBounds.height(),
                     mViewport.height());
 
             zoomScrollAnimated(newScrollX, newScrollY, newZoom, null /* updateListener */);
             return true;
         }
 
+
         private float totalScrollLength() {
             // Do not need accuracy of correct hypotenuse calculation.
             return Math.abs(mTotalY) + Math.abs(mTotalX);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomableSelectionHandles.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomableSelectionHandles.java
index fc1b785..6c4593c1 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomableSelectionHandles.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomableSelectionHandles.java
@@ -40,7 +40,7 @@
 @SuppressWarnings("deprecation")
 public abstract class ZoomableSelectionHandles<S> {
     private static final float SCALE_OFFSET = 0.5f;
-    private static final float HANDLE_ALPHA = 0.75f;
+    private static final float HANDLE_ALPHA = 1.0f;
     private static final float RIGHT_HANDLE_X_MARGIN = -0.25f;
     private static final float LEFT_HANDLE_X_MARGIN = -0.75f;
     protected final ZoomView mZoomView;
@@ -128,8 +128,8 @@
 
     protected void showHandle(@NonNull ImageView handle, float rawX, float rawY, boolean isRight) {
         int resId = isRight
-                ? R.drawable.text_select_handle_right
-                : R.drawable.text_select_handle_left;
+                ? R.drawable.selection_drag_handle_right
+                : R.drawable.selection_drag_handle_left;
         handle.setImageResource(resId);
 
         // The sharp point of the handle is found at a particular point in the image -
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/exceptions/PdfPasswordException.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/exceptions/PdfPasswordException.kt
new file mode 100644
index 0000000..bf37790
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/exceptions/PdfPasswordException.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.exceptions
+
+import androidx.annotation.RestrictTo
+import java.lang.SecurityException
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+/**
+ * Represents the exception thrown when a password is required or an incorrect password is supplied
+ * to a protected document
+ */
+class PdfPasswordException(message: String) : SecurityException(message)
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/loader/PdfDocument.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/loader/PdfDocument.kt
new file mode 100644
index 0000000..d39576e
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/loader/PdfDocument.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.loader
+
+import android.graphics.Bitmap
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.pdf.content.PdfPageGotoLinkContent
+import android.graphics.pdf.content.PdfPageImageContent
+import android.graphics.pdf.content.PdfPageLinkContent
+import android.graphics.pdf.content.PdfPageTextContent
+import android.graphics.pdf.models.PageMatchBounds
+import android.graphics.pdf.models.selection.PageSelection
+import android.util.Size
+import android.util.SparseArray
+import androidx.annotation.RestrictTo
+import java.io.Closeable
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+/** Represents a PDF document and provides methods to interact with its content. */
+interface PdfDocument : Closeable {
+
+    /** The total number of pages in the document. */
+    val pageCount: Int
+
+    /** Indicates whether the document is linearized (optimized for fast web viewing). */
+    val isLinearized: Boolean
+
+    /** The type of form present in the document. */
+    val formType: Int
+
+    /** Indicates whether the document is password-protected. */
+    val isProtected: Boolean
+
+    /**
+     * Asynchronously retrieves information about the specified page.
+     *
+     * @param pageNumber The page number (0-based).
+     * @return A [PageInfo] object containing information about the page.
+     */
+    suspend fun getPageInfo(pageNumber: Int): PageInfo
+
+    /**
+     * Asynchronously retrieves information about a range of pages.
+     *
+     * @param pageRange The range of page numbers (0-based, inclusive).
+     * @return A list of {@link PageInfo} objects, one for each page in the range.
+     */
+    suspend fun getPageInfos(pageRange: IntRange): List<PageInfo>
+
+    /**
+     * Asynchronously searches the document for the specified query within a range of pages.
+     *
+     * @param query The search query string.
+     * @param pageRange The range of page numbers (0-based, inclusive) to search within.
+     * @return A {@link SparseArray} mapping page numbers to lists of {@link PageMatchBounds}
+     *   objects representing the search results on each page.
+     */
+    suspend fun searchDocument(
+        query: String,
+        pageRange: IntRange
+    ): SparseArray<List<PageMatchBounds>>
+
+    /**
+     * Asynchronously retrieves the selection bounds (in PDF coordinates) for the specified text
+     * selection.
+     *
+     * @param start The starting point of the text selection.
+     * @param stop The ending point of the text selection.
+     * @return A SparseArray mapping page numbers to {@link PageSelection} objects representing the
+     *   selection bounds on each page.
+     */
+    suspend fun getSelectionBounds(start: PdfPoint, stop: PdfPoint): SparseArray<PageSelection>
+
+    /**
+     * Asynchronously retrieves the content (text and images) of the specified page.
+     *
+     * @param pageNumber The page number (0-based).
+     * @return A {@link PdfPageContent} object representing the page's content.
+     */
+    suspend fun getPageContent(pageNumber: Int): PdfPageContent
+
+    /**
+     * Asynchronously retrieves the links (Go To and external) present on the specified page.
+     *
+     * @param pageNumber The page number (0-based).
+     * @return A [PdfPageLinks] object representing the page's links.
+     */
+    suspend fun getPageLinks(pageNumber: Int): PdfPageLinks
+
+    /**
+     * Gets a [BitmapSource] for retrieving bitmap representations of the specified page.
+     *
+     * @param pageNumber The page number (0-based).
+     * @return A [BitmapSource] for the specified page, or null if the page number is invalid.
+     */
+    fun getPageBitmapSource(pageNumber: Int): BitmapSource
+
+    /**
+     * Represents information about a single page in the PDF document.
+     *
+     * @property pageNum The page number (0-based).
+     * @property height The height of the page in points.
+     * @property width The width of the page in points.
+     */
+    class PageInfo(val pageNum: Int, val height: Int, val width: Int)
+
+    /** A source for retrieving bitmap representations of PDF pages. */
+    interface BitmapSource : Closeable {
+        /**
+         * Asynchronously retrieves a bitmap representation of the page, optionally constrained to a
+         * specific tile region.
+         *
+         * @param scaledPageSizePx The scaled page size in pixels, representing the page size in
+         *   case of no zoom, and scaled page size in case of zooming.
+         * @param tileRegion (Optional) The region of the page to include in the bitmap, in pixels
+         *   within the `scaledPageSizePx`. This identifies the tile. If null, the entire page is
+         *   included.
+         * @return The bitmap representation of the page.
+         */
+        suspend fun getBitmap(scaledPageSizePx: Size, tileRegion: Rect? = null): Bitmap
+    }
+
+    /**
+     * Represents the combined text and image content within a single page of a PDF document.
+     *
+     * @property textContents A list of {@link PdfPageTextContent} objects representing the text
+     *   elements on the page.
+     * @property imageContents A list of {@link PdfPageImageContent} objects representing the image
+     *   elements on the page.
+     */
+    class PdfPageContent(
+        val textContents: List<PdfPageTextContent>,
+        val imageContents: List<PdfPageImageContent>
+    )
+
+    /**
+     * Represents the links within a single page of a PDF document.
+     *
+     * @property gotoLinks A list of internal links (links to other pages within the same document)
+     *   represented as `PdfPageGotoLinkContent` objects.
+     * @property externalLinks A list of external links (links to web pages or other resources)
+     *   represented as `PdfPageLinkContent` objects.
+     */
+    class PdfPageLinks(
+        val gotoLinks: List<PdfPageGotoLinkContent>,
+        val externalLinks: List<PdfPageLinkContent>
+    )
+
+    /**
+     * Represents a point within a specific page of a PDF document.
+     *
+     * @property pageNumber The page number (0-based) where the point is located.
+     * @property pagePoint The coordinates (x, y) of the point relative to the page's origin.
+     */
+    class PdfPoint(val pageNumber: Int, val pagePoint: PointF)
+}
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/loader/PdfLoader.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/loader/PdfLoader.kt
new file mode 100644
index 0000000..f8b8e776
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/loader/PdfLoader.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.loader
+
+import android.net.Uri
+import androidx.annotation.RestrictTo
+import androidx.pdf.exceptions.PdfPasswordException
+import java.io.IOException
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+/**
+ * Provides an abstraction for asynchronously opening PDF documents from a Uri. Implementations of
+ * this interface are responsible for handling the retrieval, decryption (if necessary), and
+ * creation of a [PdfDocument] representation.
+ */
+interface PdfLoader {
+
+    /**
+     * Asynchronously opens a PDF document from the specified [Uri].
+     *
+     * @param uri The URI of the PDF document to open.
+     * @param password (Optional) The password to unlock the document if it is encrypted.
+     * @return The opened [PdfDocument].
+     * @throws PdfPasswordException If the provided password is incorrect.
+     * @throws IOException If an error occurs while opening the document.
+     */
+    @Throws(PdfPasswordException::class, IOException::class)
+    suspend fun openDocument(uri: Uri, password: String? = null): PdfDocument
+}
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/viewmodel/PdfViewerViewModel.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/viewmodel/PdfViewerViewModel.kt
new file mode 100644
index 0000000..b7ca9fb
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/viewmodel/PdfViewerViewModel.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewmodel
+
+import android.content.Context
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.ViewModel
+import androidx.pdf.data.DisplayData
+import androidx.pdf.util.TileBoard
+import androidx.pdf.viewer.loader.PdfLoader
+import androidx.pdf.viewer.loader.PdfLoaderCallbacks
+import androidx.pdf.viewer.loader.WeakPdfLoaderCallbacks
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class PdfLoaderViewModel : ViewModel() {
+    private val _pdfLoaderStateFlow = MutableStateFlow<PdfLoader?>(null)
+    val pdfLoaderStateFlow: StateFlow<PdfLoader?> = _pdfLoaderStateFlow.asStateFlow()
+
+    private var currentData: DisplayData? = null
+
+    fun updatePdfLoader(
+        context: Context,
+        newData: DisplayData,
+        callbacks: PdfLoaderCallbacks,
+    ) {
+        // Case 1: New file opened. Replace the existing loader with a new one
+        if (newData != currentData) {
+            currentData = newData
+            _pdfLoaderStateFlow.update { currentLoader ->
+                // Clear the existing loader
+                currentLoader?.cancelAll()
+                currentLoader?.disconnect()
+                val loader =
+                    PdfLoader.create(context, newData, TileBoard.DEFAULT_RECYCLER, callbacks, false)
+                loader
+            }
+        } else {
+            // Case 2: Configuration change. Update callbacks and re-trigger document loading
+            val loader = _pdfLoaderStateFlow.value
+            loader?.callbacks = WeakPdfLoaderCallbacks.wrap(callbacks)
+            loader?.reloadDocument() // Force re-triggering documentLoaded flow
+        }
+    }
+
+    @VisibleForTesting
+    internal fun updatePdfLoader(pdfLoader: PdfLoader) {
+        _pdfLoaderStateFlow.value = pdfLoader
+    }
+}
diff --git a/pdf/pdf-viewer/src/main/native/CMakeLists.txt b/pdf/pdf-viewer/src/main/native/CMakeLists.txt
deleted file mode 100644
index e687666..0000000
--- a/pdf/pdf-viewer/src/main/native/CMakeLists.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2024 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-# For more information about using CMake with Android Studio, read the
-# documentation: https://d.android.com/studio/projects/add-native-code.html.
-# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
-
-# Sets the minimum CMake version required for this project.
-cmake_minimum_required(VERSION 3.22.1)
-
-# Declares the project name. The project name can be accessed via ${PROJECT_NAME}.
-# Since this is the top level CMakeLists.txt, the project name is also accessible
-# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
-# build script scope).
-project("bitmapParcel")
-
-# Creates and names a library, sets it as either STATIC
-# or SHARED, and provides the relative paths to its source code.
-# You can define multiple libraries, and CMake builds them for you.
-# Gradle automatically packages shared libraries with your APK.
-#
-# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
-# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
-# is preferred for the same purpose.
-#
-# In order to load a library into your app from Java/Kotlin, you must call
-# System.loadLibrary() and pass the name of the library defined here;
-# for GameActivity/NativeActivity derived applications, the same library name must be
-# used in the AndroidManifest.xml file.
-add_library(${CMAKE_PROJECT_NAME} SHARED
-        # List C/C++ source files with relative paths to this CMakeLists.txt.
-        bitmap_parcel.cc
-        extractors.cc
-)
-
-# Specifies libraries CMake should link to your target library. You
-# can link libraries from various origins, such as libraries defined in this
-# build script, prebuilt third-party libraries, or Android system libraries.
-target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log)
-target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE android)
-target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE jnigraphics)
diff --git a/pdf/pdf-viewer/src/main/native/bitmap_parcel.cc b/pdf/pdf-viewer/src/main/native/bitmap_parcel.cc
deleted file mode 100644
index 036437f..0000000
--- a/pdf/pdf-viewer/src/main/native/bitmap_parcel.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "bitmap_parcel.h"
-
-#include <android/bitmap.h>
-#include <jni.h>
-#include <stdint.h>
-
-#include "extractors.h"
-#include "logging.h"
-
-#define LOG_TAG "bitmap_parcel"
-
-using pdflib::Extractor;
-using pdflib::BufferReader;
-using pdflib::FdReader;
-
-namespace {
-
-    static const int kBytesPerPixel = 4;
-
-    bool FeedBitmap(JNIEnv *env, jobject jbitmap, Extractor *source);
-
-}  // namespace
-
-extern "C" JNIEXPORT jboolean JNICALL
-Java_androidx_pdf_util_BitmapParcel_readIntoBitmap
-        (JNIEnv *env, jclass, jobject jbitmap, int fd) {
-    FdReader source(fd);
-    return FeedBitmap(env, jbitmap, &source);
-}
-
-namespace {
-
-    bool FeedBitmap(JNIEnv *env, jobject jbitmap, Extractor *source) {
-        void *bitmap_pixels;
-        int ret;
-        if ((ret = AndroidBitmap_lockPixels(env, jbitmap, &bitmap_pixels)) < 0) {
-            LOGE("AndroidBitmap_lockPixels() failed! error=%d", ret);
-            return false;
-        }
-
-
-        AndroidBitmapInfo info;
-        AndroidBitmap_getInfo(env, jbitmap, &info);
-
-        int num_bytes = info.width * info.height * kBytesPerPixel;
-        uint8_t *bitmap_bytes = reinterpret_cast<uint8_t *>(bitmap_pixels);
-        source->extract(bitmap_bytes, num_bytes);
-
-        AndroidBitmap_unlockPixels(env, jbitmap);
-        LOGV("Copied %d bytes into bitmap", num_bytes);
-        return true;
-    }
-
-}  // namespace
diff --git a/pdf/pdf-viewer/src/main/native/bitmap_parcel.h b/pdf/pdf-viewer/src/main/native/bitmap_parcel.h
deleted file mode 100644
index 8f9832e..0000000
--- a/pdf/pdf-viewer/src/main/native/bitmap_parcel.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <jni.h>
-
-#ifndef ANDROIDX_PDF_BITMAP_PARCEL_H_
-#define ANDROIDX_PDF_BITMAP_PARCEL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-JNIEXPORT jboolean JNICALL
-Java_androidx_pdf_util_BitmapParcel_readIntoBitmap
-        (JNIEnv *env, jclass, jobject jbitmap, jint jfd);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif  // ANDROIDX_PDF_BITMAP_PARCEL_H_
diff --git a/pdf/pdf-viewer/src/main/native/extractors.cc b/pdf/pdf-viewer/src/main/native/extractors.cc
deleted file mode 100644
index c7ead25..0000000
--- a/pdf/pdf-viewer/src/main/native/extractors.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "extractors.h"
-
-#include <stdint.h>
-#include <unistd.h>
-
-#include <cstring>
-
-#include "logging.h"
-
-#define LOG_TAG "extractor"
-
-namespace pdflib {
-
-    Extractor::~Extractor() {}
-
-    BufferWriter::~BufferWriter() {}
-
-    bool BufferWriter::extract(uint8_t *source, int num_bytes) {
-        memcpy(buffer_, source, num_bytes);
-        return true;
-    }
-
-    BufferReader::~BufferReader() {}
-
-    bool BufferReader::extract(uint8_t *destination, int num_bytes) {
-        memcpy(destination, buffer_, num_bytes);
-        return true;
-    }
-
-    FdWriter::~FdWriter() {}
-
-    bool FdWriter::extract(uint8_t *source, int num_bytes) {
-        LOGV("FdWriter Extracting %d bytes on %d", num_bytes, fd_);
-        bool ret = true;
-        while (num_bytes > 0) {
-            int len = write(fd_, source, num_bytes);
-            if (len == -1 || len == 0) {
-                ret = false;
-                LOGD("FdWriter extract failed at %d on %d", num_bytes, fd_);
-                break;
-            }
-            num_bytes -= len;
-            source += len;
-        }
-        close(fd_);
-        return ret;
-    }
-
-    FdReader::~FdReader() {}
-
-    bool FdReader::extract(uint8_t *destination, int num_bytes) {
-        LOGV("FdReader Extracting %d bytes from %d", num_bytes, fd_);
-        bool ret = true;
-        while (num_bytes > 0) {
-            int len = read(fd_, destination, num_bytes);
-            if (len == -1 || len == 0) {
-                ret = false;
-                LOGD("FdWriter extract failed at %d on %d", num_bytes, fd_);
-                break;
-            }
-            num_bytes -= len;
-            destination += len;
-        }
-        close(fd_);
-        return ret;
-    }
-
-}  // namespace pdflib
diff --git a/pdf/pdf-viewer/src/main/native/extractors.h b/pdf/pdf-viewer/src/main/native/extractors.h
deleted file mode 100644
index 493ac51..0000000
--- a/pdf/pdf-viewer/src/main/native/extractors.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROIDX_PDF_EXTRACTORS_H
-#define ANDROIDX_PDF_EXTRACTORS_H
-
-#include <stdint.h>
-
-namespace pdflib {
-
-// Interface for extracting bytes from or into an underlying something.
-    class Extractor {
-    public:
-        // Transfers {num_bytes} bytes between the underlying something and {buffer}.
-        virtual ~Extractor();
-
-        virtual bool extract(uint8_t *buffer, int num_bytes) = 0;
-    };
-
-
-// An Extractor that copies bytes on the given buffer.
-    class BufferWriter : public pdflib::Extractor {
-    public:
-        explicit BufferWriter(uint8_t *buffer) : buffer_(buffer) {}
-
-        ~BufferWriter() override;
-
-        bool extract(uint8_t *source, int num_bytes) override;
-
-    private:
-        uint8_t *buffer_;
-    };
-
-// An Extractor that copies bytes from the given buffer.
-    class BufferReader : public pdflib::Extractor {
-    public:
-        explicit BufferReader(uint8_t *buffer) : buffer_(buffer) {}
-
-        ~BufferReader() override;
-
-        bool extract(uint8_t *source, int num_bytes) override;
-
-    private:
-        uint8_t *buffer_;
-    };
-
-// An extractor that writes bytes on the given fd. It closes the fd thereafter.
-    class FdWriter : public pdflib::Extractor {
-    public:
-        explicit FdWriter(int fd) : fd_(fd) {}
-
-        ~FdWriter() override;
-
-        bool extract(uint8_t *source, int num_bytes) override;
-
-    private:
-        int fd_;
-    };
-
-// An extractor that read bytes from the given fd. It closes the fd thereafter.
-    class FdReader : public pdflib::Extractor {
-    public:
-        explicit FdReader(int fd) : fd_(fd) {}
-
-        ~FdReader() override;
-
-        bool extract(uint8_t *destination, int num_bytes) override;
-
-    private:
-        int fd_;
-    };
-
-}  // namespace pdfClient
-
-#endif  // ANDROIDX_PDF_EXTRACTORS_H
diff --git a/pdf/pdf-viewer/src/main/native/logging.h b/pdf/pdf-viewer/src/main/native/logging.h
deleted file mode 100644
index a04bed4..0000000
--- a/pdf/pdf-viewer/src/main/native/logging.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROIDX_PDF_LOGGING_H
-#define ANDROIDX_PDF_LOGGING_H
-
-#include <android/log.h>
-
-#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
-#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
-#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
-
-#endif //ANDROIDX_PDF_LOGGING_H
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/chevron_down.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/chevron_down.png
deleted file mode 100644
index dbfb6fd..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/chevron_down.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/chevron_up.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/chevron_up.png
deleted file mode 100644
index 733ada1..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/chevron_up.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/close_button.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/close_button.png
deleted file mode 100644
index 7687437..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/close_button.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/edit_fab.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/edit_fab.png
deleted file mode 100644
index 43d62d1..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/edit_fab.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
deleted file mode 100644
index fb242e2..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_alert.png
deleted file mode 100644
index 41bd51a..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_select_handle_left.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_select_handle_left.png
deleted file mode 100644
index 0ad542b..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_select_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_select_handle_right.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_select_handle_right.png
deleted file mode 100644
index 05e2fba..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/text_select_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png b/pdf/pdf-viewer/src/main/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
deleted file mode 100644
index 8b5b757..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-ldpi/textfield_default_mtrl_alpha.9.png b/pdf/pdf-viewer/src/main/res/drawable-ldpi/textfield_default_mtrl_alpha.9.png
deleted file mode 100644
index 6f0c57f..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-ldpi/textfield_default_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/chevron_down.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/chevron_down.png
deleted file mode 100644
index 1c885781..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/chevron_down.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/chevron_up.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/chevron_up.png
deleted file mode 100644
index d9dbb65..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/chevron_up.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/close_button.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/close_button.png
deleted file mode 100644
index a52f5eb..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/close_button.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/edit_fab.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/edit_fab.png
deleted file mode 100644
index 9092052..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/edit_fab.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
deleted file mode 100644
index 019f3dd..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_alert.png
deleted file mode 100644
index 8e5c9ab..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_select_handle_left.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_select_handle_left.png
deleted file mode 100644
index 9575110..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_select_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_select_handle_right.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_select_handle_right.png
deleted file mode 100644
index 7f2a818..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/text_select_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png b/pdf/pdf-viewer/src/main/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
deleted file mode 100644
index 455818f..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-night-hdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-night-hdpi/text_alert.png
deleted file mode 100644
index 3b1280f..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-night-hdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-night-mdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-night-mdpi/text_alert.png
deleted file mode 100644
index 7bc2275..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-night-mdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-night-xhdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-night-xhdpi/text_alert.png
deleted file mode 100644
index 5933daa..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-night-xhdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-night-xxhdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-night-xxhdpi/text_alert.png
deleted file mode 100644
index 5e96508..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-night-xxhdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-night-xxxhdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-night-xxxhdpi/text_alert.png
deleted file mode 100644
index 8ccfe11..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-night-xxxhdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/chevron_down.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/chevron_down.png
deleted file mode 100644
index 9291ad1..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/chevron_down.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/chevron_up.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/chevron_up.png
deleted file mode 100644
index 76562d5..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/chevron_up.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/close_button.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/close_button.png
deleted file mode 100644
index e86ea2d..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/close_button.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/edit_fab.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/edit_fab.png
deleted file mode 100644
index eb850fd..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/edit_fab.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
deleted file mode 100644
index e651fd1..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_alert.png
deleted file mode 100644
index 89ca55f..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_select_handle_left.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_select_handle_left.png
deleted file mode 100644
index a08c2af..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_select_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_select_handle_right.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_select_handle_right.png
deleted file mode 100644
index 48ea7de..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/text_select_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png b/pdf/pdf-viewer/src/main/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
deleted file mode 100644
index b0c74eb..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/chevron_down.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/chevron_down.png
deleted file mode 100644
index 4b28efe..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/chevron_down.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/chevron_up.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/chevron_up.png
deleted file mode 100644
index 7967ced..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/chevron_up.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/close_button.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/close_button.png
deleted file mode 100644
index ef3b4de..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/close_button.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/edit_fab.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/edit_fab.png
deleted file mode 100644
index 14efb02..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/edit_fab.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
deleted file mode 100644
index ccc75aa..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_alert.png
deleted file mode 100644
index 01912c1..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_select_handle_left.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_select_handle_left.png
deleted file mode 100644
index 36854b3..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_select_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_select_handle_right.png b/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_select_handle_right.png
deleted file mode 100644
index 172787a..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxhdpi/text_select_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/chevron_down.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/chevron_down.png
deleted file mode 100644
index 5a78264..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/chevron_down.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/chevron_up.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/chevron_up.png
deleted file mode 100644
index 50feeed..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/chevron_up.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/close_button.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/close_button.png
deleted file mode 100644
index c6805cc..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/close_button.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/edit_fab.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/edit_fab.png
deleted file mode 100644
index 06f6ee0..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/edit_fab.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
deleted file mode 100644
index c92252b..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/quantum_gm_ic_drag_indicator_gm_grey_24.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_alert.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_alert.png
deleted file mode 100644
index 3e39ecd..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_alert.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_select_handle_left.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_select_handle_left.png
deleted file mode 100644
index 47e9a8b..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_select_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_select_handle_right.png b/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_select_handle_right.png
deleted file mode 100644
index f65daf1..0000000
--- a/pdf/pdf-viewer/src/main/res/drawable-xxxhdpi/text_select_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/pdf/pdf-viewer/src/main/res/drawable/close_button.xml b/pdf/pdf-viewer/src/main/res/drawable/close_button.xml
new file mode 100644
index 0000000..f6345f3
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/close_button.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:tint="?attr/colorOnSurfaceVariant"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+
+    <path
+        android:fillColor="?attr/colorOnSurfaceVariant"
+        android:pathData="M336,680L280,624L424,480L280,337L336,281L480,425L623,281L679,337L535,480L679,624L623,680L480,536L336,680Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/drawable/drag_indicator.xml b/pdf/pdf-viewer/src/main/res/drawable/drag_indicator.xml
new file mode 100644
index 0000000..4a0d1b6
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/drag_indicator.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:tint="?attr/colorOnSurfaceVariant"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+
+    <path
+        android:fillColor="?attr/colorOnSurfaceVariant"
+        android:pathData="M480,880L240,640L297,583L480,766L663,583L720,640L480,880ZM298,376L240,320L480,80L720,320L662,376L480,194L298,376Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/drawable/edit_fab.xml b/pdf/pdf-viewer/src/main/res/drawable/edit_fab.xml
new file mode 100644
index 0000000..febd780
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/edit_fab.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:tint="?attr/colorOnPrimaryContainer"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+
+    <path
+        android:fillColor="?attr/colorOnPrimaryContainer"
+        android:pathData="M200,760L257,760L648,369L591,312L200,703L200,760ZM120,840L120,670L648,143Q660,132 674.5,126Q689,120 705,120Q721,120 736,126Q751,132 762,144L817,200Q829,211 834.5,226Q840,241 840,256Q840,272 834.5,286.5Q829,301 817,313L290,840L120,840ZM760,256L760,256L704,200L704,200L760,256ZM619,341L591,312L591,312L648,369L648,369L619,341Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/drawable/keyboard_down.xml b/pdf/pdf-viewer/src/main/res/drawable/keyboard_down.xml
new file mode 100644
index 0000000..ccdcd6e
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/keyboard_down.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:tint="?attr/colorOnSurfaceVariant"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+
+    <path
+        android:fillColor="?attr/colorOnSurfaceVariant"
+        android:pathData="M480,616L240,376L296,320L480,504L664,320L720,376L480,616Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/drawable/keyboard_up.xml b/pdf/pdf-viewer/src/main/res/drawable/keyboard_up.xml
new file mode 100644
index 0000000..aacc567
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/keyboard_up.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:tint="?attr/colorOnSurfaceVariant"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+
+    <path
+        android:fillColor="?attr/colorOnSurfaceVariant"
+        android:pathData="M480,432L296,616L240,560L480,320L720,560L664,616L480,432Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/drawable/selection_drag_handle_left.xml b/pdf/pdf-viewer/src/main/res/drawable/selection_drag_handle_left.xml
new file mode 100644
index 0000000..7132904
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/selection_drag_handle_left.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="44dp"
+    android:height="44dp"
+    android:autoMirrored="true"
+    android:viewportHeight="44"
+    android:viewportWidth="44">
+
+    <path
+        android:fillColor="#FFA8C7FA"
+        android:fillType="evenOdd"
+        android:pathData="M33.939,9.899L33.897,23.94C33.874,31.695 27.569,38.001 19.814,38.024C12.059,38.047 5.791,31.779 5.814,24.024C5.837,16.269 12.142,9.963 19.897,9.94L33.939,9.899Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/drawable/selection_drag_handle_right.xml b/pdf/pdf-viewer/src/main/res/drawable/selection_drag_handle_right.xml
new file mode 100644
index 0000000..49021c7b
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/selection_drag_handle_right.xml
@@ -0,0 +1,30 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="44dp"
+    android:height="44dp"
+    android:autoMirrored="true"
+    android:tint="#FFA8C7FA"
+    android:viewportHeight="44"
+    android:viewportWidth="44">
+
+    <path
+        android:fillColor="#FFA8C7FA"
+        android:fillType="evenOdd"
+        android:pathData="M9.901,9.899L9.942,23.94C9.965,31.695 16.271,38.001 24.026,38.024C31.781,38.047 38.049,31.779 38.026,24.024C38.003,16.269 31.697,9.963 23.942,9.94L9.901,9.899Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/drawable/shape_oval.xml b/pdf/pdf-viewer/src/main/res/drawable/shape_oval.xml
new file mode 100644
index 0000000..e8b5b8e
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/shape_oval.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/search_textbox">
+    <item>
+        <shape android:shape="oval">
+            <solid android:color="@color/search_background" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/res/drawable/text_alert.xml b/pdf/pdf-viewer/src/main/res/drawable/text_alert.xml
new file mode 100644
index 0000000..0dd6f56
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/drawable/text_alert.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:autoMirrored="true"
+    android:tint="?attr/colorError"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+
+    <path
+        android:fillColor="?attr/colorError"
+        android:pathData="M480,680Q497,680 508.5,668.5Q520,657 520,640Q520,623 508.5,611.5Q497,600 480,600Q463,600 451.5,611.5Q440,623 440,640Q440,657 451.5,668.5Q463,680 480,680ZM440,520L520,520L520,280L440,280L440,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
+
+</vector>
diff --git a/pdf/pdf-viewer/src/main/res/layout/fastscroll_handle.xml b/pdf/pdf-viewer/src/main/res/layout/fastscroll_handle.xml
index 921f4e9..11ae8df 100644
--- a/pdf/pdf-viewer/src/main/res/layout/fastscroll_handle.xml
+++ b/pdf/pdf-viewer/src/main/res/layout/fastscroll_handle.xml
@@ -21,5 +21,5 @@
     android:elevation="4dp"
     android:importantForAccessibility="no"
     android:scaleType="center"
-    android:src="@drawable/quantum_gm_ic_drag_indicator_gm_grey_24"
+    android:src="@drawable/drag_indicator"
     android:translationX="18dp" />
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/res/layout/file_viewer_pdf.xml b/pdf/pdf-viewer/src/main/res/layout/file_viewer_pdf.xml
index d47b623..2aec9a5 100644
--- a/pdf/pdf-viewer/src/main/res/layout/file_viewer_pdf.xml
+++ b/pdf/pdf-viewer/src/main/res/layout/file_viewer_pdf.xml
@@ -24,6 +24,8 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:clipToPadding="false"
+        android:paddingStart="@dimen/viewer_doc_padding_x"
+        android:paddingEnd="@dimen/viewer_doc_padding_x"
         android:paddingBottom="@dimen/viewer_doc_padding_y"
         android:paddingTop="@dimen/viewer_doc_padding_y">
 
diff --git a/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml b/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml
index 729db9f..1c6032d 100644
--- a/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml
+++ b/pdf/pdf-viewer/src/main/res/layout/find_in_file.xml
@@ -23,9 +23,9 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
+        android:layout_marginRight="5dp"
         android:background="@drawable/shape_textbox">
 
-        <!--        android:background = "@color/black"-->
         <androidx.pdf.widget.SearchEditText
             android:id="@+id/find_query_box"
             android:layout_width="wrap_content"
@@ -54,26 +54,40 @@
 
     <ImageButton
         android:id="@+id/find_prev_btn"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:src="@drawable/chevron_up"
-        app:tint="@color/search_prev_button"  />
+        android:layout_width="34dp"
+        android:layout_height="34dp"
+        android:background="@drawable/shape_oval"
+        android:src="@drawable/keyboard_up"
+        app:tint="@color/search_prev_button"
+        android:cropToPadding="true"
+        android:padding="3dp"
+        android:scaleType="centerInside"
+        android:layout_margin="5dp"/>
 
     <ImageButton
         android:id="@+id/find_next_btn"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:src="@drawable/chevron_down"
+        android:layout_width="34dp"
+        android:layout_height="34dp"
+        android:background="@drawable/shape_oval"
+        android:src="@drawable/keyboard_down"
         app:tint="@color/search_next_button"
+        android:cropToPadding="true"
+        android:padding="3dp"
+        android:scaleType="centerInside"
+        android:layout_margin="5dp"
         />
     <ImageButton
         android:id="@+id/close_btn"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:background="?android:attr/selectableItemBackground"
+        android:layout_width="34dp"
+        android:layout_height="34dp"
+        android:background="@drawable/shape_oval"
         android:src="@drawable/close_button"
-        app:tint="@color/search_close_button" />
+        app:tint="@color/search_close_button"
+        android:cropToPadding="true"
+        android:padding="5dp"
+        android:scaleType="centerInside"
+        android:layout_marginVertical="5dp"
+        android:layout_marginLeft="5dp"
+        android:layout_marginRight="10dp"/>
 
 </merge>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml b/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml
index 651e0d5..a08ee39 100644
--- a/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml
+++ b/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml
@@ -23,6 +23,11 @@
 
     <include layout="@layout/file_viewer_pdf"/>
     <include layout="@layout/search" android:id="@+id/search"/>
+    <androidx.pdf.viewer.LoadingView
+        android:id="@+id/loadingView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center" />
     <com.google.android.material.floatingactionbutton.FloatingActionButton
         android:id="@+id/edit_fab"
         android:layout_width="wrap_content"
diff --git a/pdf/pdf-viewer/src/main/res/menu/context_menu.xml b/pdf/pdf-viewer/src/main/res/menu/context_menu.xml
new file mode 100644
index 0000000..9557c52
--- /dev/null
+++ b/pdf/pdf-viewer/src/main/res/menu/context_menu.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/selection_menu">
+    <item
+        android:id="@+id/action_copy"
+        android:icon="?android:attr/actionModeCopyDrawable"
+        android:title="@android:string/copy"
+        android:alphabeticShortcut="c" />
+    <item
+        android:id="@+id/action_selectAll"
+        android:icon="?android:attr/actionModeSelectAllDrawable"
+        android:title="@android:string/selectAll"
+        android:alphabeticShortcut="a" />
+</menu>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/res/values-night-v31/colors.xml b/pdf/pdf-viewer/src/main/res/values-night-v31/colors.xml
deleted file mode 100644
index c543141..0000000
--- a/pdf/pdf-viewer/src/main/res/values-night-v31/colors.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-
-    <color name="google_blue">#ff1a73e8</color>
-    <color name="google_white">#ffffffff</color>
-    <color name="google_grey">#ff3c4043</color>
-    <color name="text_default">#666</color>
-    <color name="text_error">#da4336</color>
-    <color name="selection_handles">#00aadd</color>
-    <color name="search_background">@color/material_dynamic_neutral_variant10</color>
-    <color name="search_textbox">@color/material_dynamic_neutral_variant20</color>
-    <color name="search_texthint">@android:color/system_neutral2_200</color>
-    <color name="search_textColor">@android:color/system_neutral1_100</color>
-    <color name="search_count">@android:color/system_neutral2_200</color>
-    <color name="search_prev_button">@android:color/system_neutral2_200</color>
-    <color name="search_next_button">@android:color/system_neutral2_200</color>
-    <color name="search_close_button">@android:color/system_neutral2_200</color>
-
-</resources>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/res/values-v31/colors.xml b/pdf/pdf-viewer/src/main/res/values-v31/colors.xml
deleted file mode 100644
index 22db258..0000000
--- a/pdf/pdf-viewer/src/main/res/values-v31/colors.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-
-    <color name="google_blue">#ff1a73e8</color>
-    <color name="google_white">#ffffffff</color>
-    <color name="google_grey">#ff3c4043</color>
-    <color name="text_default">#666</color>
-    <color name="text_error">#da4336</color>
-    <color name="selection_handles">#00aadd</color>
-    <color name="search_background">@color/material_dynamic_neutral_variant95</color>
-    <color name="search_textbox">@color/material_dynamic_neutral_variant99</color>
-    <color name="search_texthint">@android:color/system_neutral2_700</color>
-    <color name="search_textColor">@android:color/system_neutral1_900</color>
-    <color name="search_count">@android:color/system_neutral2_700</color>
-    <color name="search_prev_button">@android:color/system_neutral2_700</color>
-    <color name="search_next_button">@android:color/system_neutral2_700</color>
-    <color name="search_close_button">@android:color/system_neutral2_700</color>
-
-</resources>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/res/values/strings.xml b/pdf/pdf-viewer/src/main/res/values/strings.xml
index 906e005..80fb57f 100644
--- a/pdf/pdf-viewer/src/main/res/values/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values/strings.xml
@@ -135,4 +135,16 @@
        load a file, but there was a problem (e.g. device was offline). So we show the user a button with
        this "retry" text to try and fetch the file again. [CHAR LIMIT=20] -->
     <string name="retry_button_text">Retry</string>
+
+    <!-- Exception message when unable to process PDF document -->
+    <string name="pdf_error">Failed to process the PDF document!</string>
+
+    <!-- Exception message when the process was unable to access the file -->
+    <string name="file_error">Failed to open the file. Possible permission issue?</string>
+
+    <!-- Exception message when a page is unable to load in the document -->
+    <string name="page_broken">Page broken for the PDF document</string>
+
+    <!-- Exception message when the PDF document was insufficient file contents -->
+    <string name="needs_more_data">Insufficient data for processing the PDF document</string>
 </resources>
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/PdfViewerFragmentTest.kt b/pdf/pdf-viewer/src/test/java/androidx/pdf/PdfViewerFragmentTest.kt
index eedcc0a..5033091 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/PdfViewerFragmentTest.kt
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/PdfViewerFragmentTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.pdf
 
+import android.os.Bundle
 import androidx.fragment.app.testing.FragmentScenario
-import androidx.fragment.app.testing.launchFragment
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -26,12 +26,17 @@
 
 @RunWith(AndroidJUnit4::class)
 class PdfViewerFragmentTest {
-    private lateinit var scenario: FragmentScenario<PdfViewerFragment>
-    private lateinit var fragment: PdfViewerFragment
+    private lateinit var scenario: FragmentScenario<androidx.pdf.PdfViewerFragment>
+    private lateinit var fragment: androidx.pdf.PdfViewerFragment
 
     @Before
     fun setup() {
-        scenario = launchFragment()
+        scenario =
+            FragmentScenario.Companion.launchInContainer(
+                androidx.pdf.PdfViewerFragment::class.java,
+                Bundle.EMPTY,
+                androidx.appcompat.R.style.Theme_AppCompat
+            )
         scenario.onFragment { fragment = it }
     }
 
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/find/FindInFileViewTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/find/FindInFileViewTest.java
new file mode 100644
index 0000000..118e955
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/find/FindInFileViewTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.find;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+
+import androidx.pdf.viewer.PaginatedView;
+import androidx.pdf.viewer.loader.PdfLoader;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Unit tests for {@link FindInFileView}
+ */
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class FindInFileViewTest extends TestCase {
+    @Mock
+    private PdfLoader mPdfLoader;
+    @Mock
+    private PaginatedView mPaginatedView;
+    @Mock
+    private FloatingActionButton mAnnotationButton;
+    private FindInFileView mFindInFileView;
+    private AutoCloseable mOpenMocks;
+
+    @Before
+    public void setUp() throws Exception {
+        mOpenMocks = MockitoAnnotations.openMocks(this);
+        mFindInFileView = new FindInFileView(ApplicationProvider.getApplicationContext());
+        mFindInFileView.setPdfLoader(mPdfLoader);
+        mFindInFileView.setPaginatedView(mPaginatedView);
+        mFindInFileView.setAnnotationButton(mAnnotationButton);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mOpenMocks.close();
+    }
+
+    @Test
+    public void testSetFindInFileView_visibilityTrue() {
+        doNothing().when(mAnnotationButton).setVisibility(anyInt());
+        mFindInFileView.setFindInFileView(true);
+        assertThat(mFindInFileView.getVisibility()).isEqualTo(VISIBLE);
+    }
+
+    @Test
+    public void testSetFindInFileView_visibilityFalse() {
+        mFindInFileView.setFindInFileView(false);
+        assertThat(mFindInFileView.getVisibility()).isEqualTo(GONE);
+    }
+}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/mocks/MockBitmapParcel.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/mocks/MockBitmapParcel.java
deleted file mode 100644
index 01442a8..0000000
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/mocks/MockBitmapParcel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.mocks;
-
-import android.graphics.Bitmap;
-import android.os.ParcelFileDescriptor;
-
-import androidx.pdf.util.BitmapParcel;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-
-public class MockBitmapParcel extends BitmapParcel {
-
-    private final int mBufferSize;
-
-    public MockBitmapParcel(Bitmap bitmap, int bufferSize) {
-        super(bitmap);
-        this.mBufferSize = bufferSize;
-    }
-
-    @Override
-    protected void receiveBitmap(Bitmap bitmap, ParcelFileDescriptor sourceFd) {
-        try (FileInputStream fis = new FileInputStream(sourceFd.getFileDescriptor())) {
-            FileChannel channel = fis.getChannel();
-
-            ByteBuffer buffer = ByteBuffer.allocateDirect(mBufferSize);
-            while (channel.read(buffer) != -1) {
-                buffer.rewind();
-                bitmap.copyPixelsFromBuffer(buffer);
-            }
-            buffer.rewind();
-            channel.close();
-            sourceFd.close();
-        } catch (IOException ignored) {
-        }
-    }
-}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/select/SelectionActionModeTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/select/SelectionActionModeTest.java
new file mode 100644
index 0000000..a2d9294
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/select/SelectionActionModeTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.select;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.util.ObservableValue;
+import androidx.pdf.util.Observables;
+import androidx.pdf.viewer.PageMosaicView;
+import androidx.pdf.viewer.PageViewFactory;
+import androidx.pdf.viewer.PaginatedView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+public class SelectionActionModeTest {
+
+    @Mock
+    private Context mContext;
+
+    private SelectionActionMode mSelectionActionMode;
+
+    @Mock
+    private SelectionModel<PageSelection> mSelectionModel;
+
+    @Mock
+    private PaginatedView mPaginatedView;
+
+    @Mock
+    private PageMosaicView mPageMosaicView;
+
+    @Mock
+    private PageSelection mPageSelection;
+
+    @Mock
+    Observables.ExposedValue<PageSelection> mSelection;
+
+    @Mock
+    PageViewFactory.PageView mPageView;
+
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.openMocks(this);
+    }
+
+    @Test
+    public void testStartActionMode() {
+        SelectionModel<PageSelection> selectionModel = new SelectionModel<PageSelection>() {
+
+            @NonNull
+            @Override
+            public ObservableValue<PageSelection> selection() {
+                return new ObservableValue<PageSelection>() {
+                    @Nullable
+                    @Override
+                    public PageSelection get() {
+                        return mPageSelection;
+                    }
+
+                    @NonNull
+                    @Override
+                    public Object addObserver(ValueObserver<PageSelection> observer) {
+                        observer.onChange(null, mPageSelection);
+                        return observer;
+                    }
+
+                    @Override
+                    public void removeObserver(@NonNull Object observerKey) {
+
+                    }
+                };
+            }
+            @NonNull
+            @Override
+            public String getText() {
+                return "";
+            }
+
+        };
+
+        selectionModel.setSelection(mPageSelection);
+
+        when(mPaginatedView.getViewAt(anyInt())).thenReturn(mPageView);
+        when(mPageView.getPageView()).thenReturn(mPageMosaicView);
+
+        mSelectionActionMode = new SelectionActionMode(mContext, mPaginatedView, selectionModel);
+
+        verify(mPaginatedView).getViewAt(anyInt());
+        verify(mPageMosaicView).startActionMode(any(), anyInt());
+    }
+
+    @Test
+    public void testDestroyRemoveObserver() {
+        when(mSelectionModel.selection()).thenReturn(mSelection);
+
+        mSelectionActionMode = new SelectionActionMode(mContext, mPaginatedView, mSelectionModel);
+
+        mSelectionActionMode.destroy();
+
+        verify(mSelectionModel.selection()).removeObserver(any());
+
+    }
+
+}
+
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/AnnotationUtilsTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/util/AnnotationUtilsTest.java
index 4b29401..63f84b5 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/AnnotationUtilsTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/util/AnnotationUtilsTest.java
@@ -66,7 +66,7 @@
     }
 
     @Test
-    public void launchAnnotationIntent_nonNullUri_returnsTrue() {
+    public void resolveAnnotationIntent_nonNullUri_returnsTrue() {
         String fileName = "file:://dummyfile.pdf";
         Uri uri = Uri.fromFile(new File(fileName));
 
@@ -85,23 +85,23 @@
                 mockResolveInfo);
         when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
 
-        boolean result = AnnotationUtils.launchAnnotationIntent(mockContext, uri);
+        boolean result = AnnotationUtils.resolveAnnotationIntent(mockContext, uri);
         assertTrue(result);
     }
 
     @Test
-    public void launchAnnotationIntent_nullContext_returnsNullPointerException() {
+    public void resolveAnnotationIntent_nullContext_returnsNullPointerException() {
         assertThrows(NullPointerException.class,
-                () -> AnnotationUtils.launchAnnotationIntent(
+                () -> AnnotationUtils.resolveAnnotationIntent(
                         ApplicationProvider.getApplicationContext(), null));
     }
 
     @Test
-    public void launchAnnotationIntent_nullUri_returnsNullPointerException() {
+    public void resolveAnnotationIntent_nullUri_returnsNullPointerException() {
         String fileName = "file:://dummyfile.pdf";
         Uri uri = Uri.fromFile(new File(fileName));
         assertThrows(NullPointerException.class,
-                () -> AnnotationUtils.launchAnnotationIntent(
+                () -> AnnotationUtils.resolveAnnotationIntent(
                         null, uri));
     }
 }
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/BitmapParcelTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/util/BitmapParcelTest.java
deleted file mode 100644
index 91db95d..0000000
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/BitmapParcelTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.ParcelFileDescriptor;
-
-import androidx.pdf.mocks.MockBitmapParcel;
-import androidx.pdf.test.R;
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Before;
-
-import java.io.ByteArrayInputStream;
-import java.io.OutputStream;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-
-//@SmallTest
-//@RunWith(RobolectricTestRunner.class)
-public class BitmapParcelTest {
-
-    private static final String TAG = "BitmapParcelTest";
-
-    private Bitmap mSourceBitmap;
-
-    @Before
-    public void setUp() {
-        mSourceBitmap =
-                BitmapFactory.decodeResource(
-                        ApplicationProvider.getApplicationContext().getResources(),
-                        R.raw.launcher_pdfviewer);
-    }
-
-    // TODO: Fails in the first execution and then passes. Uncomment when fixed
-//    @Test
-//    public void testBitmapTransferWithOutputFileDescriptor() {
-//        int bufferSize = mSourceBitmap.getWidth() * mSourceBitmap.getHeight() * 256;
-//        testBitmapTransfer(bufferSize);
-//    }
-
-    private void testBitmapTransfer(int bufferSize) {
-        Bitmap bitmap = Bitmap.createBitmap(mSourceBitmap.getWidth(), mSourceBitmap.getHeight(),
-                mSourceBitmap.getConfig());
-        BitmapParcel bitmapParcel = new MockBitmapParcel(bitmap, bufferSize);
-        ParcelFileDescriptor fd = bitmapParcel.openOutputFd();
-        if (fd == null) {
-            fail("fd is null");
-        }
-        sendBytes(fd);
-        bitmapParcel.close();
-        assertThat(validateBitmap(bitmap)).isTrue();
-    }
-
-    private boolean validateBitmap(Bitmap bitmap) {
-        boolean same = bitmap.sameAs(mSourceBitmap);
-        return same;
-    }
-
-    private void sendBytes(OutputStream os) {
-        int numBytes = mSourceBitmap.getByteCount();
-        byte[] bytes = new byte[numBytes];
-        Buffer buffer = ByteBuffer.wrap(bytes);
-        mSourceBitmap.copyPixelsToBuffer(buffer);
-        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
-        Utils.copyAndClose(is, os);
-    }
-
-
-    private void sendBytes(ParcelFileDescriptor fd) {
-        OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
-        int numBytes = mSourceBitmap.getByteCount();
-        byte[] bytes = new byte[numBytes];
-        Buffer buffer = ByteBuffer.wrap(bytes);
-        mSourceBitmap.copyPixelsToBuffer(buffer);
-        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
-        Utils.copyAndClose(is, os);
-    }
-}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/ZoomUtilsTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/util/ZoomUtilsTest.java
index 6f76315..e385bae 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/util/ZoomUtilsTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/util/ZoomUtilsTest.java
@@ -18,6 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
@@ -52,4 +56,146 @@
         assertThat(ZoomUtils.calculateZoomToFit(20, 2, 1, 0)).isEqualTo(1f);
         assertThat(ZoomUtils.calculateZoomToFit(0, 0, 1, 2)).isEqualTo(0f);
     }
+
+    @Test
+    public void toContentCoordinate_nonZeroZoomScroll_returnsNonZeroCoordinate() {
+        // Arrange
+        float dummyZoomViewCoordinate = 10f;
+        float dummyZoom = 2f;
+        int dummyScroll = 5;
+        float expectedContentCoordinate = 7.5f;
+
+        // Act
+        float contentCoordinate = ZoomUtils.toContentCoordinate(dummyZoomViewCoordinate,
+                dummyZoom, dummyScroll);
+
+        // Assert
+        assertThat(contentCoordinate).isEqualTo(expectedContentCoordinate);
+    }
+
+    @Test
+    public void toContentCoordinate_zeroZoom_throwsIllegalArgumentException() {
+        float dummyZoomViewCoordinate = 10f;
+        float zoom = 0;
+        int dummyScroll = 10;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> ZoomUtils.toContentCoordinate(dummyZoomViewCoordinate, zoom,
+                        dummyScroll));
+    }
+
+    @Test
+    public void toZoomViewCoordinate_nonZeroZoomScroll_returnsNonZeroCoordinate() {
+        float dummyContentCoordinate = 10f;
+        float dummyZoom = 2f;
+        int dummyScroll = 5;
+        float expectedZoomViewCoordinate = 15f;
+
+        float zoomViewCoordinate = ZoomUtils.toZoomViewCoordinate(dummyContentCoordinate,
+                dummyZoom, dummyScroll);
+
+        assertThat(zoomViewCoordinate).isEqualTo(expectedZoomViewCoordinate);
+    }
+
+    @Test
+    public void toZoomViewCoordinate_zeroZoom_throwsIllegalArgumentException() {
+        float dummyContentCoordinate = 10f;
+        float zoom = 0;
+        int dummyScroll = 10;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> ZoomUtils.toZoomViewCoordinate(dummyContentCoordinate, zoom,
+                        dummyScroll));
+    }
+
+    @Test
+    public void scrollDeltaNeededForZoomChange_noZoomChange_returnsZeroDelta() {
+        float oldZoom = 1f;
+        float newZoom = 1f;
+        float zoomPivot = 5f;
+        int scroll = 1;
+
+        int delta = ZoomUtils.scrollDeltaNeededForZoomChange(oldZoom, newZoom, zoomPivot,
+                scroll);
+
+        assertEquals(0, delta);
+    }
+
+    @Test
+    public void scrollDeltaNeededForZoomChange_zoomedIn_returnsPositiveDelta() {
+        float oldZoom = 1f;
+        float newZoom = 3f;
+        float zoomPivot = 5f;
+        int scroll = 1;
+
+        int delta = ZoomUtils.scrollDeltaNeededForZoomChange(oldZoom, newZoom, zoomPivot,
+                scroll);
+
+        assertTrue(delta >= 0);
+    }
+
+    @Test
+    public void scrollDeltaNeededForZoomChange_zoomedOut_returnsNegativeDelta() {
+        float oldZoom = 2f;
+        float newZoom = 1f;
+        float zoomPivot = 5f;
+        int scroll = 1;
+
+        int delta = ZoomUtils.scrollDeltaNeededForZoomChange(oldZoom, newZoom, zoomPivot,
+                scroll);
+
+        assertTrue(delta <= 0);
+    }
+
+    @Test
+    public void constrainCoordinate_contentLargerThanViewport_returnsZero() {
+        float zoom = 1f;
+        int scroll = 0;
+        int rawContentDimension = 5;
+        int viewportDimension = 5;
+
+        int adjustedCoordinate = ZoomUtils.constrainCoordinate(zoom, scroll,
+                rawContentDimension, viewportDimension);
+
+        assertEquals(0, adjustedCoordinate);
+    }
+
+    @Test
+    public void constrainCoordinate_scaledContentWithinViewport_returnsNegativeAdjustedCoord() {
+        float zoom = 1f;
+        int scroll = 0;
+        int rawContentDimension = 6;
+        int viewportDimension = 8;
+
+        int adjustedCoordinate = ZoomUtils.constrainCoordinate(zoom, scroll,
+                rawContentDimension, viewportDimension);
+
+        assertTrue(adjustedCoordinate <= 0);
+    }
+
+    @Test
+    public void constrainCoordinate_contentWithLeftDeadMargins_returnsPositiveAdjustedCoord() {
+        float zoom = 1f;
+        int scroll = -2;
+        int rawContentDimension = 6;
+        int viewportDimension = 5;
+
+        int adjustedCoordinate = ZoomUtils.constrainCoordinate(zoom, scroll,
+                rawContentDimension, viewportDimension);
+
+        assertTrue(adjustedCoordinate >= 0);
+    }
+
+    @Test
+    public void constrainCoordinate_contentWithRightDeadMargin_returnsNegativeAdjustedCoord() {
+        float zoom = 1f;
+        int scroll = 2;
+        int rawContentDimension = 6;
+        int viewportDimension = 5;
+
+        int adjustedCoordinate = ZoomUtils.constrainCoordinate(zoom, scroll,
+                rawContentDimension, viewportDimension);
+
+        assertTrue(adjustedCoordinate <= 0);
+    }
 }
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityDisabledFactory.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityDisabledFactory.java
index 0a45c71..7b070fc 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityDisabledFactory.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityDisabledFactory.java
@@ -29,8 +29,9 @@
     public MockPageViewAccessbilityDisabledFactory(@NonNull Context context,
             @NonNull PdfLoader pdfLoader,
             @NonNull PaginatedView paginatedView,
-            @NonNull ZoomView zoomView) {
-        super(context, pdfLoader, paginatedView, zoomView);
+            @NonNull ZoomView zoomView,
+            @NonNull SingleTapHandler singleTapHandler) {
+        super(context, pdfLoader, paginatedView, zoomView, singleTapHandler);
     }
 
     @NonNull
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityEnabledFactory.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityEnabledFactory.java
index 1dd7e58..cb51642 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityEnabledFactory.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/MockPageViewAccessbilityEnabledFactory.java
@@ -31,8 +31,9 @@
             @NonNull Context context,
             @NonNull PdfLoader pdfLoader,
             @NonNull PaginatedView paginatedView,
-            @NonNull ZoomView zoomView) {
-        super(context, pdfLoader, paginatedView, zoomView);
+            @NonNull ZoomView zoomView,
+            @NonNull SingleTapHandler singleTapHandler) {
+        super(context, pdfLoader, paginatedView, zoomView, singleTapHandler);
     }
 
     @NonNull
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageIndicatorTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageIndicatorTest.java
deleted file mode 100644
index d874b39..0000000
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageIndicatorTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.pdf.viewer;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.widget.TextView;
-
-import androidx.pdf.data.Range;
-import androidx.pdf.util.Accessibility;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-/** Tests for {@link PageIndicator}. */
-@SmallTest
-@RunWith(RobolectricTestRunner.class)
-public class PageIndicatorTest {
-    @Mock
-    Accessibility mAccessibility;
-
-    private final Context mContext = ApplicationProvider.getApplicationContext();
-    private final TextView mPageNumberView = new TextView(mContext);
-    private PageIndicator mPageIndicator;
-
-    private AutoCloseable mOpenMocks;
-
-    @Before
-    public void setUp() {
-        mOpenMocks = MockitoAnnotations.openMocks(this);
-        when(mAccessibility.isAccessibilityEnabled(isA(Context.class))).thenReturn(true);
-
-        mPageIndicator = new PageIndicator(mContext, mPageNumberView, mAccessibility);
-        mPageIndicator.setNumPages(10);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mOpenMocks.close();
-    }
-
-
-    @Test
-    public void testSetRangeAndZoom_whenFirstCall_returnsFalse() {
-        assertThat(mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false)).isFalse();
-    }
-
-    @Test
-    public void testSetRangeAndZoom_whenRangeIsTheSame_returnsFalse() {
-        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
-        assertThat(mPageIndicator.setRangeAndZoom(new Range(0, 1), 2.5f, true)).isFalse();
-    }
-
-    @Test
-    public void testSetRangeAndZoom_whenRangeIsTheDifferent_returnsTrue() {
-        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
-        assertThat(mPageIndicator.setRangeAndZoom(new Range(0, 3), 1.5f, false)).isTrue();
-    }
-
-    @Test
-    public void testAnnouncePageChanges() {
-        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
-        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
-        mPageIndicator.setRangeAndZoom(new Range(1, 1), 1.5f, false);
-        verify(mAccessibility).announce(mContext, mPageNumberView, "page 2 of 10");
-
-        mPageIndicator.setRangeAndZoom(new Range(1, 1), 1.5f, false);
-        mPageIndicator.setRangeAndZoom(new Range(1, 1), 1.5f, false);
-        mPageIndicator.setRangeAndZoom(new Range(1, 2), 1.5f, false);
-        verify(mAccessibility).announce(mContext, mPageNumberView, "pages 2 to 3 of 10");
-
-        mPageIndicator.setRangeAndZoom(new Range(1, 2), 1.5f, true);
-    }
-
-    @Test
-    public void testAnnounceZoomChanges() {
-        mPageIndicator.setRangeAndZoom(new Range(0, 0), 1.5f, false);
-        mPageIndicator.setRangeAndZoom(new Range(0, 0), 2.0f, false);
-        mPageIndicator.setRangeAndZoom(new Range(0, 0), 2.5f, false);
-        mPageIndicator.setRangeAndZoom(new Range(0, 0), 3.0f, false);
-        mPageIndicator.setRangeAndZoom(new Range(0, 0), 3.5f, false);
-        mPageIndicator.setRangeAndZoom(new Range(0, 0), 4.0f, false);
-        mPageIndicator.setRangeAndZoom(new Range(0, 0), 4.0f, true);
-        verify(mAccessibility).announce(mContext, mPageNumberView,
-                String.format("%s\n%s", "page 1 of 10", "zoom 400 percent"));
-    }
-}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageMosaicViewTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageMosaicViewTest.java
index 0832ad5..f0b2b622 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageMosaicViewTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageMosaicViewTest.java
@@ -18,11 +18,24 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.PageSelection;
+import androidx.pdf.models.SelectionBoundary;
 import androidx.pdf.util.BitmapRecycler;
 import androidx.pdf.util.MockDrawable;
+import androidx.pdf.util.ObservableValue;
+import androidx.pdf.viewer.loader.PdfLoader;
 import androidx.pdf.widget.MosaicView;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
@@ -33,6 +46,8 @@
 import org.mockito.Mock;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.List;
+
 /** Unit tests for {@link PageMosaicView}. */
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
@@ -43,6 +58,17 @@
     @Mock
     private BitmapRecycler mMockBitmapRecycler;
 
+    @Mock
+    private PdfLoader mMockPdfLoader;
+
+    @Mock
+    private PdfSelectionModel mPdfSelectionModel;
+
+    @Mock
+    private SearchModel mSearchModel;
+
+    PdfSelectionHandles mSelectionHandles = mock(PdfSelectionHandles.class);
+
     @Before
     public void setup() {
 
@@ -53,7 +79,8 @@
         Dimensions dimensions = new Dimensions(800, 800);
         PageMosaicView pageMosaicView = new PageMosaicView(
                 ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
-                mMockBitmapSource, mMockBitmapRecycler);
+                mMockBitmapSource, mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel,
+                mSearchModel, mSelectionHandles);
 
         assertThat(pageMosaicView.getPageText() == null).isTrue();
     }
@@ -63,7 +90,8 @@
         Dimensions dimensions = new Dimensions(800, 800);
         PageMosaicView pageMosaicView = new PageMosaicView(
                 ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
-                mMockBitmapSource, mMockBitmapRecycler);
+                mMockBitmapSource, mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel,
+                mSearchModel, mSelectionHandles);
 
         pageMosaicView.setOverlay(new MockDrawable());
         assertThat(pageMosaicView.hasOverlay()).isTrue();
@@ -74,7 +102,8 @@
         Dimensions dimensions = new Dimensions(800, 800);
         PageMosaicView pageMosaicView = new PageMosaicView(
                 ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
-                mMockBitmapSource, mMockBitmapRecycler);
+                mMockBitmapSource, mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel,
+                mSearchModel, mSelectionHandles);
 
         pageMosaicView.setOverlay(new MockDrawable());
         assertThat(pageMosaicView.hasOverlay()).isTrue();
@@ -88,7 +117,8 @@
         Dimensions dimensions = new Dimensions(800, 800);
         PageMosaicView pageMosaicView = new PageMosaicView(
                 ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
-                mMockBitmapSource, mMockBitmapRecycler) {
+                mMockBitmapSource, mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel,
+                mSearchModel, mSelectionHandles) {
             @Override
             @NonNull
             protected String buildContentDescription(@Nullable String pageText, int pageNum) {
@@ -108,7 +138,8 @@
         Dimensions dimensions = new Dimensions(800, 800);
         PageMosaicView pageMosaicView = new PageMosaicView(
                 ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
-                mMockBitmapSource, mMockBitmapRecycler) {
+                mMockBitmapSource, mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel,
+                mSearchModel, mSelectionHandles) {
             @Override
             @NonNull
             protected String buildContentDescription(@Nullable String pageText, int pageNum) {
@@ -120,4 +151,133 @@
         assertThat(pageMosaicView.getPageText() == null).isTrue();
         assertThat(pageMosaicView.getContentDescription()).isEqualTo(dummyContentDescription);
     }
+
+    @Test
+    public void refreshPageContentAndOverlays_needsPageText_callsLoadPageTextRemovesOverlay() {
+        PdfLoader dummyPdfLoader = mock(PdfLoader.class);
+        PdfSelectionModel dummyPdfSelectionModel = mock(PdfSelectionModel.class);
+        SearchModel dummySearchModel = mock(SearchModel.class);
+        when(dummyPdfSelectionModel.getPage()).thenReturn(1);
+        when(dummySearchModel.query()).thenReturn(new ObservableValue<String>() {
+            @Nullable
+            @Override
+            public String get() {
+                return null;
+            }
+
+            @NonNull
+            @Override
+            public Object addObserver(ValueObserver<String> observer) {
+                return null;
+            }
+
+            @Override
+            public void removeObserver(@NonNull Object observerKey) {
+
+            }
+        });
+
+        Dimensions dimensions = new Dimensions(800, 800);
+        PageMosaicView pageMosaicView = new PageMosaicView(
+                ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
+                mMockBitmapSource, mMockBitmapRecycler, dummyPdfLoader, dummyPdfSelectionModel,
+                dummySearchModel, mSelectionHandles) {
+            @Override
+            public boolean needsPageText() {
+                return true;
+            }
+        };
+
+        pageMosaicView.refreshPageContentAndOverlays();
+
+        verify(dummyPdfLoader).loadPageText(any(Integer.class));
+        verify(dummyPdfLoader).loadPageUrlLinks(any(Integer.class));
+        verify(dummyPdfLoader).loadPageGotoLinks(any(Integer.class));
+        assertFalse(pageMosaicView.hasOverlay());
+    }
+
+    @Test
+    public void refreshPageContentAndOverlays_sameSelectionPage_setsHighlightOverlay() {
+        List<Rect> boundingBoxes = List.of(new Rect(10, 10, 10, 10));
+        PdfLoader dummyPdfLoader = mock(PdfLoader.class);
+        PdfSelectionModel dummyPdfSelectionModel = mock(PdfSelectionModel.class);
+        SearchModel dummySearchModel = mock(SearchModel.class);
+        when(dummyPdfSelectionModel.getPage()).thenReturn(0);
+        when(dummyPdfSelectionModel.selection()).thenReturn(new ObservableValue<PageSelection>() {
+
+            @NonNull
+            @Override
+            public Object addObserver(ValueObserver<PageSelection> observer) {
+                return null;
+            }
+
+            @Override
+            public void removeObserver(@NonNull Object observerKey) {
+
+            }
+
+            @Nullable
+            @Override
+            public PageSelection get() {
+                return new PageSelection(0, mock(SelectionBoundary.class),
+                        mock(SelectionBoundary.class), boundingBoxes, "");
+            }
+        });
+
+        Dimensions dimensions = new Dimensions(800, 800);
+        PageMosaicView pageMosaicView = new PageMosaicView(
+                ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
+                mMockBitmapSource, mMockBitmapRecycler, dummyPdfLoader, dummyPdfSelectionModel,
+                dummySearchModel, mSelectionHandles) {
+            @Override
+            public boolean needsPageText() {
+                return false;
+            }
+        };
+
+        pageMosaicView.refreshPageContentAndOverlays();
+
+        assertTrue(pageMosaicView.hasOverlay());
+    }
+
+    @Test
+    public void refreshPageContentAndOverlays_nonNullSearchQuery_callsSearchPageText() {
+        PdfLoader dummyPdfLoader = mock(PdfLoader.class);
+        PdfSelectionModel dummyPdfSelectionModel = mock(PdfSelectionModel.class);
+        SearchModel dummySearchModel = mock(SearchModel.class);
+        when(dummyPdfSelectionModel.getPage()).thenReturn(1);
+        when(dummySearchModel.query()).thenReturn(new ObservableValue<String>() {
+            @Nullable
+            @Override
+            public String get() {
+                return "placeholder";
+            }
+
+            @NonNull
+            @Override
+            public Object addObserver(ValueObserver<String> observer) {
+                return null;
+            }
+
+            @Override
+            public void removeObserver(@NonNull Object observerKey) {
+
+            }
+        });
+
+        Dimensions dimensions = new Dimensions(800, 800);
+        PageMosaicView pageMosaicView = new PageMosaicView(
+                ApplicationProvider.getApplicationContext(), /* pageNum= */ 0, dimensions,
+                mMockBitmapSource, mMockBitmapRecycler, dummyPdfLoader, dummyPdfSelectionModel,
+                dummySearchModel, mSelectionHandles) {
+            @Override
+            public boolean needsPageText() {
+                return false;
+            }
+        };
+
+        pageMosaicView.refreshPageContentAndOverlays();
+
+        verify(dummyPdfLoader).searchPageText(any(Integer.class), any(String.class));
+    }
 }
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageRangeHandlerTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageRangeHandlerTest.java
new file mode 100644
index 0000000..da59d113
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageRangeHandlerTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import androidx.pdf.data.Range;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.RobolectricTestRunner;
+
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class PageRangeHandlerTest {
+    @Test
+    public void getVisiblePage_nullVisiblePageRange_returnsZero() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        int expectedResult = 0;
+
+        int result = adapter.getVisiblePage();
+
+        assertThat(result).isEqualTo(expectedResult);
+    }
+
+    @Test
+    public void getVisiblePage_nonNullRange_returnsMidOfRange() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        Range dummyVisiblePageRange = new Range(1, 4);
+        int expectedResult = 2;
+
+        adapter.setVisiblePages(dummyVisiblePageRange);
+        int result = adapter.getVisiblePage();
+
+        assertThat(result).isEqualTo(expectedResult);
+    }
+
+    @Test
+    public void adjustMaxPageToUpperVisibleRange_nullVisiblePageRange_noChangeInMaxPage() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        int expectedResult = -1;
+
+        adapter.adjustMaxPageToUpperVisibleRange();
+        int result = adapter.getMaxPage();
+
+        assertThat(result).isEqualTo(expectedResult);
+    }
+
+    @Test
+    public void adjustMaxPageToUpperVisibleRange_lowerUpperVisibleRange_noChangeInMaxPage() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        Range dummyVisiblePageRange = new Range(1, 4);
+        int dummyMaxPage = 10;
+
+        adapter.setVisiblePages(dummyVisiblePageRange);
+        adapter.setMaxPage(dummyMaxPage);
+        adapter.adjustMaxPageToUpperVisibleRange();
+        int result = adapter.getMaxPage();
+
+        assertThat(result).isEqualTo(dummyMaxPage);
+    }
+
+    @Test
+    public void adjustMaxPageToUpperVisibleRange_higherUpperVisibleRange_updatedMaxPage() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        Range dummyVisiblePageRange = new Range(1, 10);
+        int dummyMaxPage = 4;
+        int expectedResult = 10;
+
+        adapter.setVisiblePages(dummyVisiblePageRange);
+        adapter.setMaxPage(dummyMaxPage);
+        adapter.adjustMaxPageToUpperVisibleRange();
+        int result = adapter.getMaxPage();
+
+        assertThat(result).isEqualTo(expectedResult);
+    }
+
+    @Test
+    public void computeVisibleRange_zeroZoom_throwsIllegalArgumentException() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        int dummyScrollY = 10;
+        float dummyZoom = 0;
+        int dummyViewHeight = 100;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> adapter.computeVisibleRange(dummyScrollY, dummyZoom, dummyViewHeight, true));
+    }
+
+    @Test
+    public void computeVisibleRange_overlappingRange_returnsOverlappedRange() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        when(mockPaginationModel.getPagesInWindow(any(Range.class), any(Boolean.class))).thenAnswer(
+                new Answer<Range>() {
+                    private final Range mDefaultRange = new Range(3, 5);
+
+                    @Override
+                    public Range answer(InvocationOnMock invocation) throws Throwable {
+                        Range intervalPx = invocation.getArgument(0);
+                        boolean includePartial = invocation.getArgument(1);
+
+                        // Condition has been added so that we can capture a change to the value
+                        if (includePartial) {
+                            return new Range(
+                                    Math.min(intervalPx.getFirst(), mDefaultRange.getFirst()),
+                                    Math.max(intervalPx.getLast(), mDefaultRange.getLast())
+                            );
+                        }
+                        return mDefaultRange;
+
+                    }
+                });
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        int dummyScrollY = 2;
+        float dummyZoom = 2;
+        int dummyViewHeight = 10;
+        Range expectedResult = new Range(1, 6);
+
+        Range result = adapter.computeVisibleRange(dummyScrollY, dummyZoom, dummyViewHeight, true);
+
+        assertEquals(result, expectedResult);
+    }
+
+    @Test
+    public void refreshVisiblePageRange_overlappingRange_updatesVisibleRangeWithOverlappedRange() {
+        PaginationModel mockPaginationModel = Mockito.mock(PaginationModel.class);
+        when(mockPaginationModel.getPagesInWindow(any(Range.class), any(Boolean.class))).thenAnswer(
+                new Answer<Range>() {
+                    private final Range mDefaultRange = new Range(3, 5);
+
+                    @Override
+                    public Range answer(InvocationOnMock invocation) throws Throwable {
+                        Range intervalPx = invocation.getArgument(0);
+                        boolean includePartial = invocation.getArgument(1);
+
+                        // Condition has been added so that we can capture a change to the value
+                        if (includePartial) {
+                            return new Range(
+                                    Math.min(intervalPx.getFirst(), mDefaultRange.getFirst()),
+                                    Math.max(intervalPx.getLast(), mDefaultRange.getLast())
+                            );
+                        }
+                        return mDefaultRange;
+
+                    }
+                });
+        PageRangeHandler adapter = new PageRangeHandler(mockPaginationModel);
+        int dummyScrollY = 2;
+        float dummyZoom = 2;
+        int dummyViewHeight = 10;
+        Range expectedResult = new Range(1, 6);
+
+        adapter.refreshVisiblePageRange(dummyScrollY, dummyZoom, dummyViewHeight);
+        Range result = adapter.getVisiblePages();
+
+        assertEquals(result, expectedResult);
+    }
+}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageSelectionValueObserverTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageSelectionValueObserverTest.java
new file mode 100644
index 0000000..8a97c4b
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageSelectionValueObserverTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import androidx.pdf.data.Range;
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.PageSelection;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class PageSelectionValueObserverTest {
+    private final PaginatedView mMockPaginatedView = mock(PaginatedView.class);
+    private final PaginationModel mMockPaginationModel = mock(PaginationModel.class);
+    private final PageViewFactory mMockPageViewFactory = mock(PageViewFactory.class);
+    private final PageSelection mMockOldPageSelection = mock(PageSelection.class);
+    private final PageSelection mMockNewPageSelection = mock(PageSelection.class);
+    private final PageViewFactory.PageView mMockOldPageView = mock(PageViewFactory.PageView.class);
+    private final PageViewFactory.PageView mMockNewPageView = mock(PageViewFactory.PageView.class);
+    private final PageMosaicView mMockOldPageMosaicView = mock(PageMosaicView.class);
+    private final PageMosaicView mMockNewPageMosaicView = mock(PageMosaicView.class);
+    private final PageRangeHandler mMockPageRangeHandler = mock(PageRangeHandler.class);
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+
+    @Test
+    public void onChange_setOverlay() {
+        when(mMockOldPageSelection.getPage()).thenReturn(1);
+        when(mMockNewPageSelection.getPage()).thenReturn(2);
+        when(mMockPaginationModel.getSize()).thenReturn(3);
+        when(mMockPaginationModel.getPageSize(2)).thenReturn(new Dimensions(100, 100));
+        when(mMockPaginatedView.getViewAt(1)).thenReturn(mMockOldPageView);
+        when(mMockPaginatedView.getViewAt(2)).thenReturn(mMockNewPageView);
+        when(mMockOldPageView.getPageView()).thenReturn(mMockOldPageMosaicView);
+        when(mMockPageViewFactory.getOrCreatePageView(2, 2, new Dimensions(100, 100))).thenReturn(
+                mMockNewPageMosaicView);
+        when(mMockPaginatedView.getPageRangeHandler()).thenReturn(mMockPageRangeHandler);
+        when(mMockPageRangeHandler.getVisiblePages()).thenReturn(new Range(1, 2));
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        displayMetrics.density = 1f;
+        mContext.getResources().getDisplayMetrics().setTo(displayMetrics);
+        PdfViewer.setScreenForTest(mContext);
+
+        PageSelectionValueObserver pageSelectionValueObserver =
+                new PageSelectionValueObserver(mMockPaginatedView, mMockPaginationModel,
+                        mMockPageViewFactory, mContext);
+        pageSelectionValueObserver.onChange(mMockOldPageSelection, mMockNewPageSelection);
+
+        verify(mMockOldPageMosaicView).setOverlay(null);
+        verify(mMockNewPageMosaicView).setOverlay(any());
+    }
+}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageViewFactoryTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageViewFactoryTest.java
index d14175d..c3869d4 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageViewFactoryTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PageViewFactoryTest.java
@@ -50,8 +50,7 @@
 
     private final ZoomView mMockZoomView = mock(ZoomView.class);
 
-    private final PdfViewer.PageTouchHandler mMockPageTouchHandler = mock(
-            PdfViewer.PageTouchHandler.class);
+    private final SingleTapHandler mMockSingleTapHandler = mock(SingleTapHandler.class);
 
     @Before
     public void setup() {
@@ -69,12 +68,11 @@
                 PageViewFactory.PageView.class);
         PageViewFactory mockPageViewFactory = new MockPageViewAccessbilityDisabledFactory(
                 ApplicationProvider.getApplicationContext(), mMockPdfLoader, mMockPaginatedView,
-                mMockZoomView
+                mMockZoomView, mMockSingleTapHandler
         );
 
         // Act
-        mockPageViewFactory.getOrCreatePageView(0, dummyPageElevation, mockDimensions,
-                mMockPageTouchHandler);
+        mockPageViewFactory.getOrCreatePageView(0, dummyPageElevation, mockDimensions);
 
         // Assert
         verify(mMockPaginatedView).addView(pageViewArgumentCaptor.capture());
@@ -101,12 +99,11 @@
                 PageViewFactory.PageView.class);
         PageViewFactory mockPageViewFactory = new MockPageViewAccessbilityEnabledFactory(
                 ApplicationProvider.getApplicationContext(), mMockPdfLoader, mMockPaginatedView,
-                mMockZoomView
+                mMockZoomView, mMockSingleTapHandler
         );
 
         // Act
-        mockPageViewFactory.getOrCreatePageView(0, dummyPageElevation, mockDimensions,
-                mMockPageTouchHandler);
+        mockPageViewFactory.getOrCreatePageView(0, dummyPageElevation, mockDimensions);
 
         // Assert
         verify(mMockPaginatedView).addView(pageViewArgumentCaptor.capture());
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java
index a7f335d..d3afdb3 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginatedViewTest.java
@@ -23,6 +23,7 @@
 import androidx.pdf.models.Dimensions;
 import androidx.pdf.util.BitmapRecycler;
 import androidx.pdf.viewer.PageViewFactory.PageView;
+import androidx.pdf.viewer.loader.PdfLoader;
 import androidx.pdf.widget.MosaicView.BitmapSource;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
@@ -52,6 +53,14 @@
     BitmapSource mMockBitmapSource;
     @Mock
     BitmapRecycler mMockBitmapRecycler;
+    @Mock
+    PdfLoader mMockPdfLoader;
+    @Mock
+    PdfSelectionModel mPdfSelectionModel;
+    @Mock
+    SearchModel mSearchModel;
+    @Mock
+    PdfSelectionHandles mSelectionHandles;
 
     PageView mTestPageView0;
     PageView mTestPageView1;
@@ -67,13 +76,15 @@
         PdfViewer.setScreenForTest(mContext);
         // Setting uninitialized model.
         mPaginatedView = new PaginatedView(mContext);
-        mPaginationModel = new PaginationModel();
+        mPaginationModel = new PaginationModel(mContext);
 
         mPaginatedView.setModel(mPaginationModel);
         mTestPageView0 = new PageMosaicView(mContext, 0, mDimensions, mMockBitmapSource,
-                mMockBitmapRecycler);
+                mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel, mSearchModel,
+                mSelectionHandles);
         mTestPageView1 = new PageMosaicView(mContext, 1, mDimensions, mMockBitmapSource,
-                mMockBitmapRecycler);
+                mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel, mSearchModel,
+                mSelectionHandles);
     }
 
     @After
@@ -98,7 +109,8 @@
 
         mPaginationModel.addPage(2, mDimensions);
         PageView testPageView3 = new PageMosaicView(mContext, 2, mDimensions, mMockBitmapSource,
-                mMockBitmapRecycler);
+                mMockBitmapRecycler, mMockPdfLoader, mPdfSelectionModel, mSearchModel,
+                mSelectionHandles);
         mPaginatedView.addView(testPageView3);
 
         mPaginatedView.removeViewAt(1);
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
index c27a4f4..a813bb4 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
@@ -47,7 +47,7 @@
     public void init() {
         mContext = ApplicationProvider.getApplicationContext();
         PdfViewer.setScreenForTest(mContext);
-        mPaginationModel = new PaginationModel();
+        mPaginationModel = new PaginationModel(mContext);
     }
 
     /** Test that the model can be initialized to a positive number of pages. */
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SearchQueryObserverTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SearchQueryObserverTest.java
new file mode 100644
index 0000000..d726676
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SearchQueryObserverTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class SearchQueryObserverTest {
+
+    private final PaginatedView mMockPaginatedView = mock(PaginatedView.class);
+
+    @Test
+    public void onChange() {
+        SearchQueryObserver searchQueryObserver = new SearchQueryObserver(mMockPaginatedView);
+        searchQueryObserver.onChange("oldQuery", "newQuery");
+        verify(mMockPaginatedView).clearAllOverlays();
+    }
+}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SelectedMatchValueObserverTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SelectedMatchValueObserverTest.java
new file mode 100644
index 0000000..7ac5b06
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/SelectedMatchValueObserverTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+
+import androidx.pdf.models.Dimensions;
+import androidx.pdf.models.MatchRects;
+import androidx.pdf.widget.ZoomView;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+
+
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class SelectedMatchValueObserverTest {
+    private final PaginatedView mMockPaginatedView = mock(PaginatedView.class);
+    private final PaginationModel mMockPaginationModel = mock(PaginationModel.class);
+    private final PageViewFactory mMockPageViewFactory = mock(PageViewFactory.class);
+    private final ZoomView mMockZoomView = mock(ZoomView.class);
+    private final LayoutHandler mMockLayoutHandler = mock(LayoutHandler.class);
+    private final SelectedMatch mMockOldSelection = mock(SelectedMatch.class);
+    private final SelectedMatch mMockNewSelection = mock(SelectedMatch.class);
+    private final PageViewFactory.PageView mMockOldPageView = mock(PageViewFactory.PageView.class);
+    private final PageViewFactory.PageView mMockNewPageView = mock(PageViewFactory.PageView.class);
+    private final PageMosaicView mMockOldPageMosaicView = mock(PageMosaicView.class);
+    private final PageMosaicView mMockNewPageMosaicView = mock(PageMosaicView.class);
+    private final MatchRects mMockMatchRects = mock(MatchRects.class);
+    private final PdfHighlightOverlay mMockPdfHighlightOverlay = mock(PdfHighlightOverlay.class);
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Test
+    public void onChange_setOverlay() {
+        when(mMockOldSelection.getPage()).thenReturn(1);
+        when(mMockNewSelection.getPage()).thenReturn(2);
+        when(mMockPaginationModel.getSize()).thenReturn(3);
+        when(mMockPaginatedView.getViewAt(1)).thenReturn(mMockOldPageView);
+        when(mMockPaginatedView.getViewAt(2)).thenReturn(mMockNewPageView);
+        when(mMockPaginationModel.getPageSize(2)).thenReturn(new Dimensions(100, 100));
+        when(mMockOldPageView.getPageView()).thenReturn(mMockOldPageMosaicView);
+        when(mMockPageViewFactory.getOrCreatePageView(2, 2, new Dimensions(100, 100))).thenReturn(
+                mMockNewPageMosaicView);
+        when(mMockOldSelection.getPageMatches()).thenReturn(
+                new MatchRects(new ArrayList<>(), new ArrayList<>(), new ArrayList<>()));
+        when(mMockNewSelection.getPageMatches()).thenReturn(mMockMatchRects);
+        when(mMockNewSelection.getSelected()).thenReturn(1);
+        when(mMockNewSelection.getPage()).thenReturn(2);
+        when(mMockMatchRects.getFirstRect(1)).thenReturn(new Rect(0, 0, 0, 0));
+        when(mMockPaginationModel.getLookAtX(2, 0)).thenReturn(0);
+        when(mMockPaginationModel.getLookAtY(2, 0)).thenReturn(0);
+        when(mMockNewSelection.getOverlay()).thenReturn(mMockPdfHighlightOverlay);
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        displayMetrics.density = 1f;
+        mContext.getResources().getDisplayMetrics().setTo(displayMetrics);
+        PdfViewer.setScreenForTest(mContext);
+
+        SelectedMatchValueObserver selectedMatchValueObserver = new SelectedMatchValueObserver(
+                mMockPaginatedView, mMockPaginationModel, mMockPageViewFactory, mMockZoomView,
+                mMockLayoutHandler, mContext);
+        selectedMatchValueObserver.onChange(mMockOldSelection, mMockNewSelection);
+
+        verify(mMockZoomView).centerAt(0, 0);
+        verify(mMockNewPageMosaicView).setOverlay(mMockPdfHighlightOverlay);
+    }
+}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/ZoomScrollValueObserverTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/ZoomScrollValueObserverTest.java
new file mode 100644
index 0000000..014ae08
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/ZoomScrollValueObserverTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewer;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+import androidx.pdf.ViewState;
+import androidx.pdf.data.Range;
+import androidx.pdf.find.FindInFileView;
+import androidx.pdf.select.SelectionActionMode;
+import androidx.pdf.util.ObservableValue;
+import androidx.pdf.util.Observables;
+import androidx.pdf.widget.FastScrollView;
+import androidx.pdf.widget.ZoomView;
+import androidx.test.filters.SmallTest;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class ZoomScrollValueObserverTest {
+    private static final ObservableValue<ViewState>
+            VIEW_STATE_EXPOSED_VALUE =
+            Observables.newExposedValueWithInitialValue(ViewState.NO_VIEW);
+    private static final Rect RECT = new Rect(0, 0, 100, 100);
+    private static final ZoomView.ZoomScroll OLD_POSITION = new ZoomView.ZoomScroll(1.0f, 0, 0,
+            false);
+    private static final Range PAGE_RANGE = new Range(0, 100);
+    private static final int ANIMATION_DELAY_MILLIS = 200;
+
+    private final PaginatedView mMockPaginatedView = mock(PaginatedView.class);
+    private final ZoomView mMockZoomView = mock(ZoomView.class);
+    private final PaginationModel mMockPaginationModel = mock(PaginationModel.class);
+    private final LayoutHandler mMockLayoutHandler = mock(LayoutHandler.class);
+    private final FloatingActionButton mMockAnnotationButton = mock(FloatingActionButton.class);
+    private final FindInFileView mMockFindInFileView = mock(FindInFileView.class);
+    private final FastScrollView mMockFastScrollView = mock(FastScrollView.class);
+    private final PageRangeHandler mPageRangeHandler = mock(PageRangeHandler.class);
+    private final SelectionActionMode mMockSelectionActionMode = mock(SelectionActionMode.class);
+
+    private boolean mIsAnnotationIntentResolvable;
+    private ZoomView.ZoomScroll mNewPosition;
+    private ZoomView.ZoomScroll mOldPosition;
+
+
+    @Before
+    public void setUp() {
+        mIsAnnotationIntentResolvable = false;
+        mNewPosition = new ZoomView.ZoomScroll(1.0f, 0, 0, false);
+
+        when(mMockPaginatedView.getPageRangeHandler()).thenReturn(mPageRangeHandler);
+        when(mMockPaginatedView.getPaginationModel()).thenReturn(mMockPaginationModel);
+        when(mMockPaginationModel.isInitialized()).thenReturn(true);
+        when(mMockZoomView.getHeight()).thenReturn(100);
+        when(mPageRangeHandler.computeVisibleRange(0, 1.0f, 100, false)).thenReturn(PAGE_RANGE);
+        when(mMockZoomView.getStableZoom()).thenReturn(1.0f);
+        when(mMockZoomView.getVisibleAreaInContentCoords()).thenReturn(RECT);
+        when(mMockPaginatedView.createPageViewsForVisiblePageRange()).thenReturn(false);
+        when(mPageRangeHandler.getVisiblePages()).thenReturn(PAGE_RANGE);
+        when(mMockPaginationModel.isInitialized()).thenReturn(true);
+        when(mMockPaginationModel.getSize()).thenReturn(1);
+    }
+
+    @Test
+    public void onChange_loadPageAssets_stablePosition() {
+        mNewPosition = new ZoomView.ZoomScroll(1.0f, 0, 0, true);
+
+        ZoomScrollValueObserver zoomScrollValueObserver = new ZoomScrollValueObserver(mMockZoomView,
+                mMockPaginatedView, mMockLayoutHandler, mMockAnnotationButton,
+                mMockFindInFileView, mIsAnnotationIntentResolvable, mMockSelectionActionMode,
+                VIEW_STATE_EXPOSED_VALUE);
+        zoomScrollValueObserver.onChange(OLD_POSITION, mNewPosition);
+
+        verify(mMockZoomView).setStableZoom(1.0f);
+        verify(mMockPaginationModel).setViewArea(RECT);
+        verify(mMockPaginatedView).refreshPageRangeInVisibleArea(mNewPosition, 100);
+        verify(mMockPaginatedView).handleGonePages(false);
+        verify(mMockPaginatedView).loadInvisibleNearPageRange(1.0f);
+        verify(mMockPaginatedView).refreshVisiblePages(false,
+                ViewState.NO_VIEW, 1.0f);
+        verify(mMockPaginatedView).handleGonePages(true);
+        verify(mMockLayoutHandler).maybeLayoutPages(100);
+    }
+
+    @Test
+    public void onChange_loadPageAssets_stableZoom() {
+        mNewPosition = new ZoomView.ZoomScroll(2.0f, 0, 0, false);
+        when(mMockZoomView.getStableZoom()).thenReturn(2.0f);
+
+        ZoomScrollValueObserver zoomScrollValueObserver = new ZoomScrollValueObserver(mMockZoomView,
+                mMockPaginatedView, mMockLayoutHandler, mMockAnnotationButton, mMockFindInFileView,
+                mIsAnnotationIntentResolvable, mMockSelectionActionMode,
+                VIEW_STATE_EXPOSED_VALUE);
+        zoomScrollValueObserver.onChange(OLD_POSITION, mNewPosition);
+
+        verify(mMockPaginatedView).refreshVisibleTiles(false, ViewState.NO_VIEW);
+    }
+
+    @Test
+    public void onChange_showAnnotationButton() {
+        mIsAnnotationIntentResolvable = true;
+        when(mMockAnnotationButton.getVisibility()).thenReturn(View.GONE);
+        when(mMockFindInFileView.getVisibility()).thenReturn(View.GONE);
+
+        ZoomScrollValueObserver zoomScrollValueObserver = new ZoomScrollValueObserver(mMockZoomView,
+                mMockPaginatedView, mMockLayoutHandler, mMockAnnotationButton,
+                mMockFindInFileView,
+                mIsAnnotationIntentResolvable, mMockSelectionActionMode,
+                VIEW_STATE_EXPOSED_VALUE);
+        zoomScrollValueObserver.onChange(OLD_POSITION, mNewPosition);
+
+        verify(mMockAnnotationButton).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void onChange_hideAnnotationButton() {
+        mIsAnnotationIntentResolvable = true;
+        mNewPosition = new ZoomView.ZoomScroll(1.0f, 0, 10, false);
+        mOldPosition = new ZoomView.ZoomScroll(1.0f, 0, 10, false);
+        when(mMockAnnotationButton.getVisibility()).thenReturn(View.VISIBLE);
+
+        ZoomScrollValueObserver zoomScrollValueObserver = new ZoomScrollValueObserver(mMockZoomView,
+                mMockPaginatedView, mMockLayoutHandler, mMockAnnotationButton,
+                mMockFindInFileView,
+                mIsAnnotationIntentResolvable, mMockSelectionActionMode,
+                VIEW_STATE_EXPOSED_VALUE);
+        zoomScrollValueObserver.onChange(mOldPosition, mNewPosition);
+//        TODO: Remove this hardcode dependency.
+        new Handler(Looper.getMainLooper()).postDelayed(() -> {
+            verify(mMockAnnotationButton).setVisibility(View.GONE);
+        }, ANIMATION_DELAY_MILLIS);
+    }
+
+}
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java
index 8fe7eba..585e06f 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/MosaicViewTest.java
@@ -71,7 +71,7 @@
 public class MosaicViewTest {
 
     @Mock
-    private BitmapSource mCallback;
+    private BitmapSource mBitmapSource;
     @Mock
     private BitmapRecycler mMockRecycler;
 
@@ -110,12 +110,12 @@
         Dimensions dimensions = new Dimensions(300, 400);
         Dimensions cappedDimensions = new Dimensions(600, 800);
 
-        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mCallback);
+        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mBitmapSource);
         mView.setViewArea(0, 0, 67, 89);  // Arbitrary value.
         mView.requestDrawAtZoom(2.0f);
 
         ArgumentCaptor<Dimensions> requestedSize = ArgumentCaptor.forClass(Dimensions.class);
-        verify(mCallback, only()).requestPageBitmap(requestedSize.capture(), eq(false));
+        verify(mBitmapSource, only()).requestPageBitmap(requestedSize.capture(), eq(false));
         assertThat(requestedSize.getValue()).isEqualTo(cappedDimensions);
     }
 
@@ -124,13 +124,13 @@
         Dimensions dimensions = new Dimensions(300, 400);
         Rect viewArea = new Rect(0, 0, 150, 200);
 
-        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mCallback);
+        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mBitmapSource);
         mView.setViewArea(viewArea);
         mView.requestDrawAtZoom(20f);
 
         ArgumentCaptor<Dimensions> requestedSize = ArgumentCaptor.forClass(Dimensions.class);
-        verify(mCallback).requestPageBitmap(requestedSize.capture(), eq(true));
-        verify(mCallback, times(1)).requestNewTiles(isA(Dimensions.class),
+        verify(mBitmapSource).requestPageBitmap(requestedSize.capture(), eq(true));
+        verify(mBitmapSource, times(1)).requestNewTiles(isA(Dimensions.class),
                 mRequestedTiles.capture());
         assertThat(requestedSize.getValue().getWidth()).isAtMost(mMaxTileSize / 2);
         assertThat(requestedSize.getValue().getHeight()).isAtMost(mMaxTileSize / 2);
@@ -142,7 +142,7 @@
     public void testMoreTiles() {
         float zoom = 20f;
         Dimensions dimensions = new Dimensions(300, 400);
-        mView.init(dimensions, mMockRecycler, mCallback);
+        mView.init(dimensions, mMockRecycler, mBitmapSource);
 
         // 1. Request 4 tiles.
         // At a zoom of 20, to cover an area of 50x50 units we will need to cover a 1000x1000 px
@@ -152,7 +152,7 @@
         mView.setViewArea(viewArea);
         mView.requestDrawAtZoom(zoom);
 
-        verify(mCallback, times(1)).requestNewTiles(isA(Dimensions.class),
+        verify(mBitmapSource, times(1)).requestNewTiles(isA(Dimensions.class),
                 mRequestedTiles.capture());
 
         Set<Integer> tiles = new HashSet<Integer>();
@@ -160,7 +160,7 @@
         assertThat(tiles.isEmpty()).isFalse();
         checkCoverage(viewArea, mRequestedTiles.getValue());
         fillTiles(mView, mRequestedTiles.getValue());
-        reset(mCallback);
+        reset(mBitmapSource);
 
         // 2. Move, request no change.
         // Moving 5 units across means we need an extra 100 pixels across - but this is still
@@ -169,9 +169,9 @@
         viewArea.offset(0, 5);
         mView.setViewArea(viewArea);
         mView.requestTiles();
-        verifyNoMoreInteractions(mCallback);
+        verifyNoMoreInteractions(mBitmapSource);
         verifyNoMoreInteractions(mMockRecycler);
-        reset(mCallback);
+        reset(mBitmapSource);
 
         // 3. Move again, request 2 new tiles
         // Moving 40 units across means we need an extra 800 pixels - this is exactly one tile wide.
@@ -180,12 +180,13 @@
         mView.setViewArea(viewArea);
         mView.requestTiles();
 
-        verify(mCallback, only()).requestNewTiles(isA(Dimensions.class), mRequestedTiles.capture());
+        verify(mBitmapSource, only()).requestNewTiles(isA(Dimensions.class),
+                mRequestedTiles.capture());
         assertThat(Iterables.size(mRequestedTiles.getValue())).isEqualTo(2);
         Iterables.addAll(tiles, Iterables.transform(mRequestedTiles.getValue(), GET_INDEX));
         assertThat(tiles.size()).isEqualTo(6);
         verifyNoMoreInteractions(mMockRecycler);
-        reset(mCallback);
+        reset(mBitmapSource);
         fillTiles(mView, mRequestedTiles.getValue());
 
         // 4. Move more, request & discard tiles.
@@ -195,7 +196,7 @@
         viewArea.offset(0, 40);
         mView.setViewArea(viewArea);
         mView.requestTiles();
-        verify(mCallback, times(1)).requestNewTiles(isA(Dimensions.class),
+        verify(mBitmapSource, times(1)).requestNewTiles(isA(Dimensions.class),
                 mRequestedTiles.capture());
         assertThat(Iterables.size(mRequestedTiles.getValue())).isEqualTo(2);
         // Verify 2 tiles were discarded.
@@ -206,13 +207,13 @@
     public void testSetTileOutOfViewAreaAndCancellation() {
         float zoom = 20f;
         Dimensions dimensions = new Dimensions(300, 400);
-        mView.init(dimensions, mMockRecycler, mCallback);
+        mView.init(dimensions, mMockRecycler, mBitmapSource);
 
         Rect viewArea = new Rect(0, 0, 50, 50);
         mView.setViewArea(viewArea);
         mView.requestDrawAtZoom(zoom);
 
-        verify(mCallback, times(1)).requestNewTiles(isA(Dimensions.class),
+        verify(mBitmapSource, times(1)).requestNewTiles(isA(Dimensions.class),
                 mRequestedTiles.capture());
 
         Set<Integer> tiles = new HashSet<Integer>();
@@ -224,7 +225,7 @@
         Rect newViewArea = new Rect(100, 100, 150, 150);
         mView.setViewArea(newViewArea);
         mView.requestTiles();
-        verify(mCallback, times(1)).cancelTiles(mCancelledTiles.capture());
+        verify(mBitmapSource, times(1)).cancelTiles(mCancelledTiles.capture());
         assertThat(Iterables.size(mCancelledTiles.getValue())).isEqualTo(tiles.size());
         verifyNoMoreInteractions(mMockRecycler);
 
@@ -237,25 +238,25 @@
     public void testCancellationOnNewTileBoard() {
         float zoom = 20f;
         Dimensions dimensions = new Dimensions(300, 400);
-        mView.init(dimensions, mMockRecycler, mCallback);
+        mView.init(dimensions, mMockRecycler, mBitmapSource);
 
         Rect viewArea = new Rect(0, 0, 50, 50);
         mView.setViewArea(viewArea);
         mView.requestDrawAtZoom(zoom);
 
-        verify(mCallback, times(1)).requestNewTiles(isA(Dimensions.class),
+        verify(mBitmapSource, times(1)).requestNewTiles(isA(Dimensions.class),
                 mRequestedTiles.capture());
 
         Set<Integer> tiles = new HashSet<Integer>();
         Iterables.addAll(tiles, Iterables.transform(mRequestedTiles.getValue(), GET_INDEX));
         assertThat(tiles.isEmpty()).isFalse();
         checkCoverage(viewArea, mRequestedTiles.getValue());
-        reset(mCallback);
+        reset(mBitmapSource);
 
         // Request draw at a different zoom, which will make the existing tile board stale. Tile
         // requests for current tile board are still pending and will be stale.
         mView.requestDrawAtZoom(10f);
-        verify(mCallback, times(1)).cancelTiles(mCancelledTiles.capture());
+        verify(mBitmapSource, times(1)).cancelTiles(mCancelledTiles.capture());
         assertThat(Iterables.size(mCancelledTiles.getValue())).isEqualTo(tiles.size());
     }
 
@@ -278,7 +279,7 @@
     public void requestRedrawAreas_pageBitmapOnly() {
         Dimensions dimensions = new Dimensions(300, 400);
 
-        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mCallback);
+        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mBitmapSource);
         mView.setViewArea(0, 0, 50, 50);
         mView.requestDrawAtZoom(2.0f); // First drawing here.
 
@@ -292,7 +293,7 @@
         mView.requestRedrawAreas(ImmutableList.of(invalidRect)); // Second drawing here.
 
         ArgumentCaptor<Dimensions> bitmapSizeArgCaptor = ArgumentCaptor.forClass(Dimensions.class);
-        verify(mCallback, times(2)).requestPageBitmap(bitmapSizeArgCaptor.capture(), eq(false));
+        verify(mBitmapSource, times(2)).requestPageBitmap(bitmapSizeArgCaptor.capture(), eq(false));
 
         // Our scaled bitmap size is below the maximum size so will not be changed.
         Dimensions scaledDimensions = new Dimensions(600, 800);
@@ -301,7 +302,7 @@
         assertThat(bitmapSizeArgCaptor.getAllValues().get(1)).isEqualTo(scaledDimensions);
 
         // Confirm we never requested tiles.
-        verify(mCallback, never()).requestNewTiles(any(), any());
+        verify(mBitmapSource, never()).requestNewTiles(any(), any());
     }
 
     @Test
@@ -309,7 +310,7 @@
         Dimensions dimensions = new Dimensions(2000, 4000);
         Rect viewArea = new Rect(0, 0, 750, 750);
 
-        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mCallback);
+        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mBitmapSource);
         mView.setViewArea(viewArea);
         mView.requestDrawAtZoom(2f); // First drawing here.
 
@@ -328,7 +329,8 @@
         // redraw.
         ArgumentCaptor<Dimensions> pageBitmapDimensCaptor = ArgumentCaptor.forClass(
                 Dimensions.class);
-        verify(mCallback, times(2)).requestPageBitmap(pageBitmapDimensCaptor.capture(), eq(true));
+        verify(mBitmapSource, times(2))
+                .requestPageBitmap(pageBitmapDimensCaptor.capture(), eq(true));
 
         // Determine the max sizes used by the view. 1024/512 or determined by context,
         int maxBitmapSize = MosaicView.getMaxTileSize(ApplicationProvider.getApplicationContext());
@@ -340,7 +342,7 @@
                 .isEqualTo(new Dimensions(maxBackgroundBitmapSize / 2, maxBackgroundBitmapSize));
 
         // Check that we tiled twice, the first time should have covered viewArea.
-        verify(mCallback, times(2)).requestNewTiles(isA(Dimensions.class),
+        verify(mBitmapSource, times(2)).requestNewTiles(isA(Dimensions.class),
                 mRequestedTiles.capture());
         List<Iterable<TileInfo>> requestedTileGroups = mRequestedTiles.getAllValues();
         assertThat(requestedTileGroups).hasSize(2);
@@ -360,7 +362,7 @@
         Dimensions dimensions = new Dimensions(2000, 4000);
         Rect viewArea = new Rect(0, 0, 750, 750);
 
-        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mCallback);
+        mView.init(dimensions, TileBoard.DEFAULT_RECYCLER, mBitmapSource);
         mView.setViewArea(viewArea);
         mView.requestDrawAtZoom(2f); // First drawing here.
 
@@ -372,8 +374,8 @@
 
         // Should have made bitmap requests only on the first draw request because there weren't any
         // to replace when requestRedrawAreas was called.
-        verify(mCallback, times(1)).requestPageBitmap(any(Dimensions.class), eq(true));
-        verify(mCallback, times(1)).requestNewTiles(any(Dimensions.class), any());
+        verify(mBitmapSource, times(1)).requestPageBitmap(any(Dimensions.class), eq(true));
+        verify(mBitmapSource, times(1)).requestNewTiles(any(Dimensions.class), any());
     }
 
     private void checkCoverage(Rect area, Iterable<TileInfo> tileInfos) {
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/PageIndicatorTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/PageIndicatorTest.java
new file mode 100644
index 0000000..0f31a1e
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/widget/PageIndicatorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.widget;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.widget.TextView;
+
+import androidx.pdf.data.Range;
+import androidx.pdf.util.Accessibility;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link PageIndicator}. */
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class PageIndicatorTest {
+    @Mock
+    Accessibility mAccessibility;
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private final TextView mPageNumberView = new TextView(mContext);
+    private PageIndicator mPageIndicator;
+
+    private AutoCloseable mOpenMocks;
+
+    @Before
+    public void setUp() {
+        mOpenMocks = MockitoAnnotations.openMocks(this);
+        when(mAccessibility.isAccessibilityEnabled(isA(Context.class))).thenReturn(true);
+
+        mPageIndicator = new PageIndicator(mContext, mPageNumberView, mAccessibility);
+        mPageIndicator.setNumPages(10);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mOpenMocks.close();
+    }
+
+
+    @Test
+    public void testSetRangeAndZoom_whenFirstCall_returnsFalse() {
+        assertThat(mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false)).isFalse();
+    }
+
+    @Test
+    public void testSetRangeAndZoom_whenRangeIsTheSame_returnsFalse() {
+        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
+        assertThat(mPageIndicator.setRangeAndZoom(new Range(0, 1), 2.5f, true)).isFalse();
+    }
+
+    @Test
+    public void testSetRangeAndZoom_whenRangeIsTheDifferent_returnsTrue() {
+        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
+        assertThat(mPageIndicator.setRangeAndZoom(new Range(0, 3), 1.5f, false)).isTrue();
+    }
+
+    @Test
+    public void testAnnouncePageChanges() {
+        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
+        mPageIndicator.setRangeAndZoom(new Range(0, 1), 1.5f, false);
+        mPageIndicator.setRangeAndZoom(new Range(1, 1), 1.5f, false);
+        verify(mAccessibility).announce(mContext, mPageNumberView, "page 2 of 10");
+
+        mPageIndicator.setRangeAndZoom(new Range(1, 1), 1.5f, false);
+        mPageIndicator.setRangeAndZoom(new Range(1, 1), 1.5f, false);
+        mPageIndicator.setRangeAndZoom(new Range(1, 2), 1.5f, false);
+        verify(mAccessibility).announce(mContext, mPageNumberView, "pages 2 to 3 of 10");
+
+        mPageIndicator.setRangeAndZoom(new Range(1, 2), 1.5f, true);
+    }
+
+    @Test
+    public void testAnnounceZoomChanges() {
+        mPageIndicator.setRangeAndZoom(new Range(0, 0), 1.5f, false);
+        mPageIndicator.setRangeAndZoom(new Range(0, 0), 2.0f, false);
+        mPageIndicator.setRangeAndZoom(new Range(0, 0), 2.5f, false);
+        mPageIndicator.setRangeAndZoom(new Range(0, 0), 3.0f, false);
+        mPageIndicator.setRangeAndZoom(new Range(0, 0), 3.5f, false);
+        mPageIndicator.setRangeAndZoom(new Range(0, 0), 4.0f, false);
+        mPageIndicator.setRangeAndZoom(new Range(0, 0), 4.0f, true);
+        verify(mAccessibility).announce(mContext, mPageNumberView,
+                String.format("%s\n%s", "page 1 of 10", "zoom 400 percent"));
+    }
+}
diff --git a/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/viewmodel/PdfViewerViewModelTest.kt b/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/viewmodel/PdfViewerViewModelTest.kt
new file mode 100644
index 0000000..3de60a8
--- /dev/null
+++ b/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/viewmodel/PdfViewerViewModelTest.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pdf.viewmodel
+
+import android.content.Context
+import android.net.Uri
+import androidx.pdf.data.DisplayData
+import androidx.pdf.data.Openable
+import androidx.pdf.viewer.loader.PdfLoader
+import androidx.pdf.viewer.loader.WeakPdfLoaderCallbacks
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.filters.SmallTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.robolectric.RobolectricTestRunner
+
+@SmallTest
+@RunWith(RobolectricTestRunner::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class PdfViewerViewModelTest {
+
+    private val context: Context = getApplicationContext()
+    private val mockCallbacks = mock(WeakPdfLoaderCallbacks::class.java)
+    private val displayData1 =
+        DisplayData(
+            Uri.parse("content://test.app.authority/fake-file1.pdf"),
+            "FakeFile1",
+            mock(Openable::class.java)
+        )
+    private val displayData2 =
+        DisplayData(
+            Uri.parse("content://test.app.authority/fake-file2.pdf"),
+            "FakeFile2",
+            mock(Openable::class.java)
+        )
+    private val testScope = TestScope(UnconfinedTestDispatcher())
+
+    @Test
+    fun testUpdatePdfLoader_CreatesNewPdfLoader() {
+        val viewModel = PdfLoaderViewModel()
+
+        assertNull(viewModel.pdfLoaderStateFlow.value)
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        assertNotNull(viewModel.pdfLoaderStateFlow.value)
+    }
+
+    @Test
+    fun testUpdatePdfLoader_WithSameDisplayData_ReusesExistingPdfLoader() {
+        val viewModel = PdfLoaderViewModel()
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        val initialLoader = viewModel.pdfLoaderStateFlow.value
+        assertNotNull(initialLoader)
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        val currentLoader = viewModel.pdfLoaderStateFlow.value
+        assertEquals(initialLoader, currentLoader)
+    }
+
+    @Test
+    fun testUpdatePdfLoader_WithNewDisplayData_CreatesNewPdfLoader() {
+        val viewModel = PdfLoaderViewModel()
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        val initialLoader = viewModel.pdfLoaderStateFlow.value
+        assertNotNull(initialLoader)
+
+        viewModel.updatePdfLoader(context, displayData2, mockCallbacks)
+        val currentLoader = viewModel.pdfLoaderStateFlow.value
+        assertNotEquals(initialLoader, currentLoader)
+    }
+
+    @Test
+    fun testUpdatePdfLoader_WithSameDisplayData_CallsReloadDocument() {
+        val viewModel = PdfLoaderViewModel()
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        val pdfLoader = mock(PdfLoader::class.java)
+        viewModel.updatePdfLoader(pdfLoader)
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        verify(pdfLoader, times(1)).reloadDocument()
+    }
+
+    @Test
+    fun testUpdatePdfLoader_WithSameDisplayData_CallsSetCallbacks() {
+        val viewModel = PdfLoaderViewModel()
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        val pdfLoader = mock(PdfLoader::class.java)
+        viewModel.updatePdfLoader(pdfLoader)
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        verify(pdfLoader, times(1)).callbacks = mockCallbacks
+    }
+
+    @Test
+    fun testUpdatePdfLoader_WithNewDisplayData_CleansUpOldLoader() {
+        val viewModel = PdfLoaderViewModel()
+
+        viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+        val pdfLoader = mock(PdfLoader::class.java)
+        viewModel.updatePdfLoader(pdfLoader)
+
+        viewModel.updatePdfLoader(context, displayData2, mockCallbacks)
+        verify(pdfLoader, times(1)).disconnect()
+        verify(pdfLoader, times(1)).cancelAll()
+    }
+
+    @Test
+    fun testUpdatePdfLoaderWithNewDisplayData_triggersObserver() =
+        testScope.runTest {
+            val viewModel = PdfLoaderViewModel()
+            var emissionCount = 0
+
+            val job =
+                testScope.launch {
+                    viewModel.pdfLoaderStateFlow
+                        // Filter out initial null value
+                        .filterNotNull()
+                        .collect { emissionCount++ }
+                }
+
+            viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+            viewModel.updatePdfLoader(context, displayData2, mockCallbacks)
+
+            assertEquals(2, emissionCount)
+
+            job.cancelAndJoin()
+        }
+
+    @Test
+    fun testUpdatePdfLoaderWithSameDisplayData_doesNotTriggerObserver() =
+        testScope.runTest {
+            val viewModel = PdfLoaderViewModel()
+            var emissionCount = 0
+
+            val job =
+                testScope.launch {
+                    viewModel.pdfLoaderStateFlow
+                        // Filter out initial null value
+                        .filterNotNull()
+                        .collect { emissionCount++ }
+                }
+
+            viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+            viewModel.updatePdfLoader(context, displayData1, mockCallbacks)
+
+            assertEquals(1, emissionCount)
+
+            job.cancelAndJoin()
+        }
+}
diff --git a/percentlayout/percentlayout/build.gradle b/percentlayout/percentlayout/build.gradle
index 054c10b..256c6e0 100644
--- a/percentlayout/percentlayout/build.gradle
+++ b/percentlayout/percentlayout/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.1.0")
 
     androidTestImplementation(libs.testExtJunit)
@@ -35,5 +35,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2015"
     description = "Android Percent Support Library"
-    metalavaK2UastEnabled = true
 }
diff --git a/playground-common/androidx-shared.properties b/playground-common/androidx-shared.properties
index df7a188..9d59fd0 100644
--- a/playground-common/androidx-shared.properties
+++ b/playground-common/androidx-shared.properties
@@ -34,6 +34,7 @@
 org.gradle.configuration-cache=true
 org.gradle.configuration-cache.problems=fail
 
+android.lint.useK2Uast=true
 android.lint.printStackTrace=true
 android.uniquePackageNames=true
 android.enableAdditionalTestOutput=true
@@ -62,7 +63,7 @@
 android.experimental.dependency.excludeLibraryComponentsFromConstraints=true
 # Disallow resolving dependencies at configuration time, which is a slight performance problem
 android.dependencyResolutionAtConfigurationTime.disallow=true
-android.suppressUnsupportedOptionWarnings=android.suppressUnsupportedOptionWarnings,android.dependencyResolutionAtConfigurationTime.disallow,android.experimental.lint.missingBaselineIsEmptyBaseline,android.lint.printStackTrace,android.lint.baselineOmitLineNumbers,android.experimental.disableCompileSdkChecks,android.overrideVersionCheck,android.r8.maxWorkers,android.experimental.lint.reservedMemoryPerTask,android.experimental.dependency.excludeLibraryComponentsFromConstraints,android.prefabVersion,android.experimental.privacysandboxsdk.plugin.enable,android.experimental.privacysandboxsdk.requireServices
+android.suppressUnsupportedOptionWarnings=android.suppressUnsupportedOptionWarnings,android.dependencyResolutionAtConfigurationTime.disallow,android.experimental.lint.missingBaselineIsEmptyBaseline,android.lint.printStackTrace,android.lint.baselineOmitLineNumbers,android.experimental.disableCompileSdkChecks,android.overrideVersionCheck,android.r8.maxWorkers,android.experimental.lint.reservedMemoryPerTask,android.experimental.dependency.excludeLibraryComponentsFromConstraints,android.prefabVersion,android.experimental.privacysandboxsdk.plugin.enable,android.experimental.privacysandboxsdk.requireServices,android.lint.useK2Uast
 # Workaround for b/162074215
 android.includeDependencyInfoInApks=false
 
diff --git a/playground-common/gradle/wrapper/gradle-wrapper.properties b/playground-common/gradle/wrapper/gradle-wrapper.properties
index 52a39743..c8a852e 100644
--- a/playground-common/gradle/wrapper/gradle-wrapper.properties
+++ b/playground-common/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
-distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index b4c9630..b0c866271 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,5 +26,5 @@
 # Disable docs
 androidx.enableDocumentation=false
 androidx.playground.snapshotBuildId=11349412
-androidx.playground.metalavaBuildId=11948267
+androidx.playground.metalavaBuildId=12109034
 androidx.studio.type=playground
\ No newline at end of file
diff --git a/preference/preference-ktx/build.gradle b/preference/preference-ktx/build.gradle
index 88fdacf..9696502 100644
--- a/preference/preference-ktx/build.gradle
+++ b/preference/preference-ktx/build.gradle
@@ -53,7 +53,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for preferences"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/preference/preference/build.gradle b/preference/preference/build.gradle
index 45783b6..c97e8fb 100644
--- a/preference/preference/build.gradle
+++ b/preference/preference/build.gradle
@@ -23,7 +23,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.appcompat:appcompat:1.1.0")
     // Use the latest version of core library for verifying insets visibility
     api("androidx.core:core:1.6.0")
@@ -67,6 +67,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2015"
     description = "AndroidX Preference"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceCategory.java b/preference/preference/src/main/java/androidx/preference/PreferenceCategory.java
index a1c033d..baaca31 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceCategory.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceCategory.java
@@ -24,7 +24,6 @@
 import android.view.View;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -108,7 +107,6 @@
 
     @RequiresApi(28)
     private static class Api28Impl {
-        @DoNotInline
         static void setAccessibilityHeading(@NonNull View view, boolean isHeading) {
             view.setAccessibilityHeading(isHeading);
         }
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
index 1a2bc35..74c62b9 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
@@ -36,7 +36,6 @@
 import android.view.WindowManager;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -327,7 +326,6 @@
         /**
          * Shows the IME on demand for the given {@link Window}.
          */
-        @DoNotInline
         static void showIme(@NonNull Window dialogWindow) {
             dialogWindow.getDecorView().getWindowInsetsController().show(WindowInsets.Type.ime());
         }
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
index 48b3821..a752ab0 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
@@ -34,7 +34,6 @@
 import android.view.WindowInsets;
 import android.widget.TextView;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -304,7 +303,6 @@
         /**
          * Shows the IME on demand for the given {@link Window}.
          */
-        @DoNotInline
         static void showIme(@NonNull Window dialogWindow) {
             dialogWindow.getDecorView().getWindowInsetsController().show(WindowInsets.Type.ime());
         }
diff --git a/print/print/build.gradle b/print/print/build.gradle
index d1e7898..c21fbe3 100644
--- a/print/print/build.gradle
+++ b/print/print/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
@@ -21,7 +21,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/print/print/src/main/java/androidx/print/PrintHelper.java b/print/print/src/main/java/androidx/print/PrintHelper.java
index c4fe138..f4c42b2 100644
--- a/print/print/src/main/java/androidx/print/PrintHelper.java
+++ b/print/print/src/main/java/androidx/print/PrintHelper.java
@@ -41,7 +41,6 @@
 import android.print.pdf.PrintedPdfDocument;
 import android.util.Log;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -839,12 +838,10 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static int getDuplexMode(PrintAttributes printAttributes) {
             return printAttributes.getDuplexMode();
         }
 
-        @DoNotInline
         static void setDuplexMode(PrintAttributes.Builder builder, int duplexMode) {
             builder.setDuplexMode(duplexMode);
         }
@@ -856,7 +853,6 @@
             // This class is not instantiable.
         }
 
-        @DoNotInline
         static ColorSpace get(ColorSpace.Named name) {
             return ColorSpace.get(name);
         }
diff --git a/privacysandbox/activity/activity-client/build.gradle b/privacysandbox/activity/activity-client/build.gradle
index 037ba6d..db098b6 100644
--- a/privacysandbox/activity/activity-client/build.gradle
+++ b/privacysandbox/activity/activity-client/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("androidx.core:core:1.12.0")
     implementation("androidx.lifecycle:lifecycle-common:2.6.2")
@@ -47,6 +47,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.privacysandbox.activity.client"
     defaultConfig {
         minSdk 21
diff --git a/privacysandbox/activity/activity-core/build.gradle b/privacysandbox/activity/activity-core/build.gradle
index 5adf408..3d57c3c 100644
--- a/privacysandbox/activity/activity-core/build.gradle
+++ b/privacysandbox/activity/activity-core/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 android {
diff --git a/privacysandbox/activity/activity-provider/build.gradle b/privacysandbox/activity/activity-provider/build.gradle
index 7c1e5b0..c191036 100644
--- a/privacysandbox/activity/activity-provider/build.gradle
+++ b/privacysandbox/activity/activity-provider/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation(project(":privacysandbox:activity:activity-core"))
     implementation("androidx.core:core:1.12.0")
@@ -45,6 +45,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.privacysandbox.activity.provider"
     defaultConfig {
         minSdk 21
diff --git a/privacysandbox/ads/OWNERS b/privacysandbox/ads/OWNERS
index dfb8b61..49fbef6 100644
--- a/privacysandbox/ads/OWNERS
+++ b/privacysandbox/ads/OWNERS
@@ -4,10 +4,15 @@
 [email protected]
 # adservices.customaudience, adservices.adselection OWNERS
 [email protected] # FLEDGE Primary PoC
[email protected]
-# adservices.topics, .adid, .appsetid, .common OWNERS
[email protected] # Primary PoC for Topics, Common
[email protected] # Creator
[email protected]
+# adservices.topics
[email protected] # Primary PoC for Topics
[email protected]
+# adservices.signals
[email protected] # Primary PoC for Signals
[email protected]
+# adservices.adid, .appsetid, .common OWNERS
[email protected] # # Primary PoC for Common
[email protected]
 [email protected] # DevRel Jetpack Lead
 [email protected] # DevRel TPM Lead
[email protected] # Common PoC
diff --git a/privacysandbox/ads/ads-adservices-java/build.gradle b/privacysandbox/ads/ads-adservices-java/build.gradle
index 48788a7..305ee1a 100644
--- a/privacysandbox/ads/ads-adservices-java/build.gradle
+++ b/privacysandbox/ads/ads-adservices-java/build.gradle
@@ -33,7 +33,7 @@
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
     implementation("androidx.core:core-ktx:1.8.0")
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     // To use CallbackToFutureAdapter
     implementation "androidx.concurrent:concurrent-futures:1.1.0"
@@ -69,6 +69,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "write Java code to call PP APIs."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/privacysandbox/ads/ads-adservices-java/lint-baseline.xml b/privacysandbox/ads/ads-adservices-java/lint-baseline.xml
index afd6f01..06846f2 100644
--- a/privacysandbox/ads/ads-adservices-java/lint-baseline.xml
+++ b/privacysandbox/ads/ads-adservices-java/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -28,13 +28,4 @@
             file="src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java"/>
     </issue>
 
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(TEST_EPOCH_JOB_PERIOD_MS);"
-        errorLine2="               ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/topics/TopicsManagerTest.java"/>
-    </issue>
-
 </issues>
diff --git a/privacysandbox/ads/ads-adservices/api/1.1.0-beta09.txt b/privacysandbox/ads/ads-adservices/api/1.1.0-beta09.txt
index 020b263..55bf72b 100644
--- a/privacysandbox/ads/ads-adservices/api/1.1.0-beta09.txt
+++ b/privacysandbox/ads/ads-adservices/api/1.1.0-beta09.txt
@@ -509,6 +509,7 @@
   }
 
   public final class GetTopicsRequest {
+    ctor public GetTopicsRequest();
     ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
     method public String getAdsSdkName();
     method public boolean shouldRecordObservation();
diff --git a/privacysandbox/ads/ads-adservices/api/current.ignore b/privacysandbox/ads/ads-adservices/api/current.ignore
new file mode 100644
index 0000000..89a8fb8
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AddedMethod: androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest#GetTopicsRequest():
+    Added constructor androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest()
diff --git a/privacysandbox/ads/ads-adservices/api/current.txt b/privacysandbox/ads/ads-adservices/api/current.txt
index 020b263..55bf72b 100644
--- a/privacysandbox/ads/ads-adservices/api/current.txt
+++ b/privacysandbox/ads/ads-adservices/api/current.txt
@@ -509,6 +509,7 @@
   }
 
   public final class GetTopicsRequest {
+    ctor public GetTopicsRequest();
     ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
     method public String getAdsSdkName();
     method public boolean shouldRecordObservation();
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta09.txt b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta09.txt
index 020b263..55bf72b 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta09.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta09.txt
@@ -509,6 +509,7 @@
   }
 
   public final class GetTopicsRequest {
+    ctor public GetTopicsRequest();
     ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
     method public String getAdsSdkName();
     method public boolean shouldRecordObservation();
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.ignore b/privacysandbox/ads/ads-adservices/api/restricted_current.ignore
new file mode 100644
index 0000000..89a8fb8
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AddedMethod: androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest#GetTopicsRequest():
+    Added constructor androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest()
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.txt b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
index 020b263..55bf72b 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
@@ -509,6 +509,7 @@
   }
 
   public final class GetTopicsRequest {
+    ctor public GetTopicsRequest();
     ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
     method public String getAdsSdkName();
     method public boolean shouldRecordObservation();
diff --git a/privacysandbox/ads/ads-adservices/build.gradle b/privacysandbox/ads/ads-adservices/build.gradle
index 25d16ef..e35df7a 100644
--- a/privacysandbox/ads/ads-adservices/build.gradle
+++ b/privacysandbox/ads/ads-adservices/build.gradle
@@ -33,7 +33,7 @@
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
     implementation("androidx.core:core-ktx:1.8.0")
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.kotlinTestJunit)
@@ -65,6 +65,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "This library enables integration with Privacy Preserving APIs, which are part of Privacy Sandbox on Android."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/privacysandbox/ads/ads-adservices/lint-baseline.xml b/privacysandbox/ads/ads-adservices/lint-baseline.xml
deleted file mode 100644
index 872f5d8..0000000
--- a/privacysandbox/ads/ads-adservices/lint-baseline.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-alpha14" type="baseline" client="gradle" dependencies="false" name="AGP (8.2.0-alpha14)" variant="all" version="8.2.0-alpha14">
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mTopicsManager.getTopics("
-        errorLine2="                       ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return android.adservices.topics.GetTopicsRequest.Builder()"
-        errorLine2="                                                          ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            .setAdsSdkName(request.adsSdkName)"
-        errorLine2="             ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            .build()"
-        errorLine2="             ~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        for (topic in response.topics) {"
-        errorLine2="                               ~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            topics.add(Topic(topic.taxonomyVersion, topic.modelVersion, topic.topicId))"
-        errorLine2="                                   ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            topics.add(Topic(topic.taxonomyVersion, topic.modelVersion, topic.topicId))"
-        errorLine2="                                                          ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ads.adservices.topics.TopicsManagerImplCommon is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            topics.add(Topic(topic.taxonomyVersion, topic.modelVersion, topic.topicId))"
-        errorLine2="                                                                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ads/adservices/topics/TopicsManagerImplCommon.kt"/>
-    </issue>
-
-</issues>
diff --git a/privacysandbox/plugins/plugins-privacysandbox-library/lint-baseline.xml b/privacysandbox/plugins/plugins-privacysandbox-library/lint-baseline.xml
index 21096cd..e01b6e6 100644
--- a/privacysandbox/plugins/plugins-privacysandbox-library/lint-baseline.xml
+++ b/privacysandbox/plugins/plugins-privacysandbox-library/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="WithPluginClasspathUsage"
         message="Avoid usage of GradleRunner#withPluginClasspath, which is broken. Instead use something like https://github.com/autonomousapps/dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
-        errorLine1="            .withPluginClasspath()"
-        errorLine2="             ~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            GradleRunner.create().withProjectDir(projectSetup.rootDir).withPluginClasspath()"
+        errorLine2="                                                                       ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/test/java/androidx/privacysandboxlibraryplugin/PrivacySandboxLibraryPluginTest.kt"/>
     </issue>
diff --git a/privacysandbox/sdkruntime/integration-tests/testsdk/build.gradle b/privacysandbox/sdkruntime/integration-tests/testsdk/build.gradle
index 35894bd..47ac29e 100644
--- a/privacysandbox/sdkruntime/integration-tests/testsdk/build.gradle
+++ b/privacysandbox/sdkruntime/integration-tests/testsdk/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation(project(':privacysandbox:sdkruntime:integration-tests:testaidl'))
     implementation(project(':privacysandbox:sdkruntime:sdkruntime-provider'))
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
index af66549..bb5cec2 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
@@ -124,11 +124,9 @@
     androidTestImplementation(libs.dexmakerMockitoInline, excludes.bytebuddy) // DexMaker has it"s own MockMaker
 
     androidTestBundleDex(project(":privacysandbox:sdkruntime:test-sdks:current"))
-    androidTestBundleDex(project(":privacysandbox:sdkruntime:test-sdks:v1"))
-    androidTestBundleDex(project(":privacysandbox:sdkruntime:test-sdks:v2"))
-    // V3 was released as V4 (original release postponed)
     androidTestBundleDex(project(":privacysandbox:sdkruntime:test-sdks:v4"))
     androidTestBundleDex(project(":privacysandbox:sdkruntime:test-sdks:v5"))
+    androidTestBundleDex(project(":privacysandbox:sdkruntime:test-sdks:v6"))
 }
 
 android {
@@ -150,6 +148,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Provides components for SdkRuntime aware Applications"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
index c0e9f08..08ad197 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
+++ b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha07)" variant="all" version="8.6.0-alpha07">
 
     <issue
         id="BanThreadSleep"
@@ -19,58 +19,4 @@
             file="src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorageTest.kt"/>
     </issue>
 
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="            if (Build.VERSION.SDK_INT >= LOLLIPOP) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(LOLLIPOP)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="@RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtils.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSdkInt"
-        message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt"/>
-    </issue>
-
 </issues>
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
index 957ab77..10e02a0 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
@@ -28,14 +28,6 @@
         <package-name>androidx.privacysandbox.sdkruntime.testsdk.invalidEntryPoint</package-name>
     </runtime-enabled-sdk>
     <runtime-enabled-sdk>
-        <compat-config-path>RuntimeEnabledSdks/v1.xml</compat-config-path>
-        <package-name>androidx.privacysandbox.sdkruntime.testsdk.v1</package-name>
-    </runtime-enabled-sdk>
-    <runtime-enabled-sdk>
-        <compat-config-path>RuntimeEnabledSdks/v2.xml</compat-config-path>
-        <package-name>androidx.privacysandbox.sdkruntime.testsdk.v2</package-name>
-    </runtime-enabled-sdk>
-    <runtime-enabled-sdk>
         <compat-config-path>RuntimeEnabledSdks/v4.xml</compat-config-path>
         <package-name>androidx.privacysandbox.sdkruntime.testsdk.v4</package-name>
     </runtime-enabled-sdk>
@@ -44,4 +36,9 @@
         <package-name>androidx.privacysandbox.sdkruntime.testsdk.v5</package-name>
         <version-major>5</version-major>
     </runtime-enabled-sdk>
+    <runtime-enabled-sdk>
+        <compat-config-path>RuntimeEnabledSdks/v6.xml</compat-config-path>
+        <package-name>androidx.privacysandbox.sdkruntime.testsdk.v6</package-name>
+        <version-major>6</version-major>
+    </runtime-enabled-sdk>
 </runtime-enabled-sdk-table>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v1.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v1.xml
deleted file mode 100644
index 38e0309..0000000
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v1.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<compat-config>
-    <compat-entrypoint>androidx.privacysandbox.sdkruntime.testsdk.CompatProvider</compat-entrypoint>
-    <dex-path>test-sdks/v1/classes.dex</dex-path>
-</compat-config>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v2.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v2.xml
deleted file mode 100644
index 2caa00d..0000000
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v2.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<compat-config>
-    <compat-entrypoint>androidx.privacysandbox.sdkruntime.testsdk.CompatProvider</compat-entrypoint>
-    <dex-path>test-sdks/v2/classes.dex</dex-path>
-</compat-config>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v6.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v6.xml
new file mode 100644
index 0000000..4bd1217
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/v6.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<compat-config>
+    <compat-entrypoint>androidx.privacysandbox.sdkruntime.testsdk.CompatProvider</compat-entrypoint>
+    <dex-path>test-sdks/v6/classes.dex</dex-path>
+</compat-config>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt
index 6d17076..4730553 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt
@@ -213,7 +213,7 @@
         val managerCompat = SdkSandboxManagerCompat.from(mContext)
 
         val localSdk = runBlocking {
-            managerCompat.loadSdk(TestSdkConfigs.forSdkName("v2").packageName, Bundle())
+            managerCompat.loadSdk(TestSdkConfigs.CURRENT.packageName, Bundle())
         }
 
         val testSdk = localSdk.asTestSdk()
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt
index 0416f3c..53be17c 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt
@@ -266,11 +266,11 @@
         val managerCompat = SdkSandboxManagerCompat.from(context)
 
         val localSdk = runBlocking {
-            managerCompat.loadSdk(TestSdkConfigs.forSdkName("v2").packageName, Bundle())
+            managerCompat.loadSdk(TestSdkConfigs.CURRENT.packageName, Bundle())
         }
 
         val anotherLocalSdk = runBlocking {
-            managerCompat.loadSdk(TestSdkConfigs.CURRENT.packageName, Bundle())
+            managerCompat.loadSdk(TestSdkConfigs.CURRENT_WITH_RESOURCES.packageName, Bundle())
         }
 
         val testSdk = localSdk.asTestSdk()
@@ -284,6 +284,17 @@
             )
     }
 
+    @Test
+    fun sdkController_getClientPackageName_returnsAppPackageName() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val localSdk = managerCompat.loadSdkWithFeature(ClientFeature.GET_CLIENT_PACKAGE_NAME)
+
+        val result = localSdk.asTestSdk().getClientPackageName()
+        assertThat(result).isEqualTo(context.getPackageName())
+    }
+
     private fun SdkSandboxManagerCompat.loadSdkWithFeature(
         clientFeature: ClientFeature
     ): SandboxedSdkCompat {
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt
index 289ec70..d553f50 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.privacysandbox.sdkruntime.client.controller
 
+import android.content.Context
 import android.os.Binder
 import android.os.Bundle
 import androidx.privacysandbox.sdkruntime.client.activity.LocalSdkActivityHandlerRegistry
@@ -25,6 +26,7 @@
 import androidx.privacysandbox.sdkruntime.core.activity.ActivityHolder
 import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
 import androidx.privacysandbox.sdkruntime.core.controller.LoadSdkCallback
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -36,15 +38,23 @@
 @RunWith(AndroidJUnit4::class)
 class LocalControllerTest {
 
+    private lateinit var applicationContext: Context
     private lateinit var localSdkRegistry: StubLocalSdkRegistry
     private lateinit var appOwnedSdkRegistry: StubAppOwnedSdkInterfaceRegistry
     private lateinit var controller: LocalController
 
     @Before
     fun setUp() {
+        applicationContext = ApplicationProvider.getApplicationContext()
         localSdkRegistry = StubLocalSdkRegistry()
         appOwnedSdkRegistry = StubAppOwnedSdkInterfaceRegistry()
-        controller = LocalController(SDK_PACKAGE_NAME, localSdkRegistry, appOwnedSdkRegistry)
+        controller =
+            LocalController(
+                SDK_PACKAGE_NAME,
+                applicationContext,
+                localSdkRegistry,
+                appOwnedSdkRegistry
+            )
     }
 
     @Test
@@ -123,7 +133,12 @@
             )
 
         val anotherSdkController =
-            LocalController("LocalControllerTest.anotherSdk", localSdkRegistry, appOwnedSdkRegistry)
+            LocalController(
+                "LocalControllerTest.anotherSdk",
+                applicationContext,
+                localSdkRegistry,
+                appOwnedSdkRegistry
+            )
         val anotherSdkHandler =
             object : SdkSandboxActivityHandlerCompat {
                 override fun onActivityCreated(activityHolder: ActivityHolder) {
@@ -155,6 +170,12 @@
         assertThat(registeredHandler).isNull()
     }
 
+    @Test
+    fun getClientPackageName_returnsAppPackageName() {
+        val result = controller.getClientPackageName()
+        assertThat(result).isEqualTo(applicationContext.getPackageName())
+    }
+
     private class StubLocalSdkRegistry : SdkRegistry {
 
         var getLoadedSdksResult: List<SandboxedSdkCompat> = emptyList()
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
index 5d5d62a..f742e9c 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
@@ -126,8 +126,6 @@
 
     @Test
     fun getSandboxedSdks_delegateToSdkController() {
-        assumeFeatureAvailable(ClientFeature.SDK_SANDBOX_CONTROLLER)
-
         val expectedResult =
             SandboxedSdkCompat(
                 sdkInterface = Binder(),
@@ -271,6 +269,18 @@
         assertThat(result.extraInformation).isSameInstanceAs(expectedError.extraInformation)
     }
 
+    @Test
+    fun getClientPackageName_returnsResultFromSdkController() {
+        assumeFeatureAvailable(ClientFeature.GET_CLIENT_PACKAGE_NAME)
+
+        val clientPackageName = "client.package.name"
+        controller.clientPackageNameResult = clientPackageName
+
+        val result = loadedSdk.loadTestSdk().getClientPackageName()
+
+        assertThat(result).isEqualTo(clientPackageName)
+    }
+
     internal class TestClassLoaderFactory(private val testStorage: TestLocalSdkStorage) :
         SdkLoader.ClassLoaderFactory {
         override fun createClassLoaderFor(
@@ -306,7 +316,7 @@
          * Create test params for each supported [ClientApiVersion] + current and future. Each
          * released version must have test-sdk named as "vX" (where X is version to test). These
          * TestSDKs should be registered in RuntimeEnabledSdkTable.xml and be compatible with
-         * [TestSdkWrapper].
+         * [SdkControllerWrapper].
          */
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
@@ -375,6 +385,7 @@
         var lastLoadSdkParams: Bundle? = null
         var loadSdkResult: SandboxedSdkCompat? = null
         var loadSdkError: LoadSdkCompatException? = null
+        var clientPackageNameResult: String? = null
 
         override fun loadSdk(
             sdkName: String,
@@ -421,8 +432,6 @@
             sdkActivityHandlers.values.remove(handlerCompat)
         }
 
-        override fun getClientPackageName(): String {
-            throw UnsupportedOperationException("Not supported yet")
-        }
+        override fun getClientPackageName(): String = clientPackageNameResult!!
     }
 }
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt
index 67db655..af74b9d 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt
@@ -110,6 +110,8 @@
         controller.callMethod("unregisterSdkSandboxActivityHandler", handler.proxy)
         handler.proxy = null
     }
+
+    fun getClientPackageName(): String = controller.callMethod("getClientPackageName") as String
 }
 
 /** Reflection wrapper for [SandboxedSdkCompat] */
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SandboxedSdkContextCompatTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SandboxedSdkContextCompatTest.kt
index af2fbef..0306fd9 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SandboxedSdkContextCompatTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SandboxedSdkContextCompatTest.kt
@@ -65,7 +65,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 21)
     fun getCodeCacheDir_returnSdkCodeCacheDirInAppCodeCacheDir() {
         val expectedSdksCodeCacheRoot = File(appStorageContext.codeCacheDir, SDK_ROOT_FOLDER)
         val expectedSdkCodeCache = File(expectedSdksCodeCacheRoot, SDK_PACKAGE_NAME)
@@ -76,7 +75,6 @@
     }
 
     @Test
-    @SdkSuppress(minSdkVersion = 21)
     fun getNoBackupFilesDir_returnSdkNoBackupDirInAppNoBackupDir() {
         val expectedSdksNoBackupRoot = File(appStorageContext.noBackupFilesDir, SDK_ROOT_FOLDER)
         val expectedSdkNoBackupDir = File(expectedSdksNoBackupRoot, SDK_PACKAGE_NAME)
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt
index ec2e47d..6c414b3 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt
@@ -22,6 +22,7 @@
 import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
 import androidx.privacysandbox.sdkruntime.core.Versions
 import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+import androidx.privacysandbox.sdkruntime.core.internal.ClientApiVersion
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
@@ -73,6 +74,18 @@
     }
 
     @Test
+    fun loadSdk_forUnsupportedApiVersion_throwsLoadSdkCompatException() {
+        val customVersionHandshake =
+            VersionHandshake(overrideApiVersion = ClientApiVersion.MIN_SUPPORTED.apiLevel - 1)
+
+        assertThrows(LoadSdkCompatException::class.java) {
+                sdkLoader.loadSdk(testSdkConfig, customVersionHandshake)
+            }
+            .hasMessageThat()
+            .startsWith("SDK built with unsupported version of sdkruntime-provider library")
+    }
+
+    @Test
     fun testContextClassloader() {
         val loadedSdk = sdkLoader.loadSdk(testSdkConfig)
 
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtilsTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtilsTest.kt
index 4799760..b787e0d 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtilsTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtilsTest.kt
@@ -17,11 +17,9 @@
 package androidx.privacysandbox.sdkruntime.client.loader.impl
 
 import android.content.Context
-import android.os.Build
 import android.system.Os
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import java.io.DataInputStream
@@ -33,7 +31,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 class MigrationUtilsTest {
 
     private lateinit var context: Context
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProviderTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProviderTest.kt
index 4ceaaf9..515f2d8 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProviderTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProviderTest.kt
@@ -44,9 +44,7 @@
     fun setUp() {
         context = ApplicationProvider.getApplicationContext()
 
-        val codeCache = File(context.applicationInfo.dataDir, "code_cache")
-
-        sdkRootFolder = File(codeCache, "RuntimeEnabledSdk")
+        sdkRootFolder = File(context.codeCacheDir, "RuntimeEnabledSdk")
         versionFile = File(sdkRootFolder, "Folder.version")
 
         @Suppress("DEPRECATION")
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt
index 9328010..0158636 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt
@@ -16,6 +16,7 @@
 
 package androidx.privacysandbox.sdkruntime.client.controller
 
+import android.content.Context
 import android.os.Bundle
 import android.os.IBinder
 import androidx.privacysandbox.sdkruntime.client.activity.LocalSdkActivityHandlerRegistry
@@ -30,6 +31,7 @@
 /** Local implementation that will be injected to locally loaded SDKs. */
 internal class LocalController(
     private val sdkPackageName: String,
+    private val applicationContext: Context,
     private val localSdkRegistry: SdkRegistry,
     private val appOwnedSdkRegistry: AppOwnedSdkRegistry
 ) : SdkSandboxControllerCompat.SandboxControllerImpl {
@@ -67,7 +69,5 @@
         LocalSdkActivityHandlerRegistry.unregister(handlerCompat)
     }
 
-    override fun getClientPackageName(): String {
-        throw UnsupportedOperationException("Not supported yet")
-    }
+    override fun getClientPackageName(): String = applicationContext.getPackageName()
 }
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerFactory.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerFactory.kt
index 5822a5d..c1e89c1 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerFactory.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerFactory.kt
@@ -16,18 +16,25 @@
 
 package androidx.privacysandbox.sdkruntime.client.controller
 
+import android.content.Context
 import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
 import androidx.privacysandbox.sdkruntime.client.loader.SdkLoader
 import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
 
 /** Create [LocalController] instance for specific sdk. */
 internal class LocalControllerFactory(
+    private val applicationContext: Context,
     private val localSdkRegistry: SdkRegistry,
     private val appOwnedSdkRegistry: AppOwnedSdkRegistry
 ) : SdkLoader.ControllerFactory {
     override fun createControllerFor(
         sdkConfig: LocalSdkConfig
     ): SdkSandboxControllerCompat.SandboxControllerImpl {
-        return LocalController(sdkConfig.packageName, localSdkRegistry, appOwnedSdkRegistry)
+        return LocalController(
+            sdkConfig.packageName,
+            applicationContext,
+            localSdkRegistry,
+            appOwnedSdkRegistry
+        )
     }
 }
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/impl/LocalSdkRegistry.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/impl/LocalSdkRegistry.kt
index 73eca5c..85c408a 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/impl/LocalSdkRegistry.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/impl/LocalSdkRegistry.kt
@@ -118,7 +118,7 @@
             localSdkRegistry.sdkLoader =
                 SdkLoader.create(
                     context,
-                    LocalControllerFactory(localSdkRegistry, appOwnedSdkRegistry)
+                    LocalControllerFactory(context, localSdkRegistry, appOwnedSdkRegistry)
                 )
 
             return localSdkRegistry
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactory.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactory.kt
index 676699c..808a3de 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactory.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactory.kt
@@ -18,7 +18,6 @@
 import android.content.Context
 import android.content.res.AssetManager
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
 import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
@@ -33,7 +32,6 @@
     private class Api27Impl(private val assetLoader: AssetLoader) :
         InMemorySdkClassLoaderFactory() {
 
-        @DoNotInline
         override fun createClassLoaderFor(
             sdkConfig: LocalSdkConfig,
             parent: ClassLoader
@@ -58,7 +56,6 @@
     private class Api26Impl(private val assetLoader: AssetLoader) :
         InMemorySdkClassLoaderFactory() {
 
-        @DoNotInline
         override fun createClassLoaderFor(
             sdkConfig: LocalSdkConfig,
             parent: ClassLoader
@@ -83,7 +80,6 @@
     }
 
     private class FailImpl : InMemorySdkClassLoaderFactory() {
-        @DoNotInline
         override fun createClassLoaderFor(
             sdkConfig: LocalSdkConfig,
             parent: ClassLoader
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt
index a446314..b6e10a4 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt
@@ -22,7 +22,7 @@
 import androidx.privacysandbox.sdkruntime.client.loader.storage.CachedLocalSdkStorage
 import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
 import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
-import androidx.privacysandbox.sdkruntime.core.internal.ClientFeature
+import androidx.privacysandbox.sdkruntime.core.internal.ClientApiVersion
 
 /** Load SDK bundled with App. */
 internal class SdkLoader
@@ -71,18 +71,26 @@
     ): LocalSdkProvider {
         try {
             val sdkApiVersion = versionHandshake.perform(sdkClassLoader)
-            ResourceRemapping.apply(sdkClassLoader, sdkConfig.resourceRemapping)
-            if (ClientFeature.SDK_SANDBOX_CONTROLLER.isAvailable(sdkApiVersion)) {
-                val controller = controllerFactory.createControllerFor(sdkConfig)
-                SandboxControllerInjector.inject(sdkClassLoader, sdkApiVersion, controller)
+            if (sdkApiVersion < ClientApiVersion.MIN_SUPPORTED.apiLevel) {
+                throw LoadSdkCompatException(
+                    LoadSdkCompatException.LOAD_SDK_NOT_FOUND,
+                    "SDK built with unsupported version of sdkruntime-provider library"
+                )
             }
+            ResourceRemapping.apply(sdkClassLoader, sdkConfig.resourceRemapping)
+            val controller = controllerFactory.createControllerFor(sdkConfig)
+            SandboxControllerInjector.inject(sdkClassLoader, sdkApiVersion, controller)
             return SdkProviderV1.create(sdkClassLoader, sdkConfig, appContext)
         } catch (ex: Exception) {
-            throw LoadSdkCompatException(
-                LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
-                "Failed to instantiate local SDK",
-                ex
-            )
+            if (ex is LoadSdkCompatException) {
+                throw ex
+            } else {
+                throw LoadSdkCompatException(
+                    LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
+                    "Failed to instantiate local SDK",
+                    ex
+                )
+            }
         }
     }
 
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtils.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtils.kt
index 11297ae..81c47ca 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtils.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/MigrationUtils.kt
@@ -21,7 +21,6 @@
 import android.system.ErrnoException
 import android.system.Os
 import android.util.Log
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import java.io.File
 import java.io.FileInputStream
@@ -30,7 +29,6 @@
 import java.io.InputStream
 import java.io.OutputStream
 
-@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 internal object MigrationUtils {
 
     private const val LOG_TAG = "LocalSdkMigrationUtils"
@@ -97,6 +95,6 @@
 
     @RequiresApi(Build.VERSION_CODES.Q)
     private object Api29 {
-        @DoNotInline fun copy(from: InputStream, to: OutputStream): Long = FileUtils.copy(from, to)
+        fun copy(from: InputStream, to: OutputStream): Long = FileUtils.copy(from, to)
     }
 }
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt
index d974c07..b17e345 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt
@@ -120,6 +120,12 @@
             }
         }
 
+        if (ClientFeature.GET_CLIENT_PACKAGE_NAME.isAvailable(sdkVersion)) {
+            handlerBuilder.addHandlerFor("getClientPackageName") {
+                controller.getClientPackageName()
+            }
+        }
+
         return handlerBuilder.build()
     }
 
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt
index 49f79f0..9237d21 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt
@@ -21,7 +21,6 @@
 import android.database.DatabaseErrorHandler
 import android.database.sqlite.SQLiteDatabase
 import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import java.io.File
 import java.io.FileInputStream
@@ -73,16 +72,14 @@
     }
 
     /** Points to <app_data_dir>/code_cache/RuntimeEnabledSdksData/<sdk_package_name> */
-    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
     override fun getCodeCacheDir(): File {
-        val sdksCodeCacheRoot = ensureDirExists(Api21.codeCacheDir(baseContext), SDK_ROOT_FOLDER)
+        val sdksCodeCacheRoot = ensureDirExists(baseContext.codeCacheDir, SDK_ROOT_FOLDER)
         return ensureDirExists(sdksCodeCacheRoot, sdkPackageName)
     }
 
     /** Points to <app_data_dir>/no_backup/RuntimeEnabledSdksData/<sdk_package_name> */
-    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
     override fun getNoBackupFilesDir(): File {
-        val sdksNoBackupRoot = ensureDirExists(Api21.noBackupFilesDir(baseContext), SDK_ROOT_FOLDER)
+        val sdksNoBackupRoot = ensureDirExists(baseContext.noBackupFilesDir, SDK_ROOT_FOLDER)
         return ensureDirExists(sdksNoBackupRoot, sdkPackageName)
     }
 
@@ -252,22 +249,13 @@
         return dir
     }
 
-    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
-    private object Api21 {
-        @DoNotInline fun codeCacheDir(context: Context): File = context.codeCacheDir
-
-        @DoNotInline fun noBackupFilesDir(context: Context): File = context.noBackupFilesDir
-    }
-
     @RequiresApi(Build.VERSION_CODES.N)
     private object Api24 {
-        @DoNotInline
         fun createDeviceProtectedStorageContext(context: Context): Context =
             context.createDeviceProtectedStorageContext()
 
-        @DoNotInline fun dataDir(context: Context): File = context.dataDir
+        fun dataDir(context: Context): File = context.dataDir
 
-        @DoNotInline
         fun deleteSharedPreferences(context: Context, name: String): Boolean =
             context.deleteSharedPreferences(name)
     }
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt
index eeab1f5..ce30930 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt
@@ -19,9 +19,7 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import android.os.Build
-import android.os.Build.VERSION_CODES.LOLLIPOP
 import android.os.Build.VERSION_CODES.TIRAMISU
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
 import java.io.DataInputStream
@@ -48,7 +46,6 @@
     companion object {
 
         private const val SDK_ROOT_FOLDER = "RuntimeEnabledSdk"
-        private const val CODE_CACHE_FOLDER = "code_cache"
         private const val VERSION_FILE_NAME = "Folder.version"
 
         /**
@@ -63,7 +60,7 @@
         }
 
         private fun createSdkRootFolder(context: Context): File {
-            val rootFolder = getRootFolderPath(context)
+            val rootFolder = File(context.codeCacheDir, SDK_ROOT_FOLDER)
             val versionFile = File(rootFolder, VERSION_FILE_NAME)
 
             val sdkRootFolderVersion = readVersion(versionFile)
@@ -86,19 +83,6 @@
             return rootFolder
         }
 
-        private fun getRootFolderPath(context: Context): File {
-            if (Build.VERSION.SDK_INT >= LOLLIPOP) {
-                return File(Api21Impl.codeCacheDir(context), SDK_ROOT_FOLDER)
-            }
-
-            // Emulate code cache folder
-            val dataDir = context.applicationInfo.dataDir
-            val codeCacheDir = File(dataDir, CODE_CACHE_FOLDER)
-            codeCacheDir.mkdir()
-
-            return File(codeCacheDir, SDK_ROOT_FOLDER)
-        }
-
         private fun appLastUpdateTime(context: Context): Long {
             if (Build.VERSION.SDK_INT >= TIRAMISU) {
                 return Api33Impl.getLastUpdateTime(context)
@@ -126,14 +110,8 @@
         }
     }
 
-    @RequiresApi(LOLLIPOP)
-    private object Api21Impl {
-        @DoNotInline fun codeCacheDir(context: Context): File = context.codeCacheDir
-    }
-
     @RequiresApi(TIRAMISU)
     private object Api33Impl {
-        @DoNotInline
         fun getLastUpdateTime(context: Context): Long =
             context.packageManager
                 .getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(0))
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
index 8803986..a8366d3 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("androidx.core:core:1.12.0")
     implementation("androidx.core:core-ktx:1.12.0")
@@ -65,6 +65,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Provides shared components for SdkRuntime libraries"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt
index d9f7d58..55333c1 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt
@@ -126,26 +126,6 @@
     }
 
     @Test
-    fun getAppOwnedSdkSandboxInterfaces_whenNotAvailable_returnsEmptyList() {
-        SdkSandboxControllerCompat.injectLocalImpl(
-            TestStubImpl(
-                appOwnedSdks =
-                    listOf(
-                        AppOwnedSdkSandboxInterfaceCompat(
-                            name = "TestSdk",
-                            version = 42,
-                            binder = Binder(),
-                        )
-                    )
-            )
-        )
-
-        val controllerCompat = SdkSandboxControllerCompat.from(context)
-        val appOwnedInterfaces = controllerCompat.getAppOwnedSdkSandboxInterfaces()
-        assertThat(appOwnedInterfaces).isEmpty()
-    }
-
-    @Test
     fun getAppOwnedSdkSandboxInterfaces_returnsListFromLocalImpl() {
         clientHandShakeForVersionIncluding(ClientFeature.APP_OWNED_INTERFACES)
 
@@ -200,34 +180,6 @@
     }
 
     @Test
-    fun registerSdkSandboxActivityHandler_whenNotAvailable_throwsUnsupportedOperationException() {
-        SdkSandboxControllerCompat.injectLocalImpl(TestStubImpl())
-        val controllerCompat = SdkSandboxControllerCompat.from(context)
-
-        Assert.assertThrows(UnsupportedOperationException::class.java) {
-            controllerCompat.registerSdkSandboxActivityHandler(
-                object : SdkSandboxActivityHandlerCompat {
-                    override fun onActivityCreated(activityHolder: ActivityHolder) {}
-                }
-            )
-        }
-    }
-
-    @Test
-    fun unregisterSdkSandboxActivityHandler_whenNotAvailable_throwsUnsupportedOperationException() {
-        SdkSandboxControllerCompat.injectLocalImpl(TestStubImpl())
-        val controllerCompat = SdkSandboxControllerCompat.from(context)
-
-        Assert.assertThrows(UnsupportedOperationException::class.java) {
-            controllerCompat.unregisterSdkSandboxActivityHandler(
-                object : SdkSandboxActivityHandlerCompat {
-                    override fun onActivityCreated(activityHolder: ActivityHolder) {}
-                }
-            )
-        }
-    }
-
-    @Test
     fun getClientPackageName_whenNotAvailable_returnsContextPackageName() {
         SdkSandboxControllerCompat.injectLocalImpl(TestStubImpl())
         val controllerCompat = SdkSandboxControllerCompat.from(context)
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatException.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatException.kt
index 84c44e0..73e0501 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatException.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatException.kt
@@ -17,7 +17,6 @@
 
 import android.app.sdksandbox.LoadSdkException
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.annotation.IntDef
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
@@ -116,12 +115,10 @@
     @RequiresApi(34)
     private object Api34Impl {
 
-        @DoNotInline
         fun toLoadSdkException(ex: LoadSdkCompatException): LoadSdkException {
             return LoadSdkException(ex.cause!!, ex.extraInformation)
         }
 
-        @DoNotInline
         fun toLoadCompatSdkException(ex: LoadSdkException): LoadSdkCompatException {
             return LoadSdkCompatException(
                 toLoadSdkErrorCodeCompat(ex.loadSdkErrorCode),
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt
index 1b8b333..97aa272 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt
@@ -17,7 +17,6 @@
 
 import android.app.sdksandbox.SandboxedSdk
 import android.os.IBinder
-import androidx.annotation.DoNotInline
 import androidx.annotation.Keep
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
@@ -100,18 +99,16 @@
 
         fun getSdkInfo(): SandboxedSdkInfo?
 
-        @RequiresApi(34) @DoNotInline fun toSandboxedSdk(): SandboxedSdk
+        @RequiresApi(34) fun toSandboxedSdk(): SandboxedSdk
     }
 
     @RequiresApi(34)
     private open class Api34Impl(protected val sandboxedSdk: SandboxedSdk) : SandboxedSdkImpl {
 
-        @DoNotInline
         override fun getInterface(): IBinder? {
             return sandboxedSdk.getInterface()
         }
 
-        @DoNotInline
         override fun getSdkInfo(): SandboxedSdkInfo {
             val sharedLibraryInfo = sandboxedSdk.sharedLibraryInfo
             return SandboxedSdkInfo(
@@ -120,13 +117,11 @@
             )
         }
 
-        @DoNotInline
         override fun toSandboxedSdk(): SandboxedSdk {
             return sandboxedSdk
         }
 
         companion object {
-            @DoNotInline
             fun createSandboxedSdk(sdkInterface: IBinder): SandboxedSdk {
                 return SandboxedSdk(sdkInterface)
             }
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapter.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapter.kt
deleted file mode 100644
index 6bb10bb..0000000
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapter.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.privacysandbox.sdkruntime.core
-
-import android.app.sdksandbox.LoadSdkException
-import android.app.sdksandbox.SandboxedSdk
-import android.app.sdksandbox.SandboxedSdkProvider
-import android.content.Context
-import android.os.Bundle
-import android.view.View
-import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
-
-/**
- * Implementation of platform [SandboxedSdkProvider] that delegate to [SandboxedSdkProviderCompat]
- * Gets compat class name from asset "SandboxedSdkProviderCompatClassName.txt"
- */
-@RequiresApi(34)
-// TODO(b/301437557) Remove after documentation migration to sdkruntime-provider
-@Deprecated(
-    message = "Use SandboxedSdkProviderAdapter from sdkruntime-provider library",
-    replaceWith =
-        ReplaceWith(
-            expression = "SandboxedSdkProviderAdapter",
-            imports =
-                arrayOf("androidx.privacysandbox.sdkruntime.provider.SandboxedSdkProviderAdapter")
-        ),
-    level = DeprecationLevel.HIDDEN
-)
-@RestrictTo(RestrictTo.Scope.LIBRARY) // removing from public API surface
-class SandboxedSdkProviderAdapter
-internal constructor(private val classNameProvider: CompatClassNameProvider) :
-    SandboxedSdkProvider() {
-
-    /** Provides classname of [SandboxedSdkProviderCompat] implementation. */
-    internal interface CompatClassNameProvider {
-        fun getCompatProviderClassName(context: Context): String
-    }
-
-    constructor() : this(DefaultClassNameProvider())
-
-    internal val delegate: SandboxedSdkProviderCompat by lazy {
-        val currentContext = context!!
-        val compatSdkProviderClassName =
-            classNameProvider.getCompatProviderClassName(currentContext)
-        val clz = Class.forName(compatSdkProviderClassName)
-        val newDelegate = clz.getConstructor().newInstance() as SandboxedSdkProviderCompat
-        newDelegate.attachContext(currentContext)
-        newDelegate
-    }
-
-    @Throws(LoadSdkException::class)
-    override fun onLoadSdk(params: Bundle): SandboxedSdk {
-        return try {
-            delegate.onLoadSdk(params).toSandboxedSdk()
-        } catch (e: LoadSdkCompatException) {
-            throw e.toLoadSdkException()
-        }
-    }
-
-    override fun beforeUnloadSdk() {
-        delegate.beforeUnloadSdk()
-    }
-
-    override fun getView(windowContext: Context, params: Bundle, width: Int, height: Int): View {
-        return delegate.getView(windowContext, params, width, height)
-    }
-
-    private class DefaultClassNameProvider : CompatClassNameProvider {
-        override fun getCompatProviderClassName(context: Context): String {
-            // TODO(b/257966930) Read classname from SDK manifest property
-            return context.assets.open(COMPAT_SDK_PROVIDER_CLASS_ASSET_NAME).use { inputStream ->
-                inputStream.bufferedReader().readLine()
-            }
-        }
-    }
-
-    private companion object {
-        private const val COMPAT_SDK_PROVIDER_CLASS_ASSET_NAME =
-            "SandboxedSdkProviderCompatClassName.txt"
-    }
-}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientApiVersion.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientApiVersion.kt
index 4120fcb..f519e3c 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientApiVersion.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientApiVersion.kt
@@ -33,10 +33,6 @@
     val apiLevel: Int,
     private val newFeatures: Set<ClientFeature> = emptySet()
 ) {
-    V1__1_0_ALPHA01(apiLevel = 1),
-    V2__1_0_ALPHA02(apiLevel = 2, newFeatures = setOf(ClientFeature.SDK_SANDBOX_CONTROLLER)),
-
-    // V3 was released as V4 (original release postponed)
     V4__1_0_ALPHA05(
         apiLevel = 4,
         newFeatures =
@@ -46,6 +42,7 @@
             )
     ),
     V5__1_0_ALPHA13(apiLevel = 5, newFeatures = setOf(ClientFeature.LOAD_SDK)),
+    V6__1_0_ALPHA14(apiLevel = 6, newFeatures = setOf(ClientFeature.GET_CLIENT_PACKAGE_NAME)),
 
     /**
      * Unreleased API version. Features not added to other versions will be automatically added here
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientFeature.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientFeature.kt
index 4940fce..8d5a6e8 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientFeature.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/internal/ClientFeature.kt
@@ -25,9 +25,6 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 enum class ClientFeature {
-    /** Support for retrieving [SdkSandboxControllerCompat] on SDK side. */
-    SDK_SANDBOX_CONTROLLER,
-
     /**
      * Support for starting SDK Activity:
      * [SdkSandboxControllerCompat.registerSdkSandboxActivityHandler]
diff --git a/privacysandbox/sdkruntime/test-sdks/v1/build.gradle b/privacysandbox/sdkruntime/test-sdks/v1/build.gradle
deleted file mode 100644
index c00c814..0000000
--- a/privacysandbox/sdkruntime/test-sdks/v1/build.gradle
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This file was created using the `create_project.py` script located in the
- * `<AndroidX root>/development/project-creator` directory.
- *
- * Please use that script when creating a new project, rather than copying an existing project and
- * modifying its settings.
- */
-import com.android.build.api.artifact.SingleArtifact
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.application")
-    id("org.jetbrains.kotlin.android")
-}
-
-android {
-    namespace "androidx.privacysandbox.sdkruntime.testsdk.v1"
-}
-
-dependencies {
-    implementation("androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha01")
-}
-
-/*
- * Allow integration tests to consume the APK produced by this project
- */
-configurations {
-    testSdkApk {
-        canBeConsumed = true
-        canBeResolved = false
-        attributes {
-            attribute(
-                    LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
-                    objects.named(LibraryElements, "testSdkApk")
-            )
-        }
-    }
-}
-
-androidComponents {
-    beforeVariants(selector().all()) { enabled = buildType == 'release' }
-    onVariants(selector().all().withBuildType("release"), { variant ->
-        artifacts {
-            testSdkApk(variant.artifacts.get(SingleArtifact.APK.INSTANCE))
-        }
-    })
-}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/v1/src/main/AndroidManifest.xml b/privacysandbox/sdkruntime/test-sdks/v1/src/main/AndroidManifest.xml
deleted file mode 100644
index 8de5974..0000000
--- a/privacysandbox/sdkruntime/test-sdks/v1/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/v1/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/CompatProvider.kt b/privacysandbox/sdkruntime/test-sdks/v1/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/CompatProvider.kt
deleted file mode 100644
index 6aab774..0000000
--- a/privacysandbox/sdkruntime/test-sdks/v1/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/CompatProvider.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.privacysandbox.sdkruntime.testsdk
-
-import android.content.Context
-import android.os.Binder
-import android.os.Bundle
-import android.view.View
-import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
-import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
-import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
-
-@Suppress("unused") // Reflection usage from tests in privacysandbox:sdkruntime:sdkruntime-client
-class CompatProvider : SandboxedSdkProviderCompat() {
-    @JvmField var onLoadSdkBinder: Binder? = null
-
-    @JvmField var lastOnLoadSdkParams: Bundle? = null
-
-    @JvmField var isBeforeUnloadSdkCalled = false
-
-    @Throws(LoadSdkCompatException::class)
-    override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
-        val result = Binder()
-        onLoadSdkBinder = result
-
-        lastOnLoadSdkParams = params
-        if (params.getBoolean("needFail", false)) {
-            throw LoadSdkCompatException(RuntimeException(), params)
-        }
-        return SandboxedSdkCompat(result)
-    }
-
-    override fun beforeUnloadSdk() {
-        isBeforeUnloadSdkCalled = true
-    }
-
-    override fun getView(windowContext: Context, params: Bundle, width: Int, height: Int): View {
-        return View(windowContext)
-    }
-}
diff --git a/privacysandbox/sdkruntime/test-sdks/v2/build.gradle b/privacysandbox/sdkruntime/test-sdks/v2/build.gradle
deleted file mode 100644
index 0678870..0000000
--- a/privacysandbox/sdkruntime/test-sdks/v2/build.gradle
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This file was created using the `create_project.py` script located in the
- * `<AndroidX root>/development/project-creator` directory.
- *
- * Please use that script when creating a new project, rather than copying an existing project and
- * modifying its settings.
- */
-import com.android.build.api.artifact.SingleArtifact
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.application")
-    id("org.jetbrains.kotlin.android")
-}
-
-android {
-    namespace "androidx.privacysandbox.sdkruntime.testsdk.v2"
-}
-
-dependencies {
-    implementation("androidx.privacysandbox.sdkruntime:sdkruntime-core:1.0.0-alpha02")
-}
-
-/*
- * Allow integration tests to consume the APK produced by this project
- */
-configurations {
-    testSdkApk {
-        canBeConsumed = true
-        canBeResolved = false
-        attributes {
-            attribute(
-                    LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
-                    objects.named(LibraryElements, "testSdkApk")
-            )
-        }
-    }
-}
-
-androidComponents {
-    beforeVariants(selector().all()) { enabled = buildType == 'release' }
-    onVariants(selector().all().withBuildType("release"), { variant ->
-        artifacts {
-            testSdkApk(variant.artifacts.get(SingleArtifact.APK.INSTANCE))
-        }
-    })
-}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/v2/src/main/AndroidManifest.xml b/privacysandbox/sdkruntime/test-sdks/v2/src/main/AndroidManifest.xml
deleted file mode 100644
index 8de5974..0000000
--- a/privacysandbox/sdkruntime/test-sdks/v2/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/v6/build.gradle b/privacysandbox/sdkruntime/test-sdks/v6/build.gradle
new file mode 100644
index 0000000..9ea383a
--- /dev/null
+++ b/privacysandbox/sdkruntime/test-sdks/v6/build.gradle
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+import com.android.build.api.artifact.SingleArtifact
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    namespace "androidx.privacysandbox.sdkruntime.testsdk.v6"
+}
+
+dependencies {
+    implementation(libs.kotlinCoroutinesCore)
+    implementation(project(":privacysandbox:sdkruntime:sdkruntime-provider"))
+}
+
+/*
+ * Allow integration tests to consume the APK produced by this project
+ */
+configurations {
+    testSdkApk {
+        canBeConsumed = true
+        canBeResolved = false
+        attributes {
+            attribute(
+                    LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
+                    objects.named(LibraryElements, "testSdkApk")
+            )
+        }
+    }
+}
+
+androidComponents {
+    beforeVariants(selector().all()) { enabled = buildType == 'release' }
+    onVariants(selector().all().withBuildType("release"), { variant ->
+        artifacts {
+            testSdkApk(variant.artifacts.get(SingleArtifact.APK.INSTANCE))
+        }
+    })
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/v6/src/main/AndroidManifest.xml b/privacysandbox/sdkruntime/test-sdks/v6/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5c1d335
--- /dev/null
+++ b/privacysandbox/sdkruntime/test-sdks/v6/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/v2/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/CompatProvider.kt b/privacysandbox/sdkruntime/test-sdks/v6/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/CompatProvider.kt
similarity index 100%
rename from privacysandbox/sdkruntime/test-sdks/v2/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/CompatProvider.kt
rename to privacysandbox/sdkruntime/test-sdks/v6/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/CompatProvider.kt
diff --git a/privacysandbox/tools/tools/build.gradle b/privacysandbox/tools/tools/build.gradle
index 50c9f7a..c988ce8 100644
--- a/privacysandbox/tools/tools/build.gradle
+++ b/privacysandbox/tools/tools/build.gradle
@@ -38,5 +38,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Tools for communicating with SDKs running in the Privacy Sandbox."
-    metalavaK2UastEnabled = true
 }
diff --git a/privacysandbox/ui/integration-tests/mediateesdkprovider/build.gradle b/privacysandbox/ui/integration-tests/mediateesdkprovider/build.gradle
index 90f3d17..4ac1624 100644
--- a/privacysandbox/ui/integration-tests/mediateesdkprovider/build.gradle
+++ b/privacysandbox/ui/integration-tests/mediateesdkprovider/build.gradle
@@ -37,7 +37,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation project(':privacysandbox:ui:integration-tests:testaidl')
     implementation project(':privacysandbox:ui:ui-core')
     implementation project(':privacysandbox:ui:ui-provider')
diff --git a/privacysandbox/ui/integration-tests/mediateesdkprovider/src/main/java/androidx/privacysandbox/ui/integration/mediateesdkprovider/MediateeSdkApi.kt b/privacysandbox/ui/integration-tests/mediateesdkprovider/src/main/java/androidx/privacysandbox/ui/integration/mediateesdkprovider/MediateeSdkApi.kt
deleted file mode 100644
index 96e38f2..0000000
--- a/privacysandbox/ui/integration-tests/mediateesdkprovider/src/main/java/androidx/privacysandbox/ui/integration/mediateesdkprovider/MediateeSdkApi.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.privacysandbox.ui.integration.mediateesdkprovider
-
-import android.content.Context
-import android.os.Bundle
-import androidx.privacysandbox.ui.core.SandboxedUiAdapter
-import androidx.privacysandbox.ui.integration.sdkproviderutils.SdkApiConstants.Companion.AdType
-import androidx.privacysandbox.ui.integration.sdkproviderutils.TestAdapters
-import androidx.privacysandbox.ui.integration.sdkproviderutils.ViewabilityHandler
-import androidx.privacysandbox.ui.integration.testaidl.IMediateeSdkApi
-import androidx.privacysandbox.ui.provider.toCoreLibInfo
-
-class MediateeSdkApi(private val sdkContext: Context) : IMediateeSdkApi.Stub() {
-    private val testAdapters = TestAdapters(sdkContext)
-
-    override fun loadBannerAd(
-        @AdType adType: Int,
-        withSlowDraw: Boolean,
-        drawViewability: Boolean
-    ): Bundle {
-        val adapter: SandboxedUiAdapter =
-            when (adType) {
-                AdType.WEBVIEW -> loadWebViewBannerAd()
-                AdType.WEBVIEW_FROM_LOCAL_ASSETS -> loadWebViewBannerAdFromLocalAssets()
-                else -> loadNonWebViewBannerAd("Mediation", withSlowDraw)
-            }
-        ViewabilityHandler.addObserverFactoryToAdapter(adapter, drawViewability)
-        return adapter.toCoreLibInfo(sdkContext)
-    }
-
-    private fun loadWebViewBannerAd(): SandboxedUiAdapter {
-        return testAdapters.WebViewBannerAd()
-    }
-
-    private fun loadWebViewBannerAdFromLocalAssets(): SandboxedUiAdapter {
-        return testAdapters.WebViewAdFromLocalAssets()
-    }
-
-    private fun loadNonWebViewBannerAd(
-        text: String,
-        waitInsideOnDraw: Boolean
-    ): SandboxedUiAdapter {
-        return testAdapters.TestBannerAd(text, waitInsideOnDraw)
-    }
-}
diff --git a/privacysandbox/ui/integration-tests/mediateesdkprovider/src/main/java/androidx/privacysandbox/ui/integration/mediateesdkprovider/SdkProviderImpl.kt b/privacysandbox/ui/integration-tests/mediateesdkprovider/src/main/java/androidx/privacysandbox/ui/integration/mediateesdkprovider/SdkProviderImpl.kt
index a9a50b1..7d6c6ba8 100644
--- a/privacysandbox/ui/integration-tests/mediateesdkprovider/src/main/java/androidx/privacysandbox/ui/integration/mediateesdkprovider/SdkProviderImpl.kt
+++ b/privacysandbox/ui/integration-tests/mediateesdkprovider/src/main/java/androidx/privacysandbox/ui/integration/mediateesdkprovider/SdkProviderImpl.kt
@@ -24,13 +24,14 @@
 import android.os.ext.SdkExtensions
 import android.view.View
 import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ui.integration.sdkproviderutils.MediateeSdkApiImpl
 
 // TODO(b/257429573): Remove this line once fixed.
 @SuppressLint("ClassVerificationFailure")
 @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
 class SdkProviderImpl : SandboxedSdkProvider() {
     override fun onLoadSdk(p0: Bundle): SandboxedSdk {
-        return SandboxedSdk(MediateeSdkApi(context!!))
+        return SandboxedSdk(MediateeSdkApiImpl(context!!))
     }
 
     override fun getView(p0: Context, p1: Bundle, p2: Int, p3: Int): View {
diff --git a/privacysandbox/ui/integration-tests/sdkproviderutils/build.gradle b/privacysandbox/ui/integration-tests/sdkproviderutils/build.gradle
index d83b172..2a9b90d9c 100644
--- a/privacysandbox/ui/integration-tests/sdkproviderutils/build.gradle
+++ b/privacysandbox/ui/integration-tests/sdkproviderutils/build.gradle
@@ -27,5 +27,6 @@
 dependencies {
     implementation project(':privacysandbox:ui:ui-core')
     implementation project(':privacysandbox:ui:ui-provider')
+    implementation project(':privacysandbox:ui:integration-tests:testaidl')
     implementation project(':webkit:webkit')
 }
diff --git a/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/MediateeSdkApiImpl.kt b/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/MediateeSdkApiImpl.kt
new file mode 100644
index 0000000..e647c7a
--- /dev/null
+++ b/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/MediateeSdkApiImpl.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.privacysandbox.ui.integration.sdkproviderutils
+
+import android.content.Context
+import android.os.Build
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.integration.sdkproviderutils.SdkApiConstants.Companion.AdType
+import androidx.privacysandbox.ui.integration.testaidl.IMediateeSdkApi
+import androidx.privacysandbox.ui.provider.toCoreLibInfo
+
+class MediateeSdkApiImpl(private val sdkContext: Context) : IMediateeSdkApi.Stub() {
+
+    private val testAdapters = TestAdapters(sdkContext)
+    private val mediationDescription =
+        if (CompatImpl.isAppOwnedMediatee()) {
+            "App Owned Mediation"
+        } else "Runtime Mediation"
+
+    override fun loadBannerAd(
+        @AdType adType: Int,
+        waitInsideOnDraw: Boolean,
+        drawViewability: Boolean
+    ): Bundle {
+        val adapter: SandboxedUiAdapter =
+            when (adType) {
+                AdType.WEBVIEW -> loadWebViewBannerAd()
+                AdType.WEBVIEW_FROM_LOCAL_ASSETS -> loadWebViewBannerAdFromLocalAssets()
+                else -> loadNonWebViewBannerAd(mediationDescription, waitInsideOnDraw)
+            }
+        ViewabilityHandler.addObserverFactoryToAdapter(adapter, drawViewability)
+        return adapter.toCoreLibInfo(sdkContext)
+    }
+
+    private fun loadWebViewBannerAd(): SandboxedUiAdapter {
+        return testAdapters.WebViewBannerAd()
+    }
+
+    private fun loadWebViewBannerAdFromLocalAssets(): SandboxedUiAdapter {
+        return testAdapters.WebViewAdFromLocalAssets()
+    }
+
+    private fun loadNonWebViewBannerAd(
+        text: String,
+        waitInsideOnDraw: Boolean
+    ): SandboxedUiAdapter {
+        return testAdapters.TestBannerAd(text, waitInsideOnDraw)
+    }
+
+    private object CompatImpl {
+        fun isAppOwnedMediatee(): Boolean {
+            return if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
+                true
+            } else {
+                Api34PlusImpl.isAppOwnedMediatee()
+            }
+        }
+
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        private object Api34PlusImpl {
+            fun isAppOwnedMediatee(): Boolean {
+                return !android.os.Process.isSdkSandbox()
+            }
+        }
+    }
+}
diff --git a/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt b/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt
index 71d47a9..e2faa3d 100644
--- a/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt
+++ b/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt
@@ -145,6 +145,16 @@
         private val text: String
     ) : View(context) {
 
+        init {
+            setOnClickListener {
+                Log.i(TAG, "Click on ad detected")
+                val visitUrl = Intent(Intent.ACTION_VIEW)
+                visitUrl.data = Uri.parse(GOOGLE_URL)
+                visitUrl.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                context.startActivity(visitUrl)
+            }
+        }
+
         private val viewColor = Color.rgb((0..255).random(), (0..255).random(), (0..255).random())
 
         @SuppressLint("BanThreadSleep")
@@ -161,14 +171,6 @@
 
             canvas.drawColor(viewColor)
             canvas.drawText(text, 75F, 75F, paint)
-
-            setOnClickListener {
-                Log.i(TAG, "Click on ad detected")
-                val visitUrl = Intent(Intent.ACTION_VIEW)
-                visitUrl.data = Uri.parse(GOOGLE_URL)
-                visitUrl.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                context.startActivity(visitUrl)
-            }
         }
     }
 
diff --git a/privacysandbox/ui/integration-tests/testaidl/build.gradle b/privacysandbox/ui/integration-tests/testaidl/build.gradle
index f51a34a..81f35c4 100644
--- a/privacysandbox/ui/integration-tests/testaidl/build.gradle
+++ b/privacysandbox/ui/integration-tests/testaidl/build.gradle
@@ -23,10 +23,6 @@
 android {
     namespace "androidx.privacysandbox.ui.integration.testaidl"
 
-    defaultConfig {
-        minSdk 33
-    }
-
     buildTypes {
         release {
             minifyEnabled false
diff --git a/privacysandbox/ui/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/ui/integration/testaidl/IAppOwnedMediateeSdkApi.aidl b/privacysandbox/ui/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/ui/integration/testaidl/IAppOwnedMediateeSdkApi.aidl
deleted file mode 100644
index 4174096..0000000
--- a/privacysandbox/ui/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/ui/integration/testaidl/IAppOwnedMediateeSdkApi.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.privacysandbox.ui.integration.testaidl;
-
-import android.os.Bundle;
-
-interface IAppOwnedMediateeSdkApi {
-    Bundle loadBannerAd(int adType, boolean withSlowDraw, boolean drawViewability);
-}
diff --git a/privacysandbox/ui/integration-tests/testapp/build.gradle b/privacysandbox/ui/integration-tests/testapp/build.gradle
index 60816a5..8ea67e8 100644
--- a/privacysandbox/ui/integration-tests/testapp/build.gradle
+++ b/privacysandbox/ui/integration-tests/testapp/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.privacysandbox.ui.integration.testapp"
 
     defaultConfig {
@@ -37,7 +38,7 @@
 
 dependencies {
     implementation(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation 'androidx.core:core-ktx:1.8.0'
     implementation 'androidx.appcompat:appcompat:1.6.0'
     implementation 'com.google.android.material:material:1.6.0'
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/AppOwnedMediateeSdkApi.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/AppOwnedMediateeSdkApi.kt
deleted file mode 100644
index 0abd0aa..0000000
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/AppOwnedMediateeSdkApi.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.privacysandbox.ui.integration.testapp
-
-import android.content.Context
-import android.os.Bundle
-import androidx.privacysandbox.ui.core.SandboxedUiAdapter
-import androidx.privacysandbox.ui.integration.sdkproviderutils.SdkApiConstants.Companion.AdType
-import androidx.privacysandbox.ui.integration.sdkproviderutils.TestAdapters
-import androidx.privacysandbox.ui.integration.sdkproviderutils.ViewabilityHandler
-import androidx.privacysandbox.ui.integration.testaidl.IAppOwnedMediateeSdkApi
-import androidx.privacysandbox.ui.provider.toCoreLibInfo
-
-class AppOwnedMediateeSdkApi(private val sdkContext: Context) : IAppOwnedMediateeSdkApi.Stub() {
-    private val testAdapters = TestAdapters(sdkContext)
-
-    override fun loadBannerAd(
-        @AdType adType: Int,
-        waitInsideOnDraw: Boolean,
-        drawViewability: Boolean
-    ): Bundle {
-        val adapter: SandboxedUiAdapter =
-            when (adType) {
-                AdType.WEBVIEW -> loadWebViewBannerAd()
-                AdType.WEBVIEW_FROM_LOCAL_ASSETS -> loadWebViewBannerAdFromLocalAssets()
-                else -> loadNonWebViewBannerAd("AppOwnedMediation", waitInsideOnDraw)
-            }
-        ViewabilityHandler.addObserverFactoryToAdapter(adapter, drawViewability)
-        return adapter.toCoreLibInfo(sdkContext)
-    }
-
-    private fun loadWebViewBannerAd(): SandboxedUiAdapter {
-        return testAdapters.WebViewBannerAd()
-    }
-
-    private fun loadWebViewBannerAdFromLocalAssets(): SandboxedUiAdapter {
-        return testAdapters.WebViewAdFromLocalAssets()
-    }
-
-    private fun loadNonWebViewBannerAd(
-        text: String,
-        waitInsideOnDraw: Boolean
-    ): SandboxedUiAdapter {
-        return testAdapters.TestBannerAd(text, waitInsideOnDraw)
-    }
-}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
index 24a5562..a54d6f1 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
@@ -34,6 +34,7 @@
 import androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat
 import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
 import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.ui.integration.sdkproviderutils.MediateeSdkApiImpl
 import androidx.privacysandbox.ui.integration.sdkproviderutils.SdkApiConstants.Companion.AdType
 import androidx.privacysandbox.ui.integration.sdkproviderutils.SdkApiConstants.Companion.MediationOption
 import com.google.android.material.navigation.NavigationView
@@ -95,7 +96,7 @@
                         AppOwnedSdkSandboxInterfaceCompat(
                             MEDIATEE_SDK_NAME,
                             /*version=*/ 0,
-                            AppOwnedMediateeSdkApi(applicationContext)
+                            MediateeSdkApiImpl(applicationContext)
                         )
                     )
                 }
diff --git a/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle b/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle
index 8519843..3c96f20 100644
--- a/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle
+++ b/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle
@@ -37,7 +37,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation project(':privacysandbox:ui:integration-tests:testaidl')
     implementation project(':privacysandbox:ui:integration-tests:sdkproviderutils')
     implementation project(':privacysandbox:ui:ui-core')
diff --git a/privacysandbox/ui/integration-tests/testsdkprovider/lint-baseline.xml b/privacysandbox/ui/integration-tests/testsdkprovider/lint-baseline.xml
deleted file mode 100644
index 264a6f3..0000000
--- a/privacysandbox/ui/integration-tests/testsdkprovider/lint-baseline.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ui.integration.testsdkprovider.SdkProviderImpl is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="class SdkProviderImpl : SandboxedSdkProvider() {"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkProviderImpl.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ui.integration.testsdkprovider.SdkProviderImpl is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return SandboxedSdk(SdkApi(context!!))"
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkProviderImpl.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 34; however, the containing class androidx.privacysandbox.ui.integration.testsdkprovider.SdkProviderImpl is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        return SandboxedSdk(SdkApi(context!!))"
-        errorLine2="                                   ~~~~~~~">
-        <location
-            file="src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkProviderImpl.kt"/>
-    </issue>
-
-</issues>
diff --git a/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt b/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
index ed223c4..054ec2d 100644
--- a/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
+++ b/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
@@ -25,7 +25,6 @@
 import androidx.privacysandbox.ui.integration.sdkproviderutils.SdkApiConstants.Companion.MediationOption
 import androidx.privacysandbox.ui.integration.sdkproviderutils.TestAdapters
 import androidx.privacysandbox.ui.integration.sdkproviderutils.ViewabilityHandler
-import androidx.privacysandbox.ui.integration.testaidl.IAppOwnedMediateeSdkApi
 import androidx.privacysandbox.ui.integration.testaidl.IMediateeSdkApi
 import androidx.privacysandbox.ui.integration.testaidl.ISdkApi
 import androidx.privacysandbox.ui.provider.toCoreLibInfo
@@ -104,7 +103,7 @@
             appOwnedSdkSandboxInterfaces.forEach { appOwnedSdkSandboxInterfaceCompat ->
                 if (appOwnedSdkSandboxInterfaceCompat.getName() == MEDIATEE_SDK) {
                     val appOwnedMediateeSdkApi =
-                        IAppOwnedMediateeSdkApi.Stub.asInterface(
+                        IMediateeSdkApi.Stub.asInterface(
                             appOwnedSdkSandboxInterfaceCompat.getInterface()
                         )
                     return appOwnedMediateeSdkApi.loadBannerAd(
diff --git a/privacysandbox/ui/ui-client/build.gradle b/privacysandbox/ui/ui-client/build.gradle
index 932b569..5d5456b 100644
--- a/privacysandbox/ui/ui-client/build.gradle
+++ b/privacysandbox/ui/ui-client/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("androidx.core:core:1.12.0")
 
@@ -57,6 +57,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.privacysandbox.ui.client"
 }
 
@@ -65,6 +66,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "show UI from an SDKRuntime aware SDK"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
index 7eb9e7a..a14cd24 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
@@ -31,6 +31,7 @@
 import android.view.ViewTreeObserver
 import android.widget.LinearLayout
 import android.widget.ScrollView
+import androidx.lifecycle.Lifecycle
 import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState
 import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionStateChangedListener
 import androidx.privacysandbox.ui.client.view.SandboxedSdkView
@@ -146,6 +147,10 @@
             assertThat(openSessionLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
         }
 
+        internal fun assertSessionNotOpened() {
+            assertThat(openSessionLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isFalse()
+        }
+
         internal fun wasNotifyResizedCalled(): Boolean {
             return resizeLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)
         }
@@ -296,6 +301,17 @@
     }
 
     @Test
+    fun sessionNotOpenedWhenWindowIsNotVisible() {
+        // the window is not visible when the activity is in the CREATED state.
+        activityScenarioRule.scenario.moveToState(Lifecycle.State.CREATED)
+        addViewToLayout()
+        testSandboxedUiAdapter.assertSessionNotOpened()
+        // the window becomes visible when the activity is in the STARTED state.
+        activityScenarioRule.scenario.moveToState(Lifecycle.State.STARTED)
+        testSandboxedUiAdapter.assertSessionOpened()
+    }
+
+    @Test
     fun onAttachedToWindowTest() {
         addViewToLayout()
         testSandboxedUiAdapter.assertSessionOpened()
@@ -518,6 +534,7 @@
         surfaceView.addOnAttachStateChangeListener(
             object : View.OnAttachStateChangeListener {
                 override fun onViewAttachedToWindow(p0: View) {
+                    @Suppress("DEPRECATION")
                     token = surfaceView.hostToken
                     surfaceViewLatch.countDown()
                 }
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
index ffdafcc..39f555f 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
@@ -30,7 +30,6 @@
 import android.view.ViewGroup
 import android.view.ViewParent
 import android.view.ViewTreeObserver
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.customview.poolingcontainer.PoolingContainerListener
@@ -206,7 +205,12 @@
     private fun checkClientOpenSession() {
         val adapter = adapter
         if (
-            client == null && adapter != null && windowInputToken != null && width > 0 && height > 0
+            client == null &&
+                adapter != null &&
+                windowInputToken != null &&
+                width > 0 &&
+                height > 0 &&
+                windowVisibility == View.VISIBLE
         ) {
             stateListenerManager.currentUiSessionState = SandboxedSdkUiSessionState.Loading
             client = Client(this)
@@ -362,6 +366,13 @@
         signalMeasurer?.maybeSendSignals()
     }
 
+    override fun onWindowVisibilityChanged(visibility: Int) {
+        super.onWindowVisibilityChanged(visibility)
+        if (visibility == VISIBLE) {
+            checkClientOpenSession()
+        }
+    }
+
     override fun setAlpha(alpha: Float) {
         super.setAlpha(alpha)
         signalMeasurer?.maybeSendSignals()
@@ -629,7 +640,6 @@
         private object Api34PlusImpl {
 
             @JvmStatic
-            @DoNotInline
             fun setClippingBounds(
                 contentView: View?,
                 isAttachedToWindow: Boolean,
@@ -667,7 +677,6 @@
             }
 
             @JvmStatic
-            @DoNotInline
             fun attachTemporarySurfaceViewAndOpenSession(
                 context: Context,
                 sandboxedSdkView: SandboxedSdkView
@@ -677,6 +686,7 @@
                     object : OnAttachStateChangeListener {
                         override fun onViewAttachedToWindow(view: View) {
                             view.removeOnAttachStateChangeListener(this)
+                            @Suppress("DEPRECATION")
                             sandboxedSdkView.windowInputToken = surfaceView.hostToken
                             sandboxedSdkView.removeTemporarySurfaceView(surfaceView)
                             sandboxedSdkView.checkClientOpenSession()
@@ -693,7 +703,6 @@
         private object Api29PlusImpl {
 
             @JvmStatic
-            @DoNotInline
             fun registerFrameCommitCallback(observer: ViewTreeObserver, callback: Runnable) {
                 observer.registerFrameCommitCallback(callback)
             }
diff --git a/privacysandbox/ui/ui-core/build.gradle b/privacysandbox/ui/ui-core/build.gradle
index e6df2ad..7feb06c 100644
--- a/privacysandbox/ui/ui-core/build.gradle
+++ b/privacysandbox/ui/ui-core/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
@@ -57,6 +57,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "contains core definitions for the privacysandbox ui library."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/privacysandbox/ui/ui-provider/build.gradle b/privacysandbox/ui/ui-provider/build.gradle
index d7ff1ce..73065aa 100644
--- a/privacysandbox/ui/ui-provider/build.gradle
+++ b/privacysandbox/ui/ui-provider/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation project(path: ':privacysandbox:ui:ui-core')
     implementation("androidx.core:core:1.12.0")
@@ -61,6 +61,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "lets an SdkRuntime aware SDK share UI with a client application."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
index a993e74..ecb4e64 100644
--- a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
+++ b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
@@ -29,7 +29,6 @@
 import android.view.Display
 import android.view.SurfaceControlViewHost
 import android.view.View
-import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.privacysandbox.ui.core.IRemoteSessionClient
@@ -319,7 +318,6 @@
         private object Api34PlusImpl {
 
             @JvmStatic
-            @DoNotInline
             fun createSurfaceControlViewHost(
                 context: Context,
                 display: Display,
diff --git a/privacysandbox/ui/ui-tests/build.gradle b/privacysandbox/ui/ui-tests/build.gradle
index 69746fb..5d00b1e 100644
--- a/privacysandbox/ui/ui-tests/build.gradle
+++ b/privacysandbox/ui/ui-tests/build.gradle
@@ -50,6 +50,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.privacysandbox.ui.tests"
 }
 
diff --git a/profileinstaller/integration-tests/profile-verification-sample-no-initializer/build.gradle b/profileinstaller/integration-tests/profile-verification-sample-no-initializer/build.gradle
index d1e8d26..4e59817 100644
--- a/profileinstaller/integration-tests/profile-verification-sample-no-initializer/build.gradle
+++ b/profileinstaller/integration-tests/profile-verification-sample-no-initializer/build.gradle
@@ -32,6 +32,8 @@
 // This project can be removed once b/239659205 has landed and use only the
 // profile-verification-sample project.
 android {
+    compileSdk 35
+
     buildTypes {
         release {
             // Minification and shrinking are disabled to avoid r8 removing unused methods and
diff --git a/profileinstaller/integration-tests/profile-verification-sample/build.gradle b/profileinstaller/integration-tests/profile-verification-sample/build.gradle
index 9a8a31d..91cf92f 100644
--- a/profileinstaller/integration-tests/profile-verification-sample/build.gradle
+++ b/profileinstaller/integration-tests/profile-verification-sample/build.gradle
@@ -29,6 +29,8 @@
     id("kotlin-android")
 }
 android {
+    compileSdk 35
+
     buildTypes {
         release {
             // Minification and shrinking are disabled to avoid r8 removing unused methods and
diff --git a/profileinstaller/integration-tests/profile-verification/build.gradle b/profileinstaller/integration-tests/profile-verification/build.gradle
index 54bf77a..cf670e9 100644
--- a/profileinstaller/integration-tests/profile-verification/build.gradle
+++ b/profileinstaller/integration-tests/profile-verification/build.gradle
@@ -83,6 +83,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 23
     }
diff --git a/profileinstaller/profileinstaller/build.gradle b/profileinstaller/profileinstaller/build.gradle
index 724107c1..cd8c23f 100644
--- a/profileinstaller/profileinstaller/build.gradle
+++ b/profileinstaller/profileinstaller/build.gradle
@@ -33,7 +33,7 @@
     api("androidx.startup:startup-runtime:1.1.1")
     api(libs.guavaListenableFuture)
     implementation("androidx.concurrent:concurrent-futures:1.1.0")
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     testImplementation(libs.junit)
     testImplementation(libs.truth)
 }
@@ -44,7 +44,6 @@
     inceptionYear = "2021"
     description = "Allows libraries to prepopulate ahead of time compilation traces to be read by" +
             " ART"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallerInitializer.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallerInitializer.java
index d90e0ee..4814766f 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallerInitializer.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileInstallerInitializer.java
@@ -22,7 +22,6 @@
 import android.os.Looper;
 import android.view.Choreographer;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.startup.Initializer;
@@ -150,7 +149,6 @@
         }
 
         // avoid aligning with vsync when available (API 28+)
-        @DoNotInline
         public static Handler createAsync(Looper looper) {
             return Handler.createAsync(looper);
         }
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVerifier.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVerifier.java
index 7bea88d..2d5b62a 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVerifier.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/ProfileVerifier.java
@@ -33,7 +33,6 @@
 import android.content.res.AssetFileDescriptor;
 import android.os.Build;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -618,7 +617,6 @@
         private Api33Impl() {
         }
 
-        @DoNotInline
         static PackageInfo getPackageInfo(
                 PackageManager packageManager,
                 Context context) throws PackageManager.NameNotFoundException {
diff --git a/recommendation/recommendation/build.gradle b/recommendation/recommendation/build.gradle
index 1873083..e14ff18 100644
--- a/recommendation/recommendation/build.gradle
+++ b/recommendation/recommendation/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 android {
@@ -26,5 +26,4 @@
     inceptionYear = "2015"
     description = "Android Support Recommendation"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/recyclerview/recyclerview-benchmark/build.gradle b/recyclerview/recyclerview-benchmark/build.gradle
index 55bfdaa..e609dfe 100644
--- a/recyclerview/recyclerview-benchmark/build.gradle
+++ b/recyclerview/recyclerview-benchmark/build.gradle
@@ -34,6 +34,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.recyclerview.benchmark"
 }
 
diff --git a/recyclerview/recyclerview-selection/build.gradle b/recyclerview/recyclerview-selection/build.gradle
index 779bf23..2b260e3 100644
--- a/recyclerview/recyclerview-selection/build.gradle
+++ b/recyclerview/recyclerview-selection/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     api("androidx.recyclerview:recyclerview:1.2.0")
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
@@ -49,7 +49,6 @@
     mavenVersion = LibraryVersions.RECYCLERVIEW_SELECTION
     inceptionYear = "2017"
     description = "Library providing item selection framework for RecyclerView. Support for touch based and band selection is provided."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index c4d6592..70a4363 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":core:core"))
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.customview:customview:1.0.0")
@@ -55,6 +55,7 @@
     }
 
     defaultConfig {
+        compileSdk = 35
         testInstrumentationRunner "androidx.recyclerview.test.TestRunner"
     }
     namespace "androidx.recyclerview"
@@ -67,5 +68,4 @@
     inceptionYear = "2014"
     description = "Android Support RecyclerView"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewScrollFrameRateTest.kt b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewScrollFrameRateTest.kt
new file mode 100644
index 0000000..1972555
--- /dev/null
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewScrollFrameRateTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:Suppress("DEPRECATION")
+
+package androidx.recyclerview.widget
+
+import android.graphics.Color
+import android.os.Build
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.widget.TextView
+import androidx.core.os.BuildCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.rule.ActivityTestRule
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+// TODO: change to VANILLA_ICE_CREAM when it is ready
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+@RunWith(AndroidJUnit4::class)
+class RecyclerViewScrollFrameRateTest {
+    @get:Rule val rule = ActivityTestRule(TestContentViewActivity::class.java)
+
+    @Test
+    fun smoothScrollFrameRateBoost() {
+        // TODO: Remove when VANILLA_ICE_CREAM is ready and the SdkSuppress is modified
+        if (!BuildCompat.isAtLeastV()) {
+            return
+        }
+        val rv = RecyclerView(rule.activity)
+        rule.runOnUiThread {
+            rv.layoutManager =
+                LinearLayoutManager(rule.activity, LinearLayoutManager.VERTICAL, false)
+            rv.adapter =
+                object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+                    override fun onCreateViewHolder(
+                        parent: ViewGroup,
+                        viewType: Int
+                    ): RecyclerView.ViewHolder {
+                        val view = TextView(parent.context)
+                        view.textSize = 40f
+                        view.setTextColor(Color.WHITE)
+                        return object : RecyclerView.ViewHolder(view) {}
+                    }
+
+                    override fun getItemCount(): Int = 10000
+
+                    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+                        val view = holder.itemView as TextView
+                        view.text = "Text $position"
+                        val color = if (position % 2 == 0) Color.BLACK else 0xFF000080.toInt()
+                        view.setBackgroundColor(color)
+                    }
+                }
+            rule.activity.contentView.addView(rv)
+        }
+        runOnDraw(rv, { rv.smoothScrollBy(0, 1000) }) {
+            // First Frame
+            assertThat(rv.frameContentVelocity).isGreaterThan(0f)
+        }
+
+        // Second frame
+        runOnDraw(rv) { assertThat(rv.frameContentVelocity).isGreaterThan(0f) }
+
+        // Third frame
+        runOnDraw(rv) { assertThat(rv.frameContentVelocity).isGreaterThan(0f) }
+    }
+
+    private fun runOnDraw(view: View, setup: () -> Unit = {}, onDraw: () -> Unit) {
+        val latch = CountDownLatch(1)
+        val onDrawListener =
+            ViewTreeObserver.OnDrawListener {
+                latch.countDown()
+                onDraw()
+            }
+        rule.runOnUiThread {
+            view.viewTreeObserver.addOnDrawListener(onDrawListener)
+            setup()
+        }
+        assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue()
+        rule.runOnUiThread { view.viewTreeObserver.removeOnDrawListener(onDrawListener) }
+    }
+}
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index 80be2f0..9d4ef74 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -64,12 +64,15 @@
 import android.widget.OverScroller;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.Px;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.os.BuildCompat;
 import androidx.core.os.TraceCompat;
 import androidx.core.util.Preconditions;
 import androidx.core.view.AccessibilityDelegateCompat;
@@ -6005,6 +6008,10 @@
                         mGapWorker.postFromTraversal(RecyclerView.this, consumedX, consumedY);
                     }
                 }
+                if (BuildCompat.isAtLeastV()) {
+                    Api35Impl.setFrameContentVelocity(RecyclerView.this,
+                            Math.abs(scroller.getCurrVelocity()));
+                }
             }
 
             SmoothScroller smoothScroller = mLayout.mSmoothScroller;
@@ -14627,4 +14634,16 @@
         }
         return mScrollingChildHelper;
     }
+
+    @RequiresApi(35)
+    private static final class Api35Impl {
+        @DoNotInline
+        public static void setFrameContentVelocity(View view, float velocity) {
+            try {
+                view.setFrameContentVelocity(velocity);
+            } catch (LinkageError e) {
+                // The setFrameContentVelocity method is unavailable on this device.
+            }
+        }
+    }
 }
diff --git a/remotecallback/remotecallback-processor/build.gradle b/remotecallback/remotecallback-processor/build.gradle
index 932f574..8b860be 100644
--- a/remotecallback/remotecallback-processor/build.gradle
+++ b/remotecallback/remotecallback-processor/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(libs.javapoet)
 }
 
diff --git a/remotecallback/remotecallback/build.gradle b/remotecallback/remotecallback/build.gradle
index b7c4754..e5ff2a7 100644
--- a/remotecallback/remotecallback/build.gradle
+++ b/remotecallback/remotecallback/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(project(":collection:collection"))
 
     androidTestImplementation(libs.testExtJunit)
@@ -50,5 +50,4 @@
     inceptionYear = "2018"
     description = "Wraps PendingIntents in a friendly and easier way to handle remote callbacks"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/resourceinspection/resourceinspection-annotation/build.gradle b/resourceinspection/resourceinspection-annotation/build.gradle
index 1476034..73d5094 100644
--- a/resourceinspection/resourceinspection-annotation/build.gradle
+++ b/resourceinspection/resourceinspection-annotation/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
@@ -37,5 +37,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Annotation processors for Android resource and layout inspection"
-    metalavaK2UastEnabled = true
 }
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 215c248..5bd9f17 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -59,6 +59,8 @@
     implementation(project(":room:room-common"))
     implementation(project(":room:room-runtime"))
     implementation(project(":room:room-paging"))
+    implementation(project(":paging:paging-runtime"))
+    implementation(project(":paging:paging-guava"))
     implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
     implementation(libs.kotlinStdlib)
@@ -92,8 +94,11 @@
     androidTestImplementation(project(":room:room-rxjava3"))
     androidTestImplementation(project(":room:room-ktx"))
     androidTestImplementation(project(":internal-testutils-common"))
+    androidTestImplementation(project(":paging:paging-runtime"))
+    androidTestImplementation(project(":paging:paging-guava"))
+    androidTestImplementation(project(":paging:paging-rxjava2"))
+    androidTestImplementation(project(":paging:paging-rxjava3"))
     androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
-    androidTestImplementation("androidx.paging:paging-runtime:3.1.1")
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
     androidTestImplementation(libs.rxjava2)
     androidTestImplementation(libs.kotlinCoroutinesTest)
diff --git a/room/integration-tests/kotlintestapp/lint-baseline.xml b/room/integration-tests/kotlintestapp/lint-baseline.xml
index 714f298..e0c18dc 100644
--- a/room/integration-tests/kotlintestapp/lint-baseline.xml
+++ b/room/integration-tests/kotlintestapp/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                Thread.sleep(3_500)"
-        errorLine2="                       ~~~~~">
+        errorLine1="                    Thread.sleep(3_500)"
+        errorLine2="                           ~~~~~">
         <location
             file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultiTypedPagingSourceTest.kt"/>
     </issue>
@@ -13,8 +13,8 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                        .doOnSubscribe { Thread.sleep(500) }"
-        errorLine2="                                                ~~~~~">
+        errorLine1="                                .doOnSubscribe { Thread.sleep(500) }"
+        errorLine2="                                                        ~~~~~">
         <location
             file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/Rx2PagingSourceTest.kt"/>
     </issue>
@@ -22,8 +22,8 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                        .doOnSubscribe { Thread.sleep(500) }"
-        errorLine2="                                                ~~~~~">
+        errorLine1="                                .doOnSubscribe { Thread.sleep(500) }"
+        errorLine2="                                                        ~~~~~">
         <location
             file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/Rx2PagingSourceTest.kt"/>
     </issue>
@@ -31,8 +31,8 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                        .doOnSubscribe { Thread.sleep(500) }"
-        errorLine2="                                                ~~~~~">
+        errorLine1="                                .doOnSubscribe { Thread.sleep(500) }"
+        errorLine2="                                                        ~~~~~">
         <location
             file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/Rx2PagingSourceTest.kt"/>
     </issue>
@@ -40,8 +40,8 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                        .doOnSubscribe { Thread.sleep(500) }"
-        errorLine2="                                                ~~~~~">
+        errorLine1="                                .doOnSubscribe { Thread.sleep(500) }"
+        errorLine2="                                                        ~~~~~">
         <location
             file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/Rx3PagingSourceTest.kt"/>
     </issue>
@@ -49,8 +49,8 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                        .doOnSubscribe { Thread.sleep(500) }"
-        errorLine2="                                                ~~~~~">
+        errorLine1="                                .doOnSubscribe { Thread.sleep(500) }"
+        errorLine2="                                                        ~~~~~">
         <location
             file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/Rx3PagingSourceTest.kt"/>
     </issue>
@@ -58,8 +58,8 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                        .doOnSubscribe { Thread.sleep(500) }"
-        errorLine2="                                                ~~~~~">
+        errorLine1="                                .doOnSubscribe { Thread.sleep(500) }"
+        errorLine2="                                                        ~~~~~">
         <location
             file="src/androidTest/java/androidx/room/integration/kotlintestapp/test/Rx3PagingSourceTest.kt"/>
     </issue>
diff --git a/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json b/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json
index 86da3aa..f917333 100644
--- a/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json
+++ b/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json
@@ -2,7 +2,7 @@
   "formatVersion": 1,
   "database": {
     "version": 3,
-    "identityHash": "7a7934a196e6d6eb3055b9957c91490c",
+    "identityHash": "3c5d400c89fa6324dee0aebb20d9c386",
     "entities": [
       {
         "tableName": "Entity1",
@@ -33,9 +33,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity2",
@@ -73,9 +71,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity3",
@@ -99,9 +95,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity4",
@@ -132,9 +126,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity5",
@@ -165,9 +157,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity6",
@@ -197,9 +187,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity7",
@@ -230,9 +218,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity8",
@@ -263,13 +249,11 @@
           "columnNames": [
             "name"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity9",
-        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`id`) REFERENCES `Entity27`(`id27`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
         "fields": [
           {
             "fieldPath": "id",
@@ -296,21 +280,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": [
-          {
-            "table": "Entity27",
-            "onDelete": "NO ACTION",
-            "onUpdate": "NO ACTION",
-            "columns": [
-              "id"
-            ],
-            "referencedColumns": [
-              "id27"
-            ]
-          }
-        ]
+        }
       },
       {
         "tableName": "Entity10",
@@ -396,9 +366,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity12",
@@ -440,8 +408,7 @@
             "orders": [],
             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity12_name` ON `${TABLE_NAME}` (`name`)"
           }
-        ],
-        "foreignKeys": []
+        ]
       },
       {
         "tableName": "Entity13_V2",
@@ -483,8 +450,7 @@
             "orders": [],
             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity13_V2_addedInV1` ON `${TABLE_NAME}` (`addedInV1`)"
           }
-        ],
-        "foreignKeys": []
+        ]
       },
       {
         "tableName": "Entity14",
@@ -508,9 +474,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity15",
@@ -534,9 +498,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity16",
@@ -567,9 +529,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity17",
@@ -600,9 +560,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity19_V2",
@@ -633,9 +591,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity20_V2",
@@ -673,28 +629,9 @@
           "columnNames": [
             "name"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
-        "ftsVersion": "FTS4",
-        "ftsOptions": {
-          "tokenizer": "simple",
-          "tokenizerArgs": [],
-          "contentTable": "Entity13_V2",
-          "languageIdColumnName": "",
-          "matchInfo": "FTS4",
-          "notIndexedColumns": [],
-          "prefixSizes": [],
-          "preferredOrder": "ASC"
-        },
-        "contentSyncTriggers": [
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END",
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END"
-        ],
         "tableName": "Entity21",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, content=`Entity13_V2`)",
         "fields": [
@@ -718,22 +655,25 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
-      },
-      {
         "ftsVersion": "FTS4",
         "ftsOptions": {
           "tokenizer": "simple",
           "tokenizerArgs": [],
-          "contentTable": "",
+          "contentTable": "Entity13_V2",
           "languageIdColumnName": "",
           "matchInfo": "FTS4",
           "notIndexedColumns": [],
           "prefixSizes": [],
           "preferredOrder": "ASC"
         },
-        "contentSyncTriggers": [],
+        "contentSyncTriggers": [
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END"
+        ]
+      },
+      {
         "tableName": "Entity22",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
         "fields": [
@@ -757,11 +697,7 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
-      },
-      {
-        "ftsVersion": "FTS3",
+        "ftsVersion": "FTS4",
         "ftsOptions": {
           "tokenizer": "simple",
           "tokenizerArgs": [],
@@ -772,7 +708,9 @@
           "prefixSizes": [],
           "preferredOrder": "ASC"
         },
-        "contentSyncTriggers": [],
+        "contentSyncTriggers": []
+      },
+      {
         "tableName": "Entity23",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2)",
         "fields": [
@@ -803,10 +741,6 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
-      },
-      {
         "ftsVersion": "FTS3",
         "ftsOptions": {
           "tokenizer": "simple",
@@ -818,7 +752,9 @@
           "prefixSizes": [],
           "preferredOrder": "ASC"
         },
-        "contentSyncTriggers": [],
+        "contentSyncTriggers": []
+      },
+      {
         "tableName": "Entity24",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
         "fields": [
@@ -842,8 +778,18 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
       },
       {
         "tableName": "Entity25",
@@ -874,9 +820,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity26",
@@ -918,8 +862,7 @@
             "orders": [],
             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity26_addedInV2` ON `${TABLE_NAME}` (`addedInV2`)"
           }
-        ],
-        "foreignKeys": []
+        ]
       },
       {
         "tableName": "Entity27",
@@ -944,9 +887,7 @@
           "columnNames": [
             "id27"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       }
     ],
     "views": [
@@ -957,7 +898,7 @@
     ],
     "setupQueries": [
       "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
-      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7a7934a196e6d6eb3055b9957c91490c')"
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3c5d400c89fa6324dee0aebb20d9c386')"
     ]
   }
 }
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/4.json b/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/4.json
new file mode 100644
index 0000000..82b6b46
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/4.json
@@ -0,0 +1,904 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 4,
+    "identityHash": "0c6b4e93c555aa8a32696b0b37548014",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity4",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity5",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity6",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity7",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity8",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity9",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity10",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`addedInV1`) REFERENCES `Entity13_V2`(`renamedInV4`) ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity10_addedInV1",
+            "unique": true,
+            "columnNames": [
+              "addedInV1"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity10_addedInV1` ON `${TABLE_NAME}` (`addedInV1`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "Entity13_V2",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "addedInV1"
+            ],
+            "referencedColumns": [
+              "renamedInV4"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "Entity11",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity12",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity12_name",
+            "unique": true,
+            "columnNames": [
+              "name"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity12_name` ON `${TABLE_NAME}` (`name`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity13_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity13_V2_renamedInV4",
+            "unique": true,
+            "columnNames": [
+              "renamedInV4"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity13_V2_renamedInV4` ON `${TABLE_NAME}` (`renamedInV4`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity14",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity15",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity16",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity17",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity19_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity20_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity21",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, content=`Entity13_V2`)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "Entity13_V2",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": [
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END"
+        ]
+      },
+      {
+        "tableName": "Entity22",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity23",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity24",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity25",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `entity1Id` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entity1Id",
+            "columnName": "entity1Id",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity26",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity26_addedInV2",
+            "unique": true,
+            "columnNames": [
+              "addedInV2"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity26_addedInV2` ON `${TABLE_NAME}` (`addedInV2`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity27",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id27` INTEGER NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id27`))",
+        "fields": [
+          {
+            "fieldPath": "id27",
+            "columnName": "id27",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id27"
+          ]
+        }
+      }
+    ],
+    "views": [
+      {
+        "viewName": "Entity25Detail",
+        "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT Entity25.id, Entity25.name, Entity25.entity1Id, Entity1.name AS userNameAndId FROM Entity25 INNER JOIN Entity1 ON Entity25.entity1Id = Entity1.id"
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0c6b4e93c555aa8a32696b0b37548014')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/5.json b/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/5.json
new file mode 100644
index 0000000..0f26f70
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas-ksp/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/5.json
@@ -0,0 +1,917 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 5,
+    "identityHash": "89ede8289d7f14d05af6781044295bb3",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity4",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity5",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity6",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity7",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity8",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity9",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`id`) REFERENCES `Entity27`(`id27`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "foreignKeys": [
+          {
+            "table": "Entity27",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "id"
+            ],
+            "referencedColumns": [
+              "id27"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "Entity10",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`addedInV1`) REFERENCES `Entity13_V2`(`renamedInV4`) ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity10_addedInV1",
+            "unique": true,
+            "columnNames": [
+              "addedInV1"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity10_addedInV1` ON `${TABLE_NAME}` (`addedInV1`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "Entity13_V2",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "addedInV1"
+            ],
+            "referencedColumns": [
+              "renamedInV4"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "Entity11",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity12",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity12_name",
+            "unique": true,
+            "columnNames": [
+              "name"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity12_name` ON `${TABLE_NAME}` (`name`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity13_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity13_V2_renamedInV4",
+            "unique": true,
+            "columnNames": [
+              "renamedInV4"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity13_V2_renamedInV4` ON `${TABLE_NAME}` (`renamedInV4`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity14",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity15",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity16",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity17",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity19_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity20_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity21",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, content=`Entity13_V2`)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "Entity13_V2",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": [
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END"
+        ]
+      },
+      {
+        "tableName": "Entity22",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity23",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity24",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity25",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `entity1Id` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entity1Id",
+            "columnName": "entity1Id",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity26",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity26_addedInV2",
+            "unique": true,
+            "columnNames": [
+              "addedInV2"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity26_addedInV2` ON `${TABLE_NAME}` (`addedInV2`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity27",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id27` INTEGER NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id27`))",
+        "fields": [
+          {
+            "fieldPath": "id27",
+            "columnName": "id27",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id27"
+          ]
+        }
+      }
+    ],
+    "views": [
+      {
+        "viewName": "Entity25Detail",
+        "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT Entity25.id, Entity25.name, Entity25.entity1Id, Entity1.name AS userNameAndId FROM Entity25 INNER JOIN Entity1 ON Entity25.entity1Id = Entity1.id"
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '89ede8289d7f14d05af6781044295bb3')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json b/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json
index 86da3aa..f917333 100644
--- a/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json
+++ b/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/3.json
@@ -2,7 +2,7 @@
   "formatVersion": 1,
   "database": {
     "version": 3,
-    "identityHash": "7a7934a196e6d6eb3055b9957c91490c",
+    "identityHash": "3c5d400c89fa6324dee0aebb20d9c386",
     "entities": [
       {
         "tableName": "Entity1",
@@ -33,9 +33,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity2",
@@ -73,9 +71,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity3",
@@ -99,9 +95,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity4",
@@ -132,9 +126,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity5",
@@ -165,9 +157,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity6",
@@ -197,9 +187,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity7",
@@ -230,9 +218,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity8",
@@ -263,13 +249,11 @@
           "columnNames": [
             "name"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity9",
-        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`id`) REFERENCES `Entity27`(`id27`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
         "fields": [
           {
             "fieldPath": "id",
@@ -296,21 +280,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": [
-          {
-            "table": "Entity27",
-            "onDelete": "NO ACTION",
-            "onUpdate": "NO ACTION",
-            "columns": [
-              "id"
-            ],
-            "referencedColumns": [
-              "id27"
-            ]
-          }
-        ]
+        }
       },
       {
         "tableName": "Entity10",
@@ -396,9 +366,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity12",
@@ -440,8 +408,7 @@
             "orders": [],
             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity12_name` ON `${TABLE_NAME}` (`name`)"
           }
-        ],
-        "foreignKeys": []
+        ]
       },
       {
         "tableName": "Entity13_V2",
@@ -483,8 +450,7 @@
             "orders": [],
             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity13_V2_addedInV1` ON `${TABLE_NAME}` (`addedInV1`)"
           }
-        ],
-        "foreignKeys": []
+        ]
       },
       {
         "tableName": "Entity14",
@@ -508,9 +474,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity15",
@@ -534,9 +498,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity16",
@@ -567,9 +529,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity17",
@@ -600,9 +560,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity19_V2",
@@ -633,9 +591,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity20_V2",
@@ -673,28 +629,9 @@
           "columnNames": [
             "name"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
-        "ftsVersion": "FTS4",
-        "ftsOptions": {
-          "tokenizer": "simple",
-          "tokenizerArgs": [],
-          "contentTable": "Entity13_V2",
-          "languageIdColumnName": "",
-          "matchInfo": "FTS4",
-          "notIndexedColumns": [],
-          "prefixSizes": [],
-          "preferredOrder": "ASC"
-        },
-        "contentSyncTriggers": [
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END",
-          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END"
-        ],
         "tableName": "Entity21",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, content=`Entity13_V2`)",
         "fields": [
@@ -718,22 +655,25 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
-      },
-      {
         "ftsVersion": "FTS4",
         "ftsOptions": {
           "tokenizer": "simple",
           "tokenizerArgs": [],
-          "contentTable": "",
+          "contentTable": "Entity13_V2",
           "languageIdColumnName": "",
           "matchInfo": "FTS4",
           "notIndexedColumns": [],
           "prefixSizes": [],
           "preferredOrder": "ASC"
         },
-        "contentSyncTriggers": [],
+        "contentSyncTriggers": [
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `addedInV1`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`addedInV1`); END"
+        ]
+      },
+      {
         "tableName": "Entity22",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
         "fields": [
@@ -757,11 +697,7 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
-      },
-      {
-        "ftsVersion": "FTS3",
+        "ftsVersion": "FTS4",
         "ftsOptions": {
           "tokenizer": "simple",
           "tokenizerArgs": [],
@@ -772,7 +708,9 @@
           "prefixSizes": [],
           "preferredOrder": "ASC"
         },
-        "contentSyncTriggers": [],
+        "contentSyncTriggers": []
+      },
+      {
         "tableName": "Entity23",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2)",
         "fields": [
@@ -803,10 +741,6 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
-      },
-      {
         "ftsVersion": "FTS3",
         "ftsOptions": {
           "tokenizer": "simple",
@@ -818,7 +752,9 @@
           "prefixSizes": [],
           "preferredOrder": "ASC"
         },
-        "contentSyncTriggers": [],
+        "contentSyncTriggers": []
+      },
+      {
         "tableName": "Entity24",
         "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
         "fields": [
@@ -842,8 +778,18 @@
             "rowid"
           ]
         },
-        "indices": [],
-        "foreignKeys": []
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
       },
       {
         "tableName": "Entity25",
@@ -874,9 +820,7 @@
           "columnNames": [
             "id"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       },
       {
         "tableName": "Entity26",
@@ -918,8 +862,7 @@
             "orders": [],
             "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity26_addedInV2` ON `${TABLE_NAME}` (`addedInV2`)"
           }
-        ],
-        "foreignKeys": []
+        ]
       },
       {
         "tableName": "Entity27",
@@ -944,9 +887,7 @@
           "columnNames": [
             "id27"
           ]
-        },
-        "indices": [],
-        "foreignKeys": []
+        }
       }
     ],
     "views": [
@@ -957,7 +898,7 @@
     ],
     "setupQueries": [
       "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
-      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7a7934a196e6d6eb3055b9957c91490c')"
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3c5d400c89fa6324dee0aebb20d9c386')"
     ]
   }
 }
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/4.json b/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/4.json
new file mode 100644
index 0000000..82b6b46
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/4.json
@@ -0,0 +1,904 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 4,
+    "identityHash": "0c6b4e93c555aa8a32696b0b37548014",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity4",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity5",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity6",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity7",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity8",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity9",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity10",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`addedInV1`) REFERENCES `Entity13_V2`(`renamedInV4`) ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity10_addedInV1",
+            "unique": true,
+            "columnNames": [
+              "addedInV1"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity10_addedInV1` ON `${TABLE_NAME}` (`addedInV1`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "Entity13_V2",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "addedInV1"
+            ],
+            "referencedColumns": [
+              "renamedInV4"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "Entity11",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity12",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity12_name",
+            "unique": true,
+            "columnNames": [
+              "name"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity12_name` ON `${TABLE_NAME}` (`name`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity13_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity13_V2_renamedInV4",
+            "unique": true,
+            "columnNames": [
+              "renamedInV4"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity13_V2_renamedInV4` ON `${TABLE_NAME}` (`renamedInV4`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity14",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity15",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity16",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity17",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity19_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity20_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity21",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, content=`Entity13_V2`)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "Entity13_V2",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": [
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END"
+        ]
+      },
+      {
+        "tableName": "Entity22",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity23",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity24",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity25",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `entity1Id` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entity1Id",
+            "columnName": "entity1Id",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity26",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity26_addedInV2",
+            "unique": true,
+            "columnNames": [
+              "addedInV2"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity26_addedInV2` ON `${TABLE_NAME}` (`addedInV2`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity27",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id27` INTEGER NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id27`))",
+        "fields": [
+          {
+            "fieldPath": "id27",
+            "columnName": "id27",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id27"
+          ]
+        }
+      }
+    ],
+    "views": [
+      {
+        "viewName": "Entity25Detail",
+        "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT Entity25.id, Entity25.name, Entity25.entity1Id, Entity1.name AS userNameAndId FROM Entity25 INNER JOIN Entity1 ON Entity25.entity1Id = Entity1.id"
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0c6b4e93c555aa8a32696b0b37548014')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/5.json b/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/5.json
new file mode 100644
index 0000000..0f26f70
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/schemas/androidx.room.integration.kotlintestapp.migration.AutoMigrationDb/5.json
@@ -0,0 +1,917 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 5,
+    "identityHash": "89ede8289d7f14d05af6781044295bb3",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity3",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity4",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity5",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity6",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity7",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity8",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity9",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`id`) REFERENCES `Entity27`(`id27`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "foreignKeys": [
+          {
+            "table": "Entity27",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "id"
+            ],
+            "referencedColumns": [
+              "id27"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "Entity10",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`), FOREIGN KEY(`addedInV1`) REFERENCES `Entity13_V2`(`renamedInV4`) ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity10_addedInV1",
+            "unique": true,
+            "columnNames": [
+              "addedInV1"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity10_addedInV1` ON `${TABLE_NAME}` (`addedInV1`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "Entity13_V2",
+            "onDelete": "NO ACTION",
+            "onUpdate": "NO ACTION",
+            "columns": [
+              "addedInV1"
+            ],
+            "referencedColumns": [
+              "renamedInV4"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "Entity11",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity12",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity12_name",
+            "unique": true,
+            "columnNames": [
+              "name"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity12_name` ON `${TABLE_NAME}` (`name`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity13_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity13_V2_renamedInV4",
+            "unique": true,
+            "columnNames": [
+              "renamedInV4"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity13_V2_renamedInV4` ON `${TABLE_NAME}` (`renamedInV4`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity14",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity15",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity16",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity17",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity19_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity20_V2",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `renamedInV2` TEXT NOT NULL DEFAULT '1', `addedInV2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV2",
+            "columnName": "renamedInV2",
+            "affinity": "TEXT",
+            "notNull": true,
+            "defaultValue": "'1'"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "name"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity21",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `renamedInV4` INTEGER NOT NULL DEFAULT 1, content=`Entity13_V2`)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "renamedInV4",
+            "columnName": "renamedInV4",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "Entity13_V2",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": [
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_UPDATE BEFORE UPDATE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_BEFORE_DELETE BEFORE DELETE ON `Entity13_V2` BEGIN DELETE FROM `Entity21` WHERE `docid`=OLD.`rowid`; END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_UPDATE AFTER UPDATE ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END",
+          "CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_Entity21_AFTER_INSERT AFTER INSERT ON `Entity13_V2` BEGIN INSERT INTO `Entity21`(`docid`, `name`, `renamedInV4`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`renamedInV4`); END"
+        ]
+      },
+      {
+        "tableName": "Entity22",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS4",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity23",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, `addedInV2` INTEGER NOT NULL DEFAULT 2)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity24",
+        "createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS3(`name` TEXT NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1)",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "rowid"
+          ]
+        },
+        "ftsVersion": "FTS3",
+        "ftsOptions": {
+          "tokenizer": "simple",
+          "tokenizerArgs": [],
+          "contentTable": "",
+          "languageIdColumnName": "",
+          "matchInfo": "FTS4",
+          "notIndexedColumns": [],
+          "prefixSizes": [],
+          "preferredOrder": "ASC"
+        },
+        "contentSyncTriggers": []
+      },
+      {
+        "tableName": "Entity25",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `entity1Id` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entity1Id",
+            "columnName": "entity1Id",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        }
+      },
+      {
+        "tableName": "Entity26",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `addedInV2` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Entity26_addedInV2",
+            "unique": true,
+            "columnNames": [
+              "addedInV2"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Entity26_addedInV2` ON `${TABLE_NAME}` (`addedInV2`)"
+          }
+        ]
+      },
+      {
+        "tableName": "Entity27",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id27` INTEGER NOT NULL, `addedInV1` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id27`))",
+        "fields": [
+          {
+            "fieldPath": "id27",
+            "columnName": "id27",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "addedInV1",
+            "columnName": "addedInV1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "id27"
+          ]
+        }
+      }
+    ],
+    "views": [
+      {
+        "viewName": "Entity25Detail",
+        "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT Entity25.id, Entity25.name, Entity25.entity1Id, Entity1.name AS userNameAndId FROM Entity25 INNER JOIN Entity1 ON Entity25.entity1Id = Entity1.id"
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '89ede8289d7f14d05af6781044295bb3')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationDb.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationDb.kt
index 39335c1..ca2c0eb 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationDb.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationDb.kt
@@ -70,7 +70,9 @@
     autoMigrations =
         [
             AutoMigration(from = 1, to = 2, spec = AutoMigrationDb.SimpleAutoMigration1::class),
-            AutoMigration(from = 2, to = 3)
+            AutoMigration(from = 2, to = 3),
+            AutoMigration(from = 3, to = 4, spec = AutoMigrationDb.SimpleAutoMigration2::class),
+            AutoMigration(from = 4, to = 5)
         ],
     views = [AutoMigrationDb.Entity25Detail::class],
     exportSchema = true
@@ -192,14 +194,14 @@
     /**
      * Change the foreign key added in Entity 10 to ‘addedInV1’. Add index for addedInV1 on
      * Entity 10. The reference table of the foreign key has been renamed from Entity13 to
-     * Entity13_V2.
+     * Entity13_V2. The foreign key column name of Entity13_V2 is renamed to "renamedInV4" in V4.
      */
     @Entity(
         foreignKeys =
             [
                 ForeignKey(
                     entity = Entity13_V2::class,
-                    parentColumns = ["addedInV1"],
+                    parentColumns = ["renamedInV4"],
                     childColumns = ["addedInV1"],
                     deferred = true
                 )
@@ -242,13 +244,13 @@
 
     /**
      * Rename to Entity13_V2, it is a table referenced by the foreign key in Entity10. Change the
-     * index added in Entity 13 to ‘addedInV1’.
+     * index added in Entity 13 to "addedInV1". Column "addedInV1" is renamed to "renamedInV4".
      */
-    @Entity(indices = [Index(value = ["addedInV1"], unique = true)])
+    @Entity(indices = [Index(value = ["renamedInV4"], unique = true)])
     data class Entity13_V2(
         @PrimaryKey var id: Int,
         var name: String,
-        @ColumnInfo(defaultValue = "1") var addedInV1: Int
+        @ColumnInfo(defaultValue = "1") var renamedInV4: Int
     ) {
         companion object {
             const val TABLE_NAME = "Entity13"
@@ -331,13 +333,16 @@
         }
     }
 
-    /** The content table of this FTS table has been renamed from Entity13 to Entity13_V2. */
+    /**
+     * The content table of this FTS table has been renamed from Entity13 to Entity13_V2. The column
+     * "addedInV1" was renamed to "renamedInV4".
+     */
     @Entity
     @Fts4(contentEntity = Entity13_V2::class)
     data class Entity21(
         @PrimaryKey var rowid: Int,
         var name: String,
-        @ColumnInfo(defaultValue = "1") var addedInV1: Int
+        @ColumnInfo(defaultValue = "1") var renamedInV4: Int
     ) {
         companion object {
             const val TABLE_NAME = "Entity21"
@@ -455,7 +460,23 @@
         }
     }
 
+    @RenameColumn(
+        tableName = "Entity21",
+        fromColumnName = "addedInV1",
+        toColumnName = "renamedInV4"
+    )
+    @RenameColumn(
+        tableName = "Entity13_V2",
+        fromColumnName = "addedInV1",
+        toColumnName = "renamedInV4"
+    )
+    internal class SimpleAutoMigration2 : AutoMigrationSpec {
+        override fun onPostMigrate(db: SupportSQLiteDatabase) {
+            // Do something
+        }
+    }
+
     companion object {
-        const val LATEST_VERSION = 3
+        const val LATEST_VERSION = 5
     }
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationTest.kt
index 82f2e2a..aec6a83 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/AutoMigrationTest.kt
@@ -60,20 +60,30 @@
     }
 
     @Test
+    @Suppress("DEPRECATION") // Due to TableInfo.read()
     fun goFromV1ToV3() {
         createFirstVersion()
+        val db = helper.runMigrationsAndValidate(TEST_DB, 3, true)
+        val info = read(db, AutoMigrationDb.Entity1.TABLE_NAME)
+        assertThat(info.columns.size).isEqualTo(3)
+    }
+
+    @Test
+    @Suppress("DEPRECATION") // Due to TableInfo.read()
+    fun goFromV1ToV4() {
+        createFirstVersion()
+        val db = helper.runMigrationsAndValidate(TEST_DB, 4, true)
+        val info = read(db, AutoMigrationDb.Entity1.TABLE_NAME)
+        assertThat(info.columns.size).isEqualTo(3)
+    }
+
+    @Test
+    fun goFromV1ToV5() {
+        createFirstVersion()
         try {
-            helper.runMigrationsAndValidate(TEST_DB, 3, true)
+            helper.runMigrationsAndValidate(TEST_DB, 5, true)
         } catch (e: SQLiteException) {
-            assertThat(e.message)
-                .isEqualTo(
-                    """Foreign key violation(s) detected in 'Entity9'.
-Number of different violations discovered: 1
-Number of rows in violation: 2
-Violation(s) detected in the following constraint(s):
-	Parent Table = Entity27, Foreign Key Constraint Index = 0
-"""
-                )
+            assertThat(e.message).contains("""Foreign key violation(s) detected in 'Entity9'""")
         }
     }
 
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/ListenableFuturePagingSourceTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/ListenableFuturePagingSourceTest.kt
index 273649f..e9df8b1 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/ListenableFuturePagingSourceTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/ListenableFuturePagingSourceTest.kt
@@ -117,7 +117,8 @@
         queryExecutor.filterFunction = { runnable ->
             // filtering out the transform async function called inside loadFuture
             // filtering as String b/c `AbstractTransformFuture` is a package-private class
-            !runnable.javaClass.enclosingClass.toString().contains("AbstractTransformFuture")
+            runnable.javaClass.enclosingClass?.toString()?.contains("AbstractTransformFuture") !=
+                true
         }
 
         runTest {
@@ -155,7 +156,9 @@
             queryExecutor.filterFunction = { runnable ->
                 // filtering out the transform async function called inside loadFuture
                 // filtering as String b/c `AbstractTransformFuture` is a package-private class
-                !runnable.javaClass.enclosingClass.toString().contains("AbstractTransformFuture")
+                runnable.javaClass.enclosingClass
+                    ?.toString()
+                    ?.contains("AbstractTransformFuture") != true
             }
 
             // now access more items that should trigger loading more
@@ -202,7 +205,9 @@
             queryExecutor.filterFunction = { runnable ->
                 // filtering out the transform async function called inside loadFuture
                 // filtering as String b/c `AbstractTransformFuture` is a package-private class
-                !runnable.javaClass.enclosingClass.toString().contains("AbstractTransformFuture")
+                runnable.javaClass.enclosingClass
+                    ?.toString()
+                    ?.contains("AbstractTransformFuture") != true
             }
 
             // now access more items that should trigger loading more
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
index 9b52dc5..b576bad 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.integration.kotlintestapp.test
 
-import androidx.arch.core.executor.testing.CountingTaskExecutorRule
 import androidx.kruth.assertThat
 import androidx.room.Dao
 import androidx.room.Database
@@ -31,22 +30,22 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.google.common.util.concurrent.MoreExecutors
 import java.util.concurrent.CopyOnWriteArrayList
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.TestScope
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Before
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QueryInterceptorTest {
-    @Rule @JvmField val countingTaskExecutorRule = CountingTaskExecutorRule()
-    lateinit var mDatabase: QueryInterceptorTestDatabase
-    var queryAndArgs = CopyOnWriteArrayList<Pair<String, ArrayList<Any?>>>()
+    private val testCoroutineScope = TestScope()
+    private lateinit var database: QueryInterceptorTestDatabase
+    private val queryAndArgs = CopyOnWriteArrayList<Pair<String, ArrayList<Any?>>>()
 
     @Entity(tableName = "queryInterceptorTestDatabase")
     data class QueryInterceptorEntity(@PrimaryKey val id: String, val description: String?)
@@ -67,30 +66,29 @@
 
     @Before
     fun setUp() {
-        mDatabase =
+        database =
             Room.inMemoryDatabaseBuilder(
                     ApplicationProvider.getApplicationContext(),
                     QueryInterceptorTestDatabase::class.java
                 )
-                .setQueryCallback(
-                    { sqlQuery, bindArgs ->
-                        val argTrace = ArrayList<Any?>()
-                        argTrace.addAll(bindArgs)
-                        queryAndArgs.add(Pair(sqlQuery, argTrace))
-                    },
-                    MoreExecutors.directExecutor()
-                )
+                .setQueryCoroutineContext(testCoroutineScope.coroutineContext)
+                .setQueryCallback(testCoroutineScope.coroutineContext) { sqlQuery, bindArgs ->
+                    val argTrace = ArrayList<Any?>()
+                    argTrace.addAll(bindArgs)
+                    queryAndArgs.add(Pair(sqlQuery, argTrace))
+                }
                 .build()
     }
 
     @After
     fun tearDown() {
-        mDatabase.close()
+        database.close()
+        testCoroutineScope.cancel()
     }
 
     @Test
     fun testInsert() {
-        mDatabase
+        database
             .queryInterceptorDao()
             .insert(QueryInterceptorEntity("Insert", "Inserted a placeholder query"))
 
@@ -104,17 +102,17 @@
 
     @Test
     fun testDelete() {
-        mDatabase.queryInterceptorDao().delete("Insert")
+        database.queryInterceptorDao().delete("Insert")
         assertQueryLogged("DELETE FROM queryInterceptorTestDatabase WHERE id=?", listOf("Insert"))
         assertTransactionQueries()
     }
 
     @Test
     fun testUpdate() {
-        mDatabase
+        database
             .queryInterceptorDao()
             .insert(QueryInterceptorEntity("Insert", "Inserted a placeholder query"))
-        mDatabase
+        database
             .queryInterceptorDao()
             .update(QueryInterceptorEntity("Insert", "Updated the placeholder query"))
 
@@ -130,10 +128,10 @@
     @Test
     fun testCompileStatement() {
         assertEquals(queryAndArgs.size, 0)
-        mDatabase
+        database
             .queryInterceptorDao()
             .insert(QueryInterceptorEntity("Insert", "Inserted a placeholder query"))
-        mDatabase.openHelper.writableDatabase
+        database.openHelper.writableDatabase
             .compileStatement("DELETE FROM queryInterceptorTestDatabase WHERE id=?")
             .execute()
         assertQueryLogged("DELETE FROM queryInterceptorTestDatabase WHERE id=?", emptyList())
@@ -141,7 +139,7 @@
 
     @Test
     fun testLoggingSupportSQLiteQuery() {
-        mDatabase.openHelper.writableDatabase.query(
+        database.openHelper.writableDatabase.query(
             SimpleSQLiteQuery(
                 "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
                     "VALUES (?,?)",
@@ -157,7 +155,7 @@
 
     @Test
     fun testExecSQLWithBindArgs() {
-        mDatabase.openHelper.writableDatabase.execSQL(
+        database.openHelper.writableDatabase.execSQL(
             "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
                 "VALUES (?,?)",
             arrayOf("3", "Description")
@@ -171,7 +169,7 @@
 
     @Test
     fun testNullBindArgument() {
-        mDatabase.openHelper.writableDatabase.query(
+        database.openHelper.writableDatabase.query(
             SimpleSQLiteQuery(
                 "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
                     "VALUES (?,?)",
@@ -190,11 +188,13 @@
         val sql =
             "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
                 "VALUES (?,?)"
-        val statement = mDatabase.openHelper.writableDatabase.compileStatement(sql)
+        val statement = database.openHelper.writableDatabase.compileStatement(sql)
         statement.bindString(1, "ID")
         statement.bindNull(2)
         statement.execute()
 
+        testCoroutineScope.testScheduler.advanceUntilIdle()
+
         val filteredQueries = queryAndArgs.filter { (query, _) -> query == sql }
 
         assertThat(filteredQueries).hasSize(1)
@@ -210,20 +210,18 @@
                     ApplicationProvider.getApplicationContext(),
                     QueryInterceptorTestDatabase::class.java
                 )
-                .setQueryCallback(
-                    { sqlQuery, bindArgs ->
-                        val argTrace = ArrayList<Any?>()
-                        argTrace.addAll(bindArgs)
-                        queryAndArgs.add(Pair(sqlQuery, argTrace))
-                    },
-                    MoreExecutors.directExecutor()
-                )
+                .setQueryCoroutineContext(testCoroutineScope.coroutineContext)
+                .setQueryCallback(testCoroutineScope.coroutineContext) { sqlQuery, bindArgs ->
+                    val argTrace = ArrayList<Any?>()
+                    argTrace.addAll(bindArgs)
+                    queryAndArgs.add(Pair(sqlQuery, argTrace))
+                }
 
         dbBuilder.build().close()
 
-        mDatabase = dbBuilder.build()
+        database = dbBuilder.build()
 
-        mDatabase
+        database
             .queryInterceptorDao()
             .insert(QueryInterceptorEntity("Insert", "Inserted a placeholder query"))
 
@@ -236,12 +234,14 @@
     }
 
     private fun assertQueryLogged(query: String, expectedArgs: List<String?>) {
+        testCoroutineScope.testScheduler.advanceUntilIdle()
         val filteredQueries = queryAndArgs.filter { it.first == query }
         assertThat(filteredQueries).hasSize(1)
         assertThat(expectedArgs).containsExactlyElementsIn(filteredQueries[0].second)
     }
 
     private fun assertTransactionQueries() {
+        testCoroutineScope.testScheduler.advanceUntilIdle()
         assertNotNull(queryAndArgs.any { it.equals("BEGIN TRANSACTION") })
         assertNotNull(queryAndArgs.any { it.equals("TRANSACTION SUCCESSFUL") })
         assertNotNull(queryAndArgs.any { it.equals("END TRANSACTION") })
diff --git a/room/integration-tests/multiplatformtestapp/build.gradle b/room/integration-tests/multiplatformtestapp/build.gradle
index 5c5f167..67a8e2e 100644
--- a/room/integration-tests/multiplatformtestapp/build.gradle
+++ b/room/integration-tests/multiplatformtestapp/build.gradle
@@ -93,7 +93,8 @@
 // TODO(b/325111583): Create a helper function to configure KSP with KMP targets
 dependencies {
     def roomCompilerDependency = project(":room:room-compiler")
-    add("kspAndroidAndroidTest", roomCompilerDependency)
+    add("kspAndroidAndroidTest", roomCompilerDependency) // Android instrumentation test
+    add("kspAndroidTest", roomCompilerDependency) // Android unit test
     add("kspJvmTest", roomCompilerDependency)
     add("kspLinuxX64Test", roomCompilerDependency)
     if (KmpPlatformsKt.enableMac(project)) {
diff --git a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
index f9784c0..b49c392 100644
--- a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
@@ -30,10 +30,10 @@
     private val file = instrumentation.targetContext.getDatabasePath("test.db")
 
     override fun getRoomDatabaseBuilder(): RoomDatabase.Builder<SampleDatabase> {
-        return Room.databaseBuilder(
+        return Room.databaseBuilder<SampleDatabase>(
                 context = instrumentation.targetContext,
                 name = file.path,
-                factory = { SampleDatabase::class.instantiateImpl() }
+                factory = SampleDatabaseConstructor::initialize
             )
             .setDriver(BundledSQLiteDriver())
     }
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt
index 430f552..1e9bc88 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt
@@ -20,6 +20,7 @@
 import androidx.kruth.assertThrows
 import androidx.room.AutoMigration
 import androidx.room.ColumnInfo
+import androidx.room.ConstructedBy
 import androidx.room.Dao
 import androidx.room.Database
 import androidx.room.Entity
@@ -28,6 +29,8 @@
 import androidx.room.ProvidedAutoMigrationSpec
 import androidx.room.Query
 import androidx.room.RoomDatabase
+import androidx.room.RoomDatabaseConstructor
+import androidx.room.integration.multiplatformtestapp.test.BaseAutoMigrationTest.AutoMigrationDatabase
 import androidx.room.migration.AutoMigrationSpec
 import androidx.room.testing.MigrationTestHelper
 import androidx.sqlite.SQLiteConnection
@@ -115,6 +118,12 @@
             .contains("Unexpected auto migration specs found.")
     }
 
+    @Test
+    fun subclassedProvidedAutoMigrationSpec() {
+        val db = getDatabaseBuilder().addAutoMigrationSpec(SubProvidedSpecFrom2To3()).build()
+        db.close()
+    }
+
     @Entity
     data class AutoMigrationEntity(
         @PrimaryKey val pk: Long,
@@ -139,6 +148,7 @@
                 AutoMigration(from = 2, to = 3, spec = ProvidedSpecFrom2To3::class)
             ]
     )
+    @ConstructedBy(BaseAutoMigrationTest_AutoMigrationDatabaseConstructor::class)
     abstract class AutoMigrationDatabase : RoomDatabase() {
         abstract fun dao(): AutoMigrationDao
     }
@@ -150,5 +160,10 @@
         }
     }
 
+    class SubProvidedSpecFrom2To3 : ProvidedSpecFrom2To3()
+
     class ExtraProvidedSpec : AutoMigrationSpec
 }
+
+expect object BaseAutoMigrationTest_AutoMigrationDatabaseConstructor :
+    RoomDatabaseConstructor<AutoMigrationDatabase>
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseMigrationTest.kt
index 632466138..a4b3b08 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseMigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseMigrationTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.kruth.assertThat
 import androidx.kruth.assertThrows
+import androidx.room.ConstructedBy
 import androidx.room.Dao
 import androidx.room.Database
 import androidx.room.Entity
@@ -25,6 +26,8 @@
 import androidx.room.PrimaryKey
 import androidx.room.Query
 import androidx.room.RoomDatabase
+import androidx.room.RoomDatabaseConstructor
+import androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase
 import androidx.room.migration.Migration
 import androidx.room.testing.MigrationTestHelper
 import androidx.sqlite.SQLiteConnection
@@ -248,7 +251,11 @@
         version = 2,
         exportSchema = true,
     )
+    @ConstructedBy(BaseMigrationTest_MigrationDatabaseConstructor::class)
     abstract class MigrationDatabase : RoomDatabase() {
         abstract fun dao(): MigrationDao
     }
 }
+
+expect object BaseMigrationTest_MigrationDatabaseConstructor :
+    RoomDatabaseConstructor<MigrationDatabase>
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
index 8aca8a4..78e59dc 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.kruth.assertThat
 import androidx.kruth.assertThrows
+import androidx.room.RoomRawQuery
 import androidx.room.execSQL
 import androidx.room.immediateTransaction
 import androidx.room.useReaderConnection
@@ -59,6 +60,18 @@
         val dao = db.dao()
         assertThat(dao.insertItem(1)).isEqualTo(1)
         assertThat(dao.getSingleItem().pk).isEqualTo(1)
+        assertThat(dao.getSingleItemSkipVerification().pk).isEqualTo(1)
+        assertThat(dao.getSingleItemRaw(RoomRawQuery("SELECT * FROM SampleEntity")).pk).isEqualTo(1)
+        assertThat(
+                dao.getSingleItemRaw(
+                        RoomRawQuery(
+                            sql = "SELECT * FROM SampleEntity WHERE pk = ?",
+                            onBindStatement = { it.bindLong(1, 1) }
+                        )
+                    )
+                    .pk
+            )
+            .isEqualTo(1)
         assertThat(dao.deleteItem(1)).isEqualTo(1)
         assertThat(dao.deleteItem(1)).isEqualTo(0) // Nothing deleted
         assertThrows<IllegalStateException> { dao.getSingleItem() }
@@ -235,6 +248,9 @@
 
         val map = dao.getMapWithDupeColumns()
         assertThat(map[sampleEntity1]).isEqualTo(sampleEntity2)
+
+        val map2 = dao.getMapWithDupeColumnsSkipVerification()
+        assertThat(map2[sampleEntity1]).isEqualTo(sampleEntity2)
     }
 
     @Test
@@ -466,16 +482,25 @@
 
     @Test
     fun relationManytoMany() = runTest {
-        val sampleEntity1 = SampleEntity(1, 1)
-        val sampleEntity1s = listOf(sampleEntity1, SampleEntity(2, 2))
+        val sampleEntity1 = StringSampleEntity1("1", "1")
+        val sampleEntity1s = listOf(sampleEntity1, StringSampleEntity1("2", "2"))
 
-        val sampleEntity2 = SampleEntity2(1, 1)
-        val sampleEntity2s = listOf(sampleEntity2, SampleEntity2(2, 2))
+        val sampleEntity2 = StringSampleEntity2("1", "1")
+        val sampleEntity2s = listOf(sampleEntity2, StringSampleEntity2("2", "2"))
 
-        db.dao().insertSampleEntityList(sampleEntity1s)
-        db.dao().insertSampleEntity2List(sampleEntity2s)
+        db.dao().insertSampleEntity1WithString(sampleEntity1s)
+        db.dao().insertSampleEntity2WithString(sampleEntity2s)
 
         assertThat(db.dao().getSampleManyToMany())
             .isEqualTo(SampleDao.SampleManyAndMany(sample1 = sampleEntity1, sample2s = listOf()))
     }
+
+    @Test
+    fun invalidRawQueryOnBindStatement() = runTest {
+        val query =
+            RoomRawQuery(sql = "SELECT * FROM SampleEntity", onBindStatement = { it.step() })
+        assertThrows<IllegalStateException> { db.dao().getSingleItemRaw(query) }
+            .hasMessageThat()
+            .contains("Only bind*() calls are allowed")
+    }
 }
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt
index 277fcf4..0d9a877 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.kruth.assertThat
 import androidx.kruth.assertThrows
+import androidx.room.ConstructedBy
 import androidx.room.Dao
 import androidx.room.Database
 import androidx.room.Entity
@@ -26,8 +27,10 @@
 import androidx.room.ProvidedTypeConverter
 import androidx.room.Query
 import androidx.room.RoomDatabase
+import androidx.room.RoomDatabaseConstructor
 import androidx.room.TypeConverter
 import androidx.room.TypeConverters
+import androidx.room.integration.multiplatformtestapp.test.BaseTypeConverterTest.TestDatabase
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
 
@@ -44,6 +47,15 @@
     }
 
     @Test
+    fun entityWithSubclassedConverter() = runTest {
+        val database = getDatabaseBuilder().addTypeConverter(SubBarConverter()).build()
+        val entity = TestEntity(1, Foo(2018), Bar("Estamos Bien"))
+        database.getDao().insertItem(entity)
+        assertThat(database.getDao().getItem(1)).isEqualTo(entity)
+        database.close()
+    }
+
+    @Test
     fun missingTypeConverter() {
         assertThrows<IllegalArgumentException> { getDatabaseBuilder().build() }
             .hasMessageThat()
@@ -56,6 +68,7 @@
 
     @Database(entities = [TestEntity::class], version = 1, exportSchema = false)
     @TypeConverters(FooConverter::class, BarConverter::class)
+    @ConstructedBy(BaseTypeConverterTest_TestDatabaseConstructor::class)
     abstract class TestDatabase : RoomDatabase() {
         abstract fun getDao(): TestDao
     }
@@ -80,9 +93,13 @@
     }
 
     @ProvidedTypeConverter
-    class BarConverter {
+    open class BarConverter {
         @TypeConverter fun toBar(text: String): Bar = Bar(text)
 
         @TypeConverter fun fromBar(bar: Bar): String = bar.text
     }
+
+    class SubBarConverter : BarConverter()
 }
+
+expect object BaseTypeConverterTest_TestDatabaseConstructor : RoomDatabaseConstructor<TestDatabase>
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
index 0583cab..e511fc3 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
@@ -17,6 +17,7 @@
 package androidx.room.integration.multiplatformtestapp.test
 
 import androidx.room.ColumnInfo
+import androidx.room.ConstructedBy
 import androidx.room.Dao
 import androidx.room.Database
 import androidx.room.Delete
@@ -29,9 +30,13 @@
 import androidx.room.MapColumn
 import androidx.room.PrimaryKey
 import androidx.room.Query
+import androidx.room.RawQuery
 import androidx.room.Relation
 import androidx.room.RewriteQueriesToDropUnusedColumns
 import androidx.room.RoomDatabase
+import androidx.room.RoomDatabaseConstructor
+import androidx.room.RoomRawQuery
+import androidx.room.SkipQueryVerification
 import androidx.room.Transaction
 import androidx.room.Update
 import androidx.room.Upsert
@@ -64,13 +69,25 @@
     @ColumnInfo(defaultValue = "0") val dataCopy: Long
 )
 
+@Entity
+data class StringSampleEntity1(
+    @PrimaryKey val stringPk1: String,
+    @ColumnInfo(defaultValue = "0") val data1: String
+)
+
+@Entity
+data class StringSampleEntity2(
+    @PrimaryKey val stringPk2: String,
+    @ColumnInfo(defaultValue = "0") val data2: String
+)
+
 @Entity(
     primaryKeys = ["sample1Key", "sample2Key"],
     indices = [Index("sample1Key"), Index("sample2Key")]
 )
 data class Sample1Sample2XRef(
-    val sample1Key: Long,
-    val sample2Key: Long,
+    val sample1Key: String,
+    val sample2Key: String,
 )
 
 @Dao
@@ -82,6 +99,12 @@
 
     @Query("SELECT * FROM SampleEntity") suspend fun getSingleItem(): SampleEntity
 
+    @SkipQueryVerification
+    @Query("SELECT * FROM SampleEntity")
+    suspend fun getSingleItemSkipVerification(): SampleEntity
+
+    @RawQuery suspend fun getSingleItemRaw(query: RoomRawQuery): SampleEntity
+
     @Query("SELECT * FROM SampleEntity") suspend fun getItemList(): List<SampleEntity>
 
     @Query("SELECT * FROM SampleEntity") suspend fun getItemArray(): Array<SampleEntity>
@@ -110,6 +133,12 @@
     )
     suspend fun getMapWithDupeColumns(): Map<SampleEntity, SampleEntityCopy>
 
+    @SkipQueryVerification
+    @Query(
+        "SELECT * FROM SampleEntity JOIN SampleEntityCopy ON SampleEntity.pk = SampleEntityCopy.pk"
+    )
+    suspend fun getMapWithDupeColumnsSkipVerification(): Map<SampleEntity, SampleEntityCopy>
+
     @Query("SELECT * FROM SampleEntity JOIN SampleEntity2 ON SampleEntity.pk = SampleEntity2.pk2")
     suspend fun getMapReturnTypeWithList(): Map<SampleEntity, List<SampleEntity2>>
 
@@ -142,6 +171,10 @@
 
     @Insert suspend fun insertSampleEntityList(entities: List<SampleEntity>)
 
+    @Insert suspend fun insertSampleEntity1WithString(entities: List<StringSampleEntity1>)
+
+    @Insert suspend fun insertSampleEntity2WithString(entities: List<StringSampleEntity2>)
+
     @Insert suspend fun insertSampleEntity2List(entities: List<SampleEntity2>)
 
     @Insert suspend fun insert(entity: SampleEntity2)
@@ -167,7 +200,7 @@
     @Transaction @Query("SELECT * FROM SampleEntity") suspend fun getSample1ToMany(): Sample1AndMany
 
     @Transaction
-    @Query("SELECT * FROM SampleEntity")
+    @Query("SELECT * FROM StringSampleEntity1")
     suspend fun getSampleManyToMany(): SampleManyAndMany
 
     data class Sample1And2(
@@ -181,10 +214,10 @@
     )
 
     data class SampleManyAndMany(
-        @Embedded val sample1: SampleEntity,
+        @Embedded val sample1: StringSampleEntity1,
         @Relation(
-            parentColumn = "pk",
-            entityColumn = "pk2",
+            parentColumn = "stringPk1",
+            entityColumn = "stringPk2",
             associateBy =
                 Junction(
                     value = Sample1Sample2XRef::class,
@@ -192,7 +225,7 @@
                     entityColumn = "sample2Key"
                 )
         )
-        val sample2s: List<SampleEntity2>
+        val sample2s: List<StringSampleEntity2>
     )
 }
 
@@ -203,11 +236,16 @@
             SampleEntity2::class,
             SampleEntity3::class,
             SampleEntityCopy::class,
+            StringSampleEntity1::class,
+            StringSampleEntity2::class,
             Sample1Sample2XRef::class
         ],
     version = 1,
     exportSchema = false
 )
+@ConstructedBy(SampleDatabaseConstructor::class)
 abstract class SampleDatabase : RoomDatabase() {
     abstract fun dao(): SampleDao
 }
+
+expect object SampleDatabaseConstructor : RoomDatabaseConstructor<SampleDatabase>
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
index e83e0f9..4a6c3bd 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
@@ -24,7 +24,10 @@
 class BuilderTest : BaseBuilderTest() {
     override fun getRoomDatabaseBuilder(): RoomDatabase.Builder<SampleDatabase> {
         val tempFile = createTempFile("test.db").also { it.toFile().deleteOnExit() }
-        return Room.databaseBuilder(tempFile.toString()) { SampleDatabase::class.instantiateImpl() }
+        return Room.databaseBuilder<SampleDatabase>(
+                name = tempFile.toString(),
+                factory = SampleDatabaseConstructor::initialize
+            )
             .setDriver(BundledSQLiteDriver())
     }
 }
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
index bf882f0..f47642f 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
@@ -30,22 +30,19 @@
     private val filename = "/tmp/test-${Random.nextInt()}.db"
     private val driver: SQLiteDriver = BundledSQLiteDriver()
 
-    private val dbFactory = { AutoMigrationDatabase::class.instantiateImpl() }
-
     private val migrationTestHelper =
         MigrationTestHelper(
             schemaDirectoryPath = getSchemaDirectoryPath(),
             fileName = filename,
             driver = driver,
             databaseClass = AutoMigrationDatabase::class,
-            databaseFactory = dbFactory,
             autoMigrationSpecs = listOf(ProvidedSpecFrom2To3())
         )
 
     override fun getTestHelper() = migrationTestHelper
 
     override fun getDatabaseBuilder(): RoomDatabase.Builder<AutoMigrationDatabase> {
-        return Room.databaseBuilder<AutoMigrationDatabase>(filename, dbFactory).setDriver(driver)
+        return Room.databaseBuilder<AutoMigrationDatabase>(filename).setDriver(driver)
     }
 
     @BeforeTest
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
index 17e05f8..9f2d4cf 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BuilderTest.kt
@@ -29,7 +29,10 @@
     private val filename = "/tmp/test-${Random.nextInt()}.db"
 
     override fun getRoomDatabaseBuilder(): RoomDatabase.Builder<SampleDatabase> {
-        return Room.databaseBuilder(filename) { SampleDatabase::class.instantiateImpl() }
+        return Room.databaseBuilder<SampleDatabase>(
+                name = filename,
+                factory = SampleDatabaseConstructor::initialize
+            )
             .setDriver(BundledSQLiteDriver())
     }
 
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/InvalidationTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/InvalidationTest.kt
index fdc83b7..c6ec8bb 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/InvalidationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/InvalidationTest.kt
@@ -22,7 +22,7 @@
 class InvalidationTest : BaseInvalidationTest() {
 
     override fun getRoomDatabase(): SampleDatabase {
-        return Room.inMemoryDatabaseBuilder { SampleDatabase::class.instantiateImpl() }
+        return Room.inMemoryDatabaseBuilder<SampleDatabase>()
             .setDriver(BundledSQLiteDriver())
             .build()
     }
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
index 42784c5..2056db88 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
@@ -30,21 +30,18 @@
     private val filename = "/tmp/test-${Random.nextInt()}.db"
     private val driver: SQLiteDriver = BundledSQLiteDriver()
 
-    private val dbFactory = { MigrationDatabase::class.instantiateImpl() }
-
     private val migrationTestHelper =
         MigrationTestHelper(
             schemaDirectoryPath = getSchemaDirectoryPath(),
             fileName = filename,
             driver = driver,
             databaseClass = MigrationDatabase::class,
-            databaseFactory = dbFactory
         )
 
     override fun getTestHelper() = migrationTestHelper
 
     override fun getDatabaseBuilder(): RoomDatabase.Builder<MigrationDatabase> {
-        return Room.databaseBuilder<MigrationDatabase>(filename, dbFactory).setDriver(driver)
+        return Room.databaseBuilder<MigrationDatabase>(filename).setDriver(driver)
     }
 
     @BeforeTest
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
index 1a67dd3..1909883 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
@@ -24,9 +24,7 @@
 class QueryTest : BaseQueryTest() {
 
     override fun getRoomDatabase(): SampleDatabase {
-        return Room.inMemoryDatabaseBuilder<SampleDatabase> {
-                SampleDatabase::class.instantiateImpl()
-            }
+        return Room.inMemoryDatabaseBuilder<SampleDatabase>()
             .setDriver(BundledSQLiteDriver())
             .setQueryCoroutineContext(Dispatchers.IO)
             .build()
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
index de89cdd..a97a05f 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
@@ -23,7 +23,6 @@
 class TypeConverterTest : BaseTypeConverterTest() {
 
     override fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> {
-        return Room.inMemoryDatabaseBuilder<TestDatabase> { TestDatabase::class.instantiateImpl() }
-            .setDriver(BundledSQLiteDriver())
+        return Room.inMemoryDatabaseBuilder<TestDatabase>().setDriver(BundledSQLiteDriver())
     }
 }
diff --git a/room/integration-tests/noappcompattestapp/build.gradle b/room/integration-tests/noappcompattestapp/build.gradle
index 0006f20..59ddf4d 100644
--- a/room/integration-tests/noappcompattestapp/build.gradle
+++ b/room/integration-tests/noappcompattestapp/build.gradle
@@ -23,6 +23,7 @@
     implementation(project(":room:room-runtime"))
     annotationProcessor(project(":room:room-compiler"))
     androidTestAnnotationProcessor(project(":room:room-compiler"))
+    androidTestImplementation(project(":annotation:annotation"))
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
diff --git a/room/integration-tests/testapp/lint-baseline.xml b/room/integration-tests/testapp/lint-baseline.xml
index 2daddb0..c60daab 100644
--- a/room/integration-tests/testapp/lint-baseline.xml
+++ b/room/integration-tests/testapp/lint-baseline.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.6.0-alpha07" type="baseline" client="gradle" dependencies="false"
-    name="AGP (8.6.0-alpha07)" variant="all" version="8.6.0-alpha07">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -101,11 +100,13 @@
             file="src/androidTest/java/androidx/room/integration/testapp/test/InvalidationTrackerBehavioralTest.java"/>
     </issue>
 
-    <issue id="BanThreadSleep" message="Uses Thread.sleep()"
+    <issue
+        id="BanThreadSleep"
+        message="Uses Thread.sleep()"
         errorLine1="                        Thread.sleep(200);"
         errorLine2="                               ~~~~~">
         <location
-            file="src/androidTest/java/androidx/room/integration/testapp/test/MultiInstanceInvalidationTest.java" />
+            file="src/androidTest/java/androidx/room/integration/testapp/test/MultiInstanceInvalidationTest.java"/>
     </issue>
 
     <issue
@@ -189,20 +190,22 @@
             file="src/main/java/androidx/room/integration/testapp/database/CustomerDao.java"/>
     </issue>
 
-    <issue id="UnknownNullness"
+    <issue
+        id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
         errorLine1="    boolean contains(int id, String name, String lastName);"
         errorLine2="                             ~~~~~~">
         <location
-            file="src/main/java/androidx/room/integration/testapp/database/CustomerDao.java" />
+            file="src/main/java/androidx/room/integration/testapp/database/CustomerDao.java"/>
     </issue>
 
-    <issue id="UnknownNullness"
+    <issue
+        id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
         errorLine1="    boolean contains(int id, String name, String lastName);"
         errorLine2="                                          ~~~~~~">
         <location
-            file="src/main/java/androidx/room/integration/testapp/database/CustomerDao.java" />
+            file="src/main/java/androidx/room/integration/testapp/database/CustomerDao.java"/>
     </issue>
 
     <issue
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
index d95285a..2194c1f 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -102,7 +102,7 @@
     }
 
     @Test
-    public void insertNull() throws Exception {
+    public void insertNullColumn() throws Exception {
         @SuppressWarnings("ConstantConditions")
         Product product = new Product(1, null);
         Throwable throwable = null;
@@ -116,6 +116,20 @@
     }
 
     @Test
+    public void insertNullEntity() throws Exception {
+        @SuppressWarnings("ConstantConditions")
+        Throwable throwable = null;
+        try {
+            //noinspection DataFlowIssue - testing insert of null arg on @NonNull param
+            mProductDao.insert((Product) null);
+        } catch (Throwable t) {
+            throwable = t;
+        }
+        assertNotNull("Was expecting an exception", throwable);
+        assertThat(throwable, instanceOf(NullPointerException.class));
+    }
+
+    @Test
     public void insertQueryForVoid() {
         mProductDao.insert("Product X");
         assertThat(mProductDao.countProducts(), is(1));
diff --git a/room/room-common/api/current.txt b/room/room-common/api/current.txt
index a817853..22e0ef2 100644
--- a/room/room-common/api/current.txt
+++ b/room/room-common/api/current.txt
@@ -74,6 +74,11 @@
   @IntDef({androidx.room.ColumnInfo.UNDEFINED, androidx.room.ColumnInfo.TEXT, androidx.room.ColumnInfo.INTEGER, androidx.room.ColumnInfo.REAL, androidx.room.ColumnInfo.BLOB}) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public static @interface ColumnInfo.SQLiteTypeAffinity {
   }
 
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface ConstructedBy {
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> value();
+    property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> value;
+  }
+
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface Dao {
   }
 
diff --git a/room/room-common/api/restricted_current.txt b/room/room-common/api/restricted_current.txt
index 6a61efc..e824b58 100644
--- a/room/room-common/api/restricted_current.txt
+++ b/room/room-common/api/restricted_current.txt
@@ -80,6 +80,11 @@
   @IntDef({androidx.room.ColumnInfo.UNDEFINED, androidx.room.ColumnInfo.TEXT, androidx.room.ColumnInfo.INTEGER, androidx.room.ColumnInfo.REAL, androidx.room.ColumnInfo.BLOB}) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public static @interface ColumnInfo.SQLiteTypeAffinity {
   }
 
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface ConstructedBy {
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> value();
+    property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> value;
+  }
+
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface Dao {
   }
 
diff --git a/room/room-common/bcv/native/current.txt b/room/room-common/bcv/native/current.txt
index 4978f37..70698e3 100644
--- a/room/room-common/bcv/native/current.txt
+++ b/room/room-common/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
@@ -117,6 +117,11 @@
         constructor <init>() // androidx.room/ColumnInfo.SQLiteTypeAffinity.<init>|<init>(){}[0]
     }
 }
+open annotation class androidx.room/ConstructedBy : kotlin/Annotation { // androidx.room/ConstructedBy|null[0]
+    constructor <init>(kotlin.reflect/KClass<*>) // androidx.room/ConstructedBy.<init>|<init>(kotlin.reflect.KClass<*>){}[0]
+    final val value // androidx.room/ConstructedBy.value|{}value[0]
+        final fun <get-value>(): kotlin.reflect/KClass<*> // androidx.room/ConstructedBy.value.<get-value>|<get-value>(){}[0]
+}
 open annotation class androidx.room/Dao : kotlin/Annotation { // androidx.room/Dao|null[0]
     constructor <init>() // androidx.room/Dao.<init>|<init>(){}[0]
 }
diff --git a/room/room-common/build.gradle b/room/room-common/build.gradle
index 59152cd..7fda363 100644
--- a/room/room-common/build.gradle
+++ b/room/room-common/build.gradle
@@ -23,14 +23,13 @@
  */
 import androidx.build.LibraryType
 import androidx.build.PlatformIdentifier
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
 
 plugins {
     id("AndroidXPlugin")
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm() {
         withJava()
     }
@@ -44,8 +43,7 @@
         commonMain {
             dependencies {
                 api(libs.kotlinStdlib)
-                api("androidx.annotation:annotation:1.8.0")
-
+                api(project(":annotation:annotation"))
             }
         }
 
@@ -67,6 +65,18 @@
                 implementation(libs.kotlinTestJunit)
             }
         }
+
+        nativeMain {
+            dependsOn(commonMain)
+        }
+
+        targets.configureEach { target ->
+            if (target.platformType == KotlinPlatformType.native) {
+                target.compilations["main"].defaultSourceSet {
+                    dependsOn(nativeMain)
+                }
+            }
+        }
     }
 }
 
@@ -76,4 +86,5 @@
     inceptionYear = "2017"
     description = "Android Room-Common"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/room/room-common/src/commonMain/kotlin/androidx/room/ConstructedBy.kt b/room/room-common/src/commonMain/kotlin/androidx/room/ConstructedBy.kt
new file mode 100644
index 0000000..653c2c3
--- /dev/null
+++ b/room/room-common/src/commonMain/kotlin/androidx/room/ConstructedBy.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+import kotlin.reflect.KClass
+
+/**
+ * Defines the [androidx.room.RoomDatabaseConstructor] that will instantiate the Room generated
+ * implementation of the annotated [Database].
+ *
+ * A [androidx.room.RoomDatabase] database definition must be annotated with this annotation if it
+ * is located in a common source set on a Kotlin Multiplatform project such that at runtime the
+ * implementation generated by the annotation processor can be used. The [value] must be an 'expect
+ * object' that implements [androidx.room.RoomDatabaseConstructor].
+ *
+ * Example usage:
+ * ```
+ * @Database(version = 1, entities = [Song::class, Album::class])
+ * @ConstructedBy(MusicDatabaseConstructor::class)
+ * abstract class MusicDatabase : RoomDatabase
+ *
+ * expect object MusicDatabaseConstructor : RoomDatabaseConstructor<MusicDatabase>
+ * ```
+ *
+ * @see androidx.room.RoomDatabaseConstructor
+ */
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+expect annotation class ConstructedBy(
+    /**
+     * The 'expect' declaration of an 'object' that implements
+     * [androidx.room.RoomDatabaseConstructor] and is able to instantiate a
+     * [androidx.room.RoomDatabase].
+     */
+    val value: KClass<*>
+)
diff --git a/room/room-common/src/commonMain/kotlin/androidx/room/Database.kt b/room/room-common/src/commonMain/kotlin/androidx/room/Database.kt
index 1e714957..1406cd6 100644
--- a/room/room-common/src/commonMain/kotlin/androidx/room/Database.kt
+++ b/room/room-common/src/commonMain/kotlin/androidx/room/Database.kt
@@ -19,7 +19,7 @@
 import kotlin.reflect.KClass
 
 /**
- * Marks a class as a RoomDatabase.
+ * Marks a class as a [androidx.room.RoomDatabase].
  *
  * The class should be an abstract class and extend [androidx.room.RoomDatabase].
  *
@@ -62,6 +62,7 @@
  * @see [Entity]
  * @see [AutoMigration]
  * @see [androidx.room.RoomDatabase]
+ * @see [ConstructedBy]
  */
 @Target(AnnotationTarget.CLASS)
 @Retention(AnnotationRetention.BINARY)
diff --git a/room/room-common/src/commonMain/kotlin/androidx/room/RawQuery.kt b/room/room-common/src/commonMain/kotlin/androidx/room/RawQuery.kt
index 4cbf11f..062a449 100644
--- a/room/room-common/src/commonMain/kotlin/androidx/room/RawQuery.kt
+++ b/room/room-common/src/commonMain/kotlin/androidx/room/RawQuery.kt
@@ -20,19 +20,19 @@
 
 /**
  * Marks a method in a [Dao] annotated class as a raw query method where you can pass the query as a
- * [androidx.sqlite.db.SupportSQLiteQuery].
+ * [androidx.room.RoomRawQuery] or [androidx.sqlite.db.SupportSQLiteQuery].
  *
  * ```
  * @Dao
  * interface RawDao {
  *     @RawQuery
- *     getSongViaQuery(query: SupportSQLiteQuery): Song
+ *     fun getSongViaQuery(query: RoomRawQuery): Song
  * }
  *
  * // Usage of RawDao
- * val query = SimpleSQLiteQuery(
- *     "SELECT * FROM Song WHERE id = ? LIMIT 1",
- *     arrayOf<Any>(songId)
+ * val query = RoomRawQuery(
+ *     sql = "SELECT * FROM Song WHERE id = ? LIMIT 1",
+ *     onBindStatement = { it.bindLong(1, songId) }
  * )
  * val song = rawDao.getSongViaQuery(query)
  * ```
@@ -41,40 +41,40 @@
  * query will result in a runtime failure or an undefined result.
  *
  * If you know the query at compile time, you should always prefer [Query] since it validates the
- * query at compile time and also generates more efficient code since Room can compute the query
+ * query at compile time and also generates more efficient code, since Room can compute the query
  * result at compile time (e.g. it does not need to account for possibly missing columns in the
- * response).
+ * result).
  *
- * On the other hand, `RawQuery` serves as an escape hatch where you can build your own SQL query at
- * runtime but still use Room to convert it into objects.
+ * On the other hand, `@RawQuery` serves as an escape hatch where you can build your own SQL query
+ * at runtime but still use Room to convert it into objects.
  *
- * `RawQuery` methods must return a non-void type. If you want to execute a raw query that does not
+ * `@RawQuery` methods must return a non-void type. If you want to execute a raw query that does not
  * return any value, use [androidx.room.RoomDatabase.query] methods.
  *
- * RawQuery methods can only be used for read queries. For write queries, use
- * [androidx.room.RoomDatabase.getOpenHelper].
+ * `@RawQuery` methods can only be used for read queries. For write queries, use
+ * [androidx.room.RoomDatabase.openHelper].
  *
  * **Observable Queries:**
  *
- * `RawQuery` methods can return observable types but you need to specify which tables are accessed
+ * `@RawQuery` methods can return observable types but you need to specify which tables are accessed
  * in the query using the [observedEntities] field in the annotation.
  *
  * ```
  * @Dao
  * interface RawDao {
  *     @RawQuery(observedEntities = Song::class)
- *     fun getSongs(query: SupportSQLiteQuery): LiveData<List<Song>>
+ *     fun getSongs(query: RoomRawQuery): Flow<List<Song>>
  * }
  *
  * // Usage of RawDao
  * val liveSongs = rawDao.getSongs(
- *     SimpleSQLiteQuery("SELECT * FROM song ORDER BY name DESC")
+ *     RoomRawQuery("SELECT * FROM song ORDER BY name DESC")
  * )
  * ```
  *
  * **Returning POJOs:**
  *
- * RawQueries can also return plain old java objects, similar to [Query] methods.
+ * `@RawQuery` can also return plain old java objects, similar to [Query] methods.
  *
  * ```
  * data class NameAndReleaseYear (
@@ -86,17 +86,21 @@
  * @Dao
  * interface RawDao {
  *     @RawQuery
- *     fun getNameAndReleaseYear(query: SupportSQLiteQuery): NameAndReleaseYear
+ *     fun getNameAndReleaseYear(query: RoomRawQuery): NameAndReleaseYear
  * }
  *
  * // Usage of RawDao
  * val result: NameAndReleaseYear = rawDao.getNameAndReleaseYear(
- *     SimpleSQLiteQuery("SELECT * FROM song WHERE id = ?", arrayOf<Any>(songId)))
+ *     RoomRawQuery(
+ *         sql = "SELECT * FROM song WHERE id = ?",
+ *         onBindStatement = { it.bindLong(1, songId) }
+ *     )
+ * )
  * ```
  *
  * **POJOs with Embedded Fields:**
  *
- * `RawQuery` methods can return POJOs that include [Embedded] fields as well.
+ * `@RawQuery` methods can return POJOs that include [Embedded] fields as well.
  *
  * ```
  * data class SongAndArtist (
@@ -109,36 +113,37 @@
  * @Dao
  * interface RawDao {
  *     @RawQuery
- *     fun getSongAndArtist(query: SupportSQLiteQuery): SongAndArtist
+ *     fun getSongAndArtist(query: RoomRawQuery): SongAndArtist
  * }
  *
  * // Usage of RawDao
  * val result: = rawDao.getSongAndArtist(
- *     SimpleSQLiteQuery("SELECT * FROM Song, Artist WHERE Song.artistId = Artist.id LIMIT 1"))
+ *     RoomRawQuery("SELECT * FROM Song, Artist WHERE Song.artistId = Artist.id LIMIT 1")
+ * )
  * ```
  *
  * **Relations:**
  *
- * `RawQuery` return types can also be objects with [Relation].
+ * `@RawQuery` return types can also be objects with [Relation].
  *
  * ```
  * data class AlbumAndSongs {
  *     @Embedded
- *     public val album: Album,
+ *     val album: Album,
  *     @Relation(parentColumn = "id", entityColumn = "albumId")
- *     public val pets: List<Song>
+ *     val songs: List<Song>
  * }
  *
  * @Dao
  * interface RawDao {
  *     @RawQuery
- *     fun getAlbumAndSongs(query: SupportSQLiteQuery): List<AlbumAndSongs>
+ *     fun getAlbumAndSongs(query: RoomRawQuery): List<AlbumAndSongs>
  * }
  *
  * // Usage of RawDao
- * val result: = rawDao.getAlbumAndSongs(
- *     SimpleSQLiteQuery("SELECT * FROM album")
- * ): List<AlbumAndSongs>
+ * val result = rawDao.getAlbumAndSongs(
+ *     RoomRawQuery("SELECT * FROM album")
+ * )
  * ```
  */
 @Target(AnnotationTarget.FUNCTION)
@@ -157,10 +162,11 @@
      * @Dao
      * interface RawDao {
      *   @RawQuery(observedEntities = Song::class)
-     *   fun getUsers(query: String): LiveData<List<User>>
+     *   fun getSongs(query: RoomRawQuery): Flow<List<Song>>
      * }
-     * val liveSongs: = rawDao.getUsers(
-     *     "SELECT * FROM song ORDER BY name DESC")
+     * val liveSongs: = rawDao.getSongs(
+     *     RoomRawQuery("SELECT * FROM song ORDER BY name DESC")
+     * )
      * ```
      *
      * @return List of entities that should invalidate the query if changed.
diff --git a/room/room-common/src/jvmMain/kotlin/androidx/room/ConstructedBy.jvm.kt b/room/room-common/src/jvmMain/kotlin/androidx/room/ConstructedBy.jvm.kt
new file mode 100644
index 0000000..6f31994
--- /dev/null
+++ b/room/room-common/src/jvmMain/kotlin/androidx/room/ConstructedBy.jvm.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+import kotlin.reflect.KClass
+
+/**
+ * Defines the [androidx.room.RoomDatabaseConstructor] that will instantiate the Room generated
+ * implementation of the annotated `@Database`.
+ *
+ * A [androidx.room.RoomDatabase] database definition must be annotated with this annotation if it
+ * is located in a common source set on a Kotlin Multiplatform project such that at runtime the
+ * implementation generated by the annotation processor can be used. The [value] must be an 'expect
+ * object' that implements [androidx.room.RoomDatabaseConstructor].
+ *
+ * Example usage:
+ * ```
+ * @Database(version = 1, entities = [Song::class, Album::class])
+ * @ConstructedBy(MusicDatabaseConstructor::class)
+ * abstract class MusicDatabase : RoomDatabase
+ *
+ * expect object MusicDatabaseConstructor : RoomDatabaseConstructor<MusicDatabase>
+ * ```
+ *
+ * @see androidx.room.RoomDatabaseConstructor
+ */
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+actual annotation class ConstructedBy(
+    /**
+     * The 'expect' declaration of an 'object' that implements
+     * [androidx.room.RoomDatabaseConstructor] and is able to instantiate a
+     * [androidx.room.RoomDatabase].
+     */
+    actual val value: KClass<*>
+)
diff --git a/room/room-common/src/nativeMain/kotlin/androidx/room/ConstructedBy.native.kt b/room/room-common/src/nativeMain/kotlin/androidx/room/ConstructedBy.native.kt
new file mode 100644
index 0000000..b43bd1e
--- /dev/null
+++ b/room/room-common/src/nativeMain/kotlin/androidx/room/ConstructedBy.native.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+import kotlin.reflect.AssociatedObjectKey
+import kotlin.reflect.ExperimentalAssociatedObjects
+import kotlin.reflect.KClass
+
+/**
+ * Defines the [androidx.room.RoomDatabaseConstructor] that will instantiate the Room generated
+ * implementation of the annotated [Database].
+ *
+ * A [androidx.room.RoomDatabase] database definition must be annotated with this annotation if it
+ * is located in a common source set on a Kotlin Multiplatform project such that at runtime the
+ * implementation generated by the annotation processor can be used. The [value] must be an 'expect
+ * object' that implements [androidx.room.RoomDatabaseConstructor].
+ *
+ * Example usage:
+ * ```
+ * @Database(version = 1, entities = [Song::class, Album::class])
+ * @ConstructedBy(MusicDatabaseConstructor::class)
+ * abstract class MusicDatabase : RoomDatabase
+ *
+ * expect object MusicDatabaseConstructor : RoomDatabaseConstructor<MusicDatabase>
+ * ```
+ *
+ * @see androidx.room.RoomDatabaseConstructor
+ */
+@OptIn(ExperimentalAssociatedObjects::class)
+@AssociatedObjectKey
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+actual annotation class ConstructedBy(
+    /**
+     * The 'expect' declaration of an 'object' that implements
+     * [androidx.room.RoomDatabaseConstructor] and is able to instantiate a
+     * [androidx.room.RoomDatabase].
+     */
+    actual val value: KClass<*>
+)
diff --git a/room/room-compiler-processing/build.gradle b/room/room-compiler-processing/build.gradle
index 2bc4be2..2d110d8 100644
--- a/room/room-compiler-processing/build.gradle
+++ b/room/room-compiler-processing/build.gradle
@@ -84,7 +84,7 @@
     implementation(libs.kspApi)
     implementation(libs.kotlinStdlibJdk8) // KSP defines older version as dependency, force update.
 
-    testImplementation("androidx.annotation:annotation:1.1.0")
+    testImplementation("androidx.annotation:annotation:1.8.1")
     testImplementation(libs.googleCompileTesting)
     testImplementation(libs.junit)
     testImplementation(libs.jsr250)
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
index 14083cb..c6b45f2 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XProcessingEnv.kt
@@ -16,13 +16,14 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.codegen.JArrayTypeName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.javac.JavacProcessingEnv
 import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import com.google.devtools.ksp.processing.Resolver
 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
-import com.squareup.javapoet.ArrayTypeName
-import com.squareup.javapoet.TypeName
+import com.squareup.kotlinpoet.javapoet.JClassName
+import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.KClassName
 import javax.annotation.processing.ProcessingEnvironment
 import kotlin.reflect.KClass
@@ -80,13 +81,6 @@
      */
     fun findType(qName: String): XType?
 
-    /**
-     * Returns the [XType] with the given qualified name or throws an exception if it does not
-     * exist.
-     */
-    fun requireType(qName: String): XType =
-        checkNotNull(findType(qName)) { "cannot find required type $qName" }
-
     /** Returns the [XTypeElement] for the annotation that should be added to the generated code. */
     fun findGeneratedAnnotation(): XTypeElement?
 
@@ -116,23 +110,89 @@
         return checkNotNull(findTypeElement(qName)) { "Cannot find required type element $qName" }
     }
 
-    // helpers for smooth migration, these could be extension methods
-    fun requireType(typeName: TypeName) =
-        checkNotNull(findType(typeName)) { "cannot find required type $typeName" }
+    fun requireTypeElement(typeName: XTypeName): XTypeElement {
+        return checkNotNull(findTypeElement(typeName)) {
+            "Cannot find required type element $typeName"
+        }
+    }
 
-    fun requireType(typeName: XTypeName): XType {
+    fun requireTypeElement(klass: KClass<*>) = requireTypeElement(klass.java.canonicalName!!)
+
+    @Deprecated(
+        message = "Prefer using XTypeName or String overload instead of JavaPoet.",
+        replaceWith = ReplaceWith(expression = "requireTypeElement(typeName.toString())")
+    )
+    fun requireTypeElement(typeName: JTypeName) = requireTypeElement(typeName.toString())
+
+    fun findTypeElement(typeName: XTypeName): XTypeElement? {
         if (typeName.isPrimitive) {
-            return requireType(typeName.java)
+            return findTypeElement(typeName.java.toString())
         }
         return when (backend) {
-            Backend.JAVAC -> requireType(typeName.java)
+            Backend.JAVAC -> {
+                val jClassName =
+                    typeName.java as? JClassName
+                        ?: error("Cannot find required type element ${typeName.java}")
+                findTypeElement(jClassName.canonicalName())
+            }
             Backend.KSP -> {
                 val kClassName =
                     typeName.kotlin as? KClassName
-                        ?: error("cannot find required type ${typeName.kotlin}")
-                requireType(kClassName.canonicalName)
+                        ?: error("Cannot find required type element ${typeName.kotlin}")
+                findTypeElement(kClassName.canonicalName)
             }
-        }.let {
+        }
+    }
+
+    fun findTypeElement(klass: KClass<*>) = findTypeElement(klass.java.canonicalName!!)
+
+    @Deprecated(
+        message = "Prefer using XTypeName or String overload instead of JavaPoet.",
+        replaceWith = ReplaceWith(expression = "findTypeElement(typeName.toString())")
+    )
+    fun findTypeElement(typeName: JTypeName) = findTypeElement(typeName.toString())
+
+    /**
+     * Returns the [XType] with the given qualified name or throws an exception if it does not
+     * exist.
+     */
+    fun requireType(qName: String): XType =
+        checkNotNull(findType(qName)) { "cannot find required type $qName" }
+
+    fun requireType(typeName: XTypeName): XType =
+        checkNotNull(findType(typeName)) { "cannot find required type $typeName" }
+
+    fun requireType(klass: KClass<*>) = requireType(klass.java.canonicalName!!)
+
+    @Deprecated(
+        message = "Prefer using XTypeName or String overload instead of JavaPoet.",
+        replaceWith = ReplaceWith(expression = "requireType(typeName.toString())")
+    )
+    fun requireType(typeName: JTypeName) =
+        checkNotNull(findType(typeName.toString())) { "cannot find required type $typeName" }
+
+    fun findType(typeName: XTypeName): XType? {
+        if (typeName.isPrimitive) {
+            return findType(typeName.java.toString())
+        }
+        val jTypeName = typeName.java
+        if (jTypeName is JArrayTypeName) {
+            return findType(jTypeName.componentType.toString())?.let { getArrayType(it) }
+        }
+        return when (backend) {
+            Backend.JAVAC -> {
+                val jClassName =
+                    typeName.java as? JClassName
+                        ?: error("Cannot find required type element ${typeName.java}")
+                findType(jClassName.canonicalName())
+            }
+            Backend.KSP -> {
+                val kClassName =
+                    typeName.kotlin as? KClassName
+                        ?: error("Cannot find required type ${typeName.kotlin}")
+                findType(kClassName.canonicalName)
+            }
+        }?.let {
             when (typeName.nullability) {
                 XNullability.NULLABLE -> it.makeNullable()
                 XNullability.NONNULL -> it.makeNonNullable()
@@ -141,45 +201,23 @@
         }
     }
 
-    fun requireType(klass: KClass<*>) = requireType(klass.java.canonicalName!!)
+    fun findType(klass: KClass<*>) = findType(klass.java.canonicalName!!)
 
-    fun findType(typeName: TypeName): XType? {
-        // TODO we probably need more complicated logic here but right now room only has these
-        //  usages.
-        if (typeName is ArrayTypeName) {
-            return findType(typeName.componentType)?.let { getArrayType(it) }
+    @Deprecated(
+        message = "Prefer using XTypeName or String overload instead of JavaPoet.",
+        replaceWith = ReplaceWith(expression = "findType(typeName.toString())")
+    )
+    fun findType(typeName: JTypeName): XType? {
+        if (typeName is JArrayTypeName) {
+            return findType(typeName.componentType.toString())?.let { getArrayType(it) }
         }
         return findType(typeName.toString())
     }
 
-    fun findType(klass: KClass<*>) = findType(klass.java.canonicalName!!)
-
-    fun requireTypeElement(typeName: XTypeName): XTypeElement {
-        if (typeName.isPrimitive) {
-            return requireTypeElement(typeName.java)
-        }
-        return when (backend) {
-            Backend.JAVAC -> requireTypeElement(typeName.java)
-            Backend.KSP -> {
-                val kClassName =
-                    typeName.kotlin as? KClassName
-                        ?: error("cannot find required type element ${typeName.kotlin}")
-                requireTypeElement(kClassName.canonicalName)
-            }
-        }
-    }
-
-    fun requireTypeElement(typeName: TypeName) = requireTypeElement(typeName.toString())
-
-    fun requireTypeElement(klass: KClass<*>) = requireTypeElement(klass.java.canonicalName!!)
-
-    fun findTypeElement(typeName: TypeName) = findTypeElement(typeName.toString())
-
-    fun findTypeElement(klass: KClass<*>) = findTypeElement(klass.java.canonicalName!!)
-
     fun getArrayType(typeName: XTypeName) = getArrayType(requireType(typeName))
 
-    fun getArrayType(typeName: TypeName) = getArrayType(requireType(typeName))
+    @Deprecated("Prefer using XTypeName or String overload instead of JavaPoet.")
+    fun getArrayType(typeName: JTypeName) = getArrayType(requireType(typeName.toString()))
 
     enum class Backend {
         JAVAC,
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt
index e4bfffd..7a21cf1 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeParameterElement.kt
@@ -47,6 +47,10 @@
         declaration.bounds
             .map { env.wrap(it, it.resolve()) }
             .toList()
+            // In Kotlin the order doesn't matter but in Java class bound should go
+            // before interface bounds:
+            // https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.4
+            .sortedBy { it.typeElement?.isInterface() ?: false }
             .ifEmpty { listOf(env.requireType(Any::class).makeNullable()) }
     }
 
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index 9d7cb8d..663815d 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -20,6 +20,7 @@
 import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
+import androidx.room.compiler.codegen.asPrimitiveTypeName
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnum
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnumArray
@@ -38,7 +39,6 @@
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
-import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth
 import com.squareup.javapoet.ClassName
 import org.junit.Test
@@ -218,7 +218,9 @@
                 assertThat(annotation.getAsTypeList("typeList").map { it.asTypeName() })
                     .containsExactly(String::class.asClassName(), XTypeName.PRIMITIVE_INT)
                 assertThat(annotation.getAsType("singleType"))
-                    .isEqualTo(invocation.processingEnv.requireType(Long::class.typeName()))
+                    .isEqualTo(
+                        invocation.processingEnv.requireType(Long::class.asPrimitiveTypeName())
+                    )
 
                 assertThat(annotation.value.intMethod).isEqualTo(3)
                 assertThat(annotation.value.doubleMethodWithDefault).isEqualTo(3.0)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index a7bf6ca..b736a07 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -671,7 +671,7 @@
             )
         runProcessorTest(sources = listOf(subject)) {
             val inner = JClassName.get("foo.bar", "Baz.Inner")
-            assertThat(it.processingEnv.requireTypeElement(inner).isInterface()).isTrue()
+            assertThat(it.processingEnv.requireTypeElement(inner.toString()).isInterface()).isTrue()
             val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
             assertThat(element.isInterface()).isFalse()
             assertThat(element.isAbstract()).isFalse()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index 7f2fba6..1312ac5 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -1243,7 +1243,7 @@
         runProcessorTest(sources = listOf(source)) { invocation ->
             val objectMethodNames =
                 invocation.processingEnv
-                    .requireTypeElement(TypeName.OBJECT)
+                    .requireTypeElement(XTypeName.ANY_OBJECT)
                     .getAllNonPrivateInstanceMethods()
                     .map { it.jvmName }
                     .toSet()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index ffb0ee8..8c7fd8f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -253,7 +253,7 @@
     fun changeNullability_primitives() {
         runProcessorTest { invocation ->
             PRIMITIVE_JTYPE_NAMES.forEachIndexed { index, primitiveJTypeName ->
-                val primitive = invocation.processingEnv.requireType(primitiveJTypeName)
+                val primitive = invocation.processingEnv.requireType(primitiveJTypeName.toString())
                 assertThat(primitive.nullability).isEqualTo(NONNULL)
                 val nullable = primitive.makeNullable()
                 assertThat(nullable.nullability).isEqualTo(NULLABLE)
@@ -268,7 +268,8 @@
                 // it) it is more consistent as it is completely valid to annotate a boxed primitive
                 // with non-null while you cannot annoteted a primitive with nullable as it is not
                 // a valid state.
-                val boxedPrimitive = invocation.processingEnv.requireType(primitiveJTypeName.box())
+                val boxedPrimitive =
+                    invocation.processingEnv.requireType(primitiveJTypeName.box().toString())
                 val nonNull = boxedPrimitive.makeNonNullable()
                 assertThat(nonNull.nullability).isEqualTo(NONNULL)
                 assertThat(nonNull.asTypeName().java).isEqualTo(primitiveJTypeName.box())
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index 9181c13..5df2e85 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -60,17 +60,18 @@
             val type = element.type
 
             assertThat(it.processingEnv.findTypeElement(qName)).isEqualTo(element)
-            assertThat(it.processingEnv.findTypeElement(jClassName)).isEqualTo(element)
+            assertThat(it.processingEnv.findTypeElement(jClassName.toString())).isEqualTo(element)
             assertThat(it.processingEnv.findTypeElement(klass)).isEqualTo(element)
 
-            assertThat(it.processingEnv.requireTypeElement(jClassName)).isEqualTo(element)
+            assertThat(it.processingEnv.requireTypeElement(jClassName.toString()))
+                .isEqualTo(element)
             assertThat(it.processingEnv.requireTypeElement(klass)).isEqualTo(element)
 
             assertThat(it.processingEnv.findType(qName)).isEqualTo(type)
-            assertThat(it.processingEnv.findType(jClassName)).isEqualTo(type)
+            assertThat(it.processingEnv.findType(jClassName.toString())).isEqualTo(type)
             assertThat(it.processingEnv.findType(klass)).isEqualTo(type)
 
-            assertThat(it.processingEnv.requireType(jClassName)).isEqualTo(type)
+            assertThat(it.processingEnv.requireType(jClassName.toString())).isEqualTo(type)
             assertThat(it.processingEnv.requireType(klass)).isEqualTo(type)
             assertThat(it.processingEnv.requireType(qName)).isEqualTo(type)
         }
@@ -190,7 +191,7 @@
         listOf(javaSrc, kotlinSrc).forEach { src ->
             runProcessorTest(sources = listOf(src)) { invocation ->
                 val className = ClassName.get("foo.bar", "ToBeGenerated")
-                if (invocation.processingEnv.findTypeElement(className) == null) {
+                if (invocation.processingEnv.findTypeElement(className.toString()) == null) {
                     // generate only if it doesn't exist to handle multi-round
                     val spec =
                         TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC).build()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
index 4459d89..97b81c8 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
@@ -917,6 +917,7 @@
                                     .getAsType("singleType")
                                     .asTypeName()
                                     .java
+                                    .toString()
                             )
                         val generatedType =
                             otherElement
@@ -1687,7 +1688,7 @@
                 ): Set<XElement> {
                     invocations[env.backend] = invocations.getOrDefault(env.backend, 0) + 1
                     val className = ClassName.get("foo", "Bar")
-                    val typeElement = env.findTypeElement(className)
+                    val typeElement = env.findTypeElement(className.toString())
                     if (typeElement == null) {
                         val spec =
                             TypeSpec.classBuilder(className)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 0088f5a..ce9c840 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -187,10 +187,12 @@
             }
             invocation.processingEnv.requireTypeElement("foo.bar.AbstractClass").let {
                 assertThat(it.superClass!!.asTypeName())
-                    .isEqualTo(invocation.processingEnv.requireType(JTypeName.OBJECT).asTypeName())
+                    .isEqualTo(
+                        invocation.processingEnv.requireType(XTypeName.ANY_OBJECT).asTypeName()
+                    )
                 assertThat(it.type.superTypes.map(XType::asTypeName))
                     .containsExactly(
-                        invocation.processingEnv.requireType(JTypeName.OBJECT).asTypeName()
+                        invocation.processingEnv.requireType(XTypeName.ANY_OBJECT).asTypeName()
                     )
                 assertThat(it.isAbstract()).isTrue()
                 assertThat(it.isInterface()).isFalse()
@@ -203,7 +205,7 @@
                 assertThat(it.superClass).isNull()
                 assertThat(it.type.superTypes.map(XType::asTypeName))
                     .containsExactly(
-                        invocation.processingEnv.requireType(JTypeName.OBJECT).asTypeName()
+                        invocation.processingEnv.requireType(XTypeName.ANY_OBJECT).asTypeName()
                     )
                 assertThat(it.isInterface()).isTrue()
                 assertThat(it.type.asTypeName())
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
index f130d0f..c350faf 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
@@ -423,4 +423,26 @@
             }
         }
     }
+
+    @Test
+    fun classBoundsGoBeforeInterfaceBounds() {
+        val src =
+            Source.kotlin(
+                "Foo.kt",
+                """
+                class Foo<T> where T: InterfaceBound1, T: InterfaceBound2, T: ClassBound
+                open class ClassBound
+                interface InterfaceBound1
+                interface InterfaceBound2
+                """
+                    .trimIndent()
+            )
+        runProcessorTest(sources = listOf(src)) { invocation ->
+            val foo = invocation.processingEnv.requireTypeElement("Foo")
+
+            val t = foo.typeParameters[0]
+            assertThat(t.bounds).hasSize(3)
+            assertThat(t.bounds[0].asTypeName().java.toString()).isEqualTo("ClassBound")
+        }
+    }
 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
index 03fecc8..db986f6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
@@ -720,7 +720,7 @@
         var runCount = 0
         runKaptTest(sources = listOf(kotlinSrc, javaSrc)) { invocation ->
             val className = ClassName.get("foo.bar", "ToBeGenerated")
-            if (invocation.processingEnv.findTypeElement(className) == null) {
+            if (invocation.processingEnv.findTypeElement(className.toString()) == null) {
                 // Assert that this is only run only on the first round
                 assertThat(++runCount).isEqualTo(1)
 
@@ -734,7 +734,8 @@
             } else {
                 // Asserts that the class was generated in the second round
                 assertThat(++runCount).isEqualTo(2)
-                assertThat(invocation.processingEnv.findTypeElement(className)).isNotNull()
+                assertThat(invocation.processingEnv.findTypeElement(className.toString()))
+                    .isNotNull()
             }
         }
     }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
index 68c7283..a07fca5 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
@@ -31,8 +31,8 @@
 import androidx.room.vo.Warning
 import androidx.room.writer.AutoMigrationWriter
 import androidx.room.writer.DaoWriter
+import androidx.room.writer.DatabaseObjectConstructorWriter
 import androidx.room.writer.DatabaseWriter
-import androidx.room.writer.InstantiateImplWriter
 import androidx.room.writer.TypeWriter
 import java.nio.file.Path
 
@@ -146,9 +146,12 @@
                     )
                     .write(context.processingEnv)
             }
-
-            if (context.codeLanguage == CodeLanguage.KOTLIN) {
-                InstantiateImplWriter(db).write(context.processingEnv)
+            if (db.constructorObjectElement != null) {
+                DatabaseObjectConstructorWriter(
+                        database = db,
+                        constructorObjectElement = db.constructorObjectElement
+                    )
+                    .write(context.processingEnv)
             }
         }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
index 883cb38..fae6733 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
@@ -87,6 +87,8 @@
     val STATEMENT_UTIL = XClassName.get("$ROOM_PACKAGE.util", "SQLiteStatementUtil")
     val CONNECTION_UTIL = XClassName.get("$ROOM_PACKAGE.util", "SQLiteConnectionUtil")
     val FLOW_UTIL = XClassName.get("$ROOM_PACKAGE.coroutines", "FlowUtil")
+    val RAW_QUERY = XClassName.get(ROOM_PACKAGE, "RoomRawQuery")
+    val ROOM_DB_CONSTRUCTOR = XClassName.get(ROOM_PACKAGE, "RoomDatabaseConstructor")
 }
 
 object RoomAnnotationTypeNames {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
index 2ffc768..8d375ac 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
@@ -314,7 +314,8 @@
      * Check if the target platform is only Android.
      *
      * Note that there is no 'Android' target in the `targetPlatforms` list, so instead we check for
-     * JVM and also validate that an Android only class [Context] is in the classpath.
+     * JVM and also validate that an Android only class `android.content.Context` is in the
+     * classpath.
      */
     fun isAndroidOnlyTarget(): Boolean {
         val targetPlatforms = this.processingEnv.targetPlatforms
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt
index 29db352..253f83e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt
@@ -51,22 +51,31 @@
             }
             val annotation = element.requireAnnotation(TypeConverters::class)
             val classes = annotation.getAsTypeList("value").mapTo(LinkedHashSet()) { it }
-            val converters =
-                classes.flatMap {
-                    val typeElement = it.typeElement
-                    if (typeElement == null) {
-                        context.logger.e(
-                            element,
-                            ProcessorErrors.typeConverterMustBeDeclared(
-                                it.asTypeName().toString(context.codeLanguage)
+            val typeElementToWrappers =
+                classes
+                    .mapNotNull {
+                        val typeElement = it.typeElement
+                        if (typeElement == null) {
+                            context.logger.e(
+                                element,
+                                ProcessorErrors.typeConverterMustBeDeclared(
+                                    it.asTypeName().toString(context.codeLanguage)
+                                )
                             )
-                        )
-                        emptyList()
-                    } else {
-                        CustomConverterProcessor(context, typeElement).process()
+                            null
+                        } else {
+                            typeElement
+                        }
                     }
-                }
-            reportDuplicates(context, converters)
+                    .associateWith {
+                        CustomConverterProcessor(context, it)
+                            .process()
+                            .map(::CustomTypeConverterWrapper)
+                    }
+            reportDuplicates(
+                context,
+                typeElementToWrappers.values.flatMap { wrappers -> wrappers.map { it.custom } }
+            )
             val builtInStates =
                 annotation.getAsAnnotationBox<BuiltInTypeConverters>("builtInTypeConverters").let {
                     BuiltInConverterFlags(
@@ -76,8 +85,7 @@
                     )
                 }
             return ProcessResult(
-                classes = classes,
-                converters = converters.map(::CustomTypeConverterWrapper),
+                typeElementToWrappers = typeElementToWrappers,
                 builtInConverterFlags = builtInStates
             )
         }
@@ -180,26 +188,33 @@
 
     /** Order of classes is important hence they are a LinkedHashSet not a set. */
     data class ProcessResult(
-        val classes: LinkedHashSet<XType>,
-        val converters: List<CustomTypeConverterWrapper>,
+        private val typeElementToWrappers: Map<XTypeElement, List<CustomTypeConverterWrapper>>,
         val builtInConverterFlags: BuiltInConverterFlags
     ) {
         companion object {
             val EMPTY =
                 ProcessResult(
-                    classes = LinkedHashSet(),
-                    converters = emptyList(),
+                    typeElementToWrappers = LinkedHashMap(),
                     builtInConverterFlags = BuiltInConverterFlags.DEFAULT
                 )
         }
 
+        val classes: Set<XTypeElement>
+            get() = typeElementToWrappers.keys
+
+        val converters: List<CustomTypeConverterWrapper>
+            get() = typeElementToWrappers.flatMap { it.value }
+
         operator fun plus(other: ProcessResult): ProcessResult {
-            val newClasses = LinkedHashSet<XType>()
-            newClasses.addAll(classes)
-            newClasses.addAll(other.classes)
+            val newMap = LinkedHashMap<XTypeElement, List<CustomTypeConverterWrapper>>()
+            newMap.putAll(typeElementToWrappers)
+            other.typeElementToWrappers.forEach { (typeElement, converters) ->
+                if (!newMap.contains(typeElement)) {
+                    newMap[typeElement] = converters
+                }
+            }
             return ProcessResult(
-                classes = newClasses,
-                converters = converters + other.converters,
+                typeElementToWrappers = newMap,
                 builtInConverterFlags = other.builtInConverterFlags.withNext(builtInConverterFlags)
             )
         }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
index 53f6f8c..7f1f632 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
@@ -135,6 +135,8 @@
             errorMsg = ProcessorErrors.INVALID_DATABASE_VERSION
         )
 
+        val constructorObjectElement = processConstructorObject(element)
+
         val database =
             Database(
                 version = dbAnnotation.value.version,
@@ -146,6 +148,7 @@
                 exportSchema = dbAnnotation.value.exportSchema,
                 enableForeignKeys = hasForeignKeys,
                 overrideClearAllTables = hasClearAllTables,
+                constructorObjectElement = constructorObjectElement
             )
         database.autoMigrations = processAutoMigrations(element, database.bundle)
         return database
@@ -539,4 +542,65 @@
         } while (unresolvedViews.isNotEmpty())
         return result
     }
+
+    private fun processConstructorObject(element: XTypeElement): XTypeElement? {
+        val annotation = element.getAnnotation(androidx.room.ConstructedBy::class)
+        if (annotation == null) {
+            context.checker.check(
+                predicate = context.isAndroidOnlyTarget(),
+                element = element,
+                errorMsg = ProcessorErrors.MISSING_CONSTRUCTED_BY_ANNOTATION
+            )
+            return null
+        }
+        val type = annotation.getAsType("value") ?: return null
+        val typeElement = type.typeElement
+        if (typeElement == null) {
+            context.logger.e(element, ProcessorErrors.INVALID_CONSTRUCTED_BY_CLASS)
+            return null
+        }
+
+        context.checker.check(
+            predicate = typeElement.isKotlinObject(),
+            element = typeElement,
+            errorMsg = ProcessorErrors.INVALID_CONSTRUCTED_BY_NOT_OBJECT
+        )
+
+        context.checker.check(
+            predicate = typeElement.isExpect(),
+            element = typeElement,
+            errorMsg = ProcessorErrors.INVALID_CONSTRUCTED_BY_NOT_EXPECT
+        )
+
+        val expectedSuperInterfaceTypeName =
+            RoomTypeNames.ROOM_DB_CONSTRUCTOR.parametrizedBy(element.asClassName())
+        val superInterface = typeElement.superInterfaces.singleOrNull()
+        if (
+            superInterface == null ||
+                superInterface.asTypeName().rawTypeName != RoomTypeNames.ROOM_DB_CONSTRUCTOR
+        ) {
+            context.logger.e(
+                element = typeElement,
+                msg =
+                    ProcessorErrors.invalidConstructedBySuperInterface(
+                        expectedSuperInterfaceTypeName.toString(context.codeLanguage)
+                    )
+            )
+            return null
+        }
+
+        val typeArg = superInterface.typeArguments.singleOrNull()
+        if (typeArg == null || typeArg.asTypeName().rawTypeName != element.asClassName()) {
+            context.logger.e(
+                element = typeElement,
+                msg =
+                    ProcessorErrors.invalidConstructedBySuperInterface(
+                        expectedSuperInterfaceTypeName.toString(context.codeLanguage)
+                    )
+            )
+            return null
+        }
+
+        return typeElement
+    }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 4b61d28..1e5d350 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -24,8 +24,8 @@
 import androidx.room.Update
 import androidx.room.Upsert
 import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.RoomTypeNames.ROOM_DB
-import androidx.room.ext.SupportDbTypeNames
 import androidx.room.parser.QueryType
 import androidx.room.parser.SQLTypeAffinity
 import androidx.room.vo.CustomTypeConverter
@@ -795,8 +795,8 @@
     }
 
     val RAW_QUERY_STRING_PARAMETER_REMOVED =
-        "RawQuery does not allow passing a string anymore." +
-            " Please use ${SupportDbTypeNames.QUERY.canonicalName}."
+        "@RawQuery does not allow passing a string anymore." +
+            " Please use ${RoomTypeNames.RAW_QUERY.canonicalName}."
 
     val MISSING_COPY_ANNOTATIONS =
         "Annotated property getter is missing " + "@AutoValue.CopyAnnotations."
@@ -1257,4 +1257,19 @@
     val RAW_QUERY_NOT_SUPPORTED_ON_NON_ANDROID =
         "@RawQuery annotated DAO functions are currently not supported in source sets targeting " +
             "non-Android platforms."
+
+    val MISSING_CONSTRUCTED_BY_ANNOTATION =
+        "The @Database class must be annotated with @ConstructedBy since the source is targeting " +
+            "non-Android platforms."
+
+    val INVALID_CONSTRUCTED_BY_CLASS = "The @ConstructedBy 'value' must be a valid class."
+
+    val INVALID_CONSTRUCTED_BY_NOT_OBJECT =
+        "The @ConstructedBy definition must be an 'object' declaration."
+
+    val INVALID_CONSTRUCTED_BY_NOT_EXPECT =
+        "The @ConstructedBy definition must be an 'expect' declaration."
+
+    fun invalidConstructedBySuperInterface(expected: String) =
+        "The @ConstructedBy definition must implement a single interface of type '$expected'."
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
index 5623a1b..17cc79f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
@@ -22,6 +22,7 @@
 import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XVariableElement
+import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.ext.isEntityElement
 import androidx.room.parser.SqlParser
@@ -52,13 +53,6 @@
             ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS
         )
 
-        // TODO(b/330586815): Support @RawQuery in KMP
-        context.checker.check(
-            context.isAndroidOnlyTarget(),
-            executableElement,
-            ProcessorErrors.RAW_QUERY_NOT_SUPPORTED_ON_NON_ANDROID
-        )
-
         val returnsDeferredType = delegate.returnsDeferredType()
         val isSuspendFunction = delegate.executableElement.isSuspendFunction()
         context.checker.check(
@@ -170,19 +164,26 @@
                         )
                 )
             }
-            // use nullable type to catch bad nullability. Because it is non-null by default in
-            // KSP, assignability will fail and we'll print a generic error instead of a specific
-            // one
-            val supportQueryType = processingEnv.requireType(SupportDbTypeNames.QUERY)
-            val isSupportSql = supportQueryType.isAssignableFrom(param)
-            if (isSupportSql) {
-                return RawQueryMethod.RuntimeQueryParameter(
-                    paramName = extractParams[0].name,
-                    typeName = supportQueryType.asTypeName()
-                )
+
+            processingEnv.findType(RoomTypeNames.RAW_QUERY)?.let { rawQueryType ->
+                if (rawQueryType.isAssignableFrom(param)) {
+                    return RawQueryMethod.RuntimeQueryParameter(
+                        paramName = extractParams[0].name,
+                        typeName = rawQueryType.asTypeName()
+                    )
+                }
             }
-            val stringType = processingEnv.requireType("java.lang.String")
-            val isString = stringType.isAssignableFrom(param)
+
+            processingEnv.findType(SupportDbTypeNames.QUERY)?.let { supportQueryType ->
+                if (supportQueryType.isAssignableFrom(param)) {
+                    return RawQueryMethod.RuntimeQueryParameter(
+                        paramName = extractParams[0].name,
+                        typeName = supportQueryType.asTypeName()
+                    )
+                }
+            }
+
+            val isString = processingEnv.requireType(String::class).isAssignableFrom(param)
             if (isString) {
                 // special error since this was initially allowed but removed in 1.1 beta1
                 context.logger.e(executableElement, RAW_QUERY_STRING_PARAMETER_REMOVED)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/cache/Cache.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/cache/Cache.kt
index a7a2645..d4b320e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/cache/Cache.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/cache/Cache.kt
@@ -14,30 +14,26 @@
  * limitations under the License.
  */
 
-@file:Suppress("AddVarianceModifier")
-
 package androidx.room.processor.cache
 
 import androidx.room.compiler.processing.XElement
-import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.XTypeElement
 import androidx.room.processor.FieldProcessor
 import androidx.room.vo.BuiltInConverterFlags
 import androidx.room.vo.EmbeddedField
 import androidx.room.vo.Entity
 import androidx.room.vo.Pojo
 import androidx.room.vo.Warning
-import java.util.LinkedHashSet
 
 /**
  * A cache key can be used to avoid re-processing elements.
  *
- * <p>
  * Each context has a cache variable that uses the same backing storage as the Root Context but adds
  * current adapters and warning suppression list to the key.
  */
 class Cache(
     val parent: Cache?,
-    val converters: LinkedHashSet<XType>,
+    val converters: Set<XTypeElement>,
     val suppressedWarnings: Set<Warning>,
     val builtInConverterFlags: BuiltInConverterFlags
 ) {
@@ -49,7 +45,7 @@
 
         fun get(key: K, calculate: () -> T): T {
             val fullKey = FullKey(converters, suppressedWarnings, builtInConverterFlags, key)
-            return entries.getOrPut(fullKey, { calculate() })
+            return entries.getOrPut(fullKey) { calculate() }
         }
     }
 
@@ -66,11 +62,10 @@
     /**
      * Internal key representation with adapters & warnings included.
      *
-     * <p>
      * Converters are kept in a linked set since the order is important for the TypeAdapterStore.
      */
     private data class FullKey<T>(
-        val converters: LinkedHashSet<XType>,
+        val converters: Set<XTypeElement>,
         val suppressedWarnings: Set<Warning>,
         val builtInConverterFlags: BuiltInConverterFlags,
         val key: T
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index 8fa161a..336e028 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -946,7 +946,9 @@
                 // we don't know what query returns. Check for entity.
                 if (typeElement.isEntityElement()) {
                     return EntityRowAdapter(
-                        EntityProcessor(context = context, element = typeElement).process()
+                        entity =
+                            EntityProcessor(context = context, element = typeElement).process(),
+                        out = typeMirror
                     )
                 }
             }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
index deebaf7..4ad1df3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
@@ -18,18 +18,24 @@
 
 import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
 import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.processing.XType
 import androidx.room.ext.AndroidTypeNames
 import androidx.room.ext.ArrayLiteral
 import androidx.room.ext.CommonTypeNames
-import androidx.room.ext.RoomMemberNames
+import androidx.room.ext.RoomTypeNames.CURSOR_UTIL
+import androidx.room.ext.RoomTypeNames.STATEMENT_UTIL
+import androidx.room.ext.SQLiteDriverTypeNames
 import androidx.room.solver.CodeGenScope
 import androidx.room.vo.ColumnIndexVar
 import androidx.room.vo.Entity
 import androidx.room.vo.columnNames
 import androidx.room.writer.EntityCursorConverterWriter
 
-class EntityRowAdapter(val entity: Entity) : QueryMappedRowAdapter(entity.type) {
+class EntityRowAdapter(val entity: Entity, out: XType) : QueryMappedRowAdapter(out) {
+
+    override fun isMigratedToDriver() = true
 
     override val mapping = EntityMapping(entity)
 
@@ -45,13 +51,19 @@
             override fun onCursorReady(cursorVarName: String, scope: CodeGenScope) {
                 indexVars =
                     entity.columnNames.map { columnName ->
+                        val packageMember =
+                            if (scope.useDriverApi) {
+                                STATEMENT_UTIL.packageMember("getColumnIndex")
+                            } else {
+                                CURSOR_UTIL.packageMember("getColumnIndex")
+                            }
                         ColumnIndexVar(
                             column = columnName,
                             indexVar =
                                 XCodeBlock.of(
                                         scope.language,
                                         "%M(%L, %S)",
-                                        RoomMemberNames.CURSOR_UTIL_GET_COLUMN_INDEX,
+                                        packageMember,
                                         cursorVarName,
                                         columnName
                                     )
@@ -75,10 +87,10 @@
         if (indices.isNotEmpty() && indices != indexAdapter.getIndexVars()) {
             // Due to entity converter code being shared and using Cursor.getColumnIndex() we can't
             // generate code that uses the mapping directly. Instead we create a wrapped Cursor that
-            // is
-            // solely used in the shared converter method and whose getColumnIndex() is overridden
-            // to return the resolved column index.
-            cursorDelegateVarName = scope.getTmpVar("_wrappedCursor")
+            // is solely used in the shared converter method and whose getColumnIndex() is
+            // overridden to return the resolved column index.
+            cursorDelegateVarName =
+                scope.getTmpVar(if (scope.useDriverApi) "_wrappedStmt" else "_wrappedCursor")
             val entityColumnNamesParam =
                 ArrayLiteral(
                     scope.language,
@@ -91,21 +103,36 @@
                     XTypeName.PRIMITIVE_INT,
                     *indices.map { it.indexVar }.toTypedArray()
                 )
+            val wrapperTypeName =
+                if (scope.useDriverApi) {
+                    SQLiteDriverTypeNames.STATEMENT
+                } else {
+                    AndroidTypeNames.CURSOR
+                }
+            val packageMember =
+                if (scope.useDriverApi) {
+                    STATEMENT_UTIL.packageMember("wrapMappedColumns")
+                } else {
+                    CURSOR_UTIL.packageMember("wrapMappedColumns")
+                }
             scope.builder.addLocalVariable(
                 checkNotNull(cursorDelegateVarName),
-                AndroidTypeNames.CURSOR,
+                wrapperTypeName,
                 assignExpr =
                     XCodeBlock.of(
                         scope.language,
                         "%M(%L, %L, %L)",
-                        RoomMemberNames.CURSOR_UTIL_WRAP_MAPPED_COLUMNS,
+                        packageMember,
                         cursorVarName,
                         entityColumnNamesParam,
                         entityColumnIndicesParam
                     )
             )
         }
-        functionSpec = scope.writer.getOrCreateFunction(EntityCursorConverterWriter(entity))
+        functionSpec =
+            scope.writer.getOrCreateFunction(
+                EntityCursorConverterWriter(entity = entity, userDriverApi = scope.useDriverApi)
+            )
     }
 
     override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaImmutableMultimapQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaImmutableMultimapQueryResultAdapter.kt
index 1d40374..1aa6e2a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaImmutableMultimapQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaImmutableMultimapQueryResultAdapter.kt
@@ -89,7 +89,8 @@
 
             val tmpKeyVarName = scope.getTmpVar("_key")
             val tmpValueVarName = scope.getTmpVar("_value")
-            beginControlFlow("while (%L.moveToNext())", cursorVarName).apply {
+            val stepName = if (scope.useDriverApi) "step" else "moveToNext"
+            beginControlFlow("while (%L.$stepName())", cursorVarName).apply {
                 addLocalVariable(name = tmpKeyVarName, typeName = keyTypeArg.asTypeName())
                 keyRowAdapter.convert(tmpKeyVarName, cursorVarName, scope)
 
@@ -122,4 +123,6 @@
             )
         }
     }
+
+    override fun isMigratedToDriver() = true
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt
index deabce9..4cc5676 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt
@@ -46,4 +46,6 @@
             )
         }
     }
+
+    override fun isMigratedToDriver() = true
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableListQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableListQueryResultAdapter.kt
index dd1e1b3..c5d97af 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableListQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableListQueryResultAdapter.kt
@@ -45,7 +45,8 @@
             )
 
             val tmpVarName = scope.getTmpVar("_item")
-            beginControlFlow("while (%L.moveToNext())", cursorVarName).apply {
+            val stepName = if (scope.useDriverApi) "step" else "moveToNext"
+            beginControlFlow("while (%L.$stepName())", cursorVarName).apply {
                 addLocalVariable(name = tmpVarName, typeName = typeArg.asTypeName())
                 rowAdapter.convert(tmpVarName, cursorVarName, scope)
                 addStatement("%L.add(%L)", immutableListBuilderName, tmpVarName)
@@ -59,4 +60,6 @@
             )
         }
     }
+
+    override fun isMigratedToDriver() = true
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableMapQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableMapQueryResultAdapter.kt
index fc07a26..bded01f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableMapQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableMapQueryResultAdapter.kt
@@ -51,4 +51,6 @@
             )
         }
     }
+
+    override fun isMigratedToDriver() = true
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt
index a722745..39caa47 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt
@@ -48,4 +48,6 @@
             )
         }
     }
+
+    override fun isMigratedToDriver() = true
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt
index 3be6247..070fe41 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt
@@ -38,12 +38,10 @@
         if (fromCursorConverter == null) {
             return
         }
-        scope.builder.apply {
-            val tmpCursorValue = scope.getTmpVar()
-            addLocalVariable(tmpCursorValue, columnTypeAdapter.outTypeName)
-            columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, indexVarName, scope)
-            fromCursorConverter.convert(tmpCursorValue, outVarName, scope)
-        }
+        val tmpCursorValue = scope.getTmpVar()
+        scope.builder.addLocalVariable(tmpCursorValue, columnTypeAdapter.outTypeName)
+        columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, indexVarName, scope)
+        fromCursorConverter.convert(tmpCursorValue, outVarName, scope)
     }
 
     override fun bindToStmt(
@@ -55,9 +53,7 @@
         if (intoStatementConverter == null) {
             return
         }
-        scope.builder.apply {
-            val bindVar = intoStatementConverter.convert(valueVarName, scope)
-            columnTypeAdapter.bindToStmt(stmtName, indexVarName, bindVar, scope)
-        }
+        val bindVar = intoStatementConverter.convert(valueVarName, scope)
+        columnTypeAdapter.bindToStmt(stmtName, indexVarName, bindVar, scope)
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeTypeConverter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeTypeConverter.kt
index f9f2f02..cd8c2aee 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeTypeConverter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeTypeConverter.kt
@@ -22,16 +22,12 @@
 class CompositeTypeConverter(val conv1: TypeConverter, val conv2: TypeConverter) :
     TypeConverter(from = conv1.from, to = conv2.to, cost = conv1.cost + conv2.cost) {
     override fun doConvert(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
-        scope.builder.apply {
-            val conv1Output = conv1.convert(inputVarName, scope)
-            conv2.convert(inputVarName = conv1Output, outputVarName = outputVarName, scope = scope)
-        }
+        val conv1Output = conv1.convert(inputVarName, scope)
+        conv2.convert(inputVarName = conv1Output, outputVarName = outputVarName, scope = scope)
     }
 
     override fun doConvert(inputVarName: String, scope: CodeGenScope): String {
-        scope.builder.apply {
-            val conv1Output = conv1.convert(inputVarName = inputVarName, scope = scope)
-            return conv2.convert(inputVarName = conv1Output, scope = scope)
-        }
+        val conv1Output = conv1.convert(inputVarName = inputVarName, scope = scope)
+        return conv2.convert(inputVarName = conv1Output, scope = scope)
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt
index b311196..63b0df8 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt
@@ -23,20 +23,16 @@
 /** A [TypeConverter] that has only 1 statement (e.g. foo ? bar : baz). */
 abstract class SingleStatementTypeConverter(from: XType, to: XType) : TypeConverter(from, to) {
     final override fun doConvert(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
-        scope.builder.apply {
-            addStatement("%L = %L", outputVarName, buildStatement(inputVarName, scope))
-        }
+        scope.builder.addStatement("%L = %L", outputVarName, buildStatement(inputVarName, scope))
     }
 
     final override fun doConvert(inputVarName: String, scope: CodeGenScope): String {
         val outputVarName = scope.getTmpVar()
-        scope.builder.apply {
-            addLocalVariable(
-                name = outputVarName,
-                typeName = to.asTypeName(),
-                assignExpr = buildStatement(inputVarName, scope)
-            )
-        }
+        scope.builder.addLocalVariable(
+            name = outputVarName,
+            typeName = to.asTypeName(),
+            assignExpr = buildStatement(inputVarName, scope)
+        )
         return outputVarName
     }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/TypeConverter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/TypeConverter.kt
index 08555b8..31b3b852 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/TypeConverter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/TypeConverter.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.solver.types
 
-import androidx.annotation.VisibleForTesting
 import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
 
@@ -41,7 +40,7 @@
      */
     protected open fun doConvert(inputVarName: String, scope: CodeGenScope): String {
         val outVarName = scope.getTmpVar()
-        scope.builder.apply { addLocalVariable(outVarName, to.asTypeName()) }
+        scope.builder.addLocalVariable(outVarName, to.asTypeName())
         doConvert(inputVarName = inputVarName, outputVarName = outVarName, scope = scope)
         return outVarName
     }
@@ -89,20 +88,16 @@
             intArrayOf(requireNotNull, converters, nullSafeWrapper, upCasts)
         )
 
-        @VisibleForTesting
-        val upCasts: Int
+        private val upCasts: Int
             get() = values[Buckets.UP_CAST]
 
-        @VisibleForTesting
-        val nullSafeWrapper: Int
+        private val nullSafeWrapper: Int
             get() = values[Buckets.NULL_SAFE]
 
-        @VisibleForTesting
-        val requireNotNull: Int
+        private val requireNotNull: Int
             get() = values[Buckets.REQUIRE_NOT_NULL]
 
-        @VisibleForTesting
-        val converters: Int
+        private val converters: Int
             get() = values[Buckets.CONVERTER]
 
         operator fun plus(other: Cost) =
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt
index 7c296f1..aaab5ca 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Database.kt
@@ -39,7 +39,8 @@
     val version: Int,
     val exportSchema: Boolean,
     val enableForeignKeys: Boolean,
-    val overrideClearAllTables: Boolean
+    val overrideClearAllTables: Boolean,
+    val constructorObjectElement: XTypeElement?
 ) {
     // This variable will be set once auto-migrations are processed given the DatabaseBundle from
     // this object. This is necessary for tracking the versions involved in the auto-migration.
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/RawQueryMethod.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/RawQueryMethod.kt
index 8b36377..c447dd3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/RawQueryMethod.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/RawQueryMethod.kt
@@ -21,6 +21,7 @@
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.isKotlinUnit
 import androidx.room.ext.CommonTypeNames
+import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.ext.isNotVoid
 import androidx.room.solver.query.result.QueryResultBinder
@@ -43,5 +44,7 @@
         fun isString() = CommonTypeNames.STRING == typeName
 
         fun isSupportQuery() = SupportDbTypeNames.QUERY == typeName
+
+        fun isRawQuery() = RoomTypeNames.RAW_QUERY == typeName
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
index 86ff171..295e26f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
@@ -610,7 +610,8 @@
             val canUseLongSparseArray =
                 context.processingEnv.findTypeElement(LONG_SPARSE_ARRAY.canonicalName) != null
             val canUseArrayMap =
-                context.processingEnv.findTypeElement(ARRAY_MAP.canonicalName) != null
+                context.processingEnv.findTypeElement(ARRAY_MAP.canonicalName) != null &&
+                    context.isAndroidOnlyTarget()
             return when {
                 canUseLongSparseArray && affinity == SQLTypeAffinity.INTEGER ->
                     LONG_SPARSE_ARRAY.parametrizedBy(valueTypeName)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt
index 72e060a..90196b8 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.vo
 
+import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XVariableElement
 
@@ -34,4 +35,6 @@
         } else {
             "handle"
         }
+
+    val isNonNull = type.nullability == XNullability.NONNULL
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
index 00d8468..e093278 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
@@ -165,6 +165,7 @@
      * @param migrateBuilder Builder for the migrate() function to be generated
      */
     private fun addComplexChangeStatements(migrateBuilder: XFunSpec.Builder) {
+        val tablesToCheckForeignKeys = mutableListOf<String>()
         // Create a collection that is sorted such that FTS bundles are handled after the normal
         // tables have been processed
         complexChangedTables.values
@@ -200,14 +201,15 @@
                     if (newEntityBundle is EntityBundle) {
                         addStatementsToRecreateIndexes(newEntityBundle, migrateBuilder)
                         if (newEntityBundle.foreignKeys.isNotEmpty()) {
-                            addStatementsToCheckForeignKeyConstraint(
-                                newEntityBundle.tableName,
-                                migrateBuilder
-                            )
+                            tablesToCheckForeignKeys.add(newEntityBundle.tableName)
                         }
                     }
                 }
             }
+        // Add the SQL statements for checking the foreign key constraints.
+        tablesToCheckForeignKeys.forEach { tableName ->
+            migrateBuilder.addStatement("%M(connection, %S)", DB_UTIL_FOREIGN_KEY_CHECK, tableName)
+        }
     }
 
     private fun addStatementsToMigrateFtsTable(
@@ -219,22 +221,6 @@
         addDatabaseExecuteSqlStatement(migrateBuilder, "DROP TABLE `${oldTable.tableName}`")
         addDatabaseExecuteSqlStatement(migrateBuilder, newTable.createTable())
 
-        // Transfer contents of the FTS table, using the content table if available.
-        val newColumnSequence =
-            oldTable.fieldsByColumnName.keys
-                .filter {
-                    oldTable.fieldsByColumnName.keys.contains(it) ||
-                        renamedColumnsMap.containsKey(it)
-                }
-                .toMutableList()
-        val oldColumnSequence = mutableListOf<String>()
-        newColumnSequence.forEach { column ->
-            oldColumnSequence.add(renamedColumnsMap[column] ?: column)
-        }
-        if (oldTable is FtsEntityBundle) {
-            oldColumnSequence.add("rowid")
-            newColumnSequence.add("docid")
-        }
         val contentTable = (newTable as FtsEntityBundle).ftsOptions.contentTable
         val selectFromTable =
             if (contentTable.isEmpty()) {
@@ -242,16 +228,14 @@
             } else {
                 contentTable
             }
-        addDatabaseExecuteSqlStatement(
-            migrateBuilder,
-            buildString {
-                append(
-                    "INSERT INTO `${newTable.tableName}` " +
-                        "(${newColumnSequence.joinToString(",") { "`$it`" }})" +
-                        " SELECT ${oldColumnSequence.joinToString(",") { "`$it`" }} " +
-                        "FROM `$selectFromTable`",
-                )
-            }
+        addStatementsToContentTransfer(
+            oldTableName = selectFromTable,
+            tableNameWithNewPrefix = newTable.tableName,
+            oldEntityBundle = oldTable,
+            newEntityBundle = newTable,
+            renamedColumnsMap = renamedColumnsMap,
+            migrateBuilder = migrateBuilder,
+            isFtsTableContentTransfer = true
         )
     }
 
@@ -285,6 +269,17 @@
     /**
      * Adds the SQL statements for transferring the contents of the old table to the new version.
      *
+     * This function is used in two scenarios: [1] Transfer content after a complex change (in any
+     * type of table) has been found where the table has been recreated to reflect the changes and
+     * needs the contents transferred from the old table.
+     *
+     * [2] FTS table content transfers. This needs to be handled separately, in the case where the
+     * referenced content table of the FTS table has also undergone a complex change such as a
+     * column rename in the same migration. In this scenario, we should be using the most up to date
+     * list of columns in the table instead of the column names of the "old" table, since the FTS
+     * table content transfer is guaranteed to take place after all complex changes to table
+     * structure has completed.
+     *
      * @param oldTableName Name of the table in the old version of the database
      * @param tableNameWithNewPrefix Name of the table with the '_new_' prefix added
      * @param oldEntityBundle Entity bundle of the table in the old version of the database
@@ -298,7 +293,8 @@
         oldEntityBundle: BaseEntityBundle,
         newEntityBundle: BaseEntityBundle,
         renamedColumnsMap: MutableMap<String, String>,
-        migrateBuilder: XFunSpec.Builder
+        migrateBuilder: XFunSpec.Builder,
+        isFtsTableContentTransfer: Boolean = false
     ) {
         val newColumnSequence =
             newEntityBundle.fieldsByColumnName.keys
@@ -307,9 +303,18 @@
                         renamedColumnsMap.containsKey(it)
                 }
                 .toMutableList()
-        val oldColumnSequence = mutableListOf<String>()
-        newColumnSequence.forEach { column ->
-            oldColumnSequence.add(renamedColumnsMap[column] ?: column)
+
+        val selectColumnSequence = mutableListOf<String>()
+        // Select correct columns for transfer based on whether we are doing an FTS table content
+        // transfer or not.
+        if (isFtsTableContentTransfer) {
+            selectColumnSequence.addAll(newColumnSequence)
+            selectColumnSequence.add("rowId")
+            newColumnSequence.add("docid")
+        } else {
+            newColumnSequence.forEach { column ->
+                selectColumnSequence.add(renamedColumnsMap[column] ?: column)
+            }
         }
 
         addDatabaseExecuteSqlStatement(
@@ -318,7 +323,7 @@
                 append(
                     "INSERT INTO `$tableNameWithNewPrefix` " +
                         "(${newColumnSequence.joinToString(",") { "`$it`" }})" +
-                        " SELECT ${oldColumnSequence.joinToString(",") { "`$it`" }} FROM " +
+                        " SELECT ${selectColumnSequence.joinToString(",") { "`$it`" }} FROM " +
                         "`$oldTableName`",
                 )
             }
@@ -363,19 +368,6 @@
     }
 
     /**
-     * Adds the SQL statement for checking the foreign key constraints.
-     *
-     * @param tableName Name of the table
-     * @param migrateBuilder Builder for the migrate() function to be generated
-     */
-    private fun addStatementsToCheckForeignKeyConstraint(
-        tableName: String,
-        migrateBuilder: XFunSpec.Builder
-    ) {
-        migrateBuilder.addStatement("%M(connection, %S)", DB_UTIL_FOREIGN_KEY_CHECK, tableName)
-    }
-
-    /**
      * Adds the SQL statements for removing a table.
      *
      * @param migrateBuilder Builder for the migrate() function to be generated
@@ -411,7 +403,7 @@
             val addNewColumnSql = buildString {
                 append(
                     "ALTER TABLE `${it.tableName}` ADD COLUMN `${it.fieldBundle.columnName}` " +
-                        "${it.fieldBundle.affinity}"
+                        it.fieldBundle.affinity
                 )
                 if (it.fieldBundle.isNonNull) {
                     append(" NOT NULL")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index 0c493c7..8ff5d2d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -327,64 +327,106 @@
     }
 
     private fun createRawQueryMethod(method: RawQueryMethod): XFunSpec {
-        val body =
-            XCodeBlock.builder(codeLanguage)
-                .apply {
-                    val scope = CodeGenScope(this@DaoWriter)
-                    val roomSQLiteQueryVar: String
-                    val queryParam = method.runtimeQueryParam
-                    val shouldReleaseQuery: Boolean
-                    if (queryParam?.isSupportQuery() == true) {
-                        roomSQLiteQueryVar = queryParam.paramName
-                        shouldReleaseQuery = false
-                    } else if (queryParam?.isString() == true) {
-                        roomSQLiteQueryVar = scope.getTmpVar("_statement")
-                        shouldReleaseQuery = true
-                        addLocalVariable(
-                            name = roomSQLiteQueryVar,
-                            typeName = RoomTypeNames.ROOM_SQL_QUERY,
-                            assignExpr =
-                                XCodeBlock.of(
-                                    codeLanguage,
-                                    "%M(%L, 0)",
-                                    RoomMemberNames.ROOM_SQL_QUERY_ACQUIRE,
-                                    queryParam.paramName
-                                ),
-                        )
-                    } else {
-                        // try to generate compiling code. we would've already reported this error
-                        roomSQLiteQueryVar = scope.getTmpVar("_statement")
-                        shouldReleaseQuery = false
-                        addLocalVariable(
-                            name = roomSQLiteQueryVar,
-                            typeName = RoomTypeNames.ROOM_SQL_QUERY,
-                            assignExpr =
-                                XCodeBlock.of(
-                                    codeLanguage,
-                                    "%M(%S, 0)",
-                                    RoomMemberNames.ROOM_SQL_QUERY_ACQUIRE,
-                                    "missing query parameter"
-                                ),
-                        )
-                    }
-                    if (method.returnsValue) {
-                        // don't generate code because it will create 1 more error. The original
-                        // error is
-                        // already reported by the processor.
-                        method.queryResultBinder.convertAndReturn(
-                            roomSQLiteQueryVar = roomSQLiteQueryVar,
-                            canReleaseQuery = shouldReleaseQuery,
-                            dbProperty = dbProperty,
-                            inTransaction = method.inTransaction,
-                            scope = scope
-                        )
-                    }
-                    add(scope.generate())
-                }
-                .build()
-        return overrideWithoutAnnotations(method.element, declaredDao).addCode(body).build()
+        return overrideWithoutAnnotations(method.element, declaredDao)
+            .addCode(createRawQueryMethodBody(method))
+            .build()
     }
 
+    private fun createRawQueryMethodBody(method: RawQueryMethod): XCodeBlock {
+        if (
+            method.runtimeQueryParam == null ||
+                !method.runtimeQueryParam.isRawQuery() ||
+                !method.queryResultBinder.isMigratedToDriver()
+        ) {
+            return compatCreateRawQueryMethodBody(method)
+        }
+
+        val scope = CodeGenScope(this@DaoWriter, useDriverApi = true)
+        val sqlQueryVar = scope.getTmpVar("_sql")
+        scope.builder.addLocalVal(
+            sqlQueryVar,
+            CommonTypeNames.STRING,
+            "%L.%L",
+            method.runtimeQueryParam.paramName,
+            when (codeLanguage) {
+                CodeLanguage.JAVA -> "getSql()"
+                CodeLanguage.KOTLIN -> "sql"
+            }
+        )
+        if (method.returnsValue) {
+            method.queryResultBinder.convertAndReturn(
+                sqlQueryVar = sqlQueryVar,
+                dbProperty = dbProperty,
+                bindStatement = { stmtVar ->
+                    this.builder.addStatement(
+                        "%L.getBindingFunction().invoke(%L)",
+                        method.runtimeQueryParam.paramName,
+                        stmtVar
+                    )
+                },
+                returnTypeName = method.returnType.asTypeName(),
+                inTransaction = method.inTransaction,
+                scope = scope
+            )
+        }
+        return scope.generate()
+    }
+
+    private fun compatCreateRawQueryMethodBody(method: RawQueryMethod): XCodeBlock =
+        XCodeBlock.builder(codeLanguage)
+            .apply {
+                val scope = CodeGenScope(this@DaoWriter)
+                val roomSQLiteQueryVar: String
+                val queryParam = method.runtimeQueryParam
+                val shouldReleaseQuery: Boolean
+                if (queryParam?.isSupportQuery() == true) {
+                    roomSQLiteQueryVar = queryParam.paramName
+                    shouldReleaseQuery = false
+                } else if (queryParam?.isString() == true) {
+                    roomSQLiteQueryVar = scope.getTmpVar("_statement")
+                    shouldReleaseQuery = true
+                    addLocalVariable(
+                        name = roomSQLiteQueryVar,
+                        typeName = RoomTypeNames.ROOM_SQL_QUERY,
+                        assignExpr =
+                            XCodeBlock.of(
+                                codeLanguage,
+                                "%M(%L, 0)",
+                                RoomMemberNames.ROOM_SQL_QUERY_ACQUIRE,
+                                queryParam.paramName
+                            ),
+                    )
+                } else {
+                    // try to generate compiling code. we would've already reported this error
+                    roomSQLiteQueryVar = scope.getTmpVar("_statement")
+                    shouldReleaseQuery = false
+                    addLocalVariable(
+                        name = roomSQLiteQueryVar,
+                        typeName = RoomTypeNames.ROOM_SQL_QUERY,
+                        assignExpr =
+                            XCodeBlock.of(
+                                codeLanguage,
+                                "%M(%S, 0)",
+                                RoomMemberNames.ROOM_SQL_QUERY_ACQUIRE,
+                                "missing query parameter"
+                            ),
+                    )
+                }
+                if (method.returnsValue) {
+                    // don't generate code because it will create 1 more error. The original
+                    // error is already reported by the processor.
+                    method.queryResultBinder.convertAndReturn(
+                        roomSQLiteQueryVar = roomSQLiteQueryVar,
+                        canReleaseQuery = shouldReleaseQuery,
+                        dbProperty = dbProperty,
+                        inTransaction = method.inTransaction,
+                        scope = scope
+                    )
+                }
+                add(scope.generate())
+            }
+            .build()
+
     private fun createPreparedQueryMethod(method: WriteQueryMethod): XFunSpec {
         return overrideWithoutAnnotations(method.element, declaredDao)
             .addCode(createPreparedQueryMethodBody(method))
@@ -429,6 +471,7 @@
         }
         val useDriverApi = method.methodBinder.isMigratedToDriver()
         val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
+        ShortcutQueryParameterWriter.addNullCheckValidation(scope, method.parameters)
         if (useDriverApi) {
             method.methodBinder.convertAndReturn(
                 parameters = method.parameters,
@@ -515,6 +558,7 @@
         }
         val useDriverApi = method.methodBinder.isMigratedToDriver()
         val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
+        ShortcutQueryParameterWriter.addNullCheckValidation(scope, method.parameters)
         if (useDriverApi) {
             method.methodBinder.convertAndReturn(
                 parameters = method.parameters,
@@ -566,7 +610,7 @@
         }
         val useDriverApi = method.methodBinder.isMigratedToDriver()
         val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
-
+        ShortcutQueryParameterWriter.addNullCheckValidation(scope, method.parameters)
         if (useDriverApi) {
             method.methodBinder.convertAndReturn(
                 parameters = method.parameters,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DatabaseObjectConstructorWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DatabaseObjectConstructorWriter.kt
new file mode 100644
index 0000000..fc04bab
--- /dev/null
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DatabaseObjectConstructorWriter.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.writer
+
+import androidx.room.compiler.codegen.toKotlinPoet
+import androidx.room.compiler.processing.XProcessingEnv
+import androidx.room.compiler.processing.XTypeElement
+import androidx.room.compiler.processing.addOriginatingElement
+import androidx.room.ext.RoomTypeNames.ROOM_DB_CONSTRUCTOR
+import androidx.room.vo.Database
+import com.squareup.kotlinpoet.FileSpec
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.TypeSpec
+
+class DatabaseObjectConstructorWriter(
+    private val database: Database,
+    private val constructorObjectElement: XTypeElement
+) {
+    fun write(processingEnv: XProcessingEnv) {
+        val databaseClassName = database.typeName.toKotlinPoet()
+        val objectClassName = constructorObjectElement.asClassName().toKotlinPoet()
+        val typeSpec =
+            TypeSpec.objectBuilder(objectClassName)
+                .addOriginatingElement(database.element)
+                .addModifiers(KModifier.ACTUAL)
+                .addSuperinterface(
+                    ROOM_DB_CONSTRUCTOR.toKotlinPoet().parameterizedBy(databaseClassName)
+                )
+                .addFunction(
+                    FunSpec.builder("initialize")
+                        .addModifiers(KModifier.OVERRIDE)
+                        .returns(databaseClassName)
+                        .addStatement("return %L()", database.implTypeName.toKotlinPoet())
+                        .build()
+                )
+                .build()
+        val fileSpec =
+            FileSpec.builder(objectClassName.packageName, objectClassName.simpleName)
+                .addType(typeSpec)
+                .build()
+        processingEnv.filer.write(fileSpec)
+    }
+}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
index 627ebc5..f22987b 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
@@ -16,12 +16,14 @@
 
 package androidx.room.writer
 
-import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
 import androidx.room.compiler.codegen.XTypeName
-import androidx.room.ext.AndroidTypeNames.CURSOR
-import androidx.room.ext.RoomMemberNames
+import androidx.room.ext.AndroidTypeNames
+import androidx.room.ext.RoomTypeNames.CURSOR_UTIL
+import androidx.room.ext.RoomTypeNames.STATEMENT_UTIL
+import androidx.room.ext.SQLiteDriverTypeNames
 import androidx.room.ext.capitalize
 import androidx.room.ext.stripNonJava
 import androidx.room.solver.CodeGenScope
@@ -29,25 +31,33 @@
 import androidx.room.vo.FieldWithIndex
 import java.util.Locale
 
-class EntityCursorConverterWriter(val entity: Entity) :
+class EntityCursorConverterWriter(private val entity: Entity, private val userDriverApi: Boolean) :
     TypeWriter.SharedFunctionSpec(
-        "entityCursorConverter_${entity.typeName.toString(CodeLanguage.JAVA).stripNonJava()}"
+        if (userDriverApi) {
+            "entityStatementConverter_${entity.className.canonicalName.stripNonJava()}"
+        } else {
+            "entityCursorConverter_${entity.className.canonicalName.stripNonJava()}"
+        }
     ) {
     override fun getUniqueKey(): String {
-        return "generic_entity_converter_of_${entity.element.qualifiedName}"
+        return "generic_entity_converter_of_${entity.element.qualifiedName}-$userDriverApi"
     }
 
     override fun prepare(methodName: String, writer: TypeWriter, builder: XFunSpec.Builder) {
         builder.apply {
-            val cursorParamName = "cursor"
-            addParameter(CURSOR, cursorParamName)
+            val cursorParamName = if (userDriverApi) "statement" else "cursor"
+            if (userDriverApi) {
+                addParameter(SQLiteDriverTypeNames.STATEMENT, cursorParamName)
+            } else {
+                addParameter(AndroidTypeNames.CURSOR, cursorParamName)
+            }
             returns(entity.typeName)
             addCode(buildConvertMethodBody(writer, cursorParamName))
         }
     }
 
     private fun buildConvertMethodBody(writer: TypeWriter, cursorParamName: String): XCodeBlock {
-        val scope = CodeGenScope(writer)
+        val scope = CodeGenScope(writer, userDriverApi)
         val entityVar = scope.getTmpVar("_entity")
         scope.builder.apply {
             addLocalVariable(entityVar, entity.typeName)
@@ -57,6 +67,12 @@
                         scope.getTmpVar(
                             "_cursorIndexOf${it.name.stripNonJava().capitalize(Locale.US)}"
                         )
+                    val packageMember =
+                        if (scope.useDriverApi) {
+                            STATEMENT_UTIL.packageMember("getColumnIndex")
+                        } else {
+                            CURSOR_UTIL.packageMember("getColumnIndex")
+                        }
                     addLocalVariable(
                         name = indexVar,
                         typeName = XTypeName.PRIMITIVE_INT,
@@ -64,7 +80,7 @@
                             XCodeBlock.of(
                                 language,
                                 "%M(%N, %S)",
-                                RoomMemberNames.CURSOR_UTIL_GET_COLUMN_INDEX,
+                                packageMember,
                                 cursorParamName,
                                 it.columnName
                             )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/InstantiateImplWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/InstantiateImplWriter.kt
deleted file mode 100644
index 9f97e9a8..0000000
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/InstantiateImplWriter.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.writer
-
-import androidx.room.compiler.codegen.toKotlinPoet
-import androidx.room.compiler.processing.XProcessingEnv
-import androidx.room.compiler.processing.addOriginatingElement
-import androidx.room.ext.CommonTypeNames.KOTLIN_CLASS
-import androidx.room.vo.Database
-import com.squareup.kotlinpoet.FileSpec
-import com.squareup.kotlinpoet.FunSpec
-import com.squareup.kotlinpoet.KModifier
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
-
-/** Generates the `instantiateImpl` function which returns the generated database implementation. */
-class InstantiateImplWriter(
-    val database: Database,
-) {
-    fun write(processingEnv: XProcessingEnv) {
-        val databaseTypeName = database.typeName.toKotlinPoet()
-        val fileName = "${databaseTypeName.simpleNames.joinToString("_")}_InstantiateImpl"
-        val funSpec =
-            FunSpec.builder("instantiateImpl")
-                .addOriginatingElement(database.element)
-                .addModifiers(KModifier.INTERNAL)
-                .receiver(KOTLIN_CLASS.toKotlinPoet().parameterizedBy(databaseTypeName))
-                .returns(databaseTypeName)
-                .addStatement("return %L()", database.implTypeName.toKotlinPoet())
-                .build()
-
-        val fileSpec =
-            FileSpec.builder(database.typeName.packageName, fileName).addFunction(funSpec).build()
-        processingEnv.filer.write(fileSpec)
-    }
-}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/ShortcutQueryParameterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/ShortcutQueryParameterWriter.kt
new file mode 100644
index 0000000..c8b3e89
--- /dev/null
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/ShortcutQueryParameterWriter.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.writer
+
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.asClassName
+import androidx.room.solver.CodeGenScope
+import androidx.room.vo.ShortcutQueryParameter
+
+/** Writer for [ShortcutQueryParameter] related statements. */
+object ShortcutQueryParameterWriter {
+    fun addNullCheckValidation(scope: CodeGenScope, parameters: List<ShortcutQueryParameter>) {
+        // The null checks are only needed for Java since Kotlin instinctively adds null checks to
+        // params that are not null.
+        if (scope.language != CodeLanguage.JAVA) {
+            return
+        }
+        parameters
+            .filter { it.isNonNull }
+            .forEach {
+                scope.builder.addStatement(
+                    "if (%L == null) throw %L",
+                    it.name,
+                    XCodeBlock.ofNewInstance(
+                        scope.language,
+                        NullPointerException::class.asClassName()
+                    )
+                )
+            }
+    }
+}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseConstructorProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseConstructorProcessorTest.kt
new file mode 100644
index 0000000..44ec92f
--- /dev/null
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseConstructorProcessorTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processor
+
+import androidx.room.compiler.processing.XTypeElement
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.runKspTest
+import androidx.room.testing.context
+import org.junit.Test
+
+class DatabaseConstructorProcessorTest {
+
+    private val databaseSource =
+        Source.kotlin(
+            "Database.kt",
+            """
+        package test
+
+        import androidx.room.*
+
+        @Database(entities = [TestEntity::class], version = 1, exportSchemas = false)
+        @ConstructedBy(TestDatabaseCtor::class)
+        abstract class TestDatabase : RoomDatabase
+
+        @Entity
+        data class TestEntity(@PrimaryKey val id: Long)
+        """
+                .trimIndent()
+        )
+
+    @Test
+    fun notObjectError() {
+        runTest(
+            Source.kotlin(
+                "Constructor.kt",
+                """
+                package test
+
+                import androidx.room.*
+
+                expect class TestDatabaseCtor : RoomDatabaseConstructor<TestDatabase>
+                """
+                    .trimIndent()
+            )
+        ) {
+            it.assertCompilationResult {
+                hasErrorContaining(ProcessorErrors.INVALID_CONSTRUCTED_BY_NOT_OBJECT)
+            }
+        }
+    }
+
+    @Test
+    fun notExpectError() {
+        runTest(
+            Source.kotlin(
+                "Constructor.kt",
+                """
+                package test
+
+                import androidx.room.*
+
+                object TestDatabaseCtor : RoomDatabaseConstructor<TestDatabase>
+                """
+                    .trimIndent()
+            )
+        ) {
+            it.assertCompilationResult {
+                hasErrorContaining(ProcessorErrors.INVALID_CONSTRUCTED_BY_NOT_EXPECT)
+            }
+        }
+    }
+
+    @Test
+    fun missingSuperInterfaceError() {
+        runTest(
+            Source.kotlin(
+                "Constructor.kt",
+                """
+                package test
+
+                import androidx.room.*
+
+                expect object TestDatabaseCtor
+                """
+                    .trimIndent()
+            )
+        ) {
+            it.assertCompilationResult {
+                hasErrorContaining(
+                    ProcessorErrors.invalidConstructedBySuperInterface(
+                        "androidx.room.RoomDatabaseConstructor<test.TestDatabase>"
+                    )
+                )
+            }
+        }
+    }
+
+    @Test
+    fun incorrectSuperInterfaceTypeArgError() {
+        runTest(
+            Source.kotlin(
+                "Constructor.kt",
+                """
+                package test
+
+                import androidx.room.*
+
+                expect object TestDatabaseCtor : RoomDatabaseConstructor<RoomDatabase>
+                """
+                    .trimIndent()
+            )
+        ) {
+            it.assertCompilationResult {
+                hasErrorContaining(
+                    ProcessorErrors.invalidConstructedBySuperInterface(
+                        "androidx.room.RoomDatabaseConstructor<test.TestDatabase>"
+                    )
+                )
+            }
+        }
+    }
+
+    private fun runTest(constructorSource: Source, handler: (XTestInvocation) -> Unit = { _ -> }) {
+        runKspTest(sources = listOf(databaseSource, constructorSource)) { invocation ->
+            val entity =
+                invocation.roundEnv
+                    .getElementsAnnotatedWith(androidx.room.Database::class.qualifiedName!!)
+                    .filterIsInstance<XTypeElement>()
+                    .first()
+            DatabaseProcessor(invocation.context, entity).process()
+            handler(invocation)
+        }
+    }
+}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/verifier/DatabaseVerifierTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/verifier/DatabaseVerifierTest.kt
index e53a8714..bd1705a 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/verifier/DatabaseVerifierTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/verifier/DatabaseVerifierTest.kt
@@ -389,6 +389,7 @@
             exportSchema = false,
             enableForeignKeys = false,
             overrideClearAllTables = true,
+            constructorObjectElement = null,
         )
     }
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt
index 70c7807..46f81e2 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/vo/DatabaseTest.kt
@@ -71,7 +71,8 @@
                 version = 1,
                 exportSchema = false,
                 enableForeignKeys = false,
-                overrideClearAllTables = true
+                overrideClearAllTables = true,
+                constructorObjectElement = null,
             )
 
         val expectedLegacyHash =
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
index 2e73c14..e2e8cfc 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
@@ -989,11 +989,27 @@
                 """
             import androidx.room.*
             import androidx.sqlite.db.SupportSQLiteQuery
+            import kotlinx.coroutines.flow.Flow
 
             @Dao
             interface MyDao {
-              @RawQuery(observedEntities = [MyEntity::class])
-              fun getEntity(sql: SupportSQLiteQuery): MyEntity
+                @RawQuery
+                fun getEntitySupport(sql: SupportSQLiteQuery): MyEntity
+
+                @RawQuery
+                fun getNullableEntitySupport(sql: SupportSQLiteQuery): MyEntity?
+
+                @RawQuery(observedEntities = [MyEntity::class])
+                fun getEntitySupportFlow(sql: SupportSQLiteQuery): Flow<MyEntity>
+
+                @RawQuery
+                fun getEntity(query: RoomRawQuery): MyEntity
+
+                @RawQuery
+                fun getNullableEntity(query: RoomRawQuery): MyEntity?
+
+                @RawQuery(observedEntities = [MyEntity::class])
+                fun getEntityFlow(query: RoomRawQuery): Flow<MyEntity>
             }
 
             @Entity
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DatabaseObjectConstructorWriterKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DatabaseObjectConstructorWriterKotlinCodeGenTest.kt
new file mode 100644
index 0000000..c912440
--- /dev/null
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DatabaseObjectConstructorWriterKotlinCodeGenTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.room.writer
+
+import androidx.room.DatabaseProcessingStep
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.runKspTest
+import androidx.room.processor.Context
+import loadTestSource
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+
+class DatabaseObjectConstructorWriterKotlinCodeGenTest {
+
+    @get:Rule val testName = TestName()
+
+    @Test
+    fun actualDatabaseConstructor() {
+        val src =
+            Source.kotlin(
+                "MyDatabase.kt",
+                """
+            import androidx.room.*
+
+            @Database(entities = [MyEntity::class], version = 1, exportSchema = false)
+            @ConstructedBy(MyDatabaseCtor::class)
+            abstract class MyDatabase : RoomDatabase() {
+              abstract fun getDao(): MyDao
+            }
+
+            expect object MyDatabaseCtor : RoomDatabaseConstructor<MyDatabase>
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                var pk: Int
+            )
+            """
+                    .trimIndent()
+            )
+        runTest(sources = listOf(src), expectedFilePath = getTestGoldenPath(testName.methodName))
+    }
+
+    private fun getTestGoldenPath(testName: String): String {
+        return "kotlinCodeGen/$testName.kt"
+    }
+
+    private fun runTest(
+        sources: List<Source>,
+        expectedFilePath: String,
+        handler: (XTestInvocation) -> Unit = {}
+    ) {
+        runKspTest(
+            sources = sources,
+            options = mapOf(Context.BooleanProcessorOptions.GENERATE_KOTLIN.argName to "true"),
+        ) {
+            val databaseFqn = "androidx.room.Database"
+            DatabaseProcessingStep()
+                .process(
+                    it.processingEnv,
+                    mapOf(databaseFqn to it.roundEnv.getElementsAnnotatedWith(databaseFqn)),
+                    it.roundEnv.isProcessingOver
+                )
+            it.assertCompilationResult {
+                this.generatedSource(loadTestSource(expectedFilePath, "MyDatabaseCtor"))
+                // runKspTest only does JVM compilation (b) so it is expected to get an error due to
+                // usage of expect / actual in a non KMP compilation, however the test is still
+                // useful to validate generated code
+                this.hasErrorContaining(
+                    "'expect' and 'actual' declarations can be used only in multiplatform projects."
+                )
+            }
+            handler.invoke(it)
+        }
+    }
+}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
index a4413d6..326f7a6 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
@@ -124,7 +124,7 @@
             val writer =
                 object : TypeWriter(WriterContext(CodeLanguage.JAVA, setOf(Platform.JVM), true)) {
                     override fun createTypeSpecBuilder(): XTypeSpec.Builder {
-                        getOrCreateFunction(EntityCursorConverterWriter(entity))
+                        getOrCreateFunction(EntityCursorConverterWriter(entity, false))
                         return XTypeSpec.classBuilder(codeLanguage, className)
                             .apply(
                                 javaTypeBuilder = { addModifiers(Modifier.PUBLIC) },
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/InstantiateImplKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/InstantiateImplKotlinCodeGenTest.kt
deleted file mode 100644
index 82be80c..0000000
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/InstantiateImplKotlinCodeGenTest.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.writer
-
-import androidx.room.DatabaseProcessingStep
-import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.XTestInvocation
-import androidx.room.compiler.processing.util.runKspTest
-import androidx.room.processor.Context
-import loadTestSource
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TestName
-
-class InstantiateImplKotlinCodeGenTest {
-
-    @get:Rule val testName = TestName()
-
-    @Test
-    fun instantiateImpl_simple() {
-        val src =
-            Source.kotlin(
-                "MyDatabase.kt",
-                """
-            import androidx.room.*
-
-            @Database(entities = [MyEntity::class], version = 1, exportSchema = false)
-            abstract class MyDatabase : RoomDatabase() {
-              abstract fun getDao(): MyDao
-            }
-
-            @Dao
-            interface MyDao {
-              @Query("SELECT * FROM MyEntity")
-              fun getEntity(): MyEntity
-            }
-
-            @Entity
-            data class MyEntity(
-                @PrimaryKey
-                var pk: Int
-            )
-            """
-                    .trimIndent()
-            )
-        runTest(sources = listOf(src), expectedFilePath = getTestGoldenPath(testName.methodName))
-    }
-
-    private fun getTestGoldenPath(testName: String): String {
-        return "kotlinCodeGen/$testName.kt"
-    }
-
-    private fun runTest(
-        sources: List<Source>,
-        expectedFilePath: String,
-        handler: (XTestInvocation) -> Unit = {}
-    ) {
-        runKspTest(
-            sources = sources,
-            options = mapOf(Context.BooleanProcessorOptions.GENERATE_KOTLIN.argName to "true"),
-        ) {
-            val databaseFqn = "androidx.room.Database"
-            DatabaseProcessingStep()
-                .process(
-                    it.processingEnv,
-                    mapOf(databaseFqn to it.roundEnv.getElementsAnnotatedWith(databaseFqn)),
-                    it.roundEnv.isProcessingOver
-                )
-            it.assertCompilationResult {
-                this.generatedSource(loadTestSource(expectedFilePath, "MyDatabase_InstantiateImpl"))
-                this.hasNoWarnings()
-            }
-            handler.invoke(it)
-        }
-    }
-}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/actualDatabaseConstructor.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/actualDatabaseConstructor.kt
new file mode 100644
index 0000000..ac0ea5e
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/actualDatabaseConstructor.kt
@@ -0,0 +1,5 @@
+import androidx.room.RoomDatabaseConstructor
+
+public actual object MyDatabaseCtor : RoomDatabaseConstructor<MyDatabase> {
+    override fun initialize(): MyDatabase = MyDatabase_Impl()
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
index 153ccb4..d75dc4b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
@@ -1,11 +1,7 @@
-import android.database.Cursor
 import androidx.room.EntityInsertAdapter
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndex
 import androidx.room.util.performBlocking
-import androidx.room.util.query
 import androidx.sqlite.SQLiteStatement
 import javax.`annotation`.processing.Generated
 import kotlin.Boolean
@@ -68,85 +64,85 @@
 
   public override fun getEntity(): MyEntity {
     val _sql: String = "SELECT * FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _result: MyEntity
-      if (_cursor.moveToFirst()) {
-        _result = __entityCursorConverter_MyEntity(_cursor)
-      } else {
-        error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _result: MyEntity
+        if (_stmt.step()) {
+          _result = __entityStatementConverter_MyEntity(_stmt)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
-  private fun __entityCursorConverter_MyEntity(cursor: Cursor): MyEntity {
+  private fun __entityStatementConverter_MyEntity(statement: SQLiteStatement): MyEntity {
     val _entity: MyEntity
-    val _cursorIndexOfValuePrimitive: Int = getColumnIndex(cursor, "valuePrimitive")
-    val _cursorIndexOfValueBoolean: Int = getColumnIndex(cursor, "valueBoolean")
-    val _cursorIndexOfValueString: Int = getColumnIndex(cursor, "valueString")
-    val _cursorIndexOfValueNullableString: Int = getColumnIndex(cursor, "valueNullableString")
-    val _cursorIndexOfVariablePrimitive: Int = getColumnIndex(cursor, "variablePrimitive")
-    val _cursorIndexOfVariableNullableBoolean: Int = getColumnIndex(cursor,
+    val _cursorIndexOfValuePrimitive: Int = getColumnIndex(statement, "valuePrimitive")
+    val _cursorIndexOfValueBoolean: Int = getColumnIndex(statement, "valueBoolean")
+    val _cursorIndexOfValueString: Int = getColumnIndex(statement, "valueString")
+    val _cursorIndexOfValueNullableString: Int = getColumnIndex(statement, "valueNullableString")
+    val _cursorIndexOfVariablePrimitive: Int = getColumnIndex(statement, "variablePrimitive")
+    val _cursorIndexOfVariableNullableBoolean: Int = getColumnIndex(statement,
         "variableNullableBoolean")
-    val _cursorIndexOfVariableString: Int = getColumnIndex(cursor, "variableString")
-    val _cursorIndexOfVariableNullableString: Int = getColumnIndex(cursor, "variableNullableString")
+    val _cursorIndexOfVariableString: Int = getColumnIndex(statement, "variableString")
+    val _cursorIndexOfVariableNullableString: Int = getColumnIndex(statement,
+        "variableNullableString")
     val _tmpValuePrimitive: Long
     if (_cursorIndexOfValuePrimitive == -1) {
       _tmpValuePrimitive = 0
     } else {
-      _tmpValuePrimitive = cursor.getLong(_cursorIndexOfValuePrimitive)
+      _tmpValuePrimitive = statement.getLong(_cursorIndexOfValuePrimitive)
     }
     val _tmpValueBoolean: Boolean
     if (_cursorIndexOfValueBoolean == -1) {
       _tmpValueBoolean = false
     } else {
       val _tmp: Int
-      _tmp = cursor.getInt(_cursorIndexOfValueBoolean)
+      _tmp = statement.getLong(_cursorIndexOfValueBoolean).toInt()
       _tmpValueBoolean = _tmp != 0
     }
     val _tmpValueString: String
     if (_cursorIndexOfValueString == -1) {
       error("Missing value for a NON-NULL column 'valueString', found NULL value instead.")
     } else {
-      _tmpValueString = cursor.getString(_cursorIndexOfValueString)
+      _tmpValueString = statement.getText(_cursorIndexOfValueString)
     }
     val _tmpValueNullableString: String?
     if (_cursorIndexOfValueNullableString == -1) {
       _tmpValueNullableString = null
     } else {
-      if (cursor.isNull(_cursorIndexOfValueNullableString)) {
+      if (statement.isNull(_cursorIndexOfValueNullableString)) {
         _tmpValueNullableString = null
       } else {
-        _tmpValueNullableString = cursor.getString(_cursorIndexOfValueNullableString)
+        _tmpValueNullableString = statement.getText(_cursorIndexOfValueNullableString)
       }
     }
     _entity = MyEntity(_tmpValuePrimitive,_tmpValueBoolean,_tmpValueString,_tmpValueNullableString)
     if (_cursorIndexOfVariablePrimitive != -1) {
-      _entity.variablePrimitive = cursor.getLong(_cursorIndexOfVariablePrimitive)
+      _entity.variablePrimitive = statement.getLong(_cursorIndexOfVariablePrimitive)
     }
     if (_cursorIndexOfVariableNullableBoolean != -1) {
       val _tmp_1: Int?
-      if (cursor.isNull(_cursorIndexOfVariableNullableBoolean)) {
+      if (statement.isNull(_cursorIndexOfVariableNullableBoolean)) {
         _tmp_1 = null
       } else {
-        _tmp_1 = cursor.getInt(_cursorIndexOfVariableNullableBoolean)
+        _tmp_1 = statement.getLong(_cursorIndexOfVariableNullableBoolean).toInt()
       }
       _entity.variableNullableBoolean = _tmp_1?.let { it != 0 }
     }
     if (_cursorIndexOfVariableString != -1) {
-      _entity.variableString = cursor.getString(_cursorIndexOfVariableString)
+      _entity.variableString = statement.getText(_cursorIndexOfVariableString)
     }
     if (_cursorIndexOfVariableNullableString != -1) {
-      if (cursor.isNull(_cursorIndexOfVariableNullableString)) {
+      if (statement.isNull(_cursorIndexOfVariableNullableString)) {
         _entity.variableNullableString = null
       } else {
-        _entity.variableNullableString = cursor.getString(_cursorIndexOfVariableNullableString)
+        _entity.variableNullableString = statement.getText(_cursorIndexOfVariableNullableString)
       }
     }
     return _entity
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/instantiateImpl_simple.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/instantiateImpl_simple.kt
deleted file mode 100644
index 9e5f5f9..0000000
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/instantiateImpl_simple.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-import kotlin.reflect.KClass
-
-internal fun KClass<MyDatabase>.instantiateImpl(): MyDatabase = MyDatabase_Impl()
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt
index 45e0eb1..22b0805 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt
@@ -1,9 +1,7 @@
-import android.database.Cursor
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteStatement
 import com.google.common.collect.ImmutableMap
 import javax.`annotation`.processing.Generated
 import kotlin.Int
@@ -26,37 +24,36 @@
 
   public override fun getSongsWithArtist(): ImmutableMap<Song, Artist> {
     val _sql: String = "SELECT * FROM Song JOIN Artist ON Song.artistKey = Artist.artistId"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
-      val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
-      val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_cursor, "artistId")
-      val _mapResult: MutableMap<Song, Artist> = LinkedHashMap<Song, Artist>()
-      while (_cursor.moveToNext()) {
-        val _key: Song
-        val _tmpSongId: String
-        _tmpSongId = _cursor.getString(_cursorIndexOfSongId)
-        val _tmpArtistKey: String
-        _tmpArtistKey = _cursor.getString(_cursorIndexOfArtistKey)
-        _key = Song(_tmpSongId,_tmpArtistKey)
-        if (_cursor.isNull(_cursorIndexOfArtistId)) {
-          error("The column(s) of the map value object of type 'Artist' are NULL but the map's value type argument expect it to be NON-NULL")
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+        val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+        val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_stmt, "artistId")
+        val _mapResult: MutableMap<Song, Artist> = LinkedHashMap<Song, Artist>()
+        while (_stmt.step()) {
+          val _key: Song
+          val _tmpSongId: String
+          _tmpSongId = _stmt.getText(_cursorIndexOfSongId)
+          val _tmpArtistKey: String
+          _tmpArtistKey = _stmt.getText(_cursorIndexOfArtistKey)
+          _key = Song(_tmpSongId,_tmpArtistKey)
+          if (_stmt.isNull(_cursorIndexOfArtistId)) {
+            error("The column(s) of the map value object of type 'Artist' are NULL but the map's value type argument expect it to be NON-NULL")
+          }
+          val _value: Artist
+          val _tmpArtistId: String
+          _tmpArtistId = _stmt.getText(_cursorIndexOfArtistId)
+          _value = Artist(_tmpArtistId)
+          if (!_mapResult.containsKey(_key)) {
+            _mapResult.put(_key, _value)
+          }
         }
-        val _value: Artist
-        val _tmpArtistId: String
-        _tmpArtistId = _cursor.getString(_cursorIndexOfArtistId)
-        _value = Artist(_tmpArtistId)
-        if (!_mapResult.containsKey(_key)) {
-          _mapResult.put(_key, _value)
-        }
+        val _result: ImmutableMap<Song, Artist> = ImmutableMap.copyOf(_mapResult)
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: ImmutableMap<Song, Artist> = ImmutableMap.copyOf(_mapResult)
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMultimap.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMultimap.kt
index 9dcef3d..6ba0789 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMultimap.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMultimap.kt
@@ -1,9 +1,7 @@
-import android.database.Cursor
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteStatement
 import com.google.common.collect.ImmutableListMultimap
 import com.google.common.collect.ImmutableSetMultimap
 import javax.`annotation`.processing.Generated
@@ -25,69 +23,68 @@
 
   public override fun getArtistWithSongs(): ImmutableSetMultimap<Artist, Song> {
     val _sql: String = "SELECT * FROM Artist JOIN Song ON Artist.artistId = Song.artistKey"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_cursor, "artistId")
-      val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
-      val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
-      val _mapBuilder: ImmutableSetMultimap.Builder<Artist, Song> = ImmutableSetMultimap.builder()
-      while (_cursor.moveToNext()) {
-        val _key: Artist
-        val _tmpArtistId: String
-        _tmpArtistId = _cursor.getString(_cursorIndexOfArtistId)
-        _key = Artist(_tmpArtistId)
-        if (_cursor.isNull(_cursorIndexOfSongId) && _cursor.isNull(_cursorIndexOfArtistKey)) {
-          continue
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_stmt, "artistId")
+        val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+        val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+        val _mapBuilder: ImmutableSetMultimap.Builder<Artist, Song> = ImmutableSetMultimap.builder()
+        while (_stmt.step()) {
+          val _key: Artist
+          val _tmpArtistId: String
+          _tmpArtistId = _stmt.getText(_cursorIndexOfArtistId)
+          _key = Artist(_tmpArtistId)
+          if (_stmt.isNull(_cursorIndexOfSongId) && _stmt.isNull(_cursorIndexOfArtistKey)) {
+            continue
+          }
+          val _value: Song
+          val _tmpSongId: String
+          _tmpSongId = _stmt.getText(_cursorIndexOfSongId)
+          val _tmpArtistKey: String
+          _tmpArtistKey = _stmt.getText(_cursorIndexOfArtistKey)
+          _value = Song(_tmpSongId,_tmpArtistKey)
+          _mapBuilder.put(_key, _value)
         }
-        val _value: Song
-        val _tmpSongId: String
-        _tmpSongId = _cursor.getString(_cursorIndexOfSongId)
-        val _tmpArtistKey: String
-        _tmpArtistKey = _cursor.getString(_cursorIndexOfArtistKey)
-        _value = Song(_tmpSongId,_tmpArtistKey)
-        _mapBuilder.put(_key, _value)
+        val _result: ImmutableSetMultimap<Artist, Song> = _mapBuilder.build()
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: ImmutableSetMultimap<Artist, Song> = _mapBuilder.build()
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
   public override fun getArtistWithSongIds(): ImmutableListMultimap<Artist, Song> {
     val _sql: String = "SELECT * FROM Artist JOIN Song ON Artist.artistId = Song.artistKey"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_cursor, "artistId")
-      val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_cursor, "songId")
-      val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_cursor, "artistKey")
-      val _mapBuilder: ImmutableListMultimap.Builder<Artist, Song> = ImmutableListMultimap.builder()
-      while (_cursor.moveToNext()) {
-        val _key: Artist
-        val _tmpArtistId: String
-        _tmpArtistId = _cursor.getString(_cursorIndexOfArtistId)
-        _key = Artist(_tmpArtistId)
-        if (_cursor.isNull(_cursorIndexOfSongId) && _cursor.isNull(_cursorIndexOfArtistKey)) {
-          continue
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfArtistId: Int = getColumnIndexOrThrow(_stmt, "artistId")
+        val _cursorIndexOfSongId: Int = getColumnIndexOrThrow(_stmt, "songId")
+        val _cursorIndexOfArtistKey: Int = getColumnIndexOrThrow(_stmt, "artistKey")
+        val _mapBuilder: ImmutableListMultimap.Builder<Artist, Song> =
+            ImmutableListMultimap.builder()
+        while (_stmt.step()) {
+          val _key: Artist
+          val _tmpArtistId: String
+          _tmpArtistId = _stmt.getText(_cursorIndexOfArtistId)
+          _key = Artist(_tmpArtistId)
+          if (_stmt.isNull(_cursorIndexOfSongId) && _stmt.isNull(_cursorIndexOfArtistKey)) {
+            continue
+          }
+          val _value: Song
+          val _tmpSongId: String
+          _tmpSongId = _stmt.getText(_cursorIndexOfSongId)
+          val _tmpArtistKey: String
+          _tmpArtistKey = _stmt.getText(_cursorIndexOfArtistKey)
+          _value = Song(_tmpSongId,_tmpArtistKey)
+          _mapBuilder.put(_key, _value)
         }
-        val _value: Song
-        val _tmpSongId: String
-        _tmpSongId = _cursor.getString(_cursorIndexOfSongId)
-        val _tmpArtistKey: String
-        _tmpArtistKey = _cursor.getString(_cursorIndexOfArtistKey)
-        _value = Song(_tmpSongId,_tmpArtistKey)
-        _mapBuilder.put(_key, _value)
+        val _result: ImmutableListMultimap<Artist, Song> = _mapBuilder.build()
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: ImmutableListMultimap<Artist, Song> = _mapBuilder.build()
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaOptional.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaOptional.kt
index 3bfc1ab..6007b9f 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaOptional.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaOptional.kt
@@ -1,9 +1,7 @@
-import android.database.Cursor
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteStatement
 import com.google.common.base.Optional
 import javax.`annotation`.processing.Generated
 import kotlin.Int
@@ -24,27 +22,26 @@
 
   public override fun queryOfOptional(): Optional<MyEntity> {
     val _sql: String = "SELECT * FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-      val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-      val _value: MyEntity?
-      if (_cursor.moveToFirst()) {
-        val _tmpPk: Int
-        _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-        val _tmpOther: String
-        _tmpOther = _cursor.getString(_cursorIndexOfOther)
-        _value = MyEntity(_tmpPk,_tmpOther)
-      } else {
-        _value = null
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _value: MyEntity?
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _value = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          _value = null
+        }
+        val _result: Optional<MyEntity> = Optional.fromNullable(_value)
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: Optional<MyEntity> = Optional.fromNullable(_value)
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_immutable_list.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_immutable_list.kt
index 1ac0810..95ac02f 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_immutable_list.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_immutable_list.kt
@@ -1,9 +1,7 @@
-import android.database.Cursor
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteStatement
 import com.google.common.collect.ImmutableList
 import javax.`annotation`.processing.Generated
 import kotlin.Int
@@ -24,27 +22,26 @@
 
   public override fun queryOfList(): ImmutableList<MyEntity> {
     val _sql: String = "SELECT * FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-      val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-      val _immutableListBuilder: ImmutableList.Builder<MyEntity> = ImmutableList.builder()
-      while (_cursor.moveToNext()) {
-        val _item: MyEntity
-        val _tmpPk: Int
-        _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-        val _tmpOther: String
-        _tmpOther = _cursor.getString(_cursorIndexOfOther)
-        _item = MyEntity(_tmpPk,_tmpOther)
-        _immutableListBuilder.add(_item)
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _immutableListBuilder: ImmutableList.Builder<MyEntity> = ImmutableList.builder()
+        while (_stmt.step()) {
+          val _item: MyEntity
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _item = MyEntity(_tmpPk,_tmpOther)
+          _immutableListBuilder.add(_item)
+        }
+        val _result: ImmutableList<MyEntity> = _immutableListBuilder.build()
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: ImmutableList<MyEntity> = _immutableListBuilder.build()
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt
index 1fca428..f20abcd 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt
@@ -1,11 +1,7 @@
-import android.database.Cursor
 import androidx.room.AmbiguousColumnResolver
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndex
 import androidx.room.util.performBlocking
-import androidx.room.util.query
 import androidx.room.util.wrapMappedColumns
 import androidx.sqlite.SQLiteStatement
 import javax.`annotation`.processing.Generated
@@ -123,86 +119,85 @@
 
   public override fun getUserCommentMapWithoutQueryVerification(): Map<User, List<Comment>> {
     val _sql: String = "SELECT * FROM User JOIN Comment ON User.id = Comment.userId"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndices: Array<IntArray> =
-          AmbiguousColumnResolver.resolve(_cursor.getColumnNames(), arrayOf(arrayOf("id", "name"),
-          arrayOf("id", "userId", "text")))
-      val _wrappedCursor: Cursor = wrapMappedColumns(_cursor, arrayOf("id", "name"),
-          intArrayOf(_cursorIndices[0][0], _cursorIndices[0][1]))
-      val _wrappedCursor_1: Cursor = wrapMappedColumns(_cursor, arrayOf("id", "userId", "text"),
-          intArrayOf(_cursorIndices[1][0], _cursorIndices[1][1], _cursorIndices[1][2]))
-      val _result: MutableMap<User, MutableList<Comment>> =
-          LinkedHashMap<User, MutableList<Comment>>()
-      while (_cursor.moveToNext()) {
-        val _key: User
-        _key = __entityCursorConverter_User(_wrappedCursor)
-        val _values: MutableList<Comment>
-        if (_result.containsKey(_key)) {
-          _values = _result.getValue(_key)
-        } else {
-          _values = mutableListOf()
-          _result.put(_key, _values)
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndices: Array<IntArray> =
+            AmbiguousColumnResolver.resolve(_stmt.getColumnNames(), arrayOf(arrayOf("id", "name"),
+            arrayOf("id", "userId", "text")))
+        val _wrappedStmt: SQLiteStatement = wrapMappedColumns(_stmt, arrayOf("id", "name"),
+            intArrayOf(_cursorIndices[0][0], _cursorIndices[0][1]))
+        val _wrappedStmt_1: SQLiteStatement = wrapMappedColumns(_stmt, arrayOf("id", "userId",
+            "text"), intArrayOf(_cursorIndices[1][0], _cursorIndices[1][1], _cursorIndices[1][2]))
+        val _result: MutableMap<User, MutableList<Comment>> =
+            LinkedHashMap<User, MutableList<Comment>>()
+        while (_stmt.step()) {
+          val _key: User
+          _key = __entityStatementConverter_User(_wrappedStmt)
+          val _values: MutableList<Comment>
+          if (_result.containsKey(_key)) {
+            _values = _result.getValue(_key)
+          } else {
+            _values = mutableListOf()
+            _result.put(_key, _values)
+          }
+          if (_stmt.isNull(_cursorIndices[1][0]) && _stmt.isNull(_cursorIndices[1][1]) &&
+              _stmt.isNull(_cursorIndices[1][2])) {
+            continue
+          }
+          val _value: Comment
+          _value = __entityStatementConverter_Comment(_wrappedStmt_1)
+          _values.add(_value)
         }
-        if (_cursor.isNull(_cursorIndices[1][0]) && _cursor.isNull(_cursorIndices[1][1]) &&
-            _cursor.isNull(_cursorIndices[1][2])) {
-          continue
-        }
-        val _value: Comment
-        _value = __entityCursorConverter_Comment(_wrappedCursor_1)
-        _values.add(_value)
+        _result
+      } finally {
+        _stmt.close()
       }
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
-  private fun __entityCursorConverter_User(cursor: Cursor): User {
+  private fun __entityStatementConverter_User(statement: SQLiteStatement): User {
     val _entity: User
-    val _cursorIndexOfId: Int = getColumnIndex(cursor, "id")
-    val _cursorIndexOfName: Int = getColumnIndex(cursor, "name")
+    val _cursorIndexOfId: Int = getColumnIndex(statement, "id")
+    val _cursorIndexOfName: Int = getColumnIndex(statement, "name")
     val _tmpId: Int
     if (_cursorIndexOfId == -1) {
       _tmpId = 0
     } else {
-      _tmpId = cursor.getInt(_cursorIndexOfId)
+      _tmpId = statement.getLong(_cursorIndexOfId).toInt()
     }
     val _tmpName: String
     if (_cursorIndexOfName == -1) {
       error("Missing value for a NON-NULL column 'name', found NULL value instead.")
     } else {
-      _tmpName = cursor.getString(_cursorIndexOfName)
+      _tmpName = statement.getText(_cursorIndexOfName)
     }
     _entity = User(_tmpId,_tmpName)
     return _entity
   }
 
-  private fun __entityCursorConverter_Comment(cursor: Cursor): Comment {
+  private fun __entityStatementConverter_Comment(statement: SQLiteStatement): Comment {
     val _entity: Comment
-    val _cursorIndexOfId: Int = getColumnIndex(cursor, "id")
-    val _cursorIndexOfUserId: Int = getColumnIndex(cursor, "userId")
-    val _cursorIndexOfText: Int = getColumnIndex(cursor, "text")
+    val _cursorIndexOfId: Int = getColumnIndex(statement, "id")
+    val _cursorIndexOfUserId: Int = getColumnIndex(statement, "userId")
+    val _cursorIndexOfText: Int = getColumnIndex(statement, "text")
     val _tmpId: Int
     if (_cursorIndexOfId == -1) {
       _tmpId = 0
     } else {
-      _tmpId = cursor.getInt(_cursorIndexOfId)
+      _tmpId = statement.getLong(_cursorIndexOfId).toInt()
     }
     val _tmpUserId: Int
     if (_cursorIndexOfUserId == -1) {
       _tmpUserId = 0
     } else {
-      _tmpUserId = cursor.getInt(_cursorIndexOfUserId)
+      _tmpUserId = statement.getLong(_cursorIndexOfUserId).toInt()
     }
     val _tmpText: String
     if (_cursorIndexOfText == -1) {
       error("Missing value for a NON-NULL column 'text', found NULL value instead.")
     } else {
-      _tmpText = cursor.getString(_cursorIndexOfText)
+      _tmpText = statement.getText(_cursorIndexOfText)
     }
     _entity = Comment(_tmpId,_tmpUserId,_tmpText)
     return _entity
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_optional.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_optional.kt
index 23dc3b7..a5df95e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_optional.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_optional.kt
@@ -1,9 +1,7 @@
-import android.database.Cursor
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteStatement
 import java.util.Optional
 import javax.`annotation`.processing.Generated
 import kotlin.Int
@@ -24,27 +22,26 @@
 
   public override fun queryOfOptional(): Optional<MyEntity> {
     val _sql: String = "SELECT * FROM MyEntity"
-    val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-    __db.assertNotSuspendingTransaction()
-    val _cursor: Cursor = query(__db, _statement, false, null)
-    try {
-      val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-      val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-      val _value: MyEntity?
-      if (_cursor.moveToFirst()) {
-        val _tmpPk: Int
-        _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-        val _tmpOther: String
-        _tmpOther = _cursor.getString(_cursorIndexOfOther)
-        _value = MyEntity(_tmpPk,_tmpOther)
-      } else {
-        _value = null
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _value: MyEntity?
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _value = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          _value = null
+        }
+        val _result: Optional<MyEntity> = Optional.ofNullable(_value)
+        _result
+      } finally {
+        _stmt.close()
       }
-      val _result: Optional<MyEntity> = Optional.ofNullable(_value)
-      return _result
-    } finally {
-      _cursor.close()
-      _statement.release()
     }
   }
 
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt
index c058034..0bb30fb 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt
@@ -1,16 +1,24 @@
 import android.database.Cursor
+import androidx.room.CoroutinesRoom
 import androidx.room.RoomDatabase
+import androidx.room.RoomRawQuery
+import androidx.room.coroutines.createFlow
 import androidx.room.util.getColumnIndex
+import androidx.room.util.performBlocking
 import androidx.room.util.query
+import androidx.sqlite.SQLiteStatement
 import androidx.sqlite.db.SupportSQLiteQuery
+import java.util.concurrent.Callable
 import javax.`annotation`.processing.Generated
 import kotlin.Double
 import kotlin.Float
 import kotlin.Int
 import kotlin.Long
+import kotlin.String
 import kotlin.Suppress
 import kotlin.collections.List
 import kotlin.reflect.KClass
+import kotlinx.coroutines.flow.Flow
 
 @Generated(value = ["androidx.room.RoomProcessor"])
 @Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION", "REMOVAL"])
@@ -22,7 +30,7 @@
     this.__db = __db
   }
 
-  public override fun getEntity(sql: SupportSQLiteQuery): MyEntity {
+  public override fun getEntitySupport(sql: SupportSQLiteQuery): MyEntity {
     __db.assertNotSuspendingTransaction()
     val _cursor: Cursor = query(__db, sql, false, null)
     try {
@@ -38,6 +46,97 @@
     }
   }
 
+  public override fun getNullableEntitySupport(sql: SupportSQLiteQuery): MyEntity? {
+    __db.assertNotSuspendingTransaction()
+    val _cursor: Cursor = query(__db, sql, false, null)
+    try {
+      val _result: MyEntity?
+      if (_cursor.moveToFirst()) {
+        _result = __entityCursorConverter_MyEntity(_cursor)
+      } else {
+        _result = null
+      }
+      return _result
+    } finally {
+      _cursor.close()
+    }
+  }
+
+  public override fun getEntitySupportFlow(sql: SupportSQLiteQuery): Flow<MyEntity> =
+      CoroutinesRoom.createFlow(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity> {
+    public override fun call(): MyEntity {
+      val _cursor: Cursor = query(__db, sql, false, null)
+      try {
+        val _result: MyEntity
+        if (_cursor.moveToFirst()) {
+          _result = __entityCursorConverter_MyEntity(_cursor)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        return _result
+      } finally {
+        _cursor.close()
+      }
+    }
+  })
+
+  public override fun getEntity(query: RoomRawQuery): MyEntity {
+    val _sql: String = query.sql
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        query.getBindingFunction().invoke(_stmt)
+        val _result: MyEntity
+        if (_stmt.step()) {
+          _result = __entityStatementConverter_MyEntity(_stmt)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        _result
+      } finally {
+        _stmt.close()
+      }
+    }
+  }
+
+  public override fun getNullableEntity(query: RoomRawQuery): MyEntity? {
+    val _sql: String = query.sql
+    return performBlocking(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        query.getBindingFunction().invoke(_stmt)
+        val _result: MyEntity?
+        if (_stmt.step()) {
+          _result = __entityStatementConverter_MyEntity(_stmt)
+        } else {
+          _result = null
+        }
+        _result
+      } finally {
+        _stmt.close()
+      }
+    }
+  }
+
+  public override fun getEntityFlow(query: RoomRawQuery): Flow<MyEntity> {
+    val _sql: String = query.sql
+    return createFlow(__db, false, arrayOf("MyEntity")) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        query.getBindingFunction().invoke(_stmt)
+        val _result: MyEntity
+        if (_stmt.step()) {
+          _result = __entityStatementConverter_MyEntity(_stmt)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        _result
+      } finally {
+        _stmt.close()
+      }
+    }
+  }
+
   private fun __entityCursorConverter_MyEntity(cursor: Cursor): MyEntity {
     val _entity: MyEntity
     val _cursorIndexOfPk: Int = getColumnIndex(cursor, "pk")
@@ -65,6 +164,33 @@
     return _entity
   }
 
+  private fun __entityStatementConverter_MyEntity(statement: SQLiteStatement): MyEntity {
+    val _entity: MyEntity
+    val _cursorIndexOfPk: Int = getColumnIndex(statement, "pk")
+    val _cursorIndexOfDoubleColumn: Int = getColumnIndex(statement, "doubleColumn")
+    val _cursorIndexOfFloatColumn: Int = getColumnIndex(statement, "floatColumn")
+    val _tmpPk: Long
+    if (_cursorIndexOfPk == -1) {
+      _tmpPk = 0
+    } else {
+      _tmpPk = statement.getLong(_cursorIndexOfPk)
+    }
+    val _tmpDoubleColumn: Double
+    if (_cursorIndexOfDoubleColumn == -1) {
+      _tmpDoubleColumn = 0.0
+    } else {
+      _tmpDoubleColumn = statement.getDouble(_cursorIndexOfDoubleColumn)
+    }
+    val _tmpFloatColumn: Float
+    if (_cursorIndexOfFloatColumn == -1) {
+      _tmpFloatColumn = 0f
+    } else {
+      _tmpFloatColumn = statement.getDouble(_cursorIndexOfFloatColumn).toFloat()
+    }
+    _entity = MyEntity(_tmpPk,_tmpDoubleColumn,_tmpFloatColumn)
+    return _entity
+  }
+
   public companion object {
     public fun getRequiredConverters(): List<KClass<*>> = emptyList()
   }
diff --git a/room/room-gradle-plugin/build.gradle b/room/room-gradle-plugin/build.gradle
index cd2c3c2..287152a 100644
--- a/room/room-gradle-plugin/build.gradle
+++ b/room/room-gradle-plugin/build.gradle
@@ -78,6 +78,7 @@
     test.dependsOn(
             ":annotation:annotation:publish",
             ":annotation:annotation-experimental:publish",
+            ":collection:collection:publish",
             ":room:room-common:publish",
             ":room:room-runtime:publish",
             ":room:room-migration:publish",
diff --git a/room/room-gradle-plugin/lint-baseline.xml b/room/room-gradle-plugin/lint-baseline.xml
index c581b23..54b9dc9 100644
--- a/room/room-gradle-plugin/lint-baseline.xml
+++ b/room/room-gradle-plugin/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="WithPluginClasspathUsage"
         message="Avoid usage of GradleRunner#withPluginClasspath, which is broken. Instead use something like https://github.com/autonomousapps/dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
-        errorLine1="        .withPluginClasspath()"
-        errorLine2="         ~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            .withPluginClasspath()"
+        errorLine2="             ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/test/java/androidx/room/gradle/GradleTestUtils.kt"/>
     </issue>
@@ -13,7 +13,7 @@
     <issue
         id="WithTypeWithoutConfigureEach"
         message="Avoid passing a closure to withType, use withType().configureEach instead"
-        errorLine1="    ) = project.tasks.withType(JavaCompile::class.java) { task ->"
+        errorLine1="        project.tasks.withType(JavaCompile::class.java) { task ->"
         errorLine2="                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/room/gradle/integration/AndroidPluginIntegration.kt"/>
@@ -22,8 +22,8 @@
     <issue
         id="WithTypeWithoutConfigureEach"
         message="Avoid passing a closure to withType, use withType().configureEach instead"
-        errorLine1="        project.tasks.withType(KaptTask::class.java) { task ->"
-        errorLine2="                      ~~~~~~~~">
+        errorLine1="            project.tasks.withType(KaptTask::class.java) { task ->"
+        errorLine2="                          ~~~~~~~~">
         <location
             file="src/main/java/androidx/room/gradle/integration/AndroidPluginIntegration.kt"/>
     </issue>
@@ -31,8 +31,8 @@
     <issue
         id="WithTypeWithoutConfigureEach"
         message="Avoid passing a closure to withType, use withType().configureEach instead"
-        errorLine1="        project.tasks.withType(KspTaskJvm::class.java) { task ->"
-        errorLine2="                      ~~~~~~~~">
+        errorLine1="            project.tasks.withType(KspTaskJvm::class.java) { task ->"
+        errorLine2="                          ~~~~~~~~">
         <location
             file="src/main/java/androidx/room/gradle/integration/AndroidPluginIntegration.kt"/>
     </issue>
@@ -40,8 +40,8 @@
     <issue
         id="WithTypeWithoutConfigureEach"
         message="Avoid passing a closure to withType, use withType().configureEach instead"
-        errorLine1="        project.tasks.withType(KspTask::class.java) { task ->"
-        errorLine2="                      ~~~~~~~~">
+        errorLine1="            project.tasks.withType(KspTask::class.java) { task ->"
+        errorLine2="                          ~~~~~~~~">
         <location
             file="src/main/java/androidx/room/gradle/integration/KotlinMultiplatformPluginIntegration.kt"/>
     </issue>
diff --git a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/commonMain/kotlin/room/testapp/CommonDatabase.kt b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/commonMain/kotlin/room/testapp/CommonDatabase.kt
index 2cc4e65..5d2d2b3 100644
--- a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/commonMain/kotlin/room/testapp/CommonDatabase.kt
+++ b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/commonMain/kotlin/room/testapp/CommonDatabase.kt
@@ -19,10 +19,13 @@
 import androidx.room.*
 
 @Database(entities = [CommonEntity::class], version = 1)
+@ConstructedBy(CommonDatabaseCtor::class)
 abstract class CommonDatabase : RoomDatabase() {
     abstract fun getCommonDao(): CommonDao
 }
 
+expect object CommonDatabaseCtor : RoomDatabaseConstructor<CommonDatabase>
+
 @Entity
 data class CommonEntity(
     @PrimaryKey val id: Long
diff --git a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt
index c31091d..3690e65 100644
--- a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt
+++ b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt
@@ -19,10 +19,13 @@
 import androidx.room.*
 
 @Database(entities = [JvmEntity::class], version = 1)
+@ConstructedBy(MyDatabaseCtor::class)
 abstract class MyDatabase : RoomDatabase() {
     abstract fun getMyDao(): MyDao
 }
 
+expect object MyDatabaseCtor : RoomDatabaseConstructor<MyDatabase>
+
 @Entity
 data class JvmEntity(
     @PrimaryKey val id: Long
diff --git a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/nativeMain/kotlin/room/testapp/MyDatabase.kt b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/nativeMain/kotlin/room/testapp/MyDatabase.kt
index ac77ca5..87dbcab 100644
--- a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/nativeMain/kotlin/room/testapp/MyDatabase.kt
+++ b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/nativeMain/kotlin/room/testapp/MyDatabase.kt
@@ -19,10 +19,13 @@
 import androidx.room.*
 
 @Database(entities = [NativeEntity::class], version = 1)
+@ConstructedBy(MyDatabaseCtor::class)
 abstract class MyDatabase : RoomDatabase() {
     abstract fun getMyDao(): MyDao
 }
 
+expect object MyDatabaseCtor : RoomDatabaseConstructor<MyDatabase>
+
 @Entity
 data class NativeEntity(
     @PrimaryKey val id: Long
diff --git a/room/room-guava/build.gradle b/room/room-guava/build.gradle
index fd295a5..f475529 100644
--- a/room/room-guava/build.gradle
+++ b/room/room-guava/build.gradle
@@ -34,7 +34,7 @@
         exclude group: "com.google.guava", module: "listenablefuture"
     }
     implementation("androidx.arch.core:core-runtime:2.2.0")
-    api("androidx.annotation:annotation:1.0.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.truth)
@@ -45,7 +45,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Room Guava"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/room/room-ktx/build.gradle b/room/room-ktx/build.gradle
index 9dafedb..4a51b80 100644
--- a/room/room-ktx/build.gradle
+++ b/room/room-ktx/build.gradle
@@ -51,7 +51,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2019"
     description = "Android Room Kotlin Extensions"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/room/room-migration/bcv/native/current.txt b/room/room-migration/bcv/native/current.txt
index b6e493a..166dd9f 100644
--- a/room/room-migration/bcv/native/current.txt
+++ b/room/room-migration/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/room/room-migration/build.gradle b/room/room-migration/build.gradle
index 148d7da..b10beda 100644
--- a/room/room-migration/build.gradle
+++ b/room/room-migration/build.gradle
@@ -32,8 +32,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     jvm() {
         withJava()
     }
@@ -89,4 +87,5 @@
     inceptionYear = "2017"
     description = "Android Room Migration"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/room/room-paging-guava/build.gradle b/room/room-paging-guava/build.gradle
index bcb43e8..68e5f03 100644
--- a/room/room-paging-guava/build.gradle
+++ b/room/room-paging-guava/build.gradle
@@ -54,7 +54,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Guava integration in Room Paging"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/room/room-paging-rxjava2/build.gradle b/room/room-paging-rxjava2/build.gradle
index c6cc16a..0189e99 100644
--- a/room/room-paging-rxjava2/build.gradle
+++ b/room/room-paging-rxjava2/build.gradle
@@ -53,7 +53,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "RxJava2 integration in Room Paging"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/room/room-paging-rxjava2/lint-baseline.xml b/room/room-paging-rxjava2/lint-baseline.xml
index 884f439..97d2ed9 100644
--- a/room/room-paging-rxjava2/lint-baseline.xml
+++ b/room/room-paging-rxjava2/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                .doOnSubscribe { Thread.sleep(300) } // subscribe but delay the load"
-        errorLine2="                                        ~~~~~">
+        errorLine1="                    .doOnSubscribe { Thread.sleep(300) } // subscribe but delay the load"
+        errorLine2="                                            ~~~~~">
         <location
             file="src/androidTest/kotlin/androidx/room/paging/rxjava2/LimitOffsetRxPagingSourceTest.kt"/>
     </issue>
diff --git a/room/room-paging-rxjava3/build.gradle b/room/room-paging-rxjava3/build.gradle
index 8e84ee8..0d692e4 100644
--- a/room/room-paging-rxjava3/build.gradle
+++ b/room/room-paging-rxjava3/build.gradle
@@ -53,7 +53,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "RxJava3 integration in Room Paging"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/room/room-paging-rxjava3/lint-baseline.xml b/room/room-paging-rxjava3/lint-baseline.xml
index b44321d..7aaf6d8 100644
--- a/room/room-paging-rxjava3/lint-baseline.xml
+++ b/room/room-paging-rxjava3/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="                .doOnSubscribe { Thread.sleep(300) } // subscribe but delay the load"
-        errorLine2="                                        ~~~~~">
+        errorLine1="                    .doOnSubscribe { Thread.sleep(300) } // subscribe but delay the load"
+        errorLine2="                                            ~~~~~">
         <location
             file="src/androidTest/kotlin/androidx/room/paging/rxjava3/LimitOffsetRxPagingSourceTest.kt"/>
     </issue>
diff --git a/room/room-paging/bcv/native/current.txt b/room/room-paging/bcv/native/current.txt
new file mode 100644
index 0000000..5d6f9ec9
--- /dev/null
+++ b/room/room-paging/bcv/native/current.txt
@@ -0,0 +1,8 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.room:room-paging>
diff --git a/room/room-paging/build.gradle b/room/room-paging/build.gradle
index defbfa9..36e6f75 100644
--- a/room/room-paging/build.gradle
+++ b/room/room-paging/build.gradle
@@ -36,7 +36,7 @@
         commonMain {
             dependencies {
                 api(libs.kotlinStdlib)
-                api("androidx.paging:paging-common:3.3.0")
+                api(project(":paging:paging-common"))
                 api(project(":room:room-runtime"))
             }
         }
@@ -81,6 +81,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Room Paging integration"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/room/room-runtime/api/current.txt b/room/room-runtime/api/current.txt
index c055b76..8f01a73 100644
--- a/room/room-runtime/api/current.txt
+++ b/room/room-runtime/api/current.txt
@@ -127,6 +127,7 @@
     method public androidx.room.RoomDatabase.Builder<T> setJournalMode(androidx.room.RoomDatabase.JournalMode journalMode);
     method @SuppressCompatibility @androidx.room.ExperimentalRoomApi public androidx.room.RoomDatabase.Builder<T> setMultiInstanceInvalidationServiceIntent(android.content.Intent invalidationServiceIntent);
     method public androidx.room.RoomDatabase.Builder<T> setQueryCallback(androidx.room.RoomDatabase.QueryCallback queryCallback, java.util.concurrent.Executor executor);
+    method public final androidx.room.RoomDatabase.Builder<T> setQueryCallback(kotlin.coroutines.CoroutineContext context, androidx.room.RoomDatabase.QueryCallback queryCallback);
     method public final androidx.room.RoomDatabase.Builder<T> setQueryCoroutineContext(kotlin.coroutines.CoroutineContext context);
     method public androidx.room.RoomDatabase.Builder<T> setQueryExecutor(java.util.concurrent.Executor executor);
     method public androidx.room.RoomDatabase.Builder<T> setTransactionExecutor(java.util.concurrent.Executor executor);
@@ -169,6 +170,10 @@
     method public void onQuery(String sqlQuery, java.util.List<? extends java.lang.Object?> bindArgs);
   }
 
+  public interface RoomDatabaseConstructor<T extends androidx.room.RoomDatabase> {
+    method public T initialize();
+  }
+
   public final class RoomDatabaseKt {
     method public static kotlinx.coroutines.flow.Flow<java.util.Set<java.lang.String>> invalidationTrackerFlow(androidx.room.RoomDatabase, String[] tables, optional boolean emitInitialState);
     method public static suspend <R> Object? useReaderConnection(androidx.room.RoomDatabase, kotlin.jvm.functions.Function2<? super androidx.room.Transactor,? super kotlin.coroutines.Continuation<? super R>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super R>);
@@ -179,6 +184,14 @@
   public interface RoomOpenDelegateMarker {
   }
 
+  public final class RoomRawQuery {
+    ctor public RoomRawQuery(String sql);
+    ctor public RoomRawQuery(String sql, optional kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteStatement,kotlin.Unit> onBindStatement);
+    method public kotlin.jvm.functions.Function1<androidx.sqlite.SQLiteStatement,kotlin.Unit> getBindingFunction();
+    method public String getSql();
+    property public final String sql;
+  }
+
   public interface TransactionScope<T> extends androidx.room.PooledConnection {
     method public suspend Object? rollback(T result, kotlin.coroutines.Continuation<? extends java.lang.Object?>);
     method public suspend <R> Object? withNestedTransaction(kotlin.jvm.functions.Function2<? super androidx.room.TransactionScope<R>,? super kotlin.coroutines.Continuation<? super R>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super R>);
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index a2f7235..5a6a8e5 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -245,6 +245,7 @@
     method public androidx.room.RoomDatabase.Builder<T> setJournalMode(androidx.room.RoomDatabase.JournalMode journalMode);
     method @SuppressCompatibility @androidx.room.ExperimentalRoomApi public androidx.room.RoomDatabase.Builder<T> setMultiInstanceInvalidationServiceIntent(android.content.Intent invalidationServiceIntent);
     method public androidx.room.RoomDatabase.Builder<T> setQueryCallback(androidx.room.RoomDatabase.QueryCallback queryCallback, java.util.concurrent.Executor executor);
+    method public final androidx.room.RoomDatabase.Builder<T> setQueryCallback(kotlin.coroutines.CoroutineContext context, androidx.room.RoomDatabase.QueryCallback queryCallback);
     method public final androidx.room.RoomDatabase.Builder<T> setQueryCoroutineContext(kotlin.coroutines.CoroutineContext context);
     method public androidx.room.RoomDatabase.Builder<T> setQueryExecutor(java.util.concurrent.Executor executor);
     method public androidx.room.RoomDatabase.Builder<T> setTransactionExecutor(java.util.concurrent.Executor executor);
@@ -287,6 +288,10 @@
     method public void onQuery(String sqlQuery, java.util.List<? extends java.lang.Object?> bindArgs);
   }
 
+  public interface RoomDatabaseConstructor<T extends androidx.room.RoomDatabase> {
+    method public T initialize();
+  }
+
   public final class RoomDatabaseKt {
     method public static kotlinx.coroutines.flow.Flow<java.util.Set<java.lang.String>> invalidationTrackerFlow(androidx.room.RoomDatabase, String[] tables, optional boolean emitInitialState);
     method public static suspend <R> Object? useReaderConnection(androidx.room.RoomDatabase, kotlin.jvm.functions.Function2<? super androidx.room.Transactor,? super kotlin.coroutines.Continuation<? super R>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super R>);
@@ -327,6 +332,14 @@
     field @Deprecated public final boolean isValid;
   }
 
+  public final class RoomRawQuery {
+    ctor public RoomRawQuery(String sql);
+    ctor public RoomRawQuery(String sql, optional kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteStatement,kotlin.Unit> onBindStatement);
+    method public kotlin.jvm.functions.Function1<androidx.sqlite.SQLiteStatement,kotlin.Unit> getBindingFunction();
+    method public String getSql();
+    property public final String sql;
+  }
+
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class RoomSQLiteQuery implements androidx.sqlite.db.SupportSQLiteProgram androidx.sqlite.db.SupportSQLiteQuery {
     method public static androidx.room.RoomSQLiteQuery acquire(String query, int argumentCount);
     method public void bindBlob(int index, byte[] value);
@@ -502,6 +515,7 @@
   public final class SQLiteStatementUtil {
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static int getColumnIndex(androidx.sqlite.SQLiteStatement stmt, String name);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static int getColumnIndexOrThrow(androidx.sqlite.SQLiteStatement stmt, String name);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.sqlite.SQLiteStatement wrapMappedColumns(androidx.sqlite.SQLiteStatement statement, String[] columnNames, int[] mapping);
   }
 
   @RestrictTo({androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public final class StringUtil {
diff --git a/room/room-runtime/bcv/native/current.txt b/room/room-runtime/bcv/native/current.txt
index 2d47dce..6b3f44b 100644
--- a/room/room-runtime/bcv/native/current.txt
+++ b/room/room-runtime/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
@@ -55,6 +55,10 @@
         constructor <init>(androidx.sqlite/SQLiteDriver) // androidx.room/BaseRoomConnectionManager.DriverWrapper.<init>|<init>(androidx.sqlite.SQLiteDriver){}[0]
         final fun open(kotlin/String): androidx.sqlite/SQLiteConnection // androidx.room/BaseRoomConnectionManager.DriverWrapper.open|open(kotlin.String){}[0]
     }
+    final object Companion { // androidx.room/BaseRoomConnectionManager.Companion|null[0]
+        final const val BUSY_TIMEOUT_MS // androidx.room/BaseRoomConnectionManager.Companion.BUSY_TIMEOUT_MS|<get-BUSY_TIMEOUT_MS>(){}[0]
+            final fun <get-BUSY_TIMEOUT_MS>(): kotlin/Int // androidx.room/BaseRoomConnectionManager.Companion.BUSY_TIMEOUT_MS.<get-BUSY_TIMEOUT_MS>|<get-BUSY_TIMEOUT_MS>(){}[0]
+    }
 }
 abstract class androidx.room/RoomDatabase { // androidx.room/RoomDatabase|null[0]
     abstract class Callback { // androidx.room/RoomDatabase.Callback|null[0]
@@ -125,6 +129,9 @@
     final val version // androidx.room/RoomOpenDelegate.version|{}version[0]
         final fun <get-version>(): kotlin/Int // androidx.room/RoomOpenDelegate.version.<get-version>|<get-version>(){}[0]
 }
+abstract interface <#A: androidx.room/RoomDatabase> androidx.room/RoomDatabaseConstructor { // androidx.room/RoomDatabaseConstructor|null[0]
+    abstract fun initialize(): #A // androidx.room/RoomDatabaseConstructor.initialize|initialize(){}[0]
+}
 abstract interface <#A: kotlin/Any?> androidx.room/TransactionScope : androidx.room/PooledConnection { // androidx.room/TransactionScope|null[0]
     abstract suspend fun <#A1: kotlin/Any?> withNestedTransaction(kotlin.coroutines/SuspendFunction1<androidx.room/TransactionScope<#A1>, #A1>): #A1 // androidx.room/TransactionScope.withNestedTransaction|withNestedTransaction(kotlin.coroutines.SuspendFunction1<androidx.room.TransactionScope<0:0>,0:0>){0§<kotlin.Any?>}[0]
     abstract suspend fun rollback(#A): kotlin/Nothing // androidx.room/TransactionScope.rollback|rollback(1:0){}[0]
@@ -312,8 +319,16 @@
     final suspend fun subscribe(androidx.room/InvalidationTracker.Observer) // androidx.room/InvalidationTracker.subscribe|subscribe(androidx.room.InvalidationTracker.Observer){}[0]
     final suspend fun unsubscribe(androidx.room/InvalidationTracker.Observer) // androidx.room/InvalidationTracker.unsubscribe|unsubscribe(androidx.room.InvalidationTracker.Observer){}[0]
 }
+final class androidx.room/RoomRawQuery { // androidx.room/RoomRawQuery|null[0]
+    constructor <init>(kotlin/String, kotlin/Function1<androidx.sqlite/SQLiteStatement, kotlin/Unit> =...) // androidx.room/RoomRawQuery.<init>|<init>(kotlin.String;kotlin.Function1<androidx.sqlite.SQLiteStatement,kotlin.Unit>){}[0]
+    final fun getBindingFunction(): kotlin/Function1<androidx.sqlite/SQLiteStatement, kotlin/Unit> // androidx.room/RoomRawQuery.getBindingFunction|getBindingFunction(){}[0]
+    final val sql // androidx.room/RoomRawQuery.sql|{}sql[0]
+        final fun <get-sql>(): kotlin/String // androidx.room/RoomRawQuery.sql.<get-sql>|<get-sql>(){}[0]
+}
+final fun <#A: androidx.room/RoomDatabase> androidx.room.util/findDatabaseConstructorAndInitDatabaseImpl(kotlin.reflect/KClass<*>): #A // androidx.room.util/findDatabaseConstructorAndInitDatabaseImpl|findDatabaseConstructorAndInitDatabaseImpl(kotlin.reflect.KClass<*>){0§<androidx.room.RoomDatabase>}[0]
 final fun <#A: kotlin/Any, #B: kotlin/Any?> androidx.room.util/recursiveFetchMap(kotlin.collections/MutableMap<#A, #B>, kotlin/Boolean, kotlin/Function1<kotlin.collections/MutableMap<#A, #B>, kotlin/Unit>) // androidx.room.util/recursiveFetchMap|recursiveFetchMap(kotlin.collections.MutableMap<0:0,0:1>;kotlin.Boolean;kotlin.Function1<kotlin.collections.MutableMap<0:0,0:1>,kotlin.Unit>){0§<kotlin.Any>;1§<kotlin.Any?>}[0]
 final fun <#A: kotlin/Any?> androidx.room.coroutines/createFlow(androidx.room/RoomDatabase, kotlin/Boolean, kotlin/Array<kotlin/String>, kotlin/Function1<androidx.sqlite/SQLiteConnection, #A>): kotlinx.coroutines.flow/Flow<#A> // androidx.room.coroutines/createFlow|createFlow(androidx.room.RoomDatabase;kotlin.Boolean;kotlin.Array<kotlin.String>;kotlin.Function1<androidx.sqlite.SQLiteConnection,0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.room.util/recursiveFetchLongSparseArray(androidx.collection/LongSparseArray<#A>, kotlin/Boolean, kotlin/Function1<androidx.collection/LongSparseArray<#A>, kotlin/Unit>) // androidx.room.util/recursiveFetchLongSparseArray|recursiveFetchLongSparseArray(androidx.collection.LongSparseArray<0:0>;kotlin.Boolean;kotlin.Function1<androidx.collection.LongSparseArray<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
 final fun androidx.room.util/appendPlaceholders(kotlin.text/StringBuilder, kotlin/Int) // androidx.room.util/appendPlaceholders|appendPlaceholders(kotlin.text.StringBuilder;kotlin.Int){}[0]
 final fun androidx.room.util/dropFtsSyncTriggers(androidx.sqlite/SQLiteConnection) // androidx.room.util/dropFtsSyncTriggers|dropFtsSyncTriggers(androidx.sqlite.SQLiteConnection){}[0]
 final fun androidx.room.util/foreignKeyCheck(androidx.sqlite/SQLiteConnection, kotlin/String) // androidx.room.util/foreignKeyCheck|foreignKeyCheck(androidx.sqlite.SQLiteConnection;kotlin.String){}[0]
@@ -325,11 +340,12 @@
 final fun androidx.room.util/newStringBuilder(): kotlin.text/StringBuilder // androidx.room.util/newStringBuilder|newStringBuilder(){}[0]
 final fun androidx.room.util/splitToIntList(kotlin/String?): kotlin.collections/List<kotlin/Int>? // androidx.room.util/splitToIntList|splitToIntList(kotlin.String?){}[0]
 final fun androidx.room.util/stringError(): kotlin/String // androidx.room.util/stringError|stringError(){}[0]
+final fun androidx.room.util/wrapMappedColumns(androidx.sqlite/SQLiteStatement, kotlin/Array<kotlin/String>, kotlin/IntArray): androidx.sqlite/SQLiteStatement // androidx.room.util/wrapMappedColumns|wrapMappedColumns(androidx.sqlite.SQLiteStatement;kotlin.Array<kotlin.String>;kotlin.IntArray){}[0]
 final object androidx.room/Room { // androidx.room/Room|null[0]
     final const val MASTER_TABLE_NAME // androidx.room/Room.MASTER_TABLE_NAME|{}MASTER_TABLE_NAME[0]
         final fun <get-MASTER_TABLE_NAME>(): kotlin/String // androidx.room/Room.MASTER_TABLE_NAME.<get-MASTER_TABLE_NAME>|<get-MASTER_TABLE_NAME>(){}[0]
-    final inline fun <#A1: reified androidx.room/RoomDatabase> databaseBuilder(kotlin/String, noinline kotlin/Function0<#A1>): androidx.room/RoomDatabase.Builder<#A1> // androidx.room/Room.databaseBuilder|databaseBuilder(kotlin.String;kotlin.Function0<0:0>){0§<androidx.room.RoomDatabase>}[0]
-    final inline fun <#A1: reified androidx.room/RoomDatabase> inMemoryDatabaseBuilder(noinline kotlin/Function0<#A1>): androidx.room/RoomDatabase.Builder<#A1> // androidx.room/Room.inMemoryDatabaseBuilder|inMemoryDatabaseBuilder(kotlin.Function0<0:0>){0§<androidx.room.RoomDatabase>}[0]
+    final inline fun <#A1: reified androidx.room/RoomDatabase> databaseBuilder(kotlin/String, noinline kotlin/Function0<#A1> =...): androidx.room/RoomDatabase.Builder<#A1> // androidx.room/Room.databaseBuilder|databaseBuilder(kotlin.String;kotlin.Function0<0:0>){0§<androidx.room.RoomDatabase>}[0]
+    final inline fun <#A1: reified androidx.room/RoomDatabase> inMemoryDatabaseBuilder(noinline kotlin/Function0<#A1> =...): androidx.room/RoomDatabase.Builder<#A1> // androidx.room/Room.inMemoryDatabaseBuilder|inMemoryDatabaseBuilder(kotlin.Function0<0:0>){0§<androidx.room.RoomDatabase>}[0]
 }
 final suspend fun (androidx.room/PooledConnection).androidx.room/execSQL(kotlin/String) // androidx.room/execSQL|[email protected](kotlin.String){}[0]
 final suspend fun <#A: kotlin/Any?> (androidx.room/RoomDatabase).androidx.room/useReaderConnection(kotlin.coroutines/SuspendFunction1<androidx.room/Transactor, #A>): #A // androidx.room/useReaderConnection|[email protected](kotlin.coroutines.SuspendFunction1<androidx.room.Transactor,0:0>){0§<kotlin.Any?>}[0]
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index 760faab..ef89648 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -64,11 +64,11 @@
         }
     }
     namespace "androidx.room"
+    // TODO(b/345531033)
+    experimentalProperties["android.lint.useK2Uast"] = false
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
     ios() {
         // Link to sqlite3 available in iOS
@@ -106,8 +106,8 @@
                 api(libs.kotlinStdlib)
                 api(project(":room:room-common"))
                 api(project(":sqlite:sqlite"))
-                api("androidx.collection:collection:1.4.0")
-                api("androidx.annotation:annotation:1.8.0")
+                api(project(":collection:collection"))
+                api(project(":annotation:annotation"))
                 api(libs.kotlinCoroutinesCore)
                 implementation(libs.atomicFu)
             }
@@ -224,4 +224,5 @@
     inceptionYear = "2017"
     description = "Android Room-Runtime"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/room/room-runtime/lint-baseline.xml b/room/room-runtime/lint-baseline.xml
index 814e6a9..a216f60 100644
--- a/room/room-runtime/lint-baseline.xml
+++ b/room/room-runtime/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="RestrictedApiAndroidX"
@@ -85,7 +85,7 @@
     <issue
         id="RestrictedApiAndroidX"
         message="ProcessLock can only be called from within the same library group (referenced groupId=`androidx.sqlite` from groupId=`androidx.room`)"
-        errorLine1="        val copyLock = ProcessLock("
+        errorLine1="        val copyLock = ProcessLock(name, context.filesDir, processLevelLock)"
         errorLine2="                       ~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelper.android.kt"/>
@@ -94,8 +94,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="ProcessLock can only be called from within the same library group (referenced groupId=`androidx.sqlite` from groupId=`androidx.room`)"
-        errorLine1="            name,"
-        errorLine2="            ~~~~">
+        errorLine1="        val copyLock = ProcessLock(name, context.filesDir, processLevelLock)"
+        errorLine2="                                   ~~~~">
         <location
             file="src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelper.android.kt"/>
     </issue>
@@ -103,8 +103,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="ProcessLock can only be called from within the same library group (referenced groupId=`androidx.sqlite` from groupId=`androidx.room`)"
-        errorLine1="            context.filesDir,"
-        errorLine2="            ~~~~~~~~~~~~~~~~">
+        errorLine1="        val copyLock = ProcessLock(name, context.filesDir, processLevelLock)"
+        errorLine2="                                         ~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelper.android.kt"/>
     </issue>
@@ -130,8 +130,8 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="                    Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                val supportsDeferForeignKeys = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt"/>
     </issue>
diff --git a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/MultiInstanceInvalidationTest.kt b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/MultiInstanceInvalidationTest.kt
index a5d8e0f..e837d59 100644
--- a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/MultiInstanceInvalidationTest.kt
+++ b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/MultiInstanceInvalidationTest.kt
@@ -60,13 +60,13 @@
 
         // Remember the current auto close callback, it should be the one installed by the
         // invalidation tracker
-        val trackerCallback = autoCloseHelper.autoCloser.onAutoCloseCallback!!
+        val trackerCallback = autoCloseHelper.autoCloser.autoCloseCallbackForTest!!
 
         // Set a new callback, intercepting when DB is auto-closed
         val latch = CountDownLatch(1)
         autoCloseHelper.autoCloser.setAutoCloseCallback {
             // Run the remember auto close callback
-            trackerCallback.run()
+            trackerCallback.invoke()
             // At this point in time InvalidationTracker's callback has run and unbind should have
             // been invoked.
             latch.countDown()
diff --git a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoCloserTest.kt b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoCloserTest.kt
index cbe11d7..bd36947 100644
--- a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoCloserTest.kt
+++ b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoCloserTest.kt
@@ -16,36 +16,37 @@
 
 package androidx.room.support
 
-import android.annotation.SuppressLint
-import androidx.arch.core.executor.ArchTaskExecutor
-import androidx.arch.core.executor.testing.CountingTaskExecutorRule
 import androidx.kruth.assertThat
+import androidx.kruth.assertWithMessage
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
 import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.MediumTest
 import androidx.testutils.assertThrows
 import java.io.IOException
 import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.TestScope
 import org.junit.After
 import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @MediumTest
-public class AutoCloserTest {
+class AutoCloserTest {
 
-    @get:Rule
-    public val countingTaskExecutorRule: CountingTaskExecutorRule = CountingTaskExecutorRule()
+    companion object {
+        private const val DB_NAME = "test.db"
+        private const val TIMEOUT_AMOUNT = 1L
+    }
+
+    private val testCoroutineScope = TestScope()
 
     private lateinit var autoCloser: AutoCloser
-
+    private lateinit var testWatch: AutoCloserTestWatch
     private lateinit var callback: Callback
 
     private class Callback(var throwOnOpen: Boolean = false) : SupportSQLiteOpenHelper.Callback(1) {
@@ -61,7 +62,8 @@
     }
 
     @Before
-    public fun setUp() {
+    fun setUp() {
+        testWatch = AutoCloserTestWatch(TIMEOUT_AMOUNT, testCoroutineScope.testScheduler)
         callback = Callback()
 
         val delegateOpenHelper =
@@ -71,28 +73,28 @@
                             ApplicationProvider.getApplicationContext()
                         )
                         .callback(callback)
-                        .name("name")
+                        .name(DB_NAME)
                         .build()
                 )
 
-        val autoCloseExecutor = ArchTaskExecutor.getIOThreadExecutor()
-
         autoCloser =
-            AutoCloser(1, TimeUnit.MILLISECONDS, autoCloseExecutor).also {
-                it.init(delegateOpenHelper)
-                it.setAutoCloseCallback {}
+            AutoCloser(TIMEOUT_AMOUNT, TimeUnit.MILLISECONDS, testWatch).apply {
+                initOpenHelper(delegateOpenHelper)
+                initCoroutineScope(testCoroutineScope)
+                setAutoCloseCallback {}
             }
     }
 
     @After
-    public fun cleanUp() {
-        assertThat(countingTaskExecutorRule.isIdle).isTrue()
+    fun cleanUp() {
+        testWatch.step()
+        // At the end of all tests we always expect to auto-close the database
+        assertWithMessage("Database was not closed").that(autoCloser.delegateDatabase).isNull()
+        testCoroutineScope.cancel()
     }
 
-    @SuppressLint("BanThreadSleep")
-    @Ignore("b/283959848")
     @Test
-    public fun refCountsCounted() {
+    fun refCountsCounted() {
         autoCloser.incrementCountAndEnsureDbIsOpen()
         assertThat(autoCloser.refCountForTest).isEqualTo(1)
 
@@ -109,51 +111,28 @@
 
         autoCloser.decrementCountAndScheduleClose()
         assertThat(autoCloser.refCountForTest).isEqualTo(0)
-
-        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
-        // scheduled tasks are done.
-        Thread.sleep(5)
-        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
     }
 
-    @SuppressLint("BanThreadSleep")
-    @Ignore // b/271325600
     @Test
-    public fun executeRefCountingFunctionPropagatesFailure() {
-        assertThrows<IOException> {
-            autoCloser.executeRefCountingFunction<Nothing> { throw IOException() }
-        }
+    fun executeRefCountingFunctionPropagatesFailure() {
+        assertThrows<IOException> { autoCloser.executeRefCountingFunction { throw IOException() } }
 
         assertThat(autoCloser.refCountForTest).isEqualTo(0)
-
-        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
-        // scheduled tasks are done.
-        Thread.sleep(5)
-        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
     }
 
-    @SuppressLint("BanThreadSleep")
     @Test
-    @FlakyTest(bugId = 182343970)
-    public fun dbNotClosedWithRefCountIncremented() {
+    fun dbNotClosedWithRefCountIncremented() {
         autoCloser.incrementCountAndEnsureDbIsOpen()
 
-        Thread.sleep(10)
+        testWatch.step()
 
         assertThat(autoCloser.delegateDatabase!!.isOpen).isTrue()
 
         autoCloser.decrementCountAndScheduleClose()
-
-        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
-        // scheduled tasks are done.
-        Thread.sleep(10)
-        assertThat(autoCloser.delegateDatabase).isNull()
     }
 
-    @SuppressLint("BanThreadSleep")
-    @FlakyTest(bugId = 189775887)
     @Test
-    public fun getDelegatedDatabaseReturnsUnwrappedDatabase() {
+    fun getDelegatedDatabaseReturnsUnwrappedDatabase() {
         assertThat(autoCloser.delegateDatabase).isNull()
 
         val db = autoCloser.incrementCountAndEnsureDbIsOpen()
@@ -170,16 +149,10 @@
         autoCloser.executeRefCountingFunction {
             assertThat(autoCloser.refCountForTest).isEqualTo(1)
         }
-
-        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
-        // scheduled tasks are done.
-        Thread.sleep(5)
-        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
     }
 
-    @SuppressLint("BanThreadSleep")
     @Test
-    public fun refCountStaysIncrementedWhenErrorIsEncountered() {
+    fun refCountStaysIncrementedWhenErrorIsEncountered() {
         callback.throwOnOpen = true
         assertThrows<IOException> { autoCloser.incrementCountAndEnsureDbIsOpen() }
 
@@ -187,15 +160,10 @@
 
         autoCloser.decrementCountAndScheduleClose()
         callback.throwOnOpen = false
-
-        // TODO(rohitsat): remove these sleeps and add a hook in AutoCloser to confirm that the
-        // scheduled tasks are done.
-        Thread.sleep(5)
-        countingTaskExecutorRule.drainTasks(10, TimeUnit.MILLISECONDS)
     }
 
     @Test
-    public fun testDbCanBeManuallyClosed() {
+    fun testDbCanBeManuallyClosed() {
         val db = autoCloser.incrementCountAndEnsureDbIsOpen()
 
         assertThat(db.isOpen).isTrue()
diff --git a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoCloserTestWatch.kt b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoCloserTestWatch.kt
new file mode 100644
index 0000000..c245c83
--- /dev/null
+++ b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoCloserTestWatch.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.support
+
+import kotlinx.coroutines.test.TestCoroutineScheduler
+
+internal class AutoCloserTestWatch(
+    private val autoCloseTimeout: Long,
+    private val testDispatcher: TestCoroutineScheduler
+) : AutoCloser.Watch {
+
+    private var currentMillis: Long = 0
+
+    override fun getMillis(): Long {
+        return currentMillis
+    }
+
+    /**
+     * Advances the internal time by [autoCloseTimeout] amount and executes pending tasks in the
+     * [testDispatcher].
+     */
+    fun step() {
+        currentMillis += autoCloseTimeout
+        testDispatcher.advanceUntilIdle()
+    }
+}
diff --git a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactoryTest.kt b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactoryTest.kt
index 6eeb4d3..7bb7cd1 100644
--- a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactoryTest.kt
+++ b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactoryTest.kt
@@ -18,46 +18,61 @@
 
 import android.annotation.SuppressLint
 import android.content.Context
-import android.os.Build
-import androidx.annotation.RequiresApi
+import androidx.kruth.assertWithMessage
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
 import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
 import androidx.test.core.app.ApplicationProvider
-import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.atomic.AtomicInteger
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.TestScope
+import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 
-public class AutoClosingRoomOpenHelperFactoryTest {
-    private val DB_NAME = "name"
+class AutoClosingRoomOpenHelperFactoryTest {
+
+    companion object {
+        private const val DB_NAME = "test.db"
+        private const val TIMEOUT_AMOUNT = 10L
+    }
+
+    private val testCoroutineScope = TestScope()
+
+    private lateinit var autoCloser: AutoCloser
+    private lateinit var testWatch: AutoCloserTestWatch
+    private lateinit var autoClosingRoomOpenHelperFactory: AutoClosingRoomOpenHelperFactory
 
     @Before
-    public fun setUp() {
+    fun setUp() {
         ApplicationProvider.getApplicationContext<Context>().deleteDatabase(DB_NAME)
+
+        testWatch = AutoCloserTestWatch(TIMEOUT_AMOUNT, testCoroutineScope.testScheduler)
+        autoCloser =
+            AutoCloser(TIMEOUT_AMOUNT, TimeUnit.MILLISECONDS, testWatch).apply {
+                initCoroutineScope(testCoroutineScope)
+                setAutoCloseCallback {}
+            }
+        autoClosingRoomOpenHelperFactory =
+            AutoClosingRoomOpenHelperFactory(
+                delegate = FrameworkSQLiteOpenHelperFactory(),
+                autoCloser = autoCloser
+            )
     }
 
-    private fun getAutoClosingRoomOpenHelperFactory(
-        timeoutMillis: Long = 10
-    ): AutoClosingRoomOpenHelperFactory {
-        val delegateOpenHelperFactory = FrameworkSQLiteOpenHelperFactory()
-
-        return AutoClosingRoomOpenHelperFactory(
-            delegateOpenHelperFactory,
-            AutoCloser(timeoutMillis, TimeUnit.MILLISECONDS, Executors.newSingleThreadExecutor())
-                .also { it.onAutoCloseCallback = Runnable {} }
-        )
+    @After
+    fun cleanUp() {
+        testWatch.step()
+        // At the end of all tests we always expect to auto-close the database
+        assertWithMessage("Database was not closed").that(autoCloser.delegateDatabase).isNull()
+        testCoroutineScope.cancel()
     }
 
-    @SuppressLint("BanThreadSleep")
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
-    public fun testCallbacksCalled() {
-        val autoClosingRoomOpenHelperFactory = getAutoClosingRoomOpenHelperFactory()
-
+    fun testCallbacksCalled() {
         val callbackCount = AtomicInteger()
 
         val countingCallback =
@@ -96,7 +111,7 @@
         // onConfigure + onCreate + onOpen
         assertEquals(3, callbackCount.get())
 
-        Thread.sleep(100)
+        testWatch.step()
 
         autoClosingRoomOpenHelper.writableDatabase
 
@@ -105,28 +120,25 @@
         assertEquals(5, callbackCount.get())
     }
 
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
-    public fun testDatabaseIsOpenForSlowCallbacks() {
-        val autoClosingRoomOpenHelperFactory = getAutoClosingRoomOpenHelperFactory()
-
+    fun testDatabaseIsOpenForSlowCallbacks() {
         val refCountCheckingCallback =
             object : SupportSQLiteOpenHelper.Callback(1) {
                 @SuppressLint("BanThreadSleep")
                 override fun onCreate(db: SupportSQLiteDatabase) {
-                    Thread.sleep(100)
+                    testWatch.step()
                     db.execSQL("create table user (idk int)")
                 }
 
                 @SuppressLint("BanThreadSleep")
                 override fun onConfigure(db: SupportSQLiteDatabase) {
-                    Thread.sleep(100)
+                    testWatch.step()
                     db.setMaximumSize(100000)
                 }
 
                 @SuppressLint("BanThreadSleep")
                 override fun onOpen(db: SupportSQLiteDatabase) {
-                    Thread.sleep(100)
+                    testWatch.step()
                     db.execSQL("select * from user")
                 }
 
diff --git a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperTest.kt b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperTest.kt
index bd1478e..4781635 100644
--- a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperTest.kt
+++ b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/support/AutoClosingRoomOpenHelperTest.kt
@@ -16,28 +16,38 @@
 
 package androidx.room.support
 
-import android.annotation.SuppressLint
 import android.content.Context
-import android.database.Cursor
 import android.database.sqlite.SQLiteException
-import android.os.Build
-import androidx.annotation.RequiresApi
 import androidx.kruth.assertThat
+import androidx.kruth.assertWithMessage
+import androidx.room.util.useCursor
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
 import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
 import androidx.test.core.app.ApplicationProvider
-import androidx.test.filters.FlakyTest
 import androidx.testutils.assertThrows
 import java.io.IOException
-import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.TestScope
+import org.junit.After
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 
 class AutoClosingRoomOpenHelperTest {
 
+    companion object {
+        private const val DB_NAME = "test.db"
+        private const val TIMEOUT_AMOUNT = 10L
+    }
+
+    private val testCoroutineScope = TestScope()
+
+    private lateinit var autoCloser: AutoCloser
+    private lateinit var testWatch: AutoCloserTestWatch
+    private lateinit var callback: Callback
+    private lateinit var autoClosingRoomOpenHelper: AutoClosingRoomOpenHelper
+
     private open class Callback(var throwOnOpen: Boolean = false) :
         SupportSQLiteOpenHelper.Callback(1) {
         override fun onCreate(db: SupportSQLiteDatabase) {}
@@ -53,14 +63,10 @@
 
     @Before
     fun setUp() {
-        ApplicationProvider.getApplicationContext<Context>().deleteDatabase("name")
-    }
+        ApplicationProvider.getApplicationContext<Context>().deleteDatabase(DB_NAME)
 
-    private fun getAutoClosingRoomOpenHelper(
-        timeoutMillis: Long = 10,
-        callback: SupportSQLiteOpenHelper.Callback = Callback()
-    ): AutoClosingRoomOpenHelper {
-
+        testWatch = AutoCloserTestWatch(TIMEOUT_AMOUNT, testCoroutineScope.testScheduler)
+        callback = Callback()
         val delegateOpenHelper =
             FrameworkSQLiteOpenHelperFactory()
                 .create(
@@ -68,26 +74,29 @@
                             ApplicationProvider.getApplicationContext()
                         )
                         .callback(callback)
-                        .name("name")
+                        .name(DB_NAME)
                         .build()
                 )
-
-        val autoCloseExecutor = Executors.newSingleThreadExecutor()
-
-        return AutoClosingRoomOpenHelper(
-            delegateOpenHelper,
-            AutoCloser(timeoutMillis, TimeUnit.MILLISECONDS, autoCloseExecutor).apply {
-                init(delegateOpenHelper)
+        autoCloser =
+            AutoCloser(TIMEOUT_AMOUNT, TimeUnit.MILLISECONDS, testWatch).apply {
+                initOpenHelper(delegateOpenHelper)
+                initCoroutineScope(testCoroutineScope)
                 setAutoCloseCallback {}
             }
-        )
+        autoClosingRoomOpenHelper =
+            AutoClosingRoomOpenHelper(delegate = delegateOpenHelper, autoCloser = autoCloser)
     }
 
-    @RequiresApi(Build.VERSION_CODES.N)
+    @After
+    fun cleanUp() {
+        testWatch.step()
+        // At the end of all tests we always expect to auto-close the database
+        assertWithMessage("Database was not closed").that(autoCloser.delegateDatabase).isNull()
+        testCoroutineScope.cancel()
+    }
+
     @Test
     fun testQueryFailureDecrementsRefCount() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
-
         assertThrows<SQLiteException> {
             autoClosingRoomOpenHelper.writableDatabase.query("select * from nonexistanttable")
         }
@@ -95,10 +104,8 @@
         assertThat(autoClosingRoomOpenHelper.autoCloser.refCountForTest).isEqualTo(0)
     }
 
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
     fun testCursorKeepsDbAlive() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
         autoClosingRoomOpenHelper.writableDatabase.execSQL("create table user (idk int)")
 
         val cursor = autoClosingRoomOpenHelper.writableDatabase.query("select * from user")
@@ -107,35 +114,26 @@
         assertThat(autoClosingRoomOpenHelper.autoCloser.refCountForTest).isEqualTo(0)
     }
 
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
     fun testTransactionKeepsDbAlive() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
         autoClosingRoomOpenHelper.writableDatabase.beginTransaction()
         assertThat(autoClosingRoomOpenHelper.autoCloser.refCountForTest).isEqualTo(1)
         autoClosingRoomOpenHelper.writableDatabase.endTransaction()
         assertThat(autoClosingRoomOpenHelper.autoCloser.refCountForTest).isEqualTo(0)
     }
 
-    @SuppressLint("BanThreadSleep")
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
     fun enableWriteAheadLogging_onOpenHelper() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
-
         autoClosingRoomOpenHelper.setWriteAheadLoggingEnabled(true)
         assertThat(autoClosingRoomOpenHelper.writableDatabase.isWriteAheadLoggingEnabled).isTrue()
 
-        Thread.sleep(100) // Let the db auto close...
+        testWatch.step()
 
         assertThat(autoClosingRoomOpenHelper.writableDatabase.isWriteAheadLoggingEnabled).isTrue()
     }
 
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
     fun testEnableWriteAheadLogging_onSupportSqliteDatabase_throwsUnsupportedOperation() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
-
         assertThrows<UnsupportedOperationException> {
             autoClosingRoomOpenHelper.writableDatabase.enableWriteAheadLogging()
         }
@@ -145,67 +143,27 @@
         }
     }
 
-    @SuppressLint("BanThreadSleep")
-    @RequiresApi(Build.VERSION_CODES.N)
-    @FlakyTest(bugId = 190607416)
     @Test
-    fun testOnOpenCalledOnEachOpen() {
-        val countingCallback =
-            object : Callback() {
-                var onCreateCalls = 0
-                var onOpenCalls = 0
-
-                override fun onCreate(db: SupportSQLiteDatabase) {
-                    onCreateCalls++
-                }
-
-                override fun onOpen(db: SupportSQLiteDatabase) {
-                    super.onOpen(db)
-                    onOpenCalls++
-                }
-            }
-
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper(callback = countingCallback)
-
-        autoClosingRoomOpenHelper.writableDatabase
-        assertThat(countingCallback.onOpenCalls).isEqualTo(1)
-        assertThat(countingCallback.onCreateCalls).isEqualTo(1)
-
-        Thread.sleep(20) // Database should auto-close here
-        autoClosingRoomOpenHelper.writableDatabase
-        assertThat(countingCallback.onOpenCalls).isEqualTo(2)
-        assertThat(countingCallback.onCreateCalls).isEqualTo(1)
-    }
-
-    @SuppressLint("BanThreadSleep")
-    @Ignore // b/266993269
-    @RequiresApi(Build.VERSION_CODES.N)
-    @Test
-    fun testStatementReturnedByCompileStatement_doesntKeepDatabaseOpen() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
-
+    fun testStatementReturnedByCompileStatement_doesNotKeepDatabaseOpen() {
         val db = autoClosingRoomOpenHelper.writableDatabase
         db.execSQL("create table user (idk int)")
 
         db.compileStatement("insert into users (idk) values (1)")
 
-        Thread.sleep(20)
+        testWatch.step()
+
         assertThat(db.isOpen).isFalse() // db should close
         assertThat(autoClosingRoomOpenHelper.autoCloser.refCountForTest).isEqualTo(0)
     }
 
-    @SuppressLint("BanThreadSleep")
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
     fun testStatementReturnedByCompileStatement_reOpensDatabase() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
-
         val db = autoClosingRoomOpenHelper.writableDatabase
         db.execSQL("create table user (idk int)")
 
         val statement = db.compileStatement("insert into user (idk) values (1)")
 
-        Thread.sleep(20)
+        testWatch.step()
 
         statement.executeInsert() // This should succeed
 
@@ -214,10 +172,8 @@
         assertThat(autoClosingRoomOpenHelper.autoCloser.refCountForTest).isEqualTo(0)
     }
 
-    @RequiresApi(Build.VERSION_CODES.N)
     @Test
     fun testStatementReturnedByCompileStatement_worksWithBinds() {
-        val autoClosingRoomOpenHelper = getAutoClosingRoomOpenHelper()
         val db = autoClosingRoomOpenHelper.writableDatabase
 
         db.execSQL("create table users (i int, d double, b blob, n int, s string)")
@@ -265,27 +221,19 @@
                             ApplicationProvider.getApplicationContext()
                         )
                         .callback(Callback())
-                        .name("name")
+                        .name(DB_NAME)
                         .build()
                 )
 
-        val autoCloseExecutor = Executors.newSingleThreadExecutor()
-
         val autoClosing =
             AutoClosingRoomOpenHelper(
                 delegateOpenHelper,
-                AutoCloser(0, TimeUnit.MILLISECONDS, autoCloseExecutor)
+                AutoCloser(0, TimeUnit.MILLISECONDS).apply {
+                    initCoroutineScope(testCoroutineScope)
+                    setAutoCloseCallback {}
+                }
             )
 
         assertThat(autoClosing.delegate).isSameInstanceAs(delegateOpenHelper)
     }
-
-    // Older API versions didn't have Cursor implement Closeable
-    private inline fun Cursor.useCursor(block: (Cursor) -> Unit) {
-        try {
-            block(this)
-        } finally {
-            this.close()
-        }
-    }
 }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt
index 0cda433b..b7b8d25 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt
@@ -51,9 +51,9 @@
      * reference to it and re-use it.
      *
      * @param context The context for the database. This is usually the Application context.
-     * @param factory An optional lambda calling `initializeImpl()` on the database class which
-     *   returns the generated database implementation. If not provided then reflection is used to
-     *   find and instantiate the database implementation class.
+     * @param factory An optional lambda calling [RoomDatabaseConstructor.initialize] corresponding
+     *   to the database class of this builder. If not provided then reflection is used to find and
+     *   instantiate the database implementation class.
      * @param T The type of the database class.
      * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
      */
@@ -100,9 +100,9 @@
      *
      * @param context The context for the database. This is usually the Application context.
      * @param name The name of the database file.
-     * @param factory An optional lambda calling `initializeImpl()` on the database class which
-     *   returns the generated database implementation. If not provided then reflection is used to
-     *   find and instantiate the database implementation class.
+     * @param factory An optional lambda calling [RoomDatabaseConstructor.initialize] corresponding
+     *   to the database class of this builder. If not provided then reflection is used to find and
+     *   instantiate the database implementation class.
      * @param T The type of the database class.
      * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
      */
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
index 6d2d169..c8e2dfb 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
@@ -228,23 +228,6 @@
         validateAutoMigrations(configuration)
         validateTypeConverters(configuration)
 
-        // Configure SQLiteCopyOpenHelper if it is available
-        unwrapOpenHelper(
-                clazz = PrePackagedCopyOpenHelper::class.java,
-                openHelper = connectionManager.supportOpenHelper
-            )
-            ?.setDatabaseConfiguration(configuration)
-
-        // Configure AutoClosingRoomOpenHelper if it is available
-        unwrapOpenHelper(
-                clazz = AutoClosingRoomOpenHelper::class.java,
-                openHelper = connectionManager.supportOpenHelper
-            )
-            ?.let {
-                autoCloser = it.autoCloser
-                invalidationTracker.setAutoCloser(it.autoCloser)
-            }
-
         if (configuration.queryCoroutineContext != null) {
             // For backwards compatibility with internals not converted to Coroutines, use the
             // provided dispatcher as executor.
@@ -260,13 +243,11 @@
             transactionContext =
                 if (inCompatibilityMode()) {
                     // To prevent starvation due to primary connection blocking in
-                    // SupportSQLiteDatabase
-                    // a limited dispatcher is used for transactions.
+                    // SupportSQLiteDatabase a limited dispatcher is used for transactions.
                     coroutineScope.coroutineContext + dispatcher.limitedParallelism(1)
                 } else {
                     // When a SQLiteDriver is provided a suspending connection pool is used and
-                    // there
-                    // is no reason to limit parallelism.
+                    // there is no reason to limit parallelism.
                     coroutineScope.coroutineContext
                 }
         } else {
@@ -283,6 +264,17 @@
 
         allowMainThreadQueries = configuration.allowMainThreadQueries
 
+        // Configure PrePackagedCopyOpenHelper if it is available
+        unwrapOpenHelper<PrePackagedCopyOpenHelper>(connectionManager.supportOpenHelper)
+            ?.setDatabaseConfiguration(configuration)
+
+        // Configure AutoClosingRoomOpenHelper if it is available
+        unwrapOpenHelper<AutoClosingRoomOpenHelper>(connectionManager.supportOpenHelper)?.let {
+            autoCloser = it.autoCloser
+            it.autoCloser.initCoroutineScope(coroutineScope)
+            invalidationTracker.setAutoCloser(it.autoCloser)
+        }
+
         // Configure multi-instance invalidation, if enabled
         if (configuration.multiInstanceInvalidationServiceIntent != null) {
             requireNotNull(configuration.name)
@@ -350,21 +342,30 @@
     }
 
     /**
-     * Unwraps (delegating) open helpers until it finds clazz, otherwise returns null.
+     * Unwraps (delegating) open helpers until it finds [T], otherwise returns null.
      *
-     * @param clazz the open helper type to search for
      * @param openHelper the open helper to search through
-     * @param T the type of clazz
-     * @return the instance of clazz, otherwise null
+     * @param T the type of open helper type to search for
+     * @return the instance of [T], otherwise null
      */
-    @Suppress("UNCHECKED_CAST")
-    private fun <T> unwrapOpenHelper(clazz: Class<T>, openHelper: SupportSQLiteOpenHelper?): T? {
-        if (clazz.isInstance(openHelper)) {
-            return openHelper as T
+    private inline fun <reified T : SupportSQLiteOpenHelper> unwrapOpenHelper(
+        openHelper: SupportSQLiteOpenHelper?
+    ): T? {
+        if (openHelper == null) {
+            return null
         }
-        return if (openHelper is DelegatingOpenHelper) {
-            unwrapOpenHelper(clazz = clazz, openHelper = openHelper.delegate)
-        } else null
+        var current: SupportSQLiteOpenHelper = openHelper
+        while (true) {
+            if (current is T) {
+                return current
+            }
+            if (current is DelegatingOpenHelper) {
+                current = current.delegate
+            } else {
+                break
+            }
+        }
+        return null
     }
 
     /**
@@ -662,10 +663,7 @@
         if (autoCloser == null) {
             internalBeginTransaction()
         } else {
-            autoCloser.executeRefCountingFunction<Any?> {
-                internalBeginTransaction()
-                null
-            }
+            autoCloser.executeRefCountingFunction { internalBeginTransaction() }
         }
     }
 
@@ -689,10 +687,7 @@
         if (autoCloser == null) {
             internalEndTransaction()
         } else {
-            autoCloser.executeRefCountingFunction<Any?> {
-                internalEndTransaction()
-                null
-            }
+            autoCloser.executeRefCountingFunction { internalEndTransaction() }
         }
     }
 
@@ -873,6 +868,7 @@
         private var prepackagedDatabaseCallback: PrepackagedDatabaseCallback? = null
         private var queryCallback: QueryCallback? = null
         private var queryCallbackExecutor: Executor? = null
+        private var queryCallbackCoroutineContext: CoroutineContext? = null
         private val typeConverters: MutableList<Any> = mutableListOf()
         private var queryExecutor: Executor? = null
         private var transactionExecutor: Executor? = null
@@ -1448,6 +1444,9 @@
          * A use case for providing a callback is to allow logging executed queries. When the
          * callback implementation logs then it is recommended to use an immediate executor.
          *
+         * If a previous callback was set with [setQueryCallback] then this call will override it,
+         * including removing the Coroutine context previously set, if any.
+         *
          * @param queryCallback The query callback.
          * @param executor The executor on which the query callback will be invoked.
          * @return This builder instance.
@@ -1456,6 +1455,31 @@
         open fun setQueryCallback(queryCallback: QueryCallback, executor: Executor) = apply {
             this.queryCallback = queryCallback
             this.queryCallbackExecutor = executor
+            this.queryCallbackCoroutineContext = null
+        }
+
+        /**
+         * Sets a [QueryCallback] to be invoked when queries are executed.
+         *
+         * The callback is invoked whenever a query is executed, note that adding this callback has
+         * a small cost and should be avoided in production builds unless needed.
+         *
+         * A use case for providing a callback is to allow logging executed queries. When the
+         * callback implementation simply logs then it is recommended to use
+         * [kotlinx.coroutines.Dispatchers.Unconfined].
+         *
+         * If a previous callback was set with [setQueryCallback] then this call will override it,
+         * including removing the executor previously set, if any.
+         *
+         * @param context The coroutine context on which the query callback will be invoked.
+         * @param queryCallback The query callback.
+         * @return This builder instance.
+         */
+        @Suppress("MissingGetterMatchingBuilder")
+        fun setQueryCallback(context: CoroutineContext, queryCallback: QueryCallback) = apply {
+            this.queryCallback = queryCallback
+            this.queryCallbackExecutor = null
+            this.queryCallbackCoroutineContext = context
         }
 
         /**
@@ -1607,11 +1631,10 @@
                             }
                             val autoCloser =
                                 AutoCloser(
-                                    autoCloseTimeout,
-                                    requireNotNull(autoCloseTimeUnit),
-                                    requireNotNull(queryExecutor)
+                                    timeoutAmount = autoCloseTimeout,
+                                    timeUnit = requireNotNull(autoCloseTimeUnit)
                                 )
-                            AutoClosingRoomOpenHelperFactory(it, autoCloser)
+                            AutoClosingRoomOpenHelperFactory(delegate = it, autoCloser = autoCloser)
                         } else {
                             it
                         }
@@ -1642,10 +1665,10 @@
                                     "three configurations."
                             }
                             PrePackagedCopyOpenHelperFactory(
-                                copyFromAssetPath,
-                                copyFromFile,
-                                copyFromInputStream,
-                                it
+                                copyFromAssetPath = copyFromAssetPath,
+                                copyFromFile = copyFromFile,
+                                copyFromInputStream = copyFromInputStream,
+                                delegate = it
                             )
                         } else {
                             it
@@ -1653,10 +1676,13 @@
                     }
                     ?.let {
                         if (queryCallback != null) {
+                            val queryCallbackContext =
+                                queryCallbackExecutor?.asCoroutineDispatcher()
+                                    ?: requireNotNull(queryCallbackCoroutineContext)
                             QueryInterceptorOpenHelperFactory(
-                                it,
-                                requireNotNull(queryCallbackExecutor),
-                                requireNotNull(queryCallback)
+                                delegate = it,
+                                queryCallbackScope = CoroutineScope(queryCallbackContext),
+                                queryCallback = requireNotNull(queryCallback)
                             )
                         } else {
                             it
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoCloser.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoCloser.android.kt
index a3143e6..ac86ceb 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoCloser.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoCloser.android.kt
@@ -15,76 +15,76 @@
  */
 package androidx.room.support
 
-import android.os.Handler
-import android.os.Looper
 import android.os.SystemClock
 import androidx.annotation.GuardedBy
+import androidx.room.support.AutoCloser.Watch
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
-import java.io.IOException
-import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicLong
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 
 /**
- * AutoCloser is responsible for automatically opening (using delegateOpenHelper) and closing (on a
- * timer started when there are no remaining references) a SupportSqliteDatabase.
+ * AutoCloser is responsible for automatically opening (using `delegateOpenHelper`) and closing (on
+ * a timer started when there are no remaining references) a [SupportSQLiteDatabase].
  *
- * It is important to ensure that the ref count is incremented when using a returned database.
+ * It is important to ensure that the reference count is incremented when using a returned database.
  *
- * @param autoCloseTimeoutAmount time for auto close timer
- * @param autoCloseTimeUnit time unit for autoCloseTimeoutAmount
- * @param autoCloseExecutor the executor on which the auto close operation will happen
+ * @param timeoutAmount time for auto close timer
+ * @param timeUnit time unit for `timeoutAmount`
+ * @param watch A [Watch] implementation to get an increasing timestamp.
  */
 internal class AutoCloser(
-    autoCloseTimeoutAmount: Long,
-    autoCloseTimeUnit: TimeUnit,
-    autoCloseExecutor: Executor
+    timeoutAmount: Long,
+    timeUnit: TimeUnit,
+    private val watch: Watch = Watch { SystemClock.uptimeMillis() }
 ) {
-    lateinit var delegateOpenHelper: SupportSQLiteOpenHelper
-    private val handler = Handler(Looper.getMainLooper())
+    // The unwrapped SupportSQLiteOpenHelper (i.e. not AutoClosingRoomOpenHelper)
+    private lateinit var delegateOpenHelper: SupportSQLiteOpenHelper
 
-    internal var onAutoCloseCallback: Runnable? = null
+    private lateinit var coroutineScope: CoroutineScope
+
+    private var onAutoCloseCallback: (() -> Unit)? = null
 
     private val lock = Any()
 
-    private var autoCloseTimeoutInMs: Long = autoCloseTimeUnit.toMillis(autoCloseTimeoutAmount)
+    private val autoCloseTimeoutInMs = timeUnit.toMillis(timeoutAmount)
 
-    private val executor: Executor = autoCloseExecutor
+    private val referenceCount = AtomicInteger(0)
 
-    @GuardedBy("lock") internal var refCount = 0
+    private var lastDecrementRefCountTimeStamp = AtomicLong(watch.getMillis())
 
-    @GuardedBy("lock") internal var lastDecrementRefCountTimeStamp = SystemClock.uptimeMillis()
-
-    // The unwrapped SupportSqliteDatabase
+    // The unwrapped SupportSqliteDatabase (i.e. not AutoCloseSupportSQLiteDatabase)
     @GuardedBy("lock") internal var delegateDatabase: SupportSQLiteDatabase? = null
 
     private var manuallyClosed = false
 
-    private val executeAutoCloser = Runnable { executor.execute(autoCloser) }
+    private var autoCloseJob: Job? = null
 
-    private val autoCloser = Runnable {
+    private fun autoCloseDatabase() {
+        if (watch.getMillis() - lastDecrementRefCountTimeStamp.get() < autoCloseTimeoutInMs) {
+            // An increment + decrement beat us to closing the db. We
+            // will not close the database, and there should be at least
+            // one more auto-close scheduled.
+            return
+        }
+        if (referenceCount.get() != 0) {
+            // An increment beat us to closing the db. We don't close the
+            // db, and another closer will be scheduled once the ref
+            // count is decremented.
+            return
+        }
+        onAutoCloseCallback?.invoke()
+            ?: error(
+                "onAutoCloseCallback is null but it should  have been set before use. " +
+                    "Please file a bug against Room at: $BUG_LINK"
+            )
+
         synchronized(lock) {
-            if (
-                SystemClock.uptimeMillis() - lastDecrementRefCountTimeStamp < autoCloseTimeoutInMs
-            ) {
-                // An increment + decrement beat us to closing the db. We
-                // will not close the database, and there should be at least
-                // one more auto-close scheduled.
-                return@Runnable
-            }
-            if (refCount != 0) {
-                // An increment beat us to closing the db. We don't close the
-                // db, and another closer will be scheduled once the ref
-                // count is decremented.
-                return@Runnable
-            }
-            onAutoCloseCallback?.run()
-                ?: error(
-                    "onAutoCloseCallback is null but it should" +
-                        " have been set before use. Please file a bug " +
-                        "against Room at: $autoCloseBug"
-                )
-
             delegateDatabase?.let {
                 if (it.isOpen) {
                     it.close()
@@ -95,16 +95,27 @@
     }
 
     /**
-     * Since we need to construct the AutoCloser in the RoomDatabase.Builder, we need to set the
-     * delegateOpenHelper after construction.
+     * Since we need to construct the AutoCloser in the [androidx.room.RoomDatabase.Builder], we
+     * need to set the `delegateOpenHelper` after construction.
      *
-     * @param delegateOpenHelper the open helper that is used to create new SupportSqliteDatabases
+     * @param delegateOpenHelper the open helper that is used to create new [SupportSQLiteDatabase].
      */
-    fun init(delegateOpenHelper: SupportSQLiteOpenHelper) {
+    fun initOpenHelper(delegateOpenHelper: SupportSQLiteOpenHelper) {
+        require(delegateOpenHelper !is AutoClosingRoomOpenHelper)
         this.delegateOpenHelper = delegateOpenHelper
     }
 
     /**
+     * Since we need to construct the AutoCloser in the [androidx.room.RoomDatabase.Builder], we
+     * need to set the `coroutineScope` after construction.
+     *
+     * @param coroutineScope where the auto close will execute.
+     */
+    fun initCoroutineScope(coroutineScope: CoroutineScope) {
+        this.coroutineScope = coroutineScope
+    }
+
+    /**
      * Execute a ref counting function. The function will receive an unwrapped open database and
      * this database will stay open until at least after function returns. If there are no more
      * references in use for the db once function completes, an auto close operation will be
@@ -118,25 +129,25 @@
         }
 
     /**
-     * Confirms that autoCloser is no longer running and confirms that delegateDatabase is set and
-     * open. delegateDatabase will not be auto closed until decrementRefCountAndScheduleClose is
-     * called. decrementRefCountAndScheduleClose must be called once for each call to
-     * incrementCountAndEnsureDbIsOpen.
+     * Confirms that auto-close function is no longer running and confirms that `delegateDatabase`
+     * is set and open. `delegateDatabase` will not be auto closed until
+     * [decrementCountAndScheduleClose] is called. [decrementCountAndScheduleClose] must be called
+     * once for each call to [incrementCountAndEnsureDbIsOpen].
      *
-     * If this throws an exception, decrementCountAndScheduleClose must still be called!
+     * If this throws an exception, [decrementCountAndScheduleClose] must still be called!
      *
      * @return the *unwrapped* SupportSQLiteDatabase.
      */
     fun incrementCountAndEnsureDbIsOpen(): SupportSQLiteDatabase {
-        // TODO(rohitsat): avoid synchronized(lock) when possible. We should be able to avoid it
-        // when refCount is not hitting zero or if there is no auto close scheduled if we use
-        // Atomics.
-        synchronized(lock) {
+        val previousCount = referenceCount.getAndIncrement()
 
-            // If there is a scheduled autoclose operation, we should remove it from the handler.
-            handler.removeCallbacks(executeAutoCloser)
-            refCount++
-            check(!manuallyClosed) { "Attempting to open already closed database." }
+        // If there is a scheduled auto close operation, cancel it.
+        autoCloseJob?.cancel()
+        autoCloseJob = null
+
+        check(!manuallyClosed) { "Attempting to open already closed database." }
+
+        fun getDatabase(): SupportSQLiteDatabase {
             delegateDatabase?.let {
                 if (it.isOpen) {
                     return it
@@ -144,39 +155,41 @@
             }
             return delegateOpenHelper.writableDatabase.also { delegateDatabase = it }
         }
+
+        // Fast path: If the previous count was not zero, then there is no concurrent auto close
+        // operation that can race with getting the database, so we skip using the lock.
+        if (previousCount > 0) {
+            return getDatabase()
+        }
+        // Slow path: If the previous count was indeed zero, even though we cancel the auto close
+        // operation, it might be on-going already so to avoid a race we use the lock to get the
+        // database.
+        return synchronized(lock) { getDatabase() }
     }
 
     /**
      * Decrements the ref count and schedules a close if there are no other references to the db.
-     * This must only be called after a corresponding incrementCountAndEnsureDbIsOpen call.
+     * This must only be called after a corresponding [incrementCountAndEnsureDbIsOpen] call.
      */
     fun decrementCountAndScheduleClose() {
-        // TODO(rohitsat): avoid synchronized(lock) when possible
-        synchronized(lock) {
-            check(refCount > 0) { "ref count is 0 or lower but we're supposed to decrement" }
-            // decrement refCount
-            refCount--
-
-            // if refcount is zero, schedule close operation
-            if (refCount == 0) {
-                if (delegateDatabase == null) {
-                    // No db to close, this can happen due to exceptions when creating db...
-                    return
+        val newCount = referenceCount.decrementAndGet()
+        check(newCount >= 0) { "Unbalanced reference count." }
+        lastDecrementRefCountTimeStamp.set(watch.getMillis())
+        if (newCount == 0) {
+            autoCloseJob =
+                coroutineScope.launch {
+                    delay(autoCloseTimeoutInMs)
+                    autoCloseDatabase()
                 }
-                handler.postDelayed(executeAutoCloser, autoCloseTimeoutInMs)
-            }
         }
     }
 
-    /**
-     * Close the database if it is still active.
-     *
-     * @throws IOException if an exception is encountered when closing the underlying db.
-     */
-    @Throws(IOException::class)
+    /** Close the database if it is still active. */
     fun closeDatabaseIfOpen() {
         synchronized(lock) {
             manuallyClosed = true
+            autoCloseJob?.cancel()
+            autoCloseJob = null
             delegateDatabase?.close()
             delegateDatabase = null
         }
@@ -192,29 +205,30 @@
         get() = !manuallyClosed
 
     /**
-     * Returns the current ref count for this auto closer. This is only visible for testing.
-     *
-     * @return current ref count
-     */
-    internal val refCountForTest: Int
-        get() {
-            synchronized(lock) {
-                return refCount
-            }
-        }
-
-    /**
      * Sets a callback that will be run every time the database is auto-closed. This callback needs
      * to be lightweight since it is run while holding a lock.
      *
      * @param onAutoClose the callback to run
      */
-    fun setAutoCloseCallback(onAutoClose: Runnable) {
+    fun setAutoCloseCallback(onAutoClose: () -> Unit) {
         onAutoCloseCallback = onAutoClose
     }
 
+    /** Returns the current auto close callback. This is only visible for testing. */
+    internal val autoCloseCallbackForTest
+        get() = onAutoCloseCallback
+
+    /** Returns the current ref count for this auto closer. This is only visible for testing. */
+    internal val refCountForTest: Int
+        get() = referenceCount.get()
+
+    /** Represents a counting time tracker function. */
+    fun interface Watch {
+        fun getMillis(): Long
+    }
+
     companion object {
-        const val autoCloseBug =
-            "https://issuetracker.google.com/issues/new?component=" + "413107&template=1096568"
+        const val BUG_LINK =
+            "https://issuetracker.google.com/issues/new?component=413107&template=1096568"
     }
 }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelper.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelper.android.kt
index 2bb22cf..a5dc11d 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelper.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelper.android.kt
@@ -15,21 +15,18 @@
  */
 package androidx.room.support
 
-import android.content.ContentResolver
 import android.content.ContentValues
 import android.database.Cursor
 import android.database.SQLException
 import android.database.sqlite.SQLiteTransactionListener
-import android.net.Uri
 import android.os.Build
-import android.os.Bundle
 import android.os.CancellationSignal
 import android.util.Pair
 import androidx.annotation.RequiresApi
 import androidx.room.DelegatingOpenHelper
-import androidx.sqlite.db.SupportSQLiteCompat
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
+import androidx.sqlite.db.SupportSQLiteProgram
 import androidx.sqlite.db.SupportSQLiteQuery
 import androidx.sqlite.db.SupportSQLiteStatement
 import java.io.IOException
@@ -38,23 +35,21 @@
 /** A SupportSQLiteOpenHelper that has auto close enabled for database connections. */
 internal class AutoClosingRoomOpenHelper(
     override val delegate: SupportSQLiteOpenHelper,
-    @JvmField internal val autoCloser: AutoCloser
+    internal val autoCloser: AutoCloser
 ) : SupportSQLiteOpenHelper by delegate, DelegatingOpenHelper {
-    private val autoClosingDb: AutoClosingSupportSQLiteDatabase
+
+    private val autoClosingDb = AutoClosingSupportSQLiteDatabase(autoCloser)
 
     init {
-        autoCloser.init(delegate)
-        autoClosingDb = AutoClosingSupportSQLiteDatabase(autoCloser)
+        autoCloser.initOpenHelper(delegate)
     }
 
-    @get:RequiresApi(api = Build.VERSION_CODES.N)
     override val writableDatabase: SupportSQLiteDatabase
         get() {
             autoClosingDb.pokeOpen()
             return autoClosingDb
         }
 
-    @get:RequiresApi(api = Build.VERSION_CODES.N)
     override val readableDatabase: SupportSQLiteDatabase
         get() {
             // Note we don't differentiate between writable db and readable db
@@ -75,7 +70,7 @@
         }
 
         override fun compileStatement(sql: String): SupportSQLiteStatement {
-            return AutoClosingSupportSqliteStatement(sql, autoCloser)
+            return AutoClosingSupportSQLiteStatement(sql, autoCloser)
         }
 
         override fun beginTransaction() {
@@ -138,9 +133,6 @@
         }
 
         override fun endTransaction() {
-            checkNotNull(autoCloser.delegateDatabase) {
-                "End transaction called but delegateDb is null"
-            }
             try {
                 autoCloser.delegateDatabase!!.endTransaction()
             } finally {
@@ -149,8 +141,7 @@
         }
 
         override fun setTransactionSuccessful() {
-            autoCloser.delegateDatabase?.setTransactionSuccessful()
-                ?: error("setTransactionSuccessful called but delegateDb is null")
+            autoCloser.delegateDatabase!!.setTransactionSuccessful()
         }
 
         override fun inTransaction(): Boolean {
@@ -186,9 +177,8 @@
         override var version: Int
             get() = autoCloser.executeRefCountingFunction(SupportSQLiteDatabase::version)
             set(version) {
-                autoCloser.executeRefCountingFunction<Any?> { db: SupportSQLiteDatabase ->
+                autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
                     db.version = version
-                    null
                 }
             }
 
@@ -285,32 +275,24 @@
 
         @Throws(SQLException::class)
         override fun execSQL(sql: String) {
-            autoCloser.executeRefCountingFunction<Any?> { db: SupportSQLiteDatabase ->
-                db.execSQL(sql)
-                null
-            }
+            autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase -> db.execSQL(sql) }
         }
 
         @Throws(SQLException::class)
         override fun execSQL(sql: String, bindArgs: Array<out Any?>) {
-            autoCloser.executeRefCountingFunction<Any?> { db: SupportSQLiteDatabase ->
+            autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
                 db.execSQL(sql, bindArgs)
-                null
             }
         }
 
         override val isReadOnly: Boolean
-            get() =
-                autoCloser.executeRefCountingFunction { obj: SupportSQLiteDatabase ->
-                    obj.isReadOnly
-                }
+            get() = autoCloser.executeRefCountingFunction(SupportSQLiteDatabase::isReadOnly)
 
         override val isOpen: Boolean
             get() {
                 // Get the db without incrementing the reference cause we don't want to open
                 // the db for an isOpen call.
-                val localDelegate = autoCloser.delegateDatabase ?: return false
-                return localDelegate.isOpen
+                return autoCloser.delegateDatabase?.isOpen ?: return false
             }
 
         override fun needUpgrade(newVersion: Int): Boolean {
@@ -320,26 +302,23 @@
         }
 
         override val path: String?
-            get() = autoCloser.executeRefCountingFunction { obj: SupportSQLiteDatabase -> obj.path }
+            get() = autoCloser.executeRefCountingFunction(SupportSQLiteDatabase::path)
 
         override fun setLocale(locale: Locale) {
-            autoCloser.executeRefCountingFunction<Any?> { db: SupportSQLiteDatabase ->
+            autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
                 db.setLocale(locale)
-                null
             }
         }
 
         override fun setMaxSqlCacheSize(cacheSize: Int) {
-            autoCloser.executeRefCountingFunction<Any?> { db: SupportSQLiteDatabase ->
+            autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
                 db.setMaxSqlCacheSize(cacheSize)
-                null
             }
         }
 
         override fun setForeignKeyConstraintsEnabled(enabled: Boolean) {
-            autoCloser.executeRefCountingFunction<Any?> { db: SupportSQLiteDatabase ->
+            autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
                 db.setForeignKeyConstraintsEnabled(enabled)
-                null
             }
         }
 
@@ -359,21 +338,16 @@
 
         override val isWriteAheadLoggingEnabled: Boolean
             get() =
-                autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
-                    return@executeRefCountingFunction db.isWriteAheadLoggingEnabled
-                }
+                autoCloser.executeRefCountingFunction(
+                    SupportSQLiteDatabase::isWriteAheadLoggingEnabled
+                )
 
         override val attachedDbs: List<Pair<String, String>>?
-            get() =
-                autoCloser.executeRefCountingFunction { obj: SupportSQLiteDatabase ->
-                    obj.attachedDbs
-                }
+            get() = autoCloser.executeRefCountingFunction(SupportSQLiteDatabase::attachedDbs)
 
         override val isDatabaseIntegrityOk: Boolean
             get() =
-                autoCloser.executeRefCountingFunction { obj: SupportSQLiteDatabase ->
-                    obj.isDatabaseIntegrityOk
-                }
+                autoCloser.executeRefCountingFunction(SupportSQLiteDatabase::isDatabaseIntegrityOk)
 
         @Throws(IOException::class)
         override fun close() {
@@ -395,150 +369,141 @@
             delegate.close()
             autoCloser.decrementCountAndScheduleClose()
         }
-
-        @RequiresApi(api = Build.VERSION_CODES.Q)
-        override fun setNotificationUris(cr: ContentResolver, uris: List<Uri>) {
-            SupportSQLiteCompat.Api29Impl.setNotificationUris(delegate, cr, uris)
-        }
-
-        override fun getNotificationUri(): Uri {
-            return delegate.notificationUri
-        }
-
-        @RequiresApi(api = Build.VERSION_CODES.Q)
-        override fun getNotificationUris(): List<Uri> {
-            return SupportSQLiteCompat.Api29Impl.getNotificationUris(delegate)
-        }
-
-        @RequiresApi(api = Build.VERSION_CODES.M)
-        override fun setExtras(extras: Bundle) {
-            SupportSQLiteCompat.Api23Impl.setExtras(delegate, extras)
-        }
     }
 
     /**
-     * We can't close our db if the SupportSqliteStatement is open.
-     *
-     * Each of these that are created need to be registered with RefCounter.
-     *
-     * On auto-close, RefCounter needs to close each of these before closing the db that these were
-     * constructed from.
-     *
-     * Each of the methods here need to get
+     * Since long-living statements are a normal use-case, auto-close does not have a keep-alive
+     * statement, instead records SQL query and binding args and replicates on execution, opening
+     * the database is necessary but not helding a ref count on it.
      */
-    // TODO(rohitsat) cache the prepared statement... I'm not sure what the performance implications
-    // are for the way it's done here, but caching the prepared statement would definitely be more
-    // complicated since we need to invalidate any of the PreparedStatements that were created
-    // with this db
-    private class AutoClosingSupportSqliteStatement(
+    private class AutoClosingSupportSQLiteStatement(
         private val sql: String,
         private val autoCloser: AutoCloser
     ) : SupportSQLiteStatement {
-        private val binds = ArrayList<Any?>()
 
-        private fun <T> executeSqliteStatementWithRefCount(
-            block: (SupportSQLiteStatement) -> T
-        ): T {
-            return autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
-                val statement: SupportSQLiteStatement = db.compileStatement(sql)
-                doBinds(statement)
-                block(statement)
-            }
-        }
+        private var bindingTypes: IntArray = IntArray(0)
+        private var longBindings: LongArray = LongArray(0)
+        private var doubleBindings: DoubleArray = DoubleArray(0)
+        private var stringBindings: Array<String?> = emptyArray()
+        private var blobBindings: Array<ByteArray?> = emptyArray()
 
-        private fun doBinds(supportSQLiteStatement: SupportSQLiteStatement) {
-            // Replay the binds
-            binds.forEachIndexed { i, _ ->
-                val bindIndex = i + 1 // Bind indices are 1 based so we start at 1 not 0
-                when (val bind = binds[i]) {
-                    null -> {
-                        supportSQLiteStatement.bindNull(bindIndex)
-                    }
-                    is Long -> {
-                        supportSQLiteStatement.bindLong(bindIndex, bind)
-                    }
-                    is Double -> {
-                        supportSQLiteStatement.bindDouble(bindIndex, bind)
-                    }
-                    is String -> {
-                        supportSQLiteStatement.bindString(bindIndex, bind)
-                    }
-                    is ByteArray -> {
-                        supportSQLiteStatement.bindBlob(bindIndex, bind)
-                    }
-                }
-            }
-        }
-
-        private fun saveBinds(bindIndex: Int, value: Any?) {
-            val index = bindIndex - 1
-            if (index >= binds.size) {
-                // Add null entries to the list until we have the desired # of indices
-                for (i in binds.size..index) {
-                    binds.add(null)
-                }
-            }
-            binds[index] = value
-        }
-
-        @Throws(IOException::class)
         override fun close() {
-            // Nothing to do here since we re-compile the statement each time.
+            // Not much to do here since we re-compile the statement each time.
+            clearBindings()
         }
 
         override fun execute() {
-            executeSqliteStatementWithRefCount<Any?> { statement: SupportSQLiteStatement ->
-                statement.execute()
-                null
-            }
+            executeWithRefCount { statement: SupportSQLiteStatement -> statement.execute() }
         }
 
         override fun executeUpdateDelete(): Int {
-            return executeSqliteStatementWithRefCount { obj: SupportSQLiteStatement ->
-                obj.executeUpdateDelete()
-            }
+            return executeWithRefCount { obj: SupportSQLiteStatement -> obj.executeUpdateDelete() }
         }
 
         override fun executeInsert(): Long {
-            return executeSqliteStatementWithRefCount { obj: SupportSQLiteStatement ->
-                obj.executeInsert()
-            }
+            return executeWithRefCount { obj: SupportSQLiteStatement -> obj.executeInsert() }
         }
 
         override fun simpleQueryForLong(): Long {
-            return executeSqliteStatementWithRefCount { obj: SupportSQLiteStatement ->
-                obj.simpleQueryForLong()
-            }
+            return executeWithRefCount { obj: SupportSQLiteStatement -> obj.simpleQueryForLong() }
         }
 
         override fun simpleQueryForString(): String? {
-            return executeSqliteStatementWithRefCount { obj: SupportSQLiteStatement ->
-                obj.simpleQueryForString()
+            return executeWithRefCount { obj: SupportSQLiteStatement -> obj.simpleQueryForString() }
+        }
+
+        private fun <T> executeWithRefCount(block: (SupportSQLiteStatement) -> T): T {
+            return autoCloser.executeRefCountingFunction { db: SupportSQLiteDatabase ->
+                val actualStatement = db.compileStatement(sql)
+                bindTo(actualStatement)
+                block(actualStatement)
             }
         }
 
         override fun bindNull(index: Int) {
-            saveBinds(index, null)
+            ensureCapacity(COLUMN_TYPE_NULL, index)
+            bindingTypes[index] = COLUMN_TYPE_NULL
         }
 
         override fun bindLong(index: Int, value: Long) {
-            saveBinds(index, value)
+            ensureCapacity(COLUMN_TYPE_LONG, index)
+            bindingTypes[index] = COLUMN_TYPE_LONG
+            longBindings[index] = value
         }
 
         override fun bindDouble(index: Int, value: Double) {
-            saveBinds(index, value)
+            ensureCapacity(COLUMN_TYPE_DOUBLE, index)
+            bindingTypes[index] = COLUMN_TYPE_DOUBLE
+            doubleBindings[index] = value
         }
 
         override fun bindString(index: Int, value: String) {
-            saveBinds(index, value)
+            ensureCapacity(COLUMN_TYPE_STRING, index)
+            bindingTypes[index] = COLUMN_TYPE_STRING
+            stringBindings[index] = value
         }
 
         override fun bindBlob(index: Int, value: ByteArray) {
-            saveBinds(index, value)
+            ensureCapacity(COLUMN_TYPE_BLOB, index)
+            bindingTypes[index] = COLUMN_TYPE_BLOB
+            blobBindings[index] = value
         }
 
         override fun clearBindings() {
-            binds.clear()
+            bindingTypes = IntArray(0)
+            longBindings = LongArray(0)
+            doubleBindings = DoubleArray(0)
+            stringBindings = emptyArray()
+            blobBindings = emptyArray()
+        }
+
+        private fun ensureCapacity(columnType: Int, index: Int) {
+            val requiredSize = index + 1
+            if (bindingTypes.size < requiredSize) {
+                bindingTypes = bindingTypes.copyOf(requiredSize)
+            }
+            when (columnType) {
+                COLUMN_TYPE_LONG -> {
+                    if (longBindings.size < requiredSize) {
+                        longBindings = longBindings.copyOf(requiredSize)
+                    }
+                }
+                COLUMN_TYPE_DOUBLE -> {
+                    if (doubleBindings.size < requiredSize) {
+                        doubleBindings = doubleBindings.copyOf(requiredSize)
+                    }
+                }
+                COLUMN_TYPE_STRING -> {
+                    if (stringBindings.size < requiredSize) {
+                        stringBindings = stringBindings.copyOf(requiredSize)
+                    }
+                }
+                COLUMN_TYPE_BLOB -> {
+                    if (blobBindings.size < requiredSize) {
+                        blobBindings = blobBindings.copyOf(requiredSize)
+                    }
+                }
+            }
+        }
+
+        private fun bindTo(query: SupportSQLiteProgram) {
+            for (index in 1 until bindingTypes.size) {
+                when (bindingTypes[index]) {
+                    COLUMN_TYPE_LONG -> query.bindLong(index, longBindings[index])
+                    COLUMN_TYPE_DOUBLE -> query.bindDouble(index, doubleBindings[index])
+                    COLUMN_TYPE_STRING -> query.bindString(index, stringBindings[index]!!)
+                    COLUMN_TYPE_BLOB -> query.bindBlob(index, blobBindings[index]!!)
+                    COLUMN_TYPE_NULL -> query.bindNull(index)
+                }
+            }
+        }
+
+        companion object {
+            private const val COLUMN_TYPE_LONG = 1
+            private const val COLUMN_TYPE_DOUBLE = 2
+            private const val COLUMN_TYPE_STRING = 3
+            private const val COLUMN_TYPE_BLOB = 4
+            private const val COLUMN_TYPE_NULL = 5
         }
     }
 }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactory.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactory.android.kt
index ce1e650..7c8f88d 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactory.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/AutoClosingRoomOpenHelperFactory.android.kt
@@ -22,7 +22,6 @@
     private val delegate: SupportSQLiteOpenHelper.Factory,
     private val autoCloser: AutoCloser
 ) : SupportSQLiteOpenHelper.Factory {
-    /** @return AutoClosingRoomOpenHelper instances. */
     override fun create(
         configuration: SupportSQLiteOpenHelper.Configuration
     ): AutoClosingRoomOpenHelper {
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelperFactory.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelperFactory.android.kt
index 4d369fe..1226f62 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelperFactory.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/PrePackagedCopyOpenHelperFactory.android.kt
@@ -22,21 +22,21 @@
 
 /** Implementation of [SupportSQLiteOpenHelper.Factory] that creates [PrePackagedCopyOpenHelper]. */
 internal class PrePackagedCopyOpenHelperFactory(
-    private val mCopyFromAssetPath: String?,
-    private val mCopyFromFile: File?,
-    private val mCopyFromInputStream: Callable<InputStream>?,
-    private val mDelegate: SupportSQLiteOpenHelper.Factory
+    private val copyFromAssetPath: String?,
+    private val copyFromFile: File?,
+    private val copyFromInputStream: Callable<InputStream>?,
+    private val delegate: SupportSQLiteOpenHelper.Factory
 ) : SupportSQLiteOpenHelper.Factory {
     override fun create(
         configuration: SupportSQLiteOpenHelper.Configuration
     ): SupportSQLiteOpenHelper {
         return PrePackagedCopyOpenHelper(
             configuration.context,
-            mCopyFromAssetPath,
-            mCopyFromFile,
-            mCopyFromInputStream,
+            copyFromAssetPath,
+            copyFromFile,
+            copyFromInputStream,
             configuration.callback.version,
-            mDelegate.create(configuration)
+            delegate.create(configuration)
         )
     }
 }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt
index e57190c..c6eb76d 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt
@@ -23,12 +23,13 @@
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteQuery
 import androidx.sqlite.db.SupportSQLiteStatement
-import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
-/** Implements [SupportSQLiteDatabase] for SQLite queries. */
+/** Implements [SupportSQLiteDatabase] for intercepting SQLite queries. */
 internal class QueryInterceptorDatabase(
     private val delegate: SupportSQLiteDatabase,
-    private val queryCallbackExecutor: Executor,
+    private val queryCallbackScope: CoroutineScope,
     private val queryCallback: RoomDatabase.QueryCallback
 ) : SupportSQLiteDatabase by delegate {
 
@@ -36,27 +37,27 @@
         return QueryInterceptorStatement(
             delegate.compileStatement(sql),
             sql,
-            queryCallbackExecutor,
+            queryCallbackScope,
             queryCallback,
         )
     }
 
     override fun beginTransaction() {
-        queryCallbackExecutor.execute {
+        queryCallbackScope.launch {
             queryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION", emptyList())
         }
         delegate.beginTransaction()
     }
 
     override fun beginTransactionNonExclusive() {
-        queryCallbackExecutor.execute {
+        queryCallbackScope.launch {
             queryCallback.onQuery("BEGIN DEFERRED TRANSACTION", emptyList())
         }
         delegate.beginTransactionNonExclusive()
     }
 
     override fun beginTransactionWithListener(transactionListener: SQLiteTransactionListener) {
-        queryCallbackExecutor.execute {
+        queryCallbackScope.launch {
             queryCallback.onQuery("BEGIN EXCLUSIVE TRANSACTION", emptyList())
         }
         delegate.beginTransactionWithListener(transactionListener)
@@ -65,38 +66,37 @@
     override fun beginTransactionWithListenerNonExclusive(
         transactionListener: SQLiteTransactionListener
     ) {
-        queryCallbackExecutor.execute {
+        queryCallbackScope.launch {
             queryCallback.onQuery("BEGIN DEFERRED TRANSACTION", emptyList())
         }
         delegate.beginTransactionWithListenerNonExclusive(transactionListener)
     }
 
     override fun endTransaction() {
-        queryCallbackExecutor.execute { queryCallback.onQuery("END TRANSACTION", emptyList()) }
+        queryCallbackScope.launch { queryCallback.onQuery("END TRANSACTION", emptyList()) }
         delegate.endTransaction()
     }
 
     override fun setTransactionSuccessful() {
-        queryCallbackExecutor.execute {
-            queryCallback.onQuery("TRANSACTION SUCCESSFUL", emptyList())
-        }
+        queryCallbackScope.launch { queryCallback.onQuery("TRANSACTION SUCCESSFUL", emptyList()) }
         delegate.setTransactionSuccessful()
     }
 
     override fun query(query: String): Cursor {
-        queryCallbackExecutor.execute { queryCallback.onQuery(query, emptyList()) }
+        queryCallbackScope.launch { queryCallback.onQuery(query, emptyList()) }
         return delegate.query(query)
     }
 
     override fun query(query: String, bindArgs: Array<out Any?>): Cursor {
-        queryCallbackExecutor.execute { queryCallback.onQuery(query, bindArgs.toList()) }
+        val argsCopy = bindArgs.toList()
+        queryCallbackScope.launch { queryCallback.onQuery(query, argsCopy) }
         return delegate.query(query, bindArgs)
     }
 
     override fun query(query: SupportSQLiteQuery): Cursor {
         val queryInterceptorProgram = QueryInterceptorProgram()
         query.bindTo(queryInterceptorProgram)
-        queryCallbackExecutor.execute {
+        queryCallbackScope.launch {
             queryCallback.onQuery(query.sql, queryInterceptorProgram.bindArgsCache)
         }
         return delegate.query(query)
@@ -105,7 +105,7 @@
     override fun query(query: SupportSQLiteQuery, cancellationSignal: CancellationSignal?): Cursor {
         val queryInterceptorProgram = QueryInterceptorProgram()
         query.bindTo(queryInterceptorProgram)
-        queryCallbackExecutor.execute {
+        queryCallbackScope.launch {
             queryCallback.onQuery(query.sql, queryInterceptorProgram.bindArgsCache)
         }
         return delegate.query(query)
@@ -115,7 +115,7 @@
     // and it can't be renamed.
     @Suppress("AcronymName")
     override fun execSQL(sql: String) {
-        queryCallbackExecutor.execute { queryCallback.onQuery(sql, emptyList()) }
+        queryCallbackScope.launch { queryCallback.onQuery(sql, emptyList()) }
         delegate.execSQL(sql)
     }
 
@@ -123,8 +123,8 @@
     // and it can't be renamed.
     @Suppress("AcronymName")
     override fun execSQL(sql: String, bindArgs: Array<out Any?>) {
-        val inputArguments = buildList { addAll(bindArgs) }
-        queryCallbackExecutor.execute { queryCallback.onQuery(sql, inputArguments) }
-        delegate.execSQL(sql, inputArguments.toTypedArray())
+        val argsCopy = bindArgs.toList()
+        queryCallbackScope.launch { queryCallback.onQuery(sql, argsCopy) }
+        delegate.execSQL(sql, bindArgs)
     }
 }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelper.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelper.android.kt
index cda7232..6e7b2d6 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelper.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelper.android.kt
@@ -20,26 +20,19 @@
 import androidx.room.RoomDatabase
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
-import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineScope
 
 internal class QueryInterceptorOpenHelper(
     override val delegate: SupportSQLiteOpenHelper,
-    private val queryCallbackExecutor: Executor,
+    private val queryCallbackScope: CoroutineScope,
     private val queryCallback: RoomDatabase.QueryCallback
 ) : SupportSQLiteOpenHelper by delegate, DelegatingOpenHelper {
+
     override val writableDatabase: SupportSQLiteDatabase
         get() =
-            QueryInterceptorDatabase(
-                delegate.writableDatabase,
-                queryCallbackExecutor,
-                queryCallback
-            )
+            QueryInterceptorDatabase(delegate.writableDatabase, queryCallbackScope, queryCallback)
 
     override val readableDatabase: SupportSQLiteDatabase
         get() =
-            QueryInterceptorDatabase(
-                delegate.readableDatabase,
-                queryCallbackExecutor,
-                queryCallback
-            )
+            QueryInterceptorDatabase(delegate.readableDatabase, queryCallbackScope, queryCallback)
 }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelperFactory.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelperFactory.android.kt
index b18ad06..7d0ccb4 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelperFactory.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorOpenHelperFactory.android.kt
@@ -18,12 +18,12 @@
 
 import androidx.room.RoomDatabase
 import androidx.sqlite.db.SupportSQLiteOpenHelper
-import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineScope
 
-/** Implements [SupportSQLiteOpenHelper.Factory] to wrap [QueryInterceptorOpenHelper]. */
+/** Implements [SupportSQLiteOpenHelper.Factory] to create a [QueryInterceptorOpenHelper]. */
 internal class QueryInterceptorOpenHelperFactory(
     private val delegate: SupportSQLiteOpenHelper.Factory,
-    private val queryCallbackExecutor: Executor,
+    private val queryCallbackScope: CoroutineScope,
     private val queryCallback: RoomDatabase.QueryCallback,
 ) : SupportSQLiteOpenHelper.Factory by delegate {
     override fun create(
@@ -31,7 +31,7 @@
     ): SupportSQLiteOpenHelper {
         return QueryInterceptorOpenHelper(
             delegate.create(configuration),
-            queryCallbackExecutor,
+            queryCallbackScope,
             queryCallback
         )
     }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorStatement.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorStatement.android.kt
index 457797b..0200701 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorStatement.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorStatement.android.kt
@@ -18,40 +18,46 @@
 
 import androidx.room.RoomDatabase
 import androidx.sqlite.db.SupportSQLiteStatement
-import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
-/** Implements an instance of [SupportSQLiteStatement] for SQLite queries. */
+/** Implements an instance of [SupportSQLiteStatement] for intercepting SQLite queries. */
 internal class QueryInterceptorStatement(
     private val delegate: SupportSQLiteStatement,
     private val sqlStatement: String,
-    private val queryCallbackExecutor: Executor,
+    private val queryCallbackScope: CoroutineScope,
     private val queryCallback: RoomDatabase.QueryCallback,
 ) : SupportSQLiteStatement by delegate {
 
     private val bindArgsCache = mutableListOf<Any?>()
 
     override fun execute() {
-        queryCallbackExecutor.execute { queryCallback.onQuery(sqlStatement, bindArgsCache) }
+        val argsCopy = bindArgsCache.toList()
+        queryCallbackScope.launch { queryCallback.onQuery(sqlStatement, argsCopy) }
         delegate.execute()
     }
 
     override fun executeUpdateDelete(): Int {
-        queryCallbackExecutor.execute { queryCallback.onQuery(sqlStatement, bindArgsCache) }
+        val argsCopy = bindArgsCache.toList()
+        queryCallbackScope.launch { queryCallback.onQuery(sqlStatement, argsCopy) }
         return delegate.executeUpdateDelete()
     }
 
     override fun executeInsert(): Long {
-        queryCallbackExecutor.execute { queryCallback.onQuery(sqlStatement, bindArgsCache) }
+        val argsCopy = bindArgsCache.toList()
+        queryCallbackScope.launch { queryCallback.onQuery(sqlStatement, argsCopy) }
         return delegate.executeInsert()
     }
 
     override fun simpleQueryForLong(): Long {
-        queryCallbackExecutor.execute { queryCallback.onQuery(sqlStatement, bindArgsCache) }
+        val argsCopy = bindArgsCache.toList()
+        queryCallbackScope.launch { queryCallback.onQuery(sqlStatement, argsCopy) }
         return delegate.simpleQueryForLong()
     }
 
     override fun simpleQueryForString(): String? {
-        queryCallbackExecutor.execute { queryCallback.onQuery(sqlStatement, bindArgsCache) }
+        val argsCopy = bindArgsCache.toList()
+        queryCallbackScope.launch { queryCallback.onQuery(sqlStatement, argsCopy) }
         return delegate.simpleQueryForString()
     }
 
diff --git a/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt b/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt
index ed9ea59..4804711 100644
--- a/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt
+++ b/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt
@@ -23,7 +23,6 @@
 import androidx.sqlite.SQLiteConnection
 import androidx.sqlite.SQLiteDriver
 import androidx.sqlite.SQLiteStatement
-import androidx.test.filters.FlakyTest
 import java.lang.ref.WeakReference
 import java.util.Locale
 import java.util.concurrent.CountDownLatch
@@ -314,7 +313,7 @@
     }
 
     @Test
-    @FlakyTest(bugId = 349880963)
+    @Ignore // b/349880963
     fun multipleRefreshAsync() = runTest {
         // Validate that when multiple refresh are enqueued, that only one runs.
         tracker.refreshAsync()
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
index 30afd7e..d3e87b1 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
@@ -22,7 +22,6 @@
 import androidx.room.concurrent.CloseBarrier
 import androidx.room.migration.AutoMigrationSpec
 import androidx.room.migration.Migration
-import androidx.room.util.isAssignableFrom
 import androidx.sqlite.SQLiteConnection
 import androidx.sqlite.SQLiteDriver
 import androidx.sqlite.SQLiteException
@@ -522,8 +521,7 @@
         var foundIndex = -1
         for (providedIndex in configuration.autoMigrationSpecs.indices.reversed()) {
             val provided: Any = configuration.autoMigrationSpecs[providedIndex]
-            // TODO(b/317210564): For native only FQN is compared
-            if (spec.isAssignableFrom(provided::class)) {
+            if (spec.isInstance(provided)) {
                 foundIndex = providedIndex
                 usedSpecs[foundIndex] = true
                 break
@@ -568,7 +566,7 @@
             // traverse provided converters in reverse so that newer one overrides
             for (providedIndex in configuration.typeConverters.indices.reversed()) {
                 val provided = configuration.typeConverters[providedIndex]
-                if (converter.isAssignableFrom(provided::class)) {
+                if (converter.isInstance(provided)) {
                     foundIndex = providedIndex
                     used[foundIndex] = true
                     break
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabaseConstructor.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabaseConstructor.kt
new file mode 100644
index 0000000..ba75104
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabaseConstructor.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+/**
+ * Defines a class that can instantiate the Room generated implementation of an 'abstract'
+ * [Database] annotated [RoomDatabase] definition.
+ *
+ * This interface is to be used in conjunction with [ConstructedBy] to define an 'expect'
+ * declaration of an 'object' that implements this interface. The defined 'object' can then be
+ * optionally used in Room's `databaseBuilder` or `inMemoryDatabaseBuilder` as the `factory`.
+ *
+ * For example, with the following object definition:
+ * ```
+ * expect object MusicDatabaseConstructor : RoomDatabaseConstructor<MusicDatabase>
+ * ```
+ *
+ * one can reference the object's [initialize] during database creation:
+ * ```
+ * fun createDatabase(): MusicDatabase {
+ *     return Room.inMemoryDatabaseBuilder<MusicDatabase>(
+ *         factory = MusicDatabaseConstructor::initialize
+ *     ).build()
+ * }
+ * ```
+ *
+ * For Room to correctly and automatically use 'actual' implementations of this interface, they must
+ * be linked to their respective [Database] definition via [ConstructedBy].
+ *
+ * @param T The [Database] and [ConstructedBy] annotated class linked to this constructor.
+ * @see ConstructedBy
+ */
+interface RoomDatabaseConstructor<T : RoomDatabase> {
+    /**
+     * Instantiates an implementation of [T].
+     *
+     * @return T - A new instance of [T].
+     */
+    fun initialize(): T
+}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomRawQuery.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomRawQuery.kt
new file mode 100644
index 0000000..174d8663a
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomRawQuery.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+import androidx.sqlite.SQLiteStatement
+import kotlin.jvm.JvmOverloads
+
+/**
+ * A query with an argument binding function.
+ *
+ * @see [RawQuery]
+ */
+class RoomRawQuery
+@JvmOverloads
+constructor(
+    /**
+     * The SQL query.
+     *
+     * The query can have placeholders (?) to bind arguments.
+     */
+    val sql: String,
+    /**
+     * The function that receives a [SQLiteStatement] and binds arguments.
+     *
+     * Only `bind*()` calls should be invoked on the received statement.
+     */
+    onBindStatement: (SQLiteStatement) -> Unit = {}
+) {
+    private val bindingFunction: (SQLiteStatement) -> Unit = {
+        onBindStatement.invoke(BindOnlySQLiteStatement(it))
+    }
+
+    fun getBindingFunction(): (SQLiteStatement) -> Unit = bindingFunction
+}
+
+private class BindOnlySQLiteStatement(delegate: SQLiteStatement) : SQLiteStatement by delegate {
+
+    override fun getBlob(index: Int): ByteArray {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun getDouble(index: Int): Double {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun getLong(index: Int): Long {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun getText(index: Int): String {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun isNull(index: Int): Boolean {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun getColumnCount(): Int {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun getColumnName(index: Int): String {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun step(): Boolean {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun reset() {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    override fun close() {
+        error(ONLY_BIND_CALLS_ALLOWED_ERROR)
+    }
+
+    companion object {
+        private const val ONLY_BIND_CALLS_ALLOWED_ERROR =
+            "Only bind*() calls are allowed on the RoomRawQuery received statement."
+    }
+}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt
index 62d9754..68c6f45e 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt
@@ -186,11 +186,7 @@
     private val size = atomic(0)
     private val connections = arrayOfNulls<ConnectionWithLock>(capacity)
     private val channel =
-        Channel<ConnectionWithLock>(
-            capacity = capacity,
-            // Only trySend() is used, but due to high paranoia add an undelivered callback
-            onUndeliveredElement = { unusedConnection -> unusedConnection.close() }
-        )
+        Channel<ConnectionWithLock>(capacity = capacity, onUndeliveredElement = { recycle(it) })
 
     suspend fun acquire(): ConnectionWithLock {
         val receiveResult = channel.tryReceive()
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/KClassUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/KClassUtil.kt
deleted file mode 100644
index 6421ee5..0000000
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/KClassUtil.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:JvmName("KClassUtil")
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-
-package androidx.room.util
-
-import androidx.annotation.RestrictTo
-import kotlin.jvm.JvmName
-import kotlin.reflect.KClass
-
-/**
- * Determines if the class or interface represented by this object is the same as the class or
- * interface represented by the specified [KClass] parameter.
- */
-internal expect fun KClass<*>.isAssignableFrom(other: KClass<*>): Boolean
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
index 17b77c6..7272ad6 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
@@ -45,6 +45,9 @@
 
 // TODO(b/322183292): Consider optimizing by creating a String->Int map, similar to Android
 internal fun SQLiteStatement.columnIndexOfCommon(name: String): Int {
+    if (this is MappedColumnsSQLiteStatementWrapper) {
+        return getColumnIndex(name)
+    }
     val columnCount = getColumnCount()
     for (i in 0 until columnCount) {
         if (name == getColumnName(i)) return i
@@ -57,3 +60,52 @@
 fun getColumnIndex(stmt: SQLiteStatement, name: String): Int {
     return stmt.columnIndexOf(name)
 }
+
+/**
+ * Wraps the given statement such that `getColumnIndex()` will utilize the provided `mapping` when
+ * getting the index of a column in `columnNames`.
+ *
+ * This is useful when the original statement contains duplicate columns. Instead of letting the
+ * statement return the first matching column with a name, we can resolve the ambiguous column
+ * indices and wrap the statement such that for a set of desired column indices, the returned value
+ * will be that from the pre-computation.
+ *
+ * @param statement the statement to wrap.
+ * @param columnNames the column names whose index are known. The result column index of the column
+ *   name at i will be at `mapping[i]`.
+ * @param mapping the cursor column indices of the columns at `columnNames`.
+ * @return the wrapped Cursor.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun wrapMappedColumns(
+    statement: SQLiteStatement,
+    columnNames: Array<String>,
+    mapping: IntArray
+): SQLiteStatement {
+    return MappedColumnsSQLiteStatementWrapper(statement, columnNames, mapping)
+}
+
+internal class MappedColumnsSQLiteStatementWrapper(
+    private val delegate: SQLiteStatement,
+    private val columnNames: Array<String>,
+    private val mapping: IntArray
+) : SQLiteStatement by delegate {
+
+    init {
+        require(columnNames.size == mapping.size) { "Expected columnNames.size == mapping.size" }
+    }
+
+    private val columnNameToIndexMap = buildMap {
+        columnNames.forEachIndexed { i, mappedColumnName -> put(mappedColumnName, mapping[i]) }
+        for (i in 0 until getColumnCount()) {
+            val name = getColumnName(i)
+            if (!containsKey(name)) {
+                put(getColumnName(i), i)
+            }
+        }
+    }
+
+    fun getColumnIndex(name: String): Int {
+        return columnNameToIndexMap[name] ?: -1
+    }
+}
diff --git a/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt b/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt
index cd9bba6..e858a8c 100644
--- a/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt
+++ b/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt
@@ -30,6 +30,7 @@
 import androidx.sqlite.execSQL
 import androidx.sqlite.use
 import kotlin.coroutines.CoroutineContext
+import kotlin.random.Random
 import kotlin.test.Ignore
 import kotlin.test.Test
 import kotlin.test.assertFailsWith
@@ -551,6 +552,43 @@
     }
 
     @Test
+    fun stressCancelCoroutineAcquiringConnection() = runBlocking {
+        val multiThreadContext = newFixedThreadPoolContext(3, "Test-Threads")
+        val driver = setupDriver()
+        val pool =
+            newConnectionPool(
+                driver = driver,
+                fileName = fileName,
+                maxNumOfReaders = 1,
+                maxNumOfWriters = 1
+            )
+        // This stress test is very non-deterministic, on purpose. It launches three coroutines, two
+        // of them attempt to use the connection, but one of the coroutines is canceled shortly
+        // after by the third coroutines. The goal of this test is to validate the
+        // onUndeliveredElement callback of the Pool's Channel. If this test fails it will likely be
+        // due to a 'Timed out attempting to acquire a connection'.
+        val jobsToWaitFor = mutableListOf<Job>()
+        repeat(1000) {
+            val jobToCancel =
+                launch(multiThreadContext) {
+                    pool.useWriterConnection { delay(Random.nextLong(5)) }
+                }
+            jobsToWaitFor.add(
+                launch(multiThreadContext) {
+                    pool.useWriterConnection { delay(Random.nextLong(5)) }
+                }
+            )
+            jobsToWaitFor.add(
+                launch(multiThreadContext) {
+                    delay(Random.nextLong(5))
+                    jobToCancel.cancel()
+                }
+            )
+        }
+        jobsToWaitFor.joinAll()
+    }
+
+    @Test
     fun timeoutCoroutineWaitingForConnection() = runTest {
         val multiThreadContext = newFixedThreadPoolContext(2, "Test-Threads")
         val driver = setupDriver()
diff --git a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt
index 8dd99f5..5f7b12e 100644
--- a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt
+++ b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt
@@ -20,17 +20,6 @@
 package androidx.room.util
 
 import androidx.annotation.RestrictTo
-import kotlin.jvm.JvmName
-import kotlin.reflect.KClass
-
-/**
- * Determines if the class or interface represented by this object is the same as, or is a
- * superclass or superinterface of the class or interface represented by the specified [KClass]
- * parameter.
- */
-internal actual fun KClass<*>.isAssignableFrom(other: KClass<*>): Boolean {
-    return this.java.isAssignableFrom(other.java)
-}
 
 /**
  * Finds and instantiates via reflection the implementation class generated by Room of an
diff --git a/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt b/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt
index 61a1038..c2182d4 100644
--- a/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt
+++ b/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt
@@ -30,9 +30,9 @@
      * reference to it and re-use it.
      *
      * @param T The type of the database class.
-     * @param factory An optional lambda calling `initializeImpl()` on the database class which
-     *   returns the generated database implementation. If not provided then reflection is used to
-     *   find and instantiate the database implementation class.
+     * @param factory An optional lambda calling [RoomDatabaseConstructor.initialize] corresponding
+     *   to the database class of this builder. If not provided then reflection is used to find and
+     *   instantiate the database implementation class.
      * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
      */
     inline fun <reified T : RoomDatabase> inMemoryDatabaseBuilder(
@@ -47,9 +47,9 @@
      *
      * @param T The type of the database class.
      * @param name The name of the database file.
-     * @param factory An optional lambda calling `initializeImpl()` on the database class which
-     *   returns the generated database implementation. If not provided then reflection is used to
-     *   find and instantiate the database implementation class.
+     * @param factory An optional lambda calling [RoomDatabaseConstructor.initialize] corresponding
+     *   to the database class of this builder. If not provided then reflection is used to find and
+     *   instantiate the database implementation class.
      * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
      */
     inline fun <reified T : RoomDatabase> databaseBuilder(
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt
index 8ac2663..ef71da8 100644
--- a/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt
@@ -16,6 +16,8 @@
 
 package androidx.room
 
+import androidx.room.util.findDatabaseConstructorAndInitDatabaseImpl
+
 /** Entry point for building and initializing a [RoomDatabase]. */
 actual object Room {
 
@@ -28,12 +30,14 @@
      * reference to it and re-use it.
      *
      * @param T The type of the database class.
-     * @param factory The lambda calling `initializeImpl()` on the database class which returns the
-     *   generated database implementation.
+     * @param factory An optional lambda calling [RoomDatabaseConstructor.initialize] corresponding
+     *   to the database class of this builder. If not provided then the associated
+     *   [RoomDatabaseConstructor] is searched via the [ConstructedBy] annotation and is used to
+     *   instantiate the database implementation class.
      * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
      */
     inline fun <reified T : RoomDatabase> inMemoryDatabaseBuilder(
-        noinline factory: () -> T
+        noinline factory: () -> T = { findDatabaseConstructorAndInitDatabaseImpl(T::class) }
     ): RoomDatabase.Builder<T> {
         return RoomDatabase.Builder(T::class, null, factory)
     }
@@ -44,13 +48,15 @@
      *
      * @param T The type of the database class.
      * @param name The name of the database file.
-     * @param factory The lambda calling `initializeImpl()` on the database class which returns the
-     *   generated database implementation.
+     * @param factory An optional lambda calling [RoomDatabaseConstructor.initialize] corresponding
+     *   to the database class of this builder. If not provided then the associated
+     *   [RoomDatabaseConstructor] is searched via the [ConstructedBy] annotation and is used to
+     *   instantiate the database implementation class.
      * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
      */
     inline fun <reified T : RoomDatabase> databaseBuilder(
         name: String,
-        noinline factory: () -> T
+        noinline factory: () -> T = { findDatabaseConstructorAndInitDatabaseImpl(T::class) }
     ): RoomDatabase.Builder<T> {
         require(name.isNotBlank()) {
             "Cannot build a database with empty name." +
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt
index 091b10f..3be16d4 100644
--- a/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt
@@ -19,13 +19,26 @@
 package androidx.room.util
 
 import androidx.annotation.RestrictTo
+import androidx.room.ConstructedBy
+import androidx.room.RoomDatabase
+import androidx.room.RoomDatabaseConstructor
+import kotlin.reflect.ExperimentalAssociatedObjects
 import kotlin.reflect.KClass
+import kotlin.reflect.findAssociatedObject
 
 /**
- * Determines if the class or interface represented by this object is the same as the class or
- * interface represented by the specified [KClass] parameter. Such case is only true if the
- * qualified name of both classes match.
+ * Finds the [RoomDatabaseConstructor] of this class linked via [ConstructedBy] and invoke its
+ * function to create an instance of the implementation class generated by Room of an `@Database`
+ * annotated type.
  */
-internal actual fun KClass<*>.isAssignableFrom(other: KClass<*>): Boolean {
-    return this.qualifiedName == other.qualifiedName
+@OptIn(ExperimentalAssociatedObjects::class)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun <T : RoomDatabase> findDatabaseConstructorAndInitDatabaseImpl(klass: KClass<*>): T {
+    val constructor = klass.findAssociatedObject<ConstructedBy>() as? RoomDatabaseConstructor<*>
+    checkNotNull(constructor) {
+        "Cannot find the associated ${RoomDatabaseConstructor::class.qualifiedName} for " +
+            "${klass.qualifiedName}. Is Room annotation processor correctly configured?"
+    }
+    @Suppress("UNCHECKED_CAST") // Actually safe due to annotation processor enforcement
+    return constructor.initialize() as T
 }
diff --git a/room/room-rxjava2/build.gradle b/room/room-rxjava2/build.gradle
index f07d83d..c49ef63 100644
--- a/room/room-rxjava2/build.gradle
+++ b/room/room-rxjava2/build.gradle
@@ -50,7 +50,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "Android Room RXJava2"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/room/room-rxjava3/build.gradle b/room/room-rxjava3/build.gradle
index f616b49..dd068ad 100644
--- a/room/room-rxjava3/build.gradle
+++ b/room/room-rxjava3/build.gradle
@@ -51,7 +51,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Room RXJava3"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/room/room-testing/bcv/native/current.txt b/room/room-testing/bcv/native/current.txt
index 4e86aa3..4e8b047 100644
--- a/room/room-testing/bcv/native/current.txt
+++ b/room/room-testing/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
@@ -7,7 +7,7 @@
 
 // Library unique name: <androidx.room:room-testing>
 final class androidx.room.testing/MigrationTestHelper { // androidx.room.testing/MigrationTestHelper|null[0]
-    constructor <init>(kotlin/String, kotlin/String, androidx.sqlite/SQLiteDriver, kotlin.reflect/KClass<out androidx.room/RoomDatabase>, kotlin/Function0<androidx.room/RoomDatabase>, kotlin.collections/List<androidx.room.migration/AutoMigrationSpec> =...) // androidx.room.testing/MigrationTestHelper.<init>|<init>(kotlin.String;kotlin.String;androidx.sqlite.SQLiteDriver;kotlin.reflect.KClass<out|androidx.room.RoomDatabase>;kotlin.Function0<androidx.room.RoomDatabase>;kotlin.collections.List<androidx.room.migration.AutoMigrationSpec>){}[0]
+    constructor <init>(kotlin/String, kotlin/String, androidx.sqlite/SQLiteDriver, kotlin.reflect/KClass<out androidx.room/RoomDatabase>, kotlin/Function0<androidx.room/RoomDatabase> =..., kotlin.collections/List<androidx.room.migration/AutoMigrationSpec> =...) // androidx.room.testing/MigrationTestHelper.<init>|<init>(kotlin.String;kotlin.String;androidx.sqlite.SQLiteDriver;kotlin.reflect.KClass<out|androidx.room.RoomDatabase>;kotlin.Function0<androidx.room.RoomDatabase>;kotlin.collections.List<androidx.room.migration.AutoMigrationSpec>){}[0]
     final fun createDatabase(kotlin/Int): androidx.sqlite/SQLiteConnection // androidx.room.testing/MigrationTestHelper.createDatabase|createDatabase(kotlin.Int){}[0]
     final fun finished() // androidx.room.testing/MigrationTestHelper.finished|finished(){}[0]
     final fun runMigrationsAndValidate(kotlin/Int, kotlin.collections/List<androidx.room.migration/Migration> =...): androidx.sqlite/SQLiteConnection // androidx.room.testing/MigrationTestHelper.runMigrationsAndValidate|runMigrationsAndValidate(kotlin.Int;kotlin.collections.List<androidx.room.migration.Migration>){}[0]
diff --git a/room/room-testing/build.gradle b/room/room-testing/build.gradle
index 0b78109..13685a4 100644
--- a/room/room-testing/build.gradle
+++ b/room/room-testing/build.gradle
@@ -32,8 +32,6 @@
 }
 
 androidXMultiplatform {
-    enableBinaryCompatibilityValidator = true
-
     android()
     ios()
     jvm()
@@ -98,4 +96,5 @@
     inceptionYear = "2017"
     description = "Android Room Testing"
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt b/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt
index 14afbce..19f8018 100644
--- a/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt
+++ b/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt
@@ -21,6 +21,7 @@
 import androidx.room.migration.AutoMigrationSpec
 import androidx.room.migration.Migration
 import androidx.room.migration.bundle.SchemaBundle
+import androidx.room.util.findDatabaseConstructorAndInitDatabaseImpl
 import androidx.sqlite.SQLiteConnection
 import androidx.sqlite.SQLiteDriver
 import kotlin.reflect.KClass
@@ -93,7 +94,9 @@
     private val fileName: String,
     private val driver: SQLiteDriver,
     private val databaseClass: KClass<out RoomDatabase>,
-    databaseFactory: () -> RoomDatabase,
+    databaseFactory: () -> RoomDatabase = {
+        findDatabaseConstructorAndInitDatabaseImpl(databaseClass)
+    },
     private val autoMigrationSpecs: List<AutoMigrationSpec> = emptyList()
 ) {
     private val databaseInstance = databaseClass.cast(databaseFactory.invoke())
diff --git a/safeparcel/safeparcel/build.gradle b/safeparcel/safeparcel/build.gradle
index 572cf67..2eb2e7f 100644
--- a/safeparcel/safeparcel/build.gradle
+++ b/safeparcel/safeparcel/build.gradle
@@ -42,5 +42,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2023"
     description = "A special Parcelable interface that ensures backwards-compatibility"
-    metalavaK2UastEnabled = true
 }
diff --git a/samples/AndroidXDemos/build.gradle b/samples/AndroidXDemos/build.gradle
index 519d459..048ac9c 100644
--- a/samples/AndroidXDemos/build.gradle
+++ b/samples/AndroidXDemos/build.gradle
@@ -41,6 +41,7 @@
             proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
         }
     }
+    compileSdkVersion 35
     defaultConfig {
         vectorDrawables.useSupportLibrary = true
     }
diff --git a/samples/AndroidXDemos/lint-baseline.xml b/samples/AndroidXDemos/lint-baseline.xml
index 5bcc968..d1ef1a6 100644
--- a/samples/AndroidXDemos/lint-baseline.xml
+++ b/samples/AndroidXDemos/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="OnClick"
@@ -158,24 +158,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 29; however, the containing class com.example.androidx.app.AppCompatDefaultNightModeBootAwareActivity is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        .detectImplicitDirectBoot()"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/androidx/app/AppCompatDefaultNightModeBootAwareActivity.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class com.example.androidx.widget.selection.fancy.FancySelectionDemoActivity.OnContextClickListener is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (view.showContextMenu(x, y)) {"
-        errorLine2="                     ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/androidx/widget/selection/fancy/FancySelectionDemoActivity.java"/>
-    </issue>
-
-    <issue
         id="NullabilityAnnotationsDetector"
         message="Use `@androidx.annotation.NonNull` instead of `@org.jetbrains.annotations.NotNull`"
         errorLine1="        @NotNull"
diff --git a/samples/MediaRoutingDemo/build.gradle b/samples/MediaRoutingDemo/build.gradle
index 6da205a..617f702 100644
--- a/samples/MediaRoutingDemo/build.gradle
+++ b/samples/MediaRoutingDemo/build.gradle
@@ -28,6 +28,7 @@
             proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
         }
     }
+    compileSdk 35
     defaultConfig {
         vectorDrawables.useSupportLibrary = true
     }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java
index aed9f13..cb1f13b 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/AddEditRouteActivity.java
@@ -44,7 +44,7 @@
 public class AddEditRouteActivity extends AppCompatActivity {
     private static final String EXTRA_ROUTE_ID_KEY = "routeId";
 
-    private SampleDynamicGroupMediaRouteProviderService mService;
+    @Nullable private SampleDynamicGroupMediaRouteProviderService mService;
     private ServiceConnection mConnection;
     private RoutesManager mRoutesManager;
     private RouteItem mRouteItem;
@@ -163,7 +163,9 @@
         saveButton.setOnClickListener(
                 view -> {
                     mRoutesManager.addRoute(mRouteItem);
-                    mService.reloadRoutes();
+                    if (mService != null) {
+                        mService.reloadRoutes();
+                    }
                     finish();
                 });
     }
@@ -203,7 +205,7 @@
         }
 
         @Override
-        public void onServiceDisconnected(ComponentName arg0) {
+        public void onServiceDisconnected(ComponentName unusedComponentName) {
             mService = null;
         }
     }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java
index 65e89dd..97dd936 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/MainActivity.java
@@ -648,6 +648,9 @@
         @Override
         public void onRouteChanged(@NonNull MediaRouter router, @NonNull RouteInfo route) {
             Log.d(TAG, "onRouteChanged: route=" + route);
+            if (route.isSelected()) {
+                updateRouteDescription();
+            }
         }
 
         @Override
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/RouteListingPreferenceActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/RouteListingPreferenceActivity.java
index 5848b18..94ac61d 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/RouteListingPreferenceActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/RouteListingPreferenceActivity.java
@@ -24,6 +24,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
@@ -37,6 +39,10 @@
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.view.MenuItemCompat;
+import androidx.mediarouter.app.MediaRouteActionProvider;
+import androidx.mediarouter.media.MediaControlIntent;
+import androidx.mediarouter.media.MediaRouteSelector;
 import androidx.mediarouter.media.MediaRouter;
 import androidx.mediarouter.media.RouteListingPreference;
 import androidx.recyclerview.widget.ItemTouchHelper;
@@ -46,6 +52,7 @@
 import com.example.androidx.mediarouting.R;
 import com.example.androidx.mediarouting.RoutesManager;
 import com.example.androidx.mediarouting.RoutesManager.RouteListingPreferenceItemHolder;
+import com.example.androidx.mediarouting.providers.SampleMediaRouteProvider;
 import com.example.androidx.mediarouting.ui.UiUtils;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.common.collect.ImmutableList;
@@ -53,6 +60,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /** Allows the user to manage the route listing preference of this app. */
@@ -115,6 +123,25 @@
                                 mRoutesManager.getRouteListingPreferenceItems().size()));
     }
 
+    @Override
+    public boolean onCreateOptionsMenu(@NonNull Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        getMenuInflater().inflate(R.menu.route_button_menu, menu);
+        MenuItem mediaRouteMenuItem = menu.findItem(R.id.route_button_menu_item);
+        MediaRouteActionProvider mediaRouteActionProvider =
+                (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
+        MediaRouteSelector routeSelector =
+                new MediaRouteSelector.Builder()
+                        .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
+                        .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_AUDIO_PLAYBACK)
+                        .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_VIDEO_PLAYBACK)
+                        .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
+                        .addControlCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE)
+                        .build();
+        Objects.requireNonNull(mediaRouteActionProvider).setRouteSelector(routeSelector);
+        return true;
+    }
+
     private void setUpRouteListingPreferenceItemEditionDialog(int itemPositionInList) {
         List<RouteListingPreferenceItemHolder> routeListingPreference =
                 mRoutesManager.getRouteListingPreferenceItems();
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java
index 2db33b8..1285855 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/SettingsActivity.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.view.View;
@@ -43,6 +44,7 @@
 import com.example.androidx.mediarouting.RoutesManager;
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutingActivity;
 import com.example.androidx.mediarouting.services.SampleDynamicGroupMediaRouteProviderService;
+import com.example.androidx.mediarouting.services.SampleMediaRouteProviderService;
 import com.example.androidx.mediarouting.ui.RoutesAdapter;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 
@@ -52,20 +54,20 @@
  * SampleDynamicGroupMediaRouteProviderService}.
  */
 public final class SettingsActivity extends AppCompatActivity {
+    private final ProviderServiceConnection mConnection = new ProviderServiceConnection();
+    private PackageManager mPackageManager;
     private MediaRouter mMediaRouter;
     private RoutesManager mRoutesManager;
     private RoutesAdapter mRoutesAdapter;
-    private SampleDynamicGroupMediaRouteProviderService mService;
-    private ServiceConnection mConnection;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_settings);
 
+        mPackageManager = getPackageManager();
         mMediaRouter = MediaRouter.getInstance(this);
         mRoutesManager = RoutesManager.getInstance(getApplicationContext());
-        mConnection = new ProviderServiceConnection();
 
         setUpViews();
 
@@ -86,7 +88,11 @@
                                 android.R.string.ok,
                                 (dialogInterface, i) -> {
                                     mRoutesManager.deleteRouteWithId(routeId);
-                                    mService.reloadRoutes();
+                                    SampleDynamicGroupMediaRouteProviderService providerService =
+                                            mConnection.mService;
+                                    if (providerService != null) {
+                                        providerService.reloadRoutes();
+                                    }
                                     mRoutesAdapter.updateRoutes(
                                             mRoutesManager.getRouteItems());
                                 })
@@ -111,10 +117,7 @@
     @Override
     protected void onStart() {
         super.onStart();
-        // Bind to SampleDynamicGroupMediaRouteProviderService
-        Intent intent = new Intent(this, SampleDynamicGroupMediaRouteProviderService.class);
-        intent.setAction(SampleDynamicGroupMediaRouteProviderService.ACTION_BIND_LOCAL);
-        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+        bindToDynamicProviderService();
     }
 
     @Override
@@ -125,13 +128,20 @@
 
     @Override
     protected void onStop() {
+        try {
+            unbindService(mConnection);
+        } catch (RuntimeException e) {
+            // This happens when the provider is disabled, but there's no way of preventing this
+            // completely so we just ignore the exception.
+        }
         super.onStop();
-        unbindService(mConnection);
     }
 
     private void setUpViews() {
         setUpDynamicGroupsEnabledSwitch();
         setUpTransferToLocalSwitch();
+        setUpSimpleProviderEnabledSwitch();
+        setUpDynamicProviderEnabledSwitch();
         setUpDialogTypeDropDownList();
         setUpNewRouteButton();
         setupSystemRoutesButton();
@@ -141,9 +151,13 @@
         Switch dynamicRoutingEnabled = findViewById(R.id.dynamic_routing_switch);
         dynamicRoutingEnabled.setChecked(mRoutesManager.isDynamicRoutingEnabled());
         dynamicRoutingEnabled.setOnCheckedChangeListener(
-                (compoundButton, b) -> {
-                    mRoutesManager.setDynamicRoutingEnabled(b);
-                    mService.reloadDynamicRoutesEnabled();
+                (compoundButton, enabled) -> {
+                    mRoutesManager.setDynamicRoutingEnabled(enabled);
+                    SampleDynamicGroupMediaRouteProviderService providerService =
+                            mConnection.mService;
+                    if (providerService != null) {
+                        providerService.reloadDynamicRoutesEnabled();
+                    }
                 });
     }
 
@@ -151,14 +165,56 @@
         Switch showThisPhoneSwitch = findViewById(R.id.show_this_phone_switch);
         showThisPhoneSwitch.setChecked(mMediaRouter.getRouterParams().isTransferToLocalEnabled());
         showThisPhoneSwitch.setOnCheckedChangeListener(
-                (compoundButton, b) -> {
+                (compoundButton, enabled) -> {
                     MediaRouterParams.Builder builder =
                             new MediaRouterParams.Builder(mMediaRouter.getRouterParams());
-                    builder.setTransferToLocalEnabled(b);
+                    builder.setTransferToLocalEnabled(enabled);
                     mMediaRouter.setRouterParams(builder.build());
                 });
     }
 
+    private void setUpSimpleProviderEnabledSwitch() {
+        Switch simpleProviderEnabledSwitch = findViewById(R.id.enable_simple_provider_switch);
+        ComponentName simpleProviderComponentName =
+                new ComponentName(/* context= */ this, SampleMediaRouteProviderService.class);
+        simpleProviderEnabledSwitch.setChecked(
+                mPackageManager.getComponentEnabledSetting(simpleProviderComponentName)
+                        != PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+        simpleProviderEnabledSwitch.setOnCheckedChangeListener(
+                (compoundButton, enabled) -> {
+                    mPackageManager
+                            .setComponentEnabledSetting(
+                                    simpleProviderComponentName,
+                                    enabled
+                                            ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                                    /* flags= */ PackageManager.DONT_KILL_APP);
+                });
+    }
+
+    private void setUpDynamicProviderEnabledSwitch() {
+        Switch dynamicProviderEnabledSwitch = findViewById(R.id.enable_dynamic_provider_switch);
+        ComponentName dynamicProviderComponentName =
+                new ComponentName(
+                        /* context= */ this, SampleDynamicGroupMediaRouteProviderService.class);
+        dynamicProviderEnabledSwitch.setChecked(
+                mPackageManager.getComponentEnabledSetting(dynamicProviderComponentName)
+                        != PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+        dynamicProviderEnabledSwitch.setOnCheckedChangeListener(
+                (compoundButton, enabled) -> {
+                    mPackageManager
+                            .setComponentEnabledSetting(
+                                    dynamicProviderComponentName,
+                                    enabled
+                                            ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                                    /* flags= */ PackageManager.DONT_KILL_APP);
+                    if (enabled) {
+                        bindToDynamicProviderService();
+                    }
+                });
+    }
+
     private void setUpDialogTypeDropDownList() {
         Spinner spinner = findViewById(R.id.dialog_spinner);
         spinner.setOnItemSelectedListener(
@@ -204,13 +260,22 @@
         showSystemRoutesButton.setOnClickListener(v -> SystemRoutingActivity.launch(this));
     }
 
-    private class ProviderServiceConnection implements ServiceConnection {
+    private void bindToDynamicProviderService() {
+        Intent intent = new Intent(this, SampleDynamicGroupMediaRouteProviderService.class);
+        intent.setAction(SampleDynamicGroupMediaRouteProviderService.ACTION_BIND_LOCAL);
+        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    private static class ProviderServiceConnection implements ServiceConnection {
+
+        @Nullable private SampleDynamicGroupMediaRouteProviderService mService;
 
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             SampleDynamicGroupMediaRouteProviderService.LocalBinder binder =
                     (SampleDynamicGroupMediaRouteProviderService.LocalBinder) service;
             mService = binder.getService();
+            mService.reloadRoutes();
         }
 
         @Override
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
index c92ced8..0f331cd 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
@@ -19,71 +19,66 @@
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.example.androidx.mediarouting.activities.systemrouting.source.SystemRoutesSource;
 
 import java.util.Objects;
 
-import javax.annotation.Nullable;
-
-/**
- * An abstract model that holds information about routes from different sources.
- *
- * Can represent media routers' routes, bluetooth routes, or audio routes.
- */
+/** Holds information about a system route. */
 public final class SystemRouteItem implements SystemRoutesAdapterItem {
 
-    @NonNull
-    private final String mId;
+    /**
+     * Describes the support of a route for selection.
+     *
+     * <p>We understand by selection the action that makes a specific route the active route. Note
+     * that this terminology may not match the terminology used by the underlying {@link
+     * SystemRoutesSource}.
+     */
+    public enum SelectionSupportState {
+        /** The underlying route source doesn't support selection. */
+        UNSUPPORTED,
+        /**
+         * The corresponding route is already selected, but can be reselected.
+         *
+         * <p>Selecting an already selected route (reselection) can change the metadata of the route
+         * source. For example, reselecting a MediaRouter2 route can alter the transfer reason.
+         */
+        RESELECTABLE,
+        /** The route is available for selection. */
+        SELECTABLE
+    }
 
-    @NonNull
-    private final String mName;
+    /** The {@link SystemRoutesSource#getSourceId()} of the source that created this item. */
+    @NonNull public final String mSourceId;
 
-    @Nullable
-    private final String mAddress;
+    /** An id that uniquely identifies this route item within the source. */
+    @NonNull public final String mId;
 
-    @Nullable
-    private final String mDescription;
+    @NonNull public final String mName;
+
+    @Nullable public final String mAddress;
+
+    @Nullable public final String mDescription;
+
+    @Nullable public final String mSuitabilityStatus;
+
+    @Nullable public final Boolean mTransferInitiatedBySelf;
+
+    @Nullable public final String mTransferReason;
+
+    @NonNull public final SelectionSupportState mSelectionSupportState;
 
     private SystemRouteItem(@NonNull Builder builder) {
-        Objects.requireNonNull(builder.mId);
-        Objects.requireNonNull(builder.mName);
-
-        mId = builder.mId;
-        mName = builder.mName;
-
+        mSourceId = Objects.requireNonNull(builder.mSourceId);
+        mId = Objects.requireNonNull(builder.mId);
+        mName = Objects.requireNonNull(builder.mName);
         mAddress = builder.mAddress;
         mDescription = builder.mDescription;
-    }
-
-    /**
-     * Returns a unique identifier of a route.
-     */
-    @NonNull
-    public String getId() {
-        return mId;
-    }
-
-    /**
-     * Returns a human-readable name of the route.
-     */
-    @NonNull
-    public String getName() {
-        return mName;
-    }
-
-    /**
-     * Returns address if the route is a Bluetooth route and {@code null} otherwise.
-     */
-    @Nullable
-    public String getAddress() {
-        return mAddress;
-    }
-
-    /**
-     * Returns a route description or {@code null} if empty.
-     */
-    @Nullable
-    public String getDescription() {
-        return mDescription;
+        mSuitabilityStatus = builder.mSuitabilityStatus;
+        mTransferInitiatedBySelf = builder.mTransferInitiatedBySelf;
+        mTransferReason = builder.mTransferReason;
+        mSelectionSupportState = builder.mSelectionSupportState;
     }
 
     @Override
@@ -91,14 +86,29 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         SystemRouteItem that = (SystemRouteItem) o;
-        return mId.equals(that.mId) && mName.equals(that.mName)
-                && Objects.equals(mAddress, that.mAddress) && Objects.equals(
-                mDescription, that.mDescription);
+        return mSourceId.equals(that.mSourceId)
+                && mId.equals(that.mId)
+                && mName.equals(that.mName)
+                && Objects.equals(mAddress, that.mAddress)
+                && Objects.equals(mDescription, that.mDescription)
+                && Objects.equals(mSuitabilityStatus, that.mSuitabilityStatus)
+                && Objects.equals(mTransferInitiatedBySelf, that.mTransferInitiatedBySelf)
+                && Objects.equals(mTransferReason, that.mTransferReason)
+                && mSelectionSupportState.equals(that.mSelectionSupportState);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mName, mAddress, mDescription);
+        return Objects.hash(
+                mSourceId,
+                mId,
+                mName,
+                mAddress,
+                mDescription,
+                mSuitabilityStatus,
+                mTransferInitiatedBySelf,
+                mTransferReason,
+                mSelectionSupportState);
     }
 
     /**
@@ -106,20 +116,26 @@
      */
     public static final class Builder {
 
-        @NonNull
-        private final String mId;
+        @NonNull private String mSourceId;
+        @NonNull private final String mId;
+        @NonNull private String mName;
+        @Nullable private String mAddress;
+        @Nullable private String mDescription;
+        @Nullable private String mSuitabilityStatus;
+        @Nullable private Boolean mTransferInitiatedBySelf;
+        @Nullable private String mTransferReason;
+        @NonNull public SelectionSupportState mSelectionSupportState;
 
-        @NonNull
-        private String mName;
-
-        @Nullable
-        private String mAddress;
-
-        @Nullable
-        private String mDescription;
-
-        public Builder(@NonNull String id) {
+        /**
+         * Creates a builder with the mandatory properties.
+         *
+         * @param sourceId See {@link SystemRouteItem#mSourceId}.
+         * @param id See {@link SystemRouteItem#mId}.
+         */
+        public Builder(@NonNull String sourceId, @NonNull String id) {
+            mSourceId = sourceId;
             mId = id;
+            mSelectionSupportState = SelectionSupportState.UNSUPPORTED;
         }
 
         /**
@@ -154,6 +170,43 @@
         }
 
         /**
+         * Sets a human-readable string describing the transfer suitability of the route, or null if
+         * not applicable.
+         */
+        @NonNull
+        public Builder setSuitabilityStatus(@Nullable String suitabilityStatus) {
+            mSuitabilityStatus = suitabilityStatus;
+            return this;
+        }
+
+        /**
+         * Sets whether the corresponding route's selection is the result of an action of this app,
+         * or null if not applicable.
+         */
+        @NonNull
+        public Builder setTransferInitiatedBySelf(@Nullable Boolean transferInitiatedBySelf) {
+            mTransferInitiatedBySelf = transferInitiatedBySelf;
+            return this;
+        }
+
+        /**
+         * Sets a human-readable string describing the transfer reason, or null if not applicable.
+         */
+        @NonNull
+        public Builder setTransferReason(@Nullable String transferReason) {
+            mTransferReason = transferReason;
+            return this;
+        }
+
+        /** Sets the {@link SelectionSupportState} for the corresponding route. */
+        @NonNull
+        public Builder setSelectionSupportState(
+                @NonNull SelectionSupportState selectionSupportState) {
+            mSelectionSupportState = Objects.requireNonNull(selectionSupportState);
+            return this;
+        }
+
+        /**
          * Builds {@link SystemRouteItem}.
          */
         @NonNull
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
index aba49a1..764c050 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
@@ -16,14 +16,21 @@
 
 package com.example.androidx.mediarouting.activities.systemrouting;
 
+import static com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem.SelectionSupportState.SELECTABLE;
+import static com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem.SelectionSupportState.UNSUPPORTED;
+
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatButton;
 import androidx.appcompat.widget.AppCompatTextView;
+import androidx.core.util.Consumer;
 import androidx.recyclerview.widget.AsyncListDiffer;
 import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.RecyclerView;
@@ -32,17 +39,19 @@
 
 import java.util.List;
 
-/**
- * @link RecyclerView.Adapter} for showing system route sources and the routes discovered by each
- * source.
- */
-class SystemRoutesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+/** {@link RecyclerView.Adapter} for showing system route sources and their corresponding routes. */
+/* package */ class SystemRoutesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
     private static final int VIEW_TYPE_HEADER = 0;
     private static final int VIEW_TYPE_ITEM = 1;
 
     private final AsyncListDiffer<SystemRoutesAdapterItem> mListDiffer =
             new AsyncListDiffer<>(this, new ItemCallback());
+    private final Consumer<SystemRouteItem> mRouteItemClickedListener;
+
+    /* package */ SystemRoutesAdapter(Consumer<SystemRouteItem> routeItemClickListener) {
+        mRouteItemClickedListener = routeItemClickListener;
+    }
 
     public void setItems(@NonNull List<SystemRoutesAdapterItem> newItems) {
         mListDiffer.submitList(newItems);
@@ -108,12 +117,16 @@
         }
     }
 
-    static class ItemViewHolder extends RecyclerView.ViewHolder {
+    private class ItemViewHolder extends RecyclerView.ViewHolder {
 
         private final AppCompatTextView mRouteNameTextView;
         private final AppCompatTextView mRouteIdTextView;
         private final AppCompatTextView mRouteAddressTextView;
         private final AppCompatTextView mRouteDescriptionTextView;
+        private final AppCompatTextView mSuitabilityStatusTextView;
+        private final AppCompatTextView mTransferInitiatedBySelfTextView;
+        private final AppCompatTextView mTransferReasonTextView;
+        private final AppCompatButton mSelectionButton;
 
         ItemViewHolder(@NonNull View itemView) {
             super(itemView);
@@ -122,20 +135,41 @@
             mRouteIdTextView = itemView.findViewById(R.id.route_id);
             mRouteAddressTextView = itemView.findViewById(R.id.route_address);
             mRouteDescriptionTextView = itemView.findViewById(R.id.route_description);
+            mSuitabilityStatusTextView = itemView.findViewById(R.id.route_suitability_status);
+            mTransferInitiatedBySelfTextView =
+                    itemView.findViewById(R.id.route_transfer_initiated_by_self);
+            mTransferReasonTextView = itemView.findViewById(R.id.route_transfer_reason);
+            mSelectionButton = itemView.findViewById(R.id.route_selection_button);
         }
 
         void bind(SystemRouteItem systemRouteItem) {
-            mRouteNameTextView.setText(systemRouteItem.getName());
-            mRouteIdTextView.setText(systemRouteItem.getId());
+            mRouteNameTextView.setText(systemRouteItem.mName);
+            mRouteIdTextView.setText(systemRouteItem.mId);
+            setTextOrHide(mRouteAddressTextView, systemRouteItem.mAddress);
+            setTextOrHide(mRouteDescriptionTextView, systemRouteItem.mDescription);
+            setTextOrHide(mSuitabilityStatusTextView, systemRouteItem.mSuitabilityStatus);
+            String initiatedBySelfText =
+                    systemRouteItem.mTransferInitiatedBySelf != null
+                            ? "self-initiated: " + systemRouteItem.mTransferInitiatedBySelf
+                            : null;
+            setTextOrHide(mTransferInitiatedBySelfTextView, initiatedBySelfText);
+            String transferReasonText =
+                    systemRouteItem.mTransferReason != null
+                            ? "transfer reason: " + systemRouteItem.mTransferReason
+                            : null;
+            setTextOrHide(mTransferReasonTextView, transferReasonText);
 
-            showViewIfNotNull(mRouteAddressTextView, systemRouteItem.getAddress());
-            if (systemRouteItem.getAddress() != null) {
-                mRouteAddressTextView.setText(systemRouteItem.getAddress());
-            }
-
-            showViewIfNotNull(mRouteDescriptionTextView, systemRouteItem.getDescription());
-            if (systemRouteItem.getDescription() != null) {
-                mRouteDescriptionTextView.setText(systemRouteItem.getDescription());
+            if (systemRouteItem.mSelectionSupportState == UNSUPPORTED) {
+                mSelectionButton.setVisibility(View.GONE);
+            } else {
+                mSelectionButton.setVisibility(View.VISIBLE);
+                String text =
+                        systemRouteItem.mSelectionSupportState == SELECTABLE
+                                ? "Select"
+                                : "Reselect";
+                mSelectionButton.setText(text);
+                mSelectionButton.setOnClickListener(
+                        view -> mRouteItemClickedListener.accept(systemRouteItem));
             }
         }
     }
@@ -145,8 +179,7 @@
         public boolean areItemsTheSame(@NonNull SystemRoutesAdapterItem oldItem,
                 @NonNull SystemRoutesAdapterItem newItem) {
             if (oldItem instanceof SystemRouteItem && newItem instanceof SystemRouteItem) {
-                return ((SystemRouteItem) oldItem).getId().equals(
-                        ((SystemRouteItem) newItem).getId());
+                return ((SystemRouteItem) oldItem).mId.equals(((SystemRouteItem) newItem).mId);
             } else if (oldItem instanceof SystemRoutesSourceItem
                     && newItem instanceof SystemRoutesSourceItem) {
                 return oldItem.equals(newItem);
@@ -155,25 +188,21 @@
             }
         }
 
+        @SuppressLint("DiffUtilEquals")
         @Override
-        public boolean areContentsTheSame(@NonNull SystemRoutesAdapterItem oldItem,
+        public boolean areContentsTheSame(
+                @NonNull SystemRoutesAdapterItem oldItem,
                 @NonNull SystemRoutesAdapterItem newItem) {
-            if (oldItem instanceof SystemRouteItem && newItem instanceof SystemRouteItem) {
-                return oldItem.equals(newItem);
-            } else if (oldItem instanceof SystemRoutesSourceItem
-                    && newItem instanceof SystemRoutesSourceItem) {
-                return oldItem.equals(newItem);
-            } else {
-                return false;
-            }
+            return oldItem.equals(newItem);
         }
     }
 
-    private static <T, V extends View> void showViewIfNotNull(@NonNull V view, @Nullable T obj) {
-        if (obj == null) {
+    private static void setTextOrHide(@NonNull TextView view, @Nullable String text) {
+        if (text == null) {
             view.setVisibility(View.GONE);
         } else {
             view.setVisibility(View.VISIBLE);
+            view.setText(text);
         }
     }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java
index 73d57fe..6f18b57 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java
@@ -44,7 +44,9 @@
 import com.example.androidx.mediarouting.activities.systemrouting.source.SystemRoutesSource;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Shows available system routes gathered from different sources.
@@ -54,15 +56,11 @@
     private static final int REQUEST_CODE_BLUETOOTH_CONNECT = 4199;
 
     @NonNull
-    private final SystemRoutesAdapter mSystemRoutesAdapter = new SystemRoutesAdapter();
-    @NonNull
-    private final List<SystemRoutesSource> mSystemRoutesSources = new ArrayList<>();
-    @NonNull
-    private final SystemRoutesSourceCallback mSystemRoutesSourceCallback =
-            new SystemRoutesSourceCallback();
+    private final SystemRoutesAdapter mSystemRoutesAdapter =
+            new SystemRoutesAdapter(this::onRouteItemClicked);
 
-    @NonNull
-    private SwipeRefreshLayout mSwipeRefreshLayout;
+    @NonNull private final Map<String, SystemRoutesSource> mSystemRoutesSources = new HashMap<>();
+    @NonNull private SwipeRefreshLayout mSwipeRefreshLayout;
 
     /**
      * Creates and launches an intent to start current activity.
@@ -95,7 +93,7 @@
 
     @Override
     protected void onDestroy() {
-        for (SystemRoutesSource source: mSystemRoutesSources) {
+        for (SystemRoutesSource source : mSystemRoutesSources.values()) {
             source.stop();
         }
 
@@ -119,7 +117,7 @@
 
     private void refreshSystemRoutesList() {
         List<SystemRoutesAdapterItem> systemRoutesSourceItems = new ArrayList<>();
-        for (SystemRoutesSource source : mSystemRoutesSources) {
+        for (SystemRoutesSource source : mSystemRoutesSources.values()) {
             systemRoutesSourceItems.add(source.getSourceItem());
             systemRoutesSourceItems.addAll(source.fetchSourceRouteItems());
         }
@@ -127,6 +125,20 @@
         mSwipeRefreshLayout.setRefreshing(false);
     }
 
+    private void onRouteItemClicked(SystemRouteItem item) {
+        SystemRoutesSource systemRoutesSource = mSystemRoutesSources.get(item.mSourceId);
+        if (systemRoutesSource == null) {
+            throw new IllegalStateException("Couldn't find source with id: " + item.mSourceId);
+        }
+        if (!systemRoutesSource.select(item)) {
+            Toast.makeText(
+                            /* context= */ this,
+                            "Something went wrong with route selection",
+                            Toast.LENGTH_LONG)
+                    .show();
+        }
+    }
+
     private boolean hasBluetoothPermission() {
         return ContextCompat.checkSelfPermission(/* context= */ this, BLUETOOTH_CONNECT)
                 == PERMISSION_GRANTED
@@ -150,40 +162,28 @@
     }
 
     private void initializeSystemRoutesSources() {
-        mSystemRoutesSources.clear();
-
-        mSystemRoutesSources.add(MediaRouterSystemRoutesSource.create(/* context= */ this));
+        ArrayList<SystemRoutesSource> sources = new ArrayList<>();
+        sources.add(MediaRouterSystemRoutesSource.create(/* context= */ this));
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            mSystemRoutesSources.add(MediaRouter2SystemRoutesSource.create(/* context= */ this));
+            sources.add(MediaRouter2SystemRoutesSource.create(/* context= */ this));
         }
 
-        mSystemRoutesSources.add(AndroidXMediaRouterSystemRoutesSource.create(/* context= */ this));
+        sources.add(AndroidXMediaRouterSystemRoutesSource.create(/* context= */ this));
 
         if (hasBluetoothPermission()) {
-            mSystemRoutesSources.add(
-                    BluetoothManagerSystemRoutesSource.create(/* context= */ this));
+            sources.add(BluetoothManagerSystemRoutesSource.create(/* context= */ this));
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            mSystemRoutesSources.add(AudioManagerSystemRoutesSource.create(/* context= */ this));
+            sources.add(AudioManagerSystemRoutesSource.create(/* context= */ this));
         }
 
-        for (SystemRoutesSource source: mSystemRoutesSources) {
-            source.setOnRoutesChangedListener(mSystemRoutesSourceCallback);
+        mSystemRoutesSources.clear();
+        for (SystemRoutesSource source : sources) {
+            source.setOnRoutesChangedListener(this::refreshSystemRoutesList);
+            mSystemRoutesSources.put(source.getSourceId(), source);
             source.start();
         }
     }
-
-    private class SystemRoutesSourceCallback implements SystemRoutesSource.OnRoutesChangedListener {
-        @Override
-        public void onRouteAdded(@NonNull SystemRouteItem routeItem) {
-            refreshSystemRoutesList();
-        }
-
-        @Override
-        public void onRouteRemoved(@NonNull SystemRouteItem routeItem) {
-            refreshSystemRoutesList();
-        }
-    }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java
index 7e553ed..d7763d0 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java
@@ -36,19 +36,43 @@
     private final MediaRouter mMediaRouter;
 
     @NonNull
-    private final MediaRouter.Callback mMediaRouterCallback = new MediaRouter.Callback() {
-        @Override
-        public void onRouteAdded(@NonNull MediaRouter router,
-                @NonNull MediaRouter.RouteInfo route) {
-            mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(route));
-        }
+    private final MediaRouter.Callback mMediaRouterCallback =
+            new MediaRouter.Callback() {
+                @Override
+                public void onRouteAdded(
+                        @NonNull MediaRouter router, @NonNull MediaRouter.RouteInfo route) {
+                    mOnRoutesChangedListener.run();
+                }
 
-        @Override
-        public void onRouteRemoved(@NonNull MediaRouter router,
-                @NonNull MediaRouter.RouteInfo route) {
-            mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(route));
-        }
-    };
+                @Override
+                public void onRouteRemoved(
+                        @NonNull MediaRouter router, @NonNull MediaRouter.RouteInfo route) {
+                    mOnRoutesChangedListener.run();
+                }
+
+                @Override
+                public void onRouteSelected(
+                        @NonNull MediaRouter router,
+                        @NonNull MediaRouter.RouteInfo selectedRoute,
+                        int reason,
+                        @NonNull MediaRouter.RouteInfo requestedRoute) {
+                    mOnRoutesChangedListener.run();
+                }
+
+                @Override
+                public void onRouteUnselected(
+                        @NonNull MediaRouter router,
+                        @NonNull MediaRouter.RouteInfo route,
+                        int reason) {
+                    mOnRoutesChangedListener.run();
+                }
+
+                @Override
+                public void onRouteChanged(
+                        @NonNull MediaRouter router, @NonNull MediaRouter.RouteInfo route) {
+                    mOnRoutesChangedListener.run();
+                }
+            };
 
     /** Returns a new instance. */
     @NonNull
@@ -98,11 +122,27 @@
         return out;
     }
 
-    @NonNull
-    private static SystemRouteItem createRouteItemFor(@NonNull MediaRouter.RouteInfo routeInfo) {
-        SystemRouteItem.Builder builder = new SystemRouteItem.Builder(routeInfo.getId())
-                .setName(routeInfo.getName());
+    @Override
+    public boolean select(@NonNull SystemRouteItem item) {
+        for (MediaRouter.RouteInfo routeInfo : mMediaRouter.getRoutes()) {
+            if (routeInfo.getId().equals(item.mId)) {
+                routeInfo.select();
+                return true;
+            }
+        }
+        return false;
+    }
 
+    @NonNull
+    private SystemRouteItem createRouteItemFor(@NonNull MediaRouter.RouteInfo routeInfo) {
+        SystemRouteItem.Builder builder =
+                new SystemRouteItem.Builder(getSourceId(), routeInfo.getId())
+                        .setName(routeInfo.getName());
+
+        builder.setSelectionSupportState(
+                routeInfo.isSelected()
+                        ? SystemRouteItem.SelectionSupportState.RESELECTABLE
+                        : SystemRouteItem.SelectionSupportState.SELECTABLE);
         String description = routeInfo.getDescription();
         if (description != null) {
             builder.setDescription(description);
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java
index 096ebfd..9e7b67a 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java
@@ -39,21 +39,18 @@
     private final AudioManager mAudioManager;
 
     @NonNull
-    private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallback() {
-        @Override
-        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
-            for (AudioDeviceInfo audioDeviceInfo: addedDevices) {
-                mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(audioDeviceInfo));
-            }
-        }
+    private final AudioDeviceCallback mAudioDeviceCallback =
+            new AudioDeviceCallback() {
+                @Override
+                public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+                    mOnRoutesChangedListener.run();
+                }
 
-        @Override
-        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
-            for (AudioDeviceInfo audioDeviceInfo: removedDevices) {
-                mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(audioDeviceInfo));
-            }
-        }
-    };
+                @Override
+                public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+                    mOnRoutesChangedListener.run();
+                }
+            };
 
     /** Returns a new instance. */
     @NonNull
@@ -95,11 +92,16 @@
         return out;
     }
 
+    @Override
+    public boolean select(@NonNull SystemRouteItem item) {
+        throw new UnsupportedOperationException();
+    }
+
     @NonNull
-    private static SystemRouteItem createRouteItemFor(@NonNull AudioDeviceInfo audioDeviceInfo) {
-        SystemRouteItem.Builder builder = new SystemRouteItem.Builder(
-                String.valueOf(audioDeviceInfo.getId()))
-                .setName(audioDeviceInfo.getProductName().toString());
+    private SystemRouteItem createRouteItemFor(@NonNull AudioDeviceInfo audioDeviceInfo) {
+        SystemRouteItem.Builder builder =
+                new SystemRouteItem.Builder(getSourceId(), String.valueOf(audioDeviceInfo.getId()))
+                        .setName(audioDeviceInfo.getProductName().toString());
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
             builder.setAddress(Api28Impl.getAddress(audioDeviceInfo));
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java
index 96fab5b..7bc34d7 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java
@@ -23,7 +23,6 @@
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,7 +30,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresPermission;
-import androidx.core.content.IntentCompat;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
@@ -104,10 +102,15 @@
         return out;
     }
 
+    @Override
+    public boolean select(@NonNull SystemRouteItem item) {
+        throw new UnsupportedOperationException();
+    }
+
     @NonNull
     @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    private static SystemRouteItem createRouteItemFor(@NonNull BluetoothDevice device) {
-        return new SystemRouteItem.Builder(/* id= */ device.getAddress())
+    private SystemRouteItem createRouteItemFor(@NonNull BluetoothDevice device) {
+        return new SystemRouteItem.Builder(getSourceId(), /* id= */ device.getAddress())
                 .setName(device.getName())
                 .setAddress(device.getAddress())
                 .build();
@@ -117,27 +120,14 @@
         @Override
         @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
         public void onReceive(Context context, Intent intent) {
-            BluetoothDevice device = IntentCompat.getParcelableExtra(intent,
-                    BluetoothDevice.EXTRA_DEVICE, android.bluetooth.BluetoothDevice.class);
-
             switch (intent.getAction()) {
                 case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
                 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
                 case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
-                    handleConnectionStateChanged(intent, device);
+                    mOnRoutesChangedListener.run();
                     break;
-            }
-        }
-
-        @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-        private void handleConnectionStateChanged(Intent intent,
-                BluetoothDevice device) {
-            int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-            if (state == BluetoothProfile.STATE_CONNECTED) {
-                mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(device));
-            } else if (state == BluetoothProfile.STATE_DISCONNECTING
-                    || state == BluetoothProfile.STATE_DISCONNECTED) {
-                mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(device));
+                default:
+                    // Do nothing.
             }
         }
     }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
index 5785679..1c85f85 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
@@ -16,59 +16,61 @@
 
 package com.example.androidx.mediarouting.activities.systemrouting.source;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2;
 import android.media.RouteDiscoveryPreference;
+import android.media.RoutingSessionInfo;
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
+import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 /** Implements {@link SystemRoutesSource} using {@link MediaRouter2}. */
 @RequiresApi(Build.VERSION_CODES.R)
 public final class MediaRouter2SystemRoutesSource extends SystemRoutesSource {
 
-    @NonNull
-    private Context mContext;
-    @NonNull
-    private MediaRouter2 mMediaRouter2;
+    @NonNull private final Context mContext;
+    @NonNull private final MediaRouter2 mMediaRouter2;
+    @Nullable private final Method mSuitabilityStatusMethod;
+    @Nullable private final Method mWasTransferInitiatedBySelfMethod;
+    @Nullable private final Method mTransferReasonMethod;
+    @NonNull private final ArrayList<SystemRouteItem> mRouteItems = new ArrayList<>();
 
     @NonNull
-    private final Map<String, MediaRoute2Info> mLastKnownRoutes = new HashMap<>();
-    @NonNull
-    private final MediaRouter2.RouteCallback mRouteCallback = new MediaRouter2.RouteCallback() {
-        @Override
-        public void onRoutesUpdated(@NonNull List<MediaRoute2Info> routes) {
-            super.onRoutesUpdated(routes);
-
-            Map<String, MediaRoute2Info> routesLookup = new HashMap<>();
-            for (MediaRoute2Info route: routes) {
-                if (!mLastKnownRoutes.containsKey(route.getId())) {
-                    mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(route));
+    private final MediaRouter2.RouteCallback mRouteCallback =
+            new MediaRouter2.RouteCallback() {
+                @Override
+                public void onRoutesUpdated(@NonNull List<MediaRoute2Info> routes) {
+                    populateRouteItems(routes);
                 }
-                routesLookup.put(route.getId(), route);
-            }
+            };
 
-            for (MediaRoute2Info route: mLastKnownRoutes.values()) {
-                if (!routesLookup.containsKey(route.getId())) {
-                    mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(route));
+    @NonNull
+    private final MediaRouter2.ControllerCallback mControllerCallback =
+            new MediaRouter2.ControllerCallback() {
+                @Override
+                public void onControllerUpdated(
+                        @NonNull MediaRouter2.RoutingController unusedController) {
+                    populateRouteItems(mMediaRouter2.getRoutes());
                 }
-            }
-
-            mLastKnownRoutes.clear();
-            mLastKnownRoutes.putAll(routesLookup);
-        }
-    };
+            };
 
     /** Returns a new instance. */
     @NonNull
@@ -81,22 +83,45 @@
             @NonNull MediaRouter2 mediaRouter2) {
         mContext = context;
         mMediaRouter2 = mediaRouter2;
+
+        Method suitabilityStatusMethod = null;
+        Method wasTransferInitiatedBySelfMethod = null;
+        Method transferReasonMethod = null;
+        // TODO: b/336510942 - Remove reflection once these APIs are available in
+        // androidx-platform-dev.
+        try {
+            suitabilityStatusMethod =
+                    MediaRoute2Info.class.getDeclaredMethod("getSuitabilityStatus");
+            wasTransferInitiatedBySelfMethod =
+                    MediaRouter2.RoutingController.class.getDeclaredMethod(
+                            "wasTransferInitiatedBySelf");
+            transferReasonMethod = RoutingSessionInfo.class.getDeclaredMethod("getTransferReason");
+        } catch (NoSuchMethodException | IllegalAccessError e) {
+        }
+        mSuitabilityStatusMethod = suitabilityStatusMethod;
+        mWasTransferInitiatedBySelfMethod = wasTransferInitiatedBySelfMethod;
+        mTransferReasonMethod = transferReasonMethod;
     }
 
     @Override
     public void start() {
         RouteDiscoveryPreference routeDiscoveryPreference =
                 new RouteDiscoveryPreference.Builder(
-                        /* preferredFeatures= */ Collections.emptyList(),
-                        /* activeScan= */ false)
+                                /* preferredFeatures= */ Arrays.asList(
+                                        MediaRoute2Info.FEATURE_LIVE_AUDIO,
+                                        MediaRoute2Info.FEATURE_LIVE_VIDEO),
+                                /* activeScan= */ false)
                         .build();
 
-        mMediaRouter2.registerRouteCallback(mContext.getMainExecutor(),
-                mRouteCallback, routeDiscoveryPreference);
+        Executor mainExecutor = mContext.getMainExecutor();
+        mMediaRouter2.registerRouteCallback(mainExecutor, mRouteCallback, routeDiscoveryPreference);
+        mMediaRouter2.registerControllerCallback(mainExecutor, mControllerCallback);
+        populateRouteItems(mMediaRouter2.getRoutes());
     }
 
     @Override
     public void stop() {
+        mMediaRouter2.unregisterControllerCallback(mControllerCallback);
         mMediaRouter2.unregisterRouteCallback(mRouteCallback);
     }
 
@@ -109,28 +134,141 @@
     @NonNull
     @Override
     public List<SystemRouteItem> fetchSourceRouteItems() {
-        List<SystemRouteItem> out = new ArrayList<>();
+        return mRouteItems;
+    }
 
-        for (MediaRoute2Info routeInfo : mMediaRouter2.getRoutes()) {
-            if (!routeInfo.isSystemRoute()) {
-                continue;
-            }
-
-            if (!mLastKnownRoutes.containsKey(routeInfo.getId())) {
-                mLastKnownRoutes.put(routeInfo.getId(), routeInfo);
-            }
-
-            out.add(createRouteItemFor(routeInfo));
+    @Override
+    public boolean select(@NonNull SystemRouteItem item) {
+        Optional<MediaRoute2Info> route =
+                mMediaRouter2.getRoutes().stream()
+                        .filter(it -> it.getId().equals(item.mId))
+                        .findFirst();
+        if (!route.isPresent()) {
+            return false;
+        } else {
+            mMediaRouter2.transferTo(route.get());
+            return true;
         }
+    }
 
-        return out;
+    // BanUncheckedReflection: See b/336510942 for details on why reflection is needed.
+    // NewApi: We don't need to check the API level because the transfer reason method is only
+    // available on API 35, which is greater than API 34, where getRoutingSessionInfo was added.
+    @SuppressLint({"BanUncheckedReflection", "NewApi"})
+    private void populateRouteItems(List<MediaRoute2Info> routes) {
+        MediaRouter2.RoutingController systemController = mMediaRouter2.getSystemController();
+        Set<String> selectedRoutesIds =
+                systemController.getSelectedRoutes().stream()
+                        .map(MediaRoute2Info::getId)
+                        .collect(Collectors.toSet());
+        Boolean selectionInitiatedBySelf = null;
+        Integer sessionTransferReason = null;
+        try {
+            if (mSuitabilityStatusMethod != null) {
+                selectionInitiatedBySelf =
+                        (Boolean) mWasTransferInitiatedBySelfMethod.invoke(systemController);
+            }
+            if (mTransferReasonMethod != null) {
+                sessionTransferReason =
+                        (Integer)
+                                mTransferReasonMethod.invoke(
+                                        Api34Impl.getRoutingSessionInfo(systemController));
+            }
+        } catch (IllegalAccessException | InvocationTargetException e) {
+        }
+        // We need to filter out non-system routes, which might be reported as a result of other
+        // callbacks with non-system route features being registered in the router.
+        List<MediaRoute2Info> systemRoutes =
+                routes.stream().filter(MediaRoute2Info::isSystemRoute).collect(Collectors.toList());
+
+        mRouteItems.clear();
+        for (MediaRoute2Info route : systemRoutes) {
+            boolean isSelectedRoute = selectedRoutesIds.contains(route.getId());
+            Boolean wasTransferredBySelf = isSelectedRoute ? selectionInitiatedBySelf : null;
+            Integer routeTransferReason = isSelectedRoute ? sessionTransferReason : null;
+            mRouteItems.add(
+                    createRouteItemFor(
+                            route, isSelectedRoute, wasTransferredBySelf, routeTransferReason));
+        }
+        mOnRoutesChangedListener.run();
     }
 
     @NonNull
-    private static SystemRouteItem createRouteItemFor(@NonNull MediaRoute2Info routeInfo) {
-        return new SystemRouteItem.Builder(routeInfo.getId())
-                .setName(String.valueOf(routeInfo.getName()))
-                .setDescription(String.valueOf(routeInfo.getDescription()))
-                .build();
+    private SystemRouteItem createRouteItemFor(
+            @NonNull MediaRoute2Info routeInfo,
+            boolean isSelectedRoute,
+            @Nullable Boolean wasTransferredBySelf,
+            @Nullable Integer transferReason) {
+        SystemRouteItem.Builder builder =
+                new SystemRouteItem.Builder(getSourceId(), routeInfo.getId())
+                        .setName(String.valueOf(routeInfo.getName()))
+                        .setSelectionSupportState(
+                                isSelectedRoute
+                                        ? SystemRouteItem.SelectionSupportState.RESELECTABLE
+                                        : SystemRouteItem.SelectionSupportState.SELECTABLE)
+                        .setDescription(String.valueOf(routeInfo.getDescription()))
+                        .setTransferInitiatedBySelf(wasTransferredBySelf)
+                        .setTransferReason(getHumanReadableTransferReason(transferReason));
+        try {
+            if (mSuitabilityStatusMethod != null) {
+                // See b/336510942 for details on why reflection is needed.
+                @SuppressLint("BanUncheckedReflection")
+                int status = (Integer) mSuitabilityStatusMethod.invoke(routeInfo);
+                builder.setSuitabilityStatus(getHumanReadableSuitabilityStatus(status));
+                // TODO: b/319645714 - Populate wasTransferInitiatedBySelf. For that we need to
+                // change the implementation of this class to use the routing controller instead
+                // of a route callback.
+            }
+        } catch (IllegalAccessException | InvocationTargetException e) {
+        }
+        return builder.build();
+    }
+
+    @NonNull
+    private String getHumanReadableSuitabilityStatus(@Nullable Integer status) {
+        if (status == null) {
+            // The route is not selected, or this Android version doesn't support suitability
+            // status.
+            return null;
+        }
+        switch (status) {
+            case 0:
+                return "SUITABLE_FOR_DEFAULT_TRANSFER";
+            case 1:
+                return "SUITABLE_FOR_MANUAL_TRANSFER";
+            case 2:
+                return "NOT_SUITABLE_FOR_TRANSFER";
+            default:
+                return "UNKNOWN(" + status + ")";
+        }
+    }
+
+    @NonNull
+    private String getHumanReadableTransferReason(@Nullable Integer transferReason) {
+        if (transferReason == null) {
+            // The route is not selected, or this Android version doesn't support transfer reason.
+            return null;
+        }
+        switch (transferReason) {
+            case 0:
+                return "FALLBACK";
+            case 1:
+                return "SYSTEM_REQUEST";
+            case 2:
+                return "APP";
+            default:
+                return "UNKNOWN(" + transferReason + ")";
+        }
+    }
+
+    @RequiresApi(34)
+    private static final class Api34Impl {
+        private Api34Impl() {}
+
+        @DoNotInline
+        static RoutingSessionInfo getRoutingSessionInfo(
+                MediaRouter2.RoutingController routingController) {
+            return routingController.getRoutingSessionInfo();
+        }
     }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java
index 438ccfc..21d112f 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.media.MediaRouter;
+import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
 
@@ -34,19 +35,35 @@
     private final MediaRouter mMediaRouter;
 
     @NonNull
-    private final MediaRouter.Callback mCallback = new MediaRouter.SimpleCallback() {
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            super.onRouteAdded(router, info);
-            mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(info));
-        }
+    private final MediaRouter.Callback mCallback =
+            new MediaRouter.SimpleCallback() {
+                @Override
+                public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
+                    mOnRoutesChangedListener.run();
+                }
 
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            super.onRouteRemoved(router, info);
-            mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(info));
-        }
-    };
+                @Override
+                public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
+                    mOnRoutesChangedListener.run();
+                }
+
+                @Override
+                public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
+                    mOnRoutesChangedListener.run();
+                }
+
+                @Override
+                public void onRouteUnselected(
+                        MediaRouter router, int type, MediaRouter.RouteInfo info) {
+                    mOnRoutesChangedListener.run();
+                }
+
+                @Override
+                public void onRouteSelected(
+                        MediaRouter router, int type, MediaRouter.RouteInfo info) {
+                    mOnRoutesChangedListener.run();
+                }
+            };
 
     /** Returns a new instance. */
     @NonNull
@@ -83,23 +100,42 @@
 
         List<SystemRouteItem> out = new ArrayList<>();
 
+        MediaRouter.RouteInfo selectedRoute =
+                mMediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
         for (int i = 0; i < count; i++) {
             MediaRouter.RouteInfo info = mMediaRouter.getRouteAt(i);
             if (info.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL) {
                 // We are only interested in system routes.
-                out.add(createRouteItemFor(info));
+                out.add(createRouteItemFor(info, /* isSelected= */ selectedRoute == info));
             }
         }
 
         return out;
     }
 
-    @NonNull
-    private static SystemRouteItem createRouteItemFor(@NonNull MediaRouter.RouteInfo routeInfo) {
-        SystemRouteItem.Builder builder =
-                new SystemRouteItem.Builder(/* id= */ routeInfo.getName().toString())
-                        .setName(routeInfo.getName().toString());
+    @Override
+    public boolean select(@NonNull SystemRouteItem item) {
+        int routeCount = mMediaRouter.getRouteCount();
+        for (int i = 0; i < routeCount; i++) {
+            MediaRouter.RouteInfo route = mMediaRouter.getRouteAt(i);
+            if (TextUtils.equals(route.getName().toString(), item.mId)) {
+                mMediaRouter.selectRoute(MediaRouter.ROUTE_TYPE_LIVE_AUDIO, route);
+                return true;
+            }
+        }
+        return false;
+    }
 
+    @NonNull
+    private SystemRouteItem createRouteItemFor(
+            @NonNull MediaRouter.RouteInfo routeInfo, boolean isSelected) {
+        SystemRouteItem.Builder builder =
+                new SystemRouteItem.Builder(getSourceId(), /* id= */ routeInfo.getName().toString())
+                        .setName(routeInfo.getName().toString());
+        builder.setSelectionSupportState(
+                isSelected
+                        ? SystemRouteItem.SelectionSupportState.RESELECTABLE
+                        : SystemRouteItem.SelectionSupportState.SELECTABLE);
         CharSequence description = routeInfo.getDescription();
         if (description != null) {
             builder.setDescription(String.valueOf(description));
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java
index f5a32db..bad6317 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java
@@ -17,7 +17,6 @@
 package com.example.androidx.mediarouting.activities.systemrouting.source;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
@@ -29,23 +28,11 @@
  */
 public abstract class SystemRoutesSource {
 
-    private static final NoOpOnRoutesChangedListener NO_OP_ON_ROUTES_CHANGED_LISTENER =
-            new NoOpOnRoutesChangedListener();
+    @NonNull protected Runnable mOnRoutesChangedListener = () -> {};
 
-    @NonNull
-    protected OnRoutesChangedListener mOnRoutesChangedListener = NO_OP_ON_ROUTES_CHANGED_LISTENER;
-
-    /**
-     * Sets {@link OnRoutesChangedListener} and subscribes to the source updates.
-     * To unsubscribe from the routes update pass {@code null} instead of the listener.
-     */
-    public void setOnRoutesChangedListener(
-            @Nullable OnRoutesChangedListener onRoutesChangedListener) {
-        if (onRoutesChangedListener != null) {
-            mOnRoutesChangedListener = onRoutesChangedListener;
-        } else {
-            mOnRoutesChangedListener = NO_OP_ON_ROUTES_CHANGED_LISTENER;
-        }
+    /** Sets a {@link Runnable} to invoke whenever routes change. */
+    public void setOnRoutesChangedListener(@NonNull Runnable onRoutesChangedListener) {
+        mOnRoutesChangedListener = onRoutesChangedListener;
     }
 
     /**
@@ -63,6 +50,12 @@
         // Empty on purpose.
     }
 
+    /** Returns a string that uniquely identifies this source. */
+    @NonNull
+    public final String getSourceId() {
+        return getClass().getSimpleName();
+    }
+
     /**
      * Gets a source item containing source type.
      */
@@ -76,39 +69,11 @@
     public abstract List<SystemRouteItem> fetchSourceRouteItems();
 
     /**
-     * An interface for listening to routes changes: whether the route has been added or removed
-     * from the source.
+     * Selects the route that corresponds to the given item.
+     *
+     * @param item An item with {@link SystemRouteItem#mSelectionSupportState} {@link
+     *     SystemRouteItem.SelectionSupportState#SELECTABLE}.
+     * @return Whether the selection was successful.
      */
-    public interface OnRoutesChangedListener {
-
-        /**
-         * Called when a route has been added to the source's routes list.
-         *
-         * @param routeItem a newly added route.
-         */
-        void onRouteAdded(@NonNull SystemRouteItem routeItem);
-
-        /**
-         * Called when a route has been removed from the source's routes list.
-         *
-         * @param routeItem a recently removed route.
-         */
-        void onRouteRemoved(@NonNull SystemRouteItem routeItem);
-    }
-
-    /**
-     * Default no-op implementation of {@link OnRoutesChangedListener}.
-     * Used as a fallback implement when there is no listener.
-     */
-    private static final class NoOpOnRoutesChangedListener implements OnRoutesChangedListener {
-        @Override
-        public void onRouteAdded(@NonNull SystemRouteItem routeItem) {
-            // Empty on purpose.
-        }
-
-        @Override
-        public void onRouteRemoved(@NonNull SystemRouteItem routeItem) {
-            // Empty on purpose.
-        }
-    }
+    public abstract boolean select(@NonNull SystemRouteItem item);
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java
index 47ffed2..a597796 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/providers/SampleDynamicGroupMediaRouteProvider.java
@@ -304,7 +304,16 @@
             for (String memberRouteId : mMemberRouteIds) {
                 groupDescriptorBuilder.addGroupMemberId(memberRouteId);
             }
-
+            if (!mMemberRouteIds.isEmpty()) {
+                DynamicRouteDescriptor firstDynamicRouteDescriptor =
+                        mDynamicRouteDescriptors.get(mMemberRouteIds.get(0));
+                if (firstDynamicRouteDescriptor != null) {
+                    String name = firstDynamicRouteDescriptor.getRouteDescriptor().getName();
+                    int sizeMinusOne = mMemberRouteIds.size() - 1;
+                    String nameSuffix = sizeMinusOne == 0 ? "" : (" + " + sizeMinusOne);
+                    groupDescriptorBuilder.setName(name + nameSuffix);
+                }
+            }
             mGroupDescriptor = groupDescriptorBuilder.build();
         }
 
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/services/SampleDynamicGroupMediaRouteProviderService.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/services/SampleDynamicGroupMediaRouteProviderService.java
index 658c256..abbfbf2 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/services/SampleDynamicGroupMediaRouteProviderService.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/services/SampleDynamicGroupMediaRouteProviderService.java
@@ -58,12 +58,16 @@
 
     /** Reload all routes provided by this service. */
     public void reloadRoutes() {
-        mDynamicGroupMediaRouteProvider.reloadRoutes();
+        if (mDynamicGroupMediaRouteProvider != null) {
+            mDynamicGroupMediaRouteProvider.reloadRoutes();
+        }
     }
 
     /** Reload the flag for isDynamicRouteEnabled. */
     public void reloadDynamicRoutesEnabled() {
-        mDynamicGroupMediaRouteProvider.reloadDynamicRoutesEnabled();
+        if (mDynamicGroupMediaRouteProvider != null) {
+            mDynamicGroupMediaRouteProvider.reloadDynamicRoutesEnabled();
+        }
     }
 
     /**
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml b/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml
index 2572fc6..50418cb 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/activity_settings.xml
@@ -91,6 +91,56 @@
             android:layout_margin="12dp"
             android:padding="4dp">
 
+            <Switch
+                android:id="@+id/enable_simple_provider_switch"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
+                android:layout_centerVertical="true" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_centerVertical="true"
+                android:gravity="center"
+                android:text="Enable simple route provider" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            android:layout_margin="12dp"
+            android:padding="4dp">
+
+            <Switch
+                android:id="@+id/enable_dynamic_provider_switch"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
+                android:layout_centerVertical="true" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_centerVertical="true"
+                android:gravity="center"
+                android:text="Enable dynamic route provider" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            android:layout_margin="12dp"
+            android:padding="4dp">
+
             <Spinner
                 android:id="@+id/dialog_spinner"
                 android:layout_width="wrap_content"
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
index 5d54748..8b3ac4e 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
@@ -77,6 +77,49 @@
                 android:textSize="14sp"
                 tools:text="This is a description of an amazing system route." />
 
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/route_suitability_status"
+                android:layout_marginTop="8dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/route_description"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:textSize="14sp"
+                tools:text="Suitability status." />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/route_transfer_initiated_by_self"
+                android:layout_marginTop="8dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/route_suitability_status"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:textSize="14sp"
+                tools:text="Transfer initiated by self." />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/route_transfer_reason"
+                android:layout_marginTop="8dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/route_transfer_initiated_by_self"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:textSize="14sp"
+                tools:text="Transfer reason." />
+
+            <androidx.appcompat.widget.AppCompatButton
+                android:id="@+id/route_selection_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/route_transfer_reason"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:textSize="14sp"
+                tools:text="Selection button" />
+
         </RelativeLayout>
 
     </androidx.cardview.widget.CardView>
diff --git a/samples/MediaRoutingDemo/src/main/res/menu/route_button_menu.xml b/samples/MediaRoutingDemo/src/main/res/menu/route_button_menu.xml
new file mode 100644
index 0000000..d097803
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/menu/route_button_menu.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:id="@+id/route_button_menu_item"
+        android:title="@string/media_route_menu_title"
+        app:showAsAction="always"
+        app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
+</menu>
diff --git a/samples/Support4Demos/build.gradle b/samples/Support4Demos/build.gradle
index 7f821c2..061c521 100644
--- a/samples/Support4Demos/build.gradle
+++ b/samples/Support4Demos/build.gradle
@@ -30,5 +30,6 @@
 }
 
 android {
+    compileSdkVersion 35
     namespace "com.example.android.supportv4"
 }
diff --git a/samples/Support4Demos/lint-baseline.xml b/samples/Support4Demos/lint-baseline.xml
index 3dba431..1375f16 100644
--- a/samples/Support4Demos/lint-baseline.xml
+++ b/samples/Support4Demos/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="MissingPermission"
@@ -110,33 +110,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class com.example.android.supportv4.media.MediaNotificationManager is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                notificationManager.createNotificationChannel("
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/supportv4/media/MediaNotificationManager.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class com.example.android.supportv4.media.MediaNotificationManager is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        new NotificationChannel(NOTIFICATION_CHANNEL_ID, TAG,"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/supportv4/media/MediaNotificationManager.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class com.example.android.supportv4.media.MediaNotificationManager is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/supportv4/media/MediaNotificationManager.java"/>
-    </issue>
-
-    <issue
         id="ForegroundServiceType"
         message="To call `Service.startForeground()`, the `&lt;service>` element of manifest file must have the `foregroundServiceType` attribute specified"
         errorLine1="                mService.startForeground(NOTIFICATION_ID, notification);"
diff --git a/samples/SupportAnimationDemos/build.gradle b/samples/SupportAnimationDemos/build.gradle
index 5143dde..4288948 100644
--- a/samples/SupportAnimationDemos/build.gradle
+++ b/samples/SupportAnimationDemos/build.gradle
@@ -15,5 +15,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "com.example.android.support.animation"
 }
diff --git a/samples/SupportContentDemos/build.gradle b/samples/SupportContentDemos/build.gradle
index d2ab9a4..c543967 100644
--- a/samples/SupportContentDemos/build.gradle
+++ b/samples/SupportContentDemos/build.gradle
@@ -31,5 +31,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "com.example.android.support.content.demos"
 }
diff --git a/samples/SupportEmojiDemos/build.gradle b/samples/SupportEmojiDemos/build.gradle
index 94e9a78..21d80f8 100644
--- a/samples/SupportEmojiDemos/build.gradle
+++ b/samples/SupportEmojiDemos/build.gradle
@@ -32,6 +32,7 @@
 }
 
 android {
+    compileSdk 35
     sourceSets {
         main.assets.srcDirs = [new File(fontDir, "supported-emojis").getAbsolutePath()]
     }
diff --git a/samples/SupportLeanbackDemos/build.gradle b/samples/SupportLeanbackDemos/build.gradle
index b3aa7b7..e9845a8 100644
--- a/samples/SupportLeanbackDemos/build.gradle
+++ b/samples/SupportLeanbackDemos/build.gradle
@@ -25,5 +25,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "com.example.android.leanback"
 }
diff --git a/samples/SupportLeanbackDemos/lint-baseline.xml b/samples/SupportLeanbackDemos/lint-baseline.xml
index 4921a8f..4a5c839 100644
--- a/samples/SupportLeanbackDemos/lint-baseline.xml
+++ b/samples/SupportLeanbackDemos/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="MissingSuperCall"
@@ -65,195 +65,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                        ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                                                      ~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (mPlayer.getPlaybackParams().getSpeed() != NORMAL_SPEED) {"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            if (mPlayer.getPlaybackParams().getSpeed() != NORMAL_SPEED) {"
-        errorLine2="                                            ~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                                              ~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                                              ~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                                              ~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 23; however, the containing class com.example.android.leanback.MediaSessionService is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        mPlayer.setPlaybackParams(mPlayer.getPlaybackParams().setSpeed("
-        errorLine2="                                                              ~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/MediaSessionService.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        getActivity().enterPictureInPictureMode();"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/PlaybackFragment.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        getActivity().enterPictureInPictureMode();"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/PlaybackSupportFragment.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        getActivity().enterPictureInPictureMode();"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/PlaybackTransportControlFragment.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class null is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                        getActivity().enterPictureInPictureMode();"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/com/example/android/leanback/PlaybackTransportControlSupportFragment.java"/>
-    </issue>
-
-    <issue
         id="LongLogTag"
         message="The logging tag can be at most 23 characters, was 32 (leanback.BrowseAnimationFragment)"
         errorLine1="        Log.i(TAG, &quot;onCreate&quot;);"
diff --git a/samples/SupportPreferenceDemos/build.gradle b/samples/SupportPreferenceDemos/build.gradle
index 73d482c..02d78ee 100644
--- a/samples/SupportPreferenceDemos/build.gradle
+++ b/samples/SupportPreferenceDemos/build.gradle
@@ -26,5 +26,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "com.example.androidx.preference"
-}
\ No newline at end of file
+}
diff --git a/samples/SupportRemoteCallbackDemos/build.gradle b/samples/SupportRemoteCallbackDemos/build.gradle
index 5864d41..7876ec4 100644
--- a/samples/SupportRemoteCallbackDemos/build.gradle
+++ b/samples/SupportRemoteCallbackDemos/build.gradle
@@ -27,6 +27,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
     }
     namespace "com.example.androidx.remotecallback.demos"
diff --git a/samples/SupportSliceDemos/build.gradle b/samples/SupportSliceDemos/build.gradle
index 007adcb..89d6061 100644
--- a/samples/SupportSliceDemos/build.gradle
+++ b/samples/SupportSliceDemos/build.gradle
@@ -32,6 +32,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
     }
     namespace "com.example.androidx.slice.demos"
diff --git a/samples/SupportTransitionDemos/build.gradle b/samples/SupportTransitionDemos/build.gradle
index 6119a42..eb68924 100644
--- a/samples/SupportTransitionDemos/build.gradle
+++ b/samples/SupportTransitionDemos/build.gradle
@@ -21,6 +21,7 @@
     aaptOptions {
         additionalParameters "--no-version-transitions"
     }
+    compileSdk 35
     namespace "com.example.android.support.transition"
 }
 
diff --git a/samples/SupportWearDemos/build.gradle b/samples/SupportWearDemos/build.gradle
index 252fc05..3ae3154 100644
--- a/samples/SupportWearDemos/build.gradle
+++ b/samples/SupportWearDemos/build.gradle
@@ -27,6 +27,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 24
     }
diff --git a/savedstate/savedstate-ktx/build.gradle b/savedstate/savedstate-ktx/build.gradle
index b54f221..926f5b5 100644
--- a/savedstate/savedstate-ktx/build.gradle
+++ b/savedstate/savedstate-ktx/build.gradle
@@ -44,7 +44,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2020"
     description = "Kotlin extensions for 'savedstate' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/savedstate/savedstate/build.gradle b/savedstate/savedstate/build.gradle
index ae77ad8..7df64b7 100644
--- a/savedstate/savedstate/build.gradle
+++ b/savedstate/savedstate/build.gradle
@@ -41,7 +41,7 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                api("androidx.annotation:annotation:1.1.0")
+                api("androidx.annotation:annotation:1.8.1")
                 implementation("androidx.arch.core:core-common:2.2.0")
                 implementation("androidx.lifecycle:lifecycle-common:2.6.1")
                 api(libs.kotlinStdlib)
@@ -78,6 +78,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android Lifecycle Saved State"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/security/security-app-authenticator-testing/build.gradle b/security/security-app-authenticator-testing/build.gradle
index ea467d0..2692fac 100644
--- a/security/security-app-authenticator-testing/build.gradle
+++ b/security/security-app-authenticator-testing/build.gradle
@@ -50,5 +50,4 @@
     mavenVersion = LibraryVersions.SECURITY_APP_AUTHENTICATOR_TESTING
     inceptionYear = "2021"
     description = "This library provides a configurable AppAuthenticator that can be used during testing"
-    metalavaK2UastEnabled = true
 }
diff --git a/security/security-app-authenticator/build.gradle b/security/security-app-authenticator/build.gradle
index 5d02e7c..e5955a0 100644
--- a/security/security-app-authenticator/build.gradle
+++ b/security/security-app-authenticator/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation(libs.autoValueAnnotations)
     annotationProcessor(libs.autoValue)
     implementation("androidx.collection:collection:1.1.0")
@@ -60,5 +60,4 @@
     mavenVersion = LibraryVersions.SECURITY_APP_AUTHENTICATOR
     inceptionYear = "2020"
     description = "Verify app packages for proper app to app authentication."
-    metalavaK2UastEnabled = true
 }
diff --git a/security/security-biometric/build.gradle b/security/security-biometric/build.gradle
index accf6de..fc1c690 100644
--- a/security/security-biometric/build.gradle
+++ b/security/security-biometric/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("com.google.crypto.tink:tink-android:1.3.0")
 
@@ -55,6 +55,5 @@
     mavenVersion = LibraryVersions.SECURITY_BIOMETRIC
     inceptionYear = "2020"
     description = "AndroidX Security Biometric"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/security/security-crypto-ktx/build.gradle b/security/security-crypto-ktx/build.gradle
index 1efc02b..fa3b8bf 100644
--- a/security/security-crypto-ktx/build.gradle
+++ b/security/security-crypto-ktx/build.gradle
@@ -51,5 +51,4 @@
     mavenVersion = LibraryVersions.SECURITY
     inceptionYear = "2020"
     description = "Kotlin Extensions for the androidx.security:Security-crypto artifact"
-    metalavaK2UastEnabled = true
 }
diff --git a/security/security-crypto/build.gradle b/security/security-crypto/build.gradle
index 720c1da..7c055c2 100644
--- a/security/security-crypto/build.gradle
+++ b/security/security-crypto/build.gradle
@@ -30,11 +30,9 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("com.google.crypto.tink:tink-android:1.8.0")
-
-    implementation("androidx.annotation:annotation:1.2.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(libs.testExtJunit)
@@ -55,6 +53,5 @@
     mavenVersion = LibraryVersions.SECURITY
     inceptionYear = "2019"
     description = "AndroidX Security"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/security/security-identity-credential/build.gradle b/security/security-identity-credential/build.gradle
index 55255d9..180b1cf 100644
--- a/security/security-identity-credential/build.gradle
+++ b/security/security-identity-credential/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.biometric:biometric:1.1.0")
     implementation("co.nstant.in:cbor:0.8")
     implementation("org.bouncycastle:bcprov-jdk15on:1.65")
@@ -58,6 +58,5 @@
     mavenVersion = LibraryVersions.SECURITY_IDENTITY_CREDENTIAL
     inceptionYear = "2019"
     description = "AndroidX Security"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/security/security-state/api/current.txt b/security/security-state/api/current.txt
index 26fd3fc..4424adf 100644
--- a/security/security-state/api/current.txt
+++ b/security/security-state/api/current.txt
@@ -2,18 +2,24 @@
 package androidx.security.state {
 
   public class SecurityPatchState {
+    ctor public SecurityPatchState(android.content.Context context);
+    ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules);
     ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules, optional androidx.security.state.SecurityStateManager? customSecurityStateManager);
     method public final boolean areCvesPatched(java.util.List<java.lang.String> cveList);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component, optional java.util.List<? extends android.net.Uri>? providers);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getDeviceSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component);
-    method public java.util.Map<androidx.security.state.SecurityPatchState.Severity,java.util.List<java.lang.String>> getPatchedCves(androidx.security.state.SecurityPatchState.Component component, androidx.security.state.SecurityPatchState.SecurityPatchLevel spl);
-    method public java.util.List<androidx.security.state.SecurityPatchState.SecurityPatchLevel> getPublishedSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getSecurityPatchLevelString(androidx.security.state.SecurityPatchState.Component component, String securityPatchLevel);
-    method public final android.net.Uri getVulnerabilityReportUrl(String serverUrl);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(String component);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(String component, optional java.util.List<? extends android.net.Uri> providers);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getDeviceSecurityPatchLevel(String component);
+    method public java.util.Map<androidx.security.state.SecurityPatchState.Severity,java.util.Set<java.lang.String>> getPatchedCves(String component, androidx.security.state.SecurityPatchState.SecurityPatchLevel spl);
+    method public java.util.List<androidx.security.state.SecurityPatchState.SecurityPatchLevel> getPublishedSecurityPatchLevel(String component);
+    method @RequiresApi(26) public final android.net.Uri getVulnerabilityReportUrl(android.net.Uri serverUrl);
     method public final boolean isDeviceFullyUpdated();
     method public java.util.List<androidx.security.state.UpdateInfo> listAvailableUpdates(optional java.util.List<? extends android.net.Uri>? providers);
     method public final void loadVulnerabilityReport(String jsonString);
+    field public static final String COMPONENT_KERNEL = "KERNEL";
+    field public static final String COMPONENT_SYSTEM = "SYSTEM";
+    field public static final String COMPONENT_SYSTEM_MODULES = "SYSTEM_MODULES";
+    field public static final String COMPONENT_VENDOR = "VENDOR";
     field public static final androidx.security.state.SecurityPatchState.Companion Companion;
     field public static final java.util.List<java.lang.String> DEFAULT_SYSTEM_MODULES;
     field public static final String DEFAULT_VULNERABILITY_REPORTS_URL = "https://storage.googleapis.com/osv-android-api";
@@ -22,14 +28,6 @@
   public static final class SecurityPatchState.Companion {
   }
 
-  public enum SecurityPatchState.Component {
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component KERNEL;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component SYSTEM;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component SYSTEM_MODULES;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component VENDOR;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component WEBVIEW;
-  }
-
   public static final class SecurityPatchState.DateBasedSecurityPatchLevel extends androidx.security.state.SecurityPatchState.SecurityPatchLevel {
     ctor public SecurityPatchState.DateBasedSecurityPatchLevel(int year, int month, int day);
     method public int compareTo(androidx.security.state.SecurityPatchState.SecurityPatchLevel other);
@@ -79,12 +77,10 @@
   public class SecurityStateManager {
     ctor public SecurityStateManager(android.content.Context context);
     method public android.os.Bundle getGlobalSecurityState(optional String? moduleMetadataProvider);
-    field public static final String ANDROID_MODULE_METADATA_PROVIDER = "com.android.modulemetadata";
     field public static final androidx.security.state.SecurityStateManager.Companion Companion;
     field public static final String KEY_KERNEL_VERSION = "kernel_version";
     field public static final String KEY_SYSTEM_SPL = "system_spl";
     field public static final String KEY_VENDOR_SPL = "vendor_spl";
-    field public static final String VENDOR_SECURITY_PATCH_PROPERTY_KEY = "ro.vendor.build.security_patch";
   }
 
   public static final class SecurityStateManager.Companion {
@@ -111,10 +107,6 @@
     method public androidx.security.state.UpdateInfo.Builder setUri(String uri);
   }
 
-  public final class UpdateInfoKt {
-    method @kotlin.jvm.JvmSynthetic public static androidx.security.state.UpdateInfo UpdateInfo(kotlin.jvm.functions.Function1<? super androidx.security.state.UpdateInfo.Builder,kotlin.Unit> initializer);
-  }
-
   public class UpdateInfoProvider extends android.content.ContentProvider {
     ctor public UpdateInfoProvider(android.content.Context context, String authority, optional androidx.security.state.SecurityPatchState? customSecurityState);
     method public int delete(android.net.Uri uri, String? selection, String[]? selectionArgs);
diff --git a/security/security-state/api/restricted_current.txt b/security/security-state/api/restricted_current.txt
index 26fd3fc..4424adf 100644
--- a/security/security-state/api/restricted_current.txt
+++ b/security/security-state/api/restricted_current.txt
@@ -2,18 +2,24 @@
 package androidx.security.state {
 
   public class SecurityPatchState {
+    ctor public SecurityPatchState(android.content.Context context);
+    ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules);
     ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules, optional androidx.security.state.SecurityStateManager? customSecurityStateManager);
     method public final boolean areCvesPatched(java.util.List<java.lang.String> cveList);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component, optional java.util.List<? extends android.net.Uri>? providers);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getDeviceSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component);
-    method public java.util.Map<androidx.security.state.SecurityPatchState.Severity,java.util.List<java.lang.String>> getPatchedCves(androidx.security.state.SecurityPatchState.Component component, androidx.security.state.SecurityPatchState.SecurityPatchLevel spl);
-    method public java.util.List<androidx.security.state.SecurityPatchState.SecurityPatchLevel> getPublishedSecurityPatchLevel(androidx.security.state.SecurityPatchState.Component component);
-    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getSecurityPatchLevelString(androidx.security.state.SecurityPatchState.Component component, String securityPatchLevel);
-    method public final android.net.Uri getVulnerabilityReportUrl(String serverUrl);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(String component);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getAvailableSecurityPatchLevel(String component, optional java.util.List<? extends android.net.Uri> providers);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
+    method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getDeviceSecurityPatchLevel(String component);
+    method public java.util.Map<androidx.security.state.SecurityPatchState.Severity,java.util.Set<java.lang.String>> getPatchedCves(String component, androidx.security.state.SecurityPatchState.SecurityPatchLevel spl);
+    method public java.util.List<androidx.security.state.SecurityPatchState.SecurityPatchLevel> getPublishedSecurityPatchLevel(String component);
+    method @RequiresApi(26) public final android.net.Uri getVulnerabilityReportUrl(android.net.Uri serverUrl);
     method public final boolean isDeviceFullyUpdated();
     method public java.util.List<androidx.security.state.UpdateInfo> listAvailableUpdates(optional java.util.List<? extends android.net.Uri>? providers);
     method public final void loadVulnerabilityReport(String jsonString);
+    field public static final String COMPONENT_KERNEL = "KERNEL";
+    field public static final String COMPONENT_SYSTEM = "SYSTEM";
+    field public static final String COMPONENT_SYSTEM_MODULES = "SYSTEM_MODULES";
+    field public static final String COMPONENT_VENDOR = "VENDOR";
     field public static final androidx.security.state.SecurityPatchState.Companion Companion;
     field public static final java.util.List<java.lang.String> DEFAULT_SYSTEM_MODULES;
     field public static final String DEFAULT_VULNERABILITY_REPORTS_URL = "https://storage.googleapis.com/osv-android-api";
@@ -22,14 +28,6 @@
   public static final class SecurityPatchState.Companion {
   }
 
-  public enum SecurityPatchState.Component {
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component KERNEL;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component SYSTEM;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component SYSTEM_MODULES;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component VENDOR;
-    enum_constant public static final androidx.security.state.SecurityPatchState.Component WEBVIEW;
-  }
-
   public static final class SecurityPatchState.DateBasedSecurityPatchLevel extends androidx.security.state.SecurityPatchState.SecurityPatchLevel {
     ctor public SecurityPatchState.DateBasedSecurityPatchLevel(int year, int month, int day);
     method public int compareTo(androidx.security.state.SecurityPatchState.SecurityPatchLevel other);
@@ -79,12 +77,10 @@
   public class SecurityStateManager {
     ctor public SecurityStateManager(android.content.Context context);
     method public android.os.Bundle getGlobalSecurityState(optional String? moduleMetadataProvider);
-    field public static final String ANDROID_MODULE_METADATA_PROVIDER = "com.android.modulemetadata";
     field public static final androidx.security.state.SecurityStateManager.Companion Companion;
     field public static final String KEY_KERNEL_VERSION = "kernel_version";
     field public static final String KEY_SYSTEM_SPL = "system_spl";
     field public static final String KEY_VENDOR_SPL = "vendor_spl";
-    field public static final String VENDOR_SECURITY_PATCH_PROPERTY_KEY = "ro.vendor.build.security_patch";
   }
 
   public static final class SecurityStateManager.Companion {
@@ -111,10 +107,6 @@
     method public androidx.security.state.UpdateInfo.Builder setUri(String uri);
   }
 
-  public final class UpdateInfoKt {
-    method @kotlin.jvm.JvmSynthetic public static androidx.security.state.UpdateInfo UpdateInfo(kotlin.jvm.functions.Function1<? super androidx.security.state.UpdateInfo.Builder,kotlin.Unit> initializer);
-  }
-
   public class UpdateInfoProvider extends android.content.ContentProvider {
     ctor public UpdateInfoProvider(android.content.Context context, String authority, optional androidx.security.state.SecurityPatchState? customSecurityState);
     method public int delete(android.net.Uri uri, String? selection, String[]? selectionArgs);
diff --git a/security/security-state/build.gradle b/security/security-state/build.gradle
index 794e1e3..48b0c87 100644
--- a/security/security-state/build.gradle
+++ b/security/security-state/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
     implementation("com.google.code.gson:gson:2.10.1")
diff --git a/security/security-state/lint-baseline.xml b/security/security-state/lint-baseline.xml
new file mode 100644
index 0000000..a099b0f
--- /dev/null
+++ b/security/security-state/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.6.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha07)" variant="all" version="8.6.0-alpha07">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.time.Instant#now`"
+        errorLine1="        @set:JvmSynthetic private var publishedDate: Date = Date.from(Instant.now())"
+        errorLine2="                                                                              ~~~">
+        <location
+            file="src/main/java/androidx/security/state/UpdateInfo.kt"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 26, or core library desugaring (current min is 21): `java.util.Date#from`"
+        errorLine1="        @set:JvmSynthetic private var publishedDate: Date = Date.from(Instant.now())"
+        errorLine2="                                                                 ~~~~">
+        <location
+            file="src/main/java/androidx/security/state/UpdateInfo.kt"/>
+    </issue>
+
+</issues>
diff --git a/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt b/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt
index 5cfa248..bba752a 100644
--- a/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt
+++ b/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt
@@ -19,7 +19,8 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.net.Uri
-import androidx.annotation.RestrictTo
+import androidx.annotation.RequiresApi
+import androidx.annotation.StringDef
 import androidx.security.state.SecurityStateManager.Companion.KEY_KERNEL_VERSION
 import androidx.security.state.SecurityStateManager.Companion.KEY_SYSTEM_SPL
 import androidx.security.state.SecurityStateManager.Companion.KEY_VENDOR_SPL
@@ -44,7 +45,7 @@
  * The class uses a combination of local data storage and external data fetching to maintain and
  * update security states.
  *
- * @param context Android context used for accessing shared preferences, resources, and other
+ * @param context Application context used for accessing shared preferences, resources, and other
  *   context-dependent features.
  * @param systemModules A list of system module package names, defaults to Google provided system
  *   modules if none are provided. The first module on the list must be the system modules metadata
@@ -53,7 +54,9 @@
  *   information. If null, a default manager is instantiated.
  * @constructor Creates an instance of SecurityPatchState.
  */
-public open class SecurityPatchState(
+public open class SecurityPatchState
+@JvmOverloads
+constructor(
     private val context: Context,
     private val systemModules: List<String> = listOf(),
     private val customSecurityStateManager: SecurityStateManager? = null
@@ -73,33 +76,55 @@
                 "com.google.mainline.go.telemetry"
             )
 
+        /** URL for the Google-provided data of vulnerabilities from Android Security Bulletin. */
         public const val DEFAULT_VULNERABILITY_REPORTS_URL: String =
             "https://storage.googleapis.com/osv-android-api"
+
+        /**
+         * System component providing ro.build.version.security_patch property value as
+         * DateBasedSpl.
+         */
+        public const val COMPONENT_SYSTEM: String = "SYSTEM"
+
+        /** System modules component providing DateBasedSpl of system modules patch level. */
+        public const val COMPONENT_SYSTEM_MODULES: String = "SYSTEM_MODULES"
+
+        /**
+         * Vendor component providing ro.vendor.build.security_patch property value as DateBasedSpl.
+         */
+        public const val COMPONENT_VENDOR: String = "VENDOR"
+
+        /** Kernel component providing kernel version as VersionedSpl. */
+        public const val COMPONENT_KERNEL: String = "KERNEL"
+
+        /** WebView component providing default WebView provider version as VersionedSpl. */
+        internal const val COMPONENT_WEBVIEW: String = "WEBVIEW"
     }
 
-    /** Types of components whose security state can be accessed. */
-    public enum class Component {
-        // Provides DateBasedSpl
-        SYSTEM,
-
-        // Provides DateBasedSpl
-        SYSTEM_MODULES,
-
-        // Provides DateBasedSpl
-        VENDOR,
-
-        // Provides VersionedSpl
-        KERNEL,
-
-        // Provides VersionedSpl
-        WEBVIEW
-    }
+    /** Annotation for defining the component to use. */
+    @Retention(AnnotationRetention.SOURCE)
+    @StringDef(
+        open = true,
+        value =
+            [
+                COMPONENT_SYSTEM,
+                COMPONENT_SYSTEM_MODULES,
+                COMPONENT_KERNEL,
+                COMPONENT_VENDOR,
+                COMPONENT_WEBVIEW,
+            ]
+    )
+    internal annotation class Component
 
     /** Severity of reported security issues. */
     public enum class Severity {
+        /** Critical severity issues from Android Security Bulletin. */
         CRITICAL,
+        /** High severity issues from Android Security Bulletin. */
         HIGH,
+        /** Moderate severity issues from Android Security Bulletin. */
         MODERATE,
+        /** Low severity issues from Android Security Bulletin. */
         LOW
     }
 
@@ -177,10 +202,13 @@
             }
         }
 
+        /** Year of the security patch level. */
         public fun getYear(): Int = year
 
+        /** Month of the security patch level. */
         public fun getMonth(): Int = month
 
+        /** Day of the security patch level. */
         public fun getDay(): Int = day
     }
 
@@ -258,12 +286,16 @@
             }
         }
 
+        /** Major version of the security patch level. */
         public fun getMajorVersion(): Int = majorVersion
 
+        /** Minor version of the security patch level. */
         public fun getMinorVersion(): Int = minorVersion
 
+        /** Patch version of the security patch level. */
         public fun getPatchVersion(): Int = patchVersion
 
+        /** Build version of the security patch level. */
         public fun getBuildVersion(): Int = buildVersion
     }
 
@@ -289,8 +321,7 @@
      *
      * @return A list of strings representing system module identifiers.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public fun getSystemModules(): List<String> {
+    internal fun getSystemModules(): List<String> {
         // Use the provided systemModules if not empty; otherwise, use defaultSystemModules
         return systemModules.ifEmpty { DEFAULT_SYSTEM_MODULES }
     }
@@ -304,7 +335,7 @@
      * - create SecurityPatchState object
      * - call getVulnerabilityReportUrl()
      * - download JSON file containing vulnerability report data
-     * - call parseVulnerabilityReport()
+     * - call loadVulnerabilityReport()
      * - call getPublishedSecurityPatchLevel() or other APIs
      *
      * @param jsonString The JSON string containing the vulnerability data.
@@ -376,7 +407,7 @@
             }
 
             try {
-                Severity.valueOf(group.severity.uppercase(Locale.getDefault()))
+                Severity.valueOf(group.severity.uppercase(Locale.US))
             } catch (e: IllegalArgumentException) {
                 throw IllegalArgumentException(
                     "Severity must be: critical, high, moderate, low. Found: ${group.severity}"
@@ -395,7 +426,8 @@
      *   device.
      * @throws IllegalArgumentException if the Android SDK version is unsupported.
      */
-    public fun getVulnerabilityReportUrl(serverUrl: String): Uri {
+    @RequiresApi(26)
+    public fun getVulnerabilityReportUrl(serverUrl: Uri): Uri {
         val androidSdk = securityStateManager.getAndroidSdkInt()
         if (androidSdk < 26) {
             throw IllegalArgumentException(
@@ -403,12 +435,13 @@
             )
         }
 
-        val completeUrl = "$serverUrl/v1/android_sdk_$androidSdk.json"
-
-        return Uri.parse(completeUrl)
+        val newEndpoint = "v1/android_sdk_$androidSdk.json"
+        return serverUrl.buildUpon().appendEncodedPath(newEndpoint).build()
     }
 
-    private fun getMaxComponentSecurityPatchLevel(component: String): DateBasedSecurityPatchLevel? {
+    private fun getMaxComponentSecurityPatchLevel(
+        @Component component: String
+    ): DateBasedSecurityPatchLevel? {
         if (vulnerabilityReport == null) return null
 
         // Iterate through all SPL dates, find the latest date where
@@ -421,8 +454,8 @@
             ?.let { latestDate -> DateBasedSecurityPatchLevel.fromString(latestDate) }
     }
 
-    private fun componentToString(component: Component): String {
-        return component.name.lowercase(Locale.getDefault())
+    private fun componentToString(@Component component: String): String {
+        return component.lowercase(Locale.US)
     }
 
     private fun checkVulnerabilityReport() {
@@ -497,29 +530,30 @@
      * @param component The component for which the security patch level is requested.
      * @return A [SecurityPatchLevel] representing the current patch level of the component.
      * @throws IllegalStateException if the patch level data is not available.
+     * @throws IllegalArgumentException if the component name is unrecognized.
      */
-    public open fun getDeviceSecurityPatchLevel(component: Component): SecurityPatchLevel {
+    public open fun getDeviceSecurityPatchLevel(@Component component: String): SecurityPatchLevel {
         val globalSecurityState = securityStateManager.getGlobalSecurityState(getSystemModules()[0])
 
         return when (component) {
-            Component.SYSTEM_MODULES -> {
+            COMPONENT_SYSTEM_MODULES -> {
                 getSystemModulesSecurityPatchLevel()
             }
-            Component.KERNEL -> {
+            COMPONENT_KERNEL -> {
                 val kernelVersion =
                     globalSecurityState.getString(KEY_KERNEL_VERSION)
                         ?: throw IllegalStateException("Kernel version not available.")
 
                 VersionedSecurityPatchLevel.fromString(kernelVersion)
             }
-            Component.SYSTEM -> {
+            COMPONENT_SYSTEM -> {
                 val systemSpl =
                     globalSecurityState.getString(KEY_SYSTEM_SPL)
                         ?: throw IllegalStateException("System SPL not available.")
 
                 DateBasedSecurityPatchLevel.fromString(systemSpl)
             }
-            Component.VENDOR -> {
+            COMPONENT_VENDOR -> {
                 val vendorSpl =
                     globalSecurityState.getString(KEY_VENDOR_SPL)
                         ?: throw IllegalStateException("Vendor SPL not available.")
@@ -528,7 +562,8 @@
             }
 
             // TODO(musashi): Add support for webview package
-            Component.WEBVIEW -> TODO()
+            COMPONENT_WEBVIEW -> TODO()
+            else -> throw IllegalArgumentException("Unknown component: $component")
         }
     }
 
@@ -546,23 +581,27 @@
      *   "5.15.159", "6.1.91" ] ```
      * @throws IllegalStateException if the vulnerability report is not loaded or if patch level
      *   data is unavailable.
+     * @throws IllegalArgumentException if the component name is unrecognized.
      */
-    public open fun getPublishedSecurityPatchLevel(component: Component): List<SecurityPatchLevel> {
+    public open fun getPublishedSecurityPatchLevel(
+        @Component component: String
+    ): List<SecurityPatchLevel> {
         checkVulnerabilityReport()
 
         return when (component) {
-            Component.SYSTEM_MODULES -> listOf(getSystemModulesPublishedSecurityPatchLevel())
-            Component.SYSTEM,
-            Component.VENDOR -> {
+            COMPONENT_SYSTEM_MODULES -> listOf(getSystemModulesPublishedSecurityPatchLevel())
+            COMPONENT_SYSTEM,
+            COMPONENT_VENDOR -> {
                 listOf(
                     getMaxComponentSecurityPatchLevel(componentToString(component))
                         ?: throw IllegalStateException("SPL data not available.")
                 )
             }
-            Component.KERNEL -> getPublishedKernelVersions()
+            COMPONENT_KERNEL -> getPublishedKernelVersions()
 
             // TODO(musashi): Add support for webview package
-            Component.WEBVIEW -> TODO()
+            COMPONENT_WEBVIEW -> TODO()
+            else -> throw IllegalArgumentException("Unknown component: $component")
         }
     }
 
@@ -605,20 +644,20 @@
      *
      * @param component The component for which the available patch level is requested.
      * @param providers Optional list of URIs representing update providers; if null, defaults are
-     *   used. Check documentation of OTA update clients for content provider URIs.
+     *   used. Contact authors of custom OTA update clients included on a tested device to obtain
+     *   content provider URIs.
      * @return A [SecurityPatchLevel] representing the available patch level for updates.
      */
     @JvmOverloads
     public open fun getAvailableSecurityPatchLevel(
-        component: Component,
-        providers: List<Uri>? = null
+        @Component component: String,
+        providers: List<Uri> = listOf()
     ): SecurityPatchLevel {
         val updates = listAvailableUpdates(providers)
         return updates
-            .filter { it.component == component.toString() }
-            .maxOfOrNull {
-                getSecurityPatchLevelString(Component.valueOf(it.component), it.securityPatchLevel)
-            } ?: getDeviceSecurityPatchLevel(component)
+            .filter { it.component == component }
+            .maxOfOrNull { getComponentSecurityPatchLevel(it.component, it.securityPatchLevel) }
+            ?: getDeviceSecurityPatchLevel(component)
     }
 
     /**
@@ -636,11 +675,11 @@
      * @throws IllegalStateException if the vulnerability report is not loaded.
      */
     public open fun getPatchedCves(
-        component: Component,
+        @Component component: String,
         spl: SecurityPatchLevel
-    ): Map<Severity, List<String>> {
+    ): Map<Severity, Set<String>> {
         // Check if the component is valid for this operation
-        if (component !in listOf(Component.SYSTEM, Component.VENDOR, Component.SYSTEM_MODULES)) {
+        if (component !in listOf(COMPONENT_SYSTEM, COMPONENT_VENDOR, COMPONENT_SYSTEM_MODULES)) {
             throw IllegalArgumentException("Component must be SYSTEM, VENDOR, or SYSTEM_MODULES")
         }
         checkVulnerabilityReport()
@@ -654,15 +693,14 @@
                     groups
                         .filter { it.components.contains(componentToString(component)) }
                         .forEach { group ->
-                            val severity =
-                                Severity.valueOf(group.severity.uppercase(Locale.getDefault()))
+                            val severity = Severity.valueOf(group.severity.uppercase(Locale.US))
                             relevantFixes
                                 .getOrPut(severity, ::mutableListOf)
                                 .addAll(group.cveIdentifiers)
                         }
                 }
             }
-            return relevantFixes.mapValues { it.value.toList() }.toMap()
+            return relevantFixes.mapValues { it.value.toSet() }.toMap()
         }
     }
 
@@ -672,8 +710,8 @@
      * the component type, interpreting the string as a date for date-based components or as a
      * version number for versioned components.
      *
-     * @param component The [Component] enum indicating which type of component's patch level is
-     *   being requested.
+     * @param component The component indicating which type of component's patch level is being
+     *   requested.
      * @param securityPatchLevel The string representation of the security patch level, which could
      *   be a date or a version number.
      * @return A [SecurityPatchLevel] instance corresponding to the specified component and patch
@@ -682,22 +720,23 @@
      *   specified component type, or if the component requires a specific format that the string
      *   does not meet.
      */
-    public open fun getSecurityPatchLevelString(
-        component: Component,
+    public open fun getComponentSecurityPatchLevel(
+        @Component component: String,
         securityPatchLevel: String
     ): SecurityPatchLevel {
         return when (component) {
-            Component.SYSTEM,
-            Component.SYSTEM_MODULES,
-            Component.VENDOR -> {
+            COMPONENT_SYSTEM,
+            COMPONENT_SYSTEM_MODULES,
+            COMPONENT_VENDOR -> {
                 // These components are expected to use DateBasedSpl
                 DateBasedSecurityPatchLevel.fromString(securityPatchLevel)
             }
-            Component.KERNEL,
-            Component.WEBVIEW -> {
+            COMPONENT_KERNEL,
+            COMPONENT_WEBVIEW -> {
                 // These components are expected to use VersionedSpl
                 VersionedSecurityPatchLevel.fromString(securityPatchLevel)
             }
+            else -> throw IllegalArgumentException("Unknown component: $component")
         }
     }
 
@@ -730,12 +769,12 @@
                     val json = it.getString(it.getColumnIndexOrThrow("json"))
                     try {
                         val updateInfo = gson.fromJson(json, UpdateInfo::class.java) ?: continue
-                        val component = Component.valueOf(updateInfo.component)
+                        val component = updateInfo.component
                         val deviceSpl = getDeviceSecurityPatchLevel(component)
 
                         if (
                             deviceSpl >=
-                                getSecurityPatchLevelString(
+                                getComponentSecurityPatchLevel(
                                     component,
                                     updateInfo.securityPatchLevel
                                 )
@@ -765,23 +804,30 @@
     public fun isDeviceFullyUpdated(): Boolean {
         checkVulnerabilityReport()
 
-        val components = Component.values()
+        val components =
+            listOf(
+                COMPONENT_SYSTEM,
+                COMPONENT_SYSTEM_MODULES,
+                COMPONENT_VENDOR,
+                COMPONENT_KERNEL,
+                COMPONENT_WEBVIEW
+            )
 
         components.forEach { component ->
             // TODO(musashi): Unblock once support for WebView is present.
-            if (component == Component.WEBVIEW) return@forEach
+            if (component == COMPONENT_WEBVIEW) return@forEach
             val deviceSpl =
                 try {
                     getDeviceSecurityPatchLevel(component)
                 } catch (e: Exception) {
                     throw IllegalStateException(
-                        "Failed to retrieve device SPL for component: ${component.name}",
+                        "Failed to retrieve device SPL for component: $component",
                         e
                     )
                 }
 
             try {
-                if (component != Component.KERNEL) {
+                if (component != COMPONENT_KERNEL) {
                     val publishedSpl = getPublishedSecurityPatchLevel(component)[0]
 
                     if (deviceSpl < publishedSpl) {
@@ -801,7 +847,7 @@
                 }
             } catch (e: Exception) {
                 throw IllegalStateException(
-                    "Published SPL not available for component: ${component.name}",
+                    "Published SPL not available for component: $component",
                     e
                 )
             }
@@ -819,7 +865,7 @@
      * @return true if all provided CVEs are patched, false otherwise.
      */
     public fun areCvesPatched(cveList: List<String>): Boolean {
-        val componentsToCheck = listOf(Component.SYSTEM, Component.VENDOR, Component.SYSTEM_MODULES)
+        val componentsToCheck = listOf(COMPONENT_SYSTEM, COMPONENT_VENDOR, COMPONENT_SYSTEM_MODULES)
         val allPatchedCves = mutableSetOf<String>()
 
         // Aggregate all CVEs from security fixes across necessary components
diff --git a/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt b/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
index 71e3f5d9..011d418 100644
--- a/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
+++ b/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
@@ -23,11 +23,13 @@
 import android.os.Bundle
 import android.system.Os
 import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
 import androidx.webkit.WebViewCompat
 import java.util.regex.Pattern
 
 /**
+ * This class is a wrapper around AOSP {@link android.os.SecurityStateManager} service API added in
+ * SDK 35. Support for features on older SDKs is provided on a best effort basis.
+ *
  * Manages the retrieval and storage of security patch levels and module information for an Android
  * device. This class provides methods to fetch the current security state of the system, including
  * patch levels for the system, vendor, and kernel as well as module updates available through
@@ -43,9 +45,9 @@
         private const val TAG = "SecurityStateManager"
         private val kernelReleasePattern: Pattern = Pattern.compile("(\\d+\\.\\d+\\.\\d+)(.*)")
 
-        public const val VENDOR_SECURITY_PATCH_PROPERTY_KEY: String =
+        private const val VENDOR_SECURITY_PATCH_PROPERTY_KEY: String =
             "ro.vendor.build.security_patch"
-        public const val ANDROID_MODULE_METADATA_PROVIDER: String = "com.android.modulemetadata"
+        private const val ANDROID_MODULE_METADATA_PROVIDER: String = "com.android.modulemetadata"
 
         /**
          * The system SPL key returned as part of the {@code Bundle} from {@code
@@ -77,7 +79,7 @@
      * @return A Bundle containing keys and values representing the security state of the system,
      *   vendor, and kernel.
      */
-    @SuppressLint("NewApi")
+    @SuppressLint("NewApi") // Lint does not detect version check below.
     public open fun getGlobalSecurityState(moduleMetadataProvider: String? = null): Bundle {
         return Bundle().apply {
             // TODO(musashi): add call to SecurityStateManager API when it becomes available
@@ -103,7 +105,10 @@
                     )
                 }
             }
-            putString(KEY_KERNEL_VERSION, getKernelVersion())
+            val kernelVersion = getKernelVersion()
+            if (kernelVersion.isNotEmpty()) {
+                putString(KEY_KERNEL_VERSION, kernelVersion)
+            }
             addWebViewPackages(this)
         }
     }
@@ -118,8 +123,7 @@
      *   occurs.
      * @throws PackageManager.NameNotFoundException if the package name provided does not exist.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public open fun getPackageVersion(packageName: String): String {
+    internal open fun getPackageVersion(packageName: String): String {
         if (packageName.isNotEmpty()) {
             return try {
                 packageManager.getPackageInfo(packageName, 0).versionName ?: ""
@@ -170,8 +174,7 @@
      *
      * @return the SDK version as an integer.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public open fun getAndroidSdkInt(): Int {
+    internal open fun getAndroidSdkInt(): Int {
         return Build.VERSION.SDK_INT
     }
 
@@ -183,9 +186,8 @@
      * @return A string representing the current security patch level, or empty string if it cannot
      *   be retrieved.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @SuppressLint("NewApi")
-    public fun getSecurityPatchLevelSafe(): String {
+    @SuppressLint("NewApi") // Lint does not detect version check below.
+    internal fun getSecurityPatchLevelSafe(): String {
         return if (getAndroidSdkInt() >= Build.VERSION_CODES.M) {
             Build.VERSION.SECURITY_PATCH
         } else {
@@ -218,7 +220,7 @@
      * @return A string representing the vendor's security patch level, or an empty string if it
      *   cannot be retrieved.
      */
-    @Suppress("BanUncheckedReflection")
+    @Suppress("BanUncheckedReflection") // For accessing vendor SPL on SDK older than 35.
     private fun getVendorSpl(): String {
         try {
             // This is the only way to get vendor SPL from public API level on Android 14 or older
diff --git a/security/security-state/src/main/java/androidx/security/state/UpdateInfo.kt b/security/security-state/src/main/java/androidx/security/state/UpdateInfo.kt
index ec1bec9..8ef484f 100644
--- a/security/security-state/src/main/java/androidx/security/state/UpdateInfo.kt
+++ b/security/security-state/src/main/java/androidx/security/state/UpdateInfo.kt
@@ -16,21 +16,39 @@
 
 package androidx.security.state
 
-import java.time.Instant
 import java.util.Date
 import java.util.Objects
 
 /** Represents information about an available update for a component. */
 public class UpdateInfo(
+    /** Uri of the content provider from OTA update client serving update information data. */
     public val uri: String,
+    /** Component for which the update information is provided. */
     public val component: String,
+    /**
+     * Security patch level of the available update ready to be applied by the reporting client. Use
+     * [SecurityPatchState.getComponentSecurityPatchLevel] method to get encapsulated value.
+     */
     public val securityPatchLevel: String,
+    /** Date when the available update was published. */
     public val publishedDate: Date
 ) {
+    /**
+     * Returns a string representation of the update information.
+     *
+     * @return A string that describes the update details.
+     */
     public override fun toString(): String =
         "UpdateInfo(" +
             "uri=$uri, component=$component, SPL=$securityPatchLevel, date=$publishedDate)"
 
+    /**
+     * Compares this UpdateInfo with another object for equality.
+     *
+     * @param other The object to compare with this instance.
+     * @return true if the other object is an instance of UpdateInfo and all properties match, false
+     *   otherwise.
+     */
     public override fun equals(other: Any?): Boolean =
         other is UpdateInfo &&
             uri == other.uri &&
@@ -38,33 +56,63 @@
             securityPatchLevel == other.securityPatchLevel &&
             publishedDate == other.publishedDate
 
+    /**
+     * Provides a hash code for an UpdateInfo object.
+     *
+     * @return A hash code produced by the properties of the update info.
+     */
     public override fun hashCode(): Int =
         Objects.hash(uri, component, securityPatchLevel, publishedDate)
 
+    /** Builder class for creating an instance of UpdateInfo. */
     public class Builder {
         @set:JvmSynthetic private var uri: String = ""
         @set:JvmSynthetic private var component: String = ""
         @set:JvmSynthetic private var securityPatchLevel: String = ""
-        @set:JvmSynthetic private var publishedDate: Date = Date.from(Instant.now())
+        @set:JvmSynthetic private var publishedDate: Date = Date(0) // 1970-01-01
 
+        /**
+         * Sets the URI of the update.
+         *
+         * @param uri The URI to set.
+         * @return The builder instance for chaining.
+         */
         public fun setUri(uri: String): Builder = apply { this.uri = uri }
 
+        /**
+         * Sets the component associated with the update.
+         *
+         * @param component The component to set.
+         * @return The builder instance for chaining.
+         */
         public fun setComponent(component: String): Builder = apply { this.component = component }
 
+        /**
+         * Sets the security patch level of the update.
+         *
+         * @param securityPatchLevel The security patch level to set.
+         * @return The builder instance for chaining.
+         */
         public fun setSecurityPatchLevel(securityPatchLevel: String): Builder = apply {
             this.securityPatchLevel = securityPatchLevel
         }
 
+        /**
+         * Sets the publication date of the update.
+         *
+         * @param publishedDate The date to set.
+         * @return The builder instance for chaining.
+         */
         public fun setPublishedDate(publishedDate: Date): Builder = apply {
             this.publishedDate = publishedDate
         }
 
+        /**
+         * Builds and returns an UpdateInfo object.
+         *
+         * @return The constructed UpdateInfo.
+         */
         public fun build(): UpdateInfo =
             UpdateInfo(uri, component, securityPatchLevel, publishedDate)
     }
 }
-
-@JvmSynthetic
-public fun UpdateInfo(initializer: UpdateInfo.Builder.() -> Unit): UpdateInfo {
-    return UpdateInfo.Builder().apply(initializer).build()
-}
diff --git a/security/security-state/src/main/java/androidx/security/state/UpdateInfoProvider.kt b/security/security-state/src/main/java/androidx/security/state/UpdateInfoProvider.kt
index 31244d1..7dec3e4 100644
--- a/security/security-state/src/main/java/androidx/security/state/UpdateInfoProvider.kt
+++ b/security/security-state/src/main/java/androidx/security/state/UpdateInfoProvider.kt
@@ -30,10 +30,25 @@
  * via a content URI. It only supports querying data; insert, delete, and update operations are not
  * supported.
  *
- * This provider is typically used to expose update information to other applications or components
- * within the system that need access to the latest security updates data. An OTA update client
- * calls registerUpdate() and unregisterUpdate() to add or remove update information to a local
- * store, from which the content provider serves the data to the applications.
+ * This provider is typically used by OTA or other update client to expose update information to
+ * other applications or components within the system that need access to the latest security
+ * updates data. The client calls registerUpdate() and unregisterUpdate() to add or remove update
+ * information to a local store, from which the content provider serves the data to the
+ * applications. To setup the content provider add following snippet to the client's manifest,
+ * replacing com.example with correct namespace:
+ * <pre>
+ * <permission
+ * android:name="com.example.updateinfoprovider.WRITE_UPDATES_INFO"
+ * android:label="@string/write_permission_label"
+ * android:description="@string/write_permission_description"
+ * android:protectionLevel="signature" />
+ *
+ * <provider
+ * android:name=".UpdateInfoProvider"
+ * android:authorities="com.example.updateinfoprovider"
+ * android:exported="true"
+ * android:writePermission="com.example.updateinfoprovider.WRITE_UPDATES_INFO"/>
+ * </pre>
  *
  * @param context The [Context] of the calling application.
  * @param authority The authority for this content provider, used to construct the base URI.
@@ -222,16 +237,19 @@
         val editor = sharedPreferences?.edit() ?: return
 
         allUpdates.forEach { updateInfo ->
-            val component: SecurityPatchState.Component
+            val component = updateInfo.component
+            val currentSpl: SecurityPatchState.SecurityPatchLevel
             try {
-                component = SecurityPatchState.Component.valueOf(updateInfo.component)
+                currentSpl = securityState.getDeviceSecurityPatchLevel(component)
             } catch (e: IllegalArgumentException) {
                 // Ignore unknown components.
                 return@forEach
             }
-            val currentSpl = securityState.getDeviceSecurityPatchLevel(component)
             val updateSpl =
-                securityState.getSecurityPatchLevelString(component, updateInfo.securityPatchLevel)
+                securityState.getComponentSecurityPatchLevel(
+                    component,
+                    updateInfo.securityPatchLevel
+                )
 
             if (updateSpl <= currentSpl) {
                 val key = getKeyForUpdateInfo(updateInfo)
diff --git a/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt b/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt
index 3725bdf..1645c83 100644
--- a/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt
+++ b/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt
@@ -26,6 +26,7 @@
 import android.net.Uri
 import android.os.Build
 import android.os.Bundle
+import androidx.annotation.RequiresApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.gson.Gson
 import java.time.LocalDate
@@ -51,7 +52,7 @@
     private val updateInfo =
         UpdateInfo.Builder()
             .setUri("content://example.com/updateinfo")
-            .setComponent(SecurityPatchState.Component.SYSTEM.toString())
+            .setComponent(SecurityPatchState.COMPONENT_SYSTEM.toString())
             .setSecurityPatchLevel("2022-01-01")
             .setPublishedDate(Date.from(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC)))
             .build()
@@ -121,8 +122,8 @@
     @Test
     fun testGetSecurityPatchLevelWithDateBasedComponent() {
         val spl =
-            securityState.getSecurityPatchLevelString(
-                SecurityPatchState.Component.SYSTEM,
+            securityState.getComponentSecurityPatchLevel(
+                SecurityPatchState.COMPONENT_SYSTEM,
                 "2022-01-01"
             )
         assertTrue(spl is SecurityPatchState.DateBasedSecurityPatchLevel)
@@ -132,8 +133,8 @@
     @Test
     fun testGetSecurityPatchLevelWithVersionedComponent() {
         val spl =
-            securityState.getSecurityPatchLevelString(
-                SecurityPatchState.Component.KERNEL,
+            securityState.getComponentSecurityPatchLevel(
+                SecurityPatchState.COMPONENT_KERNEL,
                 "1.2.3.4"
             )
         assertTrue(spl is SecurityPatchState.VersionedSecurityPatchLevel)
@@ -142,24 +143,25 @@
 
     @Test(expected = IllegalArgumentException::class)
     fun testGetSecurityPatchLevelWithInvalidDateBasedInput() {
-        securityState.getSecurityPatchLevelString(
-            SecurityPatchState.Component.SYSTEM,
+        securityState.getComponentSecurityPatchLevel(
+            SecurityPatchState.COMPONENT_SYSTEM,
             "invalid-date"
         )
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun testGetSecurityPatchLevelWithInvalidVersionedInput() {
-        securityState.getSecurityPatchLevelString(
-            SecurityPatchState.Component.KERNEL,
+        securityState.getComponentSecurityPatchLevel(
+            SecurityPatchState.COMPONENT_KERNEL,
             "invalid-version"
         )
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Config(maxSdk = Build.VERSION_CODES.N_MR1)
     @Test(expected = IllegalArgumentException::class)
     fun testGetVulnerabilityReportUrl_withUnsupportedSdk_throwsException() {
-        securityState.getVulnerabilityReportUrl("https://example.com")
+        securityState.getVulnerabilityReportUrl(Uri.parse("https://example.com"))
     }
 
     @Test
@@ -185,12 +187,12 @@
 
         val fixes =
             securityState.getPatchedCves(
-                SecurityPatchState.Component.SYSTEM,
+                SecurityPatchState.COMPONENT_SYSTEM,
                 SecurityPatchState.DateBasedSecurityPatchLevel(2022, 1, 1)
             )
 
         assertEquals(1, fixes[SecurityPatchState.Severity.HIGH]?.size)
-        assertEquals(listOf("CVE-2020-1234"), fixes[SecurityPatchState.Severity.HIGH])
+        assertEquals(setOf("CVE-2020-1234"), fixes[SecurityPatchState.Severity.HIGH])
     }
 
     @Test(expected = IllegalArgumentException::class)
@@ -199,6 +201,7 @@
         securityState.loadVulnerabilityReport(invalidJson)
     }
 
+    @RequiresApi(Build.VERSION_CODES.O)
     @Test
     fun testGetVulnerabilityReportUrl_validSdkVersion_returnsCorrectUrl() {
         val sdkVersion = 34 // Android 14
@@ -207,7 +210,7 @@
 
         doReturn(sdkVersion).`when`(mockSecurityStateManager).getAndroidSdkInt()
 
-        val actualUrl = securityState.getVulnerabilityReportUrl(baseUrl).toString()
+        val actualUrl = securityState.getVulnerabilityReportUrl(Uri.parse(baseUrl)).toString()
         assertEquals(expectedUrl, actualUrl)
     }
 
@@ -220,7 +223,7 @@
         `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
 
         val spl =
-            securityState.getDeviceSecurityPatchLevel(SecurityPatchState.Component.SYSTEM)
+            securityState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
                 as SecurityPatchState.DateBasedSecurityPatchLevel
         assertEquals(2020, spl.getYear())
         assertEquals(1, spl.getMonth())
@@ -234,12 +237,12 @@
         doReturn("").`when`(mockSecurityStateManager).getPackageVersion(Mockito.anyString())
         doReturn(bundle).`when`(mockSecurityStateManager).getGlobalSecurityState(anyString())
 
-        securityState.getDeviceSecurityPatchLevel(SecurityPatchState.Component.SYSTEM)
+        securityState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
     }
 
     @Test(expected = IllegalStateException::class)
     fun testGetPublishedSpl_ThrowsWhenNoVulnerabilityReportLoaded() {
-        securityState.getPublishedSecurityPatchLevel(SecurityPatchState.Component.SYSTEM)
+        securityState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
     }
 
     @Test
@@ -286,7 +289,7 @@
             .thenReturn("2024-05-01")
 
         val spl =
-            securityState.getDeviceSecurityPatchLevel(SecurityPatchState.Component.SYSTEM_MODULES)
+            securityState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM_MODULES)
                 as SecurityPatchState.DateBasedSecurityPatchLevel
 
         assertEquals(2022, spl.getYear())
@@ -316,7 +319,7 @@
 
         val spl =
             securityState
-                .getPublishedSecurityPatchLevel(SecurityPatchState.Component.SYSTEM_MODULES)[0]
+                .getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM_MODULES)[0]
                 as SecurityPatchState.DateBasedSecurityPatchLevel
 
         assertEquals(2023, spl.getYear())
@@ -345,7 +348,7 @@
         securityState.loadVulnerabilityReport(jsonInput)
 
         val spl =
-            securityState.getPublishedSecurityPatchLevel(SecurityPatchState.Component.VENDOR)[0]
+            securityState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_VENDOR)[0]
                 as SecurityPatchState.DateBasedSecurityPatchLevel
 
         assertEquals(2023, spl.getYear())
@@ -374,7 +377,7 @@
         securityState.loadVulnerabilityReport(jsonInput)
 
         val versions =
-            securityState.getPublishedSecurityPatchLevel(SecurityPatchState.Component.KERNEL)
+            securityState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_KERNEL)
         val version0 = versions[0] as SecurityPatchState.VersionedSecurityPatchLevel
         val version1 = versions[1] as SecurityPatchState.VersionedSecurityPatchLevel
 
@@ -390,7 +393,7 @@
     @Test
     fun testGetAvailableSpl_ReturnsUpdateWhenHigherThanCurrent() {
         val availableSpl = SecurityPatchState.DateBasedSecurityPatchLevel(2023, 2, 1)
-        val component = SecurityPatchState.Component.SYSTEM
+        val component = SecurityPatchState.COMPONENT_SYSTEM
         val availableUpdateJson =
             """
             {
@@ -410,7 +413,7 @@
         `when`(mockCursor.getString(0)).thenReturn(availableUpdateJson)
         securityState.loadVulnerabilityReport(generateMockReport("system", "2023-02-01"))
 
-        val result = securityState.getAvailableSecurityPatchLevel(component, null)
+        val result = securityState.getAvailableSecurityPatchLevel(component)
 
         assertEquals(availableSpl.toString(), result.toString())
     }
@@ -418,7 +421,7 @@
     @Test
     fun testGetAvailableSpl_FallsBackToCurrentWhenNoHigherUpdate() {
         val currentSpl = SecurityPatchState.DateBasedSecurityPatchLevel(2023, 5, 15)
-        val component = SecurityPatchState.Component.SYSTEM
+        val component = SecurityPatchState.COMPONENT_SYSTEM
         val bundle = Bundle()
         bundle.putString("system_spl", "2023-05-15")
 
@@ -426,7 +429,7 @@
         `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
         securityState.loadVulnerabilityReport(generateMockReport("system", "2023-04-01"))
 
-        val result = securityState.getAvailableSecurityPatchLevel(component, null)
+        val result = securityState.getAvailableSecurityPatchLevel(component)
 
         assertEquals(currentSpl.toString(), result.toString())
     }
@@ -453,7 +456,7 @@
         securityState.loadVulnerabilityReport(generateMockReport("vendor", "2023-01-01"))
 
         val spl = SecurityPatchState.DateBasedSecurityPatchLevel.fromString("2023-01-01")
-        val fixes = securityState.getPatchedCves(SecurityPatchState.Component.SYSTEM, spl)
+        val fixes = securityState.getPatchedCves(SecurityPatchState.COMPONENT_SYSTEM, spl)
 
         assertEquals(null, fixes[SecurityPatchState.Severity.CRITICAL])
         assertEquals(null, fixes[SecurityPatchState.Severity.HIGH])
@@ -461,7 +464,7 @@
         assertEquals(null, fixes[SecurityPatchState.Severity.LOW])
 
         val spl2 = SecurityPatchState.DateBasedSecurityPatchLevel.fromString("2022-01-01")
-        val fixes2 = securityState.getPatchedCves(SecurityPatchState.Component.VENDOR, spl2)
+        val fixes2 = securityState.getPatchedCves(SecurityPatchState.COMPONENT_VENDOR, spl2)
 
         assertEquals(null, fixes2[SecurityPatchState.Severity.CRITICAL])
         assertEquals(null, fixes2[SecurityPatchState.Severity.HIGH])
@@ -495,11 +498,11 @@
                 .trimIndent()
         securityState.loadVulnerabilityReport(jsonInput)
 
-        val fixes = securityState.getPatchedCves(SecurityPatchState.Component.SYSTEM, spl)
+        val fixes = securityState.getPatchedCves(SecurityPatchState.COMPONENT_SYSTEM, spl)
 
         assertEquals(2, fixes[SecurityPatchState.Severity.HIGH]?.size)
         assertEquals(
-            listOf("CVE-2023-0001", "CVE-2023-0002"),
+            setOf("CVE-2023-0001", "CVE-2023-0002"),
             fixes[SecurityPatchState.Severity.HIGH]
         )
 
@@ -510,7 +513,7 @@
     fun testGetSecurityFixes_ThrowsExceptionForInvalidComponent() {
         val spl = SecurityPatchState.DateBasedSecurityPatchLevel.fromString("2023-01-01")
 
-        securityState.getPatchedCves(SecurityPatchState.Component.WEBVIEW, spl)
+        securityState.getPatchedCves(SecurityPatchState.COMPONENT_WEBVIEW, spl)
     }
 
     @Test
diff --git a/security/security-state/src/test/java/androidx/security/state/UpdateInfoProviderTest.kt b/security/security-state/src/test/java/androidx/security/state/UpdateInfoProviderTest.kt
index 3f5902d..ec7c5f8 100644
--- a/security/security-state/src/test/java/androidx/security/state/UpdateInfoProviderTest.kt
+++ b/security/security-state/src/test/java/androidx/security/state/UpdateInfoProviderTest.kt
@@ -21,7 +21,7 @@
 import android.content.Context
 import android.content.SharedPreferences
 import android.net.Uri
-import androidx.security.state.SecurityPatchState.Component
+import androidx.security.state.SecurityPatchState.Companion.COMPONENT_SYSTEM
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.gson.Gson
 import java.time.LocalDate
@@ -48,8 +48,9 @@
     private val mockSpl = SecurityPatchState.DateBasedSecurityPatchLevel(2022, 1, 1)
     private val mockSecurityState: SecurityPatchState =
         mock<SecurityPatchState> {
-            on { getSecurityPatchLevelString(eq(Component.SYSTEM), Mockito.anyString()) } doReturn
-                mockSpl
+            on {
+                getComponentSecurityPatchLevel(eq(COMPONENT_SYSTEM), Mockito.anyString())
+            } doReturn mockSpl
         }
     private val authority = "com.example.provider"
     private val contentUri = Uri.parse("content://$authority/updateinfo")
@@ -58,7 +59,7 @@
     private val updateInfo =
         UpdateInfo.Builder()
             .setUri("content://example.com/updateinfo")
-            .setComponent(Component.SYSTEM.toString())
+            .setComponent(COMPONENT_SYSTEM)
             .setSecurityPatchLevel("2022-01-01")
             .setPublishedDate(publishedDate)
             .build()
@@ -125,7 +126,7 @@
 
     @Test
     fun testRegisterUnregisterUpdate() {
-        `when`(mockSecurityState.getDeviceSecurityPatchLevel(Component.SYSTEM))
+        `when`(mockSecurityState.getDeviceSecurityPatchLevel(COMPONENT_SYSTEM))
             .thenReturn(SecurityPatchState.DateBasedSecurityPatchLevel(2020, 1, 1))
 
         provider.registerUpdate(updateInfo)
@@ -140,7 +141,7 @@
 
     @Test
     fun testCleanupUpdateInfo_removesUpdateInfoFromSharedPreferences() {
-        `when`(mockSecurityState.getDeviceSecurityPatchLevel(Component.SYSTEM))
+        `when`(mockSecurityState.getDeviceSecurityPatchLevel(COMPONENT_SYSTEM))
             .thenReturn(SecurityPatchState.DateBasedSecurityPatchLevel(2020, 1, 1))
 
         provider.registerUpdate(updateInfo)
@@ -148,7 +149,7 @@
         Mockito.verify(mockEditor).putString(Mockito.anyString(), Mockito.anyString())
         Mockito.verify(mockEditor, times(2)).apply()
 
-        `when`(mockSecurityState.getDeviceSecurityPatchLevel(Component.SYSTEM))
+        `when`(mockSecurityState.getDeviceSecurityPatchLevel(COMPONENT_SYSTEM))
             .thenReturn(SecurityPatchState.DateBasedSecurityPatchLevel(2023, 1, 1))
 
         provider.registerUpdate(updateInfo)
diff --git a/settings.gradle b/settings.gradle
index 3006362..20dd40c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -28,7 +28,7 @@
         classpath("com.google.protobuf:protobuf-java:3.22.3")
         classpath("com.gradle:develocity-gradle-plugin:3.17.2")
         classpath("com.gradle:common-custom-user-data-gradle-plugin:2.0.1")
-        classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta08")
+        classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta10")
     }
 }
 
@@ -96,6 +96,7 @@
         value("androidx.projects", getRequestedProjectSubsetName() ?: "Unset")
         value("androidx.useMaxDepVersions", providers.gradleProperty("androidx.useMaxDepVersions").isPresent().toString())
 
+        // Do not publish scan for androidx-platform-dev
         publishing.onlyIf { it.authenticated }
     }
 }
@@ -332,7 +333,6 @@
 includeProject(":activity:activity", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":activity:activity-compose", [BuildType.COMPOSE])
 includeProject(":activity:activity-compose:activity-compose-samples", "activity/activity-compose/samples", [BuildType.COMPOSE])
-includeProject(":activity:activity-compose:integration-tests:activity-demos", [BuildType.COMPOSE])
 includeProject(":activity:activity-compose-lint", [BuildType.COMPOSE])
 includeProject(":activity:activity-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":activity:activity-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
@@ -576,6 +576,7 @@
 includeProject(":concurrent:concurrent-futures-ktx", [BuildType.MAIN])
 includeProject(":constraintlayout:constraintlayout-compose", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose-lint", [BuildType.COMPOSE])
+includeProject(":constraintlayout:constraintlayout-compose:constraintlayout-compose-samples", "constraintlayout/constraintlayout-compose/samples", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose:integration-tests:demos", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark-target", [BuildType.COMPOSE])
@@ -623,6 +624,7 @@
 includeProject(":credentials:credentials-play-services-auth", [BuildType.MAIN])
 includeProject(":credentials:credentials-provider", [BuildType.MAIN])
 includeProject(":credentials:credentials-e2ee", [BuildType.MAIN])
+includeProject(":credentials:credentials-play-services-e2ee", [BuildType.MAIN])
 includeProject(":cursoradapter:cursoradapter", [BuildType.MAIN])
 includeProject(":customview:customview", [BuildType.MAIN])
 includeProject(":customview:customview-poolingcontainer", [BuildType.MAIN, BuildType.COMPOSE])
@@ -722,6 +724,8 @@
 includeProject(":hilt:integration-tests:hilt-testapp-viewmodel", "hilt/integration-tests/viewmodelapp", [BuildType.MAIN])
 includeProject(":hilt:integration-tests:hilt-testapp-worker", "hilt/integration-tests/workerapp", [BuildType.MAIN])
 includeProject(":ink:ink-brush", [BuildType.MAIN])
+includeProject(":ink:ink-geometry", [BuildType.MAIN])
+includeProject(":ink:ink-nativeloader", [BuildType.MAIN])
 includeProject(":input:input-motionprediction", [BuildType.MAIN])
 includeProject(":inspection:inspection", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":inspection:inspection-gradle-plugin", [BuildType.MAIN])
@@ -762,8 +766,10 @@
 includeProject(":lifecycle:lifecycle-viewmodel-compose", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples", "lifecycle/lifecycle-viewmodel-compose/samples", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-compose:integration-tests:lifecycle-viewmodel-demos", [BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-viewmodel-compose-lint", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-savedstate", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-viewmodel-testing", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.INFRAROGUE, BuildType.KMP])
 includeProject(":lint:lint-gradle", [BuildType.MAIN])
 includeProject(":lint-checks")
 includeProject(":lint-checks:integration-tests")
@@ -791,6 +797,7 @@
 includeProject(":navigation:navigation-fragment-ktx", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":navigation:navigation-integration-tests", "navigation/integration-tests", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":navigation:navigation-integration-tests:testapp", "navigation/integration-tests/testapp", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":navigation:navigation-lint-common", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":navigation:navigation-runtime", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":navigation:navigation-runtime-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
 includeProject(":navigation:navigation-runtime-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
@@ -818,6 +825,7 @@
 includeProject(":palette:palette-ktx", [BuildType.MAIN])
 includeProject(":pdf:integration-tests:testapp", [BuildType.MAIN])
 includeProject(":pdf:pdf-viewer", [BuildType.MAIN])
+includeProject(":pdf:pdf-viewer-fragment", [BuildType.MAIN])
 includeProject(":percentlayout:percentlayout", [BuildType.MAIN])
 includeProject(":preference:preference", [BuildType.MAIN])
 includeProject(":preference:preference-ktx", [BuildType.MAIN])
@@ -836,10 +844,9 @@
 includeProject(":privacysandbox:sdkruntime:sdkruntime-core", [BuildType.MAIN])
 includeProject(":privacysandbox:sdkruntime:sdkruntime-provider", [BuildType.MAIN])
 includeProject(":privacysandbox:sdkruntime:test-sdks:current", [BuildType.MAIN])
-includeProject(":privacysandbox:sdkruntime:test-sdks:v1", [BuildType.MAIN])
-includeProject(":privacysandbox:sdkruntime:test-sdks:v2", [BuildType.MAIN])
 includeProject(":privacysandbox:sdkruntime:test-sdks:v4", [BuildType.MAIN])
 includeProject(":privacysandbox:sdkruntime:test-sdks:v5", [BuildType.MAIN])
+includeProject(":privacysandbox:sdkruntime:test-sdks:v6", [BuildType.MAIN])
 includeProject(":privacysandbox:tools:tools", [BuildType.MAIN])
 includeProject(":privacysandbox:tools:tools-apicompiler", [BuildType.MAIN])
 includeProject(":privacysandbox:tools:tools-apigenerator", [BuildType.MAIN])
@@ -939,7 +946,6 @@
 includeProject(":test:screenshot:screenshot-proto")
 includeProject(":test:uiautomator:uiautomator", [BuildType.MAIN])
 includeProject(":test:uiautomator:integration-tests:testapp", [BuildType.MAIN])
-includeProject(":text:text", [BuildType.COMPOSE])
 includeProject(":tracing:tracing")
 includeProject(":tracing:tracing-ktx")
 includeProject(":tracing:tracing-perfetto")
@@ -1080,6 +1086,8 @@
 File repoRoot = new File(rootDir, "../..").canonicalFile
 
 includeOptionalProject(":xr:xr", new File(repoRoot, "xr/xr"), [BuildType.XR])
+includeOptionalProject(":xr:integration-tests:compose-adaptive-sample", new File(repoRoot, "xr/integration-tests/compose-adaptive-sample"), [BuildType.XR])
+includeOptionalProject(":xr:xr-material3-adaptive", new File(repoRoot, "xr/xr-material3-adaptive"), [BuildType.XR])
 
 /////////////////////////////
 //
diff --git a/sharetarget/sharetarget/build.gradle b/sharetarget/sharetarget/build.gradle
index c167398..3b7ba5a 100644
--- a/sharetarget/sharetarget/build.gradle
+++ b/sharetarget/sharetarget/build.gradle
@@ -47,7 +47,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "ShareTarget"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/slice/slice-benchmark/build.gradle b/slice/slice-benchmark/build.gradle
index 09530b3..f895e78 100644
--- a/slice/slice-benchmark/build.gradle
+++ b/slice/slice-benchmark/build.gradle
@@ -59,5 +59,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.slice.benchmark"
 }
diff --git a/slice/slice-benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java b/slice/slice-benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
index dc3c37d..49db9e7 100644
--- a/slice/slice-benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
+++ b/slice/slice-benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
@@ -16,11 +16,6 @@
 
 package androidx.slice;
 
-
-import static android.app.slice.SliceItem.FORMAT_ACTION;
-import static android.app.slice.SliceItem.FORMAT_SLICE;
-import static android.app.slice.SliceItem.FORMAT_TEXT;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
@@ -168,10 +163,10 @@
 
     private void assertEquivalent(SliceItem desired, SliceItem actual) {
         assertEquals(desired.getFormat(), actual.getFormat());
-        boolean isSliceType = FORMAT_SLICE.equals(desired.getFormat())
-                || FORMAT_ACTION.equals(desired.getFormat());
+        boolean isSliceType = android.app.slice.SliceItem.FORMAT_SLICE.equals(desired.getFormat())
+                || android.app.slice.SliceItem.FORMAT_ACTION.equals(desired.getFormat());
         if (!isSliceType) {
-            if (FORMAT_TEXT.equals(desired.getFormat())) {
+            if (android.app.slice.SliceItem.FORMAT_TEXT.equals(desired.getFormat())) {
                 assertEquals(String.valueOf(desired.getText()), String.valueOf(actual.getText()));
             }
         }
diff --git a/slice/slice-builders-ktx/build.gradle b/slice/slice-builders-ktx/build.gradle
index 3e68849..2966d62 100644
--- a/slice/slice-builders-ktx/build.gradle
+++ b/slice/slice-builders-ktx/build.gradle
@@ -38,7 +38,7 @@
 
 dependencies {
     implementation(project(":slice:slice-core"))
-    api "androidx.annotation:annotation:1.1.0"
+    api "androidx.annotation:annotation:1.8.1"
     implementation("androidx.core:core:1.3.0")
     api(project(":slice:slice-builders"))
     api(libs.kotlinStdlib)
@@ -57,7 +57,6 @@
     mavenVersion = LibraryVersions.SLICE_BUILDERS_KTX
     inceptionYear = "2018"
     description = "A set of Kotlin extension methods built on top of slice-builders APIs."
-    metalavaK2UastEnabled = true
     failOnDeprecationWarnings = false
     legacyDisableKotlinStrictApiMode = true
 
diff --git a/slice/slice-builders/build.gradle b/slice/slice-builders/build.gradle
index b5d3806..eceac57 100644
--- a/slice/slice-builders/build.gradle
+++ b/slice/slice-builders/build.gradle
@@ -32,7 +32,7 @@
 dependencies {
     implementation(project(":slice:slice-core"))
     api("androidx.remotecallback:remotecallback:1.0.0-alpha02")
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 }
@@ -44,7 +44,6 @@
     mavenVersion = LibraryVersions.SLICE
     inceptionYear = "2017"
     description = "A set of builders to create templates using SliceProvider APIs"
-    metalavaK2UastEnabled = true
     failOnDeprecationWarnings = false
 
     deviceTests {
diff --git a/slice/slice-builders/lint-baseline.xml b/slice/slice-builders/lint-baseline.xml
index e6c36b0..468b4e7 100644
--- a/slice/slice-builders/lint-baseline.xml
+++ b/slice/slice-builders/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="WrongConstant"
@@ -11,24 +11,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.slice.builders.impl.ListBuilderBasicImpl is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        setTtl(ttl == null ? INFINITY : ttl.toMillis());"
-        errorLine2="                                            ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.slice.builders.impl.ListBuilderImpl is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="        setTtl(ttl == null ? INFINITY : ttl.toMillis());"
-        errorLine2="                                            ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/slice/builders/impl/ListBuilderImpl.java"/>
-    </issue>
-
-    <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
         errorLine1="    @RequiresApi(21)"
diff --git a/slice/slice-core/build.gradle b/slice/slice-core/build.gradle
index 63f826f..2a55f0c 100644
--- a/slice/slice-core/build.gradle
+++ b/slice/slice-core/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.appcompat:appcompat:1.4.0")
     implementation("androidx.collection:collection:1.1.0")
 
@@ -50,7 +50,6 @@
     mavenVersion = LibraryVersions.SLICE
     inceptionYear = "2017"
     description = "The slices core library provides utilities for the slices view and provider libraries"
-    metalavaK2UastEnabled = true
     failOnDeprecationWarnings = false
 
     deviceTests {
diff --git a/slice/slice-remotecallback/build.gradle b/slice/slice-remotecallback/build.gradle
index d587921..2cbdcd7 100644
--- a/slice/slice-remotecallback/build.gradle
+++ b/slice/slice-remotecallback/build.gradle
@@ -50,7 +50,6 @@
     mavenVersion = LibraryVersions.SLICE_REMOTECALLBACK
     inceptionYear = "2019"
     description = "A library that handles PendingIntents in slices as remote callbacks"
-    metalavaK2UastEnabled = true
     failOnDeprecationWarnings = false
 
     deviceTests {
diff --git a/slice/slice-test/build.gradle b/slice/slice-test/build.gradle
index 3bf9712..2e95f16 100644
--- a/slice/slice-test/build.gradle
+++ b/slice/slice-test/build.gradle
@@ -59,5 +59,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.slice.test"
 }
diff --git a/slice/slice-view/build.gradle b/slice/slice-view/build.gradle
index cecbb5b..04d5276 100644
--- a/slice/slice-view/build.gradle
+++ b/slice/slice-view/build.gradle
@@ -54,7 +54,6 @@
     mavenVersion = LibraryVersions.SLICE
     inceptionYear = "2017"
     description = "A library that handles rendering of slice content into supported templates"
-    metalavaK2UastEnabled = true
     failOnDeprecationWarnings = false
 
     deviceTests {
@@ -64,5 +63,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.slice.view"
 }
diff --git a/slice/slice-view/lint-baseline.xml b/slice/slice-view/lint-baseline.xml
index 6f2e88b..48082e4 100644
--- a/slice/slice-view/lint-baseline.xml
+++ b/slice/slice-view/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanSynchronizedMethods"
@@ -11,15 +11,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.slice.widget.RemoteInputView.RemoteEditText is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                return isTemporarilyDetached();"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/slice/widget/RemoteInputView.java"/>
-    </issue>
-
-    <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
         errorLine1="                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {"
diff --git a/slice/slice-view/src/main/res/values-es-rUS/strings.xml b/slice/slice-view/src/main/res/values-es-rUS/strings.xml
index ce7b894..31c62d5 100644
--- a/slice/slice-view/src/main/res/values-es-rUS/strings.xml
+++ b/slice/slice-view/src/main/res/values-es-rUS/strings.xml
@@ -32,7 +32,7 @@
       <item quantity="one">Hace <xliff:g id="ID_1">%d</xliff:g> año</item>
     </plurals>
     <plurals name="abc_slice_duration_days" formatted="false" msgid="8356547162075064530">
-      <item quantity="many">Hace <xliff:g id="ID_2">%d</xliff:g> días</item>
+      <item quantity="many">Hace <xliff:g id="ID_2">%d</xliff:g> de días</item>
       <item quantity="other">Hace <xliff:g id="ID_2">%d</xliff:g> días</item>
       <item quantity="one">Hace <xliff:g id="ID_1">%d</xliff:g> día</item>
     </plurals>
diff --git a/slice/slice-view/src/main/res/values-it/strings.xml b/slice/slice-view/src/main/res/values-it/strings.xml
index 8319289..58e8fa9 100644
--- a/slice/slice-view/src/main/res/values-it/strings.xml
+++ b/slice/slice-view/src/main/res/values-it/strings.xml
@@ -22,7 +22,7 @@
     <string name="abc_slice_show_more" msgid="1112789899890391107">"Mostra altro"</string>
     <string name="abc_slice_updated" msgid="7932359091871934205">"Ultimo aggiornamento: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <plurals name="abc_slice_duration_min" formatted="false" msgid="7664017844210142826">
-      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> min fa</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> di min fa</item>
       <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min fa</item>
       <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min fa</item>
     </plurals>
@@ -32,7 +32,7 @@
       <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> anno fa</item>
     </plurals>
     <plurals name="abc_slice_duration_days" formatted="false" msgid="8356547162075064530">
-      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> giorni fa</item>
+      <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> di giorni fa</item>
       <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> giorni fa</item>
       <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> giorno fa</item>
     </plurals>
diff --git a/slidingpanelayout/slidingpanelayout/build.gradle b/slidingpanelayout/slidingpanelayout/build.gradle
index e4447cd..818c90e8 100644
--- a/slidingpanelayout/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/slidingpanelayout/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.core:core-ktx:1.1.0")
     api("androidx.customview:customview:1.1.0")
     implementation("androidx.window:window:1.2.0")
@@ -34,7 +34,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "SlidingPaneLayout offers a responsive, two pane layout that automatically switches between overlapping panes on smaller devices to a side by side view on larger devices."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/sqlite/integration-tests/inspection-room-testapp/lint-baseline.xml b/sqlite/integration-tests/inspection-room-testapp/lint-baseline.xml
index ce89072..919b2bd 100644
--- a/sqlite/integration-tests/inspection-room-testapp/lint-baseline.xml
+++ b/sqlite/integration-tests/inspection-room-testapp/lint-baseline.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.4.0-alpha08" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha08)" variant="all" version="7.4.0-alpha08">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="MissingTestSizeAnnotation"
         message="Missing test size annotation"
-        errorLine1="    fun invalidationHook() = runBlocking&lt;Unit>(testJob) {"
+        errorLine1="    fun invalidationHook() ="
         errorLine2="        ~~~~~~~~~~~~~~~~">
         <location
             file="src/androidTest/java/androidx/sqlite/inspection/RoomInvalidationHookTest.kt"/>
diff --git a/sqlite/sqlite-bundled/bcv/native/current.txt b/sqlite/sqlite-bundled/bcv/native/current.txt
index 7547657..bcd1e0c 100644
--- a/sqlite/sqlite-bundled/bcv/native/current.txt
+++ b/sqlite/sqlite-bundled/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/sqlite/sqlite-bundled/build.gradle b/sqlite/sqlite-bundled/build.gradle
index 62d4332..6c4caeb 100644
--- a/sqlite/sqlite-bundled/build.gradle
+++ b/sqlite/sqlite-bundled/build.gradle
@@ -237,8 +237,6 @@
             }
         }
     }
-
-    enableBinaryCompatibilityValidator = true
 }
 
 android {
diff --git a/sqlite/sqlite-framework/bcv/native/current.txt b/sqlite/sqlite-framework/bcv/native/current.txt
index 557991c..da783e2 100644
--- a/sqlite/sqlite-framework/bcv/native/current.txt
+++ b/sqlite/sqlite-framework/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/sqlite/sqlite-framework/build.gradle b/sqlite/sqlite-framework/build.gradle
index 035f21f..b91ad39 100644
--- a/sqlite/sqlite-framework/build.gradle
+++ b/sqlite/sqlite-framework/build.gradle
@@ -69,7 +69,7 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api("androidx.annotation:annotation:1.8.0")
+                api(project(":annotation:annotation"))
                 api(project(":sqlite:sqlite"))
             }
         }
@@ -134,8 +134,6 @@
             }
         }
     }
-
-    enableBinaryCompatibilityValidator = true
 }
 
 dependencies {
@@ -148,7 +146,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "The implementation of SQLite library using the framework code."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/sqlite/sqlite-inspection/build.gradle b/sqlite/sqlite-inspection/build.gradle
index 950e5c3..4cc8b06 100644
--- a/sqlite/sqlite-inspection/build.gradle
+++ b/sqlite/sqlite-inspection/build.gradle
@@ -32,7 +32,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     compileOnly(project(":inspection:inspection"))
 
     androidTestImplementation(project(":inspection:inspection-testing"))
diff --git a/sqlite/sqlite-ktx/build.gradle b/sqlite/sqlite-ktx/build.gradle
index 6fa6d03..b03f306 100644
--- a/sqlite/sqlite-ktx/build.gradle
+++ b/sqlite/sqlite-ktx/build.gradle
@@ -42,7 +42,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for DB"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/sqlite/sqlite/bcv/native/current.txt b/sqlite/sqlite/bcv/native/current.txt
index c26aa6f..39d8d47 100644
--- a/sqlite/sqlite/bcv/native/current.txt
+++ b/sqlite/sqlite/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxX64, macosArm64, macosX64]
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/sqlite/sqlite/build.gradle b/sqlite/sqlite/build.gradle
index bc58cf2..6b8ed860 100644
--- a/sqlite/sqlite/build.gradle
+++ b/sqlite/sqlite/build.gradle
@@ -45,7 +45,7 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api("androidx.annotation:annotation:1.8.0")
+                api(project(":annotation:annotation"))
             }
         }
         commonTest {
@@ -79,8 +79,6 @@
             }
         }
     }
-
-    enableBinaryCompatibilityValidator = true
 }
 
 android {
@@ -92,6 +90,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "SQLite API"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/stableaidl/stableaidl-gradle-plugin/lint-baseline.xml b/stableaidl/stableaidl-gradle-plugin/lint-baseline.xml
index 24cc85a..dadfd1a 100644
--- a/stableaidl/stableaidl-gradle-plugin/lint-baseline.xml
+++ b/stableaidl/stableaidl-gradle-plugin/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="InternalGradleApiUsage"
@@ -13,8 +13,8 @@
     <issue
         id="WithPluginClasspathUsage"
         message="Avoid usage of GradleRunner#withPluginClasspath, which is broken. Instead use something like https://github.com/autonomousapps/dependency-analysis-gradle-plugin/tree/main/testkit#gradle-testkit-support-plugin"
-        errorLine1="            .withPluginClasspath()"
-        errorLine2="             ~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            GradleRunner.create().withProjectDir(projectSetup.rootDir).withPluginClasspath()"
+        errorLine2="                                                                       ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/test/java/androidx/stableaidl/StableAidlPluginTest.kt"/>
     </issue>
diff --git a/startup/integration-tests/first-library/build.gradle b/startup/integration-tests/first-library/build.gradle
index 3acf247..ef0402b 100644
--- a/startup/integration-tests/first-library/build.gradle
+++ b/startup/integration-tests/first-library/build.gradle
@@ -35,5 +35,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.startup.first_library"
 }
diff --git a/startup/integration-tests/second-library/build.gradle b/startup/integration-tests/second-library/build.gradle
index 68a15f7..96216cb 100644
--- a/startup/integration-tests/second-library/build.gradle
+++ b/startup/integration-tests/second-library/build.gradle
@@ -34,5 +34,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.startup.second_library"
 }
diff --git a/startup/integration-tests/test-app/build.gradle b/startup/integration-tests/test-app/build.gradle
index 93f6d4d..f7893ef 100644
--- a/startup/integration-tests/test-app/build.gradle
+++ b/startup/integration-tests/test-app/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdkVersion 35
     buildTypes {
         getByName("release") {
             minifyEnabled = true
diff --git a/startup/startup-runtime/build.gradle b/startup/startup-runtime/build.gradle
index 177105e..c0b692d 100644
--- a/startup/startup-runtime/build.gradle
+++ b/startup/startup-runtime/build.gradle
@@ -41,7 +41,7 @@
     // baseline profile rules, but profileinstaller calls startup APIs, so we have to avoid
     // the circular dependency
 
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.tracing:tracing:1.0.0")
     lintPublish(project(":startup:startup-runtime-lint"))
     androidTestImplementation(libs.kotlinStdlib)
@@ -59,6 +59,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android App Startup Runtime"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/swiperefreshlayout/swiperefreshlayout/build.gradle b/swiperefreshlayout/swiperefreshlayout/build.gradle
index d79f78c..28bce37 100644
--- a/swiperefreshlayout/swiperefreshlayout/build.gradle
+++ b/swiperefreshlayout/swiperefreshlayout/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
     api("androidx.interpolator:interpolator:1.0.0")
 
@@ -40,9 +40,9 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
-    metalavaK2UastEnabled = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.swiperefreshlayout"
 }
diff --git a/test/ext/junit-gtest/build.gradle b/test/ext/junit-gtest/build.gradle
index 2b9b0f3..d06e231 100644
--- a/test/ext/junit-gtest/build.gradle
+++ b/test/ext/junit-gtest/build.gradle
@@ -45,7 +45,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Run GTest tests on Android devices"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     // Bypassed to better match existing androidx.test libraries
     bypassCoordinateValidation = true
diff --git a/test/screenshot/screenshot/build.gradle b/test/screenshot/screenshot/build.gradle
index 4155e6e..89e9f33 100644
--- a/test/screenshot/screenshot/build.gradle
+++ b/test/screenshot/screenshot/build.gradle
@@ -44,7 +44,7 @@
 
     api(libs.junit)
 
-    implementation("androidx.annotation:annotation:1.0.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation(libs.kotlinStdlib)
     implementation(libs.testMonitor)
 
diff --git a/test/screenshot/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.kt b/test/screenshot/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.kt
index a98ab3c..9f0979a 100644
--- a/test/screenshot/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.kt
+++ b/test/screenshot/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.kt
@@ -116,6 +116,9 @@
 
     class ScreenshotTestStatement(private val base: Statement) : Statement() {
         override fun evaluate() {
+            Assume.assumeTrue("Disabling screenshots tests due to b/355440484", false)
+
+            /* Reenable this part when FTL new image rollout is complete.
             if (Build.MODEL.contains("gphone")) {
                 // We support emulators with API 33
                 Assume.assumeTrue("Requires SDK 33.", Build.VERSION.SDK_INT == 33)
@@ -123,6 +126,7 @@
                 Assume.assumeTrue("Requires API 33 emulator", false)
             }
             base.evaluate()
+            */
         }
     }
 
diff --git a/test/uiautomator/integration-tests/testapp/build.gradle b/test/uiautomator/integration-tests/testapp/build.gradle
index eed2f10..80f7aa2 100644
--- a/test/uiautomator/integration-tests/testapp/build.gradle
+++ b/test/uiautomator/integration-tests/testapp/build.gradle
@@ -50,5 +50,6 @@
 }
 
 android {
+    compileSdkVersion 35
     namespace "androidx.test.uiautomator.testapp"
 }
diff --git a/test/uiautomator/uiautomator/build.gradle b/test/uiautomator/uiautomator/build.gradle
index a25a404..dd7255e 100644
--- a/test/uiautomator/uiautomator/build.gradle
+++ b/test/uiautomator/uiautomator/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     implementation(libs.junit)
-    implementation("androidx.annotation:annotation:1.4.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.tracing:tracing:1.1.0")
 
     androidTestImplementation(libs.mockitoCore)
@@ -55,5 +55,4 @@
     inceptionYear = "2012"
     description = "UI testing framework suitable for cross-app functional UI testing"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/testutils/testutils-espresso/build.gradle b/testutils/testutils-espresso/build.gradle
index 87da559..87744e1 100644
--- a/testutils/testutils-espresso/build.gradle
+++ b/testutils/testutils-espresso/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation(libs.espressoCore, excludes.espresso)
     implementation(libs.kotlinStdlib)
diff --git a/testutils/testutils-ktx/build.gradle b/testutils/testutils-ktx/build.gradle
index fd63ad7..4cd45f0 100644
--- a/testutils/testutils-ktx/build.gradle
+++ b/testutils/testutils-ktx/build.gradle
@@ -29,10 +29,6 @@
 
 androidXMultiplatform {
     ios()
-    js {
-        nodejs()
-        binaries.executable()
-    }
     jvm()
     linux()
     mac()
diff --git a/testutils/testutils-lifecycle/build.gradle b/testutils/testutils-lifecycle/build.gradle
index 1da51cc..b771af6 100644
--- a/testutils/testutils-lifecycle/build.gradle
+++ b/testutils/testutils-lifecycle/build.gradle
@@ -47,7 +47,7 @@
         commonMain {
             dependencies {
                 api(projectOrArtifact(":lifecycle:lifecycle-runtime"))
-                api("androidx.annotation:annotation:1.8.0")
+                api("androidx.annotation:annotation:1.8.1")
 
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
diff --git a/testutils/testutils-runtime/lint-baseline.xml b/testutils/testutils-runtime/lint-baseline.xml
index 1420667..129a1ee 100644
--- a/testutils/testutils-runtime/lint-baseline.xml
+++ b/testutils/testutils-runtime/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -37,22 +37,4 @@
             file="src/main/java/androidx/testutils/AnimationDurationScaleRule.kt"/>
     </issue>
 
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.testutils.LocaleTestUtils is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="                newConfig.setLocales(locales.unwrap() as LocaleList)"
-        errorLine2="                          ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/testutils/LocaleTestUtils.kt"/>
-    </issue>
-
-    <issue
-        id="LambdaLast"
-        message="Functional interface parameters (such as parameter 1, &quot;owner&quot;, in androidx.testutils.LifecycleOwnerUtils.waitUntilState) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
-        errorLine1="            final @NonNull Lifecycle.State state) throws Throwable {"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/testutils/LifecycleOwnerUtils.java"/>
-    </issue>
-
 </issues>
diff --git a/text/text/OWNERS b/text/text/OWNERS
deleted file mode 100644
index 0db9610..0000000
--- a/text/text/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# Bug component: 630734
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
\ No newline at end of file
diff --git a/text/text/build.gradle b/text/text/build.gradle
deleted file mode 100644
index 8174e72..0000000
--- a/text/text/build.gradle
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This file was created using the `create_project.py` script located in the
- * `<AndroidX root>/development/project-creator` directory.
- *
- * Please use that script when creating a new project, rather than copying an existing project and
- * modifying its settings.
- */
-import androidx.build.Publish
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-    id("AndroidXComposePlugin")
-    id("org.jetbrains.kotlin.android")
-}
-
-dependencies {
-    implementation(libs.kotlinStdlib)
-    implementation("androidx.core:core:1.7.0")
-    implementation("androidx.emoji2:emoji2:1.4.0")
-
-    api "androidx.annotation:annotation:1.2.0"
-
-    testImplementation(libs.testRules)
-    testImplementation(libs.testRunner)
-    testImplementation(libs.junit)
-
-    androidTestImplementation(project(":internal-testutils-fonts"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(libs.espressoCore)
-    androidTestImplementation(libs.junit)
-    androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation(libs.mockitoKotlin, {
-        exclude group: "org.mockito" // to keep control on the mockito version
-    })
-}
-
-androidx {
-    name = "UI Text utilities"
-    publish = Publish.NONE
-    inceptionYear = "2020"
-    description = "Text primitives and utilities"
-    legacyDisableKotlinStrictApiMode = true
-}
-
-androidxCompose {
-    composeCompilerPluginEnabled = false
-}
-
-android {
-    namespace "androidx.compose.ui.text.android"
-}
diff --git a/text/text/lint-baseline.xml b/text/text/lint-baseline.xml
deleted file mode 100644
index 2a6edab..0000000
--- a/text/text/lint-baseline.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-alpha10" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-alpha10)" variant="all" version="8.3.0-alpha10">
-
-    <issue
-        id="PrimitiveInCollection"
-        message="field paragraphEnds with type List&lt;Integer>: replace with IntList"
-        errorLine1="    private val paragraphEnds: List&lt;Int>"
-        errorLine2="                               ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/LayoutHelper.android.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable lineFeeds with type List&lt;Integer>: replace with IntList"
-        errorLine1="        val lineFeeds = mutableListOf&lt;Int>()"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/LayoutHelper.android.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="return type List&lt;Integer> of breakInWords: replace with IntList"
-        errorLine1="    private fun breakInWords(layoutHelper: LayoutHelper): List&lt;Int> {"
-        errorLine2="                                                          ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable words with type List&lt;? extends Integer>: replace with IntList"
-        errorLine1="        val words = breakWithBreakIterator(text, BreakIterator.getLineInstance(Locale.getDefault()))"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable set with type TreeSet&lt;Integer>: replace with IntSet"
-        errorLine1="        val set = TreeSet&lt;Int>().apply {"
-        errorLine2="        ^">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="return type List&lt;Integer> of breakWithBreakIterator: replace with IntList"
-        errorLine1="    private fun breakWithBreakIterator(text: CharSequence, breaker: BreakIterator): List&lt;Int> {"
-        errorLine2="                                                                                    ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="variable res with type List&lt;Integer>: replace with IntList"
-        errorLine1="        val res = mutableListOf(0)"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInCollection"
-        message="return type List&lt;Integer> of breakOffsets: replace with IntList"
-        errorLine1="    fun breakOffsets(layoutHelper: LayoutHelper, segmentType: SegmentType): List&lt;Int> {"
-        errorLine2="                                                                            ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt"/>
-    </issue>
-
-</issues>
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordIteratorTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordIteratorTest.kt
deleted file mode 100644
index d33f3bc..0000000
--- a/text/text/src/androidTest/java/androidx/compose/ui/text/android/selection/WordIteratorTest.kt
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.android.selection
-
-import androidx.emoji2.text.EmojiCompat
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth.assertThat
-import java.text.BreakIterator
-import java.util.Locale
-import org.junit.AfterClass
-import org.junit.BeforeClass
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class WordIteratorTest {
-    companion object {
-        private val context = InstrumentationRegistry.getInstrumentation().targetContext
-
-        @BeforeClass
-        @JvmStatic
-        fun setup() {
-            EmojiCompat.reset(null)
-            EmojiCompat.init(context)
-        }
-
-        @AfterClass
-        @JvmStatic
-        fun clean() {
-            EmojiCompat.reset(null)
-        }
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testConstructor_IndexOutOfBounds_too_big() {
-        WordIterator("text", 100, 100, Locale.ENGLISH)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testConstructor_IndexOutOfBounds_too_small() {
-        WordIterator("text", -100, -100, Locale.ENGLISH)
-    }
-
-    @Test
-    fun testConstructor_valid_full_text() {
-        val text = "text"
-        WordIterator(text, 0, text.length, Locale.ENGLISH)
-    }
-
-    @Test
-    fun testConstructor_valid_beginning() {
-        val text = "text"
-        WordIterator(text, 0, 0, Locale.ENGLISH)
-    }
-
-    @Test
-    fun testConstructor_valid_end() {
-        val text = "text"
-        WordIterator(text, 0, text.length, Locale.ENGLISH)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testNextBoundary_out_of_boundary_too_small() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.nextBoundary(-1)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testNextBoundary_out_of_boundary_too_big() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.nextBoundary(text.length + 1)
-    }
-
-    @Test
-    fun testNextBoundary_iterate_through() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        // Start from the beginning.
-        var currentOffset = 0
-        // The word is "abc".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('c') + 1)
-        // The word is space.
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('d'))
-        // The word is "def".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('f') + 1)
-        // The word is "-".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('g'))
-        // The word is "ghi".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('i') + 1)
-        // The word is ".".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
-        // The word is space.
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('j'))
-        // The word is "jkl".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.length)
-        // WordIterator reaches the end.
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
-    }
-
-    @Test
-    fun testNextBoundary_iterate_through_RTL() { // Hebrew -- "אבג דה-וז. חט"
-        val text = "\u05d0\u05d1\u05d2 \u05d3\u05d4-\u05d5\u05d6. \u05d7\u05d8"
-        val wordIterator = WordIterator(text, 0, text.length, Locale("he", "IL"))
-        // Start from the beginning.
-        var currentOffset = 0
-        // The word is "\u05d0\u05d1\u05d2"("אבג")
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d2') + 1)
-        // The word is space.
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d3'))
-        // The word is "\u05d3\u05d4"("דה")
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('-'))
-        // The word is "-".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d5'))
-        // The word is "\u05d5\u05d6("וז")
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('.'))
-        // The word is ".".
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
-        // The word is space.
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d7'))
-        // The word is "\u05d7\u05d8"("חט")
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.length)
-        // WordIterator reaches the end.
-        currentOffset = wordIterator.nextBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
-    }
-
-    @Test(timeout = 5000)
-    fun testNextBoundary_emoji() {
-        assertThat(EmojiCompat.isConfigured()).isTrue()
-        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
-        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
-
-        val text = "ab, c\uD83D\uDC4D\uD83C\uDFFEd!" // ab, c👍🏾d!
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.nextBoundary(4)).isEqualTo(10)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testPrevBoundary_out_of_boundary_too_small() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.prevBoundary(-1)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testPrevBoundary_out_of_boundary_too_big() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.prevBoundary(text.length + 1)
-    }
-
-    @Test
-    fun testPrevBoundary_iterate_through() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        // Start from the end.
-        var currentOffset = text.length
-        // The word is "jkl".
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('j'))
-        // The word is space.
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
-        // The word is ".".
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('i') + 1)
-        // The word is "ghi".
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('g'))
-        // The word is "-".
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('f') + 1)
-        // The word is "def".
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('d'))
-        // The word is space.
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('c') + 1)
-        // The word is "abc".
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('a'))
-        // WordIterator reaches the beginning.
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
-    }
-
-    @Test
-    fun testPrevBoundary_iterate_through_RTL() { // Hebrew -- "אבג דה-וז. חט"
-        val text = "\u05d0\u05d1\u05d2 \u05d3\u05d4-\u05d5\u05d6. \u05d7\u05d8"
-        val wordIterator = WordIterator(text, 0, text.length, Locale("he", "IL"))
-        // Start from the end.
-        var currentOffset = text.length
-        // The word is "\u05d7\u05d8"("חט")
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d7'))
-        // The word is space.
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('.') + 1)
-        // The word is '.'.
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('.'))
-        // The word is "\u05d5\u05d6("וז")
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d5'))
-        // The word is "-".
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('-'))
-        // The word is "\u05d3\u05d4"("דה")
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d3'))
-        // The word is space.
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf(' '))
-        // The word is "\u05d0\u05d1\u05d2"("אבג")
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(text.indexOf('\u05d0'))
-        // WordIterator reaches the beginning.
-        currentOffset = wordIterator.prevBoundary(currentOffset)
-        assertThat(currentOffset).isEqualTo(BreakIterator.DONE)
-    }
-
-    @Test(timeout = 5000)
-    fun testPrevBoundary_emoji() {
-        assertThat(EmojiCompat.isConfigured()).isTrue()
-        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
-        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
-
-        val text = "ab, c\uD83D\uDC4D\uD83C\uDFFE!" // ab, c👍🏾!
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.prevBoundary(9)).isEqualTo(4)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPrevWordBeginningOnTwoWordsBoundary_out_of_boundary_too_small() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPrevWordBeginningOnTwoWordsBoundary(-1)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPrevWordBeginningOnTwoWordsBoundary_out_of_boundary_too_big() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length + 1)
-    }
-
-    @Test
-    fun testGetPrevWordBeginningOnTwoWordsBoundary_Empty_String() {
-        val text = ""
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(0))
-            .isEqualTo(BreakIterator.DONE)
-    }
-
-    @Test
-    fun testGetPrevWordBeginningOnTwoWordsBoundary() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('a')))
-            .isEqualTo(text.indexOf('a'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('c')))
-            .isEqualTo(text.indexOf('a'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('d')))
-            .isEqualTo(text.indexOf('d'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('f')))
-            .isEqualTo(text.indexOf('d'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('-')))
-            .isEqualTo(text.indexOf('d'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('g')))
-            .isEqualTo(text.indexOf('g'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('.')))
-            .isEqualTo(text.indexOf('g'))
-    }
-
-    @Test
-    fun testGetPrevWordBeginningOnTwoWordsBoundary_CJK() {
-        // Japanese HIRAGANA letter + KATAKANA letters -- "あアィイ"
-        val text = "\u3042\u30A2\u30A3\u30A4"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.JAPANESE)
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u3042')))
-            .isEqualTo(text.indexOf('\u3042'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A2')))
-            .isEqualTo(text.indexOf('\u3042'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A4')))
-            .isEqualTo(text.indexOf('\u30A2'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length))
-            .isEqualTo(text.indexOf('\u30A2'))
-    }
-
-    @Test
-    fun testGetPrevWordBeginningOnTwoWordsBoundary_apostropheMiddleOfWord() {
-        // These tests confirm that the word "isn't" is treated like one word.
-        val text = "isn't he"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('i')))
-            .isEqualTo(text.indexOf('i'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('n')))
-            .isEqualTo(text.indexOf('i'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\'')))
-            .isEqualTo(text.indexOf('i'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('t')))
-            .isEqualTo(text.indexOf('i'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('t') + 1))
-            .isEqualTo(text.indexOf('i'))
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('h')))
-            .isEqualTo(text.indexOf('h'))
-    }
-
-    @Test(timeout = 5000)
-    fun testGetPrevWordBeginningOnTwoWordsBoundary_emoji() {
-        assertThat(EmojiCompat.isConfigured()).isTrue()
-        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
-        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
-
-        val text = "a b\uD83E\uDDD1\uD83C\uDFFF\u200D\uD83E\uDDB0c\uD83D\uDC4D\uD83C\uDFFE d"
-        // a b🧑🏿‍🦰c👍🏾 d
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('d') - 2))
-            .isEqualTo(text.indexOf('b'))
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetNextWordEndOnTwoWordBoundary_out_of_boundary_too_small() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getNextWordEndOnTwoWordBoundary(-1)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetNextWordEndOnTwoWordBoundary_out_of_boundary_too_big() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getNextWordEndOnTwoWordBoundary(text.length + 1)
-    }
-
-    @Test
-    fun testGetNextWordEndOnTwoWordBoundary_Empty_String() {
-        val text = ""
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(0)).isEqualTo(BreakIterator.DONE)
-    }
-
-    @Test
-    fun testGetNextWordEndOnTwoWordBoundary() {
-        val text = "abc def-ghi. jkl"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('a')))
-            .isEqualTo(text.indexOf(' '))
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('c')))
-            .isEqualTo(text.indexOf(' '))
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('d')))
-            .isEqualTo(text.indexOf('-'))
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('f')))
-            .isEqualTo(text.indexOf('-'))
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('-')))
-            .isEqualTo(text.indexOf('-'))
-    }
-
-    @Test
-    fun testGetNextWordEndOnTwoWordBoundary_CJK() {
-        // Japanese HIRAGANA letter + KATAKANA letters -- "あアィイ"
-        val text = "\u3042\u30A2\u30A3\u30A4"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.JAPANESE)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u3042')))
-            .isEqualTo(text.indexOf('\u3042') + 1)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A2')))
-            .isEqualTo(text.indexOf('\u30A4') + 1)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4')))
-            .isEqualTo(text.indexOf('\u30A4') + 1)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4') + 1))
-            .isEqualTo(text.indexOf('\u30A4') + 1)
-    }
-
-    @Test
-    fun testGetNextWordEndOnTwoWordBoundary_apostropheMiddleOfWord() {
-        // These tests confirm that the word "isn't" is treated like one word.
-        val text = "isn't he"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('i')))
-            .isEqualTo(text.indexOf('t') + 1)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('n')))
-            .isEqualTo(text.indexOf('t') + 1)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\'')))
-            .isEqualTo(text.indexOf('t') + 1)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('t')))
-            .isEqualTo(text.indexOf('t') + 1)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('h')))
-            .isEqualTo(text.indexOf('e') + 1)
-    }
-
-    @Test(timeout = 5000)
-    fun testGetNextWordEndOnTwoWordBoundary_emoji() {
-        assertThat(EmojiCompat.isConfigured()).isTrue()
-        // Wait for EmojiCompat to fully load (this is necessary due to inclusion of emojis)
-        while (EmojiCompat.get().loadState != EmojiCompat.LOAD_STATE_SUCCEEDED) {}
-
-        val text = "a b\uD83E\uDDD1\uD83C\uDFFF\u200D\uD83E\uDDB0c\uD83D\uDC4D\uD83C\uDFFE d"
-        // a b🧑🏿‍🦰c👍🏾 d
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(2)).isEqualTo(text.indexOf('d') - 1)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPunctuationBeginning_out_of_boundary_too_small() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPunctuationBeginning(-2)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPunctuationBeginning_out_of_boundary_too_big() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPunctuationBeginning(text.length + 1)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPunctuationBeginning_DONE() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPunctuationBeginning(BreakIterator.DONE)
-    }
-
-    @Test
-    fun testGetPunctuationBeginning() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('a')))
-            .isEqualTo(BreakIterator.DONE)
-        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('c')))
-            .isEqualTo(BreakIterator.DONE)
-        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('!')))
-            .isEqualTo(text.indexOf('!'))
-        assertThat(wordIterator.getPunctuationBeginning(text.indexOf('?') + 1))
-            .isEqualTo(text.indexOf('!'))
-        assertThat(wordIterator.getPunctuationBeginning(text.indexOf(';')))
-            .isEqualTo(text.indexOf(';'))
-        assertThat(wordIterator.getPunctuationBeginning(text.indexOf(')')))
-            .isEqualTo(text.indexOf(';'))
-        assertThat(wordIterator.getPunctuationBeginning(text.length)).isEqualTo(text.indexOf(';'))
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPunctuationEnd_out_of_boundary_too_small() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPunctuationEnd(-2)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPunctuationEnd_out_of_boundary_too_big() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPunctuationEnd(text.length + 1)
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun testGetPunctuationEnd_DONE() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        wordIterator.getPunctuationEnd(BreakIterator.DONE)
-    }
-
-    @Test
-    fun testGetPunctuationEnd() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.getPunctuationEnd(text.indexOf('a')))
-            .isEqualTo(text.indexOf('?') + 1)
-        assertThat(wordIterator.getPunctuationEnd(text.indexOf('?') + 1))
-            .isEqualTo(text.indexOf('?') + 1)
-        assertThat(wordIterator.getPunctuationEnd(text.indexOf('(')))
-            .isEqualTo(text.indexOf('(') + 1)
-        assertThat(wordIterator.getPunctuationEnd(text.indexOf('(') + 2))
-            .isEqualTo(text.indexOf(')') + 1)
-        assertThat(wordIterator.getPunctuationEnd(text.indexOf(')') + 1))
-            .isEqualTo(text.indexOf(')') + 1)
-        assertThat(wordIterator.getPunctuationEnd(text.indexOf('d'))).isEqualTo(BreakIterator.DONE)
-        assertThat(wordIterator.getPunctuationEnd(text.length)).isEqualTo(BreakIterator.DONE)
-    }
-
-    @Test
-    fun testIsAfterPunctuation() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.isAfterPunctuation(text.indexOf('a'))).isFalse()
-        assertThat(wordIterator.isAfterPunctuation(text.indexOf('!'))).isFalse()
-        assertThat(wordIterator.isAfterPunctuation(text.indexOf('?'))).isTrue()
-        assertThat(wordIterator.isAfterPunctuation(text.indexOf('?') + 1)).isTrue()
-        assertThat(wordIterator.isAfterPunctuation(text.indexOf('d'))).isFalse()
-        assertThat(wordIterator.isAfterPunctuation(BreakIterator.DONE)).isFalse()
-        assertThat(wordIterator.isAfterPunctuation(text.length + 1)).isFalse()
-    }
-
-    @Test
-    fun testIsOnPunctuation() {
-        val text = "abc!? (^^;) def"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        assertThat(wordIterator.isOnPunctuation(text.indexOf('a'))).isFalse()
-        assertThat(wordIterator.isOnPunctuation(text.indexOf('!'))).isTrue()
-        assertThat(wordIterator.isOnPunctuation(text.indexOf('?'))).isTrue()
-        assertThat(wordIterator.isOnPunctuation(text.indexOf('?') + 1)).isFalse()
-        assertThat(wordIterator.isOnPunctuation(text.indexOf(')'))).isTrue()
-        assertThat(wordIterator.isOnPunctuation(text.indexOf(')') + 1)).isFalse()
-        assertThat(wordIterator.isOnPunctuation(text.indexOf('d'))).isFalse()
-        assertThat(wordIterator.isOnPunctuation(BreakIterator.DONE)).isFalse()
-        assertThat(wordIterator.isOnPunctuation(text.length)).isFalse()
-        assertThat(wordIterator.isOnPunctuation(text.length + 1)).isFalse()
-    }
-
-    @Test
-    fun testOneWord() {
-        val text = "zen"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsWord(wordIterator, 0, 3)
-    }
-
-    @Test
-    fun testSpacesOnly() {
-        val text = " "
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsNotWord(wordIterator, 0, 1)
-    }
-
-    @Test
-    fun testCommaWithSpace() {
-        val text = ", "
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsNotWord(wordIterator, 0, 2)
-    }
-
-    @Test
-    fun testSymbols() {
-        val text = ":-)"
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsNotWord(wordIterator, 0, 3)
-    }
-
-    @Test
-    fun testBeginningEnd1() {
-        val text = "Well hello,   there! "
-        //         0123456789012345678901
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsWord(wordIterator, 0, 4)
-        verifyIsWord(wordIterator, 5, 10)
-        verifyIsNotWord(wordIterator, 11, 13)
-        verifyIsWord(wordIterator, 14, 19)
-        verifyIsNotWord(wordIterator, 20, 21)
-    }
-
-    @Test
-    fun testBeginningEnd2() {
-        val text = "  Another - sentence"
-        //         012345678901234567890
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsNotWord(wordIterator, 0, 1)
-        verifyIsWord(wordIterator, 2, 9)
-        verifyIsNotWord(wordIterator, 10, 11)
-        verifyIsWord(wordIterator, 12, 20)
-    }
-
-    @Test
-    fun testBeginningEnd3() {
-        val text = "This is \u0644\u0627 tested" // Lama-aleph
-        //         012345678     9     01234567
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsWord(wordIterator, 0, 4)
-        verifyIsWord(wordIterator, 5, 7)
-        verifyIsWord(wordIterator, 8, 10)
-        verifyIsWord(wordIterator, 11, 17)
-    }
-
-    @Test
-    fun testSurrogate() {
-        val gothicBairkan = "\uD800\uDF31"
-        val text = "one " + gothicBairkan + "xxx word"
-        //         0123    45         678901234
-        val wordIterator = WordIterator(text, 0, text.length, Locale.ENGLISH)
-        verifyIsWord(wordIterator, 0, 3)
-        verifyIsWordWithSurrogate(wordIterator, 4, 9, 5)
-        verifyIsWord(wordIterator, 10, 14)
-    }
-
-    private fun verifyIsWordWithSurrogate(
-        wordIterator: WordIterator,
-        beginning: Int,
-        end: Int,
-        surrogateIndex: Int
-    ) {
-        for (i in beginning..end) {
-            if (i == surrogateIndex) continue
-            assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(i)).isEqualTo(beginning)
-            assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(i)).isEqualTo(end)
-        }
-    }
-
-    private fun verifyIsWord(wordIterator: WordIterator, beginning: Int, end: Int) {
-        verifyIsWordWithSurrogate(wordIterator, beginning, end, -1)
-    }
-
-    private fun verifyIsNotWord(wordIterator: WordIterator, beginning: Int, end: Int) {
-        for (i in beginning..end) {
-            assertThat(wordIterator.getPrevWordBeginningOnTwoWordsBoundary(i))
-                .isEqualTo(BreakIterator.DONE)
-            assertThat(wordIterator.getNextWordEndOnTwoWordBoundary(i))
-                .isEqualTo(BreakIterator.DONE)
-        }
-    }
-}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.android.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.android.kt
deleted file mode 100644
index 9decd69..0000000
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.android.kt
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.android
-
-import android.text.Layout
-import android.text.TextUtils
-import androidx.annotation.IntRange
-import java.text.Bidi
-
-private const val LINE_FEED = '\n'
-
-/**
- * Provide utilities for Layout class
- *
- * This class is not thread-safe. Do not share an instance with multiple threads.
- */
-internal class LayoutHelper(val layout: Layout) {
-
-    private val paragraphEnds: List<Int>
-
-    // Stores the list of Bidi object for each paragraph. This could be null if Bidi is not
-    // necessary, i.e. single direction text. Do not use this directly. Use analyzeBidi function
-    // instead.
-    private val paragraphBidi: MutableList<Bidi?>
-
-    // Stores true if the each paragraph already has bidi analyze result. Do not use this
-    // directly. Use analyzeBidi function instead.
-    private val bidiProcessedParagraphs: BooleanArray
-
-    // Temporary buffer for bidi processing.
-    private var tmpBuffer: CharArray? = null
-
-    init {
-        var paragraphEnd = 0
-        val lineFeeds = mutableListOf<Int>()
-        do {
-            paragraphEnd = layout.text.indexOf(char = LINE_FEED, startIndex = paragraphEnd)
-            if (paragraphEnd < 0) {
-                // No more LINE_FEED char found. Use the end of the text as the paragraph end.
-                paragraphEnd = layout.text.length
-            } else {
-                // increment since end offset is exclusive.
-                paragraphEnd++
-            }
-            lineFeeds.add(paragraphEnd)
-        } while (paragraphEnd < layout.text.length)
-        paragraphEnds = lineFeeds
-        paragraphBidi = MutableList(paragraphEnds.size) { null }
-        bidiProcessedParagraphs = BooleanArray(paragraphEnds.size)
-    }
-
-    /**
-     * Analyze the BiDi runs for the paragraphs and returns result object.
-     *
-     * Layout#isRtlCharAt or Layout#getLineDirection is not useful for determining preceding or
-     * following run in visual order. We need to analyze by ourselves.
-     *
-     * This may return null if the Bidi process is not necessary, i.e. there is only single bidi
-     * run.
-     *
-     * @param paragraphIndex a paragraph index
-     */
-    fun analyzeBidi(paragraphIndex: Int): Bidi? {
-        // If we already analyzed target paragraph, just return the result.
-        if (bidiProcessedParagraphs[paragraphIndex]) {
-            return paragraphBidi[paragraphIndex]
-        }
-
-        val paragraphStart = if (paragraphIndex == 0) 0 else paragraphEnds[paragraphIndex - 1]
-        val paragraphEnd = paragraphEnds[paragraphIndex]
-        val paragraphLength = paragraphEnd - paragraphStart
-
-        // We allocate the character buffer for saving memories. The internal implementation
-        // anyway allocate character buffer even if we pass text through
-        // AttributedCharacterIterator. Also there is no way of passing
-        // Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT via AttributedCharacterIterator.
-        //
-        // We also cannot always reuse this buffer since the internal Bidi object keeps this
-        // reference and use it for creating lineBidi. We may be able to share buffer by avoiding
-        // using lineBidi but this is internal implementation details, so share memory as
-        // much as possible and allocate new buffer if we need Bidi object.
-        var buffer = tmpBuffer
-        buffer =
-            if (buffer == null || buffer.size < paragraphLength) {
-                CharArray(paragraphLength)
-            } else {
-                buffer
-            }
-        TextUtils.getChars(layout.text, paragraphStart, paragraphEnd, buffer, 0)
-
-        val result =
-            if (Bidi.requiresBidi(buffer, 0, paragraphLength)) {
-                val flag =
-                    if (isRtlParagraph(paragraphIndex)) {
-                        Bidi.DIRECTION_RIGHT_TO_LEFT
-                    } else {
-                        Bidi.DIRECTION_LEFT_TO_RIGHT
-                    }
-                val bidi = Bidi(buffer, 0, null, 0, paragraphLength, flag)
-
-                if (bidi.runCount == 1) {
-                    // This corresponds to the all text is Right-to-Left case. We don't need to keep
-                    // Bidi object
-                    null
-                } else {
-                    bidi
-                }
-            } else {
-                null
-            }
-
-        paragraphBidi[paragraphIndex] = result
-        bidiProcessedParagraphs[paragraphIndex] = true
-
-        tmpBuffer =
-            if (result != null) {
-                // The ownership of buffer is now passed to Bidi object.
-                // Release tmpBuffer if we didn't allocated in this time.
-                if (buffer === tmpBuffer) null else tmpBuffer
-            } else {
-                // We might allocate larger buffer in this time. Update tmpBuffer with latest one.
-                // (the latest buffer may be same as tmpBuffer)
-                buffer
-            }
-        return result
-    }
-
-    /** Retrieve the number of the paragraph in this layout. */
-    val paragraphCount = paragraphEnds.size
-
-    /**
-     * Returns the zero based paragraph number at the offset.
-     *
-     * The paragraphs are divided by line feed character (U+000A) and line feed character is
-     * included in the preceding paragraph, i.e. if the offset points the line feed character, this
-     * function returns preceding paragraph index.
-     *
-     * @param offset a character offset in the text
-     * @return the paragraph number
-     */
-    fun getParagraphForOffset(@IntRange(from = 0) offset: Int, upstream: Boolean = false): Int {
-        val paragraphIndex =
-            paragraphEnds.binarySearch(offset).let { if (it < 0) -(it + 1) else it + 1 }
-
-        if (upstream && paragraphIndex > 0 && offset == paragraphEnds[paragraphIndex - 1]) {
-            return paragraphIndex - 1
-        }
-
-        return paragraphIndex
-    }
-
-    /**
-     * Returns the inclusive paragraph starting offset of the given paragraph index.
-     *
-     * @param paragraphIndex a paragraph index.
-     * @return an inclusive start character offset of the given paragraph.
-     */
-    fun getParagraphStart(@IntRange(from = 0) paragraphIndex: Int) =
-        if (paragraphIndex == 0) 0 else paragraphEnds[paragraphIndex - 1]
-
-    /**
-     * Returns the exclusive paragraph end offset of the given paragraph index.
-     *
-     * @param paragraphIndex a paragraph index.
-     * @return an exclusive end character offset of the given paragraph.
-     */
-    fun getParagraphEnd(@IntRange(from = 0) paragraphIndex: Int) = paragraphEnds[paragraphIndex]
-
-    /**
-     * Returns true if the resolved paragraph direction is RTL, otherwise return false.
-     *
-     * @param paragraphIndex a paragraph index
-     * @return true if the paragraph is RTL, otherwise false
-     */
-    fun isRtlParagraph(@IntRange(from = 0) paragraphIndex: Int): Boolean {
-        val lineNumber = layout.getLineForOffset(getParagraphStart(paragraphIndex))
-        return layout.getParagraphDirection(lineNumber) == Layout.DIR_RIGHT_TO_LEFT
-    }
-
-    /**
-     * Returns horizontal offset from the drawing origin
-     *
-     * This is the location where a new character would be inserted. If offset points the line
-     * broken offset, this return the insertion offset of preceding line if upstream is true.
-     * Otherwise returns the following line's insertion offset.
-     *
-     * In case of Bi-Directional text, the offset may points graphically different location. Here
-     * primary means that the inserting character's direction will be resolved to the same direction
-     * to the paragraph direction. For example, set usePrimaryHorizontal to true if you want to get
-     * LTR character insertion position for the LTR paragraph, or if you want to get RTL character
-     * insertion position for the RTL paragraph. Set usePrimaryDirection to false if you want to get
-     * RTL character insertion position for the LTR paragraph, or if you want to get LTR character
-     * insertion position for the RTL paragraph.
-     *
-     * @param offset an offset to be insert a character
-     * @param usePrimaryDirection no effect if the given offset does not point the directionally
-     *   transition point. If offset points the directional transition point and this argument is
-     *   true, treat the given offset as the offset of the Bidi run that has the same direction to
-     *   the paragraph direction. Otherwise treat the given offset as the offset of the Bidi run
-     *   that has the different direction to the paragraph direction.
-     * @param upstream if offset points the line broken offset, use upstream offset if true,
-     *   otherwise false.
-     * @return the horizontal offset from the drawing origin.
-     */
-    fun getHorizontalPosition(offset: Int, usePrimaryDirection: Boolean, upstream: Boolean): Float {
-        // Android already calculates downstream
-        if (!upstream) {
-            return getDownstreamHorizontal(offset, usePrimaryDirection)
-        }
-
-        val lineNo = layout.getLineForOffset(offset, upstream)
-        val lineStart = layout.getLineStart(lineNo)
-        val lineEnd = layout.getLineEnd(lineNo)
-
-        // Early exit if the offset points not an edge of line. There is no difference between
-        // downstream and upstream horizontals. This includes out-of-range request
-        if (offset != lineStart && offset != lineEnd) {
-            return getDownstreamHorizontal(offset, usePrimaryDirection)
-        }
-
-        // Similarly, even if the offset points the edge of the line start and line end, we can
-        // use downstream result.
-        if (offset == 0 || offset == layout.text.length) {
-            return getDownstreamHorizontal(offset, usePrimaryDirection)
-        }
-
-        val paraNo = getParagraphForOffset(offset, upstream)
-        val isParaRtl = isRtlParagraph(paraNo)
-
-        // Use line visible end for creating bidi object since invisible whitespaces should not be
-        // considered for location retrieval.
-        val lineVisibleEnd = lineEndToVisibleEnd(lineEnd, lineStart)
-        val paragraphStart = getParagraphStart(paraNo)
-        val bidiStart = lineStart - paragraphStart
-        val bidiEnd = lineVisibleEnd - paragraphStart
-        val lineBidi = analyzeBidi(paraNo)?.createLineBidi(bidiStart, bidiEnd)
-        if (lineBidi == null || lineBidi.runCount == 1) { // easy case. All directions are the same
-            val runDirection = layout.isRtlCharAt(lineStart)
-            val isStartLeft =
-                if (usePrimaryDirection || isParaRtl == runDirection) {
-                    !isParaRtl
-                } else {
-                    isParaRtl
-                }
-            val isOffsetLeft = if (offset == lineStart) isStartLeft else !isStartLeft
-            return if (isOffsetLeft) layout.getLineLeft(lineNo) else layout.getLineRight(lineNo)
-        }
-
-        // Somehow need to find the character's position without using getPrimaryHorizontal.
-        val runs =
-            Array(lineBidi.runCount) {
-                // We may be able to reduce this Bidi Run allocation by using run indices
-                // but unfortunately, Bidi#reorderVisually only accepts array of Object. So auto
-                // boxing happens anyway. Also, looks like Bidi#getRunStart and Bidi#getRunLimit
-                // does non-trivial amount of work. So we save the result into BidiRun.
-                BidiRun(
-                    start = lineStart + lineBidi.getRunStart(it),
-                    end = lineStart + lineBidi.getRunLimit(it),
-                    isRtl = lineBidi.getRunLevel(it) % 2 == 1
-                )
-            }
-        val levels = ByteArray(lineBidi.runCount) { lineBidi.getRunLevel(it).toByte() }
-        Bidi.reorderVisually(levels, 0, runs, 0, runs.size)
-
-        if (offset == lineStart) {
-            // find the visual position of the last character
-            val index = runs.indexOfFirst { it.start == offset }
-            val run = runs[index]
-            // True if the requesting end offset is left edge of the run.
-            val isLeftRequested =
-                if (usePrimaryDirection || isParaRtl == run.isRtl) {
-                    !isParaRtl
-                } else {
-                    isParaRtl
-                }
-
-            if (index == 0 && isLeftRequested) {
-                // Requesting most left run's left offset, just use line left.
-                return layout.getLineLeft(lineNo)
-            } else if (index == runs.lastIndex && !isLeftRequested) {
-                // Requesting most right run's right offset, just use line right.
-                return layout.getLineRight(lineNo)
-            } else if (isLeftRequested) {
-                // Reaching here means the run is LTR, since RTL run cannot be start from the
-                // middle of the text in RTL context.
-                // This is LTR run, so left position of this run is the same to left
-                // RTL run's right (i.e. start) position.
-                return layout.getPrimaryHorizontal(runs[index - 1].start)
-            } else {
-                // Reaching here means the run is RTL, since LTR run cannot be start from the
-                // middle of the text in LTR context.
-                // This is RTL run, so right position of this run is the same to right
-                // LTR run's left (i.e. start) position.
-                return layout.getPrimaryHorizontal(runs[index + 1].start)
-            }
-        } else {
-            // Bidi runs are created between lineStart and lineVisibleEnd
-            // If the requested offset is a white space at the end of the line, it would be
-            // out of bounds for the runs in this Bidi. We are adjusting the requested offset
-            // to the visible end of line.
-            val lineEndAdjustedOffset =
-                if (offset > lineVisibleEnd) {
-                    lineEndToVisibleEnd(offset, lineStart)
-                } else {
-                    offset
-                }
-            // find the visual position of the last character
-            val index = runs.indexOfFirst { it.end == lineEndAdjustedOffset }
-            val run = runs[index]
-            // True if the requesting end offset is left edge of the run.
-            val isLeftRequested =
-                if (usePrimaryDirection || isParaRtl == run.isRtl) {
-                    isParaRtl
-                } else {
-                    !isParaRtl
-                }
-            if (index == 0 && isLeftRequested) {
-                // Requesting most left run's left offset, just use line left.
-                return layout.getLineLeft(lineNo)
-            } else if (index == runs.lastIndex && !isLeftRequested) {
-                // Requesting most right run's right offset, just use line right.
-                return layout.getLineRight(lineNo)
-            } else if (isLeftRequested) {
-                // Reaching here means the run is RTL, since LTR run cannot be broken from the
-                // middle of the text in LTR context.
-                // This is RTL run, so left position of this run is the same to left
-                // LTR run's right (i.e. end) position.
-                return layout.getPrimaryHorizontal(runs[index - 1].end)
-            } else { // !isEndLeft
-                // Reaching here means the run is LTR, since RTL run cannot be broken from the
-                // middle of the text in RTL context.
-                // This is LTR run, so right position of this run is the same to right
-                // RTL run's left (i.e. end) position.
-                return layout.getPrimaryHorizontal(runs[index + 1].end)
-            }
-        }
-    }
-
-    /**
-     * Return the text offset after the last visible character on the specified line. For example
-     * whitespaces are not counted as visible characters.
-     */
-    fun getLineVisibleEnd(lineIndex: Int): Int {
-        return lineEndToVisibleEnd(layout.getLineEnd(lineIndex), layout.getLineStart(lineIndex))
-    }
-
-    private fun getDownstreamHorizontal(offset: Int, primary: Boolean): Float {
-        val lineNo = layout.getLineForOffset(offset)
-        val lineEnd = layout.getLineEnd(lineNo)
-
-        // [android.text.Layout#getHorizontal] has a bug that causes a crash if requested offset
-        // is in an ellipsized region and comes after a line feed character. We coerce at most to
-        // lineEnd of the line this offset belongs to. getLineEnd respects line feed characters.
-        // Any ellipsized character should already return the visible end value, which they do until
-        // a line feed character. We can safely assume rest of the characters can also return the
-        // same result as the reported line end.
-        val targetOffset = offset.coerceAtMost(lineEnd)
-
-        return if (primary) {
-            layout.getPrimaryHorizontal(targetOffset)
-        } else {
-            layout.getSecondaryHorizontal(targetOffset)
-        }
-    }
-
-    internal data class BidiRun(val start: Int, val end: Int, val isRtl: Boolean)
-
-    /**
-     * Convert line end offset to the offset that is the last visible character. Last visible
-     * character on this line cannot be before line start.
-     */
-    private fun lineEndToVisibleEnd(lineEnd: Int, lineStart: Int): Int {
-        var visibleEnd = lineEnd
-        while (visibleEnd > lineStart) {
-            if (isLineEndSpace(layout.text[visibleEnd - 1 /* visibleEnd is exclusive */])) {
-                visibleEnd--
-            } else {
-                break
-            }
-        }
-        return visibleEnd
-    }
-
-    internal fun getLineBidiRuns(lineIndex: Int): Array<BidiRun> {
-        val lineStart = layout.getLineStart(lineIndex)
-        val lineEnd = layout.getLineEnd(lineIndex)
-
-        val paragraphIndex = getParagraphForOffset(lineStart)
-        val paragraphStart = getParagraphStart(paragraphIndex)
-
-        val bidiStart = lineStart - paragraphStart
-        val bidiEnd = lineEnd - paragraphStart
-        val lineBidi =
-            analyzeBidi(paragraphIndex)?.createLineBidi(bidiStart, bidiEnd)
-                ?: return arrayOf(BidiRun(lineStart, lineEnd, layout.isRtlCharAt(lineStart)))
-
-        return Array(lineBidi.runCount) {
-            BidiRun(
-                start = lineStart + lineBidi.getRunStart(it),
-                end = lineStart + lineBidi.getRunLimit(it),
-                isRtl = lineBidi.getRunLevel(it) % 2 == 1
-            )
-        }
-    }
-
-    // The spaces that will not be rendered if they are placed at the line end. In most case, it is
-    // whitespace or line feed character, hence checking linearly should be enough.
-    @Suppress("ConvertTwoComparisonsToRangeCheck")
-    fun isLineEndSpace(c: Char) =
-        c == ' ' ||
-            c == '\n' ||
-            c == '\u1680' ||
-            (c >= '\u2000' && c <= '\u200A' && c != '\u2007') ||
-            c == '\u205F' ||
-            c == '\u3000'
-}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt
deleted file mode 100644
index 0928bb4b..0000000
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.android
-
-import android.text.BoringLayout
-import android.text.Layout
-import android.text.SpannableString
-import android.text.Spanned
-import android.text.TextPaint
-import android.text.style.CharacterStyle
-import android.text.style.MetricAffectingSpan
-import androidx.compose.ui.text.android.style.LetterSpacingSpanEm
-import androidx.compose.ui.text.android.style.LetterSpacingSpanPx
-import java.text.BreakIterator
-import java.util.PriorityQueue
-import kotlin.math.ceil
-
-/** Computes and caches the text layout intrinsic values such as min/max width. */
-internal class LayoutIntrinsics(
-    private val charSequence: CharSequence,
-    private val textPaint: TextPaint,
-    @LayoutCompat.TextDirection private val textDirectionHeuristic: Int
-) {
-
-    private var _maxIntrinsicWidth: Float = Float.NaN
-    private var _minIntrinsicWidth: Float = Float.NaN
-    private var _boringMetrics: BoringLayout.Metrics? = null
-    private var boringMetricsIsInit: Boolean = false
-
-    /**
-     * Compute Android platform BoringLayout metrics. A null value means the provided CharSequence
-     * cannot be laid out using a BoringLayout.
-     */
-    val boringMetrics: BoringLayout.Metrics?
-        get() {
-            if (!boringMetricsIsInit) {
-                val frameworkTextDir = getTextDirectionHeuristic(textDirectionHeuristic)
-                _boringMetrics =
-                    BoringLayoutFactory.measure(charSequence, textPaint, frameworkTextDir)
-                boringMetricsIsInit = true
-            }
-            return _boringMetrics
-        }
-
-    /**
-     * Calculate minimum intrinsic width of the CharSequence.
-     *
-     * @see androidx.compose.ui.text.android.minIntrinsicWidth
-     */
-    val minIntrinsicWidth: Float
-        get() =
-            if (!_minIntrinsicWidth.isNaN()) {
-                _minIntrinsicWidth
-            } else {
-                _minIntrinsicWidth = minIntrinsicWidth(charSequence, textPaint)
-                _minIntrinsicWidth
-            }
-
-    /**
-     * Calculate maximum intrinsic width for the CharSequence. Maximum intrinsic width is the width
-     * of text where no soft line breaks are applied.
-     */
-    val maxIntrinsicWidth: Float
-        get() =
-            if (!_maxIntrinsicWidth.isNaN()) {
-                _maxIntrinsicWidth
-            } else {
-                var desiredWidth = (boringMetrics?.width ?: -1).toFloat()
-
-                // boring metrics doesn't cover RTL text so we fallback to different calculation
-                // when boring metrics can't be calculated
-                if (desiredWidth < 0) {
-                    // b/233856978, apply `ceil` function here to be consistent with the boring
-                    // metrics width calculation that does it under the hood, too
-                    desiredWidth =
-                        ceil(
-                            Layout.getDesiredWidth(
-                                stripNonMetricAffectingCharacterStyleSpans(charSequence),
-                                0,
-                                charSequence.length,
-                                textPaint
-                            )
-                        )
-                }
-                if (shouldIncreaseMaxIntrinsic(desiredWidth, charSequence, textPaint)) {
-                    // b/173574230, increase maxIntrinsicWidth, so that StaticLayout won't form 2
-                    // lines for the given maxIntrinsicWidth
-                    desiredWidth += 0.5f
-                }
-                _maxIntrinsicWidth = desiredWidth
-                _maxIntrinsicWidth
-            }
-}
-
-/**
- * Returns the word with the longest length. To calculate it in a performant way, it applies a
- * heuristics where
- * - it first finds a set of words with the longest length
- * - finds the word with maximum width in that set
- */
-internal fun minIntrinsicWidth(text: CharSequence, paint: TextPaint): Float {
-    val iterator = BreakIterator.getLineInstance(paint.textLocale)
-    iterator.text = CharSequenceCharacterIterator(text, 0, text.length)
-
-    // 10 is just a random number that limits the size of the candidate list
-    val heapSize = 10
-    // min heap that will hold [heapSize] many words with max length
-    val longestWordCandidates =
-        PriorityQueue(
-            heapSize,
-            Comparator<Pair<Int, Int>> { left, right ->
-                (left.second - left.first) - (right.second - right.first)
-            }
-        )
-
-    var start = 0
-    var end = iterator.next()
-    while (end != BreakIterator.DONE) {
-        if (longestWordCandidates.size < heapSize) {
-            longestWordCandidates.add(Pair(start, end))
-        } else {
-            longestWordCandidates.peek()?.let { minPair ->
-                if ((minPair.second - minPair.first) < (end - start)) {
-                    longestWordCandidates.poll()
-                    longestWordCandidates.add(Pair(start, end))
-                }
-            }
-        }
-
-        start = end
-        end = iterator.next()
-    }
-
-    var minWidth = 0f
-
-    longestWordCandidates.forEach { (start, end) ->
-        val width =
-            Layout.getDesiredWidth(
-                stripNonMetricAffectingCharacterStyleSpans(text),
-                start,
-                end,
-                paint
-            )
-        minWidth = maxOf(minWidth, width)
-    }
-
-    return minWidth
-}
-
-/**
- * See [b/346918500#comment7](https://issuetracker.google.com/346918500#comment7).
- *
- * Remove all character styling spans for measuring intrinsic width. [CharacterStyle] spans may
- * affect the intrinsic width, even though they aren't supposed to, resulting in a width that
- * doesn't actually fit the text. This can cause the line to unexpectedly wrap, even if `maxLines`
- * was set to `1` or `softWrap` was `false`.
- *
- * [MetricAffectingSpan] extends [CharacterStyle], but [MetricAffectingSpan]s are allowed to affect
- * the width, so only remove spans that **do** extend [CharacterStyle] but **don't** extend
- * [MetricAffectingSpan].
- */
-private fun stripNonMetricAffectingCharacterStyleSpans(charSequence: CharSequence): CharSequence {
-    if (charSequence !is Spanned) return charSequence
-    val spans = charSequence.getSpans(0, charSequence.length, CharacterStyle::class.java)
-    if (spans.isNullOrEmpty()) return charSequence
-    return SpannableString(charSequence).apply {
-        spans.onEach { if (it !is MetricAffectingSpan) removeSpan(it) }
-    }
-}
-
-/**
- * b/173574230 on Android 11 and above, creating a StaticLayout when
- * - desiredWidth is an Integer,
- * - letterSpacing is set
- * - lineHeight is set StaticLayout forms 2 lines for the given desiredWidth.
- *
- * This function checks if those conditions are met.
- */
-private fun shouldIncreaseMaxIntrinsic(
-    desiredWidth: Float,
-    charSequence: CharSequence,
-    textPaint: TextPaint
-): Boolean {
-    return desiredWidth != 0f &&
-        (charSequence is Spanned &&
-            (charSequence.hasSpan(LetterSpacingSpanPx::class.java) ||
-                charSequence.hasSpan(LetterSpacingSpanEm::class.java)) ||
-            textPaint.letterSpacing != 0f)
-}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt b/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt
deleted file mode 100644
index 9b61a75..0000000
--- a/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.android.kt
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.ui.text.android.animation
-
-import android.text.Layout
-import androidx.compose.ui.text.android.CharSequenceCharacterIterator
-import androidx.compose.ui.text.android.LayoutHelper
-import androidx.compose.ui.text.android.fastForEach
-import androidx.compose.ui.text.android.fastZipWithNext
-import androidx.compose.ui.text.android.getLineForOffset
-import java.text.BreakIterator
-import java.util.Locale
-import java.util.TreeSet
-import kotlin.math.ceil
-import kotlin.math.max
-import kotlin.math.min
-
-/**
- * A class represents animation segment.
- *
- * @param startOffset an inclusive start character offset of this segment.
- * @param endOffset an exclusive end character offset of this segment.
- * @param left a graphical left position from the layout origin.
- * @param top a graphical top position from the layout origin.
- * @param right a graphical right position from the layout origin.
- * @param bottom a graphical bottom position from the layout origin.
- */
-internal data class Segment(
-    val startOffset: Int,
-    val endOffset: Int,
-    val left: Int,
-    val top: Int,
-    val right: Int,
-    val bottom: Int
-)
-
-/** Provide a segmentation breaker for the text animation. */
-internal object SegmentBreaker {
-    private fun breakInWords(layoutHelper: LayoutHelper): List<Int> {
-        val text = layoutHelper.layout.text
-        val words = breakWithBreakIterator(text, BreakIterator.getLineInstance(Locale.getDefault()))
-
-        val set = TreeSet<Int>().apply { words.fastForEach { add(it) } }
-
-        for (paraIndex in 0 until layoutHelper.paragraphCount) {
-            val bidi = layoutHelper.analyzeBidi(paraIndex) ?: continue
-            val paragraphStart = layoutHelper.getParagraphStart(paraIndex)
-            for (i in 0 until bidi.runCount) {
-                set.add(bidi.getRunStart(i) + paragraphStart)
-            }
-        }
-        return set.toList()
-    }
-
-    private fun breakWithBreakIterator(text: CharSequence, breaker: BreakIterator): List<Int> {
-        val iter = CharSequenceCharacterIterator(text, 0, text.length)
-
-        val res = mutableListOf(0)
-        breaker.text = iter
-        while (breaker.next() != BreakIterator.DONE) {
-            res.add(breaker.current())
-        }
-        return res
-    }
-
-    /**
-     * Gets all offsets of the given segment type for animation.
-     *
-     * @param layoutHelper a layout helper
-     * @param segmentType a segmentation type
-     * @return all break offsets of the given segmentation type including 0 and text length.
-     */
-    fun breakOffsets(layoutHelper: LayoutHelper, segmentType: SegmentType): List<Int> {
-        val layout = layoutHelper.layout
-        val text = layout.text
-
-        return when (segmentType) {
-            SegmentType.Document -> listOf(0, text.length)
-            SegmentType.Paragraph -> {
-                mutableListOf(0).also {
-                    for (i in 0 until layoutHelper.paragraphCount) {
-                        it.add(layoutHelper.getParagraphEnd(i))
-                    }
-                }
-            }
-            SegmentType.Line -> {
-                mutableListOf(0).also {
-                    for (i in 0 until layout.lineCount) {
-                        it.add(layout.getLineEnd(i))
-                    }
-                }
-            }
-            SegmentType.Word -> breakInWords(layoutHelper)
-            SegmentType.Character ->
-                breakWithBreakIterator(
-                    text,
-                    BreakIterator.getCharacterInstance(Locale.getDefault())
-                )
-        }
-    }
-
-    /**
-     * Break Layout into list of segments.
-     *
-     * A segment represents a unit of text animation. For example, if you specify, SegmentType
-     * .Line, this function will give you a list of Line segments which have line start offset and
-     * line end offset, and also line bounding box.
-     *
-     * The dropSpaces argument is ignored if segmentType is Document or Paragraph.
-     *
-     * If segmentType is Line and dropSpaces is true, this removes trailing spaces. If segmentType
-     * is Line and dropSpace is false, this use layout width as the right position of the line.
-     *
-     * If segmentType is Word and dropSpaces is true, this removes trailing spaces if there. If
-     * segmentType is Word and dropSpace is false, this includes the trailing whitespace into
-     * segment.
-     *
-     * If segmentType is Character and dropSpace is true, this drops whitespace only segment. If
-     * segmentType is Character and dropSpace is true, this include whitespace only segment.
-     *
-     * @param layoutHelper a layout helper
-     * @param segmentType a segmentation type
-     * @param dropSpaces whether dropping spacing. See function comment for more details.
-     * @return list of segment object
-     */
-    fun breakSegments(
-        layoutHelper: LayoutHelper,
-        segmentType: SegmentType,
-        dropSpaces: Boolean
-    ): List<Segment> {
-        return when (segmentType) {
-            SegmentType.Document -> breakSegmentWithDocument(layoutHelper)
-            SegmentType.Paragraph -> breakSegmentWithParagraph(layoutHelper)
-            SegmentType.Line -> breakSegmentWithLine(layoutHelper, dropSpaces)
-            SegmentType.Word -> breakSegmentWithWord(layoutHelper, dropSpaces)
-            SegmentType.Character -> breakSegmentWithChar(layoutHelper, dropSpaces)
-        }
-    }
-
-    private fun breakSegmentWithDocument(layoutHelper: LayoutHelper): List<Segment> {
-        return listOf(
-            Segment(
-                startOffset = 0,
-                endOffset = layoutHelper.layout.text.length,
-                left = 0,
-                top = 0,
-                right = layoutHelper.layout.width,
-                bottom = layoutHelper.layout.height
-            )
-        )
-    }
-
-    private fun breakSegmentWithParagraph(layoutHelper: LayoutHelper): List<Segment> {
-        val result = mutableListOf<Segment>()
-        val layout = layoutHelper.layout
-        for (i in 0 until layoutHelper.paragraphCount) {
-            val paraStart = layoutHelper.getParagraphStart(i)
-            val paraEnd = layoutHelper.getParagraphEnd(i)
-            val paraFirstLine = layout.getLineForOffset(paraStart, false /* downstream */)
-            val paraLastLine = layout.getLineForOffset(paraEnd, true /* upstream */)
-            result.add(
-                Segment(
-                    startOffset = paraStart,
-                    endOffset = paraEnd,
-                    left = 0,
-                    top = layout.getLineTop(paraFirstLine),
-                    right = layout.width,
-                    bottom = layout.getLineBottom(paraLastLine)
-                )
-            )
-        }
-        return result
-    }
-
-    private fun breakSegmentWithLine(
-        layoutHelper: LayoutHelper,
-        dropSpaces: Boolean
-    ): List<Segment> {
-        val result = mutableListOf<Segment>()
-        val layout = layoutHelper.layout
-        for (i in 0 until layoutHelper.layout.lineCount) {
-            result.add(
-                Segment(
-                    startOffset = layout.getLineStart(i),
-                    endOffset = layout.getLineEnd(i),
-                    left = if (dropSpaces) ceil(layout.getLineLeft(i)).toInt() else 0,
-                    top = layout.getLineTop(i),
-                    right = if (dropSpaces) ceil(layout.getLineRight(i)).toInt() else layout.width,
-                    bottom = layout.getLineBottom(i)
-                )
-            )
-        }
-        return result
-    }
-
-    private fun breakSegmentWithWord(
-        layoutHelper: LayoutHelper,
-        dropSpaces: Boolean
-    ): List<Segment> {
-        val layout = layoutHelper.layout
-        val wsWidth = ceil(layout.paint.measureText(" ")).toInt()
-        return breakOffsets(layoutHelper, SegmentType.Word).fastZipWithNext { start, end ->
-            val lineNo = layout.getLineForOffset(start, false /* downstream */)
-            val paraRTL = layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT
-            val runRtl = layout.isRtlCharAt(start) // no bidi transition inside segment
-            val startPos =
-                ceil(
-                        layoutHelper.getHorizontalPosition(
-                            offset = start,
-                            usePrimaryDirection = runRtl == paraRTL,
-                            upstream = false
-                        )
-                    )
-                    .toInt()
-            val endPos =
-                ceil(
-                        layoutHelper.getHorizontalPosition(
-                            offset = end,
-                            usePrimaryDirection = runRtl == paraRTL,
-                            upstream = true
-                        )
-                    )
-                    .toInt()
-
-            // Drop trailing space is the line does not end with this word.
-            var left = min(startPos, endPos)
-            var right = max(startPos, endPos)
-            if (dropSpaces && end != 0 && layout.text[end - 1] == ' ') {
-                val lineEnd = layout.getLineEnd(lineNo)
-                if (lineEnd != end) {
-                    if (runRtl) {
-                        left += wsWidth
-                    } else {
-                        right -= wsWidth
-                    }
-                }
-            }
-
-            Segment(
-                startOffset = start,
-                endOffset = end,
-                left = left,
-                top = layout.getLineTop(lineNo),
-                right = right,
-                bottom = layout.getLineBottom(lineNo)
-            )
-        }
-    }
-
-    private fun breakSegmentWithChar(
-        layoutHelper: LayoutHelper,
-        dropSpaces: Boolean
-    ): List<Segment> {
-        val res = mutableListOf<Segment>()
-        breakOffsets(layoutHelper, SegmentType.Character).fastZipWithNext lambda@{ start, end ->
-            val layout = layoutHelper.layout
-
-            if (dropSpaces && end == start + 1 && layoutHelper.isLineEndSpace(layout.text[start]))
-                return@lambda
-            val lineNo = layout.getLineForOffset(start, false /* downstream */)
-            val paraRTL = layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT
-            val runRtl = layout.isRtlCharAt(start) // no bidi transition inside segment
-            val startPos =
-                ceil(
-                        layoutHelper.getHorizontalPosition(
-                            offset = start,
-                            usePrimaryDirection = runRtl == paraRTL,
-                            upstream = false
-                        )
-                    )
-                    .toInt()
-            val endPos =
-                ceil(
-                        layoutHelper.getHorizontalPosition(
-                            offset = end,
-                            usePrimaryDirection = runRtl == paraRTL,
-                            upstream = true
-                        )
-                    )
-                    .toInt()
-            res.add(
-                Segment(
-                    startOffset = start,
-                    endOffset = end,
-                    left = min(startPos, endPos),
-                    top = layout.getLineTop(lineNo),
-                    right = max(startPos, endPos),
-                    bottom = layout.getLineBottom(lineNo)
-                )
-            )
-        }
-        return res
-    }
-}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordIterator.android.kt b/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordIterator.android.kt
deleted file mode 100644
index 2d5cfc2..0000000
--- a/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordIterator.android.kt
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.compose.ui.text.android.selection
-
-import androidx.compose.ui.text.android.CharSequenceCharacterIterator
-import androidx.emoji2.text.EmojiCompat
-import java.text.BreakIterator
-import java.util.Locale
-import kotlin.math.max
-import kotlin.math.min
-
-/**
- * Walks through cursor positions at word boundaries.
- *
- * Also provides methods to determine word boundaries.
- *
- * Note: This file is copied from
- * [WordIterator.java](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/method/WordIterator.java)
- *
- * @param locale The locale to be used for analyzing the text. Caches [CharSequence] for performance
- *   reasons.
- * @constructor Constructs a new WordIterator for the specified locale.
- */
-internal class WordIterator(val charSequence: CharSequence, start: Int, end: Int, locale: Locale?) {
-    private val start: Int
-    private val end: Int
-    private val iterator: BreakIterator
-
-    init {
-        require(start in 0..charSequence.length) { "input start index is outside the CharSequence" }
-        require(end in 0..charSequence.length) { "input end index is outside the CharSequence" }
-        iterator = BreakIterator.getWordInstance(locale)
-        this.start = max(0, start - WINDOW_WIDTH)
-        this.end = min(charSequence.length, end + WINDOW_WIDTH)
-        iterator.text = CharSequenceCharacterIterator(charSequence, start, end)
-    }
-
-    /**
-     * Returns the position of next boundary after the given offset. Returns `BreakIterator.DONE` if
-     * there is no boundary after the given offset.
-     *
-     * @param offset the given start position to search from.
-     * @return the position of the last boundary preceding the given offset.
-     */
-    fun nextBoundary(offset: Int): Int {
-        checkOffsetIsValid(offset)
-        val following = iterator.following(offset)
-        // We should iterate through if the boundary is between a letter/digit and emoji
-        // These should not be considered boundaries
-        if (
-            isOnLetterOrDigitOrEmoji(following - 1) &&
-                isOnLetterOrDigitOrEmoji(following) &&
-                // Extra logic for Japanese to detect boundary between Hiragana and Katakana
-                // characters
-                !isHiraganaKatakanaBoundary(following)
-        ) {
-            return nextBoundary(following)
-        }
-        return following
-    }
-
-    /**
-     * Returns the position of boundary preceding the given offset or `BreakIterator.DONE` if the
-     * given offset specifies the starting position.
-     *
-     * @param offset the given start position to search from.
-     * @return the position of the last boundary preceding the given offset.
-     */
-    fun prevBoundary(offset: Int): Int {
-        checkOffsetIsValid(offset)
-        val preceding = iterator.preceding(offset)
-        // We should iterate through if the boundary is between a letter/digit and emoji
-        // These should not be considered boundaries
-        return if (
-            isOnLetterOrDigitOrEmoji(preceding) &&
-                isAfterLetterOrDigitOrEmoji(preceding) &&
-                // Extra logic for Japanese to detect boundary between Hiragana and Katakana
-                // characters
-                !isHiraganaKatakanaBoundary(preceding)
-        ) {
-            prevBoundary(preceding)
-        } else preceding
-    }
-
-    /**
-     * If the `offset` is within a word or on a word boundary that can only be considered the start
-     * of a word (e.g. _word where "_" is any character that would not be considered part of the
-     * word) then this returns the index of the first character of that word.
-     *
-     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
-     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, this
-     * would return the start of the previous word, AA.
-     *
-     * Returns BreakIterator.DONE if there is no previous boundary.
-     *
-     * @throws IllegalArgumentException is offset is not valid.
-     */
-    fun getPrevWordBeginningOnTwoWordsBoundary(offset: Int): Int {
-        return getBeginning(offset, true)
-    }
-
-    /**
-     * If the `offset` is within a word or on a word boundary that can only be considered the end of
-     * a word (e.g. word_ where "_" is any character that would not be considered part of the word)
-     * then this returns the index of the last character plus one of that word.
-     *
-     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
-     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, this
-     * would return the end of the next word, BB.
-     *
-     * Returns BreakIterator.DONE if there is no next boundary.
-     *
-     * @throws IllegalArgumentException is offset is not valid.
-     */
-    fun getNextWordEndOnTwoWordBoundary(offset: Int): Int {
-        return getEnd(offset, true)
-    }
-
-    /**
-     * If `offset` is within a group of punctuation as defined by [isPunctuation], returns the index
-     * of the first character of that group, otherwise returns BreakIterator.DONE.
-     *
-     * @param offset the offset to search from.
-     */
-    fun getPunctuationBeginning(offset: Int): Int {
-        checkOffsetIsValid(offset)
-        var result = offset
-        while (result != BreakIterator.DONE && !isPunctuationStartBoundary(result)) {
-            result = prevBoundary(result)
-        }
-        // No need to shift offset, prevBoundary handles that.
-        return result
-    }
-
-    /**
-     * If `offset` is within a group of punctuation as defined by [isPunctuation], returns the index
-     * of the last character of that group plus one, otherwise returns BreakIterator.DONE.
-     *
-     * @param offset the offset to search from.
-     */
-    fun getPunctuationEnd(offset: Int): Int {
-        checkOffsetIsValid(offset)
-        var result = offset
-        while (result != BreakIterator.DONE && !isPunctuationEndBoundary(result)) {
-            result = nextBoundary(result)
-        }
-        // No need to shift offset, nextBoundary handles that.
-        return result
-    }
-
-    /**
-     * Indicates if the provided offset is after a punctuation character as defined by
-     * [isPunctuation].
-     *
-     * @param offset the offset to check from.
-     * @return Whether the offset is after a punctuation character.
-     */
-    fun isAfterPunctuation(offset: Int): Boolean {
-        if (offset in (start + 1)..end) {
-            val codePoint = Character.codePointBefore(charSequence, offset)
-            return isPunctuation(codePoint)
-        }
-        return false
-    }
-
-    /**
-     * Indicates if the provided offset is at a punctuation character as defined by [isPunctuation].
-     *
-     * @param offset the offset to check from.
-     * @return Whether the offset is at a punctuation character.
-     */
-    fun isOnPunctuation(offset: Int): Boolean {
-        if (offset in start until end) {
-            val codePoint = Character.codePointAt(charSequence, offset)
-            return isPunctuation(codePoint)
-        }
-        return false
-    }
-
-    /**
-     * If the `offset` is within a word or on a word boundary that can only be considered the start
-     * of a word (e.g. _word where "_" is any character that would not be considered part of the
-     * word) then this returns the index of the first character of that word.
-     *
-     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
-     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, and
-     * getPrevWordBeginningOnTwoWordsBoundary is true then this would return the start of the
-     * previous word, AA. Otherwise it would return the current offset, the start of BB.
-     *
-     * Returns BreakIterator.DONE if there is no previous boundary.
-     *
-     * @throws IllegalArgumentException is offset is not valid.
-     */
-    private fun getBeginning(offset: Int, getPrevWordBeginningOnTwoWordsBoundary: Boolean): Int {
-        checkOffsetIsValid(offset)
-        if (isOnLetterOrDigitOrEmoji(offset)) {
-            return if (
-                isBoundary(offset) &&
-                    (!isAfterLetterOrDigitOrEmoji(offset) ||
-                        !getPrevWordBeginningOnTwoWordsBoundary)
-            ) {
-                offset
-            } else {
-                prevBoundary(offset)
-            }
-        } else {
-            if (isAfterLetterOrDigitOrEmoji(offset)) {
-                return prevBoundary(offset)
-            }
-        }
-        return BreakIterator.DONE
-    }
-
-    /**
-     * If the `offset` is within a word or on a word boundary that can only be considered the end of
-     * a word (e.g. word_ where "_" is any character that would not be considered part of the word)
-     * then this returns the index of the last character plus one of that word.
-     *
-     * If the offset is on a word boundary that can be considered the start and end of a word, e.g.
-     * AABB (where AA and BB are both words) and the offset is the boundary between AA and BB, and
-     * getNextWordEndOnTwoWordBoundary is true then this would return the end of the next word, BB.
-     * Otherwise it would return the current offset, the end of AA.
-     *
-     * Returns BreakIterator.DONE if there is no next boundary.
-     *
-     * @throws IllegalArgumentException is offset is not valid.
-     */
-    private fun getEnd(offset: Int, getNextWordEndOnTwoWordBoundary: Boolean): Int {
-        checkOffsetIsValid(offset)
-        if (isAfterLetterOrDigitOrEmoji(offset)) {
-            return if (
-                isBoundary(offset) &&
-                    (!isOnLetterOrDigitOrEmoji(offset) || !getNextWordEndOnTwoWordBoundary)
-            ) {
-                offset
-            } else {
-                nextBoundary(offset)
-            }
-        } else {
-            if (isOnLetterOrDigitOrEmoji(offset)) {
-                return nextBoundary(offset)
-            }
-        }
-        return BreakIterator.DONE
-    }
-
-    private fun isPunctuationStartBoundary(offset: Int): Boolean {
-        return isOnPunctuation(offset) && !isAfterPunctuation(offset)
-    }
-
-    private fun isPunctuationEndBoundary(offset: Int): Boolean {
-        return !isOnPunctuation(offset) && isAfterPunctuation(offset)
-    }
-
-    private fun isAfterLetterOrDigitOrEmoji(offset: Int): Boolean {
-        if (offset in (start + 1)..end) {
-            val codePoint = Character.codePointBefore(charSequence, offset)
-            if (Character.isLetterOrDigit(codePoint)) return true
-
-            if (EmojiCompat.isConfigured()) {
-                val emojiCompat = EmojiCompat.get()
-                if (emojiCompat.loadState == EmojiCompat.LOAD_STATE_SUCCEEDED) {
-                    val emojiStart = emojiCompat.getEmojiStart(charSequence, offset - 1)
-                    // If given offset points to emoji return true
-                    if (emojiStart != -1) return true
-                }
-            }
-        }
-        return false
-    }
-
-    private fun isOnLetterOrDigitOrEmoji(offset: Int): Boolean {
-        if (offset in start until end) {
-            val codePoint = Character.codePointAt(charSequence, offset)
-            if (Character.isLetterOrDigit(codePoint)) return true
-
-            if (EmojiCompat.isConfigured()) {
-                val emojiCompat = EmojiCompat.get()
-                if (emojiCompat.loadState == EmojiCompat.LOAD_STATE_SUCCEEDED) {
-                    val emojiStart = emojiCompat.getEmojiStart(charSequence, offset)
-                    // If given offset points to emoji return true
-                    if (emojiStart != -1) return true
-                }
-            }
-        }
-        return false
-    }
-
-    /** Check if the given offset is in the given range. */
-    private fun checkOffsetIsValid(offset: Int) {
-        require(offset in start..end) {
-            ("Invalid offset: $offset. Valid range is [$start , $end]")
-        }
-    }
-
-    /**
-     * Modified implementation of `iterator.isBoundary` that additionally checks boundary between
-     * letters/digits and emojis as these should not be treated as boundaries.
-     */
-    private fun isBoundary(offset: Int): Boolean {
-        checkOffsetIsValid(offset)
-        return iterator.isBoundary(offset) &&
-            // check offset and two characters before and after to see if all three characters
-            // are letters/digits/emojis
-            !(isOnLetterOrDigitOrEmoji(offset) &&
-                isOnLetterOrDigitOrEmoji(offset - 1) &&
-                isOnLetterOrDigitOrEmoji(offset + 1)) &&
-            // check if there is boundary between hiragana and katakana characters
-            // indexes 0 and charSequence.length - 1 should always be considered boundaries
-            !(offset > 0 &&
-                offset < charSequence.length - 1 &&
-                (isHiraganaKatakanaBoundary(offset) || isHiraganaKatakanaBoundary(offset + 1)))
-    }
-
-    /** Checks if characters before and at `offset` are either hiragana or katakana */
-    private fun isHiraganaKatakanaBoundary(offset: Int): Boolean {
-        return ((Character.UnicodeBlock.of(charSequence[offset - 1]) ==
-            Character.UnicodeBlock.HIRAGANA &&
-            Character.UnicodeBlock.of(charSequence[offset]) == Character.UnicodeBlock.KATAKANA) ||
-            (Character.UnicodeBlock.of(charSequence[offset]) == Character.UnicodeBlock.HIRAGANA &&
-                Character.UnicodeBlock.of(charSequence[offset - 1]) ==
-                    Character.UnicodeBlock.KATAKANA))
-    }
-
-    companion object {
-        // The size of the WINDOW_WIDTH is currently 50, as in Android.
-        // According to Wikipedia https://en.wikipedia.org/wiki/Longest_word_in_English , the
-        // longest English word in English contains 45 letters. Then 50 is a good number for
-        // WINDOW_WIDTH. Size of the window for the word iterator, should be greater than the
-        // longest word's length.
-        private const val WINDOW_WIDTH = 50
-
-        internal fun isPunctuation(cp: Int): Boolean {
-            val type = Character.getType(cp)
-            return type == Character.CONNECTOR_PUNCTUATION.toInt() ||
-                type == Character.DASH_PUNCTUATION.toInt() ||
-                type == Character.END_PUNCTUATION.toInt() ||
-                type == Character.FINAL_QUOTE_PUNCTUATION.toInt() ||
-                type == Character.INITIAL_QUOTE_PUNCTUATION.toInt() ||
-                type == Character.OTHER_PUNCTUATION.toInt() ||
-                type == Character.START_PUNCTUATION.toInt()
-        }
-    }
-}
diff --git a/tracing/tracing-ktx/build.gradle b/tracing/tracing-ktx/build.gradle
index 5a500d8..6add757 100644
--- a/tracing/tracing-ktx/build.gradle
+++ b/tracing/tracing-ktx/build.gradle
@@ -45,7 +45,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2020"
     description = "Android Tracing"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/tracing/tracing-perfetto-binary/build.gradle b/tracing/tracing-perfetto-binary/build.gradle
index 6798459..c8acf57 100644
--- a/tracing/tracing-perfetto-binary/build.gradle
+++ b/tracing/tracing-perfetto-binary/build.gradle
@@ -94,6 +94,5 @@
     inceptionYear = "2022"
     description = "Provides native binaries required by AndroidX Tracing: Perfetto SDK " +
         "and is not intended to be used outside of that context."
-    metalavaK2UastEnabled = true
     doNotDocumentReason = "No public API"
 }
diff --git a/tracing/tracing-perfetto-handshake/build.gradle b/tracing/tracing-perfetto-handshake/build.gradle
index ef651b4..1c80b53 100644
--- a/tracing/tracing-perfetto-handshake/build.gradle
+++ b/tracing/tracing-perfetto-handshake/build.gradle
@@ -32,7 +32,7 @@
 
 dependencies {
     compileOnly(libs.kotlinStdlib)
-    api("androidx.annotation:annotation:1.3.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 tasks.withType(Jar) {
@@ -44,5 +44,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "AndroidX Tracing: Perfetto Handshake"
-    metalavaK2UastEnabled = true
 }
diff --git a/tracing/tracing-perfetto/build.gradle b/tracing/tracing-perfetto/build.gradle
index eed7af0..3dd8eda 100644
--- a/tracing/tracing-perfetto/build.gradle
+++ b/tracing/tracing-perfetto/build.gradle
@@ -52,7 +52,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.3.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.startup:startup-runtime:1.1.1")
     implementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
@@ -69,7 +69,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "AndroidX Tracing: Perfetto SDK"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/tracing/tracing-perfetto/lint-baseline.xml b/tracing/tracing-perfetto/lint-baseline.xml
index 42ea188..7aaa549 100644
--- a/tracing/tracing-perfetto/lint-baseline.xml
+++ b/tracing/tracing-perfetto/lint-baseline.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> Build.SUPPORTED_ABIS.first()"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ->"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/tracing/perfetto/security/SafeLibLoader.kt"/>
     </issue>
@@ -13,7 +13,7 @@
     <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is always >= 21"
-        errorLine1="        if (Build.VERSION.SDK_INT >= 21) Impl21.getCodeCacheDir(context)"
+        errorLine1="        if (Build.VERSION.SDK_INT >= 21) Impl21.getCodeCacheDir(context) else null"
         errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/tracing/perfetto/security/SafeLibLoader.kt"/>
diff --git a/tracing/tracing/build.gradle b/tracing/tracing/build.gradle
index 59e89ab..2c1207d 100644
--- a/tracing/tracing/build.gradle
+++ b/tracing/tracing/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.2.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
@@ -43,7 +43,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Tracing"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
index 08ea852..05cfb2e 100644
--- a/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
+++ b/tracing/tracing/src/androidTest/java/androidx/tracing/TraceTest.java
@@ -112,11 +112,15 @@
     @SdkSuppress(minSdkVersion = 29) // SELinux
     public void setCounter() throws IOException {
         startTrace();
+        assertTrue("Checking that tracing is enabled", Trace.isEnabled());
+        Trace.beginSection("setting counters");
         Trace.setCounter("counterName", 42);
         Trace.setCounter("counterName", 47);
         Trace.setCounter("counterName", 9787);
+        Trace.endSection();
+        assertTrue("Checking that tracing is enabled", Trace.isEnabled());
         dumpTrace();
-
+        assertTraceContains("setting counters");
         assertTraceContains("tracing_mark_write:\\ C\\|.*\\|counterName\\|42");
         assertTraceContains("tracing_mark_write:\\ C\\|.*\\|counterName\\|47");
         assertTraceContains("tracing_mark_write:\\ C\\|.*\\|counterName\\|9787");
diff --git a/transition/transition-ktx/build.gradle b/transition/transition-ktx/build.gradle
index 3b67de1b..f6609db 100644
--- a/transition/transition-ktx/build.gradle
+++ b/transition/transition-ktx/build.gradle
@@ -45,7 +45,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2019"
     description = "Kotlin extensions for 'transition' artifact"
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/transition/transition/build.gradle b/transition/transition/build.gradle
index e9de30e..5a7d75d 100644
--- a/transition/transition/build.gradle
+++ b/transition/transition/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.13.0")
     implementation("androidx.collection:collection:1.1.0")
     compileOnly("androidx.fragment:fragment:1.7.0-rc02")
@@ -53,6 +53,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2016"
     description = "Android Transition Support Library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSeekingTest.kt b/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSeekingTest.kt
index fe380aa7..05a1e4b 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSeekingTest.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/FragmentTransitionSeekingTest.kt
@@ -129,16 +129,16 @@
     fun replaceOperationWithTransitionsThenBackCancelled() {
         withUse(ActivityScenario.launch(FragmentTransitionTestActivity::class.java)) {
             val fm1 = withActivity { supportFragmentManager }
-            var startedEnter = false
-            val fragment1 = TransitionFragment(R.layout.scene1)
+            val startedEnterCountDownLatch = CountDownLatch(1)
+            val fragment1 = StrictViewFragment(R.layout.scene1)
             val transitionEndCountDownLatch = CountDownLatch(1)
-            fragment1.setReenterTransition(
-                Fade().apply {
+            fragment1.reenterTransition =
+                (Fade().apply {
                     duration = 300
                     addListener(
                         object : TransitionListenerAdapter() {
                             override fun onTransitionStart(transition: Transition) {
-                                startedEnter = true
+                                startedEnterCountDownLatch.countDown()
                             }
 
                             override fun onTransitionEnd(transition: Transition) {
@@ -146,8 +146,7 @@
                             }
                         }
                     )
-                }
-            )
+                })
 
             fm1.beginTransaction()
                 .replace(R.id.fragmentContainer, fragment1, "1")
@@ -157,9 +156,9 @@
             waitForExecution()
 
             val startedExitCountDownLatch = CountDownLatch(1)
-            val fragment2 = TransitionFragment()
-            fragment2.setReturnTransition(
-                Fade().apply {
+            val fragment2 = StrictViewFragment()
+            fragment2.returnTransition =
+                (Fade().apply {
                     duration = 300
                     addListener(
                         object : TransitionListenerAdapter() {
@@ -168,8 +167,7 @@
                             }
                         }
                     )
-                }
-            )
+                })
 
             fm1.beginTransaction()
                 .replace(R.id.fragmentContainer, fragment2, "2")
@@ -178,31 +176,21 @@
                 .commit()
             waitForExecution()
 
-            fragment1.waitForTransition()
-            fragment2.waitForTransition()
-
             val dispatcher = withActivity { onBackPressedDispatcher }
             withActivity {
                 dispatcher.dispatchOnBackStarted(
                     BackEventCompat(0.1F, 0.1F, 0.1F, BackEvent.EDGE_LEFT)
                 )
-            }
-            executePendingTransactions()
-
-            withActivity {
                 dispatcher.dispatchOnBackProgressed(
                     BackEventCompat(0.2F, 0.2F, 0.2F, BackEvent.EDGE_LEFT)
                 )
             }
-            waitForExecution()
 
-            assertThat(startedEnter).isTrue()
+            assertThat(startedEnterCountDownLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
             assertThat(startedExitCountDownLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
 
             withActivity { dispatcher.dispatchOnBackCancelled() }
-            waitForExecution()
-
-            fragment1.waitForNoTransition()
+            executePendingTransactions()
 
             assertThat(fragment2.isAdded).isTrue()
             assertThat(fm1.findFragmentByTag("2")).isEqualTo(fragment2)
@@ -647,7 +635,6 @@
                     BackEventCompat(0.1F, 0.1F, 0.1F, BackEvent.EDGE_LEFT)
                 )
             }
-            executePendingTransactions()
 
             withActivity { dispatcher.dispatchOnBackCancelled() }
             executePendingTransactions()
@@ -661,4 +648,144 @@
             assertThat(fragment2.requireView().parent).isNotNull()
         }
     }
+
+    @Test
+    fun gestureBackWithNonSeekableSharedElementCancelInterruptedBack() {
+        withUse(ActivityScenario.launch(FragmentTransitionTestActivity::class.java)) {
+            val fm1 = withActivity { supportFragmentManager }
+
+            val fragment1 = StrictViewFragment(R.layout.scene1)
+
+            fm1.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment1, "1")
+                .setReorderingAllowed(true)
+                .addToBackStack(null)
+                .commit()
+            waitForExecution()
+
+            val fragment2 = TransitionFragment(R.layout.scene6)
+            fragment2.setEnterTransition(Fade())
+            fragment2.setReturnTransition(Fade())
+
+            val greenSquare = fragment1.requireView().findViewById<View>(R.id.greenSquare)
+
+            fm1.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment2, "2")
+                .addSharedElement(greenSquare, "green")
+                .setReorderingAllowed(true)
+                .addToBackStack(null)
+                .commit()
+            waitForExecution()
+
+            fragment2.waitForTransition()
+
+            val dispatcher = withActivity { onBackPressedDispatcher }
+            withActivity {
+                dispatcher.dispatchOnBackStarted(
+                    BackEventCompat(0.1F, 0.1F, 0.1F, BackEvent.EDGE_LEFT)
+                )
+            }
+
+            withActivity { supportFragmentManager.popBackStackImmediate() }
+
+            withActivity { dispatcher.dispatchOnBackCancelled() }
+            executePendingTransactions()
+
+            assertThat(fragment2.isAdded).isFalse()
+            assertThat(fm1.findFragmentByTag("1")).isNotNull()
+
+            // Make sure that fragment 2 does not have a view
+            assertThat(fragment2.view).isNull()
+            // Make sure fragment1 is still in the container
+            assertThat(fragment1.requireView().parent).isNotNull()
+        }
+    }
+
+    @Test
+    fun gestureBackWithNonSeekableSharedElementCancelInterruptedForward() {
+        withUse(ActivityScenario.launch(FragmentTransitionTestActivity::class.java)) {
+            val fm1 = withActivity { supportFragmentManager }
+
+            val fragment1 = StrictViewFragment(R.layout.scene1)
+
+            fm1.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment1, "1")
+                .setReorderingAllowed(true)
+                .addToBackStack(null)
+                .commit()
+            waitForExecution()
+
+            val fragment2 = StrictViewFragment(R.layout.scene6)
+            val fragment2EnterCountDownLatch = CountDownLatch(1)
+            fragment2.enterTransition =
+                Fade().apply {
+                    addListener(
+                        object : TransitionListenerAdapter() {
+                            override fun onTransitionEnd(transition: Transition) {
+                                fragment2EnterCountDownLatch.countDown()
+                            }
+                        }
+                    )
+                }
+            fragment2.returnTransition = Fade()
+
+            val greenSquare = fragment1.requireView().findViewById<View>(R.id.greenSquare)
+
+            fm1.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment2, "2")
+                .addSharedElement(greenSquare, "green")
+                .setReorderingAllowed(true)
+                .addToBackStack(null)
+                .commit()
+            waitForExecution()
+
+            assertThat(fragment2EnterCountDownLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+
+            val dispatcher = withActivity { onBackPressedDispatcher }
+            withActivity {
+                dispatcher.dispatchOnBackStarted(
+                    BackEventCompat(0.1F, 0.1F, 0.1F, BackEvent.EDGE_LEFT)
+                )
+            }
+            waitForExecution()
+
+            val fragment3 = StrictViewFragment(R.layout.scene6)
+            val fragment3EnterCountDownLatch = CountDownLatch(1)
+            fragment3.enterTransition =
+                Fade().apply {
+                    addListener(
+                        object : TransitionListenerAdapter() {
+                            override fun onTransitionEnd(transition: Transition) {
+                                fragment3EnterCountDownLatch.countDown()
+                            }
+                        }
+                    )
+                }
+
+            fm1.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment3, "3")
+                .setReorderingAllowed(true)
+                .addToBackStack(null)
+                .commit()
+            executePendingTransactions()
+
+            withActivity { dispatcher.dispatchOnBackCancelled() }
+            executePendingTransactions()
+
+            assertThat(fragment3.isAdded).isTrue()
+            assertThat(fm1.findFragmentByTag("3")).isNotNull()
+
+            // we verify the state of fragment2 here as this is a transitioning, non-seekable case
+            // so we wouldn't actually have run the animation until the back press was completed.
+            assertThat(fragment2.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+            assertThat(fragment3EnterCountDownLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+
+            // Make sure that fragment 2 does not have a view
+            assertThat(fragment2.view).isNull()
+            // Make sure that fragment 2 does not have a view
+            assertThat(fragment3.view).isNotNull()
+            // Make sure fragment3 is still in the container
+            assertThat(fragment3.requireView().parent).isNotNull()
+        }
+    }
 }
diff --git a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
index 6e84cee..e829456 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
@@ -1179,4 +1179,45 @@
                 )
         }
     }
+
+    // The animateToEnd() should run after the transition is ready, even if called before
+    // the transition is ready.
+    @Test
+    fun animateToEndAfterReady() {
+        val latch = CountDownLatch(1)
+
+        transition.addListener(
+            object : TransitionListenerAdapter() {
+                override fun onTransitionEnd(transition: Transition, isReverse: Boolean) {
+                    super.onTransitionEnd(transition, isReverse)
+                    latch.countDown()
+                }
+            }
+        )
+
+        rule.runOnUiThread {
+            val controller = TransitionManager.controlDelayedTransition(root, transition)
+            assertThat(controller).isNotNull()
+            view.visibility = View.GONE
+            controller!!.animateToEnd()
+        }
+
+        assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+    }
+
+    // The animateToStart() should run after the transition is ready, even if called before
+    // the transition is ready.
+    @Test
+    fun animateToStartAfterReady() {
+        val latch = CountDownLatch(1)
+
+        rule.runOnUiThread {
+            val controller = TransitionManager.controlDelayedTransition(root, transition)
+            assertThat(controller).isNotNull()
+            view.visibility = View.GONE
+            controller!!.animateToStart(latch::countDown)
+        }
+
+        assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+    }
 }
diff --git a/transition/transition/src/main/java/androidx/transition/Transition.java b/transition/transition/src/main/java/androidx/transition/Transition.java
index 460cd83..f4fc52a 100644
--- a/transition/transition/src/main/java/androidx/transition/Transition.java
+++ b/transition/transition/src/main/java/androidx/transition/Transition.java
@@ -2725,12 +2725,16 @@
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     class SeekController extends TransitionListenerAdapter implements TransitionSeekController,
             DynamicAnimation.OnAnimationUpdateListener {
+        private static final int ON_READY_NOTHING = 0;
+        private static final int ON_READY_ANIMATE_TO_END = 1;
+        private static final int ON_READY_ANIMATE_TO_START = 2;
         // Animation calculations appear to work better with numbers that range greater than 1
         private long mCurrentPlayTime = -1;
         private ArrayList<Consumer<TransitionSeekController>> mOnReadyListeners = null;
         private ArrayList<Consumer<TransitionSeekController>> mOnProgressListeners = null;
         private boolean mIsReady;
         private boolean mIsCanceled;
+        private int mOnReady = ON_READY_NOTHING;
 
         private SpringAnimation mSpringAnimation;
         private Consumer<TransitionSeekController>[] mListenerCache = null;
@@ -2767,6 +2771,13 @@
                 }
             }
             callProgressListeners();
+            if (mOnReady == ON_READY_ANIMATE_TO_END) {
+                mOnReady = ON_READY_NOTHING;
+                animateToEnd();
+            } else if (mOnReady == ON_READY_ANIMATE_TO_START) {
+                mOnReady = ON_READY_NOTHING;
+                animateToStart(mResetToStartState);
+            }
         }
 
         @Override
@@ -2901,6 +2912,11 @@
 
         @Override
         public void animateToEnd() {
+            if (!mIsReady) {
+                mOnReady = ON_READY_ANIMATE_TO_END;
+                mResetToStartState = null;
+                return; // can't animate to the end yet
+            }
             ensureAnimation();
             mSpringAnimation.animateToFinalPosition((float) (getDurationMillis() + 1));
         }
@@ -2908,6 +2924,10 @@
         @Override
         public void animateToStart(@NonNull Runnable resetToStartState) {
             mResetToStartState = resetToStartState;
+            if (!mIsReady) {
+                mOnReady = ON_READY_ANIMATE_TO_START;
+                return; // can't animate to the start yet
+            }
             ensureAnimation();
             mSpringAnimation.animateToFinalPosition(0);
         }
diff --git a/tv/integration-tests/macrobenchmark-target/build.gradle b/tv/integration-tests/macrobenchmark-target/build.gradle
index 38d7d19..2fca186 100644
--- a/tv/integration-tests/macrobenchmark-target/build.gradle
+++ b/tv/integration-tests/macrobenchmark-target/build.gradle
@@ -22,6 +22,8 @@
 }
 
 android {
+    compileSdk 35
+
     namespace "androidx.tv.integration.macrobenchmark.target"
 
     defaultConfig {
diff --git a/tv/integration-tests/playground/build.gradle b/tv/integration-tests/playground/build.gradle
index f3e23cc..3dbbc35 100644
--- a/tv/integration-tests/playground/build.gradle
+++ b/tv/integration-tests/playground/build.gradle
@@ -42,6 +42,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 28
     }
diff --git a/tv/integration-tests/presentation/build.gradle b/tv/integration-tests/presentation/build.gradle
index 995653a..faab995 100644
--- a/tv/integration-tests/presentation/build.gradle
+++ b/tv/integration-tests/presentation/build.gradle
@@ -51,6 +51,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 28
     }
diff --git a/tv/integration-tests/presentation/lint-baseline.xml b/tv/integration-tests/presentation/lint-baseline.xml
index 7b75ecb..f05a7c4 100644
--- a/tv/integration-tests/presentation/lint-baseline.xml
+++ b/tv/integration-tests/presentation/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.6.0-alpha03" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha03)" variant="all" version="8.6.0-alpha03">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ModifierParameter"
@@ -11,15 +11,6 @@
     </issue>
 
     <issue
-        id="ModifierParameter"
-        message="Modifier parameter should be the first optional parameter"
-        errorLine1="    modifier: Modifier = Modifier,"
-        errorLine2="    ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/tv/integration/presentation/ImageCard.kt"/>
-    </issue>
-
-    <issue
         id="UnnecessaryLambdaCreation"
         message="Creating an unnecessary lambda to emit a captured lambda"
         errorLine1="        content()"
diff --git a/tv/tv-foundation/api/current.txt b/tv/tv-foundation/api/current.txt
index d1b47c9..936d277 100644
--- a/tv/tv-foundation/api/current.txt
+++ b/tv/tv-foundation/api/current.txt
@@ -5,6 +5,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Immutable public final class PivotOffsets {
+    ctor @Deprecated public PivotOffsets();
     ctor @Deprecated public PivotOffsets(optional @FloatRange(from=0.0, to=1.0, fromInclusive=true, toInclusive=true) float parentFraction, optional @FloatRange(from=0.0, to=1.0, fromInclusive=true, toInclusive=true) float childFraction);
     method @Deprecated public float getChildFraction();
     method @Deprecated public float getParentFraction();
@@ -125,6 +126,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class TvLazyGridState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @Deprecated public TvLazyGridState();
     ctor @Deprecated public TvLazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     method @Deprecated public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public float dispatchRawDelta(float delta);
@@ -224,6 +226,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class TvLazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @Deprecated public TvLazyListState();
     ctor @Deprecated public TvLazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     method @Deprecated public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public float dispatchRawDelta(float delta);
diff --git a/tv/tv-foundation/api/restricted_current.txt b/tv/tv-foundation/api/restricted_current.txt
index d1b47c9..936d277 100644
--- a/tv/tv-foundation/api/restricted_current.txt
+++ b/tv/tv-foundation/api/restricted_current.txt
@@ -5,6 +5,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Immutable public final class PivotOffsets {
+    ctor @Deprecated public PivotOffsets();
     ctor @Deprecated public PivotOffsets(optional @FloatRange(from=0.0, to=1.0, fromInclusive=true, toInclusive=true) float parentFraction, optional @FloatRange(from=0.0, to=1.0, fromInclusive=true, toInclusive=true) float childFraction);
     method @Deprecated public float getChildFraction();
     method @Deprecated public float getParentFraction();
@@ -125,6 +126,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class TvLazyGridState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @Deprecated public TvLazyGridState();
     ctor @Deprecated public TvLazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     method @Deprecated public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public float dispatchRawDelta(float delta);
@@ -224,6 +226,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class TvLazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @Deprecated public TvLazyListState();
     ctor @Deprecated public TvLazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
     method @Deprecated public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public float dispatchRawDelta(float delta);
diff --git a/tv/tv-foundation/build.gradle b/tv/tv-foundation/build.gradle
index 39e0a8d..8d72b69 100644
--- a/tv/tv-foundation/build.gradle
+++ b/tv/tv-foundation/build.gradle
@@ -41,7 +41,7 @@
     def composeVersion = "1.6.8"
     def profileInstallerVersion = "1.3.1"
 
-    api("androidx.annotation:annotation:$annotationVersion")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.animation:animation:$composeVersion")
     api("androidx.compose.foundation:foundation:$composeVersion")
     api("androidx.compose.foundation:foundation-layout:$composeVersion")
@@ -62,6 +62,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.tv.foundation"
 }
 
@@ -75,6 +76,7 @@
             "functionality to support TV specific devices sizes, shapes and d-pad navigation " +
             "supported components. It builds upon the Jetpack Compose libraries."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
 
 // Functions and tasks to monitor changes in copied files.
diff --git a/tv/tv-foundation/lint-baseline.xml b/tv/tv-foundation/lint-baseline.xml
index a4b14b8..25a9c11 100644
--- a/tv/tv-foundation/lint-baseline.xml
+++ b/tv/tv-foundation/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -58,8 +58,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable pinnedItems with type List&lt;? extends Integer>: replace with IntList"
-        errorLine1="        val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices("
-        errorLine2="        ^">
+        errorLine1="            val pinnedItems ="
+        errorLine2="            ^">
         <location
             file="src/main/java/androidx/tv/foundation/lazy/grid/LazyGrid.kt"/>
     </issue>
@@ -166,7 +166,7 @@
     <issue
         id="PrimitiveInCollection"
         message="field previousDefaultSpans with type List&lt;TvGridItemSpan>: replace with LongList"
-        errorLine1="    /**"
+        errorLine1="    /** List of 1x1 spans if we do not have custom spans. */"
         errorLine2="    ^">
         <location
             file="src/main/java/androidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider.kt"/>
@@ -202,8 +202,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable pinnedItems with type List&lt;? extends Integer>: replace with IntList"
-        errorLine1="        val pinnedItems = itemProvider.calculateLazyLayoutPinnedIndices("
-        errorLine2="        ^">
+        errorLine1="            val pinnedItems ="
+        errorLine2="            ^">
         <location
             file="src/main/java/androidx/tv/foundation/lazy/list/LazyList.kt"/>
     </issue>
diff --git a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridPinnableContainerTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridPinnableContainerTest.kt
index c624a4ce..783f649 100644
--- a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridPinnableContainerTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridPinnableContainerTest.kt
@@ -39,6 +39,7 @@
 import androidx.tv.foundation.lazy.list.assertIsNotPlaced
 import androidx.tv.foundation.lazy.list.assertIsPlaced
 import com.google.common.truth.Truth.assertThat
+import kotlin.collections.removeFirst as removeFirstKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Rule
@@ -502,7 +503,7 @@
         while (handles.isNotEmpty()) {
             rule.runOnIdle {
                 assertThat(composed).contains(1)
-                handles.removeFirst().release()
+                handles.removeFirstKt().release()
             }
         }
 
diff --git a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
index 9a9766d..78b3e02 100644
--- a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
@@ -26,11 +26,14 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth
@@ -58,11 +61,12 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(1) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertIsDisplayed()
     }
 
@@ -75,14 +79,16 @@
                 items(100) { Spacer(Modifier.height(itemsSizeDp).fillMaxWidth().testTag("$it")) }
             }
         }
-
+        // Semantics IDs must be fetched before scrolling.
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(2) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
         rule.onNodeWithTag("2").assertIsDisplayed()
     }
 
@@ -99,10 +105,17 @@
                 items(100) { Spacer(Modifier.height(itemsSizeDp).fillMaxWidth().testTag("$it")) }
             }
         }
+        // Semantics IDs must be fetched before scrolling.
+        val deactivatedIds = mutableListOf<Int>()
+        repeat(DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$it").semanticsId())
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(DefaultMaxItemsToRetain + 1) } }
 
-        repeat(DefaultMaxItemsToRetain) { rule.onNodeWithTag("$it").assertIsDeactivated() }
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
+        }
         rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${DefaultMaxItemsToRetain + 1}").assertIsDisplayed()
     }
@@ -117,6 +130,8 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
@@ -135,8 +150,8 @@
         rule.onNodeWithTag("1").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("2").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         // visible
         rule.onNodeWithTag("3").assertIsDisplayed()
@@ -156,6 +171,14 @@
             runBlocking {
                 state.scrollToItem(1) // buffer is [0]
                 state.scrollToItem(2) // 0 used, buffer is [1]
+            }
+        }
+
+        // 3 should be visible at this point, so save its ID to check later
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(3) // 1 used, buffer is [2]
                 state.scrollToItem(4) // 2 used, buffer is [3]
             }
@@ -167,7 +190,7 @@
         rule.onNodeWithTag("2").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
 
         // visible
         rule.onNodeWithTag("4").assertIsDisplayed()
@@ -183,6 +206,10 @@
                 items(100) { Spacer(Modifier.height(itemsSizeDp).fillMaxWidth().testTag("$it")) }
             }
         }
+
+        val id10 = rule.onNodeWithTag("10").semanticsId()
+        val id11 = rule.onNodeWithTag("11").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(8) // buffer is [10, 11]
@@ -190,8 +217,8 @@
         }
 
         // in buffer
-        rule.onNodeWithTag("10").assertIsDeactivated()
-        rule.onNodeWithTag("11").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id10)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id11)
 
         // visible
         rule.onNodeWithTag("8").assertIsDisplayed()
@@ -211,12 +238,18 @@
             runBlocking {
                 state.scrollToItem(9) // buffer is [11]
                 state.scrollToItem(7) // 11 reused, buffer is [9]
+            }
+        }
+        // 8 should be visible at this point, so save its ID to check later
+        val id8 = rule.onNodeWithTag("8").semanticsId()
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(6) // 9 reused, buffer is [8]
             }
         }
 
         // in buffer
-        rule.onNodeWithTag("8").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id8)
 
         // visible
         rule.onNodeWithTag("6").assertIsDisplayed()
@@ -260,6 +293,15 @@
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2) // buffer is [0, 1]
+            }
+        }
+
+        // 2 and 3 should be visible at this point, so save its ID to check later
+        val id2 = rule.onNodeWithTag("2").semanticsId()
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 counter0 = 0
                 counter1 = 0
                 state.scrollToItem(0) // scrolled back, 0 and 1 are reused back. buffer: [2, 3]
@@ -278,8 +320,8 @@
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
-        rule.onNodeWithTag("2").assertIsDeactivated()
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id2)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
     }
 
     @Test
@@ -300,24 +342,25 @@
             }
         }
 
+        val deactivatedIds = mutableListOf<Int>()
         for (i in 0 until visibleItemsCount) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").semanticsId())
             rule.onNodeWithTag("$i").assertIsDisplayed()
         }
+        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").fetchSemanticsNode().id)
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(visibleItemsCount) } }
 
         rule.onNodeWithTag("$visibleItemsCount").assertIsDisplayed()
 
-        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse
-        for (i in 0 until DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
+        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse and 7 items of type 1
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
         }
-        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
 
-        // and 7 items of type 1
-        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
-        }
+        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${startOfType1 + DefaultMaxItemsToRetain}").assertDoesNotExist()
     }
 
@@ -342,6 +385,9 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2)
@@ -349,8 +395,8 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         rule.runOnIdle {
             runBlocking {
@@ -359,12 +405,20 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertDoesNotExist()
         rule.onNodeWithTag("9").assertIsDisplayed()
         rule.onNodeWithTag("10").assertIsDisplayed()
         rule.onNodeWithTag("11").assertIsDisplayed()
     }
+
+    private fun SemanticsNode.assertLayoutDeactivatedById(id: Int) {
+        children.fastForEach {
+            if (it.id == id) {
+                assert(it.layoutInfo.isDeactivated)
+            }
+        }
+    }
 }
 
 private val DefaultMaxItemsToRetain = 7
diff --git a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyColumnTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyColumnTest.kt
index c224662..af734bf 100644
--- a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyColumnTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyColumnTest.kt
@@ -62,6 +62,7 @@
 import androidx.tv.foundation.lazy.AutoTestFrameClock
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.collections.removeLast as removeLastKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Rule
 import org.junit.Test
@@ -325,7 +326,7 @@
             TvLazyColumn { items(items) { item -> Spacer(Modifier.size(itemSize).testTag(item)) } }
         }
 
-        rule.runOnIdle { items.removeLast() }
+        rule.runOnIdle { items.removeLastKt() }
 
         rule.onNodeWithTag("1").assertIsDisplayed()
 
diff --git a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListPinnableContainerTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListPinnableContainerTest.kt
index 7974b68..274ab81 100644
--- a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListPinnableContainerTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListPinnableContainerTest.kt
@@ -38,6 +38,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlin.collections.removeFirst as removeFirstKt
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Ignore
@@ -459,7 +460,7 @@
         while (handles.isNotEmpty()) {
             rule.runOnIdle {
                 assertThat(composed).contains(1)
-                handles.removeFirst().release()
+                handles.removeFirstKt().release()
             }
         }
 
diff --git a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt
index 2de3225..ff1d704 100644
--- a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt
@@ -28,11 +28,14 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.tv.foundation.PivotOffsets
@@ -69,11 +72,12 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(1) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertIsDisplayed()
     }
 
@@ -94,14 +98,16 @@
                 }
             }
         }
-
+        // Semantics IDs must be fetched before scrolling.
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
         rule.runOnIdle { runBlocking { state.scrollToItem(2) } }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
         rule.onNodeWithTag("2").assertIsDisplayed()
     }
 
@@ -122,10 +128,17 @@
                 }
             }
         }
+        // Semantics IDs must be fetched before scrolling.
+        val deactivatedIds = mutableListOf<Int>()
+        repeat(DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$it").semanticsId())
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(DefaultMaxItemsToRetain + 1) } }
 
-        repeat(DefaultMaxItemsToRetain) { rule.onNodeWithTag("$it").assertIsDeactivated() }
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
+        }
         rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${DefaultMaxItemsToRetain + 1}").assertIsDisplayed()
     }
@@ -148,6 +161,8 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
@@ -166,8 +181,8 @@
         rule.onNodeWithTag("1").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("2").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         // visible
         rule.onNodeWithTag("3").assertIsDisplayed()
@@ -195,6 +210,14 @@
             runBlocking {
                 state.scrollToItem(1) // buffer is [0]
                 state.scrollToItem(2) // 0 used, buffer is [1]
+            }
+        }
+
+        // 3 should be visible at this point, so save its ID to check later
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(3) // 1 used, buffer is [2]
                 state.scrollToItem(4) // 2 used, buffer is [3]
             }
@@ -206,7 +229,7 @@
         rule.onNodeWithTag("2").assertDoesNotExist()
 
         // in buffer
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
 
         // visible
         rule.onNodeWithTag("4").assertIsDisplayed()
@@ -230,6 +253,10 @@
                 }
             }
         }
+
+        val id10 = rule.onNodeWithTag("10").semanticsId()
+        val id11 = rule.onNodeWithTag("11").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(8) // buffer is [10, 11]
@@ -237,8 +264,8 @@
         }
 
         // in buffer
-        rule.onNodeWithTag("10").assertIsDeactivated()
-        rule.onNodeWithTag("11").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id10)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id11)
 
         // visible
         rule.onNodeWithTag("8").assertIsDisplayed()
@@ -266,12 +293,18 @@
             runBlocking {
                 state.scrollToItem(9) // buffer is [11]
                 state.scrollToItem(7) // 11 reused, buffer is [9]
+            }
+        }
+        // 8 should be visible at this point, so save its ID to check later
+        val id8 = rule.onNodeWithTag("8").semanticsId()
+        rule.runOnIdle {
+            runBlocking {
                 state.scrollToItem(6) // 9 reused, buffer is [8]
             }
         }
 
         // in buffer
-        rule.onNodeWithTag("8").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id8)
 
         // visible
         rule.onNodeWithTag("6").assertIsDisplayed()
@@ -324,6 +357,15 @@
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2) // buffer is [0, 1]
+            }
+        }
+
+        // 2 and 3 should be visible at this point, so save its ID to check later
+        val id2 = rule.onNodeWithTag("2").semanticsId()
+        val id3 = rule.onNodeWithTag("3").semanticsId()
+
+        rule.runOnIdle {
+            runBlocking {
                 counter0 = 0
                 counter1 = 0
                 state.scrollToItem(0) // scrolled back, 0 and 1 are reused back. buffer: [2, 3]
@@ -342,8 +384,8 @@
         rule.onNodeWithTag("0").assertIsDisplayed()
         rule.onNodeWithTag("1").assertIsDisplayed()
 
-        rule.onNodeWithTag("2").assertIsDeactivated()
-        rule.onNodeWithTag("3").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id2)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id3)
     }
 
     @Test
@@ -364,24 +406,25 @@
             }
         }
 
+        val deactivatedIds = mutableListOf<Int>()
         for (i in 0 until visibleItemsCount) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").semanticsId())
             rule.onNodeWithTag("$i").assertIsDisplayed()
         }
+        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
+            deactivatedIds.add(rule.onNodeWithTag("$i").fetchSemanticsNode().id)
+        }
 
         rule.runOnIdle { runBlocking { state.scrollToItem(visibleItemsCount) } }
 
         rule.onNodeWithTag("$visibleItemsCount").assertIsDisplayed()
 
-        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse
-        for (i in 0 until DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
+        // [DefaultMaxItemsToRetain] items of type 0 are left for reuse and 7 items of type 1
+        deactivatedIds.fastForEach {
+            rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(it)
         }
-        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
 
-        // and 7 items of type 1
-        for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
-            rule.onNodeWithTag("$i").assertIsDeactivated()
-        }
+        rule.onNodeWithTag("$DefaultMaxItemsToRetain").assertDoesNotExist()
         rule.onNodeWithTag("${startOfType1 + DefaultMaxItemsToRetain}").assertDoesNotExist()
     }
 
@@ -410,6 +453,9 @@
             }
         }
 
+        val id0 = rule.onNodeWithTag("0").semanticsId()
+        val id1 = rule.onNodeWithTag("1").semanticsId()
+
         rule.runOnIdle {
             runBlocking {
                 state.scrollToItem(2)
@@ -417,8 +463,8 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
-        rule.onNodeWithTag("1").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id1)
 
         rule.runOnIdle {
             runBlocking {
@@ -427,12 +473,20 @@
             }
         }
 
-        rule.onNodeWithTag("0").assertIsDeactivated()
+        rule.onRoot().fetchSemanticsNode().assertLayoutDeactivatedById(id0)
         rule.onNodeWithTag("1").assertDoesNotExist()
         rule.onNodeWithTag("9").assertIsDisplayed()
         rule.onNodeWithTag("10").assertIsDisplayed()
         rule.onNodeWithTag("11").assertIsDisplayed()
     }
+
+    private fun SemanticsNode.assertLayoutDeactivatedById(id: Int) {
+        children.fastForEach {
+            if (it.id == id) {
+                assert(it.layoutInfo.isDeactivated)
+            }
+        }
+    }
 }
 
 private val DefaultMaxItemsToRetain = 7
diff --git a/tv/tv-material/api/current.ignore b/tv/tv-material/api/current.ignore
new file mode 100644
index 0000000..df50657
--- /dev/null
+++ b/tv/tv-material/api/current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+AddedMethod: androidx.tv.material3.DrawerState#DrawerState():
+    Added constructor androidx.tv.material3.DrawerState()
+AddedMethod: androidx.tv.material3.Shapes#Shapes():
+    Added constructor androidx.tv.material3.Shapes()
+AddedMethod: androidx.tv.material3.Typography#Typography():
+    Added constructor androidx.tv.material3.Typography()
diff --git a/tv/tv-material/api/current.txt b/tv/tv-material/api/current.txt
index baebe2c..81b6dde 100644
--- a/tv/tv-material/api/current.txt
+++ b/tv/tv-material/api/current.txt
@@ -146,6 +146,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselState {
+    ctor public CarouselState();
     ctor public CarouselState(optional int initialActiveItemIndex);
     method public int getActiveItemIndex();
     method public androidx.tv.material3.ScrollPauseHandle pauseAutoScroll(int itemIndex);
@@ -333,6 +334,7 @@
   }
 
   public final class DrawerState {
+    ctor public DrawerState();
     ctor public DrawerState(optional androidx.tv.material3.DrawerValue initialValue);
     method public androidx.tv.material3.DrawerValue getCurrentValue();
     method public void setValue(androidx.tv.material3.DrawerValue drawerValue);
@@ -880,6 +882,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.tv.material3.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
@@ -1013,6 +1016,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography();
     ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.tv.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.ui.text.TextStyle getBodyLarge();
diff --git a/tv/tv-material/api/restricted_current.ignore b/tv/tv-material/api/restricted_current.ignore
new file mode 100644
index 0000000..df50657
--- /dev/null
+++ b/tv/tv-material/api/restricted_current.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+AddedMethod: androidx.tv.material3.DrawerState#DrawerState():
+    Added constructor androidx.tv.material3.DrawerState()
+AddedMethod: androidx.tv.material3.Shapes#Shapes():
+    Added constructor androidx.tv.material3.Shapes()
+AddedMethod: androidx.tv.material3.Typography#Typography():
+    Added constructor androidx.tv.material3.Typography()
diff --git a/tv/tv-material/api/restricted_current.txt b/tv/tv-material/api/restricted_current.txt
index baebe2c..81b6dde 100644
--- a/tv/tv-material/api/restricted_current.txt
+++ b/tv/tv-material/api/restricted_current.txt
@@ -146,6 +146,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselState {
+    ctor public CarouselState();
     ctor public CarouselState(optional int initialActiveItemIndex);
     method public int getActiveItemIndex();
     method public androidx.tv.material3.ScrollPauseHandle pauseAutoScroll(int itemIndex);
@@ -333,6 +334,7 @@
   }
 
   public final class DrawerState {
+    ctor public DrawerState();
     ctor public DrawerState(optional androidx.tv.material3.DrawerValue initialValue);
     method public androidx.tv.material3.DrawerValue getCurrentValue();
     method public void setValue(androidx.tv.material3.DrawerValue drawerValue);
@@ -880,6 +882,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.tv.material3.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
@@ -1013,6 +1016,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography();
     ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.tv.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.ui.text.TextStyle getBodyLarge();
diff --git a/tv/tv-material/build.gradle b/tv/tv-material/build.gradle
index 99e344a..59df813 100644
--- a/tv/tv-material/build.gradle
+++ b/tv/tv-material/build.gradle
@@ -37,7 +37,7 @@
     def composeVersion = "1.6.8"
     def profileInstallerVersion = "1.3.1"
 
-    api("androidx.annotation:annotation:$annotationVersion")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.animation:animation:$composeVersion")
     api("androidx.compose.foundation:foundation:$composeVersion")
     api("androidx.compose.foundation:foundation-layout:$composeVersion")
@@ -61,6 +61,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.tv.material"
     sourceSets.androidTest.assets.srcDirs +=
              project.rootDir.absolutePath + "/../../golden/tv/compose/material3"
@@ -73,5 +74,6 @@
     inceptionYear = "2022"
     description = "build TV applications using controls that adhere to Material Design Language."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":tv:tv-material-samples"))
 }
diff --git a/tv/tv-material/samples/build.gradle b/tv/tv-material/samples/build.gradle
index 2ab5b8b..acfcca2 100644
--- a/tv/tv-material/samples/build.gradle
+++ b/tv/tv-material/samples/build.gradle
@@ -47,6 +47,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 28
     }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
index 9e12736..3b62473 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
@@ -40,6 +40,7 @@
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.key.NativeKeyEvent
@@ -383,6 +384,7 @@
                         this.alpha = surfaceAlpha
                         this.shape = shape
                         this.clip = true
+                        this.compositingStrategy = CompositingStrategy.Offscreen
                     },
             propagateMinConstraints = true
         ) {
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceScale.kt b/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceScale.kt
index d613f42..a773335 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceScale.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/SurfaceScale.kt
@@ -27,8 +27,7 @@
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
-import androidx.compose.ui.graphics.drawscope.scale
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.tv.material3.tokens.SurfaceScaleTokens
 
 @Composable
@@ -48,7 +47,7 @@
             label = "tv-surface-scale"
         )
 
-    return drawWithContent { scale(animatedScale) { [email protected]() } }
+    return this.graphicsLayer(scaleX = animatedScale, scaleY = animatedScale)
 }
 
 private fun defaultScaleAnimationSpec(interaction: Interaction): TweenSpec<Float> =
diff --git a/tvprovider/tvprovider/build.gradle b/tvprovider/tvprovider/build.gradle
index fb73076..d37fd1b 100644
--- a/tvprovider/tvprovider/build.gradle
+++ b/tvprovider/tvprovider/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
 
     androidTestImplementation(libs.testExtJunit)
@@ -33,5 +33,4 @@
     inceptionYear = "2017"
     description = "Android Support Library for TV Provider"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/tvprovider/tvprovider/lint-baseline.xml b/tvprovider/tvprovider/lint-baseline.xml
index 6bd4e3c..c3e7f1d 100644
--- a/tvprovider/tvprovider/lint-baseline.xml
+++ b/tvprovider/tvprovider/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -11,60 +11,6 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.tvprovider.media.tv.TvContractCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return TvContract.buildRecordedProgramUri(recordedProgramId);"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.tvprovider.media.tv.TvContractCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return TvContract.isChannelUri(uri);"
-        errorLine2="                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.tvprovider.media.tv.TvContractCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return TvContract.isChannelUriForTunerInput(uri);"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.tvprovider.media.tv.TvContractCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return TvContract.isChannelUriForPassthroughInput(uri);"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 24; however, the containing class androidx.tvprovider.media.tv.TvContractCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            return TvContract.isProgramUri(uri);"
-        errorLine2="                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 26; however, the containing class androidx.tvprovider.media.tv.TvContractCompat is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            TvContract.requestChannelBrowsable(context, channelId);"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java"/>
-    </issue>
-
-    <issue
         id="PrivateConstructorForUtilityClass"
         message="Utility class is missing private constructor"
         errorLine1="public class ChannelLogoUtils {"
diff --git a/vectordrawable/integration-tests/testapp/build.gradle b/vectordrawable/integration-tests/testapp/build.gradle
index 031dfd2..9dccbed 100644
--- a/vectordrawable/integration-tests/testapp/build.gradle
+++ b/vectordrawable/integration-tests/testapp/build.gradle
@@ -28,6 +28,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         vectorDrawables.useSupportLibrary = true
     }
diff --git a/vectordrawable/vectordrawable-animated/build.gradle b/vectordrawable/vectordrawable-animated/build.gradle
index 67a39ed..247b616 100644
--- a/vectordrawable/vectordrawable-animated/build.gradle
+++ b/vectordrawable/vectordrawable-animated/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":vectordrawable:vectordrawable"))
     implementation("androidx.core:core:1.12.0")
     implementation("androidx.interpolator:interpolator:1.0.0")
@@ -47,5 +47,4 @@
     inceptionYear = "2015"
     description = "Android Support AnimatedVectorDrawable"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/vectordrawable/vectordrawable-seekable/build.gradle b/vectordrawable/vectordrawable-seekable/build.gradle
index 74444a9..29d266b 100644
--- a/vectordrawable/vectordrawable-seekable/build.gradle
+++ b/vectordrawable/vectordrawable-seekable/build.gradle
@@ -48,5 +48,4 @@
     mavenVersion = LibraryVersions.VECTORDRAWABLE_SEEKABLE
     inceptionYear = "2020"
     description = "Android SeekableAnimatedVectorDrawable"
-    metalavaK2UastEnabled = true
 }
diff --git a/vectordrawable/vectordrawable/build.gradle b/vectordrawable/vectordrawable/build.gradle
index 8d3d42c..a17b192 100644
--- a/vectordrawable/vectordrawable/build.gradle
+++ b/vectordrawable/vectordrawable/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.13.0")
     implementation("androidx.collection:collection:1.1.0")
 
@@ -41,5 +41,4 @@
     inceptionYear = "2015"
     description = "Android Support VectorDrawable"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/versionedparcelable/versionedparcelable/build.gradle b/versionedparcelable/versionedparcelable/build.gradle
index f580358..748c8e1 100644
--- a/versionedparcelable/versionedparcelable/build.gradle
+++ b/versionedparcelable/versionedparcelable/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation("androidx.collection:collection:1.0.0")
 
     androidTestImplementation(libs.testExtJunit)
@@ -63,5 +63,4 @@
     inceptionYear = "2018"
     description = "Provides a stable but relatively compact binary serialization format that can be passed across processes or persisted safely."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
diff --git a/viewpager/viewpager/build.gradle b/viewpager/viewpager/build.gradle
index f8d403d..70f64ab 100644
--- a/viewpager/viewpager/build.gradle
+++ b/viewpager/viewpager/build.gradle
@@ -13,7 +13,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     implementation "androidx.core:core:1.7.0"
     api("androidx.customview:customview:1.0.0")
 
@@ -33,7 +33,6 @@
     inceptionYear = "2018"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/viewpager2/integration-tests/targetsdk-tests/build.gradle b/viewpager2/integration-tests/targetsdk-tests/build.gradle
index e032db1..017417e 100644
--- a/viewpager2/integration-tests/targetsdk-tests/build.gradle
+++ b/viewpager2/integration-tests/targetsdk-tests/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdk 35
     flavorDimensions = ["targetSdk"]
     productFlavors {
         targetSdk29 {
diff --git a/viewpager2/integration-tests/targetsdk-tests/src/androidTest/kotlin/androidx/viewpager2/integration/targetsdktests/OnApplyWindowInsetsListenerTest.kt b/viewpager2/integration-tests/targetsdk-tests/src/androidTest/kotlin/androidx/viewpager2/integration/targetsdktests/OnApplyWindowInsetsListenerTest.kt
index 5c75467..b37fd42 100644
--- a/viewpager2/integration-tests/targetsdk-tests/src/androidTest/kotlin/androidx/viewpager2/integration/targetsdktests/OnApplyWindowInsetsListenerTest.kt
+++ b/viewpager2/integration-tests/targetsdk-tests/src/androidTest/kotlin/androidx/viewpager2/integration/targetsdktests/OnApplyWindowInsetsListenerTest.kt
@@ -114,6 +114,16 @@
                     orientation = LinearLayout.VERTICAL
                 }
 
+            // Calling setContentView when the window is attached will make the window dispatch
+            // WindowInsets again. To avoid the insets dispatched by window affecting this test,
+            // here:
+            // 1. exits the onActivity scope after calling setContentView,
+            // 2. lets the window dispatch WindowInsets, and then
+            // 3. adds the rest views to viewRoot in the next message.
+            it.setContentView(viewRoot)
+        }
+
+        activityTestRule.scenario.onActivity {
             viewPager =
                 ViewPager2(it).apply {
                     layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, 0, 1f)
@@ -131,8 +141,6 @@
                 tag = "SIBLING"
                 viewRoot.addView(this)
             }
-
-            it.setContentView(viewRoot)
         }
     }
 
diff --git a/viewpager2/integration-tests/testapp/build.gradle b/viewpager2/integration-tests/testapp/build.gradle
index bc3fc1d..ff23ca5 100644
--- a/viewpager2/integration-tests/testapp/build.gradle
+++ b/viewpager2/integration-tests/testapp/build.gradle
@@ -42,5 +42,6 @@
 }
 
 android {
+    compileSdkVersion 35
     namespace "androidx.viewpager2.integration.testapp"
 }
diff --git a/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/BaseCardActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/BaseCardActivity.kt
index ad9dbbf..61a00e2 100644
--- a/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/BaseCardActivity.kt
+++ b/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/BaseCardActivity.kt
@@ -82,6 +82,7 @@
 
         UserInputController(viewPager, findViewById(R.id.disable_user_input_checkbox)).setUp()
         OrientationController(viewPager, findViewById(R.id.orientation_spinner)).setUp()
+        OverScrollModeController(viewPager, findViewById(R.id.overscroll_mode_spinner)).setUp()
         cardSelector.adapter = createCardAdapter()
 
         viewPager.setPageTransformer(mAnimator)
diff --git a/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/OverScrollModeController.kt b/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/OverScrollModeController.kt
new file mode 100644
index 0000000..aea0a5b
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/OverScrollModeController.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.viewpager2.integration.testapp
+
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
+import androidx.viewpager2.widget.ViewPager2
+
+/**
+ * It configures a spinner to show overScrollModes and sets the overScrollMode of a ViewPager2 when
+ * an overScrollMode is selected.
+ */
+class OverScrollModeController(private val viewPager: ViewPager2, private val spinner: Spinner) {
+    fun setUp() {
+        val overScrollMode = viewPager.overScrollMode
+        val adapter =
+            ArrayAdapter(
+                spinner.context,
+                android.R.layout.simple_spinner_item,
+                arrayOf(ALWAYS, IF_CONTENT_SCROLLS, NEVER)
+            )
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+        spinner.adapter = adapter
+
+        val initialPosition = adapter.getPosition(overScrollModeToString(overScrollMode))
+        if (initialPosition >= 0) {
+            spinner.setSelection(initialPosition)
+        }
+
+        spinner.onItemSelectedListener =
+            object : AdapterView.OnItemSelectedListener {
+                override fun onItemSelected(
+                    parent: AdapterView<*>,
+                    view: View?,
+                    position: Int,
+                    id: Long
+                ) {
+                    viewPager.overScrollMode =
+                        stringToOverScrollMode(parent.selectedItem.toString())
+                }
+
+                override fun onNothingSelected(adapterView: AdapterView<*>) {}
+            }
+    }
+
+    private fun overScrollModeToString(overScrollMode: Int): String {
+        return when (overScrollMode) {
+            View.OVER_SCROLL_ALWAYS -> ALWAYS
+            View.OVER_SCROLL_IF_CONTENT_SCROLLS -> IF_CONTENT_SCROLLS
+            View.OVER_SCROLL_NEVER -> NEVER
+            else -> throw IllegalArgumentException("OverScrollMode $overScrollMode doesn't exist")
+        }
+    }
+
+    internal fun stringToOverScrollMode(string: String): Int {
+        return when (string) {
+            ALWAYS -> View.OVER_SCROLL_ALWAYS
+            IF_CONTENT_SCROLLS -> View.OVER_SCROLL_IF_CONTENT_SCROLLS
+            NEVER -> View.OVER_SCROLL_NEVER
+            else -> throw IllegalArgumentException("OverScrollMode $string doesn't exist")
+        }
+    }
+
+    companion object {
+        private const val ALWAYS = "always"
+        private const val IF_CONTENT_SCROLLS = "ifContentScrolls"
+        private const val NEVER = "never"
+    }
+}
diff --git a/viewpager2/integration-tests/testapp/src/main/res/layout-land/controls.xml b/viewpager2/integration-tests/testapp/src/main/res/layout-land/controls.xml
index 2fa297e..3b822e9 100644
--- a/viewpager2/integration-tests/testapp/src/main/res/layout-land/controls.xml
+++ b/viewpager2/integration-tests/testapp/src/main/res/layout-land/controls.xml
@@ -41,6 +41,25 @@
 
         </LinearLayout>
 
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical|start"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/label_overscroll"
+                android:textAppearance="@android:style/TextAppearance.Medium" />
+
+            <Spinner
+                android:id="@+id/overscroll_mode_spinner"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+
+        </LinearLayout>
+
         <CheckBox
             android:id="@+id/disable_user_input_checkbox"
             android:layout_width="wrap_content"
diff --git a/viewpager2/integration-tests/testapp/src/main/res/layout/controls.xml b/viewpager2/integration-tests/testapp/src/main/res/layout/controls.xml
index ba32af4..772fa36 100644
--- a/viewpager2/integration-tests/testapp/src/main/res/layout/controls.xml
+++ b/viewpager2/integration-tests/testapp/src/main/res/layout/controls.xml
@@ -38,6 +38,27 @@
 
     </LinearLayout>
 
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:gravity="center_vertical|start"
+        android:orientation="horizontal">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/label_overscroll"
+            android:textAppearance="@android:style/TextAppearance.Medium" />
+
+        <Spinner
+            android:id="@+id/overscroll_mode_spinner"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
+
     <CheckBox
         android:id="@+id/disable_user_input_checkbox"
         android:layout_width="wrap_content"
diff --git a/viewpager2/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/viewpager2/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index 74388fd..653bfd8 100644
--- a/viewpager2/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/viewpager2/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
     <string name="activity_sample_code">ViewPager2 Demos</string>
     <string name="label_orientation">Orientation:</string>
+    <string name="label_overscroll">OverScrollMode:</string>
     <string name="label_jump_to">Jump to:</string>
     <string name="label_jump">Jump</string>
     <string name="disable_user_input">Disable user input</string>
diff --git a/viewpager2/viewpager2/build.gradle b/viewpager2/viewpager2/build.gradle
index abddf8a..8045913 100644
--- a/viewpager2/viewpager2/build.gradle
+++ b/viewpager2/viewpager2/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.annotation:annotation-experimental:1.4.1")
     implementation("androidx.core:core:1.3.2")
     api("androidx.fragment:fragment:1.1.0")
@@ -62,7 +62,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "AndroidX Widget ViewPager2"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OverScrollModeTest.java b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OverScrollModeTest.java
new file mode 100644
index 0000000..18fe0ed
--- /dev/null
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OverScrollModeTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.viewpager2.widget;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.viewpager2.test.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class OverScrollModeTest {
+    @Test
+    public void test_overScrollMode_noAttrs() {
+        ViewPager2 viewPager = new ViewPager2(ApplicationProvider.getApplicationContext());
+        assertThat(viewPager.getOverScrollMode(),
+                equalTo(View.OVER_SCROLL_IF_CONTENT_SCROLLS));
+        assertOverScrollModeSyncWithRecyclerView(viewPager);
+    }
+
+    @Test
+    public void test_overScrollMode_nullAttrs() {
+        ViewPager2 viewPager = new ViewPager2(ApplicationProvider.getApplicationContext(),
+                null);
+        assertThat(viewPager.getOverScrollMode(),
+                equalTo(View.OVER_SCROLL_IF_CONTENT_SCROLLS));
+        assertOverScrollModeSyncWithRecyclerView(viewPager);
+    }
+
+    @Test
+    public void test_overScrollMode_default() {
+        assertOverScrollModeCorrect(R.layout.overscroll_mode_default,
+                View.OVER_SCROLL_IF_CONTENT_SCROLLS);
+    }
+
+    @Test
+    public void test_overScrollMode_always() {
+        assertOverScrollModeCorrect(R.layout.overscroll_mode_always, View.OVER_SCROLL_ALWAYS);
+    }
+
+    @Test
+    public void test_overScrollMode_ifContentScrolls() {
+        assertOverScrollModeCorrect(R.layout.overscroll_mode_if_content_scrolls,
+                View.OVER_SCROLL_IF_CONTENT_SCROLLS);
+    }
+
+    @Test
+    public void test_overScrollMode_never() {
+        assertOverScrollModeCorrect(R.layout.overscroll_mode_never, View.OVER_SCROLL_NEVER);
+    }
+
+    @Test
+    public void test_overScrollMode_manual_set() {
+        ViewPager2 viewPager = new ViewPager2(ApplicationProvider.getApplicationContext());
+        viewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
+        assertThat(viewPager.getOverScrollMode(), equalTo(View.OVER_SCROLL_NEVER));
+        assertOverScrollModeSyncWithRecyclerView(viewPager);
+    }
+
+    private void assertOverScrollModeSyncWithRecyclerView(ViewPager2 viewPager2) {
+        final int expectedOverScrollMode = viewPager2.getOverScrollMode();
+        final int childCount = viewPager2.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View childView = viewPager2.getChildAt(i);
+            if (childView instanceof RecyclerView) {
+                final int rvOverScrollMode = childView.getOverScrollMode();
+                assertThat(rvOverScrollMode, equalTo(expectedOverScrollMode));
+                return;
+            }
+        }
+    }
+
+    private void assertOverScrollModeCorrect(int layoutId, int expectedOverScrollMode) {
+        LayoutInflater layoutInflater = (LayoutInflater) checkNotNull(
+                ApplicationProvider.getApplicationContext().getSystemService(
+                        Context.LAYOUT_INFLATER_SERVICE));
+        ViewPager2 viewPager = (ViewPager2) layoutInflater.inflate(layoutId, null);
+        assertThat(viewPager.getOverScrollMode(), equalTo(expectedOverScrollMode));
+        assertOverScrollModeSyncWithRecyclerView(viewPager);
+    }
+}
diff --git a/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_always.xml b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_always.xml
new file mode 100644
index 0000000..8a0583e
--- /dev/null
+++ b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_always.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<androidx.viewpager2.widget.ViewPager2
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:overScrollMode="always"/>
\ No newline at end of file
diff --git a/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_default.xml b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_default.xml
new file mode 100644
index 0000000..e987013
--- /dev/null
+++ b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_default.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<androidx.viewpager2.widget.ViewPager2
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"/>
\ No newline at end of file
diff --git a/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_if_content_scrolls.xml b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_if_content_scrolls.xml
new file mode 100644
index 0000000..4dd78e6
--- /dev/null
+++ b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_if_content_scrolls.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<androidx.viewpager2.widget.ViewPager2
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:overScrollMode="ifContentScrolls"/>
\ No newline at end of file
diff --git a/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_never.xml b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_never.xml
new file mode 100644
index 0000000..320e1c2
--- /dev/null
+++ b/viewpager2/viewpager2/src/androidTest/res/layout/overscroll_mode_never.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<androidx.viewpager2.widget.ViewPager2
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:overScrollMode="never"/>
\ No newline at end of file
diff --git a/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
index 93946b2..f2a17f1 100644
--- a/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
+++ b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -211,6 +211,9 @@
         // don't want to respond on the events sent out during the attach process
         mRecyclerView.addOnScrollListener(mScrollEventAdapter);
 
+        // Pass-through ViewPager's overScrollMode settings to its impl
+        mRecyclerView.setOverScrollMode(getOverScrollMode());
+
         mPageChangeEventDispatcher = new CompositeOnPageChangeCallback(3);
         mScrollEventAdapter.setOnPageChangeCallback(mPageChangeEventDispatcher);
 
@@ -540,6 +543,15 @@
         }
     }
 
+    @Override
+    public void setOverScrollMode(int overScrollMode) {
+        // Skip calling mRecyclerView when it is not initialized
+        if (mRecyclerView != null) {
+            mRecyclerView.setOverScrollMode(overScrollMode);
+        }
+        super.setOverScrollMode(overScrollMode);
+    }
+
     /** Updates {@link #mCurrentItem} based on what is currently visible in the viewport. */
     void updateCurrentItem() {
         if (mPagerSnapHelper == null) {
diff --git a/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle b/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle
index 54fcef0..4eacf1f 100644
--- a/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle
+++ b/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         minSdk 28
     }
diff --git a/wear/compose/compose-foundation/api/current.txt b/wear/compose/compose-foundation/api/current.txt
index 929c2b9..8d64456 100644
--- a/wear/compose/compose-foundation/api/current.txt
+++ b/wear/compose/compose-foundation/api/current.txt
@@ -166,6 +166,7 @@
   }
 
   public final class CurvedTextStyle {
+    ctor public CurvedTextStyle();
     ctor public CurvedTextStyle(androidx.compose.ui.text.TextStyle style);
     ctor @Deprecated public CurvedTextStyle(optional long background, optional long color, optional long fontSize);
     ctor @Deprecated public CurvedTextStyle(optional long background, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis);
@@ -230,10 +231,12 @@
   }
 
   public final class HierarchicalFocusCoordinatorKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void HierarchicalFocusCoordinator(kotlin.jvm.functions.Function0<java.lang.Boolean> requiresFocus, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void OnFocusChange(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void RequestFocusWhenActive(androidx.compose.ui.focus.FocusRequester focusRequester);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
+    method @androidx.compose.runtime.Composable public static void ActiveFocusListener(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
+    method @androidx.compose.runtime.Composable public static void ActiveFocusRequester(androidx.compose.ui.focus.FocusRequester focusRequester);
+    method @androidx.compose.runtime.Composable public static void HierarchicalFocusCoordinator(kotlin.jvm.functions.Function0<java.lang.Boolean> requiresFocus, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void OnFocusChange(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
+    method @Deprecated @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void RequestFocusWhenActive(androidx.compose.ui.focus.FocusRequester focusRequester);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
   }
 
   @SuppressCompatibility @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public fun interface ReduceMotion {
@@ -299,6 +302,7 @@
 
   public interface ScrollInfoProvider {
     method public float getAnchorItemOffset();
+    method public float getLastItemOffset();
     method public boolean isScrollAwayValid();
     method public boolean isScrollInProgress();
     method public boolean isScrollable();
@@ -306,12 +310,13 @@
     property public abstract boolean isScrollAwayValid;
     property public abstract boolean isScrollInProgress;
     property public abstract boolean isScrollable;
+    property public abstract float lastItemOffset;
   }
 
   public final class ScrollInfoProviderKt {
-    method public static androidx.wear.compose.foundation.ScrollInfoProvider toScrollAwayInfoProvider(androidx.compose.foundation.lazy.LazyListState);
-    method public static androidx.wear.compose.foundation.ScrollInfoProvider toScrollAwayInfoProvider(androidx.compose.foundation.ScrollState);
-    method public static androidx.wear.compose.foundation.ScrollInfoProvider toScrollAwayInfoProvider(androidx.wear.compose.foundation.lazy.ScalingLazyListState);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.compose.foundation.lazy.LazyListState state);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.compose.foundation.ScrollState state, optional float bottomButtonHeight);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.lazy.ScalingLazyListState state);
   }
 
   public final class SwipeToDismissBoxDefaults {
@@ -323,6 +328,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class SwipeToDismissBoxState {
+    ctor public SwipeToDismissBoxState();
     ctor public SwipeToDismissBoxState(optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.SwipeToDismissValue,java.lang.Boolean> confirmStateChange);
     method public androidx.wear.compose.foundation.SwipeToDismissValue getCurrentValue();
     method public androidx.wear.compose.foundation.SwipeToDismissValue getTargetValue();
@@ -354,9 +360,72 @@
 package androidx.wear.compose.foundation.lazy {
 
   @androidx.compose.runtime.Immutable public final class AutoCenteringParams {
+    ctor public AutoCenteringParams();
     ctor public AutoCenteringParams(optional int itemIndex, optional int itemOffset);
   }
 
+  public final class LazyColumnDslKt {
+    method public static inline <T> void items(androidx.wear.compose.foundation.lazy.LazyColumnScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function1<? super T,? extends java.lang.Object?> contentType, kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.lazy.LazyColumnItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,? extends java.lang.Object?> contentType, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.wear.compose.foundation.lazy.LazyColumnScopeMarker public sealed interface LazyColumnItemScope {
+    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress? getScrollProgress(androidx.compose.ui.graphics.drawscope.DrawScope);
+    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress? getScrollProgress(androidx.compose.ui.graphics.GraphicsLayerScope);
+    method public androidx.compose.ui.Modifier transformedHeight(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress,java.lang.Integer> heightProvider);
+  }
+
+  public sealed interface LazyColumnItemScrollProgress {
+    method public float getBottomOffsetFraction();
+    method public float getTopOffsetFraction();
+    property public abstract float bottomOffsetFraction;
+    property public abstract float topOffsetFraction;
+  }
+
+  public final class LazyColumnKt {
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.lazy.LazyColumnState state, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.LazyColumnScope,kotlin.Unit> content);
+  }
+
+  public sealed interface LazyColumnLayoutInfo {
+    method public int getTotalItemsCount();
+    method public java.util.List<androidx.wear.compose.foundation.lazy.LazyColumnVisibleItemInfo> getVisibleItems();
+    property public abstract int totalItemsCount;
+    property public abstract java.util.List<androidx.wear.compose.foundation.lazy.LazyColumnVisibleItemInfo> visibleItems;
+  }
+
+  @androidx.wear.compose.foundation.lazy.LazyColumnScopeMarker public sealed interface LazyColumnScope {
+    method public void item(optional Object? key, optional Object? contentType, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.LazyColumnItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,? extends java.lang.Object?> contentType, kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.lazy.LazyColumnItemScope,? super java.lang.Integer,kotlin.Unit> content);
+  }
+
+  @kotlin.DslMarker public @interface LazyColumnScopeMarker {
+  }
+
+  public final class LazyColumnState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor public LazyColumnState();
+    method public float dispatchRawDelta(float delta);
+    method public androidx.wear.compose.foundation.lazy.LazyColumnLayoutInfo getLayoutInfo();
+    method public boolean isScrollInProgress();
+    method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public boolean isScrollInProgress;
+    property public final androidx.wear.compose.foundation.lazy.LazyColumnLayoutInfo layoutInfo;
+  }
+
+  public final class LazyColumnStateKt {
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.foundation.lazy.LazyColumnState rememberLazyColumnState();
+  }
+
+  public sealed interface LazyColumnVisibleItemInfo {
+    method public int getHeight();
+    method public int getIndex();
+    method public int getOffset();
+    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress getScrollProgress();
+    property public abstract int height;
+    property public abstract int index;
+    property public abstract int offset;
+    property public abstract androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress scrollProgress;
+  }
+
   public final class ScalingLazyColumnDefaults {
     method public androidx.wear.compose.foundation.lazy.ScalingParams scalingParams(optional float edgeScale, optional float edgeAlpha, optional float minElementHeight, optional float maxElementHeight, optional float minTransitionArea, optional float maxTransitionArea, optional androidx.compose.animation.core.Easing scaleInterpolator, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Constraints,java.lang.Integer> viewportVerticalOffsetResolver);
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior snapFlingBehavior(androidx.wear.compose.foundation.lazy.ScalingLazyListState state, optional float snapOffset, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decay);
@@ -441,6 +510,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class ScalingLazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor public ScalingLazyListState();
     ctor public ScalingLazyListState(optional int initialCenterItemIndex, optional int initialCenterItemScrollOffset);
     method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public float dispatchRawDelta(float delta);
diff --git a/wear/compose/compose-foundation/api/restricted_current.txt b/wear/compose/compose-foundation/api/restricted_current.txt
index 929c2b9..8d64456 100644
--- a/wear/compose/compose-foundation/api/restricted_current.txt
+++ b/wear/compose/compose-foundation/api/restricted_current.txt
@@ -166,6 +166,7 @@
   }
 
   public final class CurvedTextStyle {
+    ctor public CurvedTextStyle();
     ctor public CurvedTextStyle(androidx.compose.ui.text.TextStyle style);
     ctor @Deprecated public CurvedTextStyle(optional long background, optional long color, optional long fontSize);
     ctor @Deprecated public CurvedTextStyle(optional long background, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontSynthesis? fontSynthesis);
@@ -230,10 +231,12 @@
   }
 
   public final class HierarchicalFocusCoordinatorKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void HierarchicalFocusCoordinator(kotlin.jvm.functions.Function0<java.lang.Boolean> requiresFocus, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void OnFocusChange(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void RequestFocusWhenActive(androidx.compose.ui.focus.FocusRequester focusRequester);
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
+    method @androidx.compose.runtime.Composable public static void ActiveFocusListener(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
+    method @androidx.compose.runtime.Composable public static void ActiveFocusRequester(androidx.compose.ui.focus.FocusRequester focusRequester);
+    method @androidx.compose.runtime.Composable public static void HierarchicalFocusCoordinator(kotlin.jvm.functions.Function0<java.lang.Boolean> requiresFocus, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @Deprecated @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void OnFocusChange(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super java.lang.Boolean,kotlin.Unit> onFocusChanged);
+    method @Deprecated @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public static void RequestFocusWhenActive(androidx.compose.ui.focus.FocusRequester focusRequester);
+    method @androidx.compose.runtime.Composable public static androidx.compose.ui.focus.FocusRequester rememberActiveFocusRequester();
   }
 
   @SuppressCompatibility @androidx.wear.compose.foundation.ExperimentalWearFoundationApi public fun interface ReduceMotion {
@@ -299,6 +302,7 @@
 
   public interface ScrollInfoProvider {
     method public float getAnchorItemOffset();
+    method public float getLastItemOffset();
     method public boolean isScrollAwayValid();
     method public boolean isScrollInProgress();
     method public boolean isScrollable();
@@ -306,12 +310,13 @@
     property public abstract boolean isScrollAwayValid;
     property public abstract boolean isScrollInProgress;
     property public abstract boolean isScrollable;
+    property public abstract float lastItemOffset;
   }
 
   public final class ScrollInfoProviderKt {
-    method public static androidx.wear.compose.foundation.ScrollInfoProvider toScrollAwayInfoProvider(androidx.compose.foundation.lazy.LazyListState);
-    method public static androidx.wear.compose.foundation.ScrollInfoProvider toScrollAwayInfoProvider(androidx.compose.foundation.ScrollState);
-    method public static androidx.wear.compose.foundation.ScrollInfoProvider toScrollAwayInfoProvider(androidx.wear.compose.foundation.lazy.ScalingLazyListState);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.compose.foundation.lazy.LazyListState state);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.compose.foundation.ScrollState state, optional float bottomButtonHeight);
+    method public static androidx.wear.compose.foundation.ScrollInfoProvider ScrollInfoProvider(androidx.wear.compose.foundation.lazy.ScalingLazyListState state);
   }
 
   public final class SwipeToDismissBoxDefaults {
@@ -323,6 +328,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class SwipeToDismissBoxState {
+    ctor public SwipeToDismissBoxState();
     ctor public SwipeToDismissBoxState(optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.SwipeToDismissValue,java.lang.Boolean> confirmStateChange);
     method public androidx.wear.compose.foundation.SwipeToDismissValue getCurrentValue();
     method public androidx.wear.compose.foundation.SwipeToDismissValue getTargetValue();
@@ -354,9 +360,72 @@
 package androidx.wear.compose.foundation.lazy {
 
   @androidx.compose.runtime.Immutable public final class AutoCenteringParams {
+    ctor public AutoCenteringParams();
     ctor public AutoCenteringParams(optional int itemIndex, optional int itemOffset);
   }
 
+  public final class LazyColumnDslKt {
+    method public static inline <T> void items(androidx.wear.compose.foundation.lazy.LazyColumnScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function1<? super T,?>? key, optional kotlin.jvm.functions.Function1<? super T,? extends java.lang.Object?> contentType, kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.lazy.LazyColumnItemScope,? super T,kotlin.Unit> itemContent);
+    method public static inline <T> void itemsIndexed(androidx.compose.foundation.lazy.LazyListScope, java.util.List<? extends T> items, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,?>? key, optional kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,? extends java.lang.Object?> contentType, kotlin.jvm.functions.Function3<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,? super T,kotlin.Unit> itemContent);
+  }
+
+  @androidx.wear.compose.foundation.lazy.LazyColumnScopeMarker public sealed interface LazyColumnItemScope {
+    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress? getScrollProgress(androidx.compose.ui.graphics.drawscope.DrawScope);
+    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress? getScrollProgress(androidx.compose.ui.graphics.GraphicsLayerScope);
+    method public androidx.compose.ui.Modifier transformedHeight(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress,java.lang.Integer> heightProvider);
+  }
+
+  public sealed interface LazyColumnItemScrollProgress {
+    method public float getBottomOffsetFraction();
+    method public float getTopOffsetFraction();
+    property public abstract float bottomOffsetFraction;
+    property public abstract float topOffsetFraction;
+  }
+
+  public final class LazyColumnKt {
+    method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.lazy.LazyColumnState state, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.LazyColumnScope,kotlin.Unit> content);
+  }
+
+  public sealed interface LazyColumnLayoutInfo {
+    method public int getTotalItemsCount();
+    method public java.util.List<androidx.wear.compose.foundation.lazy.LazyColumnVisibleItemInfo> getVisibleItems();
+    property public abstract int totalItemsCount;
+    property public abstract java.util.List<androidx.wear.compose.foundation.lazy.LazyColumnVisibleItemInfo> visibleItems;
+  }
+
+  @androidx.wear.compose.foundation.lazy.LazyColumnScopeMarker public sealed interface LazyColumnScope {
+    method public void item(optional Object? key, optional Object? contentType, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.LazyColumnItemScope,kotlin.Unit> content);
+    method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,? extends java.lang.Object?> contentType, kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.lazy.LazyColumnItemScope,? super java.lang.Integer,kotlin.Unit> content);
+  }
+
+  @kotlin.DslMarker public @interface LazyColumnScopeMarker {
+  }
+
+  public final class LazyColumnState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor public LazyColumnState();
+    method public float dispatchRawDelta(float delta);
+    method public androidx.wear.compose.foundation.lazy.LazyColumnLayoutInfo getLayoutInfo();
+    method public boolean isScrollInProgress();
+    method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public boolean isScrollInProgress;
+    property public final androidx.wear.compose.foundation.lazy.LazyColumnLayoutInfo layoutInfo;
+  }
+
+  public final class LazyColumnStateKt {
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.foundation.lazy.LazyColumnState rememberLazyColumnState();
+  }
+
+  public sealed interface LazyColumnVisibleItemInfo {
+    method public int getHeight();
+    method public int getIndex();
+    method public int getOffset();
+    method public androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress getScrollProgress();
+    property public abstract int height;
+    property public abstract int index;
+    property public abstract int offset;
+    property public abstract androidx.wear.compose.foundation.lazy.LazyColumnItemScrollProgress scrollProgress;
+  }
+
   public final class ScalingLazyColumnDefaults {
     method public androidx.wear.compose.foundation.lazy.ScalingParams scalingParams(optional float edgeScale, optional float edgeAlpha, optional float minElementHeight, optional float maxElementHeight, optional float minTransitionArea, optional float maxTransitionArea, optional androidx.compose.animation.core.Easing scaleInterpolator, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.unit.Constraints,java.lang.Integer> viewportVerticalOffsetResolver);
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior snapFlingBehavior(androidx.wear.compose.foundation.lazy.ScalingLazyListState state, optional float snapOffset, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decay);
@@ -441,6 +510,7 @@
   }
 
   @androidx.compose.runtime.Stable public final class ScalingLazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor public ScalingLazyListState();
     ctor public ScalingLazyListState(optional int initialCenterItemIndex, optional int initialCenterItemScrollOffset);
     method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public float dispatchRawDelta(float delta);
diff --git a/wear/compose/compose-foundation/benchmark/build.gradle b/wear/compose/compose-foundation/benchmark/build.gradle
index 7ec4286..4b50741 100644
--- a/wear/compose/compose-foundation/benchmark/build.gradle
+++ b/wear/compose/compose-foundation/benchmark/build.gradle
@@ -32,6 +32,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
diff --git a/wear/compose/compose-foundation/build.gradle b/wear/compose/compose-foundation/build.gradle
index 78025bf..59fa0f8 100644
--- a/wear/compose/compose-foundation/build.gradle
+++ b/wear/compose/compose-foundation/build.gradle
@@ -66,6 +66,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 25
     }
@@ -90,4 +91,5 @@
             "gestures. It builds upon the Jetpack Compose libraries."
     samples(project(":wear:compose:compose-foundation-samples"))
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/wear/compose/compose-foundation/lint-baseline.xml b/wear/compose/compose-foundation/lint-baseline.xml
index 8c488fd..66b5aa3 100644
--- a/wear/compose/compose-foundation/lint-baseline.xml
+++ b/wear/compose/compose-foundation/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha09)" variant="all" version="8.4.0-alpha09">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="ReturnFromAwaitPointerEventScope"
@@ -13,7 +13,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable weights with type List&lt;? extends Float>: replace with FloatList"
-        errorLine1="        val weights = childrenInLayoutOrder.fastMap { node ->"
+        errorLine1="        val weights ="
         errorLine2="        ^">
         <location
             file="src/main/java/androidx/wear/compose/foundation/CurvedColumn.kt"/>
@@ -49,7 +49,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable weights with type List&lt;? extends Float>: replace with FloatList"
-        errorLine1="        val weights = childrenInLayoutOrder.fastMap { node ->"
+        errorLine1="        val weights ="
         errorLine2="        ^">
         <location
             file="src/main/java/androidx/wear/compose/foundation/CurvedRow.kt"/>
@@ -84,7 +84,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="return type Map&lt;RevealValue, Float> of getAnchors$lint_module: replace with IntFloatMap"
+        message="return type Map&lt;RevealValue, Float> of getAnchors$compose_foundation: replace with IntFloatMap"
         errorLine1="    internal val anchors: Map&lt;RevealValue, Float>,"
         errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -112,8 +112,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable previousAnchors with type Map&lt;T, ? extends Float>: replace with ObjectFloatMap"
-        errorLine1="        val previousAnchors = state.anchors"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                val previousAnchors = state.anchors"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt"/>
     </issue>
@@ -121,15 +121,15 @@
     <issue
         id="PrimitiveInCollection"
         message="variable newAnchors with type Map&lt;T, Float>: replace with ObjectFloatMap"
-        errorLine1="        val newAnchors = mutableMapOf&lt;T, Float>()"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                val newAnchors = mutableMapOf&lt;T, Float>()"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt"/>
     </issue>
 
     <issue
         id="PrimitiveInCollection"
-        message="method setAnchors$lint_module has parameter &lt;set-?> with type Map&lt;T, Float>: replace with ObjectFloatMap"
+        message="method setAnchors$compose_foundation has parameter anchors with type Map&lt;T, Float>: replace with ObjectFloatMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;T, Float>())"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -138,7 +138,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="return type Map&lt;T, Float> of getAnchors$lint_module: replace with ObjectFloatMap"
+        message="return type Map&lt;T, Float> of getAnchors$compose_foundation: replace with ObjectFloatMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;T, Float>())"
         errorLine2="                 ~~~~~~~">
         <location
@@ -147,7 +147,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method updateAnchors$lint_module has parameter newAnchors with type Map&lt;T, Float>: replace with ObjectFloatMap"
+        message="method updateAnchors$compose_foundation has parameter newAnchors with type Map&lt;T, Float>: replace with ObjectFloatMap"
         errorLine1="    internal fun updateAnchors(newAnchors: Map&lt;T, Float>): Boolean {"
         errorLine2="                                           ~~~~~~~~~~~~~">
         <location
@@ -184,7 +184,7 @@
     <issue
         id="PrimitiveInCollection"
         message="method closestAnchor has parameter $this$closestAnchor with type Map&lt;T, Float>: replace with ObjectFloatMap"
-        errorLine1="private fun &lt;T> Map&lt;T, Float>.closestAnchor("
+        errorLine1="private fun &lt;T> Map&lt;T, Float>.closestAnchor(offset: Float = 0f, searchUpwards: Boolean = false): T {"
         errorLine2="                ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt"/>
diff --git a/wear/compose/compose-foundation/samples/build.gradle b/wear/compose/compose-foundation/samples/build.gradle
index 7b0d038..00ced23 100644
--- a/wear/compose/compose-foundation/samples/build.gradle
+++ b/wear/compose/compose-foundation/samples/build.gradle
@@ -49,6 +49,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
diff --git a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/HierarchicalFocusCoordinatorSample.kt b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/HierarchicalFocusCoordinatorSample.kt
index f45866b..3794760 100644
--- a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/HierarchicalFocusCoordinatorSample.kt
+++ b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/HierarchicalFocusCoordinatorSample.kt
@@ -40,11 +40,9 @@
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
 import androidx.wear.compose.foundation.HierarchicalFocusCoordinator
 import androidx.wear.compose.foundation.rememberActiveFocusRequester
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Sampled
 @Composable
 fun HierarchicalFocusCoordinatorSample() {
diff --git a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/LazyColumnSample.kt b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/LazyColumnSample.kt
new file mode 100644
index 0000000..636f814
--- /dev/null
+++ b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/LazyColumnSample.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.LazyColumn
+import androidx.wear.compose.material.Text
+import kotlin.math.max
+import kotlin.math.roundToInt
+
+@Preview
+@Sampled
+@Composable
+fun LazyColumnLettersSample() {
+    val alphabet = ('A'..'Z').map { it.toString() }
+
+    fun rainbowColor(progress: Float): Color {
+        val hue = progress * 360f
+        val saturation = 1f
+        val value = 1f
+
+        return Color(android.graphics.Color.HSVToColor(floatArrayOf(hue, saturation, value)))
+    }
+
+    LazyColumn {
+        items(count = alphabet.size) { index ->
+            Text(
+                alphabet[index],
+                modifier =
+                    Modifier.transformedHeight { originalHeight, scrollProgression ->
+                            if (scrollProgression.topOffsetFraction < 0f)
+                                (originalHeight * scrollProgression.bottomOffsetFraction /
+                                        (scrollProgression.bottomOffsetFraction -
+                                            scrollProgression.topOffsetFraction))
+                                    .roundToInt()
+                            else originalHeight
+                        }
+                        .graphicsLayer {
+                            val itemProgression = scrollProgress ?: return@graphicsLayer
+                            rotationY =
+                                -180f +
+                                    (itemProgression.topOffsetFraction +
+                                        itemProgression.bottomOffsetFraction) * 180f
+                            val scale =
+                                (itemProgression.bottomOffsetFraction -
+                                    max(itemProgression.topOffsetFraction, 0f)) /
+                                    (itemProgression.bottomOffsetFraction -
+                                        itemProgression.topOffsetFraction)
+                            scaleY = scale
+                            translationY = size.height * (scale - 1f) / 2f
+                        }
+                        .drawBehind {
+                            val colorProgress =
+                                scrollProgress?.let {
+                                    (it.topOffsetFraction + it.bottomOffsetFraction) / 2f
+                                } ?: 0f
+                            drawCircle(rainbowColor(colorProgress))
+                        }
+                        .padding(20.dp)
+            )
+        }
+    }
+}
+
+@Preview
+@Composable
+fun LazyColumnRectangularBoxesSample() {
+    LazyColumn {
+        items(count = 10) {
+            Text(
+                "Item $it",
+                modifier =
+                    Modifier.background(Color.Gray).padding(10.dp).transformedHeight {
+                        originalHeight,
+                        _ ->
+                        originalHeight / 2
+                    }
+            )
+        }
+    }
+}
diff --git a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/RotarySamples.kt b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/RotarySamples.kt
index ce18792..c7c90ec 100644
--- a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/RotarySamples.kt
+++ b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/RotarySamples.kt
@@ -31,13 +31,11 @@
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastSumBy
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
 import androidx.wear.compose.foundation.rememberActiveFocusRequester
 import androidx.wear.compose.foundation.rotary.RotaryScrollableDefaults
 import androidx.wear.compose.foundation.rotary.RotarySnapLayoutInfoProvider
 import androidx.wear.compose.foundation.rotary.rotaryScrollable
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Sampled
 @Composable
 fun RotaryScrollSample() {
@@ -63,7 +61,6 @@
     }
 }
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Sampled
 @Composable
 fun RotarySnapSample() {
diff --git a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/HierarchicalFocusCoordinatorTest.kt b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/HierarchicalFocusCoordinatorTest.kt
index d9285a1..2db454e 100644
--- a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/HierarchicalFocusCoordinatorTest.kt
+++ b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/HierarchicalFocusCoordinatorTest.kt
@@ -49,7 +49,7 @@
         rule.setContent {
             repeat(numItems) { ix ->
                 HierarchicalFocusCoordinator({ ix == selected }) {
-                    OnFocusChange(onFocusChanged = { focused[ix] = it })
+                    ActiveFocusListener(onFocusChanged = { focused[ix] = it })
                 }
             }
         }
diff --git a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/lazy/LazyColumnTest.kt b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/lazy/LazyColumnTest.kt
new file mode 100644
index 0000000..761fc3d
--- /dev/null
+++ b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/lazy/LazyColumnTest.kt
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertPixels
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.layout.SubcomposeLayoutState
+import androidx.compose.ui.layout.SubcomposeSlotReusePolicy
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlin.test.Test
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class LazyColumnTest {
+    private val firstItemTag = "firstItemTag"
+    private val lastItemTag = "lastItemTag"
+    private val lazyListTag = "LazyListTag"
+
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun firstItemIsDisplayed() {
+        rule.setContent {
+            LazyColumn {
+                items(100) {
+                    Box(
+                        Modifier.requiredSize(50.dp)
+                            .testTag(
+                                if (it == 0) firstItemTag
+                                else if (it == 99) lastItemTag else "empty"
+                            )
+                    )
+                }
+            }
+        }
+        rule.onNodeWithTag(firstItemTag).assertExists()
+        // LazyColumn is really lazy and doesn't put on the screen until it's scrolled to.
+        rule.onNodeWithTag(lastItemTag).assertIsNotPlaced()
+    }
+
+    @Test
+    fun compositionsAreDisposed_whenDataIsChanged() {
+        var composed = 0
+        var disposals = 0
+        val data1 = (1..3).toList()
+        val data2 = (4..5).toList() // smaller, to ensure removal is handled properly
+
+        var part2 by mutableStateOf(false)
+
+        rule.setContentWithTestViewConfiguration {
+            androidx.compose.foundation.lazy.LazyColumn(
+                Modifier.testTag(lazyListTag).fillMaxSize()
+            ) {
+                items(if (!part2) data1 else data2) {
+                    DisposableEffect(NeverEqualObject) {
+                        composed++
+                        onDispose { disposals++ }
+                    }
+
+                    Spacer(Modifier.height(50.dp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("Not all items were composed").that(composed).isEqualTo(data1.size)
+            composed = 0
+
+            part2 = true
+        }
+
+        rule.runOnIdle {
+            assertWithMessage(
+                    "No additional items were composed after data change, something didn't work"
+                )
+                .that(composed)
+                .isEqualTo(data2.size)
+
+            // We may need to modify this test once we prefetch/cache items outside the viewport
+            assertWithMessage(
+                    "Not enough compositions were disposed after scrolling, compositions were leaked"
+                )
+                .that(disposals)
+                .isEqualTo(data1.size)
+        }
+    }
+
+    @Test
+    fun compositionsAreDisposed_whenLazyListIsDisposed() {
+        var emitLazyList by mutableStateOf(true)
+        var disposeCalledOnFirstItem = false
+        var disposeCalledOnSecondItem = false
+
+        rule.setContentWithTestViewConfiguration {
+            if (emitLazyList) {
+                LazyColumn(Modifier.fillMaxSize()) {
+                    items(2) {
+                        Box(Modifier.requiredSize(100.dp))
+                        DisposableEffect(Unit) {
+                            onDispose {
+                                if (it == 1) {
+                                    disposeCalledOnFirstItem = true
+                                } else {
+                                    disposeCalledOnSecondItem = true
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("First item was incorrectly immediately disposed")
+                .that(disposeCalledOnFirstItem)
+                .isFalse()
+            assertWithMessage("Second item was incorrectly immediately disposed")
+                .that(disposeCalledOnFirstItem)
+                .isFalse()
+            emitLazyList = false
+        }
+
+        rule.runOnIdle {
+            assertWithMessage("First item was not correctly disposed")
+                .that(disposeCalledOnFirstItem)
+                .isTrue()
+            assertWithMessage("Second item was not correctly disposed")
+                .that(disposeCalledOnSecondItem)
+                .isTrue()
+        }
+    }
+
+    @Test
+    fun removeItemsTest() {
+        var itemCount by mutableStateOf(3)
+        val tag = "List"
+        rule.setContentWithTestViewConfiguration {
+            LazyColumn(Modifier.testTag(tag)) {
+                items((0 until itemCount).toList()) { BasicText("$it") }
+            }
+        }
+
+        while (itemCount >= 0) {
+            // Confirm the children's content
+            for (i in 0 until 3) {
+                rule.onNodeWithText("$i").apply {
+                    if (i < itemCount) {
+                        assertIsPlaced()
+                    } else {
+                        assertIsNotPlaced()
+                    }
+                }
+            }
+            itemCount--
+        }
+    }
+
+    @Test
+    fun changeData() {
+        val dataLists = listOf((1..3).toList(), (4..8).toList(), (3..4).toList())
+        var dataModel by mutableStateOf(dataLists[0])
+        val tag = "List"
+        rule.setContentWithTestViewConfiguration {
+            LazyColumn(Modifier.testTag(tag)) {
+                items(dataModel.size) { BasicText("${dataModel[it]}") }
+            }
+        }
+
+        for (data in dataLists) {
+            rule.runOnIdle { dataModel = data }
+
+            // Confirm the children's content
+            for (index in 1..8) {
+                if (index in data) {
+                    rule.onNodeWithText("$index").assertIsDisplayed()
+                } else {
+                    rule.onNodeWithText("$index").assertIsNotPlaced()
+                }
+            }
+        }
+    }
+
+    @Test
+    fun removalWithMutableStateListOf() {
+        val items = mutableStateListOf("1", "2", "3")
+
+        val itemSize = with(rule.density) { 15.toDp() }
+
+        rule.setContentWithTestViewConfiguration {
+            LazyColumn { items(items.size) { Spacer(Modifier.size(itemSize).testTag(items[it])) } }
+        }
+
+        rule.runOnIdle { items.removeAt(items.lastIndex) }
+
+        rule.onNodeWithTag("1").assertIsDisplayed()
+
+        rule.onNodeWithTag("2").assertIsDisplayed()
+
+        rule.onNodeWithTag("3").assertIsNotPlaced()
+    }
+
+    @Test
+    fun recompositionOrder() {
+        val outerState = mutableStateOf(0)
+        val innerState = mutableStateOf(0)
+        val recompositions = mutableListOf<Pair<Int, Int>>()
+
+        rule.setContent {
+            val localOuterState = outerState.value
+            LazyColumn {
+                items(count = 1) {
+                    recompositions.add(localOuterState to innerState.value)
+                    Box(Modifier.fillMaxSize())
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            innerState.value++
+            outerState.value++
+        }
+
+        rule.runOnIdle { assertThat(recompositions).isEqualTo(listOf(0 to 0, 1 to 1)) }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun scrolledAwayItemIsNotDisplayedAnymore() {
+        lateinit var state: LazyColumnState
+        rule.setContentWithTestViewConfiguration {
+            state = rememberLazyColumnState()
+            LazyColumn(
+                Modifier.requiredSize(10.dp)
+                    .testTag(lazyListTag)
+                    .graphicsLayer()
+                    .background(Color.Blue),
+                state = state
+            ) {
+                items(2) {
+                    val size = if (it == 0) 5.dp else 100.dp
+                    val color = if (it == 0) Color.Red else Color.Transparent
+                    Box(Modifier.fillMaxWidth().height(size).background(color).testTag("$it"))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            with(rule.density) {
+                runBlocking {
+                    // we scroll enough to make the Red item not visible anymore
+                    state.scrollBy(6.dp.toPx())
+                }
+            }
+        }
+
+        // and verify there is no Red item displayed
+        rule.onNodeWithTag(lazyListTag).captureToImage().assertPixels { Color.Blue }
+    }
+
+    @Test
+    fun scrollingDeactivatedListIsNotCrashing() {
+        val itemSize = 10f
+        val itemSizeDp = with(rule.density) { itemSize.toDp() }
+
+        val subcomposeState = SubcomposeLayoutState(SubcomposeSlotReusePolicy(1))
+        val state = LazyColumnState()
+        var compose by mutableStateOf(true)
+        rule.setContent {
+            SubcomposeLayout(state = subcomposeState) { constraints ->
+                val node =
+                    if (compose) {
+                        subcompose(Unit) {
+                                LazyColumn(Modifier.size(itemSizeDp), state) {
+                                    items(100) { Box(Modifier.size(itemSizeDp)) }
+                                }
+                            }
+                            .first()
+                            .measure(constraints)
+                    } else {
+                        null
+                    }
+                layout(10, 10) { node?.place(0, 0) }
+            }
+        }
+        rule.runOnIdle { compose = false }
+        rule.runOnIdle { runBlocking { state.scrollBy(itemSize) } }
+    }
+}
+
+/**
+ * Asserts that the current semantics node is not placed.
+ *
+ * Throws [AssertionError] if the node is placed.
+ */
+internal fun SemanticsNodeInteraction.assertIsNotPlaced() {
+    // TODO(b/187188981): We don't have a non-throwing API to check whether an item exists.
+    //  So until this bug is fixed, we are going to catch the assertion error and then check
+    //  whether the node is placed or not.
+    try {
+        // If the node does not exist, it implies that it is also not placed.
+        assertDoesNotExist()
+    } catch (e: AssertionError) {
+        // If the node exists, we need to assert that it is not placed.
+        val errorMessageOnFail = "Assert failed: The component is placed!"
+        if (fetchSemanticsNode().layoutInfo.isPlaced) {
+            throw AssertionError(errorMessageOnFail)
+        }
+    }
+}
+
+/**
+ * Asserts that the current semantics node is placed.
+ *
+ * Throws [AssertionError] if the node is not placed.
+ */
+internal fun SemanticsNodeInteraction.assertIsPlaced(): SemanticsNodeInteraction {
+    val errorMessageOnFail = "Assert failed: The component is not placed!"
+    if (!fetchSemanticsNode(errorMessageOnFail).layoutInfo.isPlaced) {
+        throw AssertionError(errorMessageOnFail)
+    }
+    return this
+}
+
+internal val NeverEqualObject =
+    object {
+        override fun equals(other: Any?): Boolean {
+            return false
+        }
+    }
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/HierarchicalFocusCoordinator.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/HierarchicalFocusCoordinator.kt
index 208918f..be29065 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/HierarchicalFocusCoordinator.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/HierarchicalFocusCoordinator.kt
@@ -35,24 +35,25 @@
 /**
  * Coordinates focus for any composables in [content] and determines which composable will get
  * focus. [HierarchicalFocusCoordinator]s can be nested, and form a tree, with an implicit root.
- * Focus-requiring components (i.e. components using [OnFocusChange] or [RequestFocusWhenActive])
- * should only be in the leaf [HierarchicalFocusCoordinator]s, and there should be at most one per
- * [HierarchicalFocusCoordinator]. For [HierarchicalFocusCoordinator] elements sharing a parent (or
- * at the top level, sharing the implicit root parent), only one should have focus enabled. The
- * selected [HierarchicalFocusCoordinator] is the one that has focus enabled for itself and all
- * ancestors, it will pass focus to its focus-requiring component if it has one, or call
- * FocusManager#clearFocus() otherwise. If no [HierarchicalFocusCoordinator] is selected, there will
- * be no change on the focus state.
+ * Focus-requiring components (i.e. components using [ActiveFocusListener] or
+ * [ActiveFocusRequester]) should only be in the leaf [HierarchicalFocusCoordinator]s, and there
+ * should be at most one per [HierarchicalFocusCoordinator]. For [HierarchicalFocusCoordinator]
+ * elements sharing a parent (or at the top level, sharing the implicit root parent), only one
+ * should have focus enabled. The selected [HierarchicalFocusCoordinator] is the one that has focus
+ * enabled for itself and all ancestors, it will pass focus to its focus-requiring component if it
+ * has one, or call FocusManager#clearFocus() otherwise. If no [HierarchicalFocusCoordinator] is
+ * selected, there will be no change on the focus state.
  *
  * Example usage:
  *
  * @sample androidx.wear.compose.foundation.samples.HierarchicalFocusCoordinatorSample
- * @param requiresFocus a function that returns true when the [content] is active in the composition
- *   and requires the focus
+ * @param requiresFocus a function should return true when the [content] subtree of the composition
+ *   is active and may requires the focus (and false when it's not). For example, a pager can
+ *   enclose each page's content with a call to [HierarchicalFocusCoordinator], marking only the
+ *   current page as requiring focus.
  * @param content The content of this component.
  */
 @Composable
-@ExperimentalWearFoundationApi
 public fun HierarchicalFocusCoordinator(
     requiresFocus: () -> Boolean,
     content: @Composable () -> Unit
@@ -73,11 +74,20 @@
  *   new state (if true, we are becoming active and should request focus).
  */
 @Composable
-@ExperimentalWearFoundationApi
-public fun OnFocusChange(onFocusChanged: CoroutineScope.(Boolean) -> Unit) {
+public fun ActiveFocusListener(onFocusChanged: CoroutineScope.(Boolean) -> Unit) {
     FocusComposableImpl(focusEnabled = { true }, onFocusChanged = onFocusChanged, content = {})
 }
 
+@Deprecated(
+    "Renamed ActiveFocusListener, use that instead",
+    level = DeprecationLevel.HIDDEN,
+    replaceWith = ReplaceWith("ActiveFocusListener(onFocusChanged)")
+)
+@Composable
+@ExperimentalWearFoundationApi
+public fun OnFocusChange(onFocusChanged: CoroutineScope.(Boolean) -> Unit) =
+    ActiveFocusListener(onFocusChanged)
+
 /**
  * Use as part of a focus-requiring component to register a callback to automatically request focus
  * when this component is active. Note that this may call requestFocus in the provided
@@ -87,11 +97,20 @@
  * @param focusRequester The associated [FocusRequester] to request focus on.
  */
 @Composable
-@ExperimentalWearFoundationApi
-public fun RequestFocusWhenActive(focusRequester: FocusRequester) {
-    OnFocusChange { if (it) focusRequester.requestFocus() }
+public fun ActiveFocusRequester(focusRequester: FocusRequester) {
+    ActiveFocusListener { if (it) focusRequester.requestFocus() }
 }
 
+@Deprecated(
+    "Renamed ActiveFocusRequester, use that instead",
+    level = DeprecationLevel.HIDDEN,
+    replaceWith = ReplaceWith("ActiveFocusRequester(focusRequester)")
+)
+@Composable
+@ExperimentalWearFoundationApi
+public fun RequestFocusWhenActive(focusRequester: FocusRequester) =
+    ActiveFocusRequester(focusRequester)
+
 /**
  * Creates, remembers and returns a new [FocusRequester], that will have .requestFocus called when
  * the enclosing [HierarchicalFocusCoordinator] becomes active. Note that the location you call this
@@ -100,13 +119,12 @@
  * .focusRequester modifier on a Composable that is part of the composition.
  */
 @Composable
-@ExperimentalWearFoundationApi
 public fun rememberActiveFocusRequester() =
-    remember { FocusRequester() }.also { RequestFocusWhenActive(it) }
+    remember { FocusRequester() }.also { ActiveFocusRequester(it) }
 
 /**
  * Implements a node in the Focus control tree (either a [HierarchicalFocusCoordinator] or
- * [OnFocusChange]). Each [FocusComposableImpl] maps to a [FocusNode] in our internal
+ * [ActiveFocusListener]). Each [FocusComposableImpl] maps to a [FocusNode] in our internal
  * representation, this is used to:
  * 1) Check that our parent is focused (or we have no explicit parent), to see if we can be focused.
  * 2) See if we have children. If not, we are a leaf node and will forward focus status updates to
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt
index 6fb7c9c..6cfbb43 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/ScrollInfoProvider.kt
@@ -19,10 +19,13 @@
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListItemInfo
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.ui.util.fastFirstOrNull
+import androidx.compose.ui.util.fastForEach
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import androidx.wear.compose.foundation.lazy.ScalingLazyListItemInfo
 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
 import androidx.wear.compose.foundation.lazy.startOffset
 
@@ -58,33 +61,45 @@
      * returned offset is Float.NaN.
      */
     val anchorItemOffset: Float
+
+    /**
+     * The amount of space between the last item (which may not be visible) and the bottom edge of
+     * the viewport. This is always greater or equal to 0, if there is no (or negative) room
+     * (including the case in which the last item is not on screen), 0 should be returned.
+     */
+    val lastItemOffset: Float
 }
 
 /**
- * Extension function for creating a [ScrollInfoProvider] from a [ScalingLazyListState], for use
- * with [ScalingLazyColumn] - used to create a ScrollAway modifier directly that can be applied to
- * an object that appears at the top of the screen, to scroll it away vertically when the
+ * Function for creating a [ScrollInfoProvider] from a [ScalingLazyListState], for use with
+ * [ScalingLazyColumn] - used to create a ScrollAway modifier directly that can be applied to an
+ * object that appears at the top of the screen, to scroll it away vertically when the
  * [ScalingLazyColumn] is scrolled upwards.
+ *
+ * @param state
  */
-fun ScalingLazyListState.toScrollAwayInfoProvider(): ScrollInfoProvider =
-    ScalingLazyListStateScrollInfoProvider(this)
+fun ScrollInfoProvider(state: ScalingLazyListState): ScrollInfoProvider =
+    ScalingLazyListStateScrollInfoProvider(state)
 
 /**
- * Extension function for creating a [ScrollInfoProvider] from a [LazyListState], for use with
- * [LazyColumn] - used to create a ScrollAway modifier directly that can be applied to an object
- * that appears at the top of the screen, to scroll it away vertically when the [LazyColumn] is
- * scrolled upwards.
+ * Function for creating a [ScrollInfoProvider] from a [LazyListState], for use with [LazyColumn] -
+ * used to create a ScrollAway modifier directly that can be applied to an object that appears at
+ * the top of the screen, to scroll it away vertically when the [LazyColumn] is scrolled upwards.
  */
-fun LazyListState.toScrollAwayInfoProvider(): ScrollInfoProvider =
-    LazyListStateScrollInfoProvider(this)
+fun ScrollInfoProvider(state: LazyListState): ScrollInfoProvider =
+    LazyListStateScrollInfoProvider(state)
 
 /**
- * Extension function for creating a [ScrollInfoProvider] from a [ScrollState], for use with
- * [Column] - used to create a ScrollAway modifier directly that can be applied to an object that
- * appears at the top of the screen, to scroll it away vertically when the [Column] is scrolled
- * upwards.
+ * Function for creating a [ScrollInfoProvider] from a [ScrollState], for use with [Column] - used
+ * to create a ScrollAway modifier directly that can be applied to an object that appears at the top
+ * of the screen, to scroll it away vertically when the [Column] is scrolled upwards.
+ *
+ * @param state the [ScrollState] to use as the base for creating the [ScrollInfoProvider]
+ * @param bottomButtonHeight optional parameter to specify the size of a bottom button if one is
+ *   provided.
  */
-fun ScrollState.toScrollAwayInfoProvider(): ScrollInfoProvider = ScrollStateScrollInfoProvider(this)
+fun ScrollInfoProvider(state: ScrollState, bottomButtonHeight: Float = 0f): ScrollInfoProvider =
+    ScrollStateScrollInfoProvider(state, bottomButtonHeight)
 
 // Implementation of [ScrollAwayInfoProvider] for [ScalingLazyColumn].
 // Being in Foundation, this implementation has access to the ScalingLazyListState
@@ -118,10 +133,25 @@
                 } ?: Float.NaN
         }
 
+    override val lastItemOffset: Float
+        get() {
+            val screenHeightPx = state.config.value?.viewportHeightPx ?: 0
+            var lastItemInfo: ScalingLazyListItemInfo? = null
+            state.layoutInfo.visibleItemsInfo.fastForEach { ii ->
+                if (ii.index == state.layoutInfo.totalItemsCount - 1) lastItemInfo = ii
+            }
+            return lastItemInfo?.let {
+                val bottomEdge = it.offset + screenHeightPx / 2 + it.size / 2
+                (screenHeightPx - bottomEdge).toFloat().coerceAtLeast(0f)
+            } ?: 0f
+        }
+
     override fun toString(): String {
-        return "ScalingLazyColumnScrollAwayInfoProvider(isScrollAwayValid=$isScrollAwayValid, " +
+        return "ScalingLazyListStateScrollInfoProvider(isScrollAwayValid=$isScrollAwayValid, " +
+            "isScrollable=$isScrollable," +
             "isScrollInProgress=$isScrollInProgress, " +
-            "trackedItemOffset=$anchorItemOffset)"
+            "anchorItemOffset=$anchorItemOffset, " +
+            "lastItemOffset=$lastItemOffset)"
     }
 
     private var initialStartOffset: Float? = null
@@ -144,15 +174,33 @@
                 .fastFirstOrNull { it.index == 0 }
                 ?.let { -it.offset.toFloat() } ?: Float.NaN
 
+    override val lastItemOffset: Float
+        get() {
+            val screenHeightPx = state.layoutInfo.viewportSize.height
+            var lastItemInfo: LazyListItemInfo? = null
+            state.layoutInfo.visibleItemsInfo.fastForEach { ii ->
+                if (ii.index == state.layoutInfo.totalItemsCount - 1) lastItemInfo = ii
+            }
+            return lastItemInfo?.let {
+                val bottomEdge = it.offset + it.size - state.layoutInfo.viewportStartOffset
+                (screenHeightPx - bottomEdge).toFloat().coerceAtLeast(0f)
+            } ?: 0f
+        }
+
     override fun toString(): String {
-        return "LazyColumnScrollAwayInfoProvider(isScrollAwayValid=$isScrollAwayValid, " +
+        return "LazyListStateScrollInfoProvider(isScrollAwayValid=$isScrollAwayValid, " +
+            "isScrollable=$isScrollable," +
             "isScrollInProgress=$isScrollInProgress, " +
-            "trackedItemOffset=$anchorItemOffset)"
+            "anchorItemOffset=$anchorItemOffset, " +
+            "lastItemOffset=$lastItemOffset)"
     }
 }
 
 // Implementation of [ScrollAwayInfoProvider] for [Column]
-private class ScrollStateScrollInfoProvider(val state: ScrollState) : ScrollInfoProvider {
+private class ScrollStateScrollInfoProvider(
+    val state: ScrollState,
+    val bottomButtonHeight: Float = 0f
+) : ScrollInfoProvider {
     override val isScrollAwayValid: Boolean
         get() = true
 
@@ -170,8 +218,17 @@
     override val anchorItemOffset: Float
         get() = state.value.toFloat()
 
-    override fun toString(): String =
-        "DefaultScrollAwayInfoProvider(isScrollAwayValid=$isScrollAwayValid, " +
+    override val lastItemOffset: Float
+        get() {
+            return if (state.maxValue == Int.MAX_VALUE || bottomButtonHeight == 0f) 0f
+            else (state.value - state.maxValue + bottomButtonHeight).coerceAtLeast(0f)
+        }
+
+    override fun toString(): String {
+        return "ScrollStateScrollInfoProvider(isScrollAwayValid=$isScrollAwayValid, " +
+            "isScrollable=$isScrollable," +
             "isScrollInProgress=$isScrollInProgress, " +
-            "trackedItemOffset=$anchorItemOffset)"
+            "anchorItemOffset=$anchorItemOffset, " +
+            "lastItemOffset=$lastItemOffset)"
+    }
 }
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumn.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumn.kt
new file mode 100644
index 0000000..4d44569
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumn.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.lazy.layout.LazyLayout
+import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.referentialEqualityPolicy
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.dp
+
+/**
+ * The vertically scrolling list that only composes and lays out the currently visible items. This
+ * is a wear specific version of LazyColumn that adds support for scaling and morphing animations.
+ *
+ * @sample androidx.wear.compose.foundation.samples.LazyColumnLettersSample
+ * @param modifier The modifier to be applied to the layout.
+ * @param state The state object to be used to control the list and the applied layout.
+ * @param verticalArrangement The vertical arrangement of the items.
+ * @param horizontalAlignment The horizontal alignment of the items.
+ * @param flingBehavior The fling behavior to be used for the list.
+ * @param userScrollEnabled Whether the user should be able to scroll the list.
+ * @param content The content of the list.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun LazyColumn(
+    modifier: Modifier = Modifier,
+    state: LazyColumnState = rememberLazyColumnState(),
+    verticalArrangement: Arrangement.Vertical =
+        Arrangement.spacedBy(
+            space = 4.dp,
+            // TODO: b/352513793 - Add support for reverseLayout.
+            alignment = Alignment.Top
+        ),
+    horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+    flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
+    userScrollEnabled: Boolean = true,
+    content: LazyColumnScope.() -> Unit
+) {
+    val latestContent = rememberUpdatedState(newValue = content)
+
+    val itemProviderLambda by
+        remember(state) {
+            derivedStateOf(referentialEqualityPolicy()) {
+                {
+                    LazyColumnItemProvider(
+                        scope = LazyColumnScopeImpl(latestContent.value),
+                        state = state
+                    )
+                }
+            }
+        }
+
+    val measurePolicy =
+        rememberLazyColumnMeasurePolicy(
+            itemProviderLambda = itemProviderLambda,
+            state = state,
+            horizontalAlignment = horizontalAlignment,
+            verticalArrangement = verticalArrangement,
+        )
+    val reverseDirection =
+        ScrollableDefaults.reverseDirection(
+            LocalLayoutDirection.current,
+            Orientation.Vertical,
+            reverseScrolling = false
+        )
+    LazyLayout(
+        itemProvider = itemProviderLambda,
+        modifier =
+            modifier
+                .then(state.remeasurementModifier)
+                .scrollable(
+                    state = state,
+                    reverseDirection = reverseDirection,
+                    enabled = userScrollEnabled,
+                    orientation = Orientation.Vertical,
+                    flingBehavior = flingBehavior,
+                ),
+        measurePolicy = measurePolicy
+    )
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class LazyColumnItemProvider(
+    val scope: LazyColumnScopeImpl,
+    val state: LazyColumnState,
+) : LazyLayoutItemProvider {
+    override val itemCount: Int
+        get() = scope.itemCount
+
+    @Composable
+    override fun Item(index: Int, key: Any) {
+        // TODO: b/352511749 - Use keys to identify items.
+        val itemScope = remember(index) { LazyColumnItemScopeImpl(index, state = state) }
+        scope.withInterval(index) { localIndex, content -> content.item(itemScope, localIndex) }
+    }
+}
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnDsl.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnDsl.kt
new file mode 100644
index 0000000..4a77593
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnDsl.kt
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.lazy.LazyItemScope
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent
+import androidx.compose.foundation.lazy.layout.MutableIntervalList
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.GraphicsLayerScope
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.layout.ParentDataModifier
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.util.fastFirstOrNull
+
+/** Receiver scope being used by the item content parameter of [LazyColumn]. */
+@LazyColumnScopeMarker
+sealed interface LazyColumnItemScope {
+    /**
+     * Scroll progress of the item before height transformation is applied using
+     * [Modifier.transformedHeight]. Is null for the item that is off screen.
+     */
+    val DrawScope.scrollProgress: LazyColumnItemScrollProgress?
+
+    /**
+     * Scroll progress of the item before height transformation is applied using
+     * [Modifier.transformedHeight]. Is null for the item that is off screen.
+     */
+    val GraphicsLayerScope.scrollProgress: LazyColumnItemScrollProgress?
+
+    /**
+     * Applies the new height of the item depending on its scroll progress and original height.
+     *
+     * @param heightProvider The transformation to be applied. The first parameter is the original
+     *   height of the item. The second parameter is the scroll progress of the item.
+     */
+    fun Modifier.transformedHeight(
+        heightProvider: (originalHeight: Int, scrollProgress: LazyColumnItemScrollProgress) -> Int
+    ): Modifier
+}
+
+/** Receiver scope which is used by [LazyColumn]. */
+@LazyColumnScopeMarker
+sealed interface LazyColumnScope {
+    /**
+     * Adds [count] items.
+     *
+     * @param count The number of items to add to the [LazyColumn].
+     * @param key A factory of stable and unique keys representing the item. Using the same key for
+     *   multiple items in the [LazyColumn] is not allowed.
+     * @param contentType A factory of the content types for the item. The item compositions of the
+     *   same type could be reused more efficiently. Note that null is a valid type and items of
+     *   such type will be considered compatible.
+     * @param content The content displayed by a single item.
+     */
+    fun items(
+        count: Int,
+        key: ((index: Int) -> Any)? = null,
+        contentType: (index: Int) -> Any? = { null },
+        content: @Composable LazyColumnItemScope.(index: Int) -> Unit
+    )
+
+    /**
+     * Adds a single item.
+     *
+     * @param key A stable and unique key representing the item. Using the same key for multiple
+     *   items in the [LazyColumn] is not allowed. Type of the key should be saveable via Bundle on
+     *   Android. If null is passed the position in the [LazyColumn] will represent the key. When
+     *   you specify the key the scroll position will be maintained based on the key, which means if
+     *   you add/remove items before the current visible item the item with the given key will be
+     *   kept as the first visible one.
+     * @param contentType The type of the content of this item. The item compositions of the same
+     *   type could be reused more efficiently. Note that null is a valid type and items of such
+     *   type will be considered compatible.
+     * @param content The content of the item.
+     */
+    fun item(
+        key: Any? = null,
+        contentType: Any? = null,
+        content: @Composable LazyColumnItemScope.() -> Unit
+    )
+}
+
+/**
+ * Adds a list of items.
+ *
+ * @param items the data list
+ * @param key a factory of stable and unique keys representing the item. Using the same key for
+ *   multiple items in the [LazyColumn] is not allowed. Type of the key should be saveable via
+ *   Bundle on Android. If null is passed the position in the [LazyColumn] will represent the key.
+ *   When you specify the key the scroll position will be maintained based on the key, which means
+ *   if you add/remove items before the current visible item the item with the given key will be
+ *   kept as the first visible one.
+ * @param contentType a factory of the content types for the item. The item compositions of the same
+ *   type could be reused more efficiently. Note that null is a valid type and items of such type
+ *   will be considered compatible.
+ * @param itemContent the content displayed by a single item.
+ */
+inline fun <T> LazyColumnScope.items(
+    items: List<T>,
+    noinline key: ((item: T) -> Any)? = null,
+    noinline contentType: (item: T) -> Any? = { null },
+    crossinline itemContent: @Composable LazyColumnItemScope.(item: T) -> Unit
+) =
+    items(
+        count = items.size,
+        key = if (key != null) { index: Int -> key(items[index]) } else null,
+        contentType = { index: Int -> contentType(items[index]) }
+    ) {
+        itemContent(items[it])
+    }
+
+/**
+ * Adds a list of items where the content of an item is aware of its index.
+ *
+ * @param items the data list
+ * @param key a factory of stable and unique keys representing the item. Using the same key for
+ *   multiple items in the [LazyColumn] is not allowed. Type of the key should be saveable via
+ *   Bundle on Android. If null is passed the position in the list will represent the key. When you
+ *   specify the key the scroll position will be maintained based on the key, which means if you
+ *   add/remove items before the current visible item the item with the given key will be kept as
+ *   the first visible one.
+ * @param contentType a factory of the content types for the item. The item compositions of the same
+ *   type could be reused more efficiently. Note that null is a valid type and items of such type
+ *   will be considered compatible.
+ * @param itemContent the content displayed by a single item
+ */
+inline fun <T> LazyListScope.itemsIndexed(
+    items: List<T>,
+    noinline key: ((index: Int, item: T) -> Any)? = null,
+    crossinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null },
+    crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit
+) =
+    items(
+        count = items.size,
+        key = if (key != null) { index: Int -> key(index, items[index]) } else null,
+        contentType = { index -> contentType(index, items[index]) }
+    ) {
+        itemContent(it, items[it])
+    }
+
+internal class LazyColumnItemScopeImpl(val index: Int, val state: LazyColumnState) :
+    LazyColumnItemScope {
+    private val _scrollProgress: LazyColumnItemScrollProgress?
+        get() = state.layoutInfo.visibleItems.fastFirstOrNull { it.index == index }?.scrollProgress
+
+    override val DrawScope.scrollProgress: LazyColumnItemScrollProgress?
+        get() = _scrollProgress
+
+    override val GraphicsLayerScope.scrollProgress: LazyColumnItemScrollProgress?
+        get() = _scrollProgress
+
+    override fun Modifier.transformedHeight(
+        heightProvider: (Int, LazyColumnItemScrollProgress) -> Int
+    ): Modifier =
+        this then
+            object : ParentDataModifier {
+                override fun Density.modifyParentData(parentData: Any?): Any =
+                    HeightProviderParentData(heightProvider)
+            }
+}
+
+internal data class HeightProviderParentData(
+    val heightProvider: (Int, LazyColumnItemScrollProgress) -> Int
+)
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class LazyColumnScopeImpl(val content: LazyColumnScope.() -> Unit) :
+    LazyLayoutIntervalContent<LazyColumnInterval>(), LazyColumnScope {
+    override val intervals: MutableIntervalList<LazyColumnInterval> = MutableIntervalList()
+
+    init {
+        apply(content)
+    }
+
+    override fun items(
+        count: Int,
+        key: ((index: Int) -> Any)?,
+        contentType: (index: Int) -> Any?,
+        content: @Composable LazyColumnItemScope.(Int) -> Unit
+    ) {
+        intervals.addInterval(
+            count,
+            LazyColumnInterval(
+                key,
+                type = contentType,
+                item = content,
+            )
+        )
+    }
+
+    override fun item(
+        key: Any?,
+        contentType: Any?,
+        content: @Composable LazyColumnItemScope.() -> Unit
+    ) {
+        intervals.addInterval(
+            1,
+            LazyColumnInterval(
+                key = if (key != null) { _: Int -> key } else null,
+                type = { contentType },
+                item = { content() },
+            )
+        )
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class LazyColumnInterval(
+    override val key: ((index: Int) -> Any)?,
+    override val type: ((index: Int) -> Any?),
+    val item: @Composable LazyColumnItemScope.(index: Int) -> Unit,
+) : LazyLayoutIntervalContent.Interval
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnLayoutInfo.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnLayoutInfo.kt
new file mode 100644
index 0000000..c5002a0
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnLayoutInfo.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+/**
+ * Scroll progress of an item in a [LazyColumn] before any modifications to the item's height are
+ * applied (using [LazyColumnItemScope.transformedHeight] modifier).
+ */
+sealed interface LazyColumnItemScrollProgress {
+    /**
+     * The offset as a fraction of the top of the item relative to the list container. Is within
+     * (0, 1) when item is inside the screen and could be negative if the top of the item is off the
+     * screen. Value is calculated from the top of the container. This value is calculated before
+     * any height modifications are applied (using [LazyColumnItemScope.transformedHeight]
+     * modifier).
+     */
+    val topOffsetFraction: Float
+
+    /**
+     * The offset as a fraction of the bottom of the item relative to the list container. Is within
+     * (0, 1) when item is inside the screen and could exceed 1 when the bottom of item is off the
+     * screen. Value is calculated from the top of the container. This value is calculated before
+     * any height modifications are applied (using [LazyColumnItemScope.transformedHeight]
+     * modifier).
+     */
+    val bottomOffsetFraction: Float
+}
+
+/** Represents an item that is visible in the [LazyColumn] component. */
+sealed interface LazyColumnVisibleItemInfo {
+    /** The index of the item in the underlying data source. */
+    val index: Int
+    /** The offset of the item from the start of the visible area. */
+    val offset: Int
+    /** The height of the item after applying any height changes. */
+    val height: Int
+    /** The scroll progress of the item, indicating its position within the visible area. */
+    val scrollProgress: LazyColumnItemScrollProgress
+}
+
+/** Holds the layout information for a [LazyColumn]. */
+sealed interface LazyColumnLayoutInfo {
+    /** A list of [LazyColumnVisibleItemInfo] objects representing the visible items in the list. */
+    val visibleItems: List<LazyColumnVisibleItemInfo>
+
+    /** The total count of items passed to [LazyColumn]. */
+    val totalItemsCount: Int
+
+    // TODO: b/352686661 - Expose more properties related to layout.
+}
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasureResult.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasureResult.kt
new file mode 100644
index 0000000..d895dbc
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasureResult.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import androidx.compose.ui.layout.MeasureResult
+
+/** The result of the measure pass of the [LazyColumn]. */
+internal class LazyColumnMeasureResult(
+    /** MeasureResult defining the layout. */
+    measureResult: MeasureResult,
+    /** The index of the item that should be considered as an anchor during scrolling. */
+    val anchorItemIndex: Int,
+    /** The offset of the anchor item from the top of screen. */
+    val anchorItemScrollOffset: Int,
+    /** Layout information for the visible items. */
+    val visibleItems: List<LazyColumnVisibleItemInfo>,
+    /** see [LazyColumnLayoutInfo.totalItemsCount] */
+    val totalItemsCount: Int,
+) : MeasureResult by measureResult
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasuredItem.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasuredItem.kt
new file mode 100644
index 0000000..fae8d47
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasuredItem.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.LayoutDirection
+
+/** Represents a placeable item in the [LazyColumn] layout. */
+internal data class LazyColumnMeasuredItem(
+    /** The index of the item in the list. */
+    val index: Int,
+    /** The [Placeable] representing the content of the item. */
+    val placeable: Placeable,
+    /** The constraints of the container holding the item. */
+    val containerConstraints: Constraints,
+    /** The vertical offset of the item from the top of the list after transformations applied. */
+    var offset: Int,
+    /** Scroll progress of the item used to calculate transformations applied. */
+    val scrollProgress: LazyColumnItemScrollProgress,
+    /** The horizontal alignment to apply during placement. */
+    val horizontalAlignment: Alignment.Horizontal,
+    /** The [LayoutDirection] of the `Layout`. */
+    private val layoutDirection: LayoutDirection,
+) {
+    /** The height of the item after transformations applied. */
+    val height =
+        (placeable.parentData as? HeightProviderParentData)?.let {
+            it.heightProvider(placeable.height, scrollProgress)
+        } ?: placeable.height
+
+    fun place(scope: Placeable.PlacementScope) =
+        with(scope) {
+            placeable.placeWithLayer(
+                horizontalAlignment.align(
+                    space = containerConstraints.maxWidth,
+                    size = placeable.width,
+                    layoutDirection = layoutDirection
+                ),
+                offset
+            )
+        }
+}
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasurement.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasurement.kt
new file mode 100644
index 0000000..154d414
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnMeasurement.kt
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMap
+import kotlin.math.roundToInt
+
+private interface MeasuredItemProvider {
+    /**
+     * Creates a [LazyColumnMeasuredItem] with the given index and offset with the position
+     * calculated from top to bottom.
+     */
+    fun downwardMeasuredItem(index: Int, offset: Int): LazyColumnMeasuredItem
+
+    /**
+     * Creates a [LazyColumnMeasuredItem] with the given index and offset with the position
+     * calculated bottom up.
+     */
+    fun upwardMeasuredItem(index: Int, offset: Int): LazyColumnMeasuredItem
+}
+
+/**
+ * Measures the visible items for a [LazyColumn].
+ *
+ * @param itemsCount The total number of items in the list.
+ * @param measuredItemProvider A provider that returns the measured items.
+ * @param itemSpacing The spacing between items.
+ * @param containerConstraints The constraints for the list.
+ * @param anchorItemIndex The index of the anchor item. Anchor item is a visible item used to
+ *   position the rest of the items before and after it.
+ * @param anchorItemScrollOffset The scroll offset of the anchor item. Anchor item is a visible item
+ *   used to position the rest of the items before and after it.
+ * @param scrollToBeConsumed The amount of scroll to be consumed.
+ * @param layout A function that lays out the items.
+ */
+// TODO(artemiy): Add support for overscroll and scroll margins.
+private fun measureLazyColumn(
+    itemsCount: Int,
+    measuredItemProvider: MeasuredItemProvider,
+    itemSpacing: Int,
+    containerConstraints: Constraints,
+    anchorItemIndex: Int,
+    anchorItemScrollOffset: Int,
+    scrollToBeConsumed: Float,
+    layout: (Int, Int, Placeable.PlacementScope.() -> Unit) -> MeasureResult
+): LazyColumnMeasureResult {
+    if (itemsCount == 0) {
+        return LazyColumnMeasureResult(
+            anchorItemIndex = 0,
+            anchorItemScrollOffset = 0,
+            visibleItems = emptyList(),
+            totalItemsCount = 0,
+            measureResult = layout(containerConstraints.maxWidth, containerConstraints.maxHeight) {}
+        )
+    }
+
+    val visibleItems = ArrayDeque<LazyColumnMeasuredItem>()
+
+    // Place center item
+    val centerItem =
+        measuredItemProvider.upwardMeasuredItem(
+            anchorItemIndex,
+            anchorItemScrollOffset + containerConstraints.maxHeight / 2
+        )
+    centerItem.offset += scrollToBeConsumed.roundToInt()
+    visibleItems.add(centerItem)
+
+    var bottomOffset = centerItem.offset + centerItem.height + itemSpacing
+    var bottomPassIndex = anchorItemIndex + 1
+
+    while (bottomOffset < containerConstraints.maxHeight && bottomPassIndex < itemsCount) {
+        val item = measuredItemProvider.downwardMeasuredItem(bottomPassIndex, bottomOffset)
+        bottomOffset += item.height + itemSpacing
+        visibleItems.add(item)
+        bottomPassIndex += 1
+    }
+
+    var topOffset = centerItem.offset - itemSpacing
+    var topPassIndex = anchorItemIndex - 1
+
+    while (topOffset >= 0 && topPassIndex >= 0) {
+        val additionalItem = measuredItemProvider.upwardMeasuredItem(topPassIndex, topOffset)
+        visibleItems.addFirst(additionalItem)
+        topOffset -= additionalItem.height + itemSpacing
+        topPassIndex -= 1
+    }
+
+    if (visibleItems.isEmpty()) {
+        return LazyColumnMeasureResult(
+            anchorItemIndex = 0,
+            anchorItemScrollOffset = 0,
+            visibleItems = emptyList(),
+            totalItemsCount = 0,
+            measureResult = layout(containerConstraints.maxWidth, containerConstraints.maxHeight) {}
+        )
+    }
+
+    val anchorItem =
+        visibleItems.lastOrNull { it.offset + it.height <= containerConstraints.maxHeight / 2 }
+            ?: visibleItems[visibleItems.size / 2]
+
+    return LazyColumnMeasureResult(
+        anchorItemIndex = anchorItem.index,
+        anchorItemScrollOffset =
+            anchorItem.let { it.offset + it.height - containerConstraints.maxHeight / 2 },
+        visibleItems =
+            visibleItems.fastMap {
+                LazyColumnVisibleItemInfoImpl(
+                    index = it.index,
+                    offset = it.offset,
+                    height = it.height,
+                    scrollProgress = it.scrollProgress,
+                )
+            },
+        totalItemsCount = itemsCount,
+        measureResult =
+            layout(containerConstraints.maxWidth, containerConstraints.maxHeight) {
+                visibleItems.fastForEach { it.place(this) }
+            }
+    )
+}
+
+private class LazyColumnVisibleItemInfoImpl(
+    override val index: Int,
+    override val offset: Int,
+    override val height: Int,
+    override val scrollProgress: LazyColumnItemScrollProgress
+) : LazyColumnVisibleItemInfo {}
+
+private fun bottomItemScrollProgress(
+    offset: Int,
+    height: Int,
+    containerHeight: Int
+): LazyColumnItemScrollProgress =
+    LazyColumnItemScrollProgressImpl(
+        topOffsetFraction = offset.toFloat() / containerHeight.toFloat(),
+        bottomOffsetFraction = (offset + height).toFloat() / containerHeight.toFloat(),
+    )
+
+private fun topItemScrollProgress(
+    offset: Int,
+    height: Int,
+    containerHeight: Int
+): LazyColumnItemScrollProgress =
+    LazyColumnItemScrollProgressImpl(
+        topOffsetFraction = (offset - height).toFloat() / containerHeight.toFloat(),
+        bottomOffsetFraction = offset / containerHeight.toFloat(),
+    )
+
+internal data class LazyColumnItemScrollProgressImpl(
+    override val topOffsetFraction: Float,
+    override val bottomOffsetFraction: Float
+) : LazyColumnItemScrollProgress
+
+@Composable
+@OptIn(ExperimentalFoundationApi::class)
+internal fun rememberLazyColumnMeasurePolicy(
+    itemProviderLambda: () -> LazyColumnItemProvider,
+    state: LazyColumnState,
+    horizontalAlignment: Alignment.Horizontal,
+    verticalArrangement: Arrangement.Vertical,
+): LazyLayoutMeasureScope.(Constraints) -> MeasureResult =
+    remember(itemProviderLambda, state, horizontalAlignment, verticalArrangement) {
+        { containerConstraints ->
+            val measuredItemProvider =
+                object : MeasuredItemProvider {
+                    override fun downwardMeasuredItem(
+                        index: Int,
+                        offset: Int
+                    ): LazyColumnMeasuredItem {
+                        val placeables = measure(index, containerConstraints)
+                        // TODO(artemiy): Add support for multiple items.
+                        val content = placeables.last()
+                        val scrollProgress =
+                            bottomItemScrollProgress(
+                                offset = offset,
+                                height = content.height,
+                                containerHeight = containerConstraints.maxHeight
+                            )
+                        return LazyColumnMeasuredItem(
+                            index = index,
+                            placeable = content,
+                            offset = offset,
+                            containerConstraints = containerConstraints,
+                            scrollProgress = scrollProgress,
+                            horizontalAlignment = horizontalAlignment,
+                            layoutDirection = layoutDirection,
+                        )
+                    }
+
+                    override fun upwardMeasuredItem(
+                        index: Int,
+                        offset: Int
+                    ): LazyColumnMeasuredItem {
+                        val placeables = measure(index, containerConstraints)
+                        // TODO(artemiy): Add support for multiple items.
+                        val content = placeables.last()
+                        val scrollProgress =
+                            topItemScrollProgress(
+                                offset = offset,
+                                height = content.height,
+                                containerHeight = containerConstraints.maxHeight
+                            )
+                        val item =
+                            LazyColumnMeasuredItem(
+                                index = index,
+                                placeable = content,
+                                offset = offset,
+                                containerConstraints = containerConstraints,
+                                scrollProgress = scrollProgress,
+                                horizontalAlignment = horizontalAlignment,
+                                layoutDirection = layoutDirection,
+                            )
+                        item.offset -= item.height
+                        return item
+                    }
+                }
+
+            Snapshot.withMutableSnapshot {
+                    measureLazyColumn(
+                        itemsCount = itemProviderLambda().itemCount,
+                        measuredItemProvider = measuredItemProvider,
+                        itemSpacing = verticalArrangement.spacing.roundToPx(),
+                        containerConstraints = containerConstraints,
+                        scrollToBeConsumed = state.scrollToBeConsumed,
+                        anchorItemIndex = state.anchorItemIndex,
+                        anchorItemScrollOffset = state.anchorItemScrollOffset,
+                        layout = { width, height, placement ->
+                            layout(
+                                containerConstraints.constrainWidth(width),
+                                containerConstraints.constrainHeight(height),
+                                emptyMap(),
+                                placement
+                            )
+                        }
+                    )
+                }
+                .also { state.applyMeasureResult(it) }
+        }
+    }
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnScopeMarker.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnScopeMarker.kt
new file mode 100644
index 0000000..cdef389
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnScopeMarker.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+/** DSL marker used to distinguish between lazy layout scope and the item scope. */
+@DslMarker annotation class LazyColumnScopeMarker
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnState.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnState.kt
new file mode 100644
index 0000000..b81078e
--- /dev/null
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/LazyColumnState.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.lazy
+
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.layout.Remeasurement
+import androidx.compose.ui.layout.RemeasurementModifier
+import kotlin.math.abs
+
+/** Creates a [LazyColumnState] that is remembered across compositions. */
+@Composable fun rememberLazyColumnState() = remember { LazyColumnState() }
+
+/**
+ * A state object that can be hoisted to control and observe scrolling.
+ *
+ * In most cases, this will be created via [rememberLazyColumnState].
+ */
+class LazyColumnState : ScrollableState {
+    override val isScrollInProgress: Boolean
+        get() = scrollableState.isScrollInProgress
+
+    override fun dispatchRawDelta(delta: Float): Float = scrollableState.dispatchRawDelta(delta)
+
+    override suspend fun scroll(
+        scrollPriority: MutatePriority,
+        block: suspend ScrollScope.() -> Unit
+    ) = scrollableState.scroll(scrollPriority, block)
+
+    var layoutInfo: LazyColumnLayoutInfo by mutableStateOf(LazyColumnLayoutInfoImpl(emptyList(), 0))
+        private set
+
+    private data class LazyColumnLayoutInfoImpl(
+        override val visibleItems: List<LazyColumnVisibleItemInfo>,
+        override val totalItemsCount: Int,
+    ) : LazyColumnLayoutInfo
+
+    internal var scrollToBeConsumed = 0f
+        private set
+
+    internal var anchorItemIndex by mutableIntStateOf(0)
+        private set
+
+    internal var anchorItemScrollOffset by mutableIntStateOf(0)
+        private set
+
+    internal var remeasurement: Remeasurement? = null
+        private set
+
+    /** The modifier which provides [remeasurement]. */
+    internal val remeasurementModifier =
+        object : RemeasurementModifier {
+            override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
+                [email protected] = remeasurement
+            }
+        }
+
+    internal fun applyMeasureResult(measureResult: LazyColumnMeasureResult) {
+        // TODO(artemiy): Don't consume all scroll.
+        scrollToBeConsumed = 0f
+        anchorItemIndex = measureResult.anchorItemIndex
+        anchorItemScrollOffset = measureResult.anchorItemScrollOffset
+        layoutInfo =
+            LazyColumnLayoutInfoImpl(
+                visibleItems = measureResult.visibleItems,
+                totalItemsCount = measureResult.totalItemsCount
+            )
+    }
+
+    private val scrollableState = ScrollableState { -onScroll(-it) }
+
+    private fun onScroll(distance: Float): Float {
+        if (distance < 0 && !canScrollForward || distance > 0 && !canScrollBackward) {
+            return 0f
+        }
+        scrollToBeConsumed += distance
+        if (abs(scrollToBeConsumed) > 0.5f) {
+            remeasurement?.forceRemeasure()
+        }
+
+        // here scrollToBeConsumed is already consumed during the forceRemeasure invocation
+        if (abs(scrollToBeConsumed) <= 0.5f) {
+            // We consumed all of it - we'll hold onto the fractional scroll for later, so report
+            // that we consumed the whole thing
+            return distance
+        } else {
+            val scrollConsumed = distance - scrollToBeConsumed
+
+            // We did not consume all of it - return the rest to be consumed elsewhere (e.g.,
+            // nested scrolling)
+            scrollToBeConsumed = 0f // We're not consuming the rest, give it back
+            return scrollConsumed
+        }
+    }
+}
diff --git a/wear/compose/compose-material-core/build.gradle b/wear/compose/compose-material-core/build.gradle
index d1fc58f..efc7042 100644
--- a/wear/compose/compose-material-core/build.gradle
+++ b/wear/compose/compose-material-core/build.gradle
@@ -60,6 +60,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
@@ -80,7 +82,6 @@
     description = "WearOS Compose Material Core Library. This library contains themeless " +
             "components that are shared between different WearOS Compose Material libraries. It " +
             "builds upon the Jetpack Compose libraries."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index ba93c46..2168018 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -2,6 +2,7 @@
 package androidx.wear.compose.material {
 
   @Deprecated @androidx.compose.runtime.Immutable public final class AutoCenteringParams {
+    ctor @Deprecated public AutoCenteringParams();
     ctor @Deprecated public AutoCenteringParams(optional int itemIndex, optional int itemOffset);
   }
 
@@ -137,6 +138,7 @@
   }
 
   @androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class Colors {
+    ctor public Colors();
     ctor public Colors(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onSurfaceVariant, optional long onError);
     method public androidx.wear.compose.material.Colors copy(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onSurfaceVariant, optional long onError);
     method public long getBackground();
@@ -313,6 +315,7 @@
   }
 
   public final class PickerGroupState {
+    ctor public PickerGroupState();
     ctor public PickerGroupState(optional int initiallySelectedIndex);
     method public int getSelectedIndex();
     method public void setSelectedIndex(int);
@@ -573,6 +576,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class ScalingLazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @Deprecated public ScalingLazyListState();
     ctor @Deprecated public ScalingLazyListState(optional int initialCenterItemIndex, optional int initialCenterItemScrollOffset);
     method @Deprecated public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public float dispatchRawDelta(float delta);
@@ -653,6 +657,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.wear.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
@@ -727,6 +732,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class SwipeToDismissBoxState {
+    ctor @Deprecated public SwipeToDismissBoxState();
     ctor @Deprecated public SwipeToDismissBoxState(optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.material.SwipeToDismissValue,java.lang.Boolean> confirmStateChange);
     method @Deprecated public androidx.wear.compose.material.SwipeToDismissValue getCurrentValue();
     method @Deprecated public androidx.wear.compose.material.SwipeToDismissValue getTargetValue();
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index ba93c46..2168018 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -2,6 +2,7 @@
 package androidx.wear.compose.material {
 
   @Deprecated @androidx.compose.runtime.Immutable public final class AutoCenteringParams {
+    ctor @Deprecated public AutoCenteringParams();
     ctor @Deprecated public AutoCenteringParams(optional int itemIndex, optional int itemOffset);
   }
 
@@ -137,6 +138,7 @@
   }
 
   @androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class Colors {
+    ctor public Colors();
     ctor public Colors(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onSurfaceVariant, optional long onError);
     method public androidx.wear.compose.material.Colors copy(optional long primary, optional long primaryVariant, optional long secondary, optional long secondaryVariant, optional long background, optional long surface, optional long error, optional long onPrimary, optional long onSecondary, optional long onBackground, optional long onSurface, optional long onSurfaceVariant, optional long onError);
     method public long getBackground();
@@ -313,6 +315,7 @@
   }
 
   public final class PickerGroupState {
+    ctor public PickerGroupState();
     ctor public PickerGroupState(optional int initiallySelectedIndex);
     method public int getSelectedIndex();
     method public void setSelectedIndex(int);
@@ -573,6 +576,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class ScalingLazyListState implements androidx.compose.foundation.gestures.ScrollableState {
+    ctor @Deprecated public ScalingLazyListState();
     ctor @Deprecated public ScalingLazyListState(optional int initialCenterItemIndex, optional int initialCenterItemScrollOffset);
     method @Deprecated public suspend Object? animateScrollToItem(int index, optional int scrollOffset, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public float dispatchRawDelta(float delta);
@@ -653,6 +657,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.wear.compose.material.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large);
     method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
@@ -727,6 +732,7 @@
   }
 
   @Deprecated @androidx.compose.runtime.Stable public final class SwipeToDismissBoxState {
+    ctor @Deprecated public SwipeToDismissBoxState();
     ctor @Deprecated public SwipeToDismissBoxState(optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.material.SwipeToDismissValue,java.lang.Boolean> confirmStateChange);
     method @Deprecated public androidx.wear.compose.material.SwipeToDismissValue getCurrentValue();
     method @Deprecated public androidx.wear.compose.material.SwipeToDismissValue getTargetValue();
diff --git a/wear/compose/compose-material/benchmark/build.gradle b/wear/compose/compose-material/benchmark/build.gradle
index c3a7f6d..7cd3eb5 100644
--- a/wear/compose/compose-material/benchmark/build.gradle
+++ b/wear/compose/compose-material/benchmark/build.gradle
@@ -32,6 +32,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
diff --git a/wear/compose/compose-material/build.gradle b/wear/compose/compose-material/build.gradle
index a24813a..927c577 100644
--- a/wear/compose/compose-material/build.gradle
+++ b/wear/compose/compose-material/build.gradle
@@ -62,6 +62,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
@@ -87,5 +89,6 @@
             "Material Design UX guidelines and specifications. It builds upon the Jetpack Compose" +
             " libraries."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":wear:compose:compose-material-samples"))
 }
diff --git a/wear/compose/compose-material/lint-baseline.xml b/wear/compose/compose-material/lint-baseline.xml
index 39f39fb..71725ca 100644
--- a/wear/compose/compose-material/lint-baseline.xml
+++ b/wear/compose/compose-material/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="PrimitiveInCollection"
@@ -57,7 +57,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method setAnchors$lint_module has parameter &lt;set-?> with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        message="method setAnchors$compose_material has parameter anchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;Float, T>())"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -66,7 +66,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="return type Map&lt;Float, T> of getAnchors$lint_module: replace with FloatObjectMap"
+        message="return type Map&lt;Float, T> of getAnchors$compose_material: replace with FloatObjectMap"
         errorLine1="    internal var anchors by mutableStateOf(emptyMap&lt;Float, T>())"
         errorLine2="                 ~~~~~~~">
         <location
@@ -75,7 +75,7 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method ensureInit$lint_module has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        message="method ensureInit$compose_material has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
         errorLine1="    internal fun ensureInit(newAnchors: Map&lt;Float, T>) {"
         errorLine2="                                        ~~~~~~~~~~~~~">
         <location
@@ -84,18 +84,18 @@
 
     <issue
         id="PrimitiveInCollection"
-        message="method processNewAnchors$lint_module has parameter oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        oldAnchors: Map&lt;Float, T>,"
-        errorLine2="                    ~~~~~~~~~~~~~">
+        message="method processNewAnchors$compose_material has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        errorLine1="    internal suspend fun processNewAnchors(oldAnchors: Map&lt;Float, T>, newAnchors: Map&lt;Float, T>) {"
+        errorLine2="                                                                                  ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/material/Swipeable.kt"/>
     </issue>
 
     <issue
         id="PrimitiveInCollection"
-        message="method processNewAnchors$lint_module has parameter newAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        newAnchors: Map&lt;Float, T>"
-        errorLine2="                    ~~~~~~~~~~~~~">
+        message="method processNewAnchors$compose_material has parameter oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
+        errorLine1="    internal suspend fun processNewAnchors(oldAnchors: Map&lt;Float, T>, newAnchors: Map&lt;Float, T>) {"
+        errorLine2="                                                       ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/material/Swipeable.kt"/>
     </issue>
@@ -121,8 +121,8 @@
     <issue
         id="PrimitiveInCollection"
         message="variable oldAnchors with type Map&lt;Float, ? extends T>: replace with FloatObjectMap"
-        errorLine1="        val oldAnchors = state.anchors"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="            val oldAnchors = state.anchors"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/material/Swipeable.kt"/>
     </issue>
@@ -130,8 +130,8 @@
     <issue
         id="PrimitiveInCollection"
         message="method findBounds has parameter anchors with type Set&lt;Float>: replace with FloatSet"
-        errorLine1="    anchors: Set&lt;Float>"
-        errorLine2="             ~~~~~~~~~~">
+        errorLine1="private fun findBounds(offset: Float, anchors: Set&lt;Float>): List&lt;Float> {"
+        errorLine2="                                               ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/material/Swipeable.kt"/>
     </issue>
@@ -139,8 +139,8 @@
     <issue
         id="PrimitiveInCollection"
         message="return type List&lt;Float> of findBounds: replace with FloatList"
-        errorLine1="): List&lt;Float> {"
-        errorLine2="   ~~~~~~~~~~~">
+        errorLine1="private fun findBounds(offset: Float, anchors: Set&lt;Float>): List&lt;Float> {"
+        errorLine2="                                                            ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/compose/material/Swipeable.kt"/>
     </issue>
diff --git a/wear/compose/compose-material/samples/build.gradle b/wear/compose/compose-material/samples/build.gradle
index 7f31242..429e80c 100644
--- a/wear/compose/compose-material/samples/build.gradle
+++ b/wear/compose/compose-material/samples/build.gradle
@@ -58,6 +58,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/CircularProgressIndicatorSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/CircularProgressIndicatorSample.kt
index e164114..39473dc 100644
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/CircularProgressIndicatorSample.kt
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/CircularProgressIndicatorSample.kt
@@ -31,6 +31,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.material.CircularProgressIndicator
 import androidx.wear.compose.material.CompactChip
@@ -70,7 +71,7 @@
 @Composable
 public fun CircularProgressIndicatorFullscreenWithGap() {
     CircularProgressIndicator(
-        modifier = Modifier.fillMaxSize().padding(all = 1.dp),
+        modifier = Modifier.fillMaxSize().padding(all = 1.dp).clearAndSetSemantics {},
         startAngle = 295.5f,
         endAngle = 245.5f,
         progress = 0.3f,
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PickerGroup.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PickerGroup.kt
index 9c499c7..9523113 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PickerGroup.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PickerGroup.kt
@@ -42,7 +42,6 @@
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMap
 import androidx.compose.ui.util.fastMaxOfOrNull
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
 import androidx.wear.compose.foundation.HierarchicalFocusCoordinator
 import androidx.wear.compose.foundation.rememberActiveFocusRequester
 import kotlin.math.roundToInt
@@ -83,7 +82,6 @@
  *   The integer parameter to the composable depicts the index where it will be kept. For example, 0
  *   would represent the separator between the first and second picker.
  */
-@OptIn(ExperimentalWearFoundationApi::class)
 @Composable
 public fun PickerGroup(
     vararg pickers: PickerGroupItem,
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt
index 6d8bab1..7cd0f9f 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt
@@ -63,7 +63,8 @@
  *
  * [CircularProgressIndicator] supports a gap in the circular track between [endAngle] and
  * [startAngle], which leaves room for other content, such as [TimeText] at the top of the screen.
- * Example:
+ * This sample also shows how to disable accessibility semantics for the fullscreen
+ * [CircularProgressIndicator]:
  *
  * @sample androidx.wear.compose.material.samples.CircularProgressIndicatorFullscreenWithGap
  * @param modifier Modifier to be applied to the CircularProgressIndicator
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index e847dac..79ef6fc 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -208,6 +208,7 @@
   }
 
   @androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class ColorScheme {
+    ctor public ColorScheme();
     ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
     method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
     method public long getBackground();
@@ -312,6 +313,7 @@
   }
 
   public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape animatedShape(androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.foundation.shape.CornerBasedShape shape, optional androidx.compose.foundation.shape.CornerBasedShape pressedShape);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledIconButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledTonalIconButtonColors();
@@ -321,7 +323,8 @@
     method public float getExtraSmallButtonSize();
     method public float getLargeButtonSize();
     method public float getLargeIconSize();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.CornerBasedShape getPressedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.RoundedCornerShape getShape();
     method public float getSmallButtonSize();
     method public float getSmallIconSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors();
@@ -338,7 +341,8 @@
     property public final float LargeIconSize;
     property public final float SmallButtonSize;
     property public final float SmallIconSize;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.CornerBasedShape pressedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.RoundedCornerShape shape;
     field public static final androidx.wear.compose.material3.IconButtonDefaults INSTANCE;
   }
 
@@ -456,6 +460,7 @@
   }
 
   public final class PickerGroupState {
+    ctor public PickerGroupState();
     ctor public PickerGroupState(optional int initiallySelectedIndex);
     method public int getSelectedIndex();
     method public void setSelectedIndex(int);
@@ -630,10 +635,12 @@
   }
 
   public final class ScreenScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.lazy.LazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.lazy.LazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> scrollIndicator, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? bottomButton, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, float bottomButtonHeight, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void ScreenScaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional androidx.wear.compose.foundation.ScrollInfoProvider? scrollInfoProvider, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.wear.compose.foundation.lazy.ScalingLazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.wear.compose.foundation.lazy.ScalingLazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? bottomButton, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, androidx.wear.compose.foundation.ScrollInfoProvider scrollInfoProvider, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
   public enum ScreenStage {
@@ -658,6 +665,11 @@
     method @androidx.compose.runtime.Composable public static void ScrollIndicator(androidx.wear.compose.foundation.lazy.ScalingLazyListState state, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> positionAnimationSpec);
   }
 
+  public final class SegmentedCircularProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void SegmentedCircularProgressIndicator(@IntRange(from=1L) int segmentCount, kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional androidx.wear.compose.material3.ProgressIndicatorColors colors, optional float strokeWidth, optional float gapSize);
+    method @androidx.compose.runtime.Composable public static void SegmentedCircularProgressIndicator(@IntRange(from=1L) int segmentCount, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> completed, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional androidx.wear.compose.material3.ProgressIndicatorColors colors, optional float strokeWidth, optional float gapSize);
+  }
+
   public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraLarge();
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraSmall();
@@ -673,6 +685,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.wear.compose.material3.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
@@ -958,6 +971,7 @@
   }
 
   public final class TextButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape animatedShape(androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.foundation.shape.CornerBasedShape shape, optional androidx.compose.foundation.shape.CornerBasedShape pressedShape);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors filledTextButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors filledTextButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors filledTonalTextButtonColors();
@@ -966,7 +980,8 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.text.TextStyle getDefaultButtonTextStyle();
     method public float getLargeButtonSize();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.text.TextStyle getLargeButtonTextStyle();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.CornerBasedShape getPressedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.RoundedCornerShape getShape();
     method public float getSmallButtonSize();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.text.TextStyle getSmallButtonTextStyle();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors();
@@ -980,7 +995,8 @@
     property public final float SmallButtonSize;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.text.TextStyle defaultButtonTextStyle;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.text.TextStyle largeButtonTextStyle;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.CornerBasedShape pressedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.RoundedCornerShape shape;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.text.TextStyle smallButtonTextStyle;
     field public static final androidx.wear.compose.material3.TextButtonDefaults INSTANCE;
   }
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index e847dac..79ef6fc 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -208,6 +208,7 @@
   }
 
   @androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class ColorScheme {
+    ctor public ColorScheme();
     ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
     method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceContainerLow, optional long surfaceContainer, optional long surfaceContainerHigh, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer);
     method public long getBackground();
@@ -312,6 +313,7 @@
   }
 
   public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape animatedShape(androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.foundation.shape.CornerBasedShape shape, optional androidx.compose.foundation.shape.CornerBasedShape pressedShape);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledIconButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors filledTonalIconButtonColors();
@@ -321,7 +323,8 @@
     method public float getExtraSmallButtonSize();
     method public float getLargeButtonSize();
     method public float getLargeIconSize();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.CornerBasedShape getPressedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.RoundedCornerShape getShape();
     method public float getSmallButtonSize();
     method public float getSmallIconSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors();
@@ -338,7 +341,8 @@
     property public final float LargeIconSize;
     property public final float SmallButtonSize;
     property public final float SmallIconSize;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.CornerBasedShape pressedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.RoundedCornerShape shape;
     field public static final androidx.wear.compose.material3.IconButtonDefaults INSTANCE;
   }
 
@@ -456,6 +460,7 @@
   }
 
   public final class PickerGroupState {
+    ctor public PickerGroupState();
     ctor public PickerGroupState(optional int initiallySelectedIndex);
     method public int getSelectedIndex();
     method public void setSelectedIndex(int);
@@ -630,10 +635,12 @@
   }
 
   public final class ScreenScaffoldKt {
-    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.lazy.LazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.lazy.LazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> scrollIndicator, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? bottomButton, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, float bottomButtonHeight, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void ScreenScaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional androidx.wear.compose.foundation.ScrollInfoProvider? scrollInfoProvider, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.wear.compose.foundation.lazy.ScalingLazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(androidx.wear.compose.foundation.lazy.ScalingLazyListState scrollState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? bottomButton, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ScreenScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, androidx.wear.compose.foundation.ScrollInfoProvider scrollInfoProvider, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? timeText, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? scrollIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
   public enum ScreenStage {
@@ -658,6 +665,11 @@
     method @androidx.compose.runtime.Composable public static void ScrollIndicator(androidx.wear.compose.foundation.lazy.ScalingLazyListState state, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> positionAnimationSpec);
   }
 
+  public final class SegmentedCircularProgressIndicatorKt {
+    method @androidx.compose.runtime.Composable public static void SegmentedCircularProgressIndicator(@IntRange(from=1L) int segmentCount, kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional androidx.wear.compose.material3.ProgressIndicatorColors colors, optional float strokeWidth, optional float gapSize);
+    method @androidx.compose.runtime.Composable public static void SegmentedCircularProgressIndicator(@IntRange(from=1L) int segmentCount, kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> completed, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional androidx.wear.compose.material3.ProgressIndicatorColors colors, optional float strokeWidth, optional float gapSize);
+  }
+
   public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraLarge();
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraSmall();
@@ -673,6 +685,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes();
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.wear.compose.material3.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
@@ -958,6 +971,7 @@
   }
 
   public final class TextButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape animatedShape(androidx.compose.foundation.interaction.InteractionSource interactionSource, optional androidx.compose.foundation.shape.CornerBasedShape shape, optional androidx.compose.foundation.shape.CornerBasedShape pressedShape);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors filledTextButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors filledTextButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors filledTonalTextButtonColors();
@@ -966,7 +980,8 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.text.TextStyle getDefaultButtonTextStyle();
     method public float getLargeButtonSize();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.text.TextStyle getLargeButtonTextStyle();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.CornerBasedShape getPressedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.shape.RoundedCornerShape getShape();
     method public float getSmallButtonSize();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.text.TextStyle getSmallButtonTextStyle();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors();
@@ -980,7 +995,8 @@
     property public final float SmallButtonSize;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.text.TextStyle defaultButtonTextStyle;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.text.TextStyle largeButtonTextStyle;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.CornerBasedShape pressedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.shape.RoundedCornerShape shape;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.text.TextStyle smallButtonTextStyle;
     field public static final androidx.wear.compose.material3.TextButtonDefaults INSTANCE;
   }
diff --git a/wear/compose/compose-material3/benchmark/build.gradle b/wear/compose/compose-material3/benchmark/build.gradle
index 1ffe12d..092e595 100644
--- a/wear/compose/compose-material3/benchmark/build.gradle
+++ b/wear/compose/compose-material3/benchmark/build.gradle
@@ -32,6 +32,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
diff --git a/wear/compose/compose-material3/build.gradle b/wear/compose/compose-material3/build.gradle
index 5f694ed..fa693ff 100644
--- a/wear/compose/compose-material3/build.gradle
+++ b/wear/compose/compose-material3/build.gradle
@@ -45,6 +45,7 @@
     implementation("androidx.compose.ui:ui-util:1.6.0")
     implementation(project(":wear:compose:compose-material-core"))
     implementation("androidx.profileinstaller:profileinstaller:1.3.1")
+    implementation("androidx.graphics:graphics-shapes:1.0.0-beta01")
 
     androidTestImplementation(project(":compose:ui:ui-test"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
@@ -60,6 +61,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
@@ -86,6 +89,7 @@
             "implement Wear Material 3 Design UX guidelines and specifications. It builds upon " +
             "the Jetpack Compose libraries."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
     samples(project(":wear:compose:compose-material3-samples"))
 }
 
diff --git a/wear/compose/compose-material3/integration-tests/build.gradle b/wear/compose/compose-material3/integration-tests/build.gradle
index 19bacd4..955f6fd 100644
--- a/wear/compose/compose-material3/integration-tests/build.gradle
+++ b/wear/compose/compose-material3/integration-tests/build.gradle
@@ -55,8 +55,9 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 25
     }
     namespace "androidx.wear.compose.material3.demos"
-}
\ No newline at end of file
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AnimatedShapeButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AnimatedShapeButtonDemo.kt
new file mode 100644
index 0000000..740b637
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AnimatedShapeButtonDemo.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.demos
+
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Home
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.FilledIconButton
+import androidx.wear.compose.material3.FilledTonalIconButton
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.IconButton
+import androidx.wear.compose.material3.IconButtonDefaults
+import androidx.wear.compose.material3.ListHeader
+import androidx.wear.compose.material3.OutlinedIconButton
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.TextButton
+import androidx.wear.compose.material3.TextButtonDefaults
+
+@Composable
+fun AnimatedShapeButtonDemo() {
+    ScalingLazyDemo {
+        item { ListHeader { Text("Animated Text Button") } }
+        item {
+            Row {
+                val interactionSource1 = remember { MutableInteractionSource() }
+                TextButton(
+                    onClick = {},
+                    shape = TextButtonDefaults.animatedShape(interactionSource1)
+                ) {
+                    Text(text = "ABC")
+                }
+
+                Spacer(modifier = Modifier.width(5.dp))
+
+                val interactionSource2 = remember { MutableInteractionSource() }
+                TextButton(
+                    onClick = {},
+                    shape =
+                        TextButtonDefaults.animatedShape(
+                            interactionSource2,
+                            shape = CutCornerShape(15.dp),
+                            pressedShape = RoundedCornerShape(15.dp)
+                        )
+                ) {
+                    Text(text = "ABC")
+                }
+            }
+        }
+        item { ListHeader { Text("Animated Icon Button") } }
+        item {
+            Row {
+                val interactionSource1 = remember { MutableInteractionSource() }
+                IconButton(
+                    onClick = {},
+                    shape = IconButtonDefaults.animatedShape(interactionSource1),
+                    interactionSource = interactionSource1,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+
+                Spacer(modifier = Modifier.width(5.dp))
+
+                val interactionSource2 = remember { MutableInteractionSource() }
+                IconButton(
+                    onClick = {},
+                    shape =
+                        IconButtonDefaults.animatedShape(
+                            interactionSource2,
+                            shape = CutCornerShape(15.dp),
+                            pressedShape = RoundedCornerShape(15.dp)
+                        ),
+                    interactionSource = interactionSource2,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+            }
+        }
+        item { ListHeader { Text("Animated Filled Icon Button") } }
+        item {
+            Row {
+                val interactionSource1 = remember { MutableInteractionSource() }
+                FilledIconButton(
+                    onClick = {},
+                    shape = IconButtonDefaults.animatedShape(interactionSource1),
+                    interactionSource = interactionSource1,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+
+                Spacer(modifier = Modifier.width(5.dp))
+
+                val interactionSource2 = remember { MutableInteractionSource() }
+                FilledIconButton(
+                    onClick = {},
+                    shape =
+                        IconButtonDefaults.animatedShape(
+                            interactionSource2,
+                            shape = CutCornerShape(15.dp),
+                            pressedShape = RoundedCornerShape(15.dp)
+                        ),
+                    interactionSource = interactionSource2,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+            }
+        }
+        item { ListHeader { Text("Animated Filled Tonal Icon Button") } }
+        item {
+            Row {
+                val interactionSource1 = remember { MutableInteractionSource() }
+                FilledTonalIconButton(
+                    onClick = {},
+                    shape = IconButtonDefaults.animatedShape(interactionSource1),
+                    interactionSource = interactionSource1,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+
+                Spacer(modifier = Modifier.width(5.dp))
+
+                val interactionSource2 = remember { MutableInteractionSource() }
+                FilledTonalIconButton(
+                    onClick = {},
+                    shape =
+                        IconButtonDefaults.animatedShape(
+                            interactionSource2,
+                            shape = CutCornerShape(15.dp),
+                            pressedShape = RoundedCornerShape(15.dp)
+                        ),
+                    interactionSource = interactionSource2,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+            }
+        }
+        item { ListHeader { Text("Animated Outlined Icon Button") } }
+        item {
+            Row {
+                val interactionSource1 = remember { MutableInteractionSource() }
+                OutlinedIconButton(
+                    onClick = {},
+                    shape = IconButtonDefaults.animatedShape(interactionSource1),
+                    interactionSource = interactionSource1,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+
+                Spacer(modifier = Modifier.width(5.dp))
+
+                val interactionSource2 = remember { MutableInteractionSource() }
+                OutlinedIconButton(
+                    onClick = {},
+                    shape =
+                        IconButtonDefaults.animatedShape(
+                            interactionSource2,
+                            shape = CutCornerShape(15.dp),
+                            pressedShape = RoundedCornerShape(15.dp)
+                        ),
+                    interactionSource = interactionSource2,
+                ) {
+                    Icon(imageVector = Icons.Rounded.Home, contentDescription = null)
+                }
+            }
+        }
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
index 7a0ce5e..da18e5e 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
@@ -19,11 +19,14 @@
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.sizeIn
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.wear.compose.material3.Button
 import androidx.wear.compose.material3.ButtonColors
@@ -320,6 +323,15 @@
 }
 
 @Composable
+fun ButtonBackgroundImageDemo() {
+    ScalingLazyDemo {
+        item { ListHeader { Text("Button (Image Background)") } }
+        item { ButtonBackgroundImage(painterResource(R.drawable.card_background), enabled = true) }
+        item { ButtonBackgroundImage(painterResource(R.drawable.card_background), enabled = false) }
+    }
+}
+
+@Composable
 private fun AvatarButton(enabled: Boolean) =
     MultilineButton(
         enabled = enabled,
@@ -391,3 +403,13 @@
         colors = colors,
     )
 }
+
+@Composable
+private fun ButtonBackgroundImage(painter: Painter, enabled: Boolean) =
+    Button(
+        modifier = Modifier.sizeIn(maxHeight = ButtonDefaults.Height),
+        onClick = { /* Do something */ },
+        label = { Text("Label", maxLines = 1) },
+        enabled = enabled,
+        colors = ButtonDefaults.imageBackgroundButtonColors(painter)
+    )
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/EdgeButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/EdgeButtonDemo.kt
index 6c02d1e..9960e0a 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/EdgeButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/EdgeButtonDemo.kt
@@ -16,79 +16,76 @@
 
 package androidx.wear.compose.material3.demos
 
+import androidx.compose.foundation.gestures.ScrollableDefaults
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastForEach
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
-import androidx.wear.compose.foundation.lazy.ScalingLazyListItemInfo
 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import androidx.wear.compose.foundation.rotary.RotaryScrollableDefaults
+import androidx.wear.compose.foundation.rotary.rotaryScrollable
 import androidx.wear.compose.integration.demos.common.AdaptiveScreen
 import androidx.wear.compose.material3.ButtonDefaults
 import androidx.wear.compose.material3.ButtonDefaults.buttonColors
 import androidx.wear.compose.material3.Card
 import androidx.wear.compose.material3.CompactButton
 import androidx.wear.compose.material3.EdgeButton
+import androidx.wear.compose.material3.ScreenScaffold
 import androidx.wear.compose.material3.Text
 
 @Composable
-fun EdgeButtonBelowListDemo() {
-    // NOTE: This demo recomposes the Edge Button when it's appearing / disappearing.
+fun EdgeButtonBelowLazyColumnDemo() {
+    val labels =
+        listOf(
+            "Hi",
+            "Hello World",
+            "Hello world again?",
+            "More content as we add stuff",
+            "I don't know if this will fit now, testing",
+            "Really long text that it's going to take multiple lines",
+            "And now we are really pushing it because the screen is really small",
+        )
+    val selectedLabel = remember { mutableIntStateOf(0) }
     AdaptiveScreen {
-        val state = rememberScalingLazyListState()
-        val screenHeightPx =
-            with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
-        val heightPx = remember {
-            derivedStateOf {
-                val marginPx = 5f // !?
-                var lastItemInfo: ScalingLazyListItemInfo? = null
-                state.layoutInfo.visibleItemsInfo.fastForEach { ii ->
-                    if (ii.index == state.layoutInfo.totalItemsCount - 1) lastItemInfo = ii
+        val state = rememberLazyListState()
+        ScreenScaffold(
+            scrollState = state,
+            bottomButton = {
+                EdgeButton(
+                    onClick = {},
+                    buttonHeight = ButtonDefaults.EdgeButtonHeightLarge,
+                    colors = buttonColors(containerColor = Color.DarkGray)
+                ) {
+                    Text(labels[selectedLabel.intValue], color = Color.White)
                 }
-                lastItemInfo?.let {
-                    val bottomEdge = it.offset + screenHeightPx / 2 + it.size / 2
-                    (screenHeightPx - bottomEdge - marginPx).coerceAtLeast(0f)
-                } ?: 0f
             }
-        }
-
-        val labels =
-            listOf(
-                "Hi",
-                "Hello World",
-                "Hello world again?",
-                "More content as we add stuff",
-                "I don't know if this will fit now, testing",
-                "Really long text that it's going to take multiple lines",
-                "And now we are really pushing it because the screen is really small",
-            )
-        val selectedLabel = remember { mutableIntStateOf(0) }
-
-        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
-            ScalingLazyColumn(
+        ) {
+            LazyColumn(
                 state = state,
                 modifier = Modifier.fillMaxSize(),
-                autoCentering = null,
-                contentPadding = PaddingValues(10.dp, 20.dp, 10.dp, 100.dp)
+                verticalArrangement = Arrangement.spacedBy(4.dp),
+                contentPadding = PaddingValues(10.dp, 20.dp, 10.dp, 80.dp),
+                horizontalAlignment = Alignment.CenterHorizontally
             ) {
                 items(labels.size) {
                     Card(
@@ -99,30 +96,116 @@
                     }
                 }
             }
-            // We isolate the call to EdgeButton to a function so only that is recomposed when the
-            // height changes.
-            EdgeButtonCall(heightPx) {
-                Text(
-                    labels[selectedLabel.intValue],
-                    color = Color.White,
-                    textAlign = TextAlign.Center,
-                    maxLines = 3,
-                )
-            }
         }
     }
 }
 
 @Composable
-private fun EdgeButtonCall(heightPx: State<Float>, content: @Composable RowScope.() -> Unit) {
-    val heightDp = with(LocalDensity.current) { heightPx.value.toDp() }
-    EdgeButton(
-        onClick = {},
-        buttonHeight = ButtonDefaults.EdgeButtonHeightLarge,
-        colors = buttonColors(containerColor = Color.DarkGray),
-        modifier = Modifier.height(heightDp),
-        content = content
-    )
+fun EdgeButtonBelowScalingLazyColumnDemo() {
+    val labels =
+        listOf(
+            "Hi",
+            "Hello World",
+            "Hello world again?",
+            "More content as we add stuff",
+            "I don't know if this will fit now, testing",
+            "Really long text that it's going to take multiple lines",
+            "And now we are really pushing it because the screen is really small",
+        )
+    val selectedLabel = remember { mutableIntStateOf(0) }
+
+    AdaptiveScreen {
+        val state = rememberScalingLazyListState()
+        ScreenScaffold(
+            scrollState = state,
+            bottomButton = {
+                EdgeButton(
+                    onClick = {},
+                    buttonHeight = ButtonDefaults.EdgeButtonHeightLarge,
+                    colors = buttonColors(containerColor = Color.DarkGray)
+                ) {
+                    Text(labels[selectedLabel.intValue], color = Color.White)
+                }
+            }
+        ) {
+            ScalingLazyColumn(
+                state = state,
+                modifier = Modifier.fillMaxSize(),
+                autoCentering = null,
+                contentPadding = PaddingValues(10.dp, 20.dp, 10.dp, 100.dp),
+                horizontalAlignment = Alignment.CenterHorizontally
+            ) {
+                items(labels.size) {
+                    Card(
+                        onClick = { selectedLabel.intValue = it },
+                        modifier = Modifier.fillMaxWidth(0.9f)
+                    ) {
+                        Text(labels[it])
+                    }
+                }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalWearFoundationApi::class)
+@Composable
+fun EdgeButtonBelowColumnDemo() {
+    val labels =
+        listOf(
+            "Hi",
+            "Hello World",
+            "Hello world again?",
+            "More content as we add stuff",
+            "I don't know if this will fit now, testing",
+            "Really long text that it's going to take multiple lines",
+            "And now we are really pushing it because the screen is really small",
+        )
+    val selectedLabel = remember { mutableIntStateOf(0) }
+    val bottomButtonHeight = ButtonDefaults.EdgeButtonHeightLarge
+
+    AdaptiveScreen {
+        val scrollState = rememberScrollState()
+        val focusRequester = rememberActiveFocusRequester()
+
+        ScreenScaffold(
+            scrollState = scrollState,
+            bottomButton = {
+                EdgeButton(
+                    onClick = {},
+                    buttonHeight = bottomButtonHeight,
+                    colors = buttonColors(containerColor = Color.DarkGray)
+                ) {
+                    Text(labels[selectedLabel.intValue], color = Color.White)
+                }
+            },
+            bottomButtonHeight = bottomButtonHeight
+        ) {
+            Column(
+                modifier =
+                    Modifier.verticalScroll(scrollState)
+                        .rotaryScrollable(
+                            RotaryScrollableDefaults.behavior(
+                                scrollableState = scrollState,
+                                flingBehavior = ScrollableDefaults.flingBehavior()
+                            ),
+                            focusRequester = focusRequester
+                        ),
+                verticalArrangement = Arrangement.spacedBy(4.dp),
+                horizontalAlignment = Alignment.CenterHorizontally
+            ) {
+                repeat(labels.size) {
+                    Card(
+                        onClick = { selectedLabel.intValue = it },
+                        modifier = Modifier.fillMaxWidth(0.9f)
+                    ) {
+                        Text(labels[it])
+                    }
+                }
+                Spacer(Modifier.height(bottomButtonHeight))
+            }
+        }
+    }
 }
 
 @Suppress("PrimitiveInCollection")
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
index 3be24a8..a2306b9 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
@@ -16,10 +16,14 @@
 
 package androidx.wear.compose.material3.demos
 
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
@@ -113,6 +117,39 @@
                 IconButtonWithSize(IconButtonDefaults.ExtraSmallButtonSize)
             }
         }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("Rounded Corner Animation")
+                Spacer(Modifier.width(4.dp))
+                val interactionSource = remember { MutableInteractionSource() }
+                IconButton(
+                    onClick = {},
+                    shape = IconButtonDefaults.animatedShape(interactionSource),
+                    interactionSource = interactionSource
+                ) {
+                    StandardIcon(ButtonDefaults.IconSize)
+                }
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("Morphed Corner Animation")
+                Spacer(Modifier.width(4.dp))
+                val interactionSource = remember { MutableInteractionSource() }
+                IconButton(
+                    onClick = {},
+                    shape =
+                        IconButtonDefaults.animatedShape(
+                            interactionSource,
+                            shape = CutCornerShape(5.dp),
+                            pressedShape = RoundedCornerShape(5.dp)
+                        ),
+                    interactionSource = interactionSource
+                ) {
+                    StandardIcon(ButtonDefaults.IconSize)
+                }
+            }
+        }
     }
 }
 
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
index 807a43e..0179e50 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
@@ -37,6 +37,9 @@
 import androidx.wear.compose.material3.samples.FullScreenProgressIndicatorSample
 import androidx.wear.compose.material3.samples.MediaButtonProgressIndicatorSample
 import androidx.wear.compose.material3.samples.OverflowProgressIndicatorSample
+import androidx.wear.compose.material3.samples.SegmentedProgressIndicatorOnOffSample
+import androidx.wear.compose.material3.samples.SegmentedProgressIndicatorSample
+import androidx.wear.compose.material3.samples.SmallValuesProgressIndicatorSample
 
 val ProgressIndicatorDemos =
     listOf(
@@ -64,5 +67,12 @@
                     )
                 }
             }
-        }
+        },
+        ComposableDemo("Small progress values") {
+            Centralize { SmallValuesProgressIndicatorSample() }
+        },
+        ComposableDemo("Segmented progress") { Centralize { SegmentedProgressIndicatorSample() } },
+        ComposableDemo("Progress segments on/off") {
+            Centralize { SegmentedProgressIndicatorOnOffSample() }
+        },
     )
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt
index 7dc240b..ce9aa66 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt
@@ -30,11 +30,10 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.ScrollInfoProvider
 import androidx.wear.compose.foundation.rememberActiveFocusRequester
 import androidx.wear.compose.foundation.rotary.RotaryScrollableDefaults
 import androidx.wear.compose.foundation.rotary.rotaryScrollable
-import androidx.wear.compose.foundation.toScrollAwayInfoProvider
 import androidx.wear.compose.integration.demos.common.Centralize
 import androidx.wear.compose.integration.demos.common.ComposableDemo
 import androidx.wear.compose.material3.FilledTonalButton
@@ -52,7 +51,6 @@
         ComposableDemo("Column") { Centralize { ScrollAwayColumn() } }
     )
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Composable
 fun ScrollAwayLazyColumn() {
     val scrollState = rememberLazyListState()
@@ -96,7 +94,7 @@
             // default handling is unsuitable.
             modifier =
                 Modifier.scrollAway(
-                    scrollInfoProvider = scrollState.toScrollAwayInfoProvider(),
+                    scrollInfoProvider = ScrollInfoProvider(scrollState),
                     screenStage = {
                         if (scrollState.isScrollInProgress) ScreenStage.Scrolling
                         else ScreenStage.Idle
@@ -107,7 +105,6 @@
     }
 }
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Composable
 fun ScrollAwayColumn() {
     val scrollState = rememberScrollState()
@@ -148,7 +145,7 @@
             // default handling is unsuitable.
             modifier =
                 Modifier.scrollAway(
-                    scrollInfoProvider = scrollState.toScrollAwayInfoProvider(),
+                    scrollInfoProvider = ScrollInfoProvider(scrollState),
                     screenStage = {
                         if (scrollState.isScrollInProgress) ScreenStage.Scrolling
                         else ScreenStage.Idle
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
index 9f3d934..a4c5332 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
@@ -16,10 +16,14 @@
 
 package androidx.wear.compose.material3.demos
 
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
@@ -118,6 +122,39 @@
                 TextButtonWithSize(TextButtonDefaults.SmallButtonSize)
             }
         }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("Rounded Corner Animation")
+                Spacer(Modifier.width(4.dp))
+                val interactionSource = remember { MutableInteractionSource() }
+                TextButton(
+                    onClick = {},
+                    shape = TextButtonDefaults.animatedShape(interactionSource),
+                    interactionSource = interactionSource
+                ) {
+                    Text(text = "ABC")
+                }
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("Morphed Corner Animation")
+                Spacer(Modifier.width(4.dp))
+                val interactionSource = remember { MutableInteractionSource() }
+                TextButton(
+                    onClick = {},
+                    shape =
+                        TextButtonDefaults.animatedShape(
+                            interactionSource,
+                            shape = CutCornerShape(5.dp),
+                            pressedShape = RoundedCornerShape(5.dp)
+                        ),
+                    interactionSource = interactionSource
+                ) {
+                    Text(text = "ABC")
+                }
+            }
+        }
     }
 }
 
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index 7c13585..f74ad40 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -25,6 +25,7 @@
 import androidx.wear.compose.material3.samples.AnimatedTextSample
 import androidx.wear.compose.material3.samples.AnimatedTextSampleButtonResponse
 import androidx.wear.compose.material3.samples.AnimatedTextSampleSharedFontRegistry
+import androidx.wear.compose.material3.samples.EdgeButtonListSample
 import androidx.wear.compose.material3.samples.EdgeButtonSample
 import androidx.wear.compose.material3.samples.EdgeSwipeForSwipeToDismiss
 import androidx.wear.compose.material3.samples.FixedFontSize
@@ -51,8 +52,17 @@
                         "Edge Button",
                         listOf(
                             ComposableDemo("Simple Edge Button") { EdgeButtonSample() },
+                            ComposableDemo("Simple Edge Button below SLC") {
+                                EdgeButtonListSample()
+                            },
                             ComposableDemo("Edge Button Sizes") { EdgeButtonSizeDemo() },
-                            ComposableDemo("Edge Button Below List") { EdgeButtonBelowListDemo() },
+                            ComposableDemo("Edge Button Below C") { EdgeButtonBelowColumnDemo() },
+                            ComposableDemo("Edge Button Below LC") {
+                                EdgeButtonBelowLazyColumnDemo()
+                            },
+                            ComposableDemo("Edge Button Below SLC") {
+                                EdgeButtonBelowScalingLazyColumnDemo()
+                            },
                         )
                     ),
                     ComposableDemo("Button") { ButtonDemo() },
@@ -62,6 +72,7 @@
                     ComposableDemo("Compact Button") { CompactButtonDemo() },
                     ComposableDemo("Multiline Button") { MultilineButtonDemo() },
                     ComposableDemo("Avatar Button") { AvatarButtonDemo() },
+                    ComposableDemo("Button (Image Background)") { ButtonBackgroundImageDemo() },
                 )
             ),
             ComposableDemo("List Header") { Centralize { ListHeaderDemo() } },
@@ -69,6 +80,7 @@
             ComposableDemo("Card") { CardDemo() },
             ComposableDemo("Text Button") { TextButtonDemo() },
             ComposableDemo("Icon Button") { IconButtonDemo() },
+            ComposableDemo("Animated Shape Buttons") { AnimatedShapeButtonDemo() },
             ComposableDemo("Text Toggle Button") { TextToggleButtonDemo() },
             ComposableDemo("Icon Toggle Button") { IconToggleButtonDemo() },
             ComposableDemo("Checkbox Button") { CheckboxButtonDemo() },
diff --git a/wear/compose/compose-material3/samples/build.gradle b/wear/compose/compose-material3/samples/build.gradle
index 0c87364..49df01d 100644
--- a/wear/compose/compose-material3/samples/build.gradle
+++ b/wear/compose/compose-material3/samples/build.gradle
@@ -45,6 +45,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 25
     }
@@ -56,4 +57,4 @@
     type = LibraryType.SAMPLES
     inceptionYear = "2022"
     description = "Contains the sample code for the Android Wear Compose Material 3 Classes"
-}
\ No newline at end of file
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/EdgeButtonSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/EdgeButtonSample.kt
index 0d49823..122d612 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/EdgeButtonSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/EdgeButtonSample.kt
@@ -18,16 +18,26 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Check
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
 import androidx.wear.compose.material3.ButtonDefaults
+import androidx.wear.compose.material3.ButtonDefaults.buttonColors
+import androidx.wear.compose.material3.Card
 import androidx.wear.compose.material3.EdgeButton
 import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.ScreenScaffold
 import androidx.wear.compose.material3.Text
 
 @Sampled
@@ -48,3 +58,33 @@
         }
     }
 }
+
+@Sampled
+@Composable
+fun EdgeButtonListSample() {
+    val state = rememberScalingLazyListState()
+    ScreenScaffold(
+        scrollState = state,
+        bottomButton = {
+            EdgeButton(
+                onClick = {},
+                buttonHeight = ButtonDefaults.EdgeButtonHeightLarge,
+                colors = buttonColors(containerColor = Color.DarkGray)
+            ) {
+                Text("Ok", textAlign = TextAlign.Center)
+            }
+        }
+    ) {
+        ScalingLazyColumn(
+            state = state,
+            modifier = Modifier.fillMaxSize(),
+            autoCentering = null,
+            contentPadding = PaddingValues(10.dp, 20.dp, 10.dp, 100.dp),
+            horizontalAlignment = Alignment.CenterHorizontally
+        ) {
+            items(10) {
+                Card(onClick = {}, modifier = Modifier.fillMaxWidth(0.9f)) { Text("Item #$it") }
+            }
+        }
+    }
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/IconButtonSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/IconButtonSample.kt
index ccb7bf5..5646efd 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/IconButtonSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/IconButtonSample.kt
@@ -17,13 +17,16 @@
 package androidx.wear.compose.material3.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.wear.compose.material3.FilledIconButton
 import androidx.wear.compose.material3.FilledTonalIconButton
 import androidx.wear.compose.material3.Icon
 import androidx.wear.compose.material3.IconButton
+import androidx.wear.compose.material3.IconButtonDefaults
 import androidx.wear.compose.material3.OutlinedIconButton
 
 @Composable
@@ -69,3 +72,16 @@
         Icon(imageVector = Icons.Filled.Favorite, contentDescription = "Favorite icon")
     }
 }
+
+@Composable
+@Sampled
+fun IconButtonWithCornerAnimationSample() {
+    val interactionSource = remember { MutableInteractionSource() }
+    IconButton(
+        onClick = { /* Do something */ },
+        shape = IconButtonDefaults.animatedShape(interactionSource),
+        interactionSource = interactionSource
+    ) {
+        Icon(imageVector = Icons.Filled.Favorite, contentDescription = "Favorite icon")
+    }
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
index bd5d2e7..9c732d4 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
@@ -36,6 +36,8 @@
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.material3.CircularProgressIndicator
 import androidx.wear.compose.material3.Icon
@@ -43,7 +45,7 @@
 import androidx.wear.compose.material3.IconButtonDefaults
 import androidx.wear.compose.material3.MaterialTheme
 import androidx.wear.compose.material3.ProgressIndicatorDefaults
-import androidx.wear.compose.material3.ProgressIndicatorDefaults.FullScreenPadding
+import androidx.wear.compose.material3.SegmentedCircularProgressIndicator
 
 @Sampled
 @Composable
@@ -51,7 +53,7 @@
     Box(
         modifier =
             Modifier.background(MaterialTheme.colorScheme.background)
-                .padding(FullScreenPadding)
+                .padding(ProgressIndicatorDefaults.FullScreenPadding)
                 .fillMaxSize()
     ) {
         CircularProgressIndicator(
@@ -72,6 +74,7 @@
 fun MediaButtonProgressIndicatorSample() {
     var isPlaying by remember { mutableStateOf(false) }
     val progressPadding = 4.dp
+    val progress = 0.75f
 
     Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) {
         Box(
@@ -79,10 +82,18 @@
                 Modifier.align(Alignment.Center)
                     .size(IconButtonDefaults.DefaultButtonSize + progressPadding)
         ) {
-            CircularProgressIndicator(progress = { 0.75f }, strokeWidth = progressPadding)
+            CircularProgressIndicator(progress = { progress }, strokeWidth = progressPadding)
             IconButton(
                 modifier =
                     Modifier.align(Alignment.Center)
+                        .semantics {
+                            // Set custom progress semantics for accessibility.
+                            contentDescription =
+                                String.format(
+                                    "Play/pause button, track progress: %.0f%%",
+                                    progress * 100
+                                )
+                        }
                         .padding(progressPadding)
                         .clip(CircleShape)
                         .background(MaterialTheme.colorScheme.surfaceContainerLow),
@@ -90,7 +101,7 @@
             ) {
                 Icon(
                     imageVector = if (isPlaying) Icons.Filled.Close else Icons.Filled.PlayArrow,
-                    contentDescription = "Play/pause button icon"
+                    contentDescription = null,
                 )
             }
         }
@@ -103,7 +114,7 @@
     Box(
         modifier =
             Modifier.background(MaterialTheme.colorScheme.background)
-                .padding(FullScreenPadding)
+                .padding(ProgressIndicatorDefaults.FullScreenPadding)
                 .fillMaxSize()
     ) {
         CircularProgressIndicator(
@@ -125,3 +136,65 @@
         )
     }
 }
+
+@Sampled
+@Composable
+fun SmallValuesProgressIndicatorSample() {
+    Box {
+        CircularProgressIndicator(
+            // Small progress values like 2% will be rounded up to at least the stroke width.
+            progress = { 0.02f },
+            modifier = Modifier.fillMaxSize().padding(ProgressIndicatorDefaults.FullScreenPadding),
+            startAngle = 120f,
+            endAngle = 60f,
+            strokeWidth = 10.dp,
+            colors =
+                ProgressIndicatorDefaults.colors(
+                    indicatorColor = Color.Green,
+                    trackColor = Color.White
+                ),
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun SegmentedProgressIndicatorSample() {
+    Box(
+        modifier =
+            Modifier.background(MaterialTheme.colorScheme.background)
+                .padding(ProgressIndicatorDefaults.FullScreenPadding)
+                .fillMaxSize()
+    ) {
+        SegmentedCircularProgressIndicator(
+            segmentCount = 5,
+            progress = { 0.5f },
+            colors =
+                ProgressIndicatorDefaults.colors(
+                    indicatorColor = Color.Green,
+                    trackColor = Color.Green.copy(alpha = 0.5f)
+                )
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun SegmentedProgressIndicatorOnOffSample() {
+    Box(
+        modifier =
+            Modifier.background(MaterialTheme.colorScheme.background)
+                .padding(ProgressIndicatorDefaults.FullScreenPadding)
+                .fillMaxSize()
+    ) {
+        SegmentedCircularProgressIndicator(
+            segmentCount = 5,
+            completed = { it % 2 != 0 },
+            colors =
+                ProgressIndicatorDefaults.colors(
+                    indicatorColor = Color.Green,
+                    trackColor = Color.Green.copy(alpha = 0.5f)
+                )
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt
index 0d8de7d..08d9fc6 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt
@@ -25,10 +25,10 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ScrollInfoProvider
 import androidx.wear.compose.foundation.lazy.AutoCenteringParams
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.foundation.toScrollAwayInfoProvider
 import androidx.wear.compose.material3.FilledTonalButton
 import androidx.wear.compose.material3.ListHeader
 import androidx.wear.compose.material3.ScreenStage
@@ -70,7 +70,7 @@
             // [Modifier.scrollAway] directly.
             modifier =
                 Modifier.scrollAway(
-                    scrollInfoProvider = state.toScrollAwayInfoProvider(),
+                    scrollInfoProvider = ScrollInfoProvider(state),
                     screenStage = {
                         if (state.isScrollInProgress) ScreenStage.Scrolling else ScreenStage.Idle
                     }
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TextButtonSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TextButtonSample.kt
index e1efe87..6cfa86b 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TextButtonSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TextButtonSample.kt
@@ -17,8 +17,10 @@
 package androidx.wear.compose.material3.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.wear.compose.material3.ButtonDefaults
 import androidx.wear.compose.material3.Text
@@ -88,3 +90,16 @@
         Text(text = "ABC")
     }
 }
+
+@Composable
+@Sampled
+fun TextButtonWithCornerAnimationSample() {
+    val interactionSource = remember { MutableInteractionSource() }
+    TextButton(
+        onClick = { /* Do something */ },
+        shape = TextButtonDefaults.animatedShape(interactionSource),
+        interactionSource = interactionSource
+    ) {
+        Text(text = "ABC")
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt
index 7393cb2..4f8b9b4 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
@@ -38,7 +39,9 @@
 import androidx.wear.compose.material3.Button
 import androidx.wear.compose.material3.ButtonDefaults
 import androidx.wear.compose.material3.CenteredText
+import androidx.wear.compose.material3.ChildButton
 import androidx.wear.compose.material3.CompactButton
+import androidx.wear.compose.material3.FilledTonalButton
 import androidx.wear.compose.material3.MaterialTheme
 import androidx.wear.compose.material3.OutlinedButton
 import androidx.wear.compose.material3.SCREENSHOT_GOLDEN_PATH
@@ -84,10 +87,105 @@
         ImageBackgroundButton(enabled = false)
     }
 
+    @Test
+    fun button_label_only_center_aligned() = verifyScreenshot {
+        Button(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) }
+        )
+    }
+
+    @Test
+    fun button_icon_and_label_start_aligned() = verifyScreenshot {
+        Button(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) },
+            icon = { TestIcon() },
+        )
+    }
+
+    @Test
+    fun filled_tonal_button_label_only_center_aligned() = verifyScreenshot {
+        FilledTonalButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) }
+        )
+    }
+
+    @Test
+    fun filled_tonal_button_icon_and_label_start_aligned() = verifyScreenshot {
+        FilledTonalButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) },
+            icon = { TestIcon() },
+        )
+    }
+
+    @Test
+    fun outlined_tonal_button_label_only_center_aligned() = verifyScreenshot {
+        OutlinedButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) }
+        )
+    }
+
+    @Test
+    fun outlined_tonal_button_icon_and_label_start_aligned() = verifyScreenshot {
+        OutlinedButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) },
+            icon = { TestIcon() },
+        )
+    }
+
+    @Test
+    fun child_button_label_only_center_aligned() = verifyScreenshot {
+        ChildButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) }
+        )
+    }
+
+    @Test
+    fun child_button_icon_and_label_start_aligned() = verifyScreenshot {
+        ChildButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) },
+            icon = { TestIcon() },
+        )
+    }
+
     @Test fun compact_button_enabled() = verifyScreenshot { CompactButton() }
 
     @Test fun compact_button_disabled() = verifyScreenshot { CompactButton(enabled = false) }
 
+    @Test
+    fun compact_button_label_only_center_aligned() = verifyScreenshot {
+        CompactButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) }
+        )
+    }
+
+    @Test
+    fun compact_button_icon_and_label_start_aligned() = verifyScreenshot {
+        CompactButton(
+            onClick = {},
+            modifier = Modifier.fillMaxWidth().testTag(TEST_TAG),
+            label = { Text("Label only", modifier = Modifier.fillMaxWidth()) },
+            icon = { TestIcon() },
+        )
+    }
+
     @Composable
     private fun BaseButton(enabled: Boolean = true) {
         Button(enabled = enabled, onClick = {}, modifier = Modifier.testTag(TEST_TAG)) {
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index 2cc30f4..18b7304 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -57,6 +57,8 @@
 import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.height
 import org.junit.Assert.assertEquals
@@ -806,6 +808,323 @@
         )
     }
 
+    @Test
+    fun button_defines_default_overflow() {
+        var labelOverflow: TextOverflow? = null
+        var secondaryLabelOverflow: TextOverflow? = null
+
+        rule.setContentWithTheme {
+            Button(
+                onClick = {},
+                label = { labelOverflow = LocalTextOverflow.current },
+                secondaryLabel = { secondaryLabelOverflow = LocalTextOverflow.current },
+            )
+        }
+
+        assertEquals(TextOverflow.Ellipsis, labelOverflow)
+        assertEquals(TextOverflow.Ellipsis, secondaryLabelOverflow)
+    }
+
+    @Test
+    fun button_defines_default_maxlines() {
+        var labelMaxLines: Int? = null
+        var secondaryLabelMaxLines: Int? = null
+
+        rule.setContentWithTheme {
+            Button(
+                onClick = {},
+                label = { labelMaxLines = LocalTextMaxLines.current },
+                secondaryLabel = { secondaryLabelMaxLines = LocalTextMaxLines.current },
+            )
+        }
+
+        assertEquals(3, labelMaxLines)
+        assertEquals(2, secondaryLabelMaxLines)
+    }
+
+    @Test
+    fun button_defines_start_alignment() {
+        var labelAlignment: TextAlign? = null
+        var secondaryLabelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            Button(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+                secondaryLabel = { secondaryLabelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Start, labelAlignment)
+        assertEquals(TextAlign.Start, secondaryLabelAlignment)
+    }
+
+    @Test
+    fun button_defines_center_alignment_for_label_only() {
+        var labelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            Button(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Center, labelAlignment)
+    }
+
+    @Test
+    fun filled_tonal_button_defines_default_overflow() {
+        var labelOverflow: TextOverflow? = null
+        var secondaryLabelOverflow: TextOverflow? = null
+
+        rule.setContentWithTheme {
+            FilledTonalButton(
+                onClick = {},
+                label = { labelOverflow = LocalTextOverflow.current },
+                secondaryLabel = { secondaryLabelOverflow = LocalTextOverflow.current },
+            )
+        }
+
+        assertEquals(TextOverflow.Ellipsis, labelOverflow)
+        assertEquals(TextOverflow.Ellipsis, secondaryLabelOverflow)
+    }
+
+    @Test
+    fun filled_tonal_button_defines_default_maxlines() {
+        var labelMaxLines: Int? = null
+        var secondaryLabelMaxLines: Int? = null
+
+        rule.setContentWithTheme {
+            FilledTonalButton(
+                onClick = {},
+                label = { labelMaxLines = LocalTextMaxLines.current },
+                secondaryLabel = { secondaryLabelMaxLines = LocalTextMaxLines.current },
+            )
+        }
+
+        assertEquals(3, labelMaxLines)
+        assertEquals(2, secondaryLabelMaxLines)
+    }
+
+    @Test
+    fun filled_tonal_button_defines_start_alignment() {
+        var labelAlignment: TextAlign? = null
+        var secondaryLabelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            FilledTonalButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+                secondaryLabel = { secondaryLabelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Start, labelAlignment)
+        assertEquals(TextAlign.Start, secondaryLabelAlignment)
+    }
+
+    @Test
+    fun filled_tonal_button_defines_center_alignment_for_label_only() {
+        var labelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            FilledTonalButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Center, labelAlignment)
+    }
+
+    @Test
+    fun outlined_button_defines_default_overflow() {
+        var labelOverflow: TextOverflow? = null
+        var secondaryLabelOverflow: TextOverflow? = null
+
+        rule.setContentWithTheme {
+            OutlinedButton(
+                onClick = {},
+                label = { labelOverflow = LocalTextOverflow.current },
+                secondaryLabel = { secondaryLabelOverflow = LocalTextOverflow.current },
+            )
+        }
+
+        assertEquals(TextOverflow.Ellipsis, labelOverflow)
+        assertEquals(TextOverflow.Ellipsis, secondaryLabelOverflow)
+    }
+
+    @Test
+    fun outlined_button_defines_default_maxlines() {
+        var labelMaxLines: Int? = null
+        var secondaryLabelMaxLines: Int? = null
+
+        rule.setContentWithTheme {
+            OutlinedButton(
+                onClick = {},
+                label = { labelMaxLines = LocalTextMaxLines.current },
+                secondaryLabel = { secondaryLabelMaxLines = LocalTextMaxLines.current },
+            )
+        }
+
+        assertEquals(3, labelMaxLines)
+        assertEquals(2, secondaryLabelMaxLines)
+    }
+
+    @Test
+    fun outlined_tonal_button_defines_start_alignment() {
+        var labelAlignment: TextAlign? = null
+        var secondaryLabelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            OutlinedButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+                secondaryLabel = { secondaryLabelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Start, labelAlignment)
+        assertEquals(TextAlign.Start, secondaryLabelAlignment)
+    }
+
+    @Test
+    fun outlined_button_defines_center_alignment_for_label_only() {
+        var labelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            OutlinedButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Center, labelAlignment)
+    }
+
+    @Test
+    fun child_button_defines_default_overflow() {
+        var labelOverflow: TextOverflow? = null
+        var secondaryLabelOverflow: TextOverflow? = null
+
+        rule.setContentWithTheme {
+            ChildButton(
+                onClick = {},
+                label = { labelOverflow = LocalTextOverflow.current },
+                secondaryLabel = { secondaryLabelOverflow = LocalTextOverflow.current },
+            )
+        }
+
+        assertEquals(TextOverflow.Ellipsis, labelOverflow)
+        assertEquals(TextOverflow.Ellipsis, secondaryLabelOverflow)
+    }
+
+    @Test
+    fun child_button_defines_default_maxlines() {
+        var labelMaxLines: Int? = null
+        var secondaryLabelMaxLines: Int? = null
+
+        rule.setContentWithTheme {
+            ChildButton(
+                onClick = {},
+                label = { labelMaxLines = LocalTextMaxLines.current },
+                secondaryLabel = { secondaryLabelMaxLines = LocalTextMaxLines.current },
+            )
+        }
+
+        assertEquals(3, labelMaxLines)
+        assertEquals(2, secondaryLabelMaxLines)
+    }
+
+    @Test
+    fun child_button_defines_start_alignment() {
+        var labelAlignment: TextAlign? = null
+        var secondaryLabelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            ChildButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+                secondaryLabel = { secondaryLabelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Start, labelAlignment)
+        assertEquals(TextAlign.Start, secondaryLabelAlignment)
+    }
+
+    @Test
+    fun child_button_defines_center_alignment_for_label_only() {
+        var labelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            ChildButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Center, labelAlignment)
+    }
+
+    @Test
+    fun compact_button_defines_default_overflow() {
+        var labelOverflow: TextOverflow? = null
+
+        rule.setContentWithTheme {
+            CompactButton(
+                onClick = {},
+                label = { labelOverflow = LocalTextOverflow.current },
+            )
+        }
+
+        assertEquals(TextOverflow.Ellipsis, labelOverflow)
+    }
+
+    @Test
+    fun compact_button_defines_default_maxlines() {
+        var labelMaxLines: Int? = null
+
+        rule.setContentWithTheme {
+            CompactButton(
+                onClick = {},
+                label = { labelMaxLines = LocalTextMaxLines.current },
+            )
+        }
+
+        assertEquals(1, labelMaxLines)
+    }
+
+    @Test
+    fun compact_button_defines_start_alignment() {
+        var labelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            CompactButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+                icon = {},
+            )
+        }
+
+        assertEquals(TextAlign.Start, labelAlignment)
+    }
+
+    @Test
+    fun compact_button_defines_center_alignment_for_label_only() {
+        var labelAlignment: TextAlign? = null
+
+        rule.setContentWithTheme {
+            CompactButton(
+                onClick = {},
+                label = { labelAlignment = LocalTextAlign.current },
+            )
+        }
+
+        assertEquals(TextAlign.Center, labelAlignment)
+    }
+
     private fun responds_to_long_click(
         enabled: Boolean,
         onLongClick: () -> Unit,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/EdgeButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/EdgeButtonScreenshotTest.kt
index eee671e..d37e1e2 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/EdgeButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/EdgeButtonScreenshotTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -33,6 +34,7 @@
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -67,24 +69,53 @@
 
     @Test
     fun edge_button_xsmall() =
-        verifyScreenshot() { BasicEdgeButton(height = ButtonDefaults.EdgeButtonHeightExtraSmall) }
+        verifyScreenshot() {
+            BasicEdgeButton(buttonHeight = ButtonDefaults.EdgeButtonHeightExtraSmall)
+        }
 
     @Test
     fun edge_button_small() =
-        verifyScreenshot() { BasicEdgeButton(height = ButtonDefaults.EdgeButtonHeightSmall) }
+        verifyScreenshot() { BasicEdgeButton(buttonHeight = ButtonDefaults.EdgeButtonHeightSmall) }
 
     @Test
     fun edge_button_medium() =
-        verifyScreenshot() { BasicEdgeButton(height = ButtonDefaults.EdgeButtonHeightMedium) }
+        verifyScreenshot() { BasicEdgeButton(buttonHeight = ButtonDefaults.EdgeButtonHeightMedium) }
 
     @Test
     fun edge_button_large() =
-        verifyScreenshot() { BasicEdgeButton(height = ButtonDefaults.EdgeButtonHeightLarge) }
+        verifyScreenshot() { BasicEdgeButton(buttonHeight = ButtonDefaults.EdgeButtonHeightLarge) }
 
     @Test
     fun edge_button_disabled() =
         verifyScreenshot() {
-            BasicEdgeButton(height = ButtonDefaults.EdgeButtonHeightMedium, enabled = false)
+            BasicEdgeButton(buttonHeight = ButtonDefaults.EdgeButtonHeightMedium, enabled = false)
+        }
+
+    @Test
+    fun edge_button_small_space_very_limited() =
+        verifyScreenshot() {
+            BasicEdgeButton(
+                buttonHeight = ButtonDefaults.EdgeButtonHeightSmall,
+                constrainedHeight = 10.dp
+            )
+        }
+
+    @Test
+    fun edge_button_small_space_limited() =
+        verifyScreenshot() {
+            BasicEdgeButton(
+                buttonHeight = ButtonDefaults.EdgeButtonHeightSmall,
+                constrainedHeight = 30.dp
+            )
+        }
+
+    @Test
+    fun edge_button_small_slightly_limited() =
+        verifyScreenshot() {
+            BasicEdgeButton(
+                buttonHeight = ButtonDefaults.EdgeButtonHeightSmall,
+                constrainedHeight = 40.dp
+            )
         }
 
     private val LONG_TEXT =
@@ -94,23 +125,34 @@
     @Test
     fun edge_button_xsmall_long_text() =
         verifyScreenshot() {
-            BasicEdgeButton(height = ButtonDefaults.EdgeButtonHeightExtraSmall, text = LONG_TEXT)
+            BasicEdgeButton(
+                buttonHeight = ButtonDefaults.EdgeButtonHeightExtraSmall,
+                text = LONG_TEXT
+            )
         }
 
     @Test
     fun edge_button_large_long_text() =
         verifyScreenshot() {
-            BasicEdgeButton(height = ButtonDefaults.EdgeButtonHeightLarge, text = LONG_TEXT)
+            BasicEdgeButton(buttonHeight = ButtonDefaults.EdgeButtonHeightLarge, text = LONG_TEXT)
         }
 
     @Composable
-    private fun BasicEdgeButton(height: Dp, enabled: Boolean = true, text: String = "Text") {
+    private fun BasicEdgeButton(
+        buttonHeight: Dp,
+        constrainedHeight: Dp? = null,
+        enabled: Boolean = true,
+        text: String = "Text"
+    ) {
         Box(Modifier.fillMaxSize()) {
             EdgeButton(
                 onClick = { /* Do something */ },
                 enabled = enabled,
-                buttonHeight = height,
-                modifier = Modifier.align(Alignment.BottomEnd).testTag(TEST_TAG)
+                buttonHeight = buttonHeight,
+                modifier =
+                    Modifier.align(Alignment.BottomEnd)
+                        .testTag(TEST_TAG)
+                        .then(constrainedHeight?.let { Modifier.height(it) } ?: Modifier)
             ) {
                 BasicText(text)
             }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
index fb2582c..23d70b0 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
@@ -18,15 +18,21 @@
 
 import android.os.Build
 import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Home
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -45,6 +51,8 @@
 import androidx.wear.compose.material3.OutlinedIconButton
 import androidx.wear.compose.material3.SCREENSHOT_GOLDEN_PATH
 import androidx.wear.compose.material3.TEST_TAG
+import androidx.wear.compose.material3.rememberAnimatedCornerBasedShape
+import androidx.wear.compose.material3.rememberAnimatedRoundedCornerShape
 import androidx.wear.compose.material3.setContentWithTheme
 import androidx.wear.compose.material3.touchTargetAwareSize
 import org.junit.Rule
@@ -148,6 +156,73 @@
         sampleIconButton(enabled = true, isCompact = false, modifier = Modifier.offset(10.dp))
     }
 
+    @Test
+    fun button_with_corner_animation() = verifyScreenshot {
+        val interactionSource = remember { MutableInteractionSource() }
+        sampleOutlinedIconButton(
+            shape = IconButtonDefaults.animatedShape(interactionSource),
+            interactionSource = interactionSource
+        )
+    }
+
+    @Test
+    fun button_with_corner_animation_50pct() {
+        verifyScreenshot { sampleOutlinedIconButton(shape = animatedShapesAtPct(0.5f)) }
+    }
+
+    @Test
+    fun button_with_corner_animation_100pct() {
+        verifyScreenshot { sampleOutlinedIconButton(shape = animatedShapesAtPct(1.0f)) }
+    }
+
+    @Test
+    fun button_with_morph_animation() = verifyScreenshot {
+        val interactionSource = remember { MutableInteractionSource() }
+        sampleOutlinedIconButton(
+            shape =
+                IconButtonDefaults.animatedShape(
+                    interactionSource,
+                    shape = CutCornerShape(15.dp),
+                    pressedShape = RoundedCornerShape(15.dp)
+                ),
+            interactionSource = interactionSource,
+        )
+    }
+
+    @Test
+    fun button_with_morph_animation_50pct() = verifyScreenshot {
+        sampleOutlinedIconButton(shape = morphShapesAtPct(0.5f))
+    }
+
+    @Test
+    fun button_with_morph_animation_100pct() = verifyScreenshot {
+        sampleOutlinedIconButton(shape = morphShapesAtPct(1.0f))
+    }
+
+    @Composable
+    private fun animatedShapesAtPct(progress: Float): Shape {
+        val progressShape =
+            rememberAnimatedRoundedCornerShape(
+                IconButtonDefaults.shape,
+                MaterialTheme.shapes.small as RoundedCornerShape,
+                mutableStateOf(progress)
+            )
+
+        return progressShape
+    }
+
+    @Composable
+    private fun morphShapesAtPct(progress: Float): Shape {
+        val progressShape =
+            rememberAnimatedCornerBasedShape(
+                CutCornerShape(15.dp),
+                RoundedCornerShape(15.dp),
+                mutableStateOf(progress)
+            )
+
+        return progressShape
+    }
+
     @Composable
     private fun sampleFilledIconButton(enabled: Boolean, isCompact: Boolean) {
         FilledIconButton(
@@ -201,12 +276,21 @@
     }
 
     @Composable
-    private fun sampleOutlinedIconButton(enabled: Boolean, isCompact: Boolean) {
+    private fun sampleOutlinedIconButton(
+        enabled: Boolean = true,
+        isCompact: Boolean = false,
+        shape: Shape = IconButtonDefaults.shape,
+        modifier: Modifier = Modifier,
+        interactionSource: MutableInteractionSource? = null
+    ) {
         OutlinedIconButton(
             onClick = {},
             enabled = enabled,
+            shape = shape,
+            interactionSource = interactionSource,
             modifier =
-                Modifier.testTag(TEST_TAG)
+                modifier
+                    .testTag(TEST_TAG)
                     .then(
                         if (isCompact)
                             Modifier.touchTargetAwareSize(IconButtonDefaults.ExtraSmallButtonSize)
@@ -228,13 +312,17 @@
 
     @Composable
     private fun sampleIconButton(
-        enabled: Boolean,
-        isCompact: Boolean,
-        modifier: Modifier = Modifier
+        enabled: Boolean = true,
+        isCompact: Boolean = false,
+        shape: Shape = IconButtonDefaults.shape,
+        modifier: Modifier = Modifier,
+        interactionSource: MutableInteractionSource? = null
     ) {
         IconButton(
             onClick = {},
             enabled = enabled,
+            shape = shape,
+            interactionSource = interactionSource,
             modifier =
                 modifier
                     .testTag(TEST_TAG)
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorScreenshotTest.kt
index e6ad406..2cc06b3 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorScreenshotTest.kt
@@ -129,6 +129,28 @@
         )
     }
 
+    @Test
+    fun segmented_progress_indicator_with_progress() = verifyScreenshot {
+        SegmentedCircularProgressIndicator(
+            progress = { 0.5f },
+            segmentCount = 5,
+            modifier = Modifier.aspectRatio(1f).testTag(TEST_TAG),
+            startAngle = 120f,
+            endAngle = 60f,
+        )
+    }
+
+    @Test
+    fun segmented_progress_indicator_on_off() = verifyScreenshot {
+        SegmentedCircularProgressIndicator(
+            segmentCount = 6,
+            completed = { it % 2 == 0 },
+            modifier = Modifier.aspectRatio(1f).testTag(TEST_TAG),
+            startAngle = 120f,
+            endAngle = 60f,
+        )
+    }
+
     private fun verifyScreenshot(content: @Composable () -> Unit) {
         rule.setContentWithTheme {
             CompositionLocalProvider(
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
index 329c719..a78804c 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
@@ -134,8 +134,8 @@
             )
         }
         rule.waitForIdle()
-        // Color should take approximately a quarter of what it normally takes
-        // (a little bit less), eg 25% / 4 ≈ 6%.
+        // Color should take approximately a quarter of the full screen color percentages,
+        // eg 25% / 4 ≈ 6%.
         rule
             .onNodeWithTag(TEST_TAG)
             .captureToImage()
@@ -152,7 +152,7 @@
         setContentWithTheme {
             CircularProgressIndicator(
                 modifier = Modifier.testTag(TEST_TAG),
-                progress = { 0.05f },
+                progress = { 0.02f },
                 colors =
                     ProgressIndicatorDefaults.colors(
                         indicatorColor = Color.Yellow,
@@ -161,6 +161,7 @@
             )
         }
         rule.waitForIdle()
+        // Small progress values like 2% should be rounded up to at least the stroke width.
         rule
             .onNodeWithTag(TEST_TAG)
             .captureToImage()
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonScreenshotTest.kt
index 7a3b4bd..862e2b9 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/RadioButtonScreenshotTest.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
@@ -30,6 +31,7 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -177,6 +179,16 @@
             )
         }
 
+    @Test
+    fun split_radio_button_customized_padding_6dp() = verifyScreenshot {
+        sampleSplitRadioButton(contentPadding = PaddingValues(6.dp))
+    }
+
+    @Test
+    fun split_radio_button_customized_horizontal_padding_24dp() = verifyScreenshot {
+        sampleSplitRadioButton(contentPadding = PaddingValues(horizontal = 24.dp, vertical = 8.dp))
+    }
+
     @Composable
     private fun sampleRadioButton(
         enabled: Boolean = true,
@@ -197,6 +209,7 @@
     private fun sampleSplitRadioButton(
         selected: Boolean = true,
         enabled: Boolean = true,
+        contentPadding: PaddingValues = RadioButtonDefaults.ContentPadding,
     ) {
         SplitRadioButton(
             label = { Text("SplitRadioButton") },
@@ -207,6 +220,7 @@
             onContainerClick = {},
             selectionContentDescription = null,
             modifier = Modifier.testTag(TEST_TAG),
+            contentPadding = contentPadding,
         )
     }
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScaffoldTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScaffoldTest.kt
index a5f4bc9..ab8ec90 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScaffoldTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScaffoldTest.kt
@@ -19,14 +19,20 @@
 import android.os.Build
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.runtime.Composable
 import androidx.compose.testutils.assertContainsColor
 import androidx.compose.testutils.assertDoesNotContainColor
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.captureToImage
@@ -35,10 +41,13 @@
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipeUp
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.SdkSuppress
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import com.google.common.truth.Truth.assertThat
+import junit.framework.TestCase.assertEquals
 import org.junit.Rule
 import org.junit.Test
 
@@ -154,8 +163,104 @@
         rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(timeTextColor)
     }
 
+    @Test
+    fun no_initial_room_for_bottom_button() {
+        var spaceAvailable: Int = Int.MAX_VALUE
+
+        rule.setContentWithTheme {
+            // Ensure we use the same size no mater where this is run.
+            Box(Modifier.size(300.dp)) {
+                TestScreenScaffold(scrollIndicatorColor = Color.Blue, timeTextColor = Color.Red) {
+                    BoxWithConstraints {
+                        // Check how much space we have for the bottom button
+                        spaceAvailable = constraints.maxHeight
+                    }
+                }
+            }
+        }
+
+        assertEquals(0, spaceAvailable)
+    }
+
+    @Test
+    fun plenty_of_room_for_bottom_button_after_scroll() {
+        var spaceAvailable: Int = Int.MAX_VALUE
+        var expectedSpace = 0f
+
+        val screenSize = 300.dp
+        rule.setContentWithTheme {
+            // The available space is half the screen size minus half a Button height (converting
+            // dps to pixels).
+            expectedSpace =
+                with(LocalDensity.current) { ((screenSize - ButtonDefaults.Height) / 2).toPx() }
+
+            Box(Modifier.size(screenSize)) {
+                TestScreenScaffold(scrollIndicatorColor = Color.Blue, timeTextColor = Color.Red) {
+                    // Check how much space we have for the bottom button
+                    BoxWithConstraints { spaceAvailable = constraints.maxHeight }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(SCROLL_TAG).performTouchInput { repeat(5) { swipeUp() } }
+        rule.waitForIdle()
+
+        // Use floats so we can specify a pixel of tolerance.
+        assertThat(spaceAvailable.toFloat()).isWithin(1f).of(expectedSpace)
+    }
+
+    @Test
+    fun no_initial_room_for_bottom_button_lc() {
+        var spaceAvailable: Int = Int.MAX_VALUE
+
+        rule.setContentWithTheme {
+            // Ensure we use the same size no mater where this is run.
+            Box(Modifier.size(300.dp)) {
+                TestBottomButtonLC() {
+                    BoxWithConstraints {
+                        // Check how much space we have for the bottom button
+                        spaceAvailable = constraints.maxHeight
+                    }
+                }
+            }
+        }
+
+        assertEquals(0, spaceAvailable)
+    }
+
+    @Test fun no_room_for_bottom_button_after_scroll_lc() = check_bottom_button_lc(0.dp)
+
+    @Test fun some_room_for_bottom_button_after_scroll_lc() = check_bottom_button_lc(50.dp)
+
+    private fun check_bottom_button_lc(verticalPadding: Dp = 0.dp) {
+        var spaceAvailable: Int = Int.MAX_VALUE
+        var expectedSpace: Float = Float.MAX_VALUE
+
+        val screenSize = 300.dp
+        rule.setContentWithTheme {
+            expectedSpace = with(LocalDensity.current) { verticalPadding.toPx() }
+
+            Box(Modifier.size(screenSize)) {
+                TestBottomButtonLC(verticalPadding) {
+                    // Check how much space we have for the bottom button
+                    BoxWithConstraints { spaceAvailable = constraints.maxHeight }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(SCROLL_TAG).performTouchInput { repeat(5) { swipeUp() } }
+        rule.waitForIdle()
+
+        // Use floats so we can specify a pixel of tolerance.
+        assertThat(spaceAvailable.toFloat()).isWithin(1f).of(expectedSpace)
+    }
+
     @Composable
-    private fun TestScreenScaffold(scrollIndicatorColor: Color, timeTextColor: Color) {
+    private fun TestScreenScaffold(
+        scrollIndicatorColor: Color,
+        timeTextColor: Color,
+        bottomButton: @Composable BoxScope.() -> Unit = {}
+    ) {
         AppScaffold {
             val scrollState = rememberScalingLazyListState()
             ScreenScaffold(
@@ -169,7 +274,8 @@
                                 .background(scrollIndicatorColor)
                     )
                 },
-                timeText = { Box(Modifier.size(20.dp).background(timeTextColor)) }
+                timeText = { Box(Modifier.size(20.dp).background(timeTextColor)) },
+                bottomButton = bottomButton
             ) {
                 ScalingLazyColumn(
                     state = scrollState,
@@ -185,6 +291,34 @@
             }
         }
     }
+
+    @Composable
+    private fun TestBottomButtonLC(
+        verticalPadding: Dp = 0.dp,
+        bottomButton: @Composable BoxScope.() -> Unit = {}
+    ) {
+        AppScaffold {
+            val scrollState = rememberLazyListState()
+            ScreenScaffold(
+                modifier = Modifier.testTag(TEST_TAG),
+                scrollState = scrollState,
+                bottomButton = bottomButton
+            ) {
+                LazyColumn(
+                    state = scrollState,
+                    modifier = Modifier.fillMaxSize().background(Color.Black).testTag(SCROLL_TAG),
+                    contentPadding = PaddingValues(horizontal = 10.dp, vertical = verticalPadding)
+                ) {
+                    items(10) {
+                        Button(
+                            onClick = {},
+                            label = { Text("Item ${it + 1}") },
+                        )
+                    }
+                }
+            }
+        }
+    }
 }
 
 private const val CONTENT_MESSAGE = "The Content"
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
index 5dfc424..90674cb 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
@@ -47,11 +47,11 @@
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.wear.compose.foundation.ScrollInfoProvider
 import androidx.wear.compose.foundation.lazy.AutoCenteringParams
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
 import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.foundation.toScrollAwayInfoProvider
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -199,7 +199,7 @@
                 TimeText(
                     modifier =
                         Modifier.scrollAway(
-                                scrollInfoProvider = scrollState.toScrollAwayInfoProvider(),
+                                scrollInfoProvider = ScrollInfoProvider(scrollState),
                                 screenStage = {
                                     if (scrollState.isScrollInProgress) ScreenStage.Scrolling
                                     else ScreenStage.Idle
@@ -225,7 +225,7 @@
                 TimeText(
                     modifier =
                         Modifier.scrollAway(
-                                scrollInfoProvider = scrollState.toScrollAwayInfoProvider(),
+                                scrollInfoProvider = ScrollInfoProvider(scrollState),
                                 screenStage = {
                                     if (scrollState.isScrollInProgress) ScreenStage.Scrolling
                                     else ScreenStage.Idle
@@ -256,7 +256,7 @@
                     contentColor = timeTextColor,
                     modifier =
                         Modifier.scrollAway(
-                                scrollInfoProvider = scrollState.toScrollAwayInfoProvider(),
+                                scrollInfoProvider = ScrollInfoProvider(scrollState),
                                 screenStage = {
                                     if (scrollState.isScrollInProgress) ScreenStage.Scrolling
                                     else ScreenStage.Idle
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SegmentedCircularProgressIndicatorTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SegmentedCircularProgressIndicatorTest.kt
new file mode 100644
index 0000000..58e2c21
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SegmentedCircularProgressIndicatorTest.kt
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.testutils.assertDoesNotContainColor
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.ProgressBarRangeInfo
+import androidx.compose.ui.semantics.progressBarRangeInfo
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.assertRangeInfoEquals
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+
+class SegmentedCircularProgressIndicatorTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun supports_testtag() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                segmentCount = 5,
+                progress = { 0.5f },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun allows_semantics_to_be_added_correctly() {
+        val progress = mutableStateOf(0f)
+
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                progress = { progress.value },
+                segmentCount = 5,
+                modifier =
+                    Modifier.testTag(TEST_TAG).semantics {
+                        progressBarRangeInfo = ProgressBarRangeInfo(progress.value, 0f..1f)
+                    },
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertRangeInfoEquals(ProgressBarRangeInfo(0f, 0f..1f))
+
+        rule.runOnIdle { progress.value = 0.5f }
+
+        rule.onNodeWithTag(TEST_TAG).assertRangeInfoEquals(ProgressBarRangeInfo(0.5f, 0f..1f))
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun progress_full_contains_progress_color() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                progress = { 1f },
+                segmentCount = 5,
+                modifier = Modifier.testTag(TEST_TAG),
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+            )
+        }
+        rule.waitForIdle()
+        // by default fully filled progress approximately takes 25% of the control.
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Yellow, 20f..25f)
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertDoesNotContainColor(Color.Red)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun progress_zero_contains_track_color() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                progress = { 0f },
+                segmentCount = 5,
+                modifier = Modifier.testTag(TEST_TAG),
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+            )
+        }
+        rule.waitForIdle()
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertDoesNotContainColor(Color.Yellow)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Red, 20f..25f)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun change_start_end_angle() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                progress = { 0.5f },
+                segmentCount = 5,
+                modifier = Modifier.testTag(TEST_TAG),
+                startAngle = 0f,
+                endAngle = 180f,
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+            )
+        }
+        rule.waitForIdle()
+        // Color should take approximately a quarter of the full screen color percentages,
+        // eg 25% / 4 ≈ 6%.
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Yellow, 4f..8f)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Red, 4f..8f)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun set_small_stroke_width() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                progress = { 0.5f },
+                segmentCount = 5,
+                modifier = Modifier.testTag(TEST_TAG),
+                strokeWidth = 4.dp,
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+            )
+        }
+        rule.waitForIdle()
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Yellow, 2f..6f)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Red, 2f..6f)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun set_large_stroke_width() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                progress = { 0.5f },
+                segmentCount = 5,
+                modifier = Modifier.testTag(TEST_TAG),
+                strokeWidth = 36.dp,
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+            )
+        }
+        rule.waitForIdle()
+        // Because of the stroke cap, progress color takes same amount as track color.
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Yellow, 15f..20f)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Red, 15f..20f)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun set_segments_on_off() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                segmentCount = 6,
+                completed = { it % 2 != 0 },
+                modifier = Modifier.testTag(TEST_TAG),
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+                strokeWidth = 36.dp,
+            )
+        }
+
+        rule.waitForIdle()
+        // Because of the stroke cap, progress color takes same amount as track color.
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Yellow, 15f..20f)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Red, 15f..20f)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun set_segments_all_on() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                segmentCount = 6,
+                completed = { true },
+                modifier = Modifier.testTag(TEST_TAG),
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+            )
+        }
+
+        rule.waitForIdle()
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertDoesNotContainColor(Color.Red)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Yellow, 20f..25f)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun set_segments_all_off() {
+        setContentWithTheme {
+            SegmentedCircularProgressIndicator(
+                segmentCount = 6,
+                completed = { false },
+                modifier = Modifier.testTag(TEST_TAG),
+                colors =
+                    ProgressIndicatorDefaults.colors(
+                        indicatorColor = Color.Yellow,
+                        trackColor = Color.Red
+                    ),
+            )
+        }
+
+        rule.waitForIdle()
+        rule.onNodeWithTag(TEST_TAG).captureToImage().assertDoesNotContainColor(Color.Yellow)
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertColorInPercentageRange(Color.Red, 20f..25f)
+    }
+
+    private fun setContentWithTheme(composable: @Composable BoxScope.() -> Unit) {
+        // Use constant size modifier to limit relative color percentage ranges.
+        rule.setContentWithTheme(modifier = Modifier.size(204.dp), composable = composable)
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
index 9faa545..34b4ac3 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
@@ -18,12 +18,17 @@
 
 import android.os.Build
 import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -34,6 +39,7 @@
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import androidx.wear.compose.material3.ButtonDefaults
+import androidx.wear.compose.material3.IconButtonDefaults
 import androidx.wear.compose.material3.MaterialTheme
 import androidx.wear.compose.material3.SCREENSHOT_GOLDEN_PATH
 import androidx.wear.compose.material3.TEST_TAG
@@ -92,6 +98,29 @@
         sampleTextButton(enabled = true, modifier = Modifier.offset(10.dp))
     }
 
+    @Test
+    fun text_button_with_corner_animation() = verifyScreenshot {
+        val interactionSource = remember { MutableInteractionSource() }
+        sampleTextButton(
+            shape = IconButtonDefaults.animatedShape(interactionSource),
+            interactionSource = interactionSource
+        )
+    }
+
+    @Test
+    fun text_button_with_morph_animation() = verifyScreenshot {
+        val interactionSource = remember { MutableInteractionSource() }
+        sampleTextButton(
+            shape =
+                IconButtonDefaults.animatedShape(
+                    interactionSource,
+                    shape = CutCornerShape(15.dp),
+                    pressedShape = RoundedCornerShape(15.dp)
+                ),
+            interactionSource = interactionSource,
+        )
+    }
+
     @Composable
     private fun sampleFilledTextButton(enabled: Boolean) {
         TextButton(
@@ -130,8 +159,19 @@
     }
 
     @Composable
-    private fun sampleTextButton(enabled: Boolean, modifier: Modifier = Modifier) {
-        TextButton(onClick = {}, enabled = enabled, modifier = modifier.testTag(TEST_TAG)) {
+    private fun sampleTextButton(
+        enabled: Boolean = true,
+        shape: Shape = TextButtonDefaults.shape,
+        modifier: Modifier = Modifier,
+        interactionSource: MutableInteractionSource? = null
+    ) {
+        TextButton(
+            onClick = {},
+            enabled = enabled,
+            shape = shape,
+            modifier = modifier.testTag(TEST_TAG),
+            interactionSource = interactionSource
+        ) {
             Text(text = "ABC")
         }
     }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt
index d7fe226..9c9d5cb 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt
@@ -52,10 +52,16 @@
 
     @get:Rule val testName = TestName()
 
+    private val MockTimeSource =
+        object : TimeSource {
+            @Composable override fun currentTime() = "10:10"
+        }
+
     @Test
     fun time_text_with_clock_only_on_round_device() = verifyScreenshot {
         TimeText(
             modifier = Modifier.testTag(TEST_TAG),
+            timeSource = MockTimeSource,
         ) {
             time()
         }
@@ -66,6 +72,7 @@
         verifyScreenshot(false) {
             TimeText(
                 modifier = Modifier.testTag(TEST_TAG),
+                timeSource = MockTimeSource,
             ) {
                 time()
             }
@@ -75,6 +82,7 @@
     fun time_text_with_status_on_round_device() = verifyScreenshot {
         TimeText(
             modifier = Modifier.testTag(TEST_TAG),
+            timeSource = MockTimeSource,
         ) {
             text("ETA 12:48")
             separator()
@@ -87,6 +95,7 @@
         verifyScreenshot(false) {
             TimeText(
                 modifier = Modifier.testTag(TEST_TAG),
+                timeSource = MockTimeSource,
             ) {
                 text("ETA 12:48")
                 separator()
@@ -98,6 +107,7 @@
     fun time_text_with_icon_on_round_device() = verifyScreenshot {
         TimeText(
             modifier = Modifier.testTag(TEST_TAG),
+            timeSource = MockTimeSource,
         ) {
             time()
             separator()
@@ -116,6 +126,7 @@
         verifyScreenshot(false) {
             TimeText(
                 modifier = Modifier.testTag(TEST_TAG),
+                timeSource = MockTimeSource,
             ) {
                 time()
                 separator()
@@ -138,6 +149,7 @@
             contentColor = Color.Green,
             timeTextStyle = timeTextStyle,
             modifier = Modifier.testTag(TEST_TAG),
+            timeSource = MockTimeSource,
         ) {
             text("ETA", customStyle)
             composable { Spacer(modifier = Modifier.size(4.dp)) }
@@ -154,6 +166,7 @@
             contentColor = Color.Green,
             timeTextStyle = timeTextStyle,
             modifier = Modifier.testTag(TEST_TAG),
+            timeSource = MockTimeSource,
         ) {
             text("Long status that should be ellipsized.")
             composable { Spacer(modifier = Modifier.size(4.dp)) }
@@ -171,6 +184,7 @@
                 contentColor = Color.Green,
                 timeTextStyle = timeTextStyle,
                 modifier = Modifier.testTag(TEST_TAG),
+                timeSource = MockTimeSource,
             ) {
                 text("ETA", customStyle)
                 composable { Spacer(modifier = Modifier.size(4.dp)) }
@@ -180,6 +194,50 @@
             }
         }
 
+    @Test
+    fun time_text_with_very_long_text_on_round_device() =
+        verifyScreenshot(true) {
+            val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
+            val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
+            val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+            TimeText(
+                contentColor = Color.Green,
+                timeTextStyle = timeTextStyle,
+                maxSweepAngle = 180f,
+                modifier = Modifier.testTag(TEST_TAG),
+                timeSource = MockTimeSource,
+            ) {
+                text(
+                    "Very long text to ensure we are respecting the maxSweep parameter",
+                    customStyle
+                )
+                separator(separatorStyle)
+                time()
+            }
+        }
+
+    @Test
+    fun time_text_with_very_long_text_smaller_angle_on_round_device() =
+        verifyScreenshot(true) {
+            val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
+            val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
+            val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+            TimeText(
+                contentColor = Color.Green,
+                timeTextStyle = timeTextStyle,
+                maxSweepAngle = 90f,
+                modifier = Modifier.testTag(TEST_TAG),
+                timeSource = MockTimeSource,
+            ) {
+                text(
+                    "Very long text to ensure we are respecting the maxSweep parameter",
+                    customStyle
+                )
+                separator(separatorStyle)
+                time()
+            }
+        }
+
     private fun verifyScreenshot(isDeviceRound: Boolean = true, content: @Composable () -> Unit) {
         rule.verifyScreenshot(
             methodName = testName.methodName,
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonScreenshotTest.kt
index fa66e67..8fbd6c4 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ToggleButtonScreenshotTest.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
@@ -30,6 +31,7 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -221,6 +223,28 @@
             sampleSplitSwitchButton(checked = false, enabled = false)
         }
 
+    @Test
+    fun split_checkbox_button_customized_padding_6dp() = verifyScreenshot {
+        sampleSplitCheckboxButton(contentPadding = PaddingValues(6.dp))
+    }
+
+    @Test
+    fun split_checkbox_button_customized_horizontal_padding_24dp() = verifyScreenshot {
+        sampleSplitCheckboxButton(
+            contentPadding = PaddingValues(horizontal = 24.dp, vertical = 8.dp)
+        )
+    }
+
+    @Test
+    fun split_switch_button_customized_padding_6dp() = verifyScreenshot {
+        sampleSplitSwitchButton(contentPadding = PaddingValues(6.dp))
+    }
+
+    @Test
+    fun split_switch_button_customized_horizontal_padding_24dp() = verifyScreenshot {
+        sampleSplitSwitchButton(contentPadding = PaddingValues(horizontal = 24.dp, vertical = 8.dp))
+    }
+
     @Composable
     private fun sampleCheckboxButton(
         enabled: Boolean = true,
@@ -241,6 +265,7 @@
     private fun sampleSplitCheckboxButton(
         checked: Boolean = true,
         enabled: Boolean = true,
+        contentPadding: PaddingValues = CheckboxButtonDefaults.ContentPadding,
     ) {
         SplitCheckboxButton(
             label = { Text("SplitToggleButton") },
@@ -251,6 +276,7 @@
             onContainerClick = {},
             toggleContentDescription = "",
             modifier = Modifier.testTag(TEST_TAG),
+            contentPadding = contentPadding,
         )
     }
 
@@ -274,6 +300,7 @@
     private fun sampleSplitSwitchButton(
         checked: Boolean = true,
         enabled: Boolean = true,
+        contentPadding: PaddingValues = SwitchButtonDefaults.ContentPadding
     ) {
         SplitSwitchButton(
             label = { Text("SplitToggleButton") },
@@ -284,6 +311,7 @@
             onContainerClick = {},
             toggleContentDescription = "",
             modifier = Modifier.testTag(TEST_TAG),
+            contentPadding = contentPadding
         )
     }
 
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedCornerShape.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedCornerShape.kt
new file mode 100644
index 0000000..70fa44b
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedCornerShape.kt
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.foundation.shape.AbsoluteCutCornerShape
+import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateMapOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.toRect
+import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.asComposePath
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.LayoutDirection.Ltr
+import androidx.compose.ui.util.lerp
+import androidx.graphics.shapes.CornerRounding
+import androidx.graphics.shapes.Morph
+import androidx.graphics.shapes.RoundedPolygon
+import androidx.graphics.shapes.rectangle
+import androidx.graphics.shapes.toPath
+
+/**
+ * An implementation similar to RoundedCornerShape, but based on linear interpolation between a
+ * start and stop CornerSize, and an observable progress between 0.0 and 1.0.
+ *
+ * @param start the corner sizes when progress is 0.0
+ * @param stop the corner sizes when progress is 1.0
+ * @param progress returns the current progress from start to stop.
+ */
+@Stable
+internal class AnimatedRoundedCornerShape(
+    start: RoundedCornerShape,
+    stop: RoundedCornerShape,
+    progress: () -> Float
+) : Shape {
+    private val topStart = AnimatedCornerSize(start.topStart, stop.topStart, progress)
+    private val topEnd = AnimatedCornerSize(start.topEnd, stop.topEnd, progress)
+    private val bottomEnd = AnimatedCornerSize(start.bottomEnd, stop.bottomEnd, progress)
+    private val bottomStart = AnimatedCornerSize(start.bottomStart, stop.bottomStart, progress)
+
+    override fun createOutline(
+        size: Size,
+        layoutDirection: LayoutDirection,
+        density: Density
+    ): Outline =
+        Outline.Rounded(
+            RoundRect(
+                rect = size.toRect(),
+                topLeft =
+                    CornerRadius(
+                        (if (layoutDirection == Ltr) topStart else topEnd).toPx(size, density)
+                    ),
+                topRight =
+                    CornerRadius(
+                        (if (layoutDirection == Ltr) topEnd else topStart).toPx(size, density)
+                    ),
+                bottomRight =
+                    CornerRadius(
+                        (if (layoutDirection == Ltr) bottomEnd else bottomStart).toPx(size, density)
+                    ),
+                bottomLeft =
+                    CornerRadius(
+                        (if (layoutDirection == Ltr) bottomStart else bottomEnd).toPx(size, density)
+                    )
+            )
+        )
+}
+
+@Stable
+internal class AnimatedCornerSize(
+    val start: CornerSize,
+    val stop: CornerSize,
+    val progress: () -> Float
+) : CornerSize {
+    override fun toPx(shapeSize: Size, density: Density): Float =
+        lerp(start.toPx(shapeSize, density), stop.toPx(shapeSize, density), progress())
+}
+
+@Composable
+internal fun rememberAnimatedRoundedCornerShape(
+    shape: RoundedCornerShape,
+    pressedShape: RoundedCornerShape,
+    progress: State<Float>
+): Shape {
+    return remember(shape, pressedShape, progress) {
+        AnimatedRoundedCornerShape(shape, pressedShape) { progress.value }
+    }
+}
+
+/**
+ * An implementation of Shape, animating a Morph based on an observable progress between 0.0 and
+ * 1.0.
+ */
+@Stable
+internal class AnimatedMorphShape(
+    private val shape: CornerBasedShape,
+    private val pressedShape: CornerBasedShape,
+    private val progress: () -> Float
+) : Shape {
+
+    @Suppress("PrimitiveInCollection") // No way to get underlying Long of Size
+    private val morphState = mutableStateMapOf<Size, Morph?>()
+
+    override fun createOutline(
+        size: Size,
+        layoutDirection: LayoutDirection,
+        density: Density
+    ): Outline {
+        val morph =
+            morphState.computeIfAbsent(size) {
+                val polygon =
+                    shape.toRoundedPolygonOrNull(size, density, layoutDirection)
+                        ?: return@computeIfAbsent null
+                val pressedPolygon =
+                    pressedShape.toRoundedPolygonOrNull(size, density, layoutDirection)
+                        ?: return@computeIfAbsent null
+
+                Morph(polygon, pressedPolygon)
+            }
+
+        if (morph == null) {
+            return shape.createOutline(size, layoutDirection, density)
+        }
+
+        val path =
+            morph.toPath(progress()).asComposePath().apply {
+                transform(Matrix().apply { scale(size.width, size.height) })
+            }
+
+        return Outline.Generic(path)
+    }
+}
+
+/**
+ * Convert a CornerBasedShape (Compose Shape) to a RoundedPolygon (Graphics Shape), such that it can
+ * be used in a Morph between two shapes.
+ *
+ * Returns null if the CornerBasedShape is not one of RoundedCornerShape, CutCornerShape,
+ * AbsoluteRoundedCornerShape, or AbsoluteCutCornerShape.
+ *
+ * Size and density must be known at this point since Corners may be specified in either percentage
+ * or dp, and cannot be correctly scaled as either a RoundedPolygon or a Morph.
+ *
+ * @param size The size of the final Composable such as a Button.
+ * @param density The density of the composition.
+ */
+private fun CornerBasedShape.toRoundedPolygonOrNull(
+    size: Size,
+    density: Density,
+    layoutDirection: LayoutDirection
+): RoundedPolygon? {
+    return when (this) {
+        is RoundedCornerShape -> toRoundedPolygon(size, density, layoutDirection).normalized()
+        is CutCornerShape -> toRoundedPolygon(size, density, layoutDirection).normalized()
+        is AbsoluteRoundedCornerShape -> toRoundedPolygon(size, density).normalized()
+        is AbsoluteCutCornerShape -> toRoundedPolygon(size, density).normalized()
+        else -> null
+    }
+}
+
+private fun RoundedCornerShape.toRoundedPolygon(
+    size: Size,
+    density: Density,
+    layoutDirection: LayoutDirection
+) =
+    RoundedPolygon.rectangle(
+        size.width,
+        size.height,
+        perVertexRounding =
+            listOf(
+                CornerRounding(
+                    (if (layoutDirection == Ltr) bottomEnd else bottomStart).toPx(size, density)
+                ),
+                CornerRounding(
+                    (if (layoutDirection == Ltr) bottomStart else bottomEnd).toPx(size, density)
+                ),
+                CornerRounding(
+                    (if (layoutDirection == Ltr) topStart else topEnd).toPx(size, density)
+                ),
+                CornerRounding(
+                    (if (layoutDirection == Ltr) topEnd else topStart).toPx(size, density)
+                ),
+            )
+    )
+
+private fun CutCornerShape.toRoundedPolygon(
+    size: Size,
+    density: Density,
+    layoutDirection: LayoutDirection
+): RoundedPolygon {
+    val topRightPx = (if (layoutDirection == Ltr) topEnd else topStart).toPx(size, density)
+    val bottomRightPx = (if (layoutDirection == Ltr) bottomEnd else bottomStart).toPx(size, density)
+    val bottomLeftPx = (if (layoutDirection == Ltr) bottomStart else bottomEnd).toPx(size, density)
+    val topLeftPx = (if (layoutDirection == Ltr) topStart else topEnd).toPx(size, density)
+
+    val width = size.width
+    val height = size.height
+
+    return RoundedPolygon(
+        floatArrayOf(
+            width - bottomRightPx,
+            height,
+            bottomLeftPx,
+            height,
+            0f,
+            height - bottomLeftPx,
+            0f,
+            topLeftPx,
+            topLeftPx,
+            0f,
+            width - topRightPx,
+            0f,
+            width,
+            topRightPx,
+            width,
+            height - bottomRightPx,
+        ),
+    )
+}
+
+private fun AbsoluteRoundedCornerShape.toRoundedPolygon(size: Size, density: Density) =
+    RoundedPolygon.rectangle(
+        size.width,
+        size.height,
+        perVertexRounding =
+            listOf(
+                CornerRounding(bottomEnd.toPx(size, density)),
+                CornerRounding(bottomStart.toPx(size, density)),
+                CornerRounding(topStart.toPx(size, density)),
+                CornerRounding(topEnd.toPx(size, density)),
+            )
+    )
+
+private fun AbsoluteCutCornerShape.toRoundedPolygon(size: Size, density: Density): RoundedPolygon {
+    val topEndPx = topEnd.toPx(size, density)
+    val bottomEndPx = bottomEnd.toPx(size, density)
+    val bottomStartPx = bottomStart.toPx(size, density)
+    val topStartPx = topStart.toPx(size, density)
+
+    val width = size.width
+    val height = size.height
+
+    return RoundedPolygon(
+        floatArrayOf(
+            width - bottomEndPx,
+            height,
+            bottomStartPx,
+            height,
+            0f,
+            height - bottomStartPx,
+            0f,
+            topStartPx,
+            topStartPx,
+            0f,
+            width - topEndPx,
+            0f,
+            width,
+            topEndPx,
+            width,
+            height - bottomEndPx,
+        ),
+    )
+}
+
+/**
+ * Returns an implementation of Shape, animating a Morph based on an observable progress between 0.0
+ * and 1.0.
+ *
+ * The Morph supports animated between different CornerBasedShapes such as a CutCorner to
+ * RoundedCorner. Returns a simple non animated shape if `toRoundedPolygonOrNull` does not support
+ * the shape.
+ *
+ * When animating between two RoundedCornerShape, `rememberAnimatedRoundedCornerShape` should be
+ * used.
+ *
+ * Size and density must be known at this point since Corners may be specified in either percentage
+ * or dp, and cannot be correctly scaled as either a RoundedPolygon or a Morph.
+ */
+@Composable
+internal fun rememberAnimatedCornerBasedShape(
+    shape: CornerBasedShape,
+    pressedShape: CornerBasedShape,
+    progress: State<Float>
+): Shape {
+    return remember(shape, pressedShape, progress) {
+        AnimatedMorphShape(shape, pressedShape) { progress.value }
+    }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
index 6b646f3..b000fde 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
@@ -55,6 +55,8 @@
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.material3.tokens.ChildButtonTokens
@@ -472,17 +474,34 @@
         modifier = modifier.buttonSizeModifier(),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
-        secondaryLabel = secondaryLabel,
+        secondaryLabelContent =
+            provideNullableScopeContent(
+                contentColor = colors.secondaryContentColor(enabled),
+                textStyle = FilledButtonTokens.SecondaryLabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 2,
+                textAlign = TextAlign.Start,
+                content = secondaryLabel
+            ),
         icon = icon,
         enabled = enabled,
         shape = shape,
         labelFont = FilledButtonTokens.LabelFont.value,
-        secondaryLabelFont = FilledButtonTokens.SecondaryLabelFont.value,
         colors = colors,
         border = border,
         contentPadding = contentPadding,
         interactionSource = interactionSource,
-        label = label
+        labelContent =
+            provideScopeContent(
+                contentColor = colors.contentColor(enabled),
+                textStyle = FilledButtonTokens.LabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 3,
+                textAlign =
+                    if (icon != null || secondaryLabel != null) TextAlign.Start
+                    else TextAlign.Center,
+                content = label
+            )
     )
 
 /**
@@ -570,17 +589,34 @@
         modifier = modifier.buttonSizeModifier(),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
-        secondaryLabel = secondaryLabel,
+        secondaryLabelContent =
+            provideNullableScopeContent(
+                contentColor = colors.secondaryContentColor(enabled),
+                textStyle = FilledButtonTokens.SecondaryLabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 2,
+                textAlign = TextAlign.Start,
+                content = secondaryLabel
+            ),
         icon = icon,
         enabled = enabled,
         shape = shape,
         labelFont = FilledTonalButtonTokens.LabelFont.value,
-        secondaryLabelFont = FilledTonalButtonTokens.SecondaryLabelFont.value,
         colors = colors,
         border = border,
         contentPadding = contentPadding,
         interactionSource = interactionSource,
-        label = label
+        labelContent =
+            provideScopeContent(
+                contentColor = colors.contentColor(enabled),
+                textStyle = FilledButtonTokens.LabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 3,
+                textAlign =
+                    if (icon != null || secondaryLabel != null) TextAlign.Start
+                    else TextAlign.Center,
+                content = label
+            )
     )
 
 /**
@@ -663,17 +699,34 @@
         modifier = modifier.buttonSizeModifier(),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
-        secondaryLabel = secondaryLabel,
+        secondaryLabelContent =
+            provideNullableScopeContent(
+                contentColor = colors.secondaryContentColor(enabled),
+                textStyle = FilledButtonTokens.SecondaryLabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 2,
+                textAlign = TextAlign.Start,
+                content = secondaryLabel
+            ),
         icon = icon,
         enabled = enabled,
         shape = shape,
         labelFont = OutlinedButtonTokens.LabelFont.value,
-        secondaryLabelFont = OutlinedButtonTokens.SecondaryLabelFont.value,
         colors = colors,
         border = border,
         contentPadding = contentPadding,
         interactionSource = interactionSource,
-        label = label
+        labelContent =
+            provideScopeContent(
+                contentColor = colors.contentColor(enabled),
+                textStyle = FilledButtonTokens.LabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 3,
+                textAlign =
+                    if (icon != null || secondaryLabel != null) TextAlign.Start
+                    else TextAlign.Center,
+                content = label
+            )
     )
 
 /**
@@ -756,17 +809,34 @@
         modifier = modifier.buttonSizeModifier(),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
-        secondaryLabel = secondaryLabel,
+        secondaryLabelContent =
+            provideNullableScopeContent(
+                contentColor = colors.secondaryContentColor(enabled),
+                textStyle = FilledButtonTokens.SecondaryLabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 2,
+                textAlign = TextAlign.Start,
+                content = secondaryLabel
+            ),
         icon = icon,
         enabled = enabled,
         shape = shape,
         labelFont = ChildButtonTokens.LabelFont.value,
-        secondaryLabelFont = ChildButtonTokens.SecondaryLabelFont.value,
         colors = colors,
         border = border,
         contentPadding = contentPadding,
         interactionSource = interactionSource,
-        label = label
+        labelContent =
+            provideScopeContent(
+                contentColor = colors.contentColor(enabled),
+                textStyle = FilledButtonTokens.LabelFont.value,
+                overflow = TextOverflow.Ellipsis,
+                maxLines = 3,
+                textAlign =
+                    if (icon != null || secondaryLabel != null) TextAlign.Start
+                    else TextAlign.Center,
+                content = label
+            )
     )
 
 /**
@@ -826,8 +896,9 @@
  * @param onLongClick Called when this button is long clicked (long-pressed). When this callback is
  *   set, [onLongClickLabel] should be set as well.
  * @param onLongClickLabel Semantic / accessibility label for the [onLongClick] action.
- * @param label A slot for providing the button's main label. The contents are expected to be text
- *   which is "start" aligned if there is an icon preset and "center" aligned if not.
+ * @param label A slot for providing the button's main label. The contents are expected to be a
+ *   single line of text which is "start" aligned if there is an icon preset and "center" aligned if
+ *   not.
  * @param icon A slot for providing the button's icon. The contents are expected to be a
  *   horizontally and vertically aligned icon of size [ButtonDefaults.SmallIconSize] when used with
  *   a label or [ButtonDefaults.IconSize] when used as the only content in the button.
@@ -872,17 +943,24 @@
                     .padding(ButtonDefaults.CompactButtonTapTargetPadding),
             onLongClick = onLongClick,
             onLongClickLabel = onLongClickLabel,
-            secondaryLabel = null,
+            secondaryLabelContent = null,
             icon = icon,
             enabled = enabled,
             shape = shape,
             labelFont = CompactButtonTokens.LabelFont.value,
-            secondaryLabelFont = null,
             colors = colors,
             border = border,
             contentPadding = contentPadding,
             interactionSource = interactionSource,
-            label = label
+            labelContent =
+                provideScopeContent(
+                    contentColor = colors.contentColor(enabled),
+                    textStyle = CompactButtonTokens.LabelFont.value,
+                    overflow = TextOverflow.Ellipsis,
+                    maxLines = 1,
+                    textAlign = if (icon != null) TextAlign.Start else TextAlign.Center,
+                    label
+                )
         )
     } else {
         // Icon only compact buttons have their own layout with a specific width and center aligned
@@ -1662,17 +1740,16 @@
     modifier: Modifier,
     onLongClick: (() -> Unit)?,
     onLongClickLabel: String?,
-    secondaryLabel: (@Composable RowScope.() -> Unit)?,
+    secondaryLabelContent: (@Composable RowScope.() -> Unit)?,
     icon: (@Composable BoxScope.() -> Unit)?,
     enabled: Boolean,
     shape: Shape,
     labelFont: TextStyle,
-    secondaryLabelFont: TextStyle?,
     colors: ButtonColors,
     border: BorderStroke?,
     contentPadding: PaddingValues,
     interactionSource: MutableInteractionSource?,
-    label: @Composable RowScope.() -> Unit
+    labelContent: @Composable RowScope.() -> Unit
 ) {
     ButtonImpl(
         onClick = onClick,
@@ -1701,17 +1778,10 @@
                 Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing))
             }
             Column {
-                Row(content = provideScopeContent(colors.contentColor(enabled), labelFont, label))
-                if (secondaryLabel != null && secondaryLabelFont != null) {
+                Row(content = labelContent)
+                if (secondaryLabelContent != null) {
                     Spacer(modifier = Modifier.size(2.dp))
-                    Row(
-                        content =
-                            provideScopeContent(
-                                colors.secondaryContentColor(enabled),
-                                secondaryLabelFont,
-                                secondaryLabel
-                            )
-                    )
+                    Row(content = secondaryLabelContent)
                 }
             }
         }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt
index d4a95c2..119ca2e 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/CheckboxButton.kt
@@ -308,6 +308,7 @@
 
         val splitBackground = colors.splitContainerColor(enabled, checked).value
         Box(
+            contentAlignment = Alignment.Center,
             modifier =
                 Modifier.toggleable(
                         enabled = enabled,
@@ -325,8 +326,7 @@
                             drawContent()
                         }
                     }
-                    .align(Alignment.CenterVertically)
-                    .width(SPLIT_WIDTH)
+                    .defaultMinSize(minWidth = SPLIT_MIN_WIDTH)
                     .wrapContentHeight(align = Alignment.CenterVertically)
                     .padding(contentPadding)
         ) {
@@ -1468,7 +1468,7 @@
 private val BOX_RADIUS = 2.dp
 private val BOX_SIZE = 18.dp
 
-private val SPLIT_WIDTH = 52.dp
+private val SPLIT_MIN_WIDTH = 48.dp
 private val SPLIT_SECTIONS_SHAPE = ShapeTokens.CornerExtraSmall
 
 private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/EdgeButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/EdgeButton.kt
index c95871c..46550fb 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/EdgeButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/EdgeButton.kt
@@ -58,6 +58,8 @@
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
@@ -97,6 +99,10 @@
  * Example of an [EdgeButton]:
  *
  * @sample androidx.wear.compose.material3.samples.EdgeButtonSample
+ *
+ * For a sample integrating with ScalingLazyColumn, see:
+ *
+ * @sample androidx.wear.compose.material3.samples.EdgeButtonListSample
  * @param onClick Will be called when the user clicks the button
  * @param modifier Modifier to be applied to the button. When animating the button to appear/
  *   disappear from the screen, a Modifier.height can be used to change the height of the component,
@@ -145,7 +151,7 @@
             }
         }
 
-    val containerShapeHelper = ShapeHelper(LocalDensity.current)
+    val containerShapeHelper = remember { ShapeHelper(density) }
     val shape = EdgeButtonShape(containerShapeHelper)
 
     val containerFadeStartPx = with(LocalDensity.current) { CONTAINER_FADE_START_DP.toPx() }
@@ -155,7 +161,7 @@
         horizontalArrangement = Arrangement.Center,
         modifier =
             modifier
-                .padding(bottom = BOTTOM_PADDING)
+                .padding(vertical = VERTICAL_PADDING)
                 .layout { measurable, constraints ->
                     // Compute the actual size of the button, and save it for later.
                     // We take the max width available, and the height is determined by the
@@ -233,6 +239,9 @@
             provideScopeContent(
                 colors.contentColor(enabled = enabled),
                 MaterialTheme.typography.labelMedium,
+                TextOverflow.Ellipsis,
+                maxLines = 3, // TODO(): Change according to buttonHeight
+                TextAlign.Center,
                 content
             )
     )
@@ -264,7 +273,7 @@
 internal class ShapeHelper(private val density: Density) {
     private val extraSmallHeightPx =
         with(density) { ButtonDefaults.EdgeButtonHeightExtraSmall.toPx() }
-    private val bottomPaddingPx = with(density) { BOTTOM_PADDING.toPx() }
+    private val bottomPaddingPx = with(density) { VERTICAL_PADDING.toPx() }
     private val extraSmallEllipsisHeightPx = with(density) { EXTRA_SMALL_ELLIPSIS_HEIGHT.toPx() }
     private val targetSidePadding = with(density) { TARGET_SIDE_PADDING.toPx() }
 
@@ -439,5 +448,5 @@
 // straight line parallel to the x axis.
 private val TARGET_SIDE_PADDING = 20.dp
 
-// Padding between the Edge Button and the bottom of the screen
-private val BOTTOM_PADDING = 3.dp
+// Padding around the Edge Button on it's top and bottom.
+private val VERTICAL_PADDING = 3.dp
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
index fa66cf9..e49e8a5 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
@@ -18,8 +18,12 @@
 
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
@@ -34,6 +38,7 @@
 import androidx.wear.compose.material3.tokens.IconButtonTokens
 import androidx.wear.compose.material3.tokens.IconToggleButtonTokens
 import androidx.wear.compose.material3.tokens.OutlinedIconButtonTokens
+import androidx.wear.compose.material3.tokens.ShapeTokens
 
 /**
  * Wear Material [IconButton] is a circular, icon-only button with transparent background and no
@@ -59,6 +64,10 @@
  * Example of an [IconButton] with onLongClick:
  *
  * @sample androidx.wear.compose.material3.samples.IconButtonWithOnLongClickSample
+ *
+ * Example of an [IconButton] with shape animation of rounded corners on press:
+ *
+ * @sample androidx.wear.compose.material3.samples.IconButtonWithCornerAnimationSample
  * @param onClick Will be called when the user clicks the button.
  * @param modifier Modifier to be applied to the button.
  * @param onLongClick Called when this button is long clicked (long-pressed). When this callback is
@@ -94,7 +103,7 @@
 ) =
     RoundButton(
         onClick = onClick,
-        modifier.minimumInteractiveComponentSize(),
+        modifier.minimumInteractiveComponentSize().size(IconButtonDefaults.DefaultButtonSize),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
         enabled = enabled,
@@ -102,7 +111,6 @@
         interactionSource = interactionSource,
         shape = shape,
         border = { border },
-        buttonSize = IconButtonDefaults.DefaultButtonSize,
         ripple = ripple(),
         content = provideScopeContent(colors.contentColor(enabled = enabled), content)
     )
@@ -163,7 +171,8 @@
 ) =
     RoundButton(
         onClick = onClick,
-        modifier = modifier.minimumInteractiveComponentSize(),
+        modifier =
+            modifier.minimumInteractiveComponentSize().size(IconButtonDefaults.DefaultButtonSize),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
         enabled = enabled,
@@ -171,7 +180,6 @@
         interactionSource = interactionSource,
         shape = shape,
         border = { border },
-        buttonSize = IconButtonDefaults.DefaultButtonSize,
         ripple = ripple(),
         content = provideScopeContent(colors.contentColor(enabled = enabled), content)
     )
@@ -232,7 +240,8 @@
 ) =
     RoundButton(
         onClick = onClick,
-        modifier = modifier.minimumInteractiveComponentSize(),
+        modifier =
+            modifier.minimumInteractiveComponentSize().size(IconButtonDefaults.DefaultButtonSize),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
         enabled = enabled,
@@ -240,7 +249,6 @@
         interactionSource = interactionSource,
         shape = shape,
         border = { border },
-        buttonSize = IconButtonDefaults.DefaultButtonSize,
         ripple = ripple(),
         content = provideScopeContent(colors.contentColor(enabled = enabled), content)
     )
@@ -305,7 +313,8 @@
 ) =
     RoundButton(
         onClick = onClick,
-        modifier = modifier.minimumInteractiveComponentSize(),
+        modifier =
+            modifier.minimumInteractiveComponentSize().size(IconButtonDefaults.DefaultButtonSize),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
         enabled = enabled,
@@ -313,7 +322,6 @@
         interactionSource = interactionSource,
         shape = shape,
         border = { border },
-        buttonSize = IconButtonDefaults.DefaultButtonSize,
         ripple = ripple(),
         content = provideScopeContent(colors.contentColor(enabled = enabled), content)
     )
@@ -387,8 +395,26 @@
 /** Contains the default values used by [IconButton]. */
 object IconButtonDefaults {
     /** Recommended [Shape] for [IconButton]. */
-    val shape: Shape
-        @Composable get() = IconButtonTokens.ContainerShape.value
+    val shape: RoundedCornerShape
+        @Composable get() = ShapeTokens.CornerFull
+
+    /** Recommended pressed [Shape] for [IconButton]. */
+    val pressedShape: CornerBasedShape
+        @Composable get() = MaterialTheme.shapes.small
+
+    /**
+     * Creates a [Shape] with a animation between two CornerBasedShapes.
+     *
+     * @param interactionSource the interaction source applied to the Button.
+     * @param shape The normal shape of the IconButton.
+     * @param pressedShape The pressed shape of the IconButton.
+     */
+    @Composable
+    fun animatedShape(
+        interactionSource: InteractionSource,
+        shape: CornerBasedShape = IconButtonDefaults.shape,
+        pressedShape: CornerBasedShape = IconButtonDefaults.pressedShape,
+    ) = animatedPressedButtonShape(interactionSource, shape, pressedShape)
 
     /**
      * Recommended icon size for a given icon button size.
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
index cdfab19..7290311 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
@@ -34,12 +34,11 @@
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
-import androidx.wear.compose.material3.ProgressIndicatorDefaults.StartAngle
-import androidx.wear.compose.material3.ProgressIndicatorDefaults.StrokeWidth
 import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
 import androidx.wear.compose.materialcore.toRadians
 import kotlin.math.asin
 import kotlin.math.cos
+import kotlin.math.max
 import kotlin.math.min
 import kotlin.math.sin
 
@@ -59,6 +58,10 @@
  *
  * @sample androidx.wear.compose.material3.samples.MediaButtonProgressIndicatorSample
  *
+ * Example of a [CircularProgressIndicator] with small progress values:
+ *
+ * @sample androidx.wear.compose.material3.samples.SmallValuesProgressIndicatorSample
+ *
  * Progress indicators express the proportion of completion of an ongoing task.
  *
  * @param progress The progress of this progress indicator where 0.0 represents no progress and 1.0
@@ -80,10 +83,10 @@
 fun CircularProgressIndicator(
     progress: () -> Float,
     modifier: Modifier = Modifier,
-    startAngle: Float = StartAngle,
+    startAngle: Float = ProgressIndicatorDefaults.StartAngle,
     endAngle: Float = startAngle,
     colors: ProgressIndicatorColors = ProgressIndicatorDefaults.colors(),
-    strokeWidth: Dp = StrokeWidth,
+    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth,
     gapSize: Dp = ProgressIndicatorDefaults.gapSize(strokeWidth),
 ) {
     val coercedProgress = { progress().coerceIn(0f, 1f) }
@@ -96,7 +99,7 @@
             .focusable()
             .drawWithCache {
                 val fullSweep = 360f - ((startAngle - endAngle) % 360 + 360) % 360
-                val progressSweep = fullSweep * coercedProgress()
+                var progressSweep = fullSweep * coercedProgress()
                 val stroke = Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Round)
                 val minSize = min(size.height, size.width)
                 // Sweep angle between two progress indicator segments.
@@ -104,6 +107,10 @@
                     asin((stroke.width + gapSize.toPx()) / (minSize - stroke.width)).toDegrees() *
                         2f
 
+                if (progressSweep > 0) {
+                    progressSweep = max(progressSweep, gapSweep)
+                }
+
                 onDrawWithContent {
                     // Draw an indicator.
                     drawIndicatorSegment(
@@ -255,14 +262,14 @@
  *
  * If indicator gets too small, the circle that proportionally scales down is drawn instead.
  */
-private fun DrawScope.drawIndicatorSegment(
+internal fun DrawScope.drawIndicatorSegment(
     startAngle: Float,
     sweep: Float,
     gapSweep: Float,
     brush: Brush,
     stroke: Stroke
 ) {
-    if (sweep < gapSweep) {
+    if (sweep <= gapSweep) {
         // Draw a small indicator.
         val angle = (startAngle + sweep / 2f).toRadians()
         val radius = size.minDimension / 2 - stroke.width / 2
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt
index ed53ff9..a176a12 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Providers.kt
@@ -38,6 +38,25 @@
 }
 
 internal fun <T> provideScopeContent(
+    contentColor: Color,
+    textStyle: TextStyle,
+    overflow: TextOverflow,
+    maxLines: Int,
+    textAlign: TextAlign,
+    content: (@Composable T.() -> Unit)
+): (@Composable T.() -> Unit) = {
+    CompositionLocalProvider(
+        LocalContentColor provides contentColor,
+        LocalTextStyle provides textStyle,
+        LocalTextOverflow provides overflow,
+        LocalTextMaxLines provides maxLines,
+        LocalTextAlign provides textAlign
+    ) {
+        content()
+    }
+}
+
+internal fun <T> provideScopeContent(
     contentColor: State<Color>,
     textStyle: TextStyle,
     content: (@Composable T.() -> Unit)
@@ -147,3 +166,25 @@
             }
         }
     }
+
+internal fun <T> provideNullableScopeContent(
+    contentColor: Color,
+    textStyle: TextStyle,
+    overflow: TextOverflow,
+    maxLines: Int,
+    textAlign: TextAlign,
+    content: (@Composable T.() -> Unit)?
+): (@Composable T.() -> Unit)? =
+    content?.let {
+        {
+            CompositionLocalProvider(
+                LocalContentColor provides contentColor,
+                LocalTextStyle provides textStyle,
+                LocalTextOverflow provides overflow,
+                LocalTextMaxLines provides maxLines,
+                LocalTextAlign provides textAlign,
+            ) {
+                content()
+            }
+        }
+    }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt
index ddf79d5..858243c 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RadioButton.kt
@@ -327,6 +327,7 @@
         val splitContainerColor =
             colors.splitContainerColor(enabled = enabled, selected = selected).value
         Box(
+            contentAlignment = Alignment.Center,
             modifier =
                 Modifier.selectable(
                         enabled = enabled,
@@ -344,8 +345,7 @@
                             drawContent()
                         }
                     }
-                    .align(Alignment.CenterVertically)
-                    .width(SPLIT_WIDTH)
+                    .defaultMinSize(minWidth = SPLIT_MIN_WIDTH)
                     .wrapContentHeight(align = Alignment.CenterVertically)
                     .padding(contentPadding)
                     .semantics {
@@ -1357,7 +1357,7 @@
 private val SELECTION_CONTROL_SPACING = 6.dp
 private val ICON_SPACING = 6.dp
 private val MIN_HEIGHT = 52.dp
-private val SPLIT_WIDTH = 52.dp
+private val SPLIT_MIN_WIDTH = 48.dp
 private val CONTROL_WIDTH = 24.dp
 private val CONTROL_HEIGHT = 24.dp
 private val SPLIT_SECTIONS_SHAPE = ShapeTokens.CornerExtraSmall
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
index 6330bd3..8ac3933 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/RoundButton.kt
@@ -16,17 +16,24 @@
 
 package androidx.wear.compose.material3
 
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Indication
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -35,7 +42,6 @@
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.unit.Dp
 
 /**
  * This is a copy of RoundButton from materialcore, with additional onLongClick callback and usage
@@ -53,7 +59,6 @@
     interactionSource: MutableInteractionSource?,
     shape: Shape,
     border: @Composable (enabled: Boolean) -> BorderStroke?,
-    buttonSize: Dp,
     ripple: Indication,
     content: @Composable BoxScope.() -> Unit,
 ) {
@@ -63,7 +68,6 @@
         modifier =
             modifier
                 .semantics { role = Role.Button }
-                .size(buttonSize)
                 .clip(shape) // Clip for the touch area (e.g. for Ripple).
                 .combinedClickable(
                     onClick = onClick,
@@ -81,3 +85,41 @@
         content = content
     )
 }
+
+/**
+ * Returns a Shape that will internally animate between the normal shape and pressedShape as the
+ * button is pressed.
+ *
+ * Size and density must be known at this point since Corners may be specified in either percentage
+ * or dp, and cannot be correctly scaled as either a RoundedPolygon or a Morph.
+ */
+@Composable
+internal fun animatedPressedButtonShape(
+    interactionSource: InteractionSource,
+    shape: CornerBasedShape,
+    pressedShape: CornerBasedShape,
+    onPressAnimationSpec: FiniteAnimationSpec<Float> = MotionScheme.bouncyFastSpec(),
+    onReleaseAnimationSpec: FiniteAnimationSpec<Float> = MotionScheme.flatDefaultSpec(),
+): Shape {
+    val pressed = interactionSource.collectIsPressedAsState()
+
+    val transition = updateTransition(pressed.value, label = "Pressed State")
+    val progress: State<Float> =
+        transition.animateFloat(
+            label = "Pressed",
+            transitionSpec = {
+                when {
+                    false isTransitioningTo true -> onPressAnimationSpec
+                    else -> onReleaseAnimationSpec
+                }
+            }
+        ) { pressedTarget ->
+            if (pressedTarget) 1f else 0f
+        }
+
+    return when {
+        shape is RoundedCornerShape && pressedShape is RoundedCornerShape ->
+            rememberAnimatedRoundedCornerShape(shape, pressedShape, progress)
+        else -> rememberAnimatedCornerBasedShape(shape, pressedShape, progress)
+    }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
index 2c0e3d9..c88b35f 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
@@ -35,12 +35,21 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
-import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
-import androidx.wear.compose.foundation.OnFocusChange
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.wear.compose.foundation.ActiveFocusListener
 import androidx.wear.compose.foundation.ScrollInfoProvider
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
-import androidx.wear.compose.foundation.toScrollAwayInfoProvider
+import kotlin.math.roundToInt
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.launch
@@ -54,6 +63,9 @@
  * [ScreenScaffold] displays the [ScrollIndicator] at the center-end of the screen by default and
  * coordinates showing/hiding [TimeText] and [ScrollIndicator] according to [scrollState].
  *
+ * This version of [ScreenScaffold] has a special slot for a button at the bottom, that grows and
+ * shrinks to take the available space after the scrollable content.
+ *
  * Example of using AppScaffold and ScreenScaffold:
  *
  * @sample androidx.wear.compose.material3.samples.ScaffoldSample
@@ -66,6 +78,9 @@
  * @param scrollIndicator The [ScrollIndicator] to display on this screen, which is expected to be
  *   aligned to Center-End. It is recommended to use the Material3 [ScrollIndicator] which is
  *   provided by default. No scroll indicator is displayed if null is passed.
+ * @param bottomButton Optional slot for a Button (usually an [EdgeButton]) that takes the available
+ *   space below a scrolling list. It will scale up and fade in when the user scrolls to the end of
+ *   the list, and scale down and fade out as the user scrolls up.
  * @param content The body content for this screen.
  */
 @Composable
@@ -76,15 +91,27 @@
     scrollIndicator: (@Composable BoxScope.() -> Unit)? = {
         ScrollIndicator(scrollState, modifier = Modifier.align(Alignment.CenterEnd))
     },
+    bottomButton: (@Composable BoxScope.() -> Unit)? = null,
     content: @Composable BoxScope.() -> Unit,
 ) =
-    ScreenScaffold(
-        modifier,
-        timeText,
-        scrollState.toScrollAwayInfoProvider(),
-        scrollIndicator,
-        content
-    )
+    if (bottomButton != null) {
+        ScreenScaffold(
+            bottomButton,
+            ScrollInfoProvider(scrollState),
+            modifier,
+            timeText,
+            scrollIndicator,
+            content
+        )
+    } else {
+        ScreenScaffold(
+            modifier,
+            timeText,
+            ScrollInfoProvider(scrollState),
+            scrollIndicator,
+            content
+        )
+    }
 
 /**
  * [ScreenScaffold] is one of the Wear Material3 scaffold components.
@@ -95,6 +122,9 @@
  * [ScreenScaffold] displays the [ScrollIndicator] at the center-end of the screen by default and
  * coordinates showing/hiding [TimeText] and [ScrollIndicator] according to [scrollState].
  *
+ * This version of [ScreenScaffold] has a special slot for a button at the bottom, that grows and
+ * shrinks to take the available space after the scrollable content.
+ *
  * Example of using AppScaffold and ScreenScaffold:
  *
  * @sample androidx.wear.compose.material3.samples.ScaffoldSample
@@ -107,25 +137,93 @@
  * @param scrollIndicator The [ScrollIndicator] to display on this screen, which is expected to be
  *   aligned to Center-End. It is recommended to use the Material3 [ScrollIndicator] which is
  *   provided by default. No scroll indicator is displayed if null is passed.
+ * @param bottomButton Optional slot for a Button (usually an [EdgeButton]) that takes the available
+ *   space below a scrolling list. It will scale up and fade in when the user scrolls to the end of
+ *   the list, and scale down and fade out as the user scrolls up.
  * @param content The body content for this screen.
  */
 @Composable
 fun ScreenScaffold(
     scrollState: LazyListState,
     modifier: Modifier = Modifier,
+    scrollIndicator: @Composable BoxScope.() -> Unit = {
+        ScrollIndicator(scrollState, modifier = Modifier.align(Alignment.CenterEnd))
+    },
+    timeText: (@Composable () -> Unit)? = null,
+    bottomButton: (@Composable BoxScope.() -> Unit)? = null,
+    content: @Composable BoxScope.() -> Unit,
+) =
+    if (bottomButton != null) {
+        ScreenScaffold(
+            bottomButton,
+            ScrollInfoProvider(scrollState),
+            modifier,
+            timeText,
+            scrollIndicator,
+            content
+        )
+    } else {
+        ScreenScaffold(
+            modifier,
+            timeText,
+            ScrollInfoProvider(scrollState),
+            scrollIndicator,
+            content
+        )
+    }
+
+/**
+ * [ScreenScaffold] is one of the Wear Material3 scaffold components.
+ *
+ * The scaffold components [AppScaffold] and [ScreenScaffold] lay out the structure of a screen and
+ * coordinate transitions of the [ScrollIndicator] and [TimeText] components.
+ *
+ * [ScreenScaffold] displays the [ScrollIndicator] at the center-end of the screen by default and
+ * coordinates showing/hiding [TimeText] and [ScrollIndicator] according to [scrollState].
+ *
+ * This version of [ScreenScaffold] has a special slot for a button at the bottom, that grows and
+ * shrinks to take the available space after the scrollable content.
+ *
+ * Example of using AppScaffold and ScreenScaffold:
+ *
+ * @sample androidx.wear.compose.material3.samples.ScaffoldSample
+ * @param scrollState The scroll state for a Column, used to drive screen transitions such as
+ *   [TimeText] scroll away and showing/hiding [ScrollIndicator].
+ * @param bottomButton Optional slot for a Button (usually an [EdgeButton]) that takes the available
+ *   space below a scrolling list. It will scale up and fade in when the user scrolls to the end of
+ *   the list, and scale down and fade out as the user scrolls up.
+ * @param bottomButtonHeight the maximum height of the space taken by the bottom button.
+ * @param modifier The modifier for the screen scaffold.
+ * @param timeText Time text (both time and potentially status message) for this screen, if
+ *   different to the time text at the [AppScaffold] level. When null, the time text from the
+ *   [AppScaffold] is displayed for this screen.
+ * @param scrollIndicator The [ScrollIndicator] to display on this screen, which is expected to be
+ *   aligned to Center-End. It is recommended to use the Material3 [ScrollIndicator] which is
+ *   provided by default. No scroll indicator is displayed if null is passed.
+ * @param content The body content for this screen.
+ */
+@Composable
+fun ScreenScaffold(
+    scrollState: ScrollState,
+    bottomButton: @Composable BoxScope.() -> Unit,
+    bottomButtonHeight: Dp,
+    modifier: Modifier = Modifier,
     timeText: (@Composable () -> Unit)? = null,
     scrollIndicator: (@Composable BoxScope.() -> Unit)? = {
         ScrollIndicator(scrollState, modifier = Modifier.align(Alignment.CenterEnd))
     },
     content: @Composable BoxScope.() -> Unit,
-) =
+) {
+    val bottomButtonHeightPx = with(LocalDensity.current) { bottomButtonHeight.toPx() }
     ScreenScaffold(
+        bottomButton,
+        ScrollInfoProvider(scrollState, bottomButtonHeightPx),
         modifier,
         timeText,
-        scrollState.toScrollAwayInfoProvider(),
         scrollIndicator,
         content
     )
+}
 
 /**
  * [ScreenScaffold] is one of the Wear Material3 scaffold components.
@@ -159,13 +257,64 @@
         ScrollIndicator(scrollState, modifier = Modifier.align(Alignment.CenterEnd))
     },
     content: @Composable BoxScope.() -> Unit,
+) = ScreenScaffold(modifier, timeText, ScrollInfoProvider(scrollState), scrollIndicator, content)
+
+/**
+ * [ScreenScaffold] is one of the Wear Material3 scaffold components.
+ *
+ * The scaffold components [AppScaffold] and [ScreenScaffold] lay out the structure of a screen and
+ * coordinate transitions of the [ScrollIndicator] and [TimeText] components.
+ *
+ * [ScreenScaffold] displays the [ScrollIndicator] at the center-end of the screen by default and
+ * coordinates showing/hiding [TimeText], [ScrollIndicator] and the bottom button according to a
+ * [scrollInfoProvider].
+ *
+ * This version of [ScreenScaffold] has a special slot for a button at the bottom, that grows and
+ * shrinks to take the available space after the scrollable content. In this overload, both
+ * bottomButton and scrollInfoProvider must be specified.
+ *
+ * Example of using AppScaffold and ScreenScaffold:
+ *
+ * @sample androidx.wear.compose.material3.samples.ScaffoldSample
+ * @param bottomButton slot for a Button (usually an [EdgeButton]) that takes the available space
+ *   below a scrolling list. It will scale up and fade in when the user scrolls to the end of the
+ *   list, and scale down and fade out as the user scrolls up.
+ * @param scrollInfoProvider Provider for scroll information used to scroll away screen elements
+ *   such as [TimeText] and coordinate showing/hiding the [ScrollIndicator], this needs to be a
+ *   [ScrollInfoProvider].
+ * @param modifier The modifier for the screen scaffold.
+ * @param timeText Time text (both time and potentially status message) for this screen, if
+ *   different to the time text at the [AppScaffold] level. When null, the time text from the
+ *   [AppScaffold] is displayed for this screen.
+ * @param scrollIndicator The [ScrollIndicator] to display on this screen, which is expected to be
+ *   aligned to Center-End. It is recommended to use the Material3 [ScrollIndicator] which is
+ *   provided by default. No scroll indicator is displayed if null is passed.
+ * @param content The body content for this screen.
+ */
+@Composable
+fun ScreenScaffold(
+    bottomButton: @Composable BoxScope.() -> Unit,
+    scrollInfoProvider: ScrollInfoProvider,
+    modifier: Modifier = Modifier,
+    timeText: (@Composable () -> Unit)? = null,
+    scrollIndicator: (@Composable BoxScope.() -> Unit)? = null,
+    content: @Composable BoxScope.() -> Unit,
 ) =
     ScreenScaffold(
         modifier,
         timeText,
-        scrollState.toScrollAwayInfoProvider(),
+        scrollInfoProvider,
         scrollIndicator,
-        content
+        content = {
+            content()
+            Box(
+                Modifier.align(Alignment.BottomCenter).dynamicHeight {
+                    scrollInfoProvider.lastItemOffset.coerceAtLeast(0f)
+                },
+                contentAlignment = Alignment.BottomCenter,
+                content = bottomButton
+            )
+        }
     )
 
 /**
@@ -191,7 +340,6 @@
  *   provided by default. No scroll indicator is displayed if null is passed.
  * @param content The body content for this screen.
  */
-@OptIn(ExperimentalWearFoundationApi::class)
 @Composable
 fun ScreenScaffold(
     modifier: Modifier = Modifier,
@@ -206,7 +354,7 @@
     key(scrollInfoProvider) {
         DisposableEffect(key) { onDispose { scaffoldState.removeScreen(key) } }
 
-        OnFocusChange { focused ->
+        ActiveFocusListener { focused ->
             if (focused) {
                 scaffoldState.addScreen(key, timeText, scrollInfoProvider)
             } else {
@@ -257,8 +405,60 @@
                     }
             }
         }
-        Box(modifier = Modifier.fillMaxSize().graphicsLayer { alpha = alphaValue.floatValue }) {
-            scrollIndicator()
+        Box(
+            modifier = Modifier.fillMaxSize().graphicsLayer { alpha = alphaValue.floatValue },
+            content = scrollIndicator
+        )
+    }
+}
+
+// Sets the height that will be used down the line, using a state as parameter, to avoid
+// recompositions when the height changes.
+internal fun Modifier.dynamicHeight(heightState: () -> Float) =
+    this.then(DynamicHeightElement(heightState))
+
+// Following classes 'inspired' by 'WrapContentElement' / 'WrapContentNode'
+private class DynamicHeightElement(val heightState: () -> Float) :
+    ModifierNodeElement<DynamicHeightNode>() {
+    override fun create(): DynamicHeightNode = DynamicHeightNode(heightState)
+
+    override fun update(node: DynamicHeightNode) {
+        node.heightState = heightState
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "MyHeightElement"
+    }
+
+    override fun equals(other: Any?) =
+        other is DynamicHeightElement && heightState === other.heightState
+
+    override fun hashCode() = heightState.hashCode()
+}
+
+private class DynamicHeightNode(var heightState: () -> Float) :
+    LayoutModifierNode, Modifier.Node() {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        // Similar to .fillMaxWidth().height(heightState.value) but we observe the state in the
+        // measurement pass, not on Composition.
+        val height = heightState().roundToInt()
+
+        val wrappedConstraints =
+            Constraints(constraints.maxWidth, constraints.maxWidth, height, height)
+        val placeable = measurable.measure(wrappedConstraints)
+        // Report that we take the full space, and BottomCenter align the content.
+        val wrapperWidth = constraints.maxWidth
+        val wrapperHeight = constraints.maxHeight
+        return layout(wrapperWidth, wrapperHeight) {
+            val position =
+                IntOffset(
+                    x = (wrapperWidth - placeable.width) / 2,
+                    y = wrapperHeight - placeable.height
+                )
+            placeable.place(position)
         }
     }
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScrollAway.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScrollAway.kt
index b561f85..7239dbe 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScrollAway.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScrollAway.kt
@@ -33,7 +33,6 @@
 import androidx.compose.ui.util.lerp
 import androidx.wear.compose.foundation.ScrollInfoProvider
 import androidx.wear.compose.foundation.lazy.ScalingLazyListState
-import androidx.wear.compose.foundation.toScrollAwayInfoProvider
 import androidx.wear.compose.material3.tokens.MotionTokens
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
@@ -48,8 +47,8 @@
  *
  * @sample androidx.wear.compose.material3.samples.ScrollAwaySample
  * @param scrollInfoProvider Used as the basis for the scroll-away implementation, based on the
- *   state of the scrollable container. See [toScrollAwayInfoProvider] extension methods for
- *   creating a ScrollAwayInfoProvider from common lists such as [ScalingLazyListState].
+ *   state of the scrollable container. See [ScrollInfoProvider] methods for creating a
+ *   ScrollInfoProvider from common lists such as [ScalingLazyListState].
  * @param screenStage Function that returns the screen stage of the active screen. Scrolled away
  *   items are shown when the screen is new, then scrolled away or hidden when scrolling, and
  *   finally shown again when idle.
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SegmentedCircularProgressIndicator.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SegmentedCircularProgressIndicator.kt
new file mode 100644
index 0000000..edd8780
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SegmentedCircularProgressIndicator.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.annotation.IntRange
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.unit.Dp
+import kotlin.math.asin
+import kotlin.math.floor
+import kotlin.math.min
+
+/**
+ * Material Design segmented circular progress indicator.
+ *
+ * A segmented variant of [CircularProgressIndicator] that is divided into equally sized segments.
+ *
+ * Example of [SegmentedCircularProgressIndicator] with progress value:
+ *
+ * @sample androidx.wear.compose.material3.samples.SegmentedProgressIndicatorSample
+ * @param segmentCount Number of equal segments that the progress indicator should be divided into.
+ *   Has to be a number equal or greater to 1.
+ * @param progress The progress of this progress indicator where 0.0 represents no progress and 1.0
+ *   represents completion. Values outside of this range are coerced into the range 0..1. The
+ *   progress is applied to the entire [SegmentedCircularProgressIndicator] across all segments.
+ * @param modifier Modifier to be applied to the SegmentedCircularProgressIndicator.
+ * @param startAngle The starting position of the progress arc, measured clockwise in degrees (0
+ *   to 360) from the 3 o'clock position. For example, 0 and 360 represent 3 o'clock, 90 and 180
+ *   represent 6 o'clock and 9 o'clock respectively. Default is 270 degrees
+ *   [ProgressIndicatorDefaults.StartAngle] (top of the screen).
+ * @param endAngle The ending position of the progress arc, measured clockwise in degrees (0 to 360)
+ *   from the 3 o'clock position. For example, 0 and 360 represent 3 o'clock, 90 and 180 represent 6
+ *   o'clock and 9 o'clock respectively. By default equal to [startAngle].
+ * @param colors [ProgressIndicatorColors] that will be used to resolve the indicator and track
+ *   color for this progress indicator in different states.
+ * @param strokeWidth The stroke width for the progress indicator.
+ * @param gapSize The size of the gap between segments (in Dp).
+ */
+@Composable
+fun SegmentedCircularProgressIndicator(
+    @IntRange(from = 1) segmentCount: Int,
+    progress: () -> Float,
+    modifier: Modifier = Modifier,
+    startAngle: Float = ProgressIndicatorDefaults.StartAngle,
+    endAngle: Float = startAngle,
+    colors: ProgressIndicatorColors = ProgressIndicatorDefaults.colors(),
+    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth,
+    gapSize: Dp = ProgressIndicatorDefaults.gapSize(strokeWidth),
+) =
+    SegmentedCircularProgressIndicatorImpl(
+        segmentParams = SegmentParams.Progress(progress),
+        modifier = modifier,
+        segmentCount = segmentCount,
+        startAngle = startAngle,
+        endAngle = endAngle,
+        colors = colors,
+        strokeWidth = strokeWidth,
+        gapSize = gapSize,
+    )
+
+/**
+ * Material Design segmented circular progress indicator.
+ *
+ * A segmented variant of [CircularProgressIndicator] that is divided into equally sized segments.
+ * This overload of [SegmentedCircularProgressIndicator] allows for each segment to be individually
+ * indicated as completed, such as for showing activity for intervals within a longer period
+ *
+ * Example of [SegmentedCircularProgressIndicator] where the segments are turned on/off:
+ *
+ * @sample androidx.wear.compose.material3.samples.SegmentedProgressIndicatorOnOffSample
+ * @param segmentCount Number of equal segments that the progress indicator should be divided into.
+ *   Has to be a number equal or greater to 1.
+ * @param completed A function that for each segment between 1..[segmentCount] returns true if this
+ *   segment has been completed, and false if this segment has not been completed.
+ * @param modifier Modifier to be applied to the SegmentedCircularProgressIndicator.
+ * @param startAngle The starting position of the progress arc, measured clockwise in degrees (0
+ *   to 360) from the 3 o'clock position. For example, 0 and 360 represent 3 o'clock, 90 and 180
+ *   represent 6 o'clock and 9 o'clock respectively. Default is 270 degrees
+ *   [ProgressIndicatorDefaults.StartAngle] (top of the screen).
+ * @param endAngle The ending position of the progress arc, measured clockwise in degrees (0 to 360)
+ *   from the 3 o'clock position. For example, 0 and 360 represent 3 o'clock, 90 and 180 represent 6
+ *   o'clock and 9 o'clock respectively. By default equal to [startAngle].
+ * @param colors [ProgressIndicatorColors] that will be used to resolve the indicator and track
+ *   color for this progress indicator in different states.
+ * @param strokeWidth The stroke width for the progress indicator.
+ * @param gapSize The size of the gap between segments (in Dp).
+ */
+@Composable
+fun SegmentedCircularProgressIndicator(
+    @IntRange(from = 1) segmentCount: Int,
+    completed: (segmentIndex: Int) -> Boolean,
+    modifier: Modifier = Modifier,
+    startAngle: Float = ProgressIndicatorDefaults.StartAngle,
+    endAngle: Float = startAngle,
+    colors: ProgressIndicatorColors = ProgressIndicatorDefaults.colors(),
+    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth,
+    gapSize: Dp = ProgressIndicatorDefaults.gapSize(strokeWidth),
+) =
+    SegmentedCircularProgressIndicatorImpl(
+        segmentParams = SegmentParams.Completed(completed),
+        modifier = modifier,
+        segmentCount = segmentCount,
+        startAngle = startAngle,
+        endAngle = endAngle,
+        colors = colors,
+        strokeWidth = strokeWidth,
+        gapSize = gapSize,
+    )
+
+@Composable
+private fun SegmentedCircularProgressIndicatorImpl(
+    segmentParams: SegmentParams,
+    @IntRange(from = 1) segmentCount: Int,
+    modifier: Modifier,
+    startAngle: Float,
+    endAngle: Float,
+    colors: ProgressIndicatorColors,
+    strokeWidth: Dp,
+    gapSize: Dp,
+) {
+    Spacer(
+        modifier
+            .clearAndSetSemantics {}
+            .fillMaxSize()
+            .drawWithCache {
+                onDrawWithContent {
+                    val fullSweep = 360f - ((startAngle - endAngle) % 360 + 360) % 360
+                    val stroke = Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Round)
+                    val minSize = min(size.height, size.width)
+                    // Sweep angle between two progress indicator segments.
+                    val gapSweep =
+                        asin((stroke.width + gapSize.toPx()) / (minSize - stroke.width))
+                            .toDegrees() * 2f
+                    val segmentSweepAngle =
+                        if (segmentCount > 1) fullSweep / segmentCount - gapSweep else fullSweep
+
+                    for (segment in 0 until segmentCount) {
+                        val segmentStartAngle =
+                            startAngle +
+                                fullSweep * segment / segmentCount +
+                                (if (segmentCount > 1) gapSweep / 2 else 0f)
+
+                        when (segmentParams) {
+                            is SegmentParams.Completed -> {
+                                val color =
+                                    if (segmentParams.completed(segment)) colors.indicatorBrush
+                                    else colors.trackBrush
+
+                                drawIndicatorSegment(
+                                    startAngle = segmentStartAngle,
+                                    sweep = segmentSweepAngle,
+                                    gapSweep = 0f,
+                                    brush = color,
+                                    stroke = stroke
+                                )
+                            }
+                            is SegmentParams.Progress -> {
+                                val progressInSegments =
+                                    segmentCount * segmentParams.progress().coerceIn(0f, 1f)
+
+                                if (segment >= floor(progressInSegments)) {
+                                    drawIndicatorSegment(
+                                        startAngle = segmentStartAngle,
+                                        sweep = segmentSweepAngle,
+                                        gapSweep = 0f, // Overlay, no gap
+                                        brush = colors.trackBrush,
+                                        stroke = stroke
+                                    )
+                                }
+                                if (segment < progressInSegments) {
+                                    val progressSweepAngle =
+                                        segmentSweepAngle *
+                                            (progressInSegments - segment).coerceAtMost(1f)
+
+                                    drawIndicatorSegment(
+                                        startAngle = segmentStartAngle,
+                                        sweep = progressSweepAngle,
+                                        gapSweep = 0f, // Overlay, no gap
+                                        brush = colors.indicatorBrush,
+                                        stroke = stroke
+                                    )
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+    )
+}
+
+private sealed interface SegmentParams {
+    data class Completed(val completed: (segmentIndex: Int) -> Boolean) : SegmentParams
+
+    data class Progress(val progress: () -> Float) : SegmentParams
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt
index eddc0b9..416c536 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwitchButton.kt
@@ -23,24 +23,35 @@
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.IntrinsicSize
 import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.selection.toggleable
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.State
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.drawWithCache
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
@@ -52,9 +63,11 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
 import androidx.wear.compose.material3.tokens.MotionTokens
+import androidx.wear.compose.material3.tokens.ShapeTokens
 import androidx.wear.compose.material3.tokens.SplitSwitchButtonTokens
 import androidx.wear.compose.material3.tokens.SwitchButtonTokens
 import androidx.wear.compose.materialcore.SelectionStage
@@ -253,21 +266,85 @@
     contentPadding: PaddingValues = SwitchButtonDefaults.ContentPadding,
     secondaryLabel: @Composable (RowScope.() -> Unit)? = null,
     label: @Composable RowScope.() -> Unit
-) =
-    androidx.wear.compose.materialcore.SplitToggleButton(
-        checked = checked,
-        onCheckedChange = onCheckedChange,
-        label =
-            provideScopeContent(
-                contentColor = colors.contentColor(enabled = enabled, checked = checked),
-                textStyle = SplitSwitchButtonTokens.LabelFont.value,
-                overflow = TextOverflow.Ellipsis,
-                maxLines = 3,
-                textAlign = TextAlign.Start,
-                content = label
-            ),
-        onClick = onContainerClick,
-        toggleControl = {
+) {
+    val containerColor = colors.containerColor(enabled, checked).value
+
+    Row(
+        verticalAlignment = Alignment.CenterVertically,
+        modifier =
+            modifier
+                .defaultMinSize(minHeight = MIN_HEIGHT)
+                .height(IntrinsicSize.Min)
+                .width(IntrinsicSize.Max)
+                .clip(shape = shape)
+    ) {
+        Row(
+            modifier =
+                Modifier.clickable(
+                        enabled = enabled,
+                        onClick = onContainerClick,
+                        indication = ripple(),
+                        interactionSource = containerInteractionSource,
+                        onClickLabel = containerClickLabel,
+                    )
+                    .semantics { role = Role.Button }
+                    .fillMaxHeight()
+                    .clip(SPLIT_SECTIONS_SHAPE)
+                    .background(containerColor)
+                    .padding(contentPadding)
+                    .weight(1.0f),
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            Labels(
+                label =
+                    provideScopeContent(
+                        contentColor = colors.contentColor(enabled = enabled, checked = checked),
+                        textStyle = SplitSwitchButtonTokens.LabelFont.value,
+                        overflow = TextOverflow.Ellipsis,
+                        maxLines = 3,
+                        textAlign = TextAlign.Start,
+                        content = label
+                    ),
+                secondaryLabel =
+                    provideNullableScopeContent(
+                        contentColor =
+                            colors.secondaryContentColor(enabled = enabled, checked = checked),
+                        textStyle = SplitSwitchButtonTokens.SecondaryLabelFont.value,
+                        overflow = TextOverflow.Ellipsis,
+                        maxLines = 2,
+                        textAlign = TextAlign.Start,
+                        content = secondaryLabel
+                    ),
+                spacerSize = SwitchButtonDefaults.LabelSpacerSize
+            )
+        }
+
+        Spacer(modifier = Modifier.size(2.dp))
+
+        val splitBackground = colors.splitContainerColor(enabled, checked).value
+        Box(
+            contentAlignment = Alignment.Center,
+            modifier =
+                Modifier.toggleable(
+                        enabled = enabled,
+                        value = checked,
+                        onValueChange = onCheckedChange,
+                        indication = ripple(),
+                        interactionSource = toggleInteractionSource
+                    )
+                    .fillMaxHeight()
+                    .clip(SPLIT_SECTIONS_SHAPE)
+                    .background(containerColor)
+                    .drawWithCache {
+                        onDrawWithContent {
+                            drawRect(color = splitBackground)
+                            drawContent()
+                        }
+                    }
+                    .defaultMinSize(minWidth = SPLIT_MIN_WIDTH)
+                    .wrapContentHeight(align = Alignment.CenterVertically)
+                    .padding(contentPadding)
+        ) {
             Switch(
                 checked = checked,
                 enabled = enabled,
@@ -290,33 +367,9 @@
                     colors.trackBorderColor(enabled = enabled, checked = checked)
                 }
             )
-        },
-        selectionControl = null,
-        modifier = modifier.defaultMinSize(minHeight = MIN_HEIGHT).height(IntrinsicSize.Min),
-        secondaryLabel =
-            provideNullableScopeContent(
-                contentColor = colors.secondaryContentColor(enabled = enabled, checked = checked),
-                textStyle = SplitSwitchButtonTokens.SecondaryLabelFont.value,
-                overflow = TextOverflow.Ellipsis,
-                maxLines = 2,
-                textAlign = TextAlign.Start,
-                content = secondaryLabel
-            ),
-        backgroundColor = { isEnabled, isChecked ->
-            colors.containerColor(enabled = isEnabled, checked = isChecked)
-        },
-        splitBackgroundColor = { isEnabled, isChecked ->
-            colors.splitContainerColor(enabled = isEnabled, checked = isChecked)
-        },
-        enabled = enabled,
-        checkedInteractionSource = toggleInteractionSource,
-        clickInteractionSource = containerInteractionSource,
-        onClickLabel = containerClickLabel,
-        contentPadding = contentPadding,
-        shape = shape,
-        labelSpacerSize = SwitchButtonDefaults.LabelSpacerSize,
-        ripple = ripple()
-    )
+        }
+    }
+}
 
 /** Contains the default values used by [SwitchButton]s and [SplitSwitchButton]s */
 object SwitchButtonDefaults {
@@ -1744,6 +1797,21 @@
     )
 }
 
+@Composable
+private fun RowScope.Labels(
+    label: @Composable RowScope.() -> Unit,
+    secondaryLabel: @Composable (RowScope.() -> Unit)?,
+    spacerSize: Dp
+) {
+    Column(modifier = Modifier.weight(1.0f)) {
+        Row(content = label)
+        if (secondaryLabel != null) {
+            Spacer(modifier = Modifier.size(spacerSize))
+            Row(content = secondaryLabel)
+        }
+    }
+}
+
 private val SWITCH_WIDTH = 32.dp
 private val SWITCH_OUTER_HEIGHT = 24.dp
 private val SWITCH_INNER_HEIGHT = 22.dp
@@ -1754,6 +1822,9 @@
 private val ICON_SPACING = 6.dp
 private val MIN_HEIGHT = 52.dp
 
+private val SPLIT_MIN_WIDTH = 48.dp
+private val SPLIT_SECTIONS_SHAPE = ShapeTokens.CornerExtraSmall
+
 private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
     tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
 private val SWITCH_PROGRESS_ANIMATION_SPEC: TweenSpec<Float> =
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
index f744532..24fa5f4 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
@@ -18,8 +18,12 @@
 
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.ReadOnlyComposable
@@ -31,6 +35,7 @@
 import androidx.wear.compose.material3.tokens.FilledTextButtonTokens
 import androidx.wear.compose.material3.tokens.FilledTonalTextButtonTokens
 import androidx.wear.compose.material3.tokens.OutlinedTextButtonTokens
+import androidx.wear.compose.material3.tokens.ShapeTokens
 import androidx.wear.compose.material3.tokens.TextButtonTokens
 import androidx.wear.compose.material3.tokens.TextToggleButtonTokens
 
@@ -64,6 +69,10 @@
  * Example of [TextButton] with onLongClick:
  *
  * @sample androidx.wear.compose.material3.samples.TextButtonWithOnLongClickSample
+ *
+ * Example of an [TextButton] with shape animation of rounded corners on press:
+ *
+ * @sample androidx.wear.compose.material3.samples.TextButtonWithCornerAnimationSample
  * @param onClick Will be called when the user clicks the button.
  * @param modifier Modifier to be applied to the button.
  * @param onLongClick Called when this button is long clicked (long-pressed). When this callback is
@@ -100,7 +109,7 @@
 ) {
     RoundButton(
         onClick = onClick,
-        modifier.minimumInteractiveComponentSize(),
+        modifier.minimumInteractiveComponentSize().size(TextButtonDefaults.DefaultButtonSize),
         onLongClick = onLongClick,
         onLongClickLabel = onLongClickLabel,
         enabled = enabled,
@@ -108,7 +117,6 @@
         interactionSource = interactionSource,
         shape = shape,
         border = { border },
-        buttonSize = TextButtonDefaults.DefaultButtonSize,
         ripple = ripple(),
         content =
             provideScopeContent(
@@ -193,8 +201,26 @@
 /** Contains the default values used by [TextButton]. */
 object TextButtonDefaults {
     /** Recommended [Shape] for [TextButton]. */
-    val shape: Shape
-        @Composable get() = TextButtonTokens.ContainerShape.value
+    val shape: RoundedCornerShape
+        @Composable get() = ShapeTokens.CornerFull
+
+    /** Recommended pressed [Shape] for [TextButton]. */
+    val pressedShape: CornerBasedShape
+        @Composable get() = MaterialTheme.shapes.small
+
+    /**
+     * Creates a [Shape] with a animation between two CornerBasedShapes.
+     *
+     * @param interactionSource the interaction source applied to the Button.
+     * @param shape The normal shape of the IconButton.
+     * @param pressedShape The pressed shape of the IconButton.
+     */
+    @Composable
+    fun animatedShape(
+        interactionSource: InteractionSource,
+        shape: CornerBasedShape = TextButtonDefaults.shape,
+        pressedShape: CornerBasedShape = TextButtonDefaults.pressedShape,
+    ) = animatedPressedButtonShape(interactionSource, shape, pressedShape)
 
     /**
      * Creates a [TextButtonColors] with the colors for a filled [TextButton]- by default, a colored
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt
index 4ccd05a..c25632b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt
@@ -125,7 +125,8 @@
                         .sizeIn(maxSweepDegrees = maxSweepAngle)
                         .padding(contentPadding.toArcPadding())
             ) {
-                CurvedTimeTextScope(this, timeText, timeTextStyle, contentColor).content()
+                CurvedTimeTextScope(this, timeText, timeTextStyle, maxSweepAngle, contentColor)
+                    .content()
             }
         }
     } else {
@@ -293,6 +294,7 @@
     private val scope: CurvedScope,
     private val timeText: String,
     private val timeTextStyle: TextStyle,
+    private val maxSweepAngle: Float,
     contentColor: Color,
 ) : TimeTextScope() {
 
@@ -302,13 +304,18 @@
         scope.curvedText(
             text = text,
             overflow = TextOverflow.Ellipsis,
+            maxSweepAngle = maxSweepAngle,
             style = CurvedTextStyle(style = contentTextStyle.merge(style)),
             modifier = CurvedModifier.weight(1f)
         )
     }
 
     override fun time() {
-        scope.curvedText(timeText, style = CurvedTextStyle(timeTextStyle))
+        scope.curvedText(
+            timeText,
+            maxSweepAngle = maxSweepAngle,
+            style = CurvedTextStyle(timeTextStyle)
+        )
     }
 
     override fun separator(style: TextStyle?) {
diff --git a/wear/compose/compose-navigation/build.gradle b/wear/compose/compose-navigation/build.gradle
index ffe881c..2c17d38 100644
--- a/wear/compose/compose-navigation/build.gradle
+++ b/wear/compose/compose-navigation/build.gradle
@@ -55,6 +55,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
@@ -74,7 +76,6 @@
             "to write Jetpack Compose applications for Wearable devices by providing " +
             "functionality to support navigation. " +
             "It builds upon the Jetpack Compose libraries."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":wear:compose:compose-navigation-samples"))
 }
diff --git a/wear/compose/compose-navigation/samples/build.gradle b/wear/compose/compose-navigation/samples/build.gradle
index cd20076..a189b90 100644
--- a/wear/compose/compose-navigation/samples/build.gradle
+++ b/wear/compose/compose-navigation/samples/build.gradle
@@ -50,6 +50,8 @@
 }
 
 android {
+    compileSdk 35
+
     defaultConfig {
         minSdkVersion 25
     }
diff --git a/wear/compose/compose-ui-tooling/build.gradle b/wear/compose/compose-ui-tooling/build.gradle
index b1615c5..9c73539 100644
--- a/wear/compose/compose-ui-tooling/build.gradle
+++ b/wear/compose/compose-ui-tooling/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.5.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.compose.ui:ui-tooling-preview:1.7.0-beta02")
 
     implementation(libs.kotlinStdlib)
@@ -47,7 +47,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2023"
     description = "Tools for Wear Composable"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":wear:compose:compose-material-samples"))
 }
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index 5a629cc..872bc9d 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -22,11 +22,12 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         applicationId "androidx.wear.compose.integration.demos"
         minSdk 25
-        versionCode 31
-        versionName "1.31"
+        versionCode 33
+        versionName "1.33"
     }
 
     buildTypes {
diff --git a/wear/compose/integration-tests/demos/common/build.gradle b/wear/compose/integration-tests/demos/common/build.gradle
index 9e6cd01e..8f518ec 100644
--- a/wear/compose/integration-tests/demos/common/build.gradle
+++ b/wear/compose/integration-tests/demos/common/build.gradle
@@ -30,6 +30,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 25
     }
diff --git a/wear/compose/integration-tests/demos/lint-baseline.xml b/wear/compose/integration-tests/demos/lint-baseline.xml
index 467b911..8904ee5 100644
--- a/wear/compose/integration-tests/demos/lint-baseline.xml
+++ b/wear/compose/integration-tests/demos/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.3.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.3.0-beta01)" variant="all" version="8.3.0-beta01">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="WearStandaloneAppFlag"
@@ -40,7 +40,7 @@
     <issue
         id="PrimitiveInCollection"
         message="variable alignmentValues with type List&lt;? extends PositionIndicatorAlignment>: replace with IntList"
-        errorLine1="    val alignmentValues = listOf("
+        errorLine1="    val alignmentValues ="
         errorLine2="    ^">
         <location
             file="src/main/java/androidx/wear/compose/integration/demos/PositionIndicatorDemos.kt"/>
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
index b583b53..b1257ee 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ProgressIndicatorDemo.kt
@@ -31,6 +31,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.material.Button
@@ -95,7 +96,7 @@
             startAngle = startAngle,
             endAngle = endAngle,
             progress = animatedProgress,
-            modifier = Modifier.fillMaxSize().padding(all = 1.dp)
+            modifier = Modifier.clearAndSetSemantics {}.fillMaxSize().padding(all = 1.dp)
         )
     }
 }
@@ -165,7 +166,7 @@
                     }
                     if (status == Status.Loading)
                         CircularProgressIndicator(
-                            modifier = Modifier.fillMaxSize(),
+                            modifier = Modifier.fillMaxSize().clearAndSetSemantics {},
                             startAngle = playerState.startOffsetAngle,
                             indicatorColor = MaterialTheme.colors.secondary,
                             trackColor = MaterialTheme.colors.onBackground.copy(alpha = 0.1f),
@@ -174,7 +175,7 @@
                     else
                         CircularProgressIndicator(
                             progress = playerState.progress.value,
-                            modifier = Modifier.fillMaxSize(),
+                            modifier = Modifier.fillMaxSize().clearAndSetSemantics {},
                             startAngle = playerState.startOffsetAngle,
                             indicatorColor = MaterialTheme.colors.secondary,
                             trackColor = MaterialTheme.colors.onBackground.copy(alpha = 0.1f),
@@ -235,7 +236,7 @@
                     }
                     CircularProgressIndicator(
                         progress = transformState.progress.value,
-                        modifier = Modifier.fillMaxSize(),
+                        modifier = Modifier.fillMaxSize().clearAndSetSemantics {},
                         startAngle = transformState.startOffsetAngle,
                         indicatorColor = MaterialTheme.colors.secondary,
                         trackColor = MaterialTheme.colors.onBackground.copy(alpha = 0.1f),
diff --git a/wear/compose/integration-tests/macrobenchmark-target/build.gradle b/wear/compose/integration-tests/macrobenchmark-target/build.gradle
index 96f05c4..cafd69d 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/wear/compose/integration-tests/macrobenchmark-target/build.gradle
@@ -22,6 +22,7 @@
 }
 
 android {
+    compileSdk 35
 
     namespace "androidx.wear.compose.integration.macrobenchmark.target"
     buildTypes {
@@ -53,4 +54,4 @@
     implementation(project(path:':tracing:tracing-perfetto-binary'))
 }
 
-android.defaultConfig.minSdkVersion 25
\ No newline at end of file
+android.defaultConfig.minSdkVersion 25
diff --git a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/AnimatedTextActivity.kt b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/AnimatedTextActivity.kt
index 062c26c..c827b77 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/AnimatedTextActivity.kt
+++ b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/AnimatedTextActivity.kt
@@ -50,17 +50,16 @@
 
 class AnimatedTextActivity : ComponentActivity() {
 
-    @RequiresApi(Build.VERSION_CODES.S)
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        setContent { AnimatedTextScreen() }
+        setContent { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) AnimatedTextScreen() }
     }
 }
 
 @RequiresApi(Build.VERSION_CODES.S)
 @Composable
-private fun AnimatedTextScreen() {
+internal fun AnimatedTextScreen() {
     val scope = rememberCoroutineScope()
     val animatable = remember { Animatable(0.5f) }
     val textStyle = remember {
diff --git a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
index dd87e59..2bd638c 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
+++ b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
@@ -471,7 +471,6 @@
     }
 }
 
-@OptIn(ExperimentalWearFoundationApi::class)
 @Composable
 fun FocusCoordinator() {
     var selected by remember { mutableIntStateOf(0) }
diff --git a/wear/compose/integration-tests/macrobenchmark/build.gradle b/wear/compose/integration-tests/macrobenchmark/build.gradle
index 1b3a97c..a56f32a 100644
--- a/wear/compose/integration-tests/macrobenchmark/build.gradle
+++ b/wear/compose/integration-tests/macrobenchmark/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 29
     }
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt
index fd00cbd..925f27e 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt
@@ -17,6 +17,7 @@
 package androidx.wear.compose.integration.macrobenchmark
 
 import android.content.Intent
+import android.os.Build
 import androidx.benchmark.macro.CompilationMode
 import androidx.benchmark.macro.ExperimentalMetricApi
 import androidx.benchmark.macro.FrameTimingGfxInfoMetric
@@ -24,6 +25,7 @@
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
 import androidx.test.uiautomator.By
 import androidx.wear.compose.integration.macrobenchmark.test.disableChargingExperience
 import androidx.wear.compose.integration.macrobenchmark.test.enableChargingExperience
@@ -37,6 +39,7 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
 class AnimatedTextBenchmark {
 
     @get:Rule val benchmarkRule = MacrobenchmarkRule()
diff --git a/wear/compose/integration-tests/navigation/build.gradle b/wear/compose/integration-tests/navigation/build.gradle
index ca96d2b..b29b94ada 100644
--- a/wear/compose/integration-tests/navigation/build.gradle
+++ b/wear/compose/integration-tests/navigation/build.gradle
@@ -22,6 +22,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         applicationId "androidx.wear.compose.integration.navigation"
         minSdk 25
diff --git a/wear/protolayout/protolayout-expression-pipeline/build.gradle b/wear/protolayout/protolayout-expression-pipeline/build.gradle
index 0a5898e..2f479fb 100644
--- a/wear/protolayout/protolayout-expression-pipeline/build.gradle
+++ b/wear/protolayout/protolayout-expression-pipeline/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     annotationProcessor(libs.nullaway)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("androidx.collection:collection:1.2.0")
     implementation("androidx.core:core:1.7.0")
@@ -60,5 +60,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Evaluate dynamic expressions."
-    metalavaK2UastEnabled = true
 }
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/AnimatableNode.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/AnimatableNode.java
index 061acdf..53b251e 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/AnimatableNode.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/AnimatableNode.java
@@ -19,10 +19,11 @@
 import static androidx.wear.protolayout.expression.pipeline.AnimationsHelper.maybeSplitToMainAndAuxAnimationSpec;
 
 import android.animation.ArgbEvaluator;
+import android.animation.FloatEvaluator;
+import android.animation.IntEvaluator;
 import android.animation.TypeEvaluator;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.util.Pair;
@@ -31,19 +32,18 @@
 /** Data animatable source node within a dynamic data pipeline. */
 abstract class AnimatableNode {
     static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator();
+    static final IntEvaluator INT_EVALUATOR = new IntEvaluator();
+    static final FloatEvaluator FLOAT_EVALUATOR = new FloatEvaluator();
 
     private boolean mIsVisible = false;
     @NonNull
     final QuotaAwareAnimator mQuotaAwareAnimator;
 
-    protected AnimatableNode(@NonNull QuotaManager quotaManager, @NonNull AnimationSpec spec) {
-        this(quotaManager, spec, null);
-    }
 
     protected AnimatableNode(
             @NonNull QuotaManager quotaManager,
             @NonNull AnimationSpec spec,
-            @Nullable TypeEvaluator<?> evaluator) {
+            @NonNull TypeEvaluator<?> evaluator) {
         // When a reverse duration which is different from forward duration is provided for a
         // reverse repeated animation, we need to split the spec into two and use
         // QuotaAwareAnimatorWithAux to create two ValueAnimators internally to achieve the
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNode.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNode.java
index 41d8206..3e0208d7 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNode.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNode.java
@@ -33,27 +33,6 @@
  * descendants of this class will implement operations of the form "O = LHS [op] RHS", or "O =
  * op(LHS, RHS)".
  *
- * <p>This node will wait until there's no pending data from either side:
- *
- * <ul>
- *   <li>This node will wait until both sides invoked either {@code onData} or {@code onInvalidated}
- *       at least once.
- *   <li>If one side invoked {@code onPreUpdate}, this node will wait until it invoked either {@code
- *       onData} or {@code onInvalidated}.
- *   <li>This node expects each side to invoke exactly on {@code onData} or {@code onInvalidated}
- *       after each {@code onPreUpdate}:
- *       <ul>
- *         <li>If {@code onData} or {@code onInvalidated} are invoked without {@code onPreUpdate},
- *             this node will log a warning and downstream the callback immediately (unless waiting
- *             for the other side).
- *         <li>If {@code onPreUpdate} is invoked more than once without {@code onData} or {@code
- *             onInvalidated}, this node will log a warning and ignore the followup {@code
- *             onPreUpdate}.
- *       </ul>
- *       Both of these scenarios can mean {@link DynamicTypeEvaluator} might publish a partially
- *       evaluated update.
- * </ul>
- *
  * @param <LhsT> The source data type for the left-hand side of the operation.
  * @param <RhsT> The source data type for the right-hand side of the operation.
  * @param <O> The data type that this node emits.
@@ -61,100 +40,138 @@
 class DynamicDataBiTransformNode<LhsT, RhsT, O> implements DynamicDataNode<O> {
     private static final String TAG = "DynamicDataBiTransform";
 
-    private final UpstreamCallback<LhsT> mLhsUpstreamCallback = new UpstreamCallback<>();
-    private final UpstreamCallback<RhsT> mRhsUpstreamCallback = new UpstreamCallback<>();
+    private final DynamicTypeValueReceiverWithPreUpdate<LhsT> mLhsIncomingCallback;
+    private final DynamicTypeValueReceiverWithPreUpdate<RhsT> mRhsIncomingCallback;
 
     final DynamicTypeValueReceiverWithPreUpdate<O> mDownstream;
     private final BiFunction<LhsT, RhsT, O> mTransformer;
 
-    boolean mDownstreamPreUpdated = false;
+    @Nullable LhsT mCachedLhsData;
+    @Nullable RhsT mCachedRhsData;
+
+    int mPendingLhsStateUpdates = 0;
+    int mPendingRhsStateUpdates = 0;
 
     DynamicDataBiTransformNode(
             DynamicTypeValueReceiverWithPreUpdate<O> downstream,
             BiFunction<LhsT, RhsT, O> transformer) {
         this.mDownstream = downstream;
         this.mTransformer = transformer;
+
+        // These classes refer to handlePreStateUpdate, which is @UnderInitialization when these
+        // initializers run, and hence raise an error. It's invalid to annotate
+        // handle{Pre}StateUpdate as @UnderInitialization (since it refers to initialized fields),
+        // and moving this assignment into the constructor yields the same error (since one of the
+        // fields has to be assigned first, when the class is still under initialization).
+        //
+        // The only path to get these is via get{Lhs,Rhs}IncomingCallback, which can only be called
+        // when the class is initialized (and which also cannot be called from a sub-constructor, as
+        // that will again complain that it's calling something which is @UnderInitialization).
+        // Given that, suppressing the warning in onStateUpdate should be safe.
+        this.mLhsIncomingCallback =
+                new DynamicTypeValueReceiverWithPreUpdate<LhsT>() {
+                    @Override
+                    public void onPreUpdate() {
+                        mPendingLhsStateUpdates++;
+
+                        if (mPendingLhsStateUpdates == 1 && mPendingRhsStateUpdates == 0) {
+                            mDownstream.onPreUpdate();
+                        }
+                    }
+
+                    @SuppressWarnings("method.invocation")
+                    @Override
+                    public void onData(@NonNull LhsT newData) {
+                        onUpdatedImpl(newData);
+                    }
+
+                    private void onUpdatedImpl(@Nullable LhsT newData) {
+                        if (mPendingLhsStateUpdates == 0) {
+                            Log.w(
+                                    TAG,
+                                    "Received a state update, but one or more suppliers did not"
+                                            + " call onPreStateUpdate");
+                        } else {
+                            mPendingLhsStateUpdates--;
+                        }
+
+                        mCachedLhsData = newData;
+                        handleStateUpdate();
+                    }
+
+                    @Override
+                    public void onInvalidated() {
+                        // Note: Casts are required here to help out the null checker.
+                        onUpdatedImpl((LhsT) null);
+                    }
+                };
+
+        this.mRhsIncomingCallback =
+                new DynamicTypeValueReceiverWithPreUpdate<RhsT>() {
+                    @Override
+                    public void onPreUpdate() {
+                        mPendingRhsStateUpdates++;
+
+                        if (mPendingLhsStateUpdates == 0 && mPendingRhsStateUpdates == 1) {
+                            mDownstream.onPreUpdate();
+                        }
+                    }
+
+                    @SuppressWarnings("method.invocation")
+                    @Override
+                    public void onData(@NonNull RhsT newData) {
+                        onUpdatedImpl(newData);
+                    }
+
+                    private void onUpdatedImpl(@Nullable RhsT newData) {
+                        if (mPendingRhsStateUpdates == 0) {
+                            Log.w(
+                                    TAG,
+                                    "Received a state update, but one or more suppliers did not"
+                                            + " call onPreStateUpdate");
+                        } else {
+                            mPendingRhsStateUpdates--;
+                        }
+
+                        mCachedRhsData = newData;
+                        handleStateUpdate();
+                    }
+
+                    @Override
+                    public void onInvalidated() {
+                        onUpdatedImpl((RhsT) null);
+                    }
+                };
+    }
+
+    void handleStateUpdate() {
+        if (mPendingLhsStateUpdates == 0 && mPendingRhsStateUpdates == 0) {
+            LhsT lhs = mCachedLhsData;
+            RhsT rhs = mCachedRhsData;
+
+            if (lhs == null || rhs == null) {
+                mDownstream.onInvalidated();
+            } else {
+                O result = mTransformer.apply(lhs, rhs);
+                if (result == null) {
+                    mDownstream.onInvalidated();
+                } else {
+                    mDownstream.onData(result);
+                }
+            }
+        }
+    }
+
+    public DynamicTypeValueReceiverWithPreUpdate<LhsT> getLhsIncomingCallback() {
+        return mLhsIncomingCallback;
+    }
+
+    public DynamicTypeValueReceiverWithPreUpdate<RhsT> getRhsIncomingCallback() {
+        return mRhsIncomingCallback;
     }
 
     @Override
     public int getCost() {
         return DEFAULT_NODE_COST;
     }
-
-    private class UpstreamCallback<T> implements DynamicTypeValueReceiverWithPreUpdate<T> {
-        private boolean mUpstreamPreUpdated = false;
-
-        /**
-         * Whether {@link #cache} should be used, i.e. set to true when either {@link #onData} or
-         * {@link #onInvalidated} were invoked at least once, and there's no pending {@link
-         * #onPreUpdate} call.
-         */
-        boolean isCacheReady = false;
-
-        /**
-         * Latest value arrived in {@link #onData}, or {@code null} if the last callback was {@link
-         * #onInvalidated}. Should not be used if {@link #isCacheReady} is {@code false}.
-         */
-        @Nullable T cache;
-
-        @Override
-        public void onPreUpdate() {
-            if (mUpstreamPreUpdated) {
-                Log.w(TAG, "Received onPreUpdate twice without onData/onInvalidated.");
-            }
-            isCacheReady = false;
-            mUpstreamPreUpdated = true;
-            if (!mDownstreamPreUpdated) {
-                mDownstreamPreUpdated = true;
-                mDownstream.onPreUpdate();
-            }
-        }
-
-        @Override
-        public void onData(@NonNull T newData) {
-            onUpdate(newData);
-        }
-
-        @Override
-        public void onInvalidated() {
-            onUpdate(null);
-        }
-
-        private void onUpdate(@Nullable T newData) {
-            if (!mUpstreamPreUpdated) {
-                Log.w(TAG, "Received onData/onInvalidated without onPreUpdate.");
-            }
-            mUpstreamPreUpdated = false;
-            isCacheReady = true;
-            cache = newData;
-            handleStateUpdate();
-        }
-    }
-
-    void handleStateUpdate() {
-        if (!mLhsUpstreamCallback.isCacheReady || !mRhsUpstreamCallback.isCacheReady) {
-            return;
-        }
-        LhsT lhs = mLhsUpstreamCallback.cache;
-        RhsT rhs = mRhsUpstreamCallback.cache;
-
-        if (lhs == null || rhs == null) {
-            mDownstream.onInvalidated();
-        } else {
-            O result = mTransformer.apply(lhs, rhs);
-            if (result == null) {
-                mDownstream.onInvalidated();
-            } else {
-                mDownstream.onData(result);
-            }
-        }
-    }
-
-    public DynamicTypeValueReceiverWithPreUpdate<LhsT> getLhsUpstreamCallback() {
-        return mLhsUpstreamCallback;
-    }
-
-    public DynamicTypeValueReceiverWithPreUpdate<RhsT> getRhsUpstreamCallback() {
-        return mRhsUpstreamCallback;
-    }
 }
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
index 6229642..db91f91 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluator.java
@@ -558,12 +558,12 @@
                     node = concatNode;
                     bindRecursively(
                             stringSource.getConcatOp().getInputLhs(),
-                            concatNode.getLhsUpstreamCallback(),
+                            concatNode.getLhsIncomingCallback(),
                             locale,
                             resultBuilder);
                     bindRecursively(
                             stringSource.getConcatOp().getInputRhs(),
-                            concatNode.getRhsUpstreamCallback(),
+                            concatNode.getRhsIncomingCallback(),
                             locale,
                             resultBuilder);
                     break;
@@ -606,11 +606,11 @@
 
                     bindRecursively(
                             int32Source.getArithmeticOperation().getInputLhs(),
-                            arithmeticNode.getLhsUpstreamCallback(),
+                            arithmeticNode.getLhsIncomingCallback(),
                             resultBuilder);
                     bindRecursively(
                             int32Source.getArithmeticOperation().getInputRhs(),
-                            arithmeticNode.getRhsUpstreamCallback(),
+                            arithmeticNode.getRhsIncomingCallback(),
                             resultBuilder);
 
                     break;
@@ -734,11 +734,11 @@
                 node = betweenInstancesNode;
                 bindRecursively(
                         durationSource.getBetween().getStartInclusive(),
-                        betweenInstancesNode.getLhsUpstreamCallback(),
+                        betweenInstancesNode.getLhsIncomingCallback(),
                         resultBuilder);
                 bindRecursively(
                         durationSource.getBetween().getEndExclusive(),
-                        betweenInstancesNode.getRhsUpstreamCallback(),
+                        betweenInstancesNode.getRhsIncomingCallback(),
                         resultBuilder);
                 break;
             case FIXED:
@@ -909,11 +909,11 @@
 
                     bindRecursively(
                             floatSource.getArithmeticOperation().getInputLhs(),
-                            arithmeticNode.getLhsUpstreamCallback(),
+                            arithmeticNode.getLhsIncomingCallback(),
                             resultBuilder);
                     bindRecursively(
                             floatSource.getArithmeticOperation().getInputRhs(),
-                            arithmeticNode.getRhsUpstreamCallback(),
+                            arithmeticNode.getRhsIncomingCallback(),
                             resultBuilder);
 
                     break;
@@ -1092,11 +1092,11 @@
 
                     bindRecursively(
                             boolSource.getInt32Comparison().getInputLhs(),
-                            compNode.getLhsUpstreamCallback(),
+                            compNode.getLhsIncomingCallback(),
                             resultBuilder);
                     bindRecursively(
                             boolSource.getInt32Comparison().getInputRhs(),
-                            compNode.getRhsUpstreamCallback(),
+                            compNode.getRhsIncomingCallback(),
                             resultBuilder);
 
                     break;
@@ -1109,11 +1109,11 @@
 
                     bindRecursively(
                             boolSource.getLogicalOp().getInputLhs(),
-                            logicalNode.getLhsUpstreamCallback(),
+                            logicalNode.getLhsIncomingCallback(),
                             resultBuilder);
                     bindRecursively(
                             boolSource.getLogicalOp().getInputRhs(),
-                            logicalNode.getRhsUpstreamCallback(),
+                            logicalNode.getRhsIncomingCallback(),
                             resultBuilder);
 
                     break;
@@ -1136,11 +1136,11 @@
 
                     bindRecursively(
                             boolSource.getFloatComparison().getInputLhs(),
-                            compNode.getLhsUpstreamCallback(),
+                            compNode.getLhsIncomingCallback(),
                             resultBuilder);
                     bindRecursively(
                             boolSource.getFloatComparison().getInputRhs(),
-                            compNode.getRhsUpstreamCallback(),
+                            compNode.getRhsIncomingCallback(),
                             resultBuilder);
 
                     break;
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java
index 9ec6b85..418cd36 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/FloatNodes.java
@@ -148,7 +148,7 @@
                 DynamicTypeValueReceiverWithPreUpdate<Float> downstream,
                 QuotaManager quotaManager) {
 
-            super(quotaManager, protoNode.getAnimationSpec());
+            super(quotaManager, protoNode.getAnimationSpec(), AnimatableNode.FLOAT_EVALUATOR);
             this.mProtoNode = protoNode;
             this.mDownstream = downstream;
             mQuotaAwareAnimator.addUpdateCallback(
@@ -213,7 +213,7 @@
                 @NonNull AnimationSpec spec,
                 QuotaManager quotaManager) {
 
-            super(quotaManager, spec);
+            super(quotaManager, spec, AnimatableNode.FLOAT_EVALUATOR);
             this.mDownstream = downstream;
             mQuotaAwareAnimator.addUpdateCallback(
                     animatedValue -> {
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java
index e32fd70..5ebece0 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/Int32Nodes.java
@@ -258,7 +258,7 @@
                 AnimatableFixedInt32 protoNode,
                 DynamicTypeValueReceiverWithPreUpdate<Integer> downstream,
                 QuotaManager quotaManager) {
-            super(quotaManager, protoNode.getAnimationSpec());
+            super(quotaManager, protoNode.getAnimationSpec(), AnimatableNode.INT_EVALUATOR);
             this.mProtoNode = protoNode;
             this.mDownstream = downstream;
             mQuotaAwareAnimator.addUpdateCallback(
@@ -318,7 +318,7 @@
                 DynamicTypeValueReceiverWithPreUpdate<Integer> downstream,
                 @NonNull AnimationSpec spec,
                 QuotaManager quotaManager) {
-            super(quotaManager, spec);
+            super(quotaManager, spec, AnimatableNode.INT_EVALUATOR);
             this.mDownstream = downstream;
             mQuotaAwareAnimator.addUpdateCallback(
                     animatedValue -> {
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimator.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimator.java
index 6e1349b..fa7fdbd 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimator.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimator.java
@@ -21,13 +21,15 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.FloatEvaluator;
+import android.animation.IntEvaluator;
 import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
 import android.os.Handler;
 import android.os.Looper;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 import androidx.core.os.HandlerCompat;
 import androidx.wear.protolayout.expression.pipeline.AnimationsHelper.RepeatDelays;
@@ -47,16 +49,12 @@
     @NonNull protected final Handler mUiHandler;
     private long mStartDelay = 0;
     protected Runnable mAcquireQuotaAndAnimateRunnable = this::acquireQuotaAndAnimate;
-    @Nullable protected final TypeEvaluator<?> mEvaluator;
+    @NonNull protected final TypeEvaluator<?> mEvaluator;
 
     interface UpdateCallback {
         void onUpdate(@NonNull Object animatedValue);
     }
 
-    QuotaAwareAnimator(@NonNull QuotaManager quotaManager, @NonNull AnimationSpec spec) {
-        this(quotaManager, spec, null);
-    }
-
     /**
      * If an evaluator other than a float or int type shall be used when calculating the animated
      * values of this animation, use this constructor to set the preferred type evaluator.
@@ -64,14 +62,14 @@
     QuotaAwareAnimator(
             @NonNull QuotaManager quotaManager,
             @NonNull AnimationSpec spec,
-            @Nullable TypeEvaluator<?> evaluator) {
+            @NonNull TypeEvaluator<?> evaluator) {
         this(quotaManager, spec, evaluator, false);
     }
 
     protected QuotaAwareAnimator(
             @NonNull QuotaManager quotaManager,
             @NonNull AnimationSpec spec,
-            @Nullable TypeEvaluator<?> evaluator,
+            @NonNull TypeEvaluator<?> evaluator,
             boolean alwaysPauseWhenRepeatForward) {
         mQuotaManager = quotaManager;
         mAnimator = new ValueAnimator();
@@ -118,10 +116,13 @@
     }
 
     protected static void setFloatValues(
-            ValueAnimator animator, @Nullable TypeEvaluator<?> evaluator, float... values) {
+            ValueAnimator animator, @NonNull TypeEvaluator<?> evaluator, float... values) {
+        if (!(evaluator instanceof FloatEvaluator)) {
+            throw new IllegalArgumentException("FloatEvaluator is needed for setting float values");
+        }
         animator.cancel();
         // ValueAnimator#setEvaluator only valid after values are set, and only need to set once.
-        boolean needToSetEvaluator = animator.getValues() == null && evaluator != null;
+        boolean needToSetEvaluator = animator.getValues() == null;
         animator.setFloatValues(values);
         if (needToSetEvaluator) {
             animator.setEvaluator(evaluator);
@@ -138,11 +139,14 @@
     }
 
     protected static void setIntValues(
-            ValueAnimator animator, @Nullable TypeEvaluator<?> evaluator, int... values) {
+            ValueAnimator animator, @NonNull TypeEvaluator<?> evaluator, int... values) {
         animator.cancel();
-
+        if (!(evaluator instanceof IntEvaluator) && !(evaluator instanceof ArgbEvaluator)) {
+            throw new IllegalArgumentException(
+                    "IntEvaluator or ArgbEvaluator is needed for setting int values");
+        }
         // ValueAnimator#setEvaluator only valid after values are set, and only need to set once.
-        boolean needToSetEvaluator = animator.getValues() == null && evaluator != null;
+        boolean needToSetEvaluator = animator.getValues() == null;
         animator.setIntValues(values);
         if (needToSetEvaluator) {
             animator.setEvaluator(evaluator);
@@ -325,7 +329,7 @@
 
         @Override
         @UiThread
-        public void onAnimationStart(Animator animation, boolean isReverse) {
+        public void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
             super.onAnimationStart(animation, isReverse);
             mIsReverse = isReverse;
         }
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimatorWithAux.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimatorWithAux.java
index 8c8e4a8..3d050a9 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimatorWithAux.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/QuotaAwareAnimatorWithAux.java
@@ -23,7 +23,6 @@
 import android.animation.ValueAnimator;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.core.os.HandlerCompat;
 import androidx.wear.protolayout.expression.pipeline.AnimationsHelper.RepeatDelays;
 import androidx.wear.protolayout.expression.proto.AnimationParameterProto.AnimationSpec;
@@ -51,7 +50,7 @@
             @NonNull QuotaManager quotaManager,
             @NonNull AnimationSpec spec,
             @NonNull AnimationSpec auxSpec,
-            @Nullable TypeEvaluator<?> evaluator) {
+            @NonNull TypeEvaluator<?> evaluator) {
         super(quotaManager, spec, evaluator, /* alwaysPauseWhenRepeatForward= */ true);
 
         mAuxAnimator = new ValueAnimator();
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/AnimatableNodeTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/AnimatableNodeTest.java
index 5b50fe9..4c5cff9 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/AnimatableNodeTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/AnimatableNodeTest.java
@@ -64,7 +64,8 @@
     @Test
     public void infiniteAnimator_onlyStartsWhenNodeIsVisible() {
         QuotaManager quotaManager = new FixedQuotaManagerImpl(Integer.MAX_VALUE);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
 
@@ -82,7 +83,8 @@
     @Test
     public void infiniteAnimator_pausesWhenNodeIsInvisible() {
         QuotaManager quotaManager = new FixedQuotaManagerImpl(Integer.MAX_VALUE);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
 
@@ -101,7 +103,8 @@
     @Test
     public void animator_noQuota_notPlayed() {
         QuotaManager quotaManager = new FixedQuotaManagerImpl(0);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
 
@@ -114,7 +117,8 @@
     public void animator_reused_withFloatValues() {
         mUpdateValues.clear();
         QuotaManager quotaManager = new FixedQuotaManagerImpl(Integer.MAX_VALUE);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
         quotaAwareAnimator.addUpdateCallback(a -> mUpdateValues.add((float) a));
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
@@ -164,7 +168,8 @@
     public void animator_reuse_withFloatValues() {
         mUpdateValues.clear();
         QuotaManager quotaManager = new FixedQuotaManagerImpl(Integer.MAX_VALUE);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
         quotaAwareAnimator.addUpdateCallback(a -> mUpdateValues.add((float) a));
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
@@ -187,7 +192,8 @@
     public void animator_reuse_noQuota() {
         mUpdateValues.clear();
         QuotaManager quotaManager = new FixedQuotaManagerImpl(0);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
         quotaAwareAnimator.addUpdateCallback(a -> mUpdateValues.add((float) a));
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
@@ -210,7 +216,8 @@
     public void animator_reuse_noQuota_then_withQuota() {
         mUpdateValues.clear();
         QuotaManager quotaManager = new FixedQuotaManagerImpl(1);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
         quotaAwareAnimator.addUpdateCallback(a -> mUpdateValues.add((float) a));
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
@@ -237,7 +244,8 @@
     public void animator_reuse_withQuota_then_noQuota() {
         mUpdateValues.clear();
         QuotaManager quotaManager = new FixedQuotaManagerImpl(1);
-        TestQuotaAwareAnimator quotaAwareAnimator = new TestQuotaAwareAnimator(quotaManager);
+        TestQuotaAwareAnimator quotaAwareAnimator =
+                new TestQuotaAwareAnimator(quotaManager, AnimatableNode.FLOAT_EVALUATOR);
         quotaAwareAnimator.setFloatValues(0.0f, 10.0f);
         quotaAwareAnimator.addUpdateCallback(a -> mUpdateValues.add((float) a));
         TestAnimatableNode animNode = new TestAnimatableNode(quotaAwareAnimator);
@@ -262,7 +270,9 @@
     public void animationWithCustomReverseDuration_animatorWithAux() {
         QuotaManager quotaManager = new FixedQuotaManagerImpl(1);
         TestAnimatableNode animNode =
-                new TestAnimatableNode(quotaManager, createAnimationSpec(4, 100, 200, 0, 0));
+                new TestAnimatableNode(quotaManager,
+                        createAnimationSpec(4, 100, 200, 0, 0),
+                        AnimatableNode.FLOAT_EVALUATOR);
         QuotaAwareAnimator animator = animNode.getQuotaAwareAnimator();
 
         assertThat(animator).isInstanceOf(QuotaAwareAnimatorWithAux.class);
@@ -272,7 +282,9 @@
     public void animationWithCustomReverseDuration_consumeOneQuota() {
         QuotaManager quotaManager = new FixedQuotaManagerImpl(2);
         TestAnimatableNode animNode =
-                new TestAnimatableNode(quotaManager, createAnimationSpec(2, 100, 200, 0, 0));
+                new TestAnimatableNode(quotaManager,
+                        createAnimationSpec(2, 100, 200, 0, 0),
+                        AnimatableNode.INT_EVALUATOR);
         animNode.setVisibility(true);
         QuotaAwareAnimator animator = animNode.getQuotaAwareAnimator();
         animator.setIntValues(0, 10);
@@ -291,7 +303,9 @@
     public void animationWithCustomReverseDuration_releaseQuotaWhenInvisible() {
         QuotaManager quotaManager = new FixedQuotaManagerImpl(1);
         TestAnimatableNode animNode =
-                new TestAnimatableNode(quotaManager, createAnimationSpec(2, 100, 200, 0, 0));
+                new TestAnimatableNode(quotaManager,
+                        createAnimationSpec(2, 100, 200, 0, 0),
+                        AnimatableNode.INT_EVALUATOR);
         animNode.setVisibility(true);
         QuotaAwareAnimator animator = animNode.getQuotaAwareAnimator();
         animator.setIntValues(0, 10);
@@ -309,7 +323,9 @@
         mUpdateValues.clear();
         QuotaManager quotaManager = new FixedQuotaManagerImpl(1);
         TestAnimatableNode animNode =
-                new TestAnimatableNode(quotaManager, createAnimationSpec(2, 100, 200, 0, 0));
+                new TestAnimatableNode(quotaManager,
+                        createAnimationSpec(2, 100, 200, 0, 0),
+                        AnimatableNode.FLOAT_EVALUATOR);
         animNode.setVisibility(true);
         QuotaAwareAnimator animator = animNode.getQuotaAwareAnimator();
 
@@ -349,8 +365,11 @@
             super(quotaAwareAnimator);
         }
 
-        TestAnimatableNode(@NonNull QuotaManager quotaManager, @NonNull AnimationSpec spec) {
-            super(quotaManager, spec);
+        TestAnimatableNode(
+                @NonNull QuotaManager quotaManager,
+                @NonNull AnimationSpec spec,
+                @NonNull TypeEvaluator typeEvaluator) {
+            super(quotaManager, spec, typeEvaluator);
         }
 
         QuotaAwareAnimator getQuotaAwareAnimator() {
@@ -361,9 +380,6 @@
     static class TestQuotaAwareAnimator extends QuotaAwareAnimator {
         public boolean isInfiniteAnimator = false;
 
-        TestQuotaAwareAnimator(@NonNull QuotaManager mQuotaManager) {
-            super(mQuotaManager, AnimationSpec.getDefaultInstance());
-        }
 
         @SuppressWarnings("rawtypes")
         TestQuotaAwareAnimator(@NonNull QuotaManager mQuotaManager,
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
index 7a67bd8e..e4e34dd 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/BoolNodesTest.java
@@ -289,10 +289,10 @@
                 new BoolNodes.LogicalBoolOp(protoNode, new AddToListCallback<>(results));
 
         FixedBool lhsProtoNode = FixedBool.newBuilder().setValue(lhs).build();
-        FixedBoolNode lhsNode = new FixedBoolNode(lhsProtoNode, node.getLhsUpstreamCallback());
+        FixedBoolNode lhsNode = new FixedBoolNode(lhsProtoNode, node.getLhsIncomingCallback());
 
         FixedBool rhsProtoNode = FixedBool.newBuilder().setValue(rhs).build();
-        FixedBoolNode rhsNode = new FixedBoolNode(rhsProtoNode, node.getRhsUpstreamCallback());
+        FixedBoolNode rhsNode = new FixedBoolNode(rhsProtoNode, node.getRhsIncomingCallback());
 
         lhsNode.preInit();
         rhsNode.preInit();
@@ -317,11 +317,11 @@
                 new BoolNodes.ComparisonInt32Node(protoNode, new AddToListCallback<>(results));
 
         FixedInt32 lhsProtoNode = FixedInt32.newBuilder().setValue(lhs).build();
-        FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsUpstreamCallback());
+        FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsIncomingCallback());
         lhsNode.preInit();
 
         FixedInt32 rhsProtoNode = FixedInt32.newBuilder().setValue(rhs).build();
-        FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsUpstreamCallback());
+        FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsIncomingCallback());
         rhsNode.preInit();
 
         lhsNode.init();
@@ -344,11 +344,11 @@
                 new BoolNodes.ComparisonFloatNode(protoNode, new AddToListCallback<>(results));
 
         FixedFloat lhsProtoNode = FixedFloat.newBuilder().setValue(lhs).build();
-        FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsUpstreamCallback());
+        FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsIncomingCallback());
         lhsNode.preInit();
 
         FixedFloat rhsProtoNode = FixedFloat.newBuilder().setValue(rhs).build();
-        FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsUpstreamCallback());
+        FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsIncomingCallback());
         rhsNode.preInit();
 
         lhsNode.init();
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DurationNodesTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DurationNodesTest.java
index c5607d0..22ef7ad 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DurationNodesTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DurationNodesTest.java
@@ -62,8 +62,8 @@
         Instant secondInstant = Instant.ofEpochSecond(12345L);
 
         BetweenInstancesNode node = new BetweenInstancesNode(new AddToListCallback<>(results));
-        node.getLhsUpstreamCallback().onData(firstInstant);
-        node.getRhsUpstreamCallback().onData(secondInstant);
+        node.getLhsIncomingCallback().onData(firstInstant);
+        node.getRhsIncomingCallback().onData(secondInstant);
 
         assertThat(results).containsExactly(Duration.between(firstInstant, secondInstant));
     }
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNodeTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNodeTest.java
index 5501a16..4bd9905 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNodeTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicDataBiTransformNodeTest.java
@@ -18,12 +18,11 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -37,124 +36,68 @@
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private DynamicTypeValueReceiverWithPreUpdate<Integer> mMockCallback;
 
-    private DynamicDataBiTransformNode<Integer, Integer, Integer> mSumNodeUnderTest;
+    private DynamicDataBiTransformNode<Integer, Integer, Integer> mNodeUnderTest;
 
     @Before
     public void setUp() {
-        mSumNodeUnderTest = new DynamicDataBiTransformNode<>(mMockCallback, Integer::sum);
-    }
-
-    @After
-    public void assertNoOtherCallbacks() {
-        // DynamicDataBiTransformNode is fragile, we want to make sure an unexpected callback wasn't
-        // used on one of the behaviors tested here.
-        verifyNoMoreInteractions(mMockCallback);
+        mNodeUnderTest = new DynamicDataBiTransformNode<>(mMockCallback, Integer::sum);
     }
 
     @Test
     public void onPreStateUpdate_propagatesOnce() {
-        mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
+        mNodeUnderTest.getLhsIncomingCallback().onPreUpdate();
         verify(mMockCallback).onPreUpdate();
-        reset(mMockCallback);
 
         // Next call should not cause another call to be passed through.
-        mSumNodeUnderTest.getRhsUpstreamCallback().onPreUpdate();
-        verify(mMockCallback, never()).onPreUpdate();
-    }
-
-    @Test
-    public void onPreStateUpdate_twice_ignoresSecondOne() {
-        mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
+        mNodeUnderTest.getRhsIncomingCallback().onPreUpdate();
         verify(mMockCallback).onPreUpdate();
-        reset(mMockCallback);
-
-        mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
-        mSumNodeUnderTest.getRhsUpstreamCallback().onPreUpdate();
-        // Doesn't pre-update again.
-        verify(mMockCallback, never()).onPreUpdate();
-
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
-        mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
-        // Still propagates data.
-        verify(mMockCallback).onData(11);
     }
 
     @Test
     public void onStateUpdate_invokesCallback() {
-        mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
-        mSumNodeUnderTest.getRhsUpstreamCallback().onPreUpdate();
+        mNodeUnderTest.getLhsIncomingCallback().onPreUpdate();
+        mNodeUnderTest.getRhsIncomingCallback().onPreUpdate();
 
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
-        mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
+        mNodeUnderTest.getLhsIncomingCallback().onData(5);
+        mNodeUnderTest.getRhsIncomingCallback().onData(6);
 
-        verify(mMockCallback).onPreUpdate();
         verify(mMockCallback).onData(11);
     }
 
     @Test
-    public void onStateUpdate_onlyOneSide_doesNotInvokeCallback() {
+    public void onStateUpdate_onlyOneSide_doesntInvokeCallback() {
         // Note: RHS has *not* been seeded with any data here...
-        mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
+        mNodeUnderTest.getLhsIncomingCallback().onPreUpdate();
+        mNodeUnderTest.getLhsIncomingCallback().onData(5);
 
-        verify(mMockCallback).onPreUpdate();
         verify(mMockCallback, never()).onData(any());
     }
 
     @Test
     public void onStateUpdate_onPreStateUpdateNotCalled_stillPropagatesData() {
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
-        mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
+        mNodeUnderTest.getLhsIncomingCallback().onData(5);
+        mNodeUnderTest.getRhsIncomingCallback().onData(6);
         verify(mMockCallback, never()).onPreUpdate();
         verify(mMockCallback).onData(11);
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void onStateUpdate_onStateUpdate_eachSidePendingUpdateHandledSeparately() {
         // Seed with some real data
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
-        mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
+        mNodeUnderTest.getLhsIncomingCallback().onData(5);
+        mNodeUnderTest.getRhsIncomingCallback().onData(6);
         reset(mMockCallback);
 
-        mSumNodeUnderTest.getLhsUpstreamCallback().onPreUpdate();
-        verify(mMockCallback).onPreUpdate();
-        mSumNodeUnderTest.getRhsUpstreamCallback().onData(10);
+        mNodeUnderTest.getLhsIncomingCallback().onPreUpdate();
+        mNodeUnderTest.getRhsIncomingCallback().onData(10);
 
         // The "is update pending" counters should be evaluated separately here, so the incoming
         // update on the RHS should not count towards the state update from the LHS
         verify(mMockCallback, never()).onData(any());
 
         // And now, fulfilling the LHS should still cause an update to be fired.
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(20);
+        mNodeUnderTest.getLhsIncomingCallback().onData(20);
         verify(mMockCallback).onData(30);
     }
-
-    @Test
-    public void onStateUpdate_secondTimeOnlyOneSide_doesNotWaitForOtherSide() {
-        // Seed with some real data
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(5);
-        mSumNodeUnderTest.getRhsUpstreamCallback().onData(6);
-        reset(mMockCallback);
-
-        // Only on the left.
-        mSumNodeUnderTest.getLhsUpstreamCallback().onData(10);
-
-        // Not waiting for the right.
-        verify(mMockCallback).onData(16);
-    }
-
-    @Test
-    public void onInvalidated_propagatesAfterBothSides() {
-        mSumNodeUnderTest.getLhsUpstreamCallback().onInvalidated();
-        verifyNoMoreInteractions(mMockCallback);
-
-        mSumNodeUnderTest.getRhsUpstreamCallback().onData(5);
-        verify(mMockCallback).onInvalidated();
-    }
-
-    /** Same as {@link org.mockito.Mockito#reset} but suppresses the unchecked warning. */
-    @SuppressWarnings("unchecked")
-    private <T> void reset(T mock) {
-        org.mockito.Mockito.reset(mock);
-    }
 }
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java
index 8930229..4981754 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/FloatNodeTest.java
@@ -313,7 +313,7 @@
 
         float lhsValue = 6.6f;
         FixedFloat lhsProtoNode = FixedFloat.newBuilder().setValue(lhsValue).build();
-        FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsUpstreamCallback());
+        FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsIncomingCallback());
         lhsNode.init();
 
         float oldRhsValue = 6.5f;
@@ -326,7 +326,7 @@
                                         .build()));
         StateFloatSource rhsProtoNode = StateFloatSource.newBuilder().setSourceKey("foo").build();
         StateFloatSourceNode rhsNode =
-                new StateFloatSourceNode(oss, rhsProtoNode, node.getRhsUpstreamCallback());
+                new StateFloatSourceNode(oss, rhsProtoNode, node.getRhsIncomingCallback());
 
         rhsNode.preInit();
         rhsNode.init();
@@ -442,8 +442,7 @@
                         .build();
         AddToListCallback<Float> addToListCallback = new AddToListCallback<>(results);
         AnimatableFixedFloatNode node =
-                new AnimatableFixedFloatNode(
-                        protoNode, addToListCallback, quotaManager);
+                new AnimatableFixedFloatNode(protoNode, addToListCallback, quotaManager);
         node.setVisibility(true);
 
         node.preInit();
@@ -469,8 +468,7 @@
                         .build();
         AddToListCallback<Float> addToListCallback = new AddToListCallback<>(results);
         AnimatableFixedFloatNode node =
-                new AnimatableFixedFloatNode(
-                        protoNode, addToListCallback, quotaManager);
+                new AnimatableFixedFloatNode(protoNode, addToListCallback, quotaManager);
         node.setVisibility(false);
 
         node.preInit();
@@ -524,9 +522,7 @@
         AddToListCallback<Float> addToListCallback = new AddToListCallback<>(results);
         DynamicAnimatedFloatNode floatNode =
                 new DynamicAnimatedFloatNode(
-                        addToListCallback,
-                        AnimationSpec.getDefaultInstance(),
-                        quotaManager);
+                        addToListCallback, AnimationSpec.getDefaultInstance(), quotaManager);
         floatNode.setVisibility(false);
         StateFloatSourceNode stateNode =
                 new StateFloatSourceNode(
@@ -584,10 +580,10 @@
         ArithmeticFloatNode node = new ArithmeticFloatNode(protoNode, receiver);
 
         FixedFloat lhsProtoNode = FixedFloat.newBuilder().setValue(lhs).build();
-        FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsUpstreamCallback());
+        FixedFloatNode lhsNode = new FixedFloatNode(lhsProtoNode, node.getLhsIncomingCallback());
 
         FixedFloat rhsProtoNode = FixedFloat.newBuilder().setValue(rhs).build();
-        FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsUpstreamCallback());
+        FixedFloatNode rhsNode = new FixedFloatNode(rhsProtoNode, node.getRhsIncomingCallback());
         lhsNode.preInit();
         rhsNode.preInit();
 
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java
index ab6d45a..83f7ca7 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/Int32NodesTest.java
@@ -704,10 +704,10 @@
         ArithmeticInt32Node node = new ArithmeticInt32Node(protoNode, receiver);
 
         FixedInt32 lhsProtoNode = FixedInt32.newBuilder().setValue(lhs).build();
-        FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsUpstreamCallback());
+        FixedInt32Node lhsNode = new FixedInt32Node(lhsProtoNode, node.getLhsIncomingCallback());
 
         FixedInt32 rhsProtoNode = FixedInt32.newBuilder().setValue(rhs).build();
-        FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsUpstreamCallback());
+        FixedInt32Node rhsNode = new FixedInt32Node(rhsProtoNode, node.getRhsIncomingCallback());
         lhsNode.preInit();
         rhsNode.preInit();
 
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StringNodesTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StringNodesTest.java
index 1e74a05..5f23425 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StringNodesTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/StringNodesTest.java
@@ -46,6 +46,7 @@
 @RunWith(AndroidJUnit4.class)
 public class StringNodesTest {
     private static final AppDataKey<DynamicString> KEY_FOO = new AppDataKey<>("foo");
+
     @Test
     public void fixedStringNodeTest() {
         List<String> results = new ArrayList<>();
@@ -188,11 +189,11 @@
         FixedStringNode lhsNode =
                 new FixedStringNode(
                         FixedString.newBuilder().setValue(string300chars).build(),
-                        node.getLhsUpstreamCallback());
+                        node.getLhsIncomingCallback());
         FixedStringNode rhsNode =
                 new FixedStringNode(
                         FixedString.newBuilder().setValue(string300chars).build(),
-                        node.getRhsUpstreamCallback());
+                        node.getRhsIncomingCallback());
         lhsNode.preInit();
         lhsNode.init();
         rhsNode.preInit();
diff --git a/wear/protolayout/protolayout-expression/build.gradle b/wear/protolayout/protolayout-expression/build.gradle
index e21ab55..e1fbc64 100644
--- a/wear/protolayout/protolayout-expression/build.gradle
+++ b/wear/protolayout/protolayout-expression/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     annotationProcessor(libs.nullaway)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     implementation("androidx.annotation:annotation-experimental:1.4.1")
     implementation("androidx.collection:collection:1.2.0")
@@ -61,5 +61,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Create dynamic expressions (for late evaluation by a remote evaluator)."
-    metalavaK2UastEnabled = true
 }
diff --git a/wear/protolayout/protolayout-material-core/build.gradle b/wear/protolayout/protolayout-material-core/build.gradle
index ca1740a..d4c27e0 100644
--- a/wear/protolayout/protolayout-material-core/build.gradle
+++ b/wear/protolayout/protolayout-material-core/build.gradle
@@ -43,7 +43,7 @@
 
 dependencies {
     annotationProcessor(libs.nullaway)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:protolayout:protolayout"))
     implementation(project(":wear:protolayout:protolayout-proto"))
 
@@ -65,5 +65,4 @@
     inceptionYear = "2023"
     description = "Material Core components library for ProtoLayout. This library contains" +
             "themeless components that are shared with ProtoLayout Material libraries."
-    metalavaK2UastEnabled = true
 }
diff --git a/wear/protolayout/protolayout-material/build.gradle b/wear/protolayout/protolayout-material/build.gradle
index a979b44..37c2ec8 100644
--- a/wear/protolayout/protolayout-material/build.gradle
+++ b/wear/protolayout/protolayout-material/build.gradle
@@ -31,7 +31,7 @@
 
 dependencies {
     annotationProcessor(libs.nullaway)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:protolayout:protolayout"))
 
     implementation(project(":wear:protolayout:protolayout-material-core"))
@@ -83,5 +83,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2023"
     description = "Material components library for ProtoLayout."
-    metalavaK2UastEnabled = true
 }
diff --git a/wear/protolayout/protolayout-material/lint-baseline.xml b/wear/protolayout/protolayout-material/lint-baseline.xml
index 4489b50..f36285f 100644
--- a/wear/protolayout/protolayout-material/lint-baseline.xml
+++ b/wear/protolayout/protolayout-material/lint-baseline.xml
@@ -1,20 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
-        errorLine1="            Thread.sleep(1000);"
-        errorLine2="                   ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/wear/protolayout/material/RunnerUtils.java"/>
-    </issue>
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="            Thread.sleep(1000);"
-        errorLine2="                   ~~~~~">
+        errorLine1="                Thread.sleep(100);"
+        errorLine2="                       ~~~~~">
         <location
             file="src/androidTest/java/androidx/wear/protolayout/material/RunnerUtils.java"/>
     </issue>
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
index e5504f0..08c81d9 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
@@ -101,7 +101,6 @@
         @NonNull private final Context mContext;
         @Nullable private LayoutElement mCustomContent;
         @Nullable private String mImageResourceId = null;
-        private boolean mIsIconOnly = false;
         @Nullable private String mPrimaryLabel = null;
         @Nullable private String mSecondaryLabel = null;
         @Nullable private StringProp mContentDescription = null;
@@ -200,7 +199,6 @@
         @NonNull
         public Builder setPrimaryLabelContent(@NonNull String primaryLabel) {
             this.mPrimaryLabel = primaryLabel;
-            this.mIsIconOnly = false;
             this.mCustomContent = null;
             return this;
         }
@@ -246,7 +244,6 @@
         @NonNull
         public Builder setSecondaryLabelContent(@NonNull String secondaryLabel) {
             this.mSecondaryLabel = secondaryLabel;
-            this.mIsIconOnly = false;
             this.mCustomContent = null;
             return this;
         }
@@ -265,20 +262,6 @@
         }
 
         /**
-         * Sets the content of the {@link Chip} to be *only* icon. Any previously added custom
-         * content will be overridden. Provided icon will be tinted to the given content color from
-         * {@link ChipColors}. This icon should be image with chosen alpha channel and not an actual
-         * image. Should be used only for creating a {@link CompactChip}.
-         */
-        @NonNull
-        Builder setIconOnlyContent(@NonNull String imageResourceId) {
-            this.mImageResourceId = imageResourceId;
-            this.mIsIconOnly = true;
-            this.mCustomContent = null;
-            return this;
-        }
-
-        /**
          * Sets the colors for the {@link Chip}. If set, {@link ChipColors#getBackgroundColor()}
          * will be used for the background of the button, {@link ChipColors#getContentColor()} for
          * main text, {@link ChipColors#getSecondaryContentColor()} for label text and {@link
@@ -368,6 +351,10 @@
             }
         }
 
+        private boolean isIconOnly() {
+            return mPrimaryLabel == null && mSecondaryLabel == null && mCustomContent == null;
+        }
+
         @SuppressWarnings("deprecation") // TEXT_OVERFLOW_ELLIPSIZE_END as existing API
         private void setCorrectContent() {
             if (mImageResourceId != null) {
@@ -383,7 +370,7 @@
                                 .build();
                 mCoreBuilder.setIconContent(icon);
 
-                if (mIsIconOnly) {
+                if (isIconOnly()) {
                     return;
                 }
             }
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java
index ca2cd83..47115ab 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java
@@ -212,11 +212,7 @@
             }
 
             if (mIconResourceId != null) {
-                if (mText != null) {
-                    chipBuilder.setIconContent(mIconResourceId);
-                } else {
-                    chipBuilder.setIconOnlyContent(mIconResourceId);
-                }
+                chipBuilder.setIconContent(mIconResourceId);
                 chipBuilder.setIconSize(COMPACT_ICON_SIZE);
             }
 
diff --git a/wear/protolayout/protolayout-proto/build.gradle b/wear/protolayout/protolayout-proto/build.gradle
index 4c5e8fd..a29f7a6 100644
--- a/wear/protolayout/protolayout-proto/build.gradle
+++ b/wear/protolayout/protolayout-proto/build.gradle
@@ -33,7 +33,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     api(project(":wear:protolayout:protolayout-external-protobuf"))
     // Must be compileOnly to not bring in protobufLite in runtime
     // Repackaged protobufLite brought in by
diff --git a/wear/protolayout/protolayout-renderer/build.gradle b/wear/protolayout/protolayout-renderer/build.gradle
index c77342e..7e3ad42 100644
--- a/wear/protolayout/protolayout-renderer/build.gradle
+++ b/wear/protolayout/protolayout-renderer/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     annotationProcessor(libs.nullaway)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.7.0")
     api(libs.guava)
     implementation("androidx.core:core:1.3.2")
@@ -65,5 +65,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Render ProtoLayouts to an Android surface"
-    metalavaK2UastEnabled = true
 }
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java
index 92494b2..c1c0e22 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutThemeImpl.java
@@ -184,8 +184,7 @@
             ImmutableSet.of(
                     FONT_NAME_DEFAULT,
                     FONT_NAME_ROBOTO,
-                    // TODO(b/348207120): Enable Roboto Flex font.
-                    // FONT_NAME_ROBOTO_FLEX,
+                    FONT_NAME_ROBOTO_FLEX,
                     FONT_NAME_LEGACY_VARIANT_TITLE,
                     FONT_NAME_LEGACY_VARIANT_BODY);
 
diff --git a/wear/protolayout/protolayout-renderer/src/main/res/values/styles.xml b/wear/protolayout/protolayout-renderer/src/main/res/values/styles.xml
index 320e47a..a55f2c1 100644
--- a/wear/protolayout/protolayout-renderer/src/main/res/values/styles.xml
+++ b/wear/protolayout/protolayout-renderer/src/main/res/values/styles.xml
@@ -13,9 +13,15 @@
   </style>
 
   <style name="ProtoLayoutRobotoFont">
-    <item name="protoLayoutNormalFont">Roboto-Regular.ttf</item>
-    <item name="protoLayoutMediumFont">Roboto-Medium.ttf</item>
-    <item name="protoLayoutBoldFont">Roboto-Regular.ttf</item>
+    <item name="protoLayoutNormalFont">roboto-regular</item>
+    <item name="protoLayoutMediumFont">roboto-medium</item>
+    <item name="protoLayoutBoldFont">roboto-bold</item>
+  </style>
+
+  <style name="ProtoLayoutRobotoFlexFont">
+    <item name="protoLayoutNormalFont">roboto-flex</item>
+    <item name="protoLayoutMediumFont">roboto-flex</item>
+    <item name="protoLayoutBoldFont">roboto-flex</item>
   </style>
 
   <style name="ProtoLayoutBaseTheme">
@@ -24,7 +30,6 @@
     <item name="protoLayoutBodyFont">@style/ProtoLayoutBaseFont</item>
     <item name="protoLayoutDefaultSystemFont">@style/ProtoLayoutBaseFont</item>
     <item name="protoLayoutRobotoFont">@style/ProtoLayoutRobotoFont</item>
-<!--  TODO(b/348207120): Enable Roboto Flex font.  -->
-    <item name="protoLayoutRobotoFlexFont">@style/ProtoLayoutRobotoFont</item>
+    <item name="protoLayoutRobotoFlexFont">@style/ProtoLayoutRobotoFlexFont</item>
   </style>
 </resources>
diff --git a/wear/protolayout/protolayout/build.gradle b/wear/protolayout/protolayout/build.gradle
index 8b883b5..94d8c08 100644
--- a/wear/protolayout/protolayout/build.gradle
+++ b/wear/protolayout/protolayout/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     annotationProcessor(libs.nullaway)
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:protolayout:protolayout-expression"))
 
     implementation("androidx.annotation:annotation-experimental:1.4.1")
@@ -61,6 +61,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "Create layouts that can be rendered by a remote host."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/tiles/tiles-material/build.gradle b/wear/tiles/tiles-material/build.gradle
index 038a4cd..5a1fa54 100644
--- a/wear/tiles/tiles-material/build.gradle
+++ b/wear/tiles/tiles-material/build.gradle
@@ -32,7 +32,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:tiles:tiles"))
     implementation(project(":wear:tiles:tiles-proto"))
 
@@ -79,6 +79,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Material components library for Android Wear Tiles."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/tiles/tiles-proto/build.gradle b/wear/tiles/tiles-proto/build.gradle
index 3930e16..a1736dd 100644
--- a/wear/tiles/tiles-proto/build.gradle
+++ b/wear/tiles/tiles-proto/build.gradle
@@ -36,7 +36,7 @@
     constraints {
         api(project(":wear:protolayout:protolayout-proto"))
     }
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     api(project(":wear:protolayout:protolayout-proto"))
     // Must be compileOnly to not bring in protobufLite in runtime
     // Repackaged protobufLite brought in by
diff --git a/wear/tiles/tiles-renderer/build.gradle b/wear/tiles/tiles-renderer/build.gradle
index 013efbb..851ee17 100644
--- a/wear/tiles/tiles-renderer/build.gradle
+++ b/wear/tiles/tiles-renderer/build.gradle
@@ -33,7 +33,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.guavaListenableFuture)
 
     implementation "androidx.concurrent:concurrent-futures:1.1.0"
@@ -105,7 +105,6 @@
     inceptionYear = "2021"
     description = "Android Wear Tiles Renderer components. These components can be used to parse " +
             "and render an already constructed Wear Tile."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/wear/tiles/tiles-renderer/lint-baseline.xml b/wear/tiles/tiles-renderer/lint-baseline.xml
index 2d98f2c..1395f3b 100644
--- a/wear/tiles/tiles-renderer/lint-baseline.xml
+++ b/wear/tiles/tiles-renderer/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="UnspecifiedRegisterReceiverFlag"
@@ -13,8 +13,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Resources.toProto can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
-        errorLine1="                    res.toProto()"
-        errorLine2="                        ~~~~~~~">
+        errorLine1="                    androidx.wear.tiles.ResourceBuilders.Resources.fromProto(res.toProto())"
+        errorLine2="                                                                                 ~~~~~~~">
         <location
             file="src/main/java/androidx/wear/tiles/connection/DefaultTileClient.kt"/>
     </issue>
@@ -40,8 +40,8 @@
     <issue
         id="RestrictedApiAndroidX"
         message="Resources.fromProto can only be called from within the same library group (referenced groupId=`androidx.wear.protolayout` from groupId=`androidx.wear.tiles`)"
-        errorLine1="                            ResourceBuilders.Resources.fromProto(resources))"
-        errorLine2="                                                       ~~~~~~~~~">
+        errorLine1="                        continuation.resume(ResourceBuilders.Resources.fromProto(resources))"
+        errorLine2="                                                                       ~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/tiles/connection/DefaultTileClient.kt"/>
     </issue>
diff --git a/wear/tiles/tiles-testing/build.gradle b/wear/tiles/tiles-testing/build.gradle
index 9ee133a..0f189d8 100644
--- a/wear/tiles/tiles-testing/build.gradle
+++ b/wear/tiles/tiles-testing/build.gradle
@@ -32,7 +32,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.guavaListenableFuture)
     api(project(":wear:tiles:tiles-renderer"))
 
@@ -72,6 +72,5 @@
     type = LibraryType.PUBLISHED_TEST_LIBRARY
     inceptionYear = "2021"
     description = "Testing utilities for Android Wear Tiles."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/tiles/tiles-tooling-preview/build.gradle b/wear/tiles/tiles-tooling-preview/build.gradle
index 1690145..c77141c 100644
--- a/wear/tiles/tiles-tooling-preview/build.gradle
+++ b/wear/tiles/tiles-tooling-preview/build.gradle
@@ -36,7 +36,7 @@
     api(project(":wear:protolayout:protolayout-expression"))
     api(project(":wear:tiles:tiles"))
     api("androidx.wear:wear-tooling-preview:1.0.0")
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
 }
 
 android {
@@ -52,6 +52,5 @@
     inceptionYear = "2023"
     description = "Wear Tile tooling library. This library provides the API required to declare" +
             " @Preview on previewable methods in the IDE."
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/tiles/tiles-tooling/build.gradle b/wear/tiles/tiles-tooling/build.gradle
index 394d0db..f4ff483 100644
--- a/wear/tiles/tiles-tooling/build.gradle
+++ b/wear/tiles/tiles-tooling/build.gradle
@@ -48,6 +48,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2023"
     description = "A set of tools that are used to preview Tile components in Android Studio"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/tiles/tiles/build.gradle b/wear/tiles/tiles/build.gradle
index 9d64b88..316a1fa 100644
--- a/wear/tiles/tiles/build.gradle
+++ b/wear/tiles/tiles/build.gradle
@@ -29,7 +29,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:protolayout:protolayout"))
     api(project(":wear:protolayout:protolayout-expression"))
     api(libs.guavaListenableFuture)
@@ -70,5 +70,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Wear Tiles"
-    metalavaK2UastEnabled = true
 }
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
index 7d9a635..bf1dcec 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/TileBuilders.java
@@ -229,6 +229,6 @@
 
         /** The current version of the Tiles schema in use. */
         public static final VersionInfo CURRENT =
-                VersionInfo.newBuilder().setMajor(1).setMinor(400).build();
+                VersionInfo.newBuilder().setMajor(1).setMinor(500).build();
     }
 }
diff --git a/wear/watchface/watchface-client-guava/build.gradle b/wear/watchface/watchface-client-guava/build.gradle
index e268168..a393a6b 100644
--- a/wear/watchface/watchface-client-guava/build.gradle
+++ b/wear/watchface/watchface-client-guava/build.gradle
@@ -51,7 +51,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Guava wrappers for the Androidx Wear Watchface library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/wear/watchface/watchface-client/api/current.txt b/wear/watchface/watchface-client/api/current.txt
index ee4e6c4..f1626c9 100644
--- a/wear/watchface/watchface-client/api/current.txt
+++ b/wear/watchface/watchface-client/api/current.txt
@@ -227,6 +227,7 @@
   }
 
   public static final class WatchFaceControlClient.ServiceStartFailureException extends java.lang.Exception {
+    ctor public WatchFaceControlClient.ServiceStartFailureException();
     ctor public WatchFaceControlClient.ServiceStartFailureException(optional String message);
   }
 
@@ -255,6 +256,7 @@
   }
 
   public static final class WatchFaceMetadataClient.ServiceStartFailureException extends java.lang.Exception {
+    ctor public WatchFaceMetadataClient.ServiceStartFailureException();
     ctor public WatchFaceMetadataClient.ServiceStartFailureException(optional String message);
   }
 
diff --git a/wear/watchface/watchface-client/api/restricted_current.txt b/wear/watchface/watchface-client/api/restricted_current.txt
index ee4e6c4..f1626c9 100644
--- a/wear/watchface/watchface-client/api/restricted_current.txt
+++ b/wear/watchface/watchface-client/api/restricted_current.txt
@@ -227,6 +227,7 @@
   }
 
   public static final class WatchFaceControlClient.ServiceStartFailureException extends java.lang.Exception {
+    ctor public WatchFaceControlClient.ServiceStartFailureException();
     ctor public WatchFaceControlClient.ServiceStartFailureException(optional String message);
   }
 
@@ -255,6 +256,7 @@
   }
 
   public static final class WatchFaceMetadataClient.ServiceStartFailureException extends java.lang.Exception {
+    ctor public WatchFaceMetadataClient.ServiceStartFailureException();
     ctor public WatchFaceMetadataClient.ServiceStartFailureException(optional String message);
   }
 
diff --git a/wear/watchface/watchface-client/build.gradle b/wear/watchface/watchface-client/build.gradle
index 1b8300c..075f4bfe 100644
--- a/wear/watchface/watchface-client/build.gradle
+++ b/wear/watchface/watchface-client/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:watchface:watchface"))
     api(project(":wear:watchface:watchface-data"))
     api(project(":wear:watchface:watchface-style"))
@@ -76,6 +76,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Client library for controlling androidx watchfaces"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/watchface/watchface-complications-data-source-ktx/build.gradle b/wear/watchface/watchface-complications-data-source-ktx/build.gradle
index 735a43a..716ec1a 100644
--- a/wear/watchface/watchface-complications-data-source-ktx/build.gradle
+++ b/wear/watchface/watchface-complications-data-source-ktx/build.gradle
@@ -48,7 +48,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2021"
     description = "Kotlin suspend wrapper for Android Wear Complications Data Source"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/wear/watchface/watchface-complications-data-source/build.gradle b/wear/watchface/watchface-complications-data-source/build.gradle
index e3453eb..b3db5e5 100644
--- a/wear/watchface/watchface-complications-data-source/build.gradle
+++ b/wear/watchface/watchface-complications-data-source/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:watchface:watchface-complications"))
     api(project(":wear:watchface:watchface-complications-data"))
 
@@ -65,6 +65,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Wear Complications Data Source"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/watchface/watchface-complications-data-source/lint-baseline.xml b/wear/watchface/watchface-complications-data-source/lint-baseline.xml
index 5edf002..0aede8c 100644
--- a/wear/watchface/watchface-complications-data-source/lint-baseline.xml
+++ b/wear/watchface/watchface-complications-data-source/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.2.0-alpha14" type="baseline" client="cli" dependencies="false" name="AGP (8.2.0-alpha14)" variant="all" version="8.2.0-alpha14">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="NewApi"
@@ -40,8 +40,8 @@
     <issue
         id="NewApi"
         message="Field requires API level 33 (current min is 26): `TargetWatchFaceSafety`"
-        errorLine1="                        ?: TargetWatchFaceSafety.UNKNOWN"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                    ) ?: TargetWatchFaceSafety.UNKNOWN"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt"/>
     </issue>
@@ -58,8 +58,8 @@
     <issue
         id="NewApi"
         message="Field requires API level 33 (current min is 26): `TargetWatchFaceSafety`"
-        errorLine1="                        ?: TargetWatchFaceSafety.UNKNOWN"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~">
+        errorLine1="                    ) ?: TargetWatchFaceSafety.UNKNOWN"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt"/>
     </issue>
diff --git a/wear/watchface/watchface-complications-data/build.gradle b/wear/watchface/watchface-complications-data/build.gradle
index e66547f..47a9a5a 100644
--- a/wear/watchface/watchface-complications-data/build.gradle
+++ b/wear/watchface/watchface-complications-data/build.gradle
@@ -33,13 +33,12 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.versionedparcelable:versionedparcelable:1.1.0")
     api("androidx.wear.protolayout:protolayout-expression:1.0.0")
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesAndroid)
 
-    implementation("androidx.annotation:annotation:1.2.0")
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.preference:preference:1.1.0")
     implementation("androidx.wear.protolayout:protolayout-expression-pipeline:1.0.0-beta01")
@@ -80,6 +79,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Wear Complications Data"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/watchface/watchface-complications-data/src/main/res/values-es-rUS/complication_strings.xml b/wear/watchface/watchface-complications-data/src/main/res/values-es-rUS/complication_strings.xml
index fc52b84..7e2970b 100644
--- a/wear/watchface/watchface-complications-data/src/main/res/values-es-rUS/complication_strings.xml
+++ b/wear/watchface/watchface-complications-data/src/main/res/values-es-rUS/complication_strings.xml
@@ -19,12 +19,12 @@
     <string name="time_difference_short_days_and_hours" msgid="2384500200491672870">"<xliff:g id="SHORT_DAYS">%1$s</xliff:g> <xliff:g id="SHORT_HOURS">%2$s</xliff:g>"</string>
     <string name="time_difference_short_hours_and_minutes" msgid="8030280938566792191">"<xliff:g id="SHORT_HOURS">%1$s</xliff:g> <xliff:g id="SHORT_MINUTES">%2$s</xliff:g>"</string>
     <plurals name="time_difference_words_days" formatted="false" msgid="5109682345086392533">
-      <item quantity="many"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> días</item>
+      <item quantity="many"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> de días</item>
       <item quantity="other"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> días</item>
       <item quantity="one"><xliff:g id="NUMBER_OF_DAYS_0">%d</xliff:g> día</item>
     </plurals>
     <plurals name="time_difference_words_hours" formatted="false" msgid="3172220157267000186">
-      <item quantity="many"><xliff:g id="NUMBER_OF_HOURS_1">%d</xliff:g> horas</item>
+      <item quantity="many"><xliff:g id="NUMBER_OF_HOURS_1">%d</xliff:g> de horas</item>
       <item quantity="other"><xliff:g id="NUMBER_OF_HOURS_1">%d</xliff:g> horas</item>
       <item quantity="one"><xliff:g id="NUMBER_OF_HOURS_0">%d</xliff:g> hora</item>
     </plurals>
diff --git a/wear/watchface/watchface-complications-data/src/main/res/values-fr-rCA/complication_strings.xml b/wear/watchface/watchface-complications-data/src/main/res/values-fr-rCA/complication_strings.xml
index 764f019..a70cc6e 100644
--- a/wear/watchface/watchface-complications-data/src/main/res/values-fr-rCA/complication_strings.xml
+++ b/wear/watchface/watchface-complications-data/src/main/res/values-fr-rCA/complication_strings.xml
@@ -20,7 +20,7 @@
     <string name="time_difference_short_hours_and_minutes" msgid="8030280938566792191">"<xliff:g id="SHORT_HOURS">%1$s</xliff:g> <xliff:g id="SHORT_MINUTES">%2$s</xliff:g>"</string>
     <plurals name="time_difference_words_days" formatted="false" msgid="5109682345086392533">
       <item quantity="one"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> jour</item>
-      <item quantity="many"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> jours</item>
+      <item quantity="many"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> de jours</item>
       <item quantity="other"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> jours</item>
     </plurals>
     <plurals name="time_difference_words_hours" formatted="false" msgid="3172220157267000186">
diff --git a/wear/watchface/watchface-complications-data/src/main/res/values-it/complication_strings.xml b/wear/watchface/watchface-complications-data/src/main/res/values-it/complication_strings.xml
index e9bab9f..f0cce68 100644
--- a/wear/watchface/watchface-complications-data/src/main/res/values-it/complication_strings.xml
+++ b/wear/watchface/watchface-complications-data/src/main/res/values-it/complication_strings.xml
@@ -19,12 +19,12 @@
     <string name="time_difference_short_days_and_hours" msgid="2384500200491672870">"<xliff:g id="SHORT_DAYS">%1$s</xliff:g> <xliff:g id="SHORT_HOURS">%2$s</xliff:g>"</string>
     <string name="time_difference_short_hours_and_minutes" msgid="8030280938566792191">"<xliff:g id="SHORT_HOURS">%1$s</xliff:g> <xliff:g id="SHORT_MINUTES">%2$s</xliff:g>"</string>
     <plurals name="time_difference_words_days" formatted="false" msgid="5109682345086392533">
-      <item quantity="many"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> giorni</item>
+      <item quantity="many"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> di giorni</item>
       <item quantity="other"><xliff:g id="NUMBER_OF_DAYS_1">%d</xliff:g> giorni</item>
       <item quantity="one"><xliff:g id="NUMBER_OF_DAYS_0">%d</xliff:g> giorno</item>
     </plurals>
     <plurals name="time_difference_words_hours" formatted="false" msgid="3172220157267000186">
-      <item quantity="many"><xliff:g id="NUMBER_OF_HOURS_1">%d</xliff:g> ore</item>
+      <item quantity="many"><xliff:g id="NUMBER_OF_HOURS_1">%d</xliff:g> di ore</item>
       <item quantity="other"><xliff:g id="NUMBER_OF_HOURS_1">%d</xliff:g> ore</item>
       <item quantity="one"><xliff:g id="NUMBER_OF_HOURS_0">%d</xliff:g> ora</item>
     </plurals>
diff --git a/wear/watchface/watchface-complications-rendering/build.gradle b/wear/watchface/watchface-complications-rendering/build.gradle
index b8a8897..1d5e136a 100644
--- a/wear/watchface/watchface-complications-rendering/build.gradle
+++ b/wear/watchface/watchface-complications-rendering/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:watchface:watchface-complications-data"))
     api(project(":wear:watchface:watchface"))
 
@@ -72,6 +72,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Support for rendering complications on the watch face"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/watchface/watchface-complications/build.gradle b/wear/watchface/watchface-complications/build.gradle
index c1e42cf..f238bd8 100644
--- a/wear/watchface/watchface-complications/build.gradle
+++ b/wear/watchface/watchface-complications/build.gradle
@@ -32,11 +32,10 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
     api(project(":wear:watchface:watchface-complications-data"))
     implementation("androidx.core:core:1.1.0")
-    implementation("androidx.annotation:annotation:1.2.0")
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
@@ -70,6 +69,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Android Wear Complications"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/watchface/watchface-data/build.gradle b/wear/watchface/watchface-data/build.gradle
index 1e727fb..dc4a433 100644
--- a/wear/watchface/watchface-data/build.gradle
+++ b/wear/watchface/watchface-data/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.versionedparcelable:versionedparcelable:1.1.0")
     api(project(":wear:watchface:watchface-complications-data"))
     api(libs.kotlinStdlib)
@@ -59,6 +59,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Wear Watchface hidden AIDL implementaion details"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/watchface/watchface-editor-guava/build.gradle b/wear/watchface/watchface-editor-guava/build.gradle
index 13c6935..6baca89 100644
--- a/wear/watchface/watchface-editor-guava/build.gradle
+++ b/wear/watchface/watchface-editor-guava/build.gradle
@@ -49,7 +49,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Guava wrappers for the Androidx Wear Watchface Editor library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/wear/watchface/watchface-editor/build.gradle b/wear/watchface/watchface-editor/build.gradle
index 3b439c5..0bd2f0e 100644
--- a/wear/watchface/watchface-editor/build.gradle
+++ b/wear/watchface/watchface-editor/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.versionedparcelable:versionedparcelable:1.1.0")
     api("androidx.activity:activity:1.2.0")
     api(project(":wear:watchface:watchface-client"))
@@ -68,7 +68,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Classes for building Android Wear watchface editors"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":wear:watchface:watchface-samples"))
 }
diff --git a/wear/watchface/watchface-guava/build.gradle b/wear/watchface/watchface-guava/build.gradle
index 325fb31..319eb9d 100644
--- a/wear/watchface/watchface-guava/build.gradle
+++ b/wear/watchface/watchface-guava/build.gradle
@@ -56,7 +56,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "Guava wrappers for the Androidx Wear Watchface library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/wear/watchface/watchface-style/build.gradle b/wear/watchface/watchface-style/build.gradle
index f7a6b92..91d5360 100644
--- a/wear/watchface/watchface-style/build.gradle
+++ b/wear/watchface/watchface-style/build.gradle
@@ -59,7 +59,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(project(":wear:watchface:watchface-complications"))
     api(project(":wear:watchface:watchface-data"))
     api(libs.kotlinStdlib)
@@ -106,6 +106,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Wear Watchface Style"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
index 2d88c40..bfbf053 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
@@ -24,6 +24,7 @@
 import androidx.wear.watchface.complications.IllegalNodeException
 import androidx.wear.watchface.complications.iterate
 import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
+import androidx.wear.watchface.style.UserStyleSetting.LargeCustomValueUserStyleSetting.Companion.CUSTOM_VALUE_USER_STYLE_SETTING_ID
 import androidx.wear.watchface.style.UserStyleSetting.Option
 import androidx.wear.watchface.style.data.UserStyleSchemaWireFormat
 import androidx.wear.watchface.style.data.UserStyleWireFormat
@@ -379,10 +380,20 @@
         "{" +
             userStyleMap.entries.joinToString(
                 transform = {
-                    try {
-                        it.key + "=" + it.value.decodeToString()
-                    } catch (e: Exception) {
-                        it.key + "=" + it.value
+                    when (it.key) {
+                        /**
+                         * For CustomValueUserStyleSetting and LargeCustomValueUserStyleSetting, we
+                         * display only the length of the value. These style settings is always use
+                         * the same key (CustomValue).
+                         */
+                        CUSTOM_VALUE_USER_STYLE_SETTING_ID ->
+                            it.key + "=[binary data, length: ${it.value.size}]"
+                        else ->
+                            try {
+                                it.key + "=" + it.value.decodeToString()
+                            } catch (e: Exception) {
+                                it.key + "=" + it.value
+                            }
                     }
                 }
             ) +
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
index 0f7ca2d..847a4ee 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
@@ -2896,6 +2896,8 @@
             override fun write(dos: DataOutputStream) {
                 dos.write(id.value)
             }
+
+            override fun toString(): String = "[binary data, length: ${customValue.size}]"
         }
 
         override fun getOptionForId(optionId: Option.Id): Option =
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 3029d3e..d4014c6 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
@@ -600,7 +600,8 @@
         assertThat(gothicStyleOption.toString()).isEqualTo("gothic_style")
         assertThat(DoubleRangeUserStyleSetting.DoubleRangeOption(12.3).toString()).isEqualTo("12.3")
         assertThat(LongRangeUserStyleSetting.LongRangeOption(123).toString()).isEqualTo("123")
-        assertThat(CustomValueOption("test".encodeToByteArray()).toString()).isEqualTo("test")
+        assertThat(CustomValueOption("test".encodeToByteArray()).toString())
+            .isEqualTo("[binary data, length: 4]")
     }
 
     @Test
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
index c98331c..ffb9f38 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
@@ -738,7 +738,10 @@
             UserStyleSchema(listOf(styleSetting1, styleSetting2, styleSetting3, styleSetting4))
 
         assertThat(schema.toString())
-            .isEqualTo("[{id1 : 1, 2}, {id2 : 3, 4}, {id3 : true, false}, {CustomValue : default}]")
+            .isEqualTo(
+                "[{id1 : 1, 2}, {id2 : 3, 4}, {id3 : true, false}, " +
+                    "{CustomValue : [binary data, length: 7]}]"
+            )
     }
 
     @Ignore
diff --git a/wear/watchface/watchface/build.gradle b/wear/watchface/watchface/build.gradle
index bf7d2be..8b5f470 100644
--- a/wear/watchface/watchface/build.gradle
+++ b/wear/watchface/watchface/build.gradle
@@ -32,7 +32,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.5.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.activity:activity:1.7.0")
     api(project(":wear:watchface:watchface-complications-data"))
     api(project(":wear:watchface:watchface-data"))
@@ -82,7 +82,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android Wear Watchface"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":wear:watchface:watchface-samples"))
 }
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
index 398ed47..707b462 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
@@ -1030,10 +1030,16 @@
      * The complication data sent by the system. This may contain a timeline out of which
      * [complicationData] is selected.
      */
-    private var timelineComplicationData: ComplicationData = NoDataComplicationData()
-    private var timelineEntries: List<ApiTimelineEntry>? = null
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public var timelineComplicationData: ComplicationData = NoDataComplicationData()
+        private set
 
-    private class ApiTimelineEntry(
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public var timelineEntries: List<ApiTimelineEntry>? = null
+        private set
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public class ApiTimelineEntry(
         val timelineStartEpochSecond: Long?,
         val timelineEndEpochSecond: Long?,
         val complicationData: ComplicationData
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 0e19a63..a687012 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -1739,9 +1739,7 @@
             synchronized(lock) {
                 val complications = overriddenComplications ?: HashMap(complicationsFlow.value)
                 for ((frozenSlot, previewData) in editedComplicationPreviewData) {
-                    if (
-                        complicationsFlow.value[frozenSlot]!!.dataSource != previewData.dataSource
-                    ) {
+                    if (complicationsFlow.value[frozenSlot]?.dataSource != previewData.dataSource) {
                         complications[frozenSlot] = EmptyComplicationData()
                     }
                 }
@@ -2601,7 +2599,7 @@
         override fun onVisibilityChanged(visible: Boolean): Unit =
             TraceEvent("onVisibilityChanged").use {
                 super.onVisibilityChanged(visible)
-                Log.i(TAG, "onVisibilityChanged($isVisible)")
+                Log.i(TAG, "onVisibilityChanged($visible)")
 
                 // In the WSL flow Home doesn't know when WallpaperService has actually launched a
                 // watchface after requesting a change. It used [Constants.ACTION_REQUEST_STATE] as
diff --git a/wear/wear-core/build.gradle b/wear/wear-core/build.gradle
index b8bf9d0..779ef2c 100644
--- a/wear/wear-core/build.gradle
+++ b/wear/wear-core/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
 
     api(libs.kotlinStdlib)
 
diff --git a/wear/wear-input-testing/build.gradle b/wear/wear-input-testing/build.gradle
index bd12932..ead0e47 100644
--- a/wear/wear-input-testing/build.gradle
+++ b/wear/wear-input-testing/build.gradle
@@ -46,5 +46,4 @@
     mavenVersion = LibraryVersions.WEAR_INPUT_TESTING
     inceptionYear = "2020"
     description = "Android Wear Support Input Testing Helpers"
-    metalavaK2UastEnabled = true
 }
diff --git a/wear/wear-input/build.gradle b/wear/wear-input/build.gradle
index 82fb5fb..6c12779 100644
--- a/wear/wear-input/build.gradle
+++ b/wear/wear-input/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
 
     testImplementation(libs.testExtJunit)
@@ -61,7 +61,6 @@
     mavenVersion = LibraryVersions.WEAR_INPUT
     inceptionYear = "2020"
     description = "Android Wear Support Input"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":wear:wear-input-samples"))
 }
diff --git a/wear/wear-input/samples/build.gradle b/wear/wear-input/samples/build.gradle
index 6fcf666..f145716 100644
--- a/wear/wear-input/samples/build.gradle
+++ b/wear/wear-input/samples/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
     api(project(":wear:wear-input"))
 
diff --git a/wear/wear-input/src/main/res/values-es/strings.xml b/wear/wear-input/src/main/res/values-es/strings.xml
index bb90bcc..1c05bba 100644
--- a/wear/wear-input/src/main/res/values-es/strings.xml
+++ b/wear/wear-input/src/main/res/values-es/strings.xml
@@ -19,7 +19,7 @@
     <string name="buttons_round_top_left_upper" msgid="2546930425604677415">"Parte superior izquierda, arriba"</string>
     <string name="buttons_rect_left_top" msgid="5658240954637179416">"Parte superior, lateral izquierdo"</string>
     <string name="buttons_rect_left_center" msgid="2090348335774325845">"Parte central izquierda"</string>
-    <string name="buttons_rect_left_bottom" msgid="2890382227404669440">"Parte inferior, lateral izquierdo"</string>
+    <string name="buttons_rect_left_bottom" msgid="2890382227404669440">"Parte inferior izquierda"</string>
     <string name="buttons_rect_right_top" msgid="8781047384037217975">"Parte superior, lateral derecho"</string>
     <string name="buttons_rect_right_center" msgid="9046922334870476704">"Parte central derecha"</string>
     <string name="buttons_rect_right_bottom" msgid="231110280868923853">"Parte inferior, lateral derecho"</string>
diff --git a/wear/wear-ongoing/build.gradle b/wear/wear-ongoing/build.gradle
index 164a186..53aed7d 100644
--- a/wear/wear-ongoing/build.gradle
+++ b/wear/wear-ongoing/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.6.0")
     api("androidx.versionedparcelable:versionedparcelable:1.1.1")
 
@@ -44,6 +44,5 @@
     mavenVersion = LibraryVersions.WEAR_ONGOING
     inceptionYear = "2021"
     description = "Android Wear Ongoing Activities"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/wear-phone-interactions/build.gradle b/wear/wear-phone-interactions/build.gradle
index 1945068..0e3a7e9 100644
--- a/wear/wear-phone-interactions/build.gradle
+++ b/wear/wear-phone-interactions/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.6.0")
     api("androidx.wear:wear:1.2.0")
     api(libs.kotlinStdlib)
@@ -77,7 +77,6 @@
     mavenVersion = LibraryVersions.WEAR_PHONE_INTERACTIONS
     inceptionYear = "2021"
     description = "Android Wear Phone Interactions"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":wear:wear-phone-interactions-samples"))
 }
diff --git a/wear/wear-remote-interactions/api/1.1.0-beta01.txt b/wear/wear-remote-interactions/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..754f9e9
--- /dev/null
+++ b/wear/wear-remote-interactions/api/1.1.0-beta01.txt
@@ -0,0 +1,48 @@
+// Signature format: 4.0
+package androidx.wear.remote.interactions {
+
+  public final class RemoteActivityHelper {
+    ctor public RemoteActivityHelper(android.content.Context context);
+    ctor public RemoteActivityHelper(android.content.Context context, optional java.util.concurrent.Executor executor);
+    method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getAvailabilityStatus();
+    method public static android.content.Intent? getTargetIntent(android.content.Intent intent);
+    method public static String? getTargetNodeId(android.content.Intent intent);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startRemoteActivity(android.content.Intent targetIntent);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startRemoteActivity(android.content.Intent targetIntent, optional String? targetNodeId);
+    property public final kotlinx.coroutines.flow.Flow<java.lang.Integer> availabilityStatus;
+    field public static final String ACTION_REMOTE_INTENT = "com.google.android.wearable.intent.action.REMOTE_INTENT";
+    field public static final androidx.wear.remote.interactions.RemoteActivityHelper.Companion Companion;
+    field public static final int RESULT_FAILED = 1; // 0x1
+    field public static final int RESULT_OK = 0; // 0x0
+    field public static final int STATUS_AVAILABLE = 3; // 0x3
+    field public static final int STATUS_TEMPORARILY_UNAVAILABLE = 2; // 0x2
+    field public static final int STATUS_UNAVAILABLE = 1; // 0x1
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class RemoteActivityHelper.Companion {
+    method public android.content.Intent? getTargetIntent(android.content.Intent intent);
+    method public String? getTargetNodeId(android.content.Intent intent);
+  }
+
+  public static final class RemoteActivityHelper.RemoteIntentException extends java.lang.Exception {
+    ctor public RemoteActivityHelper.RemoteIntentException(String message);
+  }
+
+  public final class WatchFaceConfigIntentHelper {
+    method public static String? getPeerIdExtra(android.content.Intent watchFaceIntent);
+    method public static android.content.ComponentName? getWatchFaceComponentExtra(android.content.Intent watchFaceIntent);
+    method public static android.content.Intent putPeerIdExtra(android.content.Intent watchFaceIntent, String peerId);
+    method public static android.content.Intent putWatchFaceComponentExtra(android.content.Intent watchFaceIntent, android.content.ComponentName componentName);
+    field public static final androidx.wear.remote.interactions.WatchFaceConfigIntentHelper.Companion Companion;
+  }
+
+  public static final class WatchFaceConfigIntentHelper.Companion {
+    method public String? getPeerIdExtra(android.content.Intent watchFaceIntent);
+    method public android.content.ComponentName? getWatchFaceComponentExtra(android.content.Intent watchFaceIntent);
+    method public android.content.Intent putPeerIdExtra(android.content.Intent watchFaceIntent, String peerId);
+    method public android.content.Intent putWatchFaceComponentExtra(android.content.Intent watchFaceIntent, android.content.ComponentName componentName);
+  }
+
+}
+
diff --git a/constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt b/wear/wear-remote-interactions/api/res-1.1.0-beta01.txt
similarity index 100%
rename from constraintlayout/constraintlayout-compose/api/res-1.1.0-beta01.txt
rename to wear/wear-remote-interactions/api/res-1.1.0-beta01.txt
diff --git a/wear/wear-remote-interactions/api/restricted_1.1.0-beta01.txt b/wear/wear-remote-interactions/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..754f9e9
--- /dev/null
+++ b/wear/wear-remote-interactions/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,48 @@
+// Signature format: 4.0
+package androidx.wear.remote.interactions {
+
+  public final class RemoteActivityHelper {
+    ctor public RemoteActivityHelper(android.content.Context context);
+    ctor public RemoteActivityHelper(android.content.Context context, optional java.util.concurrent.Executor executor);
+    method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getAvailabilityStatus();
+    method public static android.content.Intent? getTargetIntent(android.content.Intent intent);
+    method public static String? getTargetNodeId(android.content.Intent intent);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startRemoteActivity(android.content.Intent targetIntent);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void> startRemoteActivity(android.content.Intent targetIntent, optional String? targetNodeId);
+    property public final kotlinx.coroutines.flow.Flow<java.lang.Integer> availabilityStatus;
+    field public static final String ACTION_REMOTE_INTENT = "com.google.android.wearable.intent.action.REMOTE_INTENT";
+    field public static final androidx.wear.remote.interactions.RemoteActivityHelper.Companion Companion;
+    field public static final int RESULT_FAILED = 1; // 0x1
+    field public static final int RESULT_OK = 0; // 0x0
+    field public static final int STATUS_AVAILABLE = 3; // 0x3
+    field public static final int STATUS_TEMPORARILY_UNAVAILABLE = 2; // 0x2
+    field public static final int STATUS_UNAVAILABLE = 1; // 0x1
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class RemoteActivityHelper.Companion {
+    method public android.content.Intent? getTargetIntent(android.content.Intent intent);
+    method public String? getTargetNodeId(android.content.Intent intent);
+  }
+
+  public static final class RemoteActivityHelper.RemoteIntentException extends java.lang.Exception {
+    ctor public RemoteActivityHelper.RemoteIntentException(String message);
+  }
+
+  public final class WatchFaceConfigIntentHelper {
+    method public static String? getPeerIdExtra(android.content.Intent watchFaceIntent);
+    method public static android.content.ComponentName? getWatchFaceComponentExtra(android.content.Intent watchFaceIntent);
+    method public static android.content.Intent putPeerIdExtra(android.content.Intent watchFaceIntent, String peerId);
+    method public static android.content.Intent putWatchFaceComponentExtra(android.content.Intent watchFaceIntent, android.content.ComponentName componentName);
+    field public static final androidx.wear.remote.interactions.WatchFaceConfigIntentHelper.Companion Companion;
+  }
+
+  public static final class WatchFaceConfigIntentHelper.Companion {
+    method public String? getPeerIdExtra(android.content.Intent watchFaceIntent);
+    method public android.content.ComponentName? getWatchFaceComponentExtra(android.content.Intent watchFaceIntent);
+    method public android.content.Intent putPeerIdExtra(android.content.Intent watchFaceIntent, String peerId);
+    method public android.content.Intent putWatchFaceComponentExtra(android.content.Intent watchFaceIntent, android.content.ComponentName componentName);
+  }
+
+}
+
diff --git a/wear/wear-remote-interactions/build.gradle b/wear/wear-remote-interactions/build.gradle
index 29bd789..a0572fc 100644
--- a/wear/wear-remote-interactions/build.gradle
+++ b/wear/wear-remote-interactions/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
     api(libs.guavaListenableFuture)
     api("androidx.concurrent:concurrent-futures-ktx:1.1.0")
@@ -51,7 +51,6 @@
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
 
-    implementation("androidx.annotation:annotation:1.2.0")
     implementation(libs.playServicesBasement)
     implementation(libs.playServicesWearable, { exclude group: "androidx.core"})
 
@@ -76,7 +75,6 @@
     mavenVersion = LibraryVersions.WEAR_REMOTE_INTERACTIONS
     inceptionYear = "2020"
     description = "Android Wear Remote Interactions"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
     samples(project(":wear:wear-remote-interactions-samples"))
 }
diff --git a/wear/wear-tooling-preview/build.gradle b/wear/wear-tooling-preview/build.gradle
index 0de3b5e..078f3f2 100644
--- a/wear/wear-tooling-preview/build.gradle
+++ b/wear/wear-tooling-preview/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.6.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
 }
 
@@ -44,6 +44,5 @@
     description = "Tools for Wear UI Previews"
     type = LibraryType.PUBLISHED_LIBRARY
     mavenVersion = LibraryVersions.WEAR_TOOLING_PREVIEW
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/wear/build.gradle b/wear/wear/build.gradle
index 070e8bb..41e2757 100644
--- a/wear/wear/build.gradle
+++ b/wear/wear/build.gradle
@@ -14,7 +14,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
     api("androidx.fragment:fragment:1.2.4")
     api("androidx.recyclerview:recyclerview:1.1.0")
@@ -77,6 +77,5 @@
     inceptionYear = "2016"
     description = "Android Wear Support UI"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/DismissibleFrameLayoutTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/DismissibleFrameLayoutTest.java
index 73460e8..f27a07f 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/DismissibleFrameLayoutTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/DismissibleFrameLayoutTest.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
+import android.os.Build;
 import android.view.KeyEvent;
 import android.view.View;
 
@@ -46,22 +47,29 @@
 import androidx.wear.widget.util.FrameLocationAvoidingEdges;
 import androidx.wear.widget.util.WakeLockRule;
 
+import org.junit.Assume;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.regex.Pattern;
+
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class DismissibleFrameLayoutTest {
     private static final long MAX_WAIT_TIME = 4000; //ms
 
     private final DismissibleFrameLayout.Callback mDismissCallback = new DismissCallback();
+    private final Pattern mCuttleFishWearPattern = Pattern.compile(
+            Pattern.quote("Cuttlefish x86 Wear"), Pattern.CASE_INSENSITIVE
+    );
 
     @Rule
     public final WakeLockRule wakeLock = new WakeLockRule();
 
     @Test
     public void testBackDismiss() {
+        assumeNotCuttlefishWear();
         // GIVEN a freshly setup DismissibleFrameLayout
         try (ActivityScenario<DismissibleFrameLayoutTestActivity> scenario =
                      ActivityScenario.launch(createDismissibleLayoutIntent())) {
@@ -81,6 +89,7 @@
 
     @Test
     public void testBackNotDismissIfDisabled() {
+        assumeNotCuttlefishWear();
         // GIVEN a freshly setup DismissibleFrameLayout
         try (ActivityScenario<DismissibleFrameLayoutTestActivity> scenario =
                      ActivityScenario.launch(createDismissibleLayoutIntent())) {
@@ -115,6 +124,7 @@
 
     @Test
     public void testSwipeNotDismissIfDisabled() {
+        assumeNotCuttlefishWear();
         // GIVEN a freshly setup DismissibleFrameLayout
         try (ActivityScenario<DismissibleFrameLayoutTestActivity> scenario =
                      ActivityScenario.launch(createDismissibleLayoutIntent())) {
@@ -130,6 +140,7 @@
 
     @Test
     public void testDisableThenEnableBackDismiss() {
+        assumeNotCuttlefishWear();
         // GIVEN a freshly setup DismissibleFrameLayout
         try (ActivityScenario<DismissibleFrameLayoutTestActivity> scenario =
                      ActivityScenario.launch(createDismissibleLayoutIntent())) {
@@ -178,9 +189,9 @@
         }
     }
 
-
     @Test
     public void testBackDismissWithRecyclerView() {
+        assumeNotCuttlefishWear();
         // GIVEN a freshly setup DismissibleFrameLayout
         try (ActivityScenario<DismissibleFrameLayoutTestActivity> scenario =
                      ActivityScenario.launch(createDismissibleLayoutWithRecyclerViewIntent())) {
@@ -276,6 +287,13 @@
         InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
     }
 
+    private void assumeNotCuttlefishWear() {
+        Assume.assumeFalse(
+                "Unable to test: Cuttlefish Wear devices do not work with these tests",
+                mCuttleFishWearPattern.matcher(Build.MODEL).find()
+        );
+    }
+
     /** Helper class hiding the view after a successful swipe-to-dismiss. */
     private static class DismissCallback extends DismissibleFrameLayout.Callback {
 
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/SwipeDismissFrameLayoutTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/SwipeDismissFrameLayoutTest.java
index b29c430..22223f0 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/SwipeDismissFrameLayoutTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/SwipeDismissFrameLayoutTest.java
@@ -28,6 +28,7 @@
 
 import android.content.Intent;
 import android.graphics.RectF;
+import android.os.Build;
 import android.view.View;
 
 import androidx.annotation.IdRes;
@@ -50,10 +51,13 @@
 import androidx.wear.widget.util.FrameLocationAvoidingEdges;
 import androidx.wear.widget.util.WakeLockRule;
 
+import org.junit.Assume;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.regex.Pattern;
+
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class SwipeDismissFrameLayoutTest {
@@ -61,6 +65,9 @@
     private static final long MAX_WAIT_TIME = 4000; //ms
 
     private final SwipeDismissFrameLayout.Callback mDismissCallback = new DismissCallback();
+    private final Pattern mCuttleFishWearPattern = Pattern.compile(
+            Pattern.quote("Cuttlefish x86 Wear"), Pattern.CASE_INSENSITIVE
+    );
 
     @Rule
     public final WakeLockRule wakeLock = new WakeLockRule();
@@ -184,6 +191,7 @@
 
     @Test
     public void testSwipeDoesNotDismissViewIfDisabled() {
+        assumeNotCuttlefishWear();
         // GIVEN a freshly setup SwipeDismissFrameLayout with dismiss turned off.
         try (ActivityScenario<DismissibleFrameLayoutTestActivity> scenario =
                      ActivityScenario.launch(createSimpleLayoutLaunchIntent())) {
@@ -341,6 +349,13 @@
         });
     }
 
+    private void assumeNotCuttlefishWear() {
+        Assume.assumeFalse(
+                "Unable to test: Cuttlefish Wear devices do not work with these tests",
+                mCuttleFishWearPattern.matcher(Build.MODEL).find()
+        );
+    }
+
     private static void assertHidden(@IdRes int layoutId) {
         onView(withId(layoutId))
                 .perform(
diff --git a/webkit/integration-tests/testapp/build.gradle b/webkit/integration-tests/testapp/build.gradle
index 3cbe3e4..6d26540 100644
--- a/webkit/integration-tests/testapp/build.gradle
+++ b/webkit/integration-tests/testapp/build.gradle
@@ -32,7 +32,7 @@
 dependencies {
     implementation("androidx.appcompat:appcompat:1.1.0")
     implementation("androidx.core:core:1.1.0")
-    implementation("androidx.annotation:annotation:1.3.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation(project(":webkit:webkit"))
     implementation(libs.guavaAndroid)
     implementation(libs.espressoIdlingNet)
diff --git a/webkit/webkit/build.gradle b/webkit/webkit/build.gradle
index bf612b6..193e4b7 100644
--- a/webkit/webkit/build.gradle
+++ b/webkit/webkit/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.2.0")
+    api("androidx.annotation:annotation:1.8.1")
     api("androidx.core:core:1.1.0")
     api("androidx.annotation:annotation-experimental:1.4.1")
 
@@ -77,5 +77,4 @@
     inceptionYear = "2017"
     description = "The WebKit Support Library is a static library you can add to your Android application in order to use android.webkit APIs that are not available for older platform versions."
     additionalDeviceTestApkKeys.add("chrome")
-    metalavaK2UastEnabled = true
 }
diff --git a/window/extensions/core/core/build.gradle b/window/extensions/core/core/build.gradle
index dd41986..fbda3e0 100644
--- a/window/extensions/core/core/build.gradle
+++ b/window/extensions/core/core/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.6.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     testApi(libs.kotlinStdlib)
     testImplementation(libs.kotlinTest)
@@ -56,6 +56,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2022"
     description = "The Core APIs for Window Manager Library Extensions"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/window/extensions/extensions/api/current.txt b/window/extensions/extensions/api/current.txt
index e72e167..8c45b062 100644
--- a/window/extensions/extensions/api/current.txt
+++ b/window/extensions/extensions/api/current.txt
@@ -52,14 +52,21 @@
 package androidx.window.extensions.embedding {
 
   public interface ActivityEmbeddingComponent {
+    method public default void clearActivityStackAttributesCalculator();
+    method public default void clearEmbeddedActivityWindowInfoCallback();
     method public void clearSplitAttributesCalculator();
     method public void clearSplitInfoCallback();
     method @Deprecated public default void finishActivityStacks(java.util.Set<android.os.IBinder!>);
     method public default void finishActivityStacksWithTokens(java.util.Set<androidx.window.extensions.embedding.ActivityStack.Token!>);
+    method public default androidx.window.extensions.embedding.ActivityStack.Token? getActivityStackToken(String);
+    method public default androidx.window.extensions.embedding.EmbeddedActivityWindowInfo? getEmbeddedActivityWindowInfo(android.app.Activity);
+    method public default androidx.window.extensions.embedding.ParentContainerInfo? getParentContainerInfo(androidx.window.extensions.embedding.ActivityStack.Token);
     method public default void invalidateTopVisibleSplitAttributes();
     method public boolean isActivityEmbedded(android.app.Activity);
     method public default boolean pinTopActivityStack(int, androidx.window.extensions.embedding.SplitPinRule);
     method public default void registerActivityStackCallback(java.util.concurrent.Executor, androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.ActivityStack!>!>);
+    method public default void setActivityStackAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.ActivityStackAttributesCalculatorParams!,androidx.window.extensions.embedding.ActivityStackAttributes!>);
+    method public default void setEmbeddedActivityWindowInfoCallback(java.util.concurrent.Executor, androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.embedding.EmbeddedActivityWindowInfo!>);
     method public void setEmbeddingRules(java.util.Set<androidx.window.extensions.embedding.EmbeddingRule!>);
     method @Deprecated public default android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.os.IBinder);
     method public void setSplitAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.SplitAttributesCalculatorParams!,androidx.window.extensions.embedding.SplitAttributes!>);
@@ -67,12 +74,14 @@
     method @Deprecated public void setSplitInfoCallback(java.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
     method public default void unpinTopActivityStack(int);
     method public default void unregisterActivityStackCallback(androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.ActivityStack!>!>);
+    method public default void updateActivityStackAttributes(androidx.window.extensions.embedding.ActivityStack.Token, androidx.window.extensions.embedding.ActivityStackAttributes);
     method @Deprecated public default void updateSplitAttributes(android.os.IBinder, androidx.window.extensions.embedding.SplitAttributes);
     method public default void updateSplitAttributes(androidx.window.extensions.embedding.SplitInfo.Token, androidx.window.extensions.embedding.SplitAttributes);
   }
 
   public class ActivityEmbeddingOptionsProperties {
     field public static final String KEY_ACTIVITY_STACK_TOKEN = "androidx.window.extensions.embedding.ActivityStackToken";
+    field public static final String KEY_OVERLAY_TAG = "androidx.window.extensions.embedding.OverlayTag";
   }
 
   public class ActivityRule extends androidx.window.extensions.embedding.EmbeddingRule {
@@ -92,6 +101,7 @@
   public class ActivityStack {
     method public java.util.List<android.app.Activity!> getActivities();
     method public androidx.window.extensions.embedding.ActivityStack.Token getActivityStackToken();
+    method public String? getTag();
     method public boolean isEmpty();
   }
 
@@ -102,6 +112,24 @@
     field public static final androidx.window.extensions.embedding.ActivityStack.Token INVALID_ACTIVITY_STACK_TOKEN;
   }
 
+  public final class ActivityStackAttributes {
+    method public android.graphics.Rect getRelativeBounds();
+    method public androidx.window.extensions.embedding.WindowAttributes getWindowAttributes();
+  }
+
+  public static final class ActivityStackAttributes.Builder {
+    ctor public ActivityStackAttributes.Builder();
+    method public androidx.window.extensions.embedding.ActivityStackAttributes build();
+    method public androidx.window.extensions.embedding.ActivityStackAttributes.Builder setRelativeBounds(android.graphics.Rect);
+    method public androidx.window.extensions.embedding.ActivityStackAttributes.Builder setWindowAttributes(androidx.window.extensions.embedding.WindowAttributes);
+  }
+
+  public class ActivityStackAttributesCalculatorParams {
+    method public String getActivityStackTag();
+    method public android.os.Bundle getLaunchOptions();
+    method public androidx.window.extensions.embedding.ParentContainerInfo getParentContainerInfo();
+  }
+
   public abstract class AnimationBackground {
     method public static androidx.window.extensions.embedding.AnimationBackground.ColorBackground createColorBackground(@ColorInt int);
     field public static final androidx.window.extensions.embedding.AnimationBackground ANIMATION_BACKGROUND_DEFAULT;
@@ -111,12 +139,48 @@
     method @ColorInt public int getColor();
   }
 
+  public final class DividerAttributes {
+    method @ColorInt public int getDividerColor();
+    method public int getDividerType();
+    method public float getPrimaryMaxRatio();
+    method public float getPrimaryMinRatio();
+    method @Dimension public int getWidthDp();
+    field public static final int DIVIDER_TYPE_DRAGGABLE = 2; // 0x2
+    field public static final int DIVIDER_TYPE_FIXED = 1; // 0x1
+    field public static final float RATIO_SYSTEM_DEFAULT = -1.0f;
+    field public static final int WIDTH_SYSTEM_DEFAULT = -1; // 0xffffffff
+  }
+
+  public static final class DividerAttributes.Builder {
+    ctor public DividerAttributes.Builder(androidx.window.extensions.embedding.DividerAttributes);
+    ctor public DividerAttributes.Builder(int);
+    method public androidx.window.extensions.embedding.DividerAttributes build();
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setDividerColor(@ColorInt int);
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setPrimaryMaxRatio(float);
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setPrimaryMinRatio(float);
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setWidthDp(@Dimension int);
+  }
+
+  public class EmbeddedActivityWindowInfo {
+    method public android.app.Activity getActivity();
+    method public android.graphics.Rect getActivityStackBounds();
+    method public android.graphics.Rect getTaskBounds();
+    method public boolean isEmbedded();
+  }
+
   public abstract class EmbeddingRule {
     method public String? getTag();
   }
 
+  public class ParentContainerInfo {
+    method public android.content.res.Configuration getConfiguration();
+    method public androidx.window.extensions.layout.WindowLayoutInfo getWindowLayoutInfo();
+    method public android.view.WindowMetrics getWindowMetrics();
+  }
+
   public class SplitAttributes {
     method public androidx.window.extensions.embedding.AnimationBackground getAnimationBackground();
+    method public androidx.window.extensions.embedding.DividerAttributes? getDividerAttributes();
     method public int getLayoutDirection();
     method public androidx.window.extensions.embedding.SplitAttributes.SplitType getSplitType();
     method public androidx.window.extensions.embedding.WindowAttributes getWindowAttributes();
@@ -124,8 +188,10 @@
 
   public static final class SplitAttributes.Builder {
     ctor public SplitAttributes.Builder();
+    ctor public SplitAttributes.Builder(androidx.window.extensions.embedding.SplitAttributes);
     method public androidx.window.extensions.embedding.SplitAttributes build();
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setAnimationBackground(androidx.window.extensions.embedding.AnimationBackground);
+    method public androidx.window.extensions.embedding.SplitAttributes.Builder setDividerAttributes(androidx.window.extensions.embedding.DividerAttributes?);
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setLayoutDirection(int);
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setSplitType(androidx.window.extensions.embedding.SplitAttributes.SplitType);
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setWindowAttributes(androidx.window.extensions.embedding.WindowAttributes);
@@ -260,6 +326,24 @@
     method public android.graphics.Rect getBounds();
   }
 
+  public final class DisplayFoldFeature {
+    method public int getType();
+    method public boolean hasProperties(int...);
+    method public boolean hasProperty(int);
+    field public static final int FOLD_PROPERTY_SUPPORTS_HALF_OPENED = 1; // 0x1
+    field public static final int TYPE_HINGE = 1; // 0x1
+    field public static final int TYPE_SCREEN_FOLD_IN = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class DisplayFoldFeature.Builder {
+    ctor public DisplayFoldFeature.Builder(int);
+    method public androidx.window.extensions.layout.DisplayFoldFeature.Builder addProperties(int...);
+    method public androidx.window.extensions.layout.DisplayFoldFeature.Builder addProperty(int);
+    method public androidx.window.extensions.layout.DisplayFoldFeature build();
+    method public androidx.window.extensions.layout.DisplayFoldFeature.Builder clearProperties();
+  }
+
   public class FoldingFeature implements androidx.window.extensions.layout.DisplayFeature {
     ctor public FoldingFeature(android.graphics.Rect, int, int);
     method public android.graphics.Rect getBounds();
@@ -271,9 +355,19 @@
     field public static final int TYPE_HINGE = 2; // 0x2
   }
 
+  public final class SupportedWindowFeatures {
+    method public java.util.List<androidx.window.extensions.layout.DisplayFoldFeature!> getDisplayFoldFeatures();
+  }
+
+  public static final class SupportedWindowFeatures.Builder {
+    ctor public SupportedWindowFeatures.Builder(java.util.List<androidx.window.extensions.layout.DisplayFoldFeature!>);
+    method public androidx.window.extensions.layout.SupportedWindowFeatures build();
+  }
+
   public interface WindowLayoutComponent {
     method @Deprecated public void addWindowLayoutInfoListener(android.app.Activity, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
     method public default void addWindowLayoutInfoListener(@UiContext android.content.Context, androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method public default androidx.window.extensions.layout.SupportedWindowFeatures getSupportedWindowFeatures();
     method public default void removeWindowLayoutInfoListener(androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
     method @Deprecated public void removeWindowLayoutInfoListener(java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
   }
diff --git a/window/extensions/extensions/api/restricted_current.txt b/window/extensions/extensions/api/restricted_current.txt
index 13940ea..6811a24 100644
--- a/window/extensions/extensions/api/restricted_current.txt
+++ b/window/extensions/extensions/api/restricted_current.txt
@@ -52,14 +52,21 @@
 package androidx.window.extensions.embedding {
 
   public interface ActivityEmbeddingComponent {
+    method public default void clearActivityStackAttributesCalculator();
+    method public default void clearEmbeddedActivityWindowInfoCallback();
     method public void clearSplitAttributesCalculator();
     method public void clearSplitInfoCallback();
     method @Deprecated public default void finishActivityStacks(java.util.Set<android.os.IBinder!>);
     method public default void finishActivityStacksWithTokens(java.util.Set<androidx.window.extensions.embedding.ActivityStack.Token!>);
+    method public default androidx.window.extensions.embedding.ActivityStack.Token? getActivityStackToken(String);
+    method public default androidx.window.extensions.embedding.EmbeddedActivityWindowInfo? getEmbeddedActivityWindowInfo(android.app.Activity);
+    method public default androidx.window.extensions.embedding.ParentContainerInfo? getParentContainerInfo(androidx.window.extensions.embedding.ActivityStack.Token);
     method public default void invalidateTopVisibleSplitAttributes();
     method public boolean isActivityEmbedded(android.app.Activity);
     method public default boolean pinTopActivityStack(int, androidx.window.extensions.embedding.SplitPinRule);
     method public default void registerActivityStackCallback(java.util.concurrent.Executor, androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.ActivityStack!>!>);
+    method public default void setActivityStackAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.ActivityStackAttributesCalculatorParams!,androidx.window.extensions.embedding.ActivityStackAttributes!>);
+    method public default void setEmbeddedActivityWindowInfoCallback(java.util.concurrent.Executor, androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.embedding.EmbeddedActivityWindowInfo!>);
     method public void setEmbeddingRules(java.util.Set<androidx.window.extensions.embedding.EmbeddingRule!>);
     method @Deprecated public default android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.os.IBinder);
     method public void setSplitAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.SplitAttributesCalculatorParams!,androidx.window.extensions.embedding.SplitAttributes!>);
@@ -67,12 +74,14 @@
     method @Deprecated public void setSplitInfoCallback(java.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
     method public default void unpinTopActivityStack(int);
     method public default void unregisterActivityStackCallback(androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.ActivityStack!>!>);
+    method public default void updateActivityStackAttributes(androidx.window.extensions.embedding.ActivityStack.Token, androidx.window.extensions.embedding.ActivityStackAttributes);
     method @Deprecated public default void updateSplitAttributes(android.os.IBinder, androidx.window.extensions.embedding.SplitAttributes);
     method public default void updateSplitAttributes(androidx.window.extensions.embedding.SplitInfo.Token, androidx.window.extensions.embedding.SplitAttributes);
   }
 
   public class ActivityEmbeddingOptionsProperties {
     field public static final String KEY_ACTIVITY_STACK_TOKEN = "androidx.window.extensions.embedding.ActivityStackToken";
+    field public static final String KEY_OVERLAY_TAG = "androidx.window.extensions.embedding.OverlayTag";
   }
 
   public class ActivityRule extends androidx.window.extensions.embedding.EmbeddingRule {
@@ -92,6 +101,7 @@
   public class ActivityStack {
     method public java.util.List<android.app.Activity!> getActivities();
     method public androidx.window.extensions.embedding.ActivityStack.Token getActivityStackToken();
+    method public String? getTag();
     method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.IBinder getToken();
     method public boolean isEmpty();
   }
@@ -103,6 +113,24 @@
     field public static final androidx.window.extensions.embedding.ActivityStack.Token INVALID_ACTIVITY_STACK_TOKEN;
   }
 
+  public final class ActivityStackAttributes {
+    method public android.graphics.Rect getRelativeBounds();
+    method public androidx.window.extensions.embedding.WindowAttributes getWindowAttributes();
+  }
+
+  public static final class ActivityStackAttributes.Builder {
+    ctor public ActivityStackAttributes.Builder();
+    method public androidx.window.extensions.embedding.ActivityStackAttributes build();
+    method public androidx.window.extensions.embedding.ActivityStackAttributes.Builder setRelativeBounds(android.graphics.Rect);
+    method public androidx.window.extensions.embedding.ActivityStackAttributes.Builder setWindowAttributes(androidx.window.extensions.embedding.WindowAttributes);
+  }
+
+  public class ActivityStackAttributesCalculatorParams {
+    method public String getActivityStackTag();
+    method public android.os.Bundle getLaunchOptions();
+    method public androidx.window.extensions.embedding.ParentContainerInfo getParentContainerInfo();
+  }
+
   public abstract class AnimationBackground {
     method public static androidx.window.extensions.embedding.AnimationBackground.ColorBackground createColorBackground(@ColorInt int);
     field public static final androidx.window.extensions.embedding.AnimationBackground ANIMATION_BACKGROUND_DEFAULT;
@@ -112,12 +140,48 @@
     method @ColorInt public int getColor();
   }
 
+  public final class DividerAttributes {
+    method @ColorInt public int getDividerColor();
+    method public int getDividerType();
+    method public float getPrimaryMaxRatio();
+    method public float getPrimaryMinRatio();
+    method @Dimension public int getWidthDp();
+    field public static final int DIVIDER_TYPE_DRAGGABLE = 2; // 0x2
+    field public static final int DIVIDER_TYPE_FIXED = 1; // 0x1
+    field public static final float RATIO_SYSTEM_DEFAULT = -1.0f;
+    field public static final int WIDTH_SYSTEM_DEFAULT = -1; // 0xffffffff
+  }
+
+  public static final class DividerAttributes.Builder {
+    ctor public DividerAttributes.Builder(androidx.window.extensions.embedding.DividerAttributes);
+    ctor public DividerAttributes.Builder(int);
+    method public androidx.window.extensions.embedding.DividerAttributes build();
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setDividerColor(@ColorInt int);
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setPrimaryMaxRatio(float);
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setPrimaryMinRatio(float);
+    method public androidx.window.extensions.embedding.DividerAttributes.Builder setWidthDp(@Dimension int);
+  }
+
+  public class EmbeddedActivityWindowInfo {
+    method public android.app.Activity getActivity();
+    method public android.graphics.Rect getActivityStackBounds();
+    method public android.graphics.Rect getTaskBounds();
+    method public boolean isEmbedded();
+  }
+
   public abstract class EmbeddingRule {
     method public String? getTag();
   }
 
+  public class ParentContainerInfo {
+    method public android.content.res.Configuration getConfiguration();
+    method public androidx.window.extensions.layout.WindowLayoutInfo getWindowLayoutInfo();
+    method public android.view.WindowMetrics getWindowMetrics();
+  }
+
   public class SplitAttributes {
     method public androidx.window.extensions.embedding.AnimationBackground getAnimationBackground();
+    method public androidx.window.extensions.embedding.DividerAttributes? getDividerAttributes();
     method public int getLayoutDirection();
     method public androidx.window.extensions.embedding.SplitAttributes.SplitType getSplitType();
     method public androidx.window.extensions.embedding.WindowAttributes getWindowAttributes();
@@ -125,8 +189,10 @@
 
   public static final class SplitAttributes.Builder {
     ctor public SplitAttributes.Builder();
+    ctor public SplitAttributes.Builder(androidx.window.extensions.embedding.SplitAttributes);
     method public androidx.window.extensions.embedding.SplitAttributes build();
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setAnimationBackground(androidx.window.extensions.embedding.AnimationBackground);
+    method public androidx.window.extensions.embedding.SplitAttributes.Builder setDividerAttributes(androidx.window.extensions.embedding.DividerAttributes?);
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setLayoutDirection(int);
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setSplitType(androidx.window.extensions.embedding.SplitAttributes.SplitType);
     method public androidx.window.extensions.embedding.SplitAttributes.Builder setWindowAttributes(androidx.window.extensions.embedding.WindowAttributes);
@@ -261,6 +327,24 @@
     method public android.graphics.Rect getBounds();
   }
 
+  public final class DisplayFoldFeature {
+    method public int getType();
+    method public boolean hasProperties(int...);
+    method public boolean hasProperty(int);
+    field public static final int FOLD_PROPERTY_SUPPORTS_HALF_OPENED = 1; // 0x1
+    field public static final int TYPE_HINGE = 1; // 0x1
+    field public static final int TYPE_SCREEN_FOLD_IN = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class DisplayFoldFeature.Builder {
+    ctor public DisplayFoldFeature.Builder(int);
+    method public androidx.window.extensions.layout.DisplayFoldFeature.Builder addProperties(int...);
+    method public androidx.window.extensions.layout.DisplayFoldFeature.Builder addProperty(int);
+    method public androidx.window.extensions.layout.DisplayFoldFeature build();
+    method public androidx.window.extensions.layout.DisplayFoldFeature.Builder clearProperties();
+  }
+
   public class FoldingFeature implements androidx.window.extensions.layout.DisplayFeature {
     ctor public FoldingFeature(android.graphics.Rect, int, int);
     method public android.graphics.Rect getBounds();
@@ -272,9 +356,19 @@
     field public static final int TYPE_HINGE = 2; // 0x2
   }
 
+  public final class SupportedWindowFeatures {
+    method public java.util.List<androidx.window.extensions.layout.DisplayFoldFeature!> getDisplayFoldFeatures();
+  }
+
+  public static final class SupportedWindowFeatures.Builder {
+    ctor public SupportedWindowFeatures.Builder(java.util.List<androidx.window.extensions.layout.DisplayFoldFeature!>);
+    method public androidx.window.extensions.layout.SupportedWindowFeatures build();
+  }
+
   public interface WindowLayoutComponent {
     method @Deprecated public void addWindowLayoutInfoListener(android.app.Activity, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
     method public default void addWindowLayoutInfoListener(@UiContext android.content.Context, androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method public default androidx.window.extensions.layout.SupportedWindowFeatures getSupportedWindowFeatures();
     method public default void removeWindowLayoutInfoListener(androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
     method @Deprecated public void removeWindowLayoutInfoListener(java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
   }
diff --git a/window/extensions/extensions/build.gradle b/window/extensions/extensions/build.gradle
index 4ad8662..399cc93 100644
--- a/window/extensions/extensions/build.gradle
+++ b/window/extensions/extensions/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.6.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.annotation:annotation-experimental:1.4.1")
     compileOnly("androidx.window.extensions.core:core:1.0.0")
 
@@ -57,7 +57,6 @@
             "This module declares the interface the the core component of this library " +
             "(androidx.window:window) will use when communicating with the " +
             "device-specific OEM extension."
-    metalavaK2UastEnabled = true
 }
 
 android {
diff --git a/window/extensions/extensions/src/androidTest/java/androidx/window/extensions/embedding/ActivityStackAttributesTest.java b/window/extensions/extensions/src/androidTest/java/androidx/window/extensions/embedding/ActivityStackAttributesTest.java
new file mode 100644
index 0000000..953ace1
--- /dev/null
+++ b/window/extensions/extensions/src/androidTest/java/androidx/window/extensions/embedding/ActivityStackAttributesTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_ACTIVITY_STACK;
+import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Rect;
+
+import org.junit.Test;
+
+/**
+ * Verifies {@link ActivityStackAttributes} behavior.
+ */
+public class ActivityStackAttributesTest {
+
+    @Test
+    public void testActivityStackAttributesDefaults() {
+        final ActivityStackAttributes defaultAttrs = new ActivityStackAttributes.Builder().build();
+        assertThat(defaultAttrs.getRelativeBounds().isEmpty()).isTrue();
+        assertThat(defaultAttrs.getWindowAttributes().getDimAreaBehavior())
+                .isEqualTo(DIM_AREA_ON_ACTIVITY_STACK);
+    }
+
+    @Test
+    public void testActivityStackAttributesEqualsMatchHashCode() {
+        final ActivityStackAttributes attrs1 = new ActivityStackAttributes.Builder()
+                .setRelativeBounds(new Rect(0, 0, 10, 10))
+                .setWindowAttributes(new WindowAttributes(DIM_AREA_ON_ACTIVITY_STACK))
+                .build();
+
+        final ActivityStackAttributes attrs2 = new ActivityStackAttributes.Builder()
+                .setRelativeBounds(new Rect(0, 0, 10, 10))
+                .setWindowAttributes(new WindowAttributes(DIM_AREA_ON_TASK))
+                .build();
+
+        final ActivityStackAttributes attrs3 = new ActivityStackAttributes.Builder()
+                .setRelativeBounds(new Rect(10, 0, 20, 10))
+                .setWindowAttributes(new WindowAttributes(DIM_AREA_ON_ACTIVITY_STACK))
+                .build();
+
+        final ActivityStackAttributes attrs4 = new ActivityStackAttributes.Builder()
+                .setRelativeBounds(new Rect(10, 0, 20, 10))
+                .setWindowAttributes(new WindowAttributes(DIM_AREA_ON_TASK))
+                .build();
+
+        final ActivityStackAttributes attrs5 = new ActivityStackAttributes.Builder()
+                .setRelativeBounds(new Rect(0, 0, 10, 10))
+                .setWindowAttributes(new WindowAttributes(DIM_AREA_ON_ACTIVITY_STACK))
+                .build();
+
+        assertThat(attrs1).isNotEqualTo(attrs2);
+        assertThat(attrs1.hashCode()).isNotEqualTo(attrs2.hashCode());
+        assertThat(attrs1).isNotEqualTo(attrs3);
+        assertThat(attrs1.hashCode()).isNotEqualTo(attrs3.hashCode());
+        assertThat(attrs1).isNotEqualTo(attrs4);
+        assertThat(attrs1.hashCode()).isNotEqualTo(attrs4.hashCode());
+        assertThat(attrs1).isEqualTo(attrs5);
+        assertThat(attrs1.hashCode()).isEqualTo(attrs5.hashCode());
+    }
+}
diff --git a/window/extensions/extensions/src/androidTest/java/androidx/window/extensions/embedding/EmbeddedActivityWindowInfoTest.java b/window/extensions/extensions/src/androidTest/java/androidx/window/extensions/embedding/EmbeddedActivityWindowInfoTest.java
new file mode 100644
index 0000000..f699b61
--- /dev/null
+++ b/window/extensions/extensions/src/androidTest/java/androidx/window/extensions/embedding/EmbeddedActivityWindowInfoTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.graphics.Rect;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link EmbeddedActivityWindowInfo} class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmbeddedActivityWindowInfoTest {
+
+    @Mock
+    private Activity mActivity;
+    @Mock
+    private Activity mActivity2;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testGetter() {
+        final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+        final Rect activityStackBounds = new Rect(0, 0, 1000, 1000);
+        final EmbeddedActivityWindowInfo info = new EmbeddedActivityWindowInfo(mActivity,
+                true /* isEmbedded */, taskBounds, activityStackBounds);
+
+        assertEquals(mActivity, info.getActivity());
+        assertTrue(info.isEmbedded());
+        assertEquals(taskBounds, info.getTaskBounds());
+        assertEquals(activityStackBounds, info.getActivityStackBounds());
+    }
+
+    @Test
+    public void testEqualsAndHashCode() {
+        final EmbeddedActivityWindowInfo info1 = new EmbeddedActivityWindowInfo(mActivity,
+                true /* isEmbedded */,
+                new Rect(0, 0, 1000, 2000),
+                new Rect(0, 0, 1000, 1000));
+        final EmbeddedActivityWindowInfo info2 = new EmbeddedActivityWindowInfo(mActivity2,
+                true /* isEmbedded */,
+                new Rect(0, 0, 1000, 2000),
+                new Rect(0, 0, 1000, 1000));
+        final EmbeddedActivityWindowInfo info3 = new EmbeddedActivityWindowInfo(mActivity,
+                false /* isEmbedded */,
+                new Rect(0, 0, 1000, 2000),
+                new Rect(0, 0, 1000, 1000));
+        final EmbeddedActivityWindowInfo info4 = new EmbeddedActivityWindowInfo(mActivity,
+                true /* isEmbedded */,
+                new Rect(0, 0, 1000, 1000),
+                new Rect(0, 0, 1000, 1000));
+        final EmbeddedActivityWindowInfo info5 = new EmbeddedActivityWindowInfo(mActivity,
+                true /* isEmbedded */,
+                new Rect(0, 0, 1000, 2000),
+                new Rect(0, 0, 1000, 1500));
+        final EmbeddedActivityWindowInfo info6 = new EmbeddedActivityWindowInfo(mActivity,
+                true /* isEmbedded */,
+                new Rect(0, 0, 1000, 2000),
+                new Rect(0, 0, 1000, 1000));
+
+        assertNotEquals(info1, info2);
+        assertNotEquals(info1, info3);
+        assertNotEquals(info1, info4);
+        assertNotEquals(info1, info5);
+        assertEquals(info1, info6);
+
+        assertNotEquals(info1.hashCode(), info2.hashCode());
+        assertNotEquals(info1.hashCode(), info3.hashCode());
+        assertNotEquals(info1.hashCode(), info4.hashCode());
+        assertNotEquals(info1.hashCode(), info5.hashCode());
+        assertEquals(info1.hashCode(), info6.hashCode());
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
index 6d01778..cb53d62 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
@@ -158,6 +158,8 @@
     void startRearDisplaySession(@NonNull Activity activity,
             @NonNull Consumer<@WindowAreaSessionState Integer> consumer);
 
+    // TODO(b/264546746): Remove deprecated Window Extensions APIs after apps in g3 is updated to
+    // the latest library.
     /**
      * Ends a RearDisplaySession and sends [STATE_INACTIVE] to the consumer
      * provided in the {@code startRearDisplaySession} method. This method is only
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
index d032b00..ef79b80 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
@@ -22,6 +22,8 @@
 import android.view.WindowMetrics;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.window.extensions.RequiresVendorApiLevel;
 import androidx.window.extensions.WindowExtensions;
 import androidx.window.extensions.core.util.function.Consumer;
@@ -42,6 +44,12 @@
 public interface ActivityEmbeddingComponent {
 
     /**
+     * The vendor API level of the overlay feature APIs.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    int OVERLAY_FEATURE_API_LEVEL = 8;
+
+    /**
      * Updates the rules of embedding activities that are started in the client process.
      */
     @RequiresVendorApiLevel(level = 1)
@@ -253,6 +261,93 @@
     }
 
     /**
+     * Returns the {@link ParentContainerInfo} by the {@link ActivityStack} token, or {@code null}
+     * if there's not such {@link ActivityStack} associated with the {@code token}.
+     *
+     * @param activityStackToken the token of an {@link ActivityStack}.
+     */
+    @RequiresVendorApiLevel(level = OVERLAY_FEATURE_API_LEVEL)
+    @Nullable
+    default ParentContainerInfo getParentContainerInfo(
+            @NonNull ActivityStack.Token activityStackToken) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Sets a function to compute the {@link ActivityStackAttributes} for the ActivityStack given
+     * for the current window and device state provided in
+     * {@link ActivityStackAttributesCalculatorParams} on the main thread.
+     * <p>
+     * This calculator function is only triggered if the {@link ActivityStack#getTag()} is
+     * specified. Similar to {@link #setSplitAttributesCalculator(Function)}, the calculator
+     * function could be triggered multiple times. It will be triggered whenever there's a
+     * launching standalone {@link ActivityStack} with {@link ActivityStack#getTag()} specified,
+     * or a parent window or device state update, such as device rotation, folding state change,
+     * or the host task goes to multi-window mode.
+     *
+     * @param calculator The calculator function to calculate {@link ActivityStackAttributes} based
+     *                   on {@link ActivityStackAttributesCalculatorParams}.
+     */
+    @RequiresVendorApiLevel(level = OVERLAY_FEATURE_API_LEVEL)
+    default void setActivityStackAttributesCalculator(@NonNull Function<
+            ActivityStackAttributesCalculatorParams, ActivityStackAttributes> calculator) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Clears the calculator function previously set by
+     * {@link #setActivityStackAttributesCalculator(Function)}
+     */
+    @RequiresVendorApiLevel(level = OVERLAY_FEATURE_API_LEVEL)
+    default void clearActivityStackAttributesCalculator() {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Updates {@link ActivityStackAttributes} to an {@link ActivityStack} specified with
+     * {@code token} and applies the change directly. If there's no such an {@link ActivityStack},
+     * this method is no-op.
+     *
+     * @param token The {@link ActivityStack} to update.
+     * @param activityStackAttributes The attributes to be applied
+     */
+    @RequiresVendorApiLevel(level = OVERLAY_FEATURE_API_LEVEL)
+    default void updateActivityStackAttributes(@NonNull ActivityStack.Token token,
+            @NonNull ActivityStackAttributes activityStackAttributes) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Gets the {@link ActivityStack}'s token by {@code tag}, or {@code null} if there's no
+     * {@link ActivityStack} associated with the {@code tag}. For example, the {@link ActivityStack}
+     * is dismissed before the is method is called.
+     * <p>
+     * The {@link ActivityStack} token can be obtained immediately after the {@link ActivityStack}
+     * is created. This method is usually used when Activity Embedding library wants to
+     * {@link #updateActivityStackAttributes} before receiving
+     * the {@link ActivityStack} record from the callback set by
+     * {@link  #registerActivityStackCallback}.
+     * <p>
+     * For example, an app launches an overlay container and calls
+     * {@link #updateActivityStackAttributes} immediately right before the overlay
+     * {@link ActivityStack} is received from {@link #registerActivityStackCallback}.
+     *
+     * @param tag A unique identifier of an {@link ActivityStack} if set
+     * @return The {@link ActivityStack}'s token that the tag is associated with, or {@code null}
+     * if there's no such an {@link ActivityStack}.
+     */
+    @RequiresVendorApiLevel(level = OVERLAY_FEATURE_API_LEVEL)
+    @Nullable
+    default ActivityStack.Token getActivityStackToken(@NonNull String tag) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
      * Registers a callback that notifies WindowManager Jetpack about changes in
      * {@link ActivityStack}.
      * <p>
@@ -309,4 +404,48 @@
         throw new UnsupportedOperationException("This method must not be called unless there is a"
                 + " corresponding override implementation on the device.");
     }
+
+    /**
+     * Sets a callback that notifies WindowManager Jetpack about changes for a given
+     * {@link Activity} to its {@link EmbeddedActivityWindowInfo}.
+     * <p>
+     * The callback will be invoked when the {@link EmbeddedActivityWindowInfo} is changed after
+     * the {@link Activity} is launched. Similar to {@link Activity#onConfigurationChanged}, the
+     * callback will only be invoked for visible {@link Activity}.
+     *
+     * @param executor the executor to dispatch {@link EmbeddedActivityWindowInfo} change.
+     * @param callback the callback to notify {@link EmbeddedActivityWindowInfo} change.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    default void setEmbeddedActivityWindowInfoCallback(@NonNull Executor executor,
+            @NonNull Consumer<EmbeddedActivityWindowInfo> callback) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Clears the callback previously set by
+     * {@link #setEmbeddedActivityWindowInfoCallback(Executor, Consumer)}
+     */
+    @RequiresVendorApiLevel(level = 6)
+    default void clearEmbeddedActivityWindowInfoCallback() {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Returns the {@link EmbeddedActivityWindowInfo} of the given {@link Activity}, or
+     * {@code null} if the {@link Activity} is not attached.
+     * <p>
+     * This API can be used when {@link #setEmbeddedActivityWindowInfoCallback} is not set before
+     * the Activity is attached.
+     *
+     * @param activity the {@link Activity} to get {@link EmbeddedActivityWindowInfo} for.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @Nullable
+    default EmbeddedActivityWindowInfo getEmbeddedActivityWindowInfo(@NonNull Activity activity) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
 }
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingOptionsProperties.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingOptionsProperties.java
index 5279caa..fce31d8 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingOptionsProperties.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingOptionsProperties.java
@@ -29,6 +29,17 @@
     private ActivityEmbeddingOptionsProperties() {}
 
     /**
+     * The key of the unique identifier that put into {@link android.app.ActivityOptions}.
+     * <p>
+     * Type: {@link android.os.Bundle#putString(String, String) String}
+     * <p>
+     * An {@code OverlayCreateParams} property that represents the unique identifier of the overlay
+     * container.
+     */
+    public static final String KEY_OVERLAY_TAG =
+            "androidx.window.extensions.embedding.OverlayTag";
+
+    /**
      * The key of {@link ActivityStack.Token#toBundle()} that put into
      * {@link android.app.ActivityOptions}.
      * <p>
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
index 69b7ad3..0441d37 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
@@ -16,12 +16,15 @@
 
 package androidx.window.extensions.embedding;
 
+import static androidx.window.extensions.embedding.ActivityEmbeddingComponent.OVERLAY_FEATURE_API_LEVEL;
+
 import android.app.Activity;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.window.extensions.RequiresVendorApiLevel;
 
@@ -43,6 +46,9 @@
     @NonNull
     private final Token mToken;
 
+    @Nullable
+    private final String mTag;
+
     /**
      * The {@code ActivityStack} constructor
      *
@@ -51,14 +57,18 @@
      * @param isEmpty Indicates whether there's any {@link Activity} running in this
      *                {@code ActivityStack}
      * @param token The token to identify this {@code ActivityStack}
+     * @param tag A unique identifier of {@link ActivityStack}. Only specifies for the overlay
+     *            standalone {@link ActivityStack} currently.
      */
-    ActivityStack(@NonNull List<Activity> activities, boolean isEmpty, @NonNull Token token) {
+    ActivityStack(@NonNull List<Activity> activities, boolean isEmpty, @NonNull Token token,
+            @Nullable String tag) {
         Objects.requireNonNull(activities);
         Objects.requireNonNull(token);
 
         mActivities = new ArrayList<>(activities);
         mIsEmpty = isEmpty;
         mToken = token;
+        mTag = tag;
     }
 
     /**
@@ -110,6 +120,15 @@
         return mToken.getRawToken();
     }
 
+    /**
+     * Returns the associated tag if specified. Otherwise, returns {@code null}.
+     */
+    @RequiresVendorApiLevel(level = OVERLAY_FEATURE_API_LEVEL)
+    @Nullable
+    public String getTag() {
+        return mTag;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -117,7 +136,8 @@
         ActivityStack that = (ActivityStack) o;
         return mActivities.equals(that.mActivities)
                 && mIsEmpty == that.mIsEmpty
-                && mToken.equals(that.mToken);
+                && mToken.equals(that.mToken)
+                && Objects.equals(mTag, that.mTag);
     }
 
     @Override
@@ -125,6 +145,7 @@
         int result = (mIsEmpty ? 1 : 0);
         result = result * 31 + mActivities.hashCode();
         result = result * 31 + mToken.hashCode();
+        result = result * 31 + Objects.hashCode(mTag);
 
         return result;
     }
@@ -135,6 +156,7 @@
         return "ActivityStack{" + "mActivities=" + mActivities
                 + ", mIsEmpty=" + mIsEmpty
                 + ", mToken=" + mToken
+                + ", mTag=" + mTag
                 + '}';
     }
 
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStackAttributes.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStackAttributes.java
new file mode 100644
index 0000000..eb4f743
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStackAttributes.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_ACTIVITY_STACK;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.window.extensions.RequiresVendorApiLevel;
+
+/**
+ * Attributes used to update the layout and configuration of an {@link ActivityStack}.
+ */
+public final class ActivityStackAttributes {
+
+    @NonNull
+    private final Rect mRelativeBounds;
+
+    @NonNull
+    private final WindowAttributes mWindowAttributes;
+
+    private ActivityStackAttributes(@NonNull Rect relativeBounds,
+            @NonNull WindowAttributes windowAttributes) {
+        mRelativeBounds = relativeBounds;
+        mWindowAttributes = windowAttributes;
+    }
+
+    /**
+     * Returns the requested bounds of an {@link ActivityStack} which relative to its parent
+     * container.
+     * <p>
+     * {@link Rect#isEmpty() Empty} bounds mean that this {@link ActivityStack} should fill its
+     * parent container bounds.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public Rect getRelativeBounds() {
+        return mRelativeBounds;
+    }
+
+    /**
+     * Returns the {@link WindowAttributes} which contains the configurations of the embedded
+     * Activity windows with this attributes.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public WindowAttributes getWindowAttributes() {
+        return mWindowAttributes;
+    }
+
+    @Override
+    public int hashCode() {
+        return mRelativeBounds.hashCode() * 31 + mWindowAttributes.hashCode();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof ActivityStackAttributes)) return false;
+        final ActivityStackAttributes attrs = (ActivityStackAttributes) obj;
+        return mRelativeBounds.equals(attrs.mRelativeBounds)
+                && mWindowAttributes.equals(attrs.mWindowAttributes);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return ActivityStackAttributes.class.getSimpleName() + ": {"
+                + " relativeBounds=" + mRelativeBounds
+                + ", windowAttributes=" + mWindowAttributes
+                + "}";
+    }
+
+    /** The builder class of {@link ActivityStackAttributes}. */
+    public static final class Builder {
+
+        /** The {@link ActivityStackAttributes} builder constructor. */
+        @RequiresVendorApiLevel(level = 6)
+        public Builder() {}
+
+        @NonNull
+        private final Rect mRelativeBounds = new Rect();
+
+        @NonNull
+        private WindowAttributes mWindowAttributes =
+                new WindowAttributes(DIM_AREA_ON_ACTIVITY_STACK);
+
+        /**
+         * Sets the requested relative bounds of the {@link ActivityStack}. If this value is
+         * not specified, {@link #getRelativeBounds()} defaults to {@link Rect#isEmpty() empty}
+         * bounds, which means to follow the parent container bounds.
+         *
+         * @param relativeBounds The requested relative bounds.
+         * @return This {@code Builder}.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public Builder setRelativeBounds(@NonNull Rect relativeBounds) {
+            mRelativeBounds.set(relativeBounds);
+            return this;
+        }
+
+        /**
+         * Sets the window attributes. If this value is not specified, the
+         * {@link WindowAttributes#getDimAreaBehavior()} will be only applied on the
+         * {@link ActivityStack} of the requested activity.
+         *
+         * @param attributes The {@link WindowAttributes}
+         * @return This {@code Builder}.
+         */
+        @NonNull
+        @RequiresVendorApiLevel(level = 6)
+        public Builder setWindowAttributes(@NonNull WindowAttributes attributes) {
+            mWindowAttributes = attributes;
+            return this;
+        }
+
+        /**
+         * Builds an {@link ActivityStackAttributes} instance.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public ActivityStackAttributes build() {
+            return new ActivityStackAttributes(mRelativeBounds, mWindowAttributes);
+        }
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStackAttributesCalculatorParams.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStackAttributesCalculatorParams.java
new file mode 100644
index 0000000..99afdc6
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStackAttributesCalculatorParams.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.RequiresVendorApiLevel;
+import androidx.window.extensions.core.util.function.Function;
+
+/**
+ * The parameter container used in standalone {@link ActivityStack} calculator function to report
+ * {@link ParentContainerInfo} and associated {@link ActivityStack#getTag()} to calculate
+ * {@link ActivityStackAttributes} when there's a parent container information update or a
+ * standalone {@link ActivityStack} is going to be launched.
+ *
+ * @see ActivityEmbeddingComponent#setActivityStackAttributesCalculator(Function)
+ */
+public class ActivityStackAttributesCalculatorParams {
+
+    @NonNull
+    private final ParentContainerInfo mParentContainerInfo;
+
+    @NonNull
+    private final String mActivityStackTag;
+
+    @NonNull
+    private final Bundle mLaunchOptions;
+
+    /**
+     * {@code ActivityStackAttributesCalculatorParams} constructor.
+     *
+     * @param parentContainerInfo The {@link ParentContainerInfo} of the standalone
+     *                            {@link ActivityStack} to apply the
+     *                            {@link ActivityStackAttributes}.
+     * @param activityStackTag The unique identifier of {@link ActivityStack} to apply the
+     *                         {@link ActivityStackAttributes}.
+     * @param launchOptions The options to launch the {@link ActivityStack}.
+     */
+    ActivityStackAttributesCalculatorParams(@NonNull ParentContainerInfo parentContainerInfo,
+            @NonNull String activityStackTag, @NonNull Bundle launchOptions) {
+        mParentContainerInfo = parentContainerInfo;
+        mActivityStackTag = activityStackTag;
+        mLaunchOptions = launchOptions;
+    }
+
+    /**
+     * Returns {@link ParentContainerInfo} of the standalone {@link ActivityStack} to calculate.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public ParentContainerInfo getParentContainerInfo() {
+        return mParentContainerInfo;
+    }
+
+    /**
+     * Returns unique identifier of the standalone {@link ActivityStack} to calculate.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public String getActivityStackTag() {
+        return mActivityStackTag;
+    }
+
+    /**
+     * Returns options that passed from WM Jetpack to WM Extensions library to launch an
+     * {@link ActivityStack}. {@link Bundle#isEmpty() empty} options mean there's no launch options.
+     * <p>
+     * For example, an {@link ActivityStack} launch options could be an
+     * {@link android.app.ActivityOptions} bundle that contains information to build an overlay
+     * {@link ActivityStack}.
+     * <p>
+     * The launch options will be used for initializing standalone {@link ActivityStack} with
+     * {@link #getActivityStackTag()} specified. The logic is owned by WM Jetpack, which is usually
+     * from the {@link android.app.ActivityOptions}, WM Extensions library must not touch the
+     * options.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public Bundle getLaunchOptions() {
+        return mLaunchOptions;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return ActivityStackAttributesCalculatorParams.class.getSimpleName() + ":{"
+                + "parentContainerInfo=" + mParentContainerInfo
+                + "activityStackTag=" + mActivityStackTag
+                + "launchOptions=" + mLaunchOptions
+                + "}";
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/DividerAttributes.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/DividerAttributes.java
new file mode 100644
index 0000000..c434521
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/DividerAttributes.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.graphics.Color;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.Dimension;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.window.extensions.RequiresVendorApiLevel;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The attributes of the divider layout and behavior.
+ *
+ * @see SplitAttributes.Builder#setDividerAttributes(DividerAttributes)
+ */
+public final class DividerAttributes {
+
+    /**
+     * A divider type that draws a static line between the primary and secondary containers.
+     */
+    public static final int DIVIDER_TYPE_FIXED = 1;
+
+    /**
+     * A divider type that draws a line between the primary and secondary containers with a drag
+     * handle that the user can drag and resize the containers.
+     */
+    public static final int DIVIDER_TYPE_DRAGGABLE = 2;
+
+    @IntDef({DIVIDER_TYPE_FIXED, DIVIDER_TYPE_DRAGGABLE})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface DividerType {
+    }
+
+    /**
+     * A special value to indicate that the ratio is unset. which means the system will choose a
+     * default value based on the display size and form factor.
+     *
+     * @see #getPrimaryMinRatio()
+     * @see #getPrimaryMaxRatio()
+     */
+    public static final float RATIO_SYSTEM_DEFAULT = -1.0f;
+
+    /**
+     * A special value to indicate that the width is unset. which means the system will choose a
+     * default value based on the display size and form factor.
+     *
+     * @see #getWidthDp()
+     */
+    public static final int WIDTH_SYSTEM_DEFAULT = -1;
+
+    /** The {@link DividerType}. */
+    private final @DividerType int mDividerType;
+
+    /**
+     * The divider width in dp. It defaults to {@link #WIDTH_SYSTEM_DEFAULT}, which means the system
+     * will choose a default value based on the display size and form factor.
+     */
+    private final @Dimension int mWidthDp;
+
+    /**
+     * The min split ratio for the primary container. It defaults to {@link #RATIO_SYSTEM_DEFAULT},
+     * the system will choose a default value based on the display size and form factor. Will only
+     * be used when the divider type is {@link #DIVIDER_TYPE_DRAGGABLE}.
+     *
+     * @see SplitAttributes.SplitType.RatioSplitType#getRatio()
+     */
+    private final float mPrimaryMinRatio;
+
+    /**
+     * The max split ratio for the primary container. It defaults to {@link #RATIO_SYSTEM_DEFAULT},
+     * the system will choose a default value based on the display size and form factor. Will only
+     * be used when the divider type is {@link #DIVIDER_TYPE_DRAGGABLE}.
+     *
+     * @see SplitAttributes.SplitType.RatioSplitType#getRatio()
+     */
+    private final float mPrimaryMaxRatio;
+
+    /** The color of the divider. */
+    private final @ColorInt int mDividerColor;
+
+    /**
+     * Constructor of {@link DividerAttributes}.
+     *
+     * @param dividerType                   the divider type. See {@link DividerType}.
+     * @param widthDp                       the width of the divider.
+     * @param primaryMinRatio               the min split ratio for the primary container.
+     * @param primaryMaxRatio               the max split ratio for the primary container.
+     * @param dividerColor                  the color of the divider.
+     * @throws IllegalStateException if the provided values are invalid.
+     */
+    private DividerAttributes(
+            @DividerType int dividerType,
+            @Dimension int widthDp,
+            float primaryMinRatio,
+            float primaryMaxRatio,
+            @ColorInt int dividerColor) {
+        if (dividerType == DIVIDER_TYPE_FIXED
+                && (primaryMinRatio != RATIO_SYSTEM_DEFAULT
+                || primaryMaxRatio != RATIO_SYSTEM_DEFAULT)) {
+            throw new IllegalStateException(
+                    "primaryMinRatio and primaryMaxRatio must be RATIO_SYSTEM_DEFAULT for "
+                            + "DIVIDER_TYPE_FIXED.");
+        }
+        if (primaryMinRatio != RATIO_SYSTEM_DEFAULT && primaryMaxRatio != RATIO_SYSTEM_DEFAULT
+                && primaryMinRatio > primaryMaxRatio) {
+            throw new IllegalStateException(
+                    "primaryMinRatio must be less than or equal to primaryMaxRatio");
+        }
+        mDividerType = dividerType;
+        mWidthDp = widthDp;
+        mPrimaryMinRatio = primaryMinRatio;
+        mPrimaryMaxRatio = primaryMaxRatio;
+        mDividerColor = dividerColor;
+    }
+
+    /**
+     * Returns the divider type.
+     *
+     * @see #DIVIDER_TYPE_FIXED
+     * @see #DIVIDER_TYPE_DRAGGABLE
+     */
+    @RequiresVendorApiLevel(level = 6)
+    public @DividerType int getDividerType() {
+        return mDividerType;
+    }
+
+    /**
+     * Returns the width of the divider. It defaults to {@link #WIDTH_SYSTEM_DEFAULT}, which means
+     * the system will choose a default value based on the display size and form factor.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    public @Dimension int getWidthDp() {
+        return mWidthDp;
+    }
+
+    /**
+     * Returns the min split ratio for the primary container the divider can be dragged to. It
+     * defaults to {@link #RATIO_SYSTEM_DEFAULT}, which means the system will choose a default value
+     * based on the display size and form factor. Will only be used when the divider type is
+     * {@link #DIVIDER_TYPE_DRAGGABLE}.
+     *
+     * @see SplitAttributes.SplitType.RatioSplitType#getRatio()
+     */
+    @RequiresVendorApiLevel(level = 6)
+    public float getPrimaryMinRatio() {
+        return mPrimaryMinRatio;
+    }
+
+    /**
+     * Returns the max split ratio for the primary container the divider can be dragged to. It
+     * defaults to {@link #RATIO_SYSTEM_DEFAULT}, which means the system will choose a default value
+     * based on the display size and form factor. Will only be used when the divider type is
+     * {@link #DIVIDER_TYPE_DRAGGABLE}.
+     *
+     * @see SplitAttributes.SplitType.RatioSplitType#getRatio()
+     */
+    @RequiresVendorApiLevel(level = 6)
+    public float getPrimaryMaxRatio() {
+        return mPrimaryMaxRatio;
+    }
+
+    /** Returns the color of the divider. */
+    @RequiresVendorApiLevel(level = 6)
+    public @ColorInt int getDividerColor() {
+        return mDividerColor;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof DividerAttributes)) return false;
+        final DividerAttributes other = (DividerAttributes) obj;
+        return mDividerType == other.mDividerType
+                && mWidthDp == other.mWidthDp
+                && mPrimaryMinRatio == other.mPrimaryMinRatio
+                && mPrimaryMaxRatio == other.mPrimaryMaxRatio
+                && mDividerColor == other.mDividerColor;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDividerType, mWidthDp, mPrimaryMinRatio, mPrimaryMaxRatio);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return DividerAttributes.class.getSimpleName() + "{"
+                + "dividerType=" + mDividerType
+                + ", width=" + mWidthDp
+                + ", minPrimaryRatio=" + mPrimaryMinRatio
+                + ", maxPrimaryRatio=" + mPrimaryMaxRatio
+                + ", dividerColor=" + mDividerColor
+                + "}";
+    }
+
+    /** The {@link DividerAttributes} builder. */
+    public static final class Builder {
+
+        private final @DividerType int mDividerType;
+
+        private @Dimension int mWidthDp = WIDTH_SYSTEM_DEFAULT;
+
+        private float mPrimaryMinRatio = RATIO_SYSTEM_DEFAULT;
+
+        private float mPrimaryMaxRatio = RATIO_SYSTEM_DEFAULT;
+
+        private @ColorInt int mDividerColor = Color.BLACK;
+
+        /**
+         * The {@link DividerAttributes} builder constructor.
+         *
+         * @param dividerType the divider type, possible values are {@link #DIVIDER_TYPE_FIXED} and
+         *                    {@link #DIVIDER_TYPE_DRAGGABLE}.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        public Builder(@DividerType int dividerType) {
+            mDividerType = dividerType;
+        }
+
+        /**
+         * The {@link DividerAttributes} builder constructor initialized by an existing
+         * {@link DividerAttributes}.
+         *
+         * @param original the original {@link DividerAttributes} to initialize the {@link Builder}.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        public Builder(@NonNull DividerAttributes original) {
+            Objects.requireNonNull(original);
+            mDividerType = original.mDividerType;
+            mWidthDp = original.mWidthDp;
+            mPrimaryMinRatio = original.mPrimaryMinRatio;
+            mPrimaryMaxRatio = original.mPrimaryMaxRatio;
+            mDividerColor = original.mDividerColor;
+        }
+
+        /**
+         * Sets the divider width. It defaults to {@link #WIDTH_SYSTEM_DEFAULT}, which means the
+         * system will choose a default value based on the display size and form factor.
+         *
+         * @throws IllegalArgumentException if the provided value is invalid.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public Builder setWidthDp(@Dimension int widthDp) {
+            if (widthDp != WIDTH_SYSTEM_DEFAULT && widthDp < 0) {
+                throw new IllegalArgumentException(
+                        "widthDp must be greater than or equal to 0 or WIDTH_SYSTEM_DEFAULT.");
+            }
+            mWidthDp = widthDp;
+            return this;
+        }
+
+        /**
+         * Sets the min split ratio for the primary container. It defaults to
+         * {@link #RATIO_SYSTEM_DEFAULT}, which means the system will choose a default value based
+         * on the display size and form factor. Will only be used when the divider type is
+         * {@link #DIVIDER_TYPE_DRAGGABLE}.
+         *
+         * @param primaryMinRatio the min ratio for the primary container. Must be in range
+         *                        [0.0, 1.0) or {@link #RATIO_SYSTEM_DEFAULT}.
+         * @throws IllegalArgumentException if the provided value is invalid.
+         * @see SplitAttributes.SplitType.RatioSplitType#getRatio()
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public Builder setPrimaryMinRatio(float primaryMinRatio) {
+            if (primaryMinRatio != RATIO_SYSTEM_DEFAULT
+                    && (primaryMinRatio >= 1.0 || primaryMinRatio < 0.0)) {
+                throw new IllegalArgumentException(
+                        "primaryMinRatio must be in [0.0, 1.0) or RATIO_SYSTEM_DEFAULT.");
+            }
+            mPrimaryMinRatio = primaryMinRatio;
+            return this;
+        }
+
+        /**
+         * Sets the max split ratio for the primary container. It defaults to
+         * {@link #RATIO_SYSTEM_DEFAULT}, which means the system will choose a default value
+         * based on the display size and form factor. Will only be used when the divider type is
+         * {@link #DIVIDER_TYPE_DRAGGABLE}.
+         *
+         * @param primaryMaxRatio the max ratio for the primary container. Must be in range
+         *                        (0.0, 1.0] or {@link #RATIO_SYSTEM_DEFAULT}.
+         * @throws IllegalArgumentException if the provided value is invalid.
+         * @see SplitAttributes.SplitType.RatioSplitType#getRatio()
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public Builder setPrimaryMaxRatio(float primaryMaxRatio) {
+            if (primaryMaxRatio != RATIO_SYSTEM_DEFAULT
+                    && (primaryMaxRatio > 1.0 || primaryMaxRatio <= 0.0)) {
+                throw new IllegalArgumentException(
+                        "primaryMaxRatio must be in (0.0, 1.0] or RATIO_SYSTEM_DEFAULT.");
+            }
+            mPrimaryMaxRatio = primaryMaxRatio;
+            return this;
+        }
+
+        /**
+         * Sets the color of the divider. If not set, the default color {@link Color#BLACK} is
+         * used.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public Builder setDividerColor(@ColorInt int dividerColor) {
+            mDividerColor = dividerColor;
+            return this;
+        }
+
+        /**
+         * Builds a {@link DividerAttributes} instance.
+         *
+         * @return a {@link DividerAttributes} instance.
+         * @throws IllegalArgumentException if the provided values are invalid.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public DividerAttributes build() {
+            return new DividerAttributes(mDividerType, mWidthDp, mPrimaryMinRatio,
+                    mPrimaryMaxRatio, mDividerColor);
+        }
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/EmbeddedActivityWindowInfo.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/EmbeddedActivityWindowInfo.java
new file mode 100644
index 0000000..afea328
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/EmbeddedActivityWindowInfo.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.app.Activity;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.RequiresVendorApiLevel;
+
+import java.util.Objects;
+
+/**
+ * Describes the embedded window related info of an activity.
+ *
+ * @see ActivityEmbeddingComponent#setEmbeddedActivityWindowInfoCallback
+ * @see ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo
+ */
+public class EmbeddedActivityWindowInfo {
+
+    @NonNull
+    private final Activity mActivity;
+    private final boolean mIsEmbedded;
+    @NonNull
+    private final Rect mTaskBounds;
+    @NonNull
+    private final Rect mActivityStackBounds;
+
+    EmbeddedActivityWindowInfo(@NonNull Activity activity, boolean isEmbedded,
+            @NonNull Rect taskBounds, @NonNull Rect activityStackBounds) {
+        mActivity = Objects.requireNonNull(activity);
+        mIsEmbedded = isEmbedded;
+        mTaskBounds = Objects.requireNonNull(taskBounds);
+        mActivityStackBounds = Objects.requireNonNull(activityStackBounds);
+    }
+
+    /**
+     * Returns the {@link Activity} this {@link EmbeddedActivityWindowInfo} is about.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public Activity getActivity() {
+        return mActivity;
+    }
+
+    /**
+     * Whether this activity is embedded, which means it is in an ActivityStack window that
+     * doesn't fill the Task.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    public boolean isEmbedded() {
+        return mIsEmbedded;
+    }
+
+    /**
+     * Returns the bounds of the Task window in display space.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public Rect getTaskBounds() {
+        return mTaskBounds;
+    }
+
+    /**
+     * Returns the bounds of the ActivityStack window in display space.
+     * This can be referring to the bounds of the same window as {@link #getTaskBounds()} when
+     * the activity is not embedded.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public Rect getActivityStackBounds() {
+        return mActivityStackBounds;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EmbeddedActivityWindowInfo)) return false;
+        final EmbeddedActivityWindowInfo that = (EmbeddedActivityWindowInfo) o;
+        return mActivity.equals(that.mActivity)
+                && mIsEmbedded == that.mIsEmbedded
+                && mTaskBounds.equals(that.mTaskBounds)
+                && mActivityStackBounds.equals(that.mActivityStackBounds);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mActivity.hashCode();
+        result = result * 31 + (mIsEmbedded ? 1 : 0);
+        result = result * 31 + mTaskBounds.hashCode();
+        result = result * 31 + mActivityStackBounds.hashCode();
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "EmbeddedActivityWindowInfo{"
+                + "activity=" + mActivity
+                + ", isEmbedded=" + mIsEmbedded
+                + ", taskBounds=" + mTaskBounds
+                + ", activityStackBounds=" + mActivityStackBounds
+                + "}";
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ParentContainerInfo.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ParentContainerInfo.java
new file mode 100644
index 0000000..e08b803
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ParentContainerInfo.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.content.res.Configuration;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.window.extensions.RequiresVendorApiLevel;
+import androidx.window.extensions.layout.WindowLayoutInfo;
+
+/**
+ * The parent container information of an {@link ActivityStack}.
+ * The data class is designed to provide information to calculate the presentation of
+ * an {@link ActivityStack}.
+ */
+@RequiresVendorApiLevel(level = 6)
+public class ParentContainerInfo {
+    @NonNull
+    private final WindowMetrics mWindowMetrics;
+
+    @NonNull
+    private final Configuration mConfiguration;
+
+    @NonNull
+    private final WindowLayoutInfo mWindowLayoutInfo;
+
+    /**
+     * {@link ParentContainerInfo} constructor, which is used in Window Manager Extensions to
+     * provide information of a parent window container.
+     *
+     * @param windowMetrics The parent container's {@link WindowMetrics}
+     * @param configuration The parent container's {@link Configuration}
+     * @param windowLayoutInfo The parent container's {@link WindowLayoutInfo}
+     */
+    ParentContainerInfo(@NonNull WindowMetrics windowMetrics, @NonNull Configuration configuration,
+            @NonNull WindowLayoutInfo windowLayoutInfo) {
+        mWindowMetrics = windowMetrics;
+        mConfiguration = configuration;
+        mWindowLayoutInfo = windowLayoutInfo;
+    }
+
+    /** Returns the parent container's {@link WindowMetrics}. */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public WindowMetrics getWindowMetrics() {
+        return mWindowMetrics;
+    }
+
+    /** Returns the parent container's {@link Configuration}. */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    /** Returns the parent container's {@link WindowLayoutInfo}. */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    public WindowLayoutInfo getWindowLayoutInfo() {
+        return mWindowLayoutInfo;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mWindowMetrics.hashCode();
+        result = 31 * result + mConfiguration.hashCode();
+        result = 31 * result + mWindowLayoutInfo.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof ParentContainerInfo)) return false;
+        final ParentContainerInfo parentContainerInfo = (ParentContainerInfo) obj;
+        return mWindowMetrics.equals(parentContainerInfo.mWindowMetrics)
+                && mConfiguration.equals(parentContainerInfo.mConfiguration)
+                && mWindowLayoutInfo.equals(parentContainerInfo.mWindowLayoutInfo);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return ParentContainerInfo.class.getSimpleName() + ": {"
+                + "windowMetrics=" + WindowMetricsCompat.toString(mWindowMetrics)
+                + ", configuration=" + mConfiguration
+                + ", windowLayoutInfo=" + mWindowLayoutInfo
+                + "}";
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java
index 844443e..2c1464c 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java
@@ -120,7 +120,7 @@
         /**
          * A window split that's based on the ratio of the size of the primary container to the
          * size of the parent window (excluding area unavailable for the containers such as the
-         * divider.
+         * divider. See {@link DividerAttributes}).
          *
          * <p>Values in the non-inclusive range (0.0, 1.0) define the size of
          * the primary container relative to the size of the parent window:
@@ -144,7 +144,7 @@
              *
              * @param ratio The proportion of the parent window occupied by the primary container
              *              of the split (excluding area unavailable for the containers such as
-             *              the divider. Can be a value in the
+             *              the divider. See {@link DividerAttributes}). Can be a value in the
              *              non-inclusive range (0.0, 1.0). Use
              *              {@link SplitType.ExpandContainersSplitType} to create a split
              *              type that occupies the entire parent window.
@@ -163,7 +163,7 @@
             /**
              * Gets the proportion of the parent window occupied by the primary activity
              * container of the split (excluding area unavailable for the containers such as the
-             * divider.
+             * divider. See {@link DividerAttributes}) .
              *
              * @return The proportion of the split occupied by the primary
              * container.
@@ -383,6 +383,10 @@
     @NonNull
     private final WindowAttributes mWindowAttributes;
 
+    /** The attributes of a divider. If {@code null}, no divider is requested. */
+    @Nullable
+    private final DividerAttributes mDividerAttributes;
+
     /**
      * Creates an instance of this {@code SplitAttributes}.
      *
@@ -396,17 +400,21 @@
      *                            animation requires a background.
      * @param attributes          The {@link WindowAttributes} of the split, such as dim area
      *                            behavior.
+     * @param dividerAttributes   The {@link DividerAttributes}. If {@code null}, no divider is
+     *                            requested.
      */
     SplitAttributes(
             @NonNull SplitType splitType,
             @ExtLayoutDirection int layoutDirection,
             @NonNull AnimationBackground animationBackground,
-            @NonNull WindowAttributes attributes
+            @NonNull WindowAttributes attributes,
+            @Nullable DividerAttributes dividerAttributes
     ) {
         mSplitType = splitType;
         mLayoutDirection = layoutDirection;
         mAnimationBackground = animationBackground;
         mWindowAttributes = attributes;
+        mDividerAttributes = dividerAttributes;
     }
 
     /**
@@ -449,6 +457,13 @@
         return mWindowAttributes;
     }
 
+    /** Returns the {@link DividerAttributes}. If {@code null}, no divider is requested. */
+    @RequiresVendorApiLevel(level = 6)
+    @Nullable
+    public DividerAttributes getDividerAttributes() {
+        return mDividerAttributes;
+    }
+
     /**
      * Builder for creating an instance of {@link SplitAttributes}.
      *
@@ -470,11 +485,28 @@
         private WindowAttributes mWindowAttributes =
                 new WindowAttributes(DIM_AREA_ON_TASK);
 
+        @Nullable
+        private DividerAttributes mDividerAttributes;
+
         /** Creates a new {@link Builder} to create {@link SplitAttributes}. */
         public Builder() {
         }
 
         /**
+         * Creates a {@link Builder} with values cloned from the original {@link SplitAttributes}.
+         *
+         * @param original the original {@link SplitAttributes} to initialize the {@link Builder}.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        public Builder(@NonNull SplitAttributes original) {
+            mSplitType = original.mSplitType;
+            mLayoutDirection = original.mLayoutDirection;
+            mAnimationBackground = original.mAnimationBackground;
+            mWindowAttributes = original.mWindowAttributes;
+            mDividerAttributes = original.mDividerAttributes;
+        }
+
+        /**
          * Sets the split type attribute.
          *
          * The default is an equal split between primary and secondary
@@ -547,6 +579,14 @@
             return this;
         }
 
+        /** Sets the {@link DividerAttributes}. If {@code null}, no divider is requested. */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public Builder setDividerAttributes(@Nullable DividerAttributes dividerAttributes) {
+            mDividerAttributes = dividerAttributes;
+            return this;
+        }
+
         /**
          * Builds a {@link SplitAttributes} instance with the attributes
          * specified by {@link #setSplitType}, {@link #setLayoutDirection}, and
@@ -557,7 +597,7 @@
         @NonNull
         public SplitAttributes build() {
             return new SplitAttributes(mSplitType, mLayoutDirection, mAnimationBackground,
-                    mWindowAttributes);
+                    mWindowAttributes, mDividerAttributes);
         }
     }
 
@@ -568,12 +608,14 @@
         SplitAttributes that = (SplitAttributes) o;
         return mLayoutDirection == that.mLayoutDirection && mSplitType.equals(that.mSplitType)
                 && mAnimationBackground.equals(that.mAnimationBackground)
-                && mWindowAttributes.equals(that.mWindowAttributes);
+                && mWindowAttributes.equals(that.mWindowAttributes)
+                && Objects.equals(mDividerAttributes, that.mDividerAttributes);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mLayoutDirection, mSplitType, mAnimationBackground, mWindowAttributes);
+        return Objects.hash(mLayoutDirection, mSplitType, mAnimationBackground, mWindowAttributes,
+                mDividerAttributes);
     }
 
     @NonNull
@@ -584,6 +626,7 @@
                 + ", splitType=" + mSplitType
                 + ", animationBackground=" + mAnimationBackground
                 + ", windowAttributes=" + mWindowAttributes
+                + ", dividerAttributes=" + mDividerAttributes
                 + "}";
     }
 
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/DisplayFoldFeature.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/DisplayFoldFeature.java
new file mode 100644
index 0000000..17bb15a
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/DisplayFoldFeature.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.layout;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Build;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.RequiresVendorApiLevel;
+import androidx.window.extensions.core.util.function.Consumer;
+import androidx.window.extensions.util.SetUtilApi23;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents a fold on a display that may intersect a window. The presence of a fold does not
+ * imply that it intersects the window an {@link android.app.Activity} is running in. For
+ * example, on a device that can fold like a book and has an outer screen, the fold should be
+ * reported regardless of the folding state, or which screen is on to indicate that there may
+ * be a fold when the user opens the device.
+ *
+ * @see WindowLayoutComponent#addWindowLayoutInfoListener(Context, Consumer) to listen to features
+ * that affect the window.
+ */
+public final class DisplayFoldFeature {
+
+    /**
+     * The type of fold is unknown. This is here for compatibility reasons if a new type is added,
+     * and cannot be reported to an incompatible application.
+     */
+    public static final int TYPE_UNKNOWN = 0;
+
+    /**
+     * The type of fold is a physical hinge separating two display panels.
+     */
+    public static final int TYPE_HINGE = 1;
+
+    /**
+     * The type of fold is a screen that folds from 0-180.
+     */
+    public static final int TYPE_SCREEN_FOLD_IN = 2;
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {TYPE_UNKNOWN, TYPE_HINGE, TYPE_SCREEN_FOLD_IN})
+    public @interface FoldType {
+    }
+
+    /**
+     * The fold supports the half opened state.
+     */
+    public static final int FOLD_PROPERTY_SUPPORTS_HALF_OPENED = 1;
+
+    @Target(ElementType.TYPE_USE)
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {FOLD_PROPERTY_SUPPORTS_HALF_OPENED})
+    public @interface FoldProperty {
+    }
+
+    @FoldType
+    private final int mType;
+
+    private final Set<@FoldProperty Integer> mProperties;
+
+    /**
+     * Creates an instance of [FoldDisplayFeature].
+     *
+     * @param type                  the type of fold, either [FoldDisplayFeature.TYPE_HINGE] or
+     *                              [FoldDisplayFeature.TYPE_FOLDABLE_SCREEN]
+     */
+    DisplayFoldFeature(@FoldType int type, @NonNull Set<@FoldProperty Integer> properties) {
+        mType = type;
+        if (Build.VERSION.SDK_INT >= 23) {
+            mProperties = SetUtilApi23.createSet();
+        } else {
+            mProperties = new HashSet<>();
+        }
+        mProperties.addAll(properties);
+    }
+
+    /**
+     * Returns the type of fold that is either a hinge or a fold.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @FoldType
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns {@code true} if the fold has the given property, {@code false} otherwise.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    public boolean hasProperty(@FoldProperty int property) {
+        return mProperties.contains(property);
+    }
+    /**
+     * Returns {@code true} if the fold has all the given properties, {@code false} otherwise.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    public boolean hasProperties(@NonNull @FoldProperty int... properties) {
+        for (int i = 0; i < properties.length; i++) {
+            if (!mProperties.contains(properties[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DisplayFoldFeature that = (DisplayFoldFeature) o;
+        return mType == that.mType && Objects.equals(mProperties, that.mProperties);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mProperties);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return "ScreenFoldDisplayFeature{mType=" + mType + ", mProperties=" + mProperties + '}';
+    }
+
+    /**
+     * A builder to construct an instance of {@link DisplayFoldFeature}.
+     */
+    public static final class Builder {
+
+        @FoldType
+        private int mType;
+
+        private Set<@FoldProperty Integer> mProperties;
+
+        /**
+         * Constructs a builder to create an instance of {@link DisplayFoldFeature}.
+         *
+         * @param type                  the type of hinge for the {@link DisplayFoldFeature}.
+         * @see DisplayFoldFeature.FoldType
+         */
+        @RequiresVendorApiLevel(level = 6)
+        public Builder(@FoldType int type) {
+            mType = type;
+            if (Build.VERSION.SDK_INT >= 23) {
+                mProperties = SetUtilApi23.createSet();
+            } else {
+                mProperties = new HashSet<>();
+            }
+        }
+
+        /**
+         * Add a property to the set of properties exposed by {@link DisplayFoldFeature}.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        @RequiresVendorApiLevel(level = 6)
+        public Builder addProperty(@FoldProperty int property) {
+            mProperties.add(property);
+            return this;
+        }
+
+        /**
+         * Add a list of properties to the set of properties exposed by
+         * {@link DisplayFoldFeature}.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        @RequiresVendorApiLevel(level = 6)
+        public Builder addProperties(@NonNull @FoldProperty int... properties) {
+            for (int i = 0; i < properties.length; i++) {
+                mProperties.add(properties[i]);
+            }
+            return this;
+        }
+
+        /**
+         * Clear the properties in the builder.
+         */
+        @NonNull
+        @RequiresVendorApiLevel(level = 6)
+        public Builder clearProperties() {
+            mProperties.clear();
+            return this;
+        }
+
+        /**
+         * Returns an instance of {@link DisplayFoldFeature}.
+         */
+        @RequiresVendorApiLevel(level = 6)
+        @NonNull
+        public DisplayFoldFeature build() {
+            return new DisplayFoldFeature(mType, mProperties);
+        }
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/SupportedWindowFeatures.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/SupportedWindowFeatures.java
new file mode 100644
index 0000000..2143e29
--- /dev/null
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/SupportedWindowFeatures.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.layout;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.RequiresVendorApiLevel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class to represent all the possible features that may interact with or appear in a window,
+ * that an application might want to respond to.
+ */
+public final class SupportedWindowFeatures {
+
+    private final List<DisplayFoldFeature> mDisplayFoldFeatureList;
+
+    private SupportedWindowFeatures(
+            @NonNull List<DisplayFoldFeature> displayFoldFeatureList) {
+        mDisplayFoldFeatureList = new ArrayList<>(displayFoldFeatureList);
+    }
+
+    /**
+     * Returns the possible {@link DisplayFoldFeature}s that can be reported to an application.
+     */
+    @NonNull
+    @RequiresVendorApiLevel(level = 6)
+    public List<DisplayFoldFeature> getDisplayFoldFeatures() {
+        return new ArrayList<>(mDisplayFoldFeatureList);
+    }
+
+
+    /**
+     * A class to create a {@link SupportedWindowFeatures} instance.
+     */
+    public static final class Builder {
+
+        private final List<DisplayFoldFeature> mDisplayFoldFeatures;
+
+        /**
+         * Creates a new instance of {@link Builder}
+         */
+        @RequiresVendorApiLevel(level = 6)
+        public Builder(@NonNull List<DisplayFoldFeature> displayFoldFeatures) {
+            mDisplayFoldFeatures = new ArrayList<>(displayFoldFeatures);
+        }
+
+        /**
+         * Creates an instance of {@link SupportedWindowFeatures} with the features set.
+         */
+        @NonNull
+        @RequiresVendorApiLevel(level = 6)
+        public SupportedWindowFeatures build() {
+            return new SupportedWindowFeatures(mDisplayFoldFeatures);
+        }
+    }
+}
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java
index 875f763..d55e341 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java
@@ -96,4 +96,17 @@
         throw new UnsupportedOperationException("This method must not be called unless there is a"
                 + " corresponding override implementation on the device.");
     }
+
+    /**
+     * Returns the {@link SupportedWindowFeatures} for the device. This value will not change
+     * over time.
+     * @see WindowLayoutComponent#addWindowLayoutInfoListener(Context, Consumer) to register a
+     * listener for features that impact the window.
+     */
+    @RequiresVendorApiLevel(level = 6)
+    @NonNull
+    default SupportedWindowFeatures getSupportedWindowFeatures() {
+        throw new UnsupportedOperationException("This method will not be called unless there is a"
+                + " corresponding override implementation on the device");
+    }
 }
diff --git a/window/extensions/extensions/src/test/java/androidx/window/extensions/embedding/DividerAttributesTest.java b/window/extensions/extensions/src/test/java/androidx/window/extensions/embedding/DividerAttributesTest.java
new file mode 100644
index 0000000..69d0784
--- /dev/null
+++ b/window/extensions/extensions/src/test/java/androidx/window/extensions/embedding/DividerAttributesTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static androidx.window.extensions.embedding.DividerAttributes.DIVIDER_TYPE_DRAGGABLE;
+import static androidx.window.extensions.embedding.DividerAttributes.DIVIDER_TYPE_FIXED;
+import static androidx.window.extensions.embedding.DividerAttributes.RATIO_SYSTEM_DEFAULT;
+import static androidx.window.extensions.embedding.DividerAttributes.WIDTH_SYSTEM_DEFAULT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Verifies {@link DividerAttributes} behavior. */
+@SmallTest
+@RunWith(RobolectricTestRunner.class)
+public class DividerAttributesTest {
+
+    @Test
+    public void testDividerAttributesDefaults() {
+        final DividerAttributes defaultAttrs =
+                new DividerAttributes.Builder(DIVIDER_TYPE_FIXED).build();
+        assertThat(defaultAttrs.getDividerType()).isEqualTo(DIVIDER_TYPE_FIXED);
+        assertThat(defaultAttrs.getWidthDp()).isEqualTo(WIDTH_SYSTEM_DEFAULT);
+        assertThat(defaultAttrs.getPrimaryMinRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+        assertThat(defaultAttrs.getPrimaryMaxRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+    }
+
+    @Test
+    public void testDividerAttributesBuilder() {
+        final DividerAttributes dividerAttributes1 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_FIXED)
+                        .setWidthDp(20)
+                        .build();
+        assertThat(dividerAttributes1.getDividerType()).isEqualTo(DIVIDER_TYPE_FIXED);
+        assertThat(dividerAttributes1.getWidthDp()).isEqualTo(20);
+        assertThat(dividerAttributes1.getPrimaryMinRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+        assertThat(dividerAttributes1.getPrimaryMaxRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+
+        final DividerAttributes dividerAttributes2 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setWidthDp(20)
+                        .setPrimaryMinRatio(0.2f)
+                        .setPrimaryMaxRatio(0.8f)
+                        .build();
+        assertThat(dividerAttributes2.getDividerType()).isEqualTo(DIVIDER_TYPE_DRAGGABLE);
+        assertThat(dividerAttributes2.getWidthDp()).isEqualTo(20);
+        assertThat(dividerAttributes2.getPrimaryMinRatio()).isEqualTo(0.2f);
+        assertThat(dividerAttributes2.getPrimaryMaxRatio()).isEqualTo(0.8f);
+
+        final DividerAttributes dividerAttributes3 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setWidthDp(20)
+                        .build();
+        assertThat(dividerAttributes3.getDividerType()).isEqualTo(DIVIDER_TYPE_DRAGGABLE);
+        assertThat(dividerAttributes3.getWidthDp()).isEqualTo(20);
+        assertThat(dividerAttributes3.getPrimaryMinRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+        assertThat(dividerAttributes3.getPrimaryMaxRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+
+        final DividerAttributes dividerAttributes4 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setWidthDp(20)
+                        .setPrimaryMinRatio(0.2f)
+                        .build();
+        assertThat(dividerAttributes4.getDividerType()).isEqualTo(DIVIDER_TYPE_DRAGGABLE);
+        assertThat(dividerAttributes4.getWidthDp()).isEqualTo(20);
+        assertThat(dividerAttributes4.getPrimaryMinRatio()).isEqualTo(0.2f);
+        assertThat(dividerAttributes4.getPrimaryMaxRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+
+        final DividerAttributes dividerAttributes5 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setWidthDp(20)
+                        .setPrimaryMaxRatio(0.2f)
+                        .build();
+        assertThat(dividerAttributes5.getDividerType()).isEqualTo(DIVIDER_TYPE_DRAGGABLE);
+        assertThat(dividerAttributes5.getWidthDp()).isEqualTo(20);
+        assertThat(dividerAttributes5.getPrimaryMinRatio()).isEqualTo(RATIO_SYSTEM_DEFAULT);
+        assertThat(dividerAttributes5.getPrimaryMaxRatio()).isEqualTo(0.2f);
+    }
+
+    @Test
+    public void testDividerAttributesEquals() {
+        final DividerAttributes dividerAttributes1 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setWidthDp(20)
+                        .setPrimaryMinRatio(0.2f)
+                        .setPrimaryMaxRatio(0.8f)
+                        .build();
+
+        final DividerAttributes dividerAttributes2 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setWidthDp(20)
+                        .setPrimaryMinRatio(0.2f)
+                        .setPrimaryMaxRatio(0.8f)
+                        .build();
+
+        final DividerAttributes dividerAttributes3 =
+                new DividerAttributes.Builder(DIVIDER_TYPE_FIXED)
+                        .setWidthDp(20)
+                        .build();
+
+        assertThat(dividerAttributes1).isEqualTo(dividerAttributes2);
+        assertThat(dividerAttributes1).isNotEqualTo(dividerAttributes3);
+    }
+
+    @Test
+    public void testDividerAttributesValidation() {
+        assertThrows(
+                "Must not set min max ratio for DIVIDER_TYPE_FIXED",
+                IllegalStateException.class,
+                () -> new DividerAttributes.Builder(DIVIDER_TYPE_FIXED)
+                        .setPrimaryMinRatio(0.2f)
+                        .setPrimaryMaxRatio(0.8f)
+                        .build()
+        );
+
+        assertThrows(
+                "Min ratio must be less than or equal to max ratio",
+                IllegalStateException.class,
+                () -> new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setPrimaryMinRatio(0.8f)
+                        .setPrimaryMaxRatio(0.2f)
+                        .build()
+        );
+
+        assertThrows(
+                "Min ratio must be in range [0.0, 1.0] or RATIO_UNSET",
+                IllegalArgumentException.class,
+                () -> new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setPrimaryMinRatio(2.0f)
+        );
+
+        assertThrows(
+                "Max ratio must be in range [0.0, 1.0] or RATIO_UNSET",
+                IllegalArgumentException.class,
+                () -> new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setPrimaryMaxRatio(2.0f)
+        );
+
+        assertThrows(
+                "Width must be greater than or equal to zero or WIDTH_UNSET",
+                IllegalArgumentException.class,
+                () -> new DividerAttributes.Builder(DIVIDER_TYPE_DRAGGABLE)
+                        .setWidthDp(-10)
+        );
+    }
+}
diff --git a/window/extensions/extensions/src/test/java/androidx/window/extensions/embedding/SplitAttributesTest.java b/window/extensions/extensions/src/test/java/androidx/window/extensions/embedding/SplitAttributesTest.java
index 1c998a0..f6d34fa 100644
--- a/window/extensions/extensions/src/test/java/androidx/window/extensions/embedding/SplitAttributesTest.java
+++ b/window/extensions/extensions/src/test/java/androidx/window/extensions/embedding/SplitAttributesTest.java
@@ -79,6 +79,17 @@
     }
 
     @Test
+    public void testSplitAttributesEqualsUsingBuilderFromExistingInstance() {
+        final SplitAttributes attrs1 = new SplitAttributes.Builder()
+                .setSplitType(splitEqually())
+                .setLayoutDirection(LayoutDirection.LOCALE)
+                .setAnimationBackground(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT)
+                .build();
+        final SplitAttributes attrs2 = new SplitAttributes.Builder(attrs1).build();
+        assertEquals(attrs1, attrs2);
+    }
+
+    @Test
     public void testSplitTypeEquals() {
         final SplitAttributes.SplitType[] splitTypes = new SplitAttributes.SplitType[]{
                 new SplitAttributes.SplitType.ExpandContainersSplitType(),
diff --git a/window/extensions/extensions/src/test/java/androidx/window/extensions/layout/DisplayFoldFeatureTest.java b/window/extensions/extensions/src/test/java/androidx/window/extensions/layout/DisplayFoldFeatureTest.java
new file mode 100644
index 0000000..e9937a0
--- /dev/null
+++ b/window/extensions/extensions/src/test/java/androidx/window/extensions/layout/DisplayFoldFeatureTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.layout;
+
+import static androidx.window.extensions.layout.DisplayFoldFeature.FOLD_PROPERTY_SUPPORTS_HALF_OPENED;
+import static androidx.window.extensions.layout.DisplayFoldFeature.TYPE_HINGE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class DisplayFoldFeatureTest {
+
+    @Test
+    public void test_builder_matches_constructor() {
+        Set<@DisplayFoldFeature.FoldProperty Integer> properties = new HashSet<>();
+        properties.add(FOLD_PROPERTY_SUPPORTS_HALF_OPENED);
+        DisplayFoldFeature expected = new DisplayFoldFeature(TYPE_HINGE, properties);
+
+        DisplayFoldFeature actual = new DisplayFoldFeature.Builder(TYPE_HINGE)
+                .addProperty(FOLD_PROPERTY_SUPPORTS_HALF_OPENED)
+                .build();
+
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void test_equals_matches() {
+        Set<@DisplayFoldFeature.FoldProperty Integer> properties = new HashSet<>();
+        properties.add(FOLD_PROPERTY_SUPPORTS_HALF_OPENED);
+        DisplayFoldFeature first = new DisplayFoldFeature(TYPE_HINGE, properties);
+        DisplayFoldFeature second = new DisplayFoldFeature(TYPE_HINGE, properties);
+
+        assertEquals(first, second);
+        assertEquals(first.hashCode(), second.hashCode());
+    }
+
+    @Test
+    public void test_getter_matches_values() {
+        final int type = TYPE_HINGE;
+        DisplayFoldFeature actual = new DisplayFoldFeature.Builder(type)
+                .addProperty(FOLD_PROPERTY_SUPPORTS_HALF_OPENED)
+                .build();
+
+        assertEquals(type, actual.getType());
+        assertTrue(actual.hasProperty(FOLD_PROPERTY_SUPPORTS_HALF_OPENED));
+        assertTrue(actual.hasProperties(FOLD_PROPERTY_SUPPORTS_HALF_OPENED));
+    }
+}
diff --git a/window/integration-tests/macrobenchmark-target/build.gradle b/window/integration-tests/macrobenchmark-target/build.gradle
index 8bec782..1631c90 100644
--- a/window/integration-tests/macrobenchmark-target/build.gradle
+++ b/window/integration-tests/macrobenchmark-target/build.gradle
@@ -21,6 +21,7 @@
 }
 
 android {
+    compileSdkVersion 35
     defaultConfig {
         minSdk 28
     }
diff --git a/window/sidecar/sidecar/build.gradle b/window/sidecar/sidecar/build.gradle
index b5badb8..f3d3be9 100644
--- a/window/sidecar/sidecar/build.gradle
+++ b/window/sidecar/sidecar/build.gradle
@@ -30,7 +30,7 @@
 }
 
 dependencies {
-    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 }
 
 androidx {
@@ -40,7 +40,6 @@
     inceptionYear = "2020"
     description = "This version of the OEM extension is deprecated. Please use window:extensions:extensions." +
             "module instead."
-    metalavaK2UastEnabled = true
     doNotDocumentReason = "Should not be published"
 }
 
diff --git a/window/window-core/build.gradle b/window/window-core/build.gradle
index 7d81e47..a484af0 100644
--- a/window/window-core/build.gradle
+++ b/window/window-core/build.gradle
@@ -26,18 +26,17 @@
 
 plugins {
     id("AndroidXPlugin")
+    id("com.android.library")
 }
 
 androidXMultiplatform {
     jvm()
-    androidLibrary {
-        namespace = "androidx.window.core"
-        withAndroidTestOnDeviceBuilder {
-            it.compilationName = "instrumentedTest"
-            it.defaultSourceSetName = "androidInstrumentedTest"
-            it.sourceSetTreeName = "test"
-        }
-    }
+    android()
+    mac()
+    linux()
+    ios()
+    watchos()
+    tvos()
 
     defaultPlatform(PlatformIdentifier.JVM)
 
@@ -45,7 +44,7 @@
         commonMain {
             dependencies {
                 api(libs.kotlinStdlib)
-                api("androidx.annotation:annotation:1.7.0")
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
@@ -66,6 +65,12 @@
             }
         }
     }
+
+    enableBinaryCompatibilityValidator = false
+}
+
+android {
+    namespace "androidx.window.core"
 }
 
 androidx {
@@ -74,4 +79,5 @@
     inceptionYear = "2022"
     description = "WindowManager Core Library."
     legacyDisableKotlinStrictApiMode = true
+    metalavaK2UastEnabled = false
 }
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
index e3d08bc..9df0361 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
@@ -60,7 +60,7 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other == null || this::class != other::class) return false
 
         other as WindowSizeClass
 
diff --git a/window/window-demos/demo-common/build.gradle b/window/window-demos/demo-common/build.gradle
index a828dd0..27c4c9c 100644
--- a/window/window-demos/demo-common/build.gradle
+++ b/window/window-demos/demo-common/build.gradle
@@ -16,13 +16,16 @@
 
 plugins {
     id("AndroidXPlugin")
+    id("AndroidXComposePlugin")
     id("com.android.library")
     id("kotlin-android")
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 23
+        targetSdkVersion 35
     }
     buildFeatures {
         viewBinding true
@@ -31,9 +34,9 @@
 }
 
 dependencies {
-    implementation("androidx.appcompat:appcompat:1.2.0")
+    implementation(project(":appcompat:appcompat"))
     implementation("androidx.core:core-ktx:1.3.2")
-    implementation("androidx.activity:activity:1.2.0")
+    implementation("androidx.activity:activity:1.9.0")
     implementation("androidx.recyclerview:recyclerview:1.2.1")
     api(libs.constraintLayout)
     // TODO(b/152245564) Conflicting dependencies cause IDE errors.
@@ -42,6 +45,11 @@
     implementation("androidx.browser:browser:1.3.0")
     implementation("androidx.startup:startup-runtime:1.1.0")
 
+    // Compose dependencies
+    implementation(project(":compose:runtime:runtime"))
+    implementation(project(":compose:foundation:foundation"))
+    implementation("androidx.compose.material3:material3:1.2.1")
+
     implementation(project(":window:window-java"))
     debugImplementation(libs.leakcanary)
 }
diff --git a/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/DemoTheme.kt b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/DemoTheme.kt
new file mode 100644
index 0000000..686c802
--- /dev/null
+++ b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/DemoTheme.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.common
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.colorResource
+
+@Composable
+fun DemoTheme(
+    darkTheme: Boolean = isSystemInDarkTheme(),
+    content: @Composable () -> Unit,
+) {
+    val colorScheme =
+        if (darkTheme) {
+            darkColorScheme(
+                primary = colorResource(R.color.colorPrimaryDark),
+                secondary = colorResource(R.color.colorAccent),
+            )
+        } else {
+            lightColorScheme(
+                primary = colorResource(R.color.colorPrimary),
+                secondary = colorResource(R.color.colorAccent),
+            )
+        }
+
+    MaterialTheme(colorScheme = colorScheme, content = content)
+}
diff --git a/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/DisplayFeaturesActivity.kt b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/DisplayFeaturesActivity.kt
index fee0c73..bef6cf6 100644
--- a/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/DisplayFeaturesActivity.kt
+++ b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/DisplayFeaturesActivity.kt
@@ -21,7 +21,6 @@
 import android.view.Menu
 import android.view.MenuItem
 import android.view.View
-import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
@@ -40,7 +39,7 @@
 import kotlinx.coroutines.launch
 
 /** Demo activity that shows all display features and current device state on the screen. */
-open class DisplayFeaturesActivity : AppCompatActivity() {
+open class DisplayFeaturesActivity : EdgeToEdgeActivity() {
 
     private val infoLogAdapter = InfoLogAdapter()
     private val displayFeatureViews = ArrayList<View>()
diff --git a/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/EdgeToEdgeActivity.kt b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/EdgeToEdgeActivity.kt
new file mode 100644
index 0000000..06bebf78
--- /dev/null
+++ b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/EdgeToEdgeActivity.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.common
+
+import android.os.Bundle
+import android.view.View
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
+
+/** An activity to make its UI content edge-to-edge and fits system windows. */
+open class EdgeToEdgeActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        enableEdgeToEdge()
+        super.onCreate(savedInstanceState)
+    }
+
+    override fun setContentView(view: View?) {
+        super.setContentView(view)
+        view?.fitsSystemWindows = true
+    }
+}
diff --git a/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/infolog/InfoLogAdapter.kt b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/infolog/InfoLogAdapter.kt
index 454d6fe..5963f3df 100644
--- a/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/infolog/InfoLogAdapter.kt
+++ b/window/window-demos/demo-common/src/main/java/androidx/window/demo/common/infolog/InfoLogAdapter.kt
@@ -53,6 +53,11 @@
         ++id
     }
 
+    fun appendAndNotify(title: String, message: String) {
+        append(title, message)
+        notifyDataSetChanged()
+    }
+
     private fun append(item: InfoLog) {
         items.add(0, item)
     }
diff --git a/window/window-demos/demo-second-app/build.gradle b/window/window-demos/demo-second-app/build.gradle
index 336950f..c39a226 100644
--- a/window/window-demos/demo-second-app/build.gradle
+++ b/window/window-demos/demo-second-app/build.gradle
@@ -21,9 +21,11 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         applicationId "androidx.window.demo2"
         minSdkVersion 23
+        targetSdkVersion 35
     }
     buildFeatures {
         viewBinding true
@@ -32,8 +34,8 @@
 }
 
 dependencies {
-    implementation("androidx.activity:activity:1.2.0")
-    implementation("androidx.appcompat:appcompat:1.2.0")
+    implementation("androidx.activity:activity:1.9.0")
+    implementation(project(":appcompat:appcompat"))
     api(libs.constraintLayout)
     implementation("androidx.core:core-ktx:1.8.0")
     // TODO(b/152245564) Conflicting dependencies cause IDE errors.
diff --git a/window/window-demos/demo-second-app/src/main/AndroidManifest.xml b/window/window-demos/demo-second-app/src/main/AndroidManifest.xml
index 15d5101..177762b 100644
--- a/window/window-demos/demo-second-app/src/main/AndroidManifest.xml
+++ b/window/window-demos/demo-second-app/src/main/AndroidManifest.xml
@@ -17,6 +17,7 @@
         android:supportsRtl="true">
         <activity
             android:name=".embedding.TrustedEmbeddingActivity"
+            android:supportsPictureInPicture="true"
             android:exported="true"
             android:label="@string/trusted_embedding_activity"
             android:configChanges=
@@ -30,6 +31,7 @@
         </activity>
         <activity
             android:name=".embedding.UntrustedEmbeddingActivity"
+            android:supportsPictureInPicture="true"
             android:exported="true"
             android:label="@string/untrusted_embedding_activity"
             android:configChanges=
@@ -39,6 +41,11 @@
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <!-- Require to restore the split after entering and exiting picture-in-picture mode
+            for untrusted embedding. -->
+            <property
+                android:name="android.window.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING"
+                android:value="true" />
         </activity>
         <activity-alias
             android:name="androidx.window.demo2.DisplayFeaturesActivity"
diff --git a/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/EmbeddedActivityBase.kt b/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/EmbeddedActivityBase.kt
new file mode 100644
index 0000000..e3406ce
--- /dev/null
+++ b/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/EmbeddedActivityBase.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo2.embedding
+
+import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.os.Bundle
+import android.widget.TextView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.WindowSdkExtensions
+import androidx.window.demo.common.EdgeToEdgeActivity
+import androidx.window.demo.common.util.PictureInPictureUtil
+import androidx.window.demo2.R
+import androidx.window.demo2.databinding.ActivityEmbeddedBinding
+import androidx.window.embedding.ActivityEmbeddingController
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+open class EmbeddedActivityBase : EdgeToEdgeActivity() {
+    lateinit var viewBinding: ActivityEmbeddedBinding
+    private lateinit var activityEmbeddingController: ActivityEmbeddingController
+    private lateinit var windowInfoView: TextView
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        viewBinding = ActivityEmbeddedBinding.inflate(layoutInflater)
+        setContentView(viewBinding.root)
+        viewBinding.buttonPip.setOnClickListener {
+            PictureInPictureUtil.startPictureInPicture(this, false)
+        }
+        viewBinding.buttonStartActivity.setOnClickListener {
+            startActivity(Intent(this, this.javaClass))
+        }
+        viewBinding.buttonStartActivityFromApplicationContext.setOnClickListener {
+            application.startActivity(Intent(this, this.javaClass).setFlags(FLAG_ACTIVITY_NEW_TASK))
+        }
+
+        activityEmbeddingController = ActivityEmbeddingController.getInstance(this)
+        initializeEmbeddedActivityInfoCallback()
+    }
+
+    private fun initializeEmbeddedActivityInfoCallback() {
+        val extensionVersion = WindowSdkExtensions.getInstance().extensionVersion
+        if (extensionVersion < 6) {
+            // EmbeddedActivityWindowInfo is only available on 6+.
+            return
+        }
+
+        windowInfoView = viewBinding.windowIntoText
+        lifecycleScope.launch(Dispatchers.Main) {
+            // Collect EmbeddedActivityWindowInfo when STARTED and stop when STOPPED.
+            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // After register, the flow will be triggered immediately if the activity is
+                // embedded.
+                // However, if the activity is changed to non-embedded state in background (after
+                // STOPPED), the flow will not report the change (because it has been unregistered).
+                // Reset before start listening.
+                resetWindowInfoView()
+                activityEmbeddingController
+                    .embeddedActivityWindowInfo(this@EmbeddedActivityBase)
+                    .collect { info ->
+                        if (info.isEmbedded) {
+                            windowInfoView.text = info.toString()
+                        } else {
+                            resetWindowInfoView()
+                        }
+                    }
+            }
+        }
+    }
+
+    private fun resetWindowInfoView() {
+        windowInfoView.text = getString(R.string.embedded_window_info_unavailable)
+    }
+}
diff --git a/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/TrustedEmbeddingActivity.kt b/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/TrustedEmbeddingActivity.kt
index 67c0b09..1a2ea23 100644
--- a/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/TrustedEmbeddingActivity.kt
+++ b/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/TrustedEmbeddingActivity.kt
@@ -16,9 +16,7 @@
 
 package androidx.window.demo2.embedding
 
-import android.app.Activity
 import android.os.Bundle
-import android.widget.TextView
 import androidx.window.demo2.R
 
 /**
@@ -26,12 +24,10 @@
  * `android:allowUntrustedActivityEmbedding` in AndroidManifest. Activity can be launched from the
  * split demos in window-samples/demos.
  */
-class TrustedEmbeddingActivity : Activity() {
+class TrustedEmbeddingActivity : EmbeddedActivityBase() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_embedded)
-        findViewById<TextView>(R.id.detail_text_view).text =
-            getString(R.string.trusted_embedding_activity_detail)
+        viewBinding.detailTextView.text = getString(R.string.trusted_embedding_activity_detail)
     }
 }
diff --git a/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/UntrustedEmbeddingActivity.kt b/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/UntrustedEmbeddingActivity.kt
index b8405f4..487e4fa 100644
--- a/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/UntrustedEmbeddingActivity.kt
+++ b/window/window-demos/demo-second-app/src/main/java/androidx/window/demo2/embedding/UntrustedEmbeddingActivity.kt
@@ -16,21 +16,17 @@
 
 package androidx.window.demo2.embedding
 
-import android.app.Activity
 import android.os.Bundle
-import android.widget.TextView
 import androidx.window.demo2.R
 
 /**
  * Activity that can be embedded in untrusted mode. See `android:allowUntrustedActivityEmbedding` in
  * AndroidManifest. Activity can be launched from the split demos in window-samples/demos.
  */
-class UntrustedEmbeddingActivity : Activity() {
+class UntrustedEmbeddingActivity : EmbeddedActivityBase() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_embedded)
-        findViewById<TextView>(R.id.detail_text_view).text =
-            getString(R.string.untrusted_embedding_activity_detail)
+        viewBinding.detailTextView.text = getString(R.string.untrusted_embedding_activity_detail)
     }
 }
diff --git a/window/window-demos/demo-second-app/src/main/res/layout/activity_embedded.xml b/window/window-demos/demo-second-app/src/main/res/layout/activity_embedded.xml
index fbd572c..102145e 100644
--- a/window/window-demos/demo-second-app/src/main/res/layout/activity_embedded.xml
+++ b/window/window-demos/demo-second-app/src/main/res/layout/activity_embedded.xml
@@ -15,9 +15,13 @@
   limitations under the License.
   -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:padding="10dp"
+    android:background="@color/colorAccent">
 
     <TextView
         android:layout_gravity="center"
@@ -25,4 +29,44 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
 
-</FrameLayout>
\ No newline at end of file
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:background="#AAAAAA" />
+
+    <TextView
+        android:layout_gravity="center"
+        android:id="@+id/window_into_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/embedded_window_info_unavailable"/>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:background="#AAAAAA" />
+
+    <Button
+        android:id="@+id/button_pip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="ENTER PIP" />
+    <Button
+        android:id="@+id/button_start_activity"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="Start a new instance of current Activity" />
+    <Button
+        android:id="@+id/button_start_activity_from_application_context"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="Start a new instance of current Activity from Application Context" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo-second-app/src/main/res/values/strings.xml b/window/window-demos/demo-second-app/src/main/res/values/strings.xml
index de4172b..7fa3d6d 100644
--- a/window/window-demos/demo-second-app/src/main/res/values/strings.xml
+++ b/window/window-demos/demo-second-app/src/main/res/values/strings.xml
@@ -22,4 +22,6 @@
     <string name="untrusted_embedding_activity">Untrusted Embedding Activity</string>
     <string name="untrusted_embedding_activity_detail">Activity allows embedding in untrusted mode
         via opt-in.</string>
+    <string name="embedded_window_info_unavailable">EmbeddedActivityWindowInfo not available
+    </string>
 </resources>
\ No newline at end of file
diff --git a/window/window-demos/demo/build.gradle b/window/window-demos/demo/build.gradle
index 615fea8..fc2d898 100644
--- a/window/window-demos/demo/build.gradle
+++ b/window/window-demos/demo/build.gradle
@@ -21,19 +21,22 @@
  * Please use that script when creating a new project, rather than copying an existing project and
  * modifying its settings.
  */
-import androidx.build.LibraryType
+
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
+    id("AndroidXComposePlugin")
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         applicationId "androidx.window.demo"
         minSdkVersion 23
+        targetSdkVersion 35
     }
     buildFeatures {
         viewBinding true
@@ -65,9 +68,9 @@
 }
 
 dependencies {
-    implementation("androidx.appcompat:appcompat:1.5.1")
+    implementation(project(":appcompat:appcompat"))
     implementation("androidx.core:core-ktx:1.3.2")
-    implementation("androidx.activity:activity:1.2.0")
+    implementation("androidx.activity:activity:1.9.0")
     implementation("androidx.recyclerview:recyclerview:1.2.1")
     // TODO(b/262583150): force tracing 1.1.0 since its required by androidTest
     implementation("androidx.tracing:tracing:1.1.0")
@@ -77,8 +80,20 @@
     implementation "androidx.browser:browser:1.3.0"
     implementation("androidx.startup:startup-runtime:1.1.0")
 
+    // Compose dependencies
+    implementation(project(":compose:runtime:runtime"))
+    implementation(project(":compose:foundation:foundation"))
+    implementation(project(":compose:foundation:foundation-layout"))
+    implementation(project(":compose:ui:ui-tooling-preview"))
+    debugImplementation(project(":compose:ui:ui-tooling"))
+    implementation("androidx.activity:activity-compose:1.9.0")
+    implementation("androidx.compose.material3:material3:1.2.1")
+    implementation("androidx.compose.material:material-icons-extended:1.6.8")
+    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3")
+
     implementation(project(":window:window-java"))
     implementation(project(":window:window-demos:demo-common"))
+
     debugImplementation(libs.leakcanary)
 
     androidTestImplementation(libs.testCore)
@@ -89,7 +104,7 @@
     androidTestImplementation(project(":window:window-testing"))
     androidTestImplementation(project(":window:window-demos:demo-common"))
 
-    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.2")
+    coreLibraryDesugaring(libs.desugarJdkLibs)
 }
 
 // Allow usage of Kotlin's @OptIn.
diff --git a/window/window-demos/demo/lint-baseline.xml b/window/window-demos/demo/lint-baseline.xml
index 5183004..bcdf5d8 100644
--- a/window/window-demos/demo/lint-baseline.xml
+++ b/window/window-demos/demo/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.6.0-alpha03" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha03)" variant="all" version="8.6.0-alpha03">
+<issues format="6" by="lint 8.6.0-alpha07" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-alpha07)" variant="all" version="8.6.0-alpha07">
 
     <issue
         id="NewApi"
@@ -29,30 +29,12 @@
     </issue>
 
     <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.window.demo.embedding.SplitImeActivityPlaceholder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            attrs.fitInsetsTypes = WindowInsets.Type.statusBars()"
-        errorLine2="                  ~~~~~~~~~~~~~~">
+        id="WrongConstant"
+        message="Must be one or more of: LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON, LayoutParams.FLAG_DIM_BEHIND, LayoutParams.FLAG_BLUR_BEHIND, LayoutParams.FLAG_NOT_FOCUSABLE, LayoutParams.FLAG_NOT_TOUCHABLE, LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, LayoutParams.FLAG_KEEP_SCREEN_ON, LayoutParams.FLAG_LAYOUT_IN_SCREEN, LayoutParams.FLAG_LAYOUT_NO_LIMITS, LayoutParams.FLAG_FULLSCREEN, LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, LayoutParams.FLAG_DITHER, LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SCALED, LayoutParams.FLAG_IGNORE_CHEEK_PRESSES, LayoutParams.FLAG_LAYOUT_INSET_DECOR, LayoutParams.FLAG_ALT_FOCUSABLE_IM, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_SHOW_WHEN_LOCKED, LayoutParams.FLAG_SHOW_WALLPAPER, LayoutParams.FLAG_TURN_SCREEN_ON, LayoutParams.FLAG_DISMISS_KEYGUARD, LayoutParams.FLAG_SPLIT_TOUCH, LayoutParams.FLAG_HARDWARE_ACCELERATED, LayoutParams.FLAG_LAYOUT_IN_OVERSCAN, LayoutParams.FLAG_TRANSLUCENT_STATUS, LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, LayoutParams.FLAG_LOCAL_FOCUS_MODE, LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR, LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS"
+        errorLine1="                    presentation!!"
+        errorLine2="                    ^">
         <location
-            file="src/main/java/androidx/window/demo/embedding/SplitImeActivityPlaceholder.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.window.demo.embedding.SplitImeActivityPlaceholder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            attrs.fitInsetsTypes = WindowInsets.Type.statusBars()"
-        errorLine2="                                                     ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/window/demo/embedding/SplitImeActivityPlaceholder.kt"/>
-    </issue>
-
-    <issue
-        id="ClassVerificationFailure"
-        message="This call references a method added in API level 30; however, the containing class androidx.window.demo.embedding.SplitImeActivityPlaceholder is reachable from earlier API levels and will fail run-time class verification."
-        errorLine1="            attrs.fitInsetsTypes = WindowInsets.Type.statusBars()"
-        errorLine2="                                                     ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/window/demo/embedding/SplitImeActivityPlaceholder.kt"/>
+            file="src/main/java/androidx/window/demo/PresentationActivity.kt"/>
     </issue>
 
 </issues>
diff --git a/window/window-demos/demo/src/main/AndroidManifest.xml b/window/window-demos/demo/src/main/AndroidManifest.xml
index 5b81535..214ca2d 100644
--- a/window/window-demos/demo/src/main/AndroidManifest.xml
+++ b/window/window-demos/demo/src/main/AndroidManifest.xml
@@ -22,18 +22,12 @@
         <property
             android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
             android:value="true" />
+        <!-- The app itself supports activity embedding, so a system override is not needed. -->
+        <property
+            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
+            android:value="false" />
 
-        <service android:name=".TestIme"
-            android:label="@string/test_ime"
-            android:permission="android.permission.BIND_INPUT_METHOD"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.view.InputMethod"/>
-            </intent-filter>
-            <meta-data android:name="android.view.im"
-                android:resource="@xml/method"/>
-        </service>
-
+        <!--region WindowManager Demos -->
         <activity android:name=".demos.WindowDemosActivity"
             android:exported="true"
             android:label="@string/windowManagerDemos">
@@ -74,6 +68,9 @@
             android:exported="false"
             android:configChanges="orientation|screenSize|screenLayout|screenSize"
             android:label="@string/window_metrics"/>
+        <!--endregion WindowManager Demos -->
+
+        <!--region Rear Display Demos -->
         <activity android:name=".area.RearDisplayActivityConfigChanges"
             android:exported="true"
             android:configChanges=
@@ -84,6 +81,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".area.RearDisplayPresentationActivity"
+            android:exported="false"
+            android:configChanges="orientation|screenLayout|screenSize|layoutDirection|smallestScreenSize"
+            android:label="@string/dual_display" />
+        <!--endregion Rear Display Demos -->
+
+        <!--region Sample showcase of split activity rules -->
         <activity
             android:name=".embedding.SplitActivityA"
             android:exported="true"
@@ -132,10 +136,17 @@
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
             android:taskAffinity="androidx.window.demo.manual_split_affinity"/>
         <activity
+            android:name=".embedding.DialogActivity"
+            android:theme="@style/Theme.AppCompat.Dialog"
+            android:exported="false"
+            android:label="Dialog Activity"
+            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+            android:taskAffinity="androidx.window.demo.manual_split_affinity"/>
+        <activity
             android:name=".embedding.ExpandedDialogActivity"
             android:theme="@style/ExpandedDialogTheme"
             android:exported="false"
-            android:label="Dialog Activity"
+            android:label="Expanded Dialog Activity"
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
             android:taskAffinity="androidx.window.demo.manual_split_affinity"/>
         <activity
@@ -176,9 +187,9 @@
             android:label="Placeholder"
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
             android:taskAffinity="androidx.window.demo.list_detail_split_affinity" />
+        <!--endregion Sample showcase of split activity rules -->
 
-        <!-- Split PiP App -->
-
+        <!-- region Split PiP App -->
         <activity
             android:name=".embedding.SplitPipActivityA"
             android:exported="true"
@@ -213,10 +224,11 @@
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
             android:taskAffinity="androidx.window.demo.split_pip">
         </activity>
+        <!-- endregion Split PiP App -->
 
+        <!-- region Split on Device State -->
         <!-- The demo App to show how to change the current split layout with the current device and
          window states -->
-
         <activity
             android:name=".embedding.SplitDeviceStateActivityA"
             android:exported="true"
@@ -235,7 +247,9 @@
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
             android:taskAffinity="androidx.window.demo.split_device_state_activity_affinity">
         </activity>
+        <!-- endregion Split on Device State -->
 
+        <!-- region Split Toggle at Runtime -->
         <!-- The demo app to show how to change layout with runtime APIs -->
         <activity
             android:name=".embedding.SplitAttributesToggleMainActivity"
@@ -263,16 +277,26 @@
             android:label="Split Toggle Placeholder"
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
             android:taskAffinity="androidx.window.demo.split_attributes_toggle_activity_affinity" />
+        <!-- endregion Split Toggle at Runtime -->
 
+        <!-- region IME usages in ActivityEmbedding split -->
         <!-- The demo app that shows various IME-related use cases -->
-
         <activity android:name=".ImeActivity"
             android:exported="false"
             android:configChanges="orientation|screenSize|screenLayout|screenSize"
-            android:label="@string/ime"/>
-
+            android:label="@string/ime" />
+        <service android:name=".TestIme"
+            android:label="@string/test_ime"
+            android:permission="android.permission.BIND_INPUT_METHOD"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.view.InputMethod" />
+            </intent-filter>
+            <meta-data
+                android:name="android.view.im"
+                android:resource="@xml/method" />
+        </service>
         <!-- The demo app to show IME usages in ActivityEmbedding split. -->
-
         <activity
             android:name=".embedding.SplitImeActivityMain"
             android:exported="true"
@@ -291,9 +315,37 @@
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
             android:taskAffinity="androidx.window.demo.split_ime">
         </activity>
+        <!-- endregion IME usages in ActivityEmbedding split -->
 
+        <!-- region Overlay features -->
+        <!-- The demo app to show how to use overlay features -->
+        <activity
+            android:name=".embedding.OverlayAssociatedActivityA"
+            android:exported="true"
+            android:label="Overlay Activity A"
+            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+            android:taskAffinity="androidx.window.demo.overlay_activity_affinity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".embedding.OverlayAssociatedActivityB"
+            android:exported="true"
+            android:label="Overlay Activity B"
+            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+            android:taskAffinity="androidx.window.demo.overlay_activity_affinity">
+        </activity>
+        <activity-alias
+            android:name=".embedding.SplitWithOverlayActivity"
+            android:targetActivity=".embedding.SplitActivityDetail"
+            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+            android:taskAffinity="androidx.window.demo.overlay_activity_affinity" />
+        <!-- endregion Overlay features -->
+
+        <!-- region Display configuration callbacks -->
         <!-- The demo app to show display configuration from different system callbacks. -->
-
         <activity
             android:name=".coresdk.WindowStateCallbackActivity"
             android:exported="true"
@@ -305,9 +357,9 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <!-- endregion Display configuration callbacks -->
 
-        <!-- Activity embedding initializer -->
-
+        <!-- region Activity embedding initializer -->
         <provider android:name="androidx.startup.InitializationProvider"
             android:authorities="${applicationId}.androidx-startup"
             android:exported="false"
@@ -316,11 +368,7 @@
             <meta-data  android:name="androidx.window.demo.embedding.ExampleWindowInitializer"
                 android:value="androidx.startup" />
         </provider>
-
-        <!-- The app itself supports activity embedding, so a system override is not needed. -->
-        <property
-            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
-            android:value="false" />
+        <!-- endregion Activity embedding initializer -->
 
     </application>
 </manifest>
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/ImeActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/ImeActivity.kt
index 898ce04..f639f70 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/ImeActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/ImeActivity.kt
@@ -21,10 +21,10 @@
 import android.provider.Settings
 import android.view.inputmethod.InputMethodManager
 import android.widget.Button
-import androidx.appcompat.app.AppCompatActivity
+import androidx.window.demo.common.EdgeToEdgeActivity
 
 /** Demo app that shows various IME-related features. */
-class ImeActivity : AppCompatActivity() {
+class ImeActivity : EdgeToEdgeActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/PresentationActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/PresentationActivity.kt
index 51b4af7..1699741 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/PresentationActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/PresentationActivity.kt
@@ -27,10 +27,10 @@
 import android.view.View
 import android.widget.TextView
 import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.layout.FoldingFeature
 import androidx.window.layout.WindowInfoTracker
 import androidx.window.layout.WindowLayoutInfo
@@ -41,7 +41,7 @@
  * Demo activity that reacts to foldable device state change and shows content on the outside
  * display when the device is folded.
  */
-class PresentationActivity : AppCompatActivity() {
+class PresentationActivity : EdgeToEdgeActivity() {
     private val TAG = "FoldablePresentation"
 
     private var presentation: DemoPresentation? = null
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/SplitLayoutActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/SplitLayoutActivity.kt
index e3b89a2..cf29e6d 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/SplitLayoutActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/SplitLayoutActivity.kt
@@ -17,16 +17,16 @@
 package androidx.window.demo
 
 import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.layout.WindowInfoTracker
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 
 /** Demo of [SplitLayout]. */
-class SplitLayoutActivity : AppCompatActivity() {
+class SplitLayoutActivity : EdgeToEdgeActivity() {
 
     private lateinit var splitLayout: SplitLayout
 
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/TestIme.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/TestIme.kt
index 2a13682..06c94dc 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/TestIme.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/TestIme.kt
@@ -78,7 +78,8 @@
                 .append(
                     "Width: $width, Height: $height\n" +
                         "Top: ${windowMetrics.bounds.top}, Bottom: ${windowMetrics.bounds.bottom}, " +
-                        "Left: ${windowMetrics.bounds.left}, Right: ${windowMetrics.bounds.right}"
+                        "Left: ${windowMetrics.bounds.left}, Right: ${windowMetrics.bounds.right}\n" +
+                        "Density: ${windowMetrics.density}"
                 )
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/WindowMetricsActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/WindowMetricsActivity.kt
index 4bdc573..8afa7d5 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/WindowMetricsActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/WindowMetricsActivity.kt
@@ -18,12 +18,12 @@
 
 import android.content.res.Configuration
 import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
 import androidx.recyclerview.widget.RecyclerView
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.demo.common.infolog.InfoLogAdapter
 import androidx.window.layout.WindowMetricsCalculator
 
-class WindowMetricsActivity : AppCompatActivity() {
+class WindowMetricsActivity : EdgeToEdgeActivity() {
 
     private val adapter = InfoLogAdapter()
 
@@ -45,7 +45,10 @@
         val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this)
         val width = windowMetrics.bounds.width()
         val height = windowMetrics.bounds.height()
-        adapter.append("WindowMetrics update", "width: $width, height: $height")
+        adapter.append(
+            "WindowMetrics update",
+            "width: $width, height: $height, " + "density: ${windowMetrics.density}"
+        )
         adapter.notifyDataSetChanged()
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
index bff1424..d0514a1 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
@@ -17,23 +17,20 @@
 package androidx.window.demo.area
 
 import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.ContextCompat
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
-import androidx.window.area.WindowAreaCapability
 import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_TRANSFER_ACTIVITY_TO_AREA
-import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNAVAILABLE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
 import androidx.window.area.WindowAreaController
 import androidx.window.area.WindowAreaInfo
-import androidx.window.area.WindowAreaInfo.Type.Companion.TYPE_REAR_FACING
 import androidx.window.area.WindowAreaSession
 import androidx.window.area.WindowAreaSessionCallback
 import androidx.window.core.ExperimentalWindowApi
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.demo.common.infolog.InfoLogAdapter
 import androidx.window.demo.databinding.ActivityRearDisplayBinding
 import java.text.SimpleDateFormat
@@ -41,9 +38,6 @@
 import java.util.Locale
 import java.util.concurrent.Executor
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
 /**
@@ -53,15 +47,14 @@
  * This Activity overrides configuration changes for simplicity.
  */
 @OptIn(ExperimentalWindowApi::class)
-class RearDisplayActivityConfigChanges : AppCompatActivity(), WindowAreaSessionCallback {
+class RearDisplayActivityConfigChanges : EdgeToEdgeActivity(), WindowAreaSessionCallback {
 
     private lateinit var windowAreaController: WindowAreaController
     private var rearDisplaySession: WindowAreaSession? = null
-    private var rearDisplayWindowAreaInfo: WindowAreaInfo? = null
-    private var rearDisplayStatus: WindowAreaCapability.Status = WINDOW_AREA_STATUS_UNSUPPORTED
     private val infoLogAdapter = InfoLogAdapter()
     private lateinit var binding: ActivityRearDisplayBinding
     private lateinit var executor: Executor
+    private var currentWindowAreaInfo: WindowAreaInfo? = null
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -73,59 +66,77 @@
 
         binding.rearStatusRecyclerView.adapter = infoLogAdapter
 
-        binding.rearDisplayButton.setOnClickListener {
-            if (rearDisplayStatus == WINDOW_AREA_STATUS_ACTIVE) {
-                if (rearDisplaySession == null) {
-                    rearDisplaySession =
-                        rearDisplayWindowAreaInfo?.getActiveSession(
-                            OPERATION_TRANSFER_ACTIVITY_TO_AREA
-                        )
+        lifecycleScope.launch(Dispatchers.Main) {
+            // The block passed to repeatOnLifecycle is executed when the lifecycle
+            // is at least STARTED and is cancelled when the lifecycle is STOPPED.
+            // It automatically restarts the block when the lifecycle is STARTED again.
+            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // Safely collect from windowInfoRepo when the lifecycle is STARTED
+                // and stops collection when the lifecycle is STOPPED
+                windowAreaController.windowAreaInfos.collect { windowAreaInfos ->
+                    infoLogAdapter.appendAndNotify(
+                        getCurrentTimeString(),
+                        "number of areas: " + windowAreaInfos.size
+                    )
+                    windowAreaInfos.forEach { windowAreaInfo ->
+                        if (windowAreaInfo.type == WindowAreaInfo.Type.TYPE_REAR_FACING) {
+                            currentWindowAreaInfo = windowAreaInfo
+                            val transferCapability =
+                                windowAreaInfo.getCapability(OPERATION_TRANSFER_ACTIVITY_TO_AREA)
+                            infoLogAdapter.append(
+                                getCurrentTimeString(),
+                                transferCapability.status.toString() +
+                                    " : " +
+                                    windowAreaInfo.metrics.toString()
+                            )
+                            updateRearDisplayButton()
+                        }
+                    }
+                    infoLogAdapter.notifyDataSetChanged()
                 }
+            }
+        }
+
+        binding.rearDisplayButton.setOnClickListener {
+            if (rearDisplaySession != null) {
                 rearDisplaySession?.close()
             } else {
-                rearDisplayWindowAreaInfo?.token?.let { token ->
+                currentWindowAreaInfo?.let {
                     windowAreaController.transferActivityToWindowArea(
-                        token = token,
-                        activity = this,
-                        executor = executor,
-                        windowAreaSessionCallback = this
+                        it.token,
+                        this,
+                        executor,
+                        this
                     )
                 }
             }
         }
 
-        lifecycleScope.launch(Dispatchers.Main) {
-            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                windowAreaController.windowAreaInfos
-                    .map { windowAreaInfoList ->
-                        windowAreaInfoList.firstOrNull { windowAreaInfo ->
-                            windowAreaInfo.type == TYPE_REAR_FACING
-                        }
-                    }
-                    .onEach { windowAreaInfo -> rearDisplayWindowAreaInfo = windowAreaInfo }
-                    .map(this@RearDisplayActivityConfigChanges::getRearDisplayStatus)
-                    .distinctUntilChanged()
-                    .collect { status ->
-                        infoLogAdapter.append(getCurrentTimeString(), status.toString())
-                        infoLogAdapter.notifyDataSetChanged()
-                        rearDisplayStatus = status
-                        updateRearDisplayButton()
-                    }
+        binding.rearDisplaySessionButton.setOnClickListener {
+            if (rearDisplaySession == null) {
+                try {
+                    rearDisplaySession =
+                        currentWindowAreaInfo?.getActiveSession(OPERATION_TRANSFER_ACTIVITY_TO_AREA)
+                    updateRearDisplayButton()
+                } catch (e: IllegalStateException) {
+                    infoLogAdapter.appendAndNotify(getCurrentTimeString(), e.toString())
+                }
             }
         }
     }
 
     override fun onSessionStarted(session: WindowAreaSession) {
         rearDisplaySession = session
-        infoLogAdapter.append(getCurrentTimeString(), "RearDisplay Session has been started")
-        infoLogAdapter.notifyDataSetChanged()
+        infoLogAdapter.appendAndNotify(
+            getCurrentTimeString(),
+            "RearDisplay Session has been started"
+        )
         updateRearDisplayButton()
     }
 
     override fun onSessionEnded(t: Throwable?) {
         rearDisplaySession = null
-        infoLogAdapter.append(getCurrentTimeString(), "RearDisplay Session has ended")
-        infoLogAdapter.notifyDataSetChanged()
+        infoLogAdapter.appendAndNotify(getCurrentTimeString(), "RearDisplay Session has ended")
         updateRearDisplayButton()
     }
 
@@ -135,22 +146,24 @@
             binding.rearDisplayButton.text = "Disable RearDisplay Mode"
             return
         }
-        when (rearDisplayStatus) {
-            WINDOW_AREA_STATUS_UNSUPPORTED -> {
-                binding.rearDisplayButton.isEnabled = false
-                binding.rearDisplayButton.text = "RearDisplay is not supported on this device"
-            }
-            WINDOW_AREA_STATUS_UNAVAILABLE -> {
-                binding.rearDisplayButton.isEnabled = false
-                binding.rearDisplayButton.text = "RearDisplay is not currently available"
-            }
-            WINDOW_AREA_STATUS_AVAILABLE -> {
-                binding.rearDisplayButton.isEnabled = true
-                binding.rearDisplayButton.text = "Enable RearDisplay Mode"
-            }
-            WINDOW_AREA_STATUS_ACTIVE -> {
-                binding.rearDisplayButton.isEnabled = true
-                binding.rearDisplayButton.text = "Disable RearDisplay Mode"
+        currentWindowAreaInfo?.let { windowAreaInfo ->
+            when (windowAreaInfo.getCapability(OPERATION_TRANSFER_ACTIVITY_TO_AREA).status) {
+                WINDOW_AREA_STATUS_UNSUPPORTED -> {
+                    binding.rearDisplayButton.isEnabled = false
+                    binding.rearDisplayButton.text = "RearDisplay is not supported on this device"
+                }
+                WINDOW_AREA_STATUS_UNAVAILABLE -> {
+                    binding.rearDisplayButton.isEnabled = false
+                    binding.rearDisplayButton.text = "RearDisplay is not currently available"
+                }
+                WINDOW_AREA_STATUS_AVAILABLE -> {
+                    binding.rearDisplayButton.isEnabled = true
+                    binding.rearDisplayButton.text = "Enable RearDisplay Mode"
+                }
+                else -> {
+                    binding.rearDisplayButton.isEnabled = false
+                    binding.rearDisplayButton.text = "RearDisplay is not supported on this device"
+                }
             }
         }
     }
@@ -161,11 +174,6 @@
         return currentDate.toString()
     }
 
-    private fun getRearDisplayStatus(windowAreaInfo: WindowAreaInfo?): WindowAreaCapability.Status {
-        val status = windowAreaInfo?.getCapability(OPERATION_TRANSFER_ACTIVITY_TO_AREA)?.status
-        return status ?: WINDOW_AREA_STATUS_UNSUPPORTED
-    }
-
     private companion object {
         private val TAG = RearDisplayActivityConfigChanges::class.java.simpleName
     }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayPresentationActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayPresentationActivity.kt
new file mode 100644
index 0000000..c5e5985
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayPresentationActivity.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.area
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.area.WindowAreaCapability
+import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_PRESENT_ON_AREA
+import androidx.window.area.WindowAreaController
+import androidx.window.area.WindowAreaInfo
+import androidx.window.area.WindowAreaInfo.Type.Companion.TYPE_REAR_FACING
+import androidx.window.area.WindowAreaPresentationSessionCallback
+import androidx.window.area.WindowAreaSessionPresenter
+import androidx.window.core.ExperimentalWindowApi
+import androidx.window.demo.R
+import androidx.window.demo.common.EdgeToEdgeActivity
+import androidx.window.demo.common.infolog.InfoLogAdapter
+import androidx.window.demo.databinding.ActivityRearDisplayPresentationBinding
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+/**
+ * Demo Activity that showcases listening for the status of the [OPERATION_PRESENT_ON_AREA]
+ * operation on a [WindowAreaInfo] of type [TYPE_REAR_FACING] as well as enabling/disabling a
+ * presentation session on that window area. This Activity implements
+ * [WindowAreaPresentationSessionCallback] for simplicity.
+ *
+ * This Activity overrides configuration changes for simplicity.
+ */
+@OptIn(ExperimentalWindowApi::class)
+class RearDisplayPresentationActivity :
+    EdgeToEdgeActivity(), WindowAreaPresentationSessionCallback {
+
+    private var activePresentation: WindowAreaSessionPresenter? = null
+    private var currentWindowAreaInfo: WindowAreaInfo? = null
+    private lateinit var windowAreaController: WindowAreaController
+    private val infoLogAdapter = InfoLogAdapter()
+
+    private lateinit var binding: ActivityRearDisplayPresentationBinding
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        windowAreaController = WindowAreaController.getOrCreate()
+
+        binding = ActivityRearDisplayPresentationBinding.inflate(layoutInflater)
+        setContentView(binding.root)
+        binding.rearStatusRecyclerView.adapter = infoLogAdapter
+
+        lifecycleScope.launch(Dispatchers.Main) {
+            // The block passed to repeatOnLifecycle is executed when the lifecycle
+            // is at least STARTED and is cancelled when the lifecycle is STOPPED.
+            // It automatically restarts the block when the lifecycle is STARTED again.
+            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                // Safely collect from windowInfoRepo when the lifecycle is STARTED
+                // and stops collection when the lifecycle is STOPPED
+                windowAreaController.windowAreaInfos.collect { windowAreaInfos ->
+                    infoLogAdapter.appendAndNotify(
+                        getCurrentTimeString(),
+                        "number of areas: " + windowAreaInfos.size
+                    )
+                    windowAreaInfos.forEach { windowAreaInfo ->
+                        if (windowAreaInfo.type == TYPE_REAR_FACING) {
+                            currentWindowAreaInfo = windowAreaInfo
+                            val presentCapability =
+                                windowAreaInfo.getCapability(OPERATION_PRESENT_ON_AREA)
+                            infoLogAdapter.append(
+                                getCurrentTimeString(),
+                                presentCapability.status.toString() +
+                                    " : " +
+                                    windowAreaInfo.metrics.toString()
+                            )
+                            updateRearDisplayPresentationButton()
+                        }
+                    }
+                    infoLogAdapter.notifyDataSetChanged()
+                }
+            }
+        }
+
+        binding.rearDisplayPresentationButton.setOnClickListener {
+            if (activePresentation != null) {
+                activePresentation?.close()
+            } else {
+                currentWindowAreaInfo?.let {
+                    windowAreaController.presentContentOnWindowArea(
+                        it.token,
+                        this@RearDisplayPresentationActivity,
+                        { obj: Runnable -> obj.run() },
+                        this@RearDisplayPresentationActivity
+                    )
+                }
+            }
+        }
+    }
+
+    override fun onSessionStarted(session: WindowAreaSessionPresenter) {
+        infoLogAdapter.appendAndNotify(
+            getCurrentTimeString(),
+            "Presentation session has been started"
+        )
+
+        activePresentation = session
+        val concurrentContext: Context = session.context
+        val contentView =
+            LayoutInflater.from(concurrentContext).inflate(R.layout.concurrent_presentation, null)
+        session.setContentView(contentView)
+        activePresentation = session
+        updateRearDisplayPresentationButton()
+    }
+
+    override fun onContainerVisibilityChanged(isVisible: Boolean) {
+        infoLogAdapter.appendAndNotify(
+            getCurrentTimeString(),
+            "Presentation content is visible: $isVisible"
+        )
+    }
+
+    override fun onSessionEnded(t: Throwable?) {
+        infoLogAdapter.appendAndNotify(
+            getCurrentTimeString(),
+            "Presentation session has been ended"
+        )
+        activePresentation = null
+    }
+
+    private fun updateRearDisplayPresentationButton() {
+        if (activePresentation != null) {
+            binding.rearDisplayPresentationButton.isEnabled = true
+            binding.rearDisplayPresentationButton.text = "Disable rear display presentation"
+            return
+        }
+        when (currentWindowAreaInfo?.getCapability(OPERATION_PRESENT_ON_AREA)?.status) {
+            WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> {
+                binding.rearDisplayPresentationButton.isEnabled = false
+                binding.rearDisplayPresentationButton.text =
+                    "Rear display presentation mode is not supported on this device"
+            }
+            WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> {
+                binding.rearDisplayPresentationButton.isEnabled = false
+                binding.rearDisplayPresentationButton.text =
+                    "Rear display presentation is not currently available"
+            }
+            WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> {
+                binding.rearDisplayPresentationButton.isEnabled = true
+                binding.rearDisplayPresentationButton.text = "Enable rear display presentation mode"
+            }
+        }
+    }
+
+    private fun getCurrentTimeString(): String {
+        val sdf = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())
+        val currentDate = sdf.format(Date())
+        return currentDate.toString()
+    }
+}
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateCallbackActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateCallbackActivity.kt
index 97d7ee7..5e71bee 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateCallbackActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateCallbackActivity.kt
@@ -25,17 +25,23 @@
 import android.os.Handler
 import android.os.Looper
 import android.view.Display.DEFAULT_DISPLAY
-import androidx.appcompat.app.AppCompatActivity
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
-import androidx.window.demo.databinding.ActivityCoresdkWindowStateCallbackLayoutBinding
+import androidx.window.demo.R
+import androidx.window.demo.common.DemoTheme
 import androidx.window.layout.WindowInfoTracker
+import androidx.window.layout.WindowMetricsCalculator
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 
 /** Activity to show display configuration from different system callbacks. */
-class WindowStateCallbackActivity : AppCompatActivity() {
+class WindowStateCallbackActivity : ComponentActivity() {
+    private val viewModel: WindowStateViewModel by viewModels()
 
     /**
      * [DisplayManager]s from `Activity` and `Application` are updated from different resource
@@ -43,15 +49,9 @@
      */
     private lateinit var applicationDisplayManager: DisplayManager
     private lateinit var activityDisplayManager: DisplayManager
+    private lateinit var windowMetricsCalculator: WindowMetricsCalculator
     private lateinit var handler: Handler
 
-    private lateinit var latestUpdateView: WindowStateView
-    private lateinit var applicationDisplayListenerView: WindowStateView
-    private lateinit var activityDisplayListenerView: WindowStateView
-    private lateinit var applicationConfigurationView: WindowStateView
-    private lateinit var activityConfigurationView: WindowStateView
-    private lateinit var displayFeatureView: WindowStateView
-
     /**
      * Runnable to poll configuration every 500ms. To always provide an up-to-date configuration so
      * it can be used to verify the configurations from other callbacks.
@@ -59,7 +59,7 @@
     private val updateWindowStateIfChanged =
         object : Runnable {
             override fun run() {
-                latestUpdateView.onWindowStateCallbackInvoked()
+                provideLatestWindowState()
                 handler.postDelayed(this, 500)
             }
         }
@@ -72,9 +72,10 @@
             override fun onDisplayRemoved(displayId: Int) {}
 
             override fun onDisplayChanged(displayId: Int) {
-                if (displayId == DEFAULT_DISPLAY) {
-                    applicationDisplayListenerView.onWindowStateCallbackInvoked()
+                if (displayId != DEFAULT_DISPLAY) {
+                    return
                 }
+                onWindowStateCallbackInvoked(R.string.application_display_listener_title, displayId)
             }
         }
 
@@ -86,36 +87,38 @@
             override fun onDisplayRemoved(displayId: Int) {}
 
             override fun onDisplayChanged(displayId: Int) {
-                if (displayId == DEFAULT_DISPLAY) {
-                    activityDisplayListenerView.onWindowStateCallbackInvoked()
+                if (displayId != DEFAULT_DISPLAY) {
+                    return
                 }
+                onWindowStateCallbackInvoked(R.string.activity_display_listener_title, displayId)
             }
         }
 
     /** [onConfigurationChanged] on `Application`. */
     private val applicationComponentCallback =
         object : ComponentCallbacks {
-            override fun onConfigurationChanged(p0: Configuration) {
-                applicationConfigurationView.onWindowStateCallbackInvoked()
+            override fun onConfigurationChanged(configuration: Configuration) {
+                onWindowStateCallbackInvoked(
+                    R.string.application_configuration_title,
+                    configuration
+                )
             }
 
+            @Deprecated(
+                "Since API level 34 this is never called. Apps targeting API level 34 " +
+                    "and above may provide an empty implementation."
+            )
             override fun onLowMemory() {}
         }
 
     override fun onCreate(savedInstanceState: Bundle?) {
+        enableEdgeToEdge()
         super.onCreate(savedInstanceState)
-        val viewBinding = ActivityCoresdkWindowStateCallbackLayoutBinding.inflate(layoutInflater)
-        setContentView(viewBinding.root)
-        latestUpdateView = viewBinding.latestUpdateView
-        applicationDisplayListenerView = viewBinding.applicationDisplayListenerView
-        activityDisplayListenerView = viewBinding.activityDisplayListenerView
-        applicationConfigurationView = viewBinding.applicationConfigurationView
-        activityConfigurationView = viewBinding.activityConfigurationView
-        displayFeatureView = viewBinding.displayFeatureView
+        setContent { DemoTheme { WindowStateScreen() } }
 
-        applicationDisplayManager =
-            application.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
-        activityDisplayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+        applicationDisplayManager = application.getSystemService(DisplayManager::class.java)
+        activityDisplayManager = getSystemService(DisplayManager::class.java)
+        windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
         handler = Handler(Looper.getMainLooper())
 
         applicationDisplayManager.registerDisplayListener(applicationDisplayListener, handler)
@@ -126,7 +129,9 @@
             lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 WindowInfoTracker.getOrCreate(this@WindowStateCallbackActivity)
                     .windowLayoutInfo(this@WindowStateCallbackActivity)
-                    .collect { _ -> displayFeatureView.onWindowStateCallbackInvoked() }
+                    .collect { info ->
+                        onWindowStateCallbackInvoked(R.string.display_feature_title, info)
+                    }
             }
         }
     }
@@ -152,10 +157,35 @@
     /** [onConfigurationChanged] on `Activity`. */
     override fun onConfigurationChanged(newConfig: Configuration) {
         super.onConfigurationChanged(newConfig)
-        activityConfigurationView.onWindowStateCallbackInvoked()
+        onWindowStateCallbackInvoked(R.string.activity_configuration_title, newConfig)
     }
 
-    companion object {
-        val TAG = WindowStateCallbackActivity::class.simpleName
+    /** Called when the corresponding system callback is invoked. */
+    private fun onWindowStateCallbackInvoked(resId: Int, details: Any?) {
+        viewModel.onWindowStateCallback(queryWindowState(resId, details = "$details"))
+    }
+
+    private fun provideLatestWindowState() {
+        viewModel.updateLatestWindowState(
+            queryWindowState(
+                R.string.latest_configuration_title,
+                "poll configuration every 500ms",
+            )
+        )
+    }
+
+    private fun queryWindowState(resId: Int, details: String): WindowState {
+        fun DisplayManager.defaultDisplayRotation() = getDisplay(DEFAULT_DISPLAY).rotation
+        fun Context.displayBounds() =
+            windowMetricsCalculator.computeMaximumWindowMetrics(this).bounds
+
+        return WindowState(
+            name = getString(resId),
+            applicationDisplayRotation = applicationDisplayManager.defaultDisplayRotation(),
+            activityDisplayRotation = activityDisplayManager.defaultDisplayRotation(),
+            applicationDisplayBounds = applicationContext.displayBounds(),
+            activityDisplayBounds = [email protected](),
+            callbackDetails = details,
+        )
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateConfigView.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateConfigView.kt
deleted file mode 100644
index 639491f..0000000
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateConfigView.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.demo.coresdk
-
-import android.content.Context
-import android.graphics.Color
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.widget.LinearLayout
-import android.widget.TextView
-import androidx.window.demo.R
-import androidx.window.demo.databinding.WindowStateConfigViewBinding
-
-/** View to show a display configuration value. */
-class WindowStateConfigView
-@JvmOverloads
-constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0,
-    defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
-
-    private val configView: TextView
-    private var configValue: String? = null
-
-    /** Whether to highlight the value when it is changed. */
-    var shouldHighlightChange = false
-
-    init {
-        val viewBinding =
-            WindowStateConfigViewBinding.inflate(LayoutInflater.from(context), this, true)
-        configView = viewBinding.configValue
-        context.theme.obtainStyledAttributes(attrs, R.styleable.WindowStateConfigView, 0, 0).apply {
-            try {
-                getString(R.styleable.WindowStateConfigView_configName)?.let {
-                    viewBinding.configName.text = it
-                }
-            } finally {
-                recycle()
-            }
-        }
-    }
-
-    /** Updates the config value. */
-    fun updateValue(value: String) {
-        if (shouldHighlightChange && configValue != null && configValue != value) {
-            // Highlight previous value if changed.
-            configView.setTextColor(Color.RED)
-        } else {
-            configView.setTextColor(Color.BLACK)
-        }
-        configValue = value
-        configView.text = value
-    }
-}
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
new file mode 100644
index 0000000..92e85eb
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.coresdk
+
+import android.graphics.Rect
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_180
+import android.view.Surface.ROTATION_270
+import android.view.Surface.ROTATION_90
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material.icons.filled.ExpandLess
+import androidx.compose.material.icons.filled.ExpandMore
+import androidx.compose.material3.Card
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.FabPosition
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.text.withStyle
+import androidx.compose.ui.tooling.preview.PreviewLightDark
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.window.demo.R
+import androidx.window.demo.common.DemoTheme
+import java.text.SimpleDateFormat
+import java.util.Date
+import kotlinx.coroutines.launch
+
+/** Composes the main screen for displaying window state information. */
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+fun WindowStateScreen(viewModel: WindowStateViewModel = viewModel()) {
+    val windowStates by viewModel.windowStates.collectAsState()
+    val listState = rememberLazyListState()
+    Scaffold(
+        topBar = {
+            TopAppBar(
+                title = { Text("Window State Callbacks") },
+                actions = {
+                    IconButton(onClick = viewModel::clearWindowStates) {
+                        Icon(Icons.Filled.Delete, "Clear list")
+                    }
+                },
+                colors =
+                    TopAppBarDefaults.topAppBarColors(
+                        containerColor = MaterialTheme.colorScheme.primary,
+                        titleContentColor = Color.White,
+                        actionIconContentColor = Color.White,
+                    ),
+            )
+        },
+        floatingActionButton = {
+            val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } }
+            if (showButton) {
+                val coroutineScope = rememberCoroutineScope()
+                FloatingActionButton(
+                    containerColor = MaterialTheme.colorScheme.primary,
+                    contentColor = Color.White,
+                    onClick = { coroutineScope.launch { listState.scrollToItem(0) } },
+                ) {
+                    Text("Back to latest!", modifier = Modifier.padding(8.dp))
+                }
+            }
+        },
+        floatingActionButtonPosition = FabPosition.Center,
+    ) { padding ->
+        WindowStateList(
+            windowStates,
+            padding,
+            listState = listState,
+            onWindowStateItemClick = viewModel::onWindowStateItemClick,
+        )
+    }
+}
+
+/** Previews composable for the [WindowStateScreen] in both light and dark modes. */
+@PreviewLightDark
+@Composable
+fun WindowStateScreenPreview() {
+    val windowStates =
+        listOf(
+            WindowState(
+                name = stringResource(R.string.application_configuration_title),
+                applicationDisplayRotation = ROTATION_270,
+                activityDisplayRotation = ROTATION_270,
+                applicationDisplayBounds = Rect(0, 0, 2208, 1840),
+                activityDisplayBounds = Rect(0, 0, 2208, 1840),
+            ),
+            WindowState(
+                name = stringResource(R.string.activity_display_listener_title),
+                applicationDisplayRotation = ROTATION_270,
+                activityDisplayRotation = ROTATION_270,
+                applicationDisplayBounds = Rect(0, 0, 2208, 1840),
+                activityDisplayBounds = Rect(0, 0, 2208, 1840),
+            ),
+            WindowState(
+                name = stringResource(R.string.display_feature_title),
+                applicationDisplayBounds = Rect(0, 0, 960, 2142),
+                activityDisplayBounds = Rect(0, 0, 960, 2142),
+            ),
+            WindowState(
+                name = stringResource(R.string.latest_configuration_title),
+                applicationDisplayBounds = Rect(0, 0, 960, 2142),
+                activityDisplayBounds = Rect(0, 0, 960, 2142),
+            ),
+        )
+    DemoTheme { WindowStateScreen(viewModel = WindowStateViewModel(windowStates)) }
+}
+
+/**
+ * Composes a scrollable list of [WindowStateCard] items.
+ *
+ * @param windowStates list of [WindowState] objects to display.
+ * @param contentPadding padding to apply to the lazy list.
+ * @param listState state object for the lazy list.
+ * @param onWindowStateItemClick callback when a [WindowState] item is clicked.
+ */
+@Composable
+fun WindowStateList(
+    windowStates: List<WindowState>,
+    contentPadding: PaddingValues = PaddingValues(0.dp),
+    listState: LazyListState = rememberLazyListState(),
+    onWindowStateItemClick: (Int) -> Unit,
+) {
+    LazyColumn(
+        contentPadding = contentPadding,
+        state = listState,
+    ) {
+        itemsIndexed(windowStates) { index, state ->
+            WindowStateCard(
+                number = windowStates.size - index,
+                state = state,
+                lastState = windowStates.getOrNull(index + 1) ?: state,
+                toggleExpand = { onWindowStateItemClick(index) },
+                modifier = Modifier.fillMaxWidth(),
+            )
+        }
+    }
+}
+
+/**
+ * Composes a card displaying information about a [WindowState].
+ *
+ * @param number the number to display for this [WindowState].
+ * @param state the [WindowState] to display.
+ * @param lastState the previous [WindowState] for comparison.
+ * @param toggleExpand callback to toggle the expanded state of the card.
+ * @param modifier modifier for this card.
+ */
+@Composable
+private fun WindowStateCard(
+    number: Int,
+    state: WindowState,
+    lastState: WindowState,
+    toggleExpand: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    val isExpanded = state.isDetailsExpanded
+    Card(modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {
+        Row(
+            modifier =
+                Modifier.padding(horizontal = 8.dp)
+                    .animateContentSize(
+                        animationSpec =
+                            spring(
+                                dampingRatio = Spring.DampingRatioMediumBouncy,
+                                stiffness = Spring.StiffnessLow
+                            )
+                    ),
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            Row(modifier = Modifier.weight(0.85f), verticalAlignment = Alignment.CenterVertically) {
+                Text("#$number")
+                Spacer(modifier = Modifier.width(6.dp))
+                Column {
+                    if (!isExpanded) {
+                        WindowStateSummary(state, lastState)
+                    } else {
+                        WindowStateDetail(state, lastState)
+                    }
+                }
+            }
+            IconButton(onClick = toggleExpand, modifier = Modifier.weight(0.07f)) {
+                Icon(
+                    if (isExpanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
+                    contentDescription = if (isExpanded) "Show less" else "Show more",
+                )
+            }
+        }
+    }
+}
+
+/**
+ * Composes a summary view for a collapsed [WindowStateCard].
+ *
+ * @param state the [WindowState] to display.
+ * @param lastState the previous [WindowState] for comparison.
+ */
+@Composable
+private fun WindowStateSummary(state: WindowState, lastState: WindowState) {
+    Row(verticalAlignment = Alignment.CenterVertically) {
+        Text(
+            state.name,
+            style = MaterialTheme.typography.bodyMedium,
+            fontWeight = FontWeight.Bold,
+        )
+        Spacer(modifier = Modifier.width(4.dp))
+        Text(
+            state.timeStamp.toSimpleTimeStr(),
+            style = MaterialTheme.typography.bodySmall,
+            overflow = TextOverflow.Ellipsis,
+            maxLines = 1,
+        )
+    }
+    Text(
+        buildAnnotatedString {
+            rotationString(
+                state.applicationDisplayRotation,
+                highlight =
+                    state.applicationDisplayRotation != lastState.applicationDisplayRotation,
+            )
+            append(" / ")
+            rotationString(
+                state.activityDisplayRotation,
+                highlight = state.activityDisplayRotation != lastState.activityDisplayRotation,
+            )
+            append(" / ")
+            boundsString(
+                state.applicationDisplayBounds,
+                highlight = state.applicationDisplayBounds != lastState.applicationDisplayBounds,
+            )
+            append(" / ")
+            boundsString(
+                state.activityDisplayBounds,
+                highlight = state.activityDisplayBounds != lastState.activityDisplayBounds,
+            )
+        },
+        style = MaterialTheme.typography.bodySmall,
+        overflow = TextOverflow.Ellipsis,
+        maxLines = 1,
+    )
+}
+
+/**
+ * Composes a detailed view for an expanded [WindowStateCard].
+ *
+ * @param state the [WindowState] to display.
+ * @param lastState the previous [WindowState] for comparison.
+ */
+@Composable
+private fun WindowStateDetail(state: WindowState, lastState: WindowState) {
+    val timestampTitle = stringResource(R.string.timestamp_title)
+    val applicationRotationTitle = stringResource(R.string.application_display_rotation_title)
+    val activityRotationTitle = stringResource(R.string.activity_display_rotation_title)
+    val applicationBoundsTittle = stringResource(R.string.application_display_bounds_title)
+    val activityBoundsTittle = stringResource(R.string.activity_display_bounds_title)
+
+    Text(
+        state.name,
+        style = MaterialTheme.typography.bodyLarge,
+        fontWeight = FontWeight.Bold,
+    )
+    Column {
+        Text(
+            "$timestampTitle ${state.timeStamp}",
+            style = MaterialTheme.typography.bodySmall,
+        )
+        DisplayRotationView(
+            applicationRotationTitle,
+            currentRotation = state.applicationDisplayRotation,
+            lastRotation = lastState.applicationDisplayRotation,
+        )
+        DisplayRotationView(
+            activityRotationTitle,
+            currentRotation = state.activityDisplayRotation,
+            lastRotation = lastState.activityDisplayRotation,
+        )
+        DisplayBoundsView(
+            applicationBoundsTittle,
+            currentBounds = state.applicationDisplayBounds,
+            lastBound = lastState.applicationDisplayBounds,
+        )
+        DisplayBoundsView(
+            activityBoundsTittle,
+            currentBounds = state.activityDisplayBounds,
+            lastBound = lastState.activityDisplayBounds,
+        )
+        Text(
+            "Callback details: ${state.callbackDetails}",
+            style = MaterialTheme.typography.bodySmall,
+        )
+    }
+}
+
+/**
+ * Composes a view for displaying rotation information in [WindowStateDetail].
+ *
+ * @param title the title for this rotation information.
+ * @param currentRotation the current rotation value to display.
+ * @param lastRotation the previous rotation value for comparison.
+ */
+@Composable
+private fun DisplayRotationView(title: String, currentRotation: Int, lastRotation: Int) {
+    val shouldHighlightChange = currentRotation != lastRotation
+    Row {
+        Text(title, style = MaterialTheme.typography.bodySmall)
+        Text(
+            buildAnnotatedString { rotationString(currentRotation, shouldHighlightChange) },
+            style = MaterialTheme.typography.bodySmall,
+        )
+        if (shouldHighlightChange) {
+            Text(
+                text = lastRotation.toRotationStr(),
+                style =
+                    MaterialTheme.typography.bodySmall.copy(
+                        textDecoration = TextDecoration.LineThrough,
+                    ),
+                modifier = Modifier.padding(start = 2.dp),
+            )
+        }
+    }
+}
+
+/** Formats a rotation value into an annotated string. */
+private fun AnnotatedString.Builder.rotationString(rotation: Int, highlight: Boolean) {
+    val color = if (highlight) Color.Red else Color.Unspecified
+    withStyle(style = SpanStyle(color = color)) { append(rotation.toRotationStr()) }
+}
+
+/**
+ * Composes a view for displaying bounds information in [WindowStateDetail].
+ *
+ * @param title the title for this bounds information.
+ * @param currentBounds the current bounds value to display.
+ * @param lastBound the previous bounds value for comparison.
+ */
+@Composable
+private fun DisplayBoundsView(title: String, currentBounds: Rect, lastBound: Rect) {
+    val shouldHighlightChange = currentBounds != lastBound
+    Row {
+        Text(title, style = MaterialTheme.typography.bodySmall)
+        Column {
+            Text(
+                buildAnnotatedString { boundsString(currentBounds, shouldHighlightChange) },
+                style = MaterialTheme.typography.bodySmall,
+            )
+            if (shouldHighlightChange) {
+                Text(
+                    text = "$lastBound",
+                    style =
+                        MaterialTheme.typography.bodySmall.copy(
+                            textDecoration = TextDecoration.LineThrough,
+                        ),
+                )
+            }
+        }
+    }
+}
+
+/** Formats a bounds value into an annotated string. */
+private fun AnnotatedString.Builder.boundsString(bound: Rect, highlight: Boolean) {
+    val color = if (highlight) Color.Red else Color.Unspecified
+    withStyle(style = SpanStyle(color = color)) { append("$bound") }
+}
+
+/** Converts an integer rotation value to a human-readable string representation. */
+private fun Int.toRotationStr(): String =
+    when (this) {
+        ROTATION_0 -> "0°"
+        ROTATION_90 -> "90°"
+        ROTATION_180 -> "180°"
+        ROTATION_270 -> "270°"
+        else -> "Unknown"
+    }
+
+/** Converts a [Date] to a simple time string for summary view. */
+private fun Date.toSimpleTimeStr(): String = SimpleDateFormat("(HH:mm:ss)").format(this)
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateView.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateView.kt
deleted file mode 100644
index a128b484..0000000
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateView.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.demo.coresdk
-
-import android.content.Context
-import android.graphics.Rect
-import android.hardware.display.DisplayManager
-import android.util.AttributeSet
-import android.util.Log
-import android.view.Display.DEFAULT_DISPLAY
-import android.view.LayoutInflater
-import android.view.WindowManager
-import android.widget.LinearLayout
-import androidx.window.demo.R
-import androidx.window.demo.databinding.WindowStateViewBinding
-import androidx.window.layout.WindowMetricsCalculator
-import java.text.SimpleDateFormat
-import java.util.Date
-
-/** View to show the display configuration from the latest update. */
-class WindowStateView
-@JvmOverloads
-constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0,
-    defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
-
-    private var title: String = "N/A"
-
-    /**
-     * [DisplayManager]s and [WindowManager]s from `Activity` and `Application` are updated from
-     * different resource configurations, so we show config from them separately.
-     */
-    private val applicationDisplayManager: DisplayManager
-    private val activityDisplayManager: DisplayManager
-    private val windowMetricsCalculator: WindowMetricsCalculator
-
-    private val timestampView: WindowStateConfigView
-    private val applicationDisplayRotationView: WindowStateConfigView
-    private val activityDisplayRotationView: WindowStateConfigView
-    private val applicationDisplayBoundsView: WindowStateConfigView
-    private val activityDisplayBoundsView: WindowStateConfigView
-    private val prevApplicationDisplayRotationView: WindowStateConfigView
-    private val prevActivityDisplayRotationView: WindowStateConfigView
-    private val prevApplicationDisplayBoundsView: WindowStateConfigView
-    private val prevActivityDisplayBoundsView: WindowStateConfigView
-
-    private val shouldHidePrevConfig: Boolean
-    private var lastApplicationDisplayRotation = -1
-    private var lastActivityDisplayRotation = -1
-    private val lastApplicationDisplayBounds = Rect()
-    private val lastActivityDisplayBounds = Rect()
-
-    init {
-        applicationDisplayManager =
-            context.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
-        activityDisplayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
-        windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
-
-        val viewBinding = WindowStateViewBinding.inflate(LayoutInflater.from(context), this, true)
-        timestampView = viewBinding.timestampView
-        applicationDisplayRotationView = viewBinding.applicationDisplayRotationView
-        activityDisplayRotationView = viewBinding.activityDisplayRotationView
-        applicationDisplayBoundsView = viewBinding.applicationDisplayBoundsView
-        activityDisplayBoundsView = viewBinding.activityDisplayBoundsView
-        prevApplicationDisplayRotationView = viewBinding.prevApplicationDisplayRotationView
-        prevActivityDisplayRotationView = viewBinding.prevActivityDisplayRotationView
-        prevApplicationDisplayBoundsView = viewBinding.prevApplicationDisplayBoundsView
-        prevActivityDisplayBoundsView = viewBinding.prevActivityDisplayBoundsView
-
-        context.theme.obtainStyledAttributes(attrs, R.styleable.WindowStateView, 0, 0).apply {
-            try {
-                getString(R.styleable.WindowStateView_title)?.let {
-                    viewBinding.callbackTitle.text = it
-                    title = it
-                }
-                shouldHidePrevConfig = getBoolean(R.styleable.WindowStateView_hidePrevConfig, false)
-                if (shouldHidePrevConfig) {
-                    timestampView.visibility = GONE
-                    applicationDisplayRotationView.shouldHighlightChange = false
-                    activityDisplayRotationView.shouldHighlightChange = false
-                    applicationDisplayBoundsView.shouldHighlightChange = false
-                    activityDisplayBoundsView.shouldHighlightChange = false
-                    prevApplicationDisplayRotationView.visibility = GONE
-                    prevActivityDisplayRotationView.visibility = GONE
-                    prevApplicationDisplayBoundsView.visibility = GONE
-                    prevActivityDisplayBoundsView.visibility = GONE
-                } else {
-                    applicationDisplayRotationView.shouldHighlightChange = true
-                    activityDisplayRotationView.shouldHighlightChange = true
-                    applicationDisplayBoundsView.shouldHighlightChange = true
-                    activityDisplayBoundsView.shouldHighlightChange = true
-                }
-            } finally {
-                recycle()
-            }
-        }
-    }
-
-    /** Called when the corresponding system callback is invoked. */
-    fun onWindowStateCallbackInvoked() {
-        val applicationDisplayRotation =
-            applicationDisplayManager.getDisplay(DEFAULT_DISPLAY).rotation
-        val activityDisplayRotation = activityDisplayManager.getDisplay(DEFAULT_DISPLAY).rotation
-        val applicationDisplayBounds =
-            windowMetricsCalculator.computeMaximumWindowMetrics(context.applicationContext).bounds
-        val activityDisplayBounds =
-            windowMetricsCalculator.computeMaximumWindowMetrics(context).bounds
-
-        if (
-            shouldHidePrevConfig &&
-                applicationDisplayRotation == lastApplicationDisplayRotation &&
-                activityDisplayRotation == lastActivityDisplayRotation &&
-                applicationDisplayBounds == lastApplicationDisplayBounds &&
-                activityDisplayBounds == lastActivityDisplayBounds
-        ) {
-            // Skip if the state is unchanged.
-            return
-        }
-
-        if (!shouldHidePrevConfig) {
-            // Debug log for the change title.
-            Log.d(WindowStateCallbackActivity.TAG, title)
-        }
-
-        timestampView.updateValue(TIME_FORMAT.format(Date()))
-        applicationDisplayRotationView.updateValue(applicationDisplayRotation.toString())
-        activityDisplayRotationView.updateValue(activityDisplayRotation.toString())
-        applicationDisplayBoundsView.updateValue(applicationDisplayBounds.toString())
-        activityDisplayBoundsView.updateValue(activityDisplayBounds.toString())
-
-        if (!shouldHidePrevConfig && lastApplicationDisplayRotation != -1) {
-            // Skip if there is no previous value.
-            prevApplicationDisplayRotationView.updateValue(
-                lastApplicationDisplayRotation.toString()
-            )
-            prevActivityDisplayRotationView.updateValue(lastActivityDisplayRotation.toString())
-            prevApplicationDisplayBoundsView.updateValue(lastApplicationDisplayBounds.toString())
-            prevActivityDisplayBoundsView.updateValue(lastActivityDisplayBounds.toString())
-        }
-
-        lastApplicationDisplayRotation = applicationDisplayRotation
-        lastActivityDisplayRotation = activityDisplayRotation
-        lastApplicationDisplayBounds.set(applicationDisplayBounds)
-        lastActivityDisplayBounds.set(activityDisplayBounds)
-    }
-
-    companion object {
-        private val TIME_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
-    }
-}
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateViewModel.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateViewModel.kt
new file mode 100644
index 0000000..3ecab868
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateViewModel.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.coresdk
+
+import android.graphics.Rect
+import android.view.Surface.ROTATION_0
+import androidx.compose.runtime.Immutable
+import androidx.lifecycle.ViewModel
+import java.util.Date
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+/** Represents the display configuration, including rotations, bounds, and callback source. */
+@Immutable
+data class WindowState(
+    val name: String,
+    val timeStamp: Date = Date(),
+    val applicationDisplayRotation: Int = ROTATION_0,
+    val activityDisplayRotation: Int = ROTATION_0,
+    val applicationDisplayBounds: Rect = Rect(),
+    val activityDisplayBounds: Rect = Rect(),
+    val callbackDetails: String = "",
+    val isDetailsExpanded: Boolean = false,
+) {
+    /**
+     * Compares the core state properties of this WindowState with another object.
+     *
+     * @param other the object to compare with.
+     * @return `true` if the core state properties are the same, `false` otherwise.
+     */
+    fun isSameState(other: Any?): Boolean =
+        (other is WindowState) &&
+            applicationDisplayRotation == other.applicationDisplayRotation &&
+            activityDisplayRotation == other.activityDisplayRotation &&
+            applicationDisplayBounds == other.applicationDisplayBounds &&
+            activityDisplayBounds == other.activityDisplayBounds
+}
+
+/**
+ * Manages the state of window events.
+ *
+ * This ViewModel maintains a list of [WindowState] objects, representing different window
+ * configurations over time. It provides methods to add new states, update existing ones, clear the
+ * list, and handle user interactions with the UI.
+ *
+ * @param initStates an optional initial list of [WindowState] objects to populate the ViewModel
+ *   with upon creation. Defaults to an empty list.
+ * @property windowStates the current list of window states, this is exposed for the UI to observe.
+ */
+class WindowStateViewModel(initStates: List<WindowState> = emptyList()) : ViewModel() {
+    private val _states = MutableStateFlow(initStates)
+    val windowStates: StateFlow<List<WindowState>> = _states.asStateFlow()
+
+    /** Clears all window states from the list. */
+    fun clearWindowStates() = _states.update { emptyList() }
+
+    /** Adds a new window state to the beginning of the list. */
+    fun onWindowStateCallback(state: WindowState) =
+        _states.update { listOf(state) + it } // Prepend to the list.
+
+    /** Updates the latest window state if it's different from the current latest state. */
+    fun updateLatestWindowState(state: WindowState) =
+        _states.update { if (state.isSameState(it.firstOrNull())) it else listOf(state) + it }
+
+    /** Toggles the expanded state of a window state at the given index. */
+    fun onWindowStateItemClick(index: Int) =
+        _states.update { states -> states.updateAtIndex(index) { it.toggleExpand() } }
+}
+
+/**
+ * Toggles the expanded state of a WindowState.
+ *
+ * @return a new [WindowState] with the `isDetailsExpanded` property toggled.
+ */
+private fun WindowState.toggleExpand(): WindowState = copy(isDetailsExpanded = !isDetailsExpanded)
+
+/**
+ * Updates an item at a specific index in a list using the provided transform function.
+ *
+ * @param index the index of the item to update.
+ * @param transform the function to apply to the item at the specified index.
+ * @return a new list with the item at the specified index updated.
+ */
+private fun <T> List<T>.updateAtIndex(index: Int, transform: (T) -> T): List<T> =
+    mapIndexed { idx, value ->
+        if (idx == index) transform(value) else value
+    }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/demos/WindowDemosActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/demos/WindowDemosActivity.kt
index c915ee8..83ac0b2 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/demos/WindowDemosActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/demos/WindowDemosActivity.kt
@@ -17,7 +17,6 @@
 package androidx.window.demo.demos
 
 import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
 import androidx.recyclerview.widget.RecyclerView
 import androidx.window.demo.DisplayFeaturesLetterboxLandscapeSlimActivity
 import androidx.window.demo.DisplayFeaturesLetterboxPortraitSlimActivity
@@ -35,10 +34,12 @@
 import androidx.window.demo.R.string.show_all_display_features_portrait_slim
 import androidx.window.demo.SplitLayoutActivity
 import androidx.window.demo.WindowMetricsActivity
+import androidx.window.demo.area.RearDisplayPresentationActivity
 import androidx.window.demo.common.DisplayFeaturesActivity
+import androidx.window.demo.common.EdgeToEdgeActivity
 
 /** Main activity that launches WindowManager demos. */
-class WindowDemosActivity : AppCompatActivity() {
+class WindowDemosActivity : EdgeToEdgeActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -84,6 +85,11 @@
                     buttonTitle = getString(R.string.ime),
                     description = getString(R.string.ime_demo_description),
                     clazz = ImeActivity::class.java
+                ),
+                DemoItem(
+                    buttonTitle = getString(R.string.dual_display),
+                    description = getString(R.string.dual_display_description),
+                    clazz = RearDisplayPresentationActivity::class.java
                 )
             )
         val recyclerView = findViewById<RecyclerView>(R.id.demo_recycler_view)
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/DemoActivityEmbeddingController.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/DemoActivityEmbeddingController.kt
index 7f1e0a7..2325804 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/DemoActivityEmbeddingController.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/DemoActivityEmbeddingController.kt
@@ -16,9 +16,14 @@
 
 package androidx.window.demo.embedding
 
+import android.graphics.Color
 import androidx.annotation.GuardedBy
+import androidx.window.demo.embedding.OverlayActivityBase.Companion.DEFAULT_OVERLAY_ATTRIBUTES
+import androidx.window.embedding.EmbeddingAnimationBackground
+import androidx.window.embedding.OverlayAttributes
 import androidx.window.embedding.SplitAttributes
 import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
 
@@ -55,6 +60,32 @@
 
     @GuardedBy("lock") private var splitTypeLocked = SplitAttributes.SplitType.SPLIT_TYPE_EQUAL
 
+    @GuardedBy("lock") private var animationBackgroundLocked = EmbeddingAnimationBackground.DEFAULT
+
+    internal var animationBackground: EmbeddingAnimationBackground
+        get() {
+            lock.withLock {
+                return animationBackgroundLocked
+            }
+        }
+        set(value) {
+            lock.withLock { animationBackgroundLocked = value }
+        }
+
+    internal var overlayAttributes: OverlayAttributes
+        get() {
+            lock.withLock {
+                return overlayAttributesLocked
+            }
+        }
+        set(value) {
+            lock.withLock { overlayAttributesLocked = value }
+        }
+
+    @GuardedBy("lock") private var overlayAttributesLocked = DEFAULT_OVERLAY_ATTRIBUTES
+
+    internal var overlayMode = AtomicInteger()
+
     companion object {
         @Volatile private var globalInstance: DemoActivityEmbeddingController? = null
         private val globalLock = ReentrantLock()
@@ -71,5 +102,15 @@
             }
             return globalInstance!!
         }
+
+        /** Animation background constants. */
+        val ANIMATION_BACKGROUND_TEXTS = arrayOf("DEFAULT", "BLUE", "GREEN", "YELLOW")
+        val ANIMATION_BACKGROUND_VALUES =
+            arrayOf(
+                EmbeddingAnimationBackground.DEFAULT,
+                EmbeddingAnimationBackground.createColorBackground(Color.BLUE),
+                EmbeddingAnimationBackground.createColorBackground(Color.GREEN),
+                EmbeddingAnimationBackground.createColorBackground(Color.YELLOW)
+            )
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/DialogActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/DialogActivity.kt
new file mode 100644
index 0000000..ef31132
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/DialogActivity.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.embedding
+
+import androidx.window.demo.common.EdgeToEdgeActivity
+
+/** Dialog style Activity. */
+open class DialogActivity : EdgeToEdgeActivity()
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt
index 0c2288e..7d8f632 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExampleWindowInitializer.kt
@@ -20,6 +20,10 @@
 import androidx.startup.Initializer
 import androidx.window.WindowSdkExtensions
 import androidx.window.demo.R
+import androidx.window.demo.embedding.OverlayActivityBase.Companion.OVERLAY_FEATURE_MINIMUM_REQUIRED_VERSION
+import androidx.window.demo.embedding.OverlayActivityBase.OverlayMode.Companion.OVERLAY_MODE_CHANGE_WITH_ORIENTATION
+import androidx.window.demo.embedding.OverlayActivityBase.OverlayMode.Companion.OVERLAY_MODE_CUSTOMIZATION
+import androidx.window.demo.embedding.OverlayActivityBase.OverlayMode.Companion.OVERLAY_MODE_SIMPLE
 import androidx.window.demo.embedding.SplitAttributesToggleMainActivity.Companion.PREFIX_FULLSCREEN_TOGGLE
 import androidx.window.demo.embedding.SplitAttributesToggleMainActivity.Companion.PREFIX_PLACEHOLDER
 import androidx.window.demo.embedding.SplitAttributesToggleMainActivity.Companion.TAG_CUSTOMIZED_SPLIT_ATTRIBUTES
@@ -31,6 +35,13 @@
 import androidx.window.demo.embedding.SplitDeviceStateActivityBase.Companion.TAG_SHOW_HORIZONTAL_LAYOUT_IN_TABLETOP
 import androidx.window.demo.embedding.SplitDeviceStateActivityBase.Companion.TAG_SHOW_LAYOUT_FOLLOWING_HINGE_WHEN_SEPARATING
 import androidx.window.demo.embedding.SplitDeviceStateActivityBase.Companion.TAG_USE_DEFAULT_SPLIT_ATTRIBUTES
+import androidx.window.embedding.ActivityEmbeddingController
+import androidx.window.embedding.EmbeddingBounds
+import androidx.window.embedding.EmbeddingConfiguration
+import androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior.Companion.ON_TASK
+import androidx.window.embedding.OverlayAttributes
+import androidx.window.embedding.OverlayAttributesCalculatorParams
+import androidx.window.embedding.OverlayController
 import androidx.window.embedding.RuleController
 import androidx.window.embedding.SplitAttributes
 import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
@@ -50,16 +61,31 @@
 /** Initializes SplitController with a set of statically defined rules. */
 class ExampleWindowInitializer : Initializer<RuleController> {
 
-    private val mDemoActivityEmbeddingController = DemoActivityEmbeddingController.getInstance()
+    private val demoActivityEmbeddingController = DemoActivityEmbeddingController.getInstance()
+
+    private lateinit var splitController: SplitController
+
+    private val extensionVersion = WindowSdkExtensions.getInstance().extensionVersion
 
     override fun create(context: Context): RuleController {
-        SplitController.getInstance(context).apply {
-            if (WindowSdkExtensions.getInstance().extensionVersion >= 2) {
-                setSplitAttributesCalculator(::sampleSplitAttributesCalculator)
+        splitController = SplitController.getInstance(context)
+
+        if (extensionVersion >= 2) {
+            splitController.setSplitAttributesCalculator(::sampleSplitAttributesCalculator)
+        }
+        if (extensionVersion >= OVERLAY_FEATURE_MINIMUM_REQUIRED_VERSION) {
+            OverlayController.getInstance(context)
+                .setOverlayAttributesCalculator(::sampleOverlayAttributesCalculator)
+        }
+        ActivityEmbeddingController.getInstance(context).apply {
+            if (WindowSdkExtensions.getInstance().extensionVersion >= 5) {
+                setEmbeddingConfiguration(
+                    EmbeddingConfiguration.Builder().setDimAreaBehavior(ON_TASK).build()
+                )
             }
         }
         return RuleController.getInstance(context).apply {
-            if (SplitController.getInstance(context).splitSupportStatus == SPLIT_AVAILABLE) {
+            if (splitController.splitSupportStatus == SPLIT_AVAILABLE) {
                 setRules(RuleController.parseRules(context, R.xml.main_split_config))
             }
         }
@@ -79,7 +105,7 @@
             SplitAttributes.Builder().setSplitType(SPLIT_TYPE_EXPAND).build()
         if (
             tag?.startsWith(PREFIX_FULLSCREEN_TOGGLE) == true &&
-                mDemoActivityEmbeddingController.shouldExpandSecondaryContainer.get()
+                demoActivityEmbeddingController.shouldExpandSecondaryContainer.get()
         ) {
             return expandContainersAttrs
         }
@@ -90,11 +116,18 @@
         val config = params.parentConfiguration
         val shouldReversed = tag?.contains(SUFFIX_REVERSED) ?: false
         // Make a copy of the default splitAttributes, but replace the animation background
-        // color to what is configured in the Demo app.
+        // to what is configured in the Demo app.
+        val animationBackground = demoActivityEmbeddingController.animationBackground
         val defaultSplitAttributes =
             SplitAttributes.Builder()
                 .setLayoutDirection(params.defaultSplitAttributes.layoutDirection)
                 .setSplitType(params.defaultSplitAttributes.splitType)
+                .setAnimationBackground(animationBackground)
+                .apply {
+                    if (extensionVersion >= 6) {
+                        setDividerAttributes(params.defaultSplitAttributes.dividerAttributes)
+                    }
+                }
                 .build()
         when (
             tag?.removePrefix(PREFIX_FULLSCREEN_TOGGLE)
@@ -125,6 +158,7 @@
                                 TOP_TO_BOTTOM
                             }
                         )
+                        .setAnimationBackground(animationBackground)
                         .build()
                 } else if (isPortrait) {
                     return expandContainersAttrs
@@ -141,6 +175,7 @@
                                 TOP_TO_BOTTOM
                             }
                         )
+                        .setAnimationBackground(animationBackground)
                         .build()
                 }
             }
@@ -155,6 +190,7 @@
                                 TOP_TO_BOTTOM
                             }
                         )
+                        .setAnimationBackground(animationBackground)
                         .build()
                 } else {
                     SplitAttributes.Builder()
@@ -166,6 +202,7 @@
                                 LEFT_TO_RIGHT
                             }
                         )
+                        .setAnimationBackground(animationBackground)
                         .build()
                 }
             }
@@ -182,6 +219,7 @@
                                 TOP_TO_BOTTOM
                             }
                         )
+                        .setAnimationBackground(animationBackground)
                         .build()
                 } else {
                     SplitAttributes.Builder()
@@ -193,6 +231,7 @@
                                 LEFT_TO_RIGHT
                             }
                         )
+                        .setAnimationBackground(animationBackground)
                         .build()
                 }
             }
@@ -214,19 +253,51 @@
                                 if (shouldReversed) RIGHT_TO_LEFT else LEFT_TO_RIGHT
                             }
                         )
+                        .setAnimationBackground(animationBackground)
                         .build()
                 }
             }
             TAG_CUSTOMIZED_SPLIT_ATTRIBUTES -> {
                 return SplitAttributes.Builder()
-                    .setSplitType(mDemoActivityEmbeddingController.customizedSplitType)
-                    .setLayoutDirection(mDemoActivityEmbeddingController.customizedLayoutDirection)
+                    .setSplitType(demoActivityEmbeddingController.customizedSplitType)
+                    .setLayoutDirection(demoActivityEmbeddingController.customizedLayoutDirection)
+                    .setAnimationBackground(animationBackground)
                     .build()
             }
         }
         return defaultSplitAttributes
     }
 
+    private fun sampleOverlayAttributesCalculator(
+        params: OverlayAttributesCalculatorParams
+    ): OverlayAttributes =
+        when (val mode = demoActivityEmbeddingController.overlayMode.get()) {
+            // Put the overlay to the right
+            OVERLAY_MODE_SIMPLE.value -> params.defaultOverlayAttributes
+            // Update the overlay with orientation:
+            // - Put the overlay to the bottom if the device is in portrait
+            // - Otherwise, put the overlay to the right if the device is in landscape
+            OVERLAY_MODE_CHANGE_WITH_ORIENTATION.value ->
+                OverlayAttributes(
+                    if (params.parentWindowMetrics.isPortrait()) {
+                        EmbeddingBounds(
+                            EmbeddingBounds.Alignment.ALIGN_BOTTOM,
+                            width = EmbeddingBounds.Dimension.DIMENSION_EXPANDED,
+                            height = EmbeddingBounds.Dimension.ratio(0.4f)
+                        )
+                    } else {
+                        EmbeddingBounds(
+                            EmbeddingBounds.Alignment.ALIGN_RIGHT,
+                            width = EmbeddingBounds.Dimension.ratio(0.5f),
+                            height = EmbeddingBounds.Dimension.ratio(0.8f)
+                        )
+                    }
+                )
+            // Fully customized overlay presentation
+            OVERLAY_MODE_CUSTOMIZATION.value -> demoActivityEmbeddingController.overlayAttributes
+            else -> throw IllegalStateException("Unknown mode $mode")
+        }
+
     private fun WindowMetrics.isPortrait(): Boolean = bounds.height() > bounds.width()
 
     private fun WindowLayoutInfo.isTabletop(): Boolean {
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExpandedDialogActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExpandedDialogActivity.kt
index 886afee..55405f6 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExpandedDialogActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/ExpandedDialogActivity.kt
@@ -18,10 +18,10 @@
 
 import android.os.Bundle
 import androidx.appcompat.app.AlertDialog
-import androidx.appcompat.app.AppCompatActivity
+import androidx.window.demo.common.EdgeToEdgeActivity
 
 /** Activity to show a dialog. */
-class ExpandedDialogActivity : AppCompatActivity() {
+class ExpandedDialogActivity : EdgeToEdgeActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayActivityBase.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayActivityBase.kt
new file mode 100644
index 0000000..2010317
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayActivityBase.kt
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.embedding
+
+import android.app.ActivityOptions
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+import android.graphics.Color
+import android.os.Bundle
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.RadioGroup
+import android.widget.SeekBar
+import android.widget.Spinner
+import android.widget.Toast
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.WindowSdkExtensions
+import androidx.window.demo.R
+import androidx.window.demo.common.EdgeToEdgeActivity
+import androidx.window.demo.databinding.ActivityOverlayActivityLayoutBinding
+import androidx.window.demo.embedding.OverlayActivityBase.OverlayMode.Companion.OVERLAY_MODE_CHANGE_WITH_ORIENTATION
+import androidx.window.demo.embedding.OverlayActivityBase.OverlayMode.Companion.OVERLAY_MODE_CUSTOMIZATION
+import androidx.window.demo.embedding.OverlayActivityBase.OverlayMode.Companion.OVERLAY_MODE_SIMPLE
+import androidx.window.embedding.ActivityEmbeddingController
+import androidx.window.embedding.ActivityStack
+import androidx.window.embedding.EmbeddingBounds
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_BOTTOM
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_LEFT
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_RIGHT
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_TOP
+import androidx.window.embedding.EmbeddingBounds.Dimension
+import androidx.window.embedding.OverlayAttributes
+import androidx.window.embedding.OverlayController
+import androidx.window.embedding.OverlayCreateParams
+import androidx.window.embedding.OverlayInfo
+import androidx.window.embedding.SplitController
+import androidx.window.embedding.SplitController.SplitSupportStatus.Companion.SPLIT_AVAILABLE
+import androidx.window.embedding.setLaunchingActivityStack
+import androidx.window.embedding.setOverlayCreateParams
+import kotlinx.coroutines.launch
+
+open class OverlayActivityBase :
+    EdgeToEdgeActivity(),
+    View.OnClickListener,
+    RadioGroup.OnCheckedChangeListener,
+    AdapterView.OnItemSelectedListener,
+    SeekBar.OnSeekBarChangeListener {
+
+    private val overlayTag = OverlayCreateParams.generateOverlayTag()
+
+    private lateinit var splitController: SplitController
+
+    private lateinit var overlayController: OverlayController
+
+    private val demoActivityEmbeddingController = DemoActivityEmbeddingController.getInstance()
+
+    private val extensionVersion = WindowSdkExtensions.getInstance().extensionVersion
+
+    lateinit var viewBinding: ActivityOverlayActivityLayoutBinding
+
+    private var overlayActivityStack: ActivityStack? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        viewBinding = ActivityOverlayActivityLayoutBinding.inflate(layoutInflater)
+        splitController = SplitController.getInstance(this)
+        overlayController = OverlayController.getInstance(this)
+
+        if (
+            splitController.splitSupportStatus != SPLIT_AVAILABLE ||
+                extensionVersion < OVERLAY_FEATURE_MINIMUM_REQUIRED_VERSION
+        ) {
+            Toast.makeText(this, R.string.toast_show_overlay_warning, Toast.LENGTH_SHORT).show()
+            finish()
+            return
+        }
+
+        viewBinding.root.setBackgroundColor(Color.parseColor("#fff3e0"))
+        setContentView(viewBinding.root)
+
+        viewBinding.buttonUpdateOverlayLayout.setOnClickListener(this)
+        viewBinding.buttonLaunchOverlayContainer.setOnClickListener(this)
+        viewBinding.buttonLaunchOverlayActivityA.setOnClickListener(this)
+        viewBinding.buttonLaunchOverlayActivityB.setOnClickListener(this)
+        viewBinding.buttonFinishThisActivity.setOnClickListener(this)
+
+        val radioGroupChooseOverlayLayout = viewBinding.radioGroupChooseOverlayLayout
+        radioGroupChooseOverlayLayout.setOnCheckedChangeListener(this)
+
+        viewBinding.spinnerAlignment.apply {
+            adapter =
+                ArrayAdapter(
+                    this@OverlayActivityBase,
+                    android.R.layout.simple_spinner_dropdown_item,
+                    POSITION_TEXT_ARRAY,
+                )
+            onItemSelectedListener = this@OverlayActivityBase
+        }
+
+        val dimensionAdapter =
+            ArrayAdapter(
+                this,
+                android.R.layout.simple_spinner_dropdown_item,
+                DIMENSION_TYPE_TEXT_ARRAY,
+            )
+        viewBinding.spinnerWidth.apply {
+            adapter = dimensionAdapter
+            onItemSelectedListener = this@OverlayActivityBase
+        }
+
+        viewBinding.spinnerHeight.apply {
+            adapter = dimensionAdapter
+            onItemSelectedListener = this@OverlayActivityBase
+        }
+
+        viewBinding.seekBarHeightInRatio.setOnSeekBarChangeListener(this)
+        viewBinding.seekBarWidthInRatio.setOnSeekBarChangeListener(this)
+
+        initializeUi()
+
+        lifecycleScope.launch {
+            // The block passed to repeatOnLifecycle is executed when the lifecycle
+            // is at least STARTED and is cancelled when the lifecycle is STOPPED.
+            // It automatically restarts the block when the lifecycle is STARTED again.
+            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                overlayController.overlayInfo(overlayTag).collect { overlayInfo ->
+                    overlayActivityStack = overlayInfo.activityStack
+                    val hasOverlay = overlayActivityStack != null
+                    viewBinding.buttonUpdateOverlayLayout.isEnabled =
+                        hasOverlay &&
+                            demoActivityEmbeddingController.overlayMode.get() !=
+                                OVERLAY_MODE_CHANGE_WITH_ORIENTATION.value
+                    updateOverlayBoundsText(overlayInfo)
+                }
+            }
+        }
+    }
+
+    private fun initializeUi() {
+        viewBinding.buttonUpdateOverlayLayout.isEnabled = false
+        viewBinding.radioGroupChooseOverlayLayout.check(R.id.radioButton_simple_overlay)
+        viewBinding.spinnerAlignment.setSelection(ALIGNMENT_VALUE_ARRAY.indexOf(ALIGN_RIGHT))
+        viewBinding.spinnerWidth.apply {
+            setSelection(INDEX_DIMENSION_RATIO)
+            updateDimensionUi(this)
+        }
+        viewBinding.spinnerHeight.apply {
+            setSelection(INDEX_DIMENSION_RATIO)
+            updateDimensionUi(this)
+        }
+    }
+
+    private fun initializeSeekbar(seekBar: SeekBar) {
+        seekBar.progress = 50
+        updateRatioText(seekBar)
+    }
+
+    private fun updateOverlayBoundsText(overlayInfo: OverlayInfo) {
+        viewBinding.textViewOverlayBounds.text =
+            resources.getString(R.string.overlay_bounds_text) +
+                overlayInfo.currentOverlayAttributes?.bounds.toString()
+    }
+
+    override fun onClick(button: View) {
+        val overlayAttributes = buildOverlayAttributesFromUi()
+        val isCustomizationMode =
+            demoActivityEmbeddingController.overlayMode.get() == OVERLAY_MODE_CUSTOMIZATION.value
+        when (button.id) {
+            R.id.button_launch_overlay_container -> {
+                if (isCustomizationMode) {
+                    // Also update controller's overlayAttributes because the launch bounds are
+                    // determined by calculator, which returns the overlayAttributes from
+                    // the controller directly.
+                    demoActivityEmbeddingController.overlayAttributes = overlayAttributes
+                }
+                try {
+                    startActivity(
+                        Intent().apply {
+                            setClassName(
+                                "androidx.window.demo2",
+                                "androidx.window.demo2.embedding.UntrustedEmbeddingActivity"
+                            )
+                        },
+                        ActivityOptions.makeBasic()
+                            .toBundle()
+                            .setOverlayCreateParams(
+                                this,
+                                OverlayCreateParams.Builder()
+                                    .setTag(overlayTag)
+                                    .setOverlayAttributes(
+                                        if (isCustomizationMode) {
+                                            overlayAttributes
+                                        } else {
+                                            DEFAULT_OVERLAY_ATTRIBUTES
+                                        }
+                                    )
+                                    .build()
+                            )
+                    )
+                } catch (e: ActivityNotFoundException) {
+                    Toast.makeText(this, R.string.install_samples_2, Toast.LENGTH_LONG).show()
+                }
+            }
+            R.id.button_launch_overlay_activity_a ->
+                startActivity(
+                    Intent(this, OverlayAssociatedActivityA::class.java).apply {
+                        if (viewBinding.checkboxReorderToFront.isChecked) {
+                            flags = FLAG_ACTIVITY_REORDER_TO_FRONT
+                        }
+                    }
+                )
+            R.id.button_launch_overlay_activity_b ->
+                startActivity(
+                    Intent(this, OverlayAssociatedActivityB::class.java),
+                    overlayActivityStack?.let {
+                        if (viewBinding.checkboxLaunchToOverlay.isChecked) {
+                            ActivityOptions.makeBasic()
+                                .toBundle()
+                                .setLaunchingActivityStack(
+                                    this,
+                                    it,
+                                )
+                        } else {
+                            null
+                        }
+                    }
+                )
+            R.id.button_finish_this_activity -> finish()
+            R.id.button_update_overlay_layout -> {
+                if (isCustomizationMode) {
+                    demoActivityEmbeddingController.overlayAttributes = overlayAttributes
+                    ActivityEmbeddingController.getInstance(this).invalidateVisibleActivityStacks()
+                } else {
+                    overlayController.updateOverlayAttributes(overlayTag, overlayAttributes)
+                }
+            }
+        }
+    }
+
+    private fun buildOverlayAttributesFromUi(): OverlayAttributes {
+        val spinnerPosition = viewBinding.spinnerAlignment
+        val spinnerWidth = viewBinding.spinnerWidth
+        val spinnerHeight = viewBinding.spinnerHeight
+
+        return OverlayAttributes.Builder()
+            .setBounds(
+                EmbeddingBounds(
+                    ALIGNMENT_VALUE_ARRAY[spinnerPosition.selectedItemPosition],
+                    createDimensionFromUi(spinnerWidth),
+                    createDimensionFromUi(spinnerHeight),
+                )
+            )
+            .build()
+    }
+
+    private fun createDimensionFromUi(spinner: Spinner): Dimension =
+        when (val position = spinner.selectedItemPosition) {
+            INDEX_DIMENSION_EXPAND -> Dimension.DIMENSION_EXPANDED
+            INDEX_DIMENSION_HINGE -> Dimension.DIMENSION_HINGE
+            INDEX_DIMENSION_RATIO ->
+                Dimension.ratio(
+                    if (spinner.isSpinnerWidth()) {
+                        viewBinding.seekBarWidthInRatio.progress.toFloat() / 100
+                    } else {
+                        viewBinding.seekBarHeightInRatio.progress.toFloat() / 100
+                    }
+                )
+            INDEX_DIMENSION_PIXEL ->
+                Dimension.pixel(
+                    if (spinner.isSpinnerWidth()) {
+                        viewBinding.editTextNumberDecimalWidthInPixel.text.toString().toInt()
+                    } else {
+                        viewBinding.editTextNumberDecimalHeightInPixel.text.toString().toInt()
+                    }
+                )
+            else -> throw IllegalStateException("Unknown spinner index: $position")
+        }
+
+    override fun onCheckedChanged(group: RadioGroup, id: Int) {
+        demoActivityEmbeddingController.overlayMode.set(
+            when (id) {
+                R.id.radioButton_simple_overlay -> OVERLAY_MODE_SIMPLE.value
+                R.id.radioButton_change_with_orientation ->
+                    OVERLAY_MODE_CHANGE_WITH_ORIENTATION.value
+                R.id.radioButton_customization -> OVERLAY_MODE_CUSTOMIZATION.value
+                else -> throw IllegalArgumentException("Unrecognized id $id")
+            }
+        )
+    }
+
+    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+        if (
+            parent is Spinner &&
+                parent in arrayOf(viewBinding.spinnerWidth, viewBinding.spinnerHeight)
+        ) {
+            updateDimensionUi(parent)
+        }
+    }
+
+    private fun updateDimensionUi(spinner: Spinner) {
+        val textViewRatio =
+            if (spinner.isSpinnerWidth()) {
+                viewBinding.textViewWidthInRatio
+            } else {
+                viewBinding.textViewHeightInRatio
+            }
+        val seekBarRatio =
+            if (spinner.isSpinnerWidth()) {
+                viewBinding.seekBarWidthInRatio
+            } else {
+                viewBinding.seekBarHeightInRatio
+            }
+        val textViewPixel =
+            if (spinner.isSpinnerWidth()) {
+                viewBinding.textViewWidthInPixel
+            } else {
+                viewBinding.textViewHeightInPixel
+            }
+        val editTextPixel =
+            if (spinner.isSpinnerWidth()) {
+                viewBinding.editTextNumberDecimalWidthInPixel
+            } else {
+                viewBinding.editTextNumberDecimalHeightInPixel
+            }
+        when (spinner.selectedItemPosition) {
+            INDEX_DIMENSION_EXPAND,
+            INDEX_DIMENSION_HINGE -> {
+                textViewRatio.visibility = View.GONE
+                seekBarRatio.visibility = View.GONE
+                textViewPixel.visibility = View.GONE
+                editTextPixel.visibility = View.GONE
+            }
+            INDEX_DIMENSION_RATIO -> {
+                textViewRatio.visibility = View.VISIBLE
+                seekBarRatio.visibility = View.VISIBLE
+                textViewPixel.visibility = View.GONE
+                editTextPixel.visibility = View.GONE
+                initializeSeekbar(seekBarRatio)
+            }
+            INDEX_DIMENSION_PIXEL -> {
+                textViewRatio.visibility = View.GONE
+                seekBarRatio.visibility = View.GONE
+                textViewPixel.visibility = View.VISIBLE
+                editTextPixel.visibility = View.VISIBLE
+                editTextPixel.text.clear()
+            }
+        }
+    }
+
+    private fun Spinner.isSpinnerWidth() = this == viewBinding.spinnerWidth
+
+    override fun onNothingSelected(view: AdapterView<*>?) {
+        // Auto-generated method stub
+    }
+
+    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+        updateRatioText(seekBar)
+    }
+
+    private fun updateRatioText(seekBar: SeekBar) {
+        if (seekBar.isSeekBarWidthInRatio()) {
+            viewBinding.textViewWidthInRatio.text =
+                resources.getString(R.string.width_in_ratio) +
+                    (seekBar.progress.toFloat() / 100).toString()
+        } else {
+            viewBinding.textViewHeightInRatio.text =
+                resources.getString(R.string.height_in_ratio) +
+                    (seekBar.progress.toFloat() / 100).toString()
+        }
+    }
+
+    private fun SeekBar.isSeekBarWidthInRatio(): Boolean = this == viewBinding.seekBarWidthInRatio
+
+    override fun onStartTrackingTouch(seekBar: SeekBar) {
+        // Auto-generated method stub
+    }
+
+    override fun onStopTrackingTouch(seekBar: SeekBar) {
+        // Auto-generated method stub
+    }
+
+    @JvmInline
+    internal value class OverlayMode(val value: Int) {
+        companion object {
+            val OVERLAY_MODE_SIMPLE = OverlayMode(0)
+            val OVERLAY_MODE_CHANGE_WITH_ORIENTATION = OverlayMode(1)
+            val OVERLAY_MODE_CUSTOMIZATION = OverlayMode(2)
+        }
+    }
+
+    companion object {
+        internal const val OVERLAY_FEATURE_MINIMUM_REQUIRED_VERSION = 8
+
+        internal val DEFAULT_OVERLAY_ATTRIBUTES =
+            OverlayAttributes(
+                EmbeddingBounds(
+                    ALIGN_RIGHT,
+                    Dimension.ratio(0.5f),
+                    Dimension.ratio(0.8f),
+                )
+            )
+
+        private val POSITION_TEXT_ARRAY = arrayOf("top", "left", "bottom", "right")
+        private val ALIGNMENT_VALUE_ARRAY =
+            arrayListOf(
+                ALIGN_TOP,
+                ALIGN_LEFT,
+                ALIGN_BOTTOM,
+                ALIGN_RIGHT,
+            )
+
+        private val DIMENSION_TYPE_TEXT_ARRAY =
+            arrayOf(
+                "expand to the task",
+                "follow the hinge",
+                "dimension in ratio",
+                "dimension in pixel",
+            )
+        private const val INDEX_DIMENSION_EXPAND = 0
+        private const val INDEX_DIMENSION_HINGE = 1
+        private const val INDEX_DIMENSION_RATIO = 2
+        private const val INDEX_DIMENSION_PIXEL = 3
+    }
+}
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayAssociatedActivityA.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayAssociatedActivityA.kt
new file mode 100644
index 0000000..0e35bb5
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayAssociatedActivityA.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.embedding
+
+class OverlayAssociatedActivityA : OverlayActivityBase()
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayAssociatedActivityB.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayAssociatedActivityB.kt
new file mode 100644
index 0000000..edc9b7e
--- /dev/null
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/OverlayAssociatedActivityB.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.demo.embedding
+
+import android.graphics.Color
+import android.os.Bundle
+
+class OverlayAssociatedActivityB : OverlayActivityBase() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        viewBinding.rootOverlayActivityLayout.setBackgroundColor(Color.parseColor("#e8f5e9"))
+    }
+}
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityB.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityB.kt
index aec91b9..4520b2c 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityB.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityB.kt
@@ -18,6 +18,7 @@
 
 import android.content.Intent
 import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.View
 import androidx.window.demo.R
@@ -25,8 +26,9 @@
 open class SplitActivityB : SplitActivityBase() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        findViewById<View>(R.id.root_split_activity_layout)
-            .setBackgroundColor(Color.parseColor("#fff3e0"))
+        val color = Color.parseColor("#fff3e0")
+        findViewById<View>(R.id.root_split_activity_layout).setBackgroundColor(color)
+        window.setBackgroundDrawable(ColorDrawable(color))
 
         if (intent.getBooleanExtra(EXTRA_LAUNCH_C_TO_SIDE, false)) {
             startActivity(Intent(this, SplitActivityC::class.java))
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java
index 7ff6884..aec588e 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityBase.java
@@ -18,12 +18,15 @@
 
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 
-import static androidx.window.embedding.SplitController.SplitSupportStatus.SPLIT_AVAILABLE;
+import static androidx.window.embedding.SplitController.SplitSupportStatus.SPLIT_ERROR_PROPERTY_NOT_DECLARED;
+import static androidx.window.embedding.SplitController.SplitSupportStatus.SPLIT_UNAVAILABLE;
 import static androidx.window.embedding.SplitRule.FinishBehavior.ADJACENT;
 import static androidx.window.embedding.SplitRule.FinishBehavior.ALWAYS;
 import static androidx.window.embedding.SplitRule.FinishBehavior.NEVER;
 
 import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -36,14 +39,19 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
 import androidx.core.util.Consumer;
 import androidx.window.WindowSdkExtensions;
 import androidx.window.demo.R;
+import androidx.window.demo.common.EdgeToEdgeActivity;
 import androidx.window.demo.databinding.ActivitySplitActivityLayoutBinding;
 import androidx.window.embedding.ActivityEmbeddingController;
+import androidx.window.embedding.ActivityEmbeddingOptions;
 import androidx.window.embedding.ActivityFilter;
 import androidx.window.embedding.ActivityRule;
+import androidx.window.embedding.DividerAttributes;
+import androidx.window.embedding.DividerAttributes.DraggableDividerAttributes;
+import androidx.window.embedding.DividerAttributes.FixedDividerAttributes;
+import androidx.window.embedding.EmbeddedActivityWindowInfo;
 import androidx.window.embedding.EmbeddingRule;
 import androidx.window.embedding.RuleController;
 import androidx.window.embedding.SplitAttributes;
@@ -51,7 +59,9 @@
 import androidx.window.embedding.SplitInfo;
 import androidx.window.embedding.SplitPairFilter;
 import androidx.window.embedding.SplitPairRule;
+import androidx.window.embedding.SplitPinRule;
 import androidx.window.embedding.SplitPlaceholderRule;
+import androidx.window.java.embedding.ActivityEmbeddingControllerCallbackAdapter;
 import androidx.window.java.embedding.SplitControllerCallbackAdapter;
 
 import java.util.HashSet;
@@ -62,7 +72,7 @@
  * Sample showcase of split activity rules. Allows the user to select some split configuration
  * options with checkboxes and launch activities with those options applied.
  */
-public class SplitActivityBase extends AppCompatActivity
+public class SplitActivityBase extends EdgeToEdgeActivity
         implements CompoundButton.OnCheckedChangeListener {
 
     private static final String TAG = "SplitActivityTest";
@@ -76,7 +86,11 @@
      */
     private SplitControllerCallbackAdapter mSplitControllerAdapter;
     private RuleController mRuleController;
-    private SplitInfoCallback mCallback;
+    private SplitInfoCallback mSplitInfoCallback;
+
+    private ActivityEmbeddingController mActivityEmbeddingController;
+    private ActivityEmbeddingControllerCallbackAdapter mActivityEmbeddingControllerCallbackAdapter;
+    private EmbeddedActivityWindowInfoCallback mEmbeddedActivityWindowInfoCallbackCallback;
 
     private ActivitySplitActivityLayoutBinding mViewBinding;
 
@@ -86,9 +100,28 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        final SplitController splitController = SplitController.getInstance(this);
+        final SplitController.SplitSupportStatus splitSupportStatus =
+                splitController.getSplitSupportStatus();
+        if (splitSupportStatus == SPLIT_UNAVAILABLE) {
+            Toast.makeText(this, R.string.toast_split_not_available,
+                    Toast.LENGTH_SHORT).show();
+            finish();
+            return;
+        } else if (splitSupportStatus == SPLIT_ERROR_PROPERTY_NOT_DECLARED) {
+            Toast.makeText(this, R.string.toast_split_not_support,
+                    Toast.LENGTH_SHORT).show();
+            finish();
+            return;
+        }
+
         mViewBinding = ActivitySplitActivityLayoutBinding.inflate(getLayoutInflater());
         setContentView(mViewBinding.getRoot());
 
+        final int extensionVersion = WindowSdkExtensions.getInstance().getExtensionVersion();
+        mActivityEmbeddingController = ActivityEmbeddingController.getInstance(this);
+
         // Setup activity launch buttons and config options.
         mViewBinding.launchB.setOnClickListener((View v) ->
                 startActivity(new Intent(this, SplitActivityB.class)));
@@ -99,10 +132,19 @@
         });
         mViewBinding.launchE.setOnClickListener((View v) -> {
             Bundle bundle = null;
+            if (mViewBinding.setLaunchingEInActivityStack.isChecked()) {
+                try {
+                    bundle = ActivityEmbeddingOptions.setLaunchingActivityStack(
+                            ActivityOptions.makeBasic().toBundle(), this,
+                            mActivityEmbeddingController.getActivityStack(this));
+                } catch (UnsupportedOperationException ex) {
+                    Log.w(TAG, "#setLaunchingActivityStack is not supported", ex);
+                }
+            }
             startActivity(new Intent(this, SplitActivityE.class), bundle);
         });
-        if (WindowSdkExtensions.getInstance().getExtensionVersion() < 3) {
-            mViewBinding.setLaunchingEInActivityStack.setEnabled(false);
+        if (extensionVersion < 3) {
+            mViewBinding.setLaunchingEInActivityStack.setVisibility(View.GONE);
         }
         mViewBinding.launchF.setOnClickListener((View v) ->
                 startActivity(new Intent(this, SplitActivityF.class)));
@@ -158,6 +200,35 @@
         });
         mViewBinding.launchExpandedDialogButton.setOnClickListener((View v) ->
                 startActivity(new Intent(this, ExpandedDialogActivity.class)));
+        mViewBinding.launchDialogActivityButton.setOnClickListener((View v) ->
+                startActivity(new Intent(this, DialogActivity.class)));
+        mViewBinding.launchDialogButton.setOnClickListener((View v) ->
+                new AlertDialog.Builder(this)
+                        .setTitle("Alert dialog demo")
+                        .setMessage("This is a dialog demo").create().show());
+
+        if (extensionVersion < 5) {
+            mViewBinding.pinTopActivityStackButton.setVisibility(View.GONE);
+            mViewBinding.unpinTopActivityStackButton.setVisibility(View.GONE);
+        } else {
+            mViewBinding.pinTopActivityStackButton.setOnClickListener((View v) -> {
+                        splitController.pinTopActivityStack(getTaskId(),
+                                new SplitPinRule.Builder().setSticky(
+                                        mViewBinding.stickyPinRule.isChecked()).build());
+                    }
+            );
+            mViewBinding.unpinTopActivityStackButton.setOnClickListener((View v) -> {
+                        splitController.unpinTopActivityStack(getTaskId());
+                    }
+            );
+        }
+        if (extensionVersion < 6) {
+            mViewBinding.dividerCheckBox.setVisibility(View.GONE);
+            mViewBinding.draggableDividerCheckBox.setVisibility(View.GONE);
+        } else {
+            mViewBinding.dividerCheckBox.setOnCheckedChangeListener(this);
+            mViewBinding.draggableDividerCheckBox.setOnCheckedChangeListener(this);
+        }
 
         // Listen for split configuration checkboxes to update the rules before launching
         // activities.
@@ -169,42 +240,84 @@
         mViewBinding.fullscreenECheckBox.setOnCheckedChangeListener(this);
         mViewBinding.splitWithFCheckBox.setOnCheckedChangeListener(this);
 
-        final SplitController splitController = SplitController.getInstance(this);
+        if (extensionVersion < 6) {
+            mViewBinding.buttonLaunchOverlayAssociatedActivity.setVisibility(View.GONE);
+        } else {
+            mViewBinding.buttonLaunchOverlayAssociatedActivity.setOnClickListener((View v) ->
+                    startActivity(new Intent(this, OverlayAssociatedActivityA.class)));
+        }
+
         mSplitControllerAdapter = new SplitControllerCallbackAdapter(splitController);
-        if (splitController.getSplitSupportStatus() != SPLIT_AVAILABLE) {
-            Toast.makeText(this, R.string.toast_split_not_support,
-                    Toast.LENGTH_SHORT).show();
-            finish();
-            return;
+        if (extensionVersion >= 6) {
+            mActivityEmbeddingControllerCallbackAdapter =
+                    new ActivityEmbeddingControllerCallbackAdapter(mActivityEmbeddingController);
+
+            // The EmbeddedActivityWindowInfoListener will only be triggered when the activity is
+            // embedded and visible (just like Activity#onConfigurationChanged).
+            // Register it in #onCreate instead of #onStart so that when the embedded status is
+            // changed to non-embedded before #onStart (like screen rotation when this activity is
+            // in background), the listener will be triggered right after #onStart.
+            // Otherwise, if registered in #onStart, it will not be triggered on registration
+            // because the activity is not embedded, which results it shows the stale info.
+            mEmbeddedActivityWindowInfoCallbackCallback = new EmbeddedActivityWindowInfoCallback();
+            mActivityEmbeddingControllerCallbackAdapter.addEmbeddedActivityWindowInfoListener(
+                    this, Runnable::run, mEmbeddedActivityWindowInfoCallbackCallback);
         }
         mRuleController = RuleController.getInstance(this);
     }
 
     @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mActivityEmbeddingControllerCallbackAdapter != null) {
+            mActivityEmbeddingControllerCallbackAdapter.removeEmbeddedActivityWindowInfoListener(
+                    mEmbeddedActivityWindowInfoCallbackCallback);
+            mEmbeddedActivityWindowInfoCallbackCallback = null;
+        }
+    }
+
+    @Override
     protected void onStart() {
         super.onStart();
-        mCallback = new SplitInfoCallback();
-        mSplitControllerAdapter.addSplitListener(this, Runnable::run, mCallback);
+        mSplitInfoCallback = new SplitInfoCallback();
+        mSplitControllerAdapter.addSplitListener(this, Runnable::run, mSplitInfoCallback);
     }
 
     @Override
     protected void onStop() {
         super.onStop();
-        mSplitControllerAdapter.removeSplitListener(mCallback);
-        mCallback = null;
+        mSplitControllerAdapter.removeSplitListener(mSplitInfoCallback);
+        mSplitInfoCallback = null;
     }
 
     /** Updates the embedding status when receives callback from the extension. */
-    class SplitInfoCallback implements Consumer<List<SplitInfo>> {
+    private class SplitInfoCallback implements Consumer<List<SplitInfo>> {
         @Override
         public void accept(List<SplitInfo> splitInfoList) {
             runOnUiThread(() -> {
-                updateEmbeddedStatus();
+                if (mActivityEmbeddingControllerCallbackAdapter == null) {
+                    // Otherwise, the embedded status will be updated from
+                    // EmbeddedActivityWindowInfoCallback.
+                    updateEmbeddedStatus(mActivityEmbeddingController.isActivityEmbedded(
+                            SplitActivityBase.this));
+                }
                 updateCheckboxesFromCurrentConfig();
             });
         }
     }
 
+    /** Updates the embedding status when receives callback from the extension. */
+    private class EmbeddedActivityWindowInfoCallback implements
+            Consumer<EmbeddedActivityWindowInfo> {
+        @Override
+        public void accept(EmbeddedActivityWindowInfo embeddedActivityWindowInfo) {
+            runOnUiThread(() -> {
+                updateEmbeddedStatus(embeddedActivityWindowInfo.isEmbedded());
+                updateEmbeddedWindowInfo(embeddedActivityWindowInfo);
+            });
+        }
+    }
+
     /** Called on checkbox changed. */
     @Override
     public void onCheckedChanged(@NonNull CompoundButton c, boolean isChecked) {
@@ -328,8 +441,19 @@
     /** Updates the split rules based on the current selection on checkboxes. */
     private void updateRulesFromCheckboxes() {
         mRuleController.clearRules();
+
+        final DividerAttributes dividerAttributes;
+        if (mViewBinding.dividerCheckBox.isChecked()) {
+            dividerAttributes = mViewBinding.draggableDividerCheckBox.isChecked()
+                    ? new DraggableDividerAttributes.Builder().setWidthDp(1).build()
+                    : new FixedDividerAttributes.Builder().setWidthDp(1).build();
+        } else {
+            dividerAttributes = DividerAttributes.NO_DIVIDER;
+        }
+
         final SplitAttributes defaultSplitAttributes = new SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(SPLIT_RATIO))
+                .setDividerAttributes(dividerAttributes)
                 .build();
 
         if (mViewBinding.splitMainCheckBox.isChecked()) {
@@ -349,6 +473,8 @@
             mRuleController.addRule(rule);
         }
 
+        mViewBinding.draggableDividerCheckBox.setEnabled(mViewBinding.dividerCheckBox.isChecked());
+
         if (mViewBinding.usePlaceholderCheckBox.isChecked()) {
             // Split B with placeholder.
             final Set<ActivityFilter> activityFilters = new HashSet<>();
@@ -436,11 +562,21 @@
     }
 
     /** Updates the status label that says when an activity is embedded. */
-    void updateEmbeddedStatus() {
-        if (ActivityEmbeddingController.getInstance(this).isActivityEmbedded(this)) {
-            mViewBinding.activityEmbeddedStatusTextView.setVisibility(View.VISIBLE);
-        } else {
-            mViewBinding.activityEmbeddedStatusTextView.setVisibility(View.GONE);
+    private void updateEmbeddedStatus(boolean isEmbedded) {
+        mViewBinding.activityEmbeddedStatusTextView.setVisibility(isEmbedded
+                ? View.VISIBLE
+                : View.GONE);
+    }
+
+    private void updateEmbeddedWindowInfo(
+            @NonNull EmbeddedActivityWindowInfo info) {
+        Log.d(TAG, "EmbeddedActivityWindowInfo changed for r=" + this + "\ninfo=" + info);
+        if (!info.isEmbedded()) {
+            mViewBinding.activityEmbeddedBoundsTextView.setVisibility(View.GONE);
+            return;
         }
+        mViewBinding.activityEmbeddedBoundsTextView.setVisibility(View.VISIBLE);
+        mViewBinding.activityEmbeddedBoundsTextView.setText(
+                "Embedded bounds=" + info.getBoundsInParentHost());
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityC.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityC.kt
index dcbbb3b..7b61240c 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityC.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityC.kt
@@ -17,6 +17,7 @@
 package androidx.window.demo.embedding
 
 import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.View
 import androidx.window.demo.R
@@ -24,7 +25,8 @@
 open class SplitActivityC : SplitActivityBase() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        findViewById<View>(R.id.root_split_activity_layout)
-            .setBackgroundColor(Color.parseColor("#e8f5e9"))
+        val color = Color.parseColor("#e8f5e9")
+        findViewById<View>(R.id.root_split_activity_layout).setBackgroundColor(color)
+        window.setBackgroundDrawable(ColorDrawable(color))
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityD.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityD.kt
index 705dfee..000ff2b 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityD.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityD.kt
@@ -17,6 +17,7 @@
 package androidx.window.demo.embedding
 
 import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.View
 import androidx.window.demo.R
@@ -24,7 +25,8 @@
 open class SplitActivityD : SplitActivityBase() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        findViewById<View>(R.id.root_split_activity_layout)
-            .setBackgroundColor(Color.parseColor("#eeeeee"))
+        val color = Color.parseColor("#eeeeee")
+        findViewById<View>(R.id.root_split_activity_layout).setBackgroundColor(color)
+        window.setBackgroundDrawable(ColorDrawable(color))
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityDetail.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityDetail.kt
index f4f6bff..7e79b83 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityDetail.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityDetail.kt
@@ -19,27 +19,34 @@
 import android.content.Intent
 import android.graphics.Color
 import android.os.Bundle
-import android.view.View
 import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.window.demo.R
+import androidx.window.demo.common.EdgeToEdgeActivity
+import androidx.window.demo.databinding.ActivitySplitActivityListDetailLayoutBinding
 
-open class SplitActivityDetail : AppCompatActivity() {
+open class SplitActivityDetail : EdgeToEdgeActivity() {
+
+    private lateinit var viewBinding: ActivitySplitActivityListDetailLayoutBinding
+    private lateinit var itemDetailTextView: TextView
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_split_activity_list_detail_layout)
-        findViewById<View>(R.id.root_split_activity_layout)
-            .setBackgroundColor(Color.parseColor("#fff3e0"))
 
-        findViewById<TextView>(R.id.item_detail_text)
-            .setText(intent.getStringExtra(EXTRA_SELECTED_ITEM))
+        viewBinding = ActivitySplitActivityListDetailLayoutBinding.inflate(layoutInflater)
+        viewBinding.rootSplitActivityLayout.setBackgroundColor(Color.parseColor("#fff3e0"))
+        setContentView(viewBinding.root)
+        itemDetailTextView = viewBinding.itemDetailText
+
+        itemDetailTextView.text = intent.getStringExtra(EXTRA_SELECTED_ITEM)
+
+        window.decorView.setOnFocusChangeListener { _, focus ->
+            itemDetailTextView.text = "${itemDetailTextView.text} focus=$focus"
+        }
     }
 
     override fun onNewIntent(intent: Intent) {
         super.onNewIntent(intent)
 
-        findViewById<TextView>(R.id.item_detail_text)
-            .setText(intent.getStringExtra(EXTRA_SELECTED_ITEM))
+        itemDetailTextView.text = intent.getStringExtra(EXTRA_SELECTED_ITEM)
     }
 
     companion object {
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityE.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityE.kt
index 2766a7c..a530d22 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityE.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityE.kt
@@ -17,6 +17,7 @@
 package androidx.window.demo.embedding
 
 import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.View
 import androidx.window.demo.R
@@ -24,7 +25,8 @@
 open class SplitActivityE : SplitActivityBase() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        findViewById<View>(R.id.root_split_activity_layout)
-            .setBackgroundColor(Color.parseColor("#ede7f6"))
+        val color = Color.parseColor("#ede7f6")
+        findViewById<View>(R.id.root_split_activity_layout).setBackgroundColor(color)
+        window.setBackgroundDrawable(ColorDrawable(color))
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityF.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityF.kt
index 5fbf9ce..d8161d0 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityF.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityF.kt
@@ -17,6 +17,7 @@
 package androidx.window.demo.embedding
 
 import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import android.view.View
 import androidx.window.demo.R
@@ -24,7 +25,8 @@
 open class SplitActivityF : SplitActivityBase() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        findViewById<View>(R.id.root_split_activity_layout)
-            .setBackgroundColor(Color.parseColor("#ffebee"))
+        val color = Color.parseColor("#ffebee")
+        findViewById<View>(R.id.root_split_activity_layout).setBackgroundColor(color)
+        window.setBackgroundDrawable(ColorDrawable(color))
     }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityList.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityList.kt
index b7920d8..d7e1cf4 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityList.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityList.kt
@@ -21,23 +21,25 @@
 import android.os.Bundle
 import android.view.View
 import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
-import androidx.window.demo.R
+import androidx.window.demo.common.EdgeToEdgeActivity
+import androidx.window.demo.databinding.ActivitySplitActivityListLayoutBinding
 import androidx.window.demo.embedding.SplitActivityDetail.Companion.EXTRA_SELECTED_ITEM
 import androidx.window.embedding.SplitController
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
-open class SplitActivityList : AppCompatActivity() {
+private lateinit var viewBinding: ActivitySplitActivityListLayoutBinding
+
+open class SplitActivityList : EdgeToEdgeActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_split_activity_list_layout)
-        findViewById<View>(R.id.root_split_activity_layout)
-            .setBackgroundColor(Color.parseColor("#e0f7fa"))
+        viewBinding = ActivitySplitActivityListLayoutBinding.inflate(layoutInflater)
+        setContentView(viewBinding.root)
+        viewBinding.root.setBackgroundColor(Color.parseColor("#e0f7fa"))
         val splitController = SplitController.getInstance(this)
 
         lifecycleScope.launch {
@@ -47,7 +49,7 @@
             lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 splitController.splitInfoList(this@SplitActivityList).collect { newSplitInfos ->
                     withContext(Dispatchers.Main) {
-                        findViewById<View>(R.id.infoButton).visibility =
+                        viewBinding.infoButton.visibility =
                             if (newSplitInfos.isEmpty()) View.VISIBLE else View.GONE
                     }
                 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityPlaceholder.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityPlaceholder.kt
index 687f931..921f72d 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityPlaceholder.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitActivityPlaceholder.kt
@@ -18,10 +18,10 @@
 
 import android.graphics.Color
 import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.demo.databinding.ActivitySplitActivityPlaceholderLayoutBinding
 
-open class SplitActivityPlaceholder : AppCompatActivity() {
+open class SplitActivityPlaceholder : EdgeToEdgeActivity() {
 
     lateinit var viewBinding: ActivitySplitActivityPlaceholderLayoutBinding
 
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleActivityBase.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleActivityBase.kt
index 6badb3e..a2fa58be 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleActivityBase.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleActivityBase.kt
@@ -18,11 +18,11 @@
 
 import android.os.Bundle
 import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.window.demo.R
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.embedding.RuleController
 import androidx.window.embedding.SplitAttributes
 import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EXPAND
@@ -35,7 +35,7 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
-open class SplitAttributesToggleActivityBase : AppCompatActivity() {
+open class SplitAttributesToggleActivityBase : EdgeToEdgeActivity() {
     internal lateinit var splitController: SplitController
     internal lateinit var ruleController: RuleController
 
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleMainActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleMainActivity.kt
index ae0246a..588b87c 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleMainActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleMainActivity.kt
@@ -80,6 +80,7 @@
             splitRuleFoldingAwareAttrsRadioButton.isEnabled = false
             viewBinding.splitRuleUseCustomizedSplitAttributes.isEnabled = false
         }
+
         viewBinding.startPrimaryActivityButton.setOnClickListener(this)
         viewBinding.useStickyPlaceholderCheckBox.setOnCheckedChangeListener(this)
         viewBinding.usePlaceholderCheckBox.setOnCheckedChangeListener(this)
@@ -238,6 +239,13 @@
                 if (apiLevel < 3) {
                     append("Finishing secondary activities is not supported on this device!\n")
                 }
+                if (
+                    viewBinding.finishSecondaryActivitiesButton.isEnabled &&
+                        getSplitRule<SplitPlaceholderRule>() != null
+                ) {
+                    append(resources.getString(R.string.show_placeholder_warning))
+                    append("\n")
+                }
             }
         withContext(Dispatchers.Main) { viewBinding.warningMessageTextView.text = warningMessages }
     }
@@ -429,8 +437,11 @@
             R.id.split_rule_layout_direction_spinner ->
                 demoActivityEmbeddingController.customizedLayoutDirection =
                     CUSTOMIZED_LAYOUT_DIRECTIONS_VALUE[position]
+            R.id.animation_background_dropdown ->
+                demoActivityEmbeddingController.animationBackground =
+                    DemoActivityEmbeddingController.ANIMATION_BACKGROUND_VALUES[position]
         }
-        splitController.invalidateTopVisibleSplitAttributes()
+        activityEmbeddingController.invalidateVisibleActivityStacks()
     }
 
     override fun onNothingSelected(view: AdapterView<*>?) {
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesTogglePrimaryActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesTogglePrimaryActivity.kt
index 0e684d2..3544ddfd 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesTogglePrimaryActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesTogglePrimaryActivity.kt
@@ -21,14 +21,19 @@
 import android.graphics.Color
 import android.os.Bundle
 import android.view.View
+import android.widget.ArrayAdapter
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import androidx.window.WindowSdkExtensions
+import androidx.window.core.ExperimentalWindowApi
+import androidx.window.demo.R
 import androidx.window.embedding.ActivityStack
 import androidx.window.embedding.SplitInfo
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
+@OptIn(ExperimentalWindowApi::class)
 class SplitAttributesTogglePrimaryActivity :
     SplitAttributesToggleMainActivity(), View.OnClickListener {
 
@@ -40,6 +45,8 @@
 
         viewBinding.rootSplitActivityLayout.setBackgroundColor(Color.parseColor("#e8f5e9"))
 
+        val isRuntimeApiSupported = WindowSdkExtensions.getInstance().extensionVersion >= 3
+
         secondaryActivityIntent = Intent(this, SplitAttributesToggleSecondaryActivity::class.java)
 
         if (intent.getBooleanExtra(EXTRA_LAUNCH_SECONDARY, false)) {
@@ -51,6 +58,30 @@
 
         // Enable to finish secondary ActivityStacks for primary Activity.
         viewBinding.finishSecondaryActivitiesDivider.visibility = View.VISIBLE
+        val finishSecondaryActivitiesButton =
+            viewBinding.finishSecondaryActivitiesButton.apply {
+                visibility = View.VISIBLE
+                if (!isRuntimeApiSupported) {
+                    isEnabled = false
+                } else {
+                    setOnClickListener(this@SplitAttributesTogglePrimaryActivity)
+                }
+            }
+
+        // Animation background
+        if (WindowSdkExtensions.getInstance().extensionVersion >= 5) {
+            val animationBackgroundDropdown = viewBinding.animationBackgroundDropdown
+            animationBackgroundDropdown.visibility = View.VISIBLE
+            viewBinding.animationBackgroundDivider.visibility = View.VISIBLE
+            viewBinding.animationBackgroundTextView.visibility = View.VISIBLE
+            animationBackgroundDropdown.adapter =
+                ArrayAdapter(
+                    this,
+                    android.R.layout.simple_spinner_dropdown_item,
+                    DemoActivityEmbeddingController.ANIMATION_BACKGROUND_TEXTS
+                )
+            animationBackgroundDropdown.onItemSelectedListener = this
+        }
 
         lifecycleScope.launch {
             // The block passed to repeatOnLifecycle is executed when the lifecycle
@@ -61,6 +92,7 @@
                     .splitInfoList(this@SplitAttributesTogglePrimaryActivity)
                     .onEach { updateUiFromRules() }
                     .collect { splitInfoList ->
+                        finishSecondaryActivitiesButton.isEnabled = splitInfoList.isNotEmpty()
                         activityStacks =
                             splitInfoList.mapTo(mutableSetOf()) { splitInfo ->
                                 splitInfo.getTheOtherActivityStack(
@@ -78,4 +110,14 @@
         } else {
             primaryActivityStack
         }
+
+    override fun onClick(button: View) {
+        super.onClick(button)
+        when (button.id) {
+            R.id.finish_secondary_activities_button -> {
+                applyRules()
+                activityEmbeddingController.finishActivityStacks(activityStacks)
+            }
+        }
+    }
 }
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleSecondaryActivity.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleSecondaryActivity.kt
index 129d0aa9..21cc4a7 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleSecondaryActivity.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitAttributesToggleSecondaryActivity.kt
@@ -175,7 +175,7 @@
                     val enableFullscreenMode =
                         DemoActivityEmbeddingController.getInstance().shouldExpandSecondaryContainer
                     enableFullscreenMode.set(!enableFullscreenMode.get())
-                    splitController.invalidateTopVisibleSplitAttributes()
+                    activityEmbeddingController.invalidateVisibleActivityStacks()
                 } else {
                     // Update the top splitInfo if single default split Attributes is used.
                     splitController.updateSplitAttributes(
@@ -201,7 +201,7 @@
                 demoActivityEmbeddingController.customizedLayoutDirection =
                     CUSTOMIZED_LAYOUT_DIRECTIONS_VALUE[position]
         }
-        splitController.invalidateTopVisibleSplitAttributes()
+        activityEmbeddingController.invalidateVisibleActivityStacks()
     }
 
     override fun onNothingSelected(view: AdapterView<*>?) {
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt
index a6c23d3..29c5046 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitDeviceStateActivityBase.kt
@@ -21,15 +21,16 @@
 import android.os.Bundle
 import android.view.View
 import android.widget.AdapterView
+import android.widget.ArrayAdapter
 import android.widget.CompoundButton
 import android.widget.RadioGroup
 import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.window.WindowSdkExtensions
 import androidx.window.demo.R
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.demo.databinding.ActivitySplitDeviceStateLayoutBinding
 import androidx.window.embedding.EmbeddingRule
 import androidx.window.embedding.RuleController
@@ -37,7 +38,8 @@
 import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EQUAL
 import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EXPAND
 import androidx.window.embedding.SplitController
-import androidx.window.embedding.SplitController.SplitSupportStatus.Companion.SPLIT_AVAILABLE
+import androidx.window.embedding.SplitController.SplitSupportStatus.Companion.SPLIT_ERROR_PROPERTY_NOT_DECLARED
+import androidx.window.embedding.SplitController.SplitSupportStatus.Companion.SPLIT_UNAVAILABLE
 import androidx.window.embedding.SplitInfo
 import androidx.window.embedding.SplitPairFilter
 import androidx.window.embedding.SplitPairRule
@@ -46,7 +48,7 @@
 import kotlinx.coroutines.withContext
 
 open class SplitDeviceStateActivityBase :
-    AppCompatActivity(),
+    EdgeToEdgeActivity(),
     View.OnClickListener,
     RadioGroup.OnCheckedChangeListener,
     CompoundButton.OnCheckedChangeListener,
@@ -64,6 +66,8 @@
     private lateinit var activityA: ComponentName
     private lateinit var activityB: ComponentName
 
+    private val demoActivityEmbeddingController = DemoActivityEmbeddingController.getInstance()
+
     /** The last selected split rule id. */
     private var lastCheckedRuleId = 0
 
@@ -73,7 +77,11 @@
         super.onCreate(savedInstanceState)
         viewBinding = ActivitySplitDeviceStateLayoutBinding.inflate(layoutInflater)
         splitController = SplitController.getInstance(this)
-        if (splitController.splitSupportStatus != SPLIT_AVAILABLE) {
+        if (splitController.splitSupportStatus == SPLIT_UNAVAILABLE) {
+            Toast.makeText(this, R.string.toast_split_not_available, Toast.LENGTH_SHORT).show()
+            finish()
+            return
+        } else if (splitController.splitSupportStatus == SPLIT_ERROR_PROPERTY_NOT_DECLARED) {
             Toast.makeText(this, R.string.toast_split_not_support, Toast.LENGTH_SHORT).show()
             finish()
             return
@@ -118,6 +126,22 @@
                 resources.getString(R.string.split_attributes_calculator_not_supported)
         }
 
+        // Animation background
+        if (WindowSdkExtensions.getInstance().extensionVersion >= 5 && componentName == activityA) {
+            // Show on only the primary activity.
+            val animationBackgroundDropdown = viewBinding.animationBackgroundDropdown
+            animationBackgroundDropdown.visibility = View.VISIBLE
+            viewBinding.animationBackgroundDivider.visibility = View.VISIBLE
+            viewBinding.animationBackgroundTextView.visibility = View.VISIBLE
+            animationBackgroundDropdown.adapter =
+                ArrayAdapter(
+                    this,
+                    android.R.layout.simple_spinner_dropdown_item,
+                    DemoActivityEmbeddingController.ANIMATION_BACKGROUND_TEXTS
+                )
+            animationBackgroundDropdown.onItemSelectedListener = this
+        }
+
         lifecycleScope.launch {
             // The block passed to repeatOnLifecycle is executed when the lifecycle
             // is at least STARTED and is cancelled when the lifecycle is STOPPED.
@@ -171,6 +195,8 @@
     }
 
     override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+        demoActivityEmbeddingController.animationBackground =
+            DemoActivityEmbeddingController.ANIMATION_BACKGROUND_VALUES[position]
         updateSplitPairRuleWithRadioButtonId(lastCheckedRuleId)
     }
 
@@ -246,6 +272,7 @@
             SplitAttributes.Builder()
                 .setSplitType(SPLIT_TYPE_EQUAL)
                 .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                .setAnimationBackground(demoActivityEmbeddingController.animationBackground)
                 .build()
         // Use the tag to control the rule how to change split attributes with the current state
         var tag =
@@ -292,7 +319,10 @@
 
     private suspend fun updateSplitAttributesText(newSplitInfos: List<SplitInfo>) {
         var splitAttributes: SplitAttributes =
-            SplitAttributes.Builder().setSplitType(SPLIT_TYPE_EXPAND).build()
+            SplitAttributes.Builder()
+                .setSplitType(SPLIT_TYPE_EXPAND)
+                .setAnimationBackground(demoActivityEmbeddingController.animationBackground)
+                .build()
         var suggestToFinishItself = false
 
         // Traverse SplitInfos from the end because last SplitInfo has the highest z-order.
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitImeActivityBase.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitImeActivityBase.kt
index aa3bdbc..d167778 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitImeActivityBase.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitImeActivityBase.kt
@@ -17,14 +17,14 @@
 package androidx.window.demo.embedding
 
 import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.demo.databinding.ActivitySplitImeLayoutBinding
 
 /**
  * Sample showcase of split activity with input field. Allows the user to use IME with split
  * activities. The split rule is defined in `main_split_config.xml`.
  */
-abstract class SplitImeActivityBase : AppCompatActivity() {
+abstract class SplitImeActivityBase : EdgeToEdgeActivity() {
 
     lateinit var viewBinding: ActivitySplitImeLayoutBinding
 
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitPipActivityBase.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitPipActivityBase.kt
index ae74e8f..a626a67 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitPipActivityBase.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/embedding/SplitPipActivityBase.kt
@@ -24,11 +24,11 @@
 import android.widget.CompoundButton
 import android.widget.RadioGroup
 import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.window.demo.R
+import androidx.window.demo.common.EdgeToEdgeActivity
 import androidx.window.demo.common.util.PictureInPictureUtil
 import androidx.window.demo.databinding.ActivitySplitPipActivityLayoutBinding
 import androidx.window.embedding.ActivityFilter
@@ -53,7 +53,7 @@
  * applied.
  */
 abstract class SplitPipActivityBase :
-    AppCompatActivity(),
+    EdgeToEdgeActivity(),
     CompoundButton.OnCheckedChangeListener,
     View.OnClickListener,
     RadioGroup.OnCheckedChangeListener {
diff --git a/window/window-demos/demo/src/main/res/layout/activity_coresdk_window_state_callback_layout.xml b/window/window-demos/demo/src/main/res/layout/activity_coresdk_window_state_callback_layout.xml
deleted file mode 100644
index 449fe9a..0000000
--- a/window/window-demos/demo/src/main/res/layout/activity_coresdk_window_state_callback_layout.xml
+++ /dev/null
@@ -1,109 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/root_split_activity_layout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical"
-        android:padding="10dp">
-
-        <!-- Update to the latest configuration. -->
-        <androidx.window.demo.coresdk.WindowStateView
-            android:id="@+id/latest_update_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:title="@string/latest_configuration_title"
-            app:hidePrevConfig="true"/>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
-            android:background="#AAAAAA" />
-
-        <!-- Update from Application DisplayListener. -->
-        <androidx.window.demo.coresdk.WindowStateView
-            android:id="@+id/application_display_listener_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:title="@string/application_display_listener_title"/>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
-            android:background="#AAAAAA" />
-
-        <!-- Update from Activity DisplayListener. -->
-        <androidx.window.demo.coresdk.WindowStateView
-            android:id="@+id/activity_display_listener_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:title="@string/activity_display_listener_title"/>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
-            android:background="#AAAAAA" />
-
-        <!-- Update from Application#onConfigurationChanged. -->
-        <androidx.window.demo.coresdk.WindowStateView
-            android:id="@+id/application_configuration_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:title="@string/application_configuration_title"/>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
-            android:background="#AAAAAA" />
-
-        <!-- Update from Activity#onConfigurationChanged. -->
-        <androidx.window.demo.coresdk.WindowStateView
-            android:id="@+id/activity_configuration_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:title="@string/activity_configuration_title"/>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
-            android:background="#AAAAAA" />
-
-        <!-- Update from WindowInfoTracker. -->
-        <androidx.window.demo.coresdk.WindowStateView
-            android:id="@+id/display_feature_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:title="@string/display_feature_title"/>
-
-    </LinearLayout>
-</ScrollView>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/activity_overlay_activity_layout.xml b/window/window-demos/demo/src/main/res/layout/activity_overlay_activity_layout.xml
new file mode 100644
index 0000000..8650bcb
--- /dev/null
+++ b/window/window-demos/demo/src/main/res/layout/activity_overlay_activity_layout.xml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/root_overlay_activity_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/textView_overlay_bounds"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/overlay_bounds_text" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginTop="10dp"
+            android:background="#AAAAAA" />
+
+        <TextView
+            android:id="@+id/textView_choose_overlay_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Choose overlay layout:" />
+
+        <RadioGroup
+            android:id="@+id/radioGroup_choose_overlay_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <RadioButton
+                android:id="@+id/radioButton_simple_overlay"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Launch the overlay container to the right" />
+
+            <RadioButton
+                android:id="@+id/radioButton_change_with_orientation"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Change overlay layout with orientation" />
+
+            <RadioButton
+                android:id="@+id/radioButton_customization"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Use customized overlay layout" />
+        </RadioGroup>
+
+        <TextView
+            android:id="@+id/textView_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Layout"
+            android:visibility="visible" />
+
+        <!-- UI to control position -->
+
+        <TextView
+            android:id="@+id/textView_position"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Position"
+            android:visibility="visible" />
+        <Spinner
+            android:id="@+id/spinner_alignment"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:spinnerMode="dropdown"
+            tools:visibility="visible" />
+
+        <!-- UI to control width -->
+
+        <TextView
+            android:id="@+id/textView_width"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Width"
+            android:visibility="visible" />
+        <Spinner
+            android:id="@+id/spinner_width"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:spinnerMode="dropdown"
+            tools:visibility="visible" />
+        <TextView
+            android:id="@+id/textView_width_in_pixel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Width in pixel"
+            android:visibility="gone" />
+        <EditText
+            android:id="@+id/editTextNumberDecimal_width_in_pixel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ems="4"
+            android:inputType="numberDecimal"
+            android:visibility="gone" />
+        <TextView
+            android:id="@+id/textView_width_in_ratio"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/width_in_ratio" />
+        <SeekBar
+            android:id="@+id/seekBar_width_in_ratio"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:min="1"
+            android:max="99"
+            android:progress="50" />
+
+        <!-- UI to control height -->
+
+        <TextView
+            android:id="@+id/textView_height"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Height"
+            android:visibility="visible" />
+        <Spinner
+            android:id="@+id/spinner_height"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:spinnerMode="dropdown"
+            tools:visibility="visible" />
+        <TextView
+            android:id="@+id/textView_height_in_pixel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Height in pixel"
+            android:visibility="gone" />
+        <EditText
+            android:id="@+id/editTextNumberDecimal_height_in_pixel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ems="4"
+            android:inputType="numberDecimal"
+            android:visibility="gone" />
+        <TextView
+            android:id="@+id/textView_height_in_ratio"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/height_in_ratio"
+            android:visibility="gone" />
+        <SeekBar
+            android:id="@+id/seekBar_height_in_ratio"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:min="1"
+            android:max="99"
+            android:progress="80"
+            android:visibility="gone" />
+
+        <!-- UI to update overlay layout  -->
+
+        <Button
+            android:id="@+id/button_update_overlay_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Update Overlay layout" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginTop="10dp"
+            android:background="#AAAAAA" />
+
+        <Button
+            android:id="@+id/button_launch_overlay_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Launch the overlay container" />
+
+        <CheckBox
+            android:id="@+id/checkbox_reorder_to_front"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Reorder Activity A to the front" />
+
+        <Button
+            android:id="@+id/button_launch_overlay_activity_a"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Launch Overlay Associated Activity A" />
+
+        <CheckBox
+            android:id="@+id/checkbox_launch_to_overlay"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Launch Activity B to the overlay container" />
+
+        <Button
+            android:id="@+id/button_launch_overlay_activity_b"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Launch Overlay Associated Activity B" />
+
+        <Button
+            android:id="@+id/button_finish_this_activity"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Finish this activity" />
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/activity_rear_display.xml b/window/window-demos/demo/src/main/res/layout/activity_rear_display.xml
index 43bea60..9d0c9c3 100644
--- a/window/window-demos/demo/src/main/res/layout/activity_rear_display.xml
+++ b/window/window-demos/demo/src/main/res/layout/activity_rear_display.xml
@@ -37,6 +37,17 @@
         android:layout_marginBottom="32dp"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/rear_display_session_button" />
+
+    <Button
+        android:id="@+id/rear_display_session_button"
+        android:text="Get active session if available"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAlignment="center"
+        android:layout_marginBottom="32dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="parent" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/activity_rear_display_presentation.xml b/window/window-demos/demo/src/main/res/layout/activity_rear_display_presentation.xml
new file mode 100644
index 0000000..ff5129d
--- /dev/null
+++ b/window/window-demos/demo/src/main/res/layout/activity_rear_display_presentation.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/rearStatusRecyclerView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"/>
+
+    <Button
+        android:id="@+id/rear_display_presentation_button"
+        android:text="Enable Rear Display Presentation"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAlignment="center"
+        android:layout_marginBottom="32dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/activity_split_activity_layout.xml b/window/window-demos/demo/src/main/res/layout/activity_split_activity_layout.xml
index ab54e1e..bb565ce 100644
--- a/window/window-demos/demo/src/main/res/layout/activity_split_activity_layout.xml
+++ b/window/window-demos/demo/src/main/res/layout/activity_split_activity_layout.xml
@@ -30,7 +30,15 @@
             android:id="@+id/activity_embedded_status_text_view"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:text="Activity is embedded" />
+            android:text="Activity is embedded"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/activity_embedded_bounds_text_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Embedded bounds not available"
+            android:visibility="gone" />
 
         <CheckBox
             android:id="@+id/splitMainCheckBox"
@@ -38,6 +46,19 @@
             android:layout_height="wrap_content"
             android:text="Split Main with other activities" />
 
+        <CheckBox
+            android:id="@+id/dividerCheckBox"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Add a divider between containers" />
+
+        <CheckBox
+            android:id="@+id/draggableDividerCheckBox"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Make the divider draggable"
+            android:enabled="false"/>
+
         <View
             android:layout_width="match_parent"
             android:layout_height="1dp"
@@ -193,5 +214,60 @@
             android:layout_height="wrap_content"
             android:layout_centerHorizontal="true"
             android:text="Launch Expanded Dialog" />
+
+        <Button
+            android:id="@+id/launch_dialog_activity_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:text="Launch Dialog Activity" />
+
+        <Button
+            android:id="@+id/launch_dialog_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:text="Launch Dialog" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:background="#AAAAAA" />
+
+        <Button
+            android:id="@+id/pin_top_activity_stack_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:text="Pin Top ActivityStack" />
+
+        <CheckBox
+            android:id="@+id/stickyPinRule"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Set Pin Rule Sticky" />
+
+        <Button
+            android:id="@+id/unpin_top_activity_stack_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:text="Unpin Top ActivityStack" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:background="#AAAAAA" />
+
+        <Button
+            android:id="@+id/button_launch_overlay_associated_activity"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:text="Launch Overlay Associated Activity A" />
     </LinearLayout>
 </ScrollView>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/activity_split_attributes_toggle_primary_activity.xml b/window/window-demos/demo/src/main/res/layout/activity_split_attributes_toggle_primary_activity.xml
index dad1100..c4e0ef7 100644
--- a/window/window-demos/demo/src/main/res/layout/activity_split_attributes_toggle_primary_activity.xml
+++ b/window/window-demos/demo/src/main/res/layout/activity_split_attributes_toggle_primary_activity.xml
@@ -192,6 +192,31 @@
                     android:visibility="gone"/>
         </RadioGroup>
 
+        <!-- Dropdown for animation background -->
+
+        <View
+            android:id="@+id/animation_background_divider"
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:background="#AAAAAA"
+            android:visibility="gone"/>
+
+        <TextView
+            android:id="@+id/animation_background_text_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/current_animation_background"
+            android:visibility="gone"/>
+
+        <Spinner
+            android:id="@+id/animation_background_dropdown"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:spinnerMode="dropdown"
+            android:visibility="gone"/>
+
         <View
             android:id="@+id/finish_secondary_activities_divider"
             android:layout_width="match_parent"
@@ -201,6 +226,14 @@
             android:visibility="gone"
             android:background="#AAAAAA" />
 
+        <Button
+            android:id="@+id/finish_secondary_activities_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:layout_centerHorizontal="true"
+            android:text="Finish secondary activities"
+            android:visibility="gone"/>
+
         <View
             android:layout_width="match_parent"
             android:layout_height="1dp"
diff --git a/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml b/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml
index c71a541..4bd1b21 100644
--- a/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml
+++ b/window/window-demos/demo/src/main/res/layout/activity_split_device_state_layout.xml
@@ -143,6 +143,31 @@
                 android:text="Swap the position of primary and secondary container" />
         </RadioGroup>
 
+        <!-- Dropdown for animation background -->
+
+        <View
+            android:id="@+id/animation_background_divider"
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:background="#AAAAAA"
+            android:visibility="gone"/>
+
+        <TextView
+            android:id="@+id/animation_background_text_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/current_animation_background"
+            android:visibility="gone"/>
+
+        <Spinner
+            android:id="@+id/animation_background_dropdown"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:spinnerMode="dropdown"
+            android:visibility="gone"/>
+
         <Button
             android:id="@+id/launch_activity_to_side"
             android:layout_width="wrap_content"
diff --git a/window/window-demos/demo/src/main/res/layout/concurrent_presentation.xml b/window/window-demos/demo/src/main/res/layout/concurrent_presentation.xml
new file mode 100644
index 0000000..9c10df2
--- /dev/null
+++ b/window/window-demos/demo/src/main/res/layout/concurrent_presentation.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/textView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Dual Display Presentation"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/window_state_config_view.xml b/window/window-demos/demo/src/main/res/layout/window_state_config_view.xml
deleted file mode 100644
index 6a97d8f..0000000
--- a/window/window-demos/demo/src/main/res/layout/window_state_config_view.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginTop="5dp"
-    android:orientation="horizontal">
-
-    <TextView
-        android:id="@+id/config_name"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textStyle="bold"
-        android:text="@string/window_state_placeholder"/>
-
-    <TextView
-        android:id="@+id/config_value"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/window_state_placeholder"
-        android:layout_marginLeft="10dp"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/layout/window_state_view.xml b/window/window-demos/demo/src/main/res/layout/window_state_view.xml
deleted file mode 100644
index 4a03bbd..0000000
--- a/window/window-demos/demo/src/main/res/layout/window_state_view.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2023 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <!-- Callback name -->
-    <TextView
-        android:id="@+id/callback_title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="@android:style/TextAppearance.Material.Title"
-        android:text="@string/window_state_placeholder"/>
-
-    <!-- Last update timestamp -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/timestamp_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/timestamp_title"/>
-
-    <!-- Application Display rotation -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/application_display_rotation_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/application_display_rotation_title"/>
-
-    <!-- Activity Display rotation -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/activity_display_rotation_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/activity_display_rotation_title"/>
-
-    <!-- Application Display bounds -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/application_display_bounds_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/application_display_bounds_title"/>
-
-    <!-- Activity Display bounds -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/activity_display_bounds_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/activity_display_bounds_title"/>
-
-    <!-- Previous Application Display rotation -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/prev_application_display_rotation_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/prev_application_display_rotation_title"/>
-
-    <!-- Previous Activity Display rotation -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/prev_activity_display_rotation_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/prev_activity_display_rotation_title"/>
-
-    <!-- Previous Application Display bounds -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/prev_application_display_bounds_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/prev_application_display_bounds_title"/>
-
-    <!-- Previous Activity Display bounds -->
-    <androidx.window.demo.coresdk.WindowStateConfigView
-        android:id="@+id/prev_activity_display_bounds_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:configName="@string/prev_activity_display_bounds_title"/>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/window/window-demos/demo/src/main/res/values/strings.xml b/window/window-demos/demo/src/main/res/values/strings.xml
index 892cecc..357ce1c 100644
--- a/window/window-demos/demo/src/main/res/values/strings.xml
+++ b/window/window-demos/demo/src/main/res/values/strings.xml
@@ -50,7 +50,7 @@
     <string name="rear_display">Rear Display Mode</string>
     <string name="rear_display_description">Demo of observing to WindowAreaStatus and enabling/disabling RearDisplay mode</string>
     <string name="current_split_attributes">Current SplitAttributes:</string>>
-    <string name="current_animation_background_color">Current Animation Background Color:</string>>
+    <string name="current_animation_background">Current Animation Background:</string>>
     <string name="test_ime">Test IME</string>
     <string name="test_ime_button_clear">Clear Logs</string>
     <string name="test_ime_button_close">Close Test IME</string>
@@ -61,13 +61,14 @@
     <string name="ime_button_settings">System IME Settings</string>
     <string name="ime_button_switch_default">Switch default IME</string>
     <string name="install_samples_2">Install window-demos:demo-second-app to launch activities from a different UID.</string>
-    <string name="toast_split_not_support">Please enable PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED and ensure device supports splits.</string>
+    <string name="toast_split_not_available">This device does not support Activity Embedding.</string>
+    <string name="toast_split_not_support">Please enable PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED.</string>
     <string name="latest_configuration_title">Latest Configuration</string>
     <string name="application_display_listener_title">Application DisplayListener#onDisplayChanged</string>
     <string name="activity_display_listener_title">Activity DisplayListener#onDisplayChanged</string>
     <string name="application_configuration_title">Application#onConfigurationChanged</string>
     <string name="activity_configuration_title">Activity#onConfigurationChanged</string>
-    <string name="display_feature_title">WindowInfoTracker</string>
+    <string name="display_feature_title">WindowInfoTracker#windowLayoutInfo</string>
     <string name="timestamp_title">Timestamp:</string>
     <string name="application_display_rotation_title">Application Display Rotation:</string>
     <string name="activity_display_rotation_title">Activity Display Rotation:</string>
@@ -86,4 +87,10 @@
     <string name="choose_split_type">Choose the split type</string>
     <string name="choose_layout_direction">Choose the layout direction</string>
     <string name="show_placeholder_warning">Placeholder Activity may show (again) because "Use a placeholder for A is checked". Clear the check box to expand Activity A to fill the task by either clicking "FINISH SECONDARY ACTIVITIES".</string>
+    <string name="dual_display">Dual Display</string>
+    <string name="dual_display_description">Demo of showing content on the rear and internal displays concurrently</string>
+    <string name="toast_show_overlay_warning">The device does not support overlay features!</string>
+    <string name="overlay_bounds_text">Overlay bounds:</string>
+    <string name="width_in_ratio">Width in ratio: </string>
+    <string name="height_in_ratio">Height in ratio: </string>
 </resources>
diff --git a/window/window-demos/demo/src/main/res/values/styles.xml b/window/window-demos/demo/src/main/res/values/styles.xml
index 5586114..347966d 100644
--- a/window/window-demos/demo/src/main/res/values/styles.xml
+++ b/window/window-demos/demo/src/main/res/values/styles.xml
@@ -15,15 +15,6 @@
   -->
 
 <resources>
-
-    <!-- Base application theme. -->
-    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
-        <!-- Customize your theme here. -->
-        <item name="colorPrimary">@color/colorPrimary</item>
-        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
-        <item name="colorAccent">@color/colorAccent</item>
-    </style>
-
     <!-- Theme to show the expanded dialog Activity as transparent. -->
     <style name="ExpandedDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
         <item name="windowNoTitle">true</item>
diff --git a/window/window-demos/demo/src/main/res/xml/main_split_config.xml b/window/window-demos/demo/src/main/res/xml/main_split_config.xml
index 8269c38..3a898ce 100644
--- a/window/window-demos/demo/src/main/res/xml/main_split_config.xml
+++ b/window/window-demos/demo/src/main/res/xml/main_split_config.xml
@@ -41,4 +41,14 @@
         <ActivityFilter
             window:activityName="androidx.window.demo.embedding.SplitImeActivityMain"/>
     </SplitPlaceholderRule>
+
+    <!-- Rules for OverlayActivity -->
+
+    <SplitPairRule
+        window:finishPrimaryWithSecondary="never"
+        window:finishSecondaryWithPrimary="adjacent">
+        <SplitPairFilter
+            window:primaryActivityName="androidx.window.demo.embedding.OverlayActivityA"
+            window:secondaryActivityName="androidx.window.demo.embedding.SplitActivityDetail"/>
+    </SplitPairRule>
 </resources>
\ No newline at end of file
diff --git a/window/window-java/api/current.txt b/window/window-java/api/current.txt
index 2e19128..c3b2d91 100644
--- a/window/window-java/api/current.txt
+++ b/window/window-java/api/current.txt
@@ -11,6 +11,18 @@
 
 package androidx.window.java.embedding {
 
+  public final class ActivityEmbeddingControllerCallbackAdapter {
+    ctor public ActivityEmbeddingControllerCallbackAdapter(androidx.window.embedding.ActivityEmbeddingController controller);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public void addEmbeddedActivityWindowInfoListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.embedding.EmbeddedActivityWindowInfo> listener);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public void removeEmbeddedActivityWindowInfoListener(androidx.core.util.Consumer<androidx.window.embedding.EmbeddedActivityWindowInfo> listener);
+  }
+
+  public final class OverlayControllerCallbackAdapter {
+    ctor public OverlayControllerCallbackAdapter(androidx.window.embedding.OverlayController controller);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void addOverlayInfoListener(String overlayTag, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.embedding.OverlayInfo> consumer);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void removeOverlayInfoListener(androidx.core.util.Consumer<androidx.window.embedding.OverlayInfo> consumer);
+  }
+
   @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class SplitControllerCallbackAdapter {
     ctor public SplitControllerCallbackAdapter(androidx.window.embedding.SplitController controller);
     method public void addSplitListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
diff --git a/window/window-java/api/restricted_current.txt b/window/window-java/api/restricted_current.txt
index 2e19128..c3b2d91 100644
--- a/window/window-java/api/restricted_current.txt
+++ b/window/window-java/api/restricted_current.txt
@@ -11,6 +11,18 @@
 
 package androidx.window.java.embedding {
 
+  public final class ActivityEmbeddingControllerCallbackAdapter {
+    ctor public ActivityEmbeddingControllerCallbackAdapter(androidx.window.embedding.ActivityEmbeddingController controller);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public void addEmbeddedActivityWindowInfoListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.embedding.EmbeddedActivityWindowInfo> listener);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public void removeEmbeddedActivityWindowInfoListener(androidx.core.util.Consumer<androidx.window.embedding.EmbeddedActivityWindowInfo> listener);
+  }
+
+  public final class OverlayControllerCallbackAdapter {
+    ctor public OverlayControllerCallbackAdapter(androidx.window.embedding.OverlayController controller);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void addOverlayInfoListener(String overlayTag, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.embedding.OverlayInfo> consumer);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void removeOverlayInfoListener(androidx.core.util.Consumer<androidx.window.embedding.OverlayInfo> consumer);
+  }
+
   @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class SplitControllerCallbackAdapter {
     ctor public SplitControllerCallbackAdapter(androidx.window.embedding.SplitController controller);
     method public void addSplitListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
diff --git a/window/window-java/build.gradle b/window/window-java/build.gradle
index 77182db..a64e31c 100644
--- a/window/window-java/build.gradle
+++ b/window/window-java/build.gradle
@@ -35,7 +35,7 @@
     api(libs.kotlinCoroutinesCore)
     api(project(":window:window"))
     implementation("androidx.core:core:1.8.0")
-    implementation("androidx.annotation:annotation:1.7.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
@@ -57,7 +57,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "WindowManager Java Support"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
diff --git a/window/window-java/src/main/java/androidx/window/java/embedding/ActivityEmbeddingControllerCallbackAdapter.kt b/window/window-java/src/main/java/androidx/window/java/embedding/ActivityEmbeddingControllerCallbackAdapter.kt
new file mode 100644
index 0000000..0f0e7f2
--- /dev/null
+++ b/window/window-java/src/main/java/androidx/window/java/embedding/ActivityEmbeddingControllerCallbackAdapter.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.java.embedding
+
+import android.app.Activity
+import androidx.core.util.Consumer
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.embedding.ActivityEmbeddingController
+import androidx.window.embedding.EmbeddedActivityWindowInfo
+import androidx.window.java.core.CallbackToFlowAdapter
+import java.util.concurrent.Executor
+
+/**
+ * An adapted interface for [ActivityEmbeddingController] that provides callback shaped APIs to
+ * report the latest [EmbeddedActivityWindowInfo].
+ *
+ * It should only be used if [ActivityEmbeddingController.embeddedActivityWindowInfo] is not
+ * available. For example, an app is written in Java and cannot use Flow APIs.
+ *
+ * @param controller an [ActivityEmbeddingController] that can be obtained by
+ *   [ActivityEmbeddingController.getInstance].
+ * @constructor creates a callback adapter of
+ *   [ActivityEmbeddingController.embeddedActivityWindowInfo] flow API.
+ */
+class ActivityEmbeddingControllerCallbackAdapter(
+    private val controller: ActivityEmbeddingController
+) {
+    private val callbackToFlowAdapter = CallbackToFlowAdapter()
+
+    /**
+     * Registers a listener for updates of [EmbeddedActivityWindowInfo] of [activity].
+     *
+     * The [listener] will immediately be invoked with the latest value upon registration if the
+     * [activity] is currently embedded as [EmbeddedActivityWindowInfo.isEmbedded] is `true`.
+     *
+     * When the [activity] is embedded, the [listener] will be invoked when
+     * [EmbeddedActivityWindowInfo] is changed. When the [activity] is not embedded, the [listener]
+     * will not be triggered unless the [activity] is becoming non-embedded from embedded.
+     *
+     * Note that this API is only supported on the device with
+     * [WindowSdkExtensions.extensionVersion] equal to or larger than 6. If
+     * [WindowSdkExtensions.extensionVersion] is less than 6, this [listener] will not be invoked.
+     *
+     * @param activity the [Activity] that is interested in getting the embedded window info.
+     * @param executor the [Executor] to dispatch the [EmbeddedActivityWindowInfo] change.
+     * @param listener the [Consumer] that will be invoked on the [executor] when there is an update
+     *   to [EmbeddedActivityWindowInfo].
+     */
+    @RequiresWindowSdkExtension(6)
+    fun addEmbeddedActivityWindowInfoListener(
+        activity: Activity,
+        executor: Executor,
+        listener: Consumer<EmbeddedActivityWindowInfo>
+    ) {
+        callbackToFlowAdapter.connect(
+            executor,
+            listener,
+            controller.embeddedActivityWindowInfo(activity)
+        )
+    }
+
+    /**
+     * Unregisters a listener that was previously registered via
+     * [addEmbeddedActivityWindowInfoListener].
+     *
+     * It's no-op if the [listener] has not been registered.
+     *
+     * @param listener the previously registered [Consumer] to unregister.
+     */
+    @RequiresWindowSdkExtension(6)
+    fun removeEmbeddedActivityWindowInfoListener(listener: Consumer<EmbeddedActivityWindowInfo>) {
+        callbackToFlowAdapter.disconnect(listener)
+    }
+}
diff --git a/window/window-java/src/main/java/androidx/window/java/embedding/OverlayControllerCallbackAdapter.kt b/window/window-java/src/main/java/androidx/window/java/embedding/OverlayControllerCallbackAdapter.kt
new file mode 100644
index 0000000..78d9e13
--- /dev/null
+++ b/window/window-java/src/main/java/androidx/window/java/embedding/OverlayControllerCallbackAdapter.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.java.embedding
+
+import androidx.core.util.Consumer
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.embedding.ActivityStack
+import androidx.window.embedding.OverlayController
+import androidx.window.embedding.OverlayCreateParams
+import androidx.window.embedding.OverlayInfo
+import androidx.window.java.core.CallbackToFlowAdapter
+import java.util.concurrent.Executor
+
+/**
+ * An adapted interface for [OverlayController] that provides callback shaped APIs to report the
+ * latest [OverlayInfo].
+ *
+ * It should only be used if [OverlayController.overlayInfo] is not available. For example, an app
+ * is written in Java and cannot use Flow APIs.
+ *
+ * @param controller an [OverlayController] that can be obtained by [OverlayController.getInstance].
+ * @constructor creates a callback adapter of [OverlayController.overlayInfo] flow API.
+ */
+class OverlayControllerCallbackAdapter(private val controller: OverlayController) {
+
+    private val callbackToFlowAdapter = CallbackToFlowAdapter()
+
+    /**
+     * Registers a listener for updates of [OverlayInfo] that [overlayTag] is associated with.
+     *
+     * If there is no active overlay [ActivityStack], the reported [OverlayInfo.activityStack] and
+     * [OverlayInfo.currentOverlayAttributes] will be `null`.
+     *
+     * Note that launching an overlay [ActivityStack] only supports on the device with
+     * [WindowSdkExtensions.extensionVersion] equal to or larger than 5. If
+     * [WindowSdkExtensions.extensionVersion] is less than 5, this flow will always report
+     * [OverlayInfo] without associated [OverlayInfo.activityStack].
+     *
+     * @param overlayTag the overlay [ActivityStack]'s tag which is set through
+     *   [OverlayCreateParams]
+     * @param executor the [Executor] to dispatch the [OverlayInfo] change
+     * @param consumer the [Consumer] that will be invoked on the [executor] when there is an update
+     *   to [OverlayInfo].
+     */
+    @RequiresWindowSdkExtension(5)
+    fun addOverlayInfoListener(
+        overlayTag: String,
+        executor: Executor,
+        consumer: Consumer<OverlayInfo>
+    ) {
+        callbackToFlowAdapter.connect(executor, consumer, controller.overlayInfo(overlayTag))
+    }
+
+    /**
+     * Unregisters a listener that was previously registered via [addOverlayInfoListener].
+     *
+     * @param consumer the previously registered [Consumer] to unregister.
+     */
+    @RequiresWindowSdkExtension(5)
+    fun removeOverlayInfoListener(consumer: Consumer<OverlayInfo>) {
+        callbackToFlowAdapter.disconnect(consumer)
+    }
+}
diff --git a/window/window-rxjava2/build.gradle b/window/window-rxjava2/build.gradle
index d2738a8..0a84a10 100644
--- a/window/window-rxjava2/build.gradle
+++ b/window/window-rxjava2/build.gradle
@@ -40,7 +40,7 @@
     api(libs.kotlinCoroutinesRx2)
     api(libs.rxjava2)
     api(project(":window:window"))
-    implementation("androidx.annotation:annotation:1.5.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRunner)
@@ -56,6 +56,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "WindowManager RxJava 2"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/window/window-rxjava3/build.gradle b/window/window-rxjava3/build.gradle
index acbc9de..27f2064 100644
--- a/window/window-rxjava3/build.gradle
+++ b/window/window-rxjava3/build.gradle
@@ -40,7 +40,7 @@
     api(libs.kotlinCoroutinesRx3)
     api(libs.rxjava3)
     api(project(":window:window"))
-    implementation("androidx.annotation:annotation:1.5.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testRunner)
@@ -56,6 +56,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2021"
     description = "WindowManager RxJava 3 Support"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/window/window-testing/api/current.txt b/window/window-testing/api/current.txt
index 847a9e8..84d25ac 100644
--- a/window/window-testing/api/current.txt
+++ b/window/window-testing/api/current.txt
@@ -1,4 +1,14 @@
 // Signature format: 4.0
+package androidx.window.testing {
+
+  public final class WindowSdkExtensionsRule implements org.junit.rules.TestRule {
+    ctor public WindowSdkExtensionsRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideExtensionVersion(@IntRange(from=0L) int version);
+  }
+
+}
+
 package androidx.window.testing.embedding {
 
   public final class ActivityEmbeddingRule implements org.junit.rules.TestRule {
diff --git a/window/window-testing/api/restricted_current.txt b/window/window-testing/api/restricted_current.txt
index 847a9e8..84d25ac 100644
--- a/window/window-testing/api/restricted_current.txt
+++ b/window/window-testing/api/restricted_current.txt
@@ -1,4 +1,14 @@
 // Signature format: 4.0
+package androidx.window.testing {
+
+  public final class WindowSdkExtensionsRule implements org.junit.rules.TestRule {
+    ctor public WindowSdkExtensionsRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideExtensionVersion(@IntRange(from=0L) int version);
+  }
+
+}
+
 package androidx.window.testing.embedding {
 
   public final class ActivityEmbeddingRule implements org.junit.rules.TestRule {
diff --git a/window/window-testing/build.gradle b/window/window-testing/build.gradle
index 328cd60..c0cace4 100644
--- a/window/window-testing/build.gradle
+++ b/window/window-testing/build.gradle
@@ -39,7 +39,7 @@
     api(project(":window:window"))
     api(libs.junit)
     implementation("androidx.core:core:1.8.0")
-    implementation("androidx.annotation:annotation:1.7.0")
+    implementation("androidx.annotation:annotation:1.8.1")
 
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testExtJunit)
@@ -63,6 +63,5 @@
     type = LibraryType.PUBLISHED_TEST_LIBRARY
     inceptionYear = "2021"
     description = "WindowManager Test Library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/window/window-testing/src/main/java/androidx/window/testing/FakeWindowSdkExtensions.kt b/window/window-testing/src/main/java/androidx/window/testing/FakeWindowSdkExtensions.kt
new file mode 100644
index 0000000..75ebdf5
--- /dev/null
+++ b/window/window-testing/src/main/java/androidx/window/testing/FakeWindowSdkExtensions.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.testing
+
+import androidx.annotation.IntRange
+import androidx.window.WindowSdkExtensions
+
+/**
+ * A fake [WindowSdkExtensions] implementation that can override [extensionVersion], which is
+ * intended to be used during unit tests.
+ */
+internal class FakeWindowSdkExtensions : WindowSdkExtensions() {
+
+    override val extensionVersion: Int
+        get() = _extensionVersion
+
+    private var _extensionVersion: Int = 0
+
+    internal fun overrideExtensionVersion(@IntRange(from = 0) version: Int) {
+        require(version >= 0) { "The override version must equal to or greater than 0." }
+        _extensionVersion = version
+    }
+}
diff --git a/window/window-testing/src/main/java/androidx/window/testing/WindowSdkExtensionsRule.kt b/window/window-testing/src/main/java/androidx/window/testing/WindowSdkExtensionsRule.kt
new file mode 100644
index 0000000..185fdae
--- /dev/null
+++ b/window/window-testing/src/main/java/androidx/window/testing/WindowSdkExtensionsRule.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.testing
+
+import androidx.annotation.IntRange
+import androidx.window.WindowSdkExtensions
+import androidx.window.WindowSdkExtensionsDecorator
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * [TestRule] for overriding [WindowSdkExtensions] properties in unit tests.
+ *
+ * The [TestRule] is designed to only be used in unit tests. Users should use the actual
+ * [WindowSdkExtensions] properties for instrumentation tests. Overriding the device's extensions
+ * version to a higher version may lead to unexpected test failures or even app crash.
+ */
+class WindowSdkExtensionsRule : TestRule {
+
+    private val fakeWindowSdkExtensions = FakeWindowSdkExtensions()
+
+    override fun apply(
+        @Suppress("InvalidNullabilityOverride") // JUnit missing annotations
+        base: Statement,
+        @Suppress("InvalidNullabilityOverride") // JUnit missing annotations
+        description: Description
+    ): Statement {
+        return object : Statement() {
+            override fun evaluate() {
+                WindowSdkExtensions.overrideDecorator(
+                    object : WindowSdkExtensionsDecorator {
+                        override fun decorate(
+                            windowSdkExtensions: WindowSdkExtensions
+                        ): WindowSdkExtensions = fakeWindowSdkExtensions
+                    }
+                )
+                try {
+                    base.evaluate()
+                } finally {
+                    WindowSdkExtensions.reset()
+                }
+            }
+        }
+    }
+
+    /**
+     * Overrides the [WindowSdkExtensions.extensionVersion] for testing.
+     *
+     * @param version The extension version to override.
+     */
+    fun overrideExtensionVersion(@IntRange(from = 0) version: Int) {
+        fakeWindowSdkExtensions.overrideExtensionVersion(version)
+    }
+}
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
index 7c62b4f..e0c3480 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/ActivityStackTesting.kt
@@ -18,9 +18,6 @@
 package androidx.window.testing.embedding
 
 import android.app.Activity
-import android.os.Binder
-import androidx.annotation.RestrictTo
-import androidx.annotation.VisibleForTesting
 import androidx.window.embedding.ActivityStack
 
 /**
@@ -41,8 +38,3 @@
     activitiesInProcess: List<Activity> = emptyList(),
     isEmpty: Boolean = false,
 ): ActivityStack = ActivityStack(activitiesInProcess, isEmpty)
-
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@VisibleForTesting
-@JvmField
-val TEST_ACTIVITY_STACK_TOKEN = Binder()
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
index 5195f28..2cabe03 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/SplitInfoTesting.kt
@@ -17,7 +17,6 @@
 
 package androidx.window.testing.embedding
 
-import android.os.Binder
 import androidx.window.embedding.ActivityStack
 import androidx.window.embedding.SplitAttributes
 import androidx.window.embedding.SplitInfo
@@ -44,6 +43,8 @@
     secondActivityStack: ActivityStack = TestActivityStack(),
     splitAttributes: SplitAttributes = SplitAttributes.Builder().build(),
 ): SplitInfo =
-    SplitInfo(primaryActivityStack, secondActivityStack, splitAttributes, TEST_SPLIT_INFO_TOKEN)
-
-private val TEST_SPLIT_INFO_TOKEN = Binder()
+    SplitInfo(
+        primaryActivityStack,
+        secondActivityStack,
+        splitAttributes,
+    )
diff --git a/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt b/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt
index 473e3d9..dcd3a23 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/embedding/StubEmbeddingBackend.kt
@@ -17,17 +17,24 @@
 package androidx.window.testing.embedding
 
 import android.app.Activity
-import android.app.ActivityOptions
-import android.os.IBinder
+import android.os.Bundle
 import androidx.core.util.Consumer
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.embedding.ActivityStack
+import androidx.window.embedding.EmbeddedActivityWindowInfo
 import androidx.window.embedding.EmbeddingBackend
+import androidx.window.embedding.EmbeddingConfiguration
 import androidx.window.embedding.EmbeddingRule
+import androidx.window.embedding.OverlayAttributes
+import androidx.window.embedding.OverlayAttributesCalculatorParams
+import androidx.window.embedding.OverlayCreateParams
+import androidx.window.embedding.OverlayInfo
 import androidx.window.embedding.SplitAttributes
 import androidx.window.embedding.SplitAttributesCalculatorParams
 import androidx.window.embedding.SplitController
 import androidx.window.embedding.SplitController.SplitSupportStatus.Companion.SPLIT_UNAVAILABLE
 import androidx.window.embedding.SplitInfo
+import androidx.window.embedding.SplitPinRule
 import java.util.concurrent.Executor
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -149,6 +156,15 @@
     override fun isActivityEmbedded(activity: Activity): Boolean =
         embeddedActivities.contains(activity)
 
+    @OptIn(ExperimentalWindowApi::class)
+    override fun pinTopActivityStack(taskId: Int, splitPinRule: SplitPinRule): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    override fun unpinTopActivityStack(taskId: Int) {
+        TODO("Not yet implemented")
+    }
+
     override fun setSplitAttributesCalculator(
         calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
     ) {
@@ -164,13 +180,29 @@
     }
 
     override fun setLaunchingActivityStack(
-        options: ActivityOptions,
-        token: IBinder
-    ): ActivityOptions {
+        options: Bundle,
+        activityStack: ActivityStack,
+    ): Bundle {
         TODO("Not yet implemented")
     }
 
-    override fun invalidateTopVisibleSplitAttributes() {
+    override fun setOverlayCreateParams(
+        options: Bundle,
+        overlayCreateParams: OverlayCreateParams
+    ): Bundle {
+        TODO("Not yet implemented")
+    }
+
+    override fun finishActivityStacks(activityStacks: Set<ActivityStack>) {
+        TODO("Not yet implemented")
+    }
+
+    @OptIn(ExperimentalWindowApi::class)
+    override fun setEmbeddingConfiguration(embeddingConfig: EmbeddingConfiguration) {
+        TODO("Not yet implemented")
+    }
+
+    override fun invalidateVisibleActivityStacks() {
         TODO("Not yet implemented")
     }
 
@@ -178,6 +210,45 @@
         TODO("Not yet implemented")
     }
 
+    override fun setOverlayAttributesCalculator(
+        calculator: (OverlayAttributesCalculatorParams) -> OverlayAttributes
+    ) {
+        TODO("Not yet implemented")
+    }
+
+    override fun clearOverlayAttributesCalculator() {
+        TODO("Not yet implemented")
+    }
+
+    override fun updateOverlayAttributes(overlayTag: String, overlayAttributes: OverlayAttributes) {
+        TODO("Not yet implemented")
+    }
+
+    override fun addOverlayInfoCallback(
+        overlayTag: String,
+        executor: Executor,
+        overlayInfoCallback: Consumer<OverlayInfo>
+    ) {
+        TODO("Not yet implemented")
+    }
+
+    override fun removeOverlayInfoCallback(overlayInfoCallback: Consumer<OverlayInfo>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun addEmbeddedActivityWindowInfoCallbackForActivity(
+        activity: Activity,
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    ) {
+        TODO("Not yet implemented")
+    }
+
+    override fun removeEmbeddedActivityWindowInfoCallbackForActivity(
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    ) {
+        TODO("Not yet implemented")
+    }
+
     private fun validateRules(rules: Set<EmbeddingRule>) {
         val tags = HashSet<String>()
         rules.forEach { rule ->
diff --git a/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculator.kt b/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculator.kt
index f59e5ae..5d7d07a 100644
--- a/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculator.kt
+++ b/window/window-testing/src/main/java/androidx/window/testing/layout/StubWindowMetricsCalculator.kt
@@ -39,13 +39,13 @@
     override fun computeCurrentWindowMetrics(activity: Activity): WindowMetrics {
         val displayMetrics = activity.resources.displayMetrics
         val bounds = Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)
-        return WindowMetrics(bounds)
+        return WindowMetrics(bounds, density = displayMetrics.density)
     }
 
     override fun computeMaximumWindowMetrics(activity: Activity): WindowMetrics {
         val displayMetrics = activity.resources.displayMetrics
         val bounds = Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)
-        return WindowMetrics(bounds)
+        return WindowMetrics(bounds, density = displayMetrics.density)
     }
 
     // WindowManager#getDefaultDisplay is deprecated but we have this for compatibility with
@@ -53,9 +53,10 @@
     @Suppress("DEPRECATION")
     override fun computeCurrentWindowMetrics(@UiContext context: Context): WindowMetrics {
         val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+        val density = context.resources.displayMetrics.density
 
         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            Api30Impl.getWindowMetrics(wm)
+            Api30Impl.getWindowMetrics(wm, context)
         } else {
             val displaySize = Point()
             // We use getRealSize instead of getSize here because:
@@ -67,7 +68,7 @@
             //      getRealSize.
             wm.defaultDisplay.getRealSize(displaySize)
             val bounds = Rect(0, 0, displaySize.x, displaySize.y)
-            WindowMetrics(bounds)
+            WindowMetrics(bounds, density = density)
         }
     }
 
@@ -77,8 +78,14 @@
 
     @RequiresApi(Build.VERSION_CODES.R)
     private object Api30Impl {
-        fun getWindowMetrics(windowManager: WindowManager): WindowMetrics {
-            return WindowMetrics(windowManager.currentWindowMetrics.bounds)
+        fun getWindowMetrics(
+            windowManager: WindowManager,
+            @UiContext context: Context
+        ): WindowMetrics {
+            return WindowMetrics(
+                windowManager.currentWindowMetrics.bounds,
+                density = context.resources.displayMetrics.density
+            )
         }
     }
 }
diff --git a/window/window-testing/src/test/java/androidx/window/testing/WindowSdkExtensionsRuleTest.kt b/window/window-testing/src/test/java/androidx/window/testing/WindowSdkExtensionsRuleTest.kt
new file mode 100644
index 0000000..24dc179
--- /dev/null
+++ b/window/window-testing/src/test/java/androidx/window/testing/WindowSdkExtensionsRuleTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.testing
+
+import androidx.window.WindowSdkExtensions
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+
+/** Test class to verify [WindowSdkExtensionsRule] behaviors. */
+class WindowSdkExtensionsRuleTest {
+
+    @JvmField @Rule val rule = WindowSdkExtensionsRule()
+
+    /** Verifies the [WindowSdkExtensionsRule] behavior. */
+    @Test
+    fun testWindowSdkExtensionsRule() {
+        assertEquals(
+            "The WindowSdkExtensions.extensionVersion is 0 in unit test",
+            0,
+            WindowSdkExtensions.getInstance().extensionVersion
+        )
+
+        rule.overrideExtensionVersion(3)
+
+        assertEquals(3, WindowSdkExtensions.getInstance().extensionVersion)
+    }
+}
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java
index ca3d9c4..d4df0a3 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/ActivityStackTestingJavaTest.java
@@ -40,8 +40,10 @@
     public void testActivityStackDefaultValue() {
         final ActivityStack activityStack = TestActivityStack.createTestActivityStack();
 
-        assertEquals(new ActivityStack(Collections.emptyList(), false /* isEmpty */),
-                activityStack);
+        assertEquals(
+                new ActivityStack(Collections.emptyList(), false /* isEmpty */),
+                activityStack
+        );
     }
 
     /** Verifies {@link TestActivityStack} */
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java
index 27e70c26..a797045 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingJavaTest.java
@@ -52,7 +52,7 @@
 public class SplitAttributesCalculatorParamsTestingJavaTest {
     private static final Rect TEST_BOUNDS = new Rect(0, 0, 2000, 2000);
     private static final WindowMetrics TEST_METRICS = new WindowMetrics(TEST_BOUNDS,
-            WindowInsetsCompat.CONSUMED);
+            WindowInsetsCompat.CONSUMED, 1f /* density */);
     private static final SplitAttributes DEFAULT_SPLIT_ATTRIBUTES =
             new SplitAttributes.Builder().build();
     private static final SplitAttributes TABLETOP_HINGE_ATTRIBUTES = new SplitAttributes.Builder()
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt
index 2e90c35..1a4eed3 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitAttributesCalculatorParamsTestingTest.kt
@@ -102,7 +102,7 @@
 
     companion object {
         private val TEST_BOUNDS = Rect(0, 0, 2000, 2000)
-        private val TEST_METRICS = WindowMetrics(TEST_BOUNDS)
+        private val TEST_METRICS = WindowMetrics(TEST_BOUNDS, density = 1f)
         private val DEFAULT_SPLIT_ATTRIBUTES = SplitAttributes.Builder().build()
         private val TABLETOP_HINGE_ATTRIBUTES =
             SplitAttributes.Builder()
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitInfoTestingTest.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitInfoTestingTest.kt
index f005d0e..ef2f690 100644
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitInfoTestingTest.kt
+++ b/window/window-testing/src/test/java/androidx/window/testing/embedding/SplitInfoTestingTest.kt
@@ -16,14 +16,12 @@
 
 package androidx.window.testing.embedding
 
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.embedding.SplitAttributes
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.mockito.kotlin.mock
 
 /** Test class to verify [TestSplitInfo] */
-@OptIn(ExperimentalWindowApi::class)
 class SplitInfoTestingTest {
 
     /** Verifies the default value of [TestSplitInfo]. */
diff --git a/window/window-testing/src/test/java/androidx/window/testing/embedding/TestSplitInfo.kt b/window/window-testing/src/test/java/androidx/window/testing/embedding/TestSplitInfo.kt
deleted file mode 100644
index ca6efd7..0000000
--- a/window/window-testing/src/test/java/androidx/window/testing/embedding/TestSplitInfo.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.testing.embedding
-
-import android.app.Activity
-import android.os.Binder
-import android.os.IBinder
-import androidx.window.core.ExperimentalWindowApi
-import androidx.window.embedding.ActivityStack
-import androidx.window.embedding.SplitAttributes
-import androidx.window.embedding.SplitInfo
-
-/**
- * A convenience method to get a test [SplitInfo] with default values provided. With the default
- * values it returns an empty [ActivityStack] for the primary and secondary stacks. The default
- * [SplitAttributes] are for splitting equally and matching the locale layout.
- *
- * Note: This method should be used for testing local logic as opposed to end to end verification.
- * End to end verification requires a device that supports Activity Embedding.
- *
- * @param primaryActivity the [Activity] for the primary container.
- * @param secondaryActivity the [Activity] for the secondary container.
- * @param splitAttributes the [SplitAttributes].
- */
-@ExperimentalWindowApi
-fun TestSplitInfo(
-    primaryActivity: Activity,
-    secondaryActivity: Activity,
-    splitAttributes: SplitAttributes = SplitAttributes(),
-    token: IBinder = Binder()
-): SplitInfo {
-    val primaryActivityStack = TestActivityStack(primaryActivity, false)
-    val secondaryActivityStack = TestActivityStack(secondaryActivity, false)
-    return SplitInfo(primaryActivityStack, secondaryActivityStack, splitAttributes, token)
-}
-
-/**
- * A convenience method to get a test [ActivityStack] with default values provided. With the default
- * values, there will be a single [Activity] in the stack and it will be considered not empty.
- *
- * Note: This method should be used for testing local logic as opposed to end to end verification.
- * End to end verification requires a device that supports Activity Embedding.
- *
- * @param testActivity an [Activity] that should be considered in the stack
- * @param isEmpty states if the stack is empty or not. In practice an [ActivityStack] with a single
- *   [Activity] but [isEmpty] set to `false` means there is an [Activity] from outside the process
- *   in the stack.
- */
-@ExperimentalWindowApi
-fun TestActivityStack(
-    testActivity: Activity,
-    isEmpty: Boolean = true,
-): ActivityStack {
-    return ActivityStack(
-        listOf(testActivity),
-        isEmpty,
-    )
-}
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index f68c635..9931de5 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -13,6 +13,8 @@
     field public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
     field public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
     field public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
+    field public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE";
+    field public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE";
   }
 
   public abstract class WindowSdkExtensions {
@@ -107,8 +109,10 @@
 
   @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
     method public android.content.Context getContext();
+    method public android.view.Window? getWindow();
     method public void setContentView(android.view.View view);
     property public abstract android.content.Context context;
+    property public abstract android.view.Window? window;
   }
 
 }
@@ -123,9 +127,13 @@
 package androidx.window.embedding {
 
   public final class ActivityEmbeddingController {
-    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public kotlinx.coroutines.flow.Flow<androidx.window.embedding.EmbeddedActivityWindowInfo> embeddedActivityWindowInfo(android.app.Activity activity);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void finishActivityStacks(java.util.Set<androidx.window.embedding.ActivityStack> activityStacks);
+    method public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
     method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+    method @androidx.window.RequiresWindowSdkExtension(version=3) public void invalidateVisibleActivityStacks();
     method public boolean isActivityEmbedded(android.app.Activity activity);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void setEmbeddingConfiguration(androidx.window.embedding.EmbeddingConfiguration embeddingConfiguration);
     field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
   }
 
@@ -133,6 +141,10 @@
     method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
   }
 
+  public final class ActivityEmbeddingOptions {
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public static android.os.Bundle setLaunchingActivityStack(android.os.Bundle, android.content.Context context, androidx.window.embedding.ActivityStack activityStack);
+  }
+
   public final class ActivityFilter {
     ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
     method public android.content.ComponentName getComponentName();
@@ -163,6 +175,84 @@
     property public final boolean isEmpty;
   }
 
+  public abstract class DividerAttributes {
+    method public final int getColor();
+    method public final int getWidthDp();
+    property public final int color;
+    property public final int widthDp;
+    field public static final androidx.window.embedding.DividerAttributes.Companion Companion;
+    field public static final androidx.window.embedding.DividerAttributes NO_DIVIDER;
+    field public static final int WIDTH_SYSTEM_DEFAULT = -1; // 0xffffffff
+  }
+
+  public static final class DividerAttributes.Companion {
+  }
+
+  public abstract static class DividerAttributes.DragRange {
+    field public static final androidx.window.embedding.DividerAttributes.DragRange.Companion Companion;
+    field public static final androidx.window.embedding.DividerAttributes.DragRange DRAG_RANGE_SYSTEM_DEFAULT;
+  }
+
+  public static final class DividerAttributes.DragRange.Companion {
+  }
+
+  public static final class DividerAttributes.DragRange.SplitRatioDragRange extends androidx.window.embedding.DividerAttributes.DragRange {
+    ctor public DividerAttributes.DragRange.SplitRatioDragRange(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float minRatio, @FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float maxRatio);
+    method public float getMaxRatio();
+    method public float getMinRatio();
+    property public final float maxRatio;
+    property public final float minRatio;
+  }
+
+  public static final class DividerAttributes.DraggableDividerAttributes extends androidx.window.embedding.DividerAttributes {
+    method public androidx.window.embedding.DividerAttributes.DragRange getDragRange();
+    property public final androidx.window.embedding.DividerAttributes.DragRange dragRange;
+  }
+
+  @androidx.window.RequiresWindowSdkExtension(version=6) public static final class DividerAttributes.DraggableDividerAttributes.Builder {
+    ctor public DividerAttributes.DraggableDividerAttributes.Builder();
+    ctor @androidx.window.RequiresWindowSdkExtension(version=6) public DividerAttributes.DraggableDividerAttributes.Builder(androidx.window.embedding.DividerAttributes.DraggableDividerAttributes original);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes build();
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes.Builder setColor(@ColorInt int color);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes.Builder setDragRange(androidx.window.embedding.DividerAttributes.DragRange dragRange);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes.Builder setWidthDp(@IntRange(from=androidx.window.embedding.DividerAttributes.WIDTH_SYSTEM_DEFAULT.toLong()) int widthDp);
+  }
+
+  public static final class DividerAttributes.FixedDividerAttributes extends androidx.window.embedding.DividerAttributes {
+  }
+
+  @androidx.window.RequiresWindowSdkExtension(version=6) public static final class DividerAttributes.FixedDividerAttributes.Builder {
+    ctor public DividerAttributes.FixedDividerAttributes.Builder();
+    ctor @androidx.window.RequiresWindowSdkExtension(version=6) public DividerAttributes.FixedDividerAttributes.Builder(androidx.window.embedding.DividerAttributes.FixedDividerAttributes original);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.FixedDividerAttributes build();
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.FixedDividerAttributes.Builder setColor(@ColorInt int color);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.FixedDividerAttributes.Builder setWidthDp(@IntRange(from=androidx.window.embedding.DividerAttributes.WIDTH_SYSTEM_DEFAULT.toLong()) int widthDp);
+  }
+
+  public final class EmbeddedActivityWindowInfo {
+    method public android.graphics.Rect getBoundsInParentHost();
+    method public android.graphics.Rect getParentHostBounds();
+    method public boolean isEmbedded();
+    property public final android.graphics.Rect boundsInParentHost;
+    property public final boolean isEmbedded;
+    property public final android.graphics.Rect parentHostBounds;
+  }
+
+  public abstract class EmbeddingAnimationBackground {
+    method public static final androidx.window.embedding.EmbeddingAnimationBackground.ColorBackground createColorBackground(@ColorInt @IntRange(from=android.graphics.Color.BLACK.toLong(), to=android.graphics.Color.WHITE.toLong()) int color);
+    field public static final androidx.window.embedding.EmbeddingAnimationBackground.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingAnimationBackground DEFAULT;
+  }
+
+  public static final class EmbeddingAnimationBackground.ColorBackground extends androidx.window.embedding.EmbeddingAnimationBackground {
+    method public int getColor();
+    property public final int color;
+  }
+
+  public static final class EmbeddingAnimationBackground.Companion {
+    method public androidx.window.embedding.EmbeddingAnimationBackground.ColorBackground createColorBackground(@ColorInt @IntRange(from=android.graphics.Color.BLACK.toLong(), to=android.graphics.Color.WHITE.toLong()) int color);
+  }
+
   public final class EmbeddingAspectRatio {
     method public static androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
     field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_ALLOW;
@@ -174,6 +264,29 @@
     method public androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
   }
 
+  public final class EmbeddingConfiguration {
+    ctor public EmbeddingConfiguration();
+    ctor public EmbeddingConfiguration(optional @androidx.window.RequiresWindowSdkExtension(version=5) androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior dimAreaBehavior);
+    method public androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior getDimAreaBehavior();
+    property public final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior dimAreaBehavior;
+  }
+
+  public static final class EmbeddingConfiguration.Builder {
+    ctor public EmbeddingConfiguration.Builder();
+    method public androidx.window.embedding.EmbeddingConfiguration build();
+    method public androidx.window.embedding.EmbeddingConfiguration.Builder setDimAreaBehavior(androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior area);
+  }
+
+  public static final class EmbeddingConfiguration.DimAreaBehavior {
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior ON_ACTIVITY_STACK;
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior ON_TASK;
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior UNDEFINED;
+  }
+
+  public static final class EmbeddingConfiguration.DimAreaBehavior.Companion {
+  }
+
   public abstract class EmbeddingRule {
     method public final String? getTag();
     property public final String? tag;
@@ -196,8 +309,17 @@
   }
 
   public final class SplitAttributes {
+    ctor public SplitAttributes();
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType);
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType, optional androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection);
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType, optional androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection, optional androidx.window.embedding.EmbeddingAnimationBackground animationBackground);
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType, optional androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection, optional androidx.window.embedding.EmbeddingAnimationBackground animationBackground, optional androidx.window.embedding.DividerAttributes dividerAttributes);
+    method public androidx.window.embedding.EmbeddingAnimationBackground getAnimationBackground();
+    method public androidx.window.embedding.DividerAttributes getDividerAttributes();
     method public androidx.window.embedding.SplitAttributes.LayoutDirection getLayoutDirection();
     method public androidx.window.embedding.SplitAttributes.SplitType getSplitType();
+    property public final androidx.window.embedding.EmbeddingAnimationBackground animationBackground;
+    property public final androidx.window.embedding.DividerAttributes dividerAttributes;
     property public final androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection;
     property public final androidx.window.embedding.SplitAttributes.SplitType splitType;
     field public static final androidx.window.embedding.SplitAttributes.Companion Companion;
@@ -206,6 +328,8 @@
   public static final class SplitAttributes.Builder {
     ctor public SplitAttributes.Builder();
     method public androidx.window.embedding.SplitAttributes build();
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public androidx.window.embedding.SplitAttributes.Builder setAnimationBackground(androidx.window.embedding.EmbeddingAnimationBackground background);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.SplitAttributes.Builder setDividerAttributes(androidx.window.embedding.DividerAttributes dividerAttributes);
     method public androidx.window.embedding.SplitAttributes.Builder setLayoutDirection(androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection);
     method public androidx.window.embedding.SplitAttributes.Builder setSplitType(androidx.window.embedding.SplitAttributes.SplitType type);
   }
@@ -256,10 +380,11 @@
     method @androidx.window.RequiresWindowSdkExtension(version=2) public void clearSplitAttributesCalculator();
     method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
     method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
-    method @SuppressCompatibility @androidx.window.RequiresWindowSdkExtension(version=3) @androidx.window.core.ExperimentalWindowApi public void invalidateTopVisibleSplitAttributes();
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public boolean pinTopActivityStack(int taskId, androidx.window.embedding.SplitPinRule splitPinRule);
     method @androidx.window.RequiresWindowSdkExtension(version=2) public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
     method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
-    method @SuppressCompatibility @androidx.window.RequiresWindowSdkExtension(version=3) @androidx.window.core.ExperimentalWindowApi public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void unpinTopActivityStack(int taskId);
+    method @androidx.window.RequiresWindowSdkExtension(version=3) public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
     property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
     field public static final androidx.window.embedding.SplitController.Companion Companion;
   }
@@ -326,6 +451,24 @@
     method public androidx.window.embedding.SplitPairRule.Builder setTag(String? tag);
   }
 
+  public final class SplitPinRule extends androidx.window.embedding.SplitRule {
+    method public boolean isSticky();
+    property public final boolean isSticky;
+  }
+
+  public static final class SplitPinRule.Builder {
+    ctor public SplitPinRule.Builder();
+    method public androidx.window.embedding.SplitPinRule build();
+    method public androidx.window.embedding.SplitPinRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPinRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPinRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPinRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPinRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPinRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPinRule.Builder setSticky(boolean isSticky);
+    method public androidx.window.embedding.SplitPinRule.Builder setTag(String? tag);
+  }
+
   public final class SplitPlaceholderRule extends androidx.window.embedding.SplitRule {
     method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
     method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithPlaceholder();
@@ -431,10 +574,20 @@
   public static final class FoldingFeature.State.Companion {
   }
 
+  public final class SupportedPosture {
+    field public static final androidx.window.layout.SupportedPosture.Companion Companion;
+    field public static final androidx.window.layout.SupportedPosture TABLETOP;
+  }
+
+  public static final class SupportedPosture.Companion {
+  }
+
   public interface WindowInfoTracker {
     method public static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public default java.util.List<androidx.window.layout.SupportedPosture> getSupportedPostures();
     method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
     method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
+    property @androidx.window.RequiresWindowSdkExtension(version=6) public default java.util.List<androidx.window.layout.SupportedPosture> supportedPostures;
     field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
   }
 
@@ -449,8 +602,10 @@
 
   public final class WindowMetrics {
     method public android.graphics.Rect getBounds();
+    method public float getDensity();
     method @SuppressCompatibility @RequiresApi(android.os.Build.VERSION_CODES.R) @androidx.window.core.ExperimentalWindowApi public androidx.core.view.WindowInsetsCompat getWindowInsets();
     property public final android.graphics.Rect bounds;
+    property public final float density;
   }
 
   public interface WindowMetricsCalculator {
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index f68c635..6f55013 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -13,6 +13,8 @@
     field public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
     field public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
     field public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
+    field public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE";
+    field public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE";
   }
 
   public abstract class WindowSdkExtensions {
@@ -107,8 +109,10 @@
 
   @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
     method public android.content.Context getContext();
+    method public android.view.Window? getWindow();
     method public void setContentView(android.view.View view);
     property public abstract android.content.Context context;
+    property public abstract android.view.Window? window;
   }
 
 }
@@ -123,9 +127,13 @@
 package androidx.window.embedding {
 
   public final class ActivityEmbeddingController {
-    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public kotlinx.coroutines.flow.Flow<androidx.window.embedding.EmbeddedActivityWindowInfo> embeddedActivityWindowInfo(android.app.Activity activity);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void finishActivityStacks(java.util.Set<androidx.window.embedding.ActivityStack> activityStacks);
+    method public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
     method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+    method @androidx.window.RequiresWindowSdkExtension(version=3) public void invalidateVisibleActivityStacks();
     method public boolean isActivityEmbedded(android.app.Activity activity);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void setEmbeddingConfiguration(androidx.window.embedding.EmbeddingConfiguration embeddingConfiguration);
     field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
   }
 
@@ -133,6 +141,11 @@
     method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
   }
 
+  public final class ActivityEmbeddingOptions {
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public static android.os.Bundle setLaunchingActivityStack(android.os.Bundle, android.content.Context context, androidx.window.embedding.ActivityStack activityStack);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.window.RequiresWindowSdkExtension(version=androidx.window.embedding.OverlayController.OVERLAY_FEATURE_VERSION) public static android.os.Bundle setOverlayCreateParams(android.os.Bundle, android.app.Activity activity, androidx.window.embedding.OverlayCreateParams overlayCreateParams);
+  }
+
   public final class ActivityFilter {
     ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
     method public android.content.ComponentName getComponentName();
@@ -163,6 +176,84 @@
     property public final boolean isEmpty;
   }
 
+  public abstract class DividerAttributes {
+    method public final int getColor();
+    method public final int getWidthDp();
+    property public final int color;
+    property public final int widthDp;
+    field public static final androidx.window.embedding.DividerAttributes.Companion Companion;
+    field public static final androidx.window.embedding.DividerAttributes NO_DIVIDER;
+    field public static final int WIDTH_SYSTEM_DEFAULT = -1; // 0xffffffff
+  }
+
+  public static final class DividerAttributes.Companion {
+  }
+
+  public abstract static class DividerAttributes.DragRange {
+    field public static final androidx.window.embedding.DividerAttributes.DragRange.Companion Companion;
+    field public static final androidx.window.embedding.DividerAttributes.DragRange DRAG_RANGE_SYSTEM_DEFAULT;
+  }
+
+  public static final class DividerAttributes.DragRange.Companion {
+  }
+
+  public static final class DividerAttributes.DragRange.SplitRatioDragRange extends androidx.window.embedding.DividerAttributes.DragRange {
+    ctor public DividerAttributes.DragRange.SplitRatioDragRange(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float minRatio, @FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float maxRatio);
+    method public float getMaxRatio();
+    method public float getMinRatio();
+    property public final float maxRatio;
+    property public final float minRatio;
+  }
+
+  public static final class DividerAttributes.DraggableDividerAttributes extends androidx.window.embedding.DividerAttributes {
+    method public androidx.window.embedding.DividerAttributes.DragRange getDragRange();
+    property public final androidx.window.embedding.DividerAttributes.DragRange dragRange;
+  }
+
+  @androidx.window.RequiresWindowSdkExtension(version=6) public static final class DividerAttributes.DraggableDividerAttributes.Builder {
+    ctor public DividerAttributes.DraggableDividerAttributes.Builder();
+    ctor @androidx.window.RequiresWindowSdkExtension(version=6) public DividerAttributes.DraggableDividerAttributes.Builder(androidx.window.embedding.DividerAttributes.DraggableDividerAttributes original);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes build();
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes.Builder setColor(@ColorInt int color);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes.Builder setDragRange(androidx.window.embedding.DividerAttributes.DragRange dragRange);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.DraggableDividerAttributes.Builder setWidthDp(@IntRange(from=androidx.window.embedding.DividerAttributes.WIDTH_SYSTEM_DEFAULT.toLong()) int widthDp);
+  }
+
+  public static final class DividerAttributes.FixedDividerAttributes extends androidx.window.embedding.DividerAttributes {
+  }
+
+  @androidx.window.RequiresWindowSdkExtension(version=6) public static final class DividerAttributes.FixedDividerAttributes.Builder {
+    ctor public DividerAttributes.FixedDividerAttributes.Builder();
+    ctor @androidx.window.RequiresWindowSdkExtension(version=6) public DividerAttributes.FixedDividerAttributes.Builder(androidx.window.embedding.DividerAttributes.FixedDividerAttributes original);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.FixedDividerAttributes build();
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.FixedDividerAttributes.Builder setColor(@ColorInt int color);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.DividerAttributes.FixedDividerAttributes.Builder setWidthDp(@IntRange(from=androidx.window.embedding.DividerAttributes.WIDTH_SYSTEM_DEFAULT.toLong()) int widthDp);
+  }
+
+  public final class EmbeddedActivityWindowInfo {
+    method public android.graphics.Rect getBoundsInParentHost();
+    method public android.graphics.Rect getParentHostBounds();
+    method public boolean isEmbedded();
+    property public final android.graphics.Rect boundsInParentHost;
+    property public final boolean isEmbedded;
+    property public final android.graphics.Rect parentHostBounds;
+  }
+
+  public abstract class EmbeddingAnimationBackground {
+    method public static final androidx.window.embedding.EmbeddingAnimationBackground.ColorBackground createColorBackground(@ColorInt @IntRange(from=android.graphics.Color.BLACK.toLong(), to=android.graphics.Color.WHITE.toLong()) int color);
+    field public static final androidx.window.embedding.EmbeddingAnimationBackground.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingAnimationBackground DEFAULT;
+  }
+
+  public static final class EmbeddingAnimationBackground.ColorBackground extends androidx.window.embedding.EmbeddingAnimationBackground {
+    method public int getColor();
+    property public final int color;
+  }
+
+  public static final class EmbeddingAnimationBackground.Companion {
+    method public androidx.window.embedding.EmbeddingAnimationBackground.ColorBackground createColorBackground(@ColorInt @IntRange(from=android.graphics.Color.BLACK.toLong(), to=android.graphics.Color.WHITE.toLong()) int color);
+  }
+
   public final class EmbeddingAspectRatio {
     method public static androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
     field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_ALLOW;
@@ -174,11 +265,149 @@
     method public androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class EmbeddingBounds {
+    ctor public EmbeddingBounds(androidx.window.embedding.EmbeddingBounds.Alignment alignment, androidx.window.embedding.EmbeddingBounds.Dimension width, androidx.window.embedding.EmbeddingBounds.Dimension height);
+    method public androidx.window.embedding.EmbeddingBounds.Alignment getAlignment();
+    method public androidx.window.embedding.EmbeddingBounds.Dimension getHeight();
+    method public androidx.window.embedding.EmbeddingBounds.Dimension getWidth();
+    property public final androidx.window.embedding.EmbeddingBounds.Alignment alignment;
+    property public final androidx.window.embedding.EmbeddingBounds.Dimension height;
+    property public final androidx.window.embedding.EmbeddingBounds.Dimension width;
+    field public static final androidx.window.embedding.EmbeddingBounds BOUNDS_EXPANDED;
+    field public static final androidx.window.embedding.EmbeddingBounds BOUNDS_HINGE_BOTTOM;
+    field public static final androidx.window.embedding.EmbeddingBounds BOUNDS_HINGE_LEFT;
+    field public static final androidx.window.embedding.EmbeddingBounds BOUNDS_HINGE_RIGHT;
+    field public static final androidx.window.embedding.EmbeddingBounds BOUNDS_HINGE_TOP;
+    field public static final androidx.window.embedding.EmbeddingBounds.Companion Companion;
+  }
+
+  public static final class EmbeddingBounds.Alignment {
+    field public static final androidx.window.embedding.EmbeddingBounds.Alignment ALIGN_BOTTOM;
+    field public static final androidx.window.embedding.EmbeddingBounds.Alignment ALIGN_LEFT;
+    field public static final androidx.window.embedding.EmbeddingBounds.Alignment ALIGN_RIGHT;
+    field public static final androidx.window.embedding.EmbeddingBounds.Alignment ALIGN_TOP;
+    field public static final androidx.window.embedding.EmbeddingBounds.Alignment.Companion Companion;
+  }
+
+  public static final class EmbeddingBounds.Alignment.Companion {
+  }
+
+  public static final class EmbeddingBounds.Companion {
+  }
+
+  public abstract static class EmbeddingBounds.Dimension {
+    method public static final androidx.window.embedding.EmbeddingBounds.Dimension pixel(@IntRange(from=1L) @Px int value);
+    method public static final androidx.window.embedding.EmbeddingBounds.Dimension ratio(@FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float ratio);
+    field public static final androidx.window.embedding.EmbeddingBounds.Dimension.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingBounds.Dimension DIMENSION_EXPANDED;
+    field public static final androidx.window.embedding.EmbeddingBounds.Dimension DIMENSION_HINGE;
+  }
+
+  public static final class EmbeddingBounds.Dimension.Companion {
+    method public androidx.window.embedding.EmbeddingBounds.Dimension pixel(@IntRange(from=1L) @Px int value);
+    method public androidx.window.embedding.EmbeddingBounds.Dimension ratio(@FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float ratio);
+  }
+
+  public final class EmbeddingConfiguration {
+    ctor public EmbeddingConfiguration();
+    ctor public EmbeddingConfiguration(optional @androidx.window.RequiresWindowSdkExtension(version=5) androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior dimAreaBehavior);
+    method public androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior getDimAreaBehavior();
+    property public final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior dimAreaBehavior;
+  }
+
+  public static final class EmbeddingConfiguration.Builder {
+    ctor public EmbeddingConfiguration.Builder();
+    method public androidx.window.embedding.EmbeddingConfiguration build();
+    method public androidx.window.embedding.EmbeddingConfiguration.Builder setDimAreaBehavior(androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior area);
+  }
+
+  public static final class EmbeddingConfiguration.DimAreaBehavior {
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior ON_ACTIVITY_STACK;
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior ON_TASK;
+    field public static final androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior UNDEFINED;
+  }
+
+  public static final class EmbeddingConfiguration.DimAreaBehavior.Companion {
+  }
+
   public abstract class EmbeddingRule {
     method public final String? getTag();
     property public final String? tag;
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class OverlayAttributes {
+    ctor public OverlayAttributes();
+    ctor public OverlayAttributes(optional androidx.window.embedding.EmbeddingBounds bounds);
+    method public androidx.window.embedding.EmbeddingBounds getBounds();
+    property public final androidx.window.embedding.EmbeddingBounds bounds;
+  }
+
+  public static final class OverlayAttributes.Builder {
+    ctor public OverlayAttributes.Builder();
+    method public androidx.window.embedding.OverlayAttributes build();
+    method public androidx.window.embedding.OverlayAttributes.Builder setBounds(androidx.window.embedding.EmbeddingBounds bounds);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class OverlayAttributesCalculatorParams {
+    method public androidx.window.embedding.OverlayAttributes getDefaultOverlayAttributes();
+    method public String getOverlayTag();
+    method public android.content.res.Configuration getParentConfiguration();
+    method public androidx.window.layout.WindowLayoutInfo getParentWindowLayoutInfo();
+    method public androidx.window.layout.WindowMetrics getParentWindowMetrics();
+    property public final androidx.window.embedding.OverlayAttributes defaultOverlayAttributes;
+    property public final String overlayTag;
+    property public final android.content.res.Configuration parentConfiguration;
+    property public final androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo;
+    property public final androidx.window.layout.WindowMetrics parentWindowMetrics;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class OverlayController {
+    method @androidx.window.RequiresWindowSdkExtension(version=androidx.window.embedding.OverlayController.OVERLAY_FEATURE_VERSION) public void clearOverlayAttributesCalculator();
+    method public static androidx.window.embedding.OverlayController getInstance(android.content.Context context);
+    method @androidx.window.RequiresWindowSdkExtension(version=androidx.window.embedding.OverlayController.OVERLAY_FEATURE_VERSION) public kotlinx.coroutines.flow.Flow<androidx.window.embedding.OverlayInfo> overlayInfo(String overlayTag);
+    method @androidx.window.RequiresWindowSdkExtension(version=androidx.window.embedding.OverlayController.OVERLAY_FEATURE_VERSION) public void setOverlayAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.OverlayAttributesCalculatorParams,androidx.window.embedding.OverlayAttributes> calculator);
+    method @androidx.window.RequiresWindowSdkExtension(version=androidx.window.embedding.OverlayController.OVERLAY_FEATURE_VERSION) public void updateOverlayAttributes(String overlayTag, androidx.window.embedding.OverlayAttributes overlayAttributes);
+    field public static final androidx.window.embedding.OverlayController.Companion Companion;
+  }
+
+  public static final class OverlayController.Companion {
+    method public androidx.window.embedding.OverlayController getInstance(android.content.Context context);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class OverlayCreateParams {
+    ctor public OverlayCreateParams();
+    ctor public OverlayCreateParams(optional String tag);
+    ctor public OverlayCreateParams(optional String tag, optional androidx.window.embedding.OverlayAttributes overlayAttributes);
+    method public static String generateOverlayTag();
+    method public androidx.window.embedding.OverlayAttributes getOverlayAttributes();
+    method public String getTag();
+    property public final androidx.window.embedding.OverlayAttributes overlayAttributes;
+    property public final String tag;
+    field public static final androidx.window.embedding.OverlayCreateParams.Companion Companion;
+  }
+
+  public static final class OverlayCreateParams.Builder {
+    ctor public OverlayCreateParams.Builder();
+    method public androidx.window.embedding.OverlayCreateParams build();
+    method public androidx.window.embedding.OverlayCreateParams.Builder setOverlayAttributes(androidx.window.embedding.OverlayAttributes attrs);
+    method public androidx.window.embedding.OverlayCreateParams.Builder setTag(String tag);
+  }
+
+  public static final class OverlayCreateParams.Companion {
+    method public String generateOverlayTag();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class OverlayInfo {
+    method public operator boolean contains(android.app.Activity activity);
+    method public androidx.window.embedding.ActivityStack? getActivityStack();
+    method public androidx.window.embedding.OverlayAttributes? getCurrentOverlayAttributes();
+    method public String getOverlayTag();
+    property public final androidx.window.embedding.ActivityStack? activityStack;
+    property public final androidx.window.embedding.OverlayAttributes? currentOverlayAttributes;
+    property public final String overlayTag;
+  }
+
   public final class RuleController {
     method public void addRule(androidx.window.embedding.EmbeddingRule rule);
     method public void clearRules();
@@ -196,8 +425,17 @@
   }
 
   public final class SplitAttributes {
+    ctor public SplitAttributes();
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType);
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType, optional androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection);
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType, optional androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection, optional androidx.window.embedding.EmbeddingAnimationBackground animationBackground);
+    ctor public SplitAttributes(optional androidx.window.embedding.SplitAttributes.SplitType splitType, optional androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection, optional androidx.window.embedding.EmbeddingAnimationBackground animationBackground, optional androidx.window.embedding.DividerAttributes dividerAttributes);
+    method public androidx.window.embedding.EmbeddingAnimationBackground getAnimationBackground();
+    method public androidx.window.embedding.DividerAttributes getDividerAttributes();
     method public androidx.window.embedding.SplitAttributes.LayoutDirection getLayoutDirection();
     method public androidx.window.embedding.SplitAttributes.SplitType getSplitType();
+    property public final androidx.window.embedding.EmbeddingAnimationBackground animationBackground;
+    property public final androidx.window.embedding.DividerAttributes dividerAttributes;
     property public final androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection;
     property public final androidx.window.embedding.SplitAttributes.SplitType splitType;
     field public static final androidx.window.embedding.SplitAttributes.Companion Companion;
@@ -206,6 +444,8 @@
   public static final class SplitAttributes.Builder {
     ctor public SplitAttributes.Builder();
     method public androidx.window.embedding.SplitAttributes build();
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public androidx.window.embedding.SplitAttributes.Builder setAnimationBackground(androidx.window.embedding.EmbeddingAnimationBackground background);
+    method @androidx.window.RequiresWindowSdkExtension(version=6) public androidx.window.embedding.SplitAttributes.Builder setDividerAttributes(androidx.window.embedding.DividerAttributes dividerAttributes);
     method public androidx.window.embedding.SplitAttributes.Builder setLayoutDirection(androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection);
     method public androidx.window.embedding.SplitAttributes.Builder setSplitType(androidx.window.embedding.SplitAttributes.SplitType type);
   }
@@ -256,10 +496,11 @@
     method @androidx.window.RequiresWindowSdkExtension(version=2) public void clearSplitAttributesCalculator();
     method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
     method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
-    method @SuppressCompatibility @androidx.window.RequiresWindowSdkExtension(version=3) @androidx.window.core.ExperimentalWindowApi public void invalidateTopVisibleSplitAttributes();
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public boolean pinTopActivityStack(int taskId, androidx.window.embedding.SplitPinRule splitPinRule);
     method @androidx.window.RequiresWindowSdkExtension(version=2) public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
     method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
-    method @SuppressCompatibility @androidx.window.RequiresWindowSdkExtension(version=3) @androidx.window.core.ExperimentalWindowApi public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
+    method @androidx.window.RequiresWindowSdkExtension(version=5) public void unpinTopActivityStack(int taskId);
+    method @androidx.window.RequiresWindowSdkExtension(version=3) public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
     property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
     field public static final androidx.window.embedding.SplitController.Companion Companion;
   }
@@ -326,6 +567,24 @@
     method public androidx.window.embedding.SplitPairRule.Builder setTag(String? tag);
   }
 
+  public final class SplitPinRule extends androidx.window.embedding.SplitRule {
+    method public boolean isSticky();
+    property public final boolean isSticky;
+  }
+
+  public static final class SplitPinRule.Builder {
+    ctor public SplitPinRule.Builder();
+    method public androidx.window.embedding.SplitPinRule build();
+    method public androidx.window.embedding.SplitPinRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPinRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPinRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPinRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPinRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPinRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPinRule.Builder setSticky(boolean isSticky);
+    method public androidx.window.embedding.SplitPinRule.Builder setTag(String? tag);
+  }
+
   public final class SplitPlaceholderRule extends androidx.window.embedding.SplitRule {
     method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
     method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithPlaceholder();
@@ -431,10 +690,20 @@
   public static final class FoldingFeature.State.Companion {
   }
 
+  public final class SupportedPosture {
+    field public static final androidx.window.layout.SupportedPosture.Companion Companion;
+    field public static final androidx.window.layout.SupportedPosture TABLETOP;
+  }
+
+  public static final class SupportedPosture.Companion {
+  }
+
   public interface WindowInfoTracker {
     method public static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public default java.util.List<androidx.window.layout.SupportedPosture> getSupportedPostures();
     method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
     method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
+    property @androidx.window.RequiresWindowSdkExtension(version=6) public default java.util.List<androidx.window.layout.SupportedPosture> supportedPostures;
     field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
   }
 
@@ -449,8 +718,10 @@
 
   public final class WindowMetrics {
     method public android.graphics.Rect getBounds();
+    method public float getDensity();
     method @SuppressCompatibility @RequiresApi(android.os.Build.VERSION_CODES.R) @androidx.window.core.ExperimentalWindowApi public androidx.core.view.WindowInsetsCompat getWindowInsets();
     property public final android.graphics.Rect bounds;
+    property public final float density;
   }
 
   public interface WindowMetricsCalculator {
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 90908f3..6044e67 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -49,18 +49,17 @@
 dependencies {
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesAndroid)
-    implementation("androidx.annotation:annotation:1.3.0")
+    implementation("androidx.annotation:annotation:1.8.1")
     implementation("androidx.collection:collection:1.1.0")
     implementation("androidx.core:core:1.8.0")
 
     def extensions_core_version = "androidx.window.extensions.core:core:1.0.0"
-    def extensions_version = "androidx.window.extensions:extensions:1.2.0"
+    def extensions_version = project(":window:extensions:extensions")
     // A compile only dependency on extnensions.core so that other libraries do not expose it
     // transitively.
     compileOnly(extensions_core_version)
-    // A compile only dependency on extnensions.core so that other libraries do not expose it
-    // transitively. The testCompile is added because tests are not getting the dependency
-    // transitively.
+    // Test implementation is required since extensions:core is on device. So it is required to
+    // import it in some form. For the library it will be available on device.
     testImplementation(extensions_core_version)
     // A compile only dependency on extnensions.core so that other libraries do not expose it
     // transitively. The androidTestCompile is added because tests are not getting the dependency
@@ -79,7 +78,6 @@
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(extensions_version)
     testImplementation(compileOnly(project(":window:sidecar:sidecar")))
-    testImplementation(compileOnly(extensions_version))
 
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.kotlinTestJunit)
@@ -103,7 +101,6 @@
     inceptionYear = "2020"
     description = "WindowManager Jetpack library. Currently only provides additional " +
             "functionality on foldable devices."
-    metalavaK2UastEnabled = true
     // Suppressing deprecation warnings, since there is a need to maintain compatibility with old
     // Sidecar interface.
     failOnDeprecationWarnings = false
diff --git a/window/window/samples/src/main/java/androidx.window.samples.embedding/FinishActivityStacksSamples.kt b/window/window/samples/src/main/java/androidx.window.samples.embedding/FinishActivityStacksSamples.kt
new file mode 100644
index 0000000..615adab
--- /dev/null
+++ b/window/window/samples/src/main/java/androidx.window.samples.embedding/FinishActivityStacksSamples.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.samples.embedding
+
+import android.app.Activity
+import androidx.annotation.Sampled
+import androidx.window.embedding.ActivityEmbeddingController
+import androidx.window.embedding.SplitController
+
+@Sampled
+suspend fun expandPrimaryContainer() {
+    SplitController.getInstance(primaryActivity).splitInfoList(primaryActivity).collect {
+        splitInfoList ->
+        // Find all associated secondary ActivityStacks
+        val associatedSecondaryActivityStacks =
+            splitInfoList.mapTo(mutableSetOf()) { splitInfo -> splitInfo.secondaryActivityStack }
+        // Finish them all.
+        ActivityEmbeddingController.getInstance(primaryActivity)
+            .finishActivityStacks(associatedSecondaryActivityStacks)
+    }
+}
+
+val primaryActivity = Activity()
diff --git a/window/window/samples/src/main/java/androidx.window.samples.embedding/LaunchingActivityStackSample.kt b/window/window/samples/src/main/java/androidx.window.samples.embedding/LaunchingActivityStackSample.kt
new file mode 100644
index 0000000..be84e59
--- /dev/null
+++ b/window/window/samples/src/main/java/androidx.window.samples.embedding/LaunchingActivityStackSample.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.samples.embedding
+
+import android.app.Activity
+import androidx.annotation.Sampled
+import androidx.core.app.ActivityOptionsCompat
+import androidx.window.embedding.ActivityStack
+import androidx.window.embedding.OverlayController
+import androidx.window.embedding.SplitController
+import androidx.window.embedding.setLaunchingActivityStack
+
+@Sampled
+suspend fun launchingOnPrimaryActivityStack() {
+    var primaryActivityStack: ActivityStack? = null
+
+    SplitController.getInstance(primaryActivity).splitInfoList(primaryActivity).collect {
+        splitInfoList ->
+        primaryActivityStack = splitInfoList.last().primaryActivityStack
+    }
+
+    primaryActivity.startActivity(
+        INTENT,
+        ActivityOptionsCompat.makeBasic()
+            .toBundle()!!
+            .setLaunchingActivityStack(primaryActivity, primaryActivityStack!!)
+    )
+}
+
+@Sampled
+suspend fun launchingOnOverlayActivityStack() {
+    var overlayActivityStack: ActivityStack? = null
+
+    OverlayController.getInstance(context).overlayInfo(TAG_OVERLAY).collect { overlayInfo ->
+        overlayActivityStack = overlayInfo.activityStack
+    }
+
+    // The use case is to launch an Activity to an existing overlay ActivityStack from the overlain
+    // Activity. If activityStack is not specified, the activity is launched to the top of the
+    // host task behind the overlay ActivityStack.
+    overlainActivity.startActivity(
+        INTENT,
+        ActivityOptionsCompat.makeBasic()
+            .toBundle()!!
+            .setLaunchingActivityStack(overlainActivity, overlayActivityStack!!)
+    )
+}
+
+const val TAG_OVERLAY = "overlay"
+val overlainActivity = Activity()
diff --git a/window/window/samples/src/main/java/androidx.window.samples.embedding/OverlaySamples.kt b/window/window/samples/src/main/java/androidx.window.samples.embedding/OverlaySamples.kt
new file mode 100644
index 0000000..b4e9b12
--- /dev/null
+++ b/window/window/samples/src/main/java/androidx.window.samples.embedding/OverlaySamples.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.samples.embedding
+
+import android.app.Activity
+import android.content.Intent
+import android.graphics.Rect
+import androidx.annotation.Sampled
+import androidx.core.app.ActivityOptionsCompat
+import androidx.window.embedding.EmbeddingBounds
+import androidx.window.embedding.EmbeddingBounds.Dimension.Companion.ratio
+import androidx.window.embedding.OverlayAttributes
+import androidx.window.embedding.OverlayController
+import androidx.window.embedding.OverlayCreateParams
+import androidx.window.embedding.setOverlayCreateParams
+
+@Sampled
+fun launchOverlayActivityStackSample() {
+    // Creates an overlay container on the right
+    val params =
+        OverlayCreateParams(
+            overlayAttributes =
+                OverlayAttributes(
+                    EmbeddingBounds(
+                        alignment = EmbeddingBounds.Alignment.ALIGN_RIGHT,
+                        width = ratio(0.5f),
+                        height = EmbeddingBounds.Dimension.DIMENSION_EXPANDED,
+                    )
+                )
+        )
+
+    val optionsWithOverlayParams =
+        ActivityOptionsCompat.makeBasic()
+            .toBundle()
+            ?.setOverlayCreateParams(launchingActivity, params)
+
+    // Start INTENT to the overlay container specified by params.
+    launchingActivity.startActivity(INTENT, optionsWithOverlayParams)
+}
+
+@Sampled
+fun overlayAttributesCalculatorSample() {
+    // A sample to show overlay on the bottom if the device is portrait, and on the right when
+    // the device is landscape.
+    OverlayController.getInstance(launchingActivity).setOverlayAttributesCalculator { params ->
+        val taskBounds = params.parentWindowMetrics.bounds
+        return@setOverlayAttributesCalculator OverlayAttributes(
+            if (taskBounds.isPortrait()) {
+                EmbeddingBounds(
+                    alignment = EmbeddingBounds.Alignment.ALIGN_BOTTOM,
+                    width = EmbeddingBounds.Dimension.DIMENSION_EXPANDED,
+                    height = ratio(0.5f),
+                )
+            } else {
+                EmbeddingBounds(
+                    alignment = EmbeddingBounds.Alignment.ALIGN_RIGHT,
+                    width = ratio(0.5f),
+                    height = EmbeddingBounds.Dimension.DIMENSION_EXPANDED,
+                )
+            }
+        )
+    }
+}
+
+private fun Rect.isPortrait(): Boolean = height() >= width()
+
+val launchingActivity: Activity = Activity()
+
+val INTENT = Intent()
diff --git a/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt b/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt
index ecf1f67..2df2387 100644
--- a/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt
+++ b/window/window/samples/src/main/java/androidx.window.samples.embedding/SplitAttributesCalculatorSamples.kt
@@ -17,7 +17,9 @@
 package androidx.window.samples.embedding
 
 import android.app.Application
+import android.graphics.Color
 import androidx.annotation.Sampled
+import androidx.window.embedding.EmbeddingAnimationBackground
 import androidx.window.embedding.SplitAttributes
 import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EQUAL
 import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EXPAND
@@ -57,6 +59,11 @@
                         SplitAttributes.LayoutDirection.LOCALE
                     }
                 )
+                // Optionally set the animation background to use when switching between
+                // vertical and horizontal
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GRAY)
+                )
                 .build()
         }
         return@setSplitAttributesCalculator if (
@@ -66,6 +73,11 @@
             SplitAttributes.Builder()
                 .setSplitType(SPLIT_TYPE_EQUAL)
                 .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                // Optionally set the animation background to use when switching between
+                // vertical and horizontal
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GRAY)
+                )
                 .build()
         } else {
             // Expand containers if the device is in portrait or the width is less than 600 dp.
@@ -84,12 +96,20 @@
         return@setSplitAttributesCalculator if (parentConfiguration.screenWidthDp >= 600) {
             builder
                 .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
-                // Set the color to use when switching between vertical and horizontal
+                // Optionally set the animation background to use when switching between
+                // vertical and horizontal
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GRAY)
+                )
                 .build()
         } else if (parentConfiguration.screenHeightDp >= 600) {
             builder
                 .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
-                // Set the color to use when switching between vertical and horizontal
+                // Optionally set the animation background to use when switching between
+                // vertical and horizontal
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GRAY)
+                )
                 .build()
         } else {
             // Fallback to expand the secondary container
diff --git a/window/window/src/androidTest/AndroidManifest.xml b/window/window/src/androidTest/AndroidManifest.xml
index e0d60f16..2393bc6 100644
--- a/window/window/src/androidTest/AndroidManifest.xml
+++ b/window/window/src/androidTest/AndroidManifest.xml
@@ -42,5 +42,13 @@
             android:name=
                 "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"
             android:value="false" />
+        <property
+            android:name=
+                "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"
+            android:value="false" />
+        <property
+            android:name=
+                "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE"
+            android:value="false" />
     </application>
 </manifest>
diff --git a/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt b/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt
index 1995125..54c87a3 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/WindowPropertiesTest.kt
@@ -119,6 +119,42 @@
         }
     }
 
+    @Test
+    fun test_property_allow_user_aspect_ratio_override() {
+        assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            // No-op, but to suppress lint
+            return
+        }
+        activityRule.scenario.onActivity { activity ->
+            // Should be false as defined in AndroidManifest.xml
+            assertFalse(
+                getProperty(
+                    activity,
+                    WindowProperties.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE
+                )
+            )
+        }
+    }
+
+    @Test
+    fun test_property_allow_user_aspect_ratio_fullscreen_override() {
+        assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            // No-op, but to suppress lint
+            return
+        }
+        activityRule.scenario.onActivity { activity ->
+            // Should be false as defined in AndroidManifest.xml
+            assertFalse(
+                getProperty(
+                    activity,
+                    WindowProperties.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE
+                )
+            )
+        }
+    }
+
     @RequiresApi(Build.VERSION_CODES.S)
     @Throws(PackageManager.NameNotFoundException::class)
     private fun getProperty(context: Context, propertyName: String): Boolean {
diff --git a/window/window/src/androidTest/java/androidx/window/WindowTestUtils.kt b/window/window/src/androidTest/java/androidx/window/WindowTestUtils.kt
index ca278ee..6199b64 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowTestUtils.kt
+++ b/window/window/src/androidTest/java/androidx/window/WindowTestUtils.kt
@@ -1,5 +1,6 @@
 package androidx.window
 
+import android.app.Activity
 import android.app.Application
 import android.content.Context
 import android.hardware.display.DisplayManager
@@ -7,7 +8,10 @@
 import android.view.Display
 import android.view.WindowManager
 import androidx.annotation.RequiresApi
+import androidx.lifecycle.Lifecycle
+import androidx.test.core.app.ActivityScenario
 import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.rules.ActivityScenarioRule
 import org.junit.Assume.assumeTrue
 
 open class WindowTestUtils {
@@ -28,17 +32,59 @@
                 )
         }
 
-        @OptIn(androidx.window.core.ExperimentalWindowApi::class)
         fun assumeAtLeastVendorApiLevel(min: Int) {
-            val version = WindowSdkExtensions.getInstance().extensionVersion
-            assumeTrue(version >= min)
+            val apiLevel = WindowSdkExtensions.getInstance().extensionVersion
+            assumeTrue(apiLevel >= min)
         }
 
-        @OptIn(androidx.window.core.ExperimentalWindowApi::class)
         fun assumeBeforeVendorApiLevel(max: Int) {
-            val version = WindowSdkExtensions.getInstance().extensionVersion
-            assumeTrue(version < max)
-            assumeTrue(version > 0)
+            val apiLevel = WindowSdkExtensions.getInstance().extensionVersion
+            assumeTrue(apiLevel < max)
+            assumeTrue(apiLevel > 0)
+        }
+
+        fun isInMultiWindowMode(activity: Activity): Boolean {
+            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                activity.isInMultiWindowMode
+            } else false
+        }
+
+        fun assumePlatformBeforeR() {
+            assumeTrue(Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
+        }
+
+        fun assumePlatformROrAbove() {
+            assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+        }
+
+        fun assumePlatformBeforeU() {
+            assumeTrue(Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        }
+
+        fun assumePlatformUOrAbove() {
+            assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        }
+
+        /**
+         * Creates and launches an activity performing the supplied actions at various points in the
+         * activity lifecycle.
+         *
+         * @param initialAction the action that will run once before the activity is created.
+         * @param verifyAction the action to run once after each change in activity lifecycle state.
+         */
+        fun runActionsAcrossActivityLifecycle(
+            scenarioRule: ActivityScenarioRule<TestActivity>,
+            initialAction: ActivityScenario.ActivityAction<TestActivity>,
+            verifyAction: ActivityScenario.ActivityAction<TestActivity>
+        ) {
+            val scenario = scenarioRule.scenario
+            scenario.onActivity(initialAction)
+            scenario.moveToState(Lifecycle.State.CREATED)
+            scenario.onActivity(verifyAction)
+            scenario.moveToState(Lifecycle.State.STARTED)
+            scenario.onActivity(verifyAction)
+            scenario.moveToState(Lifecycle.State.RESUMED)
+            scenario.onActivity(verifyAction)
         }
     }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/area/SafeWindowAreaComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/area/SafeWindowAreaComponentProviderTest.kt
index 3d658dc..76daa1b 100644
--- a/window/window/src/androidTest/java/androidx/window/area/SafeWindowAreaComponentProviderTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/area/SafeWindowAreaComponentProviderTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.WindowExtensionsProvider
+import kotlin.test.assertNotNull
 import org.junit.Assert.assertNull
 import org.junit.Assume.assumeTrue
 import org.junit.Test
@@ -35,15 +36,15 @@
      */
     @Test
     fun windowAreaComponentIsAvailable_ifProviderIsAvailable() {
-        assumeTrue(ExtensionsUtil.safeVendorApiLevel >= 2)
+        assumeTrue(ExtensionsUtil.safeVendorApiLevel >= 3)
         val loader = SafeWindowAreaComponentProvider::class.java.classLoader!!
         val safeComponent = SafeWindowAreaComponentProvider(loader).windowAreaComponent
 
         try {
             val extensions = WindowExtensionsProvider.getWindowExtensions()
             val actualComponent = extensions.windowAreaComponent
-            if (actualComponent == null) {
-                assertNull(safeComponent)
+            if (actualComponent != null) {
+                assertNotNull(safeComponent)
             }
             // TODO(b/267831038): verify upon each api level
             // TODO(b/267708462): more reliable test for testing actual method matching
diff --git a/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt b/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
index 21ecaa1..514ab50 100644
--- a/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
@@ -23,6 +23,7 @@
 import android.os.Build
 import android.util.DisplayMetrics
 import android.view.View
+import android.view.Window
 import android.widget.TextView
 import androidx.annotation.RequiresApi
 import androidx.test.ext.junit.rules.ActivityScenarioRule
@@ -84,11 +85,7 @@
             assumeAtLeastVendorApiLevel(minVendorApiLevel)
             activityScenario.scenario.onActivity {
                 val extensionComponent = FakeWindowAreaComponent()
-                val controller =
-                    WindowAreaControllerImpl(
-                        windowAreaComponent = extensionComponent,
-                        vendorApiLevel = FEATURE_VENDOR_API_LEVEL
-                    )
+                val controller = WindowAreaControllerImpl(windowAreaComponent = extensionComponent)
                 extensionComponent.currentRearDisplayStatus = STATUS_UNAVAILABLE
                 extensionComponent.currentRearDisplayPresentationStatus = STATUS_UNAVAILABLE
                 val collector = TestWindowAreaInfoListConsumer()
@@ -168,11 +165,7 @@
         testScope.runTest {
             assumeAtLeastVendorApiLevel(minVendorApiLevel)
             val extensions = FakeWindowAreaComponent()
-            val controller =
-                WindowAreaControllerImpl(
-                    windowAreaComponent = extensions,
-                    vendorApiLevel = FEATURE_VENDOR_API_LEVEL
-                )
+            val controller = WindowAreaControllerImpl(windowAreaComponent = extensions)
             extensions.currentRearDisplayStatus = STATUS_AVAILABLE
             val callback = TestWindowAreaSessionCallback()
             val windowAreaInfo: WindowAreaInfo? =
@@ -243,11 +236,7 @@
         testScope.runTest {
             assumeAtLeastVendorApiLevel(minVendorApiLevel)
             val extensions = FakeWindowAreaComponent()
-            val controller =
-                WindowAreaControllerImpl(
-                    windowAreaComponent = extensions,
-                    vendorApiLevel = FEATURE_VENDOR_API_LEVEL
-                )
+            val controller = WindowAreaControllerImpl(windowAreaComponent = extensions)
             extensions.currentRearDisplayStatus = initialState
             val callback = TestWindowAreaSessionCallback()
             val windowAreaInfo: WindowAreaInfo? =
@@ -291,11 +280,7 @@
         testScope.runTest {
             assumeAtLeastVendorApiLevel(minVendorApiLevel)
             val extensions = FakeWindowAreaComponent()
-            val controller =
-                WindowAreaControllerImpl(
-                    windowAreaComponent = extensions,
-                    vendorApiLevel = FEATURE_VENDOR_API_LEVEL
-                )
+            val controller = WindowAreaControllerImpl(windowAreaComponent = extensions)
 
             extensions.updateRearDisplayStatusListeners(STATUS_AVAILABLE)
             extensions.updateRearDisplayPresentationStatusListeners(STATUS_AVAILABLE)
@@ -345,8 +330,7 @@
         testScope.runTest {
             assumeAtLeastVendorApiLevel(minVendorApiLevel)
             val extensions = FakeWindowAreaComponent()
-            val controller =
-                WindowAreaControllerImpl(windowAreaComponent = extensions, vendorApiLevel = 3)
+            val controller = WindowAreaControllerImpl(windowAreaComponent = extensions)
 
             extensions.updateRearDisplayStatusListeners(STATUS_AVAILABLE)
             extensions.updateRearDisplayPresentationStatusListeners(STATUS_AVAILABLE)
@@ -366,8 +350,7 @@
             }
 
             // Create a new controller to start the presentation.
-            val controller2 =
-                WindowAreaControllerImpl(windowAreaComponent = extensions, vendorApiLevel = 3)
+            val controller2 = WindowAreaControllerImpl(windowAreaComponent = extensions)
 
             val callback = TestWindowAreaPresentationSessionCallback()
             activityScenario.scenario.onActivity { testActivity ->
@@ -401,8 +384,7 @@
         testScope.runTest {
             assumeAtLeastVendorApiLevel(minVendorApiLevel)
             val extensions = FakeWindowAreaComponent()
-            val controller =
-                WindowAreaControllerImpl(windowAreaComponent = extensions, vendorApiLevel = 3)
+            val controller = WindowAreaControllerImpl(windowAreaComponent = extensions)
             extensions.currentRearDisplayStatus = STATUS_AVAILABLE
             val callback = TestWindowAreaSessionCallback()
             val windowAreaInfo =
@@ -426,8 +408,7 @@
             }
 
             // Create a new controller to start the transfer.
-            val controller2 =
-                WindowAreaControllerImpl(windowAreaComponent = extensions, vendorApiLevel = 3)
+            val controller2 = WindowAreaControllerImpl(windowAreaComponent = extensions)
 
             activityScenario.scenario.onActivity { testActivity ->
                 assert(
@@ -464,11 +445,7 @@
         testScope.runTest {
             assumeAtLeastVendorApiLevel(minVendorApiLevel)
             val extensionComponent = FakeWindowAreaComponent()
-            val controller =
-                WindowAreaControllerImpl(
-                    windowAreaComponent = extensionComponent,
-                    vendorApiLevel = FEATURE_VENDOR_API_LEVEL
-                )
+            val controller = WindowAreaControllerImpl(windowAreaComponent = extensionComponent)
 
             extensionComponent.updateRearDisplayStatusListeners(STATUS_AVAILABLE)
             extensionComponent.updateRearDisplayPresentationStatusListeners(STATUS_UNAVAILABLE)
@@ -580,7 +557,7 @@
             rearDisplayPresentationSessionConsumer?.accept(SESSION_STATE_INACTIVE)
         }
 
-        override fun getRearDisplayPresentation(): ExtensionWindowAreaPresentation? {
+        override fun getRearDisplayPresentation(): ExtensionWindowAreaPresentation {
             return TestExtensionWindowAreaPresentation(
                 testActivity!!,
                 rearDisplayPresentationSessionConsumer!!
@@ -675,11 +652,13 @@
         override fun setPresentationView(view: View) {
             sessionConsumer.accept(WindowAreaComponent.SESSION_STATE_CONTENT_VISIBLE)
         }
+
+        override fun getWindow(): Window {
+            return activity.window
+        }
     }
 
     companion object {
         private const val REAR_FACING_BINDER_DESCRIPTION = "TEST_WINDOW_AREA_REAR_FACING"
-
-        private const val FEATURE_VENDOR_API_LEVEL = 3
     }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/area/reflectionguard/WindowAreaComponentValidatorTest.kt b/window/window/src/androidTest/java/androidx/window/area/reflectionguard/WindowAreaComponentValidatorTest.kt
index 2f3a207..a54dc60a 100644
--- a/window/window/src/androidTest/java/androidx/window/area/reflectionguard/WindowAreaComponentValidatorTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/area/reflectionguard/WindowAreaComponentValidatorTest.kt
@@ -37,33 +37,28 @@
         assertTrue(
             WindowAreaComponentValidator.isWindowAreaComponentValid(
                 WindowAreaComponentFullImplementation::class.java,
-                2
-            )
-        )
-        assertTrue(
-            WindowAreaComponentValidator.isWindowAreaComponentValid(
-                WindowAreaComponentFullImplementation::class.java,
-                3
+                apiLevel = 3
             )
         )
     }
 
-    /**
-     * Test that validator returns correct results for API Level 2 [WindowAreaComponent]
-     * implementation.
-     */
     @Test
-    fun isWindowAreaComponentValid_apiLevel2() {
-        assertTrue(
-            WindowAreaComponentValidator.isWindowAreaComponentValid(
-                WindowAreaComponentApiV2Implementation::class.java,
-                2
-            )
-        )
+    fun isWindowAreaComponentValid_apiLevel1() {
         assertFalse(
             WindowAreaComponentValidator.isWindowAreaComponentValid(
-                IncompleteWindowAreaComponentApiV2Implementation::class.java,
-                3
+                WindowAreaComponentApiV3Implementation::class.java,
+                apiLevel = 1
+            )
+        )
+    }
+
+    /** Test that validator returns false for API Level 2. */
+    @Test
+    fun isWindowAreaComponentValid_apiLevel2() {
+        assertFalse(
+            WindowAreaComponentValidator.isWindowAreaComponentValid(
+                WindowAreaComponentApiV3Implementation::class.java,
+                2
             )
         )
     }
@@ -74,7 +69,7 @@
      */
     @Test
     fun isWindowAreaComponentValid_apiLevel3() {
-        assertTrue(
+        assertFalse(
             WindowAreaComponentValidator.isWindowAreaComponentValid(
                 WindowAreaComponentApiV3Implementation::class.java,
                 2
@@ -99,10 +94,14 @@
         )
     }
 
-    /** Test that validator returns true if the [ExtensionWindowAreaStatus] is valid */
+    /**
+     * Test that validator returns true if the [ExtensionWindowAreaStatus] is valid and expected to
+     * be found on that vendorApiLevel. Verifies that true is returned if the vendorApiLevel is a
+     * version before [ExtensionWindowAreaStatus] was introduced.
+     */
     @Test
     fun isExtensionWindowAreaStatusValid_trueIfValid() {
-        assertTrue(
+        assertFalse(
             WindowAreaComponentValidator.isExtensionWindowAreaStatusValid(
                 ValidExtensionWindowAreaStatus::class.java,
                 2
@@ -116,18 +115,15 @@
         )
     }
 
-    /** Test that validator returns false if the [ExtensionWindowAreaStatus] is incomplete */
+    /**
+     * Test that validator returns false if the [ExtensionWindowAreaStatus] is incomplete and
+     * expected to be on the device.
+     */
     @Test
     fun isExtensionWindowAreaStatusValid_falseIfIncomplete() {
         assertFalse(
             WindowAreaComponentValidator.isExtensionWindowAreaStatusValid(
                 IncompleteExtensionWindowAreaStatus::class.java,
-                2
-            )
-        )
-        assertFalse(
-            WindowAreaComponentValidator.isExtensionWindowAreaStatusValid(
-                IncompleteExtensionWindowAreaStatus::class.java,
                 3
             )
         )
@@ -177,24 +173,6 @@
         }
     }
 
-    private class WindowAreaComponentApiV2Implementation : WindowAreaComponentApi2Requirements {
-        override fun addRearDisplayStatusListener(consumer: Consumer<Int>) {
-            throw NotImplementedError("Not implemented")
-        }
-
-        override fun removeRearDisplayStatusListener(consumer: Consumer<Int>) {
-            throw NotImplementedError("Not implemented")
-        }
-
-        override fun startRearDisplaySession(activity: Activity, consumer: Consumer<Int>) {
-            throw NotImplementedError("Not implemented")
-        }
-
-        override fun endRearDisplaySession() {
-            throw NotImplementedError("Not implemented")
-        }
-    }
-
     private class WindowAreaComponentApiV3Implementation : WindowAreaComponentApi3Requirements {
         override fun addRearDisplayStatusListener(consumer: Consumer<Int>) {
             throw NotImplementedError("Not implemented")
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt
index fc53f9e..96b4a08 100644
--- a/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/embedding/EmbeddingAdapterTest.kt
@@ -17,20 +17,26 @@
 package androidx.window.embedding
 
 import android.app.Activity
+import android.graphics.Color
 import android.os.Binder
 import android.os.IBinder
 import androidx.window.WindowSdkExtensions
 import androidx.window.WindowTestUtils
 import androidx.window.core.PredicateAdapter
-import androidx.window.embedding.EmbeddingAdapter.Companion.INVALID_ACTIVITY_STACK_TOKEN
-import androidx.window.embedding.EmbeddingAdapter.Companion.INVALID_SPLIT_INFO_TOKEN
+import androidx.window.embedding.DividerAttributes.DragRange.SplitRatioDragRange
+import androidx.window.embedding.DividerAttributes.DraggableDividerAttributes
+import androidx.window.embedding.DividerAttributes.FixedDividerAttributes
 import androidx.window.embedding.SplitAttributes.SplitType
 import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_HINGE
 import androidx.window.extensions.embedding.ActivityStack as OEMActivityStack
+import androidx.window.extensions.embedding.ActivityStack.Token as OEMActivityStackToken
+import androidx.window.extensions.embedding.AnimationBackground as OEMEmbeddingAnimationBackground
+import androidx.window.extensions.embedding.DividerAttributes as OEMDividerAttributes
 import androidx.window.extensions.embedding.SplitAttributes as OEMSplitAttributes
 import androidx.window.extensions.embedding.SplitAttributes.LayoutDirection.TOP_TO_BOTTOM
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType
 import androidx.window.extensions.embedding.SplitInfo as OEMSplitInfo
+import androidx.window.extensions.embedding.SplitInfo.Token as OEMSplitInfoToken
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
@@ -39,6 +45,7 @@
 
 /** Tests for [EmbeddingAdapter] */
 class EmbeddingAdapterTest {
+
     private lateinit var adapter: EmbeddingAdapter
 
     private val extensionVersion = WindowSdkExtensions.getInstance().extensionVersion
@@ -52,51 +59,158 @@
     }
 
     @Test
-    fun testTranslateSplitInfoWithDefaultAttrs() {
+    fun testTranslateSplitInfoWithDefaultAttrsWithApiLevel2() {
         WindowTestUtils.assumeAtLeastVendorApiLevel(2)
         WindowTestUtils.assumeBeforeVendorApiLevel(3)
 
-        val oemSplitInfo =
-            createTestOEMSplitInfo(
-                createTestOEMActivityStack(ArrayList(), true),
-                createTestOEMActivityStack(ArrayList(), true),
-                OEMSplitAttributes.Builder().build(),
-            )
+        val oemSplitInfo = createTestOEMSplitInfo(OEMSplitAttributes.Builder().build())
         val expectedSplitInfo =
             SplitInfo(
-                ActivityStack(ArrayList(), isEmpty = true),
-                ActivityStack(ArrayList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
                 SplitAttributes.Builder()
                     .setSplitType(SplitType.SPLIT_TYPE_EQUAL)
                     .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                     .build(),
-                INVALID_SPLIT_INFO_TOKEN,
+            )
+        assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
+    }
+
+    @Suppress("DEPRECATION")
+    @Test
+    fun testTranslateSplitInfoWithDefaultAttrsWithApiLevel3() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(3)
+        WindowTestUtils.assumeBeforeVendorApiLevel(5)
+
+        val oemSplitInfo =
+            createTestOEMSplitInfo(
+                OEMSplitAttributes.Builder().build(),
+                testBinder = Binder(),
+            )
+        val expectedSplitInfo =
+            SplitInfo(
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                SplitAttributes.Builder()
+                    .setSplitType(SplitType.SPLIT_TYPE_EQUAL)
+                    .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                    .build(),
+                oemSplitInfo.token,
             )
         assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
     }
 
     @Test
-    fun testTranslateSplitInfoWithExpandingContainers() {
+    fun testTranslateSplitInfoWithDefaultAttrsWithApiLevel5() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(5)
+
+        val oemSplitInfo =
+            createTestOEMSplitInfo(
+                createTestOEMActivityStack(OEMActivityStackToken.createFromBinder(Binder())),
+                createTestOEMActivityStack(OEMActivityStackToken.createFromBinder(Binder())),
+                OEMSplitAttributes.Builder().build(),
+                testToken = OEMSplitInfoToken.createFromBinder(Binder()),
+            )
+        val expectedSplitInfo =
+            SplitInfo(
+                ActivityStack(
+                    ArrayList(),
+                    isEmpty = true,
+                    oemSplitInfo.primaryActivityStack.activityStackToken,
+                ),
+                ActivityStack(
+                    ArrayList(),
+                    isEmpty = true,
+                    oemSplitInfo.secondaryActivityStack.activityStackToken,
+                ),
+                SplitAttributes.Builder()
+                    .setSplitType(SplitType.SPLIT_TYPE_EQUAL)
+                    .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                    .build(),
+                oemSplitInfo.splitInfoToken,
+            )
+        assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
+    }
+
+    @Test
+    fun testTranslateSplitInfoWithExpandingContainersWithApiLevel2() {
         WindowTestUtils.assumeAtLeastVendorApiLevel(2)
         WindowTestUtils.assumeBeforeVendorApiLevel(3)
 
         val oemSplitInfo =
             createTestOEMSplitInfo(
-                createTestOEMActivityStack(ArrayList(), true),
-                createTestOEMActivityStack(ArrayList(), true),
                 OEMSplitAttributes.Builder()
                     .setSplitType(OEMSplitAttributes.SplitType.ExpandContainersSplitType())
-                    .build(),
+                    .build()
             )
         val expectedSplitInfo =
             SplitInfo(
-                ActivityStack(ArrayList(), isEmpty = true),
-                ActivityStack(ArrayList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
                 SplitAttributes.Builder()
                     .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
                     .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                     .build(),
-                INVALID_SPLIT_INFO_TOKEN,
+            )
+        assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
+    }
+
+    @Suppress("DEPRECATION")
+    @Test
+    fun testTranslateSplitInfoWithExpandingContainersWithApiLevel3() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(3)
+        WindowTestUtils.assumeBeforeVendorApiLevel(5)
+
+        val oemSplitInfo =
+            createTestOEMSplitInfo(
+                OEMSplitAttributes.Builder()
+                    .setSplitType(OEMSplitAttributes.SplitType.ExpandContainersSplitType())
+                    .build(),
+                testBinder = Binder(),
+            )
+        val expectedSplitInfo =
+            SplitInfo(
+                ActivityStack(ArrayList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                SplitAttributes.Builder()
+                    .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
+                    .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                    .build(),
+                oemSplitInfo.token,
+            )
+        assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
+    }
+
+    @Test
+    fun testTranslateSplitInfoWithExpandingContainersWithApiLevel5() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(5)
+
+        val oemSplitInfo =
+            createTestOEMSplitInfo(
+                createTestOEMActivityStack(OEMActivityStackToken.createFromBinder(Binder())),
+                createTestOEMActivityStack(OEMActivityStackToken.createFromBinder(Binder())),
+                OEMSplitAttributes.Builder()
+                    .setSplitType(OEMSplitAttributes.SplitType.ExpandContainersSplitType())
+                    .build(),
+                testToken = OEMSplitInfoToken.createFromBinder(Binder()),
+            )
+        val expectedSplitInfo =
+            SplitInfo(
+                ActivityStack(
+                    ArrayList(),
+                    isEmpty = true,
+                    oemSplitInfo.primaryActivityStack.activityStackToken
+                ),
+                ActivityStack(
+                    ArrayList(),
+                    isEmpty = true,
+                    oemSplitInfo.secondaryActivityStack.activityStackToken
+                ),
+                SplitAttributes.Builder()
+                    .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
+                    .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                    .build(),
+                oemSplitInfo.splitInfoToken,
             )
         assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
     }
@@ -117,14 +231,13 @@
 
         val expectedSplitInfo =
             SplitInfo(
-                ActivityStack(ArrayList(), isEmpty = true),
-                ActivityStack(ArrayList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
                 SplitAttributes.Builder()
                     .setSplitType(SplitType.ratio(expectedSplitRatio))
                     // OEMSplitInfo with Vendor API level 1 doesn't provide layoutDirection.
                     .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                     .build(),
-                INVALID_SPLIT_INFO_TOKEN,
             )
         assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
     }
@@ -136,8 +249,6 @@
 
         val oemSplitInfo =
             createTestOEMSplitInfo(
-                createTestOEMActivityStack(ArrayList(), true),
-                createTestOEMActivityStack(ArrayList(), true),
                 OEMSplitAttributes.Builder()
                     .setSplitType(OEMSplitAttributes.SplitType.HingeSplitType(RatioSplitType(0.5f)))
                     .setLayoutDirection(TOP_TO_BOTTOM)
@@ -145,53 +256,220 @@
             )
         val expectedSplitInfo =
             SplitInfo(
-                ActivityStack(ArrayList(), isEmpty = true),
-                ActivityStack(ArrayList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
                 SplitAttributes.Builder()
                     .setSplitType(SPLIT_TYPE_HINGE)
                     .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
                     .build(),
-                INVALID_SPLIT_INFO_TOKEN,
-            )
-        assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
-    }
-
-    @Test
-    fun testTranslateSplitInfoWithApiLevel3() {
-        WindowTestUtils.assumeAtLeastVendorApiLevel(3)
-        WindowTestUtils.assumeBeforeVendorApiLevel(5)
-
-        val testStackToken = Binder()
-        val testSplitInfoToken = Binder()
-        val oemSplitInfo =
-            createTestOEMSplitInfo(
-                createTestOEMActivityStack(ArrayList(), true, testStackToken),
-                createTestOEMActivityStack(ArrayList(), true, testStackToken),
-                OEMSplitAttributes.Builder()
-                    .setSplitType(OEMSplitAttributes.SplitType.HingeSplitType(RatioSplitType(0.5f)))
-                    .setLayoutDirection(TOP_TO_BOTTOM)
-                    .build(),
-                testSplitInfoToken,
-            )
-        val expectedSplitInfo =
-            SplitInfo(
-                ActivityStack(ArrayList(), isEmpty = true),
-                ActivityStack(ArrayList(), isEmpty = true),
-                SplitAttributes.Builder()
-                    .setSplitType(SPLIT_TYPE_HINGE)
-                    .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
-                    .build(),
-                testSplitInfoToken,
             )
         assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
     }
 
     @Suppress("DEPRECATION")
+    @Test
+    fun testTranslateSplitInfoWithApiLevel3() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(3)
+        WindowTestUtils.assumeBeforeVendorApiLevel(5)
+
+        val oemSplitInfo =
+            createTestOEMSplitInfo(
+                OEMSplitAttributes.Builder()
+                    .setSplitType(OEMSplitAttributes.SplitType.HingeSplitType(RatioSplitType(0.5f)))
+                    .setLayoutDirection(TOP_TO_BOTTOM)
+                    .build(),
+                testBinder = Binder()
+            )
+        val expectedSplitInfo =
+            SplitInfo(
+                ActivityStack(ArrayList(), isEmpty = true),
+                ActivityStack(ArrayList(), isEmpty = true),
+                SplitAttributes.Builder()
+                    .setSplitType(SPLIT_TYPE_HINGE)
+                    .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
+                    .build(),
+                oemSplitInfo.token,
+            )
+        assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
+    }
+
+    @Test
+    fun testTranslateSplitInfoWithApiLevel5() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(5)
+
+        val oemSplitInfo =
+            createTestOEMSplitInfo(
+                createTestOEMActivityStack(OEMActivityStackToken.createFromBinder(Binder())),
+                createTestOEMActivityStack(OEMActivityStackToken.createFromBinder(Binder())),
+                OEMSplitAttributes.Builder()
+                    .setSplitType(OEMSplitAttributes.SplitType.HingeSplitType(RatioSplitType(0.5f)))
+                    .setLayoutDirection(TOP_TO_BOTTOM)
+                    .build(),
+                testToken = OEMSplitInfoToken.createFromBinder(Binder())
+            )
+        val expectedSplitInfo =
+            SplitInfo(
+                ActivityStack(
+                    emptyList(),
+                    isEmpty = true,
+                    oemSplitInfo.primaryActivityStack.activityStackToken,
+                ),
+                ActivityStack(
+                    emptyList(),
+                    isEmpty = true,
+                    oemSplitInfo.secondaryActivityStack.activityStackToken,
+                ),
+                SplitAttributes.Builder()
+                    .setSplitType(SPLIT_TYPE_HINGE)
+                    .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
+                    .build(),
+                token = oemSplitInfo.splitInfoToken,
+            )
+        assertEquals(listOf(expectedSplitInfo), adapter.translate(listOf(oemSplitInfo)))
+    }
+
+    @Test
+    fun testTranslateAnimationBackgroundWithApiLevel5() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(5)
+
+        val colorBackground = EmbeddingAnimationBackground.createColorBackground(Color.BLUE)
+        val splitAttributesWithColorBackground =
+            SplitAttributes.Builder().setAnimationBackground(colorBackground).build()
+        val splitAttributesWithDefaultBackground =
+            SplitAttributes.Builder()
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
+                .build()
+
+        val extensionsColorBackground =
+            OEMEmbeddingAnimationBackground.createColorBackground(Color.BLUE)
+        val extensionsSplitAttributesWithColorBackground =
+            OEMSplitAttributes.Builder().setAnimationBackground(extensionsColorBackground).build()
+        val extensionsSplitAttributesWithDefaultBackground =
+            OEMSplitAttributes.Builder()
+                .setAnimationBackground(
+                    OEMEmbeddingAnimationBackground.ANIMATION_BACKGROUND_DEFAULT
+                )
+                .build()
+
+        // Translate from Window to Extensions
+        assertEquals(
+            extensionsSplitAttributesWithColorBackground,
+            adapter.translateSplitAttributes(splitAttributesWithColorBackground)
+        )
+        assertEquals(
+            extensionsSplitAttributesWithDefaultBackground,
+            adapter.translateSplitAttributes(splitAttributesWithDefaultBackground)
+        )
+
+        // Translate from Extensions to Window
+        assertEquals(
+            splitAttributesWithColorBackground,
+            adapter.translate(extensionsSplitAttributesWithColorBackground)
+        )
+        assertEquals(
+            splitAttributesWithDefaultBackground,
+            adapter.translate(extensionsSplitAttributesWithDefaultBackground)
+        )
+    }
+
+    @Test
+    fun testTranslateAnimationBackgroundBeforeApiLevel5() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(2)
+        WindowTestUtils.assumeBeforeVendorApiLevel(5)
+
+        val colorBackground = EmbeddingAnimationBackground.createColorBackground(Color.BLUE)
+        val splitAttributesWithColorBackground =
+            SplitAttributes.Builder().setAnimationBackground(colorBackground).build()
+        val splitAttributesWithDefaultBackground =
+            SplitAttributes.Builder()
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
+                .build()
+
+        // No difference after translate before API level 5
+        assertEquals(
+            adapter.translateSplitAttributes(splitAttributesWithColorBackground),
+            adapter.translateSplitAttributes(splitAttributesWithDefaultBackground)
+        )
+    }
+
+    @OptIn(androidx.window.core.ExperimentalWindowApi::class)
+    @Test
+    fun testTranslateEmbeddingConfigurationToWindowAttributes() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(5)
+
+        val dimAreaBehavior = EmbeddingConfiguration.DimAreaBehavior.ON_TASK
+        adapter.embeddingConfiguration = EmbeddingConfiguration(dimAreaBehavior)
+        val oemSplitAttributes = adapter.translateSplitAttributes(SplitAttributes.Builder().build())
+
+        assertEquals(dimAreaBehavior.value, oemSplitAttributes.windowAttributes.dimAreaBehavior)
+    }
+
+    @Test
+    fun testTranslateDividerAttributes_draggable() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(6)
+        val dividerAttributes =
+            DraggableDividerAttributes.Builder()
+                .setWidthDp(20)
+                .setDragRange(SplitRatioDragRange(0.3f, 0.7f))
+                .setColor(Color.GRAY)
+                .build()
+        val oemDividerAttributes =
+            OEMDividerAttributes.Builder(OEMDividerAttributes.DIVIDER_TYPE_DRAGGABLE)
+                .setWidthDp(20)
+                .setPrimaryMinRatio(0.3f)
+                .setPrimaryMaxRatio(0.7f)
+                .setDividerColor(Color.GRAY)
+                .build()
+
+        assertEquals(oemDividerAttributes, adapter.translateDividerAttributes(dividerAttributes))
+        assertEquals(dividerAttributes, adapter.translateDividerAttributes(oemDividerAttributes))
+    }
+
+    @Test
+    fun testTranslateDividerAttributes_fixed() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(6)
+        val dividerAttributes =
+            FixedDividerAttributes.Builder().setWidthDp(20).setColor(Color.GRAY).build()
+        val oemDividerAttributes =
+            OEMDividerAttributes.Builder(OEMDividerAttributes.DIVIDER_TYPE_FIXED)
+                .setWidthDp(20)
+                .setDividerColor(Color.GRAY)
+                .build()
+
+        assertEquals(oemDividerAttributes, adapter.translateDividerAttributes(dividerAttributes))
+        assertEquals(dividerAttributes, adapter.translateDividerAttributes(oemDividerAttributes))
+    }
+
+    @Test
+    fun testTranslateDividerAttributes_noDivider() {
+        WindowTestUtils.assumeAtLeastVendorApiLevel(6)
+        val dividerAttributes = DividerAttributes.NO_DIVIDER
+        val oemDividerAttributes = null
+
+        assertEquals(oemDividerAttributes, adapter.translateDividerAttributes(dividerAttributes))
+        assertEquals(dividerAttributes, adapter.translateDividerAttributes(oemDividerAttributes))
+    }
+
+    private fun createTestOEMSplitInfo(
+        testSplitAttributes: OEMSplitAttributes,
+        testBinder: IBinder? = null,
+        testToken: OEMSplitInfo.Token? = null,
+    ): OEMSplitInfo =
+        createTestOEMSplitInfo(
+            createTestOEMActivityStack(),
+            createTestOEMActivityStack(),
+            testSplitAttributes,
+            testBinder,
+            testToken,
+        )
+
+    @Suppress("Deprecation") // Verify the behavior of version 3 and 4.
     private fun createTestOEMSplitInfo(
         testPrimaryActivityStack: OEMActivityStack,
         testSecondaryActivityStack: OEMActivityStack,
         testSplitAttributes: OEMSplitAttributes,
-        testToken: IBinder = INVALID_SPLIT_INFO_TOKEN,
+        testBinder: IBinder? = null,
+        testToken: OEMSplitInfoToken? = null,
     ): OEMSplitInfo {
         return mock<OEMSplitInfo>().apply {
             whenever(primaryActivityStack).thenReturn(testPrimaryActivityStack)
@@ -199,23 +477,32 @@
             if (extensionVersion >= 2) {
                 whenever(splitAttributes).thenReturn(testSplitAttributes)
             }
-            if (extensionVersion >= 3) {
-                whenever(token).thenReturn(testToken)
+            when (extensionVersion) {
+                in 3..4 -> whenever(token).thenReturn(testBinder)
+                in 5..Int.MAX_VALUE -> whenever(splitInfoToken).thenReturn(testToken)
             }
         }
     }
 
-    @Suppress("DEPRECATION")
+    private fun createTestOEMActivityStack(
+        testToken: OEMActivityStackToken? = null
+    ): OEMActivityStack =
+        createTestOEMActivityStack(
+            emptyList(),
+            testIsEmpty = true,
+            testToken,
+        )
+
     private fun createTestOEMActivityStack(
         testActivities: List<Activity>,
         testIsEmpty: Boolean,
-        testToken: IBinder = INVALID_ACTIVITY_STACK_TOKEN,
+        testToken: OEMActivityStackToken? = null,
     ): OEMActivityStack {
         return mock<OEMActivityStack>().apply {
             whenever(activities).thenReturn(testActivities)
             whenever(isEmpty).thenReturn(testIsEmpty)
-            if (extensionVersion in 3..4) {
-                whenever(token).thenReturn(testToken)
+            if (extensionVersion >= 5) {
+                whenever(activityStackToken).thenReturn(testToken)
             }
         }
     }
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt b/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt
index 7fa2cc6..e6c1d66 100644
--- a/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt
+++ b/window/window/src/androidTest/java/androidx/window/embedding/RuleParserTests.kt
@@ -19,6 +19,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.graphics.Color
 import android.graphics.Rect
 import android.os.Build
 import androidx.annotation.RequiresApi
@@ -79,6 +80,7 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
                 .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
                 .build()
         assertNull(rule.tag)
         assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
@@ -136,6 +138,9 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
                 .setLayoutDirection(TOP_TO_BOTTOM)
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.BLUE)
+                )
                 .build()
         assertEquals(TEST_TAG, rule.tag)
         assertEquals(NEVER, rule.finishPrimaryWithSecondary)
@@ -163,6 +168,7 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
                 .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
                 .build()
         assertNull(rule.tag)
         assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
@@ -226,6 +232,11 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
                 .setLayoutDirection(BOTTOM_TO_TOP)
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(
+                        application.resources.getColor(R.color.testColor, null)
+                    )
+                )
                 .build()
         assertEquals(TEST_TAG, rule.tag)
         assertEquals(ALWAYS, rule.finishPrimaryWithPlaceholder)
diff --git a/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
index 447640c..42e58df 100644
--- a/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/embedding/SafeActivityEmbeddingComponentProviderTest.kt
@@ -17,8 +17,8 @@
 package androidx.window.embedding
 
 import android.util.Log
+import androidx.window.WindowSdkExtensions
 import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.WindowExtensions
 import androidx.window.extensions.WindowExtensionsProvider
 import org.junit.Assert.assertNotNull
@@ -63,9 +63,12 @@
                 // TODO(b/267708462) : more reliable test for testing actual method matching
                 assertNotNull(safeComponent)
                 assertTrue(safeProvider.isActivityEmbeddingComponentAccessible())
-                when (ExtensionsUtil.safeVendorApiLevel) {
+                when (WindowSdkExtensions.getInstance().extensionVersion) {
                     1 -> assertTrue(safeProvider.hasValidVendorApiLevel1())
-                    else -> assertTrue(safeProvider.hasValidVendorApiLevel2())
+                    2 -> assertTrue(safeProvider.hasValidVendorApiLevel2())
+                    in 3..4 -> assertTrue(safeProvider.hasValidVendorApiLevel3())
+                    5 -> assertTrue(safeProvider.hasValidVendorApiLevel5())
+                    6 -> assertTrue(safeProvider.hasValidVendorApiLevel6())
                 }
             }
         } catch (e: UnsupportedOperationException) {
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt b/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
index 784a5c5..1c166e87 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/SafeWindowLayoutComponentProviderTest.kt
@@ -52,9 +52,14 @@
                 // TODO(b/267708462): more reliable test for testing actual method matching
                 assertNotNull(safeComponent)
                 assertTrue(safeProvider.isWindowLayoutComponentAccessible())
-                when (ExtensionsUtil.safeVendorApiLevel) {
-                    1 -> assertTrue(safeProvider.hasValidVendorApiLevel1())
-                    else -> assertTrue(safeProvider.hasValidVendorApiLevel2())
+                when {
+                    ExtensionsUtil.safeVendorApiLevel == 1 -> {
+                        assertTrue(safeProvider.hasValidVendorApiLevel1())
+                    }
+                    ExtensionsUtil.safeVendorApiLevel < 6 -> {
+                        assertTrue(safeProvider.hasValidVendorApiLevel2())
+                    }
+                    else -> assertTrue(safeProvider.hasValidVendorApiLevel6())
                 }
             }
         } catch (e: UnsupportedOperationException) {
diff --git a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
index e1a57bc..4560d2f 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
@@ -22,10 +22,13 @@
 import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.window.TestActivity
 import androidx.window.TestConsumer
+import androidx.window.WindowSdkExtensions
 import androidx.window.WindowTestUtils
 import androidx.window.WindowTestUtils.Companion.assumeAtLeastVendorApiLevel
+import androidx.window.WindowTestUtils.Companion.assumeBeforeVendorApiLevel
 import androidx.window.layout.adapter.WindowBackend
 import java.util.concurrent.Executor
+import kotlin.test.assertEquals
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
@@ -34,6 +37,7 @@
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.test.setMain
+import org.junit.Assert.assertThrows
 import org.junit.Rule
 import org.junit.Test
 
@@ -45,6 +49,7 @@
         ActivityScenarioRule(TestActivity::class.java)
 
     private val testScope = TestScope(UnconfinedTestDispatcher())
+    private val windowSdkExtensions = WindowSdkExtensions.getInstance()
 
     init {
         Dispatchers.setMain(UnconfinedTestDispatcher())
@@ -54,9 +59,10 @@
     public fun testWindowLayoutFeatures(): Unit =
         testScope.runTest {
             activityScenario.scenario.onActivity { testActivity ->
-                val windowMetricsCalculator = WindowMetricsCalculatorCompat
+                val windowMetricsCalculator = WindowMetricsCalculatorCompat()
                 val fakeBackend = FakeWindowBackend()
-                val repo = WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend)
+                val repo =
+                    WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend, windowSdkExtensions)
                 val collector = TestConsumer<WindowLayoutInfo>()
                 testScope.launch(Job()) {
                     repo.windowLayoutInfo(testActivity).collect(collector::accept)
@@ -74,7 +80,12 @@
             }
             assumeAtLeastVendorApiLevel(2)
             val fakeBackend = FakeWindowBackend()
-            val repo = WindowInfoTrackerImpl(WindowMetricsCalculatorCompat, fakeBackend)
+            val repo =
+                WindowInfoTrackerImpl(
+                    WindowMetricsCalculatorCompat(),
+                    fakeBackend,
+                    windowSdkExtensions
+                )
             val collector = TestConsumer<WindowLayoutInfo>()
 
             val windowContext = WindowTestUtils.createOverlayWindowContext()
@@ -89,9 +100,10 @@
     public fun testWindowLayoutFeatures_multicasting(): Unit =
         testScope.runTest {
             activityScenario.scenario.onActivity { testActivity ->
-                val windowMetricsCalculator = WindowMetricsCalculatorCompat
+                val windowMetricsCalculator = WindowMetricsCalculatorCompat()
                 val fakeBackend = FakeWindowBackend()
-                val repo = WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend)
+                val repo =
+                    WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend, windowSdkExtensions)
                 val collector = TestConsumer<WindowLayoutInfo>()
                 val job = Job()
                 launch(job) { repo.windowLayoutInfo(testActivity).collect(collector::accept) }
@@ -102,15 +114,43 @@
         }
 
     @Test
+    fun testSupportedWindowPostures_throwsBeforeApi6() {
+        assumeBeforeVendorApiLevel(6)
+        activityScenario.scenario.onActivity { _ ->
+            val windowMetricsCalculator = WindowMetricsCalculatorCompat()
+            val fakeBackend = FakeWindowBackend()
+            val repo =
+                WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend, windowSdkExtensions)
+            assertThrows(UnsupportedOperationException::class.java) { repo.supportedPostures }
+        }
+    }
+
+    @Test
+    fun testSupportedWindowPostures_reportsFeatures() {
+        assumeAtLeastVendorApiLevel(6)
+        activityScenario.scenario.onActivity { _ ->
+            val windowMetricsCalculator = WindowMetricsCalculatorCompat()
+            val expected = listOf(SupportedPosture.TABLETOP)
+            val fakeBackend = FakeWindowBackend(supportedPostures = expected)
+            val repo =
+                WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend, windowSdkExtensions)
+            val actual = repo.supportedPostures
+
+            assertEquals(expected, actual)
+        }
+    }
+
+    @Test
     public fun testWindowLayoutFeatures_multicastingWithContext(): Unit =
         testScope.runTest {
             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
                 return@runTest
             }
             assumeAtLeastVendorApiLevel(2)
-            val windowMetricsCalculator = WindowMetricsCalculatorCompat
+            val windowMetricsCalculator = WindowMetricsCalculatorCompat()
             val fakeBackend = FakeWindowBackend()
-            val repo = WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend)
+            val repo =
+                WindowInfoTrackerImpl(windowMetricsCalculator, fakeBackend, windowSdkExtensions)
             val collector = TestConsumer<WindowLayoutInfo>()
             val job = Job()
 
@@ -123,7 +163,9 @@
             collector.assertValues(WindowLayoutInfo(emptyList()), WindowLayoutInfo(emptyList()))
         }
 
-    private class FakeWindowBackend : WindowBackend {
+    private class FakeWindowBackend(
+        override val supportedPostures: List<SupportedPosture> = emptyList()
+    ) : WindowBackend {
 
         private class CallbackHolder(
             val executor: Executor,
diff --git a/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsCalculatorCompatTest.kt b/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsCalculatorCompatTest.kt
index 16caeae..4096e1e 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsCalculatorCompatTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsCalculatorCompatTest.kt
@@ -16,19 +16,24 @@
 package androidx.window.layout
 
 import android.annotation.SuppressLint
-import android.app.Activity
 import android.content.ContextWrapper
 import android.os.Build
 import android.view.Display
 import android.view.WindowManager
+import androidx.annotation.RequiresApi
 import androidx.core.view.WindowInsetsCompat
-import androidx.lifecycle.Lifecycle
 import androidx.test.core.app.ActivityScenario.ActivityAction
 import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.window.TestActivity
+import androidx.window.WindowTestUtils.Companion.assumePlatformBeforeR
+import androidx.window.WindowTestUtils.Companion.assumePlatformROrAbove
+import androidx.window.WindowTestUtils.Companion.assumePlatformUOrAbove
+import androidx.window.WindowTestUtils.Companion.isInMultiWindowMode
+import androidx.window.WindowTestUtils.Companion.runActionsAcrossActivityLifecycle
 import androidx.window.core.ExperimentalWindowApi
+import androidx.window.layout.util.DisplayHelper.getRealSizeForDisplay
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Assume
@@ -124,8 +129,9 @@
     @Test
     fun testGetCurrentWindowBounds_postR() {
         assumePlatformROrAbove()
-        runActionsAcrossActivityLifecycle({}) { activity: TestActivity ->
-            val bounds = WindowMetricsCalculatorCompat.computeCurrentWindowMetrics(activity).bounds
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val bounds =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity).bounds
             val windowMetricsBounds = activity.windowManager.currentWindowMetrics.bounds
             assertEquals(windowMetricsBounds, bounds)
         }
@@ -199,8 +205,9 @@
     @Test
     fun testGetMaximumWindowBounds_postR() {
         assumePlatformROrAbove()
-        runActionsAcrossActivityLifecycle({}) { activity: TestActivity ->
-            val bounds = WindowMetricsCalculatorCompat.computeMaximumWindowMetrics(activity).bounds
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val bounds =
+                WindowMetricsCalculatorCompat().computeMaximumWindowMetrics(activity).bounds
             val windowMetricsBounds = activity.windowManager.maximumWindowMetrics.bounds
             assertEquals(windowMetricsBounds, bounds)
         }
@@ -211,8 +218,9 @@
     @OptIn(ExperimentalWindowApi::class)
     fun testGetWindowInsetsCompat_currentWindowMetrics_postR() {
         assumePlatformROrAbove()
-        runActionsAcrossActivityLifecycle({}) { activity: TestActivity ->
-            val windowMetrics = WindowMetricsCalculatorCompat.computeCurrentWindowMetrics(activity)
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val windowMetrics =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity)
             val windowInsets = windowMetrics.getWindowInsets()
             val platformInsets = activity.windowManager.currentWindowMetrics.windowInsets
             val platformWindowInsets = WindowInsetsCompat.toWindowInsetsCompat(platformInsets)
@@ -225,8 +233,9 @@
     @OptIn(ExperimentalWindowApi::class)
     fun testGetWindowInsetsCompat_maximumWindowMetrics_postR() {
         assumePlatformROrAbove()
-        runActionsAcrossActivityLifecycle({}) { activity: TestActivity ->
-            val windowMetrics = WindowMetricsCalculatorCompat.computeMaximumWindowMetrics(activity)
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val windowMetrics =
+                WindowMetricsCalculatorCompat().computeMaximumWindowMetrics(activity)
             val windowInsets = windowMetrics.getWindowInsets()
             val platformInsets = activity.windowManager.maximumWindowMetrics.windowInsets
             val platformWindowInsets = WindowInsetsCompat.toWindowInsetsCompat(platformInsets)
@@ -234,12 +243,41 @@
         }
     }
 
+    @Test
+    fun testDensityMatchesDisplayMetricsDensity() {
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val calculator = WindowMetricsCalculatorCompat()
+            val windowMetrics = calculator.computeCurrentWindowMetrics(activity)
+            val maxWindowMetrics = calculator.computeMaximumWindowMetrics(activity)
+            assertEquals(activity.resources.displayMetrics.density, windowMetrics.density)
+            assertEquals(windowMetrics.density, maxWindowMetrics.density)
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @Test
+    fun testConvertedWindowMetricsMatchesPlatformWindowMetrics() {
+        assumePlatformUOrAbove()
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val calculator = WindowMetricsCalculatorCompat()
+            val windowMetrics = calculator.computeCurrentWindowMetrics(activity)
+            val wm = activity.getSystemService(WindowManager::class.java)
+            val androidWindowMetrics = wm.currentWindowMetrics
+            assertEquals(androidWindowMetrics.bounds, windowMetrics.bounds)
+            assertEquals(androidWindowMetrics.density, windowMetrics.density)
+        }
+    }
+
     private fun testGetCurrentWindowBoundsMatchesRealDisplaySize(
         initialAction: ActivityAction<TestActivity>
     ) {
         val assertWindowBoundsMatchesDisplayAction: ActivityAction<TestActivity> =
             AssertCurrentWindowBoundsEqualsRealDisplaySizeAction()
-        runActionsAcrossActivityLifecycle(initialAction, assertWindowBoundsMatchesDisplayAction)
+        runActionsAcrossActivityLifecycle(
+            activityScenarioRule,
+            initialAction,
+            assertWindowBoundsMatchesDisplayAction
+        )
     }
 
     private fun testGetMaximumWindowBoundsMatchesRealDisplaySize(
@@ -247,28 +285,11 @@
     ) {
         val assertWindowBoundsMatchesDisplayAction: ActivityAction<TestActivity> =
             AssertMaximumWindowBoundsEqualsRealDisplaySizeAction()
-        runActionsAcrossActivityLifecycle(initialAction, assertWindowBoundsMatchesDisplayAction)
-    }
-
-    /**
-     * Creates and launches an activity performing the supplied actions at various points in the
-     * activity lifecycle.
-     *
-     * @param initialAction the action that will run once before the activity is created.
-     * @param verifyAction the action to run once after each change in activity lifecycle state.
-     */
-    private fun runActionsAcrossActivityLifecycle(
-        initialAction: ActivityAction<TestActivity>,
-        verifyAction: ActivityAction<TestActivity>
-    ) {
-        val scenario = activityScenarioRule.scenario
-        scenario.onActivity(initialAction)
-        scenario.moveToState(Lifecycle.State.CREATED)
-        scenario.onActivity(verifyAction)
-        scenario.moveToState(Lifecycle.State.STARTED)
-        scenario.onActivity(verifyAction)
-        scenario.moveToState(Lifecycle.State.RESUMED)
-        scenario.onActivity(verifyAction)
+        runActionsAcrossActivityLifecycle(
+            activityScenarioRule,
+            initialAction,
+            assertWindowBoundsMatchesDisplayAction
+        )
     }
 
     private fun assumeNotMultiWindow() {
@@ -295,8 +316,9 @@
                 } else {
                     @Suppress("DEPRECATION") activity.windowManager.defaultDisplay
                 }
-            val realDisplaySize = WindowMetricsCalculatorCompat.getRealSizeForDisplay(display)
-            val bounds = WindowMetricsCalculatorCompat.computeCurrentWindowMetrics(activity).bounds
+            val calculator = WindowMetricsCalculatorCompat()
+            val realDisplaySize = getRealSizeForDisplay(display)
+            val bounds = calculator.computeCurrentWindowMetrics(activity).bounds
             assertNotEquals("Device can not have zero width", 0, realDisplaySize.x.toLong())
             assertNotEquals("Device can not have zero height", 0, realDisplaySize.y.toLong())
             assertEquals(
@@ -321,8 +343,9 @@
                 } else {
                     @Suppress("DEPRECATION") activity.windowManager.defaultDisplay
                 }
-            val realDisplaySize = WindowMetricsCalculatorCompat.getRealSizeForDisplay(display)
-            val bounds = WindowMetricsCalculatorCompat.computeMaximumWindowMetrics(activity).bounds
+            val calculator = WindowMetricsCalculatorCompat()
+            val realDisplaySize = getRealSizeForDisplay(display)
+            val bounds = calculator.computeMaximumWindowMetrics(activity).bounds
             assertEquals(
                 "Window bounds width does not match real display width",
                 realDisplaySize.x.toLong(),
@@ -335,20 +358,4 @@
             )
         }
     }
-
-    private companion object {
-        private fun isInMultiWindowMode(activity: Activity): Boolean {
-            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-                activity.isInMultiWindowMode
-            } else false
-        }
-
-        private fun assumePlatformBeforeR() {
-            Assume.assumeTrue(Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
-        }
-
-        private fun assumePlatformROrAbove() {
-            Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
-        }
-    }
 }
diff --git a/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsTest.kt b/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsTest.kt
index fbad2f9..3add8d9 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/WindowMetricsTest.kt
@@ -34,81 +34,89 @@
 @RunWith(AndroidJUnit4::class)
 public class WindowMetricsTest {
     @Test
-    public fun testGetBounds() {
+    fun testGetBounds() {
         val bounds = Rect(1, 2, 3, 4)
-        val windowMetrics = WindowMetrics(bounds)
+        val windowMetrics = WindowMetrics(bounds, density = 1f)
         assertEquals(bounds, windowMetrics.bounds)
     }
 
     @Test
-    public fun testEquals_sameBounds() {
+    fun testEquals_sameBounds() {
         val bounds = Rect(1, 2, 3, 4)
-        val windowMetrics0 = WindowMetrics(bounds)
-        val windowMetrics1 = WindowMetrics(bounds)
+        val windowMetrics0 = WindowMetrics(bounds, density = 1f)
+        val windowMetrics1 = WindowMetrics(bounds, density = 1f)
         assertEquals(windowMetrics0, windowMetrics1)
     }
 
     @Test
-    public fun testEquals_differentBounds() {
+    fun testEquals_differentBounds() {
         val bounds0 = Rect(1, 2, 3, 4)
-        val windowMetrics0 = WindowMetrics(bounds0)
+        val windowMetrics0 = WindowMetrics(bounds0, density = 1f)
         val bounds1 = Rect(6, 7, 8, 9)
-        val windowMetrics1 = WindowMetrics(bounds1)
+        val windowMetrics1 = WindowMetrics(bounds1, density = 1f)
         assertNotEquals(windowMetrics0, windowMetrics1)
     }
 
     @Test
-    public fun testHashCode_matchesIfEqual() {
+    fun testEquals_differentDensities() {
         val bounds = Rect(1, 2, 3, 4)
-        val windowMetrics0 = WindowMetrics(bounds)
-        val windowMetrics1 = WindowMetrics(bounds)
+        val windowMetrics0 = WindowMetrics(bounds, density = 0f)
+        val windowMetrics1 = WindowMetrics(bounds, density = 1f)
+        assertNotEquals(windowMetrics0, windowMetrics1)
+    }
+
+    @Test
+    fun testHashCode_matchesIfEqual() {
+        val bounds = Rect(1, 2, 3, 4)
+        val windowMetrics0 = WindowMetrics(bounds, density = 1f)
+        val windowMetrics1 = WindowMetrics(bounds, density = 1f)
         assertEquals(windowMetrics0.hashCode().toLong(), windowMetrics1.hashCode().toLong())
     }
 
     @RequiresApi(Build.VERSION_CODES.R)
     @Test
-    public fun testSameWindowInsets_emptyInsets() {
+    fun testSameWindowInsets_emptyInsets() {
         assumePlatformROrAbove()
         val bounds = Bounds(1, 2, 3, 4)
         val windowInsetsCompat = WindowInsetsCompat.Builder().build()
-        val windowMetricsA = WindowMetrics(bounds, windowInsetsCompat)
-        val windowMetricsB = WindowMetrics(bounds, windowInsetsCompat)
+        val windowMetricsA = WindowMetrics(bounds, windowInsetsCompat, 1f /* density */)
+        val windowMetricsB = WindowMetrics(bounds, windowInsetsCompat, 1f /* density */)
         assertEquals(windowMetricsA, windowMetricsB)
     }
 
     @RequiresApi(Build.VERSION_CODES.R)
     @Test
-    public fun testSameWindowInsets_nonEmptyInsets() {
+    fun testSameWindowInsets_nonEmptyInsets() {
         assumePlatformROrAbove()
         val bounds = Bounds(1, 2, 3, 4)
         val insets = Insets.of(6, 7, 8, 9)
         val builder = WindowInsetsCompat.Builder()
-        for (type in WindowMetricsCalculatorCompat.insetsTypeMasks) {
+        for (type in WindowMetricsCalculatorCompat().insetsTypeMasks) {
             builder.setInsets(type, insets)
         }
         val windowInsetsCompat = builder.build()
-        val windowMetricsA = WindowMetrics(bounds, windowInsetsCompat)
-        val windowMetricsB = WindowMetrics(bounds, windowInsetsCompat)
+        val windowMetricsA = WindowMetrics(bounds, windowInsetsCompat, 1f /* density */)
+        val windowMetricsB = WindowMetrics(bounds, windowInsetsCompat, 1f /* density */)
         assertEquals(windowMetricsA, windowMetricsB)
     }
 
     @RequiresApi(Build.VERSION_CODES.R)
     @Test
-    public fun testDiffWindowInsets() {
+    fun testDiffWindowInsets() {
         assumePlatformROrAbove()
         val bounds = Bounds(1, 2, 3, 4)
         val insetsA = Insets.of(1, 2, 3, 4)
         val insetsB = Insets.of(6, 7, 8, 9)
         val builderA = WindowInsetsCompat.Builder()
         val builderB = WindowInsetsCompat.Builder()
-        for (type in WindowMetricsCalculatorCompat.insetsTypeMasks) {
+        for (type in WindowMetricsCalculatorCompat().insetsTypeMasks) {
             builderA.setInsets(type, insetsA)
             builderB.setInsets(type, insetsB)
         }
         val windowInsetsCompatA = builderA.build()
         val windowInsetsCompatB = builderB.build()
-        val windowMetricsA = WindowMetrics(bounds, windowInsetsCompatA)
-        val windowMetricsB = WindowMetrics(bounds, windowInsetsCompatB)
+        val windowMetricsA = WindowMetrics(bounds, windowInsetsCompatA, 1f /* density */)
+        val windowMetricsB = WindowMetrics(bounds, windowInsetsCompatB, 1f /* density */)
         assertNotEquals(windowMetricsA, windowMetricsB)
     }
 
diff --git a/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendTest.kt
index f20e341..c557551 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendTest.kt
@@ -33,17 +33,21 @@
 import androidx.window.core.ConsumerAdapter
 import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.core.util.function.Consumer as OEMConsumer
+import androidx.window.extensions.layout.DisplayFoldFeature
 import androidx.window.extensions.layout.FoldingFeature as OEMFoldingFeature
 import androidx.window.extensions.layout.FoldingFeature.STATE_FLAT
 import androidx.window.extensions.layout.FoldingFeature.TYPE_HINGE
+import androidx.window.extensions.layout.SupportedWindowFeatures
 import androidx.window.extensions.layout.WindowLayoutComponent
 import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
+import androidx.window.layout.SupportedPosture
 import androidx.window.layout.WindowLayoutInfo
 import androidx.window.layout.WindowMetricsCalculatorCompat
 import androidx.window.layout.adapter.extensions.ExtensionsWindowLayoutInfoAdapter.translate
 import java.util.function.Consumer as JavaConsumer
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
 import org.junit.Assert.assertTrue
 import org.junit.Assume.assumeTrue
 import org.junit.Before
@@ -645,9 +649,48 @@
         consumer.assertValues(expected)
     }
 
+    @Test
+    fun testSupportedFeatures_throwsBeforeApi6() {
+        assumeBeforeVendorApiLevel(6)
+
+        val component = FakeWindowComponent()
+        val backend = ExtensionWindowBackend.newInstance(component, consumerAdapter)
+
+        assertThrows(UnsupportedOperationException::class.java) { backend.supportedPostures }
+    }
+
+    @Test
+    fun testSupportedFeatures_emptyListReturnsNoFeatures() {
+        assumeAtLeastVendorApiLevel(6)
+
+        val supportedWindowFeatures = SupportedWindowFeatures.Builder(listOf()).build()
+        val component = FakeWindowComponent(windowFeatures = supportedWindowFeatures)
+        val backend = ExtensionWindowBackend.newInstance(component, consumerAdapter)
+
+        val actual = backend.supportedPostures
+        assertEquals(emptyList<SupportedPosture>(), actual)
+    }
+
+    @Test
+    fun testSupportedFeatures_halfOpenedReturnsTabletopSupport() {
+        assumeAtLeastVendorApiLevel(6)
+
+        val foldFeature =
+            DisplayFoldFeature.Builder(DisplayFoldFeature.TYPE_SCREEN_FOLD_IN)
+                .addProperties(DisplayFoldFeature.FOLD_PROPERTY_SUPPORTS_HALF_OPENED)
+                .build()
+        val supportedWindowFeatures = SupportedWindowFeatures.Builder(listOf(foldFeature)).build()
+        val component = FakeWindowComponent(windowFeatures = supportedWindowFeatures)
+        val backend = ExtensionWindowBackend.newInstance(component, consumerAdapter)
+
+        val actual = backend.supportedPostures
+        assertEquals(listOf(SupportedPosture.TABLETOP), actual)
+    }
+
     internal companion object {
         private fun newTestOEMWindowLayoutInfo(activity: Activity): OEMWindowLayoutInfo {
-            val bounds = WindowMetricsCalculatorCompat.computeCurrentWindowMetrics(activity).bounds
+            val bounds =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity).bounds
             val featureBounds = Rect(0, bounds.centerY(), bounds.width(), bounds.centerY())
             val feature = OEMFoldingFeature(featureBounds, TYPE_HINGE, STATE_FLAT)
             val displayFeatures = listOf(feature)
@@ -661,7 +704,7 @@
          */
         @RequiresApi(Build.VERSION_CODES.R)
         private fun newTestOEMWindowLayoutInfo(@UiContext context: Context): OEMWindowLayoutInfo {
-            val bounds = WindowMetricsCalculatorCompat.computeCurrentWindowMetrics(context).bounds
+            val bounds = WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(context).bounds
             val featureBounds = Rect(0, bounds.centerY(), bounds.width(), bounds.centerY())
             val feature = OEMFoldingFeature(featureBounds, TYPE_HINGE, STATE_FLAT)
             val displayFeatures = listOf(feature)
@@ -696,7 +739,8 @@
         }
     }
 
-    private class FakeWindowComponent : WindowLayoutComponent {
+    private class FakeWindowComponent(private val windowFeatures: SupportedWindowFeatures? = null) :
+        WindowLayoutComponent {
 
         val consumers = mutableListOf<JavaConsumer<OEMWindowLayoutInfo>>()
         val oemConsumers = mutableListOf<OEMConsumer<OEMWindowLayoutInfo>>()
@@ -723,6 +767,14 @@
             oemConsumers.remove(consumer)
         }
 
+        override fun getSupportedWindowFeatures(): SupportedWindowFeatures {
+            return windowFeatures
+                ?: throw UnsupportedOperationException(
+                    "Window features are not set. Either the vendor API level is too low or value " +
+                        "was not set"
+                )
+        }
+
         @SuppressLint("NewApi")
         fun emit(info: OEMWindowLayoutInfo) {
             if (ExtensionsUtil.safeVendorApiLevel < 2) {
diff --git a/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapterTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapterTest.kt
index 3a948d3..325960a 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapterTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapterTest.kt
@@ -32,7 +32,7 @@
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
 import androidx.window.layout.TestFoldingFeatureUtil.invalidNonZeroFoldBounds
 import androidx.window.layout.WindowLayoutInfo
-import androidx.window.layout.WindowMetricsCalculatorCompat.computeCurrentWindowMetrics
+import androidx.window.layout.WindowMetricsCalculatorCompat
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
@@ -50,7 +50,8 @@
     @Test
     fun testTranslate_foldingFeature() {
         activityScenario.scenario.onActivity { activity ->
-            val windowMetrics = computeCurrentWindowMetrics(activity)
+            val windowMetrics =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity)
             val bounds = windowMetrics.bounds
             val featureBounds = Rect(0, bounds.centerY(), bounds.width(), bounds.centerY())
             val oemFeature = OEMFoldingFeature(featureBounds, TYPE_HINGE, STATE_HALF_OPENED)
@@ -66,7 +67,8 @@
     @Test
     fun testTranslate_windowLayoutInfo() {
         activityScenario.scenario.onActivity { activity ->
-            val bounds = computeCurrentWindowMetrics(activity).bounds
+            val bounds =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity).bounds
             val featureBounds = Rect(0, bounds.centerY(), bounds.width(), bounds.centerY())
             val oemFeature = OEMFoldingFeature(featureBounds, TYPE_HINGE, STATE_HALF_OPENED)
             val oemInfo = OEMWindowLayoutInfo(listOf(oemFeature))
@@ -84,7 +86,8 @@
     fun testTranslate_windowLayoutInfoFromContext() {
         assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
         activityScenario.scenario.onActivity { activity ->
-            val bounds = computeCurrentWindowMetrics(activity).bounds
+            val bounds =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity).bounds
             val featureBounds = Rect(0, bounds.centerY(), bounds.width(), bounds.centerY())
             val oemFeature = OEMFoldingFeature(featureBounds, TYPE_HINGE, STATE_HALF_OPENED)
             val oemInfo = OEMWindowLayoutInfo(listOf(oemFeature))
@@ -101,7 +104,8 @@
     @Test
     fun testTranslate_foldingFeature_invalidType() {
         activityScenario.scenario.onActivity { activity ->
-            val windowMetrics = computeCurrentWindowMetrics(activity)
+            val windowMetrics =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity)
             val bounds = windowMetrics.bounds
             val featureBounds = Rect(0, bounds.centerY(), bounds.width(), bounds.centerY())
             val oemFeature = OEMFoldingFeature(featureBounds, -1, STATE_HALF_OPENED)
@@ -115,7 +119,8 @@
     @Test
     fun testTranslate_foldingFeature_invalidState() {
         activityScenario.scenario.onActivity { activity ->
-            val windowMetrics = computeCurrentWindowMetrics(activity)
+            val windowMetrics =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity)
             val bounds = windowMetrics.bounds
             val featureBounds = Rect(0, bounds.centerY(), bounds.width(), bounds.centerY())
             val oemFeature = OEMFoldingFeature(featureBounds, TYPE_HINGE, -1)
@@ -129,7 +134,8 @@
     @Test
     fun testTranslate_foldingFeature_invalidBounds() {
         activityScenario.scenario.onActivity { activity ->
-            val windowMetrics = computeCurrentWindowMetrics(activity)
+            val windowMetrics =
+                WindowMetricsCalculatorCompat().computeCurrentWindowMetrics(activity)
             val windowBounds = windowMetrics.bounds
 
             val source =
diff --git a/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatDeviceTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatDeviceTest.kt
index 292197c..1936828 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatDeviceTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatDeviceTest.kt
@@ -42,6 +42,7 @@
 import org.junit.Assert.assertNotNull
 import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatcher
@@ -85,6 +86,7 @@
         }
     }
 
+    @Ignore("b/241174887")
     @Test
     fun testWindowLayoutCallbackOnConfigChange() {
         val testScope = TestScope(UnconfinedTestDispatcher())
diff --git a/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendTest.kt
index 69474bd..df07ffc 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendTest.kt
@@ -198,6 +198,13 @@
         secondConsumer.assertValues(secondExpected)
     }
 
+    @Test(expected = UnsupportedOperationException::class)
+    fun testSupportedWindowFeatures_throws() {
+        val interfaceCompat = SwitchOnUnregisterExtensionInterfaceCompat()
+        val backend = SidecarWindowBackend(interfaceCompat)
+        backend.supportedPostures
+    }
+
     internal companion object {
         private fun newTestWindowLayoutInfo(): WindowLayoutInfo {
             val feature1: DisplayFeature = HardwareFoldingFeature(Bounds(0, 2, 3, 4), HINGE, FLAT)
diff --git a/window/window/src/androidTest/java/androidx/window/layout/util/BoundsHelperTest.kt b/window/window/src/androidTest/java/androidx/window/layout/util/BoundsHelperTest.kt
new file mode 100644
index 0000000..00d5417
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/layout/util/BoundsHelperTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout.util
+
+import android.os.Build
+import android.view.WindowManager
+import androidx.annotation.RequiresApi
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.window.TestActivity
+import androidx.window.WindowTestUtils.Companion.assumePlatformROrAbove
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [BoundsHelper]. */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class BoundsHelperTest {
+
+    @get:Rule
+    var activityScenarioRule: ActivityScenarioRule<TestActivity> =
+        ActivityScenarioRule(TestActivity::class.java)
+
+    @RequiresApi(Build.VERSION_CODES.R)
+    @Test
+    fun testCurrentWindowBounds_postR() {
+        assumePlatformROrAbove()
+        activityScenarioRule.scenario.onActivity { activity ->
+            val currentBounds = BoundsHelper.getInstance().currentWindowBounds(activity)
+            val expectedBounds =
+                activity.getSystemService(WindowManager::class.java).currentWindowMetrics.bounds
+            assertEquals(expectedBounds, currentBounds)
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.R)
+    @Test
+    fun testMaximumWindowBounds_postR() {
+        assumePlatformROrAbove()
+        activityScenarioRule.scenario.onActivity { activity ->
+            val currentBounds = BoundsHelper.getInstance().maximumWindowBounds(activity)
+            val expectedBounds =
+                activity.getSystemService(WindowManager::class.java).maximumWindowMetrics.bounds
+            assertEquals(expectedBounds, currentBounds)
+        }
+    }
+}
diff --git a/window/window/src/androidTest/java/androidx/window/layout/util/DensityCompatHelperTest.kt b/window/window/src/androidTest/java/androidx/window/layout/util/DensityCompatHelperTest.kt
new file mode 100644
index 0000000..19c568a
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/layout/util/DensityCompatHelperTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout.util
+
+import android.graphics.Rect
+import android.os.Build
+import android.util.DisplayMetrics
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics as AndroidWindowMetrics
+import androidx.annotation.RequiresApi
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.window.TestActivity
+import androidx.window.WindowTestUtils.Companion.assumePlatformBeforeU
+import androidx.window.WindowTestUtils.Companion.assumePlatformROrAbove
+import androidx.window.WindowTestUtils.Companion.assumePlatformUOrAbove
+import androidx.window.WindowTestUtils.Companion.runActionsAcrossActivityLifecycle
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for the [DensityCompatHelper] class. */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class DensityCompatHelperTest {
+
+    @get:Rule
+    var activityScenarioRule: ActivityScenarioRule<TestActivity> =
+        ActivityScenarioRule(TestActivity::class.java)
+
+    @Test
+    fun testDensityFromContext_beforeU() {
+        assumePlatformBeforeU()
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val helper = DensityCompatHelper.getInstance()
+            assertEquals(activity.resources.displayMetrics.density, helper.density(activity))
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @Test
+    fun testDensityFromContext_UOrAbove() {
+        assumePlatformUOrAbove()
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val wm = activity.getSystemService(WindowManager::class.java)
+            val helper = DensityCompatHelper.getInstance()
+            assertEquals(wm.currentWindowMetrics.density, helper.density(activity))
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @Test
+    fun testDensityFromConfiguration_beforeU() {
+        assumePlatformBeforeU()
+        assumePlatformROrAbove()
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val helper = DensityCompatHelper.getInstance()
+
+            @Suppress("DEPRECATION")
+            val fakeWindowMetrics =
+                AndroidWindowMetrics(
+                    Rect(0, 0, 0, 0),
+                    WindowInsets.Builder().build(),
+                )
+
+            val density = helper.density(activity.resources.configuration, fakeWindowMetrics)
+            val expectedDensity =
+                activity.resources.configuration.densityDpi.toFloat() /
+                    DisplayMetrics.DENSITY_DEFAULT
+            assertEquals(expectedDensity, density)
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @Test
+    fun testDensityFromWindowMetrics_UOrAbove() {
+        assumePlatformUOrAbove()
+        runActionsAcrossActivityLifecycle(activityScenarioRule, {}) { activity: TestActivity ->
+            val helper = DensityCompatHelper.getInstance()
+
+            val fakeDensity = 123.456f
+            val fakeWindowMetrics =
+                AndroidWindowMetrics(Rect(0, 0, 0, 0), WindowInsets.Builder().build(), fakeDensity)
+
+            val density = helper.density(activity.resources.configuration, fakeWindowMetrics)
+            assertEquals(fakeDensity, density)
+        }
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/WindowProperties.kt b/window/window/src/main/java/androidx/window/WindowProperties.kt
index ed346b6..ae9ce65 100644
--- a/window/window/src/main/java/androidx/window/WindowProperties.kt
+++ b/window/window/src/main/java/androidx/window/WindowProperties.kt
@@ -109,7 +109,6 @@
      * </application>
      * ```
      */
-    // TODO(b/274924641): Make property public
     const val PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED =
         "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"
 
@@ -170,4 +169,88 @@
      */
     const val PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES =
         "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES"
+
+    /**
+     * Application-level [PackageManager][android.content.pm.PackageManager.Property] tag that (when
+     * set to false) informs the system the app has opted out of the user-facing aspect ratio
+     * compatibility override.
+     *
+     * The compatibility override enables device users to set the app's aspect ratio or force the
+     * app to fill the display regardless of the aspect ratio or orientation specified in the app
+     * manifest.
+     *
+     * The aspect ratio compatibility override is exposed to users in device settings. A menu in
+     * device settings lists all apps that have not opted out of the compatibility override. Users
+     * select apps from the menu and set the app aspect ratio on a per-app basis. Typically, the
+     * menu is available only on large screen devices.
+     *
+     * When users apply the aspect ratio override, the minimum aspect ratio specified in the app
+     * manifest is overridden. If users choose a full-screen aspect ratio, the orientation of the
+     * activity is forced to
+     * [SCREEN_ORIENTATION_USER][android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER]; see
+     * [PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE] to disable the full-screen
+     * option only.
+     *
+     * The user override is intended to improve the app experience on devices that have the ignore
+     * orientation request display setting enabled by OEMs (enables compatibility mode for fixed
+     * orientation on Android 12 (API level 31) or higher; see
+     * [Large screen compatibility mode](https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode)
+     * for more details).
+     *
+     * To opt out of the user aspect ratio compatibility override, add this property to your app
+     * manifest and set the value to `false`. Your app will be excluded from the list of apps in
+     * device settings, and users will not be able to override the app's aspect ratio.
+     *
+     * Not setting this property at all, or setting this property to `true` has no effect.
+     *
+     * **Syntax:**
+     *
+     * ```
+     * <application>
+     *   <property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"
+     *     android:value="false" />
+     * </application>
+     * ```
+     */
+    const val PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE =
+        "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE"
+
+    /**
+     * Application-level [PackageManager][android.content.pm.PackageManager.Property] tag that (when
+     * set to false) informs the system the app has opted out of the full-screen option of the user
+     * aspect ratio compatibility override settings. (For background information about the user
+     * aspect ratio compatibility override, see [PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE].)
+     *
+     * When users apply the full-screen compatibility override, the orientation of the activity is
+     * forced to [SCREEN_ORIENTATION_USER][android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER].
+     *
+     * The user override aims to improve the app experience on devices that have the ignore
+     * orientation request display setting enabled by OEMs (enables compatibility mode for fixed
+     * orientation on Android 12 (API level 31) or higher; see
+     * [Large screen compatibility mode](https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode)
+     * for more details).
+     *
+     * To opt out of the full-screen option of the user aspect ratio compatibility override, add
+     * this property to your app manifest and set the value to `false`. Your app will have
+     * full-screen option removed from the list of user aspect ratio override options in device
+     * settings, and users will not be able to apply full-screen override to your app.
+     *
+     * **Note:** If [PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE] is `false`, this property has
+     * no effect.
+     *
+     * Not setting this property at all, or setting this property to `true` has no effect.
+     *
+     * **Syntax:**
+     *
+     * ```
+     * <application>
+     *   <property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE"
+     *     android:value="false" />
+     * </application>
+     * ```
+     */
+    const val PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE =
+        "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE"
 }
diff --git a/window/window/src/main/java/androidx/window/WindowSdkExtensions.kt b/window/window/src/main/java/androidx/window/WindowSdkExtensions.kt
index 97fd83d..a78df6b 100644
--- a/window/window/src/main/java/androidx/window/WindowSdkExtensions.kt
+++ b/window/window/src/main/java/androidx/window/WindowSdkExtensions.kt
@@ -17,6 +17,7 @@
 package androidx.window
 
 import androidx.annotation.IntRange
+import androidx.annotation.RestrictTo
 import androidx.window.core.ExtensionsUtil
 
 /**
@@ -31,7 +32,7 @@
  *
  * @sample androidx.window.samples.checkWindowSdkExtensionsVersion
  */
-abstract class WindowSdkExtensions internal constructor() {
+abstract class WindowSdkExtensions @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor() {
 
     /**
      * Reports the device's extension version
@@ -56,6 +57,25 @@
         }
     }
 
+    /**
+     * Checks the [extensionVersion] and throws [UnsupportedOperationException] if the version is
+     * not in the [range].
+     *
+     * This is useful to provide compatibility for APIs updated in 2+ but deprecated in latest
+     * version.
+     *
+     * @param range the required extension range of the targeting API.
+     * @throws UnsupportedOperationException if the required [range] is not satisfied.
+     */
+    internal fun requireExtensionVersion(range: kotlin.ranges.IntRange) {
+        if (extensionVersion !in range) {
+            throw UnsupportedOperationException(
+                "This API requires extension version " +
+                    "$range, but the device is on $extensionVersion"
+            )
+        }
+    }
+
     companion object {
         /** Returns a [WindowSdkExtensions] instance. */
         @JvmStatic
@@ -65,17 +85,20 @@
 
         private var decorator: WindowSdkExtensionsDecorator = EmptyDecoratorWindowSdk
 
-        internal fun overrideDecorator(overridingDecorator: WindowSdkExtensionsDecorator) {
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        fun overrideDecorator(overridingDecorator: WindowSdkExtensionsDecorator) {
             decorator = overridingDecorator
         }
 
-        internal fun reset() {
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        fun reset() {
             decorator = EmptyDecoratorWindowSdk
         }
     }
 }
 
-internal interface WindowSdkExtensionsDecorator {
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface WindowSdkExtensionsDecorator {
     /** Returns a [WindowSdkExtensions] instance. */
     fun decorate(windowSdkExtensions: WindowSdkExtensions): WindowSdkExtensions
 }
diff --git a/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt b/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
index e0e9574..8734411 100644
--- a/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
@@ -18,6 +18,8 @@
 
 import android.content.Context
 import android.view.View
+import android.view.Window
+import androidx.window.area.utils.PresentationWindowCompatUtils
 import androidx.window.core.ExperimentalWindowApi
 import androidx.window.extensions.area.ExtensionWindowAreaPresentation
 import androidx.window.extensions.area.WindowAreaComponent
@@ -25,11 +27,16 @@
 @ExperimentalWindowApi
 internal class RearDisplayPresentationSessionPresenterImpl(
     private val windowAreaComponent: WindowAreaComponent,
-    private val presentation: ExtensionWindowAreaPresentation
+    private val presentation: ExtensionWindowAreaPresentation,
+    vendorApiLevel: Int
 ) : WindowAreaSessionPresenter {
 
     override val context: Context = presentation.presentationContext
 
+    override val window: Window? =
+        if (vendorApiLevel >= 4) presentation.window
+        else PresentationWindowCompatUtils.getWindowBeforeVendorApiLevel4(presentation)
+
     override fun setContentView(view: View) {
         presentation.setPresentationView(view)
     }
diff --git a/window/window/src/main/java/androidx/window/area/SafeWindowAreaComponentProvider.kt b/window/window/src/main/java/androidx/window/area/SafeWindowAreaComponentProvider.kt
index 84b52d0..7ea1d32 100644
--- a/window/window/src/main/java/androidx/window/area/SafeWindowAreaComponentProvider.kt
+++ b/window/window/src/main/java/androidx/window/area/SafeWindowAreaComponentProvider.kt
@@ -15,6 +15,7 @@
  */
 package androidx.window.area
 
+import android.os.Build
 import androidx.window.SafeWindowExtensionsProvider
 import androidx.window.area.reflectionguard.WindowAreaComponentValidator.isExtensionWindowAreaPresentationValid
 import androidx.window.area.reflectionguard.WindowAreaComponentValidator.isExtensionWindowAreaStatusValid
@@ -41,6 +42,7 @@
                 if (
                     windowExtensions != null &&
                         isWindowAreaProviderValid(windowExtensions) &&
+                        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
                         isWindowAreaComponentValid(
                             windowAreaComponentClass,
                             ExtensionsUtil.safeVendorApiLevel
@@ -49,11 +51,16 @@
                             extensionWindowAreaStatusClass,
                             ExtensionsUtil.safeVendorApiLevel
                         ) &&
-                        isValidExtensionWindowPresentation()
-                )
+                        isExtensionWindowAreaPresentationValid(
+                            extensionWindowAreaPresentationClass,
+                            ExtensionsUtil.safeVendorApiLevel
+                        )
+                ) {
                     windowExtensions.windowAreaComponent
-                else null
-            } catch (e: Exception) {
+                } else {
+                    null
+                }
+            } catch (e: Throwable) {
                 null
             }
         }
@@ -67,15 +74,6 @@
         }
     }
 
-    private fun isValidExtensionWindowPresentation(): Boolean {
-        // Not required for API Level 2 or below
-        return ExtensionsUtil.safeVendorApiLevel <= 2 ||
-            isExtensionWindowAreaPresentationValid(
-                extensionWindowAreaPresentationClass,
-                ExtensionsUtil.safeVendorApiLevel
-            )
-    }
-
     private val windowAreaComponentClass: Class<*>
         get() {
             return loader.loadClass(WindowExtensionsConstants.WINDOW_AREA_COMPONENT_CLASS)
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaController.kt b/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
index 90c9759..6d85d20 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
@@ -23,7 +23,6 @@
 import androidx.annotation.RestrictTo
 import androidx.window.WindowSdkExtensions
 import androidx.window.area.WindowAreaInfo.Type.Companion.TYPE_REAR_FACING
-import androidx.window.area.utils.DeviceUtils
 import androidx.window.core.BuildConfig
 import androidx.window.core.ExperimentalWindowApi
 import androidx.window.core.ExtensionsUtil
@@ -147,17 +146,16 @@
                     }
                     null
                 }
+
             val deviceSupported =
                 Build.VERSION.SDK_INT > Build.VERSION_CODES.Q &&
                     windowAreaComponentExtensions != null &&
-                    (ExtensionsUtil.safeVendorApiLevel >= 3 ||
-                        DeviceUtils.hasDeviceMetrics(Build.MANUFACTURER, Build.MODEL))
+                    ExtensionsUtil.safeVendorApiLevel >= 3
 
             val controller =
                 if (deviceSupported) {
                     WindowAreaControllerImpl(
-                        windowAreaComponentExtensions!!,
-                        ExtensionsUtil.safeVendorApiLevel
+                        windowAreaComponent = windowAreaComponentExtensions!!,
                     )
                 } else {
                     EmptyWindowAreaControllerImpl()
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt b/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
index c8396a0..121e608 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
@@ -21,14 +21,15 @@
 import android.os.Build
 import android.util.Log
 import androidx.annotation.RequiresApi
+import androidx.window.RequiresWindowSdkExtension
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNKNOWN
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
 import androidx.window.area.adapter.WindowAreaAdapter
-import androidx.window.area.utils.DeviceUtils
 import androidx.window.core.BuildConfig
 import androidx.window.core.ExperimentalWindowApi
+import androidx.window.core.ExtensionsUtil
 import androidx.window.core.VerificationMode
 import androidx.window.extensions.area.ExtensionWindowAreaStatus
 import androidx.window.extensions.area.WindowAreaComponent
@@ -57,10 +58,10 @@
  * functionality.
  */
 @ExperimentalWindowApi
+@RequiresWindowSdkExtension(3)
 @RequiresApi(Build.VERSION_CODES.Q)
 internal class WindowAreaControllerImpl(
     private val windowAreaComponent: WindowAreaComponent,
-    private val vendorApiLevel: Int
 ) : WindowAreaController {
 
     private lateinit var rearDisplaySessionConsumer: Consumer2<Int>
@@ -89,40 +90,24 @@
                     }
 
                 windowAreaComponent.addRearDisplayStatusListener(rearDisplayListener)
-                if (vendorApiLevel > 2) {
-                    windowAreaComponent.addRearDisplayPresentationStatusListener(
-                        rearDisplayPresentationListener
-                    )
-                }
+                windowAreaComponent.addRearDisplayPresentationStatusListener(
+                    rearDisplayPresentationListener
+                )
 
                 awaitClose {
                     windowAreaComponent.removeRearDisplayStatusListener(rearDisplayListener)
-                    if (vendorApiLevel > 2) {
-                        windowAreaComponent.removeRearDisplayPresentationStatusListener(
-                            rearDisplayPresentationListener
-                        )
-                    }
+                    windowAreaComponent.removeRearDisplayPresentationStatusListener(
+                        rearDisplayPresentationListener
+                    )
                 }
             }
         }
 
     private fun updateRearDisplayAvailability(status: @WindowAreaComponent.WindowAreaStatus Int) {
         val windowMetrics =
-            if (vendorApiLevel >= 3) {
-                WindowMetricsCalculator.fromDisplayMetrics(
-                    displayMetrics = windowAreaComponent.rearDisplayMetrics
-                )
-            } else {
-                val displayMetrics =
-                    DeviceUtils.getRearDisplayMetrics(Build.MANUFACTURER, Build.MODEL)
-                if (displayMetrics != null) {
-                    WindowMetricsCalculator.fromDisplayMetrics(displayMetrics = displayMetrics)
-                } else {
-                    throw IllegalArgumentException(
-                        "DeviceUtils rear display metrics entry should not be null"
-                    )
-                }
-            }
+            WindowMetricsCalculator.fromDisplayMetrics(
+                displayMetrics = windowAreaComponent.rearDisplayMetrics
+            )
 
         currentRearDisplayModeStatus = WindowAreaAdapter.translate(status, activeWindowAreaSession)
         updateRearDisplayWindowArea(
@@ -393,7 +378,8 @@
                             windowAreaPresentationSessionCallback.onSessionStarted(
                                 RearDisplayPresentationSessionPresenterImpl(
                                     windowAreaComponent,
-                                    windowAreaComponent.rearDisplayPresentation!!
+                                    windowAreaComponent.rearDisplayPresentation!!,
+                                    ExtensionsUtil.safeVendorApiLevel
                                 )
                             )
                         }
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt b/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
index af47bd5..14620ae 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
@@ -22,6 +22,7 @@
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
 import androidx.window.core.ExperimentalWindowApi
+import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.area.WindowAreaComponent
 import androidx.window.layout.WindowMetrics
 
@@ -86,7 +87,8 @@
             OPERATION_PRESENT_ON_AREA ->
                 RearDisplayPresentationSessionPresenterImpl(
                     windowAreaComponent,
-                    windowAreaComponent.rearDisplayPresentation!!
+                    windowAreaComponent.rearDisplayPresentation!!,
+                    ExtensionsUtil.safeVendorApiLevel
                 )
             else -> {
                 throw IllegalArgumentException("Invalid operation provided")
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt b/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
index cd45d92..c9b7a01 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.view.View
+import android.view.Window
 import androidx.window.core.ExperimentalWindowApi
 
 /**
@@ -34,6 +35,15 @@
     val context: Context
 
     /**
+     * Returns the [Window] associated with the active presentation window area or null if there is
+     * no [Window] currently active. This could occur if the presenter has already been dismissed,
+     * and there is no expectation that the [Window] would become non-null at a later point. This
+     * API can be used to directly access parts of the [Window] API that are not available through
+     * the [Context] provided.
+     */
+    val window: Window?
+
+    /**
      * Sets a [View] to show on a window area. After setting the view the system can turn on the
      * corresponding display and start showing content.
      */
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi2Requirements.java b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi2Requirements.java
deleted file mode 100644
index 0ab78c0..0000000
--- a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi2Requirements.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.area.reflectionguard;
-
-import android.app.Activity;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.window.extensions.area.WindowAreaComponent;
-import androidx.window.extensions.core.util.function.Consumer;
-
-/**
- * This file defines the Vendor API Level 2 Requirements for WindowAreaComponent. This is used
- * in the client library to perform reflection guard to ensure that the OEM extension implementation
- * is complete.
- *
- * @see WindowAreaComponent
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface WindowAreaComponentApi2Requirements {
-
-    /** @see WindowAreaComponent#addRearDisplayStatusListener */
-    void addRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
-
-    /** @see WindowAreaComponent#removeRearDisplayStatusListener */
-    void removeRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
-
-    /** @see WindowAreaComponent#startRearDisplaySession */
-    void startRearDisplaySession(@NonNull Activity activity,
-            @NonNull Consumer<Integer> consumer);
-
-    /** @see WindowAreaComponent#endRearDisplaySession */
-    void endRearDisplaySession();
-}
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi3Requirements.java b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi3Requirements.java
index 56c54aa..eab11a7 100644
--- a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi3Requirements.java
+++ b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentApi3Requirements.java
@@ -36,7 +36,20 @@
  * @see WindowAreaComponent
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface WindowAreaComponentApi3Requirements extends WindowAreaComponentApi2Requirements {
+public interface WindowAreaComponentApi3Requirements {
+
+    /** @see WindowAreaComponent#addRearDisplayStatusListener */
+    void addRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+
+    /** @see WindowAreaComponent#removeRearDisplayStatusListener */
+    void removeRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+
+    /** @see WindowAreaComponent#startRearDisplaySession */
+    void startRearDisplaySession(@NonNull Activity activity,
+            @NonNull Consumer<Integer> consumer);
+
+    /** @see WindowAreaComponent#endRearDisplaySession */
+    void endRearDisplaySession();
 
     /** @see WindowAreaComponent#addRearDisplayPresentationStatusListener */
     void addRearDisplayPresentationStatusListener(
diff --git a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentValidator.kt b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentValidator.kt
index 1ab5cdd..fe135be 100644
--- a/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentValidator.kt
+++ b/window/window/src/main/java/androidx/window/area/reflectionguard/WindowAreaComponentValidator.kt
@@ -24,46 +24,47 @@
 internal object WindowAreaComponentValidator {
 
     internal fun isWindowAreaComponentValid(windowAreaComponent: Class<*>, apiLevel: Int): Boolean {
-        return when {
-            apiLevel <= 1 -> false
-            apiLevel == 2 ->
-                validateImplementation(
-                    windowAreaComponent,
-                    WindowAreaComponentApi2Requirements::class.java
-                )
-            else ->
-                validateImplementation(
-                    windowAreaComponent,
-                    WindowAreaComponentApi3Requirements::class.java
-                )
-        }
+        val isWindowAreaComponentValid: Boolean =
+            when {
+                apiLevel <= 2 -> false
+                else ->
+                    validateImplementation(
+                        windowAreaComponent,
+                        WindowAreaComponentApi3Requirements::class.java
+                    )
+            }
+        return isWindowAreaComponentValid
     }
 
     internal fun isExtensionWindowAreaStatusValid(
         extensionWindowAreaStatus: Class<*>,
         apiLevel: Int
     ): Boolean {
-        return when {
-            apiLevel <= 1 -> false
-            else ->
-                validateImplementation(
-                    extensionWindowAreaStatus,
-                    ExtensionWindowAreaStatusRequirements::class.java
-                )
-        }
+        val isExtensionWindowAreaStatusValid: Boolean =
+            when {
+                apiLevel <= 2 -> false
+                else ->
+                    validateImplementation(
+                        extensionWindowAreaStatus,
+                        ExtensionWindowAreaStatusRequirements::class.java
+                    )
+            }
+        return isExtensionWindowAreaStatusValid
     }
 
     internal fun isExtensionWindowAreaPresentationValid(
         extensionWindowAreaPresentation: Class<*>,
         apiLevel: Int
     ): Boolean {
-        return when {
-            apiLevel <= 2 -> false
-            else ->
-                validateImplementation(
-                    extensionWindowAreaPresentation,
-                    ExtensionWindowAreaPresentation::class.java
-                )
-        }
+        val isExtensionWindowAreaPresentationValid: Boolean =
+            when {
+                apiLevel <= 2 -> false
+                else ->
+                    validateImplementation(
+                        extensionWindowAreaPresentation,
+                        ExtensionWindowAreaPresentation::class.java
+                    )
+            }
+        return isExtensionWindowAreaPresentationValid
     }
 }
diff --git a/window/window/src/main/java/androidx/window/area/utils/DeviceMetrics.kt b/window/window/src/main/java/androidx/window/area/utils/DeviceMetrics.kt
deleted file mode 100644
index 359e957..0000000
--- a/window/window/src/main/java/androidx/window/area/utils/DeviceMetrics.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.area.utils
-
-import android.util.DisplayMetrics
-
-/** Data class holding metrics about a specific device. */
-internal class DeviceMetrics(
-    val manufacturer: String,
-    val model: String,
-    val rearDisplayMetrics: DisplayMetrics
-) {
-    override fun equals(other: Any?): Boolean {
-        return other is DeviceMetrics &&
-            manufacturer == other.manufacturer &&
-            model == other.model &&
-            rearDisplayMetrics.equals(other.rearDisplayMetrics)
-    }
-
-    override fun hashCode(): Int {
-        var result = manufacturer.hashCode()
-        result = 31 * result + model.hashCode()
-        result = 31 * result + rearDisplayMetrics.hashCode()
-        return result
-    }
-
-    override fun toString(): String {
-        return "DeviceMetrics{ Manufacturer: $manufacturer, model: $model, " +
-            "Rear display metrics: $rearDisplayMetrics }"
-    }
-}
diff --git a/window/window/src/main/java/androidx/window/area/utils/DeviceUtils.kt b/window/window/src/main/java/androidx/window/area/utils/DeviceUtils.kt
deleted file mode 100644
index 33f241d..0000000
--- a/window/window/src/main/java/androidx/window/area/utils/DeviceUtils.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.area.utils
-
-import android.util.DisplayMetrics
-import java.util.Locale
-
-/**
- * Utility object to provide information about specific devices that may not be available through
- * the extensions API at a certain vendor API level
- */
-internal object DeviceUtils {
-
-    private val deviceList =
-        listOf(
-            DeviceMetrics(
-                "google",
-                "pixel fold",
-                DisplayMetrics().apply {
-                    widthPixels = 1080
-                    heightPixels = 2092
-                    density = 2.625f
-                    densityDpi = DisplayMetrics.DENSITY_420
-                }
-            )
-        )
-
-    internal fun hasDeviceMetrics(manufacturer: String, model: String): Boolean {
-        return deviceList.any {
-            it.manufacturer == manufacturer.lowercase(Locale.US) &&
-                it.model == model.lowercase(Locale.US)
-        }
-    }
-
-    internal fun getRearDisplayMetrics(manufacturer: String, model: String): DisplayMetrics? {
-        return deviceList
-            .firstOrNull {
-                it.manufacturer == manufacturer.lowercase(Locale.US) &&
-                    it.model == model.lowercase(Locale.US)
-            }
-            ?.rearDisplayMetrics
-    }
-}
diff --git a/window/window/src/main/java/androidx/window/area/utils/PresentationWindowCompatUtils.kt b/window/window/src/main/java/androidx/window/area/utils/PresentationWindowCompatUtils.kt
new file mode 100644
index 0000000..593fb4c
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/area/utils/PresentationWindowCompatUtils.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.area.utils
+
+import android.annotation.SuppressLint
+import android.view.Window
+import androidx.window.extensions.area.ExtensionWindowAreaPresentation
+import java.lang.reflect.Method
+
+internal object PresentationWindowCompatUtils {
+
+    // We perform our own extensions vendor API level check at the call-site
+    @SuppressLint("BanUncheckedReflection")
+    fun getWindowBeforeVendorApiLevel4(
+        extensionPresentation: ExtensionWindowAreaPresentation
+    ): Window? {
+        val getWindowMethod = getWindowMethod(extensionPresentation)
+        return if (getWindowMethod == null) null
+        else (getWindowMethod.invoke(extensionPresentation) as Window?)
+    }
+
+    private fun getWindowMethod(extensionPresentation: ExtensionWindowAreaPresentation): Method? {
+        return extensionPresentation.javaClass.methods.firstOrNull { method: Method? ->
+            method?.name == "getWindow" && method.returnType == android.view.Window::class.java
+        }
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/core/Bounds.kt b/window/window/src/main/java/androidx/window/core/Bounds.kt
index f821baa..e31ed65 100644
--- a/window/window/src/main/java/androidx/window/core/Bounds.kt
+++ b/window/window/src/main/java/androidx/window/core/Bounds.kt
@@ -28,10 +28,10 @@
  * contain any behavior or calculations.
  */
 internal class Bounds(
-    public val left: Int,
-    public val top: Int,
-    public val right: Int,
-    public val bottom: Int
+    val left: Int,
+    val top: Int,
+    val right: Int,
+    val bottom: Int,
 ) {
     constructor(rect: Rect) : this(rect.left, rect.top, rect.right, rect.bottom)
 
@@ -88,4 +88,8 @@
         result = 31 * result + bottom
         return result
     }
+
+    companion object {
+        val EMPTY_BOUNDS = Bounds(0, 0, 0, 0)
+    }
 }
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt
index 32c6e07..3f6655e 100644
--- a/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingController.kt
@@ -17,11 +17,14 @@
 package androidx.window.embedding
 
 import android.app.Activity
-import android.app.ActivityOptions
 import android.content.Context
-import android.os.IBinder
+import android.os.Bundle
+import androidx.core.util.Consumer
 import androidx.window.RequiresWindowSdkExtension
-import androidx.window.core.ExperimentalWindowApi
+import androidx.window.WindowSdkExtensions
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
 
 /** The controller that allows checking the current [Activity] embedding status. */
 class ActivityEmbeddingController internal constructor(private val backend: EmbeddingBackend) {
@@ -31,7 +34,6 @@
      *
      * @param activity the [Activity] to check.
      */
-    // TODO(b/204399167) Migrate to a Flow
     fun isActivityEmbedded(activity: Activity): Boolean = backend.isActivityEmbedded(activity)
 
     /**
@@ -39,27 +41,136 @@
      * embedding container and associated with a [SplitInfo]. Returns `null` if there is no such
      * [ActivityStack].
      *
+     * Started from [WindowSdkExtensions.extensionVersion] 5, this method can also obtain standalone
+     * [ActivityStack], which is not associated with any [SplitInfo]. For example, an
+     * [ActivityStack] launched with [ActivityRule.alwaysExpand], or an overlay [ActivityStack]
+     * launched by [setLaunchingActivityStack] with [OverlayCreateParams].
+     *
      * @param activity The [Activity] to check.
      * @return the [ActivityStack] that this [activity] is part of, or `null` if there is no such
      *   [ActivityStack].
      */
-    @ExperimentalWindowApi
     fun getActivityStack(activity: Activity): ActivityStack? = backend.getActivityStack(activity)
 
     /**
-     * Sets the launching [ActivityStack] to the given [android.app.ActivityOptions].
+     * Sets the launching [ActivityStack] to the given [Bundle].
      *
-     * @param options The [android.app.ActivityOptions] to be updated.
-     * @param token The token of the [ActivityStack] to be set.
+     * Apps can launch an [Activity] into the [ActivityStack] by [Activity.startActivity].
+     *
+     * @param options the [Bundle] to be updated.
+     * @param activityStack the [ActivityStack] to be set.
+     */
+    @RequiresWindowSdkExtension(5)
+    internal fun setLaunchingActivityStack(options: Bundle, activityStack: ActivityStack): Bundle =
+        backend.setLaunchingActivityStack(options, activityStack)
+
+    /**
+     * Finishes a set of [activityStacks][ActivityStack] from the lowest to the highest z-order
+     * regardless of the order of `activityStack` passed in the input parameter.
+     *
+     * If a remaining activityStack from a split participates in other splits with other
+     * activityStacks, the remaining activityStack might split with other activityStacks. For
+     * example, if activityStack A splits with activityStack B and C, and activityStack C covers
+     * activityStack B, finishing activityStack C might make the split of activityStack A and B
+     * show.
+     *
+     * If all split-associated activityStacks are finished, the remaining activityStack will be
+     * expanded to fill the parent task container. This is useful to expand the primary container as
+     * the sample linked below shows.
+     *
+     * **Note** that it's caller's responsibility to check whether this API is supported by checking
+     * [WindowSdkExtensions.extensionVersion] is greater than or equal to 5. If not, an alternative
+     * approach to finishing all containers above a particular activity can be to launch it again
+     * with flag [android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP].
+     *
+     * @param activityStacks The set of [ActivityStack] to be finished.
+     * @throws UnsupportedOperationException if extension version is less than 5.
+     * @sample androidx.window.samples.embedding.expandPrimaryContainer
+     */
+    @RequiresWindowSdkExtension(5)
+    fun finishActivityStacks(activityStacks: Set<ActivityStack>) {
+        backend.finishActivityStacks(activityStacks)
+    }
+
+    /**
+     * Sets the [EmbeddingConfiguration] of the Activity Embedding environment that defines how the
+     * embedded Activities behaves.
+     *
+     * The [EmbeddingConfiguration] can be supported only if the vendor API level of the target
+     * device is equals or higher than required API level. Otherwise, it would be no-op when setting
+     * the [EmbeddingConfiguration] on a target device that has lower API level.
+     *
+     * In addition, the existing configuration in the library won't be overwritten if the properties
+     * of the given [embeddingConfiguration] are undefined. Only the configuration properties that
+     * are explicitly set will be updated.
+     *
+     * **Note** that it is recommended to be configured in the [androidx.startup.Initializer] or
+     * [android.app.Application.onCreate], so that the [EmbeddingConfiguration] is applied early in
+     * the application startup, before any activities complete initialization. The
+     * [EmbeddingConfiguration] updates afterward may or may not apply to already running
+     * activities.
+     *
+     * @param embeddingConfiguration The [EmbeddingConfiguration]
+     */
+    @RequiresWindowSdkExtension(5)
+    fun setEmbeddingConfiguration(embeddingConfiguration: EmbeddingConfiguration) {
+        backend.setEmbeddingConfiguration(embeddingConfiguration)
+    }
+
+    /**
+     * Triggers calculator functions set through [SplitController.setSplitAttributesCalculator] and
+     * [OverlayController.setOverlayAttributesCalculator] to update attributes for visible
+     * [activityStacks][ActivityStack].
+     *
+     * This method can be used when the application wants to update the embedding presentation based
+     * on the application state.
+     *
+     * This method is not needed for changes that are driven by window and device state changes or
+     * new activity starts, because those will invoke the calculator functions automatically.
+     *
+     * Visible [activityStacks][ActivityStack] are usually the last element of [SplitInfo] list
+     * which was received from the callback registered in [SplitController.splitInfoList] and an
+     * active overlay [ActivityStack] if exists.
+     *
+     * The call will be no-op if there is no visible [activityStacks][ActivityStack] or there's no
+     * calculator set.
+     *
+     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
+     *   than 3.
+     * @see androidx.window.embedding.OverlayController.setOverlayAttributesCalculator
+     * @see androidx.window.embedding.SplitController.setSplitAttributesCalculator
      */
     @RequiresWindowSdkExtension(3)
-    internal fun setLaunchingActivityStack(
-        options: ActivityOptions,
-        token: IBinder
-    ): ActivityOptions {
-        return backend.setLaunchingActivityStack(options, token)
+    fun invalidateVisibleActivityStacks() {
+        backend.invalidateVisibleActivityStacks()
     }
 
+    /**
+     * A [Flow] of [EmbeddedActivityWindowInfo] that reports the change to the embedded window
+     * related info of the [activity].
+     *
+     * The [Flow] will immediately be invoked with the latest value upon registration if the
+     * [activity] is currently embedded as [EmbeddedActivityWindowInfo.isEmbedded] is `true`.
+     *
+     * When the [activity] is embedded, the [Flow] will be invoked when [EmbeddedActivityWindowInfo]
+     * is changed. When the [activity] is not embedded, the [Flow] will not be triggered unless the
+     * [activity] is becoming non-embedded from embedded.
+     *
+     * Note that this API is only supported on the device with
+     * [WindowSdkExtensions.extensionVersion] equal to or larger than 6. If
+     * [WindowSdkExtensions.extensionVersion] is less than 6, this [Flow] will not be invoked.
+     *
+     * @param activity the [Activity] that is interested in getting the embedded window info.
+     * @return a [Flow] of [EmbeddedActivityWindowInfo] of the [activity].
+     */
+    @RequiresWindowSdkExtension(6)
+    fun embeddedActivityWindowInfo(activity: Activity): Flow<EmbeddedActivityWindowInfo> =
+        callbackFlow {
+            val callback = Consumer { info: EmbeddedActivityWindowInfo -> trySend(info) }
+            backend.addEmbeddedActivityWindowInfoCallbackForActivity(activity, callback)
+            awaitClose { backend.removeEmbeddedActivityWindowInfoCallbackForActivity(callback) }
+        }
+
     companion object {
         /**
          * Obtains an instance of [ActivityEmbeddingController].
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptions.kt b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptions.kt
new file mode 100644
index 0000000..b3581ef
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptions.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("ActivityEmbeddingOptions")
+
+package androidx.window.embedding
+
+import android.app.Activity
+import android.app.ActivityOptions
+import android.content.Context
+import android.os.Bundle
+import androidx.annotation.RestrictTo
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
+
+/**
+ * Sets the target launching [ActivityStack] to the given [Bundle].
+ *
+ * The [Bundle] then could be used to launch an [Activity] to the top of the [ActivityStack] through
+ * [Activity.startActivity]. If there's a bundle used for customizing how the [Activity] should be
+ * started by [ActivityOptions.toBundle] or [androidx.core.app.ActivityOptionsCompat.toBundle], it's
+ * suggested to use the bundle to call this method.
+ *
+ * It is suggested to use a visible [ActivityStack] reported by [SplitController.splitInfoList] or
+ * [OverlayController.overlayInfo], or the launching activity will be launched on the default target
+ * if the [activityStack] no longer exists in the host task. The default target could be the top of
+ * the visible split's secondary [ActivityStack], or the top of the host task.
+ *
+ * Below samples are use cases to specify the launching [ActivityStack].
+ *
+ * @sample androidx.window.samples.embedding.launchingOnPrimaryActivityStack
+ * @sample androidx.window.samples.embedding.launchingOnOverlayActivityStack
+ * @param context The [android.content.Context] that is going to be used for launching activity with
+ *   this [Bundle], which is usually be the [android.app.Activity] of the app that hosts the task.
+ * @param activityStack The target [ActivityStack] for launching.
+ * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less than 5.
+ */
+@RequiresWindowSdkExtension(5)
+fun Bundle.setLaunchingActivityStack(context: Context, activityStack: ActivityStack): Bundle =
+    ActivityEmbeddingController.getInstance(context).setLaunchingActivityStack(this, activityStack)
+
+/**
+ * Puts [OverlayCreateParams] to [Bundle] to create a singleton-per-task overlay [ActivityStack].
+ *
+ * The [Bundle] then could be used to launch an [Activity] to the [ActivityStack] through
+ * [Activity.startActivity]. If there's a bundle used for customizing how the [Activity] should be
+ * started by [ActivityOptions.toBundle] or [androidx.core.app.ActivityOptionsCompat.toBundle], it's
+ * suggested to use the bundle to call this method.
+ *
+ * Below sample shows how to launch an overlay [ActivityStack].
+ *
+ * If there's an existing overlay [ActivityStack] shown, the existing overlay container may be
+ * dismissed or updated based on [OverlayCreateParams.tag] and [activity] because of following
+ * constraints:
+ * 1. A task can hold only one overlay container at most.
+ * 2. An overlay [ActivityStack] tag is unique per process.
+ *
+ * Belows are possible scenarios:
+ * 1. If there's an overlay container with the same `tag` as [OverlayCreateParams.tag] in the same
+ *    task as [activity], the overlay container's [OverlayAttributes] will be updated to
+ *    [OverlayCreateParams.overlayAttributes], and the activity will be launched on the top of the
+ *    overlay [ActivityStack].
+ * 2. If there's an overlay container with different `tag` from [OverlayCreateParams.tag] in the
+ *    same task as [activity], the existing overlay container will be dismissed, and a new overlay
+ *    container will be launched with the new [OverlayCreateParams].
+ * 3. If there's an overlay container with the same `tag` as [OverlayCreateParams.tag] in a
+ *    different task from [activity], the existing overlay container in the other task will be
+ *    dismissed, and a new overlay container will be launched in the task of [activity].
+ *
+ * Note that the second and the third scenarios may happen at the same time if [activity]'s task
+ * holds an overlay container and [OverlayCreateParams.tag] matches an overlay container in a
+ * different task.
+ *
+ * @sample androidx.window.samples.embedding.launchOverlayActivityStackSample
+ * @param activity The [Activity] that is going to be used for launching activity with this
+ *   [ActivityOptions], which is usually be the [Activity] of the app that hosts the task.
+ * @param overlayCreateParams The parameter container to create an overlay [ActivityStack]
+ * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less than 6.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+fun Bundle.setOverlayCreateParams(
+    activity: Activity,
+    overlayCreateParams: OverlayCreateParams
+): Bundle =
+    OverlayController.getInstance(activity).setOverlayCreateParams(this, overlayCreateParams)
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptionsImpl.kt b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptionsImpl.kt
new file mode 100644
index 0000000..34b71fe
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityEmbeddingOptionsImpl.kt
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.os.Bundle
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.embedding.EmbeddingBounds.Dimension
+import androidx.window.embedding.EmbeddingBounds.Dimension.Companion.DIMENSION_EXPANDED
+import androidx.window.embedding.EmbeddingBounds.Dimension.Companion.DIMENSION_HINGE
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
+import androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_ACTIVITY_STACK_TOKEN
+import androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG
+import androidx.window.extensions.embedding.ActivityStack.Token
+
+/**
+ * The implementation of ActivityEmbeddingOptions in WM Jetpack which uses constants defined in
+ * [androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties] and this object.
+ */
+internal object ActivityEmbeddingOptionsImpl {
+
+    /**
+     * Key of [EmbeddingBounds].
+     *
+     * Type: [Bundle]
+     *
+     * Properties:
+     *
+     * | Key                              | Type     | Property                    |
+     * |----------------------------------|----------|-----------------------------|
+     * | [KEY_EMBEDDING_BOUNDS_ALIGNMENT] | [Int]    | [EmbeddingBounds.alignment] |
+     * | [KEY_EMBEDDING_BOUNDS_WIDTH]     | [Bundle] | [EmbeddingBounds.width]     |
+     * | [KEY_EMBEDDING_BOUNDS_HEIGHT]    | [Bundle] | [EmbeddingBounds.height]    |
+     */
+    private const val KEY_EMBEDDING_BOUNDS = "androidx.window.embedding.EmbeddingBounds"
+
+    /**
+     * Key of [EmbeddingBounds.alignment].
+     *
+     * Type: [Int]
+     *
+     * Valid values are:
+     * - 0: [EmbeddingBounds.Alignment.ALIGN_LEFT]
+     * - 1: [EmbeddingBounds.Alignment.ALIGN_TOP]
+     * - 2: [EmbeddingBounds.Alignment.ALIGN_RIGHT]
+     * - 3: [EmbeddingBounds.Alignment.ALIGN_BOTTOM]
+     */
+    private const val KEY_EMBEDDING_BOUNDS_ALIGNMENT =
+        "androidx.window.embedding.EmbeddingBounds.alignment"
+
+    /**
+     * Key of [EmbeddingBounds.width].
+     *
+     * Type: [Bundle] with [putDimension]
+     *
+     * Properties:
+     *
+     * | Key                                    | Type           | Property            |
+     * |----------------------------------------|----------------|---------------------|
+     * | [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE]  | [String]       | The dimension type  |
+     * | [KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE] | [Int], [Float] | The dimension value |
+     */
+    private const val KEY_EMBEDDING_BOUNDS_WIDTH = "androidx.window.embedding.EmbeddingBounds.width"
+
+    /**
+     * Key of [EmbeddingBounds.width].
+     *
+     * Type: [Bundle] with [putDimension]
+     *
+     * Properties:
+     *
+     * | Key                                    | Type           | Property            |
+     * |----------------------------------------|----------------|---------------------|
+     * | [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE]  | [String]       | The dimension type  |
+     * | [KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE] | [Int], [Float] | The dimension value |
+     */
+    private const val KEY_EMBEDDING_BOUNDS_HEIGHT =
+        "androidx.window.embedding.EmbeddingBounds.height"
+
+    /**
+     * A [Dimension] type passed with [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE], which indicates the
+     * [Dimension] is [DIMENSION_EXPANDED].
+     */
+    private const val DIMENSION_TYPE_EXPANDED = "expanded"
+
+    /**
+     * A [Dimension] type passed with [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE], which indicates the
+     * [Dimension] is [DIMENSION_HINGE].
+     */
+    private const val DIMENSION_TYPE_HINGE = "hinge"
+
+    /**
+     * A [Dimension] type passed with [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE], which indicates the
+     * [Dimension] is from [Dimension.ratio]. If this type is used,
+     * [KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE] should also be specified with a [Float] value.
+     */
+    private const val DIMENSION_TYPE_RATIO = "ratio"
+
+    /**
+     * A [Dimension] type passed with [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE], which indicates the
+     * [Dimension] is from [Dimension.pixel]. If this type is used,
+     * [KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE] should also be specified with a [Int] value.
+     */
+    private const val DIMENSION_TYPE_PIXEL = "pixel"
+
+    /**
+     * Key of [EmbeddingBounds.Dimension] type.
+     *
+     * Type: [String]
+     *
+     * Valid values are:
+     * - [DIMENSION_TYPE_EXPANDED]: [DIMENSION_EXPANDED]
+     * - [DIMENSION_TYPE_HINGE]: [DIMENSION_HINGE]
+     * - [DIMENSION_TYPE_RATIO]: [Dimension.ratio]
+     * - [DIMENSION_TYPE_PIXEL]: [Dimension.pixel]
+     */
+    private const val KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE =
+        "androidx.window.embedding.EmbeddingBounds.dimension_type"
+
+    /**
+     * Key of [EmbeddingBounds.Dimension] value.
+     *
+     * Type: [Float] or [Int]
+     *
+     * The value passed in [Dimension.pixel] or [Dimension.ratio]:
+     * - Accept [Float] if [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE] is [DIMENSION_TYPE_RATIO]
+     * - Accept [Int] if [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE] is [DIMENSION_TYPE_PIXEL]
+     */
+    private const val KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE =
+        "androidx.window.embedding.EmbeddingBounds.dimension_value"
+
+    /**
+     * Key of [ActivityStack] alignment relative to the parent container.
+     *
+     * Type: [Int]
+     *
+     * Valid values are:
+     * - 0: [EmbeddingBounds.Alignment.ALIGN_LEFT]
+     * - 1: [EmbeddingBounds.Alignment.ALIGN_TOP]
+     * - 2: [EmbeddingBounds.Alignment.ALIGN_RIGHT]
+     * - 3: [EmbeddingBounds.Alignment.ALIGN_BOTTOM]
+     */
+    private const val KEY_ACTIVITY_STACK_ALIGNMENT =
+        "androidx.window.embedding.ActivityStackAlignment"
+
+    /**
+     * Puts [OverlayCreateParams] information to [android.app.ActivityOptions] bundle to launch the
+     * overlay container
+     *
+     * @param overlayCreateParams The [OverlayCreateParams] to launch the overlay container
+     */
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    internal fun setOverlayCreateParams(
+        options: Bundle,
+        overlayCreateParams: OverlayCreateParams,
+    ) {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(OVERLAY_FEATURE_VERSION)
+
+        options.putString(KEY_OVERLAY_TAG, overlayCreateParams.tag)
+        options.putEmbeddingBounds(overlayCreateParams.overlayAttributes.bounds)
+    }
+
+    /** Puts [EmbeddingBounds] information into a bundle for tracking. */
+    private fun Bundle.putEmbeddingBounds(embeddingBounds: EmbeddingBounds) {
+        putBundle(
+            KEY_EMBEDDING_BOUNDS,
+            Bundle().apply {
+                putInt(KEY_EMBEDDING_BOUNDS_ALIGNMENT, embeddingBounds.alignment.value)
+                putDimension(KEY_EMBEDDING_BOUNDS_WIDTH, embeddingBounds.width)
+                putDimension(KEY_EMBEDDING_BOUNDS_HEIGHT, embeddingBounds.height)
+            }
+        )
+    }
+
+    /**
+     * Puts the alignment of the overlay [ActivityStack] relative to its parent container.
+     *
+     * It could be used as a hint of the open/close animation direction.
+     */
+    internal fun Bundle.putActivityStackAlignment(embeddingBounds: EmbeddingBounds) {
+        putInt(KEY_ACTIVITY_STACK_ALIGNMENT, embeddingBounds.alignment.value)
+    }
+
+    internal fun Bundle.getOverlayAttributes(): OverlayAttributes? {
+        val embeddingBounds = getEmbeddingBounds() ?: return null
+        return OverlayAttributes(embeddingBounds)
+    }
+
+    private fun Bundle.getEmbeddingBounds(): EmbeddingBounds? {
+        val embeddingBoundsBundle = getBundle(KEY_EMBEDDING_BOUNDS) ?: return null
+        return EmbeddingBounds(
+            EmbeddingBounds.Alignment(embeddingBoundsBundle.getInt(KEY_EMBEDDING_BOUNDS_ALIGNMENT)),
+            embeddingBoundsBundle.getDimension(KEY_EMBEDDING_BOUNDS_WIDTH),
+            embeddingBoundsBundle.getDimension(KEY_EMBEDDING_BOUNDS_HEIGHT)
+        )
+    }
+
+    /**
+     * Retrieves [EmbeddingBounds.Dimension] value from bundle with given [key].
+     *
+     * See [putDimension] for the data structure of [EmbeddingBounds.Dimension] as bundle
+     */
+    private fun Bundle.getDimension(key: String): Dimension {
+        val dimensionBundle = getBundle(key)!!
+        return when (val type = dimensionBundle.getString(KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE)) {
+            DIMENSION_TYPE_EXPANDED -> DIMENSION_EXPANDED
+            DIMENSION_TYPE_HINGE -> DIMENSION_HINGE
+            DIMENSION_TYPE_RATIO ->
+                Dimension.ratio(dimensionBundle.getFloat(KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE))
+            DIMENSION_TYPE_PIXEL ->
+                Dimension.pixel(dimensionBundle.getInt(KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE))
+            else -> throw IllegalArgumentException("Illegal type $type")
+        }
+    }
+
+    /**
+     * Puts [EmbeddingBounds.Dimension] information into bundle with a given [key].
+     *
+     * [EmbeddingBounds.Dimension] is encoded as a [Bundle] with following data structure:
+     * - [KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE]: A `string` type. Must be one of:
+     *     - [DIMENSION_TYPE_EXPANDED]
+     *     - [DIMENSION_TYPE_HINGE]
+     *     - [DIMENSION_TYPE_RATIO]
+     *     - [DIMENSION_TYPE_PIXEL]
+     * - [KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE]: Only specified for [DIMENSION_TYPE_RATIO] and
+     *   [DIMENSION_TYPE_PIXEL]. [DIMENSION_TYPE_RATIO] requires a [Float], while
+     *   [DIMENSION_TYPE_PIXEL] requires a [Int].
+     */
+    private fun Bundle.putDimension(key: String, dimension: Dimension) {
+        putBundle(
+            key,
+            Bundle().apply {
+                when (dimension) {
+                    DIMENSION_EXPANDED -> {
+                        putString(KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE, DIMENSION_TYPE_EXPANDED)
+                    }
+                    DIMENSION_HINGE -> {
+                        putString(KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE, DIMENSION_TYPE_HINGE)
+                    }
+                    is Dimension.Ratio -> {
+                        putString(KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE, DIMENSION_TYPE_RATIO)
+                        putFloat(KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE, dimension.value)
+                    }
+                    is Dimension.Pixel -> {
+                        putString(KEY_EMBEDDING_BOUNDS_DIMENSION_TYPE, DIMENSION_TYPE_PIXEL)
+                        putInt(KEY_EMBEDDING_BOUNDS_DIMENSION_VALUE, dimension.value)
+                    }
+                }
+            }
+        )
+    }
+
+    @RequiresWindowSdkExtension(5)
+    internal fun setActivityStackToken(options: Bundle, activityStackToken: Token) {
+        options.putBundle(KEY_ACTIVITY_STACK_TOKEN, activityStackToken.toBundle())
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt b/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt
index 24b369a..392dd6c 100644
--- a/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityStack.kt
@@ -17,15 +17,16 @@
 
 import android.app.Activity
 import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.extensions.embedding.ActivityStack.Token
 
 /**
  * A container that holds a stack of activities, overlapping and bound to the same rectangle on the
  * screen.
  */
 class ActivityStack
-@RestrictTo(LIBRARY_GROUP)
-constructor(
+internal constructor(
     /**
      * The [Activity] list in this application's process that belongs to this [ActivityStack].
      *
@@ -42,8 +43,29 @@
      * `false`.
      */
     val isEmpty: Boolean,
+    /** A token uniquely identifying this `ActivityStack`. */
+    private val token: Token?,
 ) {
 
+    /**
+     * Creates ActivityStack ONLY for testing.
+     *
+     * @param activitiesInProcess the [Activity] list in this application's process that belongs to
+     *   this [ActivityStack].
+     * @param isEmpty whether there is no [Activity] running in this [ActivityStack].
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    constructor(
+        activitiesInProcess: List<Activity>,
+        isEmpty: Boolean
+    ) : this(activitiesInProcess, isEmpty, token = null)
+
+    @RequiresWindowSdkExtension(5)
+    internal fun getToken(): Token = let {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(5)
+        token!!
+    }
+
     /** Whether this [ActivityStack] contains the [activity]. */
     operator fun contains(activity: Activity): Boolean {
         return activitiesInProcess.contains(activity)
@@ -55,6 +77,7 @@
 
         if (activitiesInProcess != other.activitiesInProcess) return false
         if (isEmpty != other.isEmpty) return false
+        if (token != other.token) return false
 
         return true
     }
@@ -62,9 +85,14 @@
     override fun hashCode(): Int {
         var result = activitiesInProcess.hashCode()
         result = 31 * result + isEmpty.hashCode()
+        result = 31 * result + token.hashCode()
         return result
     }
 
     override fun toString(): String =
-        "ActivityStack{" + "activitiesInProcess=$activitiesInProcess" + ", isEmpty=$isEmpty" + "}"
+        "ActivityStack{" +
+            "activitiesInProcess=$activitiesInProcess" +
+            ", isEmpty=$isEmpty" +
+            ", token=$token" +
+            "}"
 }
diff --git a/window/window/src/main/java/androidx/window/embedding/ActivityWindowInfoCallbackController.kt b/window/window/src/main/java/androidx/window/embedding/ActivityWindowInfoCallbackController.kt
new file mode 100644
index 0000000..c63928f
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/ActivityWindowInfoCallbackController.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.app.Activity
+import android.graphics.Rect
+import android.util.ArrayMap
+import androidx.annotation.GuardedBy
+import androidx.annotation.VisibleForTesting
+import androidx.core.util.Consumer as JetpackConsumer
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.extensions.core.util.function.Consumer
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import androidx.window.extensions.embedding.EmbeddedActivityWindowInfo as ExtensionsActivityWindowInfo
+import androidx.window.reflection.Consumer2
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+/** Manages and dispatches update of [EmbeddedActivityWindowInfo]. */
+@RequiresWindowSdkExtension(6)
+internal open class ActivityWindowInfoCallbackController(
+    private val embeddingExtension: ActivityEmbeddingComponent,
+) {
+    private val globalLock = ReentrantLock()
+
+    @GuardedBy("globalLock") private val extensionsCallback: Consumer<ExtensionsActivityWindowInfo>
+
+    @VisibleForTesting
+    @GuardedBy("globalLock")
+    internal var callbacks:
+        MutableMap<JetpackConsumer<EmbeddedActivityWindowInfo>, CallbackWrapper> =
+        ArrayMap()
+
+    init {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(6)
+        extensionsCallback =
+            Consumer2<ExtensionsActivityWindowInfo> { info ->
+                globalLock.withLock {
+                    for (callbackWrapper in callbacks.values) {
+                        callbackWrapper.accept(info)
+                    }
+                }
+            }
+    }
+
+    fun addCallback(activity: Activity, callback: JetpackConsumer<EmbeddedActivityWindowInfo>) {
+        globalLock.withLock {
+            if (callbacks.isEmpty()) {
+                // Register when the first callback is added.
+                embeddingExtension.setEmbeddedActivityWindowInfoCallback(
+                    Runnable::run,
+                    extensionsCallback
+                )
+            }
+
+            val callbackWrapper = CallbackWrapper(activity, callback)
+            callbacks[callback] = callbackWrapper
+            embeddingExtension.getEmbeddedActivityWindowInfo(activity)?.apply {
+                // Trigger with the latest info if the window exists.
+                callbackWrapper.accept(this)
+            }
+        }
+    }
+
+    fun removeCallback(callback: JetpackConsumer<EmbeddedActivityWindowInfo>) {
+        globalLock.withLock {
+            if (callbacks.remove(callback) == null) {
+                // Early return if the callback is not registered.
+                return
+            }
+            if (callbacks.isEmpty()) {
+                // Unregister when the last callback is removed.
+                embeddingExtension.clearEmbeddedActivityWindowInfoCallback()
+            }
+        }
+    }
+
+    /** Translates from Extensions info to Jetpack info. */
+    @VisibleForTesting
+    internal open fun translate(info: ExtensionsActivityWindowInfo): EmbeddedActivityWindowInfo {
+        val parentHostBounds = Rect(info.taskBounds)
+        val boundsInParentHost = Rect(info.activityStackBounds)
+        // Converting to host container coordinate.
+        boundsInParentHost.offset(-parentHostBounds.left, -parentHostBounds.top)
+        return EmbeddedActivityWindowInfo(
+            isEmbedded = info.isEmbedded,
+            parentHostBounds = parentHostBounds,
+            boundsInParentHost = boundsInParentHost
+        )
+    }
+
+    @VisibleForTesting
+    internal inner class CallbackWrapper(
+        private val activity: Activity,
+        val callback: JetpackConsumer<EmbeddedActivityWindowInfo>
+    ) {
+        var lastReportedInfo: EmbeddedActivityWindowInfo? = null
+
+        fun accept(extensionsActivityWindowInfo: ExtensionsActivityWindowInfo) {
+            val updatedActivity = extensionsActivityWindowInfo.activity
+            if (activity != updatedActivity) {
+                return
+            }
+
+            val newInfo = translate(extensionsActivityWindowInfo)
+            if (shouldReportInfo(newInfo)) {
+                lastReportedInfo = newInfo
+                callback.accept(newInfo)
+            }
+        }
+
+        private fun shouldReportInfo(newInfo: EmbeddedActivityWindowInfo): Boolean =
+            lastReportedInfo?.let {
+                if (it.isEmbedded != newInfo.isEmbedded) {
+                    // Always report if the embedded status changes
+                    return true
+                }
+                if (!newInfo.isEmbedded) {
+                    // Do not report if the activity is not embedded
+                    return false
+                }
+                return it != newInfo
+            } ?: newInfo.isEmbedded // Always report the first available info if it is embedded
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/DividerAttributes.kt b/window/window/src/main/java/androidx/window/embedding/DividerAttributes.kt
new file mode 100644
index 0000000..48cb686
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/DividerAttributes.kt
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.graphics.Color
+import androidx.annotation.ColorInt
+import androidx.annotation.FloatRange
+import androidx.annotation.IntRange
+import androidx.window.RequiresWindowSdkExtension
+
+/**
+ * The attributes of the divider layout and behavior.
+ *
+ * @property widthDp the width of the divider.
+ * @property color the color of the divider.
+ * @see SplitAttributes.Builder.setDividerAttributes
+ * @see FixedDividerAttributes
+ * @see DraggableDividerAttributes
+ * @see NO_DIVIDER
+ */
+abstract class DividerAttributes
+private constructor(
+    @IntRange(from = WIDTH_SYSTEM_DEFAULT.toLong()) val widthDp: Int = WIDTH_SYSTEM_DEFAULT,
+    @ColorInt val color: Int = Color.BLACK,
+) {
+    override fun toString(): String =
+        DividerAttributes::class.java.simpleName + "{" + "width=$widthDp, " + "color=$color" + "}"
+
+    /**
+     * The attributes of a fixed divider. A fixed divider is a divider type that draws a static line
+     * between the primary and secondary containers.
+     *
+     * @property widthDp the width of the divider.
+     * @property color the color of the divider.
+     * @see SplitAttributes.Builder.setDividerAttributes
+     */
+    class FixedDividerAttributes
+    @RequiresWindowSdkExtension(6)
+    private constructor(
+        @IntRange(from = WIDTH_SYSTEM_DEFAULT.toLong()) widthDp: Int = WIDTH_SYSTEM_DEFAULT,
+        @ColorInt color: Int = Color.BLACK
+    ) : DividerAttributes(widthDp, color) {
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is FixedDividerAttributes) return false
+            return widthDp == other.widthDp && color == other.color
+        }
+
+        override fun hashCode(): Int = widthDp * 31 + color
+
+        /**
+         * The [FixedDividerAttributes] builder.
+         *
+         * @constructor creates a new [FixedDividerAttributes.Builder]
+         */
+        @RequiresWindowSdkExtension(6)
+        class Builder() {
+            @IntRange(from = WIDTH_SYSTEM_DEFAULT.toLong())
+            private var widthDp = WIDTH_SYSTEM_DEFAULT
+
+            @ColorInt private var color = Color.BLACK
+
+            /**
+             * The [FixedDividerAttributes] builder constructor initialized by an existing
+             * [FixedDividerAttributes].
+             *
+             * @param original the original [FixedDividerAttributes] to initialize the [Builder].
+             */
+            @RequiresWindowSdkExtension(6)
+            constructor(original: FixedDividerAttributes) : this() {
+                widthDp = original.widthDp
+                color = original.color
+            }
+
+            /**
+             * Sets the divider width. It defaults to [WIDTH_SYSTEM_DEFAULT], which means the system
+             * will choose a default value based on the display size and form factor.
+             *
+             * @throws IllegalArgumentException if the provided value is invalid.
+             */
+            @RequiresWindowSdkExtension(6)
+            fun setWidthDp(@IntRange(from = WIDTH_SYSTEM_DEFAULT.toLong()) widthDp: Int): Builder =
+                apply {
+                    validateWidth(widthDp)
+                    this.widthDp = widthDp
+                }
+
+            /**
+             * Sets the color of the divider. If not set, the default color [Color.BLACK] is used.
+             *
+             * @throws IllegalArgumentException if the provided value is invalid.
+             */
+            @RequiresWindowSdkExtension(6)
+            fun setColor(@ColorInt color: Int): Builder = apply {
+                validateColor(color)
+                this.color = color
+            }
+
+            /** Builds a [FixedDividerAttributes] instance. */
+            @RequiresWindowSdkExtension(6)
+            fun build(): FixedDividerAttributes {
+                return FixedDividerAttributes(widthDp = widthDp, color = color)
+            }
+        }
+    }
+
+    /**
+     * The attributes of a draggable divider. A draggable divider draws a line between the primary
+     * and secondary containers with a drag handle that the user can drag and resize the containers.
+     *
+     * While dragging, the content of the activity is temporarily covered by a solid color veil,
+     * where the color is determined by the window background color of the activity. Apps may use
+     * [android.app.Activity.getWindow] and [android.view.Window.setBackgroundDrawable] to configure
+     * the veil colors.
+     *
+     * @property widthDp the width of the divider.
+     * @property color the color of the divider.
+     * @property dragRange the range that a divider is allowed to be dragged. When the user drags
+     *   the divider beyond this range, the system will choose to either fully expand the container
+     *   or move the divider back into the range.
+     * @see SplitAttributes.Builder.setDividerAttributes
+     */
+    class DraggableDividerAttributes
+    @RequiresWindowSdkExtension(6)
+    private constructor(
+        @IntRange(from = WIDTH_SYSTEM_DEFAULT.toLong()) widthDp: Int = WIDTH_SYSTEM_DEFAULT,
+        @ColorInt color: Int = Color.BLACK,
+        val dragRange: DragRange = DragRange.DRAG_RANGE_SYSTEM_DEFAULT,
+    ) : DividerAttributes(widthDp, color) {
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is DraggableDividerAttributes) return false
+            return widthDp == other.widthDp && color == other.color && dragRange == other.dragRange
+        }
+
+        override fun hashCode(): Int = (widthDp * 31 + color) * 31 + dragRange.hashCode()
+
+        override fun toString(): String =
+            DividerAttributes::class.java.simpleName +
+                "{" +
+                "width=$widthDp, " +
+                "color=$color, " +
+                "primaryContainerDragRange=$dragRange" +
+                "}"
+
+        /**
+         * The [DraggableDividerAttributes] builder.
+         *
+         * @constructor creates a new [DraggableDividerAttributes.Builder]
+         */
+        @RequiresWindowSdkExtension(6)
+        class Builder() {
+            @IntRange(from = WIDTH_SYSTEM_DEFAULT.toLong())
+            private var widthDp = WIDTH_SYSTEM_DEFAULT
+
+            @ColorInt private var color = Color.BLACK
+
+            private var dragRange: DragRange = DragRange.DRAG_RANGE_SYSTEM_DEFAULT
+
+            /**
+             * The [DraggableDividerAttributes] builder constructor initialized by an existing
+             * [DraggableDividerAttributes].
+             *
+             * @param original the original [DraggableDividerAttributes] to initialize the [Builder]
+             */
+            @RequiresWindowSdkExtension(6)
+            constructor(original: DraggableDividerAttributes) : this() {
+                widthDp = original.widthDp
+                dragRange = original.dragRange
+                color = original.color
+            }
+
+            /**
+             * Sets the divider width. It defaults to [WIDTH_SYSTEM_DEFAULT], which means the system
+             * will choose a default value based on the display size and form factor.
+             *
+             * @throws IllegalArgumentException if the provided value is invalid.
+             */
+            @RequiresWindowSdkExtension(6)
+            fun setWidthDp(@IntRange(from = WIDTH_SYSTEM_DEFAULT.toLong()) widthDp: Int): Builder =
+                apply {
+                    validateWidth(widthDp)
+                    this.widthDp = widthDp
+                }
+
+            /**
+             * Sets the color of the divider. If not set, the default color [Color.BLACK] is used.
+             *
+             * @throws IllegalArgumentException if the provided value is invalid.
+             */
+            @RequiresWindowSdkExtension(6)
+            fun setColor(@ColorInt color: Int): Builder = apply {
+                validateColor(color)
+                this.color = color
+            }
+
+            /**
+             * Sets the drag range of the divider in terms of the split ratio of the primary
+             * container. It defaults to [DragRange.DRAG_RANGE_SYSTEM_DEFAULT], which means the
+             * system will choose a default value based on the display size and form factor.
+             *
+             * When the user drags the divider beyond this range, the system will choose to either
+             * fully expand the container or move the divider back into the range.
+             *
+             * @param dragRange the [DragRange] for the draggable divider.
+             */
+            @RequiresWindowSdkExtension(6)
+            fun setDragRange(dragRange: DragRange): Builder = apply { this.dragRange = dragRange }
+
+            /** Builds a [DividerAttributes] instance. */
+            @RequiresWindowSdkExtension(6)
+            fun build(): DraggableDividerAttributes =
+                DraggableDividerAttributes(
+                    widthDp = widthDp,
+                    color = color,
+                    dragRange = dragRange,
+                )
+        }
+    }
+
+    /**
+     * Describes the range that the user is allowed to drag the draggable divider.
+     *
+     * @see SplitRatioDragRange
+     * @see DRAG_RANGE_SYSTEM_DEFAULT
+     */
+    abstract class DragRange private constructor() {
+        /**
+         * A drag range represented as an interval of the primary container's split ratios.
+         *
+         * @constructor constructs a new [SplitRatioDragRange]
+         * @property minRatio the minimum split ratio of the primary container that the user is
+         *   allowed to drag to. When the divider is dragged beyond this ratio, the system will
+         *   choose to either fully expand the secondary container, or move the divider back to this
+         *   ratio.
+         * @property maxRatio the maximum split ratio of the primary container that the user is
+         *   allowed to drag to. When the divider is dragged beyond this ratio, the system will
+         *   choose to either fully expand the primary container, or move the divider back to this
+         *   ratio.
+         * @throws IllegalArgumentException if the provided values are invalid.
+         */
+        class SplitRatioDragRange(
+            @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+            val minRatio: Float,
+            @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+            val maxRatio: Float,
+        ) : DragRange() {
+            init {
+                if (minRatio <= 0.0 || minRatio >= 1.0) {
+                    throw IllegalArgumentException("minRatio must be in the interval (0.0, 1.0)")
+                }
+                if (maxRatio <= 0.0 || maxRatio >= 1.0) {
+                    throw IllegalArgumentException("maxRatio must be in the interval (0.0, 1.0)")
+                }
+                if (minRatio > maxRatio) {
+                    throw IllegalArgumentException(
+                        "minRatio must be less than or equal to maxRatio"
+                    )
+                }
+            }
+
+            override fun toString(): String = "SplitRatioDragRange[$minRatio, $maxRatio]"
+
+            override fun equals(other: Any?): Boolean {
+                if (this === other) return true
+                if (other !is SplitRatioDragRange) return false
+                return minRatio == other.minRatio && maxRatio == other.maxRatio
+            }
+
+            override fun hashCode(): Int = minRatio.hashCode() * 31 + maxRatio.hashCode()
+        }
+
+        companion object {
+            /**
+             * A special value to indicate that the system will choose default values based on the
+             * display size and form factor.
+             *
+             * @see DraggableDividerAttributes.dragRange
+             */
+            @JvmField
+            val DRAG_RANGE_SYSTEM_DEFAULT =
+                object : DragRange() {
+                    override fun toString(): String = "DRAG_RANGE_SYSTEM_DEFAULT"
+                }
+        }
+    }
+
+    companion object {
+        /**
+         * A special value to indicate that the system will choose a default value based on the
+         * display size and form factor.
+         *
+         * @see DividerAttributes.widthDp
+         */
+        const val WIDTH_SYSTEM_DEFAULT: Int = -1
+
+        /** Indicates that no divider is requested. */
+        @JvmField
+        val NO_DIVIDER =
+            object : DividerAttributes() {
+                override fun toString(): String = "NO_DIVIDER"
+            }
+
+        private fun validateWidth(widthDp: Int) = run {
+            require(widthDp == WIDTH_SYSTEM_DEFAULT || widthDp >= 0) {
+                "widthDp must be greater than or equal to 0 or WIDTH_SYSTEM_DEFAULT. Got: $widthDp"
+            }
+        }
+
+        private fun validateColor(@ColorInt color: Int) = run {
+            require(color.alpha() == 255) {
+                "Divider color must be opaque. Got: ${Integer.toHexString(color)}"
+            }
+        }
+
+        /**
+         * Returns the alpha value of the color. This is the same as [Color.alpha] and is used to
+         * avoid test-time dependency.
+         */
+        private fun Int.alpha() = this ushr 24
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddedActivityWindowInfo.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddedActivityWindowInfo.kt
new file mode 100644
index 0000000..f2a3fe6
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddedActivityWindowInfo.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.graphics.Rect
+
+/**
+ * Describes the embedded window related info of an activity.
+ *
+ * When the activity is embedded, the [ActivityEmbeddingController.embeddedActivityWindowInfo] will
+ * be invoked when any fields of [EmbeddedActivityWindowInfo] is changed. When the activity is not
+ * embedded, the [ActivityEmbeddingController.embeddedActivityWindowInfo] will not be triggered
+ * unless the activity is becoming non-embedded from embedded, in which case [isEmbedded] will be
+ * `false`.
+ *
+ * @see ActivityEmbeddingController.embeddedActivityWindowInfo
+ */
+class EmbeddedActivityWindowInfo
+internal constructor(
+    /**
+     * Whether this activity is embedded and its presentation may be customized by the host process
+     * of the task it is associated with.
+     */
+    val isEmbedded: Boolean,
+    /**
+     * The bounds of the host container in display coordinate space, which should be the Task bounds
+     * for regular embedding use case, or if the activity is not embedded.
+     */
+    val parentHostBounds: Rect,
+    /**
+     * The relative bounds of the embedded [ActivityStack] in the host container coordinate space.
+     * It has the same size as [parentHostBounds] if the activity is not embedded.
+     */
+    val boundsInParentHost: Rect,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is EmbeddedActivityWindowInfo) return false
+
+        if (isEmbedded != other.isEmbedded) return false
+        if (parentHostBounds != other.parentHostBounds) return false
+        if (boundsInParentHost != other.boundsInParentHost) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = isEmbedded.hashCode()
+        result = 31 * result + parentHostBounds.hashCode()
+        result = 31 * result + boundsInParentHost.hashCode()
+        return result
+    }
+
+    override fun toString(): String =
+        "EmbeddedActivityWindowInfo{" +
+            "isEmbedded=$isEmbedded" +
+            ", parentHostBounds=$parentHostBounds" +
+            ", boundsInParentHost=$boundsInParentHost" +
+            "}"
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
index 0191f1f..0db1ab3 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingAdapter.kt
@@ -22,10 +22,20 @@
 import android.content.Intent
 import android.os.Binder
 import android.util.LayoutDirection
+import android.util.Log
 import android.util.Pair as AndroidPair
-import android.view.WindowMetrics
+import android.view.WindowMetrics as AndroidWindowMetrics
+import androidx.window.RequiresWindowSdkExtension
 import androidx.window.WindowSdkExtensions
+import androidx.window.core.Bounds
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.core.PredicateAdapter
+import androidx.window.embedding.DividerAttributes.DragRange.Companion.DRAG_RANGE_SYSTEM_DEFAULT
+import androidx.window.embedding.DividerAttributes.DragRange.SplitRatioDragRange
+import androidx.window.embedding.DividerAttributes.DraggableDividerAttributes
+import androidx.window.embedding.DividerAttributes.FixedDividerAttributes
+import androidx.window.embedding.EmbeddingConfiguration.DimAreaBehavior.Companion.ON_ACTIVITY_STACK
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
 import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
 import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LEFT_TO_RIGHT
 import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LOCALE
@@ -38,7 +48,12 @@
 import androidx.window.embedding.SplitAttributes.SplitType.Companion.ratio
 import androidx.window.extensions.embedding.ActivityRule as OEMActivityRule
 import androidx.window.extensions.embedding.ActivityRule.Builder as ActivityRuleBuilder
+import androidx.window.extensions.embedding.ActivityStack as OEMActivityStack
+import androidx.window.extensions.embedding.AnimationBackground as OEMEmbeddingAnimationBackground
+import androidx.window.extensions.embedding.DividerAttributes as OEMDividerAttributes
+import androidx.window.extensions.embedding.DividerAttributes.RATIO_SYSTEM_DEFAULT
 import androidx.window.extensions.embedding.EmbeddingRule as OEMEmbeddingRule
+import androidx.window.extensions.embedding.ParentContainerInfo as OEMParentContainerInfo
 import androidx.window.extensions.embedding.SplitAttributes as OEMSplitAttributes
 import androidx.window.extensions.embedding.SplitAttributes.SplitType as OEMSplitType
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType
@@ -49,71 +64,130 @@
 import androidx.window.extensions.embedding.SplitPairRule.FINISH_ADJACENT
 import androidx.window.extensions.embedding.SplitPairRule.FINISH_ALWAYS
 import androidx.window.extensions.embedding.SplitPairRule.FINISH_NEVER
+import androidx.window.extensions.embedding.SplitPinRule as OEMSplitPinRule
+import androidx.window.extensions.embedding.SplitPinRule.Builder as SplitPinRuleBuilder
 import androidx.window.extensions.embedding.SplitPlaceholderRule as OEMSplitPlaceholderRule
 import androidx.window.extensions.embedding.SplitPlaceholderRule.Builder as SplitPlaceholderRuleBuilder
+import androidx.window.extensions.embedding.WindowAttributes as OEMWindowAttributes
+import androidx.window.extensions.embedding.WindowAttributes
 import androidx.window.layout.WindowMetricsCalculator
 import androidx.window.layout.adapter.extensions.ExtensionsWindowLayoutInfoAdapter
+import androidx.window.layout.util.DensityCompatHelper
 import androidx.window.reflection.JFunction2
 import androidx.window.reflection.Predicate2
 
 /** Adapter class that translates data classes between Extension and Jetpack interfaces. */
 internal class EmbeddingAdapter(private val predicateAdapter: PredicateAdapter) {
-    private val vendorApiLevel
+    private val extensionVersion
         get() = WindowSdkExtensions.getInstance().extensionVersion
 
     private val api1Impl = VendorApiLevel1Impl(predicateAdapter)
     private val api2Impl = VendorApiLevel2Impl()
+    private val api3Impl = VendorApiLevel3Impl()
+    @OptIn(ExperimentalWindowApi::class) var embeddingConfiguration: EmbeddingConfiguration? = null
 
     fun translate(splitInfoList: List<OEMSplitInfo>): List<SplitInfo> {
         return splitInfoList.map(this::translate)
     }
 
-    @Suppress("DEPRECATION")
     private fun translate(splitInfo: OEMSplitInfo): SplitInfo {
-        return when (vendorApiLevel) {
+        return when (extensionVersion) {
             1 -> api1Impl.translateCompat(splitInfo)
             2 -> api2Impl.translateCompat(splitInfo)
-            else -> {
-                val primaryActivityStack = splitInfo.primaryActivityStack
-                val secondaryActivityStack = splitInfo.secondaryActivityStack
+            in 3..4 -> api3Impl.translateCompat(splitInfo)
+            else ->
                 SplitInfo(
-                    ActivityStack(
-                        primaryActivityStack.activities,
-                        primaryActivityStack.isEmpty,
-                    ),
-                    ActivityStack(
-                        secondaryActivityStack.activities,
-                        secondaryActivityStack.isEmpty,
-                    ),
+                    translate(splitInfo.primaryActivityStack),
+                    translate(splitInfo.secondaryActivityStack),
                     translate(splitInfo.splitAttributes),
-                    splitInfo.token,
+                    splitInfo.splitInfoToken,
                 )
-            }
         }
     }
 
-    internal fun translate(splitAttributes: OEMSplitAttributes): SplitAttributes =
-        SplitAttributes.Builder()
-            .setSplitType(
-                when (val splitType = splitAttributes.splitType) {
-                    is OEMSplitType.HingeSplitType -> SPLIT_TYPE_HINGE
-                    is OEMSplitType.ExpandContainersSplitType -> SPLIT_TYPE_EXPAND
-                    is RatioSplitType -> ratio(splitType.ratio)
-                    else -> throw IllegalArgumentException("Unknown split type: $splitType")
+    internal fun translate(activityStack: OEMActivityStack): ActivityStack =
+        when (extensionVersion) {
+            in 1..4 -> api1Impl.translateCompat(activityStack)
+            else ->
+                ActivityStack(
+                    activityStack.activities,
+                    activityStack.isEmpty,
+                    activityStack.activityStackToken,
+                )
+        }
+
+    internal fun translate(activityStacks: List<OEMActivityStack>): List<ActivityStack> =
+        activityStacks.map(this::translate)
+
+    internal fun translate(splitAttributes: OEMSplitAttributes): SplitAttributes {
+        val builder =
+            SplitAttributes.Builder()
+                .setSplitType(
+                    when (val splitType = splitAttributes.splitType) {
+                        is OEMSplitType.HingeSplitType -> SPLIT_TYPE_HINGE
+                        is OEMSplitType.ExpandContainersSplitType -> SPLIT_TYPE_EXPAND
+                        is RatioSplitType -> ratio(splitType.ratio)
+                        else -> throw IllegalArgumentException("Unknown split type: $splitType")
+                    }
+                )
+                .setLayoutDirection(
+                    when (val layoutDirection = splitAttributes.layoutDirection) {
+                        OEMSplitAttributes.LayoutDirection.LEFT_TO_RIGHT -> LEFT_TO_RIGHT
+                        OEMSplitAttributes.LayoutDirection.RIGHT_TO_LEFT -> RIGHT_TO_LEFT
+                        OEMSplitAttributes.LayoutDirection.LOCALE -> LOCALE
+                        OEMSplitAttributes.LayoutDirection.TOP_TO_BOTTOM -> TOP_TO_BOTTOM
+                        OEMSplitAttributes.LayoutDirection.BOTTOM_TO_TOP -> BOTTOM_TO_TOP
+                        else ->
+                            throw IllegalArgumentException(
+                                "Unknown layout direction: $layoutDirection"
+                            )
+                    }
+                )
+        if (extensionVersion >= 5) {
+            val animationBackground = splitAttributes.animationBackground
+            builder.setAnimationBackground(
+                if (animationBackground is OEMEmbeddingAnimationBackground.ColorBackground) {
+                    EmbeddingAnimationBackground.createColorBackground(animationBackground.color)
+                } else {
+                    EmbeddingAnimationBackground.DEFAULT
                 }
             )
-            .setLayoutDirection(
-                when (val layoutDirection = splitAttributes.layoutDirection) {
-                    OEMSplitAttributes.LayoutDirection.LEFT_TO_RIGHT -> LEFT_TO_RIGHT
-                    OEMSplitAttributes.LayoutDirection.RIGHT_TO_LEFT -> RIGHT_TO_LEFT
-                    OEMSplitAttributes.LayoutDirection.LOCALE -> LOCALE
-                    OEMSplitAttributes.LayoutDirection.TOP_TO_BOTTOM -> TOP_TO_BOTTOM
-                    OEMSplitAttributes.LayoutDirection.BOTTOM_TO_TOP -> BOTTOM_TO_TOP
-                    else ->
-                        throw IllegalArgumentException("Unknown layout direction: $layoutDirection")
-                }
+        }
+        if (extensionVersion >= 6) {
+            builder.setDividerAttributes(
+                translateDividerAttributes(splitAttributes.dividerAttributes)
             )
-            .build()
+        }
+        return builder.build()
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    @OptIn(ExperimentalWindowApi::class)
+    @SuppressLint("NewApi", "ClassVerificationFailure")
+    internal fun translate(
+        parentContainerInfo: OEMParentContainerInfo,
+    ): ParentContainerInfo {
+        val configuration = parentContainerInfo.configuration
+        val density =
+            DensityCompatHelper.getInstance()
+                .density(parentContainerInfo.configuration, parentContainerInfo.windowMetrics)
+        val windowMetrics =
+            WindowMetricsCalculator.translateWindowMetrics(
+                parentContainerInfo.windowMetrics,
+                density
+            )
+
+        return ParentContainerInfo(
+            Bounds(windowMetrics.bounds),
+            ExtensionsWindowLayoutInfoAdapter.translate(
+                windowMetrics,
+                parentContainerInfo.windowLayoutInfo
+            ),
+            windowMetrics.getWindowInsets(),
+            configuration,
+            density
+        )
+    }
 
     fun translateSplitAttributesCalculator(
         calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
@@ -123,32 +197,35 @@
         }
 
     @SuppressLint("NewApi")
-    fun translate(params: OEMSplitAttributesCalculatorParams): SplitAttributesCalculatorParams =
-        let {
-            val taskWindowMetrics = params.parentWindowMetrics
-            val taskConfiguration = params.parentConfiguration
-            val windowLayoutInfo = params.parentWindowLayoutInfo
-            val defaultSplitAttributes = params.defaultSplitAttributes
-            val areDefaultConstraintsSatisfied = params.areDefaultConstraintsSatisfied()
-            val splitRuleTag = params.splitRuleTag
-            val windowMetrics = WindowMetricsCalculator.translateWindowMetrics(taskWindowMetrics)
-
-            SplitAttributesCalculatorParams(
-                windowMetrics,
-                taskConfiguration,
-                ExtensionsWindowLayoutInfoAdapter.translate(windowMetrics, windowLayoutInfo),
-                translate(defaultSplitAttributes),
-                areDefaultConstraintsSatisfied,
-                splitRuleTag,
-            )
-        }
+    fun translate(
+        params: OEMSplitAttributesCalculatorParams,
+    ): SplitAttributesCalculatorParams = let {
+        val taskWindowMetrics = params.parentWindowMetrics
+        val taskConfiguration = params.parentConfiguration
+        val windowLayoutInfo = params.parentWindowLayoutInfo
+        val defaultSplitAttributes = params.defaultSplitAttributes
+        val areDefaultConstraintsSatisfied = params.areDefaultConstraintsSatisfied()
+        val splitRuleTag = params.splitRuleTag
+        val density =
+            DensityCompatHelper.getInstance().density(taskConfiguration, taskWindowMetrics)
+        val windowMetrics =
+            WindowMetricsCalculator.translateWindowMetrics(taskWindowMetrics, density)
+        SplitAttributesCalculatorParams(
+            windowMetrics,
+            taskConfiguration,
+            ExtensionsWindowLayoutInfoAdapter.translate(windowMetrics, windowLayoutInfo),
+            translate(defaultSplitAttributes),
+            areDefaultConstraintsSatisfied,
+            splitRuleTag,
+        )
+    }
 
     private fun translateSplitPairRule(
         context: Context,
         rule: SplitPairRule,
         predicateClass: Class<*>
     ): OEMSplitPairRule {
-        if (vendorApiLevel < 2) {
+        if (extensionVersion < 2) {
             return api1Impl.translateSplitPairRuleCompat(context, rule, predicateClass)
         } else {
             val activitiesPairPredicate =
@@ -167,7 +244,7 @@
                     }
                 }
             val windowMetricsPredicate =
-                Predicate2<WindowMetrics> { windowMetrics ->
+                Predicate2<AndroidWindowMetrics> { windowMetrics ->
                     rule.checkParentMetrics(context, windowMetrics)
                 }
             val tag = rule.tag
@@ -195,30 +272,77 @@
         }
     }
 
+    @OptIn(ExperimentalWindowApi::class)
+    fun translateSplitPinRule(context: Context, splitPinRule: SplitPinRule): OEMSplitPinRule {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(5)
+        val windowMetricsPredicate =
+            Predicate2<AndroidWindowMetrics> { windowMetrics ->
+                splitPinRule.checkParentMetrics(context, windowMetrics)
+            }
+        val builder =
+            SplitPinRuleBuilder(
+                translateSplitAttributes(splitPinRule.defaultSplitAttributes),
+                windowMetricsPredicate
+            )
+        builder.setSticky(splitPinRule.isSticky)
+        val tag = splitPinRule.tag
+        if (tag != null) {
+            builder.setTag(tag)
+        }
+        return builder.build()
+    }
+
+    @OptIn(ExperimentalWindowApi::class)
     fun translateSplitAttributes(splitAttributes: SplitAttributes): OEMSplitAttributes {
-        require(vendorApiLevel >= 2)
+        require(extensionVersion >= 2)
         // To workaround the "unused" error in ktlint. It is necessary to translate SplitAttributes
         // from WM Jetpack version to WM extension version.
-        return androidx.window.extensions.embedding.SplitAttributes.Builder()
-            .setSplitType(translateSplitType(splitAttributes.splitType))
-            .setLayoutDirection(
-                when (splitAttributes.layoutDirection) {
-                    LOCALE -> OEMSplitAttributes.LayoutDirection.LOCALE
-                    LEFT_TO_RIGHT -> OEMSplitAttributes.LayoutDirection.LEFT_TO_RIGHT
-                    RIGHT_TO_LEFT -> OEMSplitAttributes.LayoutDirection.RIGHT_TO_LEFT
-                    TOP_TO_BOTTOM -> OEMSplitAttributes.LayoutDirection.TOP_TO_BOTTOM
-                    BOTTOM_TO_TOP -> OEMSplitAttributes.LayoutDirection.BOTTOM_TO_TOP
-                    else ->
-                        throw IllegalArgumentException(
-                            "Unsupported layoutDirection:" + "$splitAttributes.layoutDirection"
-                        )
-                }
+        val builder =
+            OEMSplitAttributes.Builder()
+                .setSplitType(translateSplitType(splitAttributes.splitType))
+                .setLayoutDirection(
+                    when (splitAttributes.layoutDirection) {
+                        LOCALE -> OEMSplitAttributes.LayoutDirection.LOCALE
+                        LEFT_TO_RIGHT -> OEMSplitAttributes.LayoutDirection.LEFT_TO_RIGHT
+                        RIGHT_TO_LEFT -> OEMSplitAttributes.LayoutDirection.RIGHT_TO_LEFT
+                        TOP_TO_BOTTOM -> OEMSplitAttributes.LayoutDirection.TOP_TO_BOTTOM
+                        BOTTOM_TO_TOP -> OEMSplitAttributes.LayoutDirection.BOTTOM_TO_TOP
+                        else ->
+                            throw IllegalArgumentException(
+                                "Unsupported layoutDirection:" + "$splitAttributes.layoutDirection"
+                            )
+                    }
+                )
+        if (extensionVersion >= 5) {
+            builder
+                .setWindowAttributes(translateWindowAttributes())
+                .setAnimationBackground(
+                    translateAnimationBackground(splitAttributes.animationBackground)
+                )
+        }
+        if (extensionVersion >= 6) {
+            builder.setDividerAttributes(
+                translateDividerAttributes(splitAttributes.dividerAttributes)
             )
-            .build()
+        }
+        return builder.build()
+    }
+
+    /** Translates [embeddingConfiguration] from adapter to [WindowAttributes]. */
+    @OptIn(ExperimentalWindowApi::class)
+    internal fun translateWindowAttributes(): OEMWindowAttributes = let {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(5)
+
+        OEMWindowAttributes(
+            when (embeddingConfiguration?.dimAreaBehavior) {
+                ON_ACTIVITY_STACK -> OEMWindowAttributes.DIM_AREA_ON_ACTIVITY_STACK
+                else -> OEMWindowAttributes.DIM_AREA_ON_TASK
+            }
+        )
     }
 
     private fun translateSplitType(splitType: SplitType): OEMSplitType {
-        require(vendorApiLevel >= 2)
+        require(extensionVersion >= 2)
         return when (splitType) {
             SPLIT_TYPE_HINGE -> OEMSplitType.HingeSplitType(translateSplitType(SPLIT_TYPE_EQUAL))
             SPLIT_TYPE_EXPAND -> OEMSplitType.ExpandContainersSplitType()
@@ -240,7 +364,7 @@
         rule: SplitPlaceholderRule,
         predicateClass: Class<*>
     ): OEMSplitPlaceholderRule {
-        if (vendorApiLevel < 2) {
+        if (extensionVersion < 2) {
             return api1Impl.translateSplitPlaceholderRuleCompat(context, rule, predicateClass)
         } else {
             val activityPredicate =
@@ -252,7 +376,7 @@
                     rule.filters.any { filter -> filter.matchesIntent(intent) }
                 }
             val windowMetricsPredicate =
-                Predicate2<WindowMetrics> { windowMetrics ->
+                Predicate2<AndroidWindowMetrics> { windowMetrics ->
                     rule.checkParentMetrics(context, windowMetrics)
                 }
             val tag = rule.tag
@@ -289,7 +413,7 @@
         rule: ActivityRule,
         predicateClass: Class<*>
     ): OEMActivityRule {
-        if (vendorApiLevel < 2) {
+        if (extensionVersion < 2) {
             return api1Impl.translateActivityRuleCompat(rule, predicateClass)
         } else {
             val activityPredicate =
@@ -326,29 +450,113 @@
             .toSet()
     }
 
+    private fun translateAnimationBackground(
+        animationBackground: EmbeddingAnimationBackground
+    ): OEMEmbeddingAnimationBackground {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(5)
+        return if (animationBackground is EmbeddingAnimationBackground.ColorBackground) {
+            OEMEmbeddingAnimationBackground.createColorBackground(animationBackground.color)
+        } else {
+            OEMEmbeddingAnimationBackground.ANIMATION_BACKGROUND_DEFAULT
+        }
+    }
+
+    @RequiresWindowSdkExtension(6)
+    fun translateDividerAttributes(dividerAttributes: DividerAttributes): OEMDividerAttributes? {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(6)
+        if (dividerAttributes === DividerAttributes.NO_DIVIDER) {
+            return null
+        }
+        val builder =
+            OEMDividerAttributes.Builder(
+                    when (dividerAttributes) {
+                        is FixedDividerAttributes -> OEMDividerAttributes.DIVIDER_TYPE_FIXED
+                        is DraggableDividerAttributes -> OEMDividerAttributes.DIVIDER_TYPE_DRAGGABLE
+                        else ->
+                            throw IllegalArgumentException(
+                                "Unknown divider attributes $dividerAttributes"
+                            )
+                    }
+                )
+                .setDividerColor(dividerAttributes.color)
+                .setWidthDp(dividerAttributes.widthDp)
+
+        if (
+            dividerAttributes is DraggableDividerAttributes &&
+                dividerAttributes.dragRange is SplitRatioDragRange
+        ) {
+            builder
+                .setPrimaryMinRatio(dividerAttributes.dragRange.minRatio)
+                .setPrimaryMaxRatio(dividerAttributes.dragRange.maxRatio)
+        }
+        return builder.build()
+    }
+
+    @RequiresWindowSdkExtension(6)
+    fun translateDividerAttributes(oemDividerAttributes: OEMDividerAttributes?): DividerAttributes {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(6)
+        if (oemDividerAttributes == null) {
+            return DividerAttributes.NO_DIVIDER
+        }
+        return when (oemDividerAttributes.dividerType) {
+            OEMDividerAttributes.DIVIDER_TYPE_FIXED ->
+                FixedDividerAttributes.Builder()
+                    .setWidthDp(oemDividerAttributes.widthDp)
+                    .setColor(oemDividerAttributes.dividerColor)
+                    .build()
+            OEMDividerAttributes.DIVIDER_TYPE_DRAGGABLE ->
+                DraggableDividerAttributes.Builder()
+                    .setWidthDp(oemDividerAttributes.widthDp)
+                    .setColor(oemDividerAttributes.dividerColor)
+                    .setDragRange(
+                        if (
+                            oemDividerAttributes.primaryMinRatio == RATIO_SYSTEM_DEFAULT &&
+                                oemDividerAttributes.primaryMaxRatio == RATIO_SYSTEM_DEFAULT
+                        )
+                            DRAG_RANGE_SYSTEM_DEFAULT
+                        else
+                            SplitRatioDragRange(
+                                oemDividerAttributes.primaryMinRatio,
+                                oemDividerAttributes.primaryMaxRatio,
+                            )
+                    )
+                    .build()
+            // Default to DividerType.FIXED
+            else -> {
+                Log.w(
+                    TAG,
+                    "Unknown divider type $oemDividerAttributes.dividerType, default" +
+                        " to fixed divider type"
+                )
+                FixedDividerAttributes.Builder()
+                    .setWidthDp(oemDividerAttributes.widthDp)
+                    .setColor(oemDividerAttributes.dividerColor)
+                    .build()
+            }
+        }
+    }
+
+    /** Provides backward compatibility for Window extensions with API level 3 */
+    // Suppress deprecation because this object is to provide backward compatibility.
+    @Suppress("DEPRECATION")
+    private inner class VendorApiLevel3Impl {
+        fun translateCompat(splitInfo: OEMSplitInfo): SplitInfo =
+            SplitInfo(
+                api1Impl.translateCompat(splitInfo.primaryActivityStack),
+                api1Impl.translateCompat(splitInfo.secondaryActivityStack),
+                translate(splitInfo.splitAttributes),
+                splitInfo.token,
+            )
+    }
+
     /** Provides backward compatibility for Window extensions with API level 2 */
     private inner class VendorApiLevel2Impl {
-        fun translateCompat(splitInfo: OEMSplitInfo): SplitInfo {
-            val primaryActivityStack = splitInfo.primaryActivityStack
-            val primaryFragment =
-                ActivityStack(
-                    primaryActivityStack.activities,
-                    primaryActivityStack.isEmpty,
-                )
-
-            val secondaryActivityStack = splitInfo.secondaryActivityStack
-            val secondaryFragment =
-                ActivityStack(
-                    secondaryActivityStack.activities,
-                    secondaryActivityStack.isEmpty,
-                )
-            return SplitInfo(
-                primaryFragment,
-                secondaryFragment,
+        fun translateCompat(splitInfo: OEMSplitInfo): SplitInfo =
+            SplitInfo(
+                api1Impl.translateCompat(splitInfo.primaryActivityStack),
+                api1Impl.translateCompat(splitInfo.secondaryActivityStack),
                 translate(splitInfo.splitAttributes),
-                INVALID_SPLIT_INFO_TOKEN,
             )
-        }
     }
 
     /** Provides backward compatibility for [WindowSdkExtensions] version 1 */
@@ -508,35 +716,31 @@
 
         @SuppressLint("ClassVerificationFailure", "NewApi")
         private fun translateParentMetricsPredicate(context: Context, splitRule: SplitRule): Any =
-            predicateAdapter.buildPredicate(WindowMetrics::class) { windowMetrics ->
+            predicateAdapter.buildPredicate(AndroidWindowMetrics::class) { windowMetrics ->
                 splitRule.checkParentMetrics(context, windowMetrics)
             }
 
         fun translateCompat(splitInfo: OEMSplitInfo): SplitInfo =
             SplitInfo(
-                ActivityStack(
-                    splitInfo.primaryActivityStack.activities,
-                    splitInfo.primaryActivityStack.isEmpty,
-                ),
-                ActivityStack(
-                    splitInfo.secondaryActivityStack.activities,
-                    splitInfo.secondaryActivityStack.isEmpty,
-                ),
+                translateCompat(splitInfo.primaryActivityStack),
+                translateCompat(splitInfo.secondaryActivityStack),
                 getSplitAttributesCompat(splitInfo),
-                INVALID_SPLIT_INFO_TOKEN,
+            )
+
+        fun translateCompat(activityStack: OEMActivityStack): ActivityStack =
+            ActivityStack(
+                activityStack.activities,
+                activityStack.isEmpty,
             )
     }
 
     internal companion object {
+        private val TAG = EmbeddingAdapter::class.simpleName
+
         /**
          * The default token of [SplitInfo], which provides compatibility for device prior to vendor
          * API level 3
          */
         val INVALID_SPLIT_INFO_TOKEN = Binder()
-        /**
-         * The default token of [ActivityStack], which provides compatibility for device prior to
-         * vendor API level 3
-         */
-        val INVALID_ACTIVITY_STACK_TOKEN = Binder()
     }
 }
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingAnimationBackground.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingAnimationBackground.kt
new file mode 100644
index 0000000..ad3e184
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingAnimationBackground.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.graphics.Color
+import androidx.annotation.ColorInt
+import androidx.annotation.IntRange
+
+/**
+ * Background to be used for window transition animations for embedding activities if the animation
+ * requires a background.
+ *
+ * @see SplitAttributes.animationBackground
+ */
+abstract class EmbeddingAnimationBackground private constructor() {
+
+    /**
+     * An {@link EmbeddingAnimationBackground} to specify of using a developer-defined color as the
+     * animation background. Only opaque background is supported.
+     *
+     * @see EmbeddingAnimationBackground.createColorBackground
+     */
+    class ColorBackground
+    internal constructor(
+        /** [ColorInt] to represent the color to use as the background color. */
+        @IntRange(from = Color.BLACK.toLong(), to = Color.WHITE.toLong()) @ColorInt val color: Int
+    ) : EmbeddingAnimationBackground() {
+
+        init {
+            require(Color.alpha(color) == 255) { "Background color must be opaque" }
+        }
+
+        override fun toString() = "ColorBackground{color:${Integer.toHexString(color)}}"
+
+        override fun equals(other: Any?): Boolean {
+            if (other === this) return true
+            if (other !is ColorBackground) return false
+            return color == other.color
+        }
+
+        override fun hashCode() = color.hashCode()
+    }
+
+    /** @see EmbeddingAnimationBackground.DEFAULT */
+    private class DefaultBackground : EmbeddingAnimationBackground() {
+
+        override fun toString() = "DefaultBackground"
+    }
+
+    /** Methods that create various [EmbeddingAnimationBackground]. */
+    companion object {
+
+        /**
+         * Creates a [ColorBackground] to represent the given [color].
+         *
+         * Only opaque color is supported.
+         *
+         * @param color [ColorInt] of an opaque color.
+         * @return the [ColorBackground] representing the [color].
+         * @throws IllegalArgumentException if the [color] is not opaque.
+         * @see [DEFAULT] for the default value, which means to use the current theme window
+         *   background color.
+         */
+        @JvmStatic
+        fun createColorBackground(
+            @IntRange(from = Color.BLACK.toLong(), to = Color.WHITE.toLong()) @ColorInt color: Int
+        ): ColorBackground = ColorBackground(color)
+
+        /**
+         * The special [EmbeddingAnimationBackground] to represent the default value, which means to
+         * use the current theme window background color.
+         */
+        @JvmField val DEFAULT: EmbeddingAnimationBackground = DefaultBackground()
+
+        /** Returns an [EmbeddingAnimationBackground] with the given [color] */
+        internal fun buildFromValue(@ColorInt color: Int): EmbeddingAnimationBackground {
+            return if (Color.alpha(color) != 255) {
+                // Treat any non-opaque color as the default.
+                DEFAULT
+            } else {
+                createColorBackground(color)
+            }
+        }
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
index 2711813..a66b290 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingBackend.kt
@@ -17,12 +17,12 @@
 package androidx.window.embedding
 
 import android.app.Activity
-import android.app.ActivityOptions
 import android.content.Context
-import android.os.IBinder
+import android.os.Bundle
 import androidx.annotation.RestrictTo
 import androidx.core.util.Consumer
 import androidx.window.RequiresWindowSdkExtension
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
 import java.util.concurrent.Executor
 
 /**  */
@@ -48,6 +48,11 @@
 
     fun isActivityEmbedded(activity: Activity): Boolean
 
+    @RequiresWindowSdkExtension(5)
+    fun pinTopActivityStack(taskId: Int, splitPinRule: SplitPinRule): Boolean
+
+    @RequiresWindowSdkExtension(5) fun unpinTopActivityStack(taskId: Int)
+
     @RequiresWindowSdkExtension(2)
     fun setSplitAttributesCalculator(
         calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
@@ -57,14 +62,53 @@
 
     fun getActivityStack(activity: Activity): ActivityStack?
 
-    @RequiresWindowSdkExtension(3)
-    fun setLaunchingActivityStack(options: ActivityOptions, token: IBinder): ActivityOptions
+    @RequiresWindowSdkExtension(5)
+    fun setLaunchingActivityStack(options: Bundle, activityStack: ActivityStack): Bundle
 
-    @RequiresWindowSdkExtension(3) fun invalidateTopVisibleSplitAttributes()
+    @RequiresWindowSdkExtension(5)
+    fun setOverlayCreateParams(options: Bundle, overlayCreateParams: OverlayCreateParams): Bundle
+
+    @RequiresWindowSdkExtension(5) fun finishActivityStacks(activityStacks: Set<ActivityStack>)
+
+    @RequiresWindowSdkExtension(5)
+    fun setEmbeddingConfiguration(embeddingConfig: EmbeddingConfiguration)
+
+    @RequiresWindowSdkExtension(3) fun invalidateVisibleActivityStacks()
 
     @RequiresWindowSdkExtension(3)
     fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes)
 
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun setOverlayAttributesCalculator(
+        calculator: (OverlayAttributesCalculatorParams) -> OverlayAttributes
+    )
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION) fun clearOverlayAttributesCalculator()
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun updateOverlayAttributes(overlayTag: String, overlayAttributes: OverlayAttributes)
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun addOverlayInfoCallback(
+        overlayTag: String,
+        executor: Executor,
+        overlayInfoCallback: Consumer<OverlayInfo>,
+    )
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun removeOverlayInfoCallback(overlayInfoCallback: Consumer<OverlayInfo>)
+
+    @RequiresWindowSdkExtension(6)
+    fun addEmbeddedActivityWindowInfoCallbackForActivity(
+        activity: Activity,
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    )
+
+    @RequiresWindowSdkExtension(6)
+    fun removeEmbeddedActivityWindowInfoCallbackForActivity(
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    )
+
     companion object {
 
         private var decorator: (EmbeddingBackend) -> EmbeddingBackend = { it }
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingBounds.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingBounds.kt
new file mode 100644
index 0000000..42ef668
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingBounds.kt
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.graphics.Rect
+import androidx.annotation.FloatRange
+import androidx.annotation.IntRange
+import androidx.annotation.Px
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
+import androidx.window.core.Bounds
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_BOTTOM
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_LEFT
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_RIGHT
+import androidx.window.embedding.EmbeddingBounds.Alignment.Companion.ALIGN_TOP
+import androidx.window.embedding.EmbeddingBounds.Dimension.Companion.DIMENSION_EXPANDED
+import androidx.window.embedding.EmbeddingBounds.Dimension.Companion.DIMENSION_HINGE
+import androidx.window.embedding.EmbeddingBounds.Dimension.Companion.ratio
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowLayoutInfo
+import kotlin.math.min
+
+/**
+ * The bounds of a standalone [ActivityStack].
+ *
+ * It can be either described with `alignment`, `width` and `height` or predefined constant values.
+ * Some important constants are:
+ * - [BOUNDS_EXPANDED]: To indicate the bounds fills the parent window container.
+ * - [BOUNDS_HINGE_TOP]: To indicate the bounds are at the top of the parent window container while
+ *   its bottom follows the hinge position. Refer to [BOUNDS_HINGE_LEFT], [BOUNDS_HINGE_BOTTOM] and
+ *   [BOUNDS_HINGE_RIGHT] for other bounds that follows the hinge position.
+ *
+ * @constructor creates an embedding bounds.
+ * @property alignment The alignment of the bounds relative to parent window container.
+ * @property width The width of the bounds.
+ * @property height The height of the bounds.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+class EmbeddingBounds(val alignment: Alignment, val width: Dimension, val height: Dimension) {
+    override fun toString(): String {
+        return "Bounds:{alignment=$alignment, width=$width, height=$height}"
+    }
+
+    override fun hashCode(): Int {
+        var result = alignment.hashCode()
+        result = result * 31 + width.hashCode()
+        result = result * 31 + height.hashCode()
+        return result
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is EmbeddingBounds) return false
+        return alignment == other.alignment && width == other.width && height == other.height
+    }
+
+    /** Returns `true` if the [width] should fallback to half of parent task width. */
+    internal fun shouldUseFallbackDimensionForWidth(windowLayoutInfo: WindowLayoutInfo): Boolean {
+        if (width != DIMENSION_HINGE) {
+            return false
+        }
+        return !windowLayoutInfo.isVertical() || alignment in listOf(ALIGN_TOP, ALIGN_BOTTOM)
+    }
+
+    /** Returns `true` if the [height] should fallback to half of parent task height. */
+    internal fun shouldUseFallbackDimensionForHeight(windowLayoutInfo: WindowLayoutInfo): Boolean {
+        if (height != DIMENSION_HINGE) {
+            return false
+        }
+        return !windowLayoutInfo.isHorizontal() || alignment in listOf(ALIGN_LEFT, ALIGN_RIGHT)
+    }
+
+    private fun WindowLayoutInfo.isHorizontal(): Boolean {
+        val foldingFeature = getOnlyFoldingFeatureOrNull() ?: return false
+        return foldingFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
+    }
+
+    private fun WindowLayoutInfo.isVertical(): Boolean {
+        val foldingFeature = getOnlyFoldingFeatureOrNull() ?: return false
+        return foldingFeature.orientation == FoldingFeature.Orientation.VERTICAL
+    }
+
+    /**
+     * Returns [FoldingFeature] if it's the only `FoldingFeature` in [WindowLayoutInfo]. Returns
+     * `null`, otherwise.
+     */
+    private fun WindowLayoutInfo.getOnlyFoldingFeatureOrNull(): FoldingFeature? {
+        val foldingFeatures = displayFeatures.filterIsInstance<FoldingFeature>()
+        return if (foldingFeatures.size == 1) foldingFeatures[0] else null
+    }
+
+    /** Calculates [width] in pixel with [parentContainerBounds] and [windowLayoutInfo]. */
+    @Px
+    internal fun getWidthInPixel(
+        parentContainerBounds: Bounds,
+        windowLayoutInfo: WindowLayoutInfo
+    ): Int {
+        val taskWidth = parentContainerBounds.width
+        val widthDimension =
+            if (shouldUseFallbackDimensionForWidth(windowLayoutInfo)) {
+                ratio(0.5f)
+            } else {
+                width
+            }
+        when (widthDimension) {
+            is Dimension.Ratio -> return widthDimension * taskWidth
+            is Dimension.Pixel -> return min(taskWidth, widthDimension.value)
+            DIMENSION_HINGE -> {
+                // Should be verified by #shouldUseFallbackDimensionForWidth
+                val hingeBounds = windowLayoutInfo.getOnlyFoldingFeatureOrNull()!!.bounds
+                return when (alignment) {
+                    ALIGN_LEFT -> {
+                        hingeBounds.left - parentContainerBounds.left
+                    }
+                    ALIGN_RIGHT -> {
+                        parentContainerBounds.right - hingeBounds.right
+                    }
+                    else -> {
+                        throw IllegalStateException(
+                            "Unhandled condition to get height in pixel! " +
+                                "embeddingBounds=$this taskBounds=$parentContainerBounds " +
+                                "windowLayoutInfo=$windowLayoutInfo"
+                        )
+                    }
+                }
+            }
+            else -> throw IllegalArgumentException("Unhandled width dimension=$width")
+        }
+    }
+
+    /** Calculates [height] in pixel with [parentContainerBounds] and [windowLayoutInfo]. */
+    @Px
+    internal fun getHeightInPixel(
+        parentContainerBounds: Bounds,
+        windowLayoutInfo: WindowLayoutInfo
+    ): Int {
+        val taskHeight = parentContainerBounds.height
+        val heightDimension =
+            if (shouldUseFallbackDimensionForHeight(windowLayoutInfo)) {
+                ratio(0.5f)
+            } else {
+                height
+            }
+        when (heightDimension) {
+            is Dimension.Ratio -> return heightDimension * taskHeight
+            is Dimension.Pixel -> return min(taskHeight, heightDimension.value)
+            DIMENSION_HINGE -> {
+                // Should be verified by #shouldUseFallbackDimensionForWidth
+                val hingeBounds = windowLayoutInfo.getOnlyFoldingFeatureOrNull()!!.bounds
+                return when (alignment) {
+                    ALIGN_TOP -> {
+                        hingeBounds.top - parentContainerBounds.top
+                    }
+                    ALIGN_BOTTOM -> {
+                        parentContainerBounds.bottom - hingeBounds.bottom
+                    }
+                    else -> {
+                        throw IllegalStateException(
+                            "Unhandled condition to get height in pixel! " +
+                                "embeddingBounds=$this taskBounds=$parentContainerBounds " +
+                                "windowLayoutInfo=$windowLayoutInfo"
+                        )
+                    }
+                }
+            }
+            else -> throw IllegalArgumentException("Unhandled width dimension=$width")
+        }
+    }
+
+    /** The position of the bounds relative to parent window container. */
+    class Alignment internal constructor(@IntRange(from = 0, to = 3) internal val value: Int) {
+
+        init {
+            require(value in 0..3)
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Alignment) return false
+            return value == other.value
+        }
+
+        override fun hashCode(): Int {
+            return value
+        }
+
+        override fun toString(): String =
+            when (value) {
+                0 -> "left"
+                1 -> "top"
+                2 -> "right"
+                3 -> "bottom"
+                else -> "unknown position:$value"
+            }
+
+        companion object {
+
+            /** Specifies that the bounds is at the left of the parent window container. */
+            @JvmField val ALIGN_LEFT = Alignment(0)
+
+            /** Specifies that the bounds is at the top of the parent window container. */
+            @JvmField val ALIGN_TOP = Alignment(1)
+
+            /** Specifies that the bounds is at the right of the parent window container. */
+            @JvmField val ALIGN_RIGHT = Alignment(2)
+
+            /** Specifies that the bounds is at the bottom of the parent window container. */
+            @JvmField val ALIGN_BOTTOM = Alignment(3)
+        }
+    }
+
+    /**
+     * The dimension of the bounds, which can be represented as multiple formats:
+     * - [DIMENSION_EXPANDED]: means the bounds' dimension fills parent window's dimension.
+     * - in [pixel]: To specify the dimension value in pixel.
+     * - in [ratio]: To specify the dimension that relative to the parent window container. For
+     *   example, if [width] has [ratio] value 0.6, it means the bounds' width is 0.6 to the parent
+     *   window container's width.
+     */
+    abstract class Dimension internal constructor(internal val description: String) {
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Dimension) return false
+            return description == other.description
+        }
+
+        override fun hashCode(): Int = description.hashCode()
+
+        override fun toString(): String = description
+
+        /**
+         * The [Dimension] represented in pixel format
+         *
+         * @param value The dimension length in pixel
+         */
+        internal class Pixel(@Px @IntRange(from = 1) internal val value: Int) :
+            Dimension("dimension in pixel:$value") {
+
+            init {
+                require(value >= 1) { "Pixel value must be a positive integer." }
+            }
+
+            internal operator fun compareTo(dimen: Int): Int = value - dimen
+        }
+
+        /**
+         * The [Dimension] represented in ratio format, which means the proportion of the parent
+         * window dimension.
+         *
+         * @param value The ratio in (0.0, 1.0)
+         */
+        internal class Ratio(
+            @FloatRange(from = 0.0, fromInclusive = false, to = 1.0) internal val value: Float
+        ) : Dimension("dimension in ratio:$value") {
+
+            init {
+                require(value > 0.0 && value <= 1.0) { "Ratio must be in range (0.0, 1.0]" }
+            }
+
+            internal operator fun times(dimen: Int): Int = (value * dimen).toInt()
+        }
+
+        companion object {
+
+            /** Represents this dimension follows its parent window dimension. */
+            @JvmField val DIMENSION_EXPANDED: Dimension = Ratio(1.0f)
+
+            /**
+             * Represents this dimension follows the hinge position if the current window and device
+             * state satisfies, or fallbacks to a half of the parent task dimension, otherwise.
+             *
+             * The [DIMENSION_HINGE] works only if:
+             * - The parent container is not in multi-window mode (e.g., split-screen mode or
+             *   picture-in-picture mode)
+             * - The device has a hinge or separating fold reported by
+             *   [androidx.window.layout.FoldingFeature.isSeparating]
+             * - The hinge or separating fold orientation matches [EmbeddingBounds.alignment]:
+             *     - The hinge or fold orientation is vertical, and the position is [POSITION_LEFT]
+             *       or [POSITION_RIGHT]
+             *     - The hinge or fold orientation is horizontal, and the position is [POSITION_TOP]
+             *       or [POSITION_BOTTOM]
+             */
+            @JvmField val DIMENSION_HINGE: Dimension = object : Dimension("hinge") {}
+
+            /**
+             * Creates the dimension in pixel.
+             *
+             * If the dimension length exceeds the parent window dimension, the overlay container
+             * will resize to fit the parent task dimension.
+             *
+             * @param value The dimension length in pixel
+             */
+            @JvmStatic fun pixel(@Px @IntRange(from = 1) value: Int): Dimension = Pixel(value)
+
+            /**
+             * Creates the dimension which takes a proportion of the parent window dimension.
+             *
+             * @param ratio The proportion of the parent window dimension this dimension should take
+             */
+            @JvmStatic
+            fun ratio(
+                @FloatRange(from = 0.0, fromInclusive = false, to = 1.0, toInclusive = false)
+                ratio: Float
+            ): Dimension = Ratio(ratio)
+        }
+    }
+
+    companion object {
+
+        /** The bounds fills the parent window bounds */
+        @JvmField
+        val BOUNDS_EXPANDED = EmbeddingBounds(ALIGN_TOP, DIMENSION_EXPANDED, DIMENSION_EXPANDED)
+
+        /**
+         * The bounds located on the top of the parent window, and the bounds' bottom side matches
+         * the hinge position.
+         */
+        @JvmField
+        val BOUNDS_HINGE_TOP =
+            EmbeddingBounds(ALIGN_TOP, width = DIMENSION_EXPANDED, height = DIMENSION_HINGE)
+
+        /**
+         * The bounds located on the left of the parent window, and the bounds' right side matches
+         * the hinge position.
+         */
+        @JvmField
+        val BOUNDS_HINGE_LEFT =
+            EmbeddingBounds(ALIGN_LEFT, width = DIMENSION_HINGE, height = DIMENSION_EXPANDED)
+
+        /**
+         * The bounds located on the bottom of the parent window, and the bounds' top side matches
+         * the hinge position.
+         */
+        @JvmField
+        val BOUNDS_HINGE_BOTTOM =
+            EmbeddingBounds(ALIGN_BOTTOM, width = DIMENSION_EXPANDED, height = DIMENSION_HINGE)
+
+        /**
+         * The bounds located on the right of the parent window, and the bounds' left side matches
+         * the hinge position.
+         */
+        @JvmField
+        val BOUNDS_HINGE_RIGHT =
+            EmbeddingBounds(ALIGN_RIGHT, width = DIMENSION_HINGE, height = DIMENSION_EXPANDED)
+
+        /** Translates [EmbeddingBounds] to pure [Rect] bounds with given [ParentContainerInfo]. */
+        @VisibleForTesting
+        internal fun translateEmbeddingBounds(
+            embeddingBounds: EmbeddingBounds,
+            parentContainerBounds: Bounds,
+            windowLayoutInfo: WindowLayoutInfo,
+        ): Bounds {
+            if (
+                embeddingBounds.width == DIMENSION_EXPANDED &&
+                    embeddingBounds.height == DIMENSION_EXPANDED
+            ) {
+                // If width and height are expanded, set bounds to empty to follow the parent task
+                // bounds.
+                return Bounds.EMPTY_BOUNDS
+            }
+            // 1. Fallbacks dimensions to ratio(0.5) if they can't follow the hinge with the current
+            //    device and window state.
+            val width =
+                if (embeddingBounds.shouldUseFallbackDimensionForWidth(windowLayoutInfo)) {
+                    ratio(0.5f)
+                } else {
+                    embeddingBounds.width
+                }
+            val height =
+                if (embeddingBounds.shouldUseFallbackDimensionForHeight(windowLayoutInfo)) {
+                    ratio(0.5f)
+                } else {
+                    embeddingBounds.height
+                }
+
+            // 2. Computes dimensions to pixel values. If it just matches parent task bounds,
+            // returns
+            //    the empty bounds to declare the bounds follow the parent task bounds.
+            val sanitizedBounds = EmbeddingBounds(embeddingBounds.alignment, width, height)
+            val widthInPixel =
+                sanitizedBounds.getWidthInPixel(parentContainerBounds, windowLayoutInfo)
+            val heightInPixel =
+                sanitizedBounds.getHeightInPixel(parentContainerBounds, windowLayoutInfo)
+            val taskWidth = parentContainerBounds.width
+            val taskHeight = parentContainerBounds.height
+
+            if (widthInPixel == taskWidth && heightInPixel == taskHeight) {
+                return Bounds.EMPTY_BOUNDS
+            }
+
+            // 3. Offset the bounds by position:
+            //     - For top or bottom position, the bounds should attach to the top or bottom of
+            //       the parent task bounds and centered by the middle of the width.
+            //     - For left or right position, the bounds should attach to the left or right of
+            //       the parent task bounds and centered by the middle of the height.
+            return Bounds(0, 0, widthInPixel, heightInPixel).let { bounds ->
+                when (embeddingBounds.alignment) {
+                    ALIGN_TOP -> bounds.offset(((taskWidth - widthInPixel) / 2), 0)
+                    ALIGN_LEFT -> bounds.offset(0, ((taskHeight - heightInPixel) / 2))
+                    ALIGN_BOTTOM ->
+                        bounds.offset(((taskWidth - widthInPixel) / 2), taskHeight - heightInPixel)
+                    ALIGN_RIGHT ->
+                        bounds.offset(taskWidth - widthInPixel, ((taskHeight - heightInPixel) / 2))
+                    else ->
+                        throw IllegalArgumentException(
+                            "Unknown alignment: ${embeddingBounds.alignment}"
+                        )
+                }
+            }
+        }
+
+        private fun Bounds.offset(dx: Int, dy: Int): Bounds =
+            Bounds(left + dx, top + dy, right + dx, bottom + dy)
+
+        /** Translates [EmbeddingBounds] to pure [Rect] bounds with given [ParentContainerInfo]. */
+        internal fun translateEmbeddingBounds(
+            embeddingBounds: EmbeddingBounds,
+            parentContainerInfo: ParentContainerInfo,
+        ): Bounds =
+            translateEmbeddingBounds(
+                embeddingBounds,
+                parentContainerInfo.windowBounds,
+                parentContainerInfo.windowLayoutInfo
+            )
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt
index fd35178..e982f91 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingCompat.kt
@@ -17,23 +17,27 @@
 package androidx.window.embedding
 
 import android.app.Activity
-import android.app.ActivityOptions
 import android.content.Context
-import android.os.IBinder
+import android.os.Bundle
 import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.core.util.Consumer as JetpackConsumer
 import androidx.window.RequiresWindowSdkExtension
 import androidx.window.WindowSdkExtensions
 import androidx.window.core.BuildConfig
 import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExtensionsUtil
 import androidx.window.core.VerificationMode
 import androidx.window.embedding.EmbeddingInterfaceCompat.EmbeddingCallbackInterface
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
 import androidx.window.embedding.SplitController.SplitSupportStatus.Companion.SPLIT_AVAILABLE
 import androidx.window.extensions.WindowExtensionsProvider
 import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import androidx.window.extensions.embedding.ActivityStack as OEMActivityStack
+import androidx.window.extensions.embedding.ActivityStackAttributes
 import androidx.window.extensions.embedding.SplitInfo as OEMSplitInfo
 import androidx.window.reflection.Consumer2
 import java.lang.reflect.Proxy
+import java.util.concurrent.Executor
 
 /**
  * Adapter implementation for different historical versions of activity embedding OEM interface in
@@ -43,9 +47,15 @@
     private val embeddingExtension: ActivityEmbeddingComponent,
     private val adapter: EmbeddingAdapter,
     private val consumerAdapter: ConsumerAdapter,
-    private val applicationContext: Context
+    private val applicationContext: Context,
+    @get:VisibleForTesting internal val overlayController: OverlayControllerImpl?,
+    private val activityWindowInfoCallbackController: ActivityWindowInfoCallbackController?,
 ) : EmbeddingInterfaceCompat {
 
+    private val windowSdkExtensions = WindowSdkExtensions.getInstance()
+
+    private var isCustomSplitAttributeCalculatorSet: Boolean = false
+
     override fun setRules(rules: Set<EmbeddingRule>) {
         var hasSplitRule = false
         for (rule in rules) {
@@ -74,70 +84,238 @@
     }
 
     override fun setEmbeddingCallback(embeddingCallback: EmbeddingCallbackInterface) {
-        if (ExtensionsUtil.safeVendorApiLevel < 2) {
-            consumerAdapter.addConsumer(embeddingExtension, List::class, "setSplitInfoCallback") {
-                values ->
-                val splitInfoList = values.filterIsInstance<OEMSplitInfo>()
-                embeddingCallback.onSplitInfoChanged(adapter.translate(splitInfoList))
-            }
-        } else {
-            val callback =
-                Consumer2<List<OEMSplitInfo>> { splitInfoList ->
+        when (windowSdkExtensions.extensionVersion) {
+            1 -> {
+                consumerAdapter.addConsumer(
+                    embeddingExtension,
+                    List::class,
+                    "setSplitInfoCallback"
+                ) { values ->
+                    val splitInfoList = values.filterIsInstance<OEMSplitInfo>()
                     embeddingCallback.onSplitInfoChanged(adapter.translate(splitInfoList))
                 }
-            embeddingExtension.setSplitInfoCallback(callback)
+            }
+            in 2..4 -> {
+                registerSplitInfoCallback(embeddingCallback)
+            }
+            in 5..Int.MAX_VALUE -> {
+                registerSplitInfoCallback(embeddingCallback)
+
+                // Register ActivityStack callback
+                val activityStackCallback =
+                    Consumer2<List<OEMActivityStack>> { activityStacks ->
+                        embeddingCallback.onActivityStackChanged(adapter.translate(activityStacks))
+                    }
+                embeddingExtension.registerActivityStackCallback(
+                    Runnable::run,
+                    activityStackCallback
+                )
+            }
         }
     }
 
+    private fun registerSplitInfoCallback(embeddingCallback: EmbeddingCallbackInterface) {
+        val splitInfoCallback =
+            Consumer2<List<OEMSplitInfo>> { splitInfoList ->
+                embeddingCallback.onSplitInfoChanged(adapter.translate(splitInfoList))
+            }
+        embeddingExtension.setSplitInfoCallback(splitInfoCallback)
+    }
+
     override fun isActivityEmbedded(activity: Activity): Boolean {
         return embeddingExtension.isActivityEmbedded(activity)
     }
 
+    @RequiresWindowSdkExtension(5)
+    override fun pinTopActivityStack(taskId: Int, splitPinRule: SplitPinRule): Boolean {
+        windowSdkExtensions.requireExtensionVersion(5)
+        return embeddingExtension.pinTopActivityStack(
+            taskId,
+            adapter.translateSplitPinRule(applicationContext, splitPinRule)
+        )
+    }
+
+    @RequiresWindowSdkExtension(5)
+    override fun unpinTopActivityStack(taskId: Int) {
+        windowSdkExtensions.requireExtensionVersion(5)
+        return embeddingExtension.unpinTopActivityStack(taskId)
+    }
+
     @RequiresWindowSdkExtension(2)
     override fun setSplitAttributesCalculator(
         calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
     ) {
-        WindowSdkExtensions.getInstance().requireExtensionVersion(2)
+        windowSdkExtensions.requireExtensionVersion(2)
 
         embeddingExtension.setSplitAttributesCalculator(
             adapter.translateSplitAttributesCalculator(calculator)
         )
+        isCustomSplitAttributeCalculatorSet = true
     }
 
     @RequiresWindowSdkExtension(2)
     override fun clearSplitAttributesCalculator() {
-        WindowSdkExtensions.getInstance().requireExtensionVersion(2)
+        windowSdkExtensions.requireExtensionVersion(2)
 
         embeddingExtension.clearSplitAttributesCalculator()
+        isCustomSplitAttributeCalculatorSet = false
+        setDefaultSplitAttributeCalculatorIfNeeded()
     }
 
-    @RequiresWindowSdkExtension(3)
-    override fun invalidateTopVisibleSplitAttributes() {
-        WindowSdkExtensions.getInstance().requireExtensionVersion(3)
+    @RequiresWindowSdkExtension(5)
+    override fun finishActivityStacks(activityStacks: Set<ActivityStack>) {
+        windowSdkExtensions.requireExtensionVersion(5)
+
+        embeddingExtension.finishActivityStacksWithTokens(
+            activityStacks.mapTo(mutableSetOf()) { it.getToken() }
+        )
+    }
+
+    @RequiresWindowSdkExtension(5)
+    override fun setEmbeddingConfiguration(embeddingConfig: EmbeddingConfiguration) {
+        windowSdkExtensions.requireExtensionVersion(5)
+        adapter.embeddingConfiguration = embeddingConfig
+        setDefaultSplitAttributeCalculatorIfNeeded()
 
         embeddingExtension.invalidateTopVisibleSplitAttributes()
     }
 
-    @Suppress("DEPRECATION")
-    @RequiresWindowSdkExtension(3)
-    override fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes) {
-        WindowSdkExtensions.getInstance().requireExtensionVersion(3)
-
-        embeddingExtension.updateSplitAttributes(
-            splitInfo.token,
-            adapter.translateSplitAttributes(splitAttributes)
-        )
+    private fun setDefaultSplitAttributeCalculatorIfNeeded() {
+        // Setting a default SplitAttributeCalculator if the EmbeddingConfiguration is set,
+        // in order to ensure the dimAreaBehavior in the SplitAttribute is up-to-date.
+        if (
+            windowSdkExtensions.extensionVersion >= 5 &&
+                !isCustomSplitAttributeCalculatorSet &&
+                adapter.embeddingConfiguration != null
+        ) {
+            embeddingExtension.setSplitAttributesCalculator { params ->
+                adapter.translateSplitAttributes(adapter.translate(params.defaultSplitAttributes))
+            }
+        }
     }
 
-    @Suppress("DEPRECATION")
     @RequiresWindowSdkExtension(3)
-    override fun setLaunchingActivityStack(
-        options: ActivityOptions,
-        token: IBinder
-    ): ActivityOptions {
-        WindowSdkExtensions.getInstance().requireExtensionVersion(3)
+    override fun invalidateVisibleActivityStacks() {
+        windowSdkExtensions.requireExtensionVersion(3)
 
-        return embeddingExtension.setLaunchingActivityStack(options, token)
+        embeddingExtension.invalidateVisibleActivityStacks()
+    }
+
+    /**
+     * Updates top [activityStacks][ActivityStack] layouts, which will trigger [SplitAttributes]
+     * calculator and [ActivityStackAttributes] calculator if set.
+     */
+    private fun ActivityEmbeddingComponent.invalidateVisibleActivityStacks() {
+        // Note that this API also updates overlay container regardless of its naming.
+        invalidateTopVisibleSplitAttributes()
+    }
+
+    @Suppress("Deprecation") // To compat with device with extension version 3 and 4.
+    @RequiresWindowSdkExtension(3)
+    override fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes) {
+        windowSdkExtensions.requireExtensionVersion(3)
+
+        if (windowSdkExtensions.extensionVersion >= 5) {
+            embeddingExtension.updateSplitAttributes(
+                splitInfo.getToken(),
+                adapter.translateSplitAttributes(splitAttributes)
+            )
+        } else {
+            embeddingExtension.updateSplitAttributes(
+                splitInfo.getBinder(),
+                adapter.translateSplitAttributes(splitAttributes)
+            )
+        }
+    }
+
+    @RequiresWindowSdkExtension(5)
+    override fun setLaunchingActivityStack(options: Bundle, activityStack: ActivityStack): Bundle {
+        windowSdkExtensions.requireExtensionVersion(5)
+
+        ActivityEmbeddingOptionsImpl.setActivityStackToken(options, activityStack.getToken())
+        return options
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun setOverlayCreateParams(
+        options: Bundle,
+        overlayCreateParams: OverlayCreateParams
+    ): Bundle =
+        options.apply {
+            ActivityEmbeddingOptionsImpl.setOverlayCreateParams(options, overlayCreateParams)
+        }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun setOverlayAttributesCalculator(
+        calculator: (OverlayAttributesCalculatorParams) -> OverlayAttributes
+    ) {
+        windowSdkExtensions.requireExtensionVersion(OVERLAY_FEATURE_VERSION)
+
+        overlayController!!.overlayAttributesCalculator = calculator
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun clearOverlayAttributesCalculator() {
+        windowSdkExtensions.requireExtensionVersion(OVERLAY_FEATURE_VERSION)
+
+        overlayController!!.overlayAttributesCalculator = null
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun updateOverlayAttributes(overlayTag: String, overlayAttributes: OverlayAttributes) {
+        windowSdkExtensions.requireExtensionVersion(OVERLAY_FEATURE_VERSION)
+
+        overlayController!!.updateOverlayAttributes(overlayTag, overlayAttributes)
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun addOverlayInfoCallback(
+        overlayTag: String,
+        executor: Executor,
+        overlayInfoCallback: JetpackConsumer<OverlayInfo>,
+    ) {
+        overlayController?.addOverlayInfoCallback(
+            overlayTag,
+            executor,
+            overlayInfoCallback,
+        )
+            ?: apply {
+                Log.w(TAG, "overlayInfo is not supported on device less than version 5")
+
+                overlayInfoCallback.accept(
+                    OverlayInfo(
+                        overlayTag,
+                        currentOverlayAttributes = null,
+                        activityStack = null,
+                    )
+                )
+            }
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun removeOverlayInfoCallback(overlayInfoCallback: JetpackConsumer<OverlayInfo>) {
+        overlayController?.removeOverlayInfoCallback(overlayInfoCallback)
+    }
+
+    @RequiresWindowSdkExtension(6)
+    override fun addEmbeddedActivityWindowInfoCallbackForActivity(
+        activity: Activity,
+        callback: JetpackConsumer<EmbeddedActivityWindowInfo>
+    ) {
+        activityWindowInfoCallbackController?.addCallback(activity, callback)
+            ?: apply {
+                Log.w(
+                    TAG,
+                    "EmbeddedActivityWindowInfo is not supported on device less than version 6"
+                )
+            }
+    }
+
+    @RequiresWindowSdkExtension(6)
+    override fun removeEmbeddedActivityWindowInfoCallbackForActivity(
+        callback: JetpackConsumer<EmbeddedActivityWindowInfo>
+    ) {
+        activityWindowInfoCallbackController?.removeCallback(callback)
     }
 
     companion object {
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingConfiguration.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingConfiguration.kt
new file mode 100644
index 0000000..930acb2
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingConfiguration.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import androidx.annotation.IntRange
+import androidx.window.RequiresWindowSdkExtension
+
+/**
+ * Configurations of Activity Embedding environment that defines how the embedded Activities behave.
+ *
+ * @constructor The [EmbeddingConfiguration] constructor. The properties are undefined if not
+ *   specified.
+ * @property dimAreaBehavior The requested dim area behavior.
+ * @see ActivityEmbeddingController.setEmbeddingConfiguration
+ */
+class EmbeddingConfiguration
+@JvmOverloads
+constructor(
+    @RequiresWindowSdkExtension(5) val dimAreaBehavior: DimAreaBehavior = DimAreaBehavior.UNDEFINED
+) {
+    /**
+     * The area of dimming to apply.
+     *
+     * @see [android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND]
+     */
+    class DimAreaBehavior private constructor(@IntRange(from = 0, to = 2) internal val value: Int) {
+        companion object {
+            /**
+             * The dim area is not defined.
+             *
+             * This is the default value while building a [EmbeddingConfiguration]. This would also
+             * keep the existing dim area configuration of the current Activity Embedding
+             * environment unchanged when [ActivityEmbeddingController.setEmbeddingConfiguration] is
+             * called.
+             *
+             * @see ActivityEmbeddingController.setEmbeddingConfiguration
+             */
+            @JvmField val UNDEFINED = DimAreaBehavior(0)
+
+            /**
+             * The dim effect is applying on the [ActivityStack] of the Activity window when needed.
+             * If the [ActivityStack] is split and displayed side-by-side with another
+             * [ActivityStack], the dim effect is applying only on the [ActivityStack] of the
+             * requested Activity.
+             */
+            @JvmField val ON_ACTIVITY_STACK = DimAreaBehavior(1)
+
+            /**
+             * The dimming effect is applying on the area of the whole Task when needed. If the
+             * embedded transparent activity is split and displayed side-by-side with another
+             * activity, the dim effect is applying on the Task, which across over the two
+             * [ActivityStack]s.
+             *
+             * This is the default dim area configuration of the Activity Embedding environment,
+             * before the [DimAreaBehavior] is explicitly set by
+             * [ActivityEmbeddingController.setEmbeddingConfiguration].
+             */
+            @JvmField val ON_TASK = DimAreaBehavior(2)
+        }
+
+        override fun toString(): String {
+            return "DimAreaBehavior=" +
+                when (value) {
+                    0 -> "UNDEFINED"
+                    1 -> "ON_ACTIVITY_STACK"
+                    2 -> "ON_TASK"
+                    else -> "UNKNOWN"
+                }
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is EmbeddingConfiguration) return false
+
+        if (dimAreaBehavior != other.dimAreaBehavior) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return dimAreaBehavior.hashCode()
+    }
+
+    override fun toString(): String = "EmbeddingConfiguration{$dimAreaBehavior}"
+
+    /** Builder for creating an instance of [EmbeddingConfiguration]. */
+    class Builder {
+        private var mDimAreaBehavior = DimAreaBehavior.UNDEFINED
+
+        /**
+         * Sets the dim area behavior. By default, the [DimAreaBehavior.UNDEFINED] is used if not
+         * set.
+         *
+         * @param area The dim area.
+         * @return This [Builder]
+         */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        fun setDimAreaBehavior(area: DimAreaBehavior): Builder = apply { mDimAreaBehavior = area }
+
+        /**
+         * Builds a[EmbeddingConfiguration] instance.
+         *
+         * @return The new [EmbeddingConfiguration] instance.
+         */
+        fun build(): EmbeddingConfiguration = EmbeddingConfiguration(mDimAreaBehavior)
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt b/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt
index 8c63f2a..39ac592 100644
--- a/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt
+++ b/window/window/src/main/java/androidx/window/embedding/EmbeddingInterfaceCompat.kt
@@ -17,10 +17,12 @@
 package androidx.window.embedding
 
 import android.app.Activity
-import android.app.ActivityOptions
-import android.os.IBinder
+import android.os.Bundle
+import androidx.core.util.Consumer
 import androidx.window.RequiresWindowSdkExtension
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
 import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import java.util.concurrent.Executor
 
 /**
  * Adapter interface for different historical versions of activity embedding OEM interface in
@@ -34,10 +36,17 @@
 
     interface EmbeddingCallbackInterface {
         fun onSplitInfoChanged(splitInfo: List<SplitInfo>)
+
+        fun onActivityStackChanged(activityStacks: List<ActivityStack>)
     }
 
     fun isActivityEmbedded(activity: Activity): Boolean
 
+    @RequiresWindowSdkExtension(5)
+    fun pinTopActivityStack(taskId: Int, splitPinRule: SplitPinRule): Boolean
+
+    @RequiresWindowSdkExtension(5) fun unpinTopActivityStack(taskId: Int)
+
     @RequiresWindowSdkExtension(2)
     fun setSplitAttributesCalculator(
         calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
@@ -45,11 +54,50 @@
 
     @RequiresWindowSdkExtension(2) fun clearSplitAttributesCalculator()
 
-    @RequiresWindowSdkExtension(3)
-    fun setLaunchingActivityStack(options: ActivityOptions, token: IBinder): ActivityOptions
+    @RequiresWindowSdkExtension(5)
+    fun setLaunchingActivityStack(options: Bundle, activityStack: ActivityStack): Bundle
 
-    @RequiresWindowSdkExtension(3) fun invalidateTopVisibleSplitAttributes()
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun setOverlayCreateParams(options: Bundle, overlayCreateParams: OverlayCreateParams): Bundle
+
+    @RequiresWindowSdkExtension(5) fun finishActivityStacks(activityStacks: Set<ActivityStack>)
+
+    @RequiresWindowSdkExtension(5)
+    fun setEmbeddingConfiguration(embeddingConfig: EmbeddingConfiguration)
+
+    @RequiresWindowSdkExtension(3) fun invalidateVisibleActivityStacks()
 
     @RequiresWindowSdkExtension(3)
     fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes)
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun setOverlayAttributesCalculator(
+        calculator: (OverlayAttributesCalculatorParams) -> OverlayAttributes
+    )
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION) fun clearOverlayAttributesCalculator()
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun updateOverlayAttributes(overlayTag: String, overlayAttributes: OverlayAttributes)
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun addOverlayInfoCallback(
+        overlayTag: String,
+        executor: Executor,
+        overlayInfoCallback: Consumer<OverlayInfo>,
+    )
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun removeOverlayInfoCallback(overlayInfoCallback: Consumer<OverlayInfo>)
+
+    @RequiresWindowSdkExtension(6)
+    fun addEmbeddedActivityWindowInfoCallbackForActivity(
+        activity: Activity,
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    )
+
+    @RequiresWindowSdkExtension(6)
+    fun removeEmbeddedActivityWindowInfoCallbackForActivity(
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    )
 }
diff --git a/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt b/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt
index 11e0f41..aaac205 100644
--- a/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt
+++ b/window/window/src/main/java/androidx/window/embedding/ExtensionEmbeddingBackend.kt
@@ -17,11 +17,10 @@
 package androidx.window.embedding
 
 import android.app.Activity
-import android.app.ActivityOptions
 import android.content.Context
 import android.content.pm.PackageManager
 import android.os.Build
-import android.os.IBinder
+import android.os.Bundle
 import android.util.Log
 import androidx.annotation.GuardedBy
 import androidx.annotation.RequiresApi
@@ -30,13 +29,14 @@
 import androidx.core.util.Consumer
 import androidx.window.RequiresWindowSdkExtension
 import androidx.window.WindowProperties
+import androidx.window.WindowSdkExtensions
 import androidx.window.core.BuildConfig
 import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExtensionsUtil
 import androidx.window.core.PredicateAdapter
 import androidx.window.core.VerificationMode
 import androidx.window.embedding.EmbeddingInterfaceCompat.EmbeddingCallbackInterface
 import androidx.window.embedding.ExtensionEmbeddingBackend.Api31Impl.isSplitPropertyEnabled
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.Executor
 import java.util.concurrent.locks.ReentrantLock
@@ -52,11 +52,11 @@
 ) : EmbeddingBackend {
 
     @VisibleForTesting val splitChangeCallbacks: CopyOnWriteArrayList<SplitListenerWrapper>
-    private val splitInfoEmbeddingCallback = EmbeddingCallbackImpl()
+    private val embeddingCallback = EmbeddingCallbackImpl()
 
     init {
         splitChangeCallbacks = CopyOnWriteArrayList<SplitListenerWrapper>()
-        embeddingExtension?.setEmbeddingCallback(splitInfoEmbeddingCallback)
+        embeddingExtension?.setEmbeddingCallback(embeddingCallback)
     }
 
     companion object {
@@ -87,18 +87,30 @@
             applicationContext: Context
         ): EmbeddingInterfaceCompat? {
             var impl: EmbeddingInterfaceCompat? = null
+            val version = WindowSdkExtensions.getInstance().extensionVersion
             try {
                 if (
-                    isExtensionVersionSupported(ExtensionsUtil.safeVendorApiLevel) &&
-                        EmbeddingCompat.isEmbeddingAvailable()
+                    isExtensionVersionSupported(version) && EmbeddingCompat.isEmbeddingAvailable()
                 ) {
                     impl =
                         EmbeddingBackend::class.java.classLoader?.let { loader ->
+                            val embeddingExtension = EmbeddingCompat.embeddingComponent()
+                            val adapter = EmbeddingAdapter(PredicateAdapter(loader))
                             EmbeddingCompat(
-                                EmbeddingCompat.embeddingComponent(),
-                                EmbeddingAdapter(PredicateAdapter(loader)),
+                                embeddingExtension,
+                                adapter,
                                 ConsumerAdapter(loader),
-                                applicationContext
+                                applicationContext,
+                                if (version >= OVERLAY_FEATURE_VERSION) {
+                                    OverlayControllerImpl(embeddingExtension, adapter)
+                                } else {
+                                    null
+                                },
+                                if (version >= 6) {
+                                    ActivityWindowInfoCallbackController(embeddingExtension)
+                                } else {
+                                    null
+                                },
                             )
                         }
                     // TODO(b/190433400): Check API conformance
@@ -278,11 +290,7 @@
 
             val callbackWrapper = SplitListenerWrapper(activity, executor, callback)
             splitChangeCallbacks.add(callbackWrapper)
-            if (splitInfoEmbeddingCallback.lastInfo != null) {
-                callbackWrapper.accept(splitInfoEmbeddingCallback.lastInfo!!)
-            } else {
-                callbackWrapper.accept(emptyList())
-            }
+            callbackWrapper.accept(embeddingCallback.lastInfo)
         }
     }
 
@@ -298,11 +306,13 @@
     }
 
     /**
-     * Extension callback implementation of the split information. Keeps track of last reported
+     * Extension callback implementation of the embedding information. Keeps track of last reported
      * values.
      */
     internal inner class EmbeddingCallbackImpl : EmbeddingCallbackInterface {
-        var lastInfo: List<SplitInfo>? = null
+        var lastInfo: List<SplitInfo> = emptyList()
+
+        var lastActivityStacks: List<ActivityStack> = emptyList()
 
         override fun onSplitInfoChanged(splitInfo: List<SplitInfo>) {
             lastInfo = splitInfo
@@ -310,6 +320,10 @@
                 callbackWrapper.accept(splitInfo)
             }
         }
+
+        override fun onActivityStackChanged(activityStacks: List<ActivityStack>) {
+            lastActivityStacks = activityStacks
+        }
     }
 
     private fun areExtensionsAvailable(): Boolean {
@@ -336,6 +350,16 @@
         return embeddingExtension?.isActivityEmbedded(activity) ?: false
     }
 
+    @RequiresWindowSdkExtension(5)
+    override fun pinTopActivityStack(taskId: Int, splitPinRule: SplitPinRule): Boolean {
+        return embeddingExtension?.pinTopActivityStack(taskId, splitPinRule) ?: false
+    }
+
+    @RequiresWindowSdkExtension(5)
+    override fun unpinTopActivityStack(taskId: Int) {
+        embeddingExtension?.unpinTopActivityStack(taskId)
+    }
+
     @RequiresWindowSdkExtension(2)
     override fun setSplitAttributesCalculator(
         calculator: (SplitAttributesCalculatorParams) -> SplitAttributes
@@ -348,33 +372,51 @@
         globalLock.withLock { embeddingExtension?.clearSplitAttributesCalculator() }
     }
 
-    override fun getActivityStack(activity: Activity): ActivityStack? {
+    override fun getActivityStack(activity: Activity): ActivityStack? =
         globalLock.withLock {
-            val lastInfo: List<SplitInfo> = splitInfoEmbeddingCallback.lastInfo ?: return null
-            for (info in lastInfo) {
-                if (activity !in info) {
-                    continue
-                }
-                if (activity in info.primaryActivityStack) {
-                    return info.primaryActivityStack
-                }
-                if (activity in info.secondaryActivityStack) {
-                    return info.secondaryActivityStack
-                }
-            }
-            return null
+            embeddingCallback.lastActivityStacks.find { activityStack -> activity in activityStack }
+                ?: getActivityStackFromSplitInfoList(activity)
         }
+
+    @GuardedBy("globalLock")
+    private fun getActivityStackFromSplitInfoList(activity: Activity): ActivityStack? {
+        for (info in embeddingCallback.lastInfo) {
+            if (activity !in info) {
+                continue
+            }
+            if (activity in info.primaryActivityStack) {
+                return info.primaryActivityStack
+            }
+            if (activity in info.secondaryActivityStack) {
+                return info.secondaryActivityStack
+            }
+        }
+        return null
+    }
+
+    @RequiresWindowSdkExtension(5)
+    override fun setLaunchingActivityStack(options: Bundle, activityStack: ActivityStack): Bundle =
+        embeddingExtension?.setLaunchingActivityStack(options, activityStack) ?: options
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun setOverlayCreateParams(
+        options: Bundle,
+        overlayCreateParams: OverlayCreateParams,
+    ): Bundle = embeddingExtension?.setOverlayCreateParams(options, overlayCreateParams) ?: options
+
+    @RequiresWindowSdkExtension(5)
+    override fun finishActivityStacks(activityStacks: Set<ActivityStack>) {
+        embeddingExtension?.finishActivityStacks(activityStacks)
+    }
+
+    @RequiresWindowSdkExtension(5)
+    override fun setEmbeddingConfiguration(embeddingConfig: EmbeddingConfiguration) {
+        embeddingExtension?.setEmbeddingConfiguration(embeddingConfig)
     }
 
     @RequiresWindowSdkExtension(3)
-    override fun setLaunchingActivityStack(
-        options: ActivityOptions,
-        token: IBinder
-    ): ActivityOptions = embeddingExtension?.setLaunchingActivityStack(options, token) ?: options
-
-    @RequiresWindowSdkExtension(3)
-    override fun invalidateTopVisibleSplitAttributes() {
-        embeddingExtension?.invalidateTopVisibleSplitAttributes()
+    override fun invalidateVisibleActivityStacks() {
+        embeddingExtension?.invalidateVisibleActivityStacks()
     }
 
     @RequiresWindowSdkExtension(3)
@@ -382,6 +424,60 @@
         embeddingExtension?.updateSplitAttributes(splitInfo, splitAttributes)
     }
 
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun setOverlayAttributesCalculator(
+        calculator: (OverlayAttributesCalculatorParams) -> OverlayAttributes
+    ) {
+        embeddingExtension?.setOverlayAttributesCalculator(calculator)
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun clearOverlayAttributesCalculator() {
+        embeddingExtension?.clearOverlayAttributesCalculator()
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun updateOverlayAttributes(overlayTag: String, overlayAttributes: OverlayAttributes) {
+        embeddingExtension?.updateOverlayAttributes(overlayTag, overlayAttributes)
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun addOverlayInfoCallback(
+        overlayTag: String,
+        executor: Executor,
+        overlayInfoCallback: Consumer<OverlayInfo>,
+    ) {
+        embeddingExtension?.addOverlayInfoCallback(overlayTag, executor, overlayInfoCallback)
+            // Send an empty OverlayInfo if the extension does not exist.
+            ?: overlayInfoCallback.accept(
+                OverlayInfo(
+                    overlayTag,
+                    currentOverlayAttributes = null,
+                    activityStack = null,
+                )
+            )
+    }
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    override fun removeOverlayInfoCallback(overlayInfoCallback: Consumer<OverlayInfo>) {
+        embeddingExtension?.removeOverlayInfoCallback(overlayInfoCallback)
+    }
+
+    @RequiresWindowSdkExtension(6)
+    override fun addEmbeddedActivityWindowInfoCallbackForActivity(
+        activity: Activity,
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    ) {
+        embeddingExtension?.addEmbeddedActivityWindowInfoCallbackForActivity(activity, callback)
+    }
+
+    @RequiresWindowSdkExtension(6)
+    override fun removeEmbeddedActivityWindowInfoCallbackForActivity(
+        callback: Consumer<EmbeddedActivityWindowInfo>
+    ) {
+        embeddingExtension?.removeEmbeddedActivityWindowInfoCallbackForActivity(callback)
+    }
+
     @RequiresApi(31)
     private object Api31Impl {
         fun isSplitPropertyEnabled(context: Context): SplitController.SplitSupportStatus {
diff --git a/window/window/src/main/java/androidx/window/embedding/OverlayAttributes.kt b/window/window/src/main/java/androidx/window/embedding/OverlayAttributes.kt
new file mode 100644
index 0000000..6c446102
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/OverlayAttributes.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import androidx.annotation.RestrictTo
+
+/**
+ * The attributes to describe how an overlay container should look like.
+ *
+ * @constructor creates an overlay attributes.
+ * @property bounds The overlay container's [EmbeddingBounds], which defaults to
+ *   [EmbeddingBounds.BOUNDS_EXPANDED] if not specified.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+class OverlayAttributes
+@JvmOverloads
+constructor(val bounds: EmbeddingBounds = EmbeddingBounds.BOUNDS_EXPANDED) {
+
+    override fun toString(): String =
+        "${OverlayAttributes::class.java.simpleName}: {" + "bounds=$bounds" + "}"
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is OverlayAttributes) return false
+        return bounds == other.bounds
+    }
+
+    override fun hashCode(): Int = bounds.hashCode()
+
+    /** The [OverlayAttributes] builder. */
+    class Builder {
+
+        private var bounds = EmbeddingBounds.BOUNDS_EXPANDED
+
+        /**
+         * Sets the overlay bounds, which defaults to [EmbeddingBounds.BOUNDS_EXPANDED] if not
+         * specified.
+         *
+         * @param bounds The [EmbeddingBounds] of the overlay [ActivityStack].
+         * @return The [OverlayAttributes] builder.
+         */
+        fun setBounds(bounds: EmbeddingBounds): Builder = apply { this.bounds = bounds }
+
+        /** Builds [OverlayAttributes]. */
+        fun build(): OverlayAttributes = OverlayAttributes(bounds)
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/OverlayAttributesCalculatorParams.kt b/window/window/src/main/java/androidx/window/embedding/OverlayAttributesCalculatorParams.kt
new file mode 100644
index 0000000..d357bf9
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/OverlayAttributesCalculatorParams.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.content.res.Configuration
+import androidx.annotation.RestrictTo
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.WindowMetrics
+
+/**
+ * The parameter container used to report the current device and window state in
+ * [OverlayController.setOverlayAttributesCalculator] and references the corresponding overlay
+ * [ActivityStack] by [overlayTag].
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+class OverlayAttributesCalculatorParams
+internal constructor(
+    /** The parent container's [WindowMetrics] */
+    val parentWindowMetrics: WindowMetrics,
+    /** The parent container's [Configuration] */
+    val parentConfiguration: Configuration,
+    /** The parent container's [WindowLayoutInfo] */
+    val parentWindowLayoutInfo: WindowLayoutInfo,
+    /**
+     * The unique identifier of the overlay [ActivityStack] specified by [OverlayCreateParams.tag]
+     */
+    val overlayTag: String,
+    /**
+     * The overlay [ActivityStack]'s [OverlayAttributes] specified by [overlayTag], which is the
+     * [OverlayAttributes] that is not calculated by calculator. It should be either initialized by
+     * [OverlayCreateParams.overlayAttributes] or [OverlayController.updateOverlayAttributes].
+     */
+    val defaultOverlayAttributes: OverlayAttributes,
+) {
+    override fun toString(): String =
+        "${OverlayAttributesCalculatorParams::class.java}:{" +
+            "parentWindowMetrics=$parentWindowMetrics" +
+            "parentConfiguration=$parentConfiguration" +
+            "parentWindowLayoutInfo=$parentWindowLayoutInfo" +
+            "overlayTag=$overlayTag" +
+            "defaultOverlayAttributes=$defaultOverlayAttributes"
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/OverlayController.kt b/window/window/src/main/java/androidx/window/embedding/OverlayController.kt
new file mode 100644
index 0000000..e2b347c
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/OverlayController.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.content.Context
+import android.os.Bundle
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
+import androidx.core.util.Consumer
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+
+/**
+ * The controller to manage overlay [ActivityStack], which is launched by the activityOptions that
+ * [setOverlayCreateParams].
+ *
+ * See linked sample below for how to launch an [android.app.Activity] into an overlay
+ * [ActivityStack].
+ *
+ * Supported operations are:
+ * - [setOverlayAttributesCalculator] to update overlay presentation with device or window state and
+ *   [OverlayCreateParams.tag].
+ *
+ * @sample androidx.window.samples.embedding.launchOverlayActivityStackSample
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+class OverlayController
+@VisibleForTesting
+internal constructor(private val backend: EmbeddingBackend) {
+
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    internal fun setOverlayCreateParams(
+        options: Bundle,
+        overlayCreateParams: OverlayCreateParams,
+    ): Bundle = backend.setOverlayCreateParams(options, overlayCreateParams)
+
+    /**
+     * Sets an overlay calculator function to update overlay presentation with device or window
+     * state and [OverlayCreateParams.tag].
+     *
+     * Overlay calculator function is triggered with following scenarios:
+     * - An overlay [ActivityStack] is launched.
+     * - The parent task configuration changes. i.e. orientation change, enter/exit multi-window
+     *   mode or resize apps in multi-window mode.
+     * - Device folding state changes.
+     * - Device is attached to an external display and the app is forwarded to that display.
+     *
+     * If there's no [calculator] set, the overlay presentation will be calculated with the previous
+     * set [OverlayAttributes], either from [OverlayCreateParams] to initialize the overlay
+     * container, or from the runtime API to update the overlay container's [OverlayAttributes].
+     *
+     * See the sample linked below for how to use [OverlayAttributes] calculator
+     *
+     * @param calculator The overlay calculator function to compute [OverlayAttributes] by
+     *   [OverlayAttributesCalculatorParams]. It will replace the previously set if it exists.
+     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
+     *   than 6.
+     * @sample androidx.window.samples.embedding.overlayAttributesCalculatorSample
+     */
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun setOverlayAttributesCalculator(
+        calculator: (OverlayAttributesCalculatorParams) -> OverlayAttributes
+    ) {
+        backend.setOverlayAttributesCalculator(calculator)
+    }
+
+    /**
+     * Clears the overlay calculator function previously set by [setOverlayAttributesCalculator].
+     *
+     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
+     *   than 6.
+     */
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun clearOverlayAttributesCalculator() {
+        backend.clearOverlayAttributesCalculator()
+    }
+
+    /**
+     * Updates [OverlayAttributes] of the overlay [ActivityStack] specified by [overlayTag]. It's no
+     * op if there's no such overlay [ActivityStack] associated with [overlayTag].
+     *
+     * If an [OverlayAttributes] calculator function is specified, the updated [overlayAttributes]
+     * will be passed by [OverlayAttributesCalculatorParams.defaultOverlayAttributes] when the
+     * calculator function applies to the overlay [ActivityStack] specified by [overlayTag].
+     *
+     * In most cases it is suggested to use
+     * [ActivityEmbeddingController.invalidateVisibleActivityStacks] if a calculator has been set
+     * through [OverlayController.setOverlayAttributesCalculator].
+     *
+     * @param overlayTag The overlay [ActivityStack]'s tag
+     * @param overlayAttributes The [OverlayAttributes] to update
+     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
+     *   than 6.
+     */
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun updateOverlayAttributes(overlayTag: String, overlayAttributes: OverlayAttributes) {
+        backend.updateOverlayAttributes(overlayTag, overlayAttributes)
+    }
+
+    /**
+     * A [Flow] of [OverlayInfo] that [overlayTag] is associated with.
+     *
+     * If there's an active overlay [ActivityStack] associated with [overlayTag], it will be
+     * reported in [OverlayInfo.activityStack]. Otherwise, [OverlayInfo.activityStack] is `null`.
+     *
+     * Note that launching an overlay [ActivityStack] only supports on the device with
+     * [WindowSdkExtensions.extensionVersion] equal to or larger than 6. If
+     * [WindowSdkExtensions.extensionVersion] is less than 6, this flow will always report
+     * [OverlayInfo] without associated [OverlayInfo.activityStack].
+     *
+     * @param overlayTag The overlay [ActivityStack]'s tag which is set through
+     *   [OverlayCreateParams]
+     * @return a [Flow] of [OverlayInfo] this [overlayTag] is associated with
+     */
+    @RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+    fun overlayInfo(overlayTag: String): Flow<OverlayInfo> = callbackFlow {
+        val listener = Consumer { info: OverlayInfo -> trySend(info) }
+        backend.addOverlayInfoCallback(overlayTag, Runnable::run, listener)
+        awaitClose { backend.removeOverlayInfoCallback(listener) }
+    }
+
+    companion object {
+
+        internal const val OVERLAY_FEATURE_VERSION = 8
+
+        /**
+         * Obtains an instance of [OverlayController].
+         *
+         * @param context the [Context] to initialize the controller with
+         */
+        @JvmStatic
+        fun getInstance(context: Context): OverlayController {
+            val backend = EmbeddingBackend.getInstance(context)
+            return OverlayController(backend)
+        }
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/OverlayControllerImpl.kt b/window/window/src/main/java/androidx/window/embedding/OverlayControllerImpl.kt
new file mode 100644
index 0000000..885c283d
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/OverlayControllerImpl.kt
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.content.res.Configuration
+import android.util.ArrayMap
+import androidx.annotation.GuardedBy
+import androidx.annotation.VisibleForTesting
+import androidx.core.util.Consumer as JetpackConsumer
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.embedding.ActivityEmbeddingOptionsImpl.getOverlayAttributes
+import androidx.window.embedding.ActivityEmbeddingOptionsImpl.putActivityStackAlignment
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
+import androidx.window.extensions.core.util.function.Consumer
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import androidx.window.extensions.embedding.ActivityStack
+import androidx.window.extensions.embedding.ActivityStackAttributes
+import androidx.window.extensions.embedding.ParentContainerInfo
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.WindowMetrics
+import androidx.window.layout.WindowMetricsCalculator
+import androidx.window.layout.adapter.extensions.ExtensionsWindowLayoutInfoAdapter
+import androidx.window.layout.util.DensityCompatHelper
+import androidx.window.reflection.Consumer2
+import java.util.concurrent.Executor
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+/**
+ * The core implementation of [OverlayController] APIs, which is implemented by [ActivityStack]
+ * operations in WM Extensions.
+ */
+@Suppress("NewApi") // Suppress #translateWindowMetrics, which requires R.
+@RequiresWindowSdkExtension(OVERLAY_FEATURE_VERSION)
+internal open class OverlayControllerImpl(
+    private val embeddingExtension: ActivityEmbeddingComponent,
+    private val adapter: EmbeddingAdapter,
+) {
+    private val globalLock = ReentrantLock()
+
+    @GuardedBy("globalLock")
+    internal var overlayAttributesCalculator:
+        ((OverlayAttributesCalculatorParams) -> OverlayAttributes)? =
+        null
+        get() = globalLock.withLock { field }
+        set(value) {
+            globalLock.withLock { field = value }
+        }
+
+    /**
+     * Mapping between the overlay container tag and its default [OverlayAttributes]. It's to record
+     * the [OverlayAttributes] updated through [updateOverlayAttributes] and report in
+     * [OverlayAttributesCalculatorParams].
+     */
+    @GuardedBy("globalLock")
+    private val overlayTagToDefaultAttributesMap: MutableMap<String, OverlayAttributes> = ArrayMap()
+
+    /**
+     * Mapping between the overlay container tag and its current [OverlayAttributes] to provide the
+     * [OverlayInfo] updates.
+     */
+    @GuardedBy("globalLock")
+    private val overlayTagToCurrentAttributesMap = ArrayMap<String, OverlayAttributes>()
+
+    @GuardedBy("globalLock")
+    private val overlayTagToContainerMap = ArrayMap<String, ActivityStack>()
+
+    /** The mapping from [OverlayInfo] callback to [activityStacks][ActivityStack] callback. */
+    @GuardedBy("globalLock")
+    private val overlayInfoToActivityStackCallbackMap =
+        ArrayMap<JetpackConsumer<OverlayInfo>, Consumer<List<ActivityStack>>>()
+
+    init {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(OVERLAY_FEATURE_VERSION)
+
+        embeddingExtension.setActivityStackAttributesCalculator { params ->
+            globalLock.withLock {
+                val parentContainerInfo = params.parentContainerInfo
+                val density =
+                    DensityCompatHelper.getInstance()
+                        .density(
+                            parentContainerInfo.configuration,
+                            parentContainerInfo.windowMetrics
+                        )
+                val windowMetrics =
+                    WindowMetricsCalculator.translateWindowMetrics(
+                        parentContainerInfo.windowMetrics,
+                        density
+                    )
+                val overlayAttributes =
+                    calculateOverlayAttributes(
+                        params.activityStackTag,
+                        params.launchOptions.getOverlayAttributes(),
+                        WindowMetricsCalculator.translateWindowMetrics(
+                            params.parentContainerInfo.windowMetrics,
+                            density
+                        ),
+                        params.parentContainerInfo.configuration,
+                        ExtensionsWindowLayoutInfoAdapter.translate(
+                            windowMetrics,
+                            parentContainerInfo.windowLayoutInfo
+                        ),
+                    )
+
+                // TODO(b/295805497): Migrate to either custom animation APIs or new
+                //  ActivityStackAttributes APIs.
+                // Set alignment to the bundle options as the hint of the animation direction.
+                params.launchOptions.putActivityStackAlignment(overlayAttributes.bounds)
+                return@setActivityStackAttributesCalculator overlayAttributes
+                    .toActivityStackAttributes(parentContainerInfo)
+            }
+        }
+
+        embeddingExtension.registerActivityStackCallback(Runnable::run) { activityStacks ->
+            globalLock.withLock {
+                val lastOverlayTags = overlayTagToContainerMap.keys
+
+                overlayTagToContainerMap.clear()
+                overlayTagToContainerMap.putAll(
+                    activityStacks.getOverlayContainers().map { overlayContainer ->
+                        Pair(overlayContainer.tag!!, overlayContainer)
+                    }
+                )
+
+                cleanUpDismissedOverlayContainerRecords(lastOverlayTags)
+            }
+        }
+    }
+
+    /**
+     * Clean up records associated with dismissed overlay [activityStacks][ActivityStack] when
+     * there's a [ActivityStack] state update.
+     *
+     * The dismissed overlay [activityStacks][ActivityStack] are identified by comparing the
+     * differences of [ActivityStack] state before and after update.
+     *
+     * @param lastOverlayTags Overlay containers' tag before applying [ActivityStack] state update.
+     */
+    @GuardedBy("globalLock")
+    private fun cleanUpDismissedOverlayContainerRecords(lastOverlayTags: Set<String>) {
+        if (lastOverlayTags.isEmpty()) {
+            // If there's no last overlay container, return.
+            return
+        }
+
+        val dismissedOverlayTags = ArrayList<String>()
+        val currentOverlayTags = overlayTagToContainerMap.keys
+
+        for (overlayTag in lastOverlayTags) {
+            if (
+                overlayTag !in currentOverlayTags &&
+                    // If an overlay activityStack is not in the current overlay container list,
+                    // check
+                    // whether the activityStack does really not exist in WM Extensions in case
+                    // an overlay container is just launched, but th WM Jetpack hasn't received the
+                    // update yet.
+                    embeddingExtension.getActivityStackToken(overlayTag) == null
+            ) {
+                dismissedOverlayTags.add(overlayTag)
+            }
+        }
+
+        for (overlayTag in dismissedOverlayTags) {
+            overlayTagToDefaultAttributesMap.remove(overlayTag)
+            overlayTagToCurrentAttributesMap.remove(overlayTag)
+        }
+    }
+
+    /**
+     * Calculates the [OverlayAttributes] to report to the [ActivityStackAttributes] calculator.
+     *
+     * The calculator then computes [ActivityStackAttributes] for rendering the overlay
+     * [ActivityStack].
+     *
+     * @param tag The overlay [ActivityStack].
+     * @param initialOverlayAttrs The [OverlayCreateParams.overlayAttributes] that used to launching
+     *   this overlay [ActivityStack]
+     * @param windowMetrics The parent window container's [WindowMetrics]
+     * @param configuration The parent window container's [Configuration]
+     * @param windowLayoutInfo The parent window container's [WindowLayoutInfo]
+     */
+    @VisibleForTesting
+    internal fun calculateOverlayAttributes(
+        tag: String,
+        initialOverlayAttrs: OverlayAttributes?,
+        windowMetrics: WindowMetrics,
+        configuration: Configuration,
+        windowLayoutInfo: WindowLayoutInfo,
+    ): OverlayAttributes {
+        val defaultOverlayAttrs =
+            getUpdatedOverlayAttributes(tag)
+                ?: initialOverlayAttrs
+                ?: throw IllegalArgumentException(
+                    "Can't retrieve overlay attributes from launch options"
+                )
+        val currentOverlayAttrs =
+            overlayAttributesCalculator?.invoke(
+                OverlayAttributesCalculatorParams(
+                    windowMetrics,
+                    configuration,
+                    windowLayoutInfo,
+                    tag,
+                    defaultOverlayAttrs,
+                )
+            ) ?: defaultOverlayAttrs
+
+        overlayTagToCurrentAttributesMap[tag] = currentOverlayAttrs
+
+        return currentOverlayAttrs
+    }
+
+    @VisibleForTesting
+    internal open fun getUpdatedOverlayAttributes(overlayTag: String): OverlayAttributes? =
+        overlayTagToDefaultAttributesMap[overlayTag]
+
+    internal open fun updateOverlayAttributes(
+        overlayTag: String,
+        overlayAttributes: OverlayAttributes
+    ) {
+        globalLock.withLock {
+            val activityStackToken =
+                overlayTagToContainerMap[overlayTag]?.activityStackToken
+                    // Users may call this API before any callback coming. Try to ask platform if
+                    // this container exists.
+                    ?: embeddingExtension.getActivityStackToken(overlayTag)
+                    // Early return if there's no such ActivityStack associated with the tag.
+                    ?: return
+
+            embeddingExtension.updateActivityStackAttributes(
+                activityStackToken,
+                overlayAttributes.toActivityStackAttributes(
+                    embeddingExtension.getParentContainerInfo(activityStackToken)!!
+                )
+            )
+
+            // Update the tag-overlayAttributes map, which will be treated as the default
+            // overlayAttributes in calculator.
+            overlayTagToDefaultAttributesMap[overlayTag] = overlayAttributes
+            overlayTagToCurrentAttributesMap[overlayTag] = overlayAttributes
+        }
+    }
+
+    private fun OverlayAttributes.toActivityStackAttributes(
+        parentContainerInfo: ParentContainerInfo
+    ): ActivityStackAttributes =
+        ActivityStackAttributes.Builder()
+            .setRelativeBounds(
+                EmbeddingBounds.translateEmbeddingBounds(
+                        bounds,
+                        adapter.translate(parentContainerInfo)
+                    )
+                    .toRect()
+            )
+            .setWindowAttributes(adapter.translateWindowAttributes())
+            .build()
+
+    private fun List<ActivityStack>.getOverlayContainers(): List<ActivityStack> =
+        filter { activityStack -> activityStack.tag != null }.toList()
+
+    open fun addOverlayInfoCallback(
+        overlayTag: String,
+        executor: Executor,
+        overlayInfoCallback: JetpackConsumer<OverlayInfo>,
+    ) {
+        globalLock.withLock {
+            val callback =
+                Consumer2<List<ActivityStack>> { activityStacks ->
+                    val overlayInfoList =
+                        activityStacks.filter { activityStack -> activityStack.tag == overlayTag }
+                    if (overlayInfoList.size > 1) {
+                        throw IllegalStateException(
+                            "There must be at most one overlay ActivityStack with $overlayTag"
+                        )
+                    }
+                    val overlayInfo =
+                        if (overlayInfoList.isEmpty()) {
+                            OverlayInfo(
+                                overlayTag,
+                                currentOverlayAttributes = null,
+                                activityStack = null,
+                            )
+                        } else {
+                            overlayInfoList.first().toOverlayInfo()
+                        }
+                    overlayInfoCallback.accept(overlayInfo)
+                }
+            overlayInfoToActivityStackCallbackMap[overlayInfoCallback] = callback
+
+            embeddingExtension.registerActivityStackCallback(executor, callback)
+        }
+    }
+
+    private fun ActivityStack.toOverlayInfo(): OverlayInfo =
+        OverlayInfo(
+            tag!!,
+            overlayTagToCurrentAttributesMap[tag!!],
+            adapter.translate(this),
+        )
+
+    open fun removeOverlayInfoCallback(overlayInfoCallback: JetpackConsumer<OverlayInfo>) {
+        globalLock.withLock {
+            val callback = overlayInfoToActivityStackCallbackMap.remove(overlayInfoCallback)
+            if (callback != null) {
+                embeddingExtension.unregisterActivityStackCallback(callback)
+            }
+        }
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/OverlayCreateParams.kt b/window/window/src/main/java/androidx/window/embedding/OverlayCreateParams.kt
new file mode 100644
index 0000000..25548c0
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/OverlayCreateParams.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import androidx.annotation.RestrictTo
+import java.util.UUID
+
+/**
+ * The parameter container to create an overlay [ActivityStack].
+ *
+ * If there's an shown overlay [ActivityStack] associated with the [tag], the existing
+ * [ActivityStack] will be:
+ * - Dismissed if the overlay [ActivityStack] is in different task from the launched one
+ * - Updated with [OverlayAttributes] if the overlay [ActivityStack] is in the same task.
+ *
+ * See [android.os.Bundle.setOverlayCreateParams] for how to create an overlay [ActivityStack].
+ *
+ * @constructor creates a parameter container to launch an overlay [ActivityStack].
+ * @property tag The unique identifier of the overlay [ActivityStack], which will be generated
+ *   automatically if not specified.
+ * @property overlayAttributes The attributes of the overlay [ActivityStack], which defaults to the
+ *   default value of [OverlayAttributes].
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+class OverlayCreateParams
+@JvmOverloads
+constructor(
+    val tag: String = generateOverlayTag(),
+    val overlayAttributes: OverlayAttributes = OverlayAttributes.Builder().build(),
+) {
+    override fun toString(): String =
+        "${OverlayCreateParams::class.simpleName}:{ " +
+            ", tag=$tag" +
+            ", attrs=$overlayAttributes" +
+            "}"
+
+    /** The [OverlayCreateParams] builder. */
+    class Builder {
+        private var tag: String? = null
+        private var launchAttrs: OverlayAttributes? = null
+
+        /**
+         * Sets the overlay [ActivityStack]'s unique identifier. The builder will generate one
+         * automatically if not specified.
+         *
+         * @param tag The unique identifier of the overlay [ActivityStack] to create.
+         * @return The [OverlayCreateParams] builder.
+         */
+        fun setTag(tag: String): Builder = apply { this.tag = tag }
+
+        /**
+         * Sets the overlay [ActivityStack]'s attributes, which defaults to the default value of
+         * [OverlayAttributes.Builder].
+         *
+         * @param attrs The [OverlayAttributes].
+         * @return The [OverlayCreateParams] builder.
+         */
+        fun setOverlayAttributes(attrs: OverlayAttributes): Builder = apply { launchAttrs = attrs }
+
+        /** Builds the [OverlayCreateParams] */
+        fun build(): OverlayCreateParams =
+            OverlayCreateParams(
+                tag ?: generateOverlayTag(),
+                launchAttrs ?: OverlayAttributes.Builder().build()
+            )
+    }
+
+    companion object {
+
+        /** A helper function to generate a random unique identifier. */
+        @JvmStatic
+        fun generateOverlayTag(): String = UUID.randomUUID().toString().substring(IntRange(0, 32))
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/OverlayInfo.kt b/window/window/src/main/java/androidx/window/embedding/OverlayInfo.kt
new file mode 100644
index 0000000..6f76742
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/OverlayInfo.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.app.Activity
+import androidx.annotation.RestrictTo
+
+/** Describes an overlay [ActivityStack] associated with [OverlayCreateParams.tag]. */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+class OverlayInfo
+internal constructor(
+    /** The unique identifier associated with the overlay [ActivityStack]. */
+    val overlayTag: String,
+    /**
+     * The [OverlayAttributes] of the overlay [ActivityStack] if it exists, or `null` if there's no
+     * such a [ActivityStack]
+     */
+    val currentOverlayAttributes: OverlayAttributes?,
+    /**
+     * The overlay [ActivityStack] associated with [overlayTag], or `null` if there's no such a
+     * [ActivityStack].
+     */
+    val activityStack: ActivityStack?
+) {
+    operator fun contains(activity: Activity): Boolean = activityStack?.contains(activity) ?: false
+
+    override fun toString(): String =
+        "${OverlayInfo::class.java.simpleName}: {" +
+            "tag=$overlayTag" +
+            ", currentOverlayAttrs=$currentOverlayAttributes" +
+            ", activityStack=$activityStack" +
+            "}"
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/ParentContainerInfo.kt b/window/window/src/main/java/androidx/window/embedding/ParentContainerInfo.kt
new file mode 100644
index 0000000..9edb443
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/ParentContainerInfo.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.window.embedding
+
+import android.content.res.Configuration
+import androidx.core.view.WindowInsetsCompat
+import androidx.window.core.Bounds
+import androidx.window.layout.WindowLayoutInfo
+
+/**
+ * The parent container information directly passed from WM Extensions, which is created to make
+ * test implementation easier.
+ *
+ * @property windowBounds The parent container's [Bounds].
+ * @property windowLayoutInfo The parent container's [WindowLayoutInfo].
+ * @property windowInsets The parent container's [WindowInsetsCompat].
+ * @property configuration The parent container's [Configuration].
+ * @property density The parent container's density in DP, which has the same unit as
+ *   [android.util.DisplayMetrics.density].
+ */
+internal data class ParentContainerInfo(
+    /** The parent container's [Bounds]. */
+    val windowBounds: Bounds,
+    /** The parent container's [WindowLayoutInfo]. */
+    val windowLayoutInfo: WindowLayoutInfo,
+    /** The parent container's [WindowInsetsCompat] */
+    val windowInsets: WindowInsetsCompat,
+    /** The parent container's [Configuration]. */
+    val configuration: Configuration,
+    /**
+     * The parent container's density in DP, which has the same unit as
+     * [android.util.DisplayMetrics.density].
+     */
+    val density: Float,
+)
diff --git a/window/window/src/main/java/androidx/window/embedding/RuleParser.kt b/window/window/src/main/java/androidx/window/embedding/RuleParser.kt
index 55905cc..666196a 100644
--- a/window/window/src/main/java/androidx/window/embedding/RuleParser.kt
+++ b/window/window/src/main/java/androidx/window/embedding/RuleParser.kt
@@ -168,6 +168,9 @@
                     ALWAYS.value
                 )
             val clearTop = typedArray.getBoolean(R.styleable.SplitPairRule_clearTop, false)
+            val animationBackgroundColor =
+                typedArray.getColor(R.styleable.SplitPairRule_animationBackgroundColor, 0)
+            typedArray.recycle()
 
             val defaultAttrs =
                 SplitAttributes.Builder()
@@ -175,6 +178,9 @@
                     .setLayoutDirection(
                         SplitAttributes.LayoutDirection.getLayoutDirectionFromValue(layoutDir)
                     )
+                    .setAnimationBackground(
+                        EmbeddingAnimationBackground.buildFromValue(animationBackgroundColor)
+                    )
                     .build()
 
             SplitPairRule.Builder(emptySet())
@@ -249,6 +255,9 @@
                     R.styleable.SplitPlaceholderRule_splitLayoutDirection,
                     LOCALE.value
                 )
+            val animationBackgroundColor =
+                typedArray.getColor(R.styleable.SplitPlaceholderRule_animationBackgroundColor, 0)
+            typedArray.recycle()
 
             val defaultAttrs =
                 SplitAttributes.Builder()
@@ -256,6 +265,9 @@
                     .setLayoutDirection(
                         SplitAttributes.LayoutDirection.getLayoutDirectionFromValue(layoutDir)
                     )
+                    .setAnimationBackground(
+                        EmbeddingAnimationBackground.buildFromValue(animationBackgroundColor)
+                    )
                     .build()
             val packageName = context.applicationContext.packageName
             val placeholderActivityClassName =
diff --git a/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt b/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
index 400ff6a..61492a9 100644
--- a/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SafeActivityEmbeddingComponentProvider.kt
@@ -18,25 +18,43 @@
 
 import android.app.Activity
 import android.content.Intent
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.os.Bundle
+import android.os.IBinder
+import android.view.WindowMetrics
 import androidx.annotation.VisibleForTesting
 import androidx.window.SafeWindowExtensionsProvider
+import androidx.window.WindowSdkExtensions
 import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExtensionsUtil
 import androidx.window.extensions.WindowExtensions
 import androidx.window.extensions.core.util.function.Consumer
 import androidx.window.extensions.core.util.function.Function
+import androidx.window.extensions.core.util.function.Predicate
 import androidx.window.extensions.embedding.ActivityEmbeddingComponent
 import androidx.window.extensions.embedding.ActivityRule
 import androidx.window.extensions.embedding.ActivityStack
+import androidx.window.extensions.embedding.ActivityStackAttributes
+import androidx.window.extensions.embedding.ActivityStackAttributesCalculatorParams
+import androidx.window.extensions.embedding.AnimationBackground
+import androidx.window.extensions.embedding.DividerAttributes
+import androidx.window.extensions.embedding.EmbeddedActivityWindowInfo
+import androidx.window.extensions.embedding.ParentContainerInfo
 import androidx.window.extensions.embedding.SplitAttributes
 import androidx.window.extensions.embedding.SplitAttributes.SplitType
+import androidx.window.extensions.embedding.SplitAttributesCalculatorParams
 import androidx.window.extensions.embedding.SplitInfo
 import androidx.window.extensions.embedding.SplitPairRule
+import androidx.window.extensions.embedding.SplitPinRule
 import androidx.window.extensions.embedding.SplitPlaceholderRule
+import androidx.window.extensions.embedding.SplitRule
+import androidx.window.extensions.embedding.WindowAttributes
+import androidx.window.extensions.layout.WindowLayoutInfo
 import androidx.window.reflection.ReflectionUtils.doesReturn
 import androidx.window.reflection.ReflectionUtils.isPublic
 import androidx.window.reflection.ReflectionUtils.validateReflection
 import androidx.window.reflection.WindowExtensionsConstants.ACTIVITY_EMBEDDING_COMPONENT_CLASS
+import java.util.concurrent.Executor
 
 /**
  * Reflection Guard for [ActivityEmbeddingComponent]. This will go through the
@@ -69,10 +87,12 @@
         }
         // TODO(b/267573854) : update logic to fallback to lower version
         //  if higher version is not matched
-        return when (ExtensionsUtil.safeVendorApiLevel) {
+        return when (WindowSdkExtensions.getInstance().extensionVersion) {
             1 -> hasValidVendorApiLevel1()
-            in 2..Int.MAX_VALUE -> hasValidVendorApiLevel2()
-            // TODO(b/267956499) : add  hasValidVendorApiLevel3
+            2 -> hasValidVendorApiLevel2()
+            in 3..4 -> hasValidVendorApiLevel3() // No additional API in 4.
+            5 -> hasValidVendorApiLevel5()
+            in 6..Int.MAX_VALUE -> hasValidVendorApiLevel6()
             else -> false
         }
     }
@@ -83,25 +103,34 @@
             isActivityEmbeddingComponentValid()
 
     /**
-     * [WindowExtensions.VENDOR_API_LEVEL_1] includes the following methods:
+     * Vendor API level 1 includes the following methods:
      * - [ActivityEmbeddingComponent.setEmbeddingRules]
      * - [ActivityEmbeddingComponent.isActivityEmbedded]
-     * - [ActivityEmbeddingComponent.setSplitInfoCallback] with [java.util.function.Consumer] and
-     *   following classes:
+     * - [ActivityEmbeddingComponent.setSplitInfoCallback] with [java.util.function.Consumer]
+     * - [SplitRule.getSplitRatio]
+     * - [SplitRule.getLayoutDirection] and following classes:
      * - [ActivityRule]
+     * - [ActivityRule.Builder]
      * - [SplitInfo]
      * - [SplitPairRule]
+     * - [SplitPairRule.Builder]
      * - [SplitPlaceholderRule]
+     * - [SplitPlaceholderRule.Builder]
      */
     @VisibleForTesting
     internal fun hasValidVendorApiLevel1(): Boolean {
         return isMethodSetEmbeddingRulesValid() &&
             isMethodIsActivityEmbeddedValid() &&
             isMethodSetSplitInfoCallbackJavaConsumerValid() &&
+            isMethodGetSplitRatioValid() &&
+            isMethodGetLayoutDirectionValid() &&
             isClassActivityRuleValid() &&
+            isClassActivityRuleBuilderLevel1Valid() &&
             isClassSplitInfoValid() &&
             isClassSplitPairRuleValid() &&
-            isClassSplitPlaceholderRuleValid()
+            isClassSplitPairRuleBuilderLevel1Valid() &&
+            isClassSplitPlaceholderRuleValid() &&
+            isClassSplitPlaceholderRuleBuilderLevel1Valid()
     }
 
     /**
@@ -110,9 +139,16 @@
      * - [ActivityEmbeddingComponent.clearSplitInfoCallback]
      * - [ActivityEmbeddingComponent.setSplitAttributesCalculator]
      * - [ActivityEmbeddingComponent.clearSplitAttributesCalculator]
-     * - [SplitInfo.getSplitAttributes] and following classes:
+     * - [SplitInfo.getSplitAttributes]
+     * - [SplitPlaceholderRule.getFinishPrimaryWithPlaceholder]
+     * - [SplitRule.getDefaultSplitAttributes] and following classes:
+     * - [ActivityRule.Builder]
+     * - [EmbeddingRule]
      * - [SplitAttributes]
      * - [SplitAttributes.SplitType]
+     * - [SplitAttributesCalculatorParams]
+     * - [SplitPairRule.Builder]
+     * - [SplitPlaceholderRule.Builder]
      */
     @VisibleForTesting
     internal fun hasValidVendorApiLevel2(): Boolean {
@@ -121,10 +157,135 @@
             isMethodClearSplitInfoCallbackValid() &&
             isMethodSplitAttributesCalculatorValid() &&
             isMethodGetSplitAttributesValid() &&
+            isMethodGetFinishPrimaryWithPlaceholderValid() &&
+            isMethodGetDefaultSplitAttributesValid() &&
+            isClassActivityRuleBuilderLevel2Valid() &&
+            isClassEmbeddingRuleValid() &&
             isClassSplitAttributesValid() &&
-            isClassSplitTypeValid()
+            isClassSplitAttributesCalculatorParamsValid() &&
+            isClassSplitTypeValid() &&
+            isClassSplitPairRuleBuilderLevel2Valid() &&
+            isClassSplitPlaceholderRuleBuilderLevel2Valid()
     }
 
+    /**
+     * Vendor API level 3 includes the following methods:
+     * - [ActivityEmbeddingComponent.updateSplitAttributes]
+     * - [ActivityEmbeddingComponent.invalidateTopVisibleSplitAttributes]
+     * - [SplitInfo.getToken]
+     */
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel3(): Boolean =
+        hasValidVendorApiLevel2() &&
+            isMethodInvalidateTopVisibleSplitAttributesValid() &&
+            isMethodUpdateSplitAttributesValid() &&
+            isMethodSplitInfoGetTokenValid()
+
+    /**
+     * Vendor API level 5 includes the following methods:
+     * - [ActivityEmbeddingComponent.registerActivityStackCallback]
+     * - [ActivityEmbeddingComponent.unregisterActivityStackCallback]
+     * - [ActivityStack.getActivityStackToken]
+     * - [ActivityStack.Token.createFromBinder]
+     * - [ActivityStack.Token.readFromBundle]
+     * - [ActivityStack.Token.toBundle]
+     * - [ActivityStack.Token.INVALID_ACTIVITY_STACK_TOKEN]
+     * - [AnimationBackground.createColorBackground]
+     * - [AnimationBackground.ANIMATION_BACKGROUND_DEFAULT]
+     * - [SplitAttributes.getAnimationBackground]
+     * - [SplitAttributes.Builder.setAnimationBackground]
+     * - [WindowAttributes.getDimAreaBehavior]
+     * - [SplitAttributes.getWindowAttributes]
+     * - [SplitAttributes.Builder.setWindowAttributes]
+     * - [SplitPinRule.isSticky]
+     * - [ActivityEmbeddingComponent.pinTopActivityStack]
+     * - [ActivityEmbeddingComponent.unpinTopActivityStack]
+     * - [ActivityEmbeddingComponent.updateSplitAttributes] with [SplitInfo.Token]
+     * - [SplitInfo.getSplitInfoToken] and following classes:
+     * - [AnimationBackground]
+     * - [ActivityStack.Token]
+     * - [WindowAttributes]
+     * - [SplitInfo.Token]
+     */
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel5(): Boolean =
+        hasValidVendorApiLevel3() &&
+            isActivityStackGetActivityStackTokenValid() &&
+            isMethodRegisterActivityStackCallbackValid() &&
+            isMethodUnregisterActivityStackCallbackValid() &&
+            isMethodPinUnpinTopActivityStackValid() &&
+            isMethodUpdateSplitAttributesWithTokenValid() &&
+            isMethodGetSplitInfoTokenValid() &&
+            isClassAnimationBackgroundValid() &&
+            isClassActivityStackTokenValid() &&
+            isClassWindowAttributesValid() &&
+            isClassSplitInfoTokenValid()
+
+    /**
+     * Vendor API level 6 includes the following methods:
+     * - [ActivityEmbeddingComponent.clearEmbeddedActivityWindowInfoCallback]
+     * - [ActivityEmbeddingComponent.getEmbeddedActivityWindowInfo]
+     * - [ActivityEmbeddingComponent.setEmbeddedActivityWindowInfoCallback]
+     * - [SplitAttributes.getDividerAttributes]
+     * - [SplitAttributes.Builder.setDividerAttributes] and following classes:
+     * - [EmbeddedActivityWindowInfo]
+     * - [DividerAttributes]
+     * - [DividerAttributes.Builder]
+     */
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel6(): Boolean =
+        hasValidVendorApiLevel5() &&
+            isMethodGetEmbeddedActivityWindowInfoValid() &&
+            isMethodSetEmbeddedActivityWindowInfoCallbackValid() &&
+            isMethodClearEmbeddedActivityWindowInfoCallbackValid() &&
+            isMethodGetDividerAttributesValid() &&
+            isMethodSetDividerAttributesValid() &&
+            isClassEmbeddedActivityWindowInfoValid() &&
+            isClassDividerAttributesValid() &&
+            isClassDividerAttributesBuilderValid()
+
+    /**
+     * Overlay features includes the following methods:
+     * - [ActivityEmbeddingComponent.clearActivityStackAttributesCalculator]
+     * - [ActivityEmbeddingComponent.getActivityStackToken]
+     * - [ActivityEmbeddingComponent.getParentContainerInfo]
+     * - [ActivityEmbeddingComponent.setActivityStackAttributesCalculator]
+     * - [ActivityEmbeddingComponent.updateActivityStackAttributes]
+     * - [ActivityStack.getTag] and following classes:
+     * - [ParentContainerInfo]
+     * - [ActivityStackAttributes]
+     * - [ActivityStackAttributes.Builder]
+     * - [ActivityStackAttributesCalculatorParams]
+     */
+    private fun isOverlayFeatureValid(): Boolean =
+        isActivityStackGetTagValid() &&
+            isMethodGetActivityStackTokenValid() &&
+            isClassParentContainerInfoValid() &&
+            isMethodGetParentContainerInfoValid() &&
+            isMethodSetActivityStackAttributesCalculatorValid() &&
+            isMethodClearActivityStackAttributesCalculatorValid() &&
+            isMethodUpdateActivityStackAttributesValid() &&
+            isClassActivityStackAttributesValid() &&
+            isClassActivityStackAttributesBuilderValid() &&
+            isClassActivityStackAttributesCalculatorParamsValid()
+
+    private val activityEmbeddingComponentClass: Class<*>
+        get() {
+            return loader.loadClass(ACTIVITY_EMBEDDING_COMPONENT_CLASS)
+        }
+
+    private fun isActivityEmbeddingComponentValid(): Boolean {
+        return validateReflection("WindowExtensions#getActivityEmbeddingComponent is not valid") {
+            val extensionsClass = safeWindowExtensionsProvider.windowExtensionsClass
+            val getActivityEmbeddingComponentMethod =
+                extensionsClass.getMethod("getActivityEmbeddingComponent")
+            val activityEmbeddingComponentClass = activityEmbeddingComponentClass
+            getActivityEmbeddingComponentMethod.isPublic &&
+                getActivityEmbeddingComponentMethod.doesReturn(activityEmbeddingComponentClass)
+        }
+    }
+
+    /** Vendor API level 1 validation methods */
     private fun isMethodSetEmbeddingRulesValid(): Boolean {
         return validateReflection("ActivityEmbeddingComponent#setEmbeddingRules is not valid") {
             val setEmbeddingRulesMethod =
@@ -145,6 +306,146 @@
         }
     }
 
+    private fun isMethodSetSplitInfoCallbackJavaConsumerValid(): Boolean {
+        return validateReflection("ActivityEmbeddingComponent#setSplitInfoCallback is not valid") {
+            val consumerClass =
+                consumerAdapter.consumerClassOrNull() ?: return@validateReflection false
+            val setSplitInfoCallbackMethod =
+                activityEmbeddingComponentClass.getMethod("setSplitInfoCallback", consumerClass)
+            setSplitInfoCallbackMethod.isPublic
+        }
+    }
+
+    private fun isMethodGetSplitRatioValid(): Boolean =
+        validateReflection("SplitRule#getSplitRatio is not valid") {
+            val splitRuleClass = SplitRule::class.java
+            val getSplitRatioMethod = splitRuleClass.getMethod("getSplitRatio")
+            getSplitRatioMethod.isPublic && getSplitRatioMethod.doesReturn(Float::class.java)
+        }
+
+    private fun isMethodGetLayoutDirectionValid(): Boolean =
+        validateReflection("SplitRule#getLayoutDirection is not valid") {
+            val splitRuleClass = SplitRule::class.java
+            val getLayoutDirectionMethod = splitRuleClass.getMethod("getLayoutDirection")
+            getLayoutDirectionMethod.isPublic &&
+                getLayoutDirectionMethod.doesReturn(Int::class.java)
+        }
+
+    private fun isClassActivityRuleValid(): Boolean =
+        validateReflection("Class ActivityRule is not valid") {
+            val activityRuleClass = ActivityRule::class.java
+            val shouldAlwaysExpandMethod = activityRuleClass.getMethod("shouldAlwaysExpand")
+            shouldAlwaysExpandMethod.isPublic &&
+                shouldAlwaysExpandMethod.doesReturn(Boolean::class.java)
+        }
+
+    private fun isClassActivityRuleBuilderLevel1Valid(): Boolean =
+        validateReflection("Class ActivityRule.Builder is not valid") {
+            val activityRuleBuilderClass = ActivityRule.Builder::class.java
+            val setShouldAlwaysExpandMethod =
+                activityRuleBuilderClass.getMethod("setShouldAlwaysExpand", Boolean::class.java)
+            setShouldAlwaysExpandMethod.isPublic &&
+                setShouldAlwaysExpandMethod.doesReturn(ActivityRule.Builder::class.java)
+        }
+
+    private fun isClassSplitInfoValid(): Boolean =
+        validateReflection("Class SplitInfo is not valid") {
+            val splitInfoClass = SplitInfo::class.java
+            val getPrimaryActivityStackMethod = splitInfoClass.getMethod("getPrimaryActivityStack")
+            val getSecondaryActivityStackMethod =
+                splitInfoClass.getMethod("getSecondaryActivityStack")
+            val getSplitRatioMethod = splitInfoClass.getMethod("getSplitRatio")
+            getPrimaryActivityStackMethod.isPublic &&
+                getPrimaryActivityStackMethod.doesReturn(ActivityStack::class.java) &&
+                getSecondaryActivityStackMethod.isPublic &&
+                getSecondaryActivityStackMethod.doesReturn(ActivityStack::class.java) &&
+                getSplitRatioMethod.isPublic &&
+                getSplitRatioMethod.doesReturn(Float::class.java)
+        }
+
+    private fun isClassSplitPairRuleValid(): Boolean =
+        validateReflection("Class SplitPairRule is not valid") {
+            val splitPairRuleClass = SplitPairRule::class.java
+            val getFinishPrimaryWithSecondaryMethod =
+                splitPairRuleClass.getMethod("getFinishPrimaryWithSecondary")
+            val getFinishSecondaryWithPrimaryMethod =
+                splitPairRuleClass.getMethod("getFinishSecondaryWithPrimary")
+            val shouldClearTopMethod = splitPairRuleClass.getMethod("shouldClearTop")
+            getFinishPrimaryWithSecondaryMethod.isPublic &&
+                getFinishPrimaryWithSecondaryMethod.doesReturn(Int::class.java) &&
+                getFinishSecondaryWithPrimaryMethod.isPublic &&
+                getFinishSecondaryWithPrimaryMethod.doesReturn(Int::class.java) &&
+                shouldClearTopMethod.isPublic &&
+                shouldClearTopMethod.doesReturn(Boolean::class.java)
+        }
+
+    private fun isClassSplitPairRuleBuilderLevel1Valid(): Boolean =
+        validateReflection("Class SplitPairRule.Builder is not valid") {
+            val splitPairRuleBuilderClass = SplitPairRule.Builder::class.java
+            val setSplitRatioMethod =
+                splitPairRuleBuilderClass.getMethod("setSplitRatio", Float::class.java)
+            val setLayoutDirectionMethod =
+                splitPairRuleBuilderClass.getMethod("setLayoutDirection", Int::class.java)
+            setSplitRatioMethod.isPublic &&
+                setSplitRatioMethod.doesReturn(SplitPairRule.Builder::class.java) &&
+                setLayoutDirectionMethod.isPublic &&
+                setLayoutDirectionMethod.doesReturn(SplitPairRule.Builder::class.java)
+        }
+
+    private fun isClassSplitPlaceholderRuleValid(): Boolean =
+        validateReflection("Class SplitPlaceholderRule is not valid") {
+            val splitPlaceholderRuleClass = SplitPlaceholderRule::class.java
+            val getPlaceholderIntentMethod =
+                splitPlaceholderRuleClass.getMethod("getPlaceholderIntent")
+            val isStickyMethod = splitPlaceholderRuleClass.getMethod("isSticky")
+            val getFinishPrimaryWithSecondaryMethod =
+                splitPlaceholderRuleClass.getMethod("getFinishPrimaryWithSecondary")
+            getPlaceholderIntentMethod.isPublic &&
+                getPlaceholderIntentMethod.doesReturn(Intent::class.java) &&
+                isStickyMethod.isPublic &&
+                isStickyMethod.doesReturn(Boolean::class.java) &&
+                getFinishPrimaryWithSecondaryMethod.isPublic &&
+                getFinishPrimaryWithSecondaryMethod.doesReturn(Int::class.java)
+        }
+
+    private fun isClassSplitPlaceholderRuleBuilderLevel1Valid(): Boolean =
+        validateReflection("Class SplitPlaceholderRule.Builder is not valid") {
+            val splitPlaceholderRuleBuilderClass = SplitPlaceholderRule.Builder::class.java
+            val setSplitRatioMethod =
+                splitPlaceholderRuleBuilderClass.getMethod("setSplitRatio", Float::class.java)
+            val setLayoutDirectionMethod =
+                splitPlaceholderRuleBuilderClass.getMethod("setLayoutDirection", Int::class.java)
+            val setStickyMethod =
+                splitPlaceholderRuleBuilderClass.getMethod("setSticky", Boolean::class.java)
+            val setFinishPrimaryWithSecondaryMethod =
+                splitPlaceholderRuleBuilderClass.getMethod(
+                    "setFinishPrimaryWithSecondary",
+                    Int::class.java
+                )
+            setSplitRatioMethod.isPublic &&
+                setSplitRatioMethod.doesReturn(SplitPlaceholderRule.Builder::class.java) &&
+                setLayoutDirectionMethod.isPublic &&
+                setLayoutDirectionMethod.doesReturn(SplitPlaceholderRule.Builder::class.java) &&
+                setStickyMethod.isPublic &&
+                setStickyMethod.doesReturn(SplitPlaceholderRule.Builder::class.java) &&
+                setFinishPrimaryWithSecondaryMethod.isPublic &&
+                setFinishPrimaryWithSecondaryMethod.doesReturn(
+                    SplitPlaceholderRule.Builder::class.java
+                )
+        }
+
+    /** Vendor API level 2 validation methods */
+    private fun isMethodSetSplitInfoCallbackWindowConsumerValid(): Boolean {
+        return validateReflection("ActivityEmbeddingComponent#setSplitInfoCallback is not valid") {
+            val setSplitInfoCallbackMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "setSplitInfoCallback",
+                    Consumer::class.java
+                )
+            setSplitInfoCallbackMethod.isPublic
+        }
+    }
+
     private fun isMethodClearSplitInfoCallbackValid(): Boolean {
         return validateReflection(
             "ActivityEmbeddingComponent#clearSplitInfoCallback is not valid"
@@ -179,6 +480,45 @@
                 getSplitAttributesMethod.doesReturn(SplitAttributes::class.java)
         }
 
+    private fun isMethodGetFinishPrimaryWithPlaceholderValid(): Boolean =
+        validateReflection("SplitPlaceholderRule#getFinishPrimaryWithPlaceholder is not valid") {
+            val splitPlaceholderRuleClass = SplitPlaceholderRule::class.java
+            val getFinishPrimaryWithPlaceholderMethod =
+                splitPlaceholderRuleClass.getMethod("getFinishPrimaryWithPlaceholder")
+            getFinishPrimaryWithPlaceholderMethod.isPublic &&
+                getFinishPrimaryWithPlaceholderMethod.doesReturn(Int::class.java)
+        }
+
+    private fun isMethodGetDefaultSplitAttributesValid(): Boolean =
+        validateReflection("SplitRule#getDefaultSplitAttributes is not valid") {
+            val splitRuleClass = SplitRule::class.java
+            val getDefaultSplitAttributesMethod =
+                splitRuleClass.getMethod("getDefaultSplitAttributes")
+            getDefaultSplitAttributesMethod.isPublic &&
+                getDefaultSplitAttributesMethod.doesReturn(SplitAttributes::class.java)
+        }
+
+    private fun isClassActivityRuleBuilderLevel2Valid(): Boolean =
+        validateReflection("Class ActivityRule.Builder is not valid") {
+            val activityRuleBuilderClass = ActivityRule.Builder::class.java
+            val activityRuleBuilderConstructor =
+                activityRuleBuilderClass.getDeclaredConstructor(
+                    Predicate::class.java,
+                    Predicate::class.java
+                )
+            val setTagMethod = activityRuleBuilderClass.getMethod("setTag", String::class.java)
+            activityRuleBuilderConstructor.isPublic &&
+                setTagMethod.isPublic &&
+                setTagMethod.doesReturn(ActivityRule.Builder::class.java)
+        }
+
+    private fun isClassEmbeddingRuleValid(): Boolean =
+        validateReflection("Class EmbeddingRule is not valid") {
+            val embeddingRuleClass = EmbeddingRule::class.java
+            val getTagMethod = embeddingRuleClass.getMethod("getTag")
+            getTagMethod.isPublic && getTagMethod.doesReturn(String::class.java)
+        }
+
     private fun isClassSplitAttributesValid(): Boolean =
         validateReflection("Class SplitAttributes is not valid") {
             val splitAttributesClass = SplitAttributes::class.java
@@ -197,6 +537,36 @@
                 setLayoutDirectionMethod.isPublic
         }
 
+    @Suppress("newApi") // Suppress lint check for WindowMetrics
+    private fun isClassSplitAttributesCalculatorParamsValid(): Boolean =
+        validateReflection("Class SplitAttributesCalculatorParams is not valid") {
+            val splitAttributesCalculatorParamsClass = SplitAttributesCalculatorParams::class.java
+            val getParentWindowMetricsMethod =
+                splitAttributesCalculatorParamsClass.getMethod("getParentWindowMetrics")
+            val getParentConfigurationMethod =
+                splitAttributesCalculatorParamsClass.getMethod("getParentConfiguration")
+            val getDefaultSplitAttributesMethod =
+                splitAttributesCalculatorParamsClass.getMethod("getDefaultSplitAttributes")
+            val areDefaultConstraintsSatisfiedMethod =
+                splitAttributesCalculatorParamsClass.getMethod("areDefaultConstraintsSatisfied")
+            val getParentWindowLayoutInfoMethod =
+                splitAttributesCalculatorParamsClass.getMethod("getParentWindowLayoutInfo")
+            val getSplitRuleTagMethod =
+                splitAttributesCalculatorParamsClass.getMethod("getSplitRuleTag")
+            getParentWindowMetricsMethod.isPublic &&
+                getParentWindowMetricsMethod.doesReturn(WindowMetrics::class.java) &&
+                getParentConfigurationMethod.isPublic &&
+                getParentConfigurationMethod.doesReturn(Configuration::class.java) &&
+                getDefaultSplitAttributesMethod.isPublic &&
+                getDefaultSplitAttributesMethod.doesReturn(SplitAttributes::class.java) &&
+                areDefaultConstraintsSatisfiedMethod.isPublic &&
+                areDefaultConstraintsSatisfiedMethod.doesReturn(Boolean::class.java) &&
+                getParentWindowLayoutInfoMethod.isPublic &&
+                getParentWindowLayoutInfoMethod.doesReturn(WindowLayoutInfo::class.java) &&
+                getSplitRuleTagMethod.isPublic &&
+                getSplitRuleTagMethod.doesReturn(String::class.java)
+        }
+
     private fun isClassSplitTypeValid(): Boolean =
         validateReflection("Class SplitAttributes.SplitType is not valid") {
             val ratioSplitTypeClass = SplitType.RatioSplitType::class.java
@@ -222,99 +592,486 @@
                 expandContainersSplitTypeConstructor.isPublic
         }
 
-    private fun isMethodSetSplitInfoCallbackJavaConsumerValid(): Boolean {
-        return validateReflection("ActivityEmbeddingComponent#setSplitInfoCallback is not valid") {
-            val consumerClass =
-                consumerAdapter.consumerClassOrNull() ?: return@validateReflection false
-            val setSplitInfoCallbackMethod =
-                activityEmbeddingComponentClass.getMethod("setSplitInfoCallback", consumerClass)
-            setSplitInfoCallbackMethod.isPublic
-        }
-    }
-
-    private fun isClassActivityRuleValid(): Boolean =
-        validateReflection("Class ActivityRule is not valid") {
-            val activityRuleClass = ActivityRule::class.java
-            val shouldAlwaysExpandMethod = activityRuleClass.getMethod("shouldAlwaysExpand")
-            val activityRuleBuilderClass = ActivityRule.Builder::class.java
-            val setShouldAlwaysExpandMethod =
-                activityRuleBuilderClass.getMethod("setShouldAlwaysExpand", Boolean::class.java)
-            shouldAlwaysExpandMethod.isPublic &&
-                shouldAlwaysExpandMethod.doesReturn(Boolean::class.java) &&
-                setShouldAlwaysExpandMethod.isPublic
+    private fun isClassSplitPairRuleBuilderLevel2Valid(): Boolean =
+        validateReflection("Class SplitPairRule.Builder is not valid") {
+            val splitPairRuleBuilderClass = SplitPairRule.Builder::class.java
+            val splitPairRuleBuilderConstructor =
+                splitPairRuleBuilderClass.getDeclaredConstructor(
+                    Predicate::class.java,
+                    Predicate::class.java,
+                    Predicate::class.java
+                )
+            val setDefaultSplitAttributesMethod =
+                splitPairRuleBuilderClass.getMethod(
+                    "setDefaultSplitAttributes",
+                    SplitAttributes::class.java,
+                )
+            val setTagMethod = splitPairRuleBuilderClass.getMethod("setTag", String::class.java)
+            splitPairRuleBuilderConstructor.isPublic &&
+                setDefaultSplitAttributesMethod.isPublic &&
+                setDefaultSplitAttributesMethod.doesReturn(SplitPairRule.Builder::class.java) &&
+                setTagMethod.isPublic &&
+                setTagMethod.doesReturn(SplitPairRule.Builder::class.java)
         }
 
-    private fun isClassSplitInfoValid(): Boolean =
-        validateReflection("Class SplitInfo is not valid") {
-            val splitInfoClass = SplitInfo::class.java
-            val getPrimaryActivityStackMethod = splitInfoClass.getMethod("getPrimaryActivityStack")
-            val getSecondaryActivityStackMethod =
-                splitInfoClass.getMethod("getSecondaryActivityStack")
-            val getSplitRatioMethod = splitInfoClass.getMethod("getSplitRatio")
-            getPrimaryActivityStackMethod.isPublic &&
-                getPrimaryActivityStackMethod.doesReturn(ActivityStack::class.java) &&
-                getSecondaryActivityStackMethod.isPublic &&
-                getSecondaryActivityStackMethod.doesReturn(ActivityStack::class.java) &&
-                getSplitRatioMethod.isPublic &&
-                getSplitRatioMethod.doesReturn(Float::class.java)
+    private fun isClassSplitPlaceholderRuleBuilderLevel2Valid(): Boolean =
+        validateReflection("Class SplitPlaceholderRule.Builder is not valid") {
+            val splitPlaceholderRuleBuilderClass = SplitPlaceholderRule.Builder::class.java
+            val splitPlaceholderRuleBuilderConstructor =
+                splitPlaceholderRuleBuilderClass.getDeclaredConstructor(
+                    Intent::class.java,
+                    Predicate::class.java,
+                    Predicate::class.java,
+                    Predicate::class.java
+                )
+            val setDefaultSplitAttributesMethod =
+                splitPlaceholderRuleBuilderClass.getMethod(
+                    "setDefaultSplitAttributes",
+                    SplitAttributes::class.java,
+                )
+            val setFinishPrimaryWithPlaceholderMethod =
+                splitPlaceholderRuleBuilderClass.getMethod(
+                    "setFinishPrimaryWithPlaceholder",
+                    Int::class.java
+                )
+            val setTagMethod =
+                splitPlaceholderRuleBuilderClass.getMethod("setTag", String::class.java)
+            splitPlaceholderRuleBuilderConstructor.isPublic &&
+                setDefaultSplitAttributesMethod.isPublic &&
+                setDefaultSplitAttributesMethod.doesReturn(
+                    SplitPlaceholderRule.Builder::class.java
+                ) &&
+                setFinishPrimaryWithPlaceholderMethod.isPublic &&
+                setFinishPrimaryWithPlaceholderMethod.doesReturn(
+                    SplitPlaceholderRule.Builder::class.java
+                ) &&
+                setTagMethod.isPublic &&
+                setTagMethod.doesReturn(SplitPlaceholderRule.Builder::class.java)
         }
 
-    private fun isClassSplitPairRuleValid(): Boolean =
-        validateReflection("Class SplitPairRule is not valid") {
-            val splitPairRuleClass = SplitPairRule::class.java
-            val getFinishPrimaryWithSecondaryMethod =
-                splitPairRuleClass.getMethod("getFinishPrimaryWithSecondary")
-            val getFinishSecondaryWithPrimaryMethod =
-                splitPairRuleClass.getMethod("getFinishSecondaryWithPrimary")
-            val shouldClearTopMethod = splitPairRuleClass.getMethod("shouldClearTop")
-            getFinishPrimaryWithSecondaryMethod.isPublic &&
-                getFinishPrimaryWithSecondaryMethod.doesReturn(Int::class.java) &&
-                getFinishSecondaryWithPrimaryMethod.isPublic &&
-                getFinishSecondaryWithPrimaryMethod.doesReturn(Int::class.java) &&
-                shouldClearTopMethod.isPublic &&
-                shouldClearTopMethod.doesReturn(Boolean::class.java)
+    /** Vendor API level 3 validation methods */
+    private fun isMethodInvalidateTopVisibleSplitAttributesValid(): Boolean =
+        validateReflection("#invalidateTopVisibleSplitAttributes is not valid") {
+            val invalidateTopVisibleSplitAttributesMethod =
+                activityEmbeddingComponentClass.getMethod("invalidateTopVisibleSplitAttributes")
+            invalidateTopVisibleSplitAttributesMethod.isPublic
         }
 
-    private fun isClassSplitPlaceholderRuleValid(): Boolean =
-        validateReflection("Class SplitPlaceholderRule is not valid") {
-            val splitPlaceholderRuleClass = SplitPlaceholderRule::class.java
-            val getPlaceholderIntentMethod =
-                splitPlaceholderRuleClass.getMethod("getPlaceholderIntent")
-            val isStickyMethod = splitPlaceholderRuleClass.getMethod("isSticky")
-            val getFinishPrimaryWithSecondaryMethod =
-                splitPlaceholderRuleClass.getMethod("getFinishPrimaryWithSecondary")
-            getPlaceholderIntentMethod.isPublic &&
-                getPlaceholderIntentMethod.doesReturn(Intent::class.java) &&
-                isStickyMethod.isPublic &&
-                isStickyMethod.doesReturn(Boolean::class.java)
-            getFinishPrimaryWithSecondaryMethod.isPublic &&
-                getFinishPrimaryWithSecondaryMethod.doesReturn(Int::class.java)
-        }
-
-    private fun isMethodSetSplitInfoCallbackWindowConsumerValid(): Boolean {
-        return validateReflection("ActivityEmbeddingComponent#setSplitInfoCallback is not valid") {
-            val setSplitInfoCallbackMethod =
+    private fun isMethodUpdateSplitAttributesValid(): Boolean =
+        validateReflection("#updateSplitAttributes is not valid") {
+            val updateSplitAttributesMethod =
                 activityEmbeddingComponentClass.getMethod(
-                    "setSplitInfoCallback",
+                    "updateSplitAttributes",
+                    IBinder::class.java,
+                    SplitAttributes::class.java
+                )
+            updateSplitAttributesMethod.isPublic
+        }
+
+    private fun isMethodSplitInfoGetTokenValid(): Boolean =
+        validateReflection("SplitInfo#getToken is not valid") {
+            val splitInfoClass = SplitInfo::class.java
+            val getTokenMethod = splitInfoClass.getMethod("getToken")
+            getTokenMethod.isPublic && getTokenMethod.doesReturn(IBinder::class.java)
+        }
+
+    /** Vendor API level 5 validation methods */
+    private fun isActivityStackGetActivityStackTokenValid(): Boolean =
+        validateReflection("ActivityStack#getActivityToken is not valid") {
+            val activityStackClass = ActivityStack::class.java
+            val getActivityStackTokenMethod = activityStackClass.getMethod("getActivityStackToken")
+
+            getActivityStackTokenMethod.isPublic &&
+                getActivityStackTokenMethod.doesReturn(ActivityStack.Token::class.java)
+        }
+
+    private fun isMethodRegisterActivityStackCallbackValid(): Boolean =
+        validateReflection("registerActivityStackCallback is not valid") {
+            val registerActivityStackCallbackMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "registerActivityStackCallback",
+                    Executor::class.java,
                     Consumer::class.java
                 )
-            setSplitInfoCallbackMethod.isPublic
+            registerActivityStackCallbackMethod.isPublic
         }
-    }
 
-    private fun isActivityEmbeddingComponentValid(): Boolean {
-        return validateReflection("WindowExtensions#getActivityEmbeddingComponent is not valid") {
-            val extensionsClass = safeWindowExtensionsProvider.windowExtensionsClass
-            val getActivityEmbeddingComponentMethod =
-                extensionsClass.getMethod("getActivityEmbeddingComponent")
-            val activityEmbeddingComponentClass = activityEmbeddingComponentClass
-            getActivityEmbeddingComponentMethod.isPublic &&
-                getActivityEmbeddingComponentMethod.doesReturn(activityEmbeddingComponentClass)
+    private fun isMethodUnregisterActivityStackCallbackValid(): Boolean =
+        validateReflection("unregisterActivityStackCallback is not valid") {
+            val unregisterActivityStackCallbackMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "unregisterActivityStackCallback",
+                    Consumer::class.java
+                )
+            unregisterActivityStackCallbackMethod.isPublic
         }
-    }
 
-    private val activityEmbeddingComponentClass: Class<*>
-        get() {
-            return loader.loadClass(ACTIVITY_EMBEDDING_COMPONENT_CLASS)
+    private fun isMethodPinUnpinTopActivityStackValid(): Boolean =
+        validateReflection("#pin(unPin)TopActivityStack is not valid") {
+            val splitPinRuleClass = SplitPinRule::class.java
+            val isStickyMethod = splitPinRuleClass.getMethod("isSticky")
+            val pinTopActivityStackMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "pinTopActivityStack",
+                    Int::class.java,
+                    SplitPinRule::class.java
+                )
+            val unpinTopActivityStackMethod =
+                activityEmbeddingComponentClass.getMethod("unpinTopActivityStack", Int::class.java)
+            isStickyMethod.isPublic &&
+                isStickyMethod.doesReturn(Boolean::class.java) &&
+                pinTopActivityStackMethod.isPublic &&
+                pinTopActivityStackMethod.doesReturn(Boolean::class.java) &&
+                unpinTopActivityStackMethod.isPublic
+        }
+
+    private fun isMethodUpdateSplitAttributesWithTokenValid(): Boolean =
+        validateReflection("updateSplitAttributes is not valid") {
+            val updateSplitAttributesMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "updateSplitAttributes",
+                    SplitInfo.Token::class.java,
+                    SplitAttributes::class.java,
+                )
+            updateSplitAttributesMethod.isPublic
+        }
+
+    private fun isMethodGetSplitInfoTokenValid(): Boolean =
+        validateReflection("SplitInfo#getSplitInfoToken is not valid") {
+            val splitInfoClass = SplitInfo::class.java
+            val getSplitInfoToken = splitInfoClass.getMethod("getSplitInfoToken")
+            getSplitInfoToken.isPublic && getSplitInfoToken.doesReturn(SplitInfo.Token::class.java)
+        }
+
+    private fun isClassAnimationBackgroundValid(): Boolean =
+        validateReflection("Class AnimationBackground is not valid") {
+            val animationBackgroundClass = AnimationBackground::class.java
+            val colorBackgroundClass = AnimationBackground.ColorBackground::class.java
+            val createColorBackgroundMethod =
+                animationBackgroundClass.getMethod("createColorBackground", Int::class.java)
+            val animationBackgroundDefaultField =
+                animationBackgroundClass.getDeclaredField("ANIMATION_BACKGROUND_DEFAULT")
+            val colorBackgroundGetColor = colorBackgroundClass.getMethod("getColor")
+
+            val splitAttributesClass = SplitAttributes::class.java
+            val getAnimationBackgroundMethod =
+                splitAttributesClass.getMethod("getAnimationBackground")
+
+            val splitAttributesBuilderClass = SplitAttributes.Builder::class.java
+            val setAnimationBackgroundMethod =
+                splitAttributesBuilderClass.getMethod(
+                    "setAnimationBackground",
+                    AnimationBackground::class.java
+                )
+
+            createColorBackgroundMethod.isPublic &&
+                createColorBackgroundMethod.doesReturn(colorBackgroundClass) &&
+                animationBackgroundDefaultField.isPublic &&
+                colorBackgroundGetColor.isPublic &&
+                colorBackgroundGetColor.doesReturn(Int::class.java) &&
+                getAnimationBackgroundMethod.isPublic &&
+                getAnimationBackgroundMethod.doesReturn(animationBackgroundClass) &&
+                setAnimationBackgroundMethod.isPublic &&
+                setAnimationBackgroundMethod.doesReturn(SplitAttributes.Builder::class.java)
+        }
+
+    private fun isClassActivityStackTokenValid(): Boolean =
+        validateReflection("Class ActivityStack.Token is not valid") {
+            val activityStackTokenClass = ActivityStack.Token::class.java
+            val toBundleMethod = activityStackTokenClass.getMethod("toBundle")
+            val readFromBundle =
+                activityStackTokenClass.getMethod("readFromBundle", Bundle::class.java)
+            val createFromBinder =
+                activityStackTokenClass.getMethod("createFromBinder", IBinder::class.java)
+            val invalidActivityStackTokenField =
+                activityStackTokenClass.getDeclaredField("INVALID_ACTIVITY_STACK_TOKEN")
+
+            toBundleMethod.isPublic &&
+                toBundleMethod.doesReturn(Bundle::class.java) &&
+                readFromBundle.isPublic &&
+                readFromBundle.doesReturn(activityStackTokenClass) &&
+                createFromBinder.isPublic &&
+                createFromBinder.doesReturn(activityStackTokenClass) &&
+                invalidActivityStackTokenField.isPublic
+        }
+
+    private fun isClassWindowAttributesValid(): Boolean =
+        validateReflection("Class WindowAttributes is not valid") {
+            val windowAttributesClass = WindowAttributes::class.java
+            val getDimAreaBehaviorMethod = windowAttributesClass.getMethod("getDimAreaBehavior")
+
+            val splitAttributesClass = SplitAttributes::class.java
+            val getWindowAttributesMethod = splitAttributesClass.getMethod("getWindowAttributes")
+
+            val splitAttributesBuilderClass = SplitAttributes.Builder::class.java
+            val setWindowAttributesMethod =
+                splitAttributesBuilderClass.getMethod(
+                    "setWindowAttributes",
+                    WindowAttributes::class.java
+                )
+
+            getDimAreaBehaviorMethod.isPublic &&
+                getDimAreaBehaviorMethod.doesReturn(Int::class.java) &&
+                getWindowAttributesMethod.isPublic &&
+                getWindowAttributesMethod.doesReturn(windowAttributesClass) &&
+                setWindowAttributesMethod.isPublic &&
+                setWindowAttributesMethod.doesReturn(SplitAttributes.Builder::class.java)
+        }
+
+    private fun isClassSplitInfoTokenValid(): Boolean =
+        validateReflection("SplitInfo.Token is not valid") {
+            val splitInfoTokenClass = SplitInfo.Token::class.java
+            val createFromBinder =
+                splitInfoTokenClass.getMethod("createFromBinder", IBinder::class.java)
+
+            createFromBinder.isPublic && createFromBinder.doesReturn(splitInfoTokenClass)
+        }
+
+    /** Vendor API level 6 validation methods */
+    private fun isMethodGetEmbeddedActivityWindowInfoValid(): Boolean =
+        validateReflection(
+            "ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo is not valid"
+        ) {
+            val getEmbeddedActivityWindowInfoMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "getEmbeddedActivityWindowInfo",
+                    Activity::class.java
+                )
+            getEmbeddedActivityWindowInfoMethod.isPublic &&
+                getEmbeddedActivityWindowInfoMethod.doesReturn(
+                    EmbeddedActivityWindowInfo::class.java
+                )
+        }
+
+    private fun isMethodSetEmbeddedActivityWindowInfoCallbackValid(): Boolean =
+        validateReflection(
+            "ActivityEmbeddingComponent#setEmbeddedActivityWindowInfoCallback is not valid"
+        ) {
+            val setEmbeddedActivityWindowInfoCallbackMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "setEmbeddedActivityWindowInfoCallback",
+                    Executor::class.java,
+                    Consumer::class.java
+                )
+            setEmbeddedActivityWindowInfoCallbackMethod.isPublic
+        }
+
+    private fun isMethodClearEmbeddedActivityWindowInfoCallbackValid(): Boolean =
+        validateReflection(
+            "ActivityEmbeddingComponent#clearEmbeddedActivityWindowInfoCallback is not valid"
+        ) {
+            val clearEmbeddedActivityWindowInfoCallbackMethod =
+                activityEmbeddingComponentClass.getMethod("clearEmbeddedActivityWindowInfoCallback")
+            clearEmbeddedActivityWindowInfoCallbackMethod.isPublic
+        }
+
+    private fun isMethodGetDividerAttributesValid(): Boolean =
+        validateReflection("SplitAttributes#getDividerAttributes is not valid") {
+            val splitAttributesClass = SplitAttributes::class.java
+            val getDividerAttributesMethod = splitAttributesClass.getMethod("getDividerAttributes")
+            getDividerAttributesMethod.isPublic &&
+                getDividerAttributesMethod.doesReturn(DividerAttributes::class.java)
+        }
+
+    private fun isMethodSetDividerAttributesValid(): Boolean =
+        validateReflection("SplitAttributes#setDividerAttributes is not valid") {
+            val splitAttributesBuilderClass = SplitAttributes.Builder::class.java
+            val setDividerAttributesMethod =
+                splitAttributesBuilderClass.getMethod(
+                    "setDividerAttributes",
+                    DividerAttributes::class.java
+                )
+            setDividerAttributesMethod.isPublic &&
+                setDividerAttributesMethod.doesReturn(SplitAttributes.Builder::class.java)
+        }
+
+    private fun isClassEmbeddedActivityWindowInfoValid(): Boolean =
+        validateReflection("Class EmbeddedActivityWindowInfo is not valid") {
+            val embeddedActivityWindowInfoClass = EmbeddedActivityWindowInfo::class.java
+            val getActivityMethod = embeddedActivityWindowInfoClass.getMethod("getActivity")
+            val isEmbeddedMethod = embeddedActivityWindowInfoClass.getMethod("isEmbedded")
+            val getTaskBoundsMethod = embeddedActivityWindowInfoClass.getMethod("getTaskBounds")
+            val getActivityStackBoundsMethod =
+                embeddedActivityWindowInfoClass.getMethod("getActivityStackBounds")
+            getActivityMethod.isPublic &&
+                getActivityMethod.doesReturn(Activity::class.java) &&
+                isEmbeddedMethod.isPublic &&
+                isEmbeddedMethod.doesReturn(Boolean::class.java) &&
+                getTaskBoundsMethod.isPublic &&
+                getTaskBoundsMethod.doesReturn(Rect::class.java) &&
+                getActivityStackBoundsMethod.isPublic &&
+                getActivityStackBoundsMethod.doesReturn(Rect::class.java)
+        }
+
+    private fun isClassDividerAttributesValid(): Boolean =
+        validateReflection("Class DividerAttributes is not valid") {
+            val dividerAttributesClass = DividerAttributes::class.java
+            val getDividerTypeMethod = dividerAttributesClass.getMethod("getDividerType")
+            val getWidthDpMethod = dividerAttributesClass.getMethod("getWidthDp")
+            val getPrimaryMinRatioMethod = dividerAttributesClass.getMethod("getPrimaryMinRatio")
+            val getPrimaryMaxRatioMethod = dividerAttributesClass.getMethod("getPrimaryMaxRatio")
+            val getDividerColorMethod = dividerAttributesClass.getMethod("getDividerColor")
+            getDividerTypeMethod.isPublic &&
+                getDividerTypeMethod.doesReturn(Int::class.java) &&
+                getWidthDpMethod.isPublic &&
+                getWidthDpMethod.doesReturn(Int::class.java) &&
+                getPrimaryMinRatioMethod.isPublic &&
+                getPrimaryMinRatioMethod.doesReturn(Float::class.java) &&
+                getPrimaryMaxRatioMethod.isPublic &&
+                getPrimaryMaxRatioMethod.doesReturn(Float::class.java) &&
+                getDividerColorMethod.isPublic &&
+                getDividerColorMethod.doesReturn(Int::class.java)
+        }
+
+    private fun isClassDividerAttributesBuilderValid(): Boolean =
+        validateReflection("Class DividerAttributes.Builder is not valid") {
+            val dividerAttributesBuilderClass = DividerAttributes.Builder::class.java
+            val dividerAttributesTypeBuilderConstructor =
+                dividerAttributesBuilderClass.getDeclaredConstructor(Int::class.java)
+            val dividerAttributesBuilderConstructor =
+                dividerAttributesBuilderClass.getDeclaredConstructor(DividerAttributes::class.java)
+            val setWidthDpMethod =
+                dividerAttributesBuilderClass.getMethod("setWidthDp", Int::class.java)
+            val setPrimaryMinRatioMethod =
+                dividerAttributesBuilderClass.getMethod("setPrimaryMinRatio", Float::class.java)
+            val setPrimaryMaxRatioMethod =
+                dividerAttributesBuilderClass.getMethod("setPrimaryMaxRatio", Float::class.java)
+            val setDividerColorMethod =
+                dividerAttributesBuilderClass.getMethod("setDividerColor", Int::class.java)
+            dividerAttributesTypeBuilderConstructor.isPublic &&
+                dividerAttributesBuilderConstructor.isPublic &&
+                setWidthDpMethod.isPublic &&
+                setWidthDpMethod.doesReturn(DividerAttributes.Builder::class.java) &&
+                setPrimaryMinRatioMethod.isPublic &&
+                setPrimaryMinRatioMethod.doesReturn(DividerAttributes.Builder::class.java) &&
+                setPrimaryMaxRatioMethod.isPublic &&
+                setPrimaryMaxRatioMethod.doesReturn(DividerAttributes.Builder::class.java) &&
+                setDividerColorMethod.isPublic &&
+                setDividerColorMethod.doesReturn(DividerAttributes.Builder::class.java)
+        }
+
+    /** Overlay features validation methods */
+    private fun isActivityStackGetTagValid(): Boolean =
+        validateReflection("ActivityStack#getTag is not valid") {
+            val activityStackClass = ActivityStack::class.java
+            val getTokenMethod = activityStackClass.getMethod("getTag")
+
+            getTokenMethod.isPublic && getTokenMethod.doesReturn(String::class.java)
+        }
+
+    private fun isMethodGetActivityStackTokenValid(): Boolean =
+        validateReflection("getActivityStackToken is not valid") {
+            val getActivityStackTokenMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "getActivityStackToken",
+                    String::class.java
+                )
+            getActivityStackTokenMethod.isPublic &&
+                getActivityStackTokenMethod.doesReturn(ActivityStack.Token::class.java)
+        }
+
+    @Suppress("newApi") // Suppress lint check for WindowMetrics
+    private fun isClassParentContainerInfoValid(): Boolean =
+        validateReflection("ParentContainerInfo is not valid") {
+            val parentContainerInfoClass = ParentContainerInfo::class.java
+            val getWindowMetricsMethod = parentContainerInfoClass.getMethod("getWindowMetrics")
+            val getConfigurationMethod = parentContainerInfoClass.getMethod("getConfiguration")
+            val getWindowLayoutInfoMethod =
+                parentContainerInfoClass.getMethod("getWindowLayoutInfo")
+            getWindowMetricsMethod.isPublic &&
+                getWindowMetricsMethod.doesReturn(WindowMetrics::class.java) &&
+                getConfigurationMethod.isPublic &&
+                getConfigurationMethod.doesReturn(Configuration::class.java) &&
+                getWindowLayoutInfoMethod.isPublic &&
+                getWindowLayoutInfoMethod.doesReturn(WindowLayoutInfo::class.java)
+        }
+
+    private fun isMethodGetParentContainerInfoValid(): Boolean =
+        validateReflection("ActivityEmbeddingComponent#getParentContainerInfo is not valid") {
+            val getParentContainerInfoMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "getParentContainerInfo",
+                    ActivityStack.Token::class.java
+                )
+            getParentContainerInfoMethod.isPublic &&
+                getParentContainerInfoMethod.doesReturn(ParentContainerInfo::class.java)
+        }
+
+    private fun isMethodSetActivityStackAttributesCalculatorValid(): Boolean =
+        validateReflection("setActivityStackAttributesCalculator is not valid") {
+            val setActivityStackAttributesCalculatorMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "setActivityStackAttributesCalculator",
+                    Function::class.java
+                )
+            setActivityStackAttributesCalculatorMethod.isPublic
+        }
+
+    private fun isMethodClearActivityStackAttributesCalculatorValid(): Boolean =
+        validateReflection("clearActivityStackAttributesCalculator is not valid") {
+            val setActivityStackAttributesCalculatorMethod =
+                activityEmbeddingComponentClass.getMethod("clearActivityStackAttributesCalculator")
+            setActivityStackAttributesCalculatorMethod.isPublic
+        }
+
+    private fun isMethodUpdateActivityStackAttributesValid(): Boolean =
+        validateReflection("updateActivityStackAttributes is not valid") {
+            val updateActivityStackAttributesMethod =
+                activityEmbeddingComponentClass.getMethod(
+                    "updateActivityStackAttributes",
+                    ActivityStack.Token::class.java,
+                    ActivityStackAttributes::class.java
+                )
+            updateActivityStackAttributesMethod.isPublic
+        }
+
+    private fun isClassActivityStackAttributesValid(): Boolean =
+        validateReflection("Class ActivityStackAttributes is not valid") {
+            val activityStackAttributesClass = ActivityStackAttributes::class.java
+            val getRelativeBoundsMethod =
+                activityStackAttributesClass.getMethod("getRelativeBounds")
+            val getWindowAttributesMethod =
+                activityStackAttributesClass.getMethod("getWindowAttributes")
+            getRelativeBoundsMethod.isPublic &&
+                getRelativeBoundsMethod.doesReturn(Rect::class.java) &&
+                getWindowAttributesMethod.isPublic &&
+                getWindowAttributesMethod.doesReturn(WindowAttributes::class.java)
+        }
+
+    private fun isClassActivityStackAttributesBuilderValid(): Boolean =
+        validateReflection("Class ActivityStackAttributes.Builder is not valid") {
+            val activityStackAttributesBuilderClass = ActivityStackAttributes.Builder::class.java
+            val activityStackAttributesBuilderConstructor =
+                activityStackAttributesBuilderClass.getDeclaredConstructor()
+            val setRelativeBoundsMethod =
+                activityStackAttributesBuilderClass.getMethod("setRelativeBounds", Rect::class.java)
+            val setWindowAttributesMethod =
+                activityStackAttributesBuilderClass.getMethod(
+                    "setWindowAttributes",
+                    WindowAttributes::class.java
+                )
+            activityStackAttributesBuilderConstructor.isPublic &&
+                setRelativeBoundsMethod.isPublic &&
+                setRelativeBoundsMethod.doesReturn(ActivityStackAttributes.Builder::class.java) &&
+                setWindowAttributesMethod.isPublic &&
+                setWindowAttributesMethod.doesReturn(ActivityStackAttributes.Builder::class.java)
+        }
+
+    private fun isClassActivityStackAttributesCalculatorParamsValid(): Boolean =
+        validateReflection("Class ActivityStackAttributesCalculatorParams is not valid") {
+            val activityStackAttributesCalculatorParamsClass =
+                ActivityStackAttributesCalculatorParams::class.java
+            val getParentContainerInfoMethod =
+                activityStackAttributesCalculatorParamsClass.getMethod("getParentContainerInfo")
+            val getActivityStackTagMethod =
+                activityStackAttributesCalculatorParamsClass.getMethod("getActivityStackTag")
+            val getLaunchOptionsMethod =
+                activityStackAttributesCalculatorParamsClass.getMethod("getLaunchOptions")
+            getParentContainerInfoMethod.isPublic &&
+                getParentContainerInfoMethod.doesReturn(ParentContainerInfo::class.java) &&
+                getActivityStackTagMethod.isPublic &&
+                getActivityStackTagMethod.doesReturn(String::class.java) &&
+                getLaunchOptionsMethod.isPublic &&
+                getLaunchOptionsMethod.doesReturn(Bundle::class.java)
         }
 }
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitAttributes.kt b/window/window/src/main/java/androidx/window/embedding/SplitAttributes.kt
index 5b79c82..565f729 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitAttributes.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitAttributes.kt
@@ -19,8 +19,7 @@
 import android.annotation.SuppressLint
 import androidx.annotation.FloatRange
 import androidx.annotation.IntRange
-import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import androidx.window.RequiresWindowSdkExtension
 import androidx.window.WindowSdkExtensions
 import androidx.window.core.SpecificationComputer.Companion.startSpecification
 import androidx.window.core.VerificationMode
@@ -37,34 +36,42 @@
  *   positioned (left to right, right to left, top to bottom, and so forth)
  * - Animation background color &mdash; The color of the background during animation of the split
  *   involving this `SplitAttributes` object if the animation requires a background
+ * - Divider attributes &mdash; Specifies whether a divider is needed between the split containers
+ *   and the properties of the divider, including the color, the width, whether the divider is
+ *   draggable, etc.
  *
  * Attributes can be configured by:
  * - Setting the default `SplitAttributes` using [SplitPairRule.Builder.setDefaultSplitAttributes]
  *   or [SplitPlaceholderRule.Builder.setDefaultSplitAttributes].
  * - Setting `splitRatio`, `splitLayoutDirection`, and `animationBackgroundColor` attributes in
  *   `<SplitPairRule>` or `<SplitPlaceholderRule>` tags in an XML configuration file. The attributes
- *   are parsed as [SplitType], [LayoutDirection], and [BackgroundColor], respectively. Note that
- *   [SplitType.HingeSplitType] is not supported XML format.
- * - Set `SplitAttributes` calculation function by [SplitController.setSplitAttributesCalculator] to
- *   customize the `SplitAttributes` for a given device and window state.
+ *   are parsed as [SplitType], [LayoutDirection], and [EmbeddingAnimationBackground], respectively.
+ *   Note that [SplitType.HingeSplitType] is not supported XML format.
+ * - Using [SplitAttributesCalculator.computeSplitAttributesForParams] to customize the
+ *   `SplitAttributes` for a given device and window state.
  *
+ * @property splitType The split type attribute. Defaults to an equal split of the parent window for
+ *   the primary and secondary containers.
+ * @property layoutDirection The layout direction of the parent window split. The default is based
+ *   on locale value.
+ * @property animationBackground The animation background to use during the animation of the split
+ *   involving this `SplitAttributes` object if the animation requires a background. The default is
+ *   to use the current theme window background color.
+ * @property dividerAttributes The [DividerAttributes] for this split. Defaults to
+ *   [DividerAttributes.NO_DIVIDER], which means no divider is requested.
  * @see SplitAttributes.SplitType
  * @see SplitAttributes.LayoutDirection
+ * @see EmbeddingAnimationBackground
+ * @see EmbeddingAnimationBackground.createColorBackground
+ * @see EmbeddingAnimationBackground.DEFAULT
  */
 class SplitAttributes
-@RestrictTo(LIBRARY_GROUP)
+@JvmOverloads
 constructor(
-
-    /**
-     * The split type attribute. Defaults to an equal split of the parent window for the primary and
-     * secondary containers.
-     */
     val splitType: SplitType = SPLIT_TYPE_EQUAL,
-
-    /**
-     * The layout direction attribute for the parent window split. The default is based on locale.
-     */
     val layoutDirection: LayoutDirection = LOCALE,
+    val animationBackground: EmbeddingAnimationBackground = EmbeddingAnimationBackground.DEFAULT,
+    val dividerAttributes: DividerAttributes = DividerAttributes.NO_DIVIDER,
 ) {
 
     /**
@@ -333,6 +340,8 @@
     override fun hashCode(): Int {
         var result = splitType.hashCode()
         result = result * 31 + layoutDirection.hashCode()
+        result = result * 31 + animationBackground.hashCode()
+        result = result * 31 + dividerAttributes.hashCode()
         return result
     }
 
@@ -345,7 +354,10 @@
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is SplitAttributes) return false
-        return splitType == other.splitType && layoutDirection == other.layoutDirection
+        return splitType == other.splitType &&
+            layoutDirection == other.layoutDirection &&
+            animationBackground == other.animationBackground &&
+            dividerAttributes == other.dividerAttributes
     }
 
     /**
@@ -355,17 +367,22 @@
      */
     override fun toString(): String =
         "${SplitAttributes::class.java.simpleName}:" +
-            "{splitType=$splitType, layoutDir=$layoutDirection }"
+            "{splitType=$splitType, layoutDir=$layoutDirection, " +
+            "animationBackground=$animationBackground, " +
+            "dividerAttributes=$dividerAttributes }"
 
     /**
      * Builder for creating an instance of [SplitAttributes].
      * - The default split type is an equal split between primary and secondary containers.
      * - The default layout direction is based on locale.
      * - The default animation background color is to use the current theme window background color.
+     * - The default divider attributes is not to use divider.
      */
     class Builder {
         private var splitType = SPLIT_TYPE_EQUAL
         private var layoutDirection = LOCALE
+        private var animationBackground = EmbeddingAnimationBackground.DEFAULT
+        private var dividerAttributes: DividerAttributes = DividerAttributes.NO_DIVIDER
 
         /**
          * Sets the split type attribute.
@@ -392,11 +409,40 @@
         }
 
         /**
-         * Builds a `SplitAttributes` instance with the attributes specified by [setSplitType] and
-         * [setLayoutDirection].
+         * Sets the animation background to use during animation of the split involving this
+         * `SplitAttributes` object if the animation requires a background.
+         *
+         * The default is [EmbeddingAnimationBackground.DEFAULT], which means to use the current
+         * theme window background color.
+         *
+         * The [EmbeddingAnimationBackground] can be supported only if the vendor API level of the
+         * target device is equals or higher than required API level. Otherwise, it would be no-op
+         * when setting the [EmbeddingAnimationBackground] on a target device that has lower API
+         * level.
+         *
+         * @param background The animation background.
+         * @return This `Builder`.
+         * @see EmbeddingAnimationBackground.createColorBackground
+         * @see EmbeddingAnimationBackground.DEFAULT
+         */
+        @RequiresWindowSdkExtension(5)
+        fun setAnimationBackground(background: EmbeddingAnimationBackground): Builder = apply {
+            animationBackground = background
+        }
+
+        /** Sets the [DividerAttributes]. */
+        @RequiresWindowSdkExtension(6)
+        fun setDividerAttributes(dividerAttributes: DividerAttributes): Builder = apply {
+            this.dividerAttributes = dividerAttributes
+        }
+
+        /**
+         * Builds a `SplitAttributes` instance with the attributes specified by [setSplitType],
+         * [setLayoutDirection], and [setAnimationBackground].
          *
          * @return The new `SplitAttributes` instance.
          */
-        fun build(): SplitAttributes = SplitAttributes(splitType, layoutDirection)
+        fun build(): SplitAttributes =
+            SplitAttributes(splitType, layoutDirection, animationBackground, dividerAttributes)
     }
 }
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitController.kt b/window/window/src/main/java/androidx/window/embedding/SplitController.kt
index fc28d14e..91a498b 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitController.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitController.kt
@@ -22,7 +22,6 @@
 import androidx.window.RequiresWindowSdkExtension
 import androidx.window.WindowProperties
 import androidx.window.WindowSdkExtensions
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.WindowMetrics
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
@@ -80,6 +79,56 @@
         get() = embeddingBackend.splitSupportStatus
 
     /**
+     * Pins the top-most [ActivityStack] to keep the stack of the Activities to be always positioned
+     * on top. The rest of the activities in the Task will be split with the pinned [ActivityStack].
+     * The pinned [ActivityStack] would also have isolated activity navigation in which only the
+     * activities that are started from the pinned [ActivityStack] can be added on top of the
+     * [ActivityStack].
+     *
+     * The pinned [ActivityStack] is unpinned whenever the pinned [ActivityStack] is expanded. Use
+     * [SplitPinRule.Builder.setSticky] if the same [ActivityStack] should be pinned again whenever
+     * the [ActivityStack] is on top and split with another [ActivityStack] again.
+     *
+     * The caller **must** make sure if [WindowSdkExtensions.extensionVersion] is greater than or
+     * equal to 5.
+     *
+     * @param taskId The id of the Task that top [ActivityStack] should be pinned.
+     * @param splitPinRule The SplitRule that specifies how the top [ActivityStack] should be split
+     *   with others.
+     * @return Returns `true` if the top [ActivityStack] is successfully pinned. Otherwise, `false`.
+     *   Few examples are:
+     *     1. There's no [ActivityStack].
+     *     2. There is already an existing pinned [ActivityStack].
+     *     3. There's no other [ActivityStack] to split with the top [ActivityStack].
+     *
+     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
+     *   than 5.
+     */
+    @RequiresWindowSdkExtension(5)
+    fun pinTopActivityStack(taskId: Int, splitPinRule: SplitPinRule): Boolean {
+        return embeddingBackend.pinTopActivityStack(taskId, splitPinRule)
+    }
+
+    /**
+     * Unpins the pinned [ActivityStack]. The [ActivityStack] will still be the top-most
+     * [ActivityStack] right after unpinned, and the [ActivityStack] could be expanded or continue
+     * to be split with the next top [ActivityStack] if the current state matches any of the
+     * existing [SplitPairRule]. It is a no-op call if the task does not have a pinned
+     * [ActivityStack].
+     *
+     * The caller **must** make sure if [WindowSdkExtensions.extensionVersion] is greater than or
+     * equal to 5.
+     *
+     * @param taskId The id of the Task that top [ActivityStack] should be unpinned.
+     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
+     *   than 5.
+     */
+    @RequiresWindowSdkExtension(5)
+    fun unpinTopActivityStack(taskId: Int) {
+        embeddingBackend.unpinTopActivityStack(taskId)
+    }
+
+    /**
      * Sets or replaces the previously registered [SplitAttributes] calculator.
      *
      * **Note** that it's callers' responsibility to check if this API is supported by checking
@@ -141,27 +190,6 @@
     }
 
     /**
-     * Triggers a [SplitAttributes] update callback for the current topmost and visible split layout
-     * if there is one. This method can be used when a change to the split presentation originates
-     * from an application state change. Changes that are driven by parent window changes or new
-     * activity starts invoke the callback provided in [setSplitAttributesCalculator] automatically
-     * without the need to call this function.
-     *
-     * The top [SplitInfo] is usually the last element of [SplitInfo] list which was received from
-     * the callback registered in [splitInfoList].
-     *
-     * The call will be ignored if there is no visible split.
-     *
-     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
-     *   than 3.
-     */
-    @ExperimentalWindowApi
-    @RequiresWindowSdkExtension(3)
-    fun invalidateTopVisibleSplitAttributes() {
-        embeddingBackend.invalidateTopVisibleSplitAttributes()
-    }
-
-    /**
      * Updates the [SplitAttributes] of a split pair. This is an alternative to using a split
      * attributes calculator callback set in [setSplitAttributesCalculator], useful when apps only
      * need to update the splits in a few cases proactively but rely on the default split attributes
@@ -176,15 +204,15 @@
      * - A new Activity being launched.
      * - A window or device state updates (e,g. due to screen rotation or folding state update).
      *
-     * In most cases it is suggested to use [invalidateTopVisibleSplitAttributes] if
-     * [SplitAttributes] calculator callback is used.
+     * In most cases it is suggested to use
+     * [ActivityEmbeddingController.invalidateTopVisibleActivityStacks] if a calculator has been set
+     * through [setSplitAttributesCalculator].
      *
      * @param splitInfo the split pair to update
      * @param splitAttributes the [SplitAttributes] to be applied
      * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
      *   than 3.
      */
-    @ExperimentalWindowApi
     @RequiresWindowSdkExtension(3)
     fun updateSplitAttributes(splitInfo: SplitInfo, splitAttributes: SplitAttributes) {
         embeddingBackend.updateSplitAttributes(splitInfo, splitAttributes)
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt b/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt
index 0a8b4dc..40fa692 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitInfo.kt
@@ -19,21 +19,89 @@
 import android.app.Activity
 import android.os.IBinder
 import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.WindowSdkExtensions
+import androidx.window.extensions.embedding.SplitInfo.Token
 
 /** Describes a split pair of two containers with activities. */
+@Suppress("Deprecation") // To compat with device with version 3 and 4.
 class SplitInfo
-@RestrictTo(LIBRARY_GROUP)
-constructor(
+private constructor(
     /** The [ActivityStack] representing the primary split container. */
     val primaryActivityStack: ActivityStack,
     /** The [ActivityStack] representing the secondary split container. */
     val secondaryActivityStack: ActivityStack,
     /** The [SplitAttributes] of this split pair. */
     val splitAttributes: SplitAttributes,
+    @Deprecated(
+        message = "Use [token] instead",
+        replaceWith =
+            ReplaceWith(
+                expression = "SplitInfo.token",
+                imports = arrayOf("androidx.window.embedding.SplitInfo"),
+            )
+    )
+    private val binder: IBinder?,
     /** A token uniquely identifying this `SplitInfo`. */
-    internal val token: IBinder,
+    private val token: Token?,
 ) {
+    @RequiresWindowSdkExtension(5)
+    internal constructor(
+        primaryActivityStack: ActivityStack,
+        secondaryActivityStack: ActivityStack,
+        splitAttributes: SplitAttributes,
+        token: Token,
+    ) : this(primaryActivityStack, secondaryActivityStack, splitAttributes, binder = null, token)
+
+    /** Creates SplitInfo for [WindowSdkExtensions.extensionVersion] 3 and 4. */
+    @RequiresWindowSdkExtension(3)
+    internal constructor(
+        primaryActivityStack: ActivityStack,
+        secondaryActivityStack: ActivityStack,
+        splitAttributes: SplitAttributes,
+        binder: IBinder,
+    ) : this(
+        primaryActivityStack,
+        secondaryActivityStack,
+        splitAttributes,
+        binder,
+        token = null,
+    ) {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(3..4)
+    }
+
+    /**
+     * Creates SplitInfo ONLY for testing.
+     *
+     * @param primaryActivityStack the [ActivityStack] representing the primary split container.
+     * @param secondaryActivityStack the [ActivityStack] representing the secondary split container.
+     * @param splitAttributes the [SplitAttributes] of this split pair.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    constructor(
+        primaryActivityStack: ActivityStack,
+        secondaryActivityStack: ActivityStack,
+        splitAttributes: SplitAttributes,
+    ) : this(
+        primaryActivityStack,
+        secondaryActivityStack,
+        splitAttributes,
+        binder = null,
+        token = null,
+    )
+
+    @RequiresWindowSdkExtension(3)
+    internal fun getBinder(): IBinder = let {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(3..4)
+        requireNotNull(binder)
+    }
+
+    @RequiresWindowSdkExtension(5)
+    internal fun getToken(): Token = let {
+        WindowSdkExtensions.getInstance().requireExtensionVersion(5)
+        requireNotNull(token)
+    }
+
     /**
      * Whether the [primaryActivityStack] or the [secondaryActivityStack] in this [SplitInfo]
      * contains the [activity].
@@ -50,6 +118,7 @@
         if (secondaryActivityStack != other.secondaryActivityStack) return false
         if (splitAttributes != other.splitAttributes) return false
         if (token != other.token) return false
+        if (binder != other.binder) return false
 
         return true
     }
@@ -59,6 +128,7 @@
         result = 31 * result + secondaryActivityStack.hashCode()
         result = 31 * result + splitAttributes.hashCode()
         result = 31 * result + token.hashCode()
+        result = 31 * result + binder.hashCode()
         return result
     }
 
@@ -68,7 +138,12 @@
             append("primaryActivityStack=$primaryActivityStack, ")
             append("secondaryActivityStack=$secondaryActivityStack, ")
             append("splitAttributes=$splitAttributes, ")
-            append("token=$token")
+            if (token != null) {
+                append("token=$token")
+            }
+            if (binder != null) {
+                append("binder=$binder")
+            }
             append("}")
         }
     }
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitPinRule.kt b/window/window/src/main/java/androidx/window/embedding/SplitPinRule.kt
new file mode 100644
index 0000000..73c45cf
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/embedding/SplitPinRule.kt
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import androidx.annotation.IntRange
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW
+import androidx.window.embedding.SplitRule.Companion.SPLIT_MIN_DIMENSION_DP_DEFAULT
+
+/**
+ * Split configuration rules for pinning an [ActivityStack]. Define how the pinned [ActivityStack]
+ * should be displayed side-by-side with the other [ActivityStack].
+ */
+class SplitPinRule
+internal constructor(
+    /** A unique string to identify this [SplitPinRule]. */
+    tag: String? = null,
+    /** The default [SplitAttributes] to apply on the pin container and the paired container. */
+    defaultSplitAttributes: SplitAttributes,
+    /**
+     * Whether this rule should be sticky. If the value is `false`, this rule be removed whenever
+     * the pinned [ActivityStack] is unpinned. Set to `true` if the rule should be applied whenever
+     * once again possible (e.g. the host Task bounds satisfies the size and aspect ratio
+     * requirements).
+     */
+    val isSticky: Boolean,
+    @IntRange(from = 0) minWidthDp: Int = SPLIT_MIN_DIMENSION_DP_DEFAULT,
+    @IntRange(from = 0) minHeightDp: Int = SPLIT_MIN_DIMENSION_DP_DEFAULT,
+    @IntRange(from = 0) minSmallestWidthDp: Int = SPLIT_MIN_DIMENSION_DP_DEFAULT,
+    maxAspectRatioInPortrait: EmbeddingAspectRatio = SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT,
+    maxAspectRatioInLandscape: EmbeddingAspectRatio = SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+) :
+    SplitRule(
+        tag,
+        minWidthDp,
+        minHeightDp,
+        minSmallestWidthDp,
+        maxAspectRatioInPortrait,
+        maxAspectRatioInLandscape,
+        defaultSplitAttributes
+    ) {
+
+    /** Builder for [SplitPinRule]. */
+    class Builder {
+        private var tag: String? = null
+        @IntRange(from = 0) private var minWidthDp = SPLIT_MIN_DIMENSION_DP_DEFAULT
+        @IntRange(from = 0) private var minHeightDp = SPLIT_MIN_DIMENSION_DP_DEFAULT
+        @IntRange(from = 0) private var minSmallestWidthDp = SPLIT_MIN_DIMENSION_DP_DEFAULT
+        private var maxAspectRatioInPortrait = SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
+        private var maxAspectRatioInLandscape = SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
+        private var defaultSplitAttributes = SplitAttributes.Builder().build()
+        private var isSticky: Boolean = false
+
+        /**
+         * Sets the smallest value of width of the parent window when the split should be used, in
+         * DP. When the window size is smaller than requested here, activities in the secondary
+         * container will be stacked on top of the activities in the primary one, completely
+         * overlapping them.
+         *
+         * The default is [SPLIT_MIN_DIMENSION_DP_DEFAULT] if the app doesn't set.
+         * [SPLIT_MIN_DIMENSION_ALWAYS_ALLOW] means to always allow split.
+         *
+         * @param minWidthDp the smallest value of width of the parent window when the split should
+         *   be used, in DP.
+         */
+        fun setMinWidthDp(@IntRange(from = 0) minWidthDp: Int): Builder = apply {
+            this.minWidthDp = minWidthDp
+        }
+
+        /**
+         * Sets the smallest value of height of the parent task window when the split should be
+         * used, in DP. When the window size is smaller than requested here, activities in the
+         * secondary container will be stacked on top of the activities in the primary one,
+         * completely overlapping them.
+         *
+         * It is useful if it's necessary to split the parent window horizontally for this
+         * [SplitPinRule].
+         *
+         * The default is [SPLIT_MIN_DIMENSION_DP_DEFAULT] if the app doesn't set.
+         * [SPLIT_MIN_DIMENSION_ALWAYS_ALLOW] means to always allow split.
+         *
+         * @param minHeightDp the smallest value of height of the parent task window when the split
+         *   should be used, in DP.
+         * @see SplitAttributes.LayoutDirection.TOP_TO_BOTTOM
+         * @see SplitAttributes.LayoutDirection.BOTTOM_TO_TOP
+         */
+        fun setMinHeightDp(@IntRange(from = 0) minHeightDp: Int): Builder = apply {
+            this.minHeightDp = minHeightDp
+        }
+
+        /**
+         * Sets the smallest value of the smallest possible width of the parent window in any
+         * rotation when the split should be used, in DP. When the window size is smaller than
+         * requested here, activities in the secondary container will be stacked on top of the
+         * activities in the primary one, completely overlapping them.
+         *
+         * The default is [SPLIT_MIN_DIMENSION_DP_DEFAULT] if the app doesn't set.
+         * [SPLIT_MIN_DIMENSION_ALWAYS_ALLOW] means to always allow split.
+         *
+         * @param minSmallestWidthDp the smallest value of the smallest possible width of the parent
+         *   window in any rotation when the split should be used, in DP.
+         */
+        fun setMinSmallestWidthDp(@IntRange(from = 0) minSmallestWidthDp: Int): Builder = apply {
+            this.minSmallestWidthDp = minSmallestWidthDp
+        }
+
+        /**
+         * Sets the largest value of the aspect ratio, expressed as `height / width` in decimal
+         * form, of the parent window bounds in portrait when the split should be used. When the
+         * window aspect ratio is greater than requested here, activities in the secondary container
+         * will be stacked on top of the activities in the primary one, completely overlapping them.
+         *
+         * This value is only used when the parent window is in portrait (height >= width).
+         *
+         * The default is [SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT] if the app doesn't set, which is
+         * the recommend value to only allow split when the parent window is not too stretched in
+         * portrait.
+         *
+         * @param aspectRatio the largest value of the aspect ratio, expressed as `height / width`
+         *   in decimal form, of the parent window bounds in portrait when the split should be used.
+         * @see EmbeddingAspectRatio.ratio
+         * @see EmbeddingAspectRatio.ALWAYS_ALLOW
+         * @see EmbeddingAspectRatio.ALWAYS_DISALLOW
+         */
+        fun setMaxAspectRatioInPortrait(aspectRatio: EmbeddingAspectRatio): Builder = apply {
+            this.maxAspectRatioInPortrait = aspectRatio
+        }
+
+        /**
+         * Sets the largest value of the aspect ratio, expressed as `width / height` in decimal
+         * form, of the parent window bounds in landscape when the split should be used. When the
+         * window aspect ratio is greater than requested here, activities in the secondary container
+         * will be stacked on top of the activities in the primary one, completely overlapping them.
+         *
+         * This value is only used when the parent window is in landscape (width > height).
+         *
+         * The default is [SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT] if the app doesn't set, which
+         * is the recommend value to always allow split when the parent window is in landscape.
+         *
+         * @param aspectRatio the largest value of the aspect ratio, expressed as `width / height`
+         *   in decimal form, of the parent window bounds in landscape when the split should be
+         *   used.
+         * @see EmbeddingAspectRatio.ratio
+         * @see EmbeddingAspectRatio.ALWAYS_ALLOW
+         * @see EmbeddingAspectRatio.ALWAYS_DISALLOW
+         */
+        fun setMaxAspectRatioInLandscape(aspectRatio: EmbeddingAspectRatio): Builder = apply {
+            this.maxAspectRatioInLandscape = aspectRatio
+        }
+
+        /**
+         * Sets the default [SplitAttributes] to apply on the activity containers pair when the host
+         * task bounds satisfy [minWidthDp], [minHeightDp], [minSmallestWidthDp],
+         * [maxAspectRatioInPortrait] and [maxAspectRatioInLandscape] requirements.
+         *
+         * @param defaultSplitAttributes the default [SplitAttributes] to apply on the activity
+         *   containers pair when the host task bounds satisfy all the rule requirements.
+         */
+        fun setDefaultSplitAttributes(defaultSplitAttributes: SplitAttributes): Builder = apply {
+            this.defaultSplitAttributes = defaultSplitAttributes
+        }
+
+        /**
+         * Sets a unique string to identify this [SplitPinRule], which defaults to `null`. The
+         * suggested usage is to set the tag to be able to differentiate between different rules in
+         * the [SplitAttributesCalculatorParams.splitRuleTag].
+         *
+         * @param tag unique string to identify this [SplitPinRule].
+         */
+        fun setTag(tag: String?): Builder = apply { this.tag = tag }
+
+        /**
+         * Sets this rule to be sticky.
+         *
+         * @param isSticky whether to be a sticky rule.
+         * @see isSticky
+         */
+        fun setSticky(isSticky: Boolean): Builder = apply { this.isSticky = isSticky }
+
+        /**
+         * Builds a [SplitPinRule] instance.
+         *
+         * @return The new [SplitPinRule] instance.
+         */
+        fun build() =
+            SplitPinRule(
+                tag,
+                defaultSplitAttributes,
+                isSticky,
+                minWidthDp,
+                minHeightDp,
+                minSmallestWidthDp,
+                maxAspectRatioInPortrait,
+                maxAspectRatioInLandscape
+            )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is SplitPinRule) return false
+
+        if (!super.equals(other)) return false
+        if (isSticky != other.isSticky) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = super.hashCode()
+        result = 31 * result + isSticky.hashCode()
+        return result
+    }
+
+    override fun toString(): String =
+        "${SplitPinRule::class.java.simpleName}{" +
+            "tag=$tag" +
+            ", defaultSplitAttributes=$defaultSplitAttributes" +
+            ", isSticky=$isSticky" +
+            "}"
+}
diff --git a/window/window/src/main/java/androidx/window/embedding/SplitRule.kt b/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
index adc8732..393862c 100644
--- a/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
+++ b/window/window/src/main/java/androidx/window/embedding/SplitRule.kt
@@ -129,6 +129,8 @@
      * The default [SplitAttributes] to apply on the activity containers pair when the host task
      * bounds satisfy [minWidthDp], [minHeightDp], [minSmallestWidthDp], [maxAspectRatioInPortrait]
      * and [maxAspectRatioInLandscape] requirements.
+     *
+     * It is set to split the host parent task vertically and equally by default.
      */
     val defaultSplitAttributes: SplitAttributes,
 ) : EmbeddingRule(tag) {
diff --git a/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt b/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
index 5ad6e24..75f172d 100644
--- a/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
+++ b/window/window/src/main/java/androidx/window/layout/SafeWindowLayoutComponentProvider.kt
@@ -30,10 +30,13 @@
 import androidx.window.reflection.ReflectionUtils.doesReturn
 import androidx.window.reflection.ReflectionUtils.isPublic
 import androidx.window.reflection.ReflectionUtils.validateReflection
+import androidx.window.reflection.WindowExtensionsConstants.DISPLAY_FOLD_FEATURE_CLASS
 import androidx.window.reflection.WindowExtensionsConstants.FOLDING_FEATURE_CLASS
 import androidx.window.reflection.WindowExtensionsConstants.JAVA_CONSUMER
+import androidx.window.reflection.WindowExtensionsConstants.SUPPORTED_WINDOW_FEATURES_CLASS
 import androidx.window.reflection.WindowExtensionsConstants.WINDOW_CONSUMER
 import androidx.window.reflection.WindowExtensionsConstants.WINDOW_LAYOUT_COMPONENT_CLASS
+import java.lang.reflect.ParameterizedType
 
 /**
  * Reflection Guard for [WindowLayoutComponent]. This will go through the [WindowLayoutComponent]'s
@@ -63,13 +66,12 @@
         if (!isWindowLayoutComponentAccessible()) {
             return false
         }
-        // TODO(b/267831038): can fallback to VendorApiLevel1 when level2 is not match
-        //  but level 1 is matched
-        return when (ExtensionsUtil.safeVendorApiLevel) {
-            1 -> hasValidVendorApiLevel1()
-            in 2..Int.MAX_VALUE -> hasValidVendorApiLevel2()
-            // TODO(b/267956499): add hasValidVendorApiLevel3
-            else -> false
+        val vendorApiLevel = ExtensionsUtil.safeVendorApiLevel
+        return when {
+            vendorApiLevel < 1 -> false
+            vendorApiLevel == 1 -> hasValidVendorApiLevel1()
+            vendorApiLevel < 5 -> hasValidVendorApiLevel2()
+            else -> hasValidVendorApiLevel6()
         }
     }
 
@@ -100,6 +102,14 @@
         return hasValidVendorApiLevel1() && isMethodWindowLayoutInfoListenerWindowConsumerValid()
     }
 
+    @VisibleForTesting
+    internal fun hasValidVendorApiLevel6(): Boolean {
+        return hasValidVendorApiLevel2() &&
+            isDisplayFoldFeatureValid() &&
+            isSupportedWindowFeaturesValid() &&
+            isGetSupportedWindowFeaturesValid()
+    }
+
     private fun isWindowLayoutProviderValid(): Boolean {
         return validateReflection("WindowExtensions#getWindowLayoutComponent is not valid") {
             val extensionsClass = safeWindowExtensionsProvider.windowExtensionsClass
@@ -167,6 +177,63 @@
         }
     }
 
+    private fun isDisplayFoldFeatureValid(): Boolean {
+        return validateReflection("DisplayFoldFeature is not valid") {
+            val displayFoldFeatureClass = displayFoldFeatureClass
+
+            val getTypeMethod = displayFoldFeatureClass.getMethod("getType")
+            val hasPropertyMethod =
+                displayFoldFeatureClass.getMethod("hasProperty", Int::class.java)
+            val hasPropertiesMethod =
+                displayFoldFeatureClass.getMethod("hasProperties", IntArray::class.java)
+
+            getTypeMethod.isPublic &&
+                getTypeMethod.doesReturn(Int::class.java) &&
+                hasPropertyMethod.isPublic &&
+                hasPropertyMethod.doesReturn(Boolean::class.java) &&
+                hasPropertiesMethod.isPublic &&
+                hasPropertiesMethod.doesReturn(Boolean::class.java)
+        }
+    }
+
+    private fun isSupportedWindowFeaturesValid(): Boolean {
+        return validateReflection("SupportedWindowFeatures is not valid") {
+            val supportedWindowFeaturesClass = supportedWindowFeaturesClass
+
+            val getDisplayFoldFeaturesMethod =
+                supportedWindowFeaturesClass.getMethod("getDisplayFoldFeatures")
+            val returnTypeGeneric =
+                (getDisplayFoldFeaturesMethod.genericReturnType as ParameterizedType)
+                    .actualTypeArguments[0]
+                    as Class<*>
+
+            getDisplayFoldFeaturesMethod.isPublic &&
+                getDisplayFoldFeaturesMethod.doesReturn(List::class.java) &&
+                returnTypeGeneric == displayFoldFeatureClass
+        }
+    }
+
+    private fun isGetSupportedWindowFeaturesValid(): Boolean {
+        return validateReflection("WindowLayoutComponent#getSupportedWindowFeatures is not valid") {
+            val windowLayoutComponent = windowLayoutComponentClass
+            val getSupportedWindowFeaturesMethod =
+                windowLayoutComponent.getMethod("getSupportedWindowFeatures")
+
+            getSupportedWindowFeaturesMethod.isPublic &&
+                getSupportedWindowFeaturesMethod.doesReturn(supportedWindowFeaturesClass)
+        }
+    }
+
+    private val displayFoldFeatureClass: Class<*>
+        get() {
+            return loader.loadClass(DISPLAY_FOLD_FEATURE_CLASS)
+        }
+
+    private val supportedWindowFeaturesClass: Class<*>
+        get() {
+            return loader.loadClass(SUPPORTED_WINDOW_FEATURES_CLASS)
+        }
+
     private val foldingFeatureClass: Class<*>
         get() {
             return loader.loadClass(FOLDING_FEATURE_CLASS)
diff --git a/window/window/src/main/java/androidx/window/layout/SupportedPosture.kt b/window/window/src/main/java/androidx/window/layout/SupportedPosture.kt
new file mode 100644
index 0000000..0e29629
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/layout/SupportedPosture.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout
+
+/** A class to represent a posture that the device supports. */
+class SupportedPosture internal constructor(private val rawValue: Int) {
+
+    override fun toString(): String {
+        return when (this) {
+            TABLETOP -> "TABLETOP"
+            else -> "UNKNOWN"
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null) return false
+        if (other::class != SupportedPosture::class) return false
+
+        other as SupportedPosture
+
+        return rawValue == other.rawValue
+    }
+
+    override fun hashCode(): Int {
+        return rawValue
+    }
+
+    companion object {
+        /** The posture where there is a single fold in the half-opened state. */
+        @JvmField val TABLETOP: SupportedPosture = SupportedPosture(0)
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
index ffb3a1f..d4b7534 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
@@ -23,6 +23,7 @@
 import androidx.annotation.RestrictTo
 import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
 import androidx.annotation.UiContext
+import androidx.window.RequiresWindowSdkExtension
 import androidx.window.WindowSdkExtensions
 import androidx.window.core.ConsumerAdapter
 import androidx.window.layout.adapter.WindowBackend
@@ -96,6 +97,23 @@
      */
     fun windowLayoutInfo(activity: Activity): Flow<WindowLayoutInfo>
 
+    /**
+     * Returns the [List] of [SupportedPosture] values. This value will not change during runtime.
+     * These values are for determining if the device supports the given [SupportedPosture] but does
+     * not mean the device is in the given [SupportedPosture]. Use [windowLayoutInfo] to determine
+     * the current state of the [DisplayFeature]'s on the device.
+     *
+     * @throws UnsupportedOperationException if [WindowSdkExtensions.extensionVersion] is less
+     *   than 6.
+     * @throws NotImplementedError if a derived test class does not override this method.
+     * @see windowLayoutInfo
+     */
+    @RequiresWindowSdkExtension(version = 6)
+    val supportedPostures: List<SupportedPosture>
+        get() {
+            throw NotImplementedError("Method was not implemented.")
+        }
+
     companion object {
 
         private val DEBUG = false
@@ -134,7 +152,12 @@
         @JvmStatic
         fun getOrCreate(context: Context): WindowInfoTracker {
             val backend = extensionBackend ?: SidecarWindowBackend.getInstance(context)
-            val repo = WindowInfoTrackerImpl(WindowMetricsCalculatorCompat, backend)
+            val repo =
+                WindowInfoTrackerImpl(
+                    WindowMetricsCalculatorCompat(),
+                    backend,
+                    WindowSdkExtensions.getInstance()
+                )
             return decorator.decorate(repo)
         }
 
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
index e6702d0..308042f 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import androidx.annotation.UiContext
 import androidx.core.util.Consumer
+import androidx.window.WindowSdkExtensions
 import androidx.window.layout.adapter.WindowBackend
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.channels.awaitClose
@@ -36,7 +37,8 @@
  */
 internal class WindowInfoTrackerImpl(
     private val windowMetricsCalculator: WindowMetricsCalculator,
-    private val windowBackend: WindowBackend
+    private val windowBackend: WindowBackend,
+    private val windowSdkExtensions: WindowSdkExtensions
 ) : WindowInfoTracker {
 
     /**
@@ -61,4 +63,10 @@
             }
             .flowOn(Dispatchers.Main)
     }
+
+    override val supportedPostures: List<SupportedPosture>
+        get() {
+            windowSdkExtensions.requireExtensionVersion(6)
+            return windowBackend.supportedPostures
+        }
 }
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt b/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt
index 7aaa8e1..6707353 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt
@@ -17,6 +17,7 @@
 
 import android.graphics.Rect
 import android.os.Build.VERSION_CODES
+import android.util.DisplayMetrics
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.core.view.WindowInsetsCompat
@@ -34,15 +35,23 @@
 class WindowMetrics
 internal constructor(
     private val _bounds: Bounds,
-    private val _windowInsetsCompat: WindowInsetsCompat
+    private val _windowInsetsCompat: WindowInsetsCompat,
+
+    /**
+     * Returns the logical density of the display this window is in.
+     *
+     * @see [DisplayMetrics.density]
+     */
+    val density: Float
 ) {
 
     /** An internal constructor for [WindowMetrics] */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     constructor(
         bounds: Rect,
-        insets: WindowInsetsCompat = WindowInsetsCompat.Builder().build()
-    ) : this(Bounds(bounds), insets)
+        insets: WindowInsetsCompat = WindowInsetsCompat.Builder().build(),
+        density: Float
+    ) : this(Bounds(bounds), insets, density)
 
     /**
      * Returns a new [Rect] describing the bounds of the area the window occupies.
@@ -68,6 +77,7 @@
 
         if (_bounds != other._bounds) return false
         if (_windowInsetsCompat != other._windowInsetsCompat) return false
+        if (density != other.density) return false
 
         return true
     }
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
index 9630be0..04060a1 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
@@ -28,6 +28,7 @@
 import androidx.annotation.UiContext
 import androidx.core.view.WindowInsetsCompat
 import androidx.window.core.Bounds
+import androidx.window.layout.util.WindowMetricsCompatHelper
 
 /** An interface to calculate the [WindowMetrics] for an [Activity] or a [UiContext]. */
 interface WindowMetricsCalculator {
@@ -123,10 +124,11 @@
     companion object {
 
         private var decorator: (WindowMetricsCalculator) -> WindowMetricsCalculator = { it }
+        private val windowMetricsCalculatorCompat = WindowMetricsCalculatorCompat()
 
         @JvmStatic
         fun getOrCreate(): WindowMetricsCalculator {
-            return decorator(WindowMetricsCalculatorCompat)
+            return decorator(windowMetricsCalculatorCompat)
         }
 
         @JvmStatic
@@ -145,18 +147,20 @@
          * Converts [Android API WindowMetrics][AndroidWindowMetrics] to
          * [Jetpack version WindowMetrics][WindowMetrics]
          */
-        @Suppress("ClassVerificationFailure")
         @RequiresApi(Build.VERSION_CODES.R)
-        internal fun translateWindowMetrics(windowMetrics: AndroidWindowMetrics): WindowMetrics =
-            WindowMetrics(
-                windowMetrics.bounds,
-                WindowInsetsCompat.toWindowInsetsCompat(windowMetrics.windowInsets)
-            )
+        internal fun translateWindowMetrics(
+            windowMetrics: AndroidWindowMetrics,
+            density: Float
+        ): WindowMetrics {
+            return WindowMetricsCompatHelper.getInstance()
+                .translateWindowMetrics(windowMetrics, density)
+        }
 
         internal fun fromDisplayMetrics(displayMetrics: DisplayMetrics): WindowMetrics {
             return WindowMetrics(
                 Bounds(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels),
-                WindowInsetsCompat.Builder().build()
+                WindowInsetsCompat.Builder().build(),
+                displayMetrics.density
             )
         }
     }
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt
index fd77478..ffaa59a 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt
@@ -15,40 +15,18 @@
  */
 package androidx.window.layout
 
-import android.annotation.SuppressLint
 import android.app.Activity
 import android.content.Context
-import android.content.res.Configuration
-import android.graphics.Point
-import android.graphics.Rect
 import android.inputmethodservice.InputMethodService
-import android.os.Build
-import android.os.Build.VERSION_CODES
-import android.util.Log
-import android.view.Display
-import android.view.DisplayCutout
-import android.view.WindowManager
-import androidx.annotation.RequiresApi
 import androidx.annotation.UiContext
-import androidx.annotation.VisibleForTesting
 import androidx.core.view.WindowInsetsCompat
-import androidx.window.core.Bounds
-import androidx.window.layout.util.ActivityCompatHelperApi24.isInMultiWindowMode
-import androidx.window.layout.util.ContextCompatHelper.unwrapUiContext
-import androidx.window.layout.util.ContextCompatHelperApi30.currentWindowBounds
-import androidx.window.layout.util.ContextCompatHelperApi30.currentWindowInsets
-import androidx.window.layout.util.ContextCompatHelperApi30.currentWindowMetrics
-import androidx.window.layout.util.ContextCompatHelperApi30.maximumWindowBounds
-import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetBottom
-import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetLeft
-import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetRight
-import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetTop
-import java.lang.reflect.InvocationTargetException
+import androidx.window.layout.util.DensityCompatHelper
+import androidx.window.layout.util.WindowMetricsCompatHelper
 
 /** Helper class used to compute window metrics across Android versions. */
-internal object WindowMetricsCalculatorCompat : WindowMetricsCalculator {
-
-    private val TAG: String = WindowMetricsCalculatorCompat::class.java.simpleName
+internal class WindowMetricsCalculatorCompat(
+    private val densityCompatHelper: DensityCompatHelper = DensityCompatHelper.getInstance()
+) : WindowMetricsCalculator {
 
     /**
      * Computes the current [WindowMetrics] for a given [Context]. The context can be either an
@@ -57,33 +35,8 @@
      * @see WindowMetricsCalculator.computeCurrentWindowMetrics
      */
     override fun computeCurrentWindowMetrics(@UiContext context: Context): WindowMetrics {
-        // TODO(b/259148796): Make WindowMetricsCalculatorCompat more testable
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.R) {
-            return currentWindowMetrics(context)
-        } else {
-            when (val unwrappedContext = unwrapUiContext(context)) {
-                is Activity -> {
-                    return computeCurrentWindowMetrics(unwrappedContext)
-                }
-                is InputMethodService -> {
-                    val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-
-                    // On older SDK levels, the app and IME could show up on different displays.
-                    // However, there isn't a way for us to figure this out from the application
-                    // layer. But, this should be good enough for now given the small likelihood of
-                    // IMEs showing up on non-primary displays on these SDK levels.
-                    @Suppress("DEPRECATION")
-                    val displaySize = getRealSizeForDisplay(wm.defaultDisplay)
-
-                    // IME occupies the whole display bounds.
-                    val imeBounds = Rect(0, 0, displaySize.x, displaySize.y)
-                    return WindowMetrics(imeBounds)
-                }
-                else -> {
-                    throw IllegalArgumentException("$context is not a UiContext")
-                }
-            }
-        }
+        return WindowMetricsCompatHelper.getInstance()
+            .currentWindowMetrics(context, densityCompatHelper)
     }
 
     /**
@@ -92,26 +45,8 @@
      * @see WindowMetricsCalculator.computeCurrentWindowMetrics
      */
     override fun computeCurrentWindowMetrics(activity: Activity): WindowMetrics {
-        val bounds =
-            if (Build.VERSION.SDK_INT >= VERSION_CODES.R) {
-                currentWindowBounds(activity)
-            } else if (Build.VERSION.SDK_INT >= VERSION_CODES.Q) {
-                computeWindowBoundsQ(activity)
-            } else if (Build.VERSION.SDK_INT >= VERSION_CODES.P) {
-                computeWindowBoundsP(activity)
-            } else if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
-                computeWindowBoundsN(activity)
-            } else {
-                computeWindowBoundsIceCreamSandwich(activity)
-            }
-        // TODO (b/233899790): compute insets for other platform versions below R
-        val windowInsetsCompat =
-            if (Build.VERSION.SDK_INT >= VERSION_CODES.R) {
-                computeWindowInsetsCompat(activity)
-            } else {
-                WindowInsetsCompat.Builder().build()
-            }
-        return WindowMetrics(Bounds(bounds), windowInsetsCompat)
+        return WindowMetricsCompatHelper.getInstance()
+            .currentWindowMetrics(activity, densityCompatHelper)
     }
 
     /**
@@ -120,7 +55,8 @@
      * @see WindowMetricsCalculator.computeMaximumWindowMetrics
      */
     override fun computeMaximumWindowMetrics(activity: Activity): WindowMetrics {
-        return computeMaximumWindowMetrics(activity as Context)
+        return WindowMetricsCompatHelper.getInstance()
+            .maximumWindowMetrics(activity, densityCompatHelper)
     }
 
     /**
@@ -129,288 +65,8 @@
      * @See WindowMetricsCalculator.computeMaximumWindowMetrics
      */
     override fun computeMaximumWindowMetrics(@UiContext context: Context): WindowMetrics {
-        // TODO(b/259148796): Make WindowMetricsCalculatorCompat more testable
-        val bounds =
-            if (Build.VERSION.SDK_INT >= VERSION_CODES.R) {
-                maximumWindowBounds(context)
-            } else {
-                val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-                // [WindowManager#getDefaultDisplay] is deprecated but we have this for
-                // compatibility with older versions, as we can't reliably get the display
-                // associated
-                // with a Context through public APIs either.
-                @Suppress("DEPRECATION") val display = wm.defaultDisplay
-                val displaySize = getRealSizeForDisplay(display)
-                Rect(0, 0, displaySize.x, displaySize.y)
-            }
-        // TODO (b/233899790): compute insets for other platform versions below R
-        val windowInsetsCompat =
-            if (Build.VERSION.SDK_INT >= VERSION_CODES.R) {
-                computeWindowInsetsCompat(context)
-            } else {
-                WindowInsetsCompat.Builder().build()
-            }
-        return WindowMetrics(Bounds(bounds), windowInsetsCompat)
-    }
-
-    /** Computes the window bounds for [Build.VERSION_CODES.Q]. */
-    @SuppressLint("BanUncheckedReflection", "BlockedPrivateApi")
-    @RequiresApi(VERSION_CODES.Q)
-    internal fun computeWindowBoundsQ(activity: Activity): Rect {
-        var bounds: Rect
-        val config = activity.resources.configuration
-        try {
-            val windowConfigField =
-                Configuration::class.java.getDeclaredField("windowConfiguration")
-            windowConfigField.isAccessible = true
-            val windowConfig = windowConfigField[config]
-            val getBoundsMethod = windowConfig.javaClass.getDeclaredMethod("getBounds")
-            bounds = Rect(getBoundsMethod.invoke(windowConfig) as Rect)
-        } catch (e: NoSuchFieldException) {
-            Log.w(TAG, e)
-            // If reflection fails for some reason default to the P implementation which still
-            // has the ability to account for display cutouts.
-            bounds = computeWindowBoundsP(activity)
-        } catch (e: NoSuchMethodException) {
-            Log.w(TAG, e)
-            bounds = computeWindowBoundsP(activity)
-        } catch (e: IllegalAccessException) {
-            Log.w(TAG, e)
-            bounds = computeWindowBoundsP(activity)
-        } catch (e: InvocationTargetException) {
-            Log.w(TAG, e)
-            bounds = computeWindowBoundsP(activity)
-        }
-        return bounds
-    }
-
-    /**
-     * Computes the window bounds for [Build.VERSION_CODES.P].
-     *
-     * NOTE: This method may result in incorrect values if the [android.content.res.Resources] value
-     * stored at 'navigation_bar_height' does not match the true navigation bar inset on the window.
-     */
-    @SuppressLint("BanUncheckedReflection", "BlockedPrivateApi")
-    @RequiresApi(VERSION_CODES.P)
-    internal fun computeWindowBoundsP(activity: Activity): Rect {
-        val bounds = Rect()
-        val config = activity.resources.configuration
-        try {
-            val windowConfigField =
-                Configuration::class.java.getDeclaredField("windowConfiguration")
-            windowConfigField.isAccessible = true
-            val windowConfig = windowConfigField[config]
-
-            // In multi-window mode we'll use the WindowConfiguration#mBounds property which
-            // should match the window size. Otherwise we'll use the mAppBounds property and
-            // will adjust it below.
-            if (isInMultiWindowMode(activity)) {
-                val getAppBounds = windowConfig.javaClass.getDeclaredMethod("getBounds")
-                bounds.set((getAppBounds.invoke(windowConfig) as Rect))
-            } else {
-                val getAppBounds = windowConfig.javaClass.getDeclaredMethod("getAppBounds")
-                bounds.set((getAppBounds.invoke(windowConfig) as Rect))
-            }
-        } catch (e: NoSuchFieldException) {
-            Log.w(TAG, e)
-            getRectSizeFromDisplay(activity, bounds)
-        } catch (e: NoSuchMethodException) {
-            Log.w(TAG, e)
-            getRectSizeFromDisplay(activity, bounds)
-        } catch (e: IllegalAccessException) {
-            Log.w(TAG, e)
-            getRectSizeFromDisplay(activity, bounds)
-        } catch (e: InvocationTargetException) {
-            Log.w(TAG, e)
-            getRectSizeFromDisplay(activity, bounds)
-        }
-        val platformWindowManager = activity.windowManager
-
-        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
-        // compatibility with older versions
-        @Suppress("DEPRECATION") val currentDisplay = platformWindowManager.defaultDisplay
-        val realDisplaySize = Point()
-        @Suppress("DEPRECATION") currentDisplay.getRealSize(realDisplaySize)
-
-        if (!isInMultiWindowMode(activity)) {
-            // The activity is not in multi-window mode. Check if the addition of the
-            // navigation bar size to mAppBounds results in the real display size and if so
-            // assume the nav bar height should be added to the result.
-            val navigationBarHeight = getNavigationBarHeight(activity)
-            if (bounds.bottom + navigationBarHeight == realDisplaySize.y) {
-                bounds.bottom += navigationBarHeight
-            } else if (bounds.right + navigationBarHeight == realDisplaySize.x) {
-                bounds.right += navigationBarHeight
-            } else if (bounds.left == navigationBarHeight) {
-                bounds.left = 0
-            }
-        }
-        if (
-            (bounds.width() < realDisplaySize.x || bounds.height() < realDisplaySize.y) &&
-                !isInMultiWindowMode(activity)
-        ) {
-            // If the corrected bounds are not the same as the display size and the activity is
-            // not in multi-window mode it is possible there are unreported cutouts inset-ing
-            // the window depending on the layoutInCutoutMode. Check for them here by getting
-            // the cutout from the display itself.
-            val displayCutout = getCutoutForDisplay(currentDisplay)
-            if (displayCutout != null) {
-                if (bounds.left == safeInsetLeft(displayCutout)) {
-                    bounds.left = 0
-                }
-                if (realDisplaySize.x - bounds.right == safeInsetRight(displayCutout)) {
-                    bounds.right += safeInsetRight(displayCutout)
-                }
-                if (bounds.top == safeInsetTop(displayCutout)) {
-                    bounds.top = 0
-                }
-                if (realDisplaySize.y - bounds.bottom == safeInsetBottom(displayCutout)) {
-                    bounds.bottom += safeInsetBottom(displayCutout)
-                }
-            }
-        }
-        return bounds
-    }
-
-    private fun getRectSizeFromDisplay(activity: Activity, bounds: Rect) {
-        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
-        // compatibility with older versions
-        @Suppress("DEPRECATION") val defaultDisplay = activity.windowManager.defaultDisplay
-        // [Display#getRectSize] is deprecated but we have this for
-        // compatibility with older versions
-        @Suppress("DEPRECATION") defaultDisplay.getRectSize(bounds)
-    }
-
-    /**
-     * Computes the window bounds for platforms between [Build.VERSION_CODES.N] and
-     * [Build.VERSION_CODES.O_MR1], inclusive.
-     *
-     * NOTE: This method may result in incorrect values under the following conditions:
-     * * If the activity is in multi-window mode the origin of the returned bounds will always be
-     *   anchored at (0, 0).
-     * * If the [android.content.res.Resources] value stored at 'navigation_bar_height' does not
-     *   match the true navigation bar size the returned bounds will not take into account the
-     *   navigation bar.
-     */
-    @RequiresApi(VERSION_CODES.N)
-    internal fun computeWindowBoundsN(activity: Activity): Rect {
-        val bounds = Rect()
-        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
-        // compatibility with older versions
-        @Suppress("DEPRECATION") val defaultDisplay = activity.windowManager.defaultDisplay
-        // [Display#getRectSize] is deprecated but we have this for
-        // compatibility with older versions
-        @Suppress("DEPRECATION") defaultDisplay.getRectSize(bounds)
-        if (!isInMultiWindowMode(activity)) {
-            // The activity is not in multi-window mode. Check if the addition of the
-            // navigation bar size to Display#getSize() results in the real display size and
-            // if so return this value. If not, return the result of Display#getSize().
-            val realDisplaySize = getRealSizeForDisplay(defaultDisplay)
-            val navigationBarHeight = getNavigationBarHeight(activity)
-            if (bounds.bottom + navigationBarHeight == realDisplaySize.y) {
-                bounds.bottom += navigationBarHeight
-            } else if (bounds.right + navigationBarHeight == realDisplaySize.x) {
-                bounds.right += navigationBarHeight
-            }
-        }
-        return bounds
-    }
-
-    /**
-     * Computes the window bounds for platforms between [Build.VERSION_CODES.JELLY_BEAN] and
-     * [Build.VERSION_CODES.M], inclusive.
-     *
-     * Given that multi-window mode isn't supported before N we simply return the real display size
-     * which should match the window size of a full-screen app.
-     */
-    internal fun computeWindowBoundsIceCreamSandwich(activity: Activity): Rect {
-        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
-        // compatibility with older versions
-        @Suppress("DEPRECATION") val defaultDisplay = activity.windowManager.defaultDisplay
-        val realDisplaySize = getRealSizeForDisplay(defaultDisplay)
-        val bounds = Rect()
-        if (realDisplaySize.x == 0 || realDisplaySize.y == 0) {
-            // [Display#getRectSize] is deprecated but we have this for
-            // compatibility with older versions
-            @Suppress("DEPRECATION") defaultDisplay.getRectSize(bounds)
-        } else {
-            bounds.right = realDisplaySize.x
-            bounds.bottom = realDisplaySize.y
-        }
-        return bounds
-    }
-
-    /**
-     * Returns the full (real) size of the display, in pixels, without subtracting any window decor
-     * or applying any compatibility scale factors.
-     *
-     * The size is adjusted based on the current rotation of the display.
-     *
-     * @return a point representing the real display size in pixels.
-     * @see Display.getRealSize
-     */
-    @VisibleForTesting
-    @Suppress("DEPRECATION")
-    internal fun getRealSizeForDisplay(display: Display): Point {
-        val size = Point()
-        display.getRealSize(size)
-        return size
-    }
-
-    /**
-     * Returns the [android.content.res.Resources] value stored as 'navigation_bar_height'.
-     *
-     * Note: This is error-prone and is **not** the recommended way to determine the size of the
-     * overlapping region between the navigation bar and a given window. The best approach is to
-     * acquire the [android.view.WindowInsets].
-     */
-    private fun getNavigationBarHeight(context: Context): Int {
-        val resources = context.resources
-        val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
-        return if (resourceId > 0) {
-            resources.getDimensionPixelSize(resourceId)
-        } else 0
-    }
-
-    /**
-     * Returns the [DisplayCutout] for the given display. Note that display cutout returned here is
-     * for the display and the insets provided are in the display coordinate system.
-     *
-     * @return the display cutout for the given display.
-     */
-    @SuppressLint("BanUncheckedReflection")
-    @RequiresApi(VERSION_CODES.P)
-    private fun getCutoutForDisplay(display: Display): DisplayCutout? {
-        var displayCutout: DisplayCutout? = null
-        try {
-            val displayInfoClass = Class.forName("android.view.DisplayInfo")
-            val displayInfoConstructor = displayInfoClass.getConstructor()
-            displayInfoConstructor.isAccessible = true
-            val displayInfo = displayInfoConstructor.newInstance()
-            val getDisplayInfoMethod =
-                display.javaClass.getDeclaredMethod("getDisplayInfo", displayInfo.javaClass)
-            getDisplayInfoMethod.isAccessible = true
-            getDisplayInfoMethod.invoke(display, displayInfo)
-            val displayCutoutField = displayInfo.javaClass.getDeclaredField("displayCutout")
-            displayCutoutField.isAccessible = true
-            val cutout = displayCutoutField[displayInfo]
-            if (cutout is DisplayCutout) {
-                displayCutout = cutout
-            }
-        } catch (e: ClassNotFoundException) {
-            Log.w(TAG, e)
-        } catch (e: NoSuchMethodException) {
-            Log.w(TAG, e)
-        } catch (e: NoSuchFieldException) {
-            Log.w(TAG, e)
-        } catch (e: IllegalAccessException) {
-            Log.w(TAG, e)
-        } catch (e: InvocationTargetException) {
-            Log.w(TAG, e)
-        } catch (e: InstantiationException) {
-            Log.w(TAG, e)
-        }
-        return displayCutout
+        return WindowMetricsCompatHelper.getInstance()
+            .maximumWindowMetrics(context, densityCompatHelper)
     }
 
     /** [ArrayList] that defines different types of sources causing window insets. */
@@ -425,17 +81,4 @@
             WindowInsetsCompat.Type.tappableElement(),
             WindowInsetsCompat.Type.displayCutout()
         )
-
-    /** Computes the current [WindowInsetsCompat] for a given [Context]. */
-    @RequiresApi(VERSION_CODES.R)
-    internal fun computeWindowInsetsCompat(@UiContext context: Context): WindowInsetsCompat {
-        val build = Build.VERSION.SDK_INT
-        val windowInsetsCompat =
-            if (build >= VERSION_CODES.R) {
-                currentWindowInsets(context)
-            } else {
-                throw Exception("Incompatible SDK version")
-            }
-        return windowInsetsCompat
-    }
 }
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/WindowBackend.kt b/window/window/src/main/java/androidx/window/layout/adapter/WindowBackend.kt
index 9b7e977..c147914 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/WindowBackend.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/WindowBackend.kt
@@ -20,6 +20,8 @@
 import androidx.annotation.RestrictTo
 import androidx.annotation.UiContext
 import androidx.core.util.Consumer
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.layout.SupportedPosture
 import androidx.window.layout.WindowLayoutInfo
 import java.util.concurrent.Executor
 
@@ -48,4 +50,11 @@
     fun hasRegisteredListeners(): Boolean {
         return false
     }
+
+    /**
+     * Returns a [List] of [SupportedPosture] for the device.
+     *
+     * @throws UnsupportedOperationException if the Window SDK version is less than 6.
+     */
+    @RequiresWindowSdkExtension(version = 6) val supportedPostures: List<SupportedPosture>
 }
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackend.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackend.kt
index 9179308..9d53488 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackend.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackend.kt
@@ -42,7 +42,8 @@
         fun newInstance(component: WindowLayoutComponent, adapter: ConsumerAdapter): WindowBackend {
             val safeVendorApiLevel = ExtensionsUtil.safeVendorApiLevel
             return when {
-                safeVendorApiLevel >= 2 -> ExtensionWindowBackendApi2(component)
+                safeVendorApiLevel >= 6 -> ExtensionWindowBackendApi6(component, adapter)
+                safeVendorApiLevel >= 2 -> ExtensionWindowBackendApi2(component, adapter)
                 safeVendorApiLevel == 1 -> ExtensionWindowBackendApi1(component, adapter)
                 else -> ExtensionWindowBackendApi0()
             }
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi0.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi0.kt
index 39e2cfe..72b4c87 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi0.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi0.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import androidx.core.util.Consumer
+import androidx.window.layout.SupportedPosture
 import androidx.window.layout.WindowLayoutInfo
 import androidx.window.layout.adapter.WindowBackend
 import java.util.concurrent.Executor
@@ -35,4 +36,10 @@
     override fun unregisterLayoutChangeCallback(callback: Consumer<WindowLayoutInfo>) {
         // empty implementation since there are no consumers
     }
+
+    override val supportedPostures: List<SupportedPosture>
+        get() =
+            throw UnsupportedOperationException(
+                "supportedPostures is only supported on Window SDK 6."
+            )
 }
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi1.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi1.kt
index a32b465..33c53a7 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi1.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi1.kt
@@ -25,14 +25,15 @@
 import androidx.window.core.ConsumerAdapter
 import androidx.window.extensions.layout.WindowLayoutComponent
 import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
+import androidx.window.layout.SupportedPosture
 import androidx.window.layout.WindowLayoutInfo
 import androidx.window.layout.adapter.WindowBackend
 import java.util.concurrent.Executor
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
 
-internal class ExtensionWindowBackendApi1(
-    private val component: WindowLayoutComponent,
+internal open class ExtensionWindowBackendApi1(
+    val component: WindowLayoutComponent,
     private val consumerAdapter: ConsumerAdapter
 ) : WindowBackend {
 
@@ -125,4 +126,7 @@
             listenerToContext.isEmpty() &&
             consumerToToken.isEmpty())
     }
+
+    override val supportedPostures: List<SupportedPosture>
+        get() = throw UnsupportedOperationException("Extensions version must be at least 6")
 }
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi2.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi2.kt
index 07e5917..0a20ba9 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi2.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi2.kt
@@ -22,15 +22,17 @@
 import androidx.annotation.UiContext
 import androidx.annotation.VisibleForTesting
 import androidx.core.util.Consumer
+import androidx.window.core.ConsumerAdapter
 import androidx.window.extensions.layout.WindowLayoutComponent
 import androidx.window.layout.WindowLayoutInfo
-import androidx.window.layout.adapter.WindowBackend
 import java.util.concurrent.Executor
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
 
-internal class ExtensionWindowBackendApi2(private val component: WindowLayoutComponent) :
-    WindowBackend {
+internal open class ExtensionWindowBackendApi2(
+    component: WindowLayoutComponent,
+    adapter: ConsumerAdapter
+) : ExtensionWindowBackendApi1(component, adapter) {
 
     private val globalLock = ReentrantLock()
 
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi6.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi6.kt
new file mode 100644
index 0000000..ee9ff3c
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowBackendApi6.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout.adapter.extensions
+
+import androidx.window.RequiresWindowSdkExtension
+import androidx.window.core.ConsumerAdapter
+import androidx.window.extensions.layout.WindowLayoutComponent
+import androidx.window.layout.SupportedPosture
+
+@RequiresWindowSdkExtension(version = 6)
+internal class ExtensionWindowBackendApi6(
+    component: WindowLayoutComponent,
+    adapter: ConsumerAdapter
+) : ExtensionWindowBackendApi2(component, adapter) {
+    override val supportedPostures: List<SupportedPosture>
+        get() = ExtensionsWindowLayoutInfoAdapter.translate(component.supportedWindowFeatures)
+}
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapter.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapter.kt
index 66cdf23..3ad07c2 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapter.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapter.kt
@@ -21,7 +21,9 @@
 import android.os.Build
 import androidx.annotation.UiContext
 import androidx.window.core.Bounds
+import androidx.window.extensions.layout.DisplayFoldFeature
 import androidx.window.extensions.layout.FoldingFeature as OEMFoldingFeature
+import androidx.window.extensions.layout.SupportedWindowFeatures
 import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
 import androidx.window.layout.FoldingFeature
 import androidx.window.layout.FoldingFeature.State.Companion.FLAT
@@ -29,9 +31,10 @@
 import androidx.window.layout.HardwareFoldingFeature
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
+import androidx.window.layout.SupportedPosture
 import androidx.window.layout.WindowLayoutInfo
 import androidx.window.layout.WindowMetrics
-import androidx.window.layout.WindowMetricsCalculatorCompat.computeCurrentWindowMetrics
+import androidx.window.layout.WindowMetricsCalculatorCompat
 
 internal object ExtensionsWindowLayoutInfoAdapter {
 
@@ -63,10 +66,11 @@
         @UiContext context: Context,
         info: OEMWindowLayoutInfo,
     ): WindowLayoutInfo {
+        val calculator = WindowMetricsCalculatorCompat()
         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            translate(computeCurrentWindowMetrics(context), info)
+            translate(calculator.computeCurrentWindowMetrics(context), info)
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && (context is Activity)) {
-            translate(computeCurrentWindowMetrics(context), info)
+            translate(calculator.computeCurrentWindowMetrics(context), info)
         } else {
             throw UnsupportedOperationException(
                 "Display Features are only supported after Q. Display features for non-Activity " +
@@ -89,6 +93,18 @@
         return WindowLayoutInfo(features)
     }
 
+    internal fun translate(features: SupportedWindowFeatures): List<SupportedPosture> {
+        val isTableTopSupported =
+            features.displayFoldFeatures.any { feature ->
+                feature.hasProperties(DisplayFoldFeature.FOLD_PROPERTY_SUPPORTS_HALF_OPENED)
+            }
+        return if (isTableTopSupported) {
+            listOf(SupportedPosture.TABLETOP)
+        } else {
+            emptyList()
+        }
+    }
+
     /**
      * Checks the bounds for a [FoldingFeature] within a given [WindowMetrics]. Validates the
      * following:
diff --git a/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackend.kt b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackend.kt
index 14a603c..31ee16f 100644
--- a/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackend.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackend.kt
@@ -23,6 +23,7 @@
 import androidx.annotation.VisibleForTesting
 import androidx.core.util.Consumer
 import androidx.window.core.Version
+import androidx.window.layout.SupportedPosture
 import androidx.window.layout.WindowLayoutInfo
 import androidx.window.layout.adapter.WindowBackend
 import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
@@ -127,6 +128,9 @@
         }
     }
 
+    override val supportedPostures: List<SupportedPosture>
+        get() = throw UnsupportedOperationException("Must be called from extensions.")
+
     /**
      * Checks if there are no more registered callbacks left for the activity and inform extension
      * if needed.
diff --git a/window/window/src/main/java/androidx/window/layout/util/BoundsHelper.kt b/window/window/src/main/java/androidx/window/layout/util/BoundsHelper.kt
new file mode 100644
index 0000000..3d24d92
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/layout/util/BoundsHelper.kt
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout.util
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.Build
+import android.util.Log
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.WindowManager
+import androidx.annotation.RequiresApi
+import androidx.annotation.UiContext
+import androidx.window.layout.util.ActivityCompatHelperApi24.isInMultiWindowMode
+import androidx.window.layout.util.BoundsHelper.Companion.TAG
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetBottom
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetLeft
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetRight
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetTop
+import androidx.window.layout.util.DisplayHelper.getRealSizeForDisplay
+import java.lang.reflect.InvocationTargetException
+
+/** Provides compatibility behavior for calculating bounds of an activity. */
+internal interface BoundsHelper {
+
+    /** Compute the current bounds for the given [Activity]. */
+    fun currentWindowBounds(activity: Activity): Rect
+
+    fun maximumWindowBounds(@UiContext context: Context): Rect
+
+    companion object {
+
+        val TAG: String = BoundsHelper::class.java.simpleName
+
+        fun getInstance(): BoundsHelper {
+            return when {
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
+                    BoundsHelperApi30Impl
+                }
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
+                    BoundsHelperApi29Impl
+                }
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
+                    BoundsHelperApi28Impl
+                }
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
+                    BoundsHelperApi24Impl
+                }
+                else -> {
+                    BoundsHelperApi16Impl
+                }
+            }
+        }
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.R)
+private object BoundsHelperApi30Impl : BoundsHelper {
+    override fun currentWindowBounds(activity: Activity): Rect {
+        val wm = activity.getSystemService(WindowManager::class.java)
+        return wm.currentWindowMetrics.bounds
+    }
+
+    override fun maximumWindowBounds(@UiContext context: Context): Rect {
+        val wm = context.getSystemService(WindowManager::class.java)
+        return wm.maximumWindowMetrics.bounds
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+private object BoundsHelperApi29Impl : BoundsHelper {
+
+    /** Computes the window bounds for [Build.VERSION_CODES.Q]. */
+    @SuppressLint("BanUncheckedReflection", "BlockedPrivateApi")
+    override fun currentWindowBounds(activity: Activity): Rect {
+        var bounds: Rect
+        val config = activity.resources.configuration
+        try {
+            val windowConfigField =
+                Configuration::class.java.getDeclaredField("windowConfiguration")
+            windowConfigField.isAccessible = true
+            val windowConfig = windowConfigField[config]
+            val getBoundsMethod = windowConfig.javaClass.getDeclaredMethod("getBounds")
+            bounds = Rect(getBoundsMethod.invoke(windowConfig) as Rect)
+        } catch (e: Exception) {
+            when (e) {
+                is NoSuchFieldException,
+                is NoSuchMethodException,
+                is IllegalAccessException,
+                is InvocationTargetException -> {
+                    Log.w(TAG, e)
+                    // If reflection fails for some reason default to the P implementation which
+                    // still has the ability to account for display cutouts.
+                    bounds = BoundsHelperApi28Impl.currentWindowBounds(activity)
+                }
+                else -> throw e
+            }
+        }
+        return bounds
+    }
+
+    override fun maximumWindowBounds(@UiContext context: Context): Rect {
+        return BoundsHelperApi28Impl.maximumWindowBounds(context)
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.P)
+private object BoundsHelperApi28Impl : BoundsHelper {
+
+    /**
+     * Computes the window bounds for [Build.VERSION_CODES.P].
+     *
+     * NOTE: This method may result in incorrect values if the [android.content.res.Resources] value
+     * stored at 'navigation_bar_height' does not match the true navigation bar inset on the window.
+     */
+    @SuppressLint("BanUncheckedReflection", "BlockedPrivateApi")
+    override fun currentWindowBounds(activity: Activity): Rect {
+        val bounds = Rect()
+        val config = activity.resources.configuration
+        try {
+            val windowConfigField =
+                Configuration::class.java.getDeclaredField("windowConfiguration")
+            windowConfigField.isAccessible = true
+            val windowConfig = windowConfigField[config]
+
+            // In multi-window mode we'll use the WindowConfiguration#mBounds property which
+            // should match the window size. Otherwise we'll use the mAppBounds property and
+            // will adjust it below.
+            if (isInMultiWindowMode(activity)) {
+                val getAppBounds = windowConfig.javaClass.getDeclaredMethod("getBounds")
+                bounds.set((getAppBounds.invoke(windowConfig) as Rect))
+            } else {
+                val getAppBounds = windowConfig.javaClass.getDeclaredMethod("getAppBounds")
+                bounds.set((getAppBounds.invoke(windowConfig) as Rect))
+            }
+        } catch (e: Exception) {
+            when (e) {
+                is NoSuchFieldException,
+                is NoSuchMethodException,
+                is IllegalAccessException,
+                is InvocationTargetException -> {
+                    Log.w(TAG, e)
+                    getRectSizeFromDisplay(activity, bounds)
+                }
+                else -> throw e
+            }
+        }
+
+        val platformWindowManager = activity.windowManager
+
+        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
+        // compatibility with older versions
+        @Suppress("DEPRECATION") val currentDisplay = platformWindowManager.defaultDisplay
+        val realDisplaySize = Point()
+        @Suppress("DEPRECATION") currentDisplay.getRealSize(realDisplaySize)
+
+        if (!isInMultiWindowMode(activity)) {
+            // The activity is not in multi-window mode. Check if the addition of the
+            // navigation bar size to mAppBounds results in the real display size and if so
+            // assume the nav bar height should be added to the result.
+            val navigationBarHeight = getNavigationBarHeight(activity)
+            if (bounds.bottom + navigationBarHeight == realDisplaySize.y) {
+                bounds.bottom += navigationBarHeight
+            } else if (bounds.right + navigationBarHeight == realDisplaySize.x) {
+                bounds.right += navigationBarHeight
+            } else if (bounds.left == navigationBarHeight) {
+                bounds.left = 0
+            }
+        }
+        if (
+            (bounds.width() < realDisplaySize.x || bounds.height() < realDisplaySize.y) &&
+                !isInMultiWindowMode(activity)
+        ) {
+            // If the corrected bounds are not the same as the display size and the activity is
+            // not in multi-window mode it is possible there are unreported cutouts inset-ing
+            // the window depending on the layoutInCutoutMode. Check for them here by getting
+            // the cutout from the display itself.
+            val displayCutout = getCutoutForDisplay(currentDisplay)
+            if (displayCutout != null) {
+                if (bounds.left == safeInsetLeft(displayCutout)) {
+                    bounds.left = 0
+                }
+                if (realDisplaySize.x - bounds.right == safeInsetRight(displayCutout)) {
+                    bounds.right += safeInsetRight(displayCutout)
+                }
+                if (bounds.top == safeInsetTop(displayCutout)) {
+                    bounds.top = 0
+                }
+                if (realDisplaySize.y - bounds.bottom == safeInsetBottom(displayCutout)) {
+                    bounds.bottom += safeInsetBottom(displayCutout)
+                }
+            }
+        }
+        return bounds
+    }
+
+    override fun maximumWindowBounds(@UiContext context: Context): Rect {
+        return BoundsHelperApi24Impl.maximumWindowBounds(context)
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.N)
+private object BoundsHelperApi24Impl : BoundsHelper {
+
+    /**
+     * Computes the window bounds for platforms between [Build.VERSION_CODES.N] and
+     * [Build.VERSION_CODES.O_MR1], inclusive.
+     *
+     * NOTE: This method may result in incorrect values under the following conditions:
+     * * If the activity is in multi-window mode the origin of the returned bounds will always be
+     *   anchored at (0, 0).
+     * * If the [android.content.res.Resources] value stored at 'navigation_bar_height' does not
+     *   match the true navigation bar size the returned bounds will not take into account the
+     *   navigation bar.
+     */
+    override fun currentWindowBounds(activity: Activity): Rect {
+        val bounds = Rect()
+        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
+        // compatibility with older versions
+        @Suppress("DEPRECATION") val defaultDisplay = activity.windowManager.defaultDisplay
+        // [Display#getRectSize] is deprecated but we have this for
+        // compatibility with older versions
+        @Suppress("DEPRECATION") defaultDisplay.getRectSize(bounds)
+        if (!isInMultiWindowMode(activity)) {
+            // The activity is not in multi-window mode. Check if the addition of the
+            // navigation bar size to Display#getSize() results in the real display size and
+            // if so return this value. If not, return the result of Display#getSize().
+            val realDisplaySize = getRealSizeForDisplay(defaultDisplay)
+            val navigationBarHeight = getNavigationBarHeight(activity)
+            if (bounds.bottom + navigationBarHeight == realDisplaySize.y) {
+                bounds.bottom += navigationBarHeight
+            } else if (bounds.right + navigationBarHeight == realDisplaySize.x) {
+                bounds.right += navigationBarHeight
+            }
+        }
+        return bounds
+    }
+
+    override fun maximumWindowBounds(@UiContext context: Context): Rect {
+        return BoundsHelperApi16Impl.maximumWindowBounds(context)
+    }
+}
+
+private object BoundsHelperApi16Impl : BoundsHelper {
+
+    /**
+     * Computes the window bounds for platforms between [Build.VERSION_CODES.JELLY_BEAN] and
+     * [Build.VERSION_CODES.M], inclusive.
+     *
+     * Given that multi-window mode isn't supported before N we simply return the real display size
+     * which should match the window size of a full-screen app.
+     */
+    override fun currentWindowBounds(activity: Activity): Rect {
+        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
+        // compatibility with older versions
+        @Suppress("DEPRECATION") val defaultDisplay = activity.windowManager.defaultDisplay
+        val realDisplaySize = getRealSizeForDisplay(defaultDisplay)
+        val bounds = Rect()
+        if (realDisplaySize.x == 0 || realDisplaySize.y == 0) {
+            // [Display#getRectSize] is deprecated but we have this for
+            // compatibility with older versions
+            @Suppress("DEPRECATION") defaultDisplay.getRectSize(bounds)
+        } else {
+            bounds.right = realDisplaySize.x
+            bounds.bottom = realDisplaySize.y
+        }
+        return bounds
+    }
+
+    override fun maximumWindowBounds(@UiContext context: Context): Rect {
+        val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+        // [WindowManager#getDefaultDisplay] is deprecated but we have this for
+        // compatibility with older versions, as we can't reliably get the display associated
+        // with a Context through public APIs either.
+        @Suppress("DEPRECATION") val display = wm.defaultDisplay
+        val displaySize = getRealSizeForDisplay(display)
+        return Rect(0, 0, displaySize.x, displaySize.y)
+    }
+}
+
+/**
+ * Returns the [android.content.res.Resources] value stored as 'navigation_bar_height'.
+ *
+ * Note: This is error-prone and is **not** the recommended way to determine the size of the
+ * overlapping region between the navigation bar and a given window. The best approach is to acquire
+ * the [android.view.WindowInsets].
+ */
+private fun getNavigationBarHeight(context: Context): Int {
+    val resources = context.resources
+    val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
+    return if (resourceId > 0) {
+        resources.getDimensionPixelSize(resourceId)
+    } else 0
+}
+
+private fun getRectSizeFromDisplay(activity: Activity, bounds: Rect) {
+    // [WindowManager#getDefaultDisplay] is deprecated but we have this for
+    // compatibility with older versions
+    @Suppress("DEPRECATION") val defaultDisplay = activity.windowManager.defaultDisplay
+    // [Display#getRectSize] is deprecated but we have this for
+    // compatibility with older versions
+    @Suppress("DEPRECATION") defaultDisplay.getRectSize(bounds)
+}
+
+/**
+ * Returns the [DisplayCutout] for the given display. Note that display cutout returned here is for
+ * the display and the insets provided are in the display coordinate system.
+ *
+ * @return the display cutout for the given display.
+ */
+@SuppressLint("BanUncheckedReflection")
+@RequiresApi(Build.VERSION_CODES.P)
+private fun getCutoutForDisplay(display: Display): DisplayCutout? {
+    var displayCutout: DisplayCutout? = null
+    try {
+        val displayInfoClass = Class.forName("android.view.DisplayInfo")
+        val displayInfoConstructor = displayInfoClass.getConstructor()
+        displayInfoConstructor.isAccessible = true
+        val displayInfo = displayInfoConstructor.newInstance()
+        val getDisplayInfoMethod =
+            display.javaClass.getDeclaredMethod("getDisplayInfo", displayInfo.javaClass)
+        getDisplayInfoMethod.isAccessible = true
+        getDisplayInfoMethod.invoke(display, displayInfo)
+        val displayCutoutField = displayInfo.javaClass.getDeclaredField("displayCutout")
+        displayCutoutField.isAccessible = true
+        val cutout = displayCutoutField[displayInfo]
+        if (cutout is DisplayCutout) {
+            displayCutout = cutout
+        }
+    } catch (e: Exception) {
+        when (e) {
+            is ClassNotFoundException,
+            is NoSuchMethodException,
+            is NoSuchFieldException,
+            is IllegalAccessException,
+            is InvocationTargetException,
+            is InstantiationException -> {
+                Log.w(TAG, e)
+            }
+            else -> throw e
+        }
+    }
+    return displayCutout
+}
diff --git a/window/window/src/main/java/androidx/window/layout/util/ContextCompatHelper.kt b/window/window/src/main/java/androidx/window/layout/util/ContextCompatHelper.kt
index 36fc60b..9207d98 100644
--- a/window/window/src/main/java/androidx/window/layout/util/ContextCompatHelper.kt
+++ b/window/window/src/main/java/androidx/window/layout/util/ContextCompatHelper.kt
@@ -19,14 +19,12 @@
 import android.app.Activity
 import android.content.Context
 import android.content.ContextWrapper
-import android.graphics.Rect
 import android.inputmethodservice.InputMethodService
 import android.os.Build
 import android.view.WindowManager
 import androidx.annotation.RequiresApi
 import androidx.annotation.UiContext
 import androidx.core.view.WindowInsetsCompat
-import androidx.window.layout.WindowMetrics
 
 internal object ContextCompatHelper {
     /**
@@ -59,32 +57,9 @@
     }
 }
 
-@RequiresApi(Build.VERSION_CODES.N)
-internal object ContextCompatHelperApi24 {
-    fun isInMultiWindowMode(activity: Activity): Boolean {
-        return activity.isInMultiWindowMode
-    }
-}
-
 @RequiresApi(Build.VERSION_CODES.R)
 internal object ContextCompatHelperApi30 {
 
-    fun currentWindowMetrics(@UiContext context: Context): WindowMetrics {
-        val wm = context.getSystemService(WindowManager::class.java)
-        val insets = WindowInsetsCompat.toWindowInsetsCompat(wm.currentWindowMetrics.windowInsets)
-        return WindowMetrics(wm.currentWindowMetrics.bounds, insets)
-    }
-
-    fun currentWindowBounds(@UiContext context: Context): Rect {
-        val wm = context.getSystemService(WindowManager::class.java)
-        return wm.currentWindowMetrics.bounds
-    }
-
-    fun maximumWindowBounds(@UiContext context: Context): Rect {
-        val wm = context.getSystemService(WindowManager::class.java)
-        return wm.maximumWindowMetrics.bounds
-    }
-
     /**
      * Computes the [WindowInsetsCompat] for platforms above [Build.VERSION_CODES.R], inclusive.
      *
diff --git a/window/window/src/main/java/androidx/window/layout/util/DensityCompatHelper.kt b/window/window/src/main/java/androidx/window/layout/util/DensityCompatHelper.kt
new file mode 100644
index 0000000..96408ed
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/layout/util/DensityCompatHelper.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout.util
+
+import android.content.Context
+import android.content.res.Configuration
+import android.os.Build
+import android.util.DisplayMetrics
+import android.view.WindowManager
+import android.view.WindowMetrics as AndroidWindowMetrics
+import androidx.annotation.RequiresApi
+import androidx.annotation.UiContext
+
+/** Provides compatibility behavior for functionality related to display density. */
+internal interface DensityCompatHelper {
+
+    /** Returns the logical density of the display associated with the [Context]. */
+    fun density(context: Context): Float
+
+    /**
+     * Returns the logical density of the display associated with the [Configuration] or
+     * [AndroidWindowMetrics], depending on the SDK level.
+     */
+    fun density(configuration: Configuration, windowMetrics: AndroidWindowMetrics): Float
+
+    companion object {
+        fun getInstance(): DensityCompatHelper {
+            return when {
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ->
+                    DensityCompatHelperApi34Impl
+                else -> DensityCompatHelperBaseImpl
+            }
+        }
+    }
+}
+
+private object DensityCompatHelperBaseImpl : DensityCompatHelper {
+    override fun density(context: Context): Float {
+        return context.resources.displayMetrics.density
+    }
+
+    override fun density(configuration: Configuration, windowMetrics: AndroidWindowMetrics): Float {
+        return configuration.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+private object DensityCompatHelperApi34Impl : DensityCompatHelper {
+    override fun density(@UiContext context: Context): Float {
+        return context.getSystemService(WindowManager::class.java).currentWindowMetrics.density
+    }
+
+    override fun density(configuration: Configuration, windowMetrics: AndroidWindowMetrics): Float {
+        return windowMetrics.density
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/layout/util/DisplayHelper.kt b/window/window/src/main/java/androidx/window/layout/util/DisplayHelper.kt
new file mode 100644
index 0000000..30463dc
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/layout/util/DisplayHelper.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout.util
+
+import android.graphics.Point
+import android.view.Display
+
+internal object DisplayHelper {
+
+    /**
+     * Returns the full (real) size of the display, in pixels, without subtracting any window decor
+     * or applying any compatibility scale factors.
+     *
+     * The size is adjusted based on the current rotation of the display.
+     *
+     * @return a point representing the real display size in pixels.
+     * @see Display.getRealSize
+     */
+    @Suppress("DEPRECATION")
+    fun getRealSizeForDisplay(display: Display): Point {
+        val size = Point()
+        display.getRealSize(size)
+        return size
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/layout/util/WindowMetricsCompatHelper.kt b/window/window/src/main/java/androidx/window/layout/util/WindowMetricsCompatHelper.kt
new file mode 100644
index 0000000..313ebf6
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/layout/util/WindowMetricsCompatHelper.kt
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.layout.util
+
+import android.app.Activity
+import android.content.Context
+import android.graphics.Rect
+import android.inputmethodservice.InputMethodService
+import android.os.Build
+import android.view.WindowManager
+import android.view.WindowMetrics as AndroidWindowMetrics
+import androidx.annotation.RequiresApi
+import androidx.annotation.UiContext
+import androidx.core.view.WindowInsetsCompat
+import androidx.window.core.Bounds
+import androidx.window.layout.WindowMetrics
+import androidx.window.layout.WindowMetricsCalculator
+import androidx.window.layout.util.ContextCompatHelper.unwrapUiContext
+import androidx.window.layout.util.ContextCompatHelperApi30.currentWindowInsets
+import androidx.window.layout.util.DisplayHelper.getRealSizeForDisplay
+
+/** Provides compatibility behavior for functionality related to [WindowMetricsCalculator]. */
+internal interface WindowMetricsCompatHelper {
+
+    /** Translates platform [AndroidWindowMetrics] to jetpack [WindowMetrics]. */
+    @RequiresApi(Build.VERSION_CODES.R)
+    fun translateWindowMetrics(windowMetrics: AndroidWindowMetrics, density: Float): WindowMetrics
+
+    /** Returns the [WindowMetrics] associated with the provided [Context]. */
+    fun currentWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics
+
+    /** Returns the [WindowMetrics] associated with the provided [Activity]. */
+    fun currentWindowMetrics(
+        activity: Activity,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics
+
+    /** Returns the maximum [WindowMetrics] for a given [UiContext]. */
+    fun maximumWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics
+
+    companion object {
+        fun getInstance(): WindowMetricsCompatHelper {
+            return when {
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ->
+                    WindowMetricsCompatHelperApi34Impl
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> WindowMetricsCompatHelperApi30Impl
+                else -> WindowMetricsCompatHelperBaseImpl
+            }
+        }
+    }
+}
+
+internal object WindowMetricsCompatHelperBaseImpl : WindowMetricsCompatHelper {
+
+    override fun translateWindowMetrics(
+        windowMetrics: AndroidWindowMetrics,
+        density: Float
+    ): WindowMetrics {
+        throw UnsupportedOperationException("translateWindowMetrics not available before API30")
+    }
+
+    override fun currentWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        when (val unwrappedContext = unwrapUiContext(context)) {
+            is Activity -> {
+                return currentWindowMetrics(unwrappedContext, densityCompatHelper)
+            }
+            is InputMethodService -> {
+                val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+
+                // On older SDK levels, the app and IME could show up on different displays.
+                // However, there isn't a way for us to figure this out from the application
+                // layer. But, this should be good enough for now given the small likelihood of
+                // IMEs showing up on non-primary displays on these SDK levels.
+                @Suppress("DEPRECATION") val displaySize = getRealSizeForDisplay(wm.defaultDisplay)
+
+                // IME occupies the whole display bounds.
+                val imeBounds = Rect(0, 0, displaySize.x, displaySize.y)
+                return WindowMetrics(imeBounds, density = densityCompatHelper.density(context))
+            }
+            else -> {
+                throw IllegalArgumentException("$context is not a UiContext")
+            }
+        }
+    }
+
+    override fun currentWindowMetrics(
+        activity: Activity,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        // TODO (b/233899790): compute insets for other platform versions below R
+        return WindowMetrics(
+            Bounds(BoundsHelper.getInstance().currentWindowBounds(activity)),
+            WindowInsetsCompat.Builder().build(),
+            densityCompatHelper.density(activity)
+        )
+    }
+
+    override fun maximumWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        // TODO (b/233899790): compute insets for other platform versions below Rs
+        return WindowMetrics(
+            Bounds(BoundsHelper.getInstance().maximumWindowBounds(context)),
+            WindowInsetsCompat.Builder().build(),
+            densityCompatHelper.density(context)
+        )
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.R)
+internal object WindowMetricsCompatHelperApi30Impl : WindowMetricsCompatHelper {
+
+    override fun translateWindowMetrics(
+        windowMetrics: AndroidWindowMetrics,
+        density: Float
+    ): WindowMetrics {
+        return WindowMetrics(
+            windowMetrics.bounds,
+            WindowInsetsCompat.toWindowInsetsCompat(windowMetrics.windowInsets),
+            density
+        )
+    }
+
+    override fun currentWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        val wm = context.getSystemService(WindowManager::class.java)
+        val insets = WindowInsetsCompat.toWindowInsetsCompat(wm.currentWindowMetrics.windowInsets)
+        val density = context.resources.displayMetrics.density
+        return WindowMetrics(wm.currentWindowMetrics.bounds, insets, density)
+    }
+
+    override fun currentWindowMetrics(
+        activity: Activity,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        return WindowMetrics(
+            Bounds(BoundsHelper.getInstance().currentWindowBounds(activity)),
+            currentWindowInsets(activity),
+            densityCompatHelper.density(activity)
+        )
+    }
+
+    override fun maximumWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        return WindowMetrics(
+            Bounds(BoundsHelper.getInstance().maximumWindowBounds(context)),
+            currentWindowInsets(context),
+            densityCompatHelper.density(context)
+        )
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+internal object WindowMetricsCompatHelperApi34Impl : WindowMetricsCompatHelper {
+
+    override fun translateWindowMetrics(
+        windowMetrics: AndroidWindowMetrics,
+        density: Float
+    ): WindowMetrics {
+        return WindowMetrics(
+            windowMetrics.bounds,
+            WindowInsetsCompat.toWindowInsetsCompat(windowMetrics.windowInsets),
+            windowMetrics.density
+        )
+    }
+
+    override fun currentWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        val wm = context.getSystemService(WindowManager::class.java)
+        return WindowMetrics(
+            wm.currentWindowMetrics.bounds,
+            WindowInsetsCompat.toWindowInsetsCompat(wm.currentWindowMetrics.windowInsets),
+            wm.currentWindowMetrics.density
+        )
+    }
+
+    override fun currentWindowMetrics(
+        activity: Activity,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        return WindowMetricsCompatHelperApi30Impl.currentWindowMetrics(
+            activity,
+            densityCompatHelper
+        )
+    }
+
+    override fun maximumWindowMetrics(
+        @UiContext context: Context,
+        densityCompatHelper: DensityCompatHelper
+    ): WindowMetrics {
+        return WindowMetricsCompatHelperApi30Impl.maximumWindowMetrics(context, densityCompatHelper)
+    }
+}
diff --git a/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt b/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt
index d437ab35..71bfe6a 100644
--- a/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt
+++ b/window/window/src/main/java/androidx/window/reflection/ReflectionUtils.kt
@@ -18,6 +18,7 @@
 
 import android.util.Log
 import java.lang.reflect.Constructor
+import java.lang.reflect.Field
 import java.lang.reflect.Method
 import java.lang.reflect.Modifier
 import kotlin.reflect.KClass
@@ -42,10 +43,10 @@
      * result from the [block]
      */
     @JvmStatic
-    internal fun validateReflection(errorMessage: String? = null, block: () -> Boolean): Boolean {
+    internal fun validateReflection(errorMessage: String, block: () -> Boolean): Boolean {
         return try {
             val result = block()
-            if (!result && errorMessage != null) {
+            if (!result) {
                 Log.e("ReflectionGuard", errorMessage)
             }
             result
@@ -55,6 +56,9 @@
         } catch (noMethod: NoSuchMethodException) {
             Log.e("ReflectionGuard", "NoSuchMethod: ${errorMessage.orEmpty()}")
             false
+        } catch (noField: NoSuchFieldException) {
+            Log.e("ReflectionGuard", "NoSuchField: ${errorMessage.orEmpty()}")
+            false
         }
     }
 
@@ -70,6 +74,12 @@
             return Modifier.isPublic(modifiers)
         }
 
+    /** Checks if a field has public modifier */
+    internal val Field.isPublic: Boolean
+        get() {
+            return Modifier.isPublic(modifiers)
+        }
+
     /** Checks if a method's return value is type of kotlin [clazz] */
     internal fun Method.doesReturn(clazz: KClass<*>): Boolean {
         return doesReturn(clazz.java)
diff --git a/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt b/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt
index dc2bfd8..1a72e56 100644
--- a/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt
+++ b/window/window/src/main/java/androidx/window/reflection/WindowExtensionsConstants.kt
@@ -31,19 +31,29 @@
     /** Constant name for class [androidx.window.extensions.WindowExtensions] used for reflection */
     internal const val WINDOW_EXTENSIONS_CLASS = "$WINDOW_EXTENSIONS_PACKAGE_NAME.WindowExtensions"
 
+    internal const val LAYOUT_PACKAGE = "layout"
+
     /**
      * Constant name for class [androidx.window.extensions.layout.FoldingFeature] used for
      * reflection
      */
     internal const val FOLDING_FEATURE_CLASS =
-        "$WINDOW_EXTENSIONS_PACKAGE_NAME.layout.FoldingFeature"
+        "$WINDOW_EXTENSIONS_PACKAGE_NAME.$LAYOUT_PACKAGE.FoldingFeature"
+
+    /** Constant name for class [androidx.window.extensions.layout.SupportedWindowFeatures] */
+    internal const val SUPPORTED_WINDOW_FEATURES_CLASS =
+        "$WINDOW_EXTENSIONS_PACKAGE_NAME.$LAYOUT_PACKAGE.SupportedWindowFeatures"
+
+    /** Constant name for class [androidx.window.extensions.layout.DisplayFoldFeature] */
+    internal const val DISPLAY_FOLD_FEATURE_CLASS =
+        "$WINDOW_EXTENSIONS_PACKAGE_NAME.$LAYOUT_PACKAGE.DisplayFoldFeature"
 
     /**
      * Constant name for class [androidx.window.extensions.layout.WindowLayoutComponent] used for
      * reflection
      */
     internal const val WINDOW_LAYOUT_COMPONENT_CLASS =
-        "$WINDOW_EXTENSIONS_PACKAGE_NAME.layout.WindowLayoutComponent"
+        "$WINDOW_EXTENSIONS_PACKAGE_NAME.$LAYOUT_PACKAGE.WindowLayoutComponent"
 
     /**
      * Constant name for class [androidx.window.extensions.area.WindowAreaComponent] used for
diff --git a/window/window/src/main/res/values/attrs.xml b/window/window/src/main/res/values/attrs.xml
index 5342612..a2880ca 100644
--- a/window/window/src/main/res/values/attrs.xml
+++ b/window/window/src/main/res/values/attrs.xml
@@ -43,7 +43,7 @@
          `ActivityRule`. The suggested usage is to set the tag to be able to differentiate between
          different rules in the callbacks.
          For example, it can be used to compute the right `SplitAttributes` for the given split rule
-         in `SplitAttributes` calculator function. -->
+         in `SplitAttributesCalculator.computeSplitAttributesForParams`. -->
     <attr name="tag" format="string" />
     <!-- An attribute for Activity Embedding rules.
          Background color of Activity Embedding window animation if the animation requires a
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityEmbeddingControllerTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityEmbeddingControllerTest.kt
index 6f671e9..6a414bf 100644
--- a/window/window/src/test/java/androidx/window/embedding/ActivityEmbeddingControllerTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityEmbeddingControllerTest.kt
@@ -18,19 +18,33 @@
 
 import android.app.Activity
 import android.content.Context
-import androidx.window.core.ExperimentalWindowApi
+import android.graphics.Rect
+import androidx.core.util.Consumer
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
 import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
 /** The unit tests for [ActivityEmbeddingController]. */
+@OptIn(ExperimentalCoroutinesApi::class)
 class ActivityEmbeddingControllerTest {
 
+    private val testScope = TestScope(UnconfinedTestDispatcher())
+
     private lateinit var mockEmbeddingBackend: EmbeddingBackend
     private lateinit var mockContext: Context
     private lateinit var mockActivity: Activity
@@ -58,11 +72,73 @@
     }
 
     @Test
-    @OptIn(ExperimentalWindowApi::class)
     fun testGetActivityStack() {
         val activityStack = ActivityStack(listOf(), true)
         whenever(mockEmbeddingBackend.getActivityStack(mockActivity)).thenReturn(activityStack)
 
         assertEquals(activityStack, activityEmbeddingController.getActivityStack(mockActivity))
     }
+
+    @Test
+    fun testFinishActivityStacks() {
+        val activityStacks: Set<ActivityStack> = mock()
+        activityEmbeddingController.finishActivityStacks(activityStacks)
+
+        verify(mockEmbeddingBackend).finishActivityStacks(activityStacks)
+    }
+
+    @Test
+    fun test_invalidateTopVisibleSplitAttributes_delegates() {
+        activityEmbeddingController.invalidateVisibleActivityStacks()
+        verify(mockEmbeddingBackend).invalidateVisibleActivityStacks()
+    }
+
+    @Test
+    fun test_embeddedActivityWindowInfo_delegates() =
+        testScope.runTest {
+            val expectedInfo =
+                EmbeddedActivityWindowInfo(
+                    isEmbedded = true,
+                    parentHostBounds = Rect(0, 0, 1000, 2000),
+                    boundsInParentHost = Rect(0, 0, 500, 2000)
+                )
+            doAnswer { invocationOnMock ->
+                    @Suppress("UNCHECKED_CAST")
+                    val callback =
+                        invocationOnMock.arguments.last() as Consumer<EmbeddedActivityWindowInfo>
+                    callback.accept(expectedInfo)
+                }
+                .whenever(mockEmbeddingBackend)
+                .addEmbeddedActivityWindowInfoCallbackForActivity(any(), any())
+
+            val actualInfo =
+                activityEmbeddingController
+                    .embeddedActivityWindowInfo(mockActivity)
+                    .take(1)
+                    .toList()
+                    .first()
+
+            assertEquals(expectedInfo, actualInfo)
+            verify(mockEmbeddingBackend)
+                .addEmbeddedActivityWindowInfoCallbackForActivity(eq(mockActivity), any())
+            verify(mockEmbeddingBackend).removeEmbeddedActivityWindowInfoCallbackForActivity(any())
+        }
+
+    @Test
+    fun testGetInstance() {
+        EmbeddingBackend.overrideDecorator(
+            object : EmbeddingBackendDecorator {
+                override fun decorate(embeddingBackend: EmbeddingBackend): EmbeddingBackend =
+                    mockEmbeddingBackend
+            }
+        )
+        val controller = ActivityEmbeddingController.getInstance(mockActivity)
+        val activityStacks: Set<ActivityStack> = mock()
+
+        controller.finishActivityStacks(activityStacks)
+
+        verify(mockEmbeddingBackend).finishActivityStacks(activityStacks)
+
+        EmbeddingBackend.reset()
+    }
 }
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityEmbeddingOptionsTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityEmbeddingOptionsTest.kt
new file mode 100644
index 0000000..53fca91
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityEmbeddingOptionsTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import androidx.window.extensions.embedding.ActivityStack.Token as ActivityStackToken
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/**
+ * The unit tests for activity embedding extension functions to [Bundle]
+ *
+ * @see Bundle.setLaunchingActivityStack
+ * @see Bundle.setOverlayCreateParams
+ */
+class ActivityEmbeddingOptionsTest {
+
+    @Mock private lateinit var mockEmbeddingBackend: EmbeddingBackend
+    @Mock private lateinit var mockContext: Context
+    @Mock private lateinit var mockActivity: Activity
+    @Mock private lateinit var mockOptions: Bundle
+
+    private lateinit var annotationClosable: AutoCloseable
+
+    private lateinit var mockActivityStack: ActivityStack
+
+    @Before
+    fun setUp() {
+        annotationClosable = MockitoAnnotations.openMocks(this)
+
+        mockActivityStack =
+            ActivityStack(listOf(), true, ActivityStackToken.INVALID_ACTIVITY_STACK_TOKEN)
+        whenever(mockActivity.applicationContext).doReturn(mockContext)
+
+        EmbeddingBackend.overrideDecorator(
+            object : EmbeddingBackendDecorator {
+                override fun decorate(embeddingBackend: EmbeddingBackend): EmbeddingBackend =
+                    mockEmbeddingBackend
+            }
+        )
+    }
+
+    @After
+    fun tearDown() {
+        EmbeddingBackend.reset()
+        annotationClosable.close()
+    }
+
+    @Test
+    fun testSetLaunchingActivityStack() {
+        mockOptions.setLaunchingActivityStack(mockActivity, mockActivityStack)
+
+        verify(mockEmbeddingBackend).setLaunchingActivityStack(mockOptions, mockActivityStack)
+    }
+
+    @Test
+    fun testSetOverlayCreateParams() {
+        val overlayCreateParams = OverlayCreateParams(overlayAttributes = OverlayAttributes())
+        mockOptions.setOverlayCreateParams(mockActivity, overlayCreateParams)
+
+        verify(mockEmbeddingBackend).setOverlayCreateParams(mockOptions, overlayCreateParams)
+    }
+}
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
index 7c50304..203e1ff 100644
--- a/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
@@ -17,8 +17,11 @@
 package androidx.window.embedding
 
 import android.app.Activity
+import android.os.Binder
+import androidx.window.extensions.embedding.ActivityStack.Token as ActivityStackToken
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.mockito.kotlin.mock
@@ -37,11 +40,18 @@
     @Test
     fun testEqualsImpliesHashCode() {
         val activity = mock<Activity>()
-        val first = ActivityStack(listOf(activity), isEmpty = false)
-        val second = ActivityStack(listOf(activity), isEmpty = false)
+        val token = ActivityStackToken.createFromBinder(Binder())
+        val first = ActivityStack(listOf(activity), isEmpty = false, token)
+        val second = ActivityStack(listOf(activity), isEmpty = false, token)
 
         assertEquals(first, second)
         assertEquals(first.hashCode(), second.hashCode())
+
+        val anotherToken = ActivityStackToken.createFromBinder(Binder())
+        val third = ActivityStack(emptyList(), isEmpty = true, anotherToken)
+
+        assertNotEquals(first, third)
+        assertNotEquals(first.hashCode(), third.hashCode())
     }
 
     @Test
@@ -59,10 +69,12 @@
     fun testToString() {
         val activitiesInProcess = mock<List<Activity>>()
         val isEmpty = false
+        val token = ActivityStackToken.INVALID_ACTIVITY_STACK_TOKEN
 
-        val stackString = ActivityStack(activitiesInProcess, isEmpty).toString()
+        val stackString = ActivityStack(activitiesInProcess, isEmpty, token).toString()
 
         assertTrue(stackString.contains(activitiesInProcess.toString()))
         assertTrue(stackString.contains(isEmpty.toString()))
+        assertTrue(stackString.contains(token.toString()))
     }
 }
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityWindowInfoCallbackControllerTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityWindowInfoCallbackControllerTest.kt
new file mode 100644
index 0000000..1b90cb5
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityWindowInfoCallbackControllerTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.app.Activity
+import androidx.core.util.Consumer as JetpackConsumer
+import androidx.window.WindowSdkExtensionsRule
+import androidx.window.extensions.core.util.function.Consumer
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import androidx.window.extensions.embedding.EmbeddedActivityWindowInfo as ExtensionsActivityWindowInfo
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/** Verifies [ActivityWindowInfoCallbackController] */
+@Suppress("GuardedBy")
+class ActivityWindowInfoCallbackControllerTest {
+
+    @get:Rule val testRule = WindowSdkExtensionsRule()
+
+    @Mock private lateinit var embeddingExtension: ActivityEmbeddingComponent
+    @Mock private lateinit var callback1: JetpackConsumer<EmbeddedActivityWindowInfo>
+    @Mock private lateinit var callback2: JetpackConsumer<EmbeddedActivityWindowInfo>
+    @Mock private lateinit var activity1: Activity
+    @Mock private lateinit var activity2: Activity
+
+    @Captor
+    private lateinit var callbackCaptor: ArgumentCaptor<Consumer<ExtensionsActivityWindowInfo>>
+
+    private lateinit var controller: ActivityWindowInfoCallbackController
+    private lateinit var mockAnnotations: AutoCloseable
+
+    @Before
+    fun setUp() {
+        testRule.overrideExtensionVersion(6)
+        mockAnnotations = MockitoAnnotations.openMocks(this)
+
+        controller = ActivityWindowInfoCallbackController(embeddingExtension)
+        // ArrayMap is not available in JVM test
+        controller.callbacks = HashMap()
+        controller = spy(controller)
+    }
+
+    @After
+    fun tearDown() {
+        mockAnnotations.close()
+    }
+
+    @Test
+    fun testAddCallback() {
+        // Register the extensions callback
+        controller.addCallback(activity1, callback1)
+
+        verify(embeddingExtension).setEmbeddedActivityWindowInfoCallback(any(), any())
+
+        // Do not register for the additional callback
+        clearInvocations(embeddingExtension)
+        controller.addCallback(activity1, callback2)
+
+        verify(embeddingExtension, never()).setEmbeddedActivityWindowInfoCallback(any(), any())
+    }
+
+    @Test
+    fun testRemoveCallback() {
+        // Unregister after the last Jetpack callback is removed.
+        controller.addCallback(activity1, callback1)
+        controller.addCallback(activity1, callback2)
+
+        controller.removeCallback(callback1)
+
+        verify(embeddingExtension, never()).clearEmbeddedActivityWindowInfoCallback()
+
+        controller.removeCallback(callback2)
+
+        verify(embeddingExtension).clearEmbeddedActivityWindowInfoCallback()
+    }
+
+    @Test
+    fun testActivityWindowInfoChanged() {
+        controller.addCallback(activity1, callback1)
+        controller.addCallback(activity1, callback2)
+
+        verify(embeddingExtension)
+            .setEmbeddedActivityWindowInfoCallback(any(), callbackCaptor.capture())
+        val extensionsCallback = callbackCaptor.value
+
+        val extensionsInfo: ExtensionsActivityWindowInfo = mock()
+        val expectedInfo =
+            EmbeddedActivityWindowInfo(
+                isEmbedded = true,
+                parentHostBounds = mock(),
+                boundsInParentHost = mock()
+            )
+        doReturn(expectedInfo).whenever(controller).translate(extensionsInfo)
+
+        // No callback because the activity doesn't match.
+        doReturn(activity2).whenever(extensionsInfo).activity
+        extensionsCallback.accept(extensionsInfo)
+
+        verify(callback1, never()).accept(any())
+        verify(callback2, never()).accept(any())
+
+        // Should receive the correct translated info.
+        doReturn(activity1).whenever(extensionsInfo).activity
+        extensionsCallback.accept(extensionsInfo)
+
+        verify(callback1).accept(expectedInfo)
+        verify(callback2).accept(expectedInfo)
+
+        // Do not send unchanged info.
+        clearInvocations(callback1)
+        clearInvocations(callback2)
+        extensionsCallback.accept(extensionsInfo)
+
+        verify(callback1, never()).accept(any())
+        verify(callback2, never()).accept(any())
+
+        // Only the remaining callback can receive the info.
+        controller.removeCallback(callback1)
+        val expectedInfo2 =
+            EmbeddedActivityWindowInfo(
+                isEmbedded = false,
+                parentHostBounds = mock(),
+                boundsInParentHost = mock()
+            )
+        doReturn(expectedInfo2).whenever(controller).translate(extensionsInfo)
+        extensionsCallback.accept(extensionsInfo)
+
+        verify(callback1, never()).accept(any())
+        verify(callback2).accept(any())
+    }
+}
diff --git a/window/window/src/test/java/androidx/window/embedding/DividerAttributesTest.kt b/window/window/src/test/java/androidx/window/embedding/DividerAttributesTest.kt
new file mode 100644
index 0000000..dbca076
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/DividerAttributesTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.graphics.Color
+import androidx.window.embedding.DividerAttributes.DragRange.Companion.DRAG_RANGE_SYSTEM_DEFAULT
+import androidx.window.embedding.DividerAttributes.DragRange.SplitRatioDragRange
+import androidx.window.embedding.DividerAttributes.DraggableDividerAttributes
+import androidx.window.embedding.DividerAttributes.FixedDividerAttributes
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertThrows
+import org.junit.Test
+
+/** Test class to verify [DividerAttributes] */
+class DividerAttributesTest {
+
+    @Test
+    fun testDividerAttributesEquals() {
+        val attrs1 = FixedDividerAttributes.Builder().build()
+        val attrs2 = DraggableDividerAttributes.Builder().build()
+        val attrs3 =
+            DraggableDividerAttributes.Builder()
+                .setWidthDp(20)
+                .setDragRange(SplitRatioDragRange(0.3f, 0.7f))
+                .build()
+        val attrs4 =
+            DraggableDividerAttributes.Builder()
+                .setWidthDp(DividerAttributes.WIDTH_SYSTEM_DEFAULT)
+                .setDragRange(DRAG_RANGE_SYSTEM_DEFAULT)
+                .build()
+
+        assertNotEquals(attrs1, attrs2)
+        assertNotEquals(attrs1.hashCode(), attrs2.hashCode())
+
+        assertNotEquals(attrs2, attrs3)
+        assertNotEquals(attrs2.hashCode(), attrs3.hashCode())
+
+        assertNotEquals(attrs3, attrs4)
+        assertNotEquals(attrs3.hashCode(), attrs4.hashCode())
+
+        // attrs2 and attrs4 must be equal because attrs4 uses default values.
+        assertEquals(attrs2, attrs4)
+        assertEquals(attrs2.hashCode(), attrs4.hashCode())
+    }
+
+    @Test
+    fun testBuilder() {
+        val attrs =
+            DraggableDividerAttributes.Builder()
+                .setWidthDp(20)
+                .setDragRange(SplitRatioDragRange(0.3f, 0.7f))
+                .setColor(Color.GRAY)
+                .build()
+
+        assertEquals(20, attrs.widthDp)
+        assertEquals(SplitRatioDragRange(0.3f, 0.7f), attrs.dragRange)
+        assertEquals(Color.GRAY, attrs.color)
+    }
+
+    @Test
+    fun testBuilder_defaultValues() {
+        val attrs = DraggableDividerAttributes.Builder().build()
+
+        assertEquals(DividerAttributes.WIDTH_SYSTEM_DEFAULT, attrs.widthDp)
+        assertEquals(DRAG_RANGE_SYSTEM_DEFAULT, attrs.dragRange)
+        assertEquals(Color.BLACK, attrs.color)
+    }
+
+    @Test
+    fun testSplitRatioDragRange_validation() {
+        // Valid range
+        SplitRatioDragRange(minRatio = 0.2f, maxRatio = 0.8f)
+
+        // Invalid minRatio and maxRatio
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitRatioDragRange(minRatio = 0.0f, maxRatio = 1.0f)
+        }
+
+        // Invalid minRatio
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitRatioDragRange(minRatio = -1.0f, maxRatio = 0.8f)
+        }
+
+        // Invalid maxRatio
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitRatioDragRange(minRatio = 0.2f, maxRatio = 1.2f)
+        }
+
+        // minRatio should not be less than maxRatio
+        assertThrows(IllegalArgumentException::class.java) {
+            SplitRatioDragRange(minRatio = 0.6f, maxRatio = 0.4f)
+        }
+    }
+}
diff --git a/window/window/src/test/java/androidx/window/embedding/EmbeddingBoundsTests.kt b/window/window/src/test/java/androidx/window/embedding/EmbeddingBoundsTests.kt
new file mode 100644
index 0000000..d5df40d
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/EmbeddingBoundsTests.kt
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.graphics.Rect
+import androidx.window.core.Bounds
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowLayoutInfo
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/** Unit tests for [EmbeddingBounds] */
+class EmbeddingBoundsTests {
+
+    private val taskBounds = Bounds(0, 0, 10, 10)
+
+    private val layoutInfoWithoutHinge = WindowLayoutInfo(emptyList())
+
+    private val layoutInfoWithTwoHinges =
+        WindowLayoutInfo(
+            listOf(
+                TestFoldingFeature(Bounds(left = 4, top = 0, right = 6, bottom = 10)),
+                TestFoldingFeature(Bounds(left = 0, top = 4, right = 10, bottom = 6)),
+            )
+        )
+
+    private val layoutInfoWithVerticalHinge =
+        WindowLayoutInfo(
+            listOf(TestFoldingFeature(Bounds(left = 4, top = 0, right = 6, bottom = 10)))
+        )
+
+    private val layoutInfoWithHorizontalHinge =
+        WindowLayoutInfo(
+            listOf(TestFoldingFeature(Bounds(left = 0, top = 4, right = 10, bottom = 6)))
+        )
+
+    @Test
+    fun testTranslateBOUNDS_EXPANDED_returnEmptyBounds() {
+        assertThat(
+                EmbeddingBounds.translateEmbeddingBounds(
+                        EmbeddingBounds.BOUNDS_EXPANDED,
+                        taskBounds,
+                        layoutInfoWithVerticalHinge
+                    )
+                    .isZero
+            )
+            .isTrue()
+    }
+
+    @Test
+    fun testTranslateBoundsMatchParentTask_returnEmptyBounds() {
+        assertThat(
+                EmbeddingBounds.translateEmbeddingBounds(
+                        EmbeddingBounds(
+                            EmbeddingBounds.Alignment.ALIGN_TOP,
+                            width = EmbeddingBounds.Dimension.pixel(taskBounds.width),
+                            height = EmbeddingBounds.Dimension.pixel(taskBounds.height),
+                        ),
+                        taskBounds,
+                        layoutInfoWithVerticalHinge
+                    )
+                    .isZero
+            )
+            .isTrue()
+    }
+
+    @Test
+    fun testTranslateBoundsLargerThanParentTask_returnEmptyBounds() {
+        assertThat(
+                EmbeddingBounds.translateEmbeddingBounds(
+                        EmbeddingBounds(
+                            EmbeddingBounds.Alignment.ALIGN_TOP,
+                            width = EmbeddingBounds.Dimension.pixel(taskBounds.width + 1),
+                            height = EmbeddingBounds.Dimension.pixel(taskBounds.height + 1),
+                        ),
+                        taskBounds,
+                        layoutInfoWithVerticalHinge
+                    )
+                    .isZero
+            )
+            .isTrue()
+    }
+
+    @Test
+    fun testTranslateBOUNDS_HINGE_LEFT() {
+        val fallbackBoundsHingeLeft = Bounds(left = 0, top = 0, right = 5, bottom = 10)
+
+        assertWithMessage("Must fallback to the left half on device without hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_LEFT,
+                    taskBounds,
+                    layoutInfoWithoutHinge,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeLeft)
+
+        assertWithMessage("Must fallback to the left half on device with multiple hinges")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_LEFT,
+                    taskBounds,
+                    layoutInfoWithTwoHinges,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeLeft)
+
+        assertWithMessage("Must fallback to the left half on device with a horizontal hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_LEFT,
+                    taskBounds,
+                    layoutInfoWithHorizontalHinge,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeLeft)
+
+        assertWithMessage("Must report bounds on the left of hinge on device with a vertical hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_LEFT,
+                    taskBounds,
+                    layoutInfoWithVerticalHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 0, top = 0, right = 4, bottom = 10))
+    }
+
+    @Test
+    fun testTranslateBOUNDS_HINGE_TOP() {
+        val fallbackBoundsHingeTop = Bounds(left = 0, top = 0, right = 10, bottom = 5)
+
+        assertWithMessage("Must fallback to the top half on device without hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_TOP,
+                    taskBounds,
+                    layoutInfoWithoutHinge
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeTop)
+
+        assertWithMessage("Must fallback to the top half on device with multiple hinges")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_TOP,
+                    taskBounds,
+                    layoutInfoWithTwoHinges,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeTop)
+
+        assertWithMessage("Must fallback to the top half on device with a vertical hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_TOP,
+                    taskBounds,
+                    layoutInfoWithVerticalHinge,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeTop)
+
+        assertWithMessage(
+                "Must report bounds on the top of hinge on device with a horizontal hinge"
+            )
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_TOP,
+                    taskBounds,
+                    layoutInfoWithHorizontalHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 0, top = 0, right = 10, bottom = 4))
+    }
+
+    @Test
+    fun testTranslateBOUNDS_HINGE_RIGHT() {
+        val fallbackBoundsHingeRight = Bounds(left = 5, top = 0, right = 10, bottom = 10)
+
+        assertWithMessage("Must fallback to the right half on device without hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_RIGHT,
+                    taskBounds,
+                    layoutInfoWithoutHinge,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeRight)
+
+        assertWithMessage("Must fallback to the right half on device with multiple hinges")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_RIGHT,
+                    taskBounds,
+                    layoutInfoWithTwoHinges,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeRight)
+
+        assertWithMessage("Must fallback to the right half on device with a horizontal hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_RIGHT,
+                    taskBounds,
+                    layoutInfoWithHorizontalHinge,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeRight)
+
+        assertWithMessage(
+                "Must report bounds on the right of hinge on device with a vertical hinge"
+            )
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_RIGHT,
+                    taskBounds,
+                    layoutInfoWithVerticalHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 6, top = 0, right = 10, bottom = 10))
+    }
+
+    @Test
+    fun testTranslateBOUNDS_HINGE_BOTTOM() {
+        val fallbackBoundsHingeBottom = Bounds(left = 0, top = 5, right = 10, bottom = 10)
+
+        assertWithMessage("Must fallback to the bottom half on device without hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_BOTTOM,
+                    taskBounds,
+                    layoutInfoWithoutHinge,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeBottom)
+
+        assertWithMessage("Must fallback to the bottom half on device with multiple hinges")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_BOTTOM,
+                    taskBounds,
+                    layoutInfoWithTwoHinges,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeBottom)
+
+        assertWithMessage("Must fallback to the bottom half on device with a vertical hinge")
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_BOTTOM,
+                    taskBounds,
+                    layoutInfoWithVerticalHinge,
+                )
+            )
+            .isEqualTo(fallbackBoundsHingeBottom)
+
+        assertWithMessage(
+                "Must report bounds on the bottom of hinge on device with a horizontal hinge"
+            )
+            .that(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds.BOUNDS_HINGE_BOTTOM,
+                    taskBounds,
+                    layoutInfoWithHorizontalHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 0, top = 6, right = 10, bottom = 10))
+    }
+
+    @Test
+    fun testTranslateShrunkLeftBounds() {
+        assertThat(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds(
+                        EmbeddingBounds.Alignment.ALIGN_LEFT,
+                        EmbeddingBounds.Dimension.ratio(0.7f),
+                        EmbeddingBounds.Dimension.pixel(8),
+                    ),
+                    taskBounds,
+                    layoutInfoWithoutHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 0, top = 1, right = 7, bottom = 9))
+    }
+
+    @Test
+    fun testTranslateShrunkTopBounds() {
+        assertThat(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds(
+                        EmbeddingBounds.Alignment.ALIGN_TOP,
+                        EmbeddingBounds.Dimension.pixel(8),
+                        EmbeddingBounds.Dimension.ratio(0.5f),
+                    ),
+                    taskBounds,
+                    layoutInfoWithoutHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 1, top = 0, right = 9, bottom = 5))
+    }
+
+    @Test
+    fun testTranslateShrunkBottomBounds() {
+        assertThat(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds(
+                        EmbeddingBounds.Alignment.ALIGN_BOTTOM,
+                        EmbeddingBounds.Dimension.DIMENSION_HINGE,
+                        EmbeddingBounds.Dimension.DIMENSION_EXPANDED,
+                    ),
+                    taskBounds,
+                    layoutInfoWithoutHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 2, top = 0, right = 7, bottom = 10))
+    }
+
+    @Test
+    fun testTranslateShrunkRightBounds() {
+        assertThat(
+                EmbeddingBounds.translateEmbeddingBounds(
+                    EmbeddingBounds(
+                        EmbeddingBounds.Alignment.ALIGN_RIGHT,
+                        EmbeddingBounds.Dimension.DIMENSION_HINGE,
+                        EmbeddingBounds.Dimension.pixel(4),
+                    ),
+                    taskBounds,
+                    layoutInfoWithVerticalHinge,
+                )
+            )
+            .isEqualTo(Bounds(left = 6, top = 3, right = 10, bottom = 7))
+    }
+
+    private class TestFoldingFeature(val rawBounds: Bounds) : FoldingFeature {
+        override val bounds: Rect
+            get() =
+                mock<Rect>().apply {
+                    left = rawBounds.left
+                    top = rawBounds.top
+                    right = rawBounds.right
+                    bottom = rawBounds.bottom
+                    doReturn(rawBounds.width).whenever(this).width()
+                    doReturn(rawBounds.height).whenever(this).height()
+                }
+
+        override val isSeparating: Boolean
+            get() = true
+
+        override val occlusionType: FoldingFeature.OcclusionType
+            get() = FoldingFeature.OcclusionType.FULL
+
+        override val orientation: FoldingFeature.Orientation
+            get() =
+                if (rawBounds.width > rawBounds.height) {
+                    FoldingFeature.Orientation.HORIZONTAL
+                } else {
+                    FoldingFeature.Orientation.VERTICAL
+                }
+
+        override val state: FoldingFeature.State
+            get() = FoldingFeature.State.FLAT
+    }
+}
diff --git a/window/window/src/test/java/androidx/window/embedding/EmbeddingCompatTest.kt b/window/window/src/test/java/androidx/window/embedding/EmbeddingCompatTest.kt
index 03df207..4f88743 100644
--- a/window/window/src/test/java/androidx/window/embedding/EmbeddingCompatTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/EmbeddingCompatTest.kt
@@ -17,12 +17,14 @@
 package androidx.window.embedding
 
 import android.app.Activity
+import androidx.window.WindowSdkExtensions
 import androidx.window.core.ConsumerAdapter
-import androidx.window.core.ExtensionsUtil
 import androidx.window.core.PredicateAdapter
 import androidx.window.extensions.core.util.function.Consumer
 import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import androidx.window.extensions.embedding.ActivityStack as OEMActivityStack
 import androidx.window.extensions.embedding.SplitInfo as OEMSplitInfo
+import java.util.concurrent.Executor
 import java.util.function.Consumer as JavaConsumer
 import org.junit.Test
 import org.mockito.kotlin.any
@@ -32,9 +34,16 @@
 class EmbeddingCompatTest {
 
     private val component = mock<ActivityEmbeddingComponent>()
-    private val vendorApiLevel = ExtensionsUtil.safeVendorApiLevel
+    private val extensionVersion = WindowSdkExtensions.getInstance().extensionVersion
     private val embeddingCompat =
-        EmbeddingCompat(component, EMBEDDING_ADAPTER, CONSUMER_ADAPTER, mock())
+        EmbeddingCompat(
+            component,
+            EMBEDDING_ADAPTER,
+            CONSUMER_ADAPTER,
+            mock(),
+            mock(),
+            mock(),
+        )
 
     @Suppress("Deprecation")
     @Test
@@ -42,13 +51,22 @@
         val callback =
             object : EmbeddingInterfaceCompat.EmbeddingCallbackInterface {
                 override fun onSplitInfoChanged(splitInfo: List<SplitInfo>) {}
+
+                override fun onActivityStackChanged(activityStacks: List<ActivityStack>) {}
             }
         embeddingCompat.setEmbeddingCallback(callback)
 
-        if (vendorApiLevel < 2) {
-            verify(component).setSplitInfoCallback(any<JavaConsumer<List<OEMSplitInfo>>>())
-        } else {
-            verify(component).setSplitInfoCallback(any<Consumer<List<OEMSplitInfo>>>())
+        when (extensionVersion) {
+            1 -> verify(component).setSplitInfoCallback(any<JavaConsumer<List<OEMSplitInfo>>>())
+            in 2..4 -> verify(component).setSplitInfoCallback(any<Consumer<List<OEMSplitInfo>>>())
+            5 -> {
+                verify(component).setSplitInfoCallback(any<Consumer<List<OEMSplitInfo>>>())
+                verify(component)
+                    .registerActivityStackCallback(
+                        any<Executor>(),
+                        any<Consumer<List<OEMActivityStack>>>()
+                    )
+            }
         }
     }
 
diff --git a/window/window/src/test/java/androidx/window/embedding/OverlayControllerImplTest.kt b/window/window/src/test/java/androidx/window/embedding/OverlayControllerImplTest.kt
new file mode 100644
index 0000000..aa2bb20
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/OverlayControllerImplTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.annotation.SuppressLint
+import android.content.res.Configuration
+import android.graphics.Rect
+import androidx.core.view.WindowInsetsCompat
+import androidx.window.WindowSdkExtensionsRule
+import androidx.window.core.PredicateAdapter
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.WindowMetrics
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.kotlin.mock
+
+/** Verifies [OverlayControllerImpl] */
+@Suppress("GuardedBy")
+class OverlayControllerImplTest {
+
+    @get:Rule val testRule = WindowSdkExtensionsRule()
+
+    private lateinit var overlayController: TestableOverlayControllerImpl
+
+    @Before
+    fun setUp() {
+        testRule.overrideExtensionVersion(OVERLAY_FEATURE_VERSION)
+
+        overlayController = TestableOverlayControllerImpl()
+    }
+
+    /** Verifies the behavior of [OverlayControllerImpl.calculateOverlayAttributes] */
+    @SuppressLint("NewApi")
+    @Test
+    fun testCalculateOverlayAttributes() {
+        assertThat(overlayController.calculateOverlayAttributes(DEFAULT_OVERLAY_ATTRS))
+            .isEqualTo(DEFAULT_OVERLAY_ATTRS)
+
+        overlayController.overlayAttributesCalculator = { _ -> CALCULATED_OVERLAY_ATTRS }
+
+        assertWithMessage("Calculated overlay attrs must be reported if calculator exists.")
+            .that(overlayController.calculateOverlayAttributes(DEFAULT_OVERLAY_ATTRS))
+            .isEqualTo(CALCULATED_OVERLAY_ATTRS)
+
+        overlayController.updateOverlayAttributes(TAG_TEST, UPDATED_OVERLAY_ATTRS)
+
+        assertWithMessage("Calculated overlay attrs must be reported if calculator exists.")
+            .that(overlayController.calculateOverlayAttributes(DEFAULT_OVERLAY_ATTRS))
+            .isEqualTo(CALCULATED_OVERLAY_ATTRS)
+
+        overlayController.overlayAttributesCalculator = null
+
+        assertWithMessage("#updateOverlayAttributes should also update the current overlay attrs.")
+            .that(overlayController.calculateOverlayAttributes(DEFAULT_OVERLAY_ATTRS))
+            .isEqualTo(UPDATED_OVERLAY_ATTRS)
+    }
+
+    private fun OverlayControllerImpl.calculateOverlayAttributes(
+        initialOverlayAttrs: OverlayAttributes?
+    ): OverlayAttributes =
+        calculateOverlayAttributes(
+            TAG_TEST,
+            initialOverlayAttrs,
+            WindowMetrics(Rect(), WindowInsetsCompat.CONSUMED, density = 1f),
+            Configuration(),
+            WindowLayoutInfo(emptyList())
+        )
+
+    companion object {
+        private const val TAG_TEST = "test"
+
+        private val DEFAULT_OVERLAY_ATTRS = OverlayAttributes.Builder().build()
+
+        private val CALCULATED_OVERLAY_ATTRS =
+            OverlayAttributes.Builder().setBounds(EmbeddingBounds.BOUNDS_HINGE_RIGHT).build()
+
+        private val UPDATED_OVERLAY_ATTRS =
+            OverlayAttributes.Builder().setBounds(EmbeddingBounds.BOUNDS_HINGE_BOTTOM).build()
+    }
+
+    private class TestableOverlayControllerImpl(
+        mockExtension: ActivityEmbeddingComponent = mock<ActivityEmbeddingComponent>(),
+    ) :
+        OverlayControllerImpl(
+            mockExtension,
+            EmbeddingAdapter(PredicateAdapter(ClassLoader.getSystemClassLoader()))
+        ) {
+        val overlayTagToAttributesMap = HashMap<String, OverlayAttributes>()
+
+        override fun getUpdatedOverlayAttributes(overlayTag: String): OverlayAttributes? =
+            overlayTagToAttributesMap[overlayTag]
+
+        override fun updateOverlayAttributes(
+            overlayTag: String,
+            overlayAttributes: OverlayAttributes
+        ) {
+            overlayTagToAttributesMap[overlayTag] = overlayAttributes
+        }
+    }
+}
diff --git a/window/window/src/test/java/androidx/window/embedding/OverlayControllerTest.kt b/window/window/src/test/java/androidx/window/embedding/OverlayControllerTest.kt
new file mode 100644
index 0000000..c20b535
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/OverlayControllerTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.embedding
+
+import android.os.Bundle
+import androidx.core.util.Consumer
+import androidx.window.extensions.embedding.ActivityStack.Token as ActivityStackToken
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert
+import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+internal class OverlayControllerTest {
+    private val mockBackend = mock<EmbeddingBackend>()
+    private val overlayController = OverlayController(mockBackend)
+    private val testScope = TestScope(UnconfinedTestDispatcher())
+
+    @Test
+    fun testSetOverlayCreateParams() {
+        val options = Bundle()
+        val params = OverlayCreateParams()
+        overlayController.setOverlayCreateParams(options, params)
+
+        verify(mockBackend).setOverlayCreateParams(options, params)
+    }
+
+    @Test
+    fun test_overlayAttributesCalculator_delegates() {
+        val calculator = { _: OverlayAttributesCalculatorParams -> OverlayAttributes() }
+
+        overlayController.setOverlayAttributesCalculator(calculator)
+
+        verify(mockBackend).setOverlayAttributesCalculator(calculator)
+
+        overlayController.clearOverlayAttributesCalculator()
+
+        verify(mockBackend).clearOverlayAttributesCalculator()
+    }
+
+    @Test
+    fun test_updateOverlayAttributes_delegates() {
+        val tag = "test"
+        val overlayAttributes = OverlayAttributes()
+
+        overlayController.updateOverlayAttributes(tag, overlayAttributes)
+
+        verify(mockBackend).updateOverlayAttributes(tag, overlayAttributes)
+    }
+
+    @Test
+    fun test_overlayInfoComesFromBackend() =
+        testScope.runTest {
+            val tag = "test"
+            val expected =
+                OverlayInfo(
+                    overlayTag = tag,
+                    currentOverlayAttributes = OverlayAttributes(),
+                    activityStack =
+                        ActivityStack(
+                            emptyList(),
+                            true,
+                            ActivityStackToken.INVALID_ACTIVITY_STACK_TOKEN
+                        )
+                )
+            doAnswer { invocationOnMock ->
+                    @Suppress("UNCHECKED_CAST")
+                    val listener = invocationOnMock.arguments.last() as Consumer<OverlayInfo>
+                    listener.accept(expected)
+                }
+                .whenever(mockBackend)
+                .addOverlayInfoCallback(any(), any(), any())
+
+            val actual = overlayController.overlayInfo(tag).take(1).toList().first()
+
+            Assert.assertEquals(expected, actual)
+            verify(mockBackend).addOverlayInfoCallback(eq(tag), any(), any())
+            verify(mockBackend).removeOverlayInfoCallback(any())
+        }
+}
diff --git a/window/window/src/test/java/androidx/window/embedding/RequiresWindowSdkExtensionTests.kt b/window/window/src/test/java/androidx/window/embedding/RequiresWindowSdkExtensionTests.kt
index 3cd660b..8c3d4de 100644
--- a/window/window/src/test/java/androidx/window/embedding/RequiresWindowSdkExtensionTests.kt
+++ b/window/window/src/test/java/androidx/window/embedding/RequiresWindowSdkExtensionTests.kt
@@ -16,24 +16,27 @@
 
 package androidx.window.embedding
 
-import android.app.ActivityOptions
 import android.content.Context
-import android.os.Binder
-import android.os.Build
+import android.os.Bundle
 import android.os.IBinder
-import androidx.annotation.RequiresApi
 import androidx.window.RequiresWindowSdkExtension
 import androidx.window.WindowSdkExtensions
 import androidx.window.WindowSdkExtensionsRule
 import androidx.window.core.ConsumerAdapter
 import androidx.window.core.PredicateAdapter
-import androidx.window.embedding.EmbeddingAdapter.Companion.INVALID_ACTIVITY_STACK_TOKEN
 import androidx.window.embedding.EmbeddingAdapter.Companion.INVALID_SPLIT_INFO_TOKEN
+import androidx.window.embedding.OverlayController.Companion.OVERLAY_FEATURE_VERSION
 import androidx.window.extensions.core.util.function.Function
 import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties
+import androidx.window.extensions.embedding.ActivityStack.Token as ActivityStackToken
 import androidx.window.extensions.embedding.SplitAttributes as OemSplitAttributes
 import androidx.window.extensions.embedding.SplitAttributesCalculatorParams as OemSplitAttributesCalculatorParams
+import androidx.window.extensions.embedding.SplitInfo.Token as SplitInfoToken
+import java.util.concurrent.Executor
 import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Rule
@@ -41,8 +44,11 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.any
-import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
@@ -53,7 +59,7 @@
  *   successfully
  * - Otherwise, [UnsupportedOperationException] must be thrown.
  */
-@RequiresApi(Build.VERSION_CODES.M) // To call ActivityOptions.makeBasic()
+@Suppress("Deprecation")
 class RequiresWindowSdkExtensionTests {
 
     @get:Rule val testRule = WindowSdkExtensionsRule()
@@ -61,26 +67,18 @@
     @Mock private lateinit var embeddingExtension: ActivityEmbeddingComponent
     @Mock private lateinit var classLoader: ClassLoader
     @Mock private lateinit var applicationContext: Context
-    @Mock private lateinit var activityOptions: ActivityOptions
+    @Mock private lateinit var options: Bundle
 
     private lateinit var mockAnnotations: AutoCloseable
     private lateinit var embeddingCompat: EmbeddingCompat
+    private lateinit var activityStack: ActivityStack
 
-    @Suppress("DEPRECATION")
+    private val activityStackToken = ActivityStackToken.INVALID_ACTIVITY_STACK_TOKEN
+
     @Before
     fun setUp() {
         mockAnnotations = MockitoAnnotations.openMocks(this)
-        embeddingCompat =
-            EmbeddingCompat(
-                embeddingExtension,
-                EmbeddingAdapter(PredicateAdapter(classLoader)),
-                ConsumerAdapter(classLoader),
-                applicationContext
-            )
-
-        doReturn(activityOptions)
-            .whenever(embeddingExtension)
-            .setLaunchingActivityStack(activityOptions, INVALID_ACTIVITY_STACK_TOKEN)
+        activityStack = ActivityStack(emptyList(), isEmpty = true, activityStackToken)
     }
 
     @After
@@ -88,10 +86,10 @@
         mockAnnotations.close()
     }
 
-    @Suppress("DEPRECATION")
     @Test
-    fun testVendorApiLevel1() {
+    fun testWindowExtensionsVersion1() {
         testRule.overrideExtensionVersion(1)
+        createTestEmbeddingCompat()
 
         assertThrows(UnsupportedOperationException::class.java) {
             embeddingCompat.setSplitAttributesCalculator { _ -> TEST_SPLIT_ATTRIBUTES }
@@ -104,26 +102,34 @@
         verify(embeddingExtension, never()).clearSplitAttributesCalculator()
 
         assertThrows(UnsupportedOperationException::class.java) {
-            embeddingCompat.setLaunchingActivityStack(activityOptions, Binder())
+            embeddingCompat.setLaunchingActivityStack(options, activityStack)
         }
-        verify(embeddingExtension, never()).setLaunchingActivityStack(any(), any())
+        verify(options, never()).putBinder(any(), any())
+
+        assertThrows(UnsupportedOperationException::class.java) {
+            embeddingCompat.finishActivityStacks(emptySet())
+        }
+        verify(embeddingExtension, never())
+            .finishActivityStacksWithTokens(any<Set<ActivityStackToken>>())
 
         assertThrows(UnsupportedOperationException::class.java) {
             embeddingCompat.updateSplitAttributes(TEST_SPLIT_INFO, TEST_SPLIT_ATTRIBUTES)
         }
-        verify(embeddingExtension, never())
-            .updateSplitAttributes(any<IBinder>(), any<OemSplitAttributes>())
+        verify(embeddingExtension, never()).updateSplitAttributes(any<IBinder>(), any())
 
         assertThrows(UnsupportedOperationException::class.java) {
-            embeddingCompat.invalidateTopVisibleSplitAttributes()
+            embeddingCompat.invalidateVisibleActivityStacks()
         }
         verify(embeddingExtension, never()).invalidateTopVisibleSplitAttributes()
+
+        verifyOverlayFeatureApis()
+        verifyActivityWindowInfoCallbackController()
     }
 
-    @Suppress("DEPRECATION")
     @Test
-    fun testVendorApiLevel2() {
+    fun testWindowExtensionsVersion2() {
         testRule.overrideExtensionVersion(2)
+        createTestEmbeddingCompat()
 
         embeddingCompat.setSplitAttributesCalculator { _ -> TEST_SPLIT_ATTRIBUTES }
         verify(embeddingExtension)
@@ -135,26 +141,42 @@
         verify(embeddingExtension).clearSplitAttributesCalculator()
 
         assertThrows(UnsupportedOperationException::class.java) {
-            embeddingCompat.setLaunchingActivityStack(activityOptions, INVALID_ACTIVITY_STACK_TOKEN)
+            embeddingCompat.setLaunchingActivityStack(options, activityStack)
         }
-        verify(embeddingExtension, never()).setLaunchingActivityStack(any(), any())
+        verify(options, never()).putBundle(any(), any())
+
+        assertThrows(UnsupportedOperationException::class.java) {
+            embeddingCompat.finishActivityStacks(emptySet())
+        }
+        verify(embeddingExtension, never())
+            .finishActivityStacksWithTokens(any<Set<ActivityStackToken>>())
 
         assertThrows(UnsupportedOperationException::class.java) {
             embeddingCompat.updateSplitAttributes(TEST_SPLIT_INFO, TEST_SPLIT_ATTRIBUTES)
         }
-        verify(embeddingExtension, never())
-            .updateSplitAttributes(any<IBinder>(), any<OemSplitAttributes>())
+        verify(embeddingExtension, never()).updateSplitAttributes(any<IBinder>(), any())
 
         assertThrows(UnsupportedOperationException::class.java) {
-            embeddingCompat.invalidateTopVisibleSplitAttributes()
+            embeddingCompat.invalidateVisibleActivityStacks()
         }
         verify(embeddingExtension, never()).invalidateTopVisibleSplitAttributes()
+
+        verifyOverlayFeatureApis()
+        verifyActivityWindowInfoCallbackController()
     }
 
-    @Suppress("DEPRECATION")
     @Test
-    fun testVendorApiLevel3() {
+    fun testWindowExtensionsVersion3() {
         testRule.overrideExtensionVersion(3)
+        createTestEmbeddingCompat()
+
+        val splitInfo =
+            SplitInfo(
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                SplitAttributes.Builder().build(),
+                binder = INVALID_SPLIT_INFO_TOKEN,
+            )
 
         embeddingCompat.setSplitAttributesCalculator { _ -> TEST_SPLIT_ATTRIBUTES }
         verify(embeddingExtension)
@@ -165,17 +187,228 @@
         embeddingCompat.clearSplitAttributesCalculator()
         verify(embeddingExtension).clearSplitAttributesCalculator()
 
-        embeddingCompat.setLaunchingActivityStack(activityOptions, INVALID_ACTIVITY_STACK_TOKEN)
+        assertThrows(UnsupportedOperationException::class.java) {
+            embeddingCompat.setLaunchingActivityStack(options, activityStack)
+        }
+        verify(options, never()).putBundle(any(), any())
 
+        assertThrows(UnsupportedOperationException::class.java) {
+            embeddingCompat.finishActivityStacks(emptySet())
+        }
+        verify(embeddingExtension, never())
+            .finishActivityStacksWithTokens(any<Set<ActivityStackToken>>())
+
+        embeddingCompat.updateSplitAttributes(splitInfo, TEST_SPLIT_ATTRIBUTES)
         verify(embeddingExtension)
-            .setLaunchingActivityStack(activityOptions, INVALID_ACTIVITY_STACK_TOKEN)
+            .updateSplitAttributes(splitInfo.getBinder(), OemSplitAttributes.Builder().build())
 
-        embeddingCompat.updateSplitAttributes(TEST_SPLIT_INFO, TEST_SPLIT_ATTRIBUTES)
-        verify(embeddingExtension)
-            .updateSplitAttributes(INVALID_SPLIT_INFO_TOKEN, OemSplitAttributes.Builder().build())
-
-        embeddingCompat.invalidateTopVisibleSplitAttributes()
+        embeddingCompat.invalidateVisibleActivityStacks()
         verify(embeddingExtension).invalidateTopVisibleSplitAttributes()
+
+        verifyOverlayFeatureApis()
+        verifyActivityWindowInfoCallbackController()
+    }
+
+    @Test
+    fun testWindowExtensionsVersion4() {
+        testRule.overrideExtensionVersion(4)
+        createTestEmbeddingCompat()
+
+        val splitInfo =
+            SplitInfo(
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                SplitAttributes.Builder().build(),
+                binder = INVALID_SPLIT_INFO_TOKEN,
+            )
+
+        embeddingCompat.setSplitAttributesCalculator { _ -> TEST_SPLIT_ATTRIBUTES }
+        verify(embeddingExtension)
+            .setSplitAttributesCalculator(
+                any<Function<OemSplitAttributesCalculatorParams, OemSplitAttributes>>()
+            )
+
+        embeddingCompat.clearSplitAttributesCalculator()
+        verify(embeddingExtension).clearSplitAttributesCalculator()
+
+        assertThrows(UnsupportedOperationException::class.java) {
+            embeddingCompat.setLaunchingActivityStack(options, activityStack)
+        }
+        verify(options, never()).putBinder(any(), any())
+
+        assertThrows(UnsupportedOperationException::class.java) {
+            embeddingCompat.finishActivityStacks(emptySet())
+        }
+        verify(embeddingExtension, never())
+            .finishActivityStacksWithTokens(any<Set<ActivityStackToken>>())
+
+        embeddingCompat.updateSplitAttributes(splitInfo, TEST_SPLIT_ATTRIBUTES)
+        verify(embeddingExtension)
+            .updateSplitAttributes(splitInfo.getBinder(), OemSplitAttributes.Builder().build())
+
+        embeddingCompat.invalidateVisibleActivityStacks()
+        verify(embeddingExtension).invalidateTopVisibleSplitAttributes()
+
+        verifyOverlayFeatureApis()
+        verifyActivityWindowInfoCallbackController()
+    }
+
+    @Test
+    fun testWindowExtensionsVersion5() {
+        testRule.overrideExtensionVersion(5)
+        createTestEmbeddingCompat()
+
+        val splitInfo =
+            SplitInfo(
+                ActivityStack(emptyList(), isEmpty = true),
+                ActivityStack(emptyList(), isEmpty = true),
+                SplitAttributes.Builder().build(),
+                token = SplitInfoToken.createFromBinder(INVALID_SPLIT_INFO_TOKEN),
+            )
+
+        embeddingCompat.setSplitAttributesCalculator { _ -> TEST_SPLIT_ATTRIBUTES }
+        verify(embeddingExtension)
+            .setSplitAttributesCalculator(
+                any<Function<OemSplitAttributesCalculatorParams, OemSplitAttributes>>()
+            )
+
+        embeddingCompat.clearSplitAttributesCalculator()
+        verify(embeddingExtension).clearSplitAttributesCalculator()
+
+        embeddingCompat.setLaunchingActivityStack(options, activityStack)
+        verify(options)
+            .putBundle(eq(ActivityEmbeddingOptionsProperties.KEY_ACTIVITY_STACK_TOKEN), any())
+
+        embeddingCompat.finishActivityStacks(emptySet())
+        verify(embeddingExtension).finishActivityStacksWithTokens(emptySet())
+
+        embeddingCompat.updateSplitAttributes(splitInfo, TEST_SPLIT_ATTRIBUTES)
+        verify(embeddingExtension)
+            .updateSplitAttributes(splitInfo.getToken(), OemSplitAttributes.Builder().build())
+
+        embeddingCompat.invalidateVisibleActivityStacks()
+        verify(embeddingExtension).invalidateTopVisibleSplitAttributes()
+
+        verifyOverlayFeatureApis()
+        verifyActivityWindowInfoCallbackController()
+    }
+
+    @Test
+    fun testWindowExtensionsVersion6() {
+        testRule.overrideExtensionVersion(6)
+        createTestEmbeddingCompat()
+
+        verifyOverlayFeatureApis()
+        verifyActivityWindowInfoCallbackController()
+    }
+
+    @Test
+    fun testWindowExtensionsVersion7() {
+        testRule.overrideExtensionVersion(7)
+        createTestEmbeddingCompat()
+
+        verifyOverlayFeatureApis()
+        verifyActivityWindowInfoCallbackController()
+    }
+
+    private fun verifyOverlayFeatureApis() {
+        if (WindowSdkExtensions.getInstance().extensionVersion >= OVERLAY_FEATURE_VERSION) {
+            embeddingCompat.setOverlayCreateParams(options, OverlayCreateParams())
+            // Verify if the overlay tag is put to the activityOptions bundle
+            verify(options).putString(any(), any())
+
+            val calculator = { _: OverlayAttributesCalculatorParams -> OverlayAttributes() }
+            embeddingCompat.setOverlayAttributesCalculator(calculator)
+            assertEquals(
+                calculator,
+                embeddingCompat.overlayController!!.overlayAttributesCalculator
+            )
+
+            embeddingCompat.updateOverlayAttributes("", OverlayAttributes())
+            verify(embeddingCompat.overlayController)!!.updateOverlayAttributes(
+                "",
+                OverlayAttributes()
+            )
+
+            val executor = mock<Executor>()
+            embeddingCompat.addOverlayInfoCallback("", executor) {}
+            verify(embeddingCompat.overlayController)!!.addOverlayInfoCallback(
+                eq(""),
+                eq(executor),
+                any()
+            )
+
+            embeddingCompat.removeOverlayInfoCallback {}
+            verify(embeddingCompat.overlayController)!!.removeOverlayInfoCallback(any())
+        } else {
+            assertThrows(UnsupportedOperationException::class.java) {
+                embeddingCompat.setOverlayCreateParams(options, OverlayCreateParams())
+            }
+            // Verify if the overlay tag is put to the activityOptions bundle
+            verify(options, never()).putString(any(), any())
+
+            assertThrows(UnsupportedOperationException::class.java) {
+                embeddingCompat.setOverlayAttributesCalculator { _ -> OverlayAttributes() }
+            }
+            assertNull(embeddingCompat.overlayController)
+
+            assertThrows(UnsupportedOperationException::class.java) {
+                embeddingCompat.updateOverlayAttributes("", OverlayAttributes())
+            }
+            verify(embeddingExtension, never()).updateActivityStackAttributes(any(), any())
+
+            embeddingCompat.addOverlayInfoCallback("", Runnable::run) {}
+            verify(embeddingExtension, never()).registerActivityStackCallback(any(), any())
+
+            embeddingCompat.removeOverlayInfoCallback {}
+            verify(embeddingExtension, never()).unregisterActivityStackCallback(any())
+        }
+    }
+
+    private fun verifyActivityWindowInfoCallbackController() {
+        if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
+            ActivityWindowInfoCallbackController(embeddingExtension)
+        } else {
+            assertThrows(UnsupportedOperationException::class.java) {
+                ActivityWindowInfoCallbackController(embeddingExtension)
+            }
+        }
+    }
+
+    private fun createTestEmbeddingCompat() {
+        val overlayController =
+            if (WindowSdkExtensions.getInstance().extensionVersion >= OVERLAY_FEATURE_VERSION) {
+                spy(
+                    OverlayControllerImpl(
+                        embeddingExtension,
+                        EmbeddingAdapter(PredicateAdapter(classLoader))
+                    )
+                )
+            } else {
+                null
+            }
+        overlayController?.apply {
+            doNothing().whenever(this).updateOverlayAttributes(any(), any())
+            doNothing().whenever(this).addOverlayInfoCallback(any(), any(), any())
+            doNothing().whenever(this).removeOverlayInfoCallback(any())
+        }
+
+        val activityWindowInfoCallbackController =
+            if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
+                spy(ActivityWindowInfoCallbackController(embeddingExtension))
+            } else {
+                null
+            }
+
+        embeddingCompat =
+            EmbeddingCompat(
+                embeddingExtension,
+                EmbeddingAdapter(PredicateAdapter(classLoader)),
+                ConsumerAdapter(classLoader),
+                applicationContext,
+                overlayController,
+                activityWindowInfoCallbackController,
+            )
     }
 
     companion object {
@@ -184,7 +417,6 @@
                 ActivityStack(emptyList(), isEmpty = true),
                 ActivityStack(emptyList(), isEmpty = true),
                 SplitAttributes.Builder().build(),
-                INVALID_SPLIT_INFO_TOKEN,
             )
 
         private val TEST_SPLIT_ATTRIBUTES = SplitAttributes.Builder().build()
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitAttributesCalculatorParamsTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitAttributesCalculatorParamsTest.kt
index 70fa3cf..43817d5 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitAttributesCalculatorParamsTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitAttributesCalculatorParamsTest.kt
@@ -32,7 +32,7 @@
 
     @Test
     fun testSplitAttributesCalculatorParams() {
-        val parentWindowMetrics = WindowMetrics(Rect())
+        val parentWindowMetrics = WindowMetrics(Rect(), density = 1f)
         val parentConfiguration = Configuration()
         val parentWindowLayoutInfo = WindowLayoutInfo(emptyList())
         val defaultSplitAttributes = SplitAttributes.Builder().build()
@@ -59,7 +59,7 @@
 
     @Test
     fun testToString() {
-        val parentWindowMetrics = WindowMetrics(Rect())
+        val parentWindowMetrics = WindowMetrics(Rect(), density = 1f)
         val parentConfiguration = Configuration()
         val parentWindowLayoutInfo = WindowLayoutInfo(emptyList())
         val defaultSplitAttributes = SplitAttributes.Builder().build()
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitAttributesTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitAttributesTest.kt
index 313fd4e..7b63820 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitAttributesTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitAttributesTest.kt
@@ -16,7 +16,10 @@
 
 package androidx.window.embedding
 
+import android.graphics.Color
 import androidx.window.core.WindowStrictModeException
+import androidx.window.embedding.DividerAttributes.DraggableDividerAttributes
+import androidx.window.embedding.DividerAttributes.FixedDividerAttributes
 import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.BOTTOM_TO_TOP
 import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LEFT_TO_RIGHT
 import androidx.window.embedding.SplitAttributes.LayoutDirection.Companion.LOCALE
@@ -42,16 +45,35 @@
             SplitAttributes.Builder()
                 .setSplitType(SPLIT_TYPE_EQUAL)
                 .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
                 .build()
         val attrs2 =
             SplitAttributes.Builder()
                 .setSplitType(SPLIT_TYPE_HINGE)
                 .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
                 .build()
         val attrs3 =
             SplitAttributes.Builder()
                 .setSplitType(SPLIT_TYPE_HINGE)
                 .setLayoutDirection(TOP_TO_BOTTOM)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
+                .build()
+        val attrs4 =
+            SplitAttributes.Builder()
+                .setSplitType(SPLIT_TYPE_HINGE)
+                .setLayoutDirection(TOP_TO_BOTTOM)
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GREEN)
+                )
+                .build()
+        val attrs5 =
+            SplitAttributes.Builder()
+                .setSplitType(SPLIT_TYPE_HINGE)
+                .setLayoutDirection(TOP_TO_BOTTOM)
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GREEN)
+                )
                 .build()
 
         assertNotEquals(attrs1, attrs2)
@@ -62,6 +84,62 @@
 
         assertNotEquals(attrs3, attrs1)
         assertNotEquals(attrs3.hashCode(), attrs1.hashCode())
+
+        assertNotEquals(attrs3, attrs4)
+        assertNotEquals(attrs3.hashCode(), attrs4.hashCode())
+
+        assertEquals(attrs4, attrs5)
+        assertEquals(attrs4.hashCode(), attrs5.hashCode())
+    }
+
+    @Test
+    fun testSplitAttributesEquals_withDividerAttributes() {
+        // No divider
+        val attrs1 =
+            SplitAttributes.Builder()
+                .setSplitType(SPLIT_TYPE_EQUAL)
+                .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
+                .build()
+
+        // Fixed divider
+        val attrs2 =
+            SplitAttributes.Builder()
+                .setSplitType(SPLIT_TYPE_EQUAL)
+                .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
+                .setDividerAttributes(FixedDividerAttributes.Builder().build())
+                .build()
+
+        // Draggable divider
+        val attrs3 =
+            SplitAttributes.Builder()
+                .setSplitType(SPLIT_TYPE_EQUAL)
+                .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
+                .setDividerAttributes(DraggableDividerAttributes.Builder().build())
+                .build()
+
+        // Draggable divider same as attrs3
+        val attrs4 =
+            SplitAttributes.Builder()
+                .setSplitType(SPLIT_TYPE_EQUAL)
+                .setLayoutDirection(LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
+                .setDividerAttributes(DraggableDividerAttributes.Builder().build())
+                .build()
+
+        // No divider vs fixed divider
+        assertNotEquals(attrs1, attrs2)
+        assertNotEquals(attrs1.hashCode(), attrs2.hashCode())
+
+        // Fixed divider vs draggable divider
+        assertNotEquals(attrs2, attrs3)
+        assertNotEquals(attrs2.hashCode(), attrs3.hashCode())
+
+        // Same draggable divider
+        assertEquals(attrs3, attrs4)
+        assertEquals(attrs3.hashCode(), attrs4.hashCode())
     }
 
     @Test
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt
index 2636fce..cca8a13 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitControllerTest.kt
@@ -18,7 +18,6 @@
 
 import android.app.Activity
 import androidx.core.util.Consumer
-import androidx.window.core.ExperimentalWindowApi
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.take
 import kotlinx.coroutines.flow.toList
@@ -34,7 +33,7 @@
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
-@OptIn(ExperimentalCoroutinesApi::class, ExperimentalWindowApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 internal class SplitControllerTest {
 
     private val mockBackend = mock<EmbeddingBackend>()
@@ -50,7 +49,6 @@
                         ActivityStack(emptyList(), true),
                         ActivityStack(emptyList(), true),
                         SplitAttributes(),
-                        mock()
                     )
                 )
             doAnswer { invocationOnMock ->
@@ -98,15 +96,8 @@
                 ActivityStack(emptyList(), true),
                 ActivityStack(emptyList(), true),
                 mockSplitAttributes,
-                mock()
             )
         splitController.updateSplitAttributes(mockSplitInfo, mockSplitAttributes)
         verify(mockBackend).updateSplitAttributes(eq(mockSplitInfo), eq(mockSplitAttributes))
     }
-
-    @Test
-    fun test_invalidateTopVisibleSplitAttributes_delegates() {
-        splitController.invalidateTopVisibleSplitAttributes()
-        verify(mockBackend).invalidateTopVisibleSplitAttributes()
-    }
 }
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
index 71bfc69..fd77ee8 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
@@ -18,14 +18,24 @@
 
 import android.app.Activity
 import android.os.Binder
+import androidx.window.WindowSdkExtensionsRule
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.mockito.kotlin.mock
 
 /** Unit tests for [SplitInfo] */
 class SplitInfoTest {
 
+    @get:Rule val testRule = WindowSdkExtensionsRule()
+
+    @Before
+    fun setUp() {
+        testRule.overrideExtensionVersion(3)
+    }
+
     @Test
     fun testSplitInfoContainsActivityFirstStack() {
         val activity = mock<Activity>()
@@ -95,6 +105,6 @@
 
     private fun createTestActivityStack(
         activitiesInProcess: List<Activity>,
-        isEmpty: Boolean = false,
+        isEmpty: Boolean = false
     ): ActivityStack = ActivityStack(activitiesInProcess, isEmpty)
 }
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
index 1fdfe34..5600053 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitPairRuleTest.kt
@@ -17,6 +17,7 @@
 package androidx.window.embedding
 
 import android.content.ComponentName
+import android.graphics.Color
 import android.graphics.Rect
 import androidx.window.core.ActivityComponentInfo
 import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
@@ -78,6 +79,7 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
                 .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
                 .build()
         TestCase.assertNull(rule.tag)
         assertEquals(SPLIT_MIN_DIMENSION_DP_DEFAULT, rule.minWidthDp)
@@ -101,6 +103,9 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
                 .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GREEN)
+                )
                 .build()
         filters.add(SplitPairFilter(ComponentName("a", "b"), ComponentName("c", "d"), "ACTION"))
         val rule =
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt
index bd5dfe5..8dae4c38 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitPlaceHolderRuleTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName
 import android.content.Intent
+import android.graphics.Color
 import android.graphics.Rect
 import androidx.window.core.ActivityComponentInfo
 import androidx.window.embedding.SplitRule.Companion.SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT
@@ -72,6 +73,7 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.5f))
                 .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+                .setAnimationBackground(EmbeddingAnimationBackground.DEFAULT)
                 .build()
         assertEquals(expectedSplitLayout, rule.defaultSplitAttributes)
         assertTrue(rule.checkParentBounds(density, validBounds))
@@ -91,6 +93,9 @@
             SplitAttributes.Builder()
                 .setSplitType(SplitAttributes.SplitType.ratio(0.3f))
                 .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+                .setAnimationBackground(
+                    EmbeddingAnimationBackground.createColorBackground(Color.GREEN)
+                )
                 .build()
         val rule =
             SplitPlaceholderRule.Builder(filters, intent)
diff --git a/window/window/src/test/java/androidx/window/reflection/ReflectionUtilsTest.kt b/window/window/src/test/java/androidx/window/reflection/ReflectionUtilsTest.kt
index 1a51386..c34c857 100644
--- a/window/window/src/test/java/androidx/window/reflection/ReflectionUtilsTest.kt
+++ b/window/window/src/test/java/androidx/window/reflection/ReflectionUtilsTest.kt
@@ -36,43 +36,46 @@
 
     @Test
     fun testValidateReflectionSuccess() {
-        val result = validateReflection { true }
+        val result = validateReflection("") { true }
         assertTrue(result)
     }
 
     @Test
     fun testValidateReflectionFail() {
-        val result = validateReflection {
-            classLoader.loadClass("SomeUnExistedClass.java")
-            true
-        }
+        val result =
+            validateReflection("") {
+                classLoader.loadClass("SomeUnExistedClass.java")
+                true
+            }
         assertFalse(result)
     }
 
     @Test
     fun testMethodModifier() {
-        val result = validateReflection {
-            val testClass = this::class.java
-            val privateMethod = testClass.getDeclaredMethod("testMethod").isPublic
-            assertFalse(privateMethod)
-            val publicMethod = testClass.getDeclaredMethod("testMethodModifier").isPublic
-            assertTrue(publicMethod)
-            true
-        }
+        val result =
+            validateReflection("") {
+                val testClass = this::class.java
+                val privateMethod = testClass.getDeclaredMethod("testMethod").isPublic
+                assertFalse(privateMethod)
+                val publicMethod = testClass.getDeclaredMethod("testMethodModifier").isPublic
+                assertTrue(publicMethod)
+                true
+            }
         assertTrue(result)
     }
 
     @Test
     fun testDoesReturn() {
-        val result = validateReflection {
-            val testClass = this::class.java
-            val privateMethod = testClass.getDeclaredMethod("testMethod")
-            assertTrue(privateMethod.doesReturn(Int::class.java))
-            assertTrue(privateMethod.doesReturn(Int::class))
-            assertFalse(privateMethod.doesReturn(Any::class.java))
-            assertFalse(privateMethod.doesReturn(Any::class))
-            true
-        }
+        val result =
+            validateReflection("") {
+                val testClass = this::class.java
+                val privateMethod = testClass.getDeclaredMethod("testMethod")
+                assertTrue(privateMethod.doesReturn(Int::class.java))
+                assertTrue(privateMethod.doesReturn(Int::class))
+                assertFalse(privateMethod.doesReturn(Any::class.java))
+                assertFalse(privateMethod.doesReturn(Any::class))
+                true
+            }
         assertTrue(result)
     }
 
diff --git a/window/window/src/testUtil/java/androidx/window/layout/TestWindowMetricsCalculator.kt b/window/window/src/testUtil/java/androidx/window/layout/TestWindowMetricsCalculator.kt
index a0c2a4b..dffdc4f 100644
--- a/window/window/src/testUtil/java/androidx/window/layout/TestWindowMetricsCalculator.kt
+++ b/window/window/src/testUtil/java/androidx/window/layout/TestWindowMetricsCalculator.kt
@@ -66,7 +66,7 @@
 
     override fun computeCurrentWindowMetrics(@UiContext context: Context): WindowMetrics {
         val bounds = overrideBounds ?: currentBounds[context] ?: Rect()
-        return WindowMetrics(bounds)
+        return WindowMetrics(bounds, density = 1f)
     }
 
     override fun computeMaximumWindowMetrics(activity: Activity): WindowMetrics {
@@ -75,7 +75,7 @@
 
     override fun computeMaximumWindowMetrics(@UiContext context: Context): WindowMetrics {
         val bounds = overrideMaxBounds ?: maxBounds[context] ?: Rect()
-        return WindowMetrics(bounds)
+        return WindowMetrics(bounds, density = 1f)
     }
 
     /** Clears any overrides set with [.setCurrentBounds] or [.setCurrentBoundsForActivity]. */
diff --git a/work/integration-tests/testapp/build.gradle b/work/integration-tests/testapp/build.gradle
index 7bac479..e9e5895 100644
--- a/work/integration-tests/testapp/build.gradle
+++ b/work/integration-tests/testapp/build.gradle
@@ -26,6 +26,7 @@
             minifyEnabled = true
         }
     }
+    compileSdkVersion 35
     defaultConfig {
         javaCompileOptions {
             annotationProcessorOptions {
diff --git a/work/integration-tests/testapp/lint-baseline.xml b/work/integration-tests/testapp/lint-baseline.xml
index a4a2489..c4275d6 100644
--- a/work/integration-tests/testapp/lint-baseline.xml
+++ b/work/integration-tests/testapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha12" type="baseline" client="gradle" dependencies="false" name="AGP (8.4.0-alpha12)" variant="all" version="8.4.0-alpha12">
+<issues format="6" by="lint 8.6.0-beta01" type="baseline" client="gradle" dependencies="false" name="AGP (8.6.0-beta01)" variant="all" version="8.6.0-beta01">
 
     <issue
         id="BanThreadSleep"
@@ -20,6 +20,15 @@
     </issue>
 
     <issue
+        id="MissingServiceExportedEqualsTrue"
+        message="Missing exported=true in &lt;service> tag"
+        errorLine1="        &lt;service"
+        errorLine2="        ^">
+        <location
+            file="src/main/AndroidManifest.xml"/>
+    </issue>
+
+    <issue
         id="ObsoleteSdkInt"
         message="Unnecessary; SDK_INT is never &lt; 21"
         errorLine1="    if (Build.VERSION.SDK_INT &lt; 21) {"
diff --git a/work/integration-tests/testapp/src/main/AndroidManifest.xml b/work/integration-tests/testapp/src/main/AndroidManifest.xml
index fde3cf6..5db02d6 100644
--- a/work/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/work/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -17,6 +17,11 @@
     xmlns:tools="http://schemas.android.com/tools">
 
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
+
     <application
         android:name=".TestApplication"
         android:allowBackup="true"
@@ -47,6 +52,11 @@
             tools:node="remove" />
 
         <service
+            android:name="androidx.work.impl.foreground.SystemForegroundService"
+            android:foregroundServiceType="dataSync|location"
+            tools:node="merge" />
+
+        <service
             android:name="androidx.work.multiprocess.RemoteWorkerService"
             android:exported="false"
             android:process=":worker1"
diff --git a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/ForegroundLocationWorker.kt b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/ForegroundLocationWorker.kt
new file mode 100644
index 0000000..feecb7e
--- /dev/null
+++ b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/ForegroundLocationWorker.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.integration.testapp
+
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.annotation.SuppressLint
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
+import android.location.LocationManager
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.core.app.ActivityCompat
+import androidx.core.app.NotificationCompat
+import androidx.work.CoroutineWorker
+import androidx.work.Data
+import androidx.work.ForegroundInfo
+import androidx.work.WorkerParameters
+import androidx.work.workDataOf
+import kotlinx.coroutines.delay
+
+class ForegroundLocationWorker(context: Context, parameters: WorkerParameters) :
+    CoroutineWorker(context, parameters) {
+
+    private val notificationManager =
+        context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+    private val locationManager =
+        context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+
+    private var currentLocation: Data = Data.EMPTY
+
+    override suspend fun doWork(): Result {
+        val permissionStatus =
+            ActivityCompat.checkSelfPermission(applicationContext, ACCESS_COARSE_LOCATION)
+        if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
+            return Result.failure()
+        }
+
+        val notificationId = inputData.getInt(InputNotificationId, NotificationId)
+        val delayTime = inputData.getLong(InputDelayTime, Delay)
+
+        setForeground(getForegroundInfo(notificationId))
+
+        repeat(20) {
+            val location =
+                locationManager.getLastKnownLocation(locationManager.allProviders.first())
+            currentLocation =
+                workDataOf(Lat to (location?.latitude ?: 0.0), Lon to (location?.longitude ?: 0.0))
+            setProgress(currentLocation)
+            if (Build.VERSION.SDK_INT < 31) {
+                // No need for notifications starting S.
+                notificationManager.notify(
+                    notificationId,
+                    getForegroundInfo(notificationId).notification
+                )
+            }
+            delay(delayTime)
+        }
+
+        return Result.success()
+    }
+
+    override suspend fun getForegroundInfo(): ForegroundInfo {
+        val notificationId = inputData.getInt(InputNotificationId, NotificationId)
+        return getForegroundInfo(notificationId)
+    }
+
+    private fun getForegroundInfo(notificationId: Int): ForegroundInfo {
+        val latitude = currentLocation.getDouble(Lat, 0.0)
+        val longitude = currentLocation.getDouble(Lon, 0.0)
+        val id = applicationContext.getString(R.string.channel_id)
+        val title = applicationContext.getString(R.string.notification_title)
+        val content = "Location ($latitude,$longitude) %"
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            createChannel()
+        }
+
+        val notification =
+            NotificationCompat.Builder(applicationContext, id)
+                .setContentTitle(title)
+                .setTicker(title)
+                .setContentText(content)
+                .setSmallIcon(R.drawable.ic_work_notification)
+                .setOngoing(true)
+                .build()
+
+        return ForegroundInfo(notificationId, notification, FOREGROUND_SERVICE_TYPE_LOCATION)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @SuppressLint("ClassVerificationFailure")
+    private fun createChannel() {
+        val id = applicationContext.getString(R.string.channel_id)
+        val name = applicationContext.getString(R.string.channel_name)
+        val description = applicationContext.getString(R.string.channel_description)
+        val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW)
+        channel.description = description
+        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
+        notificationManager.createNotificationChannel(channel)
+    }
+
+    companion object {
+        private const val NotificationId = 10
+        private const val Delay = 1000L
+        private const val Lat = "Location.lat"
+        private const val Lon = "Location.Lon"
+        const val InputNotificationId = "NotificationId"
+        const val InputDelayTime = "DelayTime"
+    }
+}
diff --git a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/ForegroundWorker.kt b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/ForegroundWorker.kt
index ea8315c..d327c6f 100644
--- a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/ForegroundWorker.kt
+++ b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/ForegroundWorker.kt
@@ -21,6 +21,7 @@
 import android.app.NotificationChannel
 import android.app.NotificationManager
 import android.content.Context
+import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
 import android.os.Build
 import androidx.annotation.RequiresApi
 import androidx.core.app.NotificationCompat
@@ -81,7 +82,7 @@
                 .setOngoing(true)
                 .build()
 
-        return ForegroundInfo(notificationId, notification)
+        return ForegroundInfo(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)
     }
 
     @RequiresApi(Build.VERSION_CODES.O)
diff --git a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.kt b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.kt
index 45ff989..6e55ac3 100644
--- a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.kt
+++ b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.kt
@@ -299,6 +299,19 @@
             lastForegroundWorkRequest = request
             workManager.enqueue(request)
         }
+        findViewById<View>(R.id.run_foreground_worker_location).setOnClickListener {
+            lastNotificationId += 1
+            val inputData =
+                workDataOf(ForegroundLocationWorker.InputNotificationId to lastNotificationId)
+
+            val request =
+                OneTimeWorkRequest.Builder(ForegroundLocationWorker::class.java)
+                    .setInputData(inputData)
+                    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
+                    .build()
+            lastForegroundWorkRequest = request
+            workManager.enqueue(request)
+        }
         findViewById<View>(R.id.run_foreground_worker_network_request).setOnClickListener {
             lastNotificationId += 1
             val inputData = workDataOf(ForegroundWorker.InputNotificationId to lastNotificationId)
@@ -382,9 +395,7 @@
         findViewById<View>(R.id.crash_app).setOnClickListener {
             throw RuntimeException("Crashed app")
         }
-        findViewById<View>(R.id.enqueue_infinite_work_charging).setOnClickListener {
-            queueLotsOfWorkers(workManager)
-        }
+        findViewById<View>(R.id.stress_test).setOnClickListener { queueLotsOfWorkers(workManager) }
         findViewById<View>(R.id.enqueue_network_request).setOnClickListener {
             enqueueWithNetworkRequest(workManager)
         }
diff --git a/work/integration-tests/testapp/src/main/res/layout/activity_main.xml b/work/integration-tests/testapp/src/main/res/layout/activity_main.xml
index 3a10bf4..7488ec0 100644
--- a/work/integration-tests/testapp/src/main/res/layout/activity_main.xml
+++ b/work/integration-tests/testapp/src/main/res/layout/activity_main.xml
@@ -239,6 +239,14 @@
             android:layout_marginLeft="16dp"
             android:layout_marginStart="16dp"/>
 
+        <Button android:text="@string/run_foreground_worker_location"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/run_foreground_worker_location"
+            android:layout_marginTop="12dp"
+            android:layout_marginLeft="16dp"
+            android:layout_marginStart="16dp"/>
+
         <Button android:text="@string/cancel_foreground_worker"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/work/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/work/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index c2a49ce..50af1cc 100644
--- a/work/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/work/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -28,6 +28,7 @@
     <string name="run_constraint_tracking_worker">Run Constraint Tracking Worker</string>
     <string name="run_foreground_worker">Run Foreground Worker</string>
     <string name="run_foreground_worker_network_request">Run Foreground Worker with Network request</string>
+    <string name="run_foreground_worker_location">Run Foreground Worker with Location</string>
     <string name="cancel_foreground_worker">Cancel Foreground Worker</string>
     <string name="cancel_foreground_worker_intent">Cancel Foreground Worker (Intent)</string>
     <string name="enqueue_work_multi_process">Enqueue Work (Multi-process)</string>
diff --git a/work/work-benchmark/build.gradle b/work/work-benchmark/build.gradle
index edf429d..3ac1da2 100644
--- a/work/work-benchmark/build.gradle
+++ b/work/work-benchmark/build.gradle
@@ -53,5 +53,6 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.work.benchmark"
 }
diff --git a/work/work-datatransfer/build.gradle b/work/work-datatransfer/build.gradle
index b90d5b32..21e3a10 100644
--- a/work/work-datatransfer/build.gradle
+++ b/work/work-datatransfer/build.gradle
@@ -30,6 +30,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         minSdkVersion 23
     }
diff --git a/work/work-gcm/build.gradle b/work/work-gcm/build.gradle
index a6faef5..b316707 100644
--- a/work/work-gcm/build.gradle
+++ b/work/work-gcm/build.gradle
@@ -30,6 +30,7 @@
 }
 
 android {
+    compileSdk 35
     buildTypes.configureEach {
         consumerProguardFiles "proguard-rules.pro"
     }
@@ -58,6 +59,5 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2019"
     description = "Android WorkManager GCMNetworkManager Support"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/work/work-inspection/build.gradle b/work/work-inspection/build.gradle
index b06520e..c86171b 100644
--- a/work/work-inspection/build.gradle
+++ b/work/work-inspection/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.8.1")
     api(libs.kotlinStdlib)
     compileOnly("androidx.inspection:inspection:1.0.0")
     compileOnly(project(":lifecycle:lifecycle-runtime"))
@@ -57,6 +57,7 @@
 }
 
 android {
+    compileSdk 35
     defaultConfig {
         // studio pipeline works only starting with Android O
         minSdkVersion 26
diff --git a/work/work-multiprocess/build.gradle b/work/work-multiprocess/build.gradle
index 733379d..692e72f 100644
--- a/work/work-multiprocess/build.gradle
+++ b/work/work-multiprocess/build.gradle
@@ -30,6 +30,7 @@
 }
 
 android {
+    compileSdk 35
     buildTypes.configureEach {
         consumerProguardFiles "proguard-rules.pro"
     }
@@ -41,7 +42,6 @@
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesAndroid)
     api(libs.guavaListenableFuture)
-    implementation("androidx.core:core:1.12.0")
     implementation("androidx.concurrent:concurrent-futures-ktx:1.2.0-alpha03")
     implementation("androidx.room:room-runtime:2.6.1")
     androidTestImplementation(libs.testExtJunit)
@@ -59,6 +59,5 @@
     inceptionYear = "2020"
     description = "Android WorkManager runtime library"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/work/work-runtime-ktx/build.gradle b/work/work-runtime-ktx/build.gradle
index e7a7ef4..98868c5 100644
--- a/work/work-runtime-ktx/build.gradle
+++ b/work/work-runtime-ktx/build.gradle
@@ -38,10 +38,10 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Android WorkManager Kotlin Extensions"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.work.ktx"
 }
diff --git a/work/work-runtime/build.gradle b/work/work-runtime/build.gradle
index 004fffe..8a5ec39 100644
--- a/work/work-runtime/build.gradle
+++ b/work/work-runtime/build.gradle
@@ -55,6 +55,7 @@
             }
         }
     }
+    compileSdk = 35
     sourceSets {
         androidTest.assets.srcDirs += files("$projectDir/src/schemas".toString())
     }
@@ -63,7 +64,7 @@
 
 dependencies {
     ksp("androidx.room:room-compiler:2.6.1")
-    implementation("androidx.core:core:1.12.0")
+    api("androidx.core:core:1.12.0")
     implementation("androidx.room:room-ktx:2.6.1")
     implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     api("androidx.annotation:annotation-experimental:1.4.1")
@@ -106,6 +107,5 @@
     inceptionYear = "2018"
     description = "Android WorkManager runtime library"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/WorkConstrainsDaoTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/WorkConstrainsDaoTest.kt
new file mode 100644
index 0000000..59ce0a2
--- /dev/null
+++ b/work/work-runtime/src/androidTest/java/androidx/work/WorkConstrainsDaoTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.work.impl.model.WorkTypeConverters
+import androidx.work.worker.TestWorker
+import com.google.common.truth.Truth.assertThat
+import java.nio.ByteBuffer
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// TODO(b/209145335): Combine with WorkSpecDaoTest when it is converted to Kotlin
+@RunWith(AndroidJUnit4::class)
+class WorkConstrainsDaoTest : DatabaseTest() {
+    @Test
+    @SmallTest
+    @SdkSuppress(minSdkVersion = 31)
+    fun readWithNetworkRequestWithInvalidCapability() {
+        val workRequest =
+            OneTimeWorkRequest.Builder(TestWorker::class.java)
+                .setConstraints(
+                    Constraints.Builder()
+                        .setRequiredNetworkRequest(
+                            NetworkRequest.Builder()
+                                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                                .build(),
+                            NetworkType.CONNECTED
+                        )
+                        .build()
+                )
+                .build()
+
+        val workSpec = workRequest.workSpec
+        mDatabase.workSpecDao().insertWorkSpec(workSpec)
+
+        val currentBlob =
+            WorkTypeConverters.fromNetworkRequest(workSpec.constraints.requiredNetworkRequestCompat)
+        val newBlob =
+            ByteBuffer.allocate(currentBlob.size + 4)
+                .apply {
+                    currentBlob.forEach { put(it) } // Copy current blob content
+                    put(5, (currentBlob[5] + 4).toByte()) // Update blob size
+                    put(17, (currentBlob[17] + 1).toByte()) // Update capabilities size
+                    putInt(37) // Add a new invalid capability.
+                }
+                .array()
+
+        // Update the blob in work spec row include the new invalid capability
+        mDatabase
+            .compileStatement("UPDATE workspec SET required_network_request = ? WHERE id = ?")
+            .use { stmt ->
+                stmt.bindBlob(1, newBlob)
+                stmt.bindString(2, workSpec.id)
+                assertThat(stmt.executeUpdateDelete()).isEqualTo(1)
+            }
+
+        // Reading a work spec with an invalid capability should not cause an issue.
+        val newWorkSpec = checkNotNull(mDatabase.workSpecDao().getWorkSpec(workRequest.stringId))
+        assertThat(workSpec.constraints.requiredNetworkRequest!!.capabilities)
+            .isEqualTo(newWorkSpec.constraints.requiredNetworkRequest!!.capabilities)
+    }
+}
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java
index 8dd4953..6397ca8 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/background/systemjob/SystemJobInfoConverterTest.java
@@ -401,6 +401,20 @@
         }
     }
 
+    @Test
+    @SmallTest
+    public void testEnsureTraceTags() {
+        if (Build.VERSION.SDK_INT < 35) {
+            return;
+        }
+
+        final String id = "id";
+        WorkSpec workSpec = new WorkSpec(id, TestWorker.class.getName());
+        workSpec.setTraceTag(TestWorker.class.getSimpleName());
+        JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID);
+        assertEquals(jobInfo.getTraceTag(), TestWorker.class.getSimpleName());
+    }
+
     private WorkSpec getTestWorkSpecWithConstraints(Constraints constraints) {
         return new OneTimeWorkRequest.Builder(TestWorker.class)
                 .setConstraints(constraints)
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.kt b/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.kt
index f21c8f1..ae791e3 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.kt
@@ -51,6 +51,7 @@
 import java.util.concurrent.CancellationException
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.Future
+import kotlin.collections.removeLast as removeLastKt
 import kotlin.coroutines.coroutineContext
 import kotlin.coroutines.resumeWithException
 import kotlinx.coroutines.CancellableContinuation
@@ -420,7 +421,7 @@
     private fun iterativelyFailWorkAndDependents(workSpecId: String) {
         val idsToProcess = mutableListOf(workSpecId)
         while (idsToProcess.isNotEmpty()) {
-            val id = idsToProcess.removeLast()
+            val id = idsToProcess.removeLastKt()
             // Don't fail already cancelled work.
             if (workSpecDao.getState(id) !== WorkInfo.State.CANCELLED) {
                 workSpecDao.setState(WorkInfo.State.FAILED, id)
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java b/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
index dc462d6..158f9fc 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
@@ -139,6 +139,13 @@
             //noinspection NewApi
             builder.setExpedited(true);
         }
+        if (Build.VERSION.SDK_INT >= 35) {
+            // Add a trace tag that shows the actual worker running.
+            String traceTag = workSpec.getTraceTag();
+            if (traceTag != null) {
+                builder.setTraceTag(traceTag);
+            }
+        }
         return builder.build();
     }
 
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
index 9b1ac79..5057015 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
@@ -175,6 +175,11 @@
                 // is called, and the app is no longer in a state where it's possible to start a
                 // foreground service. WorkManager will eventually call stop() to clean up.
                 Logger.get().warning(TAG, "Unable to start foreground service", exception);
+            } catch (SecurityException exception) {
+                // In the case that a foreground type prerequisites permission is revoked
+                // and the service is restarted it will not be possible to start the
+                // foreground service.
+                Logger.get().warning(TAG, "Unable to start foreground service", exception);
             }
         }
     }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.kt b/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.kt
index 4854c3d..363724e 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.kt
@@ -25,6 +25,7 @@
 import androidx.work.impl.WorkManagerImpl
 import androidx.work.launchOperation
 import java.util.UUID
+import kotlin.collections.removeLast as removeLastKt
 
 private fun cancel(workManagerImpl: WorkManagerImpl, workSpecId: String) {
     iterativelyCancelWorkAndDependents(workManagerImpl.workDatabase, workSpecId)
@@ -48,7 +49,7 @@
     val dependencyDao = workDatabase.dependencyDao()
     val idsToProcess = mutableListOf(workSpecId)
     while (idsToProcess.isNotEmpty()) {
-        val id = idsToProcess.removeLast()
+        val id = idsToProcess.removeLastKt()
         // Don't fail already cancelled work.
         val state = workSpecDao.getState(id)
         if (state !== WorkInfo.State.SUCCEEDED && state !== WorkInfo.State.FAILED) {
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
index 47d8d5a..9e35c48 100644
--- 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
@@ -30,6 +30,7 @@
 import androidx.work.impl.model.WorkSpec
 import androidx.work.impl.workers.ARGUMENT_CLASS_NAME
 import androidx.work.impl.workers.ConstraintTrackingWorker
+import kotlin.collections.removeLast as removeLastKt
 
 internal fun checkContentUriTriggerWorkerLimits(
     workDatabase: WorkDatabase,
@@ -40,7 +41,7 @@
     val continuations = mutableListOf(continuation)
     var newCount = 0
     while (continuations.isNotEmpty()) {
-        val current = continuations.removeLast()
+        val current = continuations.removeLastKt()
         newCount += current.work.count { it.workSpec.constraints.hasContentUriTriggers() }
         (current.parents as List<WorkContinuationImpl>?)?.let { continuations.addAll(it) }
     }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/NetworkRequestCompat.kt b/work/work-runtime/src/main/java/androidx/work/impl/utils/NetworkRequestCompat.kt
index d928d8b..0d152ba 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/NetworkRequestCompat.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/NetworkRequestCompat.kt
@@ -20,8 +20,14 @@
 import android.net.NetworkRequest
 import android.os.Build
 import androidx.annotation.RequiresApi
+import androidx.work.Logger
 
 internal data class NetworkRequestCompat(val wrapped: Any? = null) {
+
+    companion object {
+        val TAG = Logger.tagWithPrefix("NetworkRequestCompat")
+    }
+
     @get:RequiresApi(21)
     val networkRequest: NetworkRequest?
         get() = wrapped as NetworkRequest?
@@ -100,7 +106,19 @@
     @JvmStatic
     fun createNetworkRequest(capabilities: IntArray, transports: IntArray): NetworkRequest {
         val networkRequest = NetworkRequest.Builder()
-        capabilities.forEach { networkRequest.addCapability(it) }
+        capabilities.forEach {
+            try {
+                networkRequest.addCapability(it)
+            } catch (ex: IllegalArgumentException) {
+                // b/351180465 - Ignoring the IAE that addCapability() can throw on SDK < 35 and
+                // aligning with newer SDK behaviour. Capabilities are persisted in the database
+                // and the framework can by default add new ones. Catching this exception mitigates
+                // the case where decoding the capabilities from the database fails across OS
+                // changes.
+                Logger.get()
+                    .warning(NetworkRequestCompat.TAG, "Ignoring adding capability '$it'", ex)
+            }
+        }
         transports.forEach { networkRequest.addTransportType(it) }
         return networkRequest.build()
     }
diff --git a/work/work-rxjava2/build.gradle b/work/work-rxjava2/build.gradle
index 0a17f15..08025cc 100644
--- a/work/work-rxjava2/build.gradle
+++ b/work/work-rxjava2/build.gradle
@@ -45,10 +45,10 @@
     inceptionYear = "2018"
     description = "Android WorkManager RxJava2 interoperatibility library"
     failOnDeprecationWarnings = false
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.work.rxjava2"
 }
diff --git a/work/work-rxjava3/build.gradle b/work/work-rxjava3/build.gradle
index 8457e44..122af65 100644
--- a/work/work-rxjava3/build.gradle
+++ b/work/work-rxjava3/build.gradle
@@ -44,10 +44,10 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2020"
     description = "Android WorkManager RxJava3 interoperatibility library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.work.rxjava3"
 }
diff --git a/work/work-testing/build.gradle b/work/work-testing/build.gradle
index 32597de..a9caf52 100644
--- a/work/work-testing/build.gradle
+++ b/work/work-testing/build.gradle
@@ -58,10 +58,10 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2018"
     description = "Android WorkManager testing library"
-    metalavaK2UastEnabled = true
     legacyDisableKotlinStrictApiMode = true
 }
 
 android {
+    compileSdk 35
     namespace "androidx.work.testing"
 }